Skip to content
167 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,
36 title: "Orchestrator",
37 responsibilities: [],
38 subagents: [],
39 faces: "internal",
Agents recall what Docs say before they answer or work, and each has required reading40 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.41 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.42 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)43 };
44}
45
46/** What nobody may change on the built-in agent: who it is and what its job is. */
47export const PROTECTED: (keyof NewWorkspaceAgent)[] = [
48 "handle",
49 "display_name",
50 "role",
51 "title",
52 "responsibilities",
53 "subagents",
54 "template",
55 "faces",
56];
57
58/**
59 * `changes` to the built-in agent with its fixed fields taken out, whatever
60 * they say: a profile form sends every field (an empty title, no
61 * responsibilities), and only the editable ones apply.
62 */
63export function builtinChanges(_current: Definition, changes: Partial<NewWorkspaceAgent>): Checked<Partial<NewWorkspaceAgent>> {
64 if (!changes || typeof changes !== "object") return { ok: false, message: "Send the agent's fields." };
65 const rest: Partial<NewWorkspaceAgent> = { ...changes };
66 for (const key of PROTECTED) delete rest[key];
67 return { ok: true, value: rest };
68}
69
70/** One specialist as @g1t sees it. */
71export type Specialist = {
72 handle: string;
73 display_name: string;
74 role: string;
75 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.76 /** The teams it is on, by name (identity's team memberships). */
77 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)78 responsibilities?: string[];
79 status: AgentStatus;
80 spent_month_micros: number;
81 /** The monthly cap, or null for none. */
82 monthly_micros: number | null;
83};
84
85const STATUS_WORDS: Record<AgentStatus, string> = {
86 idle: "idle",
87 working: "working",
88 waiting: "waiting on someone",
89 out_of_budget: "out of budget",
90 paused: "paused",
91};
92
93const dollars = (micros: number) => `$${(Math.max(0, micros) / 1_000_000).toFixed(2)}`;
94
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.95/** 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)96function 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.97 const title = agent.title?.trim() || agent.role.replace(/\.$/, "");
98 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)99}
100
101/**
102 * 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.103 * 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)104 * requests; write test plans. idle; $1.20 of $20.00 this month.`
105 */
106export function rosterLines(specialists: Specialist[]): string {
107 if (!specialists.length) return "There are no specialists in this workspace yet.";
108 return specialists
109 .map((agent) => {
110 const name = agent.display_name && agent.display_name.toLowerCase() !== agent.handle ? ` (${agent.display_name})` : "";
111 const spend = agent.monthly_micros ? `${dollars(agent.spent_month_micros)} of ${dollars(agent.monthly_micros)}` : `${dollars(agent.spent_month_micros)}, no cap`;
112 const duties = agent.responsibilities?.length ? ` Does: ${agent.responsibilities.map((d) => d.replace(/\.$/, "")).join("; ")}.` : "";
113 return `- @${agent.handle}${name}: ${placeLine(agent)}.${duties} ${STATUS_WORDS[agent.status] ?? agent.status}; ${spend} this month.`;
114 })
115 .join("\n");
116}
117
Merge branch 'worktree-agent-a1398e81ad1a64c5f'118/** The most colleagues one reply may hand work to (the hand_off tool's rail). */
119export 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)120
121/** @g1t's job: fixed, whatever the workspace adds. */
122export function orchestratorInstructions(specialists: Specialist[], extra: string): string {
123 const available = specialists.filter((agent) => agent.status !== "out_of_budget" && agent.status !== "paused");
124 const job = [
125 "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.",
126 "",
127 "### The team",
128 "",
129 rosterLines(specialists),
130 "",
131 "### How you decide",
132 "",
133 "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'134 "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)135 "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?\").",
136 "",
137 "### Rules for delegating",
138 "",
Merge branch 'worktree-agent-a1398e81ad1a64c5f'139 "- 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.",
140 "- Describing the team is not delegating: name specialists without @ (\"Mike, our technical recruiter\") unless they are in this conversation.",
141 `- 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.`,
142 "- 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)143 "- Don't delegate to a specialist who is out of budget or paused; say they are unavailable and why.",
Merge branch 'worktree-agent-a1398e81ad1a64c5f'144 "- The person sees every hand-off, and their access still limits what any agent does for them.",
145 "- 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)146 "- When asked what everyone is working on, answer from the team list above.",
147 available.length ? "" : "\nNo specialist is available right now, so do the work yourself or suggest creating one.",
148 ].join("\n");
149 const added = extra.trim();
150 return added ? `${job}\n\n### Added by this workspace\n\n${added}` : job;
151}
152
153/** How long a thread is before @g1t's delegation call is worth a larger model. */
154export const LONG_THREAD = 6;
155
156/**
157 * Where @g1t's reply starts: the small tier, or the large one for a
158 * delegation decision in a long thread (more than `LONG_THREAD` messages,
159 * with specialists to choose from). Its floor and ceiling apply after.
160 */
161export function orchestratorTier(messages: number, specialists: number): ModelTier {
162 return messages > LONG_THREAD && specialists > 0 ? "large" : "small";
163}
164
165/** The friendly notice when @g1t has no model to run on. */
166export const BUILTIN_NO_MODEL =
167 "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.