@gorlitzer-labs/apiary 1.13.6 → 1.13.8

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.
Files changed (29) hide show
  1. package/README.md +31 -6
  2. package/dist/agent/index.js +2 -2
  3. package/dist/{chunk-7JXPJO3R.js → chunk-3O76FESR.js} +4 -4
  4. package/dist/{chunk-4A7MREYX.js → chunk-63Q3KJ4W.js} +2 -2
  5. package/dist/{chunk-BCQQJR5E.js → chunk-FTN3D6WM.js} +3 -3
  6. package/dist/{chunk-MEWZT5KA.js → chunk-J3T2PS3M.js} +2 -2
  7. package/dist/{chunk-TISBXM4R.js → chunk-LN6ATWE3.js} +157 -157
  8. package/dist/chunk-LN6ATWE3.js.map +1 -0
  9. package/dist/{chunk-HTNCLNPX.js → chunk-XKFWEK7W.js} +4 -4
  10. package/dist/{chunk-TMXQDGMN.js → chunk-YP4K2A7G.js} +30 -30
  11. package/dist/chunk-YP4K2A7G.js.map +1 -0
  12. package/dist/cli/index.js +62 -44
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/{invites-GRROUSCE.js → invites-YZWCUC67.js} +5 -5
  15. package/dist/{join-H62VB4HU.js → join-FSKOS5BO.js} +5 -5
  16. package/dist/{room-IHVB5CWO.js → room-MAUAV2XD.js} +7 -7
  17. package/dist/{room-IHVB5CWO.js.map → room-MAUAV2XD.js.map} +1 -1
  18. package/dist/{run-UJO7NO3C.js → run-4EJB2KC3.js} +5 -5
  19. package/package.json +1 -1
  20. package/dist/chunk-TISBXM4R.js.map +0 -1
  21. package/dist/chunk-TMXQDGMN.js.map +0 -1
  22. /package/dist/{chunk-7JXPJO3R.js.map → chunk-3O76FESR.js.map} +0 -0
  23. /package/dist/{chunk-4A7MREYX.js.map → chunk-63Q3KJ4W.js.map} +0 -0
  24. /package/dist/{chunk-BCQQJR5E.js.map → chunk-FTN3D6WM.js.map} +0 -0
  25. /package/dist/{chunk-MEWZT5KA.js.map → chunk-J3T2PS3M.js.map} +0 -0
  26. /package/dist/{chunk-HTNCLNPX.js.map → chunk-XKFWEK7W.js.map} +0 -0
  27. /package/dist/{invites-GRROUSCE.js.map → invites-YZWCUC67.js.map} +0 -0
  28. /package/dist/{join-H62VB4HU.js.map → join-FSKOS5BO.js.map} +0 -0
  29. /package/dist/{run-UJO7NO3C.js.map → run-4EJB2KC3.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/claude/run.ts","../../src/cli/composer.ts","../../src/cli/claude/tmux-bridge.ts","../../src/cli/runtime-setup.ts","../../src/cli/agent-state.ts","../../src/cli/approvals.ts","../../src/cli/claude/trust.ts","../../src/cli/claude/jsonl-stats.ts","../../src/cli/opencode/run.ts","../../src/cli/codex/run.ts","../../src/cli/codex/queue.ts","../../src/cli/codex/tmux-bridge.ts","../../src/cli/codex/launch.ts","../../src/cli/version.ts","../../src/cli/help.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * apiary claude — client-side agent runtime for Claude Code.\n *\n * Uses the shared runtime setup (EventProcessor, MCP server)\n * then adds Claude-specific pieces: tmux session + TmuxBridge delivery.\n *\n * Claude Code connects to the runtime MCP server via a stdio bridge\n * (Claude's HTTP MCP transport requires OAuth, which hangs on localhost).\n * The agent joins rooms by calling join_room() — no auto-injection needed.\n */\n\nimport { writeFileSync, mkdtempSync, mkdirSync, rmSync, chmodSync, existsSync, readFileSync, readdirSync, realpathSync, renameSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir, homedir } from \"node:os\";\n\nimport {\n tmuxAvailable,\n tmuxCreateSession,\n tmuxSendCommand,\n tmuxAttach,\n tmuxKillSession,\n tmuxSessionExists,\n resetTerminal,\n} from \"../tmux.js\";\nimport { TmuxBridge } from \"./tmux-bridge.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\nimport { claudeStateToActivity } from \"../agent-state.js\";\nimport { wantsUnattendedAgents } from \"../approvals.js\";\nimport { preAcceptClaudeTrust } from \"./trust.js\";\nimport { shouldReportActivity, type AgentActivityState } from \"../../agent/event-processor.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport { agentEmoji } from \"../config.js\";\nimport { deliverInvite, sameApiaryServer } from \"../invites.js\";\nimport { readAgentMetrics } from \"./jsonl-stats.js\";\n\nconst SKIP_PERMISSIONS_FLAG = \"--dangerously-skip-permissions\";\n\nexport { type AgentRuntimeOptions as RunClaudeOptions };\n\n// ── Session state persistence ─────────────────────────────────────────────────\n\nconst SESSION_DIR = join(homedir(), \".apiary\", \"sessions\");\n\ninterface PersistedSession {\n tmuxSession: string;\n agentName: string;\n tmpDir: string;\n mcpPort: string;\n pid: number;\n}\n\nfunction sessionFilePath(name: string): string {\n return join(SESSION_DIR, `claude_${name}.json`);\n}\n\nfunction saveSession(session: PersistedSession): void {\n const dir = SESSION_DIR;\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(sessionFilePath(session.agentName), JSON.stringify(session, null, 2));\n}\n\nfunction loadSession(name: string): PersistedSession | null {\n const path = sessionFilePath(name);\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"));\n } catch {\n return null;\n }\n}\n\nfunction clearSession(name: string): void {\n const path = sessionFilePath(name);\n try { rmSync(path); } catch { /* ok */ }\n}\n\n/**\n * Pre-accept Claude Code's first-run onboarding dialogs for this cwd so a\n * background-spawned agent (no human attached to the tmux pane) doesn't hang\n * forever at a blocking prompt.\n *\n * Claude shows two blocking screens during `showSetupScreens()`:\n *\n * 1. \"Quick safety check: is this a project you trust?\" — gated on\n * `projects[<realpath>].hasTrustDialogAccepted`. Neither\n * `--dangerously-skip-permissions` nor `--permission-mode bypassPermissions`\n * bypasses it (those gate tool calls, not workspace trust).\n * 2. Per-project onboarding hints — gated on `hasCompletedProjectOnboarding`\n * and `projectOnboardingSeenCount`.\n *\n * Pre-writing these three keys is what claude itself does after the user\n * clicks through. Best-effort: if claude.json is missing or unparseable we\n * silently skip rather than create/corrupt the file. The key MUST be the\n * realpath because claude resolves symlinks before lookup (`/tmp` →\n * `/private/tmp` on macOS).\n */\nfunction pretrustCwd(cwd: string): void {\n try {\n const path = join(homedir(), \".claude.json\");\n if (!existsSync(path)) return;\n const real = realpathSync(cwd);\n const raw = readFileSync(path, \"utf-8\");\n const data = JSON.parse(raw);\n if (typeof data !== \"object\" || data === null) return;\n const projects = (data.projects ??= {});\n const entry = (projects[real] ??= {});\n\n const needsWrite =\n entry.hasTrustDialogAccepted !== true ||\n entry.hasCompletedProjectOnboarding !== true ||\n !(typeof entry.projectOnboardingSeenCount === \"number\" && entry.projectOnboardingSeenCount >= 1);\n if (!needsWrite) return;\n\n entry.hasTrustDialogAccepted = true;\n entry.hasCompletedProjectOnboarding = true;\n if (!(typeof entry.projectOnboardingSeenCount === \"number\" && entry.projectOnboardingSeenCount >= 1)) {\n entry.projectOnboardingSeenCount = 1;\n }\n\n // Atomic write so a concurrent claude reading the file never sees partial JSON\n const tmpPath = `${path}.apiary-${process.pid}.tmp`;\n writeFileSync(tmpPath, JSON.stringify(data, null, 2));\n renameSync(tmpPath, path);\n } catch {\n // Don't block agent spawn on a settings hiccup\n }\n}\n\n/** List all active Claude sessions. */\nexport function listClaudeSessions(): PersistedSession[] {\n if (!existsSync(SESSION_DIR)) return [];\n const files = readdirSync(SESSION_DIR) as string[];\n const sessions: PersistedSession[] = [];\n for (const f of files) {\n if (!f.startsWith(\"claude_\") || !f.endsWith(\".json\")) continue;\n try {\n const s = JSON.parse(readFileSync(join(SESSION_DIR, f), \"utf-8\"));\n sessions.push(s);\n } catch { /* skip */ }\n }\n return sessions;\n}\n\n/**\n * Stdio-to-HTTP bridge script. Written to a temp file and spawned by Claude Code\n * as an MCP stdio server. Proxies JSON-RPC messages to the runtime HTTP MCP server.\n *\n * The HTTP MCP server returns SSE-formatted responses (event: message\\ndata: {...}).\n * The bridge extracts the JSON from data: lines and writes raw JSON-RPC to stdout.\n */\n// CommonJS (.cjs) for minimal startup latency — ESM requires module parsing which\n// can exceed Claude Code's MCP handshake timeout on first run.\nconst MCP_STDIO_BRIDGE = [\n '#!/usr/bin/env node',\n '\"use strict\";',\n 'const { createInterface } = require(\"readline\");',\n 'const url = `http://127.0.0.1:${process.argv[2]}/mcp`;',\n 'const rl = createInterface({ input: process.stdin });',\n '(async () => {',\n ' for await (const line of rl) {',\n ' if (!line.trim()) continue;',\n ' try {',\n ' const res = await fetch(url, {',\n ' method: \"POST\",',\n ' headers: { \"Content-Type\": \"application/json\", Accept: \"application/json, text/event-stream\" },',\n ' body: line,',\n ' });',\n ' if (res.status === 202) continue;',\n ' const body = await res.text();',\n ' for (const bl of body.split(\"\\\\n\")) {',\n ' const m = bl.match(/^data: (.+)/);',\n ' if (m) process.stdout.write(m[1] + \"\\\\n\");',\n ' }',\n ' } catch {',\n ' process.exit(1);',\n ' }',\n ' }',\n '})();',\n].join('\\n');\n\nexport async function runClaude(options: AgentRuntimeOptions): Promise<void> {\n // ── Headless mode — skip tmux, deliver events as plain text to stdout ────\n\n if (options.headless) {\n const setup = await setupAgentRuntime(options);\n\n const deliver = async (parts: Parameters<typeof contentPartsToString>[0]) => {\n const text = contentPartsToString(parts);\n if (text.trim()) process.stdout.write(text + \"\\n\");\n };\n\n const eventLoopPromise = setup.processor\n .run(deliver, setup.wrappedSource, setup.initialParts)\n .catch(() => {});\n\n process.stderr.write(`MCP server: ${setup.mcpServer.url}\\n`);\n\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", resolve);\n process.on(\"SIGTERM\", resolve);\n });\n\n await setup.cleanup();\n await eventLoopPromise;\n return;\n }\n\n // ── Preflight checks ────────────────────────────────────────────────────\n\n if (!tmuxAvailable()) {\n console.error(\"Error: tmux is required but not found. Install it with: brew install tmux\");\n process.exit(1);\n }\n\n // ── Resume existing session ─────────────────────────────────────────────\n\n if (options.resume) {\n return resumeClaude(options);\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // Don't pass joinUrls — Claude Code agents join rooms manually via join_room()\n\n const setup = await setupAgentRuntime({ ...options, joinUrls: undefined });\n\n // ── Write MCP stdio bridge + config ────────────────────────────────────\n\n const tmpDir = mkdtempSync(join(tmpdir(), \"apiary_agent_\"));\n\n const bridgePath = join(tmpDir, \"mcp-bridge.cjs\");\n writeFileSync(bridgePath, MCP_STDIO_BRIDGE);\n chmodSync(bridgePath, 0o755);\n\n const mcpPort = new URL(setup.mcpServer.url).port;\n const mcpConfigPath = join(tmpDir, \"mcp.json\");\n\n // Use process.execPath (absolute path to Node) so the bridge works regardless\n // of whether `node` is in the tmux session's PATH.\n const mcpConfig = {\n mcpServers: {\n apiary: {\n type: \"stdio\",\n command: process.execPath,\n args: [bridgePath, mcpPort],\n },\n },\n };\n writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));\n\n // ── Create tmux session + launch Claude Code ────────────────────────────\n\n const tmuxSession = `apiary_${setup.agentName}`;\n\n if (tmuxSessionExists(tmuxSession)) {\n tmuxKillSession(tmuxSession);\n }\n\n const home = homedir();\n const cwdFull = process.cwd();\n const cwdShort = cwdFull.startsWith(home) ? \"~\" + cwdFull.slice(home.length) : cwdFull;\n const tabTitle = `${agentEmoji(setup.agentName)} ${setup.agentName} · ${cwdShort}`;\n\n console.log(\"Launching Claude Code...\");\n tmuxCreateSession(tmuxSession, tabTitle);\n\n // Pre-accept claude's workspace-trust dialog for this cwd. Otherwise\n // background-spawned agents (no human attached to the tmux pane) hang\n // forever at the dialog and never reach the MCP-load step.\n pretrustCwd(process.cwd());\n\n // Launch claude with MCP config + any passthrough args.\n // --strict-mcp-config so we ignore the user's global MCP servers (playwright,\n // project .mcp.json, etc.). Otherwise the agent pays tool-schema tax on\n // every request for tools it will never call — measured 17K tokens of\n // unused schema vs 24K of actual conversation in one real session.\n // Permission prompts are skipped for every apiary agent, not just\n // wizard-spawned ones. An agent in a room has nobody watching its pane, so a\n // prompt is a permanent stall — it cannot call apiary__join_room, cannot\n // answer, and from the room's side looks like it is thinking. Opt out with\n // APIARY_AGENT_APPROVALS=ask.\n // Claude's trust dialog is not skippable by any flag, so pre-record the\n // acceptance for this agent's cwd or it stalls on a modal nobody will see.\n if (wantsUnattendedAgents()) {\n const trust = preAcceptClaudeTrust(process.cwd());\n if (trust === \"failed\" && !options.background) {\n console.log(\"Note: could not pre-accept Claude's folder-trust dialog — the agent may ask once.\");\n }\n }\n\n const extraArgs = options.extraArgs ?? [];\n const flags = [`claude --mcp-config ${mcpConfigPath} --strict-mcp-config`];\n // Don't duplicate it if the caller already passed it themselves.\n if (wantsUnattendedAgents() && !extraArgs.includes(SKIP_PERMISSIONS_FLAG)) {\n flags.push(SKIP_PERMISSIONS_FLAG);\n }\n const claudeCmd = [...flags, ...extraArgs].join(\" \");\n tmuxSendCommand(tmuxSession, claudeCmd);\n\n // ── Start event loop + attach ──────────────────────────────────────────\n\n const bridge = new TmuxBridge(tmuxSession);\n\n // Start the event loop in the background — no initial injection.\n // The agent joins rooms by calling join_room() when the user tells it to.\n const eventLoopPromise = setup.processor.run(bridge.deliver.bind(bridge), setup.wrappedSource)\n .catch(() => {}); // Prevent unhandled rejection from crashing the process\n\n // Activity heartbeat — every ~1.5s, scrape claude's status line (\"Sautéed\n // for 12s\", \"Compacting…\", etc.) and push to all connected rooms when it\n // changes. Gives the room TUI live \"is the agent stuck or working?\" signal\n // instead of just a static green dot.\n let lastActivityLabel: string | null = null;\n let lastState: AgentActivityState | undefined;\n let lastReportAt = 0;\n const activityTimer: NodeJS.Timeout = setInterval(() => {\n let label: string | null;\n let state: AgentActivityState | undefined;\n try {\n label = bridge.getActivityLabel();\n // detectReportedState, not detectState: the latter is the delivery\n // gate and deliberately over-matches prompts, which is right for\n // deciding whether to type and wrong for telling a human \"needs you\".\n state = claudeStateToActivity(bridge.detectReportedState());\n } catch { return; }\n // Report the state as well as the label. A permission prompt shows no\n // activity label at all, so on the label alone the room cannot tell\n // \"waiting for you\" from \"quietly working\" — and only one of those ends\n // without the user doing something.\n // Re-send an unchanged state periodically, not just on change. The room\n // server ages reported state out so a dead runtime does not pin a stale\n // value — but with change-only reporting a settled agent stops reporting,\n // ages out, and reads as \"not reporting at all\". Observed end-to-end: an\n // idle agent's state vanished ~15s after it settled. One tiny POST per\n // agent per interval is nothing next to being wrong about who is idle.\n if (!shouldReportActivity({\n label, state,\n lastLabel: lastActivityLabel, lastState,\n lastReportAt, now: Date.now(),\n })) return;\n lastActivityLabel = label;\n lastState = state;\n lastReportAt = Date.now();\n setup.processor.broadcastActivity(label, state).catch(() => { /* best-effort */ });\n }, 1500);\n activityTimer.unref();\n\n // Metrics heartbeat — every ~10s, re-parse claude's session jsonl to\n // pull last-turn context size. Broadcast only when the context number\n // actually changes (avoid spamming the room on idle ticks).\n let lastBroadcastCtx = -1;\n const metricsTimer: NodeJS.Timeout = setInterval(() => {\n try {\n const m = readAgentMetrics(process.cwd());\n if (!m) return;\n if (m.lastTurnContextTokens === lastBroadcastCtx) return;\n lastBroadcastCtx = m.lastTurnContextTokens;\n setup.processor.broadcastMetrics({\n input_tokens: m.inputTokens,\n output_tokens: m.outputTokens,\n cache_read_tokens: m.cacheReadTokens,\n cache_create_tokens: m.cacheCreateTokens,\n last_ctx_tokens: m.lastTurnContextTokens,\n model: m.lastModel,\n }).catch(() => { /* best-effort */ });\n } catch { /* never crash the runtime on a metrics read */ }\n }, 10_000);\n metricsTimer.unref();\n\n // Wait for Claude to start, checking the session is still alive\n for (let i = 0; i < 10; i++) {\n await new Promise((r) => setTimeout(r, 500));\n if (!tmuxSessionExists(tmuxSession)) {\n console.error(\"Error: Claude Code exited during startup. Try running again.\");\n clearInterval(activityTimer); clearInterval(metricsTimer);\n bridge.stop();\n await setup.cleanup();\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n resetTerminal();\n return;\n }\n }\n\n // ── Deliver queued invite (auto-join) ─────────────────────────────────\n // If the wizard background-spawned this agent, ~/.apiary/invites/<name>\n // holds the room's join URL. deliverInvite keeps asking until the agent is\n // really in the room, and only then deletes the file — a CLI still sitting\n // on a trust or onboarding prompt gets another go once it reaches a prompt,\n // instead of losing the URL to a menu that swallowed it.\n const inviteAbort = new AbortController();\n const invitePromise = deliverInvite({\n agentName: setup.agentName,\n deliver: (parts) => bridge.deliver(parts, { dedupe: true }),\n hasJoined: (url) => setup.joinResults.some((jr) => sameApiaryServer(jr.serverUrl, url)),\n signal: inviteAbort.signal,\n }).then((outcome) => {\n // Don't let a failed auto-join be silent — that is exactly how an agent\n // ends up sitting outside the room it was spawned for. The invite is kept\n // on disk, so `apiary room resume <room>` will hand it over again.\n if (outcome === \"abandoned\" && !options.background) {\n console.log(\n `Note: \"${setup.agentName}\" did not join the room it was invited to. ` +\n `The invite is still queued — check the pane, then re-run or use \"apiary room resume\".`,\n );\n }\n return outcome;\n }).catch(() => \"cancelled\" as const);\n\n // ── Save session state for resume ──────────────────────────────────────\n\n saveSession({\n tmuxSession,\n agentName: setup.agentName,\n tmpDir,\n mcpPort,\n pid: process.pid,\n });\n\n // Background mode: spawned by room create — run silently until SIGTERM.\n if (options.background) {\n // Suppress output so this process doesn't bleed into the parent terminal.\n const noop = (() => true) as unknown as typeof process.stdout.write;\n process.stdout.write = noop;\n process.stderr.write = noop;\n await new Promise<void>((resolve) => {\n process.on(\"SIGTERM\", resolve);\n process.on(\"SIGINT\", resolve);\n });\n clearInterval(activityTimer); clearInterval(metricsTimer);\n inviteAbort.abort();\n await invitePromise;\n bridge.stop();\n await setup.cleanup();\n if (tmuxSessionExists(tmuxSession)) tmuxKillSession(tmuxSession);\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n clearSession(setup.agentName);\n return;\n }\n\n console.log(\"Attaching...\");\n console.log(`(Ctrl+B D to detach — resume with: apiary claude ${setup.agentName} --resume)\\n`);\n\n try {\n await tmuxAttach(tmuxSession);\n } catch {\n // User detached or session ended\n }\n\n // ── On detach: keep running in background ──────────────────────────────\n\n if (tmuxSessionExists(tmuxSession)) {\n console.log(`\"${setup.agentName}\" still running in background.`);\n console.log(` Resume: apiary claude ${setup.agentName} --resume`);\n console.log(` Stop: apiary stop ${setup.agentName}`);\n\n // Silence stdout/stderr so background process doesn't bleed\n // into other terminals (e.g. another agent's tmux session)\n const noop = (() => true) as any;\n process.stdout.write = noop;\n process.stderr.write = noop;\n\n // Keep the process alive so the event loop, MCP server, and SSE stay up\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", resolve);\n process.on(\"SIGTERM\", resolve);\n });\n }\n\n // ── Full cleanup (session ended or process killed) ─────────────────────\n\n clearInterval(activityTimer); clearInterval(metricsTimer);\n inviteAbort.abort();\n await invitePromise;\n bridge.stop();\n await setup.cleanup();\n if (tmuxSessionExists(tmuxSession)) tmuxKillSession(tmuxSession);\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n clearSession(setup.agentName);\n resetTerminal();\n}\n\n// ── Resume ──────────────────────────────────────────────────────────────────\n\nasync function resumeClaude(options: AgentRuntimeOptions): Promise<void> {\n const name = options.name;\n\n // Find session to resume\n const sessions = listClaudeSessions();\n if (sessions.length === 0) {\n console.error(\"No active Claude sessions to resume.\");\n process.exit(1);\n }\n\n let session: PersistedSession | undefined;\n if (name) {\n session = sessions.find((s) => s.agentName === name);\n if (!session) {\n console.error(`No active session named \"${name}\". Active sessions:`);\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n } else if (sessions.length === 1) {\n session = sessions[0];\n } else {\n console.error(\"Multiple active sessions. Specify which one with --name:\");\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n\n // Verify tmux session is alive\n if (!tmuxSessionExists(session.tmuxSession)) {\n console.error(`Session \"${session.agentName}\" tmux session is gone. Cleaning up.`);\n clearSession(session.agentName);\n process.exit(1);\n }\n\n // Verify background process is alive\n try {\n process.kill(session.pid, 0);\n } catch {\n console.error(`Session \"${session.agentName}\" background process (pid ${session.pid}) is gone. Cleaning up.`);\n tmuxKillSession(session.tmuxSession);\n clearSession(session.agentName);\n try { rmSync(session.tmpDir, { recursive: true }); } catch { /* ok */ }\n process.exit(1);\n }\n\n console.log(`Resuming session \"${session.agentName}\"...\\n`);\n\n try {\n await tmuxAttach(session.tmuxSession);\n } catch {\n // User detached\n }\n\n if (tmuxSessionExists(session.tmuxSession)) {\n console.log(`\"${session.agentName}\" still running in background.`);\n console.log(` Resume: apiary claude ${session.agentName} --resume`);\n console.log(` Stop: apiary stop ${session.agentName}`);\n }\n}\n\n// ── Stop ────────────────────────────────────────────────────────────────────\n\nexport async function stopClaude(name?: string, all?: boolean): Promise<void> {\n const sessions = listClaudeSessions();\n if (sessions.length === 0) {\n // --all is allowed to no-op (rooms may have already cleaned up everything)\n if (all) return;\n console.error(\"No active Claude sessions.\");\n process.exit(1);\n }\n\n if (all) {\n for (const s of sessions) {\n stopSession(s);\n }\n return;\n }\n\n let session: PersistedSession | undefined;\n if (name) {\n session = sessions.find((s) => s.agentName === name);\n if (!session) {\n console.error(`No active session named \"${name}\". Active sessions:`);\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n } else if (sessions.length === 1) {\n session = sessions[0];\n } else {\n console.error(\"Multiple active sessions. Use --name or --all:\");\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n\n stopSession(session);\n}\n\nfunction stopSession(session: PersistedSession): void {\n // Kill tmux session first (prevents orphaned panes with broken state)\n if (tmuxSessionExists(session.tmuxSession)) {\n tmuxKillSession(session.tmuxSession);\n }\n\n // Then signal the background process\n try {\n process.kill(session.pid, \"SIGTERM\");\n } catch {\n // Process already gone\n }\n\n // Clean up temp files\n try { rmSync(session.tmpDir, { recursive: true }); } catch { /* ok */ }\n\n clearSession(session.agentName);\n resetTerminal();\n console.log(`Stopped \"${session.agentName}\".`);\n}\n","/**\n * Did the text we injected actually get submitted?\n *\n * Both bridges type into a TUI and then assume it worked. On 2026-09-10 that\n * assumption cost seven minutes of apparent silence: a room message reached a\n * Codex agent, the `@`-mention popup swallowed the Enter, and the message sat in\n * the composer while apiary reported success and moved on. Nobody learned it had\n * failed until a human read the pane.\n *\n * The check is deliberately narrow. After a submit, the text appears in the\n * *transcript* — that is success, not failure — so the presence of our text on\n * screen proves nothing. What distinguishes the two is where it sits: an\n * unsubmitted message is still in the composer, which both TUIs render as the\n * last prompt-marked block at the bottom of the screen.\n */\n\n/** Prompt markers: Codex renders the composer with ›, Claude with ❯ (> on older builds). */\nconst PROMPT_MARKER = /^\\s*[›❯>]\\s?/;\n\n/**\n * The composer's contents: everything from the last prompt marker down.\n *\n * Long messages wrap, so the marker line alone is not the whole composer — the\n * rest of the block below it belongs to the same input.\n */\nexport function composerBlock(lines: string[]): string {\n const tail = lines.slice(-25);\n let start = -1;\n for (let i = tail.length - 1; i >= 0; i--) {\n if (PROMPT_MARKER.test(tail[i])) {\n start = i;\n break;\n }\n }\n if (start === -1) return \"\";\n return tail.slice(start).join(\" \");\n}\n\n/**\n * A stable fragment of the message to look for.\n *\n * The head of the text rather than the whole of it: the composer wraps, the pane\n * truncates, and a trailing `@mention` may already have been rewritten by the\n * very popup we are checking for — so the tail is exactly the part that cannot\n * be relied on. Whitespace is collapsed because wrapping introduces its own.\n */\nexport function submissionProbe(text: string): string {\n return text.replace(/\\s+/g, \" \").trim().slice(0, 40);\n}\n\n/**\n * True when the message is still sitting in the composer, i.e. it was NOT\n * submitted. A message short enough to produce no usable probe is reported as\n * submitted rather than guessed at — a false \"undelivered\" would have the\n * caller resend, and a duplicate room message is worse than an unchecked one.\n */\nexport function composerStillHolds(lines: string[], text: string): boolean {\n const probe = submissionProbe(text);\n if (probe.length < 8) return false;\n const composer = composerBlock(lines).replace(/\\s+/g, \" \");\n return composer.includes(probe);\n}\n","/**\n * TmuxBridge — state-aware event injection into Claude Code.\n *\n * Reads the Claude Code TUI screen via `tmux capture-pane`, detects the\n * current UI state, and applies the right injection strategy:\n *\n * idle → inject directly\n * typing → Ctrl+U (cut), inject, Ctrl+Y (restore)\n * dialog → queue and poll\n * permission → queue and poll\n * streaming → queue and poll\n * unknown → queue and poll (safe default)\n *\n * Events that can't be injected immediately are queued and drained\n * when the state becomes safe.\n */\n\nimport {\n tmuxPaneIsShell,\n tmuxCapturePane,\n tmuxInjectText,\n tmuxSendEnter,\n tmuxSendKey,\n} from \"../tmux.js\";\nimport { composerStillHolds } from \"../composer.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\n\nexport type TuiState =\n /** The CLI is not running — the pane has fallen back to a shell. */\n | \"absent\"\n | \"idle\"\n | \"typing\"\n | \"dialog\"\n | \"permission\"\n | \"streaming\"\n | \"unknown\";\n\n// Spinner characters used by Claude Code during streaming\nconst SPINNER_CHARS = \"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\";\n\n/**\n * Claude Code is busy in a way that shows NO spinner.\n *\n * While a subagent runs, the main area is quiet and the only sign is the\n * footer: \"⏵⏵ bypass permissions on (shift+tab to cycle) · ← 1 agent\".\n * Spinner-only detection therefore read the pane as idle, so apiary pasted\n * into a composer that will not submit — verified against a live agent, where\n * even a manual Enter did nothing — and the message sat there as a draft until\n * the subagent finished. The room showed the agent as idle throughout.\n */\nconst BUSY_PATTERNS: RegExp[] = [\n // \"← 1 agent\" / \"← 3 agents\"\n /←\\s*\\d+\\s+agents?\\b/,\n];\n\n// Patterns that indicate a selection/question dialog\nconst DIALOG_PATTERNS = [\n \"Enter to select\",\n \"to navigate\",\n \"Esc to cancel\",\n \"Ready to code?\",\n \"Review your answers\",\n \"ctrl+g to edit in\",\n];\n\n// Patterns that indicate a permission/confirmation prompt\nconst PERMISSION_PATTERNS = [\n \"(Y)\",\n \"Allow \",\n \"Deny \",\n \"approve\",\n \"Yes / No\",\n // Current Claude Code wording, captured from a live prompt. Without these a\n // permission dialog reads as idle and the bridge pastes a room event into\n // it, answering on the agent's behalf.\n \"Do you want to proceed?\",\n \"don't ask again\",\n // The folder-trust modal, which no flag skips.\n \"Yes, I trust this folder\",\n];\n\n/**\n * Patterns specific enough to put \"⏸ needs you\" in front of a human.\n *\n * DIALOG_PATTERNS and PERMISSION_PATTERNS above deliberately over-match, and\n * for their original consumer that is correct: they gate DELIVERY, where a\n * false positive only queues a room event for the drain loop to retry. Cheap.\n *\n * That reasoning was then reused for a second consumer with the opposite cost\n * — the agent state reported to the room — where a false positive stops the\n * operator and makes them wait on an agent that needs nothing. Observed: the\n * bare substring `\"approve\"` matched an agent's own tool output, \"hard cap, set\n * by the approved top\", and the room strip read \"⏸ needs you\" at an agent 23\n * minutes into a harness run. `\"to navigate\"` is the same trap and worse — it\n * matches any sentence about navigating, in a project whose entire subject is\n * navigating by bearings.\n *\n * So: two lists. Over-match to protect the agent, under-match before\n * interrupting the human. These are UI chrome a prompt actually renders —\n * wordings and shapes, not vocabulary that can occur in prose.\n */\nconst BLOCKING_PATTERNS: Array<string | RegExp> = [\n \"Do you want to proceed?\",\n \"don't ask again\",\n \"Yes, I trust this folder\",\n \"Ready to code?\",\n \"Enter to select\",\n /❯\\s*1\\.\\s*Yes/,\n /\\(y\\s*\\/\\s*n\\)/i,\n // The arrow glyphs claude renders beside it. Bare \"to navigate\" is prose.\n /[↑↓⬆⬇]\\s*(?:[↑↓⬆⬇]\\s*)?to navigate/,\n];\n\n/** Match a mixed string/RegExp pattern list, case-insensitively for strings. */\nfunction matchesAny(text: string, patterns: Array<string | RegExp>): boolean {\n const haystack = text.toLowerCase();\n for (const pattern of patterns) {\n if (typeof pattern === \"string\") {\n if (haystack.includes(pattern.toLowerCase())) return true;\n } else if (pattern.test(text)) {\n return true;\n }\n }\n return false;\n}\n\nexport interface DeliverOptions {\n /**\n * Drop this text if an identical copy is already queued. For callers that\n * retry a delivery they cannot observe landing (the room invite loop).\n */\n dedupe?: boolean;\n}\n\nexport interface TmuxBridgeOptions {\n /** How often to poll when events are queued (ms). Default: 200 */\n pollIntervalMs?: number;\n /** How long to wait between Ctrl+U/inject/Ctrl+Y steps (ms). Default: 50 */\n keystrokeDelayMs?: number;\n /**\n * Called when a message could not be got into the agent, after the retry.\n *\n * Delivery used to be assumed: apiary typed, reported success and moved on,\n * so a swallowed submit looked exactly like an agent choosing not to answer.\n * This is the hook that makes the difference visible to someone.\n */\n onUndelivered?: (text: string) => void;\n}\n\n/**\n * Last resort when no one has wired a handler: say it on stderr, which lands in\n * the launcher pane. Better than nothing, and a great deal better than success.\n */\nfunction defaultUndeliveredWarning(text: string): void {\n const preview = text.replace(/\\s+/g, \" \").slice(0, 80);\n process.stderr.write(`apiary: message NOT delivered to the agent — still in its composer: ${preview}\\n`);\n}\n\nexport class TmuxBridge {\n private session: string;\n private queue: string[] = [];\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private pollIntervalMs: number;\n private keystrokeDelayMs: number;\n private onUndelivered: (text: string) => void;\n private stopped = false;\n private blockedCount = 0;\n private static readonly BLOCKED_THRESHOLD = 150; // ~30s at 200ms poll — force inject if stuck\n\n constructor(session: string, opts?: TmuxBridgeOptions) {\n this.session = session;\n this.pollIntervalMs = opts?.pollIntervalMs ?? 200;\n this.keystrokeDelayMs = opts?.keystrokeDelayMs ?? 50;\n this.onUndelivered = opts?.onUndelivered ?? defaultUndeliveredWarning;\n }\n\n /**\n * Delivery callback — drop-in replacement for the raw tmuxDeliver lambda.\n * Pass `bridge.deliver.bind(bridge)` to EventProcessor.run().\n */\n async deliver(parts: ContentPart[], opts?: DeliverOptions): Promise<void> {\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n this.inject(text, opts?.dedupe === true);\n }\n\n /**\n * Detect the current TUI state by reading the screen.\n * Exported for testing — the heuristic logic is in detectStateFromLines().\n */\n detectState(): TuiState {\n // See the Codex bridge: a shell prompt is indistinguishable from an idle\n // composer once you are only looking at rendered text.\n if (tmuxPaneIsShell(this.session)) return \"absent\";\n const lines = this.captureScreen();\n return detectStateFromLines(lines);\n }\n\n /**\n * State to REPORT to the room, as opposed to the state used to decide\n * whether it is safe to type. Same screen, stricter about claiming a human\n * is needed — see detectReportedStateFromLines.\n */\n detectReportedState(): TuiState {\n if (tmuxPaneIsShell(this.session)) return \"absent\";\n return detectReportedStateFromLines(this.captureScreen());\n }\n\n /**\n * Extract claude's live status label from the pane (the \"Sautéed for 12s\",\n * \"Compacting conversation… 23%\" lines under the prompt). Returns null when\n * the agent is idle / no status visible.\n *\n * The room TUI calls this through the agent runtime's poll loop to surface\n * \"is the agent actually working or stuck?\" without making the user attach\n * to each tmux pane.\n */\n getActivityLabel(): string | null {\n return extractActivityLabel(this.captureScreen());\n }\n\n /**\n * Try to inject text, choosing strategy based on TUI state.\n * If the state is unsafe, queues the text and starts polling.\n *\n * Text is flattened to a single line before injection to avoid triggering\n * Claude Code's paste detection. When multi-line text arrives via\n * `send-keys -l`, Claude Code detects it as a paste and collapses it into\n * \"[Pasted text #1 +N lines]\" which may not reliably submit with Enter.\n */\n private inject(text: string, dedupe: boolean): void {\n const flat = text.replace(/\\n/g, \" \");\n const state = this.detectState();\n\n switch (state) {\n case \"idle\":\n this.injectIdle(flat);\n break;\n case \"typing\":\n this.injectWhileTyping(flat);\n break;\n default:\n // dialog, permission, streaming, unknown — queue it\n this.enqueue(flat, dedupe);\n break;\n }\n }\n\n /** Capture the screen via tmux capture-pane. */\n private captureScreen(): string[] {\n return tmuxCapturePane(this.session);\n }\n\n /**\n * Inject into an idle prompt: type text + Enter, then check it went.\n *\n * This used to fire a second Enter unconditionally as a \"safety net\" against\n * Claude Code's paste detection swallowing the first. That is a guess in both\n * directions: it cannot tell whether the first Enter worked, and it cannot\n * tell whether the second one did either. Reading the composer back answers\n * the question instead, and only sends the second Enter when it is needed.\n */\n private injectIdle(text: string): void {\n tmuxInjectText(this.session, text);\n tmuxSendEnter(this.session);\n this.confirmSubmitted(text);\n }\n\n /**\n * Check the message left the composer, and press Enter once more if it did not.\n *\n * A submitted message moves into the transcript; one still in the composer was\n * never sent. If it is still there after the retry, the room is told rather\n * than left to read the agent's silence as a choice.\n */\n private confirmSubmitted(text: string): void {\n this.sleep(80);\n if (!composerStillHolds(this.captureScreen(), text)) return;\n\n tmuxSendEnter(this.session);\n this.sleep(120);\n if (!composerStillHolds(this.captureScreen(), text)) return;\n\n this.onUndelivered(text);\n }\n\n /**\n * Inject while the user is typing:\n * 1. Ctrl+U — cut line to kill ring\n * 2. Inject our text + Enter\n * 3. Ctrl+Y — paste the user's text back\n */\n private injectWhileTyping(text: string): void {\n // Cut user's current input\n tmuxSendKey(this.session, \"C-u\");\n this.sleep(this.keystrokeDelayMs);\n\n // Inject our event, then confirm rather than hope\n tmuxInjectText(this.session, text);\n tmuxSendEnter(this.session);\n this.confirmSubmitted(text);\n this.sleep(this.keystrokeDelayMs);\n\n // Restore user's text\n tmuxSendKey(this.session, \"C-y\");\n }\n\n /** Add to queue and start polling if not already. */\n private enqueue(text: string, dedupe: boolean): void {\n // Off by default — room events must never be collapsed. Only callers that\n // retry a delivery they cannot observe landing (the invite loop) opt in.\n if (dedupe && this.queue.includes(text)) return;\n this.queue.push(text);\n this.startPolling();\n }\n\n /** Start the polling timer to drain queued events. */\n private startPolling(): void {\n if (this.pollTimer || this.stopped) return;\n this.pollTimer = setInterval(() => this.drainQueue(), this.pollIntervalMs);\n }\n\n /** Stop the polling timer. */\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Try to drain one queued event if the state is safe.\n *\n * Drains one event at a time rather than batching all into one multi-line\n * string — multi-line text triggers Claude Code's paste detection which\n * collapses it into \"[Pasted text #1 +N lines]\".\n *\n * After injecting one event, the poll continues. The next cycle re-checks\n * state: if Claude is busy (streaming), remaining events wait. If idle,\n * the next event is injected. Events are already flattened in inject().\n */\n private drainQueue(): void {\n if (this.queue.length === 0) {\n this.stopPolling();\n this.blockedCount = 0;\n return;\n }\n\n const state = this.detectState();\n if (state === \"idle\" || state === \"typing\") {\n this.blockedCount = 0;\n const text = this.queue.shift()!;\n\n if (state === \"idle\") {\n this.injectIdle(text);\n } else {\n this.injectWhileTyping(text);\n }\n\n if (this.queue.length === 0) {\n this.stopPolling();\n }\n // else: keep polling to drain remaining events\n } else {\n // Any non-injectable state (streaming, dialog, permission, unknown)\n this.blockedCount++;\n if (this.blockedCount >= TmuxBridge.BLOCKED_THRESHOLD) {\n // Stuck too long — force inject to unblock\n this.blockedCount = 0;\n const text = this.queue.shift()!;\n this.injectIdle(text);\n if (this.queue.length === 0) this.stopPolling();\n }\n }\n }\n\n /** Cleanup. */\n stop(): void {\n this.stopped = true;\n this.stopPolling();\n this.queue.length = 0;\n }\n\n /** Synchronous sleep — only used for tiny keystroke delays. */\n private sleep(ms: number): void {\n if (ms <= 0) return;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n }\n}\n\n// ── Activity label extraction ────────────────────────────────────────────────\n\n/**\n * Pull claude's status string out of a captured pane.\n *\n * Claude's TUI shows these markers above the input prompt while it's working:\n * ✻ Fluttering… (38s · ↓ 2.1k tokens) <- the LIVE form, present throughout\n * ✻ Sautéed for 12s <- the FINISHED form, a moment at the end\n * ✻ Compacting conversation… 23%\n * ✻ Thinking…\n *\n * Three patterns, tried narrowest-first so the broad one cannot swallow the\n * others:\n * - PROG: a known long-op verb (Compacting/Thinking/Loading/Connecting)\n * followed by an optional description and optional N%. Stops at the first\n * `(` so it doesn't capture a trailing metadata blob.\n * - COOK: cooking-verb + \"for Ns\", the form claude leaves behind once a turn\n * ends. Matches unicode letters (Sautéed) via the Latin-1 supplement range.\n * - LIVE: any word, an ellipsis, and a parenthesised progress group. Matched\n * on SHAPE rather than vocabulary, because the verb is whimsical and\n * rotates — Improvising, Fluttering, Reticulating, Cogitating. A word list\n * goes stale the next time someone adds one, which is exactly how this\n * broke: with only COOK and PROG, a *working* agent matched nothing and\n * reported no label, so the room strip showed a bare glyph for as long as\n * the work took. The one moment the operator most needs to know what is\n * happening was the one moment it said nothing.\n *\n * LIVE deliberately keeps its parenthesised group — \"(38s · ↓ 2.1k tokens)\" is\n * the elapsed time and stream progress, which is what distinguishes \"moving\"\n * from \"wedged\". PROG still stops at `(` because its verbs carry their own\n * detail, and it is tried first so that stays true.\n *\n * Scan the last ~25 lines, return the newest match. Null when none → idle.\n */\nconst ACTIVITY_LIVE = /([A-Za-zÀ-ÿ]+…\\s*\\([^)\\n]*\\))/;\nconst ACTIVITY_COOK = /([A-Za-zÀ-ÿ]+ed\\s+for\\s+\\d+s)/;\nconst ACTIVITY_PROG = /((?:Compacting|Thinking|Loading|Connecting)[^()]*?(?:\\s+\\d+%)?)(?=\\s*[(\\n]|\\s*$)/;\nexport function extractActivityLabel(lines: string[]): string | null {\n if (lines.length === 0) return null;\n let best: string | null = null;\n for (const line of lines.slice(-25)) {\n // PROG first, then COOK, then LIVE. LIVE is the broadest — any word plus a\n // parenthesised group — so it must go last or it swallows lines the narrower\n // patterns own: \"Compacting conversation… (2m 9s · ↑ 3.3k tokens)\" is\n // reported as \"Compacting conversation…\" on purpose, and LIVE-first turned\n // that into \"conversation… (2m 9s · ↑ 3.3k tokens)\".\n const m = line.match(ACTIVITY_PROG) ?? line.match(ACTIVITY_COOK) ?? line.match(ACTIVITY_LIVE);\n if (m) best = m[1].trim();\n }\n return best;\n}\n\n// ── State detection heuristics ──────────────────────────────────────────────\n\n/**\n * Detect TUI state from capture-pane output lines.\n * Exported separately so it can be unit-tested without tmux.\n *\n * Claude Code's TUI layout (v2.1+):\n * ────────────────────────\n * ❯ <user input here>\n * ────────────────────────\n * PR #2 /ide ...\n *\n * The ❯ prompt sits between separator lines (─). No ❯❯ footer in v2.1+.\n */\nexport function detectStateFromLines(\n lines: string[],\n opts?: { ignorePrompts?: boolean },\n): TuiState {\n if (lines.length === 0) return \"unknown\";\n\n // Work with the last ~30 lines (the visible bottom of the screen)\n const tail = lines.slice(-30);\n const tailText = tail.join(\"\\n\");\n\n // `ignorePrompts` asks \"what is this screen doing APART from any prompt?\" —\n // used by detectReportedStateFromLines to answer the human's question once\n // the loose lists have been found to be matching prose rather than chrome.\n if (opts?.ignorePrompts !== true) {\n // 1. Dialog: selection/question/plan approval\n for (const pattern of DIALOG_PATTERNS) {\n if (tailText.includes(pattern)) return \"dialog\";\n }\n\n // 2. Permission prompt\n for (const pattern of PERMISSION_PATTERNS) {\n if (tailText.includes(pattern)) return \"permission\";\n }\n }\n\n // 3. Streaming: spinner characters in the last few lines\n const lastFew = tail.slice(-5).join(\"\");\n for (const ch of SPINNER_CHARS) {\n if (lastFew.includes(ch)) return \"streaming\";\n }\n\n // 3b. Busy with no spinner — a subagent is running. Checked after the\n // dialog and permission patterns on purpose: a prompt that needs a human\n // outranks \"also busy\", because only one of those the user can clear.\n for (const pattern of BUSY_PATTERNS) {\n if (pattern.test(tailText)) return \"streaming\";\n }\n\n // 4. Look for the ❯/› prompt line near the bottom.\n // Supports both old layout (❯❯ footer) and new layout (separator lines).\n const promptChar = /^[❯›](\\s|$)/;\n const footerChar = /^[❯›]{2}\\s/;\n const separatorLine = /^[─━─\\-]{10,}/;\n\n // Strategy A: old layout — find ❯❯ footer then ❯ prompt above it\n for (let i = tail.length - 1; i >= 0; i--) {\n const line = tail[i].trimStart();\n if (footerChar.test(line)) {\n // Found old-style footer, look for prompt above\n for (let j = i - 1; j >= 0; j--) {\n const above = tail[j].trimStart();\n if (promptChar.test(above)) {\n const content = above.replace(/^[❯›]\\s*/, \"\").trim();\n return content.length === 0 ? \"idle\" : \"typing\";\n }\n }\n break;\n }\n }\n\n // Strategy B: new layout — find ❯ prompt between/near separator lines\n for (let i = tail.length - 1; i >= 0; i--) {\n const line = tail[i].trimStart();\n if (promptChar.test(line)) {\n // Verify it's Claude's prompt by checking for separator line nearby\n const above = i > 0 ? tail[i - 1].trimStart() : \"\";\n const below = i < tail.length - 1 ? tail[i + 1].trimStart() : \"\";\n if (separatorLine.test(above) || separatorLine.test(below)) {\n const content = line.replace(/^[❯›]\\s*/, \"\").trim();\n return content.length === 0 ? \"idle\" : \"typing\";\n }\n }\n }\n\n return \"unknown\";\n}\n\n/**\n * The same screen read, but tuned for a human rather than for the delivery\n * queue: only claim a prompt is waiting when one is actually on screen.\n *\n * `detectStateFromLines` leans toward \"there is a prompt\" because a false\n * positive there merely queues an event. This one leans the other way, because\n * its false positive is \"⏸ needs you\" in the operator's face at an agent that\n * needs nothing — which costs a person's attention, and teaches them to stop\n * trusting the strip. When the loose lists fire but no specific prompt wording\n * is present, the screen is re-read with the prompt scan disabled so the\n * spinner and composer get to answer instead.\n */\nexport function detectReportedStateFromLines(lines: string[]): TuiState {\n const loose = detectStateFromLines(lines);\n if (loose !== \"dialog\" && loose !== \"permission\") return loose;\n if (matchesAny(lines.slice(-30).join(\"\\n\"), BLOCKING_PATTERNS)) return loose;\n return detectStateFromLines(lines, { ignorePrompts: true });\n}\n","/**\n * Shared agent runtime setup — extracted from cli/claude/run.ts.\n *\n * Both `apiary run claude` and `apiary run opencode` use this to:\n * 1. Create SSE multiplexer + EventProcessor\n * 2. Create local runtime MCP server (handlers shared via runtime-handlers.ts)\n * 3. Wire up participant cache updates\n * 4. Provide cleanup\n *\n * Rooms are NOT joined during setup. The agent joins rooms by calling\n * join_room() via MCP. If --join URLs are provided, the startup event\n * asks the agent to call join_room for each URL.\n *\n * Each runtime only needs to provide its own delivery mechanism\n * (TmuxBridge for Claude, HTTP API for OpenCode).\n */\n\nimport { randomName } from \"../core/names.js\";\nimport { SseMultiplexer } from \"../agent/sse-multiplexer.js\";\nimport { EventProcessor } from \"../agent/event-processor.js\";\nimport { createRuntimeMcpServer, type RuntimeMcpServer } from \"../agent/mcp/runtime.js\";\nimport type { AuthorityLevel } from \"../core/types.js\";\nimport type { EngagementMode } from \"../agent/engagement.js\";\nimport type { LabeledEvent } from \"../agent/multiplexer.js\";\nimport type { ContentPart } from \"../agent/types.js\";\nimport { buildRuntimeMcpOptions, type JoinResult } from \"./runtime-handlers.js\";\n\nexport type { JoinResult };\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface AgentRuntimeOptions {\n joinUrls?: string[];\n name?: string;\n /**\n * Legacy CLI surface flag. `true` is equivalent to `authority: \"admin\"` —\n * exposes the full set of admin tools in the local MCP server. Prefer\n * setting `authority` directly when the desired tier isn't admin.\n */\n admin?: boolean;\n /**\n * Pre-declared authority this agent expects to operate at. Drives which\n * privileged MCP tools the local server registers. The real authority is\n * still discovered + enforced by the apiary server on join — this flag is\n * only about tool exposure. Defaults to \"admin\" if `admin: true`,\n * undefined otherwise.\n */\n authority?: AuthorityLevel;\n extraArgs?: string[];\n /** Skip tmux/UI — deliver events as plain text to stdout. MCP server still runs. */\n headless?: boolean;\n /** Re-attach to an existing background session instead of starting a new one. */\n resume?: boolean;\n /** Spawned by room create: start tmux session but skip attach; wait for SIGTERM. */\n background?: boolean;\n /** Called after a room is successfully joined via join_room MCP tool. */\n onRoomJoined?: () => void | Promise<void>;\n /**\n * Engagement mode this agent starts in. A standby mode means it stays quiet\n * until addressed — @mentioned, pinged, or whispered to — which is how a\n * room with several agents avoids every one of them answering every remark.\n * Defaults to \"everyone\" (respond to all traffic).\n */\n mode?: EngagementMode;\n}\n\nexport interface AgentRuntimeSetup {\n agentName: string;\n joinResults: JoinResult[];\n initialParts: ContentPart[] | undefined;\n processor: EventProcessor;\n sseMux: SseMultiplexer;\n mcpServer: RuntimeMcpServer;\n wrappedSource: AsyncIterable<LabeledEvent>;\n cleanup(): Promise<void>;\n}\n\n// ── Setup ─────────────────────────────────────────────────────────────────────\n\nexport async function setupAgentRuntime(options: AgentRuntimeOptions): Promise<AgentRuntimeSetup> {\n const agentName = options.name ?? randomName();\n // Translate the legacy --admin flag into a tier; the new `authority` field\n // is the canonical knob.\n const authority: AuthorityLevel | undefined = options.authority\n ?? (options.admin ? \"admin\" : undefined);\n\n // ── Pending join URLs (not joined yet — agent calls join_room) ─────────\n const pendingUrls = options.joinUrls ?? [];\n\n // ── Mutable join results (populated as agent calls join_room) ──────────\n const joinResults: JoinResult[] = [];\n\n // ── Create SSE multiplexer (starts empty) ──────────────────────────────\n const sseMux = new SseMultiplexer();\n\n // ── Create EventProcessor (selfId set on first join_room) ──────────────\n const processor = new EventProcessor(\"\", agentName, {\n defaultMode: options.mode ?? \"everyone\",\n });\n\n // ── Create local runtime MCP server (shared handler set) ───────────────\n const mcpServer = await createRuntimeMcpServer(\n buildRuntimeMcpOptions({\n processor,\n sseMux,\n joinResults,\n agentName,\n authority,\n onRoomJoined: options.onRoomJoined,\n }),\n );\n\n // ── Wrap SSE source for participant cache updates ─────────────────────\n const wrappedSource: AsyncIterable<LabeledEvent> = {\n [Symbol.asyncIterator]() {\n const inner = sseMux[Symbol.asyncIterator]();\n return {\n async next() {\n const result = await inner.next();\n if (!result.done) {\n const { roomId, event } = result.value;\n const jr = joinResults.find((j) => j.roomId === roomId);\n if (jr) {\n if (event.type === \"ParticipantJoined\") {\n jr.dataSource.addParticipant(event.participant);\n } else if (event.type === \"ParticipantLeft\") {\n jr.dataSource.removeParticipant(event.participant_id);\n }\n }\n }\n return result;\n },\n };\n },\n };\n\n // ── Cleanup function ──────────────────────────────────────────────────\n async function cleanup(): Promise<void> {\n await processor.stop();\n sseMux.close();\n await mcpServer.stop();\n\n for (const jr of joinResults) {\n try {\n await fetch(`${jr.serverUrl}/disconnect`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: jr.sessionToken }),\n });\n } catch {\n // Server may be down\n }\n }\n }\n\n // ── Build startup event (if --join URLs were provided) ────────────────\n let initialParts: ContentPart[] | undefined;\n if (pendingUrls.length > 0) {\n if (pendingUrls.length === 1) {\n initialParts = [{ type: \"text\", text: `Use join_room(\"${pendingUrls[0]}\") to connect.` }];\n } else {\n const lines = pendingUrls.map((u) => ` join_room(\"${u}\")`);\n initialParts = [{ type: \"text\", text: `Rooms to join:\\n${lines.join(\"\\n\")}` }];\n }\n }\n\n return {\n agentName,\n joinResults,\n initialParts,\n processor,\n sseMux,\n mcpServer,\n wrappedSource,\n cleanup,\n };\n}\n","/**\n * Bridge TUI state → the coarse state the room shows.\n *\n * Each bridge already works out what its CLI is doing, in order to decide\n * whether it is safe to inject text. That knowledge never left the runtime, so\n * the room fell back to inferring \"working\" for every agent whenever anyone\n * spoke — which meant an agent parked on an approval prompt was displayed\n * exactly like one that was thinking. One of those resolves by waiting and the\n * other never does, so they must not look the same.\n */\n\nimport type { AgentActivityState } from \"../agent/event-processor.js\";\nimport type { CodexTuiState } from \"./codex/tmux-bridge.js\";\nimport type { TuiState } from \"./claude/tmux-bridge.js\";\n\n/**\n * Codex: `approval` is a tool/command confirmation, `blocked` is a sign-in,\n * onboarding or directory-trust screen. Both need a human.\n */\nexport function codexStateToActivity(state: CodexTuiState): AgentActivityState | undefined {\n switch (state) {\n case \"absent\":\n // A human has to restart it, which is what \"needs you\" means. Reporting\n // idle here is how a dead agent sat in the room looking available.\n return \"blocked\";\n case \"approval\":\n case \"blocked\":\n return \"blocked\";\n case \"streaming\":\n return \"working\";\n case \"idle\":\n case \"typing\":\n return \"idle\";\n case \"unknown\":\n default:\n // Don't report a guess as ground truth — leave the room's inference be.\n return undefined;\n }\n}\n\n/**\n * Claude Code: `permission` is a tool-permission prompt, `dialog` a\n * selection/question overlay. Both sit there until a human answers.\n */\nexport function claudeStateToActivity(state: TuiState): AgentActivityState | undefined {\n switch (state) {\n case \"absent\":\n return \"blocked\";\n case \"permission\":\n case \"dialog\":\n return \"blocked\";\n case \"streaming\":\n return \"working\";\n case \"idle\":\n case \"typing\":\n return \"idle\";\n case \"unknown\":\n default:\n return undefined;\n }\n}\n","/**\n * Whether apiary-launched agents run unattended.\n *\n * An agent apiary spawned to sit in a room has nobody watching its pane. Every\n * approval prompt is therefore a permanent stall: the agent stops, the room\n * cannot answer it, and from the outside it looks like the agent is thinking.\n * Observed repeatedly in practice — a single session hit a command approval, a\n * dev-server approval and a `git add` approval within an hour, each one halting\n * work until a human happened to look at the right tmux pane.\n *\n * So the default is unattended, per CLI:\n *\n * - Claude Code: `--dangerously-skip-permissions`\n * - Codex: `approval_policy = \"never\"` in apiary's config profile, plus\n * per-tool `approval_mode` for apiary's own MCP tools (the two are\n * separate gates — `approval_policy` does not cover MCP tool calls)\n *\n * Codex still executes inside its sandbox, so anything it will not allow\n * returns a failure to the model rather than asking. Claude's flag is broader\n * and genuinely skips all permission checks — the room is only as trusted as\n * the repos its agents point at.\n */\n\n/** Env var that turns the default off. */\nexport const APPROVALS_ENV = \"APIARY_AGENT_APPROVALS\";\n\n/**\n * True unless `APIARY_AGENT_APPROVALS=ask`.\n *\n * \"ask\" restores the interactive behaviour, at the cost of having to babysit\n * each agent's terminal.\n */\nexport function wantsUnattendedAgents(env: NodeJS.ProcessEnv = process.env): boolean {\n return env[APPROVALS_ENV]?.trim().toLowerCase() !== \"ask\";\n}\n","/**\n * Pre-accept Claude Code's trust dialog for an agent's working directory.\n *\n * The first time Claude Code runs in a directory it shows a modal:\n *\n * Quick safety check: Is this a project you created or one you trust?\n * ❯ No, exit\n * Yes, I trust this folder\n *\n * Neither `--dangerously-skip-permissions` nor\n * `--permission-mode bypassPermissions` skips it — verified against Claude\n * Code directly, both land on the same modal. It is a deliberate security\n * boundary, and the only supported way past it is the acceptance Claude itself\n * records in `~/.claude.json` under `projects[<dir>].hasTrustDialogAccepted`.\n *\n * That matters for apiary specifically: nobody watches an agent's tmux pane, so\n * an agent spawned into a repo Claude has not seen before waits on that modal\n * forever. The room reports it (the bridge reads \"Esc to cancel\" as a dialog,\n * which surfaces as \"needs you\"), but reporting a stall is worse than not\n * having one.\n *\n * Recording it is a faithful reading of intent rather than an escalation: the\n * user pointed an agent at this directory, which is the same assertion the\n * dialog asks for. Nothing else in the file is touched, and the write is\n * atomic so a crash cannot truncate a config holding dozens of projects.\n */\n\nimport { existsSync, readFileSync, renameSync, writeFileSync, unlinkSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n/** Claude Code's config file. */\nexport function claudeConfigPath(home: string = homedir()): string {\n return join(home, \".claude.json\");\n}\n\nexport type TrustOutcome =\n | \"already-trusted\" // Claude had already recorded acceptance\n | \"trusted\" // we recorded it\n | \"no-config\" // Claude has never run here; it will ask once\n | \"skipped\" // opted out\n | \"failed\"; // unreadable/unwritable/malformed — left alone\n\n/**\n * Record trust for `cwd` in Claude Code's config.\n *\n * Never throws. Returns what happened so the caller can tell the user, because\n * a silent no-op here shows up much later as an agent that never joined.\n */\nexport function preAcceptClaudeTrust(\n cwd: string,\n opts?: { configPath?: string; enabled?: boolean },\n): TrustOutcome {\n if (opts?.enabled === false) return \"skipped\";\n\n const path = opts?.configPath ?? claudeConfigPath();\n // Claude keys projects by absolute path, exactly as it resolves the cwd.\n const key = resolve(cwd);\n\n if (!existsSync(path)) return \"no-config\";\n\n let parsed: Record<string, unknown>;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf-8\");\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // Malformed or unreadable — do not attempt a rewrite. Losing a config with\n // dozens of projects is far worse than one trust prompt.\n return \"failed\";\n }\n if (typeof parsed !== \"object\" || parsed === null) return \"failed\";\n\n const projects = (parsed.projects ?? {}) as Record<string, Record<string, unknown>>;\n if (typeof projects !== \"object\" || projects === null) return \"failed\";\n\n const existing = projects[key];\n if (existing && existing.hasTrustDialogAccepted === true) return \"already-trusted\";\n\n // Merge into whatever is already recorded for this project — it may hold\n // allowedTools, MCP server lists and history we must not drop.\n projects[key] = { ...(existing ?? {}), hasTrustDialogAccepted: true };\n parsed.projects = projects;\n\n const tmp = `${path}.apiary-${process.pid}.tmp`;\n try {\n writeFileSync(tmp, JSON.stringify(parsed, null, 2), { mode: 0o600 });\n renameSync(tmp, path);\n return \"trusted\";\n } catch {\n try { unlinkSync(tmp); } catch { /* nothing to clean */ }\n return \"failed\";\n }\n}\n","/**\n * Read live token usage out of claude's per-session JSONL log.\n *\n * Claude writes one event per line to\n * ~/.claude/projects/<cwd-encoded>/<sessionId>.jsonl\n * where <cwd-encoded> is the cwd with `/` replaced by `-` (leading `/` becomes\n * a leading `-`). We find the newest jsonl in that directory and aggregate\n * usage across all assistant messages.\n *\n * Incremental tailing: the first call parses the whole file; subsequent\n * calls only parse bytes appended since the last call. Active sessions\n * accumulate ~300KB over an hour — re-reading every 10s is wasteful and\n * scales badly with N agents. Cache is keyed by path so rotation (a new\n * session starts → newest jsonl changes) resets cleanly.\n */\n\nimport { readdirSync, statSync, existsSync, openSync, readSync, closeSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ── Path resolution ──────────────────────────────────────────────────────────\n\n/**\n * Convert an absolute cwd to claude's encoded project directory name.\n * `/Users/foo/bar` → `-Users-foo-bar`.\n * Exported for testing.\n */\nexport function cwdToProjectDir(cwd: string): string {\n // Claude Code slugifies the cwd by replacing BOTH separators and dots with\n // dashes, so /Users/ada.lovelace/x becomes -Users-ada-lovelace-x. Replacing\n // only \"/\" silently produced a path that never exists for any user whose\n // home directory contains a dot — and the caller treats \"no file\" as \"no\n // metrics yet\", so the room's context meter simply never appeared. It had\n // reported zero metrics events in its entire life before this was found,\n // which is how an agent reached 262k tokens with nothing on screen to say so.\n return cwd.replace(/[/.]/g, \"-\");\n}\n\nfunction findLatestJsonl(cwd: string): string | null {\n const projectsDir = join(homedir(), \".claude\", \"projects\", cwdToProjectDir(cwd));\n if (!existsSync(projectsDir)) return null;\n let newest: { path: string; mtime: number } | null = null;\n for (const f of readdirSync(projectsDir)) {\n if (!f.endsWith(\".jsonl\")) continue;\n const p = join(projectsDir, f);\n try {\n const st = statSync(p);\n if (!newest || st.mtimeMs > newest.mtime) newest = { path: p, mtime: st.mtimeMs };\n } catch { /* skip */ }\n }\n return newest?.path ?? null;\n}\n\n// ── Incremental tail state ───────────────────────────────────────────────────\n\ninterface TailState {\n path: string;\n offset: number;\n /** Running totals across all bytes consumed so far. */\n totals: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreateTokens: number;\n };\n /** Last-seen values (for per-turn fields like context size). */\n lastTurnContextTokens: number;\n lastModel: string;\n /** Tail buffer for an incomplete trailing line. */\n carry: string;\n}\n\n// One state per cwd. Module-scope so successive readAgentMetrics calls reuse it.\nconst tailStates = new Map<string, TailState>();\n\nfunction freshState(path: string): TailState {\n return {\n path,\n offset: 0,\n totals: { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreateTokens: 0 },\n lastTurnContextTokens: 0,\n lastModel: \"\",\n carry: \"\",\n };\n}\n\n// ── Public API ───────────────────────────────────────────────────────────────\n\nexport interface AgentMetrics {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreateTokens: number;\n /** Approximate input-context size on the last turn (in + cache_read + cache_create). */\n lastTurnContextTokens: number;\n /** Last sample's model id (the one used by the latest assistant message). */\n lastModel: string;\n}\n\n/**\n * Aggregate session metrics, reading only bytes added since the last call.\n * Returns null when no jsonl exists yet (agent hasn't taken a turn).\n *\n * Handles rotation (newest jsonl path changes → fresh state) and truncation\n * (current file shorter than our remembered offset → fresh state).\n */\nexport function readAgentMetrics(cwd: string): AgentMetrics | null {\n const path = findLatestJsonl(cwd);\n if (!path) return null;\n\n let state = tailStates.get(cwd);\n // Rotation: a new session started, replacing the latest jsonl.\n if (state && state.path !== path) state = undefined;\n if (!state) {\n state = freshState(path);\n tailStates.set(cwd, state);\n }\n\n let size: number;\n try { size = statSync(path).size; }\n catch { return null; }\n\n // Truncation (rare for claude but defensive): file shrunk → reset.\n if (size < state.offset) {\n state = freshState(path);\n tailStates.set(cwd, state);\n }\n if (size === state.offset && state.lastModel === \"\") return null;\n if (size === state.offset) return snapshot(state);\n\n // Read the new chunk\n const chunkLen = size - state.offset;\n const buf = Buffer.alloc(chunkLen);\n let fd: number;\n try { fd = openSync(path, \"r\"); }\n catch { return snapshot(state); }\n try {\n readSync(fd, buf, 0, chunkLen, state.offset);\n } finally {\n closeSync(fd);\n }\n state.offset = size;\n\n const text = state.carry + buf.toString(\"utf-8\");\n const newlineEnd = text.lastIndexOf(\"\\n\");\n const completeLines = newlineEnd >= 0 ? text.slice(0, newlineEnd) : \"\";\n state.carry = newlineEnd >= 0 ? text.slice(newlineEnd + 1) : text;\n\n for (const line of completeLines.split(\"\\n\")) {\n consumeLine(state, line);\n }\n\n return snapshot(state);\n}\n\nfunction snapshot(s: TailState): AgentMetrics {\n return {\n ...s.totals,\n lastTurnContextTokens: s.lastTurnContextTokens,\n lastModel: s.lastModel,\n };\n}\n\n/**\n * Apply one jsonl line to the rolling totals. Tolerant of malformed lines\n * and missing usage fields. Exported for testing.\n */\nexport function consumeLine(state: {\n totals: TailState[\"totals\"];\n lastTurnContextTokens: number;\n lastModel: string;\n}, line: string): void {\n if (!line.trim()) return;\n let event: { message?: { usage?: Record<string, number>; model?: string } };\n try { event = JSON.parse(line); }\n catch { return; }\n const msg = event.message;\n if (!msg || typeof msg !== \"object\") return;\n const usage = msg.usage;\n if (!usage) return;\n\n const inp = Number(usage.input_tokens) || 0;\n const out = Number(usage.output_tokens) || 0;\n const cr = Number(usage.cache_read_input_tokens) || 0;\n const cw = Number(usage.cache_creation_input_tokens) || 0;\n\n state.totals.inputTokens += inp;\n state.totals.outputTokens += out;\n state.totals.cacheReadTokens += cr;\n state.totals.cacheCreateTokens += cw;\n\n if (typeof msg.model === \"string\") state.lastModel = msg.model;\n\n state.lastTurnContextTokens = inp + cr + cw;\n}\n\n/** Reset all in-memory tail state. For tests. */\nexport function _resetForTest(): void {\n tailStates.clear();\n}\n","/**\n * apiary run opencode — client-side agent runtime for OpenCode.\n *\n * Spawns `opencode serve` with OPENCODE_CONFIG_CONTENT to inject the apiary\n * MCP server. The user opens OpenCode's UI, starts a conversation, and tells\n * the agent to join a room URL.\n *\n * Session detection: we subscribe to OpenCode's global SSE event stream and\n * watch for apiary tool call events. Each event carries the sessionID of the\n * calling session — no guessing, no race conditions.\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\nimport {\n saveAgentSession,\n clearAgentSession,\n listAgentSessions,\n isAgentAlive,\n type AgentSession,\n} from \"../agent-session.js\";\n\nexport { type AgentRuntimeOptions as RunOpencodeOptions };\n\nexport function listOpencodeSessions(): AgentSession[] {\n return listAgentSessions(\"opencode\");\n}\n\nexport async function runOpencode(options: AgentRuntimeOptions): Promise<void> {\n // ── Pick a port for OpenCode ────────────────────────────────────────────\n\n const opencodePort = 14096 + Math.floor(Math.random() * 1000);\n const opencodeUrl = `http://127.0.0.1:${opencodePort}`;\n\n // ── Room → OpenCode session mapping ────────────────────────────────────\n //\n // Each OpenCode session can join different rooms. Lazily detected on first\n // delivery for each room by inspecting session messages for apiary tool calls.\n\n const roomSessions = new Map<string, string>(); // roomId → OpenCode sessionId\n\n /** Find the OpenCode session that most recently called an apiary tool. */\n async function findApiarySession(): Promise<string | null> {\n try {\n const res = await fetch(`${opencodeUrl}/session`);\n if (!res.ok) return null;\n // Sessions sorted by time.updated desc — first is most recent\n const sessions = await res.json() as Array<{ id: string; time: { updated: number } }>;\n\n // Check the most recently updated sessions for apiary tool parts\n for (const sess of sessions.slice(0, 3)) {\n const msgRes = await fetch(`${opencodeUrl}/session/${sess.id}/message`);\n if (!msgRes.ok) continue;\n const messages = await msgRes.json() as Array<{\n parts?: Array<{ type?: string; tool?: string }>;\n }>;\n for (const msg of messages) {\n for (const part of msg.parts ?? []) {\n if (part.type === \"tool\" && part.tool?.includes(\"apiary__\")) {\n return sess.id;\n }\n }\n }\n }\n // Fallback: most recently updated session\n return sessions.length > 0 ? sessions[0].id : null;\n } catch {\n return null;\n }\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // No --join URLs — the user tells the agent to join from within OpenCode.\n\n const setup = await setupAgentRuntime({\n ...options,\n joinUrls: undefined,\n });\n\n // ── Build MCP config for OpenCode ───────────────────────────────────────\n\n const opencodeConfig = {\n mcp: {\n apiary: {\n type: \"remote\",\n url: setup.mcpServer.url,\n oauth: false,\n },\n },\n };\n\n // ── Spawn OpenCode in headless mode ─────────────────────────────────────\n\n const extraArgs = options.extraArgs ?? [];\n const opencodeArgs = [\"serve\", \"--port\", String(opencodePort), ...extraArgs];\n\n console.log(\"Launching OpenCode...\");\n\n let child: ChildProcess;\n try {\n child = spawn(\"opencode\", opencodeArgs, {\n env: {\n ...process.env,\n OPENCODE_CONFIG_CONTENT: JSON.stringify(opencodeConfig),\n },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch {\n console.error(\"Error: opencode is required but not found. Install it from https://opencode.ai\");\n await setup.cleanup();\n process.exit(1);\n }\n\n // Forward stderr to console for visibility\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n process.stderr.write(chunk);\n });\n\n let childExited = false;\n child.on(\"exit\", () => { childExited = true; });\n\n child.on(\"error\", async () => {\n console.error(\"Error: failed to start opencode. Is it installed?\");\n await setup.cleanup();\n process.exit(1);\n });\n\n // ── Wait for OpenCode to be ready ───────────────────────────────────────\n\n const ready = await pollForReady(opencodeUrl, 30_000);\n if (!ready) {\n console.error(\"OpenCode did not become ready within 30 seconds.\");\n child.kill();\n await setup.cleanup();\n process.exit(1);\n }\n\n console.log(` OpenCode running on ${opencodeUrl}`);\n\n // Register session so `apiary ps` / `apiary stop` can see it.\n saveAgentSession({\n runtime: \"opencode\",\n agentName: setup.agentName,\n pid: process.pid,\n childPid: child.pid,\n });\n\n // ── Build deliver callback ──────────────────────────────────────────────\n //\n // OpenCode's POST /session/:id/message uses Hono stream() — headers (200)\n // arrive immediately but the LLM runs inside the stream callback. We MUST\n // consume the response body (await res.text()) to block until the LLM\n // finishes, preserving the EventProcessor's _processing lock.\n\n async function deliver(parts: ContentPart[]): Promise<void> {\n const roomId = setup.processor.currentContextRoomId;\n if (!roomId) return;\n\n // Lazy session detection: look up on first delivery for this room\n if (!roomSessions.has(roomId)) {\n const sid = await findApiarySession();\n if (!sid) return;\n roomSessions.set(roomId, sid);\n console.log(` Linked room ${roomId} → session ${sid}`);\n }\n const targetSession = roomSessions.get(roomId)!;\n\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n try {\n const res = await fetch(`${opencodeUrl}/session/${targetSession}/message`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n parts: [{ type: \"text\", text }],\n }),\n });\n await res.text();\n } catch {\n // OpenCode may have exited\n }\n }\n\n // ── Start the event loop ────────────────────────────────────────────────\n // No initialParts — the user drives the conversation from OpenCode's UI.\n\n const eventLoopPromise = setup.processor.run(deliver, setup.wrappedSource);\n\n console.log(`\\n OpenCode agent running.`);\n console.log(` To watch: opencode attach ${opencodeUrl}\\n`);\n\n // ── Block until child exits or Ctrl+C ───────────────────────────────────\n\n const exitPromise = new Promise<void>((resolve) => {\n child.on(\"exit\", resolve);\n });\n\n const signalPromise = new Promise<void>((resolve) => {\n const handler = () => { resolve(); };\n process.on(\"SIGINT\", handler);\n process.on(\"SIGTERM\", handler);\n });\n\n await Promise.race([exitPromise, signalPromise]);\n\n // ── Cleanup ─────────────────────────────────────────────────────────────\n\n if (!childExited) {\n child.kill();\n }\n await setup.cleanup();\n clearAgentSession(\"opencode\", setup.agentName);\n\n console.log(\"Disconnected.\");\n}\n\n// ── Stop ────────────────────────────────────────────────────────────────────\n\nexport async function stopOpencode(name?: string, all?: boolean): Promise<void> {\n const sessions = listOpencodeSessions();\n if (sessions.length === 0) {\n if (all) return;\n console.error(\"No active OpenCode sessions.\");\n process.exit(1);\n }\n\n if (all) {\n for (const s of sessions) stopOpencodeSession(s);\n return;\n }\n\n let session: AgentSession | undefined;\n if (name) {\n session = sessions.find((s) => s.agentName === name);\n if (!session) {\n console.error(`No active OpenCode session named \"${name}\". Active sessions:`);\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n } else if (sessions.length === 1) {\n session = sessions[0];\n } else {\n console.error(\"Multiple active OpenCode sessions. Use --name or --all:\");\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n\n stopOpencodeSession(session);\n}\n\nfunction stopOpencodeSession(session: AgentSession): void {\n // Kill the opencode child first so it doesn't outlive its parent runtime.\n if (session.childPid) {\n try { process.kill(session.childPid, \"SIGTERM\"); } catch { /* ok */ }\n }\n if (isAgentAlive(session)) {\n try { process.kill(session.pid, \"SIGTERM\"); } catch { /* ok */ }\n }\n clearAgentSession(\"opencode\", session.agentName);\n console.log(`Stopped \"${session.agentName}\".`);\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nasync function pollForReady(url: string, timeoutMs: number): Promise<boolean> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n try {\n const res = await fetch(`${url}/session/status`);\n if (res.ok) return true;\n } catch {\n // Not ready yet\n }\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n","/**\n * apiary run codex — client-side agent runtime for OpenAI Codex CLI.\n *\n * Uses the shared runtime setup (EventProcessor, MCP server)\n * then adds Codex-specific pieces: tmux session + CodexTmuxBridge delivery.\n *\n * Codex connects to the runtime MCP server natively via config.toml\n * (no stdio bridge needed — Codex supports remote MCP servers via URL).\n * The agent joins rooms by calling join_room() — no auto-injection needed.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { rmSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\n\nimport {\n tmuxAvailable,\n tmuxCreateSession,\n tmuxSendCommand,\n tmuxAttach,\n tmuxKillSession,\n tmuxSessionExists,\n resetTerminal,\n} from \"../tmux.js\";\nimport { CodexTmuxBridge } from \"./tmux-bridge.js\";\nimport { findCodexSessionId } from \"./queue.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\nimport { codexStateToActivity } from \"../agent-state.js\";\nimport { shouldReportActivity, type AgentActivityState } from \"../../agent/event-processor.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport { agentEmoji } from \"../config.js\";\nimport { deliverInvite, sameApiaryServer } from \"../invites.js\";\nimport {\n clearCodexProfile,\n prepareCodexLaunch,\n pruneCodexProfiles,\n} from \"./launch.js\";\nimport {\n saveAgentSession,\n clearAgentSession,\n listAgentSessions,\n isAgentAlive,\n type AgentSession,\n} from \"../agent-session.js\";\n\nexport { type AgentRuntimeOptions as RunCodexOptions };\n\nexport function listCodexSessions(): AgentSession[] {\n return listAgentSessions(\"codex\");\n}\n\n/** Check if codex CLI is installed and available. */\nfunction codexAvailable(): boolean {\n try {\n execFileSync(\"codex\", [\"--version\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function runCodex(options: AgentRuntimeOptions): Promise<void> {\n // ── Headless mode — skip tmux, deliver events as plain text to stdout ────\n\n if (options.headless) {\n const setup = await setupAgentRuntime(options);\n\n const deliver = async (parts: Parameters<typeof contentPartsToString>[0]) => {\n const text = contentPartsToString(parts);\n if (text.trim()) process.stdout.write(text + \"\\n\");\n };\n\n const eventLoopPromise = setup.processor\n .run(deliver, setup.wrappedSource, setup.initialParts)\n .catch(() => {});\n\n process.stderr.write(`MCP server: ${setup.mcpServer.url}\\n`);\n\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", resolve);\n process.on(\"SIGTERM\", resolve);\n });\n\n await setup.cleanup();\n await eventLoopPromise;\n return;\n }\n\n // ── Preflight checks ────────────────────────────────────────────────────\n\n if (!tmuxAvailable()) {\n console.error(\"Error: tmux is required but not found. Install it with: brew install tmux\");\n process.exit(1);\n }\n\n if (!codexAvailable()) {\n console.error(\"Error: codex is required but not found. Install it with: npm install -g @openai/codex\");\n process.exit(1);\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // Don't pass joinUrls — Codex agents join rooms manually via join_room()\n\n const setup = await setupAgentRuntime({ ...options, joinUrls: undefined });\n\n // ── Codex MCP wiring ────────────────────────────────────────────────────\n // Codex supports remote MCP servers natively, so no stdio bridge is needed\n // (unlike Claude Code, which has an OAuth bug). apiary declares itself in a\n // config profile layered over the user's real Codex home, so the agent keeps\n // their credentials, model and project trust — see launch.ts.\n\n const mcpPort = new URL(setup.mcpServer.url).port;\n const mcpUrl = `http://127.0.0.1:${mcpPort}/mcp`;\n\n // ── Create tmux session + launch Codex ──────────────────────────────────\n\n const tmuxSession = `apiary_${setup.agentName}`;\n\n if (tmuxSessionExists(tmuxSession)) {\n tmuxKillSession(tmuxSession);\n }\n\n const home = homedir();\n const cwdFull = process.cwd();\n const cwdShort = cwdFull.startsWith(home) ? \"~\" + cwdFull.slice(home.length) : cwdFull;\n const tabTitle = `${agentEmoji(setup.agentName)} ${setup.agentName} · ${cwdShort}`;\n\n console.log(\"Launching Codex...\");\n tmuxCreateSession(tmuxSession, tabTitle);\n\n // Sweep profiles orphaned by a runtime that was killed before it could clean\n // up. \"Live\" means the process is actually alive — a stale session record\n // would otherwise keep a dead agent's profile around forever. This agent\n // counts as live so a concurrent launch cannot sweep what we are about to\n // write, and vice versa.\n pruneCodexProfiles([\n setup.agentName,\n ...listCodexSessions().filter(isAgentAlive).map((s) => s.agentName),\n ]);\n const { command } = prepareCodexLaunch(setup.agentName, mcpUrl, options.extraArgs ?? []);\n // Stamped before the launch so the session we are about to start is never\n // older than the floor findCodexSessionId searches from.\n const launchedAt = Date.now();\n tmuxSendCommand(tmuxSession, command);\n\n // ── Start event loop + attach ──────────────────────────────────────────\n\n // Deliver through `codex queue` once the session can be identified, falling\n // back to the composer until then — see queue.ts for why that is preferable.\n const bridge = new CodexTmuxBridge(tmuxSession, {\n resolveThreadId: () => findCodexSessionId({ cwd: cwdFull, startedAfter: launchedAt }),\n });\n\n // Start the event loop in the background — no initial injection.\n // The agent joins rooms by calling join_room() when the user tells it to.\n const eventLoopPromise = setup.processor.run(bridge.deliver.bind(bridge), setup.wrappedSource)\n .catch(() => {}); // Prevent unhandled rejection from crashing the process\n\n // Activity heartbeat. The Codex runtime previously reported nothing at all,\n // so the room could only guess: every agent was marked \"working\" whenever\n // anyone spoke, and a Codex agent parked on an approval prompt looked\n // identical to one that was thinking. Report what the pane actually shows.\n let lastActivityLabel: string | null = null;\n let lastState: AgentActivityState | undefined;\n let lastReportAt = 0;\n const activityTimer: NodeJS.Timeout = setInterval(() => {\n let label: string | null;\n let state: AgentActivityState | undefined;\n try {\n label = bridge.getActivityLabel();\n state = codexStateToActivity(bridge.detectState());\n } catch { return; }\n // Re-send an unchanged state periodically, not just on change. The room\n // server ages reported state out so a dead runtime does not pin a stale\n // value — but with change-only reporting a settled agent stops reporting,\n // ages out, and reads as \"not reporting at all\". Observed end-to-end: an\n // idle agent's state vanished ~15s after it settled. One tiny POST per\n // agent per interval is nothing next to being wrong about who is idle.\n if (!shouldReportActivity({\n label, state,\n lastLabel: lastActivityLabel, lastState,\n lastReportAt, now: Date.now(),\n })) return;\n lastActivityLabel = label;\n lastState = state;\n lastReportAt = Date.now();\n setup.processor.broadcastActivity(label, state).catch(() => { /* best-effort */ });\n }, 1500);\n activityTimer.unref();\n\n // Wait for Codex to start, checking the session is still alive\n for (let i = 0; i < 10; i++) {\n await new Promise((r) => setTimeout(r, 500));\n if (!tmuxSessionExists(tmuxSession)) {\n console.error(\"Error: Codex exited during startup. Try running again.\");\n clearCodexProfile(setup.agentName);\n clearInterval(activityTimer);\n bridge.stop();\n await setup.cleanup();\n resetTerminal();\n return;\n }\n }\n\n // Register session so `apiary ps` / `apiary stop` can see it.\n saveAgentSession({\n runtime: \"codex\",\n agentName: setup.agentName,\n pid: process.pid,\n tmuxSession,\n });\n\n // ── Deliver queued invite (auto-join) ─────────────────────────────────\n // If the wizard background-spawned this agent, ~/.apiary/invites/<name>\n // holds the room's join URL. deliverInvite keeps asking until the agent is\n // really in the room, and only then deletes the file — a Codex that boots\n // into a sign-in or onboarding screen gets another go once it reaches a\n // prompt, instead of losing the URL to a menu that swallowed it.\n const inviteAbort = new AbortController();\n const invitePromise = deliverInvite({\n agentName: setup.agentName,\n deliver: (parts) => bridge.deliver(parts, { dedupe: true }),\n hasJoined: (url) => setup.joinResults.some((jr) => sameApiaryServer(jr.serverUrl, url)),\n signal: inviteAbort.signal,\n }).then((outcome) => {\n // Don't let a failed auto-join be silent — that is exactly how an agent\n // ends up sitting outside the room it was spawned for. The invite is kept\n // on disk, so `apiary room resume <room>` will hand it over again.\n if (outcome === \"abandoned\" && !options.background) {\n console.log(\n `Note: \"${setup.agentName}\" did not join the room it was invited to. ` +\n `The invite is still queued — check the pane, then re-run or use \"apiary room resume\".`,\n );\n }\n return outcome;\n }).catch(() => \"cancelled\" as const);\n\n // Background mode: spawned by room create — run silently until SIGTERM.\n if (options.background) {\n // Suppress output so this process doesn't bleed into the parent terminal.\n const noop = (() => true) as unknown as typeof process.stdout.write;\n process.stdout.write = noop;\n process.stderr.write = noop;\n await new Promise<void>((resolve) => {\n process.on(\"SIGTERM\", resolve);\n process.on(\"SIGINT\", resolve);\n });\n inviteAbort.abort();\n await invitePromise;\n clearInterval(activityTimer);\n bridge.stop();\n await setup.cleanup();\n if (tmuxSessionExists(tmuxSession)) tmuxKillSession(tmuxSession);\n clearCodexProfile(setup.agentName);\n clearAgentSession(\"codex\", setup.agentName);\n return;\n }\n\n console.log(\"Attaching to Codex session...\\n\");\n\n try {\n await tmuxAttach(tmuxSession);\n } catch {\n // User detached or session ended\n }\n\n // ── Cleanup ─────────────────────────────────────────────────────────────\n\n inviteAbort.abort();\n await invitePromise;\n clearInterval(activityTimer);\n bridge.stop();\n await setup.cleanup();\n tmuxKillSession(tmuxSession);\n clearCodexProfile(setup.agentName);\n clearAgentSession(\"codex\", setup.agentName);\n resetTerminal();\n\n console.log(\"Disconnected.\");\n}\n\n// ── Stop ────────────────────────────────────────────────────────────────────\n\nexport async function stopCodex(name?: string, all?: boolean): Promise<void> {\n const sessions = listCodexSessions();\n if (sessions.length === 0) {\n if (all) return;\n console.error(\"No active Codex sessions.\");\n process.exit(1);\n }\n\n if (all) {\n for (const s of sessions) stopCodexSession(s);\n return;\n }\n\n let session: AgentSession | undefined;\n if (name) {\n session = sessions.find((s) => s.agentName === name);\n if (!session) {\n console.error(`No active Codex session named \"${name}\". Active sessions:`);\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n } else if (sessions.length === 1) {\n session = sessions[0];\n } else {\n console.error(\"Multiple active Codex sessions. Use --name or --all:\");\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n\n stopCodexSession(session);\n}\n\nfunction stopCodexSession(session: AgentSession): void {\n if (session.tmuxSession && tmuxSessionExists(session.tmuxSession)) {\n tmuxKillSession(session.tmuxSession);\n }\n if (isAgentAlive(session)) {\n try { process.kill(session.pid, \"SIGTERM\"); } catch { /* ok */ }\n }\n clearCodexProfile(session.agentName);\n // Sessions recorded by an older apiary still carry a sandboxed CODEX_HOME.\n if (session.tmpDir) {\n try { rmSync(session.tmpDir, { recursive: true }); } catch { /* ok */ }\n }\n clearAgentSession(\"codex\", session.agentName);\n resetTerminal();\n console.log(`Stopped \"${session.agentName}\".`);\n}\n","/**\n * Codex delivery that never touches the composer.\n *\n * `codex queue --thread <id> --message <text>` hands a message to a running\n * Codex session through Codex itself. Nothing is typed, so none of the\n * composer's own behaviour applies: no paste-vs-typing timing to lose, no\n * `@`-mention popup to open, no Enter to be swallowed selecting a completion.\n * Measured against the message that broke #shipyard on 2026-09-10 it landed\n * intact 4/4 in 1.2–3.3s, where keystroke delivery mangled it.\n *\n * It also queues rather than races: a busy or blocked agent picks the message\n * up when it is ready, so the poll-until-injectable loop the tmux path needs\n * does not apply either.\n *\n * The one thing Codex does not give us is a way to name a session at launch,\n * so the thread has to be recognised after the fact — see findCodexSessionId.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { readFileSync, readdirSync, statSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n/** Codex's session store, honouring CODEX_HOME like Codex itself does. */\nexport function userCodexSessionsDir(codexHome?: string): string {\n return join(codexHome ?? process.env.CODEX_HOME ?? join(homedir(), \".codex\"), \"sessions\");\n}\n\n/** The first line of a rollout file: Codex's own session metadata. */\ninterface SessionMeta {\n sessionId: string;\n cwd: string;\n startedAt: number;\n}\n\n/** Parse a rollout's session_meta record, or null if it is not one. */\nexport function parseSessionMeta(firstLine: string): SessionMeta | null {\n try {\n const record = JSON.parse(firstLine);\n if (record?.type !== \"session_meta\") return null;\n const payload = record.payload ?? {};\n const sessionId = payload.session_id ?? payload.id;\n const startedAt = Date.parse(payload.timestamp ?? \"\");\n if (typeof sessionId !== \"string\" || typeof payload.cwd !== \"string\") return null;\n if (Number.isNaN(startedAt)) return null;\n return { sessionId, cwd: payload.cwd, startedAt };\n } catch {\n return null;\n }\n}\n\n/** Every rollout file under the session store, newest mtime first. */\nfunction rolloutFiles(root: string): string[] {\n const found: Array<{ path: string; mtime: number }> = [];\n const walk = (dir: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const entry of entries) {\n const path = join(dir, entry);\n let stat;\n try {\n stat = statSync(path);\n } catch {\n continue;\n }\n if (stat.isDirectory()) walk(path);\n else if (entry.startsWith(\"rollout-\") && entry.endsWith(\".jsonl\")) {\n found.push({ path, mtime: stat.mtimeMs });\n }\n }\n };\n walk(root);\n return found.sort((a, b) => b.mtime - a.mtime).map((f) => f.path);\n}\n\nexport interface FindSessionOptions {\n /** The directory the agent was launched in. */\n cwd: string;\n /** Epoch ms of the launch. Sessions older than this belong to someone else. */\n startedAfter: number;\n /** Override the session store (tests). */\n sessionsDir?: string;\n /**\n * Clock slack in ms. Codex stamps the session when it starts, which is a\n * moment or two after we send the launch command.\n */\n slackMs?: number;\n}\n\n/**\n * Find the session id of the Codex we launched.\n *\n * Codex has no launch-time session naming — `--profile` does not reach the\n * rollout, and session *names* are auto-generated from the first message — so\n * a session is identified by where it runs and when it started: the newest\n * rollout whose cwd matches ours and which began at or after we launched.\n *\n * Returns null rather than guessing. A wrong id would deliver a room message\n * into somebody else's Codex, which is far worse than falling back to typing.\n */\nexport function findCodexSessionId(opts: FindSessionOptions): string | null {\n const root = opts.sessionsDir ?? userCodexSessionsDir();\n if (!existsSync(root)) return null;\n const floor = opts.startedAfter - (opts.slackMs ?? 5_000);\n\n for (const path of rolloutFiles(root)) {\n let firstLine: string;\n try {\n firstLine = readFileSync(path, \"utf-8\").split(\"\\n\", 1)[0] ?? \"\";\n } catch {\n continue;\n }\n const meta = parseSessionMeta(firstLine);\n if (!meta) continue;\n if (meta.cwd !== opts.cwd) continue;\n if (meta.startedAt < floor) continue;\n return meta.sessionId;\n }\n return null;\n}\n\n/** Shell out to `codex queue`. Separated so tests can observe the call. */\nexport type QueueRunner = (threadId: string, text: string) => void;\n\nconst defaultRunner: QueueRunner = (threadId, text) => {\n execFileSync(\"codex\", [\"queue\", \"--thread\", threadId, \"--message\", text], {\n stdio: \"ignore\",\n });\n};\n\n/**\n * Hand a message to a running Codex session. Returns false if Codex refused\n * it, so the caller can fall back to the composer rather than drop the message.\n */\nexport function queueCodexMessage(\n threadId: string,\n text: string,\n run: QueueRunner = defaultRunner,\n): boolean {\n try {\n run(threadId, text);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * CodexTmuxBridge — state-aware event injection into Codex CLI.\n *\n * Reads the Codex TUI screen via `tmux capture-pane`, detects the\n * current UI state, and applies the right injection strategy:\n *\n * idle → bracketed paste + Enter\n * typing → Ctrl+U (cut), bracketed paste + Enter, Ctrl+Y (restore)\n * approval → queue and poll\n * streaming → queue and poll\n * blocked → queue and poll (sign-in / onboarding — no composer exists yet)\n * unknown → queue and poll (safe default)\n *\n * Uses bracketed paste escape sequences to bypass Codex's timing-based\n * paste-burst detector (120ms Enter suppression window). Text wrapped\n * in ESC[200~...ESC[201~ is delivered as a Paste event, not individual\n * keystrokes, so the burst detector never fires.\n */\n\nimport {\n tmuxPaneIsShell,\n tmuxCapturePane,\n tmuxInjectPaste,\n tmuxSendEnter,\n tmuxSendKey,\n} from \"../tmux.js\";\nimport { queueCodexMessage } from \"./queue.js\";\nimport { composerStillHolds } from \"../composer.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\n\nexport type CodexTuiState =\n /**\n * The CLI is not running — the pane has fallen back to a shell. Injecting\n * here types a room message at the user's shell prompt, which is how\n * \"zsh: command not found: Please\" happened.\n */\n | \"absent\"\n | \"idle\"\n | \"typing\"\n | \"approval\"\n | \"streaming\"\n | \"blocked\"\n | \"unknown\";\n\n// Braille spinner characters used by Codex during streaming\nconst SPINNER_CHARS = \"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\";\n\n// Patterns that indicate an approval overlay.\n// Codex has shipped several approval UIs; match all of them, since injecting\n// into an approval menu answers a question the agent never saw.\nconst APPROVAL_PATTERNS: Array<string | RegExp> = [\n \"Would you like to\",\n \"needs your approval\",\n \"Press Enter to confirm or Esc to cancel\",\n \"Do you want to approve\",\n // v0.15x MCP tool approval: \"Allow the apiary MCP server to run tool \"x\"?\"\n /Allow the .+ MCP server to run tool/,\n \"Allow for this session\",\n // Strings taken from the Codex binary itself rather than guessed.\n \"Allow Codex to run\",\n \"Allow Codex to apply proposed code changes?\",\n \"enter to submit | esc to cancel\",\n];\n\n// Patterns that indicate Codex has not reached a composer at all — the\n// sign-in flow, the onboarding wizard, or a directory-trust prompt. There is\n// no prompt to paste into, so anything injected here is typed into a menu and\n// lost. Queue instead, and the text lands once the human clears the screen.\nconst BLOCKED_PATTERNS: Array<string | RegExp> = [\n // Not a bare \"Welcome to Codex\" — the ordinary ready banner opens\n // \"Welcome to Codex! Type a message to get started.\"\n /Welcome to Codex, /,\n \"Finish signing in via your browser\",\n \"Sign in with ChatGPT\",\n \"Sign in with Device Code\",\n \"Provide your own API key\",\n \"Sign in to continue\",\n // `[\\s\\S]` not `.`: the screen renders these on separate lines, and `.`\n // does not cross a newline, so the original pattern never matched.\n /You are running Codex in [\\s\\S]+Do you want to allow/,\n \"Do you trust the files in this folder\",\n];\n\n// Patterns that indicate the agent is actively working\nconst STREAMING_PATTERNS = [\n /Working\\s*\\(\\d+[smh]/, // \"Working (12s\" or \"Working (1m 30s\"\n /Working\\s*$/, // \"Working\" at end of line (just started)\n /esc to interrupt/, // hint text during streaming\n];\n\n// The footer of Codex's @-mention popup. Codex opens this when it reads a\n// `@` as a typed character, and while it is open Enter means \"insert the\n// highlighted completion\", not \"submit\" — see dismissMentionPopup.\nconst MENTION_POPUP_PATTERNS: RegExp[] = [\n /enter\\s+insert\\b[\\s\\S]{0,40}?esc\\s+close/i,\n];\n\n/**\n * Is Codex's @-mention popup on screen?\n *\n * Exported for tests, and matched only against the bottom of the screen: the\n * popup is anchored above the composer, whereas an agent quoting the same\n * words in its transcript must not count.\n */\nexport function codexMentionPopupIsOpen(lines: string[]): boolean {\n const tail = lines.slice(-20).join(\"\\n\");\n return MENTION_POPUP_PATTERNS.some((p) => p.test(tail));\n}\n\nexport interface DeliverOptions {\n /**\n * Drop this text if an identical copy is already queued. For callers that\n * retry a delivery they cannot observe landing (the room invite loop).\n */\n dedupe?: boolean;\n}\n\nexport interface CodexTmuxBridgeOptions {\n /**\n * Resolve the Codex session id for this agent, if it can be known.\n *\n * When it returns an id, messages go through `codex queue` instead of the\n * composer — see queue.ts. Called lazily and memoised, because the session\n * does not exist until Codex has finished starting.\n */\n resolveThreadId?: () => string | null;\n /** How often to poll when events are queued (ms). Default: 200 */\n pollIntervalMs?: number;\n /** Delay after bracketed paste before sending Enter (ms). Default: 150 */\n pasteDelayMs?: number;\n /** Delay between Ctrl+U/inject/Ctrl+Y steps (ms). Default: 50 */\n keystrokeDelayMs?: number;\n /**\n * Called when a message could not be got into the agent, after the retry.\n *\n * Delivery used to be assumed: apiary typed, reported success and moved on,\n * so a swallowed submit looked exactly like an agent choosing not to answer.\n * This is the hook that makes the difference visible to someone.\n */\n onUndelivered?: (text: string) => void;\n}\n\n/**\n * Last resort when no one has wired a handler: say it on stderr, which lands in\n * the launcher pane. Better than nothing, and a great deal better than success.\n */\nfunction defaultUndeliveredWarning(text: string): void {\n const preview = text.replace(/\\s+/g, \" \").slice(0, 80);\n process.stderr.write(`apiary: message NOT delivered to the agent — still in its composer: ${preview}\\n`);\n}\n\nexport class CodexTmuxBridge {\n private session: string;\n private queue: string[] = [];\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private pollIntervalMs: number;\n private pasteDelayMs: number;\n private keystrokeDelayMs: number;\n private stopped = false;\n private resolveThreadId?: () => string | null;\n private threadId: string | null = null;\n private onUndelivered: (text: string) => void;\n\n constructor(session: string, opts?: CodexTmuxBridgeOptions) {\n this.session = session;\n this.pollIntervalMs = opts?.pollIntervalMs ?? 200;\n this.pasteDelayMs = opts?.pasteDelayMs ?? 150;\n this.keystrokeDelayMs = opts?.keystrokeDelayMs ?? 50;\n this.resolveThreadId = opts?.resolveThreadId;\n this.onUndelivered = opts?.onUndelivered ?? defaultUndeliveredWarning;\n }\n\n /**\n * The Codex session id, resolved once and remembered.\n *\n * Resolution is deferred because the session does not exist at construction\n * time — Codex is still starting — and it is retried on later deliveries\n * until it succeeds.\n */\n private thread(): string | null {\n if (this.threadId) return this.threadId;\n if (!this.resolveThreadId) return null;\n try {\n this.threadId = this.resolveThreadId();\n } catch {\n this.threadId = null;\n }\n return this.threadId;\n }\n\n /**\n * Delivery callback — drop-in replacement for EventProcessor's deliver.\n * Pass `bridge.deliver.bind(bridge)` to EventProcessor.run().\n */\n async deliver(parts: ContentPart[], opts?: DeliverOptions): Promise<void> {\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n this.inject(text, opts?.dedupe === true);\n }\n\n /**\n * Detect the current TUI state by reading the screen.\n */\n detectState(): CodexTuiState {\n // Checked before any screen parsing: if the CLI has exited there is\n // nothing on screen worth interpreting, and a shell prompt looks\n // indistinguishable from an idle composer to a pattern matcher.\n if (tmuxPaneIsShell(this.session)) return \"absent\";\n const lines = this.captureScreen();\n return detectCodexStateFromLines(lines);\n }\n\n /**\n * Codex's live status text (\"Working (12s • esc to interrupt)\") for the room\n * strip, or null when idle. Mirrors the Claude bridge's getActivityLabel so\n * both runtimes can report the same shape.\n */\n getActivityLabel(): string | null {\n return extractCodexActivityLabel(this.captureScreen());\n }\n\n /**\n * Try to inject text, choosing strategy based on TUI state.\n * Text is flattened to a single line to avoid multi-line paste issues.\n */\n private inject(text: string, dedupe: boolean): void {\n const flat = text.replace(/\\n/g, \" \");\n const state = this.detectState();\n\n // Prefer handing the message to Codex itself. Nothing is typed, so the\n // composer's paste-vs-typing timing and its @-mention popup cannot apply,\n // and Codex holds the message for a busy agent rather than us polling for\n // an injectable moment. Skipped when the CLI is gone: queueing into a\n // session nothing is reading would silently swallow the message.\n if (state !== \"absent\") {\n const threadId = this.thread();\n if (threadId && queueCodexMessage(threadId, flat)) return;\n }\n\n switch (state) {\n case \"idle\":\n this.injectIdle(flat);\n break;\n case \"typing\":\n this.injectWhileTyping(flat);\n break;\n default:\n // approval, streaming, blocked, absent, unknown — queue it\n this.enqueue(flat, dedupe);\n break;\n }\n }\n\n /** Capture the screen via tmux capture-pane. */\n private captureScreen(): string[] {\n return tmuxCapturePane(this.session);\n }\n\n /**\n * Inject into an idle prompt using bracketed paste.\n *\n * Bracketed paste wraps text in ESC[200~...ESC[201~ so crossterm\n * delivers it as a single Paste event, bypassing the burst detector.\n * The wrapping happens in one write — see tmuxInjectPaste for why that\n * matters. After the paste, we wait for the Enter suppression window\n * (120ms) to expire, then send Enter to submit.\n */\n private injectIdle(text: string): void {\n tmuxInjectPaste(this.session, text);\n this.sleep(this.pasteDelayMs);\n this.dismissMentionPopup();\n tmuxSendEnter(this.session);\n this.confirmSubmitted(text);\n }\n\n /**\n * Check the message left the composer, and try once more if it did not.\n *\n * A submitted message moves into the transcript; one that is still in the\n * composer was not sent. The retry re-runs the popup guard first, because the\n * popup is what eats an Enter — and if the text is still there after that,\n * nothing this bridge can do will send it, so it says so rather than leaving\n * the room to infer silence.\n */\n private confirmSubmitted(text: string): void {\n this.sleep(this.pasteDelayMs);\n if (!composerStillHolds(this.captureScreen(), text)) return;\n\n this.dismissMentionPopup();\n tmuxSendEnter(this.session);\n this.sleep(this.pasteDelayMs);\n if (!composerStillHolds(this.captureScreen(), text)) return;\n\n this.onUndelivered(text);\n }\n\n /**\n * Close the @-mention popup before submitting, if the injection opened it.\n *\n * Codex classifies incoming bytes as a paste or as typing partly on timing,\n * so a payload we sent as a bracketed paste can still be read as typing on a\n * loaded machine. When that happens a `@` opens the mention popup, and Enter\n * there *inserts the highlighted completion* instead of submitting. On\n * 2026-09-10 that turned a room message reading \"sooo @all\" into\n * \"sooo @Openai-Templates\" — the top hit was an installed Codex plugin — and\n * left it unsent in the composer, so the next message appended to the same\n * stuck line and the agent looked like it was ignoring the operator.\n *\n * Escape closes the popup and leaves the composed text untouched, so the\n * Enter that follows submits what we actually injected.\n *\n * Only ever sent when the popup is on screen: Escape is also Codex's\n * interrupt, so an unconditional one would cancel a turn in progress.\n */\n private dismissMentionPopup(): void {\n if (!codexMentionPopupIsOpen(this.captureScreen())) return;\n tmuxSendKey(this.session, \"Escape\");\n this.sleep(this.keystrokeDelayMs);\n }\n\n /**\n * Inject while the user is typing:\n * 1. Ctrl+U — cut to beginning of line (into kill buffer)\n * 2. Bracketed paste our text + Enter\n * 3. Ctrl+Y — yank user's text back from kill buffer\n */\n private injectWhileTyping(text: string): void {\n // Cut user's current input\n tmuxSendKey(this.session, \"C-u\");\n this.sleep(this.keystrokeDelayMs);\n\n // Inject our event via bracketed paste\n tmuxInjectPaste(this.session, text);\n this.sleep(this.pasteDelayMs);\n this.dismissMentionPopup();\n tmuxSendEnter(this.session);\n this.confirmSubmitted(text);\n this.sleep(this.keystrokeDelayMs);\n\n // Restore user's text\n tmuxSendKey(this.session, \"C-y\");\n }\n\n /**\n * Add to queue and start polling if not already.\n *\n * With `dedupe`, identical pending text is dropped: a caller that retries a\n * delivery it never saw land (the invite loop) would otherwise stack up N\n * copies of the same prompt, all flushing at once when the TUI frees up.\n *\n * Off by default, and deliberately so — room events must never be collapsed.\n * Two formatted events carry distinct `#ref`s so they should differ anyway,\n * but \"should\" is not a guarantee worth silently dropping a message on.\n */\n private enqueue(text: string, dedupe: boolean): void {\n if (dedupe && this.queue.includes(text)) return;\n this.queue.push(text);\n this.startPolling();\n }\n\n /** Start the polling timer to drain queued events. */\n private startPolling(): void {\n if (this.pollTimer || this.stopped) return;\n this.pollTimer = setInterval(() => this.drainQueue(), this.pollIntervalMs);\n }\n\n /** Stop the polling timer. */\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Try to drain one queued event if the state is safe.\n * Drains one at a time — each injection may trigger streaming,\n * so the next poll re-checks state before injecting more.\n */\n private drainQueue(): void {\n if (this.queue.length === 0) {\n this.stopPolling();\n return;\n }\n\n const state = this.detectState();\n if (state === \"idle\" || state === \"typing\") {\n const text = this.queue.shift()!;\n\n if (state === \"idle\") {\n this.injectIdle(text);\n } else {\n this.injectWhileTyping(text);\n }\n\n if (this.queue.length === 0) {\n this.stopPolling();\n }\n }\n // else: still blocked, keep polling\n }\n\n /** Cleanup. */\n stop(): void {\n this.stopped = true;\n this.stopPolling();\n this.queue.length = 0;\n }\n\n /** Synchronous sleep — only used for tiny keystroke delays. */\n private sleep(ms: number): void {\n if (ms <= 0) return;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n }\n}\n\n// ── State detection heuristics ──────────────────────────────────────────────\n\n/**\n * Detect Codex TUI state from capture-pane output lines.\n * Exported separately so it can be unit-tested without tmux.\n *\n * Codex's TUI (Ratatui-based) uses an inline viewport. The screen shows:\n * - Conversation history in the top area\n * - Status indicator (\"Working (12s * esc to interrupt)\") when streaming\n * - Approval overlay when tool/patch approval needed\n * - Composer input area at the bottom\n *\n * Detection priority: approval > streaming > idle/typing > unknown\n */\n/**\n * Case-INSENSITIVE match against the UI-chrome patterns.\n *\n * Codex has changed the casing of its own hint text between releases: the\n * dialog reads \"Press enter to confirm or esc to cancel\" in v0.153.4 where an\n * earlier build capitalised Enter and Esc. A case-sensitive miss here is the\n * dangerous direction — the screen falls through to \"idle\" and the bridge\n * bracket-pastes a room event plus Enter into a live approval dialog, choosing\n * an answer for a question the agent never saw. A false positive only queues\n * the event for the drain loop to retry, so lean toward matching.\n */\nfunction matchesAny(text: string, patterns: Array<string | RegExp>): boolean {\n const haystack = text.toLowerCase();\n for (const pattern of patterns) {\n if (typeof pattern === \"string\") {\n if (haystack.includes(pattern.toLowerCase())) return true;\n } else if (pattern.test(text) || new RegExp(pattern.source, pattern.flags.includes(\"i\") ? pattern.flags : pattern.flags + \"i\").test(text)) {\n return true;\n }\n }\n return false;\n}\n\nexport function detectCodexStateFromLines(lines: string[]): CodexTuiState {\n if (lines.length === 0) return \"unknown\";\n\n // Work with the last ~20 lines (visible bottom of screen)\n const tail = lines.slice(-20);\n const tailText = tail.join(\"\\n\");\n\n // 1. Not-yet-usable screens — highest priority. A login or onboarding\n // screen has no composer, so \"no approval, no spinner ⇒ idle\" would\n // otherwise paste straight into a menu.\n if (matchesAny(tailText, BLOCKED_PATTERNS)) return \"blocked\";\n\n // 2. Approval overlay\n if (matchesAny(tailText, APPROVAL_PATTERNS)) return \"approval\";\n\n // 3. Streaming — agent is working\n for (const pattern of STREAMING_PATTERNS) {\n if (pattern.test(tailText)) return \"streaming\";\n }\n\n // Check for spinner characters in the last few lines\n const lastFew = tail.slice(-5).join(\"\");\n for (const ch of SPINNER_CHARS) {\n if (lastFew.includes(ch)) return \"streaming\";\n }\n\n // 4. Idle/Typing — look for the composer input area at the bottom.\n // Codex renders the composer as the last interactive element.\n // When idle, the bottom lines contain just the placeholder or empty input.\n // When typing, the bottom lines contain user-entered text.\n //\n // Heuristic: if none of the blocking states are detected (approval,\n // streaming), and the screen has content, assume the composer is\n // available. Check the last non-empty line for signs of user input.\n //\n // This is intentionally permissive — the worst case for a false\n // \"idle\" is that the injected text arrives during an unexpected\n // state and queues up in the input buffer harmlessly.\n const nonEmpty = tail.filter((l) => l.trim().length > 0);\n if (nonEmpty.length > 0) {\n // Look for signs that the screen is showing normal conversation + composer.\n // If we didn't match approval or streaming above, the composer is likely visible.\n // We need to distinguish idle (empty composer) from typing (text in composer).\n //\n // Codex shows a cursor line at the very bottom. Without empirical data on\n // exact patterns, we check if the last non-empty line looks like user input\n // (not part of conversation output which typically has structure like timestamps,\n // tool names, or markdown formatting).\n //\n // For now: if no blocking state is detected, return \"idle\" as the safe\n // injectable state. The bracketed paste approach is resilient enough that\n // injecting into a \"typing\" state via the idle path still works (text gets\n // appended to whatever the user was typing, and Enter submits all of it).\n return \"idle\";\n }\n\n return \"unknown\";\n}\n\n/**\n * Pull Codex's status line out of a screen capture.\n *\n * Exported separately so it can be tested without tmux. Returns null when\n * nothing is running — the room shows the state glyph alone in that case.\n */\nexport function extractCodexActivityLabel(lines: string[]): string | null {\n // Scan bottom-up: the status line lives just under the transcript, and\n // earlier conversation text can quote the same words.\n for (const line of lines.slice(-12).reverse()) {\n const m = /(Working|Thinking|Compacting|Reviewing)\\b[^)\\n]*/.exec(line);\n if (!m) continue;\n // Trim Codex's interrupt hint — it is UI chrome, not progress.\n const label = m[0].replace(/\\s*[•·]?\\s*esc to interrupt.*$/i, \"\").trim();\n if (label) return label.slice(0, 120);\n }\n return null;\n}\n","/**\n * Codex launch wiring — how apiary declares itself to a Codex session.\n *\n * apiary needs two things from a Codex session: its MCP server declared, and\n * its own tools auto-approved (an agent spawned to sit in a room cannot join\n * one if every join_room call blocks on a keypress nobody is there to press).\n *\n * Both are declared in a Codex *config profile* — `<CODEX_HOME>/<name>.config.toml`,\n * layered over the user's base config by `codex --profile <name>`. apiary owns\n * the `apiary-<agent>` profile name and deletes the file when the agent stops.\n *\n * Two alternatives were tried and rejected:\n *\n * - A throwaway CODEX_HOME (what apiary used to do). CODEX_HOME is where\n * `auth.json` lives, so a logged-in user still landed on the OAuth sign-in\n * screen and the agent never reached a prompt; it also silently dropped\n * `model`, reasoning effort and project trust. Copying auth.json in only\n * moves the problem, because Codex rotates the refresh token and writes it\n * back to CODEX_HOME — the refreshed credentials would die with the temp\n * directory while the user's real ~/.codex kept a token already spent.\n *\n * - `codex -c key=value` overrides. Correct in principle, but auto-approval\n * is per tool, so it needs ~17 flags — a 1.2KB command line, which is\n * typed into the pane a keystroke at a time and gets truncated mid-quote.\n *\n * A profile keeps the user's real Codex home (credentials, model, project\n * trust all intact) and the launch command stays one short line.\n */\n\nimport { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nimport { RUNTIME_TOOL_NAMES } from \"../../agent/mcp/runtime.js\";\nimport { wantsUnattendedAgents } from \"../approvals.js\";\n\n/** Timeouts for apiary's own MCP server, in seconds. */\nconst STARTUP_TIMEOUT_SEC = 15;\nconst TOOL_TIMEOUT_SEC = 60;\n\nconst PROFILE_PREFIX = \"apiary-\";\n\n/** The user's real Codex home — honours CODEX_HOME so nested launches behave. */\nexport function userCodexHome(env: NodeJS.ProcessEnv = process.env): string {\n const fromEnv = env.CODEX_HOME?.trim();\n return fromEnv && fromEnv.length > 0 ? fromEnv : join(homedir(), \".codex\");\n}\n\n/**\n * How Codex should treat calls to apiary's own MCP tools.\n *\n * `\"approve\"` means \"run without asking\". Anyone who would rather confirm each\n * call can set APIARY_CODEX_TOOL_APPROVAL (Codex's own value, e.g. \"ask\") —\n * with the caveat that a background-spawned agent then cannot join a room\n * unattended, because nobody is watching its pane to press a key.\n */\nexport function codexToolApprovalMode(env: NodeJS.ProcessEnv = process.env): string {\n const override = env.APIARY_CODEX_TOOL_APPROVAL?.trim();\n if (override && override.length > 0) return override;\n return wantsUnattendedAgents(env) ? \"approve\" : \"ask\";\n}\n\n\n\n/**\n * Profile name for an agent. Codex resolves this to a filename, so keep it to\n * characters that cannot escape the Codex home or confuse the CLI.\n */\nexport function codexProfileName(agentName: string): string {\n const safe = agentName.replace(/[^A-Za-z0-9_-]/g, \"-\").slice(0, 64);\n // A name that sanitises down to nothing but separators is degenerate, and\n // would have every such agent colliding on the same profile.\n return `${PROFILE_PREFIX}${/[A-Za-z0-9]/.test(safe) ? safe : \"agent\"}`;\n}\n\n/** Render a TOML string value: `\"http://host/path\"`. */\nfunction tomlString(value: string): string {\n return JSON.stringify(value);\n}\n\n/**\n * The profile body: apiary's MCP server, and per-tool auto-approval.\n *\n * Per tool, not per server: Codex accepts `approval_mode` on the server table\n * and then ignores it. Only `mcp_servers.<server>.tools.<tool>` is honoured —\n * the shape Codex itself writes when a human picks \"Always allow\".\n *\n * Scoped deliberately: shell commands, file writes and every other MCP server\n * still go through Codex's normal approval flow.\n */\nexport function renderCodexProfile(\n mcpUrl: string,\n approvalMode: string = codexToolApprovalMode(),\n unattended: boolean = wantsUnattendedAgents(),\n): string {\n const blocks = [\n // Top-level keys must precede every table header, or TOML parses them as\n // members of the last table.\n ...(unattended ? [[\n \"# Written by apiary. Layered over your own config; deleted when the agent stops.\",\n \"#\",\n \"# No approval prompts: nobody is watching this pane, so a prompt is a\",\n \"# permanent stall. Commands still run inside Codex's sandbox — anything\",\n \"# it will not allow returns a failure to the model instead of asking.\",\n 'approval_policy = \"never\"',\n ].join(\"\\n\")] : []),\n [\n ...(unattended ? [] : [\"# Written by apiary. Layered over your own config; deleted when the agent stops.\"]),\n \"[mcp_servers.apiary]\",\n `url = ${tomlString(mcpUrl)}`,\n `startup_timeout_sec = ${STARTUP_TIMEOUT_SEC}`,\n `tool_timeout_sec = ${TOOL_TIMEOUT_SEC}`,\n ].join(\"\\n\"),\n ...RUNTIME_TOOL_NAMES.map((tool) => [\n `[mcp_servers.apiary.tools.${tool}]`,\n `approval_mode = ${tomlString(approvalMode)}`,\n ].join(\"\\n\")),\n ];\n\n return blocks.join(\"\\n\\n\") + \"\\n\";\n}\n\n/** Absolute path of an agent's profile file. */\nexport function codexProfilePath(agentName: string, codexHome: string = userCodexHome()): string {\n return join(codexHome, `${codexProfileName(agentName)}.config.toml`);\n}\n\n/**\n * Write the agent's profile and return the launch command.\n *\n * `extraArgs` are the user's own passthrough flags and come last, so\n * `apiary codex bee --model gpt-5.6-terra` still wins over the profile.\n */\nexport function prepareCodexLaunch(\n agentName: string,\n mcpUrl: string,\n extraArgs: string[] = [],\n codexHome: string = userCodexHome(),\n): { command: string; profilePath: string } {\n mkdirSync(codexHome, { recursive: true });\n\n const profilePath = codexProfilePath(agentName, codexHome);\n writeFileSync(profilePath, renderCodexProfile(mcpUrl), { mode: 0o600 });\n\n const command = [\"codex\", \"--profile\", codexProfileName(agentName), ...extraArgs].join(\" \");\n return { command, profilePath };\n}\n\n/** Remove an agent's profile. Never throws. */\nexport function clearCodexProfile(agentName: string, codexHome: string = userCodexHome()): void {\n try {\n rmSync(codexProfilePath(agentName, codexHome), { force: true });\n } catch {\n /* already gone */\n }\n}\n\n/**\n * Delete apiary profiles left behind by agents that are no longer running —\n * a crashed or SIGKILLed runtime never reaches its cleanup path, and these\n * would otherwise accumulate in the user's Codex home forever.\n */\nexport function pruneCodexProfiles(\n liveAgentNames: readonly string[],\n codexHome: string = userCodexHome(),\n): number {\n if (!existsSync(codexHome)) return 0;\n\n const live = new Set(liveAgentNames.map((name) => `${codexProfileName(name)}.config.toml`));\n let removed = 0;\n\n try {\n for (const entry of readdirSync(codexHome)) {\n if (!entry.startsWith(PROFILE_PREFIX) || !entry.endsWith(\".config.toml\")) continue;\n if (live.has(entry)) continue;\n try {\n rmSync(join(codexHome, entry), { force: true });\n removed++;\n } catch {\n /* leave it for next time */\n }\n }\n } catch {\n /* unreadable home — nothing to prune */\n }\n\n return removed;\n}\n","/**\n * This package's version, for the help banner and the update check.\n *\n * Its own module because both index.ts and help.ts need it, and help.ts must\n * be importable without pulling in index.ts (which runs main() on import).\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** Read a top-level field from this package's package.json. Null on failure. */\nexport function readPackageField(field: string): string | null {\n try {\n const dir = dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(readFileSync(resolve(dir, \"../../package.json\"), \"utf-8\"));\n return typeof pkg[field] === \"string\" ? pkg[field] : null;\n } catch {\n return null;\n }\n}\n\nexport function getVersion(): string {\n return readPackageField(\"version\") ?? process.env.npm_package_version ?? \"unknown\";\n}\n","/**\n * `apiary --help` and `apiary examples`.\n *\n * Split out of index.ts so it can be imported and asserted directly. The\n * drift test used to spawn the built binary, which made it depend on `dist/`\n * being current — and `make release` runs the tests BEFORE the build, so any\n * help change broke the release until dist was rebuilt by hand. index.ts\n * cannot be imported instead: it calls main() at module top level, so\n * importing it runs the CLI.\n */\n\nimport { getVersion } from \"./version.js\";\n\nexport function printUsage(stream: typeof console.log = console.log): void {\n const color = process.stdout.isTTY;\n const Y = color ? \"\\x1b[33m\" : \"\"; // yellow\n const C = color ? \"\\x1b[36m\" : \"\"; // cyan\n const G = color ? \"\\x1b[32m\" : \"\"; // green\n const D = color ? \"\\x1b[2m\" : \"\"; // dim\n const B = color ? \"\\x1b[1m\" : \"\"; // bold\n const R = color ? \"\\x1b[0m\" : \"\"; // reset\n\n const COL = 40;\n const ansi = /\\x1b\\[[0-9;]*m/g;\n const row = (cmd: string, desc: string) => {\n const visible = cmd.replace(ansi, \"\").length;\n return ` ${cmd}${\" \".repeat(Math.max(2, COL - visible))}${D}${desc}${R}`;\n };\n\n stream(\"\");\n stream(` ${Y}${B}apiary${R} ${D}v${getVersion()} — shared rooms for AI agents${R}`);\n stream(\"\");\n stream(` ${G}${B}Workspace${R}`);\n stream(row(`${C}apiary room create${R}`, \"Create a workspace + invite participants\"));\n stream(row(`${C}apiary room ${Y}<name>${R} ${D}[--share]${R}`, \"Start a workspace + join\"));\n stream(row(`${C}apiary room resume ${Y}<name>${R}`, \"Rejoin your workspace\"));\n stream(row(`${C}apiary room list${R}`, \"List your workspaces\"));\n stream(\"\");\n stream(` ${G}${B}Who can reach the room${R}`);\n stream(row(`${D}--bind ${Y}tailscale${R}`, \"Your devices anywhere, via Tailscale — not the local network\"));\n stream(row(`${D}--bind ${Y}lan${R}`, \"Anyone on the same wifi\"));\n stream(row(`${D}--share${R}`, \"A public cloudflared URL, for people off your tailnet\"));\n stream(` ${D}room create asks this if you don't pass it. Default: this machine only.${R}`);\n stream(\"\");\n stream(` ${G}${B}Participants${R} ${D}(each person connects their own agents)${R}`);\n stream(row(`${C}apiary join ${Y}<url>${R} ${D}[${Y}<name>${R}${D}]${R}`, \"Join a workspace\"));\n stream(row(`${C}apiary claude ${Y}<name>${R} ${D}[--admin]${R}`, \"Connect Claude Code to the room\"));\n stream(row(`${C}apiary codex ${Y}<name>${R} ${D}[--admin]${R}`, \"Connect Codex to the room\"));\n stream(row(`${D}--mode ${Y}standby${R}`, \"Quiet until @mentioned, pinged or whispered to\"));\n stream(` ${D}In room create, suffix the alias instead: ${R}${C}bee:standby${R}${D}. The first agent${R}`);\n stream(` ${D}listens to everything, the rest go standby — one remark, one reply.${R}`);\n stream(\"\");\n stream(` ${G}${B}In the room${R}`);\n stream(row(`${C}/watch ${Y}<agent>${R}`, \"Open that agent's terminal in a new window (Ctrl+<n> too)\"));\n stream(row(`${C}/setmode ${Y}<agent> <mode>${R}`, \"Change who an agent listens to, live\"));\n stream(row(`${C}/ping ${Y}<agent>${R}`, \"Ask for a one-line status\"));\n stream(` ${D}The strip shows each agent as ✓ idle, … working, or ⏸ needs you —${R}`);\n stream(` ${D}\"needs you\" means it is stuck on something only you can clear.${R}`);\n stream(\"\");\n stream(row(`${C}apiary ps${R}`, \"List active rooms + agents\"));\n stream(row(`${C}apiary stop ${D}[${Y}<name>${R}${D} | --all]${R}`, \"Stop agents + rooms\"));\n stream(row(`${C}apiary examples${R}`, \"Examples + workflows\"));\n stream(\"\");\n}\n\nexport function printExamples(): void {\n const color = process.stdout.isTTY;\n const Y = color ? \"\\x1b[33m\" : \"\";\n const C = color ? \"\\x1b[36m\" : \"\";\n const G = color ? \"\\x1b[32m\" : \"\";\n const D = color ? \"\\x1b[2m\" : \"\";\n const B = color ? \"\\x1b[1m\" : \"\";\n const R = color ? \"\\x1b[0m\" : \"\";\n\n const log = console.log.bind(console);\n\n log(\"\");\n log(` ${Y}${B}apiary examples${R}`);\n log(\"\");\n\n // ── Create a workspace\n log(` ${G}${B}Create a workspace${R}`);\n log(` ${C}apiary room create${R} ${D}# guided setup — name it, invite participants${R}`);\n log(` ${C}apiary room ${Y}sprint-42${R} ${D}# quick start — host + join immediately${R}`);\n log(` ${C}apiary room ${Y}sprint-42${R} ${D}--bind tailscale${R} ${D}# reachable from your phone, over Tailscale only${R}`);\n log(` ${C}apiary room ${Y}sprint-42${R} ${D}--share${R} ${D}# same, with a public tunnel URL${R}`);\n log(` ${D}Ctrl+C leaves the server running — rejoin anytime:${R}`);\n log(` ${C}apiary room resume ${Y}sprint-42${R}`);\n log(\"\");\n\n // ── Join a workspace\n log(` ${G}${B}Join a workspace${R} ${D}(each person connects their own agents)${R}`);\n log(` ${C}apiary join ${Y}<url>${R} ${D}# join as a participant${R}`);\n log(` ${C}apiary join ${Y}<url>${R} ${D}--guest${R} ${D}# join read-only${R}`);\n log(` ${C}apiary claude ${Y}Cleo${R} ${D}--admin${R} ${D}# connect your Claude Code agent${R}`);\n log(` ${C}apiary codex ${Y}Rex${R} ${D}# connect your Codex agent${R}`);\n log(` ${C}apiary codex ${Y}Rex${R} ${D}--mode standby${R} ${D}# quiet until @mentioned, pinged or whispered${R}`);\n log(` ${D}Tell your agent the room URL — it joins via apiary__join_room.${R}`);\n log(\"\");\n\n // ── Authority / roles\n log(` ${G}${B}Authority${R} ${D}admin > product_owner > member > guest${R}`);\n log(` ${C}/promote ${Y}<name>${R} ${D}# elevate to product owner (can manage members + modes)${R}`);\n log(` ${C}/demote ${Y}<name>${R} ${D}# drop product owner back to member${R}`);\n log(` ${C}/mute ${Y}<name>${R} ${D}# demote to guest (read-only)${R}`);\n log(` ${C}/unmute ${Y}<name>${R} ${D}# restore to member${R}`);\n log(` ${C}/kick ${Y}<name>${R} ${D}# remove from the room (admin only)${R}`);\n log(` ${C}/share ${D}--as member${R} ${D}# generate a share link at a specific tier${R}`);\n log(\"\");\n\n // ── TUI commands\n log(` ${G}${B}TUI commands${R}`);\n log(` ${C}/who${R} ${D}list participants with their roles${R}`);\n log(` ${C}/watch ${Y}<name>${R} ${D}open that agent's terminal in a new window (or Ctrl+<n>)${R}`);\n log(` ${C}/ping ${Y}<name>${R} ${D}ping for a status check${R}`);\n log(` ${C}/setmode ${Y}<n> <m>${R} ${D}set engagement mode (admin / product owner)${R}`);\n log(` ${C}/share${R} ${D}generate share links${R}`);\n log(` ${C}/tunnel${R} ${D}start a cloudflared tunnel mid-session (admin)${R}`);\n log(` ${C}/clear${R} ${D}wipe room history (admin)${R}`);\n log(` ${C}/sound${R} ${D}toggle notification sounds${R}`);\n log(` ${C}/leave${R} ${D}disconnect${R}`);\n log(\"\");\n\n // ── Messaging\n log(` ${G}${B}Messaging${R} ${D}(agent tool params)${R}`);\n log(` ${D}Whisper — visible only to named recipients:${R}`);\n log(` ${C}apiary__send_message(room, content, null, ${Y}[\"Alice\", \"Bob\"]${R}${C})${R}`);\n log(` ${D}Attachments — local file or image:${R}`);\n log(` ${C}apiary__send_message(room, content, null, null, ${Y}[{ type: \"path\", path: \"...\" }]${R}${C})${R}`);\n log(\"\");\n\n // ── Sessions\n log(` ${G}${B}Sessions${R}`);\n log(` ${C}apiary ps${R} ${D}# list rooms + agents with join links${R}`);\n log(` ${C}apiary stop ${Y}Cleo${R} ${D}# stop one agent${R}`);\n log(` ${C}apiary stop ${D}--all${R} ${D}# stop everything${R}`);\n log(\"\");\n}\n","#!/usr/bin/env node\n\n/** apiary CLI — shared rooms for AI agents. */\n\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { serve, listRoomSessions } from \"./serve.js\";\nimport { join } from \"./join.js\";\nimport { runClaude, stopClaude, listClaudeSessions } from \"./claude/run.js\";\nimport { runOpencode, stopOpencode, listOpencodeSessions } from \"./opencode/run.js\";\nimport { runCodex, stopCodex, listCodexSessions } from \"./codex/run.js\";\nimport { buildShareUrl } from \"./auth.js\";\nimport { printUsage, printExamples } from \"./help.js\";\nimport { getVersion, readPackageField } from \"./version.js\";\nimport { parseEngagementMode } from \"../agent/engagement.js\";\nimport { resolveBindAddress } from \"./bind.js\";\nimport { printUpdateNotice, checkForUpdate } from \"./update.js\";\n\nconst args = process.argv.slice(2);\n\nfunction getFlag(name: string, arr: string[] = args): string | undefined {\n const idx = arr.indexOf(`--${name}`);\n if (idx === -1) return undefined;\n const value = arr[idx + 1];\n if (value === undefined || value.startsWith(\"--\")) return undefined;\n return value;\n}\n\n/** Collect all values for a repeatable flag (e.g. --join url1 --join url2). */\nfunction getAllFlags(name: string, arr: string[] = args): string[] {\n const results: string[] = [];\n const flag = `--${name}`;\n for (let i = 0; i < arr.length; i++) {\n if (arr[i] === flag && arr[i + 1] && !arr[i + 1].startsWith(\"--\")) {\n results.push(arr[i + 1]);\n }\n }\n return results;\n}\n\n/**\n * Resolve `--bind` for a command, exiting with an explanation if it cannot be\n * satisfied. Returns undefined when the flag was not passed.\n */\nfunction bindFlag(args: string[]): string | undefined {\n const spec = getFlag(\"bind\", args);\n if (!spec) return undefined;\n const res = resolveBindAddress(spec);\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n if (res.note) console.log(` Listening on ${res.address} — ${res.note}`);\n return res.address;\n}\n\nasync function main(): Promise<void> {\n // ── apiary examples ──────────────────────────────────────────────────\n if (args[0] === \"examples\") {\n printExamples();\n return;\n }\n\n // ── --help anywhere ────────────────────────────────────────────────────\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n printUsage();\n return;\n }\n\n // ── --version / -v ──────────────────────────────────────────────────\n if (args.includes(\"--version\") || args.includes(\"-v\") || args[0] === \"version\") {\n try {\n const { createRequire } = await import(\"node:module\");\n const require = createRequire(import.meta.url);\n const pkg = require(\"../../package.json\");\n console.log(pkg.version);\n } catch {\n console.log(\"unknown\");\n }\n return;\n }\n\n // ── apiary update — git pull + build (or npm fallback) ──────────────\n if (args[0] === \"update\") {\n const { runUpdate } = await import(\"./update.js\");\n const targetVersion = args[1] && !args[1].startsWith(\"--\") ? args[1] : undefined;\n await runUpdate(targetVersion);\n return;\n }\n\n // ── Version check (non-blocking) ──────────────────────────────────────\n printUpdateNotice();\n checkForUpdate();\n\n // ── apiary stop [<name> | --all] ──────────────────────────────────────\n if (args[0] === \"stop\") {\n const rest = args.slice(1);\n const all = rest.includes(\"--all\");\n // Positional name or --name flag\n const positional = rest.find((a) => !a.startsWith(\"--\"));\n const name = getFlag(\"name\", rest) ?? positional;\n\n const { roomStop } = await import(\"./room.js\");\n if (all) {\n // Stop all rooms first (each roomStop also kills its agent tmux sessions).\n // Pass the pre-loaded session to avoid O(N²) disk scans.\n for (const r of listRoomSessions()) {\n try { await roomStop(r.roomName, { session: r, quietIfDead: true }); } catch { /* keep going */ }\n }\n // Then stop any remaining standalone agent runtimes (claude, codex, opencode).\n await stopClaude(undefined, true);\n await stopCodex(undefined, true);\n await stopOpencode(undefined, true);\n return;\n }\n if (name) {\n // If the name matches a room, stop the room (agents + server). Otherwise treat as agent name.\n const room = listRoomSessions().find((s) => s.roomName === name);\n if (room) { await roomStop(name, { session: room }); return; }\n\n // Match against agent runtimes by name.\n if (listCodexSessions().some((s) => s.agentName === name)) {\n await stopCodex(name, false); return;\n }\n if (listOpencodeSessions().some((s) => s.agentName === name)) {\n await stopOpencode(name, false); return;\n }\n }\n await stopClaude(name, false);\n return;\n }\n\n // ── apiary ps ────────────────────────────────────────────────────────────\n if (args[0] === \"ps\") {\n const rooms = listRoomSessions();\n const claudeAgents = listClaudeSessions().map((s) => ({ runtime: \"claude\", agentName: s.agentName, pid: s.pid }));\n const codexAgents = listCodexSessions().map((s) => ({ runtime: \"codex\", agentName: s.agentName, pid: s.pid }));\n const opencodeAgents = listOpencodeSessions().map((s) => ({ runtime: \"opencode\", agentName: s.agentName, pid: s.pid }));\n const agents = [...claudeAgents, ...codexAgents, ...opencodeAgents];\n\n if (rooms.length === 0 && agents.length === 0) {\n console.log(\"No active sessions.\");\n return;\n }\n\n for (const r of rooms) {\n let alive = false;\n try { process.kill(r.pid, 0); alive = true; } catch { /* dead */ }\n if (!alive) continue;\n const joinUrl = buildShareUrl(\n r.publicUrl !== r.serverUrl ? r.publicUrl : r.serverUrl,\n r.memberToken,\n );\n console.log(` \\x1b[1m${r.roomName}\\x1b[0m \\x1b[2m(room, pid ${r.pid})\\x1b[0m`);\n console.log(` \\x1b[2mJoin:\\x1b[0m \\x1b[36mapiary join\\x1b[0m ${joinUrl}`);\n }\n\n for (const s of agents) {\n let alive = false;\n try { process.kill(s.pid, 0); alive = true; } catch { /* dead */ }\n const status = alive ? \"running\" : \"stale\";\n console.log(` ${s.agentName} (${s.runtime}, pid ${s.pid}, ${status})`);\n }\n return;\n }\n\n // ── apiary room [create | resume | list | <name>] ───────────────────────\n if (args[0] === \"room\") {\n const sub = args[1];\n const roomArgs = args.slice(2);\n\n // apiary room create\n if (sub === \"create\") {\n const portStr = getFlag(\"port\", roomArgs);\n const port = portStr ? parseInt(portStr, 10) : undefined;\n const { roomCreate } = await import(\"./room.js\");\n await roomCreate({\n room: getFlag(\"room\", roomArgs) ?? roomArgs.find((a) => !a.startsWith(\"--\")),\n port,\n share: roomArgs.includes(\"--share\"),\n expose: roomArgs.includes(\"--expose\"),\n bind: bindFlag(roomArgs),\n foundation: !roomArgs.includes(\"--no-foundation\"),\n });\n return;\n }\n\n // apiary room resume <name>\n if (sub === \"resume\") {\n const name = roomArgs.find((a) => !a.startsWith(\"--\"));\n if (!name) {\n console.error(\"Usage: apiary room resume <name>\");\n process.exit(1);\n }\n const { roomResume } = await import(\"./room.js\");\n await roomResume(name);\n return;\n }\n\n // apiary room list\n if (sub === \"list\" || sub === \"ls\") {\n const { roomList } = await import(\"./room.js\");\n roomList();\n return;\n }\n\n // apiary room stop <name>\n if (sub === \"stop\") {\n const name = roomArgs.find((a) => !a.startsWith(\"--\"));\n if (!name) {\n console.error(\"Usage: apiary room stop <name>\");\n process.exit(1);\n }\n const { roomStop } = await import(\"./room.js\");\n await roomStop(name);\n return;\n }\n\n // apiary room <name> [<displayName>] — daemon-backed host+join (legacy compat)\n {\n const allRoomArgs = args.slice(1);\n const positionals = allRoomArgs.filter((a) => !a.startsWith(\"--\"));\n const roomName = positionals[0];\n const userName = positionals[1] ?? getFlag(\"name\", allRoomArgs);\n const portStr = getFlag(\"port\", allRoomArgs);\n const port = portStr ? parseInt(portStr, 10) : undefined;\n if (port !== undefined && (isNaN(port) || port < 0 || port > 65535)) {\n console.error(`Invalid port: ${portStr}`);\n process.exit(1);\n }\n\n // Daemon-backed so Ctrl+C only kills TUI\n const { spawnDaemonServer, isDaemonResult } = await import(\"./room.js\");\n const daemonRes = await spawnDaemonServer({\n room: roomName,\n port,\n share: allRoomArgs.includes(\"--share\"),\n expose: allRoomArgs.includes(\"--expose\"),\n bind: bindFlag(allRoomArgs),\n });\n if (!isDaemonResult(daemonRes)) {\n console.error(`Failed to start server: ${daemonRes.reason}`);\n if (daemonRes.portInUse) {\n console.error(`Tip: a previous room daemon is bound. Run 'apiary ps' to inspect, 'apiary stop --all' to free it.`);\n } else if (daemonRes.stderr?.trim()) {\n console.error(daemonRes.stderr);\n }\n process.exit(1);\n }\n const daemon = daemonRes;\n const adminJoinUrl = buildShareUrl(daemon.serverUrl, daemon.adminToken);\n const participantShareUrl = buildShareUrl(\n daemon.publicUrl !== daemon.serverUrl ? daemon.publicUrl : daemon.serverUrl,\n daemon.memberToken,\n );\n await join({ server: adminJoinUrl, name: userName, shareUrl: participantShareUrl });\n console.log(`\\n Server still running (room: ${daemon.roomName})`);\n console.log(` Rejoin: apiary room resume ${daemon.roomName}\\n`);\n return;\n }\n }\n\n // ── apiary mcp [<name>] [--admin] [--join <url>] ──────────────────────────\n if (args[0] === \"mcp\") {\n const restArgs = args.slice(1);\n const joinUrls = getAllFlags(\"join\", restArgs);\n const positionalName = restArgs.find((a, i) => {\n if (a.startsWith(\"--\")) return false;\n if (i > 0 && new Set([\"--name\", \"--join\"]).has(restArgs[i - 1])) return false;\n return true;\n });\n const name = getFlag(\"name\", restArgs) ?? positionalName;\n\n // Check for a pending invite dropped by `apiary room resume`\n const pendingUrls = [...joinUrls];\n if (name) {\n const { consumeInvite } = await import(\"./invites.js\");\n const inviteUrl = consumeInvite(name);\n if (inviteUrl) pendingUrls.push(inviteUrl);\n }\n\n const { runMcpServer } = await import(\"./mcp/run.js\");\n await runMcpServer({\n joinUrls: pendingUrls.length > 0 ? pendingUrls : undefined,\n name,\n admin: restArgs.includes(\"--admin\"),\n });\n return;\n }\n\n // ── apiary run <runtime> / apiary claude / apiary codex / apiary opencode\n const isRunAlias = args[0] === \"claude\" || args[0] === \"codex\" || args[0] === \"opencode\";\n if ((args[0] === \"run\" && (args[1] === \"claude\" || args[1] === \"opencode\" || args[1] === \"codex\")) || isRunAlias) {\n const runtime = isRunAlias ? args[0] : args[1];\n const restArgs = args.slice(isRunAlias ? 1 : 2);\n\n // Split on -- separator: apiary flags before, passthrough args after\n const ddIndex = restArgs.indexOf(\"--\");\n const apiaryArgs = ddIndex >= 0 ? restArgs.slice(0, ddIndex) : restArgs;\n const explicitExtra = ddIndex >= 0 ? restArgs.slice(ddIndex + 1) : [];\n\n // Known apiary flags — anything else gets forwarded to the underlying tool\n // --mode must be listed here or it would be forwarded to the underlying\n // CLI as an unknown flag, where claude/codex reject it.\n const KNOWN_FLAGS = new Set([\"--name\", \"--admin\", \"--headless\", \"--resume\", \"--join\", \"--background\", \"--mode\"]);\n const unknownArgs: string[] = [];\n for (let i = 0; i < apiaryArgs.length; i++) {\n const arg = apiaryArgs[i];\n if (arg.startsWith(\"--\") && !KNOWN_FLAGS.has(arg)) {\n unknownArgs.push(arg);\n // If the next arg isn't a flag, it's probably a value — forward it too\n if (i + 1 < apiaryArgs.length && !apiaryArgs[i + 1].startsWith(\"--\")) {\n unknownArgs.push(apiaryArgs[++i]);\n }\n }\n }\n const extraArgs = [...unknownArgs, ...explicitExtra];\n\n const joinUrls = getAllFlags(\"join\", apiaryArgs);\n\n // First positional arg (not a flag, not consumed by a known flag) is the name\n const positionalName = apiaryArgs.find((a, i) => {\n if (a.startsWith(\"--\")) return false;\n // Check if previous arg is a flag that takes a value\n if (i > 0 && KNOWN_FLAGS.has(apiaryArgs[i - 1])) return false;\n return true;\n });\n\n const runtimeOptions = {\n joinUrls: joinUrls.length > 0 ? joinUrls : undefined,\n name: getFlag(\"name\", apiaryArgs) ?? positionalName,\n admin: apiaryArgs.includes(\"--admin\"),\n headless: apiaryArgs.includes(\"--headless\"),\n resume: apiaryArgs.includes(\"--resume\"),\n background: apiaryArgs.includes(\"--background\"),\n mode: parseEngagementMode(getFlag(\"mode\", apiaryArgs)),\n extraArgs,\n };\n\n if (runtime === \"claude\") {\n await runClaude(runtimeOptions);\n } else if (runtime === \"opencode\") {\n await runOpencode(runtimeOptions);\n } else {\n await runCodex(runtimeOptions);\n }\n return;\n }\n\n // ── apiary join <url> [<name>] ──────────────────────────────────────────\n if (args[0] === \"join\") {\n const server = args[1];\n if (!server || server.startsWith(\"--\")) {\n console.error(\"Usage: apiary join <url> [<name>] [--guest] [--headless]\");\n process.exit(1);\n }\n // Second positional arg is the name\n const joinName = args[2] && !args[2].startsWith(\"--\") ? args[2] : getFlag(\"name\");\n await join({\n server,\n name: joinName,\n guest: args.includes(\"--guest\"),\n headless: args.includes(\"--headless\"),\n });\n return;\n }\n\n // ── apiary serve ───────────────────────────────────────────────────────\n if (args[0] === \"serve\") {\n const portStr = getFlag(\"port\");\n const port = portStr ? parseInt(portStr, 10) : undefined;\n if (port !== undefined && (isNaN(port) || port < 0 || port > 65535)) {\n console.error(`Invalid port: ${portStr}`);\n process.exit(1);\n }\n const shareTtlStr = getFlag(\"share-token-ttl\");\n const shareTtlMs = shareTtlStr ? parseInt(shareTtlStr, 10) : undefined;\n await serve({\n room: getFlag(\"room\"),\n port,\n share: args.includes(\"--share\"),\n headless: args.includes(\"--headless\"),\n expose: args.includes(\"--expose\"),\n bind: bindFlag(args),\n corsOrigins: getAllFlags(\"cors-origin\"),\n shareTtlMs,\n });\n return;\n }\n\n // ── apiary (bare) — host + join ────────────────────────────────────────\n if (args.length === 0 || args[0]?.startsWith(\"--\")) {\n const portStr = getFlag(\"port\");\n const port = portStr ? parseInt(portStr, 10) : undefined;\n if (port !== undefined && (isNaN(port) || port < 0 || port > 65535)) {\n console.error(`Invalid port: ${portStr}`);\n process.exit(1);\n }\n const result = await serve({\n room: getFlag(\"room\"),\n port,\n share: args.includes(\"--share\"),\n quiet: true,\n expose: args.includes(\"--expose\"),\n bind: bindFlag(args),\n corsOrigins: getAllFlags(\"cors-origin\"),\n });\n\n // Host joins locally as admin using the admin share token\n const adminJoinUrl = buildShareUrl(result.serverUrl, result.adminToken);\n const participantShareUrl = buildShareUrl(\n result.publicUrl !== result.serverUrl ? result.publicUrl : result.serverUrl,\n result.memberToken,\n );\n\n await join({\n server: adminJoinUrl,\n name: getFlag(\"name\"),\n shareUrl: participantShareUrl,\n });\n return;\n }\n\n // ── Unknown command ────────────────────────────────────────────────────\n console.error(`Unknown command: ${args[0]}\\n`);\n printUsage(console.error);\n process.exit(1);\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"],"mappings":"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","names":["writeFileSync","existsSync","readFileSync","readdirSync","renameSync","join","homedir","join","existsSync","join","homedir","join","homedir","existsSync","writeFileSync","join","homedir","existsSync","readFileSync","writeFileSync","renameSync","readdirSync","setup","eventLoopPromise","resolve","resolve","execFileSync","rmSync","homedir","readFileSync","readdirSync","statSync","existsSync","join","homedir","SPINNER_CHARS","defaultUndeliveredWarning","matchesAny","existsSync","mkdirSync","readdirSync","rmSync","writeFileSync","join","homedir","join","homedir","mkdirSync","writeFileSync","rmSync","existsSync","readdirSync","execFileSync","setup","eventLoopPromise","resolve","homedir","rmSync","readFileSync","resolve","args","require"]}
1
+ {"version":3,"sources":["../../src/cli/claude/run.ts","../../src/cli/composer.ts","../../src/cli/claude/tmux-bridge.ts","../../src/cli/runtime-setup.ts","../../src/cli/agent-state.ts","../../src/cli/approvals.ts","../../src/cli/claude/trust.ts","../../src/cli/claude/jsonl-stats.ts","../../src/cli/opencode/run.ts","../../src/cli/codex/run.ts","../../src/cli/codex/queue.ts","../../src/cli/codex/tmux-bridge.ts","../../src/cli/codex/launch.ts","../../src/cli/version.ts","../../src/cli/help.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * apiary claude — client-side agent runtime for Claude Code.\n *\n * Uses the shared runtime setup (EventProcessor, MCP server)\n * then adds Claude-specific pieces: tmux session + TmuxBridge delivery.\n *\n * Claude Code connects to the runtime MCP server via a stdio bridge\n * (Claude's HTTP MCP transport requires OAuth, which hangs on localhost).\n * The agent joins rooms by calling join_room() — no auto-injection needed.\n */\n\nimport { writeFileSync, mkdtempSync, mkdirSync, rmSync, chmodSync, existsSync, readFileSync, readdirSync, realpathSync, renameSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir, homedir } from \"node:os\";\n\nimport {\n tmuxAvailable,\n tmuxCreateSession,\n tmuxSendCommand,\n tmuxAttach,\n tmuxKillSession,\n tmuxSessionExists,\n resetTerminal,\n} from \"../tmux.js\";\nimport { TmuxBridge } from \"./tmux-bridge.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\nimport { claudeStateToActivity } from \"../agent-state.js\";\nimport { wantsUnattendedAgents } from \"../approvals.js\";\nimport { preAcceptClaudeTrust } from \"./trust.js\";\nimport { shouldReportActivity, type AgentActivityState } from \"../../agent/event-processor.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport { agentEmoji } from \"../config.js\";\nimport { deliverInvite, sameApiaryServer } from \"../invites.js\";\nimport { readAgentMetrics } from \"./jsonl-stats.js\";\n\nconst SKIP_PERMISSIONS_FLAG = \"--dangerously-skip-permissions\";\n\nexport { type AgentRuntimeOptions as RunClaudeOptions };\n\n// ── Session state persistence ─────────────────────────────────────────────────\n\nconst SESSION_DIR = join(homedir(), \".apiary\", \"sessions\");\n\ninterface PersistedSession {\n tmuxSession: string;\n agentName: string;\n tmpDir: string;\n mcpPort: string;\n pid: number;\n}\n\nfunction sessionFilePath(name: string): string {\n return join(SESSION_DIR, `claude_${name}.json`);\n}\n\nfunction saveSession(session: PersistedSession): void {\n const dir = SESSION_DIR;\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(sessionFilePath(session.agentName), JSON.stringify(session, null, 2));\n}\n\nfunction loadSession(name: string): PersistedSession | null {\n const path = sessionFilePath(name);\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"));\n } catch {\n return null;\n }\n}\n\nfunction clearSession(name: string): void {\n const path = sessionFilePath(name);\n try { rmSync(path); } catch { /* ok */ }\n}\n\n/**\n * Pre-accept Claude Code's first-run onboarding dialogs for this cwd so a\n * background-spawned agent (no human attached to the tmux pane) doesn't hang\n * forever at a blocking prompt.\n *\n * Claude shows two blocking screens during `showSetupScreens()`:\n *\n * 1. \"Quick safety check: is this a project you trust?\" — gated on\n * `projects[<realpath>].hasTrustDialogAccepted`. Neither\n * `--dangerously-skip-permissions` nor `--permission-mode bypassPermissions`\n * bypasses it (those gate tool calls, not workspace trust).\n * 2. Per-project onboarding hints — gated on `hasCompletedProjectOnboarding`\n * and `projectOnboardingSeenCount`.\n *\n * Pre-writing these three keys is what claude itself does after the user\n * clicks through. Best-effort: if claude.json is missing or unparseable we\n * silently skip rather than create/corrupt the file. The key MUST be the\n * realpath because claude resolves symlinks before lookup (`/tmp` →\n * `/private/tmp` on macOS).\n */\nfunction pretrustCwd(cwd: string): void {\n try {\n const path = join(homedir(), \".claude.json\");\n if (!existsSync(path)) return;\n const real = realpathSync(cwd);\n const raw = readFileSync(path, \"utf-8\");\n const data = JSON.parse(raw);\n if (typeof data !== \"object\" || data === null) return;\n const projects = (data.projects ??= {});\n const entry = (projects[real] ??= {});\n\n const needsWrite =\n entry.hasTrustDialogAccepted !== true ||\n entry.hasCompletedProjectOnboarding !== true ||\n !(typeof entry.projectOnboardingSeenCount === \"number\" && entry.projectOnboardingSeenCount >= 1);\n if (!needsWrite) return;\n\n entry.hasTrustDialogAccepted = true;\n entry.hasCompletedProjectOnboarding = true;\n if (!(typeof entry.projectOnboardingSeenCount === \"number\" && entry.projectOnboardingSeenCount >= 1)) {\n entry.projectOnboardingSeenCount = 1;\n }\n\n // Atomic write so a concurrent claude reading the file never sees partial JSON\n const tmpPath = `${path}.apiary-${process.pid}.tmp`;\n writeFileSync(tmpPath, JSON.stringify(data, null, 2));\n renameSync(tmpPath, path);\n } catch {\n // Don't block agent spawn on a settings hiccup\n }\n}\n\n/** List all active Claude sessions. */\nexport function listClaudeSessions(): PersistedSession[] {\n if (!existsSync(SESSION_DIR)) return [];\n const files = readdirSync(SESSION_DIR) as string[];\n const sessions: PersistedSession[] = [];\n for (const f of files) {\n if (!f.startsWith(\"claude_\") || !f.endsWith(\".json\")) continue;\n try {\n const s = JSON.parse(readFileSync(join(SESSION_DIR, f), \"utf-8\"));\n sessions.push(s);\n } catch { /* skip */ }\n }\n return sessions;\n}\n\n/**\n * Stdio-to-HTTP bridge script. Written to a temp file and spawned by Claude Code\n * as an MCP stdio server. Proxies JSON-RPC messages to the runtime HTTP MCP server.\n *\n * The HTTP MCP server returns SSE-formatted responses (event: message\\ndata: {...}).\n * The bridge extracts the JSON from data: lines and writes raw JSON-RPC to stdout.\n */\n// CommonJS (.cjs) for minimal startup latency — ESM requires module parsing which\n// can exceed Claude Code's MCP handshake timeout on first run.\nconst MCP_STDIO_BRIDGE = [\n '#!/usr/bin/env node',\n '\"use strict\";',\n 'const { createInterface } = require(\"readline\");',\n 'const url = `http://127.0.0.1:${process.argv[2]}/mcp`;',\n 'const rl = createInterface({ input: process.stdin });',\n '(async () => {',\n ' for await (const line of rl) {',\n ' if (!line.trim()) continue;',\n ' try {',\n ' const res = await fetch(url, {',\n ' method: \"POST\",',\n ' headers: { \"Content-Type\": \"application/json\", Accept: \"application/json, text/event-stream\" },',\n ' body: line,',\n ' });',\n ' if (res.status === 202) continue;',\n ' const body = await res.text();',\n ' for (const bl of body.split(\"\\\\n\")) {',\n ' const m = bl.match(/^data: (.+)/);',\n ' if (m) process.stdout.write(m[1] + \"\\\\n\");',\n ' }',\n ' } catch {',\n ' process.exit(1);',\n ' }',\n ' }',\n '})();',\n].join('\\n');\n\nexport async function runClaude(options: AgentRuntimeOptions): Promise<void> {\n // ── Headless mode — skip tmux, deliver events as plain text to stdout ────\n\n if (options.headless) {\n const setup = await setupAgentRuntime(options);\n\n const deliver = async (parts: Parameters<typeof contentPartsToString>[0]) => {\n const text = contentPartsToString(parts);\n if (text.trim()) process.stdout.write(text + \"\\n\");\n };\n\n const eventLoopPromise = setup.processor\n .run(deliver, setup.wrappedSource, setup.initialParts)\n .catch(() => {});\n\n process.stderr.write(`MCP server: ${setup.mcpServer.url}\\n`);\n\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", resolve);\n process.on(\"SIGTERM\", resolve);\n });\n\n await setup.cleanup();\n await eventLoopPromise;\n return;\n }\n\n // ── Preflight checks ────────────────────────────────────────────────────\n\n if (!tmuxAvailable()) {\n console.error(\"Error: tmux is required but not found. Install it with: brew install tmux\");\n process.exit(1);\n }\n\n // ── Resume existing session ─────────────────────────────────────────────\n\n if (options.resume) {\n return resumeClaude(options);\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // Don't pass joinUrls — Claude Code agents join rooms manually via join_room()\n\n const setup = await setupAgentRuntime({ ...options, joinUrls: undefined });\n\n // ── Write MCP stdio bridge + config ────────────────────────────────────\n\n const tmpDir = mkdtempSync(join(tmpdir(), \"apiary_agent_\"));\n\n const bridgePath = join(tmpDir, \"mcp-bridge.cjs\");\n writeFileSync(bridgePath, MCP_STDIO_BRIDGE);\n chmodSync(bridgePath, 0o755);\n\n const mcpPort = new URL(setup.mcpServer.url).port;\n const mcpConfigPath = join(tmpDir, \"mcp.json\");\n\n // Use process.execPath (absolute path to Node) so the bridge works regardless\n // of whether `node` is in the tmux session's PATH.\n const mcpConfig = {\n mcpServers: {\n apiary: {\n type: \"stdio\",\n command: process.execPath,\n args: [bridgePath, mcpPort],\n },\n },\n };\n writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));\n\n // ── Create tmux session + launch Claude Code ────────────────────────────\n\n const tmuxSession = `apiary_${setup.agentName}`;\n\n if (tmuxSessionExists(tmuxSession)) {\n tmuxKillSession(tmuxSession);\n }\n\n const home = homedir();\n const cwdFull = process.cwd();\n const cwdShort = cwdFull.startsWith(home) ? \"~\" + cwdFull.slice(home.length) : cwdFull;\n const tabTitle = `${agentEmoji(setup.agentName)} ${setup.agentName} · ${cwdShort}`;\n\n console.log(\"Launching Claude Code...\");\n tmuxCreateSession(tmuxSession, tabTitle);\n\n // Pre-accept claude's workspace-trust dialog for this cwd. Otherwise\n // background-spawned agents (no human attached to the tmux pane) hang\n // forever at the dialog and never reach the MCP-load step.\n pretrustCwd(process.cwd());\n\n // Launch claude with MCP config + any passthrough args.\n // --strict-mcp-config so we ignore the user's global MCP servers (playwright,\n // project .mcp.json, etc.). Otherwise the agent pays tool-schema tax on\n // every request for tools it will never call — measured 17K tokens of\n // unused schema vs 24K of actual conversation in one real session.\n // Permission prompts are skipped for every apiary agent, not just\n // wizard-spawned ones. An agent in a room has nobody watching its pane, so a\n // prompt is a permanent stall — it cannot call apiary__join_room, cannot\n // answer, and from the room's side looks like it is thinking. Opt out with\n // APIARY_AGENT_APPROVALS=ask.\n // Claude's trust dialog is not skippable by any flag, so pre-record the\n // acceptance for this agent's cwd or it stalls on a modal nobody will see.\n if (wantsUnattendedAgents()) {\n const trust = preAcceptClaudeTrust(process.cwd());\n if (trust === \"failed\" && !options.background) {\n console.log(\"Note: could not pre-accept Claude's folder-trust dialog — the agent may ask once.\");\n }\n }\n\n const extraArgs = options.extraArgs ?? [];\n const flags = [`claude --mcp-config ${mcpConfigPath} --strict-mcp-config`];\n // Don't duplicate it if the caller already passed it themselves.\n if (wantsUnattendedAgents() && !extraArgs.includes(SKIP_PERMISSIONS_FLAG)) {\n flags.push(SKIP_PERMISSIONS_FLAG);\n }\n const claudeCmd = [...flags, ...extraArgs].join(\" \");\n tmuxSendCommand(tmuxSession, claudeCmd);\n\n // ── Start event loop + attach ──────────────────────────────────────────\n\n const bridge = new TmuxBridge(tmuxSession);\n\n // Start the event loop in the background — no initial injection.\n // The agent joins rooms by calling join_room() when the user tells it to.\n const eventLoopPromise = setup.processor.run(bridge.deliver.bind(bridge), setup.wrappedSource)\n .catch(() => {}); // Prevent unhandled rejection from crashing the process\n\n // Activity heartbeat — every ~1.5s, scrape claude's status line (\"Sautéed\n // for 12s\", \"Compacting…\", etc.) and push to all connected rooms when it\n // changes. Gives the room TUI live \"is the agent stuck or working?\" signal\n // instead of just a static green dot.\n let lastActivityLabel: string | null = null;\n let lastState: AgentActivityState | undefined;\n let lastReportAt = 0;\n const activityTimer: NodeJS.Timeout = setInterval(() => {\n let label: string | null;\n let state: AgentActivityState | undefined;\n try {\n label = bridge.getActivityLabel();\n // detectReportedState, not detectState: the latter is the delivery\n // gate and deliberately over-matches prompts, which is right for\n // deciding whether to type and wrong for telling a human \"needs you\".\n state = claudeStateToActivity(bridge.detectReportedState());\n } catch { return; }\n // Report the state as well as the label. A permission prompt shows no\n // activity label at all, so on the label alone the room cannot tell\n // \"waiting for you\" from \"quietly working\" — and only one of those ends\n // without the user doing something.\n // Re-send an unchanged state periodically, not just on change. The room\n // server ages reported state out so a dead runtime does not pin a stale\n // value — but with change-only reporting a settled agent stops reporting,\n // ages out, and reads as \"not reporting at all\". Observed end-to-end: an\n // idle agent's state vanished ~15s after it settled. One tiny POST per\n // agent per interval is nothing next to being wrong about who is idle.\n if (!shouldReportActivity({\n label, state,\n lastLabel: lastActivityLabel, lastState,\n lastReportAt, now: Date.now(),\n })) return;\n lastActivityLabel = label;\n lastState = state;\n lastReportAt = Date.now();\n setup.processor.broadcastActivity(label, state).catch(() => { /* best-effort */ });\n }, 1500);\n activityTimer.unref();\n\n // Metrics heartbeat — every ~10s, re-parse claude's session jsonl to\n // pull last-turn context size. Broadcast only when the context number\n // actually changes (avoid spamming the room on idle ticks).\n let lastBroadcastCtx = -1;\n const metricsTimer: NodeJS.Timeout = setInterval(() => {\n try {\n const m = readAgentMetrics(process.cwd());\n if (!m) return;\n if (m.lastTurnContextTokens === lastBroadcastCtx) return;\n lastBroadcastCtx = m.lastTurnContextTokens;\n setup.processor.broadcastMetrics({\n input_tokens: m.inputTokens,\n output_tokens: m.outputTokens,\n cache_read_tokens: m.cacheReadTokens,\n cache_create_tokens: m.cacheCreateTokens,\n last_ctx_tokens: m.lastTurnContextTokens,\n model: m.lastModel,\n }).catch(() => { /* best-effort */ });\n } catch { /* never crash the runtime on a metrics read */ }\n }, 10_000);\n metricsTimer.unref();\n\n // Wait for Claude to start, checking the session is still alive\n for (let i = 0; i < 10; i++) {\n await new Promise((r) => setTimeout(r, 500));\n if (!tmuxSessionExists(tmuxSession)) {\n console.error(\"Error: Claude Code exited during startup. Try running again.\");\n clearInterval(activityTimer); clearInterval(metricsTimer);\n bridge.stop();\n await setup.cleanup();\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n resetTerminal();\n return;\n }\n }\n\n // ── Deliver queued invite (auto-join) ─────────────────────────────────\n // If the wizard background-spawned this agent, ~/.apiary/invites/<name>\n // holds the room's join URL. deliverInvite keeps asking until the agent is\n // really in the room, and only then deletes the file — a CLI still sitting\n // on a trust or onboarding prompt gets another go once it reaches a prompt,\n // instead of losing the URL to a menu that swallowed it.\n const inviteAbort = new AbortController();\n const invitePromise = deliverInvite({\n agentName: setup.agentName,\n deliver: (parts) => bridge.deliver(parts, { dedupe: true }),\n hasJoined: (url) => setup.joinResults.some((jr) => sameApiaryServer(jr.serverUrl, url)),\n signal: inviteAbort.signal,\n }).then((outcome) => {\n // Don't let a failed auto-join be silent — that is exactly how an agent\n // ends up sitting outside the room it was spawned for. The invite is kept\n // on disk, so `apiary room resume <room>` will hand it over again.\n if (outcome === \"abandoned\" && !options.background) {\n console.log(\n `Note: \"${setup.agentName}\" did not join the room it was invited to. ` +\n `The invite is still queued — check the pane, then re-run or use \"apiary room resume\".`,\n );\n }\n return outcome;\n }).catch(() => \"cancelled\" as const);\n\n // ── Save session state for resume ──────────────────────────────────────\n\n saveSession({\n tmuxSession,\n agentName: setup.agentName,\n tmpDir,\n mcpPort,\n pid: process.pid,\n });\n\n // Background mode: spawned by room create — run silently until SIGTERM.\n if (options.background) {\n // Suppress output so this process doesn't bleed into the parent terminal.\n const noop = (() => true) as unknown as typeof process.stdout.write;\n process.stdout.write = noop;\n process.stderr.write = noop;\n await new Promise<void>((resolve) => {\n process.on(\"SIGTERM\", resolve);\n process.on(\"SIGINT\", resolve);\n });\n clearInterval(activityTimer); clearInterval(metricsTimer);\n inviteAbort.abort();\n await invitePromise;\n bridge.stop();\n await setup.cleanup();\n if (tmuxSessionExists(tmuxSession)) tmuxKillSession(tmuxSession);\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n clearSession(setup.agentName);\n return;\n }\n\n console.log(\"Attaching...\");\n console.log(`(Ctrl+B D to detach — resume with: apiary claude ${setup.agentName} --resume)\\n`);\n\n try {\n await tmuxAttach(tmuxSession);\n } catch {\n // User detached or session ended\n }\n\n // ── On detach: keep running in background ──────────────────────────────\n\n if (tmuxSessionExists(tmuxSession)) {\n console.log(`\"${setup.agentName}\" still running in background.`);\n console.log(` Resume: apiary claude ${setup.agentName} --resume`);\n console.log(` Stop: apiary stop ${setup.agentName}`);\n\n // Silence stdout/stderr so background process doesn't bleed\n // into other terminals (e.g. another agent's tmux session)\n const noop = (() => true) as any;\n process.stdout.write = noop;\n process.stderr.write = noop;\n\n // Keep the process alive so the event loop, MCP server, and SSE stay up\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", resolve);\n process.on(\"SIGTERM\", resolve);\n });\n }\n\n // ── Full cleanup (session ended or process killed) ─────────────────────\n\n clearInterval(activityTimer); clearInterval(metricsTimer);\n inviteAbort.abort();\n await invitePromise;\n bridge.stop();\n await setup.cleanup();\n if (tmuxSessionExists(tmuxSession)) tmuxKillSession(tmuxSession);\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n clearSession(setup.agentName);\n resetTerminal();\n}\n\n// ── Resume ──────────────────────────────────────────────────────────────────\n\nasync function resumeClaude(options: AgentRuntimeOptions): Promise<void> {\n const name = options.name;\n\n // Find session to resume\n const sessions = listClaudeSessions();\n if (sessions.length === 0) {\n console.error(\"No active Claude sessions to resume.\");\n process.exit(1);\n }\n\n let session: PersistedSession | undefined;\n if (name) {\n session = sessions.find((s) => s.agentName === name);\n if (!session) {\n console.error(`No active session named \"${name}\". Active sessions:`);\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n } else if (sessions.length === 1) {\n session = sessions[0];\n } else {\n console.error(\"Multiple active sessions. Specify which one with --name:\");\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n\n // Verify tmux session is alive\n if (!tmuxSessionExists(session.tmuxSession)) {\n console.error(`Session \"${session.agentName}\" tmux session is gone. Cleaning up.`);\n clearSession(session.agentName);\n process.exit(1);\n }\n\n // Verify background process is alive\n try {\n process.kill(session.pid, 0);\n } catch {\n console.error(`Session \"${session.agentName}\" background process (pid ${session.pid}) is gone. Cleaning up.`);\n tmuxKillSession(session.tmuxSession);\n clearSession(session.agentName);\n try { rmSync(session.tmpDir, { recursive: true }); } catch { /* ok */ }\n process.exit(1);\n }\n\n console.log(`Resuming session \"${session.agentName}\"...\\n`);\n\n try {\n await tmuxAttach(session.tmuxSession);\n } catch {\n // User detached\n }\n\n if (tmuxSessionExists(session.tmuxSession)) {\n console.log(`\"${session.agentName}\" still running in background.`);\n console.log(` Resume: apiary claude ${session.agentName} --resume`);\n console.log(` Stop: apiary stop ${session.agentName}`);\n }\n}\n\n// ── Stop ────────────────────────────────────────────────────────────────────\n\nexport async function stopClaude(name?: string, all?: boolean): Promise<void> {\n const sessions = listClaudeSessions();\n if (sessions.length === 0) {\n // --all is allowed to no-op (rooms may have already cleaned up everything)\n if (all) return;\n console.error(\"No active Claude sessions.\");\n process.exit(1);\n }\n\n if (all) {\n for (const s of sessions) {\n stopSession(s);\n }\n return;\n }\n\n let session: PersistedSession | undefined;\n if (name) {\n session = sessions.find((s) => s.agentName === name);\n if (!session) {\n console.error(`No active session named \"${name}\". Active sessions:`);\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n } else if (sessions.length === 1) {\n session = sessions[0];\n } else {\n console.error(\"Multiple active sessions. Use --name or --all:\");\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n\n stopSession(session);\n}\n\nfunction stopSession(session: PersistedSession): void {\n // Kill tmux session first (prevents orphaned panes with broken state)\n if (tmuxSessionExists(session.tmuxSession)) {\n tmuxKillSession(session.tmuxSession);\n }\n\n // Then signal the background process\n try {\n process.kill(session.pid, \"SIGTERM\");\n } catch {\n // Process already gone\n }\n\n // Clean up temp files\n try { rmSync(session.tmpDir, { recursive: true }); } catch { /* ok */ }\n\n clearSession(session.agentName);\n resetTerminal();\n console.log(`Stopped \"${session.agentName}\".`);\n}\n","/**\n * Did the text we injected actually get submitted?\n *\n * Both bridges type into a TUI and then assume it worked. On 2026-09-10 that\n * assumption cost seven minutes of apparent silence: a room message reached a\n * Codex agent, the `@`-mention popup swallowed the Enter, and the message sat in\n * the composer while apiary reported success and moved on. Nobody learned it had\n * failed until a human read the pane.\n *\n * The check is deliberately narrow. After a submit, the text appears in the\n * *transcript* — that is success, not failure — so the presence of our text on\n * screen proves nothing. What distinguishes the two is where it sits: an\n * unsubmitted message is still in the composer, which both TUIs render as the\n * last prompt-marked block at the bottom of the screen.\n */\n\n/** Prompt markers: Codex renders the composer with ›, Claude with ❯ (> on older builds). */\nconst PROMPT_MARKER = /^\\s*[›❯>]\\s?/;\n\n/**\n * The composer's contents: everything from the last prompt marker down.\n *\n * Long messages wrap, so the marker line alone is not the whole composer — the\n * rest of the block below it belongs to the same input.\n */\nexport function composerBlock(lines: string[]): string {\n const tail = lines.slice(-25);\n let start = -1;\n for (let i = tail.length - 1; i >= 0; i--) {\n if (PROMPT_MARKER.test(tail[i])) {\n start = i;\n break;\n }\n }\n if (start === -1) return \"\";\n return tail.slice(start).join(\" \");\n}\n\n/**\n * A stable fragment of the message to look for.\n *\n * The head of the text rather than the whole of it: the composer wraps, the pane\n * truncates, and a trailing `@mention` may already have been rewritten by the\n * very popup we are checking for — so the tail is exactly the part that cannot\n * be relied on. Whitespace is collapsed because wrapping introduces its own.\n */\nexport function submissionProbe(text: string): string {\n return text.replace(/\\s+/g, \" \").trim().slice(0, 40);\n}\n\n/**\n * True when the message is still sitting in the composer, i.e. it was NOT\n * submitted. A message short enough to produce no usable probe is reported as\n * submitted rather than guessed at — a false \"undelivered\" would have the\n * caller resend, and a duplicate room message is worse than an unchecked one.\n */\nexport function composerStillHolds(lines: string[], text: string): boolean {\n const probe = submissionProbe(text);\n if (probe.length < 8) return false;\n const composer = composerBlock(lines).replace(/\\s+/g, \" \");\n return composer.includes(probe);\n}\n","/**\n * TmuxBridge — state-aware event injection into Claude Code.\n *\n * Reads the Claude Code TUI screen via `tmux capture-pane`, detects the\n * current UI state, and applies the right injection strategy:\n *\n * idle → inject directly\n * typing → Ctrl+U (cut), inject, Ctrl+Y (restore)\n * dialog → queue and poll\n * permission → queue and poll\n * streaming → queue and poll\n * unknown → queue and poll (safe default)\n *\n * Events that can't be injected immediately are queued and drained\n * when the state becomes safe.\n */\n\nimport {\n tmuxPaneIsShell,\n tmuxCapturePane,\n tmuxInjectText,\n tmuxSendEnter,\n tmuxSendKey,\n} from \"../tmux.js\";\nimport { composerStillHolds } from \"../composer.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\n\nexport type TuiState =\n /** The CLI is not running — the pane has fallen back to a shell. */\n | \"absent\"\n | \"idle\"\n | \"typing\"\n | \"dialog\"\n | \"permission\"\n | \"streaming\"\n | \"unknown\";\n\n// Spinner characters used by Claude Code during streaming\nconst SPINNER_CHARS = \"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\";\n\n/**\n * Claude Code is busy in a way that shows NO spinner.\n *\n * While a subagent runs, the main area is quiet and the only sign is the\n * footer: \"⏵⏵ bypass permissions on (shift+tab to cycle) · ← 1 agent\".\n * Spinner-only detection therefore read the pane as idle, so apiary pasted\n * into a composer that will not submit — verified against a live agent, where\n * even a manual Enter did nothing — and the message sat there as a draft until\n * the subagent finished. The room showed the agent as idle throughout.\n */\nconst BUSY_PATTERNS: RegExp[] = [\n // \"← 1 agent\" / \"← 3 agents\"\n /←\\s*\\d+\\s+agents?\\b/,\n];\n\n// Patterns that indicate a selection/question dialog\nconst DIALOG_PATTERNS = [\n \"Enter to select\",\n \"to navigate\",\n \"Esc to cancel\",\n \"Ready to code?\",\n \"Review your answers\",\n \"ctrl+g to edit in\",\n];\n\n// Patterns that indicate a permission/confirmation prompt\nconst PERMISSION_PATTERNS = [\n \"(Y)\",\n \"Allow \",\n \"Deny \",\n \"approve\",\n \"Yes / No\",\n // Current Claude Code wording, captured from a live prompt. Without these a\n // permission dialog reads as idle and the bridge pastes a room event into\n // it, answering on the agent's behalf.\n \"Do you want to proceed?\",\n \"don't ask again\",\n // The folder-trust modal, which no flag skips.\n \"Yes, I trust this folder\",\n];\n\n/**\n * Patterns specific enough to put \"⏸ needs you\" in front of a human.\n *\n * DIALOG_PATTERNS and PERMISSION_PATTERNS above deliberately over-match, and\n * for their original consumer that is correct: they gate DELIVERY, where a\n * false positive only queues a room event for the drain loop to retry. Cheap.\n *\n * That reasoning was then reused for a second consumer with the opposite cost\n * — the agent state reported to the room — where a false positive stops the\n * operator and makes them wait on an agent that needs nothing. Observed: the\n * bare substring `\"approve\"` matched an agent's own tool output, \"hard cap, set\n * by the approved top\", and the room strip read \"⏸ needs you\" at an agent 23\n * minutes into a harness run. `\"to navigate\"` is the same trap and worse — it\n * matches any sentence about navigating, in a project whose entire subject is\n * navigating by bearings.\n *\n * So: two lists. Over-match to protect the agent, under-match before\n * interrupting the human. These are UI chrome a prompt actually renders —\n * wordings and shapes, not vocabulary that can occur in prose.\n */\nconst BLOCKING_PATTERNS: Array<string | RegExp> = [\n \"Do you want to proceed?\",\n \"don't ask again\",\n \"Yes, I trust this folder\",\n \"Ready to code?\",\n \"Enter to select\",\n /❯\\s*1\\.\\s*Yes/,\n /\\(y\\s*\\/\\s*n\\)/i,\n // The arrow glyphs claude renders beside it. Bare \"to navigate\" is prose.\n /[↑↓⬆⬇]\\s*(?:[↑↓⬆⬇]\\s*)?to navigate/,\n];\n\n/** Match a mixed string/RegExp pattern list, case-insensitively for strings. */\nfunction matchesAny(text: string, patterns: Array<string | RegExp>): boolean {\n const haystack = text.toLowerCase();\n for (const pattern of patterns) {\n if (typeof pattern === \"string\") {\n if (haystack.includes(pattern.toLowerCase())) return true;\n } else if (pattern.test(text)) {\n return true;\n }\n }\n return false;\n}\n\nexport interface DeliverOptions {\n /**\n * Drop this text if an identical copy is already queued. For callers that\n * retry a delivery they cannot observe landing (the room invite loop).\n */\n dedupe?: boolean;\n}\n\nexport interface TmuxBridgeOptions {\n /** How often to poll when events are queued (ms). Default: 200 */\n pollIntervalMs?: number;\n /** How long to wait between Ctrl+U/inject/Ctrl+Y steps (ms). Default: 50 */\n keystrokeDelayMs?: number;\n /**\n * Called when a message could not be got into the agent, after the retry.\n *\n * Delivery used to be assumed: apiary typed, reported success and moved on,\n * so a swallowed submit looked exactly like an agent choosing not to answer.\n * This is the hook that makes the difference visible to someone.\n */\n onUndelivered?: (text: string) => void;\n}\n\n/**\n * Last resort when no one has wired a handler: say it on stderr, which lands in\n * the launcher pane. Better than nothing, and a great deal better than success.\n */\nfunction defaultUndeliveredWarning(text: string): void {\n const preview = text.replace(/\\s+/g, \" \").slice(0, 80);\n process.stderr.write(`apiary: message NOT delivered to the agent — still in its composer: ${preview}\\n`);\n}\n\nexport class TmuxBridge {\n private session: string;\n private queue: string[] = [];\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private pollIntervalMs: number;\n private keystrokeDelayMs: number;\n private onUndelivered: (text: string) => void;\n private stopped = false;\n private blockedCount = 0;\n private static readonly BLOCKED_THRESHOLD = 150; // ~30s at 200ms poll — force inject if stuck\n\n constructor(session: string, opts?: TmuxBridgeOptions) {\n this.session = session;\n this.pollIntervalMs = opts?.pollIntervalMs ?? 200;\n this.keystrokeDelayMs = opts?.keystrokeDelayMs ?? 50;\n this.onUndelivered = opts?.onUndelivered ?? defaultUndeliveredWarning;\n }\n\n /**\n * Delivery callback — drop-in replacement for the raw tmuxDeliver lambda.\n * Pass `bridge.deliver.bind(bridge)` to EventProcessor.run().\n */\n async deliver(parts: ContentPart[], opts?: DeliverOptions): Promise<void> {\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n this.inject(text, opts?.dedupe === true);\n }\n\n /**\n * Detect the current TUI state by reading the screen.\n * Exported for testing — the heuristic logic is in detectStateFromLines().\n */\n detectState(): TuiState {\n // See the Codex bridge: a shell prompt is indistinguishable from an idle\n // composer once you are only looking at rendered text.\n if (tmuxPaneIsShell(this.session)) return \"absent\";\n const lines = this.captureScreen();\n return detectStateFromLines(lines);\n }\n\n /**\n * State to REPORT to the room, as opposed to the state used to decide\n * whether it is safe to type. Same screen, stricter about claiming a human\n * is needed — see detectReportedStateFromLines.\n */\n detectReportedState(): TuiState {\n if (tmuxPaneIsShell(this.session)) return \"absent\";\n return detectReportedStateFromLines(this.captureScreen());\n }\n\n /**\n * Extract claude's live status label from the pane (the \"Sautéed for 12s\",\n * \"Compacting conversation… 23%\" lines under the prompt). Returns null when\n * the agent is idle / no status visible.\n *\n * The room TUI calls this through the agent runtime's poll loop to surface\n * \"is the agent actually working or stuck?\" without making the user attach\n * to each tmux pane.\n */\n getActivityLabel(): string | null {\n return extractActivityLabel(this.captureScreen());\n }\n\n /**\n * Try to inject text, choosing strategy based on TUI state.\n * If the state is unsafe, queues the text and starts polling.\n *\n * Text is flattened to a single line before injection to avoid triggering\n * Claude Code's paste detection. When multi-line text arrives via\n * `send-keys -l`, Claude Code detects it as a paste and collapses it into\n * \"[Pasted text #1 +N lines]\" which may not reliably submit with Enter.\n */\n private inject(text: string, dedupe: boolean): void {\n const flat = text.replace(/\\n/g, \" \");\n const state = this.detectState();\n\n switch (state) {\n case \"idle\":\n this.injectIdle(flat);\n break;\n case \"typing\":\n this.injectWhileTyping(flat);\n break;\n default:\n // dialog, permission, streaming, unknown — queue it\n this.enqueue(flat, dedupe);\n break;\n }\n }\n\n /** Capture the screen via tmux capture-pane. */\n private captureScreen(): string[] {\n return tmuxCapturePane(this.session);\n }\n\n /**\n * Inject into an idle prompt: type text + Enter, then check it went.\n *\n * This used to fire a second Enter unconditionally as a \"safety net\" against\n * Claude Code's paste detection swallowing the first. That is a guess in both\n * directions: it cannot tell whether the first Enter worked, and it cannot\n * tell whether the second one did either. Reading the composer back answers\n * the question instead, and only sends the second Enter when it is needed.\n */\n private injectIdle(text: string): void {\n tmuxInjectText(this.session, text);\n tmuxSendEnter(this.session);\n this.confirmSubmitted(text);\n }\n\n /**\n * Check the message left the composer, and press Enter once more if it did not.\n *\n * A submitted message moves into the transcript; one still in the composer was\n * never sent. If it is still there after the retry, the room is told rather\n * than left to read the agent's silence as a choice.\n */\n private confirmSubmitted(text: string): void {\n this.sleep(80);\n if (!composerStillHolds(this.captureScreen(), text)) return;\n\n tmuxSendEnter(this.session);\n this.sleep(120);\n if (!composerStillHolds(this.captureScreen(), text)) return;\n\n this.onUndelivered(text);\n }\n\n /**\n * Inject while the user is typing:\n * 1. Ctrl+U — cut line to kill ring\n * 2. Inject our text + Enter\n * 3. Ctrl+Y — paste the user's text back\n */\n private injectWhileTyping(text: string): void {\n // Cut user's current input\n tmuxSendKey(this.session, \"C-u\");\n this.sleep(this.keystrokeDelayMs);\n\n // Inject our event, then confirm rather than hope\n tmuxInjectText(this.session, text);\n tmuxSendEnter(this.session);\n this.confirmSubmitted(text);\n this.sleep(this.keystrokeDelayMs);\n\n // Restore user's text\n tmuxSendKey(this.session, \"C-y\");\n }\n\n /** Add to queue and start polling if not already. */\n private enqueue(text: string, dedupe: boolean): void {\n // Off by default — room events must never be collapsed. Only callers that\n // retry a delivery they cannot observe landing (the invite loop) opt in.\n if (dedupe && this.queue.includes(text)) return;\n this.queue.push(text);\n this.startPolling();\n }\n\n /** Start the polling timer to drain queued events. */\n private startPolling(): void {\n if (this.pollTimer || this.stopped) return;\n this.pollTimer = setInterval(() => this.drainQueue(), this.pollIntervalMs);\n }\n\n /** Stop the polling timer. */\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Try to drain one queued event if the state is safe.\n *\n * Drains one event at a time rather than batching all into one multi-line\n * string — multi-line text triggers Claude Code's paste detection which\n * collapses it into \"[Pasted text #1 +N lines]\".\n *\n * After injecting one event, the poll continues. The next cycle re-checks\n * state: if Claude is busy (streaming), remaining events wait. If idle,\n * the next event is injected. Events are already flattened in inject().\n */\n private drainQueue(): void {\n if (this.queue.length === 0) {\n this.stopPolling();\n this.blockedCount = 0;\n return;\n }\n\n const state = this.detectState();\n if (state === \"idle\" || state === \"typing\") {\n this.blockedCount = 0;\n const text = this.queue.shift()!;\n\n if (state === \"idle\") {\n this.injectIdle(text);\n } else {\n this.injectWhileTyping(text);\n }\n\n if (this.queue.length === 0) {\n this.stopPolling();\n }\n // else: keep polling to drain remaining events\n } else {\n // Any non-injectable state (streaming, dialog, permission, unknown)\n this.blockedCount++;\n if (this.blockedCount >= TmuxBridge.BLOCKED_THRESHOLD) {\n // Stuck too long — force inject to unblock\n this.blockedCount = 0;\n const text = this.queue.shift()!;\n this.injectIdle(text);\n if (this.queue.length === 0) this.stopPolling();\n }\n }\n }\n\n /** Cleanup. */\n stop(): void {\n this.stopped = true;\n this.stopPolling();\n this.queue.length = 0;\n }\n\n /** Synchronous sleep — only used for tiny keystroke delays. */\n private sleep(ms: number): void {\n if (ms <= 0) return;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n }\n}\n\n// ── Activity label extraction ────────────────────────────────────────────────\n\n/**\n * Pull claude's status string out of a captured pane.\n *\n * Claude's TUI shows these markers above the input prompt while it's working:\n * ✻ Fluttering… (38s · ↓ 2.1k tokens) <- the LIVE form, present throughout\n * ✻ Sautéed for 12s <- the FINISHED form, a moment at the end\n * ✻ Compacting conversation… 23%\n * ✻ Thinking…\n *\n * Three patterns, tried narrowest-first so the broad one cannot swallow the\n * others:\n * - PROG: a known long-op verb (Compacting/Thinking/Loading/Connecting)\n * followed by an optional description and optional N%. Stops at the first\n * `(` so it doesn't capture a trailing metadata blob.\n * - COOK: cooking-verb + \"for Ns\", the form claude leaves behind once a turn\n * ends. Matches unicode letters (Sautéed) via the Latin-1 supplement range.\n * - LIVE: any word, an ellipsis, and a parenthesised progress group. Matched\n * on SHAPE rather than vocabulary, because the verb is whimsical and\n * rotates — Improvising, Fluttering, Reticulating, Cogitating. A word list\n * goes stale the next time someone adds one, which is exactly how this\n * broke: with only COOK and PROG, a *working* agent matched nothing and\n * reported no label, so the room strip showed a bare glyph for as long as\n * the work took. The one moment the operator most needs to know what is\n * happening was the one moment it said nothing.\n *\n * LIVE deliberately keeps its parenthesised group — \"(38s · ↓ 2.1k tokens)\" is\n * the elapsed time and stream progress, which is what distinguishes \"moving\"\n * from \"wedged\". PROG still stops at `(` because its verbs carry their own\n * detail, and it is tried first so that stays true.\n *\n * Scan the last ~25 lines, return the newest match. Null when none → idle.\n */\nconst ACTIVITY_LIVE = /([A-Za-zÀ-ÿ]+…\\s*\\([^)\\n]*\\))/;\nconst ACTIVITY_COOK = /([A-Za-zÀ-ÿ]+ed\\s+for\\s+\\d+s)/;\nconst ACTIVITY_PROG = /((?:Compacting|Thinking|Loading|Connecting)[^()]*?(?:\\s+\\d+%)?)(?=\\s*[(\\n]|\\s*$)/;\nexport function extractActivityLabel(lines: string[]): string | null {\n if (lines.length === 0) return null;\n let best: string | null = null;\n for (const line of lines.slice(-25)) {\n // PROG first, then COOK, then LIVE. LIVE is the broadest — any word plus a\n // parenthesised group — so it must go last or it swallows lines the narrower\n // patterns own: \"Compacting conversation… (2m 9s · ↑ 3.3k tokens)\" is\n // reported as \"Compacting conversation…\" on purpose, and LIVE-first turned\n // that into \"conversation… (2m 9s · ↑ 3.3k tokens)\".\n const m = line.match(ACTIVITY_PROG) ?? line.match(ACTIVITY_COOK) ?? line.match(ACTIVITY_LIVE);\n if (m) best = m[1].trim();\n }\n return best;\n}\n\n// ── State detection heuristics ──────────────────────────────────────────────\n\n/**\n * Detect TUI state from capture-pane output lines.\n * Exported separately so it can be unit-tested without tmux.\n *\n * Claude Code's TUI layout (v2.1+):\n * ────────────────────────\n * ❯ <user input here>\n * ────────────────────────\n * PR #2 /ide ...\n *\n * The ❯ prompt sits between separator lines (─). No ❯❯ footer in v2.1+.\n */\nexport function detectStateFromLines(\n lines: string[],\n opts?: { ignorePrompts?: boolean },\n): TuiState {\n if (lines.length === 0) return \"unknown\";\n\n // Work with the last ~30 lines (the visible bottom of the screen)\n const tail = lines.slice(-30);\n const tailText = tail.join(\"\\n\");\n\n // `ignorePrompts` asks \"what is this screen doing APART from any prompt?\" —\n // used by detectReportedStateFromLines to answer the human's question once\n // the loose lists have been found to be matching prose rather than chrome.\n if (opts?.ignorePrompts !== true) {\n // 1. Dialog: selection/question/plan approval\n for (const pattern of DIALOG_PATTERNS) {\n if (tailText.includes(pattern)) return \"dialog\";\n }\n\n // 2. Permission prompt\n for (const pattern of PERMISSION_PATTERNS) {\n if (tailText.includes(pattern)) return \"permission\";\n }\n }\n\n // 3. Streaming: spinner characters in the last few lines\n const lastFew = tail.slice(-5).join(\"\");\n for (const ch of SPINNER_CHARS) {\n if (lastFew.includes(ch)) return \"streaming\";\n }\n\n // 3b. Busy with no spinner — a subagent is running. Checked after the\n // dialog and permission patterns on purpose: a prompt that needs a human\n // outranks \"also busy\", because only one of those the user can clear.\n for (const pattern of BUSY_PATTERNS) {\n if (pattern.test(tailText)) return \"streaming\";\n }\n\n // 4. Look for the ❯/› prompt line near the bottom.\n // Supports both old layout (❯❯ footer) and new layout (separator lines).\n const promptChar = /^[❯›](\\s|$)/;\n const footerChar = /^[❯›]{2}\\s/;\n const separatorLine = /^[─━─\\-]{10,}/;\n\n // Strategy A: old layout — find ❯❯ footer then ❯ prompt above it\n for (let i = tail.length - 1; i >= 0; i--) {\n const line = tail[i].trimStart();\n if (footerChar.test(line)) {\n // Found old-style footer, look for prompt above\n for (let j = i - 1; j >= 0; j--) {\n const above = tail[j].trimStart();\n if (promptChar.test(above)) {\n const content = above.replace(/^[❯›]\\s*/, \"\").trim();\n return content.length === 0 ? \"idle\" : \"typing\";\n }\n }\n break;\n }\n }\n\n // Strategy B: new layout — find ❯ prompt between/near separator lines\n for (let i = tail.length - 1; i >= 0; i--) {\n const line = tail[i].trimStart();\n if (promptChar.test(line)) {\n // Verify it's Claude's prompt by checking for separator line nearby\n const above = i > 0 ? tail[i - 1].trimStart() : \"\";\n const below = i < tail.length - 1 ? tail[i + 1].trimStart() : \"\";\n if (separatorLine.test(above) || separatorLine.test(below)) {\n const content = line.replace(/^[❯›]\\s*/, \"\").trim();\n return content.length === 0 ? \"idle\" : \"typing\";\n }\n }\n }\n\n return \"unknown\";\n}\n\n/**\n * The same screen read, but tuned for a human rather than for the delivery\n * queue: only claim a prompt is waiting when one is actually on screen.\n *\n * `detectStateFromLines` leans toward \"there is a prompt\" because a false\n * positive there merely queues an event. This one leans the other way, because\n * its false positive is \"⏸ needs you\" in the operator's face at an agent that\n * needs nothing — which costs a person's attention, and teaches them to stop\n * trusting the strip. When the loose lists fire but no specific prompt wording\n * is present, the screen is re-read with the prompt scan disabled so the\n * spinner and composer get to answer instead.\n */\nexport function detectReportedStateFromLines(lines: string[]): TuiState {\n const loose = detectStateFromLines(lines);\n if (loose !== \"dialog\" && loose !== \"permission\") return loose;\n if (matchesAny(lines.slice(-30).join(\"\\n\"), BLOCKING_PATTERNS)) return loose;\n return detectStateFromLines(lines, { ignorePrompts: true });\n}\n","/**\n * Shared agent runtime setup — extracted from cli/claude/run.ts.\n *\n * Both `apiary run claude` and `apiary run opencode` use this to:\n * 1. Create SSE multiplexer + EventProcessor\n * 2. Create local runtime MCP server (handlers shared via runtime-handlers.ts)\n * 3. Wire up participant cache updates\n * 4. Provide cleanup\n *\n * Rooms are NOT joined during setup. The agent joins rooms by calling\n * join_room() via MCP. If --join URLs are provided, the startup event\n * asks the agent to call join_room for each URL.\n *\n * Each runtime only needs to provide its own delivery mechanism\n * (TmuxBridge for Claude, HTTP API for OpenCode).\n */\n\nimport { randomName } from \"../core/names.js\";\nimport { SseMultiplexer } from \"../agent/sse-multiplexer.js\";\nimport { EventProcessor } from \"../agent/event-processor.js\";\nimport { createRuntimeMcpServer, type RuntimeMcpServer } from \"../agent/mcp/runtime.js\";\nimport type { AuthorityLevel } from \"../core/types.js\";\nimport type { EngagementMode } from \"../agent/engagement.js\";\nimport type { LabeledEvent } from \"../agent/multiplexer.js\";\nimport type { ContentPart } from \"../agent/types.js\";\nimport { buildRuntimeMcpOptions, type JoinResult } from \"./runtime-handlers.js\";\n\nexport type { JoinResult };\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface AgentRuntimeOptions {\n joinUrls?: string[];\n name?: string;\n /**\n * Legacy CLI surface flag. `true` is equivalent to `authority: \"admin\"` —\n * exposes the full set of admin tools in the local MCP server. Prefer\n * setting `authority` directly when the desired tier isn't admin.\n */\n admin?: boolean;\n /**\n * Pre-declared authority this agent expects to operate at. Drives which\n * privileged MCP tools the local server registers. The real authority is\n * still discovered + enforced by the apiary server on join — this flag is\n * only about tool exposure. Defaults to \"admin\" if `admin: true`,\n * undefined otherwise.\n */\n authority?: AuthorityLevel;\n extraArgs?: string[];\n /** Skip tmux/UI — deliver events as plain text to stdout. MCP server still runs. */\n headless?: boolean;\n /** Re-attach to an existing background session instead of starting a new one. */\n resume?: boolean;\n /** Spawned by room create: start tmux session but skip attach; wait for SIGTERM. */\n background?: boolean;\n /** Called after a room is successfully joined via join_room MCP tool. */\n onRoomJoined?: () => void | Promise<void>;\n /**\n * Engagement mode this agent starts in. A standby mode means it stays quiet\n * until addressed — @mentioned, pinged, or whispered to — which is how a\n * room with several agents avoids every one of them answering every remark.\n * Defaults to \"everyone\" (respond to all traffic).\n */\n mode?: EngagementMode;\n}\n\nexport interface AgentRuntimeSetup {\n agentName: string;\n joinResults: JoinResult[];\n initialParts: ContentPart[] | undefined;\n processor: EventProcessor;\n sseMux: SseMultiplexer;\n mcpServer: RuntimeMcpServer;\n wrappedSource: AsyncIterable<LabeledEvent>;\n cleanup(): Promise<void>;\n}\n\n// ── Setup ─────────────────────────────────────────────────────────────────────\n\nexport async function setupAgentRuntime(options: AgentRuntimeOptions): Promise<AgentRuntimeSetup> {\n const agentName = options.name ?? randomName();\n // Translate the legacy --admin flag into a tier; the new `authority` field\n // is the canonical knob.\n const authority: AuthorityLevel | undefined = options.authority\n ?? (options.admin ? \"admin\" : undefined);\n\n // ── Pending join URLs (not joined yet — agent calls join_room) ─────────\n const pendingUrls = options.joinUrls ?? [];\n\n // ── Mutable join results (populated as agent calls join_room) ──────────\n const joinResults: JoinResult[] = [];\n\n // ── Create SSE multiplexer (starts empty) ──────────────────────────────\n const sseMux = new SseMultiplexer();\n\n // ── Create EventProcessor (selfId set on first join_room) ──────────────\n const processor = new EventProcessor(\"\", agentName, {\n defaultMode: options.mode ?? \"everyone\",\n });\n\n // ── Create local runtime MCP server (shared handler set) ───────────────\n const mcpServer = await createRuntimeMcpServer(\n buildRuntimeMcpOptions({\n processor,\n sseMux,\n joinResults,\n agentName,\n authority,\n onRoomJoined: options.onRoomJoined,\n }),\n );\n\n // ── Wrap SSE source for participant cache updates ─────────────────────\n const wrappedSource: AsyncIterable<LabeledEvent> = {\n [Symbol.asyncIterator]() {\n const inner = sseMux[Symbol.asyncIterator]();\n return {\n async next() {\n const result = await inner.next();\n if (!result.done) {\n const { roomId, event } = result.value;\n const jr = joinResults.find((j) => j.roomId === roomId);\n if (jr) {\n if (event.type === \"ParticipantJoined\") {\n jr.dataSource.addParticipant(event.participant);\n } else if (event.type === \"ParticipantLeft\") {\n jr.dataSource.removeParticipant(event.participant_id);\n }\n }\n }\n return result;\n },\n };\n },\n };\n\n // ── Cleanup function ──────────────────────────────────────────────────\n async function cleanup(): Promise<void> {\n await processor.stop();\n sseMux.close();\n await mcpServer.stop();\n\n for (const jr of joinResults) {\n try {\n await fetch(`${jr.serverUrl}/disconnect`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: jr.sessionToken }),\n });\n } catch {\n // Server may be down\n }\n }\n }\n\n // ── Build startup event (if --join URLs were provided) ────────────────\n let initialParts: ContentPart[] | undefined;\n if (pendingUrls.length > 0) {\n if (pendingUrls.length === 1) {\n initialParts = [{ type: \"text\", text: `Use join_room(\"${pendingUrls[0]}\") to connect.` }];\n } else {\n const lines = pendingUrls.map((u) => ` join_room(\"${u}\")`);\n initialParts = [{ type: \"text\", text: `Rooms to join:\\n${lines.join(\"\\n\")}` }];\n }\n }\n\n return {\n agentName,\n joinResults,\n initialParts,\n processor,\n sseMux,\n mcpServer,\n wrappedSource,\n cleanup,\n };\n}\n","/**\n * Bridge TUI state → the coarse state the room shows.\n *\n * Each bridge already works out what its CLI is doing, in order to decide\n * whether it is safe to inject text. That knowledge never left the runtime, so\n * the room fell back to inferring \"working\" for every agent whenever anyone\n * spoke — which meant an agent parked on an approval prompt was displayed\n * exactly like one that was thinking. One of those resolves by waiting and the\n * other never does, so they must not look the same.\n */\n\nimport type { AgentActivityState } from \"../agent/event-processor.js\";\nimport type { CodexTuiState } from \"./codex/tmux-bridge.js\";\nimport type { TuiState } from \"./claude/tmux-bridge.js\";\n\n/**\n * Codex: `approval` is a tool/command confirmation, `blocked` is a sign-in,\n * onboarding or directory-trust screen. Both need a human.\n */\nexport function codexStateToActivity(state: CodexTuiState): AgentActivityState | undefined {\n switch (state) {\n case \"absent\":\n // A human has to restart it, which is what \"needs you\" means. Reporting\n // idle here is how a dead agent sat in the room looking available.\n return \"blocked\";\n case \"approval\":\n case \"blocked\":\n return \"blocked\";\n case \"streaming\":\n return \"working\";\n case \"idle\":\n case \"typing\":\n return \"idle\";\n case \"unknown\":\n default:\n // Don't report a guess as ground truth — leave the room's inference be.\n return undefined;\n }\n}\n\n/**\n * Claude Code: `permission` is a tool-permission prompt, `dialog` a\n * selection/question overlay. Both sit there until a human answers.\n */\nexport function claudeStateToActivity(state: TuiState): AgentActivityState | undefined {\n switch (state) {\n case \"absent\":\n return \"blocked\";\n case \"permission\":\n case \"dialog\":\n return \"blocked\";\n case \"streaming\":\n return \"working\";\n case \"idle\":\n case \"typing\":\n return \"idle\";\n case \"unknown\":\n default:\n return undefined;\n }\n}\n","/**\n * Whether apiary-launched agents run unattended.\n *\n * An agent apiary spawned to sit in a room has nobody watching its pane. Every\n * approval prompt is therefore a permanent stall: the agent stops, the room\n * cannot answer it, and from the outside it looks like the agent is thinking.\n * Observed repeatedly in practice — a single session hit a command approval, a\n * dev-server approval and a `git add` approval within an hour, each one halting\n * work until a human happened to look at the right tmux pane.\n *\n * So the default is unattended, per CLI:\n *\n * - Claude Code: `--dangerously-skip-permissions`\n * - Codex: `approval_policy = \"never\"` in apiary's config profile, plus\n * per-tool `approval_mode` for apiary's own MCP tools (the two are\n * separate gates — `approval_policy` does not cover MCP tool calls)\n *\n * Codex still executes inside its sandbox, so anything it will not allow\n * returns a failure to the model rather than asking. Claude's flag is broader\n * and genuinely skips all permission checks — the room is only as trusted as\n * the repos its agents point at.\n */\n\n/** Env var that turns the default off. */\nexport const APPROVALS_ENV = \"APIARY_AGENT_APPROVALS\";\n\n/**\n * True unless `APIARY_AGENT_APPROVALS=ask`.\n *\n * \"ask\" restores the interactive behaviour, at the cost of having to babysit\n * each agent's terminal.\n */\nexport function wantsUnattendedAgents(env: NodeJS.ProcessEnv = process.env): boolean {\n return env[APPROVALS_ENV]?.trim().toLowerCase() !== \"ask\";\n}\n","/**\n * Pre-accept Claude Code's trust dialog for an agent's working directory.\n *\n * The first time Claude Code runs in a directory it shows a modal:\n *\n * Quick safety check: Is this a project you created or one you trust?\n * ❯ No, exit\n * Yes, I trust this folder\n *\n * Neither `--dangerously-skip-permissions` nor\n * `--permission-mode bypassPermissions` skips it — verified against Claude\n * Code directly, both land on the same modal. It is a deliberate security\n * boundary, and the only supported way past it is the acceptance Claude itself\n * records in `~/.claude.json` under `projects[<dir>].hasTrustDialogAccepted`.\n *\n * That matters for apiary specifically: nobody watches an agent's tmux pane, so\n * an agent spawned into a repo Claude has not seen before waits on that modal\n * forever. The room reports it (the bridge reads \"Esc to cancel\" as a dialog,\n * which surfaces as \"needs you\"), but reporting a stall is worse than not\n * having one.\n *\n * Recording it is a faithful reading of intent rather than an escalation: the\n * user pointed an agent at this directory, which is the same assertion the\n * dialog asks for. Nothing else in the file is touched, and the write is\n * atomic so a crash cannot truncate a config holding dozens of projects.\n */\n\nimport { existsSync, readFileSync, renameSync, writeFileSync, unlinkSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n/** Claude Code's config file. */\nexport function claudeConfigPath(home: string = homedir()): string {\n return join(home, \".claude.json\");\n}\n\nexport type TrustOutcome =\n | \"already-trusted\" // Claude had already recorded acceptance\n | \"trusted\" // we recorded it\n | \"no-config\" // Claude has never run here; it will ask once\n | \"skipped\" // opted out\n | \"failed\"; // unreadable/unwritable/malformed — left alone\n\n/**\n * Record trust for `cwd` in Claude Code's config.\n *\n * Never throws. Returns what happened so the caller can tell the user, because\n * a silent no-op here shows up much later as an agent that never joined.\n */\nexport function preAcceptClaudeTrust(\n cwd: string,\n opts?: { configPath?: string; enabled?: boolean },\n): TrustOutcome {\n if (opts?.enabled === false) return \"skipped\";\n\n const path = opts?.configPath ?? claudeConfigPath();\n // Claude keys projects by absolute path, exactly as it resolves the cwd.\n const key = resolve(cwd);\n\n if (!existsSync(path)) return \"no-config\";\n\n let parsed: Record<string, unknown>;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf-8\");\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // Malformed or unreadable — do not attempt a rewrite. Losing a config with\n // dozens of projects is far worse than one trust prompt.\n return \"failed\";\n }\n if (typeof parsed !== \"object\" || parsed === null) return \"failed\";\n\n const projects = (parsed.projects ?? {}) as Record<string, Record<string, unknown>>;\n if (typeof projects !== \"object\" || projects === null) return \"failed\";\n\n const existing = projects[key];\n if (existing && existing.hasTrustDialogAccepted === true) return \"already-trusted\";\n\n // Merge into whatever is already recorded for this project — it may hold\n // allowedTools, MCP server lists and history we must not drop.\n projects[key] = { ...(existing ?? {}), hasTrustDialogAccepted: true };\n parsed.projects = projects;\n\n const tmp = `${path}.apiary-${process.pid}.tmp`;\n try {\n writeFileSync(tmp, JSON.stringify(parsed, null, 2), { mode: 0o600 });\n renameSync(tmp, path);\n return \"trusted\";\n } catch {\n try { unlinkSync(tmp); } catch { /* nothing to clean */ }\n return \"failed\";\n }\n}\n","/**\n * Read live token usage out of claude's per-session JSONL log.\n *\n * Claude writes one event per line to\n * ~/.claude/projects/<cwd-encoded>/<sessionId>.jsonl\n * where <cwd-encoded> is the cwd with `/` replaced by `-` (leading `/` becomes\n * a leading `-`). We find the newest jsonl in that directory and aggregate\n * usage across all assistant messages.\n *\n * Incremental tailing: the first call parses the whole file; subsequent\n * calls only parse bytes appended since the last call. Active sessions\n * accumulate ~300KB over an hour — re-reading every 10s is wasteful and\n * scales badly with N agents. Cache is keyed by path so rotation (a new\n * session starts → newest jsonl changes) resets cleanly.\n */\n\nimport { readdirSync, statSync, existsSync, openSync, readSync, closeSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ── Path resolution ──────────────────────────────────────────────────────────\n\n/**\n * Convert an absolute cwd to claude's encoded project directory name.\n * `/Users/foo/bar` → `-Users-foo-bar`.\n * Exported for testing.\n */\nexport function cwdToProjectDir(cwd: string): string {\n // Claude Code slugifies the cwd by replacing BOTH separators and dots with\n // dashes, so /Users/ada.lovelace/x becomes -Users-ada-lovelace-x. Replacing\n // only \"/\" silently produced a path that never exists for any user whose\n // home directory contains a dot — and the caller treats \"no file\" as \"no\n // metrics yet\", so the room's context meter simply never appeared. It had\n // reported zero metrics events in its entire life before this was found,\n // which is how an agent reached 262k tokens with nothing on screen to say so.\n return cwd.replace(/[/.]/g, \"-\");\n}\n\nfunction findLatestJsonl(cwd: string): string | null {\n const projectsDir = join(homedir(), \".claude\", \"projects\", cwdToProjectDir(cwd));\n if (!existsSync(projectsDir)) return null;\n let newest: { path: string; mtime: number } | null = null;\n for (const f of readdirSync(projectsDir)) {\n if (!f.endsWith(\".jsonl\")) continue;\n const p = join(projectsDir, f);\n try {\n const st = statSync(p);\n if (!newest || st.mtimeMs > newest.mtime) newest = { path: p, mtime: st.mtimeMs };\n } catch { /* skip */ }\n }\n return newest?.path ?? null;\n}\n\n// ── Incremental tail state ───────────────────────────────────────────────────\n\ninterface TailState {\n path: string;\n offset: number;\n /** Running totals across all bytes consumed so far. */\n totals: {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreateTokens: number;\n };\n /** Last-seen values (for per-turn fields like context size). */\n lastTurnContextTokens: number;\n lastModel: string;\n /** Tail buffer for an incomplete trailing line. */\n carry: string;\n}\n\n// One state per cwd. Module-scope so successive readAgentMetrics calls reuse it.\nconst tailStates = new Map<string, TailState>();\n\nfunction freshState(path: string): TailState {\n return {\n path,\n offset: 0,\n totals: { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreateTokens: 0 },\n lastTurnContextTokens: 0,\n lastModel: \"\",\n carry: \"\",\n };\n}\n\n// ── Public API ───────────────────────────────────────────────────────────────\n\nexport interface AgentMetrics {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheCreateTokens: number;\n /** Approximate input-context size on the last turn (in + cache_read + cache_create). */\n lastTurnContextTokens: number;\n /** Last sample's model id (the one used by the latest assistant message). */\n lastModel: string;\n}\n\n/**\n * Aggregate session metrics, reading only bytes added since the last call.\n * Returns null when no jsonl exists yet (agent hasn't taken a turn).\n *\n * Handles rotation (newest jsonl path changes → fresh state) and truncation\n * (current file shorter than our remembered offset → fresh state).\n */\nexport function readAgentMetrics(cwd: string): AgentMetrics | null {\n const path = findLatestJsonl(cwd);\n if (!path) return null;\n\n let state = tailStates.get(cwd);\n // Rotation: a new session started, replacing the latest jsonl.\n if (state && state.path !== path) state = undefined;\n if (!state) {\n state = freshState(path);\n tailStates.set(cwd, state);\n }\n\n let size: number;\n try { size = statSync(path).size; }\n catch { return null; }\n\n // Truncation (rare for claude but defensive): file shrunk → reset.\n if (size < state.offset) {\n state = freshState(path);\n tailStates.set(cwd, state);\n }\n if (size === state.offset && state.lastModel === \"\") return null;\n if (size === state.offset) return snapshot(state);\n\n // Read the new chunk\n const chunkLen = size - state.offset;\n const buf = Buffer.alloc(chunkLen);\n let fd: number;\n try { fd = openSync(path, \"r\"); }\n catch { return snapshot(state); }\n try {\n readSync(fd, buf, 0, chunkLen, state.offset);\n } finally {\n closeSync(fd);\n }\n state.offset = size;\n\n const text = state.carry + buf.toString(\"utf-8\");\n const newlineEnd = text.lastIndexOf(\"\\n\");\n const completeLines = newlineEnd >= 0 ? text.slice(0, newlineEnd) : \"\";\n state.carry = newlineEnd >= 0 ? text.slice(newlineEnd + 1) : text;\n\n for (const line of completeLines.split(\"\\n\")) {\n consumeLine(state, line);\n }\n\n return snapshot(state);\n}\n\nfunction snapshot(s: TailState): AgentMetrics {\n return {\n ...s.totals,\n lastTurnContextTokens: s.lastTurnContextTokens,\n lastModel: s.lastModel,\n };\n}\n\n/**\n * Apply one jsonl line to the rolling totals. Tolerant of malformed lines\n * and missing usage fields. Exported for testing.\n */\nexport function consumeLine(state: {\n totals: TailState[\"totals\"];\n lastTurnContextTokens: number;\n lastModel: string;\n}, line: string): void {\n if (!line.trim()) return;\n let event: { message?: { usage?: Record<string, number>; model?: string } };\n try { event = JSON.parse(line); }\n catch { return; }\n const msg = event.message;\n if (!msg || typeof msg !== \"object\") return;\n const usage = msg.usage;\n if (!usage) return;\n\n const inp = Number(usage.input_tokens) || 0;\n const out = Number(usage.output_tokens) || 0;\n const cr = Number(usage.cache_read_input_tokens) || 0;\n const cw = Number(usage.cache_creation_input_tokens) || 0;\n\n state.totals.inputTokens += inp;\n state.totals.outputTokens += out;\n state.totals.cacheReadTokens += cr;\n state.totals.cacheCreateTokens += cw;\n\n if (typeof msg.model === \"string\") state.lastModel = msg.model;\n\n state.lastTurnContextTokens = inp + cr + cw;\n}\n\n/** Reset all in-memory tail state. For tests. */\nexport function _resetForTest(): void {\n tailStates.clear();\n}\n","/**\n * apiary run opencode — client-side agent runtime for OpenCode.\n *\n * Spawns `opencode serve` with OPENCODE_CONFIG_CONTENT to inject the apiary\n * MCP server. The user opens OpenCode's UI, starts a conversation, and tells\n * the agent to join a room URL.\n *\n * Session detection: we subscribe to OpenCode's global SSE event stream and\n * watch for apiary tool call events. Each event carries the sessionID of the\n * calling session — no guessing, no race conditions.\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\nimport {\n saveAgentSession,\n clearAgentSession,\n listAgentSessions,\n isAgentAlive,\n type AgentSession,\n} from \"../agent-session.js\";\n\nexport { type AgentRuntimeOptions as RunOpencodeOptions };\n\nexport function listOpencodeSessions(): AgentSession[] {\n return listAgentSessions(\"opencode\");\n}\n\nexport async function runOpencode(options: AgentRuntimeOptions): Promise<void> {\n // ── Pick a port for OpenCode ────────────────────────────────────────────\n\n const opencodePort = 14096 + Math.floor(Math.random() * 1000);\n const opencodeUrl = `http://127.0.0.1:${opencodePort}`;\n\n // ── Room → OpenCode session mapping ────────────────────────────────────\n //\n // Each OpenCode session can join different rooms. Lazily detected on first\n // delivery for each room by inspecting session messages for apiary tool calls.\n\n const roomSessions = new Map<string, string>(); // roomId → OpenCode sessionId\n\n /** Find the OpenCode session that most recently called an apiary tool. */\n async function findApiarySession(): Promise<string | null> {\n try {\n const res = await fetch(`${opencodeUrl}/session`);\n if (!res.ok) return null;\n // Sessions sorted by time.updated desc — first is most recent\n const sessions = await res.json() as Array<{ id: string; time: { updated: number } }>;\n\n // Check the most recently updated sessions for apiary tool parts\n for (const sess of sessions.slice(0, 3)) {\n const msgRes = await fetch(`${opencodeUrl}/session/${sess.id}/message`);\n if (!msgRes.ok) continue;\n const messages = await msgRes.json() as Array<{\n parts?: Array<{ type?: string; tool?: string }>;\n }>;\n for (const msg of messages) {\n for (const part of msg.parts ?? []) {\n if (part.type === \"tool\" && part.tool?.includes(\"apiary__\")) {\n return sess.id;\n }\n }\n }\n }\n // Fallback: most recently updated session\n return sessions.length > 0 ? sessions[0].id : null;\n } catch {\n return null;\n }\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // No --join URLs — the user tells the agent to join from within OpenCode.\n\n const setup = await setupAgentRuntime({\n ...options,\n joinUrls: undefined,\n });\n\n // ── Build MCP config for OpenCode ───────────────────────────────────────\n\n const opencodeConfig = {\n mcp: {\n apiary: {\n type: \"remote\",\n url: setup.mcpServer.url,\n oauth: false,\n },\n },\n };\n\n // ── Spawn OpenCode in headless mode ─────────────────────────────────────\n\n const extraArgs = options.extraArgs ?? [];\n const opencodeArgs = [\"serve\", \"--port\", String(opencodePort), ...extraArgs];\n\n console.log(\"Launching OpenCode...\");\n\n let child: ChildProcess;\n try {\n child = spawn(\"opencode\", opencodeArgs, {\n env: {\n ...process.env,\n OPENCODE_CONFIG_CONTENT: JSON.stringify(opencodeConfig),\n },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch {\n console.error(\"Error: opencode is required but not found. Install it from https://opencode.ai\");\n await setup.cleanup();\n process.exit(1);\n }\n\n // Forward stderr to console for visibility\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n process.stderr.write(chunk);\n });\n\n let childExited = false;\n child.on(\"exit\", () => { childExited = true; });\n\n child.on(\"error\", async () => {\n console.error(\"Error: failed to start opencode. Is it installed?\");\n await setup.cleanup();\n process.exit(1);\n });\n\n // ── Wait for OpenCode to be ready ───────────────────────────────────────\n\n const ready = await pollForReady(opencodeUrl, 30_000);\n if (!ready) {\n console.error(\"OpenCode did not become ready within 30 seconds.\");\n child.kill();\n await setup.cleanup();\n process.exit(1);\n }\n\n console.log(` OpenCode running on ${opencodeUrl}`);\n\n // Register session so `apiary ps` / `apiary stop` can see it.\n saveAgentSession({\n runtime: \"opencode\",\n agentName: setup.agentName,\n pid: process.pid,\n childPid: child.pid,\n });\n\n // ── Build deliver callback ──────────────────────────────────────────────\n //\n // OpenCode's POST /session/:id/message uses Hono stream() — headers (200)\n // arrive immediately but the LLM runs inside the stream callback. We MUST\n // consume the response body (await res.text()) to block until the LLM\n // finishes, preserving the EventProcessor's _processing lock.\n\n async function deliver(parts: ContentPart[]): Promise<void> {\n const roomId = setup.processor.currentContextRoomId;\n if (!roomId) return;\n\n // Lazy session detection: look up on first delivery for this room\n if (!roomSessions.has(roomId)) {\n const sid = await findApiarySession();\n if (!sid) return;\n roomSessions.set(roomId, sid);\n console.log(` Linked room ${roomId} → session ${sid}`);\n }\n const targetSession = roomSessions.get(roomId)!;\n\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n try {\n const res = await fetch(`${opencodeUrl}/session/${targetSession}/message`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n parts: [{ type: \"text\", text }],\n }),\n });\n await res.text();\n } catch {\n // OpenCode may have exited\n }\n }\n\n // ── Start the event loop ────────────────────────────────────────────────\n // No initialParts — the user drives the conversation from OpenCode's UI.\n\n const eventLoopPromise = setup.processor.run(deliver, setup.wrappedSource);\n\n console.log(`\\n OpenCode agent running.`);\n console.log(` To watch: opencode attach ${opencodeUrl}\\n`);\n\n // ── Block until child exits or Ctrl+C ───────────────────────────────────\n\n const exitPromise = new Promise<void>((resolve) => {\n child.on(\"exit\", resolve);\n });\n\n const signalPromise = new Promise<void>((resolve) => {\n const handler = () => { resolve(); };\n process.on(\"SIGINT\", handler);\n process.on(\"SIGTERM\", handler);\n });\n\n await Promise.race([exitPromise, signalPromise]);\n\n // ── Cleanup ─────────────────────────────────────────────────────────────\n\n if (!childExited) {\n child.kill();\n }\n await setup.cleanup();\n clearAgentSession(\"opencode\", setup.agentName);\n\n console.log(\"Disconnected.\");\n}\n\n// ── Stop ────────────────────────────────────────────────────────────────────\n\nexport async function stopOpencode(name?: string, all?: boolean): Promise<void> {\n const sessions = listOpencodeSessions();\n if (sessions.length === 0) {\n if (all) return;\n console.error(\"No active OpenCode sessions.\");\n process.exit(1);\n }\n\n if (all) {\n for (const s of sessions) stopOpencodeSession(s);\n return;\n }\n\n let session: AgentSession | undefined;\n if (name) {\n session = sessions.find((s) => s.agentName === name);\n if (!session) {\n console.error(`No active OpenCode session named \"${name}\". Active sessions:`);\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n } else if (sessions.length === 1) {\n session = sessions[0];\n } else {\n console.error(\"Multiple active OpenCode sessions. Use --name or --all:\");\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n\n stopOpencodeSession(session);\n}\n\nfunction stopOpencodeSession(session: AgentSession): void {\n // Kill the opencode child first so it doesn't outlive its parent runtime.\n if (session.childPid) {\n try { process.kill(session.childPid, \"SIGTERM\"); } catch { /* ok */ }\n }\n if (isAgentAlive(session)) {\n try { process.kill(session.pid, \"SIGTERM\"); } catch { /* ok */ }\n }\n clearAgentSession(\"opencode\", session.agentName);\n console.log(`Stopped \"${session.agentName}\".`);\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nasync function pollForReady(url: string, timeoutMs: number): Promise<boolean> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n try {\n const res = await fetch(`${url}/session/status`);\n if (res.ok) return true;\n } catch {\n // Not ready yet\n }\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n","/**\n * apiary run codex — client-side agent runtime for OpenAI Codex CLI.\n *\n * Uses the shared runtime setup (EventProcessor, MCP server)\n * then adds Codex-specific pieces: tmux session + CodexTmuxBridge delivery.\n *\n * Codex connects to the runtime MCP server natively via config.toml\n * (no stdio bridge needed — Codex supports remote MCP servers via URL).\n * The agent joins rooms by calling join_room() — no auto-injection needed.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { rmSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\n\nimport {\n tmuxAvailable,\n tmuxCreateSession,\n tmuxSendCommand,\n tmuxAttach,\n tmuxKillSession,\n tmuxSessionExists,\n resetTerminal,\n} from \"../tmux.js\";\nimport { CodexTmuxBridge } from \"./tmux-bridge.js\";\nimport { findCodexSessionId } from \"./queue.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\nimport { codexStateToActivity } from \"../agent-state.js\";\nimport { shouldReportActivity, type AgentActivityState } from \"../../agent/event-processor.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport { agentEmoji } from \"../config.js\";\nimport { deliverInvite, sameApiaryServer } from \"../invites.js\";\nimport {\n clearCodexProfile,\n prepareCodexLaunch,\n pruneCodexProfiles,\n} from \"./launch.js\";\nimport {\n saveAgentSession,\n clearAgentSession,\n listAgentSessions,\n isAgentAlive,\n type AgentSession,\n} from \"../agent-session.js\";\n\nexport { type AgentRuntimeOptions as RunCodexOptions };\n\nexport function listCodexSessions(): AgentSession[] {\n return listAgentSessions(\"codex\");\n}\n\n/** Check if codex CLI is installed and available. */\nfunction codexAvailable(): boolean {\n try {\n execFileSync(\"codex\", [\"--version\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function runCodex(options: AgentRuntimeOptions): Promise<void> {\n // ── Headless mode — skip tmux, deliver events as plain text to stdout ────\n\n if (options.headless) {\n const setup = await setupAgentRuntime(options);\n\n const deliver = async (parts: Parameters<typeof contentPartsToString>[0]) => {\n const text = contentPartsToString(parts);\n if (text.trim()) process.stdout.write(text + \"\\n\");\n };\n\n const eventLoopPromise = setup.processor\n .run(deliver, setup.wrappedSource, setup.initialParts)\n .catch(() => {});\n\n process.stderr.write(`MCP server: ${setup.mcpServer.url}\\n`);\n\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", resolve);\n process.on(\"SIGTERM\", resolve);\n });\n\n await setup.cleanup();\n await eventLoopPromise;\n return;\n }\n\n // ── Preflight checks ────────────────────────────────────────────────────\n\n if (!tmuxAvailable()) {\n console.error(\"Error: tmux is required but not found. Install it with: brew install tmux\");\n process.exit(1);\n }\n\n if (!codexAvailable()) {\n console.error(\"Error: codex is required but not found. Install it with: npm install -g @openai/codex\");\n process.exit(1);\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // Don't pass joinUrls — Codex agents join rooms manually via join_room()\n\n const setup = await setupAgentRuntime({ ...options, joinUrls: undefined });\n\n // ── Codex MCP wiring ────────────────────────────────────────────────────\n // Codex supports remote MCP servers natively, so no stdio bridge is needed\n // (unlike Claude Code, which has an OAuth bug). apiary declares itself in a\n // config profile layered over the user's real Codex home, so the agent keeps\n // their credentials, model and project trust — see launch.ts.\n\n const mcpPort = new URL(setup.mcpServer.url).port;\n const mcpUrl = `http://127.0.0.1:${mcpPort}/mcp`;\n\n // ── Create tmux session + launch Codex ──────────────────────────────────\n\n const tmuxSession = `apiary_${setup.agentName}`;\n\n if (tmuxSessionExists(tmuxSession)) {\n tmuxKillSession(tmuxSession);\n }\n\n const home = homedir();\n const cwdFull = process.cwd();\n const cwdShort = cwdFull.startsWith(home) ? \"~\" + cwdFull.slice(home.length) : cwdFull;\n const tabTitle = `${agentEmoji(setup.agentName)} ${setup.agentName} · ${cwdShort}`;\n\n console.log(\"Launching Codex...\");\n tmuxCreateSession(tmuxSession, tabTitle);\n\n // Sweep profiles orphaned by a runtime that was killed before it could clean\n // up. \"Live\" means the process is actually alive — a stale session record\n // would otherwise keep a dead agent's profile around forever. This agent\n // counts as live so a concurrent launch cannot sweep what we are about to\n // write, and vice versa.\n pruneCodexProfiles([\n setup.agentName,\n ...listCodexSessions().filter(isAgentAlive).map((s) => s.agentName),\n ]);\n const { command } = prepareCodexLaunch(setup.agentName, mcpUrl, options.extraArgs ?? []);\n // Stamped before the launch so the session we are about to start is never\n // older than the floor findCodexSessionId searches from.\n const launchedAt = Date.now();\n tmuxSendCommand(tmuxSession, command);\n\n // ── Start event loop + attach ──────────────────────────────────────────\n\n // Deliver through `codex queue` once the session can be identified, falling\n // back to the composer until then — see queue.ts for why that is preferable.\n const bridge = new CodexTmuxBridge(tmuxSession, {\n resolveThreadId: () => findCodexSessionId({ cwd: cwdFull, startedAfter: launchedAt }),\n });\n\n // Start the event loop in the background — no initial injection.\n // The agent joins rooms by calling join_room() when the user tells it to.\n const eventLoopPromise = setup.processor.run(bridge.deliver.bind(bridge), setup.wrappedSource)\n .catch(() => {}); // Prevent unhandled rejection from crashing the process\n\n // Activity heartbeat. The Codex runtime previously reported nothing at all,\n // so the room could only guess: every agent was marked \"working\" whenever\n // anyone spoke, and a Codex agent parked on an approval prompt looked\n // identical to one that was thinking. Report what the pane actually shows.\n let lastActivityLabel: string | null = null;\n let lastState: AgentActivityState | undefined;\n let lastReportAt = 0;\n const activityTimer: NodeJS.Timeout = setInterval(() => {\n let label: string | null;\n let state: AgentActivityState | undefined;\n try {\n label = bridge.getActivityLabel();\n state = codexStateToActivity(bridge.detectState());\n } catch { return; }\n // Re-send an unchanged state periodically, not just on change. The room\n // server ages reported state out so a dead runtime does not pin a stale\n // value — but with change-only reporting a settled agent stops reporting,\n // ages out, and reads as \"not reporting at all\". Observed end-to-end: an\n // idle agent's state vanished ~15s after it settled. One tiny POST per\n // agent per interval is nothing next to being wrong about who is idle.\n if (!shouldReportActivity({\n label, state,\n lastLabel: lastActivityLabel, lastState,\n lastReportAt, now: Date.now(),\n })) return;\n lastActivityLabel = label;\n lastState = state;\n lastReportAt = Date.now();\n setup.processor.broadcastActivity(label, state).catch(() => { /* best-effort */ });\n }, 1500);\n activityTimer.unref();\n\n // Wait for Codex to start, checking the session is still alive\n for (let i = 0; i < 10; i++) {\n await new Promise((r) => setTimeout(r, 500));\n if (!tmuxSessionExists(tmuxSession)) {\n console.error(\"Error: Codex exited during startup. Try running again.\");\n clearCodexProfile(setup.agentName);\n clearInterval(activityTimer);\n bridge.stop();\n await setup.cleanup();\n resetTerminal();\n return;\n }\n }\n\n // Register session so `apiary ps` / `apiary stop` can see it.\n saveAgentSession({\n runtime: \"codex\",\n agentName: setup.agentName,\n pid: process.pid,\n tmuxSession,\n });\n\n // ── Deliver queued invite (auto-join) ─────────────────────────────────\n // If the wizard background-spawned this agent, ~/.apiary/invites/<name>\n // holds the room's join URL. deliverInvite keeps asking until the agent is\n // really in the room, and only then deletes the file — a Codex that boots\n // into a sign-in or onboarding screen gets another go once it reaches a\n // prompt, instead of losing the URL to a menu that swallowed it.\n const inviteAbort = new AbortController();\n const invitePromise = deliverInvite({\n agentName: setup.agentName,\n deliver: (parts) => bridge.deliver(parts, { dedupe: true }),\n hasJoined: (url) => setup.joinResults.some((jr) => sameApiaryServer(jr.serverUrl, url)),\n signal: inviteAbort.signal,\n }).then((outcome) => {\n // Don't let a failed auto-join be silent — that is exactly how an agent\n // ends up sitting outside the room it was spawned for. The invite is kept\n // on disk, so `apiary room resume <room>` will hand it over again.\n if (outcome === \"abandoned\" && !options.background) {\n console.log(\n `Note: \"${setup.agentName}\" did not join the room it was invited to. ` +\n `The invite is still queued — check the pane, then re-run or use \"apiary room resume\".`,\n );\n }\n return outcome;\n }).catch(() => \"cancelled\" as const);\n\n // Background mode: spawned by room create — run silently until SIGTERM.\n if (options.background) {\n // Suppress output so this process doesn't bleed into the parent terminal.\n const noop = (() => true) as unknown as typeof process.stdout.write;\n process.stdout.write = noop;\n process.stderr.write = noop;\n await new Promise<void>((resolve) => {\n process.on(\"SIGTERM\", resolve);\n process.on(\"SIGINT\", resolve);\n });\n inviteAbort.abort();\n await invitePromise;\n clearInterval(activityTimer);\n bridge.stop();\n await setup.cleanup();\n if (tmuxSessionExists(tmuxSession)) tmuxKillSession(tmuxSession);\n clearCodexProfile(setup.agentName);\n clearAgentSession(\"codex\", setup.agentName);\n return;\n }\n\n console.log(\"Attaching to Codex session...\\n\");\n\n try {\n await tmuxAttach(tmuxSession);\n } catch {\n // User detached or session ended\n }\n\n // ── Cleanup ─────────────────────────────────────────────────────────────\n\n inviteAbort.abort();\n await invitePromise;\n clearInterval(activityTimer);\n bridge.stop();\n await setup.cleanup();\n tmuxKillSession(tmuxSession);\n clearCodexProfile(setup.agentName);\n clearAgentSession(\"codex\", setup.agentName);\n resetTerminal();\n\n console.log(\"Disconnected.\");\n}\n\n// ── Stop ────────────────────────────────────────────────────────────────────\n\nexport async function stopCodex(name?: string, all?: boolean): Promise<void> {\n const sessions = listCodexSessions();\n if (sessions.length === 0) {\n if (all) return;\n console.error(\"No active Codex sessions.\");\n process.exit(1);\n }\n\n if (all) {\n for (const s of sessions) stopCodexSession(s);\n return;\n }\n\n let session: AgentSession | undefined;\n if (name) {\n session = sessions.find((s) => s.agentName === name);\n if (!session) {\n console.error(`No active Codex session named \"${name}\". Active sessions:`);\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n } else if (sessions.length === 1) {\n session = sessions[0];\n } else {\n console.error(\"Multiple active Codex sessions. Use --name or --all:\");\n for (const s of sessions) console.error(` - ${s.agentName}`);\n process.exit(1);\n }\n\n stopCodexSession(session);\n}\n\nfunction stopCodexSession(session: AgentSession): void {\n if (session.tmuxSession && tmuxSessionExists(session.tmuxSession)) {\n tmuxKillSession(session.tmuxSession);\n }\n if (isAgentAlive(session)) {\n try { process.kill(session.pid, \"SIGTERM\"); } catch { /* ok */ }\n }\n clearCodexProfile(session.agentName);\n // Sessions recorded by an older apiary still carry a sandboxed CODEX_HOME.\n if (session.tmpDir) {\n try { rmSync(session.tmpDir, { recursive: true }); } catch { /* ok */ }\n }\n clearAgentSession(\"codex\", session.agentName);\n resetTerminal();\n console.log(`Stopped \"${session.agentName}\".`);\n}\n","/**\n * Codex delivery that never touches the composer.\n *\n * `codex queue --thread <id> --message <text>` hands a message to a running\n * Codex session through Codex itself. Nothing is typed, so none of the\n * composer's own behaviour applies: no paste-vs-typing timing to lose, no\n * `@`-mention popup to open, no Enter to be swallowed selecting a completion.\n * Measured against the message that broke #shipyard on 2026-09-10 it landed\n * intact 4/4 in 1.2–3.3s, where keystroke delivery mangled it.\n *\n * It also queues rather than races: a busy or blocked agent picks the message\n * up when it is ready, so the poll-until-injectable loop the tmux path needs\n * does not apply either.\n *\n * The one thing Codex does not give us is a way to name a session at launch,\n * so the thread has to be recognised after the fact — see findCodexSessionId.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { readFileSync, readdirSync, statSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n/** Codex's session store, honouring CODEX_HOME like Codex itself does. */\nexport function userCodexSessionsDir(codexHome?: string): string {\n return join(codexHome ?? process.env.CODEX_HOME ?? join(homedir(), \".codex\"), \"sessions\");\n}\n\n/** The first line of a rollout file: Codex's own session metadata. */\ninterface SessionMeta {\n sessionId: string;\n cwd: string;\n startedAt: number;\n}\n\n/** Parse a rollout's session_meta record, or null if it is not one. */\nexport function parseSessionMeta(firstLine: string): SessionMeta | null {\n try {\n const record = JSON.parse(firstLine);\n if (record?.type !== \"session_meta\") return null;\n const payload = record.payload ?? {};\n const sessionId = payload.session_id ?? payload.id;\n const startedAt = Date.parse(payload.timestamp ?? \"\");\n if (typeof sessionId !== \"string\" || typeof payload.cwd !== \"string\") return null;\n if (Number.isNaN(startedAt)) return null;\n return { sessionId, cwd: payload.cwd, startedAt };\n } catch {\n return null;\n }\n}\n\n/** Every rollout file under the session store, newest mtime first. */\nfunction rolloutFiles(root: string): string[] {\n const found: Array<{ path: string; mtime: number }> = [];\n const walk = (dir: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const entry of entries) {\n const path = join(dir, entry);\n let stat;\n try {\n stat = statSync(path);\n } catch {\n continue;\n }\n if (stat.isDirectory()) walk(path);\n else if (entry.startsWith(\"rollout-\") && entry.endsWith(\".jsonl\")) {\n found.push({ path, mtime: stat.mtimeMs });\n }\n }\n };\n walk(root);\n return found.sort((a, b) => b.mtime - a.mtime).map((f) => f.path);\n}\n\nexport interface FindSessionOptions {\n /** The directory the agent was launched in. */\n cwd: string;\n /** Epoch ms of the launch. Sessions older than this belong to someone else. */\n startedAfter: number;\n /** Override the session store (tests). */\n sessionsDir?: string;\n /**\n * Clock slack in ms. Codex stamps the session when it starts, which is a\n * moment or two after we send the launch command.\n */\n slackMs?: number;\n}\n\n/**\n * Find the session id of the Codex we launched.\n *\n * Codex has no launch-time session naming — `--profile` does not reach the\n * rollout, and session *names* are auto-generated from the first message — so\n * a session is identified by where it runs and when it started: the newest\n * rollout whose cwd matches ours and which began at or after we launched.\n *\n * Returns null rather than guessing. A wrong id would deliver a room message\n * into somebody else's Codex, which is far worse than falling back to typing.\n */\nexport function findCodexSessionId(opts: FindSessionOptions): string | null {\n const root = opts.sessionsDir ?? userCodexSessionsDir();\n if (!existsSync(root)) return null;\n const floor = opts.startedAfter - (opts.slackMs ?? 5_000);\n\n for (const path of rolloutFiles(root)) {\n let firstLine: string;\n try {\n firstLine = readFileSync(path, \"utf-8\").split(\"\\n\", 1)[0] ?? \"\";\n } catch {\n continue;\n }\n const meta = parseSessionMeta(firstLine);\n if (!meta) continue;\n if (meta.cwd !== opts.cwd) continue;\n if (meta.startedAt < floor) continue;\n return meta.sessionId;\n }\n return null;\n}\n\n/** Shell out to `codex queue`. Separated so tests can observe the call. */\nexport type QueueRunner = (threadId: string, text: string) => void;\n\nconst defaultRunner: QueueRunner = (threadId, text) => {\n execFileSync(\"codex\", [\"queue\", \"--thread\", threadId, \"--message\", text], {\n stdio: \"ignore\",\n });\n};\n\n/**\n * Hand a message to a running Codex session. Returns false if Codex refused\n * it, so the caller can fall back to the composer rather than drop the message.\n */\nexport function queueCodexMessage(\n threadId: string,\n text: string,\n run: QueueRunner = defaultRunner,\n): boolean {\n try {\n run(threadId, text);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * CodexTmuxBridge — state-aware event injection into Codex CLI.\n *\n * Reads the Codex TUI screen via `tmux capture-pane`, detects the\n * current UI state, and applies the right injection strategy:\n *\n * idle → bracketed paste + Enter\n * typing → Ctrl+U (cut), bracketed paste + Enter, Ctrl+Y (restore)\n * approval → queue and poll\n * streaming → queue and poll\n * blocked → queue and poll (sign-in / onboarding — no composer exists yet)\n * unknown → queue and poll (safe default)\n *\n * Uses bracketed paste escape sequences to bypass Codex's timing-based\n * paste-burst detector (120ms Enter suppression window). Text wrapped\n * in ESC[200~...ESC[201~ is delivered as a Paste event, not individual\n * keystrokes, so the burst detector never fires.\n */\n\nimport {\n tmuxPaneIsShell,\n tmuxCapturePane,\n tmuxInjectPaste,\n tmuxSendEnter,\n tmuxSendKey,\n} from \"../tmux.js\";\nimport { queueCodexMessage } from \"./queue.js\";\nimport { composerStillHolds } from \"../composer.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\n\nexport type CodexTuiState =\n /**\n * The CLI is not running — the pane has fallen back to a shell. Injecting\n * here types a room message at the user's shell prompt, which is how\n * \"zsh: command not found: Please\" happened.\n */\n | \"absent\"\n | \"idle\"\n | \"typing\"\n | \"approval\"\n | \"streaming\"\n | \"blocked\"\n | \"unknown\";\n\n// Braille spinner characters used by Codex during streaming\nconst SPINNER_CHARS = \"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\";\n\n// Patterns that indicate an approval overlay.\n// Codex has shipped several approval UIs; match all of them, since injecting\n// into an approval menu answers a question the agent never saw.\nconst APPROVAL_PATTERNS: Array<string | RegExp> = [\n \"Would you like to\",\n \"needs your approval\",\n \"Press Enter to confirm or Esc to cancel\",\n \"Do you want to approve\",\n // v0.15x MCP tool approval: \"Allow the apiary MCP server to run tool \"x\"?\"\n /Allow the .+ MCP server to run tool/,\n \"Allow for this session\",\n // Strings taken from the Codex binary itself rather than guessed.\n \"Allow Codex to run\",\n \"Allow Codex to apply proposed code changes?\",\n \"enter to submit | esc to cancel\",\n];\n\n// Patterns that indicate Codex has not reached a composer at all — the\n// sign-in flow, the onboarding wizard, or a directory-trust prompt. There is\n// no prompt to paste into, so anything injected here is typed into a menu and\n// lost. Queue instead, and the text lands once the human clears the screen.\nconst BLOCKED_PATTERNS: Array<string | RegExp> = [\n // Not a bare \"Welcome to Codex\" — the ordinary ready banner opens\n // \"Welcome to Codex! Type a message to get started.\"\n /Welcome to Codex, /,\n \"Finish signing in via your browser\",\n \"Sign in with ChatGPT\",\n \"Sign in with Device Code\",\n \"Provide your own API key\",\n \"Sign in to continue\",\n // `[\\s\\S]` not `.`: the screen renders these on separate lines, and `.`\n // does not cross a newline, so the original pattern never matched.\n /You are running Codex in [\\s\\S]+Do you want to allow/,\n \"Do you trust the files in this folder\",\n // Codex 0.156 reworded the trust prompt; the old string above no longer appears.\n \"Trust this folder?\",\n // The startup update menu (\"Update now / Skip\") sits in front of the composer too.\n /Update available[\\s\\S]+Update now/,\n];\n\n// Patterns that indicate the agent is actively working\nconst STREAMING_PATTERNS = [\n /Working\\s*\\(\\d+[smh]/, // \"Working (12s\" or \"Working (1m 30s\"\n /Working\\s*$/, // \"Working\" at end of line (just started)\n /esc to interrupt/, // hint text during streaming\n];\n\n// The footer of Codex's @-mention popup. Codex opens this when it reads a\n// `@` as a typed character, and while it is open Enter means \"insert the\n// highlighted completion\", not \"submit\" — see dismissMentionPopup.\nconst MENTION_POPUP_PATTERNS: RegExp[] = [\n /enter\\s+insert\\b[\\s\\S]{0,40}?esc\\s+close/i,\n];\n\n/**\n * Is Codex's @-mention popup on screen?\n *\n * Exported for tests, and matched only against the bottom of the screen: the\n * popup is anchored above the composer, whereas an agent quoting the same\n * words in its transcript must not count.\n */\nexport function codexMentionPopupIsOpen(lines: string[]): boolean {\n const tail = lines.slice(-20).join(\"\\n\");\n return MENTION_POPUP_PATTERNS.some((p) => p.test(tail));\n}\n\nexport interface DeliverOptions {\n /**\n * Drop this text if an identical copy is already queued. For callers that\n * retry a delivery they cannot observe landing (the room invite loop).\n */\n dedupe?: boolean;\n}\n\nexport interface CodexTmuxBridgeOptions {\n /**\n * Resolve the Codex session id for this agent, if it can be known.\n *\n * When it returns an id, messages go through `codex queue` instead of the\n * composer — see queue.ts. Called lazily and memoised, because the session\n * does not exist until Codex has finished starting.\n */\n resolveThreadId?: () => string | null;\n /** How often to poll when events are queued (ms). Default: 200 */\n pollIntervalMs?: number;\n /** Delay after bracketed paste before sending Enter (ms). Default: 150 */\n pasteDelayMs?: number;\n /** Delay between Ctrl+U/inject/Ctrl+Y steps (ms). Default: 50 */\n keystrokeDelayMs?: number;\n /**\n * Called when a message could not be got into the agent, after the retry.\n *\n * Delivery used to be assumed: apiary typed, reported success and moved on,\n * so a swallowed submit looked exactly like an agent choosing not to answer.\n * This is the hook that makes the difference visible to someone.\n */\n onUndelivered?: (text: string) => void;\n}\n\n/**\n * Last resort when no one has wired a handler: say it on stderr, which lands in\n * the launcher pane. Better than nothing, and a great deal better than success.\n */\nfunction defaultUndeliveredWarning(text: string): void {\n const preview = text.replace(/\\s+/g, \" \").slice(0, 80);\n process.stderr.write(`apiary: message NOT delivered to the agent — still in its composer: ${preview}\\n`);\n}\n\nexport class CodexTmuxBridge {\n private session: string;\n private queue: string[] = [];\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private pollIntervalMs: number;\n private pasteDelayMs: number;\n private keystrokeDelayMs: number;\n private stopped = false;\n private resolveThreadId?: () => string | null;\n private threadId: string | null = null;\n private onUndelivered: (text: string) => void;\n\n constructor(session: string, opts?: CodexTmuxBridgeOptions) {\n this.session = session;\n this.pollIntervalMs = opts?.pollIntervalMs ?? 200;\n this.pasteDelayMs = opts?.pasteDelayMs ?? 150;\n this.keystrokeDelayMs = opts?.keystrokeDelayMs ?? 50;\n this.resolveThreadId = opts?.resolveThreadId;\n this.onUndelivered = opts?.onUndelivered ?? defaultUndeliveredWarning;\n }\n\n /**\n * The Codex session id, resolved once and remembered.\n *\n * Resolution is deferred because the session does not exist at construction\n * time — Codex is still starting — and it is retried on later deliveries\n * until it succeeds.\n */\n private thread(): string | null {\n if (this.threadId) return this.threadId;\n if (!this.resolveThreadId) return null;\n try {\n this.threadId = this.resolveThreadId();\n } catch {\n this.threadId = null;\n }\n return this.threadId;\n }\n\n /**\n * Delivery callback — drop-in replacement for EventProcessor's deliver.\n * Pass `bridge.deliver.bind(bridge)` to EventProcessor.run().\n */\n async deliver(parts: ContentPart[], opts?: DeliverOptions): Promise<void> {\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n this.inject(text, opts?.dedupe === true);\n }\n\n /**\n * Detect the current TUI state by reading the screen.\n */\n detectState(): CodexTuiState {\n // Checked before any screen parsing: if the CLI has exited there is\n // nothing on screen worth interpreting, and a shell prompt looks\n // indistinguishable from an idle composer to a pattern matcher.\n if (tmuxPaneIsShell(this.session)) return \"absent\";\n const lines = this.captureScreen();\n return detectCodexStateFromLines(lines);\n }\n\n /**\n * Codex's live status text (\"Working (12s • esc to interrupt)\") for the room\n * strip, or null when idle. Mirrors the Claude bridge's getActivityLabel so\n * both runtimes can report the same shape.\n */\n getActivityLabel(): string | null {\n return extractCodexActivityLabel(this.captureScreen());\n }\n\n /**\n * Try to inject text, choosing strategy based on TUI state.\n * Text is flattened to a single line to avoid multi-line paste issues.\n */\n private inject(text: string, dedupe: boolean): void {\n const flat = text.replace(/\\n/g, \" \");\n const state = this.detectState();\n\n // Prefer handing the message to Codex itself. Nothing is typed, so the\n // composer's paste-vs-typing timing and its @-mention popup cannot apply,\n // and Codex holds the message for a busy agent rather than us polling for\n // an injectable moment. Skipped when the CLI is gone: queueing into a\n // session nothing is reading would silently swallow the message.\n if (state !== \"absent\") {\n const threadId = this.thread();\n if (threadId && queueCodexMessage(threadId, flat)) return;\n }\n\n switch (state) {\n case \"idle\":\n this.injectIdle(flat);\n break;\n case \"typing\":\n this.injectWhileTyping(flat);\n break;\n default:\n // approval, streaming, blocked, absent, unknown — queue it\n this.enqueue(flat, dedupe);\n break;\n }\n }\n\n /** Capture the screen via tmux capture-pane. */\n private captureScreen(): string[] {\n return tmuxCapturePane(this.session);\n }\n\n /**\n * Inject into an idle prompt using bracketed paste.\n *\n * Bracketed paste wraps text in ESC[200~...ESC[201~ so crossterm\n * delivers it as a single Paste event, bypassing the burst detector.\n * The wrapping happens in one write — see tmuxInjectPaste for why that\n * matters. After the paste, we wait for the Enter suppression window\n * (120ms) to expire, then send Enter to submit.\n */\n private injectIdle(text: string): void {\n tmuxInjectPaste(this.session, text);\n this.sleep(this.pasteDelayMs);\n this.dismissMentionPopup();\n tmuxSendEnter(this.session);\n this.confirmSubmitted(text);\n }\n\n /**\n * Check the message left the composer, and try once more if it did not.\n *\n * A submitted message moves into the transcript; one that is still in the\n * composer was not sent. The retry re-runs the popup guard first, because the\n * popup is what eats an Enter — and if the text is still there after that,\n * nothing this bridge can do will send it, so it says so rather than leaving\n * the room to infer silence.\n */\n private confirmSubmitted(text: string): void {\n this.sleep(this.pasteDelayMs);\n if (!composerStillHolds(this.captureScreen(), text)) return;\n\n this.dismissMentionPopup();\n tmuxSendEnter(this.session);\n this.sleep(this.pasteDelayMs);\n if (!composerStillHolds(this.captureScreen(), text)) return;\n\n this.onUndelivered(text);\n }\n\n /**\n * Close the @-mention popup before submitting, if the injection opened it.\n *\n * Codex classifies incoming bytes as a paste or as typing partly on timing,\n * so a payload we sent as a bracketed paste can still be read as typing on a\n * loaded machine. When that happens a `@` opens the mention popup, and Enter\n * there *inserts the highlighted completion* instead of submitting. On\n * 2026-09-10 that turned a room message reading \"sooo @all\" into\n * \"sooo @Openai-Templates\" — the top hit was an installed Codex plugin — and\n * left it unsent in the composer, so the next message appended to the same\n * stuck line and the agent looked like it was ignoring the operator.\n *\n * Escape closes the popup and leaves the composed text untouched, so the\n * Enter that follows submits what we actually injected.\n *\n * Only ever sent when the popup is on screen: Escape is also Codex's\n * interrupt, so an unconditional one would cancel a turn in progress.\n */\n private dismissMentionPopup(): void {\n if (!codexMentionPopupIsOpen(this.captureScreen())) return;\n tmuxSendKey(this.session, \"Escape\");\n this.sleep(this.keystrokeDelayMs);\n }\n\n /**\n * Inject while the user is typing:\n * 1. Ctrl+U — cut to beginning of line (into kill buffer)\n * 2. Bracketed paste our text + Enter\n * 3. Ctrl+Y — yank user's text back from kill buffer\n */\n private injectWhileTyping(text: string): void {\n // Cut user's current input\n tmuxSendKey(this.session, \"C-u\");\n this.sleep(this.keystrokeDelayMs);\n\n // Inject our event via bracketed paste\n tmuxInjectPaste(this.session, text);\n this.sleep(this.pasteDelayMs);\n this.dismissMentionPopup();\n tmuxSendEnter(this.session);\n this.confirmSubmitted(text);\n this.sleep(this.keystrokeDelayMs);\n\n // Restore user's text\n tmuxSendKey(this.session, \"C-y\");\n }\n\n /**\n * Add to queue and start polling if not already.\n *\n * With `dedupe`, identical pending text is dropped: a caller that retries a\n * delivery it never saw land (the invite loop) would otherwise stack up N\n * copies of the same prompt, all flushing at once when the TUI frees up.\n *\n * Off by default, and deliberately so — room events must never be collapsed.\n * Two formatted events carry distinct `#ref`s so they should differ anyway,\n * but \"should\" is not a guarantee worth silently dropping a message on.\n */\n private enqueue(text: string, dedupe: boolean): void {\n if (dedupe && this.queue.includes(text)) return;\n this.queue.push(text);\n this.startPolling();\n }\n\n /** Start the polling timer to drain queued events. */\n private startPolling(): void {\n if (this.pollTimer || this.stopped) return;\n this.pollTimer = setInterval(() => this.drainQueue(), this.pollIntervalMs);\n }\n\n /** Stop the polling timer. */\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Try to drain one queued event if the state is safe.\n * Drains one at a time — each injection may trigger streaming,\n * so the next poll re-checks state before injecting more.\n */\n private drainQueue(): void {\n if (this.queue.length === 0) {\n this.stopPolling();\n return;\n }\n\n const state = this.detectState();\n if (state === \"idle\" || state === \"typing\") {\n const text = this.queue.shift()!;\n\n if (state === \"idle\") {\n this.injectIdle(text);\n } else {\n this.injectWhileTyping(text);\n }\n\n if (this.queue.length === 0) {\n this.stopPolling();\n }\n }\n // else: still blocked, keep polling\n }\n\n /** Cleanup. */\n stop(): void {\n this.stopped = true;\n this.stopPolling();\n this.queue.length = 0;\n }\n\n /** Synchronous sleep — only used for tiny keystroke delays. */\n private sleep(ms: number): void {\n if (ms <= 0) return;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n }\n}\n\n// ── State detection heuristics ──────────────────────────────────────────────\n\n/**\n * Detect Codex TUI state from capture-pane output lines.\n * Exported separately so it can be unit-tested without tmux.\n *\n * Codex's TUI (Ratatui-based) uses an inline viewport. The screen shows:\n * - Conversation history in the top area\n * - Status indicator (\"Working (12s * esc to interrupt)\") when streaming\n * - Approval overlay when tool/patch approval needed\n * - Composer input area at the bottom\n *\n * Detection priority: approval > streaming > idle/typing > unknown\n */\n/**\n * Case-INSENSITIVE match against the UI-chrome patterns.\n *\n * Codex has changed the casing of its own hint text between releases: the\n * dialog reads \"Press enter to confirm or esc to cancel\" in v0.153.4 where an\n * earlier build capitalised Enter and Esc. A case-sensitive miss here is the\n * dangerous direction — the screen falls through to \"idle\" and the bridge\n * bracket-pastes a room event plus Enter into a live approval dialog, choosing\n * an answer for a question the agent never saw. A false positive only queues\n * the event for the drain loop to retry, so lean toward matching.\n */\nfunction matchesAny(text: string, patterns: Array<string | RegExp>): boolean {\n const haystack = text.toLowerCase();\n for (const pattern of patterns) {\n if (typeof pattern === \"string\") {\n if (haystack.includes(pattern.toLowerCase())) return true;\n } else if (pattern.test(text) || new RegExp(pattern.source, pattern.flags.includes(\"i\") ? pattern.flags : pattern.flags + \"i\").test(text)) {\n return true;\n }\n }\n return false;\n}\n\nexport function detectCodexStateFromLines(lines: string[]): CodexTuiState {\n if (lines.length === 0) return \"unknown\";\n\n // Work with the last ~20 lines (visible bottom of screen)\n const tail = lines.slice(-20);\n const tailText = tail.join(\"\\n\");\n\n // 1. Not-yet-usable screens — highest priority. A login or onboarding\n // screen has no composer, so \"no approval, no spinner ⇒ idle\" would\n // otherwise paste straight into a menu.\n if (matchesAny(tailText, BLOCKED_PATTERNS)) return \"blocked\";\n\n // 2. Approval overlay\n if (matchesAny(tailText, APPROVAL_PATTERNS)) return \"approval\";\n\n // 3. Streaming — agent is working\n for (const pattern of STREAMING_PATTERNS) {\n if (pattern.test(tailText)) return \"streaming\";\n }\n\n // Check for spinner characters in the last few lines\n const lastFew = tail.slice(-5).join(\"\");\n for (const ch of SPINNER_CHARS) {\n if (lastFew.includes(ch)) return \"streaming\";\n }\n\n // 4. Idle/Typing — look for the composer input area at the bottom.\n // Codex renders the composer as the last interactive element.\n // When idle, the bottom lines contain just the placeholder or empty input.\n // When typing, the bottom lines contain user-entered text.\n //\n // Heuristic: if none of the blocking states are detected (approval,\n // streaming), and the screen has content, assume the composer is\n // available. Check the last non-empty line for signs of user input.\n //\n // This is intentionally permissive — the worst case for a false\n // \"idle\" is that the injected text arrives during an unexpected\n // state and queues up in the input buffer harmlessly.\n const nonEmpty = tail.filter((l) => l.trim().length > 0);\n if (nonEmpty.length > 0) {\n // Look for signs that the screen is showing normal conversation + composer.\n // If we didn't match approval or streaming above, the composer is likely visible.\n // We need to distinguish idle (empty composer) from typing (text in composer).\n //\n // Codex shows a cursor line at the very bottom. Without empirical data on\n // exact patterns, we check if the last non-empty line looks like user input\n // (not part of conversation output which typically has structure like timestamps,\n // tool names, or markdown formatting).\n //\n // For now: if no blocking state is detected, return \"idle\" as the safe\n // injectable state. The bracketed paste approach is resilient enough that\n // injecting into a \"typing\" state via the idle path still works (text gets\n // appended to whatever the user was typing, and Enter submits all of it).\n return \"idle\";\n }\n\n return \"unknown\";\n}\n\n/**\n * Pull Codex's status line out of a screen capture.\n *\n * Exported separately so it can be tested without tmux. Returns null when\n * nothing is running — the room shows the state glyph alone in that case.\n */\nexport function extractCodexActivityLabel(lines: string[]): string | null {\n // Scan bottom-up: the status line lives just under the transcript, and\n // earlier conversation text can quote the same words.\n for (const line of lines.slice(-12).reverse()) {\n const m = /(Working|Thinking|Compacting|Reviewing)\\b[^)\\n]*/.exec(line);\n if (!m) continue;\n // Trim Codex's interrupt hint — it is UI chrome, not progress.\n const label = m[0].replace(/\\s*[•·]?\\s*esc to interrupt.*$/i, \"\").trim();\n if (label) return label.slice(0, 120);\n }\n return null;\n}\n","/**\n * Codex launch wiring — how apiary declares itself to a Codex session.\n *\n * apiary needs two things from a Codex session: its MCP server declared, and\n * its own tools auto-approved (an agent spawned to sit in a room cannot join\n * one if every join_room call blocks on a keypress nobody is there to press).\n *\n * Both are declared in a Codex *config profile* — `<CODEX_HOME>/<name>.config.toml`,\n * layered over the user's base config by `codex --profile <name>`. apiary owns\n * the `apiary-<agent>` profile name and deletes the file when the agent stops.\n *\n * Two alternatives were tried and rejected:\n *\n * - A throwaway CODEX_HOME (what apiary used to do). CODEX_HOME is where\n * `auth.json` lives, so a logged-in user still landed on the OAuth sign-in\n * screen and the agent never reached a prompt; it also silently dropped\n * `model`, reasoning effort and project trust. Copying auth.json in only\n * moves the problem, because Codex rotates the refresh token and writes it\n * back to CODEX_HOME — the refreshed credentials would die with the temp\n * directory while the user's real ~/.codex kept a token already spent.\n *\n * - `codex -c key=value` overrides. Correct in principle, but auto-approval\n * is per tool, so it needs ~17 flags — a 1.2KB command line, which is\n * typed into the pane a keystroke at a time and gets truncated mid-quote.\n *\n * A profile keeps the user's real Codex home (credentials, model, project\n * trust all intact) and the launch command stays one short line.\n */\n\nimport { existsSync, mkdirSync, readdirSync, realpathSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nimport { RUNTIME_TOOL_NAMES } from \"../../agent/mcp/runtime.js\";\nimport { wantsUnattendedAgents } from \"../approvals.js\";\n\n/** Timeouts for apiary's own MCP server, in seconds. */\nconst STARTUP_TIMEOUT_SEC = 15;\nconst TOOL_TIMEOUT_SEC = 60;\n\nconst PROFILE_PREFIX = \"apiary-\";\n\n/** The user's real Codex home — honours CODEX_HOME so nested launches behave. */\nexport function userCodexHome(env: NodeJS.ProcessEnv = process.env): string {\n const fromEnv = env.CODEX_HOME?.trim();\n return fromEnv && fromEnv.length > 0 ? fromEnv : join(homedir(), \".codex\");\n}\n\n/**\n * How Codex should treat calls to apiary's own MCP tools.\n *\n * `\"approve\"` means \"run without asking\". Anyone who would rather confirm each\n * call can set APIARY_CODEX_TOOL_APPROVAL (Codex's own value, e.g. \"ask\") —\n * with the caveat that a background-spawned agent then cannot join a room\n * unattended, because nobody is watching its pane to press a key.\n */\nexport function codexToolApprovalMode(env: NodeJS.ProcessEnv = process.env): string {\n const override = env.APIARY_CODEX_TOOL_APPROVAL?.trim();\n if (override && override.length > 0) return override;\n return wantsUnattendedAgents(env) ? \"approve\" : \"ask\";\n}\n\n\n\n/**\n * Profile name for an agent. Codex resolves this to a filename, so keep it to\n * characters that cannot escape the Codex home or confuse the CLI.\n */\nexport function codexProfileName(agentName: string): string {\n const safe = agentName.replace(/[^A-Za-z0-9_-]/g, \"-\").slice(0, 64);\n // A name that sanitises down to nothing but separators is degenerate, and\n // would have every such agent colliding on the same profile.\n return `${PROFILE_PREFIX}${/[A-Za-z0-9]/.test(safe) ? safe : \"agent\"}`;\n}\n\n/** Render a TOML string value: `\"http://host/path\"`. */\nfunction tomlString(value: string): string {\n return JSON.stringify(value);\n}\n\n/**\n * The profile body: apiary's MCP server, and per-tool auto-approval.\n *\n * Per tool, not per server: Codex accepts `approval_mode` on the server table\n * and then ignores it. Only `mcp_servers.<server>.tools.<tool>` is honoured —\n * the shape Codex itself writes when a human picks \"Always allow\".\n *\n * Scoped deliberately: shell commands, file writes and every other MCP server\n * still go through Codex's normal approval flow.\n *\n * Unattended, it also clears the two screens Codex shows BEFORE its composer,\n * each of which is a permanent stall in a pane nobody watches:\n * - \"Trust this folder?\" — answered by a `[projects.\"<dir>\"]` trust entry for the\n * agent's own working directory. It lives in this profile, so the user's real\n * config.toml is never edited and the grant goes away with the agent. Same\n * reading of intent as the Claude trust pre-accept: the user pointed an agent here.\n * - \"Update available · Update now?\" — `check_for_update_on_startup = false`.\n */\nexport function renderCodexProfile(\n mcpUrl: string,\n approvalMode: string = codexToolApprovalMode(),\n unattended: boolean = wantsUnattendedAgents(),\n trustDirs: string[] = [],\n): string {\n const blocks = [\n // Top-level keys must precede every table header, or TOML parses them as\n // members of the last table.\n ...(unattended ? [[\n \"# Written by apiary. Layered over your own config; deleted when the agent stops.\",\n \"#\",\n \"# No approval prompts: nobody is watching this pane, so a prompt is a\",\n \"# permanent stall. Commands still run inside Codex's sandbox — anything\",\n \"# it will not allow returns a failure to the model instead of asking.\",\n 'approval_policy = \"never\"',\n \"# The update prompt is a menu in front of the composer: another stall.\",\n \"check_for_update_on_startup = false\",\n ].join(\"\\n\")] : []),\n [\n ...(unattended ? [] : [\"# Written by apiary. Layered over your own config; deleted when the agent stops.\"]),\n \"[mcp_servers.apiary]\",\n `url = ${tomlString(mcpUrl)}`,\n `startup_timeout_sec = ${STARTUP_TIMEOUT_SEC}`,\n `tool_timeout_sec = ${TOOL_TIMEOUT_SEC}`,\n ].join(\"\\n\"),\n ...RUNTIME_TOOL_NAMES.map((tool) => [\n `[mcp_servers.apiary.tools.${tool}]`,\n `approval_mode = ${tomlString(approvalMode)}`,\n ].join(\"\\n\")),\n ...(unattended ? [...new Set(trustDirs)].map((dir) => [\n `[projects.${tomlString(dir)}]`,\n 'trust_level = \"trusted\"',\n ].join(\"\\n\")) : []),\n ];\n\n return blocks.join(\"\\n\\n\") + \"\\n\";\n}\n\n/** Absolute path of an agent's profile file. */\nexport function codexProfilePath(agentName: string, codexHome: string = userCodexHome()): string {\n return join(codexHome, `${codexProfileName(agentName)}.config.toml`);\n}\n\n/**\n * Write the agent's profile and return the launch command.\n *\n * `extraArgs` are the user's own passthrough flags and come last, so\n * `apiary codex bee --model gpt-5.6-terra` still wins over the profile.\n */\nexport function prepareCodexLaunch(\n agentName: string,\n mcpUrl: string,\n extraArgs: string[] = [],\n codexHome: string = userCodexHome(),\n cwd: string = process.cwd(),\n): { command: string; profilePath: string } {\n mkdirSync(codexHome, { recursive: true });\n\n const profilePath = codexProfilePath(agentName, codexHome);\n writeFileSync(profilePath, renderCodexProfile(mcpUrl, undefined, undefined, trustDirsFor(cwd)), { mode: 0o600 });\n\n const command = [\"codex\", \"--profile\", codexProfileName(agentName), ...extraArgs].join(\" \");\n return { command, profilePath };\n}\n\n/** Remove an agent's profile. Never throws. */\nexport function clearCodexProfile(agentName: string, codexHome: string = userCodexHome()): void {\n try {\n rmSync(codexProfilePath(agentName, codexHome), { force: true });\n } catch {\n /* already gone */\n }\n}\n\n/**\n * Delete apiary profiles left behind by agents that are no longer running —\n * a crashed or SIGKILLed runtime never reaches its cleanup path, and these\n * would otherwise accumulate in the user's Codex home forever.\n */\nexport function pruneCodexProfiles(\n liveAgentNames: readonly string[],\n codexHome: string = userCodexHome(),\n): number {\n if (!existsSync(codexHome)) return 0;\n\n const live = new Set(liveAgentNames.map((name) => `${codexProfileName(name)}.config.toml`));\n let removed = 0;\n\n try {\n for (const entry of readdirSync(codexHome)) {\n if (!entry.startsWith(PROFILE_PREFIX) || !entry.endsWith(\".config.toml\")) continue;\n if (live.has(entry)) continue;\n try {\n rmSync(join(codexHome, entry), { force: true });\n removed++;\n } catch {\n /* leave it for next time */\n }\n }\n } catch {\n /* unreadable home — nothing to prune */\n }\n\n return removed;\n}\n\n/** The spellings Codex may key a directory's trust by: as given, and with symlinks resolved. */\nexport function trustDirsFor(cwd: string): string[] {\n const dirs = [resolve(cwd)];\n try { dirs.push(realpathSync(cwd)); } catch { /* keep the resolved form */ }\n return [...new Set(dirs)];\n}\n","/**\n * This package's version, for the help banner and the update check.\n *\n * Its own module because both index.ts and help.ts need it, and help.ts must\n * be importable without pulling in index.ts (which runs main() on import).\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** Read a top-level field from this package's package.json. Null on failure. */\nexport function readPackageField(field: string): string | null {\n try {\n const dir = dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(readFileSync(resolve(dir, \"../../package.json\"), \"utf-8\"));\n return typeof pkg[field] === \"string\" ? pkg[field] : null;\n } catch {\n return null;\n }\n}\n\nexport function getVersion(): string {\n return readPackageField(\"version\") ?? process.env.npm_package_version ?? \"unknown\";\n}\n","/**\n * `apiary --help` and `apiary examples`.\n *\n * Split out of index.ts so it can be imported and asserted directly. The\n * drift test used to spawn the built binary, which made it depend on `dist/`\n * being current — and `make release` runs the tests BEFORE the build, so any\n * help change broke the release until dist was rebuilt by hand. index.ts\n * cannot be imported instead: it calls main() at module top level, so\n * importing it runs the CLI.\n */\n\nimport { getVersion } from \"./version.js\";\n\nexport function printUsage(stream: typeof console.log = console.log): void {\n const color = process.stdout.isTTY;\n const Y = color ? \"\\x1b[33m\" : \"\"; // yellow\n const C = color ? \"\\x1b[36m\" : \"\"; // cyan\n const G = color ? \"\\x1b[32m\" : \"\"; // green\n const D = color ? \"\\x1b[2m\" : \"\"; // dim\n const B = color ? \"\\x1b[1m\" : \"\"; // bold\n const R = color ? \"\\x1b[0m\" : \"\"; // reset\n\n const COL = 40;\n const ansi = /\\x1b\\[[0-9;]*m/g;\n const row = (cmd: string, desc: string) => {\n const visible = cmd.replace(ansi, \"\").length;\n return ` ${cmd}${\" \".repeat(Math.max(2, COL - visible))}${D}${desc}${R}`;\n };\n\n stream(\"\");\n stream(` ${Y}${B}apiary${R} ${D}v${getVersion()} — shared rooms for AI agents${R}`);\n stream(\"\");\n stream(` ${G}${B}Workspace${R}`);\n stream(row(`${C}apiary room create${R}`, \"Create a workspace + invite participants\"));\n stream(row(`${C}apiary room ${Y}<name>${R} ${D}[--share]${R}`, \"Start a workspace + join\"));\n stream(row(`${C}apiary room resume ${Y}<name>${R}`, \"Rejoin your workspace\"));\n stream(row(`${C}apiary room list${R}`, \"List your workspaces\"));\n stream(\"\");\n stream(` ${G}${B}Who can reach the room${R}`);\n stream(row(`${D}--bind ${Y}tailscale${R}`, \"Your devices anywhere, via Tailscale — not the local network\"));\n stream(row(`${D}--bind ${Y}lan${R}`, \"Anyone on the same wifi\"));\n stream(row(`${D}--share${R}`, \"A public cloudflared URL, for people off your tailnet\"));\n stream(` ${D}room create asks this if you don't pass it. Default: this machine only.${R}`);\n stream(\"\");\n stream(` ${G}${B}Participants${R} ${D}(each person connects their own agents)${R}`);\n stream(row(`${C}apiary join ${Y}<url>${R} ${D}[${Y}<name>${R}${D}]${R}`, \"Join a workspace\"));\n stream(row(`${C}apiary claude ${Y}<name>${R} ${D}[--admin]${R}`, \"Connect Claude Code to the room\"));\n stream(row(`${C}apiary codex ${Y}<name>${R} ${D}[--admin]${R}`, \"Connect Codex to the room\"));\n stream(row(`${D}--mode ${Y}standby${R}`, \"Quiet until @mentioned, pinged or whispered to\"));\n stream(` ${D}In room create, suffix the alias instead: ${R}${C}bee:standby${R}${D}. The first agent${R}`);\n stream(` ${D}listens to everything, the rest go standby — one remark, one reply.${R}`);\n stream(\"\");\n stream(` ${G}${B}In the room${R}`);\n stream(row(`${C}/watch ${Y}<agent>${R}`, \"Open that agent's terminal in a new window (Ctrl+<n> too)\"));\n stream(row(`${C}/setmode ${Y}<agent> <mode>${R}`, \"Change who an agent listens to, live\"));\n stream(row(`${C}/ping ${Y}<agent>${R}`, \"Ask for a one-line status\"));\n stream(` ${D}The strip shows each agent as ✓ idle, … working, or ⏸ needs you —${R}`);\n stream(` ${D}\"needs you\" means it is stuck on something only you can clear.${R}`);\n stream(\"\");\n stream(row(`${C}apiary ps${R}`, \"List active rooms + agents\"));\n stream(row(`${C}apiary stop ${D}[${Y}<name>${R}${D} | --all]${R}`, \"Stop agents + rooms\"));\n stream(row(`${C}apiary examples${R}`, \"Examples + workflows\"));\n stream(\"\");\n}\n\nexport function printExamples(): void {\n const color = process.stdout.isTTY;\n const Y = color ? \"\\x1b[33m\" : \"\";\n const C = color ? \"\\x1b[36m\" : \"\";\n const G = color ? \"\\x1b[32m\" : \"\";\n const D = color ? \"\\x1b[2m\" : \"\";\n const B = color ? \"\\x1b[1m\" : \"\";\n const R = color ? \"\\x1b[0m\" : \"\";\n\n const log = console.log.bind(console);\n\n log(\"\");\n log(` ${Y}${B}apiary examples${R}`);\n log(\"\");\n\n // ── Create a workspace\n log(` ${G}${B}Create a workspace${R}`);\n log(` ${C}apiary room create${R} ${D}# guided setup — name it, invite participants${R}`);\n log(` ${C}apiary room ${Y}sprint-42${R} ${D}# quick start — host + join immediately${R}`);\n log(` ${C}apiary room ${Y}sprint-42${R} ${D}--bind tailscale${R} ${D}# reachable from your phone, over Tailscale only${R}`);\n log(` ${C}apiary room ${Y}sprint-42${R} ${D}--share${R} ${D}# same, with a public tunnel URL${R}`);\n log(` ${D}Ctrl+C leaves the server running — rejoin anytime:${R}`);\n log(` ${C}apiary room resume ${Y}sprint-42${R}`);\n log(\"\");\n\n // ── Join a workspace\n log(` ${G}${B}Join a workspace${R} ${D}(each person connects their own agents)${R}`);\n log(` ${C}apiary join ${Y}<url>${R} ${D}# join as a participant${R}`);\n log(` ${C}apiary join ${Y}<url>${R} ${D}--guest${R} ${D}# join read-only${R}`);\n log(` ${C}apiary claude ${Y}Cleo${R} ${D}--admin${R} ${D}# connect your Claude Code agent${R}`);\n log(` ${C}apiary codex ${Y}Rex${R} ${D}# connect your Codex agent${R}`);\n log(` ${C}apiary codex ${Y}Rex${R} ${D}--mode standby${R} ${D}# quiet until @mentioned, pinged or whispered${R}`);\n log(` ${D}Tell your agent the room URL — it joins via apiary__join_room.${R}`);\n log(\"\");\n\n // ── Authority / roles\n log(` ${G}${B}Authority${R} ${D}admin > product_owner > member > guest${R}`);\n log(` ${C}/promote ${Y}<name>${R} ${D}# elevate to product owner (can manage members + modes)${R}`);\n log(` ${C}/demote ${Y}<name>${R} ${D}# drop product owner back to member${R}`);\n log(` ${C}/mute ${Y}<name>${R} ${D}# demote to guest (read-only)${R}`);\n log(` ${C}/unmute ${Y}<name>${R} ${D}# restore to member${R}`);\n log(` ${C}/kick ${Y}<name>${R} ${D}# remove from the room (admin only)${R}`);\n log(` ${C}/share ${D}--as member${R} ${D}# generate a share link at a specific tier${R}`);\n log(\"\");\n\n // ── TUI commands\n log(` ${G}${B}TUI commands${R}`);\n log(` ${C}/who${R} ${D}list participants with their roles${R}`);\n log(` ${C}/watch ${Y}<name>${R} ${D}open that agent's terminal in a new window (or Ctrl+<n>)${R}`);\n log(` ${C}/ping ${Y}<name>${R} ${D}ping for a status check${R}`);\n log(` ${C}/setmode ${Y}<n> <m>${R} ${D}set engagement mode (admin / product owner)${R}`);\n log(` ${C}/share${R} ${D}generate share links${R}`);\n log(` ${C}/tunnel${R} ${D}start a cloudflared tunnel mid-session (admin)${R}`);\n log(` ${C}/clear${R} ${D}wipe room history (admin)${R}`);\n log(` ${C}/sound${R} ${D}toggle notification sounds${R}`);\n log(` ${C}/leave${R} ${D}disconnect${R}`);\n log(\"\");\n\n // ── Messaging\n log(` ${G}${B}Messaging${R} ${D}(agent tool params)${R}`);\n log(` ${D}Whisper — visible only to named recipients:${R}`);\n log(` ${C}apiary__send_message(room, content, null, ${Y}[\"Alice\", \"Bob\"]${R}${C})${R}`);\n log(` ${D}Attachments — local file or image:${R}`);\n log(` ${C}apiary__send_message(room, content, null, null, ${Y}[{ type: \"path\", path: \"...\" }]${R}${C})${R}`);\n log(\"\");\n\n // ── Sessions\n log(` ${G}${B}Sessions${R}`);\n log(` ${C}apiary ps${R} ${D}# list rooms + agents with join links${R}`);\n log(` ${C}apiary stop ${Y}Cleo${R} ${D}# stop one agent${R}`);\n log(` ${C}apiary stop ${D}--all${R} ${D}# stop everything${R}`);\n log(\"\");\n}\n","#!/usr/bin/env node\n\n/** apiary CLI — shared rooms for AI agents. */\n\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { serve, listRoomSessions } from \"./serve.js\";\nimport { join } from \"./join.js\";\nimport { runClaude, stopClaude, listClaudeSessions } from \"./claude/run.js\";\nimport { runOpencode, stopOpencode, listOpencodeSessions } from \"./opencode/run.js\";\nimport { runCodex, stopCodex, listCodexSessions } from \"./codex/run.js\";\nimport { buildShareUrl } from \"./auth.js\";\nimport { printUsage, printExamples } from \"./help.js\";\nimport { getVersion, readPackageField } from \"./version.js\";\nimport { parseEngagementMode } from \"../agent/engagement.js\";\nimport { resolveBindAddress } from \"./bind.js\";\nimport { printUpdateNotice, checkForUpdate } from \"./update.js\";\n\nconst args = process.argv.slice(2);\n\nfunction getFlag(name: string, arr: string[] = args): string | undefined {\n const idx = arr.indexOf(`--${name}`);\n if (idx === -1) return undefined;\n const value = arr[idx + 1];\n if (value === undefined || value.startsWith(\"--\")) return undefined;\n return value;\n}\n\n/** Collect all values for a repeatable flag (e.g. --join url1 --join url2). */\nfunction getAllFlags(name: string, arr: string[] = args): string[] {\n const results: string[] = [];\n const flag = `--${name}`;\n for (let i = 0; i < arr.length; i++) {\n if (arr[i] === flag && arr[i + 1] && !arr[i + 1].startsWith(\"--\")) {\n results.push(arr[i + 1]);\n }\n }\n return results;\n}\n\n/**\n * Resolve `--bind` for a command, exiting with an explanation if it cannot be\n * satisfied. Returns undefined when the flag was not passed.\n */\nfunction bindFlag(args: string[]): string | undefined {\n const spec = getFlag(\"bind\", args);\n if (!spec) return undefined;\n const res = resolveBindAddress(spec);\n if (!res.ok) {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n if (res.note) console.log(` Listening on ${res.address} — ${res.note}`);\n return res.address;\n}\n\nasync function main(): Promise<void> {\n // ── apiary examples ──────────────────────────────────────────────────\n if (args[0] === \"examples\") {\n printExamples();\n return;\n }\n\n // ── --help anywhere ────────────────────────────────────────────────────\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n printUsage();\n return;\n }\n\n // ── --version / -v ──────────────────────────────────────────────────\n if (args.includes(\"--version\") || args.includes(\"-v\") || args[0] === \"version\") {\n try {\n const { createRequire } = await import(\"node:module\");\n const require = createRequire(import.meta.url);\n const pkg = require(\"../../package.json\");\n console.log(pkg.version);\n } catch {\n console.log(\"unknown\");\n }\n return;\n }\n\n // ── apiary update — git pull + build (or npm fallback) ──────────────\n if (args[0] === \"update\") {\n const { runUpdate } = await import(\"./update.js\");\n const targetVersion = args[1] && !args[1].startsWith(\"--\") ? args[1] : undefined;\n await runUpdate(targetVersion);\n return;\n }\n\n // ── Version check (non-blocking) ──────────────────────────────────────\n printUpdateNotice();\n checkForUpdate();\n\n // ── apiary stop [<name> | --all] ──────────────────────────────────────\n if (args[0] === \"stop\") {\n const rest = args.slice(1);\n const all = rest.includes(\"--all\");\n // Positional name or --name flag\n const positional = rest.find((a) => !a.startsWith(\"--\"));\n const name = getFlag(\"name\", rest) ?? positional;\n\n const { roomStop } = await import(\"./room.js\");\n if (all) {\n // Stop all rooms first (each roomStop also kills its agent tmux sessions).\n // Pass the pre-loaded session to avoid O(N²) disk scans.\n for (const r of listRoomSessions()) {\n try { await roomStop(r.roomName, { session: r, quietIfDead: true }); } catch { /* keep going */ }\n }\n // Then stop any remaining standalone agent runtimes (claude, codex, opencode).\n await stopClaude(undefined, true);\n await stopCodex(undefined, true);\n await stopOpencode(undefined, true);\n return;\n }\n if (name) {\n // If the name matches a room, stop the room (agents + server). Otherwise treat as agent name.\n const room = listRoomSessions().find((s) => s.roomName === name);\n if (room) { await roomStop(name, { session: room }); return; }\n\n // Match against agent runtimes by name.\n if (listCodexSessions().some((s) => s.agentName === name)) {\n await stopCodex(name, false); return;\n }\n if (listOpencodeSessions().some((s) => s.agentName === name)) {\n await stopOpencode(name, false); return;\n }\n }\n await stopClaude(name, false);\n return;\n }\n\n // ── apiary ps ────────────────────────────────────────────────────────────\n if (args[0] === \"ps\") {\n const rooms = listRoomSessions();\n const claudeAgents = listClaudeSessions().map((s) => ({ runtime: \"claude\", agentName: s.agentName, pid: s.pid }));\n const codexAgents = listCodexSessions().map((s) => ({ runtime: \"codex\", agentName: s.agentName, pid: s.pid }));\n const opencodeAgents = listOpencodeSessions().map((s) => ({ runtime: \"opencode\", agentName: s.agentName, pid: s.pid }));\n const agents = [...claudeAgents, ...codexAgents, ...opencodeAgents];\n\n if (rooms.length === 0 && agents.length === 0) {\n console.log(\"No active sessions.\");\n return;\n }\n\n for (const r of rooms) {\n let alive = false;\n try { process.kill(r.pid, 0); alive = true; } catch { /* dead */ }\n if (!alive) continue;\n const joinUrl = buildShareUrl(\n r.publicUrl !== r.serverUrl ? r.publicUrl : r.serverUrl,\n r.memberToken,\n );\n console.log(` \\x1b[1m${r.roomName}\\x1b[0m \\x1b[2m(room, pid ${r.pid})\\x1b[0m`);\n console.log(` \\x1b[2mJoin:\\x1b[0m \\x1b[36mapiary join\\x1b[0m ${joinUrl}`);\n }\n\n for (const s of agents) {\n let alive = false;\n try { process.kill(s.pid, 0); alive = true; } catch { /* dead */ }\n const status = alive ? \"running\" : \"stale\";\n console.log(` ${s.agentName} (${s.runtime}, pid ${s.pid}, ${status})`);\n }\n return;\n }\n\n // ── apiary room [create | resume | list | <name>] ───────────────────────\n if (args[0] === \"room\") {\n const sub = args[1];\n const roomArgs = args.slice(2);\n\n // apiary room create\n if (sub === \"create\") {\n const portStr = getFlag(\"port\", roomArgs);\n const port = portStr ? parseInt(portStr, 10) : undefined;\n const { roomCreate } = await import(\"./room.js\");\n await roomCreate({\n room: getFlag(\"room\", roomArgs) ?? roomArgs.find((a) => !a.startsWith(\"--\")),\n port,\n share: roomArgs.includes(\"--share\"),\n expose: roomArgs.includes(\"--expose\"),\n bind: bindFlag(roomArgs),\n foundation: !roomArgs.includes(\"--no-foundation\"),\n });\n return;\n }\n\n // apiary room resume <name>\n if (sub === \"resume\") {\n const name = roomArgs.find((a) => !a.startsWith(\"--\"));\n if (!name) {\n console.error(\"Usage: apiary room resume <name>\");\n process.exit(1);\n }\n const { roomResume } = await import(\"./room.js\");\n await roomResume(name);\n return;\n }\n\n // apiary room list\n if (sub === \"list\" || sub === \"ls\") {\n const { roomList } = await import(\"./room.js\");\n roomList();\n return;\n }\n\n // apiary room stop <name>\n if (sub === \"stop\") {\n const name = roomArgs.find((a) => !a.startsWith(\"--\"));\n if (!name) {\n console.error(\"Usage: apiary room stop <name>\");\n process.exit(1);\n }\n const { roomStop } = await import(\"./room.js\");\n await roomStop(name);\n return;\n }\n\n // apiary room <name> [<displayName>] — daemon-backed host+join (legacy compat)\n {\n const allRoomArgs = args.slice(1);\n const positionals = allRoomArgs.filter((a) => !a.startsWith(\"--\"));\n const roomName = positionals[0];\n const userName = positionals[1] ?? getFlag(\"name\", allRoomArgs);\n const portStr = getFlag(\"port\", allRoomArgs);\n const port = portStr ? parseInt(portStr, 10) : undefined;\n if (port !== undefined && (isNaN(port) || port < 0 || port > 65535)) {\n console.error(`Invalid port: ${portStr}`);\n process.exit(1);\n }\n\n // Daemon-backed so Ctrl+C only kills TUI\n const { spawnDaemonServer, isDaemonResult } = await import(\"./room.js\");\n const daemonRes = await spawnDaemonServer({\n room: roomName,\n port,\n share: allRoomArgs.includes(\"--share\"),\n expose: allRoomArgs.includes(\"--expose\"),\n bind: bindFlag(allRoomArgs),\n });\n if (!isDaemonResult(daemonRes)) {\n console.error(`Failed to start server: ${daemonRes.reason}`);\n if (daemonRes.portInUse) {\n console.error(`Tip: a previous room daemon is bound. Run 'apiary ps' to inspect, 'apiary stop --all' to free it.`);\n } else if (daemonRes.stderr?.trim()) {\n console.error(daemonRes.stderr);\n }\n process.exit(1);\n }\n const daemon = daemonRes;\n const adminJoinUrl = buildShareUrl(daemon.serverUrl, daemon.adminToken);\n const participantShareUrl = buildShareUrl(\n daemon.publicUrl !== daemon.serverUrl ? daemon.publicUrl : daemon.serverUrl,\n daemon.memberToken,\n );\n await join({ server: adminJoinUrl, name: userName, shareUrl: participantShareUrl });\n console.log(`\\n Server still running (room: ${daemon.roomName})`);\n console.log(` Rejoin: apiary room resume ${daemon.roomName}\\n`);\n return;\n }\n }\n\n // ── apiary mcp [<name>] [--admin] [--join <url>] ──────────────────────────\n if (args[0] === \"mcp\") {\n const restArgs = args.slice(1);\n const joinUrls = getAllFlags(\"join\", restArgs);\n const positionalName = restArgs.find((a, i) => {\n if (a.startsWith(\"--\")) return false;\n if (i > 0 && new Set([\"--name\", \"--join\"]).has(restArgs[i - 1])) return false;\n return true;\n });\n const name = getFlag(\"name\", restArgs) ?? positionalName;\n\n // Check for a pending invite dropped by `apiary room resume`\n const pendingUrls = [...joinUrls];\n if (name) {\n const { consumeInvite } = await import(\"./invites.js\");\n const inviteUrl = consumeInvite(name);\n if (inviteUrl) pendingUrls.push(inviteUrl);\n }\n\n const { runMcpServer } = await import(\"./mcp/run.js\");\n await runMcpServer({\n joinUrls: pendingUrls.length > 0 ? pendingUrls : undefined,\n name,\n admin: restArgs.includes(\"--admin\"),\n });\n return;\n }\n\n // ── apiary run <runtime> / apiary claude / apiary codex / apiary opencode\n const isRunAlias = args[0] === \"claude\" || args[0] === \"codex\" || args[0] === \"opencode\";\n if ((args[0] === \"run\" && (args[1] === \"claude\" || args[1] === \"opencode\" || args[1] === \"codex\")) || isRunAlias) {\n const runtime = isRunAlias ? args[0] : args[1];\n const restArgs = args.slice(isRunAlias ? 1 : 2);\n\n // Split on -- separator: apiary flags before, passthrough args after\n const ddIndex = restArgs.indexOf(\"--\");\n const apiaryArgs = ddIndex >= 0 ? restArgs.slice(0, ddIndex) : restArgs;\n const explicitExtra = ddIndex >= 0 ? restArgs.slice(ddIndex + 1) : [];\n\n // Known apiary flags — anything else gets forwarded to the underlying tool\n // --mode must be listed here or it would be forwarded to the underlying\n // CLI as an unknown flag, where claude/codex reject it.\n const KNOWN_FLAGS = new Set([\"--name\", \"--admin\", \"--headless\", \"--resume\", \"--join\", \"--background\", \"--mode\"]);\n const unknownArgs: string[] = [];\n for (let i = 0; i < apiaryArgs.length; i++) {\n const arg = apiaryArgs[i];\n if (arg.startsWith(\"--\") && !KNOWN_FLAGS.has(arg)) {\n unknownArgs.push(arg);\n // If the next arg isn't a flag, it's probably a value — forward it too\n if (i + 1 < apiaryArgs.length && !apiaryArgs[i + 1].startsWith(\"--\")) {\n unknownArgs.push(apiaryArgs[++i]);\n }\n }\n }\n const extraArgs = [...unknownArgs, ...explicitExtra];\n\n const joinUrls = getAllFlags(\"join\", apiaryArgs);\n\n // First positional arg (not a flag, not consumed by a known flag) is the name\n const positionalName = apiaryArgs.find((a, i) => {\n if (a.startsWith(\"--\")) return false;\n // Check if previous arg is a flag that takes a value\n if (i > 0 && KNOWN_FLAGS.has(apiaryArgs[i - 1])) return false;\n return true;\n });\n\n const runtimeOptions = {\n joinUrls: joinUrls.length > 0 ? joinUrls : undefined,\n name: getFlag(\"name\", apiaryArgs) ?? positionalName,\n admin: apiaryArgs.includes(\"--admin\"),\n headless: apiaryArgs.includes(\"--headless\"),\n resume: apiaryArgs.includes(\"--resume\"),\n background: apiaryArgs.includes(\"--background\"),\n mode: parseEngagementMode(getFlag(\"mode\", apiaryArgs)),\n extraArgs,\n };\n\n if (runtime === \"claude\") {\n await runClaude(runtimeOptions);\n } else if (runtime === \"opencode\") {\n await runOpencode(runtimeOptions);\n } else {\n await runCodex(runtimeOptions);\n }\n return;\n }\n\n // ── apiary join <url> [<name>] ──────────────────────────────────────────\n if (args[0] === \"join\") {\n const server = args[1];\n if (!server || server.startsWith(\"--\")) {\n console.error(\"Usage: apiary join <url> [<name>] [--guest] [--headless]\");\n process.exit(1);\n }\n // Second positional arg is the name\n const joinName = args[2] && !args[2].startsWith(\"--\") ? args[2] : getFlag(\"name\");\n await join({\n server,\n name: joinName,\n guest: args.includes(\"--guest\"),\n headless: args.includes(\"--headless\"),\n });\n return;\n }\n\n // ── apiary serve ───────────────────────────────────────────────────────\n if (args[0] === \"serve\") {\n const portStr = getFlag(\"port\");\n const port = portStr ? parseInt(portStr, 10) : undefined;\n if (port !== undefined && (isNaN(port) || port < 0 || port > 65535)) {\n console.error(`Invalid port: ${portStr}`);\n process.exit(1);\n }\n const shareTtlStr = getFlag(\"share-token-ttl\");\n const shareTtlMs = shareTtlStr ? parseInt(shareTtlStr, 10) : undefined;\n await serve({\n room: getFlag(\"room\"),\n port,\n share: args.includes(\"--share\"),\n headless: args.includes(\"--headless\"),\n expose: args.includes(\"--expose\"),\n bind: bindFlag(args),\n corsOrigins: getAllFlags(\"cors-origin\"),\n shareTtlMs,\n });\n return;\n }\n\n // ── apiary (bare) — host + join ────────────────────────────────────────\n if (args.length === 0 || args[0]?.startsWith(\"--\")) {\n const portStr = getFlag(\"port\");\n const port = portStr ? parseInt(portStr, 10) : undefined;\n if (port !== undefined && (isNaN(port) || port < 0 || port > 65535)) {\n console.error(`Invalid port: ${portStr}`);\n process.exit(1);\n }\n const result = await serve({\n room: getFlag(\"room\"),\n port,\n share: args.includes(\"--share\"),\n quiet: true,\n expose: args.includes(\"--expose\"),\n bind: bindFlag(args),\n corsOrigins: getAllFlags(\"cors-origin\"),\n });\n\n // Host joins locally as admin using the admin share token\n const adminJoinUrl = buildShareUrl(result.serverUrl, result.adminToken);\n const participantShareUrl = buildShareUrl(\n result.publicUrl !== result.serverUrl ? result.publicUrl : result.serverUrl,\n result.memberToken,\n );\n\n await join({\n server: adminJoinUrl,\n name: getFlag(\"name\"),\n shareUrl: participantShareUrl,\n });\n return;\n }\n\n // ── Unknown command ────────────────────────────────────────────────────\n console.error(`Unknown command: ${args[0]}\\n`);\n printUsage(console.error);\n process.exit(1);\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"],"mappings":"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","names":["writeFileSync","existsSync","readFileSync","readdirSync","renameSync","join","homedir","join","existsSync","join","homedir","join","homedir","existsSync","writeFileSync","join","homedir","existsSync","readFileSync","writeFileSync","renameSync","readdirSync","setup","eventLoopPromise","resolve","resolve","execFileSync","rmSync","homedir","readFileSync","readdirSync","statSync","existsSync","join","homedir","SPINNER_CHARS","defaultUndeliveredWarning","matchesAny","existsSync","mkdirSync","readdirSync","realpathSync","rmSync","writeFileSync","join","resolve","homedir","join","homedir","mkdirSync","writeFileSync","rmSync","existsSync","readdirSync","resolve","realpathSync","execFileSync","setup","eventLoopPromise","resolve","homedir","rmSync","readFileSync","resolve","args","require"]}