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

Anyone makes an agent by describing it: one box drafts the whole agent (name, job, responsibilities, voice, skills to keep, integrations it needs, model limits and budget) as a card to change and try in a chat beside it before creating it, and an agent changes later by saying what to change, shown as a diff before it's saved. Members can make personal agents, which only they talk to and which spend from their own budget, unless owners turn that off; owners promote one to a workspace agent. The agents guide says how.1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { changeRows, integrationHint, lineDiff, readDefinition } from "./agent-builder.ts";
5
6test("a draft comes back from the page checked for shape", () => {
7 const read = readDefinition(
8 JSON.stringify({
9 handle: "juniper",
10 display_name: " Juniper ",
11 title: "Release Manager",
12 instructions: "Cut releases.",
13 responsibilities: ["One", 2, "Two"],
14 personality_preset: "loud",
15 routing: { floor: "huge", ceiling: "small", providers: ["x"], pinned: "a/b" },
16 budget: { monthly_micros: 20_000_000, task_micros: -1 },
17 skills_off: ["data"],
18 team: "ops",
19 scope: "personal",
20 }),
21 );
22 assert.ok(read);
23 assert.equal(read.display_name, "Juniper");
24 assert.deepEqual(read.responsibilities, ["One", "Two"]);
25 assert.equal(read.personality_preset, "crisp");
26 assert.deepEqual(read.routing, { floor: null, ceiling: "small", providers: [], pinned: null }, "nothing but limits");
27 assert.deepEqual(read.budget, { monthly_micros: 20_000_000, daily_micros: null, task_micros: null });
28 assert.equal(read.team, null, "a draft is on no team");
29 assert.equal(read.scope, "personal");
30 assert.equal(readDefinition("not json"), null);
31 assert.equal(readDefinition("[1]"), null);
32});
33
34test("an integration it needs: owners connect it, anyone else asks, and soon is soon", () => {
35 const owner = integrationHint("acme", { id: "linear", why: "Reads issues." }, true);
36 assert.equal(owner?.action, "connect");
37 assert.equal(owner?.href, "/acme/-/integrations/trackers?add=linear#add");
38 const member = integrationHint("acme", { id: "linear", why: "Reads issues." }, false);
39 assert.deepEqual(member && { action: member.action, href: member.href }, { action: "ask", href: "/acme/-/marketplace/integrations/linear" });
40 assert.equal(integrationHint("acme", { id: "box", why: "" }, true)?.action, "soon");
41 assert.equal(integrationHint("acme", { id: "nope", why: "" }, true), null);
42});
43
44test("a job's change reads as lines kept, removed and added", () => {
45 assert.deepEqual(lineDiff("a\nb\nc", "a\nB\nc\nd"), [
46 { kind: "same", text: "a" },
47 { kind: "removed", text: "b" },
48 { kind: "added", text: "B" },
49 { kind: "same", text: "c" },
50 { kind: "added", text: "d" },
51 ]);
52 assert.deepEqual(lineDiff("same", "same"), [{ kind: "same", text: "same" }]);
53});
54
55test("a drafted change lists each field it touches, before and after", () => {
56 const agent = {
57 handle: "juniper",
58 display_name: "Juniper",
59 title: "Release Manager",
60 department: "",
61 instructions: "Cut releases.",
62 responsibilities: ["One", "Two"],
63 personality_preset: "crisp" as const,
64 personality: "",
65 skills_off: [],
66 routing: { floor: null, ceiling: null, providers: [], pinned: null },
67 budget: { monthly_micros: 20_000_000, daily_micros: null, task_micros: 2_000_000 },
68 };
69 const rows = changeRows(agent as never, {
70 display_name: "Wren",
71 handle: "wren",
72 instructions: "Cut releases.\nAnswer in Spanish.",
73 skills_off: ["data"],
74 routing: { ceiling: "small" },
75 budget: { monthly_micros: 50_000_000 },
76 });
77 assert.deepEqual(
78 rows.map((r) => [r.label, r.before, r.after]),
79 [
80 ["Name", "Juniper", "Wren"],
81 ["Handle", "@juniper", "@wren"],
82 ["Job", "Cut releases.", "Cut releases.\nAnswer in Spanish."],
83 ["Skills", "All on", "Turned off: data"],
84 ["Model ceiling", "None", "Fast"],
85 ["Monthly budget", "$20", "$50"],
86 ],
87 );
88 assert.equal(rows[2]!.lines?.filter((l) => l.kind === "added").length, 1);
89});