Skip to content
75 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { type Colleague, type HandOffPorts, addressed, handOffPort } from "./handoff.ts";
5import type { HandedOff } from "./surface.ts";
6
7const mike: Colleague = { id: "agt_mike", handle: "mike", display_name: "Mike", builtin: false, status: "idle" };
8const g1t: Colleague = { id: "agt_g1t", handle: "g1t", display_name: "g1t", builtin: true, status: "idle" };
9const margo: Colleague = { id: "agt_margo", handle: "margo", display_name: "Margo", builtin: false, status: "paused" };
10const sam: Colleague = { id: "agt_sam", handle: "sam", display_name: "Sam", builtin: false, status: "out_of_budget" };
11
12/** A small workspace: these agents, and the person syntaqx. */
13function world(over: Partial<HandOffPorts> = {}, answer: HandedOff = { ok: true, where: "group_dm", opened: true }) {
14 const handed: { colleague: string; brief: string }[] = [];
15 const agents = [mike, g1t, margo, sam];
16 const ports: HandOffPorts = {
17 self: { id: "agt_g1t", handle: "g1t" },
18 chain: [],
19 asker: { username: "syntaqx", role: "owner", can_write: true },
20 agent: async (handle) => agents.find((a) => a.handle === handle) ?? null,
21 person: async (handle) => handle === "syntaqx",
22 handOff: async (colleague, brief) => {
23 handed.push({ colleague, brief });
24 return answer;
25 },
26 ...over,
27 };
28 return { port: handOffPort(ports), handed };
29}
30
31test("g1t hands Mike the hiring work: he is woken with the brief addressed to him, and g1t is told where it went", async () => {
32 const { port, handed } = world();
33 const done = await port("mike", "Help Chase hire an engineering manager: draft the role brief.");
34 assert.equal(done.ok, true);
35 assert.deepEqual(handed, [{ colleague: "agt_mike", brief: "@mike Help Chase hire an engineering manager: draft the role brief." }]);
36 assert.match(done.message, /Handed to @mike in a new group message with @syntaqx, you and them/);
37 assert.match(done.message, /a card here links to it/);
38});
39
40test("handed here, or into the group message they already share", async () => {
41 const here = await world({}, { ok: true, where: "here", opened: false }).port("mike", "x");
42 assert.match(here.message, /your brief is posted in this conversation/);
43 const again = await world({}, { ok: true, where: "group_dm", opened: false }).port("mike", "x");
44 assert.match(again.message, /the group message you three already have/);
45});
46
47test("refusals: unknown, a person, itself, @g1t, back along the chain, unavailable, an outside asker", async () => {
48 const refused = async (handle: string, over: Partial<HandOffPorts> = {}) => {
49 const { port, handed } = world(over);
50 const done = await port(handle, "Do it.");
51 assert.equal(done.ok, false, handle);
52 assert.equal(handed.length, 0, `nothing handed to ${handle}`);
53 return done.message;
54 };
55 assert.match(await refused("nobody"), /no agent called @nobody/);
56 assert.match(await refused("syntaqx"), /@syntaqx is a person, not an agent/);
57 assert.match(await refused("mike", { self: { id: "agt_mike", handle: "mike" } }), /That's you/);
58 assert.match(await refused("g1t", { self: { id: "agt_mike", handle: "mike" } }), /@g1t can't be handed work by an agent/);
59 assert.match(await refused("mike", { self: { id: "agt_sam", handle: "sam" }, chain: ["agt_g1t", "agt_mike"] }), /already handled this request/);
60 assert.match(await refused("margo"), /is paused/);
61 assert.match(await refused("sam"), /out of budget/);
62 assert.match(await refused("mike", { asker: { username: "eve", role: "outside", can_write: false } }), /isn't a member of this workspace/);
63});
64
65test("chat's own refusal comes back to the agent as the tool's answer", async () => {
66 const { port } = world({}, { ok: false, message: "This request has been passed along too many times." });
67 const done = await port("mike", "x");
68 assert.equal(done.ok, false);
69 assert.match(done.message, /couldn't be handed over: This request has been passed along too many times/);
70});
71
72test("a brief that already names the colleague isn't addressed twice", () => {
73 assert.equal(addressed("mike", "@Mike, please draft it."), "@Mike, please draft it.");
74 assert.equal(addressed("mike", "Please draft it, cc @mikey."), "@mike Please draft it, cc @mikey.");
75});