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

Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { MemberProfile } from "@g1t/contracts";
5
Chat, artifacts and notifications no longer serve g1t's own agents. Direct messages and conversation invites take people only, and a space or folio is shared with people and teams. Chat stops waking agents, drops the RPCs agents posted, read, searched and handed off through, and stops answering buttons on agent cards; old cards keep their links. An agent's old messages still show, under its saved name or as "A deleted agent". Artifacts drops the folio and docs RPCs agents read and edited pages with, and an old agent member of a space can still be removed. Notifications drop "an agent is waiting on you". A deployment no longer trusts a pull request with secrets just because g1t's agent opened it, and the sign-up card no longer promises a team of agents.6import { conversationHref, countsAfterRead, messageDeliveries, preview, recipients, type Person } from "./notify.ts";
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)7
8const person = (id: string, username: string, muted = false): Person => ({ key: `user:${id}`, user_id: id, username, muted });
9const ana = person("u1", "ana");
10const bo = person("u2", "bo");
11const cy = person("u3", "cy", true);
12
13const kinds = (list: ReturnType<typeof recipients>) => Object.fromEntries(list.map((r) => [r.user_id, r.kind]));
14
15test("a direct message notifies everyone else in it, muted or not, never the author", () => {
16 const out = recipients({ author: "user:u1", channelKind: "dm", people: [ana, bo, cy], mentioned: [], thread: null });
17 assert.deepEqual(kinds(out), { u2: "dm", u3: "dm" });
18});
19
20test("in a channel, a mention notifies the person named even when muted; the rest only count", () => {
21 const out = recipients({ author: "user:u1", channelKind: "channel", people: [ana, bo, cy], mentioned: ["CY"], thread: null });
22 assert.deepEqual(kinds(out), { u2: null, u3: "mention" });
23 assert.deepEqual(
24 out.map((r) => [r.user_id, r.mentioned, r.muted]),
25 [
26 ["u2", false, false],
27 ["u3", true, true],
28 ],
29 );
30});
31
32test("a reply notifies the people in its thread unless they muted the conversation", () => {
33 const out = recipients({ author: "user:u1", channelKind: "channel", people: [ana, bo, cy], mentioned: [], thread: new Set(["user:u1", "user:u2", "user:u3"]) });
34 assert.deepEqual(kinds(out), { u2: "thread_reply", u3: null });
35});
36
37test("deliveries: counts for everyone, a notification for those it is for, the author's count cleared", () => {
38 const author: MemberProfile = { kind: "user", id: "u1", name: "ana", display_name: "Ana Lima", avatar: "abc", role: null, avatar_seed: null };
39 const items = messageDeliveries({
40 slug: "acme",
41 channel: { id: "chn_1", kind: "channel", name: "design" },
Chat, artifacts and notifications no longer serve g1t's own agents. Direct messages and conversation invites take people only, and a space or folio is shared with people and teams. Chat stops waking agents, drops the RPCs agents posted, read, searched and handed off through, and stops answering buttons on agent cards; old cards keep their links. An agent's old messages still show, under its saved name or as "A deleted agent". Artifacts drops the folio and docs RPCs agents read and edited pages with, and an old agent member of a space can still be removed. Notifications drop "an agent is waiting on you". A deployment no longer trusts a pull request with secrets just because g1t's agent opened it, and the sign-up card no longer promises a team of agents.42 message: { id: "msg_1", author: "user:u1", body: "hey **@bo** look", thread_root: null, created_at: "2026-10-08T00:00:00Z" },
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)43 author,
44 recipients: recipients({ author: "user:u1", channelKind: "channel", people: [ana, bo, cy], mentioned: ["bo"], thread: null }),
45 });
46 assert.equal(items.length, 3);
47 const [toBo, toCy, self] = items;
48 assert.deepEqual(toBo.counts, { channel_id: "chn_1", unread: 1, mentions: 1, muted: false });
49 assert.equal(toBo.notification?.kind, "mention");
50 assert.equal(toBo.notification?.title, "Ana Lima in #design");
51 assert.equal(toBo.notification?.body, "hey @bo look");
52 assert.equal(toBo.notification?.href, "/acme/-/chat/design");
53 assert.equal(toBo.notification?.actor.avatar, "abc");
54 assert.deepEqual(toCy.counts, { channel_id: "chn_1", unread: 1, mentions: 0, muted: true });
55 assert.equal(toCy.notification, null);
56 assert.deepEqual(self, { user_id: "u1", workspace: "acme", counts: { channel_id: "chn_1", unread: 0, mentions: 0, set: true }, notification: null });
57});
58
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar59test("a person without a display name is named by their username as they wrote it", () => {
60 const author: MemberProfile = { kind: "user", id: "u1", name: "ana", display_username: "Ana", display_name: "", avatar: null, role: null, avatar_seed: null };
61 const [toBo] = messageDeliveries({
62 slug: "acme",
63 channel: { id: "chn_9", kind: "dm", name: null },
64 message: { id: "msg_2", author: "user:u1", body: "hi", card_title: null, thread_root: null, created_at: "2026-10-08T00:00:00Z" },
65 author,
66 recipients: recipients({ author: "user:u1", channelKind: "dm", people: [ana, bo], mentioned: [], thread: null }),
67 });
68 assert.equal(toBo.notification?.title, "Ana");
69 assert.equal(toBo.notification?.actor.name, "Ana");
70});
71
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)72test("a reply links to its thread; a DM to the DM", () => {
73 assert.equal(conversationHref("acme", { id: "chn_9", kind: "dm", name: null }, null), "/acme/-/chat/dm/chn_9");
74 assert.equal(conversationHref("acme", { id: "chn_1", kind: "channel", name: "ops" }, "msg_1"), "/acme/-/chat/ops?thread=msg_1");
75});
76
77test("previews are one short line without markup", () => {
78 assert.equal(preview("**bold** and `code`\n\nnext [link](https://x)"), "bold and code next link");
Merge main into Artifacts Phase 279 assert.equal(preview("```\nlong code\n```"), "long code");
80 assert.equal(preview("## Done\n- shipped **#412** in #web\n- see \\*notes\\*"), "Done shipped #412 in #web see *notes*");
81 assert.equal(preview("snake_case and ~/src stay, <b>as typed</b>"), "snake_case and ~/src stay, <b>as typed</b>");
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)82 assert.equal(preview("x".repeat(300)).length, 140);
83});
84
85test("counts after a read: what is left past the mark, mentions included, never your own", () => {
86 const rows = [
87 { channel_id: "c", id: "m3", author: "user:u2", mentions: " ana " },
88 { channel_id: "c", id: "m4", author: "user:u1", mentions: "" },
89 { channel_id: "c", id: "m5", author: "agent:a1", mentions: "" },
90 ];
91 assert.deepEqual(countsAfterRead(rows, "c", "user:u1", "ana", "m2"), { unread: 2, mentions: 1 });
92 assert.deepEqual(countsAfterRead([], "c", "user:u1", "ana", "m9"), { unread: 0, mentions: 0 });
Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread93});
94

This file's history is long; its oldest lines are credited to the oldest commit read.