Skip to content
222 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 has sounds. A short warm cue plays for a message in a conversation you have open but aren't looking at, a direct message, a mention of you and an agent finishing something for you, with a soft tick for sending that is off until you turn it on; never for your own messages, a muted conversation, a conversation you are looking at in a window that has the front, or while you have paused notifications, and never twice for one message or more than once in a second and a half. The cues are made by the browser itself in two sets, Soft and Bright, so nothing is downloaded; the Sounds and notifications section of your notification settings turns them on and off, picks the set and the volume, plays each one, and offers desktop notifications for when the window is behind; Do not disturb is the one pause everyone sees, set from the chat sidebar's bell, the settings, or your avatar menu, now with a two-hour choice, and the top bar's bell shows a dot while it's on. Your choices are kept with your account. The sound layer sits behind one small interface so the desktop app can play them natively later. The chat and notifications guides say the rules.1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import type { ChatMessage, FeedNotification } from "@g1t/contracts";
5import { DEFAULT_SOUND_SETTINGS, type SoundSettings } from "@g1t/contracts/sounds";
6
7import {
8 type Arrival,
9 SAME_MESSAGE_MS,
10 SOUND_GAP_MS,
11 type SoundContext,
12 SoundGate,
13 arrivalKey,
14 cueFor,
15 cueForNotificationKind,
16 finishedCard,
17 lookingAt,
18 mentionsMe,
19 wantsDesktopToast,
20} from "./chat-sounds.ts";
21
22const ana = { kind: "user" as const, id: "u1", name: "ana", display_name: "Ana", avatar: null, role: null };
23const bea = { kind: "user" as const, id: "u2", name: "bea", display_name: "Bea", avatar: null, role: null };
24const margo = { kind: "agent" as const, id: "a1", name: "margo", display_name: "Margo", avatar: null, role: "Reviews pull requests" };
25
26function message(over: Partial<ChatMessage> = {}): ChatMessage {
27 return {
28 id: over.id ?? "msg_1",
29 channel_id: "chn_general",
30 author: bea,
31 kind: "text",
32 body: "hello there",
33 card: null,
34 thread_root: null,
35 reply_count: 0,
36 last_reply_at: null,
37 created_at: "2026-10-10T10:00:00.000Z",
38 edited_at: null,
39 deleted_at: null,
40 ...over,
41 };
42}
43
44function notification(over: Partial<FeedNotification> = {}): FeedNotification {
45 return {
46 id: "msg_9",
47 kind: "dm",
48 workspace: "acme",
49 title: "Bea",
50 body: "hi",
51 href: "/acme/-/chat/dm/chn_dm",
52 actor: { kind: "user", id: "u2", name: "Bea" },
53 channel_id: "chn_dm",
54 created_at: "2026-10-10T10:00:00.000Z",
55 ...over,
56 };
57}
58
59/** Ana, in #general, window in front: the quiet case every rule starts from. */
60function ctx(over: Partial<SoundContext> = {}, settings: Partial<SoundSettings> = {}): SoundContext {
61 return {
62 me: { id: "u1", username: "ana" },
63 focus: { visible: true, focused: true, viewingChannel: "chn_general" },
64 muted: new Set(),
65 dnd: false,
66 settings: { ...DEFAULT_SOUND_SETTINGS, ...settings, sound_cues: { ...DEFAULT_SOUND_SETTINGS.sound_cues, ...settings.sound_cues } },
67 ...over,
68 };
69}
70
71const chat = (m: ChatMessage, channelKind: "channel" | "dm" = "channel"): Arrival => ({ source: "chat", message: m, channelKind });
72const feed = (n: FeedNotification): Arrival => ({ source: "feed", notification: n });
73
74test("a message in the conversation on screen, in a window in front, is silent", () => {
75 assert.equal(cueFor(chat(message()), ctx()), null);
76 assert.ok(lookingAt("chn_general", ctx().focus));
77});
78
79test("the same message plays `message` once the window is not in front, or the tab is hidden", () => {
80 assert.equal(cueFor(chat(message()), ctx({ focus: { visible: true, focused: false, viewingChannel: "chn_general" } })), "message");
81 assert.equal(cueFor(chat(message()), ctx({ focus: { visible: false, focused: false, viewingChannel: "chn_general" } })), "message");
82});
83
84test("a message in another conversation you have open plays `message`", () => {
85 assert.equal(cueFor(chat(message({ channel_id: "chn_other" })), ctx()), "message");
86 assert.equal(cueFor(feed(notification({ kind: "thread_reply", channel_id: "chn_other" })), ctx()), "message");
87});
88
89test("a direct message not from you plays `direct`; one from you plays nothing", () => {
90 const dm = message({ channel_id: "chn_dm" });
91 assert.equal(cueFor(chat(dm, "dm"), ctx()), "direct");
92 assert.equal(cueFor(chat({ ...dm, author: ana }, "dm"), ctx()), null);
93 assert.equal(cueFor(feed(notification()), ctx()), "direct");
94 assert.equal(cueFor(feed(notification({ actor: { kind: "user", id: "u1", name: "Ana" } })), ctx()), null);
95});
96
97test("a mention of you plays `mention`, over `direct` and `message`", () => {
98 assert.equal(cueFor(chat(message({ channel_id: "chn_other", body: "hey @ana look" })), ctx()), "mention");
99 assert.equal(cueFor(chat(message({ channel_id: "chn_dm", body: "@ana?" }), "dm"), ctx()), "mention");
100 assert.equal(cueFor(feed(notification({ kind: "mention", channel_id: "chn_other" })), ctx()), "mention");
101 // Looking at it, in front: still silent.
102 assert.equal(cueFor(chat(message({ body: "@ana" })), ctx()), null);
103});
104
105test("@you is a whole handle, outside code", () => {
106 assert.ok(mentionsMe("thanks @ana", "ana"));
107 assert.ok(mentionsMe("@Ana: see this", "ana"));
108 assert.ok(mentionsMe("(@ana)", "ana"));
109 assert.ok(!mentionsMe("@ana-marie did it", "ana"));
110 assert.ok(!mentionsMe("@anatole", "ana"));
111 assert.ok(!mentionsMe("mail me@ana.example", "ana"));
112 assert.ok(!mentionsMe("run `@ana` in code", "ana"));
113 assert.ok(!mentionsMe("```\n@ana\n```", "ana"));
114 assert.ok(!mentionsMe("@ana", ""));
115});
116
117test("your own messages never sound, whichever way they arrive", () => {
118 const unfocused = ctx({ focus: { visible: true, focused: false, viewingChannel: "chn_general" } });
119 assert.equal(cueFor(chat(message({ author: ana })), unfocused), null);
120 assert.equal(cueFor(chat(message({ author: ana, body: "@ana" })), unfocused), null);
121 assert.equal(cueFor(feed(notification({ kind: "mention", actor: { kind: "user", id: "u1", name: "Ana" } })), unfocused), null);
122});
123
124test("a muted conversation is silent, even for a mention or a DM", () => {
125 const muted = ctx({ muted: new Set(["chn_other", "chn_dm"]) });
126 assert.equal(cueFor(chat(message({ channel_id: "chn_other", body: "@ana" })), muted), null);
127 assert.equal(cueFor(chat(message({ channel_id: "chn_dm" }), "dm"), muted), null);
128 assert.equal(cueFor(feed(notification()), muted), null);
129 assert.equal(cueFor(chat(message({ channel_id: "chn_third" })), muted), "message");
130});
131
132test("Do not disturb silences everything", () => {
133 const quiet = ctx({ dnd: true, focus: { visible: false, focused: false, viewingChannel: null } });
134 assert.equal(cueFor(chat(message({ body: "@ana" })), quiet), null);
135 assert.equal(cueFor(chat(message({ channel_id: "chn_dm" }), "dm"), quiet), null);
136 assert.equal(cueFor(feed(notification({ kind: "agent_waiting" })), quiet), null);
137});
138
139test("sounds off, or one cue off, is silent for that", () => {
140 const away = { visible: false, focused: false, viewingChannel: null };
141 assert.equal(cueFor(chat(message()), ctx({ focus: away }, { sounds_enabled: false })), null);
142 assert.equal(cueFor(chat(message()), ctx({ focus: away }, { sound_cues: { message: false } })), null);
143 assert.equal(cueFor(chat(message({ body: "@ana" })), ctx({ focus: away }, { sound_cues: { message: false } })), "mention");
144 assert.equal(cueFor(chat(message({ body: "@ana" })), ctx({ focus: away }, { sound_cues: { mention: false } })), null);
145});
146
147test("agents' messages count like people's, and a finished card plays `agent_done`", () => {
148 const away = ctx({ focus: { visible: true, focused: false, viewingChannel: "chn_general" } });
149 assert.equal(cueFor(chat(message({ author: margo })), away), "message");
150 assert.equal(cueFor(chat(message({ author: margo, channel_id: "chn_dm" }), "dm"), away), "direct");
151 assert.equal(cueFor(chat(message({ author: margo, body: "@ana done?" })), away), "mention");
152 const card = message({
153 author: margo,
154 kind: "card",
155 body: "",
156 card: { kind: "session", title: "Fix the flaky test", detail: "2 files changed", state: "Done", href: "/acme/web/pull/4" },
157 });
158 assert.ok(finishedCard(card));
159 assert.equal(cueFor(chat(card), away), "agent_done");
160 assert.equal(cueFor(chat(card), ctx()), null, "looking at it: silent");
161 assert.equal(cueFor(chat(card), ctx({ focus: away.focus }, { sound_cues: { agent_done: false } })), null);
162 // A card still running, or a person's card, is a message.
163 assert.ok(!finishedCard({ ...card, card: { ...card.card!, state: "Running" } }));
164 assert.ok(!finishedCard({ ...card, author: bea }));
165 assert.equal(cueFor(chat({ ...card, card: { ...card.card!, state: "Running" } }), away), "message");
166 // An agent waiting on you, over the feed, is the same cue.
167 assert.equal(cueFor(feed(notification({ kind: "agent_waiting", actor: { kind: "agent", id: "a1", name: "Margo" } })), away), "agent_done");
168});
169
170test("a deleted message, and an inbox notification, make no sound", () => {
171 const away = ctx({ focus: { visible: false, focused: false, viewingChannel: null } });
172 assert.equal(cueFor(chat(message({ deleted_at: "2026-10-10T10:01:00.000Z" })), away), null);
173 assert.equal(cueFor(feed(notification({ kind: "inbox", channel_id: null })), away), null);
174 assert.equal(cueForNotificationKind("inbox"), null);
175 assert.equal(cueForNotificationKind("approval"), "mention");
176});
177
178test("sounds come at most one every 1.5 s, and one message sounds once however it arrives", () => {
179 const gate = new SoundGate();
180 const t0 = 1_000_000;
181 assert.ok(gate.allow("msg:1", t0));
182 assert.ok(!gate.allow("msg:2", t0 + 200), "a burst is one sound");
183 assert.ok(!gate.allow("msg:3", t0 + SOUND_GAP_MS - 1));
184 assert.ok(gate.allow("msg:4", t0 + SOUND_GAP_MS));
185 // The same message, from the conversation's socket and then the feed.
186 assert.ok(!gate.allow("msg:4", t0 + SOUND_GAP_MS * 3));
187 assert.ok(!gate.allow("msg:4", t0 + SOUND_GAP_MS + SAME_MESSAGE_MS - 1));
188 assert.ok(gate.allow("msg:4", t0 + SOUND_GAP_MS + SAME_MESSAGE_MS + 1), "long after, it is a new message to the gate");
189 // A key refused by the gap is still remembered, so the feed's copy a moment later stays quiet.
190 const again = new SoundGate();
191 assert.ok(again.allow("msg:a", t0));
192 assert.ok(!again.allow("msg:b", t0 + 100));
193 assert.ok(!again.allow("msg:b", t0 + SOUND_GAP_MS + 100), "msg:b was heard of, if not sounded");
194 // No key: only the gap applies.
195 assert.ok(again.allow(null, t0 + SOUND_GAP_MS * 2));
196 assert.ok(!again.allow(null, t0 + SOUND_GAP_MS * 2 + 10));
197});
198
199test("an arrival's key is its message id, the same over the socket and the feed", () => {
200 assert.equal(arrivalKey(chat(message({ id: "msg_7" }))), "msg:msg_7");
201 assert.equal(arrivalKey(feed(notification({ id: "msg_7" }))), "msg:msg_7");
202});
203
204test("a desktop notification shows only when wanted, allowed, out of front, and not already pushed", () => {
205 const base = {
206 notification: { kind: "dm" as const },
207 toast: true,
208 ctx: ctx({ focus: { visible: true, focused: false, viewingChannel: null } }, { desktop_toasts: true }),
209 permission: "granted" as const,
210 pushOn: false,
211 };
212 assert.ok(wantsDesktopToast(base));
213 assert.ok(!wantsDesktopToast({ ...base, ctx: ctx({ focus: base.ctx.focus }) }), "off unless turned on");
214 assert.ok(!wantsDesktopToast({ ...base, permission: "default" }));
215 assert.ok(!wantsDesktopToast({ ...base, permission: "denied" }));
216 assert.ok(!wantsDesktopToast({ ...base, pushOn: true }), "the push shows it instead");
217 assert.ok(!wantsDesktopToast({ ...base, toast: false }), "what the feed would not toast");
218 assert.ok(!wantsDesktopToast({ ...base, ctx: { ...base.ctx, dnd: true } }));
219 assert.ok(!wantsDesktopToast({ ...base, ctx: { ...base.ctx, focus: { visible: true, focused: true, viewingChannel: null } } }), "in front: the toast is enough");
220 assert.ok(wantsDesktopToast({ ...base, ctx: { ...base.ctx, focus: { visible: false, focused: false, viewingChannel: null } } }));
221 assert.ok(!wantsDesktopToast({ ...base, notification: { kind: "inbox" } }));
222});