Skip to content
178 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { AgentDelivery } from "@g1t/contracts";
5
6import { g1tSurface } from "./surface.ts";
7
8const delivery: AgentDelivery = {
9 workspace: "acme",
10 workspace_id: "wsp_1",
11 channel_id: "chn_1",
12 channel_kind: "channel",
13 channel_name: "releases",
14 agent_id: "agt_ship",
15 message_id: "msg_1",
16 thread_root: "msg_0",
17 asked_by: "usr_dana",
18 hops: 2,
19 asker: { username: "dana", role: "member", can_write: false },
20 chain: ["agt_g1t"],
21 surface: "g1t",
22};
23
24/** A chat binding that records each call and answers `answers[method]`. */
25function fakeChat(answers: Record<string, unknown>) {
26 const calls: { method: string; body: any }[] = [];
27 const binding = {
28 async fetch(url: string, init: { body: string }) {
29 const method = new URL(url).pathname.replace("/rpc/", "");
30 calls.push({ method, body: JSON.parse(init.body) });
31 return Response.json(answers[method] ?? { ok: true, value: null });
32 },
33 };
34 return { binding: binding as any, calls };
35}
36
37test("the g1t adapter posts in the thread, handing on hops, asked_by and the asker's access", async () => {
38 const chat = fakeChat({ post_as_agent: { ok: true, value: { id: "msg_2" } } });
39 const id = await g1tSurface(chat.binding, delivery).post("Tagged 1.4.");
40 assert.equal(id, "msg_2");
41 const [call] = chat.calls;
42 assert.equal(call.method, "post_as_agent");
43 assert.equal(call.body.agent_id, "agt_ship");
44 assert.equal(call.body.channel_id, "chn_1");
45 assert.deepEqual(call.body.message, {
46 body: "Tagged 1.4.",
47 card: null,
48 thread_root: "msg_0",
49 hops: 2,
50 asked_by: "usr_dana",
51 asker: { username: "dana", role: "member", can_write: false },
52 chain: ["agt_g1t"],
53 });
54});
55
56test("the g1t adapter reads the thread as the agent, and leaves out deleted messages", async () => {
57 const message = (id: string, deleted: string | null) => ({
58 id,
59 channel_id: "chn_1",
60 author: { kind: "user", id: "usr_dana", name: "dana", display_name: "Dana", avatar: null, role: null },
61 kind: "text",
62 body: "hi",
63 card: null,
64 thread_root: "msg_0",
65 reply_count: 0,
66 last_reply_at: null,
67 created_at: "2026-10-08T12:00:00Z",
68 edited_at: null,
69 deleted_at: deleted,
70 });
71 const chat = fakeChat({ history_for_agent: { ok: true, value: [message("msg_0", null), message("msg_1", "2026-10-08T12:01:00Z")] } });
72 const history = await g1tSurface(chat.binding, delivery).history(30);
73 assert.deepEqual(chat.calls[0].body, { workspace: "acme", channel_id: "chn_1", agent_id: "agt_ship", thread_root: "msg_0", limit: 30 });
74 assert.deepEqual(history.map((m) => m.id), ["msg_0"]);
75 assert.equal(history[0].author.name, "dana");
76});
77
78test("a failed post is an error the reply loop records; typing never throws", async () => {
79 const chat = fakeChat({ post_as_agent: { ok: false, error: { code: "forbidden", message: "not a member" } } });
80 await assert.rejects(g1tSurface(chat.binding, delivery).post("x"), /not a member/);
81 const broken = { fetch: async () => new Response("down", { status: 500 }) } as any;
82 await g1tSurface(broken, delivery).typing();
83});
84
85test("reactions: in channels and group DMs, never a one-person DM", async () => {
86 const { reactsIn } = await import("./surface.ts");
87 assert.equal(reactsIn("channel", 1), true);
88 assert.equal(reactsIn("dm", 2), true);
89 assert.equal(reactsIn("dm", 1), false);
90});
91
92test("👀 when the desk picks it up, then ✅ when it answered", async () => {
93 const chat = fakeChat({ react_as_agent: { ok: true, value: [] } });
94 const surface = g1tSurface(chat.binding, delivery);
95 await surface.acknowledge();
96 await surface.settle("done");
97 const reacts = chat.calls.filter((c) => c.method === "react_as_agent").map((c) => [c.body.emoji, c.body.remove, c.body.message_id]);
98 assert.deepEqual(reacts, [
99 ["👀", false, "msg_1"],
100 ["👀", true, "msg_1"],
101 ["✅", false, "msg_1"],
102 ]);
103});
104
105test("👀 is taken back after a notice or an apology", async () => {
106 const chat = fakeChat({ react_as_agent: { ok: true, value: [] } });
107 const surface = g1tSurface(chat.binding, delivery);
108 await surface.acknowledge();
109 await surface.settle("withdrawn");
110 assert.deepEqual(chat.calls.map((c) => [c.body.emoji, c.body.remove]), [["👀", false], ["👀", true]]);
111});
112
113test("a one-person DM gets no reactions; a group DM does", async () => {
114 const dm = { ...delivery, channel_kind: "dm" as const, channel_name: null };
115 const alone = fakeChat({ audience: { ok: true, value: { kind: "dm", member_user_ids: ["usr_dana"], member_count: 1 } } });
116 const quiet = g1tSurface(alone.binding, dm);
117 await quiet.acknowledge();
118 await quiet.settle("done");
119 assert.deepEqual(alone.calls.map((c) => c.method), ["audience"]);
120 const group = fakeChat({ audience: { ok: true, value: { kind: "dm", member_user_ids: ["usr_dana", "usr_bo"], member_count: 2 } }, react_as_agent: { ok: true, value: [] } });
121 const loud = g1tSurface(group.binding, dm);
122 await loud.acknowledge();
123 assert.deepEqual(group.calls.map((c) => c.method), ["audience", "react_as_agent"]);
124});
125
126const profile = (kind: "user" | "agent", id: string, name: string, display_name: string, title: string | null = null) => ({ kind, id, name, display_name, avatar: null, role: kind === "agent" ? "Does things" : null, title });
127const channel = (kind: "channel" | "dm", isPrivate: boolean, name: string | null) => ({ id: "chn_1", workspace_id: "wsp_1", kind, name, topic: null, private: isPrivate, created_by: { kind: "user", id: "usr_dana" }, created_at: "", archived_at: null, last_message_at: null });
128
129test("the conversation: what kind it is and who is in it, asked as the agent with who asked", async () => {
130 const dm = fakeChat({
131 conversation_for_agent: { ok: true, value: { channel: channel("dm", true, null), members: [profile("agent", "agt_ship", "ship", "Ship"), profile("user", "usr_dana", "dana", "Dana")], people: 1, agents: 1 } },
132 });
133 const one = await g1tSurface(dm.binding, delivery).conversation();
134 assert.deepEqual(dm.calls[0].body, { workspace: "acme", channel_id: "chn_1", agent_id: "agt_ship", asked_by: "usr_dana" });
135 assert.equal(one?.kind, "dm");
136 assert.deepEqual(one?.members.map((m) => [m.kind, m.name, m.title]), [["agent", "ship", "Does things"], ["user", "dana", null]]);
137 const group = fakeChat({ conversation_for_agent: { ok: true, value: { channel: channel("dm", true, null), members: [], people: 1, agents: 2 } } });
138 assert.equal((await g1tSurface(group.binding, delivery).conversation())?.kind, "group_dm");
139 const open = fakeChat({ conversation_for_agent: { ok: true, value: { channel: channel("channel", false, "releases"), members: [], people: 300, agents: 1 } } });
140 assert.deepEqual(await g1tSurface(open.binding, delivery).conversation(), { kind: "public_channel", name: "releases", members: [], people: 300, agents: 1 });
141 const broken = { fetch: async () => new Response("down", { status: 500 }) } as any;
142 assert.equal(await g1tSurface(broken, delivery).conversation(), null, "never throws");
143});
144
145test("a hand-off goes to chat with the delivery's chain, so the colleague works for the same person", async () => {
146 const chat = fakeChat({ hand_off_as_agent: { ok: true, value: { where: "group_dm", channel_id: "chn_9", message_id: "msg_9", opened: true } } });
147 const done = await g1tSurface(chat.binding, delivery).handOff("agt_mike", "@mike draft the role brief.");
148 assert.deepEqual(done, { ok: true, where: "group_dm", opened: true });
149 assert.equal(chat.calls[0].method, "hand_off_as_agent");
150 assert.deepEqual(chat.calls[0].body, {
151 workspace: "acme",
152 channel_id: "chn_1",
153 agent_id: "agt_ship",
154 hand_off: {
155 colleague_id: "agt_mike",
156 brief: "@mike draft the role brief.",
157 thread_root: "msg_0",
158 hops: 2,
159 asked_by: "usr_dana",
160 asker: { username: "dana", role: "member", can_write: false },
161 chain: ["agt_g1t"],
162 },
163 });
164 const refused = fakeChat({ hand_off_as_agent: { ok: false, error: { code: "invalid", message: "too many times" } } });
165 assert.deepEqual(await g1tSurface(refused.binding, delivery).handOff("agt_mike", "x"), { ok: false, message: "too many times" });
166});
167
168test("reacting that fails never throws, and nothing is taken back that never went on", async () => {
169 const broken = { fetch: async () => new Response("down", { status: 500 }) } as any;
170 const surface = g1tSurface(broken, delivery);
171 await surface.acknowledge();
172 await surface.settle("done");
173 const refused = fakeChat({ react_as_agent: { ok: false, error: { code: "forbidden", message: "not a member" } } });
174 const s2 = g1tSurface(refused.binding, delivery);
175 await s2.acknowledge();
176 await s2.settle("done");
177 assert.equal(refused.calls.length, 1, "no ✅ or removal after a refused 👀");
178});