Skip to content
96 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)1// g1t's service worker: browser notifications, nothing else. It caches
2// nothing and answers no requests, so pages load exactly as without it.
3// Pushes come from the notify service (services/notify), encrypted for
Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread4// this browser: { title, body, href, tag, kind, urgent, workspace, actions,
5// card }. A notification about a chat card (a session at its cap, a draft
6// issue) has buttons for the card's actions that need nothing typed: a
7// link opens its page; the others are pressed here, as on the card, by
8// posting `card_action` to the site, and the answer shows as a notification.
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)9
10self.addEventListener("install", () => self.skipWaiting());
11self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
12
13self.addEventListener("push", (event) => {
14 let data = {};
15 try {
16 data = event.data ? event.data.json() : {};
17 } catch {
18 data = { title: "g1t", body: event.data ? event.data.text() : "" };
19 }
20 const title = data.title || "g1t";
Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread21 const safe = (href) => (typeof href === "string" && href.startsWith("/") && !href.startsWith("//") ? href : null);
22 const actions = Array.isArray(data.actions)
23 ? data.actions
24 .filter((a) => a && typeof a.id === "string" && typeof a.label === "string")
25 .slice(0, 2)
26 .map((a) => ({ id: a.id, label: a.label, href: safe(a.href) }))
27 : [];
28 const card =
29 data.card && typeof data.card.channel_id === "string" && typeof data.card.message_id === "string" && typeof data.workspace === "string"
30 ? { workspace: data.workspace, channel_id: data.card.channel_id, message_id: data.card.message_id }
31 : null;
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)32 event.waitUntil(
33 self.registration.showNotification(title, {
34 body: data.body || "",
35 icon: "/icon-192.png",
36 badge: "/badge-96.png",
37 // One notification per conversation: a newer message replaces the last.
38 tag: data.tag || undefined,
39 renotify: Boolean(data.tag),
Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread40 actions: actions.map((a) => ({ action: a.id, title: a.label })),
41 data: { href: safe(data.href) || "/", actions, card },
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)42 }),
43 );
44});
45
Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread46/** Presses a card's action for the person, with their session, and says what happened. */
47async function pressCard(card, action, title) {
48 let message = `${action.label} didn't go through. Open g1t to try again.`;
49 try {
50 const response = await fetch(`/${encodeURIComponent(card.workspace)}/-/chat/api`, {
51 method: "POST",
52 credentials: "same-origin",
53 headers: { "content-type": "application/json" },
54 body: JSON.stringify({ intent: "card_action", channel_id: card.channel_id, message_id: card.message_id, action_id: action.id, input: null }),
55 });
56 const result = await response.json();
57 if (result && result.ok) message = result.value.message || (result.value.ok ? `${action.label}: done.` : `${action.label} didn't work.`);
58 else if (result && result.error && result.error.message) message = result.error.message;
59 } catch {
60 // Offline, or signed out: the message above says so.
61 }
62 await self.registration.showNotification(title || "g1t", { body: message, icon: "/icon-192.png", badge: "/badge-96.png", tag: `card:${card.message_id}` });
63}
64
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)65self.addEventListener("notificationclick", (event) => {
66 event.notification.close();
Write a thread up in Docs from chat; cards' buttons in notifications; a session's card stays at the top of its thread67 const data = event.notification.data || {};
68 const pressed = event.action && Array.isArray(data.actions) ? data.actions.find((a) => a.id === event.action) : null;
69 if (pressed && !pressed.href && data.card) {
70 event.waitUntil(pressCard(data.card, pressed, event.notification.title));
71 return;
72 }
73 const href = (pressed && pressed.href) || data.href || "/";
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)74 const target = new URL(href, self.location.origin).href;
75 event.waitUntil(
76 (async () => {
77 const open = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
78 // A tab already on g1t: brought forward and moved there.
79 const tab = open.find((client) => new URL(client.url).origin === self.location.origin);
80 if (tab) {
81 await tab.focus();
82 if ("navigate" in tab) {
83 try {
84 await tab.navigate(target);
85 return;
86 } catch {
87 // Not ours to navigate (uncontrolled): told instead.
88 }
89 }
90 tab.postMessage({ type: "g1t:navigate", href });
91 return;
92 }
93 await self.clients.openWindow(target);
94 })(),
95 );
96});

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