Skip to content
214 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.

Chat and workspace agents: channels, DMs and named agents you talk to1/**
2 * Agents as stored in D1, and their spend. Shared by the RPC methods and
3 * the desk.
4 */
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)5import type { SubagentDef, WorkspaceAgent } from "@g1t/contracts";
Chat and workspace agents: channels, DMs and named agents you talk to6
7import { agentStatus, budgetBlock, dayKey, monthKey } from "./budget.ts";
8import { type Definition, DEFAULT_AUTONOMY, DEFAULT_BUDGET, DEFAULT_ROUTING, PRESETS, readJson } from "./definition.ts";
9
10export type Row = {
11 id: string;
12 workspace_id: string;
13 handle: string;
14 display_name: string;
15 avatar: string | null;
16 role: string;
17 instructions: string;
18 personality_preset: string;
19 personality: string;
20 routing: string;
21 budget: string;
22 autonomy: string;
23 capacity: number;
24 template: 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)25 avatar_seed: string | null;
26 title: string | null;
27 team: string | null;
28 department: string | null;
29 responsibilities: string | null;
30 subagents: string | null;
31 faces: string | null;
Agents recall what Docs say before they answer or work, and each has required reading32 reading?: string | null;
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.33 skills_off?: string | null;
Chat and workspace agents: channels, DMs and named agents you talk to34 version: number;
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)35 /** 1 for the workspace's built-in @g1t. */
36 builtin: number;
Chat and workspace agents: channels, DMs and named agents you talk to37 busy_until: string | null;
38 created_by: string;
39 created_at: string;
40 updated_at: string;
41 archived_at: string | null;
42 /** Joined from agent_spend: this month and today. */
43 spent_month?: number | null;
44 spent_day?: number | null;
45};
46
47/**
48 * Agents with this month's and today's spend: `where` filters `agents`
49 * (aliased `a`); its parameters follow the two periods.
50 */
51export function selectAgents(where: string): string {
52 return `SELECT a.*, m.micros AS spent_month, d.micros AS spent_day FROM agents a
53 LEFT JOIN agent_spend m ON m.agent_id = a.id AND m.period = ?1
54 LEFT JOIN agent_spend d ON d.agent_id = a.id AND d.period = ?2
55 WHERE ${where}`;
56}
57
58export function periods(now: Date): [string, string] {
59 return [monthKey(now), dayKey(now)];
60}
61
62export function definitionOf(row: Row): Definition {
63 return {
64 handle: row.handle,
65 display_name: row.display_name,
66 role: row.role,
67 instructions: row.instructions,
68 personality_preset: PRESETS.includes(row.personality_preset as Definition["personality_preset"])
69 ? (row.personality_preset as Definition["personality_preset"])
70 : "crisp",
71 personality: row.personality,
72 routing: readJson(row.routing, DEFAULT_ROUTING),
73 budget: readJson(row.budget, DEFAULT_BUDGET),
74 autonomy: readJson(row.autonomy, DEFAULT_AUTONOMY),
75 capacity: row.capacity,
76 template: row.template,
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)77 avatar_seed: row.avatar_seed || row.handle,
78 title: row.title ?? "",
79 team: row.team ?? null,
80 department: row.department ?? "",
81 responsibilities: readList<string>(row.responsibilities),
82 subagents: readList<SubagentDef>(row.subagents),
83 faces: "internal",
Agents recall what Docs say before they answer or work, and each has required reading84 reading: readList<string>(row.reading ?? null),
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.85 skills_off: readList<string>(row.skills_off ?? null),
Chat and workspace agents: channels, DMs and named agents you talk to86 };
87}
88
89export function toAgent(row: Row, now: Date): WorkspaceAgent {
90 const definition = definitionOf(row);
91 const spent = { month: row.spent_month ?? 0, day: row.spent_day ?? 0 };
92 return {
93 id: row.id,
94 workspace_id: row.workspace_id,
95 avatar: row.avatar,
96 ...definition,
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)97 builtin: !!row.builtin,
Chat and workspace agents: channels, DMs and named agents you talk to98 version: row.version,
99 status: row.archived_at ? "paused" : agentStatus({ busyUntil: row.busy_until, now, blocked: budgetBlock(definition.budget, spent, now) !== null }),
100 spent_month_micros: spent.month,
101 created_by: row.created_by,
102 created_at: row.created_at,
103 updated_at: row.updated_at,
104 archived_at: row.archived_at,
105 };
106}
107
Agents work in sessions: bounded, visible, steerable work spun off from chat, with subagents and colleagues in a tree paid by its root; memory with sources and scopes; routines; a workspace budget for every agent; agents file issues for whoever asked108/**
109 * Adds a charge to the paying agent's month and day: a reply's, or a
110 * session step's (`task`), counted as one of each.
111 */
112export function spendStatements(db: D1Database, agentId: string, micros: number, now: Date, task: "reply" | "session" = "reply"): D1PreparedStatement[] {
113 const [replies, sessions] = task === "reply" ? [1, 0] : [0, 1];
Chat and workspace agents: channels, DMs and named agents you talk to114 return periods(now).map((period) =>
115 db
116 .prepare(
Agents work in sessions: bounded, visible, steerable work spun off from chat, with subagents and colleagues in a tree paid by its root; memory with sources and scopes; routines; a workspace budget for every agent; agents file issues for whoever asked117 `INSERT INTO agent_spend (agent_id, period, micros, replies, sessions) VALUES (?1, ?2, ?3, ?4, ?5)
118 ON CONFLICT (agent_id, period) DO UPDATE SET micros = micros + ?3, replies = replies + ?4, sessions = sessions + ?5`,
Chat and workspace agents: channels, DMs and named agents you talk to119 )
Agents work in sessions: bounded, visible, steerable work spun off from chat, with subagents and colleagues in a tree paid by its root; memory with sources and scopes; routines; a workspace budget for every agent; agents file issues for whoever asked120 .bind(agentId, period, Math.max(0, Math.ceil(micros)), replies, sessions),
Chat and workspace agents: channels, DMs and named agents you talk to121 );
122}
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)123
124/** A stored JSON list, read defensively: anything else is empty. */
125function readList<T>(raw: string | null): T[] {
126 if (!raw) return [];
127 try {
128 const parsed = JSON.parse(raw) as unknown;
129 return Array.isArray(parsed) ? (parsed as T[]) : [];
130 } catch {
131 return [];
132 }
133}
134
135/**
136 * The agents table's definition columns, in the order `definitionColumns`
137 * gives their values. Statements are built from this one list, so a new
138 * field is added in one place.
139 */
140export const DEFINITION_COLUMNS = [
141 "handle",
142 "display_name",
143 "role",
144 "instructions",
145 "personality_preset",
146 "personality",
147 "routing",
148 "budget",
149 "autonomy",
150 "capacity",
151 "template",
152 "avatar_seed",
153 "title",
154 "team",
155 "department",
156 "responsibilities",
157 "subagents",
158 "faces",
Agents recall what Docs say before they answer or work, and each has required reading159 "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.160 "skills_off",
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)161] as const;
162
163/** A definition's values, in `DEFINITION_COLUMNS` order. */
164export function definitionColumns(d: Definition): (string | number | null)[] {
165 return [
166 d.handle,
167 d.display_name,
168 d.role,
169 d.instructions,
170 d.personality_preset,
171 d.personality,
172 JSON.stringify(d.routing),
173 JSON.stringify(d.budget),
174 JSON.stringify(d.autonomy),
175 d.capacity,
176 d.template,
177 d.avatar_seed,
178 d.title,
179 d.team,
180 d.department,
181 JSON.stringify(d.responsibilities),
182 JSON.stringify(d.subagents),
183 d.faces,
Agents recall what Docs say before they answer or work, and each has required reading184 JSON.stringify(d.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.185 JSON.stringify(d.skills_off ?? []),
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)186 ];
187}
188
189/**
190 * A new agent row: `id`, `workspace_id`, the definition, then `extra`
191 * columns (version, builtin, who made it, when), all bound in order.
192 */
193export function insertAgent(db: D1Database, id: string, workspaceId: string, d: Definition, extra: Record<string, string | number>, orIgnore = false): D1PreparedStatement {
194 const names = ["id", "workspace_id", ...DEFINITION_COLUMNS, ...Object.keys(extra)];
195 const values = [id, workspaceId, ...definitionColumns(d), ...Object.values(extra)];
196 return db
197 .prepare(`INSERT ${orIgnore ? "OR IGNORE " : ""}INTO agents (${names.join(", ")}) VALUES (${names.map(() => "?").join(", ")})`)
198 .bind(...values);
199}
200
201/** Sets a row's definition, from the version read only, with `extra` columns. */
202export function updateAgent(db: D1Database, id: string, readVersion: number, d: Definition, extra: Record<string, string | number>): D1PreparedStatement {
203 const names = [...DEFINITION_COLUMNS, ...Object.keys(extra)];
204 return db
205 .prepare(`UPDATE agents SET ${names.map((name) => `${name} = ?`).join(", ")} WHERE id = ? AND version = ?`)
206 .bind(...definitionColumns(d), ...Object.values(extra), id, readVersion);
207}
208
209/** A version of a definition, as saved. */
210export function versionStatement(db: D1Database, agentId: string, version: number, d: Definition, by: string, at: string): D1PreparedStatement {
211 return db
212 .prepare("INSERT INTO agent_versions (agent_id, version, definition, changed_by, created_at) VALUES (?, ?, ?, ?, ?)")
213 .bind(agentId, version, JSON.stringify(d), by, at);
214}

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