Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Chat and workspace agents: channels, DMs and named agents you talk to | 1 | /** |
| 2 | * Chat: channels, direct messages, threads and messages, kept by the chat | |
| The docs folder is gone, and what it held lives where people read it: how a self-hosted g1t runs and how to deploy g1t to Cloudflare are pages on docs.g1t.sh under Run g1t yourself, and speed, rate limits and operating g1t.sh are sections of CONTRIBUTING.md; code that cited a file in docs/ now points to the page or section that covers it, or says what it means itself, and applied migrations and the runner images are left as they were. | 3 | * service (`services/chat`). People and agents are members alike. |
| Chat and workspace agents: channels, DMs and named agents you talk to | 4 | * |
| 5 | * Wire shapes are snake_case end to end, so the site, the public API and | |
| 6 | * the live socket all carry the same objects. | |
| 7 | */ | |
| 8 | import type { ServiceBinding } from "./clients"; | |
| 9 | import type { User } from "./identity"; | |
| 10 | import type { Result } from "./result"; | |
| Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent. | 11 | import type { AgentLook } from "./agent-look"; |
| Chat and workspace agents: channels, DMs and named agents you talk to | 12 | import type { AskerAccess } from "./workspace-agents"; |
| 13 | ||
| 14 | /** Who is speaking: a person (by user id) or a workspace agent (by agent id). */ | |
| 15 | export type Principal = { kind: "user" | "agent"; id: string }; | |
| 16 | ||
| 17 | export function principalKey(principal: Principal): string { | |
| 18 | return `${principal.kind}:${principal.id}`; | |
| 19 | } | |
| 20 | ||
| 21 | export function parsePrincipalKey(key: string): Principal | null { | |
| 22 | const at = key.indexOf(":"); | |
| 23 | if (at < 0) return null; | |
| 24 | const kind = key.slice(0, at); | |
| 25 | const id = key.slice(at + 1); | |
| 26 | if ((kind !== "user" && kind !== "agent") || !id) return null; | |
| 27 | return { kind, id }; | |
| 28 | } | |
| 29 | ||
| 30 | /** How a member shows: resolved by the chat service when it answers. */ | |
| 31 | export type MemberProfile = Principal & { | |
| Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar | 32 | /** `username` for a person (lowercased: what `@` mentions), `handle` for an agent. */ |
| Chat and workspace agents: channels, DMs and named agents you talk to | 33 | name: string; |
| Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar | 34 | /** |
| 35 | * A person's username as they wrote it (`Ana`), when that differs from | |
| 36 | * `name`; absent for an agent. Shown in their card and in autocomplete. | |
| 37 | */ | |
| 38 | display_username?: string | null; | |
| 39 | /** | |
| 40 | * What chat shows them as: a person's display name, else their username | |
| 41 | * in its chosen case; an agent's display name. Read it with `memberName`. | |
| 42 | */ | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 43 | display_name: string; |
| 44 | /** Uploaded avatar hash for a person, or null for the letter avatar. */ | |
| 45 | avatar: string | null; | |
| 46 | /** An agent's one-line role ("Release manager for g1t"). */ | |
| 47 | role: string | null; | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 48 | /** |
| 49 | * An agent's title ("QA Engineer"); null for a person. Chat always sets | |
| 50 | * it; optional so profiles a page makes for itself need not. | |
| 51 | */ | |
| 52 | title?: string | null; | |
| Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent. | 53 | /** What an agent's face is drawn from when it chose none; null for a person. Always set by chat. */ |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 54 | avatar_seed?: string | null; |
| Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent. | 55 | /** An agent's chosen face (./agent-look.ts); null for a person, or an agent wearing its seed's face. */ |
| 56 | look?: AgentLook | null; | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 57 | }; |
| 58 | ||
| Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar | 59 | /** |
| 60 | * A member's handle as it shows (`@Ana`, without the `@`): a person's | |
| 61 | * username in its chosen case, an agent's handle. | |
| 62 | */ | |
| 63 | export function memberHandle(member: { name: string; display_username?: string | null }): string { | |
| 64 | const display = member.display_username; | |
| 65 | return display && display.toLowerCase() === member.name.toLowerCase() ? display : member.name; | |
| 66 | } | |
| 67 | ||
| 68 | /** | |
| 69 | * How a member's name is shown everywhere in chat (messages, the | |
| 70 | * sidebar, direct-message titles, typing, notifications and pushes): their | |
| 71 | * display name, else their handle in its chosen case. One rule, so every | |
| 72 | * surface agrees. | |
| 73 | */ | |
| 74 | export function memberName(member: { name: string; display_name?: string | null; display_username?: string | null }): string { | |
| 75 | return member.display_name?.trim() || memberHandle(member); | |
| 76 | } | |
| 77 | ||
| Chat and workspace agents: channels, DMs and named agents you talk to | 78 | export type ChannelKind = "channel" | "dm"; |
| 79 | ||
| 80 | export type Channel = { | |
| 81 | id: string; | |
| 82 | workspace_id: string; | |
| 83 | kind: ChannelKind; | |
| 84 | /** Lowercase, no `#`. Null for a direct message. */ | |
| 85 | name: string | null; | |
| 86 | topic: string | null; | |
| 87 | private: boolean; | |
| 88 | created_by: Principal; | |
| 89 | created_at: string; | |
| 90 | archived_at: string | null; | |
| 91 | last_message_at: string | null; | |
| 92 | }; | |
| 93 | ||
| 94 | export type ChannelMember = { | |
| 95 | channel_id: string; | |
| 96 | member: MemberProfile; | |
| 97 | role: "owner" | "member"; | |
| 98 | starred: boolean; | |
| 99 | muted: boolean; | |
| 100 | last_read_id: string | null; | |
| 101 | joined_at: string; | |
| 102 | }; | |
| 103 | ||
| 104 | /** A card g1t or an agent posts: an event, a task, an approval. */ | |
| 105 | export type MessageCard = { | |
| Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar | 106 | /** What it is about, e.g. `pull`, `issue`, `task`, `deploy`, `approval`, `session`, `draft_issue`. */ |
| Chat and workspace agents: channels, DMs and named agents you talk to | 107 | kind: string; |
| 108 | title: string; | |
| 109 | /** A short line under the title: "3/3 checks · +214 −87". */ | |
| 110 | detail: string | null; | |
| 111 | /** A status shown on the right: "Needs approval", "Merged". */ | |
| 112 | state: string | null; | |
| 113 | /** Where clicking the card goes, relative to the site. */ | |
| 114 | href: string | null; | |
| Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar | 115 | /** A preview in Markdown under the title: a draft issue's body, a report's first lines. */ |
| 116 | body?: string | null; | |
| 117 | /** Labelled facts shown in two columns: "Repository · acme/web", "Cap · $2.00". */ | |
| 118 | fields?: CardField[]; | |
| 119 | /** | |
| 120 | * What people can do right here. Pressing one goes to the service that | |
| 121 | * owns the card (`owner`), which checks the person may, does it, and | |
| 122 | * updates the card in place. A card without `owner` has no actions. | |
| 123 | */ | |
| 124 | actions?: CardAction[]; | |
| 125 | /** The service whose card this is and that answers its actions: `agents`. */ | |
| 126 | owner?: "agents" | null; | |
| 127 | /** What the card is about, for its owner: a session id, a draft id. */ | |
| 128 | ref?: string | null; | |
| 129 | }; | |
| 130 | ||
| 131 | export type CardField = { label: string; value: string }; | |
| 132 | ||
| 133 | /** A button on a card. */ | |
| 134 | export type CardAction = { | |
| 135 | /** Unique on the card: `stop`, `approve`, `file`. */ | |
| 136 | id: string; | |
| 137 | label: string; | |
| 138 | style?: "primary" | "danger" | "default"; | |
| 139 | /** Asked before it runs: "Stop this session and everything under it?". */ | |
| 140 | confirm?: string | null; | |
| 141 | /** A value it needs first, asked inline: an amount in dollars, or a line of text. */ | |
| 142 | input?: { kind: "money" | "text"; label: string; placeholder?: string | null; initial?: string | null } | null; | |
| 143 | /** A link instead of an action: opens this place in the site. */ | |
| 144 | href?: string | null; | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 145 | }; |
| 146 | ||
| Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar | 147 | /** What pressing a card's action did, as the person who pressed it is told. */ |
| 148 | export type CardActionResult = { ok: boolean; message: string | null }; | |
| 149 | ||
| Chat and workspace agents: channels, DMs and named agents you talk to | 150 | export type ChatMessage = { |
| 151 | /** Time-sortable (ULID-like), so ordering by id is ordering by time. */ | |
| 152 | id: string; | |
| 153 | channel_id: string; | |
| 154 | author: MemberProfile; | |
| 155 | kind: "text" | "card"; | |
| 156 | body: string; | |
| 157 | card: MessageCard | null; | |
| 158 | /** The message this replies under, or null for a top-level message. */ | |
| 159 | thread_root: string | null; | |
| 160 | reply_count: number; | |
| 161 | last_reply_at: string | null; | |
| 162 | created_at: string; | |
| 163 | edited_at: string | null; | |
| 164 | deleted_at: string | null; | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 165 | /** |
| 166 | * Its reactions, one per emoji in the order each was first used. Chat | |
| 167 | * always sets it; optional so messages a page makes for itself need not. | |
| 168 | */ | |
| 169 | reactions?: ChatReaction[]; | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 170 | }; |
| 171 | ||
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 172 | /** |
| 173 | * One emoji's reactions on a message. `emoji` is a Unicode emoji or | |
| 174 | * `:name:` for one of the workspace's own (`CustomEmoji`). | |
| 175 | */ | |
| 176 | export type ChatReaction = { | |
| 177 | emoji: string; | |
| 178 | /** Everyone who reacted with it, people and agents alike. */ | |
| 179 | count: number; | |
| 180 | /** | |
| 181 | * Whether the viewer did. In live `message.*` events, which everyone in | |
| 182 | * the room gets, it is always false: keep your own from what you know. | |
| 183 | */ | |
| 184 | me: boolean; | |
| 185 | /** The first ten who reacted with it, for the hover list. */ | |
| 186 | by: MemberProfile[]; | |
| 187 | }; | |
| 188 | ||
| 189 | /** Who may add a workspace's emoji: every member (the default), or only its owners. */ | |
| 190 | export type EmojiUpload = "members" | "admins"; | |
| 191 | ||
| 192 | /** | |
| 193 | * One of a workspace's own emoji, used as `:name:`. Its image is served | |
| 194 | * from the usercontent origin at `/emoji/<file>` (never from the site). | |
| 195 | */ | |
| 196 | export type CustomEmoji = { | |
| 197 | name: string; | |
| 198 | /** For an alias, the emoji it is another name for; it shows that one's image. */ | |
| 199 | alias_of: string | null; | |
| 200 | /** The SHA-256 of the image's bytes. */ | |
| 201 | file: string; | |
| 202 | content_type: "image/png" | "image/gif" | "image/webp"; | |
| 203 | bytes: number; | |
| 204 | created_by: MemberProfile; | |
| 205 | created_at: string; | |
| 206 | }; | |
| 207 | ||
| 208 | export type EmojiList = { | |
| 209 | emoji: CustomEmoji[]; | |
| 210 | emoji_upload: EmojiUpload; | |
| 211 | /** Whether the viewer may add emoji and aliases. */ | |
| 212 | can_upload: boolean; | |
| 213 | /** Whether the viewer is an owner: may remove any emoji and change `emoji_upload`. */ | |
| 214 | can_manage: boolean; | |
| 215 | }; | |
| 216 | ||
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 217 | /** Who may do something in a workspace's chat: every member, or only its owners. */ |
| 218 | export type ChatAllowed = "members" | "owners"; | |
| 219 | ||
| 220 | /** | |
| 221 | * Who may rename and archive a channel: its owners (whoever made it) and | |
| 222 | * the workspace's owners (the default), or the workspace's owners only. | |
| 223 | */ | |
| 224 | export type ChannelManagers = "channel_owners" | "owners"; | |
| 225 | ||
| 226 | /** | |
| 227 | * A workspace's chat settings, which its owners choose (Workspace, | |
| 228 | * Settings, Chat). The chat service enforces each one; the page only | |
| 229 | * hides what the viewer may not do. | |
| 230 | */ | |
| 231 | export type ChatSettings = { | |
| 232 | /** Who may create public channels. */ | |
| 233 | public_channels: ChatAllowed; | |
| 234 | /** Who may create private channels. */ | |
| 235 | private_channels: ChatAllowed; | |
| 236 | /** Who may rename, archive and unarchive channels. */ | |
| 237 | manage_channels: ChannelManagers; | |
| 238 | /** Who may add custom emoji (`admins` is the workspace's owners). */ | |
| 239 | emoji_upload: EmojiUpload; | |
| 240 | /** | |
| 241 | * The public channels someone new is put in the first time they open | |
| 242 | * Chat, by id. #general unless the owners chose otherwise. | |
| 243 | */ | |
| 244 | default_channels: string[]; | |
| 245 | }; | |
| 246 | ||
| 247 | /** What the viewer may do in a workspace's chat, from its settings and their role. */ | |
| 248 | export type ChatPermissions = { | |
| 249 | create_public_channels: boolean; | |
| 250 | create_private_channels: boolean; | |
| 251 | add_emoji: boolean; | |
| 252 | /** Whether they may change the workspace's chat settings: owners only. */ | |
| 253 | manage_settings: boolean; | |
| 254 | }; | |
| 255 | ||
| 256 | /** The chat settings page: the settings, what the viewer may do, and the public channels to choose defaults from. */ | |
| 257 | export type ChatSettingsView = { | |
| 258 | settings: ChatSettings; | |
| 259 | can: ChatPermissions; | |
| 260 | channels: Channel[]; | |
| 261 | }; | |
| 262 | ||
| 263 | /** A change to a channel: its name, its topic, or whether it is archived. Anything left out stays. */ | |
| 264 | export type ChannelChange = { name?: string; topic?: string | null; archived?: boolean }; | |
| 265 | ||
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 266 | /** An image for a new emoji, as base64. Its type is read from its bytes, never taken from here. */ |
| 267 | export type EmojiFile = { data: string }; | |
| 268 | ||
| 269 | /** The largest custom emoji image, in bytes, and the widest or tallest, in pixels. */ | |
| 270 | export const MAX_EMOJI_BYTES = 256 * 1024; | |
| 271 | export const MAX_EMOJI_SIDE = 512; | |
| 272 | ||
| Chat and workspace agents: channels, DMs and named agents you talk to | 273 | /** One row of the Chat sidebar. */ |
| 274 | export type ChatSidebarEntry = { | |
| 275 | channel: Channel; | |
| 276 | /** "g1t-core", or the other members' names for a direct message. */ | |
| 277 | title: string; | |
| 278 | /** For a direct message: who else is in it (up to four). */ | |
| 279 | others: MemberProfile[]; | |
| 280 | starred: boolean; | |
| 281 | muted: boolean; | |
| 282 | unread: number; | |
| 283 | mentions: number; | |
| 284 | }; | |
| 285 | ||
| 286 | export type ChatSidebar = { | |
| 287 | entries: ChatSidebarEntry[]; | |
| 288 | /** Public channels in the workspace the viewer has not joined. */ | |
| 289 | browsable: number; | |
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 290 | /** What the viewer may do, from the workspace's chat settings: the page hides what they may not. */ |
| 291 | can: ChatPermissions; | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 292 | }; |
| 293 | ||
| Home says what people did as well as what agents did. Since you were last here now has a People column and an Agents column: who pushed how many commits to which projects, pull requests opened, merged and reviewed, issues opened and closed, docs edited, messages sent and deploys that went out, each line a link to where those are listed, with the agents' acceptance (first time, after review, didn't finish) kept as a row of their column; the sentence under the heading sums it up honestly, and says when a part of g1t could not be read, or that the span was quiet. Landed counts merged pull requests, commits pushed straight to a default branch, production deploys that went live, releases and packages, newest first; Running now adds workflow runs. Behind it, the events service answers an activity digest over a span in one round trip from its existing indexes, every push now records how many commits it carried, and chat counts the messages sent in the conversations you can read. The Home guide defines every line, and says how this scales. | 294 | /** |
| 295 | * `activity`: what was said over a span, in the conversations the viewer | |
| 296 | * can read (every public channel, and the private channels and direct | |
| 297 | * messages they are in). Messages are text messages and thread replies | |
| 298 | * that were not deleted; cards agents post are not messages. Home reads it | |
| 299 | * for what people and agents said since you were last there. | |
| 300 | */ | |
| 301 | export type ChatActivity = { | |
| 302 | /** RFC 3339: `[from, until)`. */ | |
| 303 | from: string; | |
| 304 | until: string; | |
| 305 | messages: number; | |
| 306 | /** Conversations with at least one message. */ | |
| 307 | channels: number; | |
| 308 | /** Who said how much: member keys (`user:<id>`, `agent:<id>`), most first. */ | |
| 309 | authors: { key: string; messages: number }[]; | |
| 310 | }; | |
| 311 | ||
| Chat and workspace agents: channels, DMs and named agents you talk to | 312 | export type MessagePage = { |
| 313 | messages: ChatMessage[]; | |
| 314 | /** Pass as `before` to read further back; null at the beginning. */ | |
| 315 | older: string | null; | |
| 316 | /** | |
| Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread | 317 | * For a thread's first page: the message the thread is under, however |
| 318 | * many replies it has, so a thread panel always shows it (a session's | |
| 319 | * live card, say) at its top. | |
| 320 | */ | |
| 321 | root?: ChatMessage | null; | |
| 322 | /** | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 323 | * Set when the page was read with `after`: pass it as `after` again for |
| 324 | * the next messages, or null when this page reached the newest. | |
| 325 | */ | |
| 326 | newer?: string | null; | |
| 327 | }; | |
| 328 | ||
| 329 | export type NewChannel = { name: string; topic?: string | null; private?: boolean }; | |
| 330 | ||
| 331 | export type PostMessage = { body: string; thread_root?: string | null }; | |
| 332 | ||
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 333 | /** |
| The docs folder is gone, and what it held lives where people read it: how a self-hosted g1t runs and how to deploy g1t to Cloudflare are pages on docs.g1t.sh under Run g1t yourself, and speed, rate limits and operating g1t.sh are sections of CONTRIBUTING.md; code that cited a file in docs/ now points to the page or section that covers it, or says what it means itself, and applied migrations and the runner images are left as they were. | 334 | * Who will read what is said in a conversation: a direct message's or |
| 335 | * private channel's people, or, for a public channel, the whole workspace. | |
| 336 | * An agent answers there only with what every one of them may see. | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 337 | */ |
| 338 | export type ChatAudience = { | |
| 339 | kind: "dm" | "private" | "public"; | |
| 340 | /** The people in it (agents left out). For a public channel, its current members, for reference only. */ | |
| 341 | member_user_ids: string[]; | |
| 342 | member_count: number; | |
| 343 | }; | |
| 344 | ||
| 345 | /** A message an agent found by searching, with the conversation it is in. */ | |
| 346 | export type AgentFoundMessage = { | |
| 347 | channel_id: string; | |
| 348 | /** The channel's name, or null for a direct message. */ | |
| 349 | channel: string | null; | |
| 350 | message: ChatMessage; | |
| 351 | }; | |
| 352 | ||
| The docs folder is gone, and what it held lives where people read it: how a self-hosted g1t runs and how to deploy g1t to Cloudflare are pages on docs.g1t.sh under Run g1t yourself, and speed, rate limits and operating g1t.sh are sections of CONTRIBUTING.md; code that cited a file in docs/ now points to the page or section that covers it, or says what it means itself, and applied migrations and the runner images are left as they were. | 353 | /** The most agent-to-agent hops one person's request may start. */ |
| Chat and workspace agents: channels, DMs and named agents you talk to | 354 | export const CHAT_MAX_HOPS = 6; |
| 355 | ||
| 356 | /** | |
| Merge branch 'worktree-agent-a1398e81ad1a64c5f' | 357 | * What an agent posts. An agent's message never wakes another agent, even |
| 358 | * when it @mentions one: only a hand-off does (`handOffAsAgent`). Its | |
| 359 | * mentions of anyone who is not a member of the conversation lose their | |
| 360 | * `@`, so they show as plain names and notify nobody. When it answers a | |
| 361 | * delivery, it passes that delivery's `hops`, `asked_by`, `asker` and | |
| 362 | * `chain` back, which a card that waits on the asker uses. Left out: asked | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 363 | * by the person who created the agent. |
| 364 | */ | |
| 365 | export type AgentPostMessage = PostMessage & { | |
| 366 | card?: MessageCard | null; | |
| 367 | hops?: number; | |
| 368 | asked_by?: string | null; | |
| 369 | /** The delivery's `asker`, handed on to agents this message wakes. Absent: none (they treat the asker as unable to change code). */ | |
| 370 | asker?: AskerAccess | null; | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 371 | /** |
| 372 | * The delivery's `chain`: the agents that handled this request before | |
| 373 | * the one posting, oldest first. Chat adds the poster, and never hands | |
| 374 | * the message to the agent that sent the work to it (no ping-pong). | |
| 375 | */ | |
| 376 | chain?: string[]; | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 377 | }; |
| 378 | ||
| 379 | /** | |
| Merge branch 'worktree-agent-a1398e81ad1a64c5f' | 380 | * A conversation as an agent is told about it before every turn: what it |
| 381 | * is and who is in it, so it knows who reads what it says and who doesn't. | |
| 382 | * Every agent member is listed; people up to `CONVERSATION_PEOPLE_SHOWN` | |
| 383 | * (the person who asked always among them), with the totals beside. | |
| 384 | */ | |
| 385 | export type ConversationForAgent = { | |
| 386 | channel: Channel; | |
| 387 | /** Agents first, then people. */ | |
| 388 | members: MemberProfile[]; | |
| 389 | /** How many people and agents are in it, listed or not. */ | |
| 390 | people: number; | |
| 391 | agents: number; | |
| 392 | }; | |
| 393 | ||
| 394 | /** The most people `conversationForAgent` lists; the rest are a count. */ | |
| 395 | export const CONVERSATION_PEOPLE_SHOWN = 20; | |
| 396 | ||
| 397 | /** | |
| 398 | * An agent handing work to a colleague agent for the person who asked | |
| The docs folder is gone, and what it held lives where people read it: how a self-hosted g1t runs and how to deploy g1t to Cloudflare are pages on docs.g1t.sh under Run g1t yourself, and speed, rate limits and operating g1t.sh are sections of CONTRIBUTING.md; code that cited a file in docs/ now points to the page or section that covers it, or says what it means itself, and applied migrations and the runner images are left as they were. | 399 | * (docs.g1t.sh/guides/agents/, "Hand off"). The fields after `brief` |
| Merge branch 'worktree-agent-a1398e81ad1a64c5f' | 400 | * are the delivery the agent is answering, passed back as for a post. |
| 401 | */ | |
| 402 | export type AgentHandOff = { | |
| 403 | /** The colleague, by agent id. */ | |
| 404 | colleague_id: string; | |
| 405 | /** What they are asked to do, addressed to them. */ | |
| 406 | brief: string; | |
| 407 | thread_root?: string | null; | |
| 408 | hops?: number; | |
| 409 | asked_by: string; | |
| 410 | asker?: AskerAccess | null; | |
| 411 | chain?: string[]; | |
| 412 | }; | |
| 413 | ||
| 414 | /** | |
| 415 | * Where a hand-off went. `here`: the colleague is in this channel or group | |
| 416 | * direct message, and the brief was posted here. `group_dm`: the brief was | |
| 417 | * posted in the direct message of the person who asked, the agent and the | |
| 418 | * colleague (`opened` when it was new), and a card linking to it was | |
| 419 | * posted here. | |
| 420 | */ | |
| 421 | export type HandOffResult = { where: "here" | "group_dm"; channel_id: string; message_id: string; opened: boolean }; | |
| 422 | ||
| 423 | /** | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 424 | * What the live socket sends. The site opens |
| 425 | * `wss://<site>/<workspace>/chat/live?channel=<id>`; the site checks the | |
| 426 | * session and forwards the upgrade to the chat service with the viewer. | |
| 427 | */ | |
| 428 | export type ChatLiveEvent = | |
| 429 | | { type: "message.created"; message: ChatMessage } | |
| 430 | | { type: "message.updated"; message: ChatMessage } | |
| 431 | | { type: "message.deleted"; channel_id: string; id: string } | |
| 432 | | { type: "typing"; channel_id: string; member: MemberProfile; until: string } | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 433 | | { type: "read"; channel_id: string; principal: Principal; last_read_id: string } |
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 434 | | { type: "channel.updated"; channel: Channel } |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 435 | | { type: "reaction.added" | "reaction.removed"; channel_id: string; message_id: string; emoji: string; member: MemberProfile }; |
| Chat and workspace agents: channels, DMs and named agents you talk to | 436 | |
| 437 | /** | |
| 438 | * Header the site sets on a forwarded live socket: the viewer, as JSON. | |
| 439 | * The site forwards the upgrade to the chat service's | |
| 440 | * `GET /live?workspace=<slug>&channel=<id>` (`workspace` may be left out, | |
| 441 | * at the cost of looking up each of the viewer's workspaces). | |
| 442 | */ | |
| 443 | export const CHAT_VIEWER_HEADER = "x-g1t-chat-viewer"; | |
| 444 | ||
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 445 | /** A channel with its members, and whether the viewer may rename or archive it. */ |
| 446 | export type ChannelDetail = { channel: Channel; members: ChannelMember[]; can_manage: boolean }; | |
| 447 | ||
| Chat and workspace agents: channels, DMs and named agents you talk to | 448 | export type ChatApi = { |
| 449 | sidebar(workspace: string, viewer: User): Promise<Result<ChatSidebar>>; | |
| Home says what people did as well as what agents did. Since you were last here now has a People column and an Agents column: who pushed how many commits to which projects, pull requests opened, merged and reviewed, issues opened and closed, docs edited, messages sent and deploys that went out, each line a link to where those are listed, with the agents' acceptance (first time, after review, didn't finish) kept as a row of their column; the sentence under the heading sums it up honestly, and says when a part of g1t could not be read, or that the span was quiet. Landed counts merged pull requests, commits pushed straight to a default branch, production deploys that went live, releases and packages, newest first; Running now adds workflow runs. Behind it, the events service answers an activity digest over a span in one round trip from its existing indexes, every push now records how many commits it carried, and chat counts the messages sent in the conversations you can read. The Home guide defines every line, and says how this scales. | 450 | /** What was said in `[from, until)` (RFC 3339) where the viewer can read, counted. */ |
| 451 | activity(workspace: string, viewer: User, span: { from: string; until: string }): Promise<Result<ChatActivity>>; | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 452 | channel( |
| 453 | workspace: string, | |
| 454 | channelId: string, | |
| 455 | viewer: User, | |
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 456 | ): Promise<Result<ChannelDetail>>; |
| Chat and workspace agents: channels, DMs and named agents you talk to | 457 | /** The same, found by its name in the workspace (`#general` or `general`), as the site's URLs name channels. */ |
| 458 | channelByName( | |
| 459 | workspace: string, | |
| 460 | name: string, | |
| 461 | viewer: User, | |
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 462 | ): Promise<Result<ChannelDetail>>; |
| 463 | /** | |
| 464 | * Browse channels: every public channel, and the private ones the viewer | |
| 465 | * is in. With `archived`, the archived ones instead. | |
| 466 | */ | |
| 467 | browse(workspace: string, viewer: User, options?: { archived?: boolean }): Promise<Result<Channel[]>>; | |
| 468 | /** Makes a channel. Who may make a public or a private one is the workspace's setting. */ | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 469 | createChannel(workspace: string, viewer: User, input: NewChannel): Promise<Result<Channel>>; |
| 470 | /** | |
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 471 | * Renames, archives or unarchives a channel (the workspace's |
| 472 | * `manage_channels` setting says who may), or changes its topic (any | |
| 473 | * member). #general is never renamed or archived. Everyone looking at it | |
| 474 | * gets `channel.updated`. | |
| 475 | */ | |
| 476 | updateChannel(workspace: string, channelId: string, viewer: User, change: ChannelChange): Promise<Result<Channel>>; | |
| 477 | /** The workspace's chat settings, for any member; only owners may change them. */ | |
| 478 | chatSettings(workspace: string, viewer: User): Promise<Result<ChatSettingsView>>; | |
| 479 | /** Owners only: changes some of the workspace's chat settings, and answers all of them. */ | |
| 480 | setChatSettings(workspace: string, viewer: User, change: Partial<ChatSettings>): Promise<Result<ChatSettings>>; | |
| 481 | /** | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 482 | * The direct message between the viewer and these members, created on |
| 483 | * first use. The same set of members always gets the same channel. | |
| 484 | */ | |
| 485 | openDm(workspace: string, viewer: User, members: Principal[]): Promise<Result<Channel>>; | |
| 486 | join(workspace: string, channelId: string, viewer: User): Promise<Result<null>>; | |
| 487 | leave(workspace: string, channelId: string, viewer: User): Promise<Result<null>>; | |
| 488 | /** Adds a person or an agent. An invite grants read, never write. */ | |
| 489 | invite(workspace: string, channelId: string, viewer: User, member: Principal): Promise<Result<null>>; | |
| 490 | /** | |
| 491 | * Newest first. Without `thread_root`, the channel's top-level messages; | |
| 492 | * with it, that thread's replies, plus the message they reply to as the | |
| 493 | * oldest once the page reaches the start of the thread (`older` null). | |
| 494 | * With `after` (catching up after a reconnect): the messages after that | |
| 495 | * id instead, oldest first, deleted ones included so the client can | |
| 496 | * drop them; `newer` says whether there are more. | |
| 497 | * Limit 50 by default, 200 at most. | |
| 498 | */ | |
| 499 | messages( | |
| 500 | workspace: string, | |
| 501 | channelId: string, | |
| 502 | viewer: User, | |
| 503 | page?: { before?: string | null; after?: string | null; limit?: number; thread_root?: string | null }, | |
| 504 | ): Promise<Result<MessagePage>>; | |
| 505 | post(workspace: string, channelId: string, viewer: User, message: PostMessage): Promise<Result<ChatMessage>>; | |
| 506 | edit(workspace: string, channelId: string, viewer: User, id: string, body: string): Promise<Result<ChatMessage>>; | |
| 507 | remove(workspace: string, channelId: string, viewer: User, id: string): Promise<Result<null>>; | |
| 508 | markRead(workspace: string, channelId: string, viewer: User, id: string): Promise<Result<null>>; | |
| 509 | setPreferences( | |
| 510 | workspace: string, | |
| 511 | channelId: string, | |
| 512 | viewer: User, | |
| 513 | prefs: { starred?: boolean; muted?: boolean }, | |
| 514 | ): Promise<Result<null>>; | |
| 515 | /** | |
| 516 | * Posts as an agent. Only the agents service calls this, for replies and | |
| 517 | * cards; the agent must be a member of the channel. | |
| 518 | */ | |
| 519 | postAsAgent( | |
| 520 | workspace: string, | |
| 521 | channelId: string, | |
| 522 | agentId: string, | |
| 523 | message: AgentPostMessage, | |
| 524 | ): Promise<Result<ChatMessage>>; | |
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 525 | /** |
| 526 | * Changes a message an agent posted: its body, its card, or both. Only | |
| 527 | * the agents service calls this, to keep a session's live card current. | |
| 528 | * Changing a message wakes nobody. | |
| 529 | */ | |
| 530 | updateAsAgent( | |
| 531 | workspace: string, | |
| 532 | channelId: string, | |
| 533 | agentId: string, | |
| 534 | id: string, | |
| 535 | change: { body?: string; card?: MessageCard | null }, | |
| 536 | ): Promise<Result<ChatMessage>>; | |
| Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar | 537 | /** |
| 538 | * A person presses an action on a card in a conversation they can read. | |
| 539 | * Chat hands it to the card's owner with the person, and the owner | |
| 540 | * decides, acts and updates the card. | |
| 541 | */ | |
| 542 | cardAction( | |
| 543 | workspace: string, | |
| 544 | channelId: string, | |
| 545 | viewer: User, | |
| 546 | messageId: string, | |
| 547 | actionId: string, | |
| 548 | input?: string | null, | |
| 549 | ): Promise<Result<CardActionResult>>; | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 550 | /** Shows "is typing" for an agent while it works on a reply. */ |
| 551 | agentTyping(workspace: string, channelId: string, agentId: string): Promise<Result<null>>; | |
| 552 | /** | |
| 553 | * What an agent reads before it replies, oldest first: with | |
| 554 | * `thread_root`, that thread (its root, then its replies); without, the | |
| 555 | * channel's or direct message's latest top-level messages. Only the | |
| 556 | * agents service calls this; the agent must be a member of the channel, | |
| 557 | * so it reads only what was said where it was invited. `limit` defaults | |
| 558 | * to 30, at most 100. | |
| 559 | */ | |
| 560 | historyForAgent( | |
| 561 | workspace: string, | |
| 562 | channelId: string, | |
| 563 | agentId: string, | |
| 564 | page?: { thread_root?: string | null; limit?: number }, | |
| 565 | ): Promise<Result<ChatMessage[]>>; | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 566 | /** Internal: who reads a conversation. */ |
| 567 | audience(workspace: string, channelId: string): Promise<Result<ChatAudience>>; | |
| 568 | /** | |
| Merge branch 'worktree-agent-a1398e81ad1a64c5f' | 569 | * Internal, for an agent about to answer in `channelId`, which it must be |
| 570 | * a member of: the conversation and its members (`ConversationForAgent`). | |
| 571 | * `askedBy` (a user id) is always among the people listed. | |
| 572 | */ | |
| 573 | conversationForAgent(workspace: string, channelId: string, agentId: string, askedBy?: string | null): Promise<Result<ConversationForAgent>>; | |
| 574 | /** | |
| 575 | * Internal: `agentId`, answering in `channelId`, hands work to a | |
| 576 | * colleague agent for the person who asked. If the colleague is in this | |
| 577 | * channel or group direct message, the brief is posted here; otherwise in | |
| 578 | * the group direct message of the person, the agent and the colleague | |
| 579 | * (opened on first use), with a card here linking to it. Either way the | |
| 580 | * brief wakes the colleague, one hop further along the same chain, and | |
| 581 | * nobody else. Refused for an agent that is not of the workspace, the | |
| 582 | * agent itself, @g1t, one already in the chain, past the hop limit, or | |
| 583 | * when the person who asked is not in this conversation. | |
| 584 | */ | |
| 585 | handOffAsAgent(workspace: string, channelId: string, agentId: string, handOff: AgentHandOff): Promise<Result<HandOffResult>>; | |
| 586 | /** | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 587 | * Internal, for an agent replying in `channelId`: messages matching |
| 588 | * `query` (newest first, at most 20) from conversations every person in | |
| 589 | * that conversation's audience is in, and from public channels. Never | |
| 590 | * another direct message unless it has exactly the same people. The | |
| 591 | * audience is worked out here from `channelId`, never taken from the | |
| 592 | * caller. | |
| 593 | */ | |
| 594 | searchForAgent(workspace: string, channelId: string, query: string, limit?: number): Promise<Result<AgentFoundMessage[]>>; | |
| 595 | /** | |
| 596 | * Internal, for an agent replying in `channelId`: a thread (`id`, its | |
| 597 | * root or any reply) in `targetChannelId`, oldest first, under the same | |
| 598 | * rule. A conversation the audience may not read is not found, exactly | |
| 599 | * as one that does not exist. | |
| 600 | */ | |
| 601 | threadForAgent(workspace: string, channelId: string, targetChannelId: string, id: string): Promise<Result<AgentFoundMessage[]>>; | |
| 602 | /** | |
| 603 | * Reacts to a message with `emoji` (one Unicode emoji, or `:name:` for | |
| 604 | * one of the workspace's own). Once per member per emoji; at most 50 | |
| 605 | * different emoji on a message. Answers the message's reactions now. | |
| 606 | */ | |
| 607 | react(workspace: string, channelId: string, viewer: User, messageId: string, emoji: string): Promise<Result<ChatReaction[]>>; | |
| 608 | unreact(workspace: string, channelId: string, viewer: User, messageId: string, emoji: string): Promise<Result<ChatReaction[]>>; | |
| 609 | /** Internal, for the agents service: an agent reacts (or, with `remove`, takes it back). It must be in the channel. */ | |
| 610 | reactAsAgent( | |
| 611 | workspace: string, | |
| 612 | channelId: string, | |
| 613 | agentId: string, | |
| 614 | messageId: string, | |
| 615 | emoji: string, | |
| 616 | remove?: boolean, | |
| 617 | ): Promise<Result<ChatReaction[]>>; | |
| 618 | /** The workspace's own emoji, by name, and who may add them. */ | |
| 619 | listEmoji(workspace: string, viewer: User): Promise<Result<EmojiList>>; | |
| 620 | /** Adds an emoji: a PNG, GIF or WebP of at most 256 KB and 512×512. */ | |
| 621 | addEmoji(workspace: string, viewer: User, name: string, file: EmojiFile): Promise<Result<CustomEmoji>>; | |
| 622 | /** Gives an existing emoji (`target`) another name. */ | |
| 623 | aliasEmoji(workspace: string, viewer: User, name: string, target: string): Promise<Result<CustomEmoji>>; | |
| 624 | /** Removes an emoji, and its aliases with it. Its creator or an owner may. */ | |
| 625 | removeEmoji(workspace: string, viewer: User, name: string): Promise<Result<null>>; | |
| 626 | /** Owners only: who may add emoji. */ | |
| 627 | setEmojiUpload(workspace: string, viewer: User, value: EmojiUpload): Promise<Result<EmojiUpload>>; | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 628 | }; |
| 629 | ||
| 630 | async function rpc<T>(service: ServiceBinding, method: string, args: object): Promise<T> { | |
| 631 | const response = await service.fetch(`https://service/rpc/${method}`, { | |
| 632 | method: "POST", | |
| 633 | headers: { "content-type": "application/json" }, | |
| 634 | body: JSON.stringify(args), | |
| 635 | }); | |
| 636 | if (!response.ok) { | |
| 637 | throw new Error(`${method} failed with status ${response.status}`); | |
| 638 | } | |
| 639 | return (await response.json()) as T; | |
| 640 | } | |
| 641 | ||
| 642 | export function chatClient(service: ServiceBinding): ChatApi { | |
| 643 | const call = <T>(method: string, args: object) => rpc<T>(service, method, args); | |
| 644 | return { | |
| 645 | sidebar: (workspace, viewer) => call("sidebar", { workspace, viewer }), | |
| Home says what people did as well as what agents did. Since you were last here now has a People column and an Agents column: who pushed how many commits to which projects, pull requests opened, merged and reviewed, issues opened and closed, docs edited, messages sent and deploys that went out, each line a link to where those are listed, with the agents' acceptance (first time, after review, didn't finish) kept as a row of their column; the sentence under the heading sums it up honestly, and says when a part of g1t could not be read, or that the span was quiet. Landed counts merged pull requests, commits pushed straight to a default branch, production deploys that went live, releases and packages, newest first; Running now adds workflow runs. Behind it, the events service answers an activity digest over a span in one round trip from its existing indexes, every push now records how many commits it carried, and chat counts the messages sent in the conversations you can read. The Home guide defines every line, and says how this scales. | 646 | activity: (workspace, viewer, span) => call("activity", { workspace, viewer, from: span.from, until: span.until }), |
| Chat and workspace agents: channels, DMs and named agents you talk to | 647 | channel: (workspace, channelId, viewer) => call("channel", { workspace, channel_id: channelId, viewer }), |
| 648 | channelByName: (workspace, name, viewer) => call("channel_by_name", { workspace, name, viewer }), | |
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 649 | browse: (workspace, viewer, options) => call("browse", { workspace, viewer, archived: options?.archived ?? false }), |
| Chat and workspace agents: channels, DMs and named agents you talk to | 650 | createChannel: (workspace, viewer, input) => call("create_channel", { workspace, viewer, input }), |
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 651 | updateChannel: (workspace, channelId, viewer, change) => call("update_channel", { workspace, channel_id: channelId, viewer, change }), |
| 652 | chatSettings: (workspace, viewer) => call("chat_settings", { workspace, viewer }), | |
| 653 | setChatSettings: (workspace, viewer, change) => call("set_chat_settings", { workspace, viewer, change }), | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 654 | openDm: (workspace, viewer, members) => call("open_dm", { workspace, viewer, members }), |
| 655 | join: (workspace, channelId, viewer) => call("join", { workspace, channel_id: channelId, viewer }), | |
| 656 | leave: (workspace, channelId, viewer) => call("leave", { workspace, channel_id: channelId, viewer }), | |
| 657 | invite: (workspace, channelId, viewer, member) => | |
| 658 | call("invite", { workspace, channel_id: channelId, viewer, member }), | |
| 659 | messages: (workspace, channelId, viewer, page) => | |
| 660 | call("messages", { | |
| 661 | workspace, | |
| 662 | channel_id: channelId, | |
| 663 | viewer, | |
| 664 | before: page?.before ?? null, | |
| 665 | after: page?.after ?? null, | |
| 666 | limit: page?.limit ?? null, | |
| 667 | thread_root: page?.thread_root ?? null, | |
| 668 | }), | |
| 669 | post: (workspace, channelId, viewer, message) => call("post", { workspace, channel_id: channelId, viewer, message }), | |
| 670 | edit: (workspace, channelId, viewer, id, body) => call("edit", { workspace, channel_id: channelId, viewer, id, body }), | |
| 671 | remove: (workspace, channelId, viewer, id) => call("remove", { workspace, channel_id: channelId, viewer, id }), | |
| 672 | markRead: (workspace, channelId, viewer, id) => call("mark_read", { workspace, channel_id: channelId, viewer, id }), | |
| 673 | setPreferences: (workspace, channelId, viewer, prefs) => | |
| 674 | call("set_preferences", { workspace, channel_id: channelId, viewer, prefs }), | |
| 675 | postAsAgent: (workspace, channelId, agentId, message) => | |
| 676 | call("post_as_agent", { workspace, channel_id: channelId, agent_id: agentId, message }), | |
| Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar | 677 | cardAction: (workspace, channelId, viewer, messageId, actionId, input) => |
| 678 | call("card_action", { workspace, channel_id: channelId, viewer, message_id: messageId, action_id: actionId, input: input ?? null }), | |
| Chat controls, public profiles, shadcn selects, and no Docs tab in a project | 679 | updateAsAgent: (workspace, channelId, agentId, id, change) => |
| 680 | call("update_as_agent", { workspace, channel_id: channelId, agent_id: agentId, id, change }), | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 681 | agentTyping: (workspace, channelId, agentId) => |
| 682 | call("agent_typing", { workspace, channel_id: channelId, agent_id: agentId }), | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 683 | audience: (workspace, channelId) => call("audience", { workspace, channel_id: channelId }), |
| Merge branch 'worktree-agent-a1398e81ad1a64c5f' | 684 | conversationForAgent: (workspace, channelId, agentId, askedBy) => |
| 685 | call("conversation_for_agent", { workspace, channel_id: channelId, agent_id: agentId, asked_by: askedBy ?? null }), | |
| 686 | handOffAsAgent: (workspace, channelId, agentId, handOff) => | |
| 687 | call("hand_off_as_agent", { workspace, channel_id: channelId, agent_id: agentId, hand_off: handOff }), | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 688 | searchForAgent: (workspace, channelId, query, limit) => |
| 689 | call("search_for_agent", { workspace, channel_id: channelId, query, limit: limit ?? null }), | |
| 690 | threadForAgent: (workspace, channelId, targetChannelId, id) => | |
| 691 | call("thread_for_agent", { workspace, channel_id: channelId, target_channel_id: targetChannelId, id }), | |
| 692 | react: (workspace, channelId, viewer, messageId, emoji) => | |
| 693 | call("react", { workspace, channel_id: channelId, viewer, message_id: messageId, emoji }), | |
| 694 | unreact: (workspace, channelId, viewer, messageId, emoji) => | |
| 695 | call("unreact", { workspace, channel_id: channelId, viewer, message_id: messageId, emoji }), | |
| 696 | reactAsAgent: (workspace, channelId, agentId, messageId, emoji, remove) => | |
| 697 | call("react_as_agent", { workspace, channel_id: channelId, agent_id: agentId, message_id: messageId, emoji, remove: remove ?? false }), | |
| 698 | listEmoji: (workspace, viewer) => call("list_emoji", { workspace, viewer }), | |
| 699 | addEmoji: (workspace, viewer, name, file) => call("add_emoji", { workspace, viewer, name, file }), | |
| 700 | aliasEmoji: (workspace, viewer, name, target) => call("alias_emoji", { workspace, viewer, name, target }), | |
| 701 | removeEmoji: (workspace, viewer, name) => call("remove_emoji", { workspace, viewer, name }), | |
| 702 | setEmojiUpload: (workspace, viewer, value) => call("set_emoji_upload", { workspace, viewer, value }), | |
| Chat and workspace agents: channels, DMs and named agents you talk to | 703 | historyForAgent: (workspace, channelId, agentId, page) => |
| 704 | call("history_for_agent", { | |
| 705 | workspace, | |
| 706 | channel_id: channelId, | |
| 707 | agent_id: agentId, | |
| 708 | thread_root: page?.thread_root ?? null, | |
| 709 | limit: page?.limit ?? null, | |
| 710 | }), | |
| 711 | }; | |
| 712 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.