Skip to content
168 linesCodeBlameRaw

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.

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)1/**
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.2 * `@g1t`, every workspace's built-in orchestrator
3 * (docs.g1t.sh/guides/agents/, "g1t, the orchestrator"). Pure, so it is
4 * tested on its own.
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)5 *
6 * It is an agent row like any other, marked builtin, so it is billed,
7 * gated, routed and audited the same way. What sets it apart is here:
8 * the fields nobody may change, the job it always has, the roster of
9 * specialists it is given, and the rails on how it delegates.
10 */
11import type { AgentStatus, ModelTier, NewWorkspaceAgent } from "@g1t/contracts";
12
Each agent has an Abilities tab: g1t's built-ins, always on within the asker's access; its computer, coming; each connected integration's actions one row each, read, import, comment or resolve, with a level for each, alone, alone when the person asked for it, ask first or never, and whose connection it runs on; and MCP servers an owner adds. Reading is alone, writing inside g1t follows today's choices, anything that leaves g1t asks first, and production deploys can't go above ask. The agents service enforces every level: ask first posts a card to allow or deny and parks a session until it's answered, a refusal names its rule in the transcript and the audit log, and a missing ability posts a request to owners. The agent abilities guide says how.13import { EMPTY_ABILITIES } from "../../../packages/contracts/src/abilities.ts";
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)14import { BUILTIN_AGENT_HANDLE, ORCHESTRATOR_TEMPLATE } from "../../../packages/contracts/src/workspace-agents.ts";
15import { type Checked, type Definition, DEFAULT_AUTONOMY, DEFAULT_BUDGET, DEFAULT_CAPACITY, DEFAULT_ROUTING } from "./definition.ts";
An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how.16import { listOf } from "./teammates.ts";
Merge branch 'worktree-agent-a1398e81ad1a64c5f'17import { MAX_HAND_OFFS } from "./tools.ts";
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)18
19export const BUILTIN_ROLE = "Your orchestrator: delegates to the team's agents, or does the work itself";
20
21/** What a new workspace's @g1t starts as. `instructions` are the workspace's additions, none at first. */
22export function builtinDefinition(): Definition {
23 return {
24 handle: BUILTIN_AGENT_HANDLE,
25 display_name: "g1t",
26 role: BUILTIN_ROLE,
27 instructions: "",
28 personality_preset: "friendly",
29 personality: "",
30 routing: DEFAULT_ROUTING,
31 budget: DEFAULT_BUDGET,
32 autonomy: DEFAULT_AUTONOMY,
33 capacity: DEFAULT_CAPACITY,
34 template: ORCHESTRATOR_TEMPLATE,
35 avatar_seed: BUILTIN_AGENT_HANDLE,
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.36 look: 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)37 title: "Orchestrator",
38 responsibilities: [],
39 subagents: [],
40 faces: "internal",
Agents recall what Docs say before they answer or work, and each has required reading41 reading: [],
Every agent starts with g1t's foundational skills, playbooks for documents, research, data, code, communication, and files and media, each using only tools the agent already has and saying plainly what isn't possible yet: agents make PDFs, Word documents, spreadsheets, CSV and Markdown files with make_file, kept with a doc in Artifacts and served from g1tusercontent.com; an agent's Skills tab shows what each skill can do and with which tools, and owners turn a skill off per agent; the agent skills guide says how.42 skills_off: [],
Each agent has an Abilities tab: g1t's built-ins, always on within the asker's access; its computer, coming; each connected integration's actions one row each, read, import, comment or resolve, with a level for each, alone, alone when the person asked for it, ask first or never, and whose connection it runs on; and MCP servers an owner adds. Reading is alone, writing inside g1t follows today's choices, anything that leaves g1t asks first, and production deploys can't go above ask. The agents service enforces every level: ask first posts a card to allow or deny and parks a session until it's answered, a refusal names its rule in the transcript and the audit log, and a missing ability posts a request to owners. The agent abilities guide says how.43 abilities: EMPTY_ABILITIES,
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)44 };
45}
46
47/** What nobody may change on the built-in agent: who it is and what its job is. */
48export const PROTECTED: (keyof NewWorkspaceAgent)[] = [
49 "handle",
50 "display_name",
51 "role",
52 "title",
53 "responsibilities",
54 "subagents",
55 "template",
56 "faces",
57];
58
59/**
60 * `changes` to the built-in agent with its fixed fields taken out, whatever
61 * they say: a profile form sends every field (an empty title, no
62 * responsibilities), and only the editable ones apply.
63 */
64export function builtinChanges(_current: Definition, changes: Partial<NewWorkspaceAgent>): Checked<Partial<NewWorkspaceAgent>> {
65 if (!changes || typeof changes !== "object") return { ok: false, message: "Send the agent's fields." };
66 const rest: Partial<NewWorkspaceAgent> = { ...changes };
67 for (const key of PROTECTED) delete rest[key];
68 return { ok: true, value: rest };
69}
70
71/** One specialist as @g1t sees it. */
72export type Specialist = {
73 handle: string;
74 display_name: string;
75 role: string;
76 title?: string;
An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how.77 /** The teams it is on, by name (identity's team memberships). */
78 teams?: 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)79 responsibilities?: string[];
80 status: AgentStatus;
81 spent_month_micros: number;
82 /** The monthly cap, or null for none. */
83 monthly_micros: number | null;
84};
85
86const STATUS_WORDS: Record<AgentStatus, string> = {
87 idle: "idle",
88 working: "working",
89 waiting: "waiting on someone",
90 out_of_budget: "out of budget",
91 paused: "paused",
92};
93
94const dollars = (micros: number) => `$${(Math.max(0, micros) / 1_000_000).toFixed(2)}`;
95
An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how.96/** Where a specialist sits: "QA Engineer on QA and Web", or its title or role alone. */
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)97function placeLine(agent: Specialist): string {
An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how.98 const title = agent.title?.trim() || agent.role.replace(/\.$/, "");
99 return agent.teams?.length ? `${title} on ${listOf(agent.teams)}` : title;
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)100}
101
102/**
103 * The roster, one line per specialist, so g1t can route "QA should look"
An agent is on teams the way a person is: through team membership, with no team or department of its own. Add it to teams when you create it, from its profile or from the team's page; the Agents sidebar groups agents by the teams they're on, with those on none last; what an agent is told about its teammates, team budgets, skills reach, Spend's By team and the People pages all read memberships. Agents with an old team name matching a team are moved onto it once; the rest are left off a team. Templates name a role only. The agents, teams and people guides say how.104 * to the right one: `- @margo (Margo): QA Engineer on QA. Does: review pull
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)105 * requests; write test plans. idle; $1.20 of $20.00 this month.`
106 */
107export function rosterLines(specialists: Specialist[]): string {
108 if (!specialists.length) return "There are no specialists in this workspace yet.";
109 return specialists
110 .map((agent) => {
111 const name = agent.display_name && agent.display_name.toLowerCase() !== agent.handle ? ` (${agent.display_name})` : "";
112 const spend = agent.monthly_micros ? `${dollars(agent.spent_month_micros)} of ${dollars(agent.monthly_micros)}` : `${dollars(agent.spent_month_micros)}, no cap`;
113 const duties = agent.responsibilities?.length ? ` Does: ${agent.responsibilities.map((d) => d.replace(/\.$/, "")).join("; ")}.` : "";
114 return `- @${agent.handle}${name}: ${placeLine(agent)}.${duties} ${STATUS_WORDS[agent.status] ?? agent.status}; ${spend} this month.`;
115 })
116 .join("\n");
117}
118
Merge branch 'worktree-agent-a1398e81ad1a64c5f'119/** The most colleagues one reply may hand work to (the hand_off tool's rail). */
120export const MAX_DELEGATES = MAX_HAND_OFFS;
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)121
122/** @g1t's job: fixed, whatever the workspace adds. */
123export function orchestratorInstructions(specialists: Specialist[], extra: string): string {
124 const available = specialists.filter((agent) => agent.status !== "out_of_budget" && agent.status !== "paused");
125 const job = [
126 "You are the workspace's orchestrator. People come to you when they don't know who should do something. You either answer, hand the work to the right specialist, or do it yourself.",
127 "",
128 "### The team",
129 "",
130 rosterLines(specialists),
131 "",
132 "### How you decide",
133 "",
134 "1. **Answer directly** when it is a question, a summary or a quick judgment you can give from this conversation.",
Merge branch 'worktree-agent-a1398e81ad1a64c5f'135 "2. **Delegate** when a specialist's role fits the work: call hand_off with their handle and a crisp brief, written to them: what is wanted, why, what done looks like, and any constraint (deadlines, what not to touch). If they are in this conversation the brief is posted here; otherwise a group message opens with the person who asked, you and them, and a card here links to it. Then tell the person in a sentence who has it and where.",
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)136 "3. **Do it yourself, or suggest a specialist,** when nobody fits. Say so plainly, offer to take it on yourself, and if this kind of work will recur, suggest setting one up (for example: \"Want me to set up a release manager for this?\").",
137 "",
138 "### Rules for delegating",
139 "",
Merge branch 'worktree-agent-a1398e81ad1a64c5f'140 "- Only hand_off delegates. An @mention in your message wakes no agent and reaches nobody outside this conversation, so writing \"@mike, could you…\" hands nothing over.",
141 "- Describing the team is not delegating: name specialists without @ (\"Mike, our technical recruiter\") unless they are in this conversation.",
142 `- Hand off to at most ${MAX_DELEGATES} specialists for one message. Split bigger work into steps and hand off the next step when the first is done.`,
143 "- Never delegate in a loop: don't hand work back to a specialist who handed it to you, and don't hand the same work to the same specialist twice.",
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)144 "- Don't delegate to a specialist who is out of budget or paused; say they are unavailable and why.",
Merge branch 'worktree-agent-a1398e81ad1a64c5f'145 "- The person sees every hand-off, and their access still limits what any agent does for them.",
146 "- Once you've handed something off, let the specialist answer; speak again when someone asks you.",
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)147 "- When asked what everyone is working on, answer from the team list above.",
148 available.length ? "" : "\nNo specialist is available right now, so do the work yourself or suggest creating one.",
149 ].join("\n");
150 const added = extra.trim();
151 return added ? `${job}\n\n### Added by this workspace\n\n${added}` : job;
152}
153
154/** How long a thread is before @g1t's delegation call is worth a larger model. */
155export const LONG_THREAD = 6;
156
157/**
158 * Where @g1t's reply starts: the small tier, or the large one for a
159 * delegation decision in a long thread (more than `LONG_THREAD` messages,
160 * with specialists to choose from). Its floor and ceiling apply after.
161 */
162export function orchestratorTier(messages: number, specialists: number): ModelTier {
163 return messages > LONG_THREAD && specialists > 0 ? "large" : "small";
164}
165
166/** The friendly notice when @g1t has no model to run on. */
167export const BUILTIN_NO_MODEL =
168 "Hi, I'm g1t. I'd love to help, but this workspace doesn't have a model I can use yet. An owner can add AI credit under Billing, or connect the workspace's own model provider under Integrations, and I'll be ready.";

This file's history is long; its oldest lines are credited to the oldest commit read.