Skip to content
93 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 to1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { User } from "@g1t/contracts";
5
6import { canManage, canSee } from "./access.ts";
7import { DEFAULT_AUTONOMY, DEFAULT_CAPACITY, applyChanges, readJson } from "./definition.ts";
8import { TEMPLATES, TEMPLATE_IDS } from "./templates.ts";
9
10const input = { handle: "Ship", display_name: " Ship ", role: "Release manager", instructions: "Cut releases." };
11
12test("a new agent gets safe defaults", () => {
13 const made = applyChanges(null, input, TEMPLATE_IDS);
14 assert.ok(made.ok);
15 if (!made.ok) return;
16 assert.equal(made.value.handle, "ship");
17 assert.equal(made.value.display_name, "Ship");
18 assert.equal(made.value.personality_preset, "crisp");
19 assert.equal(made.value.capacity, DEFAULT_CAPACITY);
20 assert.deepEqual(made.value.autonomy, DEFAULT_AUTONOMY);
21 assert.equal(made.value.autonomy.merge, "approval");
22 assert.deepEqual(made.value.routing, { floor: null, ceiling: null, providers: [], pinned: null });
23});
24
25test("a new agent needs a handle, a name, a role and instructions", () => {
26 for (const missing of ["handle", "display_name", "role", "instructions"] as const) {
27 const made = applyChanges(null, { ...input, [missing]: "" }, TEMPLATE_IDS);
28 assert.equal(made.ok, false, missing);
29 }
30 assert.equal(applyChanges(null, { ...input, handle: "g1t" }, TEMPLATE_IDS).ok, false);
31});
32
33test("a change merges onto the definition, and is checked", () => {
34 const made = applyChanges(null, input, TEMPLATE_IDS);
35 assert.ok(made.ok);
36 if (!made.ok) return;
37 const changed = applyChanges(made.value, { routing: { floor: "large" }, budget: { monthly_micros: 5_000_000 }, autonomy: { merge: "never" } }, TEMPLATE_IDS);
38 assert.ok(changed.ok);
39 if (!changed.ok) return;
40 assert.equal(changed.value.handle, "ship", "untouched fields stay");
41 assert.equal(changed.value.routing.floor, "large");
42 assert.equal(changed.value.routing.ceiling, null);
43 assert.equal(changed.value.budget.monthly_micros, 5_000_000);
44 assert.equal(changed.value.budget.daily_micros, null);
45 assert.equal(changed.value.autonomy.merge, "never");
46 assert.equal(changed.value.autonomy.open_pull_requests, "alone");
47
48 const bad = (changes: object) => applyChanges(made.value, changes, TEMPLATE_IDS).ok;
49 assert.equal(bad({ routing: { floor: "frontier", ceiling: "small" } }), false, "floor above ceiling");
50 assert.equal(bad({ routing: { floor: "huge" } }), false);
51 assert.equal(bad({ routing: { pinned: "no-slash" } }), false);
52 assert.equal(bad({ budget: { monthly_micros: -1 } }), false);
53 assert.equal(bad({ budget: { daily_micros: 1.5 } }), false);
54 assert.equal(bad({ autonomy: { deploy_production: "alone" } }), false, "production deploys always ask");
55 assert.equal(bad({ capacity: 0 }), false);
56 assert.equal(bad({ capacity: 11 }), false);
57 assert.equal(bad({ personality_preset: "pirate" }), false);
58 assert.equal(bad({ template: "nope" }), false);
59 assert.equal(bad({ template: "reviewer" }), true);
60 for (const providers of [[], ["g1t"], ["workspace"], ["int_1"], ["g1t", "workspace"]]) assert.equal(bad({ routing: { providers } }), true, JSON.stringify(providers));
61 assert.equal(bad({ routing: { providers: [""] } }), false);
62 assert.equal(bad({ instructions: "x".repeat(8001) }), false);
63});
64
65test("a stored JSON column that cannot be read is the default", () => {
66 assert.deepEqual(readJson("{bad", { a: 1 }), { a: 1 });
67 assert.deepEqual(readJson('{"b":2}', { a: 1 }), { a: 1, b: 2 });
68 assert.deepEqual(readJson(null, { a: 1 }), { a: 1 });
69});
70
71test("every template is a valid definition with the routing its job needs", () => {
72 assert.deepEqual(TEMPLATE_IDS, ["planner", "implementer", "reviewer", "triage", "documenter", "release-manager", "on-call"]);
73 for (const template of TEMPLATES) {
74 const made = applyChanges(null, { ...template, template: template.id }, TEMPLATE_IDS);
75 assert.ok(made.ok, template.id);
76 }
77 const byId = Object.fromEntries(TEMPLATES.map((t) => [t.id, t]));
78 assert.equal(byId.reviewer.routing.floor, "large");
79 assert.equal(byId.triage.routing.ceiling, "large");
80 assert.equal(new Set(TEMPLATES.map((t) => t.handle)).size, TEMPLATES.length, "handles are distinct");
81});
82
83test("members see a workspace's agents; owners change them", () => {
84 const person = (role: "owner" | "member"): User => ({ id: "u", username: "u", workspaces: [{ slug: "acme", role }] }) as User;
85 assert.ok(canSee(person("member"), "ACME"));
86 assert.ok(!canManage(person("member"), "acme"));
87 assert.ok(canManage(person("owner"), "acme"));
88 assert.ok(!canSee(person("owner"), "other"));
89 assert.ok(!canSee(null, "acme"));
90 const token = { id: "w", username: "acme", kind: "workspace" } as User;
91 assert.ok(canManage(token, "acme"), "the workspace's own token");
92 assert.ok(!canSee(token, "other"));
93});