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

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 asked1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { User } from "@g1t/contracts";
5
6import { Audience, type AudienceInfo, type AudiencePorts, type RepoRef, WITHHELD } from "./audience.ts";
7import { type MemoryViewer, type RecallPlace, changeableBy, cleanFact, memorySection, recallable, scopeFor, visibleTo } from "./memory.ts";
8import { alertDue, checkPolicy, DEFAULT_POLICY, policyBlock } from "./policy.ts";
9import { checkRoutine, checkSchedule, describeSchedule, nextRun } from "./schedule.ts";
10import { type ActionPorts, type ToolPorts, MAX_SESSION_TOOL_CALLS, MAX_TOOL_CALLS, ToolBox } from "./tools.ts";
11import { dollars } from "./money.ts";
12
13// ── Memory: where a fact is recalled, who sees it, who changes it ───────
14
15const dmWithAnn: RecallPlace = { channel_id: "chn_dm_ann", kind: "dm", people: ["ann"] };
16const groupDm: RecallPlace = { channel_id: "chn_dm_group", kind: "dm", people: ["ann", "bob"] };
17const privateOps: RecallPlace = { channel_id: "chn_ops", kind: "private", people: ["ann", "bob"] };
18const publicGeneral: RecallPlace = { channel_id: "chn_general", kind: "public", people: ["ann"] };
19
20test("a person's fact is recalled only in a direct message with that one person", () => {
21 const fact = { scope: "person", scope_ref: "ann" };
22 assert.equal(recallable(fact, dmWithAnn), true);
23 assert.equal(recallable(fact, groupDm), false, "not with Bob there too");
24 assert.equal(recallable(fact, privateOps), false);
25 assert.equal(recallable(fact, publicGeneral), false);
26 assert.equal(recallable(fact, { channel_id: "chn_dm_bob", kind: "dm", people: ["bob"] }), false);
27});
28
29test("a conversation's fact stays in it; a workspace fact goes anywhere", () => {
30 assert.equal(recallable({ scope: "channel", scope_ref: "chn_ops" }, privateOps), true);
31 assert.equal(recallable({ scope: "channel", scope_ref: "chn_ops" }, publicGeneral), false);
32 assert.equal(recallable({ scope: "workspace", scope_ref: "" }, dmWithAnn), true);
33 assert.equal(recallable({ scope: "made-up", scope_ref: "" }, dmWithAnn), false);
34});
35
36test("an agent remembers into the narrowest scope the conversation allows", () => {
37 assert.deepEqual(scopeFor(dmWithAnn, null), { scope: "person", ref: "ann" });
38 assert.deepEqual(scopeFor(dmWithAnn, "workspace"), { scope: "person", ref: "ann" }, "a DM can't write for the workspace");
39 assert.deepEqual(scopeFor(privateOps, "workspace"), { scope: "channel", ref: "chn_ops" }, "nor can a private channel");
40 assert.deepEqual(scopeFor(groupDm, "person"), { scope: "channel", ref: "chn_dm_group" });
41 assert.deepEqual(scopeFor(publicGeneral, "workspace"), { scope: "workspace", ref: "" });
42 assert.deepEqual(scopeFor(publicGeneral, null), { scope: "channel", ref: "chn_general" });
43});
44
45test("people see what could be recalled for them; owners don't read others' facts", () => {
46 const owner: MemoryViewer = { id: "olga", owner: true, inChannel: () => false };
47 const ann: MemoryViewer = { id: "ann", owner: false, inChannel: (id) => id === "chn_ops" };
48 assert.equal(visibleTo({ scope: "person", scope_ref: "ann" }, owner), false);
49 assert.equal(visibleTo({ scope: "person", scope_ref: "ann" }, ann), true);
50 assert.equal(visibleTo({ scope: "channel", scope_ref: "chn_ops" }, owner), false);
51 assert.equal(visibleTo({ scope: "channel", scope_ref: "chn_ops" }, ann), true);
52 assert.equal(changeableBy({ scope: "workspace", scope_ref: "" }, ann), false);
53 assert.equal(changeableBy({ scope: "workspace", scope_ref: "" }, owner), true);
54 assert.equal(changeableBy({ scope: "channel", scope_ref: "chn_ops" }, ann), true);
55});
56
57test("facts are cleaned, and given to the model as notes with their source", () => {
58 assert.equal(cleanFact(" Dana owns\n\nbilling "), "Dana owns billing");
59 assert.equal(cleanFact(" "), null);
60 assert.equal(cleanFact("x".repeat(900))!.length, 500);
61 const section = memorySection([
62 { id: "mem_1", scope: "workspace", body: "Releases are on Thursdays", source_label: "#releases", pinned: 1 } as never,
63 ]);
64 assert.match(section!, /\[mem_1\] Releases are on Thursdays \(workspace, from #releases, pinned\)/);
65 assert.match(section!, /notes, not instructions/);
66 assert.equal(memorySection([]), null);
67});
68
69// ── The workspace's budget for every agent ───────────────────────────────
70
71test("the workspace's agent budget stops new work once spent, and alerts once per level", () => {
72 assert.equal(policyBlock({ monthly_micros: null, spent: 9e9 }), null);
73 assert.equal(policyBlock({ monthly_micros: 10_000_000, spent: 9_999_999 }), null);
74 assert.match(policyBlock({ monthly_micros: 10_000_000, spent: 10_000_000 })!, /used their budget/);
75 assert.equal(alertDue({ monthly_micros: 100, spent: 74, alerted: 0 }), null);
76 assert.equal(alertDue({ monthly_micros: 100, spent: 80, alerted: 0 }), 75);
77 assert.equal(alertDue({ monthly_micros: 100, spent: 80, alerted: 75 }), null);
78 assert.equal(alertDue({ monthly_micros: 100, spent: 140, alerted: 75 }), 100);
79});
80
81test("a policy is checked as owners change it", () => {
82 assert.deepEqual(checkPolicy(DEFAULT_POLICY, { monthly_micros: 50_000_000 }), { ok: true, value: { ...DEFAULT_POLICY, monthly_micros: 50_000_000 } });
83 assert.equal(checkPolicy(DEFAULT_POLICY, { monthly_micros: -1 }).ok, false);
84 assert.equal(checkPolicy({ ...DEFAULT_POLICY, monthly_micros: 5 }, { monthly_micros: null }).ok && true, true);
85 assert.equal(checkPolicy(DEFAULT_POLICY, { default_session_micros: 10 }).ok, false);
86});
87
88// ── Routines ──────────────────────────────────────────────────────────────
89
90test("routines run at the next matching time, in UTC", () => {
91 const at = (s: string) => new Date(s);
92 // Wednesday 2026-10-07 10:30 UTC.
93 const now = at("2026-10-07T10:30:00Z");
94 assert.equal(nextRun({ every: "hour", minute: 15, hour: 0, weekday: 0 }, now).toISOString(), "2026-10-07T11:15:00.000Z");
95 assert.equal(nextRun({ every: "hour", minute: 45, hour: 0, weekday: 0 }, now).toISOString(), "2026-10-07T10:45:00.000Z");
96 assert.equal(nextRun({ every: "day", minute: 0, hour: 9, weekday: 0 }, now).toISOString(), "2026-10-08T09:00:00.000Z");
97 assert.equal(nextRun({ every: "day", minute: 0, hour: 12, weekday: 0 }, now).toISOString(), "2026-10-07T12:00:00.000Z");
98 // Friday evening: the next weekday is Monday.
99 assert.equal(nextRun({ every: "weekday", minute: 0, hour: 9, weekday: 0 }, at("2026-10-09T18:00:00Z")).toISOString(), "2026-10-12T09:00:00.000Z");
100 assert.equal(nextRun({ every: "week", minute: 30, hour: 8, weekday: 1 }, now).toISOString(), "2026-10-12T08:30:00.000Z");
101 assert.equal(describeSchedule({ every: "weekday", minute: 5, hour: 9, weekday: 0 }), "Every weekday at 09:05 UTC");
102});
103
104test("routines and schedules are checked", () => {
105 assert.equal(checkSchedule({ every: "fortnight" }).ok, false);
106 assert.equal(checkSchedule({ every: "day", hour: 24 }).ok, false);
107 assert.deepEqual(checkSchedule({ every: "day" }), { ok: true, value: { every: "day", minute: 0, hour: 9, weekday: 1 } });
108 assert.equal(checkRoutine({ name: "", instructions: "Summarise support", schedule: { every: "day", minute: 0, hour: 9, weekday: 1 }, channel_id: "c" }).ok, false);
109 assert.equal(checkRoutine({ name: "Digest", instructions: "short", schedule: { every: "day", minute: 0, hour: 9, weekday: 1 }, channel_id: "c" }).ok, false);
110 assert.equal(checkRoutine({ name: "Digest", instructions: "Summarise this week's support themes", schedule: { every: "day", minute: 0, hour: 9, weekday: 1 }, channel_id: "c" }).ok, true);
111});
112
113test("money reads as people expect", () => {
114 assert.equal(dollars(140_000), "$0.14");
115 assert.equal(dollars(2_000_000), "$2.00");
116 assert.equal(dollars(3_000), "<$0.01");
117 assert.equal(dollars(1_204_000_000), "$1,204");
118});
119
120// ── Tools that act ────────────────────────────────────────────────────────
121
122const WEB: RepoRef = { id: "rep_web", namespace: "acme", name: "web", isPrivate: true, defaultBranch: "main" };
123const readPorts = {
124 readFile: async () => null,
125 searchCode: async () => [],
126 listIssues: async () => [],
127 getIssue: async () => null,
128 getPull: async () => null,
129 recentPulls: async () => [],
130 searchMessages: async () => [],
131 readThread: async () => null,
132 roster: async () => "",
133 consult: async () => ({ ok: false as const, message: "no" }),
134} satisfies ToolPorts;
135
136function audienceWorld(info: AudienceInfo, people: User[], reads: Record<string, string[]>): AudiencePorts {
137 return {
138 info: async () => info,
139 users: async (ids) => people.filter((u) => ids.includes(u.id)),
140 workspaceRepos: async (viewer) => (reads[viewer.id] ?? []).includes(WEB.id) ? [WEB] : [],
141 readable: async (ids, viewer) => (ids.includes(WEB.id) && (reads[viewer.id] ?? []).includes(WEB.id) ? [WEB] : []),
142 };
143}
144
145const member = (id: string, code = true): User => ({ id, username: id, workspaces: [{ slug: "acme", role: "member", code_access: code }] }) as User;
146
147function actions(log: string[]): ActionPorts {
148 return {
149 remember: async (body) => (log.push(`remember:${body}`), { ok: true, message: "ok" }),
150 forget: async () => ({ ok: true, message: "ok" }),
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar151 draftIssue: async (repo, input) => (log.push(`draft:${repo.name}:${input.title}`), { ok: true, message: "drafted" }),
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 asked152 startSession: async (title) => (log.push(`session:${title}`), { ok: true, message: "started" }),
153 postUpdate: async () => ({ ok: true, message: "posted" }),
154 useSubagent: async () => ({ ok: true, message: "on it" }),
155 bringIn: async () => ({ ok: true, message: "on it" }),
156 };
157}
158
159const ctx = { agentId: "agt_me", notConsult: ["me"], hops: 0, maxHops: 6 };
160
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar161test("a reply can spin off a session and draft an issue; a session can't spin off", async () => {
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 asked162 const log: string[] = [];
163 const audience = await Audience.build("acme", "ann", audienceWorld({ kind: "dm", member_user_ids: ["ann"], member_count: 1 }, [member("ann")], { ann: [WEB.id] }));
164 const reply = new ToolBox(audience, readPorts, ctx, [], actions(log));
165 const names = reply.definitions().map((t) => t.name);
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar166 assert.ok(names.includes("start_session") && names.includes("draft_issue") && names.includes("remember"));
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 asked167 assert.ok(!names.includes("bring_in") && !names.includes("use_subagent") && !names.includes("post_update"));
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar168 const drafted = await reply.run("draft_issue", { repo: "web", title: "CSV export times out", body: "Over 100k rows." });
169 assert.equal(drafted.outcome, "allowed");
170 assert.deepEqual(log, ["draft:web:CSV export times out"]);
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 asked171 // A repository the audience can't read is withheld, never named.
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar172 const hidden = await reply.run("draft_issue", { repo: "acme/secret", title: "x", body: "y" });
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 asked173 assert.equal(hidden.text, WITHHELD);
174
175 const session = new ToolBox(audience, readPorts, { ...ctx, session: true }, [], actions(log));
176 const inSession = session.definitions().map((t) => t.name);
177 assert.ok(!inSession.includes("start_session"));
178 assert.ok(inSession.includes("bring_in") && inSession.includes("use_subagent") && inSession.includes("post_update"));
179 assert.equal((await session.run("start_session", { title: "x", goal: "y" })).outcome, "refused");
180 assert.equal(reply.maxCalls, MAX_TOOL_CALLS);
181 assert.equal(session.maxCalls, MAX_SESSION_TOOL_CALLS);
182});
183
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar184test("nobody drafts issues for someone who can't read code, and no hand-offs at the hop limit", async () => {
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 asked185 const log: string[] = [];
186 const noCode = await Audience.build("acme", "cal", audienceWorld({ kind: "dm", member_user_ids: ["cal"], member_count: 1 }, [member("cal", false)], { cal: [WEB.id] }));
187 const box = new ToolBox(noCode, readPorts, ctx, [], actions(log));
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar188 assert.ok(!box.definitions().some((t) => t.name === "draft_issue"));
189 assert.equal((await box.run("draft_issue", { repo: "web", title: "x", body: "y" })).outcome, "refused");
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 asked190 assert.deepEqual(log, []);
191 const audience = await Audience.build("acme", "ann", audienceWorld({ kind: "dm", member_user_ids: ["ann"], member_count: 1 }, [member("ann")], { ann: [WEB.id] }));
192 const atLimit = new ToolBox(audience, readPorts, { ...ctx, session: true, hops: 6 }, [], actions(log));
193 assert.ok(!atLimit.definitions().some((t) => t.name === "bring_in" || t.name === "use_subagent"));
194 // The agent can't bring itself in.
195 const session = new ToolBox(audience, readPorts, { ...ctx, session: true }, [], actions(log));
196 assert.equal((await session.run("bring_in", { handle: "@me", brief: "help" })).outcome, "refused");
197});
198
199test("updates are limited per step", async () => {
200 const audience = await Audience.build("acme", "ann", audienceWorld({ kind: "dm", member_user_ids: ["ann"], member_count: 1 }, [member("ann")], { ann: [WEB.id] }));
201 const session = new ToolBox(audience, readPorts, { ...ctx, session: true }, [], actions([]));
202 for (let i = 0; i < 3; i++) assert.equal((await session.run("post_update", { text: `step ${i}` })).outcome, "allowed");
203 assert.equal((await session.run("post_update", { text: "again" })).outcome, "refused");
204});
Routines run when something happens: a pull request ready for review or merged, checks or a deploy failing, an issue opened205
206// ── Routines that run when something happens ─────────────────────────────
207
208import { describeEvents, suggestRoutines } from "./suggest.ts";
209import { checkRoutine as checkEventRoutine } from "./schedule.ts";
210
211test("Margo's responsibilities suggest the routines that bind to events", () => {
212 const margo = ["Reviewing pull requests for risk and test coverage", "Test plans for new features", "Chasing flaky checks"];
213 const suggested = suggestRoutines(margo, []);
214 assert.deepEqual(
215 suggested.map((s) => [s.routine.name, s.routine.events]),
216 [
217 ["Review pull requests", ["pull_ready"]],
218 ["Test plans for new work", ["issue_opened"]],
219 ["Chase failing checks", ["checks_failed"]],
220 ],
221 );
222 assert.match(suggested[0].routine.instructions, /risk and test coverage/);
223 // What it already runs on isn't suggested again.
224 assert.deepEqual(
225 suggestRoutines(margo, [{ name: "PR reviews", events: ["pull_ready"] }]).map((s) => s.routine.name),
226 ["Test plans for new work", "Chase failing checks"],
227 );
228 assert.deepEqual(suggestRoutines(["Keep the office plants alive"], []), []);
229});
230
231test("an event routine needs no schedule, but a routine needs one or the other", () => {
232 const base = { name: "Review", instructions: "Review pull requests for risk", channel_id: "chn_qa" };
233 assert.equal(checkEventRoutine({ ...base, schedule: null, events: ["pull_ready"] }).ok, true);
234 assert.equal(checkEventRoutine({ ...base, schedule: null, events: [] }).ok, false);
235 assert.equal(checkEventRoutine({ ...base, schedule: null, events: ["pull_exploded" as never] }).ok, false);
236 assert.equal(checkEventRoutine({ ...base, schedule: null, events: ["pull_ready"], repos: ["not a repo"] }).ok, false);
237 const ok = checkEventRoutine({ ...base, schedule: null, events: ["pull_ready", "pull_ready"], repos: ["Acme/Web"] });
238 assert.ok(ok.ok && ok.value.events.length === 1 && ok.value.repos[0] === "acme/web");
239 assert.equal(describeEvents(["pull_ready", "checks_failed"]), "When a pull request is ready for review or checks fail on a pull request");
240});
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar241
242test("agents comment and review on issues and pull requests only where the asker can read", async () => {
243 const log: string[] = [];
244 const ports = {
245 ...actions(log),
246 comment: async (repo: RepoRef, asker: User, number: number) => (log.push(`comment:${repo.name}#${number}:${asker.username}`), { ok: true, message: "ok" }),
247 review: async (repo: RepoRef, asker: User, number: number, verdict: string) => (log.push(`review:${repo.name}#${number}:${verdict}:${asker.username}`), { ok: true, message: "ok" }),
248 };
249 const audience = await Audience.build("acme", "ann", audienceWorld({ kind: "dm", member_user_ids: ["ann"], member_count: 1 }, [member("ann")], { ann: [WEB.id] }));
250 const session = new ToolBox(audience, readPorts, { ...ctx, session: true }, [], ports);
251 assert.ok(session.definitions().some((t) => t.name === "review_pull"));
252 await session.run("review_pull", { repo: "web", number: 12, verdict: "approve", body: "" });
253 await session.run("review_pull", { repo: "web", number: 12, verdict: "made-up", body: "Looks risky" });
254 await session.run("comment", { repo: "web", number: 3, body: "Test plan: …" });
255 assert.equal((await session.run("comment", { repo: "acme/secret", number: 3, body: "x" })).text, WITHHELD);
256 assert.deepEqual(log, ["review:web#12:approve:ann", "review:web#12:comment:ann", "comment:web#3:ann"]);
257 const noCode = await Audience.build("acme", "cal", audienceWorld({ kind: "dm", member_user_ids: ["cal"], member_count: 1 }, [member("cal", false)], { cal: [WEB.id] }));
258 assert.ok(!new ToolBox(noCode, readPorts, { ...ctx, session: true }, [], ports).definitions().some((t) => t.name === "comment" || t.name === "review_pull"));
259});
Docs: a workspace knowledge base people and agents write together260
261// ── Docs, for everyone ───────────────────────────────────────────────────
262
263import { pageId } from "./tools.ts";
264
265test("someone without Code still gets Docs; what they can't read is withheld, never named", async () => {
266 const asked: string[] = [];
267 const docs = {
268 spaces: async () => "- General (id spc_1, workspace; you can suggest edits)",
269 search: async (_v: User, audience: unknown, query: string) => (asked.push(`search:${query}:${JSON.stringify(audience)}`), "- Refunds policy (/acme/-/docs/general/refunds-pag_1, id pag_1)"),
270 read: async (_v: User, _a: unknown, page: string) => (page === "pag_secret" ? null : "# Refunds policy"),
271 edit: async () => ({ ok: true, message: "Suggested" }),
272 create: async () => ({ ok: true, message: "Wrote" }),
273 };
274 const rep = await Audience.build("acme", "cal", audienceWorld({ kind: "dm", member_user_ids: ["cal"], member_count: 1 }, [member("cal", false)], {}));
275 const box = new ToolBox(rep, { ...readPorts, docs }, ctx, [], actions([]));
276 const names = box.definitions().map((t) => t.name);
277 assert.ok(names.includes("search_docs") && names.includes("read_page") && names.includes("edit_page") && names.includes("create_page"));
278 assert.ok(!names.includes("read_file"), "still no code");
279 await box.run("search_docs", { query: "refund window" });
280 assert.deepEqual(asked, ['search:refund window:{"kind":"people","user_ids":["cal"]}']);
281 assert.equal((await box.run("read_page", { page: "pag_secret" })).text, WITHHELD);
282 assert.match((await box.run("read_page", { page: "/acme/-/docs/general/refunds-pag_1" })).text, /Refunds policy/);
283 assert.equal((await box.run("edit_page", { page: "pag_1", target: "section", markdown: "x" })).outcome, "refused", "a section edit names its heading");
284 // Without a docs service, no docs tools.
285 assert.ok(!new ToolBox(rep, readPorts, ctx, [], actions([])).definitions().some((t) => t.name === "search_docs"));
286});
287
288test("page ids come from ids or links", () => {
289 assert.equal(pageId("pag_01jabc"), "pag_01jabc");
290 assert.equal(pageId("/acme/-/docs/general/refunds-policy-pag_01jabc"), "pag_01jabc");
291 assert.equal(pageId("https://g1t.sh/acme/-/docs/general/refunds-pag_01jabc?x=1"), "pag_01jabc");
292 assert.equal(pageId(" "), null);
293});