@gorlitzer-labs/apiary 1.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +328 -0
- package/dist/agent/index.d.ts +689 -0
- package/dist/agent/index.js +34 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/chunk-5V7QLCGB.js +184 -0
- package/dist/chunk-5V7QLCGB.js.map +1 -0
- package/dist/chunk-AYQJIAR5.js +152 -0
- package/dist/chunk-AYQJIAR5.js.map +1 -0
- package/dist/chunk-BZ4VDWC2.js +76 -0
- package/dist/chunk-BZ4VDWC2.js.map +1 -0
- package/dist/chunk-CFY6Z6Q6.js +95 -0
- package/dist/chunk-CFY6Z6Q6.js.map +1 -0
- package/dist/chunk-CZHO4DBN.js +1719 -0
- package/dist/chunk-CZHO4DBN.js.map +1 -0
- package/dist/chunk-D7DXZQWT.js +208 -0
- package/dist/chunk-D7DXZQWT.js.map +1 -0
- package/dist/chunk-ED6ROH6B.js +2021 -0
- package/dist/chunk-ED6ROH6B.js.map +1 -0
- package/dist/chunk-PEB6HEFT.js +606 -0
- package/dist/chunk-PEB6HEFT.js.map +1 -0
- package/dist/chunk-QL66XSDB.js +157 -0
- package/dist/chunk-QL66XSDB.js.map +1 -0
- package/dist/chunk-TPNFOQA7.js +288 -0
- package/dist/chunk-TPNFOQA7.js.map +1 -0
- package/dist/chunk-ZEFRNAX6.js +1771 -0
- package/dist/chunk-ZEFRNAX6.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +2432 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index-Dp3QSGog.d.ts +837 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/invites-OFXIQLLN.js +23 -0
- package/dist/invites-OFXIQLLN.js.map +1 -0
- package/dist/join-CQXEF65B.js +15 -0
- package/dist/join-CQXEF65B.js.map +1 -0
- package/dist/room-Z7KDRVYD.js +1099 -0
- package/dist/room-Z7KDRVYD.js.map +1 -0
- package/dist/run-DOADB4T7.js +90 -0
- package/dist/run-DOADB4T7.js.map +1 -0
- package/dist/update-JGE3WCQB.js +11 -0
- package/dist/update-JGE3WCQB.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/join.ts","../src/cli/tui.tsx","../src/cli/watch.ts"],"sourcesContent":["/**\n * apiary join — connect to a room as a human participant.\n *\n * Opens the TUI and connects to a apiary server over HTTP.\n * Events stream in via SSE, messages sent via POST /message.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { createInterface } from \"node:readline\";\nimport { randomName } from \"../core/names.js\";\nimport { loadConfig, saveConfig, roomEmoji } from \"./config.js\";\nimport type { RoomEvent } from \"../core/events.js\";\nimport type { AuthorityLevel } from \"../core/types.js\";\n\n/** Format a Date as local HH:MM:SS for TUI display. */\nfunction formatTimestamp(date: Date): string {\n const h = String(date.getHours()).padStart(2, \"0\");\n const m = String(date.getMinutes()).padStart(2, \"0\");\n const s = String(date.getSeconds()).padStart(2, \"0\");\n return `${h}:${m}:${s}`;\n}\nimport { startTUI, type TUIHandle, type DisplayEvent } from \"./tui.js\";\nimport { extractToken, buildShareUrl } from \"./auth.js\";\nimport { can } from \"../core/authority.js\";\nimport { tmuxSessionExists, tmuxCapturePane, tmuxInjectText, tmuxSendEnter } from \"./tmux.js\";\nimport { listRoomSessions, saveRoomSession } from \"./serve.js\";\nimport { listAgentSessions, isAgentAlive } from \"./agent-session.js\";\nimport { openAgentWatcher } from \"./watch.js\";\n\n/**\n * Update one participant's persisted model in ~/.apiary/sessions/room_X.json.\n * Used by /model so the swap survives `apiary room resume`. No-op when the\n * room or participant isn't found locally (e.g. user is joined to a remote\n * room they didn't create).\n */\n/**\n * Printed to stderr by `apiary join --headless` once its event stream is live.\n * Scripted callers wait for this instead of sleeping and hoping.\n */\nexport const HEADLESS_READY = \"apiary: ready\";\n\nfunction persistAgentModel(roomName: string, alias: string, model: string): void {\n const session = listRoomSessions().find((s) => s.roomName === roomName);\n if (!session?.participants) return;\n const p = session.participants.find((x) => x.alias === alias);\n if (!p) return;\n p.model = model;\n saveRoomSession(session);\n}\n\n/**\n * Send a slash command (e.g. \"/clear\", \"/model sonnet\") into an agent's\n * claude tmux pane. Uses inject-then-Enter with a second Enter as a safety\n * net for claude's paste detection (same pattern as TmuxBridge.injectIdle).\n */\nfunction sendSlashToAgent(session: string, slashCommand: string): void {\n tmuxInjectText(session, slashCommand);\n tmuxSendEnter(session);\n // Sleep ~80ms then re-send Enter — see tmux-bridge.ts injectIdle for why\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 80);\n tmuxSendEnter(session);\n}\n\nexport interface JoinOptions {\n server: string;\n name?: string;\n guest?: boolean;\n /** Share URL to display before TUI starts (for host+join mode with --share). */\n shareUrl?: string;\n /** Skip TUI — stream events as JSON to stdout, read messages from stdin. */\n headless?: boolean;\n /** Agent names the wizard spawned but who haven't connected yet. Shown as\n * `⏳ booting` in the footer until ParticipantJoined fires for each, then\n * flips to `⚠ stalled` after 30s if they never show up. */\n expectedAgents?: string[];\n}\n\n/**\n * The tmux session for a locally-started agent, or null.\n *\n * Agent names in a room are display names chosen at join time; the tmux\n * session is keyed on the alias the runtime was started with. They match by\n * convention (the wizard uses one string for both), so compare\n * case-insensitively and fall back to the naming scheme.\n */\nfunction findAgentTmuxSession(agentName: string): string | null {\n const wanted = agentName.toLowerCase();\n for (const runtime of [\"claude\", \"codex\"] as const) {\n for (const s of listAgentSessions(runtime)) {\n if (s.agentName.toLowerCase() !== wanted) continue;\n if (!isAgentAlive(s)) return null;\n return s.tmuxSession ?? `apiary_${s.agentName}`;\n }\n }\n return null;\n}\n\nexport async function join(options: JoinOptions): Promise<void> {\n // Extract token from URL if present\n const token = extractToken(options.server);\n // Strip query params to get clean server URL\n let serverUrl: string;\n try {\n const parsed = new URL(options.server);\n parsed.search = \"\";\n serverUrl = parsed.toString().replace(/\\/$/, \"\");\n } catch {\n serverUrl = options.server.replace(/\\/$/, \"\");\n }\n\n const name = options.name ?? randomName();\n const isGuest = options.guest ?? false;\n\n // ── Register with server ────────────────────────────────────────────────\n\n let sessionToken: string;\n let participantId: string;\n let roomName: string;\n let authority: AuthorityLevel;\n let participants: Array<{ id: string; name: string; type: string; authority?: string }>;\n\n try {\n const joinBody: Record<string, unknown> = {};\n if (token) {\n joinBody.token = token;\n joinBody.type = \"human\";\n joinBody.name = name;\n } else if (isGuest) {\n joinBody.type = \"guest\";\n } else {\n joinBody.type = \"human\";\n joinBody.name = name;\n }\n\n const res = await fetch(`${serverUrl}/join`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(joinBody),\n });\n\n if (!res.ok) {\n const err = await res.text();\n console.error(`Failed to join: ${err}`);\n process.exit(1);\n }\n\n const data = await res.json() as Record<string, unknown>;\n sessionToken = String(data.sessionToken ?? \"\");\n participantId = String(data.participantId);\n roomName = String(data.roomName);\n authority = (data.authority as AuthorityLevel) ?? \"member\";\n participants = (data.participants as Array<{ id: string; name: string; type: string; authority?: string }>) ?? [];\n } catch (err) {\n console.error(`Cannot reach apiary server at ${serverUrl}. Is it running?`);\n console.error(` Error: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n\n // ── Disconnect helper ───────────────────────────────────────────────────\n\n let disconnected = false;\n let cleanupStream: (() => void) | null = null;\n const disconnect = async () => {\n if (disconnected) return;\n disconnected = true;\n cleanupStream?.();\n try {\n await fetch(`${serverUrl}/disconnect`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${sessionToken}` },\n body: JSON.stringify({}),\n });\n } catch {\n // Server may be down\n }\n };\n\n // ── Headless mode — skip TUI, stream events as JSON, read messages from stdin ──\n\n if (options.headless) {\n const sseController = new AbortController();\n const cleanup = async () => {\n sseController.abort();\n await disconnect();\n };\n\n process.on(\"SIGINT\", async () => { await cleanup(); process.exit(0); });\n process.on(\"SIGTERM\", async () => { await cleanup(); process.exit(0); });\n\n // Read messages from stdin and send them\n const rl = createInterface({ input: process.stdin, terminal: false });\n rl.on(\"line\", async (line) => {\n const content = line.trim();\n if (!content || authority === \"guest\") return;\n try {\n await fetch(`${serverUrl}/message`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${sessionToken}` },\n body: JSON.stringify({ content }),\n });\n } catch { /* server may be down */ }\n });\n\n // Stream events from SSE and write as JSON lines to stdout\n try {\n const res = await fetch(`${serverUrl}/events`, {\n method: \"POST\",\n headers: { Accept: \"text/event-stream\", Authorization: `Bearer ${sessionToken}` },\n signal: sseController.signal,\n });\n\n if (!res.ok || !res.body) {\n process.stderr.write(\"Failed to connect event stream\\n\");\n await cleanup();\n process.exit(1);\n }\n\n // Readiness signal for scripted callers. The server registers an SSE\n // connection in the same tick it flushes the response headers, so by the\n // time this fetch has resolved every subsequent broadcast will reach us.\n // Without a signal a caller can only guess with a sleep, and anything\n // the room broadcasts before the stream is live is missed forever.\n // stdout is reserved for events, so this goes to stderr.\n process.stderr.write(`${HEADLESS_READY} ${roomName}\\n`);\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n const parts = buf.split(\"\\n\\n\");\n buf = parts.pop()!;\n for (const part of parts) {\n const dataLine = part.split(\"\\n\").find((l) => l.startsWith(\"data: \"));\n if (!dataLine) continue;\n try {\n const event = JSON.parse(dataLine.slice(6));\n process.stdout.write(JSON.stringify(event) + \"\\n\");\n } catch { /* malformed */ }\n }\n }\n } catch {\n // Stream ended or aborted\n }\n\n if (!disconnected) { await cleanup(); }\n process.exit(0);\n }\n\n // ── Start TUI ───────────────────────────────────────────────────────────\n\n const isReadOnly = authority === \"guest\" || isGuest;\n\n // ── Slash command helper ──────────────────────────────────────────────\n\n function systemEvent(content: string): void {\n tui.push({\n id: randomUUID(),\n ts: formatTimestamp(new Date()),\n kind: \"system\",\n content,\n });\n }\n\n /** Resolve a participant by name via GET /participants. Emits a system\n * message and returns null on any failure (bad list, not found, network). */\n async function resolveParticipant(name: string): Promise<{ id: string; name: string } | null> {\n try {\n const res = await fetch(`${serverUrl}/participants`, { headers: { Authorization: `Bearer ${sessionToken}` } });\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return null; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string }> };\n const target = data.participants.find((p) => p.name.toLowerCase() === name.toLowerCase());\n if (!target) { systemEvent(`Participant \"${name}\" not found.`); return null; }\n return target;\n } catch {\n systemEvent(\"Failed to reach server.\");\n return null;\n }\n }\n\n /** POST a participant action to the server and report the outcome. */\n async function participantAction(\n opts: { path: string; body: Record<string, unknown>; failVerb: string; success: string },\n ): Promise<void> {\n try {\n const res = await fetch(`${serverUrl}${opts.path}`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${sessionToken}` },\n body: JSON.stringify(opts.body),\n });\n if (!res.ok) { systemEvent(`Failed to ${opts.failVerb}: ${await res.text()}`); return; }\n systemEvent(opts.success);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n }\n\n async function handleSlashCommand(input: string): Promise<void> {\n const parts = input.slice(1).split(/\\s+/);\n const cmd = parts[0]?.toLowerCase();\n const args = parts.slice(1);\n\n switch (cmd) {\n // ── /help ─────────────────────────────────────────────────────\n case \"help\": {\n const lines = [\n \"/who list participants\",\n \"/ping <name> ping for a status check\",\n \"/peek <name> show how to view an agent's tmux session\",\n \"/share generate share links\",\n \"/sound toggle notification sounds\",\n \"/leave disconnect\",\n ];\n if (authority === \"product_owner\") {\n lines.push(\n \"\",\n \"/mute <name> demote to guest\",\n \"/unmute <name> restore to member\",\n \"/setmode <n> <m> set engagement mode\",\n );\n }\n if (authority === \"admin\") {\n lines.push(\n \"\",\n \"/promote <name> promote to product owner\",\n \"/demote <name> demote to member\",\n \"/kick <name> remove a participant\",\n \"/mute <name> demote to guest\",\n \"/unmute <name> restore to member\",\n \"/setmode <n> <m> set engagement mode\",\n \"/clear wipe room history\",\n \"/clear <name> clear an agent's claude context\",\n \"/model <n> <id> swap an agent's model (sonnet/opus/haiku)\",\n \"/tunnel start cloudflared tunnel\",\n );\n }\n systemEvent(lines.join(\"\\n\"));\n return;\n }\n\n // ── /watch ────────────────────────────────────────────────────\n // Opens the agent's tmux session in a NEW terminal window. The room TUI\n // is untouched and stays usable — this attaches a second client rather\n // than moving the agent's pane anywhere.\n case \"watch\": {\n const target = args[0];\n if (!target) {\n systemEvent(\"Usage: /watch <agent> [--control] (read-only unless --control)\");\n return;\n }\n const control = args.includes(\"--control\");\n const session = findAgentTmuxSession(target);\n if (!session) {\n systemEvent(\n `No local tmux session for \"${target}\". ` +\n `/watch only works for agents started on this machine by apiary claude/codex.`,\n );\n return;\n }\n const res = openAgentWatcher(session, { control });\n systemEvent(\n res.ok\n ? `Opened ${target} in a new ${res.terminal} window (${res.readOnly ? \"read-only\" : \"INTERACTIVE — your keystrokes go to the agent\"}).`\n : `Could not open a window for ${target}: ${res.reason}`,\n );\n return;\n }\n\n // ── /who ──────────────────────────────────────────────────────\n case \"who\": {\n try {\n const res = await fetch(`${serverUrl}/participants`, { headers: { Authorization: `Bearer ${sessionToken}` } });\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string; type: string; authority?: string }> };\n const lines = data.participants.map((p) => {\n const auth = p.authority ?? \"member\";\n const label = auth === \"product_owner\" ? \"owner\" : auth;\n return ` ${p.type === \"agent\" ? \"agent\" : \"human\"} ${p.name} (${label})`;\n });\n systemEvent(`Participants:\\n${lines.join(\"\\n\")}`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /peek ─────────────────────────────────────────────────────\n // Capture a snapshot of the agent's tmux pane and render it inline.\n // For sustained interactive viewing the user can still `tmux attach`\n // (hint printed below), but the common case — \"what is this agent\n // showing right now?\" — is answered with a single keystroke.\n case \"peek\": {\n const name = args[0];\n if (!name) { systemEvent(\"Usage: /peek <name>\"); return; }\n const session = `apiary_${name}`;\n if (!tmuxSessionExists(session)) {\n systemEvent(\n `No local tmux session for \"${name}\" (apiary_${name} not found).\\n` +\n `This is normal for agents on another host — peek only works for ` +\n `local same-host agents.`,\n );\n return;\n }\n const lines = tmuxCapturePane(session);\n // Strip trailing blank rows so we don't waste vertical space\n let lastIdx = lines.length - 1;\n while (lastIdx >= 0 && lines[lastIdx].trim() === \"\") lastIdx--;\n const tail = lines.slice(Math.max(0, lastIdx - 24), lastIdx + 1);\n if (tail.length === 0) {\n systemEvent(`Peek ${name}: pane is empty.`);\n return;\n }\n systemEvent(\n `Peek ${name} (last ${tail.length} lines):\\n` +\n tail.map((l) => \"│ \" + l).join(\"\\n\") + \"\\n\" +\n `For interactive view: detach apiary (Ctrl+C ×2) then ` +\n `tmux attach -t ${session} (Ctrl+B D to leave the agent).`,\n );\n return;\n }\n\n // ── /rules ────────────────────────────────────────────────────\n // Read-only display of the room's current rules. Any participant\n // (including guests) can view.\n case \"rules\": {\n try {\n const res = await fetch(`${serverUrl}/rules`, { headers: { Authorization: `Bearer ${sessionToken}` } });\n if (!res.ok) { systemEvent(\"Failed to fetch rules.\"); return; }\n const data = (await res.json()) as { rules: string[] };\n if (data.rules.length === 0) {\n systemEvent(\"No rules set for this room.\");\n return;\n }\n const lines = [\"Room rules:\"];\n data.rules.forEach((r, i) => lines.push(` ${i + 1}. ${r}`));\n systemEvent(lines.join(\"\\n\"));\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /leave ────────────────────────────────────────────────────\n case \"leave\": {\n await disconnect();\n tui.stop();\n process.exit(0);\n return;\n }\n\n // ── /kick <name> (admin only) ─────────────────────────────────\n case \"kick\": {\n if (!can(authority, \"kick\")) { systemEvent(\"Only admins can kick.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /kick <name>\"); return; }\n const target = await resolveParticipant(targetName);\n if (!target) return;\n await participantAction({\n path: \"/kick\", body: { participantId: target.id },\n failVerb: \"kick\", success: `Kicked ${targetName}.`,\n });\n return;\n }\n\n // ── /mute <name> (admin or product_owner) — demote to guest ─────\n case \"mute\": {\n if (!can(authority, \"mute\")) { systemEvent(\"Only admins and product owners can mute.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /mute <name>\"); return; }\n const target = await resolveParticipant(targetName);\n if (!target) return;\n await participantAction({\n path: \"/set-authority\", body: { participantId: target.id, authority: \"guest\" },\n failVerb: \"mute\", success: `Muted ${targetName} (guest).`,\n });\n return;\n }\n\n // ── /unmute <name> (admin or product_owner) — restore to member ──\n case \"unmute\": {\n if (!can(authority, \"unmute\")) { systemEvent(\"Only admins and product owners can unmute.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /unmute <name>\"); return; }\n const target = await resolveParticipant(targetName);\n if (!target) return;\n await participantAction({\n path: \"/set-authority\", body: { participantId: target.id, authority: \"member\" },\n failVerb: \"unmute\", success: `Unmuted ${targetName} (member).`,\n });\n return;\n }\n\n // ── /setmode <name> <mode> (admin or product_owner) ──────────\n case \"setmode\": {\n if (!can(authority, \"set_mode_for\")) { systemEvent(\"Only admins and product owners can set modes.\"); return; }\n const targetName = args[0];\n const mode = args[1];\n if (!targetName || !mode) { systemEvent(\"Usage: /setmode <name> <mode>\"); return; }\n const target = await resolveParticipant(targetName);\n if (!target) return;\n await participantAction({\n path: \"/set-mode\", body: { participantId: target.id, mode },\n failVerb: \"set mode\", success: `Set ${targetName} to ${mode}.`,\n });\n return;\n }\n\n // ── /promote <name> (admin only) — elevate to product_owner ──\n case \"promote\": {\n if (!can(authority, \"promote\")) { systemEvent(\"Only admins can promote.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /promote <name>\"); return; }\n const target = await resolveParticipant(targetName);\n if (!target) return;\n await participantAction({\n path: \"/set-authority\", body: { participantId: target.id, authority: \"product_owner\" },\n failVerb: \"promote\", success: `${targetName} is now a product owner.`,\n });\n return;\n }\n\n // ── /demote <name> (admin only) — restore product_owner to member ──\n case \"demote\": {\n if (!can(authority, \"demote\")) { systemEvent(\"Only admins can demote.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /demote <name>\"); return; }\n const target = await resolveParticipant(targetName);\n if (!target) return;\n await participantAction({\n path: \"/set-authority\", body: { participantId: target.id, authority: \"member\" },\n failVerb: \"demote\", success: `${targetName} is now a member.`,\n });\n return;\n }\n\n // ── /ping <name> ─────────────────────────────────────────────\n case \"ping\": {\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /ping <name>\"); return; }\n const target = await resolveParticipant(targetName);\n if (!target) return;\n await participantAction({\n path: \"/ping\", body: { participantId: target.id },\n failVerb: \"ping\", success: `Pinged ${targetName}.`,\n });\n return;\n }\n\n // ── /share [--as <tier>] ──────────────────────────────────────\n case \"share\": {\n if (authority === \"guest\") { systemEvent(\"Guests cannot create share links.\"); return; }\n\n let targetAuthority: string | undefined;\n if (args[0] === \"--as\" && args[1]) {\n targetAuthority = args[1];\n }\n\n try {\n const shareBody: Record<string, unknown> = {};\n if (targetAuthority) shareBody.authority = targetAuthority;\n\n const res = await fetch(`${serverUrl}/share`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${sessionToken}` },\n body: JSON.stringify(shareBody),\n });\n if (!res.ok) { systemEvent(`Failed: ${await res.text()}`); return; }\n const data = (await res.json()) as { links: Record<string, string> };\n const lines = Object.entries(data.links).map(([tier, url]) =>\n ` ${tier}: apiary join ${url}`\n );\n systemEvent(`Share links:\\n${lines.join(\"\\n\")}`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /sound — toggle notification sounds ───────────────────────\n case \"sound\": {\n const enabled = tui.toggleSound();\n const cfg = loadConfig();\n cfg.sound = enabled;\n saveConfig(cfg);\n systemEvent(`Sound ${enabled ? \"on\" : \"off\"} (saved for future rooms).`);\n return;\n }\n\n // ── /clear — wipe room history OR a specific agent's claude context\n // /clear → admin: wipe room history (storage + all clients)\n // /clear <agent-name> → admin: inject /clear into the agent's tmux pane,\n // clearing claude's local conversation. Frees\n // token budget mid-session without restart.\n case \"clear\": {\n if (!can(authority, \"clear_history\")) { systemEvent(\"Only admins can clear.\"); return; }\n\n const targetName = args[0];\n if (targetName) {\n // Per-agent claude /clear injection — local tmux only.\n const session = `apiary_${targetName}`;\n if (!tmuxSessionExists(session)) {\n systemEvent(\n `No local tmux session for \"${targetName}\". /clear <name> only ` +\n `works for same-host agents.`,\n );\n return;\n }\n try {\n sendSlashToAgent(session, \"/clear\");\n systemEvent(`Sent /clear to ${targetName}.`);\n } catch (err) {\n systemEvent(`Failed to send /clear: ${(err as Error).message}`);\n }\n return;\n }\n\n try {\n const res = await fetch(`${serverUrl}/clear`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${sessionToken}` },\n });\n if (!res.ok) { systemEvent(`Failed to clear: ${await res.text()}`); return; }\n // Server broadcasts RoomCleared — TUI clear happens in SSE handler\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /model — swap an agent's claude model mid-session ───────────\n // /model <agent-name> <model-id> e.g. /model bf sonnet\n // Injects claude's built-in /model command into the agent's tmux. No\n // restart — conversation continues at the new model.\n case \"model\": {\n if (!can(authority, \"clear_history\")) { systemEvent(\"Only admins can swap models.\"); return; }\n const targetName = args[0];\n const modelId = args[1];\n if (!targetName || !modelId) { systemEvent(\"Usage: /model <agent> <model-id>\"); return; }\n const session = `apiary_${targetName}`;\n if (!tmuxSessionExists(session)) {\n systemEvent(`No local tmux session for \"${targetName}\".`);\n return;\n }\n try {\n sendSlashToAgent(session, `/model ${modelId}`);\n // Persist so `apiary room resume` respawns at the swapped model.\n // Falls through silently if this client isn't the room owner.\n persistAgentModel(roomName, targetName, modelId);\n systemEvent(`Sent /model ${modelId} to ${targetName} (will persist on resume).`);\n } catch (err) {\n systemEvent(`Failed to swap model: ${(err as Error).message}`);\n }\n return;\n }\n\n // ── /tunnel — start cloudflared tunnel mid-session ────────────\n case \"tunnel\": {\n if (!can(authority, \"start_tunnel\")) { systemEvent(\"Only admins can start tunnels.\"); return; }\n\n try {\n const res = await fetch(`${serverUrl}/tunnel`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${sessionToken}` },\n });\n if (!res.ok) { systemEvent(`Failed: ${(await res.json() as { error: string }).error}`); return; }\n const data = (await res.json()) as { url: string; alreadyRunning: boolean };\n if (data.alreadyRunning) {\n systemEvent(`Tunnel already running: ${data.url}`);\n } else {\n systemEvent(`Tunnel started: ${data.url}\\nUse /share to generate share links.`);\n }\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n default:\n systemEvent(`Unknown command: /${cmd}`);\n }\n }\n\n // Set terminal tab title — hive emoji stable per (room, name) pair.\n process.stdout.write(`\\x1b]0;${roomEmoji(`${roomName}·${name}`)} ${roomName} · ${name}\\x07`);\n\n // Print share info before Ink renders — plain text, fully selectable.\n if (options.shareUrl) {\n console.log();\n console.log(` \\x1b[2mShare:\\x1b[0m \\x1b[33m${options.shareUrl}\\x1b[0m`);\n console.log();\n console.log(` \\x1b[2mJoin:\\x1b[0m \\x1b[36mapiary join\\x1b[0m \\x1b[2m<url>\\x1b[0m`);\n console.log(` \\x1b[2mAgent:\\x1b[0m \\x1b[36mapiary claude\\x1b[0m \\x1b[2m<name> → tell it the URL, it joins\\x1b[0m`);\n console.log();\n }\n\n const config = loadConfig();\n const tui = startTUI({\n roomName,\n readOnly: isReadOnly,\n authority,\n soundEnabled: config.sound ?? true,\n onSend: isReadOnly ? undefined : async (content: string) => {\n // Intercept slash commands\n if (content.startsWith(\"/\")) {\n await handleSlashCommand(content);\n return;\n }\n\n try {\n await fetch(`${serverUrl}/message`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${sessionToken}` },\n body: JSON.stringify({ content }),\n });\n } catch {\n // Server may be down — silently fail\n }\n },\n onWatchAgent: (agentName: string) => {\n const session = findAgentTmuxSession(agentName);\n if (!session) {\n systemEvent(\n `No local tmux session for \"${agentName}\". ` +\n `Watching only works for agents started on this machine by apiary claude/codex.`,\n );\n return;\n }\n const res = openAgentWatcher(session);\n systemEvent(\n res.ok\n ? `Opened ${agentName} in a new ${res.terminal} window (read-only). Use \"/watch ${agentName} --control\" to type into it.`\n : `Could not open a window for ${agentName}: ${res.reason}`,\n );\n },\n onCtrlC: (() => {\n // Two-step exit: single Ctrl+C shows a hint, second within 2s actually\n // leaves. Prevents the \"I closed it by mistake and lost my admin\n // identity\" scenario when the user just wanted to interrupt something.\n let lastPress = 0;\n const CONFIRM_MS = 2000;\n return async () => {\n const now = Date.now();\n if (now - lastPress < CONFIRM_MS) {\n await disconnect();\n tui.stop();\n process.exit(0);\n }\n lastPress = now;\n systemEvent(\"Press Ctrl+C again within 2s to leave (server keeps running).\");\n };\n })(),\n });\n\n // Welcome note\n if (!isReadOnly) {\n const hint = authority === \"admin\" ? \"/help for commands (admin)\" : \"/help for commands\";\n systemEvent(hint);\n }\n\n // Set initial agent names + participant names\n const agentNames = participants\n .filter((p) => p.type === \"agent\")\n .map((p) => p.name);\n\n // Seed any wizard-spawned-but-not-yet-joined agents as `pending` so the\n // footer shows them booting instead of staying empty for 5–15s. The set\n // is mutated (drained) as ParticipantJoined events arrive, and merged\n // into setAgentNames calls so they don't get clobbered.\n const pendingAgentSet = new Set(\n (options.expectedAgents ?? []).filter((n) => !agentNames.includes(n)),\n );\n const renderAgentList = (joined: Iterable<string>): void => {\n const merged = new Set<string>([...joined, ...pendingAgentSet]);\n tui.setAgentNames([...merged]);\n };\n if (agentNames.length + pendingAgentSet.size > 0) {\n renderAgentList(agentNames);\n for (const n of pendingAgentSet) tui.setAgentState(n, \"pending\");\n }\n if (agentNames.length > 0) {\n\n // Determine initial agent state from recent messages.\n //\n // Walk newest-first to find the most recent human message:\n // - If older than INIT_FRESHNESS_MS → leave everyone \"unknown\" (don't fabricate\n // a busy/idle signal from stale context).\n // - Otherwise: agents that replied after that human message are idle.\n // Agents that didn't reply: if the human message was a whisper, only its\n // recipients are working; otherwise all silent agents are working.\n const INIT_FRESHNESS_MS = 5 * 60_000;\n try {\n const msgRes = await fetch(`${serverUrl}/messages?count=20`, {\n headers: { Authorization: `Bearer ${sessionToken}` },\n });\n if (msgRes.ok) {\n const { items: messages } = (await msgRes.json()) as {\n items: Array<{\n sender_id: string;\n sender_name: string;\n timestamp?: string;\n recipients?: string[];\n }>;\n };\n const agentSet = new Set(agentNames);\n const idleAgents = new Set<string>();\n let humanMsg: typeof messages[0] | null = null;\n for (const msg of messages) {\n const sType = participants.find((p) => p.id === msg.sender_id)?.type;\n if (sType === \"agent\" && agentSet.has(msg.sender_name)) {\n idleAgents.add(msg.sender_name);\n } else if (sType === \"human\") {\n humanMsg = msg;\n break;\n }\n }\n const fresh = humanMsg?.timestamp\n ? (Date.now() - new Date(humanMsg.timestamp).getTime()) < INIT_FRESHNESS_MS\n : false;\n if (humanMsg && fresh) {\n // Whisper? Only its recipients are candidates for \"working\".\n const targetedAgents: Set<string> = humanMsg.recipients && humanMsg.recipients.length > 0\n ? new Set(\n participants\n .filter((p) => p.type === \"agent\" && humanMsg!.recipients!.includes(p.id))\n .map((p) => p.name),\n )\n : agentSet;\n for (const agent of agentSet) {\n if (idleAgents.has(agent)) {\n tui.setAgentState(agent, \"idle\");\n } else if (targetedAgents.has(agent)) {\n tui.setAgentState(agent, \"working\");\n }\n // else: leave at default \"unknown\" — agent wasn't addressed\n }\n }\n // Stale or no human message → leave everyone at \"unknown\" default\n }\n } catch { /* non-critical — default to no indicator */ }\n }\n const participantNames = new Set(\n participants.filter((p) => p.id !== participantId).map((p) => p.name),\n );\n tui.setParticipants([...participantNames]);\n\n // ── Connect SSE event stream ────────────────────────────────────────────\n // ⚠️ MUST use POST — DO NOT change to GET.\n // Cloudflare Quick Tunnels buffer GET streaming responses and only flush\n // when the connection closes. POST streams in real-time. (cloudflared#1449)\n // https://github.com/cloudflare/cloudflared/issues/1449\n\n {\n const participantTypes = new Map<string, \"human\" | \"agent\">();\n const participantNameById = new Map<string, string>();\n for (const p of participants) {\n participantTypes.set(p.id, p.type as \"human\" | \"agent\");\n participantNameById.set(p.id, p.name);\n }\n const currentAgents = new Set(agentNames);\n const PING_DECAY_MS = 10_000; // pings are status checks — much shorter\n let sseController: AbortController | null = null;\n\n cleanupStream = () => {\n if (sseController) { sseController.abort(); sseController = null; }\n };\n\n const connectSSE = async () => {\n sseController = new AbortController();\n\n try {\n const res = await fetch(`${serverUrl}/events`, {\n method: \"POST\",\n headers: {\n Accept: \"text/event-stream\",\n Authorization: `Bearer ${sessionToken}`,\n },\n signal: sseController.signal,\n });\n\n if (!res.ok || !res.body) {\n console.error(\"Failed to connect event stream\");\n await disconnect();\n process.exit(1);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const parts = buffer.split(\"\\n\\n\");\n buffer = parts.pop()!;\n\n for (const part of parts) {\n const dataLine = part.split(\"\\n\").find((l) => l.startsWith(\"data: \"));\n if (!dataLine) continue;\n\n try {\n const event = JSON.parse(dataLine.slice(6)) as RoomEvent & { _replyToName?: string };\n\n // Handle room clear — wipe TUI and show system message\n if (event.type === \"RoomCleared\") {\n tui.clear();\n systemEvent(`Room cleared by ${(event as any).cleared_by ?? \"admin\"}.`);\n continue;\n }\n\n if (event.type === \"ParticipantJoined\") {\n participantTypes.set(event.participant.id, event.participant.type);\n }\n\n const displayEvent = toDisplayEvent(event, participantId, participantTypes);\n if (displayEvent) {\n tui.push(displayEvent);\n }\n\n if (event.type === \"ParticipantJoined\") {\n participantNameById.set(event.participant.id, event.participant.name);\n if (event.participant.type === \"agent\") {\n currentAgents.add(event.participant.name);\n // Drain from pending — agent we were waiting on has arrived.\n pendingAgentSet.delete(event.participant.name);\n renderAgentList(currentAgents);\n // A fresh agent that just connected — assume idle until proven otherwise.\n tui.setAgentState(event.participant.name, \"idle\");\n }\n if (event.participant.id !== participantId) {\n participantNames.add(event.participant.name);\n tui.setParticipants([...participantNames]);\n }\n }\n\n // ── Agent state transitions ───────────────────────────────────\n if (event.type === \"MessageSent\") {\n const senderType = participantTypes.get(event.message.sender_id);\n if (senderType === \"agent\" && currentAgents.has(event.message.sender_name)) {\n // The agent spoke — definitively idle.\n tui.setAgentState(event.message.sender_name, \"idle\");\n } else if (senderType === \"human\") {\n // Whisper-aware: only mark agents addressed by this message as working.\n const recipients = (event.message as { recipients?: string[] }).recipients ?? [];\n if (recipients.length > 0) {\n // Whisper — only resolve recipients that are agents we know.\n for (const recipientId of recipients) {\n const name = participantNameById.get(recipientId);\n if (name && currentAgents.has(name)) {\n tui.setAgentState(name, \"working\");\n }\n }\n } else {\n // Public message — every agent is a candidate (engagement-unknown from here).\n for (const agent of currentAgents) tui.setAgentState(agent, \"working\");\n }\n }\n }\n if (event.type === \"Pinged\") {\n // Pings are status checks, not real work — short decay.\n const pinged = event as RoomEvent & { participant_id?: string };\n if (pinged.participant_id) {\n const name = participantNameById.get(pinged.participant_id);\n if (name && currentAgents.has(name)) {\n tui.setAgentState(name, \"working\", { decayMs: PING_DECAY_MS });\n }\n }\n }\n if (event.type === \"Activity\" && (event as { action?: string }).action === \"status_label\") {\n // Live activity label from agent runtime (e.g. \"Sautéed for 12s\",\n // \"Compacting conversation… 23%\"). Updates the participant strip\n // so user can see real progress instead of guessing \"is it stuck?\".\n const detail = (event as { detail?: Record<string, unknown> }).detail ?? {};\n const name = typeof detail.participant_name === \"string\" ? detail.participant_name : undefined;\n const label = typeof detail.label === \"string\" ? detail.label : null;\n const observed = detail.state === \"idle\" || detail.state === \"working\" || detail.state === \"blocked\"\n ? detail.state\n : undefined;\n if (name && currentAgents.has(name)) {\n tui.setAgentActivity(name, label);\n // Ground truth from the agent's own screen — outranks the\n // \"someone spoke, so everyone must be working\" guess below.\n if (observed) tui.setAgentState(name, observed, { observed: true });\n }\n }\n if (event.type === \"Activity\" && (event as { action?: string }).action === \"metrics\") {\n // Last-turn context size from the agent's jsonl. Used by\n // the TUI to show per-agent context and a room total.\n const detail = (event as { detail?: Record<string, unknown> }).detail ?? {};\n const name = typeof detail.participant_name === \"string\" ? detail.participant_name : undefined;\n if (name && currentAgents.has(name)) {\n tui.setAgentMetrics(name, {\n ctxTokens: Number(detail.last_ctx_tokens) || 0,\n model: typeof detail.model === \"string\" ? detail.model : undefined,\n });\n }\n }\n if (event.type === \"StatusChanged\") {\n // Server-driven presence. Use it as the source of truth for unresponsive/offline.\n const sc = event as RoomEvent & {\n name: string;\n status: \"online\" | \"unresponsive\" | \"offline\";\n previous_status: \"online\" | \"unresponsive\" | \"offline\";\n };\n if (currentAgents.has(sc.name)) {\n if (sc.status === \"unresponsive\") {\n tui.setAgentState(sc.name, \"unresponsive\");\n } else if (sc.status === \"online\" && sc.previous_status === \"unresponsive\") {\n // Recovered — assume idle until next signal.\n tui.setAgentState(sc.name, \"idle\");\n }\n // status === \"offline\" is followed by ParticipantLeft/Kicked, handled below.\n }\n }\n\n if (event.type === \"ParticipantLeft\" || event.type === \"ParticipantKicked\") {\n if (event.participant.type === \"agent\") {\n currentAgents.delete(event.participant.name);\n renderAgentList(currentAgents);\n }\n participantTypes.delete(event.participant.id);\n participantNameById.delete(event.participant.id);\n participantNames.delete(event.participant.name);\n tui.setParticipants([...participantNames]);\n }\n } catch {\n // Malformed event — skip\n }\n }\n }\n\n // Stream ended — server closed connection\n if (!disconnected) {\n tui.stop();\n console.log(\"\\nServer disconnected.\");\n process.exit(0);\n }\n } catch {\n if (!disconnected) {\n tui.stop();\n console.log(\"\\nServer disconnected.\");\n process.exit(0);\n }\n }\n };\n\n connectSSE();\n }\n\n // ── Graceful shutdown ───────────────────────────────────────────────────\n\n process.on(\"SIGINT\", async () => {\n await disconnect();\n tui.stop();\n process.exit(0);\n });\n process.on(\"SIGTERM\", async () => {\n await disconnect();\n tui.stop();\n process.exit(0);\n });\n}\n\n// ── RoomEvent → DisplayEvent conversion ───────────────────────────────────────\n\nfunction toDisplayEvent(\n event: RoomEvent & { _replyToName?: string },\n selfId: string,\n participantTypes: Map<string, \"human\" | \"agent\">,\n): DisplayEvent | null {\n const ts = formatTimestamp(new Date(event.timestamp));\n\n switch (event.type) {\n case \"MessageSent\": {\n const msg = event.message;\n const senderType = participantTypes.get(msg.sender_id) ?? \"human\";\n return {\n id: msg.id,\n ts,\n kind: \"message\",\n senderName: msg.sender_name,\n senderType,\n isSelf: msg.sender_id === selfId,\n content: msg.content,\n replyToName: event._replyToName ?? undefined,\n };\n }\n case \"ParticipantJoined\":\n return {\n id: randomUUID(),\n ts,\n kind: \"join\",\n name: event.participant.name,\n participantType: event.participant.type,\n };\n case \"ParticipantLeft\":\n return {\n id: randomUUID(),\n ts,\n kind: \"leave\",\n name: event.participant.name,\n participantType: event.participant.type,\n };\n case \"ParticipantKicked\":\n return {\n id: randomUUID(),\n ts,\n kind: \"system\",\n content: `${event.participant.name} was kicked`,\n };\n case \"AuthorityChanged\": {\n const name = event.participant.name;\n if (event.new_authority === \"guest\") {\n return { id: randomUUID(), ts, kind: \"system\", content: `${name} was muted` };\n }\n if (event.new_authority === \"member\") {\n return { id: randomUUID(), ts, kind: \"system\", content: `${name} was unmuted` };\n }\n if (event.new_authority === \"product_owner\") {\n return { id: randomUUID(), ts, kind: \"system\", content: `${name} was promoted to product owner` };\n }\n return { id: randomUUID(), ts, kind: \"system\", content: `${name} → ${event.new_authority}` };\n }\n case \"Pinged\":\n return {\n id: randomUUID(),\n ts,\n kind: \"ping\",\n pingerName: event.pinger_name ?? \"someone\",\n };\n case \"Activity\":\n if (event.action === \"mode_changed\") {\n return {\n id: randomUUID(),\n ts,\n kind: \"mode\",\n mode: String((event.detail as Record<string, unknown>)?.mode ?? \"\"),\n };\n }\n return null;\n default:\n return null;\n }\n}\n","/**\n * apiary TUI — ink-based terminal UI for the room server.\n *\n * Uses ink's <Static> for events (rendered once, selectable terminal text)\n * and a dynamic footer for input + status. Same architecture as Claude Code.\n */\n\nimport React, { useState, useEffect, useCallback, useMemo, useRef } from \"react\";\nimport { render, Box, Text, Static, useStdout, useInput } from \"ink\";\nimport { exec } from \"node:child_process\";\n\nimport type { AuthorityLevel } from \"../core/types.js\";\nimport { can, type Operation } from \"../core/authority.js\";\n\n// ── Palette (from apiary-app) ─────────────────────────────────────────────────\n\nconst C = {\n cyan: \"#00d4ff\",\n purple: \"#8b5cf6\",\n orange: \"#ff8c42\",\n pink: \"#f472b6\",\n green: \"#34d399\",\n yellow: \"#fbbf24\",\n danger: \"#f87171\",\n text: \"#eceff4\",\n secondary: \"#b0b7c4\",\n dim: \"#7e8798\",\n muted: \"#5b6679\",\n border: \"#475264\",\n} as const;\n\nconst AGENT_COLORS = [C.cyan, C.purple, C.orange, C.pink, C.green, C.yellow] as const;\nconst SIGILS = [\"◆\", \"▲\", \"●\", \"■\", \"★\", \"◉\", \"◈\", \"▸\"] as const;\n\n// ── Banner ───────────────────────────────────────────────────────────────────\n// Figlet \"slant\" font, colored with an amber → gold gradient per line.\n\n// Each banner line is an array of { text, color } segments for multi-color rendering.\ntype BannerSegment = { text: string; color: string };\nconst AMB = \"#fbbf24\"; // amber/yellow\nconst WNG = \"#e0e7ee\"; // wings (white membrane)\nconst BLK = \"#3a3a4a\"; // wing veins (dark)\n\nconst BANNER: BannerSegment[][] = [\n [\n { text: \" _ \", color: \"#ffb347\" },\n { text: \"\\\\ \", color: BLK },\n { text: \"\\\\ \", color: WNG },\n ],\n [\n { text: \" ____ _____ (_)___ ________ __ \", color: \"#ffa733\" },\n { text: \"\\\\ \", color: WNG },\n { text: \"\\\\ \", color: BLK },\n { text: \"\\\\ \", color: WNG },\n ],\n [\n { text: \" / __ `/ __ \\\\/ / __ `/ ___/ / / / \", color: \"#ff9b1f\" },\n { text: \"(o o)\", color: AMB },\n ],\n [\n { text: \"/ /_/ / /_/ / / /_/ / / / /_/ / \", color: \"#ff8f0b\" },\n { text: \")=BzZz=(\", color: AMB },\n ],\n [\n { text: \"\\\\__,_/ .___/_/\\\\__,_/_/ \\\\__, / \", color: \"#f7a600\" },\n { text: \"/ \", color: WNG },\n { text: \"/ \", color: BLK },\n { text: \"/ \", color: WNG },\n ],\n [\n { text: \" /_/ /____/ \", color: \"#f0c000\" },\n { text: \"/ \", color: BLK },\n { text: \"/ \", color: WNG },\n { text: \"/ \", color: BLK },\n ],\n];\n\n// ── Fight mode ───────────────────────────────────────────────────────────────\n\nconst FIGHT_BANNER: BannerSegment[][] = [\n [{ text: \"╔══════════════════════════════════════════════════════╗\", color: \"#f87171\" }],\n [{ text: \"║\", color: \"#f87171\" }, { text: \" ███████╗██╗ ██████╗ ██╗ ██╗████████╗██╗██╗ \", color: \"#ff4444\" }, { text: \"║\", color: \"#f87171\" }],\n [{ text: \"║\", color: \"#f87171\" }, { text: \" ██╔════╝██║██╔════╝ ██║ ██║╚══██╔══╝██║██║ \", color: \"#ff6b6b\" }, { text: \"║\", color: \"#f87171\" }],\n [{ text: \"║\", color: \"#f87171\" }, { text: \" █████╗ ██║██║ ███╗███████║ ██║ ██║██║ \", color: \"#ff8888\" }, { text: \"║\", color: \"#f87171\" }],\n [{ text: \"║\", color: \"#f87171\" }, { text: \" ██╔══╝ ██║██║ ██║██╔══██║ ██║ ╚═╝╚═╝ \", color: \"#ff6b6b\" }, { text: \"║\", color: \"#f87171\" }],\n [{ text: \"║\", color: \"#f87171\" }, { text: \" ██║ ██║╚██████╔╝██║ ██║ ██║ ██╗██╗ \", color: \"#ff4444\" }, { text: \"║\", color: \"#f87171\" }],\n [{ text: \"║\", color: \"#f87171\" }, { text: \" ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ \", color: \"#ff4444\" }, { text: \"║\", color: \"#f87171\" }],\n [{ text: \"║\", color: \"#f87171\" }, { text: \" \", color: \"#f87171\" }, { text: \"║\", color: \"#f87171\" }],\n [{ text: \"║\", color: \"#f87171\" }, { text: \" ⚔️ NO MERCY · NO RULES · NO FILTER ⚔️ \", color: \"#fbbf24\" }, { text: \"║\", color: \"#f87171\" }],\n [{ text: \"╚══════════════════════════════════════════════════════╝\", color: \"#f87171\" }],\n];\n\nconst FIGHT_HITS = [\"POW!\", \"WHAM!\", \"K.O.!\", \"BAM!\", \"CRACK!\", \"BOOM!\", \"SLAM!\", \"ZAP!\", \"THWACK!\", \"KAPOW!\", \"HADOUKEN!\", \"FATALITY!\", \"COMBO!\", \"ULTRA!\"];\nconst FIGHT_COLORS = [\"#ff4444\", \"#ff6b6b\", \"#fbbf24\", \"#ff8c42\", \"#f472b6\", \"#8b5cf6\"] as const;\n\nfunction pickFightHit(messageId: string): { word: string; color: string } {\n let h = 0;\n for (let i = 0; i < messageId.length; i++) h = ((h << 5) - h + messageId.charCodeAt(i)) | 0;\n const idx = Math.abs(h);\n return {\n word: FIGHT_HITS[idx % FIGHT_HITS.length],\n color: FIGHT_COLORS[idx % FIGHT_COLORS.length],\n };\n}\n\n// ── Slash commands ────────────────────────────────────────────────────────────\n\ninterface SlashParam {\n label: string; // display hint: \"name\", \"mode\", etc.\n completions?: string[] | \"participants\"; // static values, dynamic lookup, or undefined (hint only)\n}\n\ninterface SlashCommand {\n name: string;\n description: string;\n /** The operation this command performs — checked against the user's authority via `can()`. */\n op?: Operation;\n params?: SlashParam[];\n}\n\nconst ENGAGEMENT_MODES = [\n \"everyone\", \"people\", \"agents\", \"fight\",\n \"standby-everyone\", \"standby-people\", \"standby-agents\",\n];\n\nconst SLASH_COMMANDS: SlashCommand[] = [\n { name: \"/help\", description: \"Show commands\" },\n { name: \"/who\", description: \"List participants\" },\n { name: \"/rules\", description: \"Show the room rules\" },\n { name: \"/watch\", description: \"Open an agent's terminal in a new window\", params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/leave\", description: \"Disconnect and exit\" },\n { name: \"/share\", description: \"Generate share links\", op: \"create_share_link\" },\n { name: \"/kick\", description: \"Remove a participant\", op: \"kick\", params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/mute\", description: \"Make read-only (guest)\", op: \"mute\", params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/unmute\", description: \"Restore to member\", op: \"unmute\", params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/setmode\", description: \"Set engagement mode\", op: \"set_mode_for\", params: [\n { label: \"name\", completions: \"participants\" },\n { label: \"mode\", completions: ENGAGEMENT_MODES },\n ]},\n { name: \"/promote\", description: \"Promote to product owner\", op: \"promote\", params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/demote\", description: \"Demote product owner to member\", op: \"demote\", params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/ping\", description: \"Ping a participant\", op: \"ping\", params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/peek\", description: \"Show tmux command to view an agent's session\", params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/clear\", description: \"Wipe room history\", op: \"clear_history\" },\n { name: \"/tunnel\", description: \"Start a cloudflared tunnel\", op: \"start_tunnel\" },\n { name: \"/sound\", description: \"Toggle notification sounds\" },\n];\n\nconst CMD_DISPLAY_COL = 26; // width for command + params display column\n\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type DisplayEvent =\n | { id: string; ts: string; kind: \"message\"; senderName: string; senderType: \"human\" | \"agent\"; isSelf: boolean; content: string; replyToName?: string }\n | { id: string; ts: string; kind: \"join\"; name: string; participantType: \"human\" | \"agent\" }\n | { id: string; ts: string; kind: \"leave\"; name: string; participantType: \"human\" | \"agent\" }\n | { id: string; ts: string; kind: \"mode\"; mode: string }\n | { id: string; ts: string; kind: \"ping\"; pingerName: string }\n | { id: string; ts: string; kind: \"system\"; content: string };\n\n/**\n * How long a state read off an agent's screen outranks one inferred from room\n * traffic. Runtimes heartbeat every ~1.5s, so a living agent stays comfortably\n * fresh; a dead one falls back to inference within seconds.\n */\nexport const OBSERVED_TTL_MS = 10_000;\n\n/**\n * How long an INFERRED state survives before falling back to \"unknown\".\n *\n * It was five minutes, chosen when guessing from room traffic was the only\n * signal available. Runtimes now report the real state every ~1.5s, so five\n * minutes of showing a guess is just five minutes of being wrong: an agent\n * that never started looked busy for the rest of the conversation. Twice the\n * observed TTL bridges a missed heartbeat and no more.\n */\nexport const INFERRED_DECAY_MS = OBSERVED_TTL_MS * 2;\n\n/**\n * Should this state update be applied?\n *\n * The room infers \"working\" for every agent on every human message. That must\n * not overwrite a state the runtime actually read off the agent's screen —\n * otherwise \"needs you\", the one state the user has to act on, is buried the\n * moment anybody speaks. Observed updates always apply and refresh the window;\n * inferred ones are dropped while an observed state is still fresh.\n */\nexport function shouldApplyAgentState(opts: {\n observed: boolean;\n lastObservedAt: number | undefined;\n now: number;\n ttlMs?: number;\n}): boolean {\n if (opts.observed) return true;\n if (opts.lastObservedAt === undefined) return true;\n return opts.now - opts.lastObservedAt >= (opts.ttlMs ?? OBSERVED_TTL_MS);\n}\n\nexport type AgentState =\n | \"pending\" | \"stalled\" | \"idle\" | \"working\" | \"unknown\" | \"unresponsive\"\n /**\n * The agent's CLI is sitting on a prompt only a human can clear — a tool or\n * command approval, a sign-in screen, an onboarding step. Distinct from\n * \"working\" on purpose: both look like a spinner from the room's side, but\n * this one never resolves on its own.\n */\n | \"blocked\";\n\nexport interface SetAgentStateOpts {\n /** When `state === \"working\"`, auto-revert to \"unknown\" after this many ms. Default 60_000. */\n decayMs?: number;\n /**\n * True when the state was read off the agent's own screen rather than\n * guessed from room traffic.\n *\n * The room otherwise infers \"working\" for every agent on every human\n * message, which would immediately overwrite a reported \"needs you\" and\n * hide the one state the user has to act on. Observed states therefore win\n * for OBSERVED_TTL_MS, refreshed by each runtime heartbeat; if a runtime\n * dies its state goes stale and inference takes over again.\n */\n observed?: boolean;\n}\n\nexport interface TUIHandle {\n push(event: DisplayEvent): void;\n clear(): void;\n toggleSound(): boolean;\n /** Set one agent's state. `working` auto-decays to `unknown` after `opts.decayMs` (default 60s). */\n setAgentState(name: string, state: AgentState, opts?: SetAgentStateOpts): void;\n /**\n * Set a free-form live activity label for an agent (e.g. \"Sautéed for 12s\",\n * \"Compacting…\"). Pushed by the agent-runtime poll loop scraping claude's\n * status line so the user can see real progress instead of guessing.\n * Pass null to clear.\n */\n setAgentActivity(name: string, label: string | null): void;\n /**\n * Set live context-size for an agent. Pushed by the agent-runtime\n * jsonl-stats poll loop. The TUI shows per-agent values in the\n * participant strip and rolls up a room total in the status bar.\n */\n setAgentMetrics(name: string, m: { ctxTokens: number; model?: string }): void;\n setAgentNames(names: string[]): void;\n setParticipants(names: string[]): void;\n stop(): void;\n}\n\nexport interface TUIOptions {\n roomName: string;\n onSend?(content: string): void;\n onCtrlC?(): void;\n /** Ctrl+<n> — open that agent's terminal in a new window. */\n onWatchAgent?(agentName: string): void;\n readOnly?: boolean;\n /** Server-granted authority. Drives which slash commands appear and run. */\n authority?: AuthorityLevel;\n soundEnabled?: boolean;\n}\n\n// ── Number formatters ────────────────────────────────────────────────────────\n\n/** \"47\" / \"12k\" / \"1.3M\" — tight token-count formatting for status strips. */\nfunction formatTokens(n: number): string {\n if (!Number.isFinite(n) || n < 0) return \"0\";\n if (n < 1000) return String(Math.round(n));\n if (n < 1_000_000) return `${(n / 1000).toFixed(n < 10_000 ? 1 : 0)}k`;\n return `${(n / 1_000_000).toFixed(n < 10_000_000 ? 1 : 0)}M`;\n}\n\n// ── Identity (seed → color + sigil) ──────────────────────────────────────────\n\nfunction seedHash(s: string): number {\n let h = 0;\n for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0;\n return Math.abs(h);\n}\n\nfunction makeIdentityAssigner(): (name: string) => { color: string; sigil: string } {\n // Color + sigil both derive from the name hash so the same person always\n // shows up the same way — across sessions, across surfaces (footer, message\n // border, @mentions), and regardless of the order they first appear.\n const map = new Map<string, { color: string; sigil: string }>();\n return (name: string) => {\n if (!map.has(name)) {\n const h = seedHash(name);\n map.set(name, {\n color: AGENT_COLORS[h % AGENT_COLORS.length],\n sigil: SIGILS[Math.floor(h / AGENT_COLORS.length) % SIGILS.length],\n });\n }\n return map.get(name)!;\n };\n}\n\n// ── Local shell `!` ───────────────────────────────────────────────────────────\n\nconst SHELL_MAX_OUTPUT_BYTES = 64 * 1024;\nconst SHELL_TIMEOUT_MS = 60_000;\n\nfunction runLocalShell(cmd: string, push: (e: DisplayEvent) => void): void {\n const ts = new Date().toISOString().slice(11, 19);\n const startId = `sh-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n push({ id: `${startId}-cmd`, ts, kind: \"system\", content: `$ ${cmd}` });\n\n exec(cmd, {\n maxBuffer: SHELL_MAX_OUTPUT_BYTES,\n timeout: SHELL_TIMEOUT_MS,\n shell: process.env.SHELL || \"/bin/sh\",\n }, (err, stdout, stderr) => {\n const doneTs = new Date().toISOString().slice(11, 19);\n const out = (stdout || \"\").toString().replace(/\\s+$/, \"\");\n const errOut = (stderr || \"\").toString().replace(/\\s+$/, \"\");\n if (out) push({ id: `${startId}-out`, ts: doneTs, kind: \"system\", content: out });\n if (errOut) push({ id: `${startId}-err`, ts: doneTs, kind: \"system\", content: errOut });\n if (err) {\n const code = (err as NodeJS.ErrnoException).code;\n const exitCode = (err as { code?: number | string }).code;\n const tag = code === \"ETIMEDOUT\" ? \"timeout\" : `exit ${exitCode ?? \"?\"}`;\n push({ id: `${startId}-end`, ts: doneTs, kind: \"system\", content: `↳ ${tag}` });\n } else if (!out && !errOut) {\n push({ id: `${startId}-end`, ts: doneTs, kind: \"system\", content: \"↳ (no output)\" });\n }\n });\n}\n\n// ── Word wrap ─────────────────────────────────────────────────────────────────\n\nfunction wordWrap(text: string, width: number): string[] {\n if (width < 10) width = 10;\n const lines: string[] = [];\n for (const paragraph of text.split(\"\\n\")) {\n if (paragraph.length === 0) { lines.push(\"\"); continue; }\n let line = \"\";\n for (const word of paragraph.split(/( +)/)) {\n if (line.length + word.length > width && line.length > 0) {\n lines.push(line);\n line = word.replace(/^ +/, \"\"); // trim leading spaces on new line\n } else {\n line += word;\n }\n }\n if (line.length > 0) lines.push(line);\n }\n if (lines.length === 0) lines.push(\"\");\n return lines;\n}\n\n// ── Styled content (highlight @mentions) ──────────────────────────────────────\n\nfunction StyledContent({\n text,\n contentColor,\n identify,\n}: {\n text: string;\n contentColor: string;\n identify: (n: string) => { color: string; sigil: string };\n}) {\n // Split on @mention patterns, highlight them\n const parts: React.ReactNode[] = [];\n const pattern = /@([a-zA-Z0-9_-]+)/g;\n let lastIdx = 0;\n let match;\n let key = 0;\n\n while ((match = pattern.exec(text)) !== null) {\n // Text before the mention\n if (match.index > lastIdx) {\n parts.push(<Text key={key++} color={contentColor}>{text.slice(lastIdx, match.index)}</Text>);\n }\n // The @mention itself — use the mentioned name's color\n const mentionName = match[1];\n const { color } = identify(mentionName);\n parts.push(<Text key={key++} color={color} bold>{\"@\"}{mentionName}</Text>);\n lastIdx = match.index + match[0].length;\n }\n\n // Remaining text\n if (lastIdx < text.length) {\n parts.push(<Text key={key++} color={contentColor}>{text.slice(lastIdx)}</Text>);\n }\n\n if (parts.length === 0) {\n return <Text color={contentColor}>{text}</Text>;\n }\n\n return <>{parts}</>;\n}\n\n// ── Reply whispers ────────────────────────────────────────────────────────────\n// Tiny phrases that precede a reply-to name. Rotated for personality.\n\nconst REPLY_WHISPERS = [\n \"re:\", \"↩\", \"∿∿\", \"⤷\", \"↫\", \"«\", \"↳\", \"⟲\", \"∿\", \"⮑\",\n];\n\nfunction pickWhisper(messageId: string): string {\n let h = 0;\n for (let i = 0; i < messageId.length; i++) h = ((h << 5) - h + messageId.charCodeAt(i)) | 0;\n return REPLY_WHISPERS[Math.abs(h) % REPLY_WHISPERS.length];\n}\n\n// ── Event line ────────────────────────────────────────────────────────────────\n\nconst NAME_COL = 12;\n\nfunction EventLine({\n event,\n identify,\n cols,\n zebra,\n fightMode,\n}: {\n event: DisplayEvent;\n identify: (n: string) => { color: string; sigil: string };\n cols: number;\n zebra?: boolean;\n fightMode?: boolean;\n}) {\n const ts = <Text color={C.muted}>{event.ts}{\" \"}</Text>;\n\n // ── Message ──\n if (event.kind === \"message\") {\n const { color, sigil } = identify(event.senderName);\n const isSelf = event.isSelf;\n const isFight = fightMode && !isSelf;\n\n const nameColor = isSelf ? C.text : event.senderType === \"agent\" ? color : C.secondary;\n const sigilColor = isSelf ? C.dim : event.senderType === \"agent\" ? color : C.dim;\n const sigilChar = isSelf ? \"›\" : isFight ? \"⚔\" : event.senderType === \"agent\" ? sigil : \"·\";\n const contentColor = isSelf ? C.text : zebra ? \"#d0d5de\" : C.secondary;\n\n // Border: fight mode = double borders in red/orange, normal = round\n const borderColor = isFight\n ? FIGHT_COLORS[Math.abs(seedHash(event.id)) % FIGHT_COLORS.length]\n : isSelf ? C.border : event.senderType === \"agent\" ? color : C.dim;\n const borderStyle = isFight ? \"double\" as const : \"round\" as const;\n\n // Box eats ~4 cols (2 border + 2 padding). Inner content width:\n const boxChrome = 4; // │ + space + space + │\n const innerWidth = Math.max(10, cols - 2 - boxChrome); // -2 for outer paddingX\n\n const wrapped = wordWrap(event.content, innerWidth);\n\n // Fight mode: comic book hit effect\n const hit = isFight ? pickFightHit(event.id) : null;\n\n // Reply indicator — subtle tag in header. Mentions are already\n // highlighted in content by StyledContent, no separate indicator needed.\n const replyTag = event.replyToName\n ? <><Text color={C.dim}>{\" ↩ \"}</Text><Text color={C.secondary}>{event.replyToName}</Text></>\n : null;\n\n return (\n <Box paddingX={1} flexDirection=\"column\">\n <Box>\n <Box\n flexDirection=\"column\"\n borderStyle={borderStyle}\n borderColor={borderColor}\n paddingX={1}\n width={cols - 2}\n >\n {/* Header: timestamp + sigil + name (+ reply tag) (+ fight hit) */}\n <Box>\n {ts}\n <Text color={sigilColor}>{sigilChar}{\" \"}</Text>\n <Text color={nameColor} bold={isSelf || isFight}>{event.senderName}</Text>\n {replyTag}\n {hit && <Text color={hit.color} bold>{` « ${hit.word} »`}</Text>}\n </Box>\n {/* Content */}\n {wrapped.map((line, i) => (\n <Box key={i}>\n <Text wrap=\"truncate\">\n <StyledContent text={line} contentColor={isFight ? C.text : contentColor} identify={identify} />\n </Text>\n </Box>\n ))}\n </Box>\n </Box>\n </Box>\n );\n }\n\n // ── Join ──\n if (event.kind === \"join\") {\n const isAgent = event.participantType === \"agent\";\n const { color, sigil } = isAgent ? identify(event.name) : { color: C.dim, sigil: \"·\" };\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={isAgent ? color : C.dim}>{sigil}{\" \"}</Text>\n <Text color={isAgent ? color : C.dim}>{event.name}</Text>\n <Text color={C.green}>{\" joined\"}</Text>\n </Box>\n );\n }\n\n // ── Leave ──\n if (event.kind === \"leave\") {\n const isAgent = event.participantType === \"agent\";\n const { color: nameColor } = isAgent ? identify(event.name) : { color: C.muted };\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={C.muted}>{\"· \"}</Text>\n <Text color={nameColor}>{event.name}</Text>\n <Text color={C.danger}>{\" left\"}</Text>\n </Box>\n );\n }\n\n // ── Mode change ──\n if (event.kind === \"mode\") {\n if (event.mode === \"fight\") {\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n <Box><Text>{\" \"}</Text></Box>\n {FIGHT_BANNER.map((segments, i) => (\n <Text key={i}>{segments.map((s, j) => (\n <Text key={j} color={s.color} bold>{s.text}</Text>\n ))}</Text>\n ))}\n <Box><Text>{\" \"}</Text></Box>\n </Box>\n );\n }\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={C.dim}>{\"mode → \"}</Text>\n <Text color={C.yellow} bold>{event.mode}</Text>\n </Box>\n );\n }\n\n // ── Ping notification ──\n if (event.kind === \"ping\") {\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={C.yellow}>{\"🔔 \"}</Text>\n <Text color={C.yellow} bold>{event.pingerName}</Text>\n <Text color={C.yellow}>{\" pinged you.\"}</Text>\n </Box>\n );\n }\n\n // ── System message (slash command output) ──\n if (event.kind === \"system\") {\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={C.dim}>{\" \"}</Text>\n <Text color={C.secondary}>{event.content}</Text>\n </Box>\n );\n }\n\n return null;\n}\n\n// ── Internal bridge ───────────────────────────────────────────────────────────\n\ninterface AgentMetricsRow {\n ctxTokens: number;\n model?: string;\n}\n\ninterface AppHandle {\n push: (event: DisplayEvent) => void;\n clear: () => void;\n setAgentNames: (names: string[]) => void;\n setParticipants: (names: string[]) => void;\n toggleSound: () => boolean;\n setAgentState: (name: string, state: AgentState, opts?: SetAgentStateOpts) => void;\n setAgentActivity: (name: string, label: string | null) => void;\n setAgentMetrics: (name: string, m: AgentMetricsRow) => void;\n}\n\n// ── App ───────────────────────────────────────────────────────────────────────\n\ntype StaticEntry = { id: string; event?: DisplayEvent; zebra?: boolean };\n\nfunction App({\n roomName,\n onSend,\n onCtrlC,\n onWatchAgent,\n onReady,\n readOnly,\n authority,\n initialSound = true,\n}: {\n roomName: string;\n onSend?: (content: string) => void;\n onCtrlC?: () => void;\n /**\n * Ctrl+<n> — open that agent's terminal in a new window. The TUI only\n * reports the intent; the host wires it to the watcher so this component\n * stays free of tmux and terminal-spawning concerns.\n */\n onWatchAgent?: (agentName: string) => void;\n onReady: (handle: AppHandle) => void;\n readOnly?: boolean;\n authority?: AuthorityLevel;\n initialSound?: boolean;\n}) {\n const [events, setEvents] = useState<DisplayEvent[]>([]);\n const [agentNames, setAgentNames] = useState<string[]>([]);\n const [participants, setParticipants] = useState<string[]>([]);\n const [input, setInput] = useState(\"\");\n const [cursorPos, setCursorPos] = useState(0);\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [soundEnabled, setSoundEnabled] = useState(initialSound);\n const [agentStates, setAgentStatesMap] = useState<Map<string, AgentState>>(new Map());\n const [agentActivities, setAgentActivitiesMap] = useState<Map<string, { label: string; updatedAt: number }>>(new Map());\n const [agentMetrics, setAgentMetricsMap] = useState<Map<string, AgentMetricsRow>>(new Map());\n // Tick every 5s so activity-staleness rendering refreshes even when no\n // events arrive (otherwise a frozen \"Cooked for 8s\" stays bright forever).\n const [, setStaleTick] = useState(0);\n useEffect(() => {\n const t = setInterval(() => setStaleTick((n) => n + 1), 5000);\n return () => clearInterval(t);\n }, []);\n const [fightMode, setFightMode] = useState(false);\n const fightModeRef = useRef(false);\n const soundRef = useRef(initialSound);\n const stateRef = useRef<Map<string, AgentState>>(new Map());\n const activityRef = useRef<Map<string, { label: string; updatedAt: number }>>(new Map());\n const metricsRef = useRef<Map<string, AgentMetricsRow>>(new Map());\n const decayTimers = useRef<Map<string, NodeJS.Timeout>>(new Map());\n const { stdout } = useStdout();\n const identify = useMemo(makeIdentityAssigner, []);\n\n // Atomic input + cursor update\n const setInputAt = useCallback((newInput: string, newPos: number) => {\n setInput(newInput);\n setCursorPos(Math.max(0, Math.min(newPos, newInput.length)));\n }, []);\n\n // Clamp cursor when input changes externally\n useEffect(() => { setCursorPos(p => Math.min(p, input.length)); }, [input]);\n\n // Event batching — buffer incoming events and flush as a single state update\n // to prevent render thrashing that corrupts the input area mid-keystroke.\n const eventBuffer = useRef<DisplayEvent[]>([]);\n const eventFlushTimer = useRef<NodeJS.Timeout | null>(null);\n\n // Hard cap on retained events — long-running TUIs (days of uptime) would\n // otherwise accumulate tens of thousands of DisplayEvents in React state and\n // make every render measurably slower. The oldest are dropped when full.\n const MAX_EVENTS = 5_000;\n\n const push = useCallback((event: DisplayEvent) => {\n // Bell for incoming messages (not self) and pings\n const shouldBell =\n event.kind === \"ping\" ||\n (event.kind === \"message\" && !event.isSelf);\n if (shouldBell && soundRef.current && stdout.isTTY) stdout.write(\"\\x07\");\n\n // Track fight mode state\n if (event.kind === \"mode\") {\n const entering = event.mode === \"fight\";\n setFightMode(entering);\n fightModeRef.current = entering;\n }\n\n eventBuffer.current.push(event);\n if (!eventFlushTimer.current) {\n eventFlushTimer.current = setTimeout(() => {\n eventFlushTimer.current = null;\n const batch = eventBuffer.current.splice(0);\n if (batch.length > 0) {\n setEvents((prev) => {\n const next = [...prev, ...batch];\n return next.length > MAX_EVENTS ? next.slice(next.length - MAX_EVENTS) : next;\n });\n }\n }, 80);\n }\n }, [stdout]);\n\n const clear = useCallback(() => {\n // Flush any pending events first, then wipe\n if (eventFlushTimer.current) {\n clearTimeout(eventFlushTimer.current);\n eventFlushTimer.current = null;\n }\n eventBuffer.current.length = 0;\n setEvents([]);\n // Ink's <Static> items are already committed to the terminal buffer.\n // Clear the terminal so old messages disappear visually.\n if (stdout.isTTY) stdout.write(\"\\x1b[2J\\x1b[H\");\n }, [stdout]);\n\n const toggleSound = useCallback((): boolean => {\n const next = !soundRef.current;\n soundRef.current = next;\n setSoundEnabled(next);\n return next;\n }, []);\n\n // name → when its state was last read off the agent's own screen.\n const observedAtRef = React.useRef<Map<string, number>>(new Map());\n const [stateSource, setStateSource] = useState<Map<string, \"reported\" | \"inferred\">>(new Map());\n\n const DEFAULT_WORKING_DECAY_MS = INFERRED_DECAY_MS;\n const PENDING_STALL_MS = 30_000;\n const setAgentState = useCallback(\n (name: string, state: AgentState, opts?: SetAgentStateOpts) => {\n const observed = opts?.observed === true;\n if (!shouldApplyAgentState({\n observed,\n lastObservedAt: observedAtRef.current.get(name),\n now: Date.now(),\n })) return;\n if (observed) observedAtRef.current.set(name, Date.now());\n // Remember which it was, so the strip can stop presenting a guess with\n // the same confidence as a fact.\n setStateSource((prev) => {\n const next = new Map(prev);\n next.set(name, observed ? \"reported\" : \"inferred\");\n return next;\n });\n\n // Cancel any existing decay timer — every state change is a fresh signal.\n const existing = decayTimers.current.get(name);\n if (existing) { clearTimeout(existing); decayTimers.current.delete(name); }\n\n stateRef.current.set(name, state);\n setAgentStatesMap(new Map(stateRef.current));\n\n // Decay rules:\n // working → unknown after DEFAULT_WORKING_DECAY_MS (5min)\n // pending → stalled after PENDING_STALL_MS (30s, agent never showed up)\n // idle/unknown/unresponsive/stalled are sticky until the next signal.\n if (state === \"working\") {\n const ms = opts?.decayMs ?? DEFAULT_WORKING_DECAY_MS;\n decayTimers.current.set(name, setTimeout(() => {\n if (stateRef.current.get(name) === \"working\") {\n stateRef.current.set(name, \"unknown\");\n setAgentStatesMap(new Map(stateRef.current));\n }\n decayTimers.current.delete(name);\n }, ms));\n } else if (state === \"pending\") {\n decayTimers.current.set(name, setTimeout(() => {\n if (stateRef.current.get(name) === \"pending\") {\n stateRef.current.set(name, \"stalled\");\n setAgentStatesMap(new Map(stateRef.current));\n }\n decayTimers.current.delete(name);\n }, PENDING_STALL_MS));\n }\n },\n [],\n );\n\n // Live activity label (e.g. \"Sautéed for 12s\", \"Compacting…\") pushed by the\n // agent runtime poll loop. Free-form string; null/empty clears. We stash\n // updatedAt alongside so render can dim the label when it goes stale (the\n // bridge sends only when the label CHANGES, so a long-running \"Cooked for\n // 8s\" can sit forever — if claude has died or the bridge stopped we'd\n // otherwise show frozen state forever).\n const setAgentActivity = useCallback((name: string, label: string | null) => {\n if (label && label.trim()) {\n activityRef.current.set(name, { label: label.trim(), updatedAt: Date.now() });\n } else {\n activityRef.current.delete(name);\n }\n setAgentActivitiesMap(new Map(activityRef.current));\n }, []);\n\n // Last-turn context size (from claude jsonl, broadcast by agent runtime).\n const setAgentMetrics = useCallback((name: string, m: AgentMetricsRow) => {\n metricsRef.current.set(name, m);\n setAgentMetricsMap(new Map(metricsRef.current));\n }, []);\n\n useEffect(() => {\n onReady({ push, clear, setAgentNames, setParticipants, toggleSound, setAgentState, setAgentActivity, setAgentMetrics });\n return () => {\n if (eventFlushTimer.current) clearTimeout(eventFlushTimer.current);\n for (const t of decayTimers.current.values()) clearTimeout(t);\n decayTimers.current.clear();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Sync fightMode ref for use in memoized callbacks\n useEffect(() => { fightModeRef.current = fightMode; }, [fightMode]);\n\n // Note: no resize handler — Ink's <Static> items are already committed to the\n // terminal buffer. Forcing a re-render on resize causes cursor position\n // miscalculation and screen corruption. The divider width updates naturally\n // on the next state change (new event, input change, etc.).\n\n // ── Slash command suggestions ──────────────────────────────────────────────\n\n type SuggestionItem =\n | { kind: \"command\"; cmd: SlashCommand; insert: string }\n | { kind: \"param\"; value: string; insert: string }\n | { kind: \"mention\"; value: string; insert: string };\n\n const suggestionState = useMemo((): { items: SuggestionItem[]; ghostHint: string } => {\n // @mention detection — match @partial at cursor position\n const textBeforeCursor = input.slice(0, cursorPos);\n const mentionMatch = textBeforeCursor.match(/@([a-zA-Z0-9_-]*)$/);\n if (mentionMatch && participants.length > 0) {\n const prefix = mentionMatch[1].toLowerCase();\n const candidates = [\"all\", ...participants.filter((p) => p.toLowerCase() !== \"all\")];\n const filtered = candidates.filter((p) => p.toLowerCase().startsWith(prefix));\n if (filtered.length > 0) {\n const before = input.slice(0, mentionMatch.index!);\n const after = input.slice(cursorPos);\n const items: SuggestionItem[] = filtered.map((p) => ({\n kind: \"mention\" as const,\n value: p,\n insert: before + \"@\" + p + \" \" + after,\n }));\n const ghostHint = prefix.length === 0 ? \"\" : (filtered[0].slice(prefix.length));\n return { items, ghostHint };\n }\n }\n\n if (!input.startsWith(\"/\")) return { items: [], ghostHint: \"\" };\n\n const spaceIdx = input.indexOf(\" \");\n\n // Phase 1: completing command name (no space yet)\n if (spaceIdx === -1) {\n const prefix = input.toLowerCase();\n const items: SuggestionItem[] = SLASH_COMMANDS\n .filter((cmd) => {\n if (cmd.op && !can(authority, cmd.op)) return false;\n return cmd.name.startsWith(prefix);\n })\n .map((cmd) => ({\n kind: \"command\" as const,\n cmd,\n insert: cmd.name + \" \",\n }));\n return { items, ghostHint: \"\" };\n }\n\n // Phase 2: completing params\n const cmdName = input.slice(0, spaceIdx).toLowerCase();\n const cmd = SLASH_COMMANDS.find((c) => c.name === cmdName && (!c.op || can(authority, c.op)));\n if (!cmd?.params) return { items: [], ghostHint: \"\" };\n\n const rest = input.slice(spaceIdx + 1);\n const words = rest.split(/\\s+/);\n const hasTrailingSpace = rest.endsWith(\" \") || rest === \"\";\n const completedCount = hasTrailingSpace\n ? words.filter(Boolean).length\n : Math.max(0, words.length - 1);\n const currentPrefix = hasTrailingSpace ? \"\" : (words[words.length - 1] ?? \"\").toLowerCase();\n const paramIdx = completedCount;\n\n // All params filled\n if (paramIdx >= cmd.params.length) return { items: [], ghostHint: \"\" };\n\n const param = cmd.params[paramIdx];\n\n // Ghost hint: remaining unfilled params (skip current if partially typed)\n const ghostStart = currentPrefix ? paramIdx + 1 : paramIdx;\n const ghostHint = cmd.params.slice(ghostStart).map((p) => `<${p.label}>`).join(\" \");\n\n // No completions defined — hint only\n if (!param.completions) return { items: [], ghostHint };\n\n const values = param.completions === \"participants\" ? participants : param.completions;\n const filtered = currentPrefix\n ? values.filter((v) => v.toLowerCase().startsWith(currentPrefix))\n : values;\n\n // Build insert string: full command up to current param + selected value\n const completedWords = words.slice(0, completedCount).filter(Boolean);\n const base = cmdName + (completedWords.length ? \" \" + completedWords.join(\" \") : \"\") + \" \";\n\n const items: SuggestionItem[] = filtered.map((v) => ({\n kind: \"param\" as const,\n value: v,\n insert: base + v + \" \",\n }));\n\n return { items, ghostHint };\n }, [input, cursorPos, authority, participants]);\n\n const suggestions = suggestionState.items;\n\n // Reset selection when input changes (arrow keys don't change input)\n useEffect(() => { setSelectedIndex(0); }, [input]);\n\n // ── Keyboard ───────────────────────────────────────────────────────────────\n // Single useInput handles everything — no TextInput, no dual-handler conflicts.\n\n useInput((char, key) => {\n if (key.ctrl && char === \"c\") { onCtrlC?.(); return; }\n\n // Ctrl+1..9 — open that agent's terminal in a new window. Works while\n // read-only (watching is not participating), and never blocks the room.\n // A keybinding rather than a click: mouse reporting would take over\n // selection for the whole view, costing click-to-copy everywhere.\n if (key.ctrl && char >= \"1\" && char <= \"9\") {\n const idx = Number(char) - 1;\n const target = agentNames[idx];\n if (target) onWatchAgent?.(target);\n return;\n }\n\n if (readOnly || !onSend) return;\n\n // Suggestion navigation\n if (suggestions.length > 0) {\n if (key.downArrow) {\n setSelectedIndex((i) => Math.min(i + 1, suggestions.length - 1));\n return;\n }\n if (key.upArrow) {\n setSelectedIndex((i) => Math.max(i - 1, 0));\n return;\n }\n if (key.return || key.tab) {\n const picked = suggestions[selectedIndex];\n if (!picked) return;\n // No-param command + Enter → submit directly\n if (key.return && picked.kind === \"command\" && !picked.cmd.params) {\n onSend(picked.cmd.name);\n setInputAt(\"\", 0);\n return;\n }\n setInputAt(picked.insert, picked.insert.length);\n return;\n }\n if (key.escape) {\n setInputAt(\"\", 0);\n return;\n }\n }\n\n // Left/right arrow — cursor navigation\n if (key.leftArrow) {\n setCursorPos((p) => Math.max(0, p - 1));\n return;\n }\n if (key.rightArrow) {\n setCursorPos((p) => Math.min(p + 1, input.length));\n return;\n }\n\n // Ctrl+A → home, Ctrl+E → end\n if (key.ctrl && char === \"a\") {\n setCursorPos(0);\n return;\n }\n if (key.ctrl && char === \"e\") {\n setCursorPos(input.length);\n return;\n }\n\n // Ctrl+W → delete word backward\n if (key.ctrl && char === \"w\") {\n if (cursorPos === 0) return;\n let pos = cursorPos - 1;\n // Skip trailing spaces\n while (pos > 0 && input[pos] === \" \") pos--;\n // Skip word chars\n while (pos > 0 && input[pos - 1] !== \" \") pos--;\n setInputAt(input.slice(0, pos) + input.slice(cursorPos), pos);\n return;\n }\n\n // Option+Enter → newline at cursor\n if (key.return && key.meta) {\n setInputAt(input.slice(0, cursorPos) + \"\\n\" + input.slice(cursorPos), cursorPos + 1);\n return;\n }\n\n // Enter → submit. (Pastes containing newlines arrive as one multi-char\n // event with `\\n` inside `char`, so they never reach this branch — no\n // paste/Enter race to disambiguate.)\n if (key.return) {\n const content = input.trim();\n if (!content) { setInputAt(\"\", 0); return; }\n\n // `! <cmd>` — run locally, render output as system lines. Output stays\n // in your TUI only; agents and other participants see nothing.\n if (content.startsWith(\"!\")) {\n const cmd = content.slice(1).trim();\n setInputAt(\"\", 0);\n if (cmd) runLocalShell(cmd, push);\n return;\n }\n\n onSend(content);\n setInputAt(\"\", 0);\n return;\n }\n\n // Backspace — delete at cursor position\n if (key.backspace || key.delete) {\n if (cursorPos > 0) {\n setInputAt(input.slice(0, cursorPos - 1) + input.slice(cursorPos), cursorPos - 1);\n }\n return;\n }\n\n // Ignore remaining special keys\n if (key.ctrl || key.meta || key.escape || key.tab ||\n key.upArrow || key.downArrow) {\n return;\n }\n\n // Regular character or multi-char paste. ink batches paste into one event,\n // so `char` may be many characters with embedded newlines — insert as-is.\n if (char) {\n const normalized = char.replace(/\\r\\n?/g, \"\\n\");\n setInputAt(\n input.slice(0, cursorPos) + normalized + input.slice(cursorPos),\n cursorPos + normalized.length,\n );\n }\n });\n\n const cols = stdout.columns ?? 80;\n\n // Static items: banner (rendered once) + events (appended over time)\n const entries: StaticEntry[] = useMemo(() => {\n let msgIdx = 0;\n return [{ id: \"__banner__\" }, ...events.map((e) => {\n const isMsg = e.kind === \"message\" && !e.isSelf;\n const entry: StaticEntry = { id: e.id, event: e, zebra: isMsg ? msgIdx % 2 === 1 : undefined };\n if (isMsg) msgIdx++;\n return entry;\n })];\n }, [events]);\n\n return (\n <>\n {/* Permanent output — rendered once, selectable terminal text */}\n <Static items={entries}>\n {(entry) => {\n if (!entry.event) {\n return (\n <Box key={entry.id} flexDirection=\"column\" paddingX={2} paddingTop={1} paddingBottom={1}>\n {BANNER.map((segments, i) => (\n <Text key={i}>{segments.map((s, j) => (\n <Text key={j} color={s.color}>{s.text}</Text>\n ))}</Text>\n ))}\n <Text color={C.muted} italic>{\" ah — it's not a sad alien, it's a bee\"}</Text>\n <Text>{\" \"}</Text>\n <Text>\n <Text color={C.dim}>{\" room \"}</Text>\n <Text color={C.cyan} bold>{roomName}</Text>\n </Text>\n </Box>\n );\n }\n return <EventLine key={entry.id} event={entry.event} identify={identify} cols={cols} zebra={entry.zebra} fightMode={fightMode} />;\n }}\n </Static>\n\n {/* Dynamic footer — only this area repaints */}\n {fightMode ? (\n <Box paddingX={1}>\n <Text color=\"#f87171\" bold>{\"═\"}</Text>\n <Text color=\"#ff4444\">{\"═\".repeat(Math.max(0, cols - 4))}</Text>\n <Text color=\"#f87171\" bold>{\"═\"}</Text>\n </Box>\n ) : (\n <Box paddingX={1}>\n <Text color={C.orange}>{\"─\"}</Text>\n <Text color={C.border}>{\"─\".repeat(Math.max(0, cols - 4))}</Text>\n <Text color={C.yellow}>{\"─\"}</Text>\n </Box>\n )}\n {agentMetrics.size > 0 && (() => {\n // Roll up context across all known agents (not just currently-\n // visible ones — a disconnected agent's last context still counts).\n let totalCtx = 0;\n for (const m of agentMetrics.values()) {\n totalCtx += m.ctxTokens;\n }\n return (\n <Box paddingX={1}>\n <Text color={C.muted}>{\" room \"}</Text>\n <Text color={C.muted}>{`${formatTokens(totalCtx)} ctx total`}</Text>\n </Box>\n );\n })()}\n {agentNames.length > 0 && (\n <Box paddingX={1} flexWrap=\"wrap\">\n {agentNames.map((name, i) => {\n const { color, sigil } = identify(name);\n const state = agentStates.get(name) ?? \"unknown\";\n const activity = agentActivities.get(name);\n const metrics = agentMetrics.get(name);\n // Color is locked to identity (same color in footer, borders, @mentions).\n // State is communicated by a suffix icon — never by changing the color.\n const stateSuffix = (() => {\n switch (state) {\n case \"pending\": return { glyph: \" ⏳ booting\", color: C.yellow };\n case \"stalled\": return { glyph: \" ⚠ stalled\", color: C.danger };\n case \"idle\": return { glyph: \" ✓\", color: C.green };\n case \"blocked\": return { glyph: \" ⏸ needs you\", color: C.orange };\n case \"working\": return { glyph: \" …\", color: C.yellow };\n case \"unresponsive\": return { glyph: \" ⚠\", color: C.danger };\n case \"unknown\":\n default: return { glyph: \" zzz\", color: C.muted };\n }\n })();\n // A guess is drawn dim, a reported state in its own colour. Both\n // used to look identical, so \"…\" could mean \"the runtime says it is\n // working\" or \"somebody spoke five minutes ago and nobody has\n // heard from this agent since\" — indistinguishable, and the second\n // one is why the strip felt like it was lying.\n const inferred = stateSource.get(name) === \"inferred\";\n const glyphColor = inferred ? C.muted : stateSuffix.color;\n\n // A stale activity label is DROPPED, not dimmed. \"Baked for 1m 35s\"\n // from three minutes ago is not faded information, it is wrong\n // information — the run it describes finished long ago.\n const STALE_MS = 30_000;\n const fresh = activity && Date.now() - activity.updatedAt <= STALE_MS;\n const metricsText = metrics\n ? ` ${formatTokens(metrics.ctxTokens)}`\n : \"\";\n return (\n <React.Fragment key={name}>\n {i > 0 && <Text color={C.border}>{\" · \"}</Text>}\n <Text color={color}>{sigil}{\" \"}{name}</Text>\n <Text color={glyphColor}>{stateSuffix.glyph}</Text>\n {/* The affordance for Ctrl+<n> / \"/watch <name>\". Only shown\n for the first 9, which are the ones the binding reaches. */}\n {i < 9 && <Text color={C.border}>{` ⧉${i + 1}`}</Text>}\n {fresh && <Text color={C.yellow}>{\" \"}{activity!.label}</Text>}\n {metricsText && <Text color={C.muted}>{metricsText}</Text>}\n </React.Fragment>\n );\n })}\n </Box>\n )}\n {readOnly || !onSend ? (\n <Box paddingX={1}>\n <Text color={C.muted}>{\" watching as guest\"}</Text>\n </Box>\n ) : (\n <Box paddingX={1} flexDirection=\"column\">\n {/* Render visible lines with cursor — scroll to keep cursor in view */}\n {(() => {\n const MAX_INPUT_LINES = 6;\n const lines = (input || \"\").split(\"\\n\");\n // Find which line the cursor is on and the column within that line\n let charsSoFar = 0;\n let cursorLine = lines.length - 1;\n let cursorCol = 0;\n for (let i = 0; i < lines.length; i++) {\n const lineEnd = charsSoFar + lines[i].length;\n if (cursorPos <= lineEnd) {\n cursorLine = i;\n cursorCol = cursorPos - charsSoFar;\n break;\n }\n charsSoFar += lines[i].length + 1; // +1 for \\n\n }\n\n // Compute visible window that keeps cursorLine in view\n let scrollTop = 0;\n if (lines.length > MAX_INPUT_LINES) {\n // Centre cursor in window, clamped to bounds\n scrollTop = Math.min(\n Math.max(0, cursorLine - Math.floor(MAX_INPUT_LINES / 2)),\n lines.length - MAX_INPUT_LINES,\n );\n }\n const visibleLines = lines.slice(scrollTop, scrollTop + MAX_INPUT_LINES);\n const hasMore = lines.length > MAX_INPUT_LINES;\n\n return (\n <>\n {hasMore && scrollTop > 0 && (\n <Box width={Math.max(0, cols - 2)}>\n <Text color={C.dim}>{\" ↑ \"}{scrollTop}{\" more line\"}{scrollTop > 1 ? \"s\" : \"\"}</Text>\n </Box>\n )}\n {visibleLines.map((line, vi) => {\n const i = scrollTop + vi;\n return (\n <Box key={i} width={Math.max(0, cols - 2)}>\n <Text color={fightMode ? \"#f87171\" : C.cyan} bold>{i === 0 ? (fightMode ? \"⚔ \" : \"› \") : \" \"}</Text>\n <Text wrap=\"wrap\">\n {i === cursorLine ? (\n <>\n <Text>{line.slice(0, cursorCol)}</Text>\n <Text inverse>{cursorCol < line.length ? line[cursorCol] : \" \"}</Text>\n <Text>{cursorCol < line.length ? line.slice(cursorCol + 1) : \"\"}</Text>\n </>\n ) : (\n line\n )}\n {i === lines.length - 1 && cursorPos === input.length && suggestionState.ghostHint !== \"\" && (\n <Text color={C.muted}>{suggestionState.ghostHint}</Text>\n )}\n </Text>\n </Box>\n );\n })}\n {hasMore && scrollTop + MAX_INPUT_LINES < lines.length && (\n <Box width={Math.max(0, cols - 2)}>\n <Text color={C.dim}>{\" ↓ \"}{lines.length - scrollTop - MAX_INPUT_LINES}{\" more line\"}{lines.length - scrollTop - MAX_INPUT_LINES > 1 ? \"s\" : \"\"}</Text>\n </Box>\n )}\n </>\n );\n })()}\n </Box>\n )}\n {/* Slash command suggestions — below input */}\n {suggestions.length > 0 && (\n <Box flexDirection=\"column\" paddingX={1}>\n {suggestions.map((s, i) => {\n const selected = i === selectedIndex;\n if (s.kind === \"command\") {\n const paramHint = s.cmd.params\n ? \" \" + s.cmd.params.map((p) => `<${p.label}>`).join(\" \")\n : \"\";\n const display = s.cmd.name + paramHint;\n return (\n <Box key={s.cmd.name}>\n <Text color={selected ? C.cyan : C.muted}>{selected ? \"› \" : \" \"}</Text>\n <Text>\n <Text color={selected ? C.cyan : C.secondary} bold={selected}>\n {s.cmd.name}\n </Text>\n <Text color={C.muted}>\n {paramHint.padEnd(CMD_DISPLAY_COL - display.length + paramHint.length)}\n </Text>\n </Text>\n <Text color={C.dim}>{s.cmd.description}</Text>\n </Box>\n );\n }\n return (\n <Box key={s.value}>\n <Text color={selected ? C.cyan : C.muted}>{selected ? \"› \" : \" \"}</Text>\n {s.kind === \"mention\" && <Text color={C.dim}>{\"@\"}</Text>}\n <Text color={selected ? C.cyan : C.secondary} bold={selected}>{s.value}</Text>\n </Box>\n );\n })}\n </Box>\n )}\n </>\n );\n}\n\n// ── startTUI ──────────────────────────────────────────────────────────────────\n\nexport function startTUI(opts: TUIOptions): TUIHandle {\n let handle: AppHandle | null = null;\n const queue: DisplayEvent[] = [];\n\n const onReady = (h: AppHandle) => {\n handle = h;\n for (const event of queue.splice(0)) h.push(event);\n };\n\n const { unmount } = render(\n <App\n roomName={opts.roomName}\n onSend={opts.onSend}\n onCtrlC={opts.onCtrlC}\n onWatchAgent={opts.onWatchAgent}\n onReady={onReady}\n readOnly={opts.readOnly}\n authority={opts.authority}\n initialSound={opts.soundEnabled ?? true}\n />,\n { exitOnCtrlC: false },\n );\n\n return {\n push(event) {\n if (handle) handle.push(event);\n else queue.push(event);\n },\n clear() {\n handle?.clear();\n },\n setAgentNames(names) {\n handle?.setAgentNames(names);\n },\n setParticipants(names) {\n handle?.setParticipants(names);\n },\n toggleSound() {\n return handle?.toggleSound() ?? false;\n },\n setAgentState(name, state, opts) {\n handle?.setAgentState(name, state, opts);\n },\n setAgentActivity(name, label) {\n handle?.setAgentActivity(name, label);\n },\n setAgentMetrics(name, m) {\n handle?.setAgentMetrics(name, m);\n },\n stop() {\n unmount();\n },\n };\n}\n","/**\n * Agent watcher — open an agent's tmux session in a NEW terminal window.\n *\n * Deliberately a second tmux client on the agent's existing session, in its\n * own window: the room TUI keeps running untouched and stays usable, no pane\n * moves, no session is destroyed. (`tmux join-pane` would pull the agent's\n * pane into the room's window, but moving the last pane out of a session\n * destroys that session — and the bridges address agents by session name, so\n * event delivery would silently die.)\n *\n * Two things this has to get right, both found the hard way:\n *\n * 1. Terminals launch the command WITHOUT a login shell, so `tmux` is not\n * on PATH — the window opens and instantly dies. Always pass an absolute\n * path.\n * 2. Attaching resizes the session to the newest client, read-only clients\n * included, which reflows the pane the bridges pattern-match on. Agent\n * sessions are created with `window-size manual` (see tmux.ts) so this\n * cannot happen; watchers letterbox instead.\n */\n\nimport { execFileSync, spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\n\n/** Where tmux actually lives. Terminals get no login shell, so PATH is not enough. */\nexport function resolveTmuxPath(env: NodeJS.ProcessEnv = process.env): string | null {\n try {\n const found = execFileSync(\"which\", [\"tmux\"], { encoding: \"utf-8\", env }).trim();\n if (found) return found;\n } catch {\n /* fall through to the usual suspects */\n }\n for (const candidate of [\"/opt/homebrew/bin/tmux\", \"/usr/local/bin/tmux\", \"/usr/bin/tmux\"]) {\n if (existsSync(candidate)) return candidate;\n }\n return null;\n}\n\nexport type TerminalKind = \"iterm\" | \"apple-terminal\" | \"ghostty\" | \"wezterm\" | \"kitty\" | \"alacritty\";\n\n/** A terminal we know how to open a new window in, and how. */\nexport interface TerminalLauncher {\n kind: TerminalKind;\n /** True if this terminal is usable on this machine. */\n available(env: NodeJS.ProcessEnv): boolean;\n /** argv that opens a new window running `command`. */\n argv(command: string): string[];\n}\n\n/** Quote for AppleScript's double-quoted string literals. */\nfunction osaQuote(value: string): string {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Known terminals, in fallback order. Exported so tests can inject their own\n * table — availability here probes the real filesystem, and a test asserting\n * \"some terminal exists on this machine\" passes on a laptop and fails on a\n * Linux CI runner, which says nothing about the selection logic.\n */\nexport const LAUNCHERS: TerminalLauncher[] = [\n {\n kind: \"iterm\",\n available: (env) => env.TERM_PROGRAM === \"iTerm.app\" || existsSync(\"/Applications/iTerm.app\"),\n argv: (command) => [\"osascript\", \"-e\",\n `tell application \"iTerm\"\\n create window with default profile command \"${osaQuote(command)}\"\\n activate\\nend tell`,\n ],\n },\n {\n kind: \"ghostty\",\n available: (env) => env.TERM_PROGRAM === \"ghostty\" || existsSync(\"/Applications/Ghostty.app\"),\n argv: (command) => [\"open\", \"-na\", \"Ghostty\", \"--args\", \"-e\", command],\n },\n {\n kind: \"wezterm\",\n available: () => commandExists(\"wezterm\"),\n argv: (command) => [\"wezterm\", \"start\", \"--\", \"sh\", \"-c\", command],\n },\n {\n kind: \"kitty\",\n available: () => commandExists(\"kitty\"),\n argv: (command) => [\"kitty\", \"sh\", \"-c\", command],\n },\n {\n kind: \"alacritty\",\n available: () => commandExists(\"alacritty\"),\n argv: (command) => [\"alacritty\", \"-e\", \"sh\", \"-c\", command],\n },\n {\n // Last resort: every mac has it, but it steals focus and ignores profiles.\n kind: \"apple-terminal\",\n available: () => existsSync(\"/System/Applications/Utilities/Terminal.app\")\n || existsSync(\"/Applications/Utilities/Terminal.app\"),\n argv: (command) => [\"osascript\", \"-e\",\n `tell application \"Terminal\"\\n do script \"${osaQuote(command)}\"\\n activate\\nend tell`,\n ],\n },\n];\n\nfunction commandExists(bin: string): boolean {\n try {\n execFileSync(\"which\", [bin], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Pick a terminal to open. Prefers the one the user is already in, so the\n * watcher window matches their theme and font, then falls back down the list.\n */\nexport function pickTerminal(\n env: NodeJS.ProcessEnv = process.env,\n launchers: readonly TerminalLauncher[] = LAUNCHERS,\n): TerminalLauncher | null {\n const current = launchers.find((l) => l.kind === terminalFromEnv(env));\n if (current?.available(env)) return current;\n return launchers.find((l) => l.available(env)) ?? null;\n}\n\n/** The launcher for one terminal, regardless of whether it is installed. */\nexport function launcherFor(kind: TerminalKind): TerminalLauncher {\n const found = LAUNCHERS.find((l) => l.kind === kind);\n if (!found) throw new Error(`no launcher for ${kind}`);\n return found;\n}\n\n/** The terminal apiary is currently running inside, if we recognise it. */\nexport function terminalFromEnv(env: NodeJS.ProcessEnv = process.env): TerminalKind | null {\n switch (env.TERM_PROGRAM) {\n case \"iTerm.app\": return \"iterm\";\n case \"Apple_Terminal\": return \"apple-terminal\";\n case \"ghostty\": return \"ghostty\";\n case \"WezTerm\": return \"wezterm\";\n default: break;\n }\n if (env.KITTY_WINDOW_ID) return \"kitty\";\n if (env.ALACRITTY_WINDOW_ID) return \"alacritty\";\n return null;\n}\n\n/**\n * The tmux command a watcher window runs.\n *\n * Read-only by default: a stray keystroke in an agent's pane types into its\n * composer and submits whatever was there. `control` gives a writable client\n * for when you actually mean to drive it.\n */\nexport function watchCommand(\n tmuxPath: string,\n tmuxSession: string,\n opts?: { control?: boolean },\n): string {\n const flags = opts?.control ? [\"attach\"] : [\"attach\", \"-r\"];\n return [tmuxPath, ...flags, \"-t\", tmuxSession].join(\" \");\n}\n\nexport type WatchOutcome =\n | { ok: true; terminal: TerminalKind; readOnly: boolean }\n | { ok: false; reason: string };\n\n/**\n * Open a new terminal window watching `tmuxSession`.\n *\n * Never throws and never blocks: the child is detached and unref'd so the room\n * TUI carries on regardless of what the terminal does.\n */\nexport function openAgentWatcher(\n tmuxSession: string,\n opts?: { control?: boolean; env?: NodeJS.ProcessEnv },\n): WatchOutcome {\n const env = opts?.env ?? process.env;\n\n const tmuxPath = resolveTmuxPath(env);\n if (!tmuxPath) return { ok: false, reason: \"tmux not found\" };\n\n const launcher = pickTerminal(env);\n if (!launcher) return { ok: false, reason: \"no supported terminal found\" };\n\n const [bin, ...args] = launcher.argv(watchCommand(tmuxPath, tmuxSession, opts));\n\n try {\n const child = spawn(bin, args, { detached: true, stdio: \"ignore\" });\n child.unref();\n } catch {\n return { ok: false, reason: `could not launch ${launcher.kind}` };\n }\n\n return { ok: true, terminal: launcher.kind, readOnly: !opts?.control };\n}\n"],"mappings":"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","names":["filtered","items","ghostHint","cmd","opts","name"]}
|