zidane 7.0.30 → 7.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acp-cli.js +3 -3
- package/dist/{acp-Cu879kus.js → acp-m3TLCY_C.js} +2 -2
- package/dist/{acp-Cu879kus.js.map → acp-m3TLCY_C.js.map} +1 -1
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-BipX4BBj.js → agent-9gf8ZlBR.js} +54 -16
- package/dist/agent-9gf8ZlBR.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/{approvals-staFZZ5o.js → approvals-DaCEPCTV.js} +348 -10
- package/dist/approvals-DaCEPCTV.js.map +1 -0
- package/dist/{auth-D4MpTfvF.js → auth-CJogE9q7.js} +2 -2
- package/dist/{auth-D4MpTfvF.js.map → auth-CJogE9q7.js.map} +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +6 -6
- package/dist/chat.js +5 -5
- package/dist/{context-transport-BqYwzJYE.js → context-transport-T_6AYIvy.js} +2 -2
- package/dist/{context-transport-BqYwzJYE.js.map → context-transport-T_6AYIvy.js.map} +1 -1
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/daemon-cli.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/extensions.d.ts +2 -2
- package/dist/extensions.js +2 -2
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{index-CnXKeZ8p.d.ts → index-JkEO3MBV.d.ts} +160 -6
- package/dist/index-JkEO3MBV.d.ts.map +1 -0
- package/dist/{index-C0y2tSgk.d.ts → index-hd4zLhp8.d.ts} +52 -2
- package/dist/index-hd4zLhp8.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/{ipc-CbkrpfkI.js → ipc-BtN5SIc0.js} +2 -2
- package/dist/{ipc-CbkrpfkI.js.map → ipc-BtN5SIc0.js.map} +1 -1
- package/dist/ipc.d.ts +5 -5
- package/dist/ipc.js +3 -3
- package/dist/{mcp-BIWa57eY.js → mcp-CgNd-f3F.js} +3 -3
- package/dist/{mcp-BIWa57eY.js.map → mcp-CgNd-f3F.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/restate.d.ts +1 -1
- package/dist/restate.js +1 -1
- package/dist/{server-GJ2gNRt_.js → server-ktOMu5gk.js} +6 -6
- package/dist/{server-GJ2gNRt_.js.map → server-ktOMu5gk.js.map} +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/{src-DzzhE2YO.js → src-CjkY6fBj.js} +2 -2
- package/dist/{src-DzzhE2YO.js.map → src-CjkY6fBj.js.map} +1 -1
- package/dist/{stdio-loader-CI4sPFve.js → stdio-loader-Bbx19dMZ.js} +2 -2
- package/dist/{stdio-loader-CI4sPFve.js.map → stdio-loader-Bbx19dMZ.js.map} +1 -1
- package/dist/{tool-formatters-bbgUM3zI.d.ts → tool-formatters-C4QIFCNa.d.ts} +2 -2
- package/dist/{tool-formatters-bbgUM3zI.d.ts.map → tool-formatters-C4QIFCNa.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-BHyrChoO.d.ts → transcript-anchors-Bbn1HX5D.d.ts} +8 -2
- package/dist/{transcript-anchors-BHyrChoO.d.ts.map → transcript-anchors-Bbn1HX5D.d.ts.map} +1 -1
- package/dist/tui.d.ts +3 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +30 -7
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-ZP5-2OWi.d.ts → turn-operations-BhHCpCkP.d.ts} +3 -3
- package/dist/{turn-operations-ZP5-2OWi.d.ts.map → turn-operations-BhHCpCkP.d.ts.map} +1 -1
- package/dist/types.d.ts +2 -2
- package/docs/TUI.md +5 -3
- package/package.json +1 -1
- package/dist/agent-BipX4BBj.js.map +0 -1
- package/dist/approvals-staFZZ5o.js.map +0 -1
- package/dist/index-C0y2tSgk.d.ts.map +0 -1
- package/dist/index-CnXKeZ8p.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvals-DaCEPCTV.js","names":["errorMessage","renderBlock","findGitRoot","realpathOr","MAX_BYTES_PER_FILE","findGitRoot","readIfPresent","errorMessage","OPT_OUT_VARS","overrideManager","reinstallHint","roots","findGitRoot","findGitRoot","errorMessage","findGitRoot","errorMessage","findGitRoot","errorMessage","errorMessage","findGitRoot","isThinkingLevel","isThinkingLevel","createSessionInStore","createSessionInSharedStore","artifactsPreset","errorMessage","findGitRoot","errorMessage"],"sources":["../src/chat/store.ts","../src/tools/read-chat-turns.ts","../src/chat/agent-prompt.ts","../src/chat/todos.ts","../src/chat/agents.ts","../src/chat/ruler-configs.ts","../src/chat/agents-md.ts","../src/chat/artifacts-view.ts","../src/chat/auto-compact.ts","../src/chat/auto-update.ts","../src/chat/auto-update-companion.ts","../src/chat/auto-update-restart.ts","../src/chat/auto-update-cli.ts","../src/chat/auto-update-hook.ts","../src/chat/auto-update-service.ts","../src/chat/boot-profiler.ts","../src/chat/browser.ts","../src/chat/foreign-configs.ts","../src/chat/mcps-discovery.ts","../src/chat/mcps-write.ts","../src/chat/claude-import.ts","../src/chat/completion.ts","../src/chat/completion-artifacts.ts","../src/chat/user-config.ts","../src/chat/config.ts","../src/chat/config-context.tsx","../src/chat/discovery-context.tsx","../src/chat/discovery-slot.ts","../src/chat/editor-command.ts","../src/chat/themes/catppuccin.ts","../src/chat/themes/crush.ts","../src/chat/themes/gruvbox.ts","../src/chat/themes/light.ts","../src/chat/themes/vaporwave.ts","../src/chat/theme.ts","../src/chat/settings-context.tsx","../src/chat/enabled-toggle-set.ts","../src/chat/files-discovery.ts","../src/chat/footer-hints.ts","../src/chat/hints.ts","../src/chat/host/compaction.ts","../src/chat/host/edit-outcome-ledger.ts","../src/mcp/warn-log.ts","../src/chat/plugins-discovery.ts","../src/chat/run-end-notice.ts","../src/chat/model-chain-metadata.ts","../src/chat/session-factory.ts","../src/chat/builtin-commands.ts","../src/chat/host/helpers.ts","../src/chat/host/host-runtime.ts","../src/chat/host/commands.ts","../src/chat/host/interactions.ts","../src/chat/host/orchestration.ts","../src/chat/host/provider-diagnostic.ts","../src/chat/host/signals.ts","../src/chat/host/engine.ts","../src/chat/interactions.tsx","../src/chat/mcp-auth-state.ts","../src/chat/mcp-auth-context.tsx","../src/chat/mcp-credentials.ts","../src/chat/mcp-rows.ts","../src/chat/mcp-tool-toggle.ts","../src/chat/mcp-tools-cache.ts","../src/chat/oauth.ts","../src/chat/oauth-redirect.ts","../src/chat/path-display.ts","../src/chat/provider-usage.ts","../src/chat/shell-parse.ts","../src/chat/safe-mode.ts","../src/chat/safe-mode-context.tsx","../src/chat/session-cwd.ts","../src/chat/session-export.ts","../src/chat/skills-discovery.ts","../src/chat/spawn-markers.ts","../src/chat/streaming.ts","../src/chat/system-md.ts","../src/chat/theme-context.tsx","../src/chat/todo-anchor.ts","../src/chat/transcript-anchors.ts","../src/chat/host/approvals.ts"],"sourcesContent":["import type { SessionData, SessionMetaRow, SessionRun, SessionStore } from '../session'\nimport type { JsonValue, SessionContentBlock, SessionTurn, ThinkingLevel, ToolResultContent } from '../types'\nimport type { ProviderKey } from './auth'\nimport type { ModelChainEntry } from './model-chain'\nimport type { EditOutcome, SessionMeta, Settings, StreamEvent } from './types'\nimport { Buffer } from 'node:buffer'\nimport { existsSync, mkdirSync, readFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { writeFileAtomic } from '../atomic-write'\nimport { errorMessage } from '../errors'\nimport { formatTokenUsage } from '../stats'\nimport { toolResultToText } from '../types'\nimport { parseEditOutcomesFromResult } from './edit-approval'\nimport { extractEditPayload } from './edit-diff'\nimport { formatDuration, previewLine } from './format'\nimport { isMainSession, parseSessionEventXml } from './orchestration'\nimport { formatRunReceiptLine, hasRunReceipt, runReceiptFromRun } from './run-receipt'\n\n// NOTE: `createTuiStore` moved to `src/session/sqlite.ts` so this module —\n// the public `zidane/chat` entry — does NOT statically import `bun:sqlite`.\n// A future GUI consumer that imports `zidane/chat` no longer transitively\n// pulls in the Bun-only sqlite binding. Hosts construct their own store\n// (e.g. via `createTuiStore` from `zidane/session/sqlite` for terminal hosts)\n// and pass it through `runTui({ store })` / `resolveConfig({ store })`.\n\nfunction ensureStateDir(path: string): void {\n const dir = dirname(path)\n if (existsSync(dir))\n return\n try {\n mkdirSync(dir, { recursive: true })\n }\n catch (err) {\n throw new Error(\n `Could not create TUI state directory at \"${dir}\". `\n + `Override the location via \\`runTui({ storageDir, prefix })\\` or the `\n + `\\`ZIDANE_STORAGE_DIR\\` env var. Original error: ${errorMessage(err)}`,\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Persisted UI state — what to resume on next launch.\n//\n// `StateStoreApi` is a thin facade that binds load/save to a specific JSON\n// path, so the rest of the UI can persist state without knowing the layout.\n// ---------------------------------------------------------------------------\n\nexport interface TuiState {\n lastProvider?: ProviderKey\n lastSessionId?: string\n /** Per-provider last-used model id. */\n lastModelByProvider?: Partial<Record<ProviderKey, string>>\n /**\n * Last-applied model fallback chain (entry 0 = primary, mirroring\n * `lastProvider` + `lastModelByProvider`; entries 1+ = fallbacks).\n * Additive — absent for single-model users and older state files.\n * Restored on launch only when entry 0 still matches the resolved\n * primary; entries are re-validated against the live registry.\n */\n lastModelChain?: ModelChainEntry[]\n /**\n * Per-model last-picked reasoning effort. Keyed by model id (not provider)\n * so switching the same model across providers preserves the user's\n * intent. Models without reasoning support are absent from the map.\n */\n lastEffortByModel?: Record<string, ThinkingLevel>\n /**\n * Per-model last-picked custom options (e.g. `{ fast: true }`). Keyed by\n * model id like {@link lastEffortByModel}, so a toggle follows the model\n * across providers. Models with no options are absent from the map.\n */\n lastModelOptionsByModel?: Record<string, Record<string, boolean>>\n /**\n * Last-active agent profile id (see {@link AgentRegistry}). Resumed on\n * launch so a returning user lands back in the same mode (Build / Plan /\n * a host profile). Falls back to the host's `defaultAgent` then the first\n * registry key if the id is no longer registered.\n */\n /**\n * @deprecated No longer read: the agent profile is per-session and fresh\n * sessions always start in the default. Kept so older state files parse.\n */\n lastAgent?: string\n /** User-toggled transcript filters. Persisted so they outlive a launch. */\n settings?: Partial<Settings>\n}\n\nexport interface StateStoreApi {\n load: () => TuiState\n save: (state: TuiState) => void\n}\n\nexport function createStateStore(path: string): StateStoreApi {\n return {\n load: () => loadState(path),\n save: state => saveState(path, state),\n }\n}\n\nexport function loadState(path: string): TuiState {\n if (!existsSync(path))\n return {}\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf-8'))\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))\n return parsed as TuiState\n }\n catch {\n // Corrupt state file → treat as absent so the user can reach the auth screen.\n }\n return {}\n}\n\nexport function saveState(path: string, state: TuiState): void {\n ensureStateDir(path)\n writeFileAtomic(path, JSON.stringify(state, null, 2))\n}\n\n// Re-export the model-info shape used by views below so callers don't have to\n// pull from `./config` for transcript helpers alone.\nexport type { ModelInfo } from './config'\n\n// ---------------------------------------------------------------------------\n// Session view helpers — derive UI-friendly shapes from persisted turns.\n// ---------------------------------------------------------------------------\n\n/**\n * Load every session and project it to the compact `SessionMeta` shape used by\n * the picker. Sorted by recency via the underlying store's `list()` contract\n * (sqlite store returns by `updated_at DESC`).\n *\n * Robust to per-row failures: `Promise.allSettled` so a single corrupt or\n * unreadable row (malformed JSON, partial write, transient I/O error)\n * doesn't take down the entire picker. Failed rows are silently skipped;\n * a stderr line is emitted under `ZIDANE_DEBUG` for diagnosis.\n */\nexport async function listSessionMeta(\n store: SessionStore,\n filter?: {\n /**\n * Restrict to sessions belonging to this project. Omit to ignore\n * the axis; pass `null` to ask for untagged (legacy) sessions\n * specifically. The TUI defaults to passing the current git root /\n * cwd here so each project sees only its own conversations.\n */\n projectRoot?: string | null\n },\n): Promise<SessionMeta[]> {\n // Fast path: stores with an indexed summary query (sqlite) answer in a\n // single round-trip without materializing any session. Falls back to\n // the generic fan-out below on error so a store with a broken/legacy\n // index degrades to slow-but-correct instead of an empty picker.\n if (store.listMeta) {\n try {\n const rows = await store.listMeta(filter)\n return rows.map(metaFromRow)\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG) {\n const cause = err instanceof Error ? err.message : String(err)\n process.stderr.write(`[zidane/chat] store.listMeta failed, falling back to full loads: ${cause}\\n`)\n }\n }\n }\n const ids = await store.list(filter)\n const settled = await Promise.allSettled(ids.map(async (id) => {\n const data = await store.load(id)\n return data ? metaFromData(id, data) : null\n }))\n const metas: SessionMeta[] = []\n for (let i = 0; i < settled.length; i++) {\n const result = settled[i]\n if (result.status === 'fulfilled' && result.value) {\n metas.push(result.value)\n }\n else if (result.status === 'rejected' && process.env.ZIDANE_DEBUG) {\n const cause = result.reason instanceof Error ? result.reason.message : String(result.reason)\n process.stderr.write(`[zidane/chat] failed to load session \"${ids[i]}\": ${cause}\\n`)\n }\n }\n return metas\n}\n\n/** Project a store header row to the picker's shape. */\nfunction metaFromRow(row: SessionMetaRow): SessionMeta {\n return {\n id: row.id,\n title: deriveSessionTitle(row.firstUserTurn ? [row.firstUserTurn] : [], row.metadata),\n turnCount: row.turnCount,\n userMessageCount: row.userMessageCount,\n runCount: row.runCount,\n ...(isMainSession(row.metadata) ? { isMain: true } : {}),\n ...(row.projectRoot ? { projectRoot: row.projectRoot } : {}),\n ...(typeof row.artifactCount === 'number' ? { artifactCount: row.artifactCount } : {}),\n updatedAt: row.updatedAt,\n }\n}\n\n/** Same projection from a fully materialized session (no header index). */\nfunction metaFromData(id: string, data: SessionData): SessionMeta {\n return {\n id,\n title: deriveSessionTitle(data.turns, data.metadata),\n turnCount: data.turns.length,\n userMessageCount: data.turns.reduce((n, t) => t.role === 'user' ? n + 1 : n, 0),\n runCount: data.runs.length,\n ...(isMainSession(data.metadata) ? { isMain: true } : {}),\n ...(data.projectRoot ? { projectRoot: data.projectRoot } : {}),\n updatedAt: data.updatedAt,\n }\n}\n\n/**\n * One session's `SessionMeta` BY ID — the by-id complement to\n * `listSessionMeta`.\n *\n * For rows a filtered list cannot return: the orchestration main is\n * directory-less, so no project-scoped query yields it, and re-listing the\n * whole store on every sessions refresh just to find that one row is O(store).\n * Uses the store's indexed header read when it has one; degrades to a full\n * load otherwise. `null` = no such session.\n */\nexport async function getSessionMeta(store: SessionStore, id: string): Promise<SessionMeta | null> {\n if (store.getMeta) {\n try {\n const row = await store.getMeta(id)\n return row ? metaFromRow(row) : null\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG) {\n const cause = err instanceof Error ? err.message : String(err)\n process.stderr.write(`[zidane/chat] store.getMeta failed, falling back to a full load: ${cause}\\n`)\n }\n }\n }\n const data = await store.load(id)\n return data ? metaFromData(id, data) : null\n}\n\n/** Derive a short title from the first user message — returns null when empty. */\nexport function titleFromTurns(turns: SessionTurn[]): string | null {\n const first = turns.find(t => t.role === 'user')\n if (!first)\n return null\n for (const block of first.content) {\n if (block.type === 'text' && block.text.trim()) {\n const oneLine = block.text.replace(/\\s+/g, ' ').trim()\n return oneLine.length > 60 ? `${oneLine.slice(0, 60)}…` : oneLine\n }\n }\n return null\n}\n\n/**\n * Fallback title for a session with no stored `metadata.title` and no\n * turns to derive one from yet — i.e. a freshly created session. Shown\n * in the sessions list, chat header, and details modal until the first\n * message (or an explicitly generated title) gives it a real name.\n */\nexport const DEFAULT_SESSION_TITLE = 'New session'\n\n/**\n * Display title for a session — preferred sources, in order:\n *\n * 1. `metadata.title` if it's a non-empty string (typically set by\n * the session-details modal's \"generate title\" action).\n * 2. {@link titleFromTurns} — the first user message, one-line + clipped.\n * 3. {@link DEFAULT_SESSION_TITLE} (`'New session'`).\n *\n * Total / pure / defensive on non-string metadata values.\n */\nexport function deriveSessionTitle(\n turns: SessionTurn[],\n metadata?: Record<string, unknown>,\n): string {\n const stored = typeof metadata?.title === 'string' ? metadata.title.trim() : ''\n if (stored.length > 0)\n return stored\n return titleFromTurns(turns) ?? DEFAULT_SESSION_TITLE\n}\n\n/**\n * Replay persisted turns as a viewable transcript. Mirrors the event shape\n * produced live by the agent hooks so loaded and streaming history render\n * identically — including subagent ancestry when `runs` is supplied.\n *\n * Subagent reconstruction:\n * - Every turn carries a `runId`. We look that up in `runs` to get the\n * run's `depth` and tag the resulting events with `{ depth, childId }`\n * — the same shape the live `child:*` bubble hooks produce.\n * - We synthesize `spawn-start` / `spawn-end` markers at each child-run\n * boundary so the transcript reads the same as a live run did\n * (`🌱 [run-id] task` … child events … `🌳 [run-id] done · tokens`).\n * - For child runs (`depth > 0`), the user-role \"task\" text is suppressed\n * because `spawn-start` already shows it.\n *\n * Without `runs` (legacy callers / tests), the function falls back to the\n * old behavior: depth-0 events with no subagent grouping.\n */\n/**\n * Map a `compact-summary` content block to its `compact-summary` StreamEvent —\n * the single source of truth for the compaction boundary card. Used by\n * {@link eventsFromTurns} (reload / TUI live repaint) AND by chat hosts that\n * surface the boundary live without a full reload (the GUI broadcasts this\n * from the loop's `compact:end` hook), so the live card and the reloaded card\n * are byte-identical regardless of who created the marker (manual, the old\n * host trigger, or loop-native auto-compaction — all share this block shape).\n */\nexport function compactSummaryEvent(\n block: Extract<SessionContentBlock, { type: 'compact-summary' }>,\n turnId?: string,\n): StreamEvent {\n return {\n kind: 'compact-summary',\n text: block.summary,\n compact: {\n replacedCount: block.replacesTurnIds.length,\n model: block.model,\n compactedAt: block.compactedAt,\n inputTokens: block.usage.input ?? 0,\n outputTokens: block.usage.output ?? 0,\n cacheReadTokens: block.usage.cacheRead ?? 0,\n cacheCreationTokens: block.usage.cacheCreation ?? 0,\n ...(typeof block.usage.cost === 'number' && block.usage.cost > 0 ? { costUsd: block.usage.cost } : {}),\n },\n ...(turnId ? { turnId } : {}),\n }\n}\n\n/**\n * Decoded byte size of a base64 payload, computed arithmetically.\n * `Buffer.from(data, 'base64').length` decodes the whole attachment —\n * a full allocation per media block on EVERY `eventsFromTurns` replay\n * (session load/switch/export/search) just to label a chip with a size.\n */\nfunction base64DecodedSize(data: string): number {\n const padding = data.endsWith('==') ? 2 : data.endsWith('=') ? 1 : 0\n return Math.floor(data.length * 3 / 4) - padding\n}\n\n/** Severity values a persisted notice may carry. Anything else is ignored. */\nconst NOTICE_SEVERITIES = new Set(['info', 'warn', 'error'])\n\n/** A persisted notice, plus the origin the live event carried. */\ninterface PersistedNotice {\n notice: NonNullable<StreamEvent['notice']>\n /** Subagent that produced it, when the live event named one. */\n childId?: string\n /** Indent depth the live event rendered at. */\n depth?: number\n}\n\n/**\n * Read a persisted `metadata.command.notice` back into the event shape, or\n * `null` when the block is an ordinary command detail. Defensive because the\n * value round-tripped through JSON written by a possibly older build.\n */\nfunction readNoticeMeta(value: unknown): PersistedNotice | null {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return null\n const { severity, label, childId, depth } = value as {\n severity?: unknown\n label?: unknown\n childId?: unknown\n depth?: unknown\n }\n if (typeof severity !== 'string' || !NOTICE_SEVERITIES.has(severity))\n return null\n if (typeof label !== 'string' || label === '')\n return null\n return {\n notice: { severity: severity as NonNullable<StreamEvent['notice']>['severity'], label },\n ...(typeof childId === 'string' && childId !== '' ? { childId } : {}),\n ...(typeof depth === 'number' && Number.isFinite(depth) && depth >= 0 ? { depth: Math.floor(depth) } : {}),\n }\n}\n\n/** The notice a command block carries, or `null` for an ordinary detail block. */\nfunction noticeFromBlock(block: SessionTurn['content'][number]): PersistedNotice | null {\n if (block.type !== 'tool_result')\n return null\n const meta = block.metadata?.command\n if (!meta || typeof meta !== 'object' || Array.isArray(meta))\n return null\n return readNoticeMeta((meta as Record<string, unknown>).notice)\n}\n\n/**\n * Persisted form of a harness notice: a display-only `role: 'command'` turn,\n * which is the existing carrier for content that must survive a reload without\n * ever reaching the model.\n *\n * Pairs with the `readNoticeMeta` branch in {@link eventsFromTurns}. Callers\n * pre-mint `turnId` and emit the live event with the same id so the live row\n * and the reloaded row are the same row.\n *\n * `origin` carries the live event's `childId` / `depth`. A subagent notice is\n * persisted by the PARENT's depth-0 run, so without it the run-derived tag in\n * `eventsFromTurns` would reload the row un-indented and unattributed.\n */\nexport function noticeTurnContent(\n notice: NonNullable<StreamEvent['notice']>,\n text: string,\n origin: { childId?: string, depth?: number } = {},\n): SessionTurn['content'] {\n return [{\n type: 'tool_result' as const,\n callId: `notice_${crypto.randomUUID()}`,\n output: text,\n metadata: {\n command: {\n notice: {\n severity: notice.severity,\n label: notice.label,\n ...(origin.childId ? { childId: origin.childId } : {}),\n ...(origin.depth !== undefined ? { depth: origin.depth } : {}),\n },\n },\n },\n }]\n}\n\nexport function eventsFromTurns(\n turns: SessionTurn[],\n runs: readonly SessionRun[] = [],\n): StreamEvent[] {\n // First-write-wins on duplicate ids. Historical sessions persisted\n // before the per-mint `initialRunCounter` re-sync (see `src/agent.ts`)\n // could have a child agent and its parent both registering runs with\n // the SAME `run_<n>` id — the subagent ran first (lands first in\n // `runs[]`), the parent's collision came later (lands last).\n // Last-write-wins would resolve the id to the collision (depth=0,\n // no parentRunId), the structural ancestry walk would return empty,\n // and the renderer would paint a subagent's transcript at depth 0:\n // indent gets crazy, spawn markers placed wrong, the whole block\n // looks \"off\". Preferring the FIRST entry keeps the depth + parent\n // info from the subagent record, which is what every turn under\n // that runId actually wants. Clean sessions have one entry per id,\n // so first/last makes no difference.\n const runById = new Map<string, SessionRun>()\n for (const run of runs) {\n if (!runById.has(run.id))\n runById.set(run.id, run)\n }\n\n // Defensive synthesis for legacy sessions whose `runs[]` is missing\n // entries the turns reference (e.g. the pre-fix `updateRun`-not-\n // upsert bug in the sqlite/memory stores dropped subagent records\n // silently). Without it, an orphan turn's `ancestryOf()` returns an\n // empty chain → depth = 0 → the subagent's user-text-block leaks\n // as a `user-prompt` event and pollutes the TUI's history.\n //\n // Inference is structural: walk turns chronologically, maintain a\n // stack of currently-open runIds, and decide whether a NEW runId\n // is nested or top-level based on whether the stack-top run has any\n // unresolved tool_calls. A nested runId pushes (depth grows); a\n // top-level runId resets the stack first. Tracked per-runId in\n // `pendingToolCallsPerRun` — incremented on `tool_call` blocks,\n // decremented on `tool_result` blocks.\n //\n // Without the nesting gate, the naive \"push every new runId, never\n // pop\" walk treated back-to-back top-level prompts as a deepening\n // chain of subagents, and synthesized depth grew unbounded —\n // `indentFor(36)` = 72 cells, which is the far-right rendering the\n // user reported for a session with many top-level chats plus one\n // missing run record. Real records (present in `runById`) are NEVER\n // overwritten — synthesis only fills the gap.\n const runStack: string[] = []\n const pendingToolCallsPerRun = new Map<string, Set<string>>()\n for (const turn of turns) {\n const rid = turn.runId\n if (!rid)\n continue\n const idxInStack = runStack.indexOf(rid)\n if (idxInStack >= 0) {\n // Already open — pop any deeper nesting that's now closed.\n runStack.length = idxInStack + 1\n }\n else {\n // New runId — nested only when the parent has a pending\n // SPAWN-shaped tool_call (the call that's waiting on a subagent).\n // Restricting to spawn-like names matters: a run killed mid-tool\n // (dev-loop restart, crash) leaves its ordinary tool_calls\n // unresolved FOREVER, and under the old \"any pending call\" gate\n // every subsequent top-level run with a missing record was\n // synthesized as that dead run's child — the user's own prompt\n // rendered as `child-N · <prompt>` with `0 / 0 out`.\n const top = runStack[runStack.length - 1]\n const parentHasPending = top !== undefined && (pendingToolCallsPerRun.get(top)?.size ?? 0) > 0\n if (!parentHasPending) {\n // Fresh top-level — drop the stack so a long chat history\n // doesn't accumulate phantom depth.\n runStack.length = 0\n }\n runStack.push(rid)\n\n if (!runById.has(rid)) {\n // Missing record — synthesize. Depth = stack depth - 1 (zero-based).\n // Prompt: the first user text-block on this turn (when present)\n // so the `child-N` spawn-start marker renders with a meaningful\n // preview rather than an empty string. Empty when the first\n // turn isn't a user turn or has no text block — `pushSpawnStart`\n // then falls through to the bare label.\n const depth = runStack.length - 1\n const parentRunId = depth > 0 ? runStack[depth - 1] : undefined\n let inferredPrompt = ''\n if (turn.role === 'user') {\n for (const block of turn.content) {\n if (block.type === 'text' && block.text.trim()) {\n inferredPrompt = block.text\n break\n }\n }\n }\n runById.set(rid, {\n id: rid,\n startedAt: turn.createdAt,\n prompt: inferredPrompt,\n status: 'completed',\n depth,\n ...(parentRunId ? { parentRunId } : {}),\n })\n }\n }\n\n // Update the pending-tool-calls set for this turn's runId. Tracked\n // here (not in the synthesis branch alone) so we have an accurate\n // running view for every turn — the stack-top check above reads\n // this for the next iteration.\n const pending = pendingToolCallsPerRun.get(rid) ?? new Set<string>()\n for (const block of turn.content) {\n // Only spawn-shaped calls can open a child run — tracking every\n // tool_call would let a run killed mid-`shell`/`read_file` adopt\n // unrelated later runs as phantom children (see gate above).\n if (block.type === 'tool_call' && /spawn/i.test(block.name))\n pending.add(block.id)\n else if (block.type === 'tool_result')\n pending.delete(block.callId)\n }\n pendingToolCallsPerRun.set(rid, pending)\n }\n\n // Build a chronological mapping from the persisted `runId` (e.g. `run_2`) to\n // a friendly `child-N` label that mirrors what the spawn tool emits live\n // (see `src/tools/spawn.ts`'s local counter). Without this the reloaded\n // transcript labels its subagent block `[run_2]` while the persisted tool\n // result text inside that very block says `[sub-agent child-1]`.\n //\n // Reads from `runById` (NOT the raw `runs` param) so synthesized\n // fallback runs from the legacy-sessions block above also get a\n // proper `child-N` label.\n const childLabelByRunId = new Map<string, string>()\n const childRuns = [...runById.values()]\n .filter(r => (r.depth ?? 0) > 0)\n .sort((a, b) => a.startedAt - b.startedAt)\n // Prefer the label persisted at `startRun` (`SessionRun.label`) — the\n // positional fallback covers legacy sessions and synthesized runs, but\n // can desync from announced labels under concurrent spawns or counter\n // drift, which would mislabel resume targets.\n childRuns.forEach((r, i) => childLabelByRunId.set(r.id, r.label ?? `child-${i + 1}`))\n const labelFor = (runId: string) => childLabelByRunId.get(runId) ?? runId\n\n // Build a `callId → toolName` map from the assistant `tool_call` blocks so\n // we can tag each user-role `tool_result` event with the tool it answers.\n // The renderer uses this for tool-aware filters (e.g. hiding the spawn\n // tool's result when `hideSubagentOutput` is on).\n const toolByCallId = new Map<string, string>()\n for (const turn of turns) {\n if (turn.role !== 'assistant')\n continue\n for (const block of turn.content) {\n if (block.type === 'tool_call')\n toolByCallId.set(block.id, block.name)\n }\n }\n\n // Build a `callId → resultText` map so the replay path can attach per-\n // edit outcomes back onto the corresponding `tool` event. Live capture\n // mutates `_outcomes` on input before tool execution; on disk, only the\n // tool_result body carries the post-decision shape, so the replay\n // parses it (see `parseEditOutcomesFromResult`).\n const resultByCallId = new Map<string, string>()\n for (const turn of turns) {\n if (turn.role !== 'user')\n continue\n for (const block of turn.content) {\n if (block.type === 'tool_result') {\n resultByCallId.set(\n block.callId,\n typeof block.output === 'string' ? block.output : toolResultToText(block.output),\n )\n }\n }\n }\n\n // Ancestry chain for a turn's run — `[root child, …, the turn's own run]`,\n // excluding any depth-0 (top-level) ancestor since top-level runs don't\n // render spawn-start/spawn-end markers. Used by the stack-based\n // transition logic below so depth ≥ 2 (grandchild) sequences don't\n // emit duplicate `spawn-start` / premature `spawn-end` markers when\n // walking back up the tree. parent→A→B→A→parent walks cleanly:\n // T1 ancestry [] → no transition\n // T3 ancestry [A] → open A\n // T5 ancestry [A, B] → open B (A stays)\n // T7 ancestry [A] → close B (A stays)\n // T9 ancestry [] → close A\n const ancestryOf = (turnRunId: string | undefined): SessionRun[] => {\n if (!turnRunId)\n return []\n const chain: SessionRun[] = []\n let cursor: SessionRun | undefined = runById.get(turnRunId)\n const seen = new Set<string>()\n while (cursor) {\n // Defensive cycle guard — a corrupt run row pointing at its own\n // ancestor would otherwise loop forever.\n if (seen.has(cursor.id))\n break\n seen.add(cursor.id)\n if ((cursor.depth ?? 0) > 0)\n chain.unshift(cursor)\n cursor = cursor.parentRunId ? runById.get(cursor.parentRunId) : undefined\n }\n return chain\n }\n\n // Children index for subtree aggregation. Run records are\n // deliberately per-run additive (children persist as their own runs —\n // folding cumulative totals into the parent would double-count, see\n // `completeRun` in agent.ts), while the live spawn-end marker reports\n // subtree totals: cost via the `flattenTurns` walk, tokens via the\n // grandchildren fold in agent.ts's `finalStats`. Walk descendants here\n // so live and reloaded markers agree for nested spawns — on BOTH\n // numbers, not just cost.\n const childRunsOf = new Map<string, SessionRun[]>()\n for (const run of runById.values()) {\n if (!run.parentRunId)\n continue\n const siblings = childRunsOf.get(run.parentRunId) ?? []\n siblings.push(run)\n childRunsOf.set(run.parentRunId, siblings)\n }\n const subtreeTotals = (root: SessionRun): { cost: number, tokensIn: number, tokensOut: number, cacheRead: number, cacheCreation: number } => {\n const totals = { cost: 0, tokensIn: 0, tokensOut: 0, cacheRead: 0, cacheCreation: 0 }\n const queue = [root]\n const seen = new Set<string>()\n while (queue.length > 0) {\n const run = queue.pop() as SessionRun\n if (seen.has(run.id))\n continue\n seen.add(run.id)\n totals.cost += run.cost ?? run.turnUsage?.reduce((sum, u) => sum + (u.cost ?? 0), 0) ?? 0\n totals.tokensIn += run.tokensIn ?? run.totalUsage?.input ?? 0\n totals.tokensOut += run.tokensOut ?? run.totalUsage?.output ?? 0\n totals.cacheRead += run.totalUsage?.cacheRead ?? 0\n totals.cacheCreation += run.totalUsage?.cacheCreation ?? 0\n queue.push(...(childRunsOf.get(run.id) ?? []))\n }\n return totals\n }\n\n const events: StreamEvent[] = []\n /** Currently-open child runs, root-of-tree first, innermost last. */\n const openStack: SessionRun[] = []\n /** True iff anything has been emitted at the active depth-0 level. Used to decide separators between top-level turns. */\n let lastDepthAtEmission = -1\n\n const pushSpawnEnd = (run: SessionRun) => {\n const tag = run.status === 'aborted' || run.status === 'error' ? run.status : 'done'\n const totals = subtreeTotals(run)\n // Same caching gotcha as the live path: `tokensIn` mirrors\n // `AgentStats.totalIn` (new uncached only). `formatTokenUsage` folds\n // cache reads/creations into the headline `in` number. Totals span\n // the run's whole subtree — the live marker formats the child's\n // `finalStats`, which already folds grandchildren in.\n const usage = formatTokenUsage({\n totalIn: totals.tokensIn,\n totalOut: totals.tokensOut,\n totalCacheRead: totals.cacheRead,\n totalCacheCreation: totals.cacheCreation,\n })\n // Optional ` · `-joined segments — keep the shape in sync with the live\n // `spawn:complete` hook in `src/tui/app.tsx` so a reloaded transcript\n // renders byte-identical markers. `endedAt` is absent on runs\n // synthesized from bare turns (legacy sessions) — the segment drops.\n // Zero/unknown cost hides the segment (no \"$0.00\" lies).\n const elapsed = run.endedAt !== undefined ? ` · ${formatDuration(run.endedAt - run.startedAt)}` : ''\n const cost = totals.cost\n const costPart = cost > 0 ? ` · $${cost.toFixed(cost < 0.01 ? 4 : 2)}` : ''\n // Legacy runs with neither segment keep the bare `done in N / M out`\n // shape — no dangling `·` on sessions recorded before duration/cost.\n const segments = `${elapsed}${costPart}`\n const model = run.turnUsage?.find(u => u.modelId)?.modelId\n const turns = run.turns ?? run.turnUsage?.length\n events.push({\n kind: 'spawn-end',\n text: segments ? `${tag}${segments} · ${usage}` : `${tag} ${usage}`,\n childId: labelFor(run.id),\n depth: run.depth ?? 1,\n spawn: { startedAt: run.startedAt, task: previewLine(run.prompt, 80), ...(turns !== undefined ? { turns } : {}) },\n ...(model ? { model } : {}),\n })\n }\n\n const pushSpawnStart = (run: SessionRun) => {\n // Single-line preview — newlines / tabs in the prompt collapse to\n // single spaces so the spawn-start marker stays on one visual row.\n // See the matching comment in `app.tsx`'s `spawn:before` hook for\n // the failure mode (multi-line prompt prints the marker across\n // multiple visual rows, the next event lands far right).\n const taskPreview = previewLine(run.prompt, 80)\n // Which model the child ran on — mirrors the live `spawn:before` hook's\n // `model`. Reconstructed from the first turn that recorded a model id\n // (providers echo it per turn; the run-level `totalUsage` aggregate\n // deliberately drops `modelId`, so per-turn usage is the only source).\n const model = run.turnUsage?.find(u => u.modelId)?.modelId\n // A resumed child (spawn `resume`) is tagged with the label of the run\n // it continues — mirrors the live `spawn:before` hook's `resumesChildId`.\n const resumes = run.resumedFromRunId ? labelFor(run.resumedFromRunId) : undefined\n events.push({\n kind: 'spawn-start',\n text: resumes ? `resumes ${resumes} · ${taskPreview}` : taskPreview,\n childId: labelFor(run.id),\n depth: run.depth ?? 1,\n spawn: { startedAt: run.startedAt, task: taskPreview },\n ...(model ? { model } : {}),\n })\n }\n\n for (let i = 0; i < turns.length; i++) {\n const turn = turns[i]\n const targetChain = ancestryOf(turn.runId)\n const depth = targetChain.length // 0 for top-level, otherwise innermost depth\n\n // Diff the open stack against the turn's ancestry chain. Common\n // prefix stays; anything beyond in the stack is closed, anything\n // beyond in the chain is opened. Single-shot for depth = 1 (matches\n // the old behavior); multi-shot for depth ≥ 2 (the case the prior\n // implementation got wrong).\n let common = 0\n while (\n common < openStack.length\n && common < targetChain.length\n && openStack[common].id === targetChain[common].id\n ) {\n common++\n }\n while (openStack.length > common) {\n const run = openStack.pop()!\n pushSpawnEnd(run)\n }\n while (openStack.length < targetChain.length) {\n const run = targetChain[openStack.length]\n pushSpawnStart(run)\n openStack.push(run)\n }\n\n const subagentTag = depth > 0 && turn.runId ? { childId: labelFor(turn.runId), depth } : undefined\n // `turnId` tags every event so the TUI's select-turn mode can highlight\n // all events emitted by a single SessionTurn. Subagent ancestry stays in\n // a separate object so the spread order keeps existing tag overrides\n // (childId/depth) wins-last semantics intact.\n const tag = { turnId: turn.id, ...subagentTag }\n\n if (turn.role === 'user') {\n // A display-marker turn (command sub-run prompt, or a context note)\n // emits its `❯ marker` echo ONCE, even across multiple text blocks.\n let turnMarkerEmitted = false\n const attachmentOnly = !turn.content.some(block => block.type === 'text' && block.text.trim())\n && turn.content.some(block => ['document', 'image', 'audio', 'video'].includes(block.type))\n const displayContent: SessionContentBlock[] = attachmentOnly\n ? [{ type: 'text', text: '[Attachment]' }, ...turn.content]\n : turn.content\n for (const block of displayContent) {\n if (block.type === 'text' && block.text.trim()) {\n // Background-task completion notifications ride as text\n // blocks carrying `<task-notification>` XML (the format\n // `renderTaskNotificationXml` produces). Detect and emit\n // them as their own structured event so the TUI can render\n // a banner — and IMPORTANT: DON'T also emit a `user-prompt`\n // event for the same text, otherwise the raw XML would\n // double-paint alongside the banner. Same dedupe pattern\n // the compact-summary path uses.\n // Pass the FULL tag (turnId + optional childId/depth) so a\n // subagent's task notification renders under the right\n // `child-N` lane instead of bleeding up to depth 0.\n const taskEvent = parseTaskNotificationBlock(block.text, tag)\n if (taskEvent) {\n events.push(taskEvent)\n continue\n }\n\n // Cross-session event delivered to a main session. Same dedupe\n // rule as the task notification above: emit the structured row,\n // and DON'T also emit the `user-prompt` echo of the same text —\n // the model-facing `<session-event>` block is not a user message\n // and must not paint as one.\n const sessionEvent = parseSessionEventXml(block.text)\n if (sessionEvent) {\n events.push({ kind: 'session-event', text: sessionEvent.summary, sessionEvent, ...tag })\n continue\n }\n\n // For child runs the \"user\" text is the spawn task itself, already\n // rendered as the `spawn-start` marker above. Drop the duplicate.\n //\n // `user-prompt` carries the **raw** text; the renderer prepends the\n // `❯ ` chevron. No prefix in the payload means `refs` offsets stay\n // raw (no shift), and consumers building user-message indices or\n // history can read `event.text` directly.\n if (depth === 0) {\n // Separator BEFORE the user prompt — matches the live\n // behavior (see `app.tsx`'s prompt-commit path, which only\n // appends a separator when committing a new prompt). The\n // previous \"fire on every depth-0 turn pair\" rule was too\n // aggressive: it inserted a separator between every\n // mid-exchange assistant tool turn and its paired\n // user-side tool_result turn, producing a 2-row gap\n // between consecutive `todowrite` calls in the replayed\n // transcript that's absent during the live cascade.\n if (lastDepthAtEmission === 0)\n events.push({ kind: 'separator', text: '' })\n\n // Reconstruct attachment metadata from sibling content\n // blocks so the transcript shows chips after session reload.\n const attachments: { name: string, mediaType: string, size: number }[] = []\n for (const sibling of turn.content) {\n if (sibling.type === 'image' || sibling.type === 'audio' || sibling.type === 'video') {\n attachments.push({\n name: sibling.name ?? sibling.type,\n mediaType: sibling.mediaType,\n size: typeof sibling.data === 'string'\n ? base64DecodedSize(sibling.data)\n : 0,\n })\n }\n else if (sibling.type === 'document') {\n // Documents are structured blocks, not <attachment>-tagged\n // text — surface their own chip. Covers base64 (e.g. a PDF\n // prompt) AND text encoding (the shape `paste.txt` /\n // dragged-text-file attachments persist as); text-encoded\n // ones were previously dropped from the reloaded transcript.\n attachments.push({\n name: sibling.name ?? 'document',\n mediaType: sibling.mediaType,\n size: typeof sibling.data !== 'string'\n ? 0\n : sibling.encoding === 'text'\n ? Buffer.byteLength(sibling.data)\n : base64DecodedSize(sibling.data),\n })\n }\n else if (sibling !== block && sibling.type === 'text') {\n const m = sibling.text.match(/^<attachment\\s+(?:name=\"([^\"]+)\"\\s*)?(?:media_type=\"([^\"]+)\")?/)\n if (m) {\n attachments.push({\n name: m[1] ?? 'attachment',\n mediaType: m[2] ?? 'text/plain',\n size: sibling.text.length,\n })\n }\n }\n }\n\n // A `/`-command sub-run's injected prompt (often embedding bulky\n // material like a diff) renders minimally: the run's short\n // `origin.display` line as the echo + the full prompt as a\n // collapsed command-detail row — mirroring the live `runAgent`\n // path. Command context notes (`ctx.appendContext`) carry the\n // same hint on the TURN itself (`turn.display` — they belong to\n // no run) and replay identically, so a note whose body the\n // command also emitted never shows twice. Normal prompts\n // untouched.\n //\n // Label + `synthetic` derive from the ACTUAL display source, not\n // the mere presence of a run origin: `context` for a turn-level\n // note, `prompt` for a command sub-run's injected prompt. The\n // echo is `synthetic` (a marker the user never typed) so it\n // stays out of up-arrow prompt history. The marker+detail pair\n // fires once PER TURN, not per text block, so a multi-block\n // note can't echo the marker twice.\n const commandOrigin = turn.runId ? runById.get(turn.runId)?.origin : undefined\n const originDisplay = commandOrigin?.kind === 'command' ? commandOrigin.display : undefined\n const commandDisplay = originDisplay ?? turn.display\n if (commandDisplay) {\n if (!turnMarkerEmitted) {\n events.push({ kind: 'user-prompt', text: commandDisplay, turnId: turn.id, synthetic: true })\n turnMarkerEmitted = true\n }\n events.push({ kind: 'command-detail', label: originDisplay !== undefined ? 'prompt' : 'context', text: block.text, ...tag })\n }\n else {\n events.push({\n kind: 'user-prompt',\n text: block.text,\n turnId: turn.id,\n ...(attachments.length > 0 ? { attachments } : {}),\n })\n }\n }\n }\n else if (block.type === 'tool_result') {\n const tool = toolByCallId.get(block.callId)\n // Spawn tool-results duplicate the spawn-end marker's `Tokens: …`\n // line — and on pre-fix sessions the legacy \"N in / M out\" shape\n // disagrees with the cache-aware spawn-end. Strip it at the\n // display layer; the persisted output stays intact.\n const raw = toolResultText(block.output)\n const text = tool === 'spawn' ? stripSpawnTokensLine(raw) : raw\n events.push({\n kind: 'tool-result',\n text,\n ...(tool ? { tool } : {}),\n callId: block.callId,\n ...tag,\n })\n }\n else if (block.type === 'compact-summary') {\n // Compaction boundary — single event per marker block, via the\n // shared `compactSummaryEvent` mapper so the reloaded card matches\n // the live one byte-for-byte. The renderer reads `compact` for the\n // metadata badge; `text` carries the summary (the TUI keeps it\n // hidden by default so it doesn't read like new dialogue). Subagents\n // never produce these markers (compaction targets the top-level\n // conversation), so the ancestry tag isn't threaded — but `turnId`\n // is, so select-turn mode still highlights.\n events.push(compactSummaryEvent(block, turn.id))\n }\n }\n lastDepthAtEmission = depth\n continue\n }\n\n if (turn.role === 'command') {\n // Display-only local output from a `/` command (never sent to the\n // model). Delimit from the prior exchange, then replay each block in\n // the same shape the live `emit()` path appends, so reloaded and live\n // command output look identical: plain text → full markdown;\n // `tool_result` blocks (detail output — diffs, logs) → a\n // `command-detail` event: dim label + line-capped body.\n //\n // Notice turns are exempt from the delimiter: live they are appended\n // straight into the running transcript with no separator, and\n // `marginTopFor` collapses consecutive ones into a single block. Pushing\n // one here would reload a burst of harness events as a double-spaced\n // ladder, and put a break mid-exchange that never existed live.\n const noticeOnly = turn.content.length > 0 && turn.content.every(block => noticeFromBlock(block) !== null)\n if (lastDepthAtEmission === 0 && !noticeOnly)\n events.push({ kind: 'separator', text: '' })\n for (const block of turn.content) {\n if (block.type === 'text' && block.text.trim()) {\n events.push({ kind: 'markdown', text: block.text, streaming: false, ...tag })\n }\n else if (block.type === 'tool_result') {\n const meta = block.metadata?.command\n const commandMeta = (meta && typeof meta === 'object' && !Array.isArray(meta))\n ? meta as Record<string, unknown>\n : undefined\n // Harness notice (model fallback, repeat-guard trip, run error).\n // Replayed into the same `info` / `error` event the live path\n // emits, so a reopened conversation shows the identical row in\n // the same place instead of losing every harness event.\n const persistedNotice = readNoticeMeta(commandMeta?.notice)\n if (persistedNotice) {\n events.push({\n kind: persistedNotice.notice.severity === 'error' ? 'error' : 'info',\n text: toolResultText(block.output),\n notice: persistedNotice.notice,\n ...tag,\n // Origin wins over the run-derived tag: a subagent notice is\n // persisted by the parent's depth-0 run, so the tag alone would\n // reload it flat instead of indented under its child.\n ...(persistedNotice.childId ? { childId: persistedNotice.childId } : {}),\n ...(persistedNotice.depth !== undefined ? { depth: persistedNotice.depth } : {}),\n })\n continue\n }\n // Custom-rendered block: re-bind the renderer id + data; the\n // persisted output is the fallback (see commandBlocksToContent).\n const customMeta = commandMeta?.custom\n if (customMeta && typeof customMeta === 'object' && !Array.isArray(customMeta)\n && typeof (customMeta as { renderer?: unknown }).renderer === 'string') {\n // Persisted metadata is JsonValue by construction (see\n // commandBlocksToContent) — the cast re-narrows what JSON\n // round-tripping already guarantees.\n const bound = customMeta as { renderer: string, data?: JsonValue }\n events.push({\n kind: 'custom',\n text: toolResultText(block.output),\n custom: { renderer: bound.renderer, ...(bound.data !== undefined ? { data: bound.data } : {}) },\n ...tag,\n })\n continue\n }\n const label = typeof commandMeta?.label === 'string' ? commandMeta.label : undefined\n events.push({\n kind: 'command-detail',\n text: toolResultText(block.output),\n ...(label ? { label } : {}),\n ...(commandMeta?.syntax === 'diff' ? { syntax: 'diff' as const } : {}),\n ...tag,\n })\n }\n }\n lastDepthAtEmission = depth\n continue\n }\n\n if (turn.role === 'assistant') {\n for (const block of turn.content) {\n if (block.type === 'text' && block.text.trim()) {\n // Persisted turns are finalized — `streaming: false` so the markdown\n // renderer commits to the final layout (no heal pass needed).\n events.push({ kind: 'markdown', text: block.text, streaming: false, ...tag })\n }\n else if (block.type === 'thinking' && block.text.trim()) {\n // Reasoning is persisted as part of the assistant content (the\n // provider needs it echoed back), so replay it the same way the\n // live `stream:thinking` hook does: one event per line. The TUI\n // renderer gives consecutive thinking rows a fixed icon gutter so\n // replayed and streamed reasoning look identical. Without this,\n // reloading a session drops every trace.\n //\n // Blank lines are SKIPPED: a paragraph break inside one thinking\n // block would otherwise replay as an empty thinking event — a\n // bare icon row with no text, one per paragraph, in every\n // renderer (the GUI's \"double icon\" artifact).\n for (const line of block.text.split('\\n')) {\n if (line.trim())\n events.push({ kind: 'thinking', text: line, ...tag })\n }\n }\n else if (block.type === 'tool_call') {\n // Historical replay has no pre-write snapshot for `write_file`\n // (the captured priorContent only exists at live-hook time).\n // `extractEditPayload` falls back to `oldString: ''` so the\n // diff renders as an all-add view — matches git's \"new file\"\n // convention and is the most honest reconstruction available.\n let edit = extractEditPayload(block.name, block.input)\n if (edit) {\n // Attach per-edit outcomes parsed out of the paired tool_result\n // body so a replayed transcript shows the same applied / denied\n // / skipped badges the live run displayed. Falls through when\n // the body is unstructured (legacy results, error preamble) —\n // the renderer treats absent outcomes as \"all applied\".\n const resultText = resultByCallId.get(block.id)\n if (resultText) {\n const outcomes = parseEditOutcomesFromResult(resultText)\n if (outcomes && outcomes.length === edit.hunks.length)\n edit = { ...edit, outcomes }\n }\n }\n events.push({\n kind: 'tool',\n text: toolCallPreview(block.name, block.input),\n tool: block.name,\n input: block.input,\n ...(edit ? { edit } : {}),\n callId: block.id,\n ...tag,\n })\n }\n else if (block.type === 'server_tool_use') {\n // Native server-tool invocation (Anthropic `web_search`). Live runs\n // emit the same shape from `stream:server_tool_use`; replay this\n // way so the transcript looks identical on reload.\n events.push({\n kind: 'tool',\n text: toolCallPreview(block.name, block.input),\n tool: block.name,\n input: block.input,\n callId: block.id,\n ...tag,\n })\n }\n else if (block.type === 'server_tool_result') {\n events.push({\n kind: 'tool-result',\n text: serverToolResultSummary(block.content),\n tool: block.toolName,\n callId: block.toolUseId,\n ...tag,\n })\n }\n }\n lastDepthAtEmission = depth\n }\n }\n\n // Drain any trailing open child runs (e.g. session ended mid-spawn). Pop\n // innermost-first so depth ≥ 2 chains emit `spawn-end` in the right order.\n while (openStack.length > 0) {\n const run = openStack.pop()!\n pushSpawnEnd(run)\n }\n\n /*\n * Per-run receipts. For every finished TOP-LEVEL run, a `run-summary` lands\n * right after the last event of that run — the same record and the same\n * formatter the live path uses at `run:end`, so a reloaded session shows\n * exactly the line the user saw when the run ended. Child runs (subagents)\n * get no receipt of their own: their spawn-end marker already carries their\n * totals, and a receipt per child would bury the parent's.\n */\n const lastTurnIdByRun = new Map<string, string>()\n for (const turn of turns) {\n if (turn.runId)\n lastTurnIdByRun.set(turn.runId, turn.id)\n }\n const receiptAfterTurn = new Map<string, StreamEvent[]>()\n for (const run of runs) {\n if (run.parentRunId !== undefined || run.endedAt === undefined)\n continue\n const lastTurnId = lastTurnIdByRun.get(run.id)\n if (!lastTurnId)\n continue\n const ending: StreamEvent[] = []\n if (run.status === 'error' && run.error)\n ending.push({ kind: 'error', text: run.error, turnId: lastTurnId })\n const receipt = runReceiptFromRun(run)\n if (hasRunReceipt(receipt))\n ending.push({ kind: 'run-summary', text: formatRunReceiptLine(receipt), runSummary: receipt, turnId: lastTurnId })\n if (ending.length > 0)\n receiptAfterTurn.set(lastTurnId, ending)\n }\n if (receiptAfterTurn.size > 0) {\n // Insert after the LAST event carrying that turn id (a turn can emit\n // several events — text, tool calls, spawn markers).\n const lastIndexByTurn = new Map<string, number>()\n events.forEach((e, idx) => {\n if (e.turnId && receiptAfterTurn.has(e.turnId))\n lastIndexByTurn.set(e.turnId, idx)\n })\n const inserts = [...lastIndexByTurn.entries()].sort((a, b) => b[1] - a[1])\n for (const [turnId, idx] of inserts)\n events.splice(idx + 1, 0, ...receiptAfterTurn.get(turnId)!)\n }\n\n return events\n}\n\n/** Shared formatter for the `↳ name(args)` line shown on tool calls. */\n/**\n * Pattern matching the leading `<task-notification>` shape that the\n * agent's notification-injection path produces (see\n * `renderTaskNotificationXml` in `src/agent.ts`). Anchored so a stray\n * notification-shaped phrase mid-prompt doesn't trigger a false-positive\n * detection — the model's wire format always starts the user-turn\n * content block with this exact opening tag.\n */\nconst TASK_NOTIFICATION_RE = /^\\s*<task-notification>([\\s\\S]*?)<\\/task-notification>\\s*$/\n\n/**\n * Per-field extractors. Compiled once at module load (parsing happens\n * on every session replay, and a fresh `new RegExp` per pick burns\n * cycles for no reason).\n */\nconst TASK_NOTIFICATION_FIELD_RES = {\n taskId: /<task-id>([\\s\\S]*?)<\\/task-id>/,\n status: /<status>([\\s\\S]*?)<\\/status>/,\n exitCode: /<exit-code>([\\s\\S]*?)<\\/exit-code>/,\n command: /<command>([\\s\\S]*?)<\\/command>/,\n outputFile: /<output-file>([\\s\\S]*?)<\\/output-file>/,\n durationMs: /<duration-ms>([\\s\\S]*?)<\\/duration-ms>/,\n summary: /<summary>([\\s\\S]*?)<\\/summary>/,\n} as const\n\n/**\n * Detect a `<task-notification>` text block on replay and convert it\n * into a structured `task-notification` StreamEvent so the TUI's banner\n * renderer can paint it cleanly. Returns `null` when the block isn't a\n * notification (the caller falls back to the normal user-prompt path).\n *\n * Reads every field DIRECTLY from its tag — does NOT parse `<summary>`\n * for the underlying data. The summary is a derived display string,\n * not a source of truth: changing its format (localization, signal in\n * the label, whatever) must NEVER break replay. Each field falls back\n * to a safe default so older transcripts pre-dating a tag addition\n * still render.\n */\nfunction parseTaskNotificationBlock(\n text: string,\n tag: { turnId: string, childId?: string, depth?: number },\n): StreamEvent | null {\n const m = text.match(TASK_NOTIFICATION_RE)\n if (!m)\n return null\n\n const body = m[1]\n const pick = (re: RegExp): string | undefined => {\n const inner = body.match(re)\n return inner ? unescapeXml(inner[1].trim()) : undefined\n }\n\n const taskId = pick(TASK_NOTIFICATION_FIELD_RES.taskId) ?? '?'\n const statusRaw = pick(TASK_NOTIFICATION_FIELD_RES.status) ?? 'exited'\n const status: 'exited' | 'killed' = statusRaw === 'killed' ? 'killed' : 'exited'\n const exitCode = Number.parseInt(pick(TASK_NOTIFICATION_FIELD_RES.exitCode) ?? '0', 10) || 0\n const command = pick(TASK_NOTIFICATION_FIELD_RES.command) ?? ''\n const outputPath = pick(TASK_NOTIFICATION_FIELD_RES.outputFile) ?? ''\n const durationMs = Number.parseInt(pick(TASK_NOTIFICATION_FIELD_RES.durationMs) ?? '0', 10) || 0\n const summary = pick(TASK_NOTIFICATION_FIELD_RES.summary)\n ?? `${taskId} ${statusRaw}${exitCode ? ` ${exitCode}` : ''}`\n\n return {\n kind: 'task-notification',\n text: summary,\n task: { taskId, status, exitCode, outputPath, command, durationMs },\n turnId: tag.turnId,\n ...(tag.childId !== undefined ? { childId: tag.childId } : {}),\n ...(tag.depth !== undefined ? { depth: tag.depth } : {}),\n }\n}\n\n/**\n * Reverse of `escapeXml` for the small set of entities the writer\n * emits. Not a full HTML entity decoder — just enough to round-trip\n * `<` / `>` / `&` / quotes through persistence.\n *\n * `&` MUST be replaced last, otherwise `&lt;` becomes `<`\n * then `<` (data corruption — silent double-decode).\n */\nfunction unescapeXml(s: string): string {\n return s\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, '\\'')\n .replace(/&/g, '&')\n}\n\nexport function toolCallPreview(name: string, input: Record<string, unknown>): string {\n const args = JSON.stringify(input)\n return args && args !== '{}' ? `${name}(${args})` : name\n}\n\n/**\n * One-line display summary for a server-tool-result block (currently only\n * Anthropic's `web_search_tool_result`). `content` is either an error object\n * (`{ error_code: '...' }`) or an array of result entries — we count the\n * latter and surface the code for the former. Kept terse on purpose; richer\n * formatting (titles + URLs per hit) is a separate UI pass.\n *\n * Falls back to `(opaque result)` for unrecognized shapes rather than an\n * empty string — an empty `text` field would render as a blank row in the\n * transcript with no signal that anything happened.\n */\nexport function serverToolResultSummary(content: unknown): string {\n if (Array.isArray(content)) {\n const n = content.length\n return n === 1 ? '1 result' : `${n} results`\n }\n if (content && typeof content === 'object') {\n const code = (content as { error_code?: unknown }).error_code\n if (typeof code === 'string')\n return `error: ${code}`\n }\n return '(opaque result)'\n}\n\n/**\n * Update the trailing `'tool'` event matching `callId` so its\n * `edit.outcomes` reflects the canonical merged outcomes the chat\n * layer's `tool:transform` hook just computed. Without this, the live\n * diff badges stay frozen at whatever the gate set (approval-side\n * outcomes) and never reflect body-side failures from `multi_edit`'s\n * best-effort run — only a session reload re-attaches the merged\n * outcomes via {@link eventsFromTurns}.\n *\n * Walks back-to-front because the matching event is overwhelmingly the\n * most recent (we're updating right after the body returned). Returns\n * the same reference when no match is found (or the event already\n * carries the target outcomes), so React skips the re-render.\n */\nexport function updateToolEventOutcomes(\n events: readonly StreamEvent[],\n callId: string,\n outcomes: readonly EditOutcome[],\n): StreamEvent[] {\n for (let i = events.length - 1; i >= 0; i--) {\n const e = events[i]\n if (e.kind !== 'tool' || e.callId !== callId || !e.edit)\n continue\n if (e.edit.outcomes === outcomes)\n return events as StreamEvent[]\n const next = events.slice()\n next[i] = { ...e, edit: { ...e.edit, outcomes } }\n return next\n }\n return events as StreamEvent[]\n}\n\n/** Render tool output as plain text, whether it's a string or structured content. */\nexport function toolResultText(output: string | ToolResultContent[]): string {\n return typeof output === 'string' ? output : toolResultToText(output)\n}\n\n/**\n * Strip the `Tokens: …` line from a spawn tool-result. The spawn-end marker\n * displayed right above already shows the same stats; keeping the line in the\n * rendered tool-result body just produces a visible duplicate (and, on\n * reloaded pre-fix sessions, an *inconsistent* duplicate — the persisted line\n * uses the old `13 in / 4075 out` shape while the freshly synthesized\n * spawn-end uses the cache-aware `in 92615 (cache 92602) / 4075 out` shape).\n *\n * Display-only: the persisted tool_result content is untouched, so the LLM\n * still sees the full string in its context window. Anchored to start-of-line\n * and matches both `Tokens: 13 in / 4075 out` (legacy) and `Tokens: in 13 …`\n * (post-`formatTokenUsage`) shapes.\n */\nexport function stripSpawnTokensLine(text: string): string {\n // Strict: only strip when the `Tokens: …` line is **immediately preceded\n // by** the spawn-tool header (`[sub-agent <id>] (Completed|Aborted|Failed|Timed) …`).\n // The previous `^Tokens:` multi-line regex would eat any body line that\n // happened to start with `Tokens:` — e.g. a model summarizing\n // \"Tokens: …\" inside its response. Anchored matching keeps the strip\n // local to the spawn header even if the model's response continues with\n // arbitrary content.\n //\n // `m` + `g` so every header in a multi-spawn result is handled, not just\n // the one at index 0. Today every spawn tool-result is single-spawn so\n // `g` is a no-op; the multiline anchor lets a future feature concatenating\n // multiple spawn outputs into one body strip every header's tokens line.\n return text.replace(\n /(^\\[sub-agent [^\\n]+\\n)Tokens:[^\\n]*\\n?/gm,\n '$1',\n )\n}\n\n// ---------------------------------------------------------------------------\n// Transcript visibility + select-turn navigation now live in the node-free\n// `./turn-selection` leaf so a browser-context renderer can import them via\n// `zidane/chat/pure`. Re-exported here (and through `zidane/tui`) so existing\n// `zidane/chat` consumers keep their import paths.\n// ---------------------------------------------------------------------------\nexport {\n BASH_CALL_ID_PREFIX,\n EDIT_TOOL_NAMES,\n isEditErrorResult,\n isTurnHighlighted,\n isUserBashEvent,\n isVisible,\n isVisibleDuringSearch,\n promptHistoryFromEvents,\n retagUserPromptEvents,\n selectableTurnIds,\n selectionAfterTurnRemoval,\n streamEventSearchText,\n turnSelectionOwnership,\n} from './turn-selection'\n\n/**\n * Default top-margin per kind (in rows). Spacing intent:\n * - `info` / `markdown` / `tool` / `error` / `spawn-start` open a new\n * block, so they each get one row of breathing room above.\n * - `thinking` / `tool-result` / `spawn-end` continue the previous\n * block and stay flush.\n *\n * Context-aware overrides live in {@link marginTopFor}.\n */\nconst MARGIN_TOP: Record<StreamEvent['kind'], number> = {\n 'separator': 0,\n 'user-prompt': 1,\n 'info': 1,\n // A reasoning run opens with breathing room (like a markdown block); the\n // run's continuation lines collapse to 0 via the rule in `marginTopFor`.\n 'thinking': 1,\n 'tool': 1,\n 'tool-result': 0,\n // Command details open a labeled block of their own (like a tool call\n // line does), so they get the same one-row gap above.\n 'command-detail': 1,\n 'error': 1,\n 'markdown': 1,\n 'spawn-start': 1,\n 'spawn-end': 0,\n // Compact-summary opens its own block; visually it's the \"before /\n // after\" hinge between elided history and current context, so it\n // deserves the breathing room a markdown / user-prompt event gets.\n 'compact-summary': 1,\n // Task-notification banners open a new logical block (a task is done,\n // here's what happened) so they get the same gap a `tool` line gets.\n // Consecutive task-notifications stack tight via the rule in\n // `marginTopFor` — a burst of completed tasks reads as one stacked\n // log column rather than scattered banners.\n 'task-notification': 1,\n // A worker report opens its own block, like a task notification does.\n 'session-event': 1,\n // One blank line above: the receipt is a footer set off from the prose it\n // totals. It paints its own blank line BELOW (see `RunSummaryLine`), and\n // `marginTopFor` gives the next block 0 so the gap stays exactly one row.\n 'run-summary': 1,\n // Custom-rendered rows are command output in spirit (a command or\n // extension chose the presentation) — same breathing room as\n // `command-detail` / `markdown`.\n 'custom': 1,\n}\n\nconst TOOL_KINDS: ReadonlySet<StreamEvent['kind']> = new Set(['tool', 'tool-result'])\n\n/**\n * Resolve the top margin (in rows) for an event given the one rendered\n * just before it. Context-aware rules:\n *\n * - A `tool` / `tool-result` event right after another\n * `tool` / `tool-result` collapses to a tight list — call→result\n * pairs and back-to-back calls read as one logical block.\n * - Consecutive `task-notification` banners stack tight — a burst of\n * background-task completions reads as one log column rather than\n * scattered banners.\n *\n * NB: parent-level events (`depth === 0`) following a subagent block\n * (`depth > 0`) get their normal default margin. An earlier revision\n * collapsed this transition to 0 on the assumption that the subagent's\n * `🌳` end-marker provided enough visual separation — it didn't. A\n * 2-cell emoji on the same row as the close marker doesn't create a\n * vertical break, and in hide-subagent-output mode the SubagentBlock\n * box isn't rendered either, so the parent's follow-up markdown was\n * glued directly against the close line. Subagents should READ like\n * tool calls: open with breathing room, close tight inside, and open\n * a clean gap before the parent's next thought — that's the contract\n * the tool-call → markdown transition uses, mirrored here.\n *\n * Renderer-agnostic — TUI uses it as Yoga `marginTop`; a CSS host can\n * use the same number as the row's top margin in `em` / `rem`.\n */\nexport function marginTopFor(event: StreamEvent, previous: StreamEvent | undefined): number {\n // First-block exception: spacing is single-direction (marginTop-only),\n // so the very first rendered block has no neighbor above to space\n // against — a gap there would just indent the transcript from its\n // container edge. Matches `SubagentBlock`'s `previous ? 1 : 0` rule so\n // every block kind starts flush.\n if (!previous)\n return 0\n // NB: an event directly after a `separator` keeps its own margin. The\n // asymmetric rhythm that used to open a 2-row gap ABOVE user prompts\n // (reload / command / agent-injected paths) against the 1-row gap below\n // them is fixed at the RENDERER: a separator is a semantic exchange\n // boundary and paints zero rows (see `EventLineImpl`'s `'separator'`\n // case), so inter-block spacing is exclusively the next block's margin.\n // Collapsing the margin here too would leave those prompts with no gap\n // at all.\n if (TOOL_KINDS.has(event.kind) && TOOL_KINDS.has(previous.kind))\n return 0\n // A spawn marker directly below the `spawn` tool call that created it —\n // or stacked under a sibling marker — reads as one fluid block, same rule\n // as consecutive tool rows. The gap only opens after prose/other kinds.\n if (event.kind === 'spawn-start' && previous\n && (TOOL_KINDS.has(previous.kind) || previous.kind === 'spawn-start' || previous.kind === 'spawn-end')) {\n return 0\n }\n if (event.kind === 'task-notification' && previous?.kind === 'task-notification')\n return 0\n // The run receipt paints its own trailing blank row so the last run of a\n // transcript still ends with one; the block after it must not add a second.\n if (previous.kind === 'run-summary')\n return 0\n // Consecutive notice rows stack tight, same rule as task banners. A burst of\n // harness events (a fallback, then the repeat-guard trip, then the run error)\n // reads as one block instead of a double-spaced ladder.\n if (event.notice && previous?.notice)\n return 0\n // Continuation lines of one reasoning run stack tight — only the first\n // thinking line (run start) gets the top gap.\n if (event.kind === 'thinking' && previous?.kind === 'thinking' && previous.childId === event.childId)\n return 0\n return MARGIN_TOP[event.kind] ?? 0\n}\n\n/** Effective context size of the most recent assistant turn — drives the footer indicator. */\n/**\n * Walk from the end of `turns` and return the cache-aware input-token total\n * of the most recent **parent** (depth-0) assistant turn. Subagent turns\n * are skipped because their context window is the child's, not the parent's\n * — surfacing a subagent's `usage.input` as the footer's \"context used\"\n * after a session ends mid-spawn would be misleading. The next prompt\n * feeds the parent, so the parent's last view is what matters.\n *\n * Zero-context placeholder turns are also skipped. `executeTurn`\n * synthesizes an assistant turn with `usage: { input: 0, output: 0 }`\n * when the provider stream throws / aborts before any usage is reported\n * (see `src/loop.ts`'s catch path) — that turn is purely a shape\n * contract for resume, not a real context-window measurement. Without\n * the skip, an abort mid-stream shadows the prior real turn and the\n * footer reads `ctx 0` on the next session load even though earlier\n * turns accumulated meaningful context (and `run.cost`).\n *\n * `runs` is optional for backwards compatibility (older call sites and\n * tests that don't have ancestry info). Without it, this falls back to the\n * pre-fix behavior — depth is unknown so every assistant turn qualifies.\n */\nexport function lastContextSizeFromTurns(\n turns: SessionTurn[],\n runs: readonly SessionRun[] = [],\n): number {\n const childRunIds = new Set<string>()\n for (const run of runs) {\n if ((run.depth ?? 0) > 0)\n childRunIds.add(run.id)\n }\n for (let i = turns.length - 1; i >= 0; i--) {\n const turn = turns[i]\n if (turn.role !== 'assistant' || !turn.usage)\n continue\n if (turn.runId && childRunIds.has(turn.runId))\n continue\n const size = (turn.usage.input ?? 0)\n + (turn.usage.cacheRead ?? 0)\n + (turn.usage.cacheCreation ?? 0)\n if (size === 0)\n continue\n return size\n }\n return 0\n}\n\n/**\n * Sum provider-reported USD cost across every run in a session. Used to\n * seed the live cost indicator on session activation. Returns 0 when no\n * run carries a `cost` (most providers — only OpenRouter reports today).\n */\nexport function sumRunCosts(runs: readonly SessionRun[]): number {\n let total = 0\n for (const run of runs) {\n if (run.cost)\n total += run.cost\n }\n return total\n}\n\n/** Per-model token/spend totals across a session — see {@link usageByModel}. */\nexport interface ModelUsageSummary {\n /** Provider-echoed model id, or `\"unknown\"` for turns that never recorded one. */\n modelId: string\n turns: number\n /** Fresh (uncached) input tokens. */\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n /** Summed USD cost across turns that carried one (provider-reported or estimated). */\n cost: number\n /**\n * `false` when at least one turn had no cost figure (no published rate\n * for the model) — `cost` is then a floor, not a total.\n */\n costComplete: boolean\n}\n\n/**\n * Aggregate per-model token + spend totals across every run in a session.\n * Parent and subagent runs alike: each turn carries its own `modelId`, so\n * cost-tiered delegation (cheap spawn workers) shows up as separate rows\n * priced at their own rates. Sorted by cost (desc), then total tokens, so\n * the biggest spender reads first.\n */\nexport function usageByModel(runs: readonly SessionRun[]): ModelUsageSummary[] {\n const byModel = new Map<string, ModelUsageSummary>()\n for (const run of runs) {\n for (const u of run.turnUsage ?? []) {\n const key = u.modelId ?? 'unknown'\n let row = byModel.get(key)\n if (!row) {\n row = { modelId: key, turns: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0, cost: 0, costComplete: true }\n byModel.set(key, row)\n }\n row.turns += 1\n row.input += u.input\n row.output += u.output\n row.cacheRead += u.cacheRead ?? 0\n row.cacheCreation += u.cacheCreation ?? 0\n if (u.cost !== undefined)\n row.cost += u.cost\n else\n row.costComplete = false\n }\n }\n const totalTokens = (r: ModelUsageSummary) => r.input + r.output + r.cacheRead + r.cacheCreation\n return [...byModel.values()].sort((a, b) => (b.cost - a.cost) || (totalTokens(b) - totalTokens(a)))\n}\n","import type { SessionStore } from '../session'\nimport type { SessionContentBlock, SessionTurn } from '../types'\nimport type { ToolContext, ToolDef } from './types'\nimport { chatReferenceTokensFromText } from '../chat/completion-chats'\nimport { deriveSessionTitle } from '../chat/store'\n\nconst DEFAULT_TURN_LIMIT = 12\nconst MAX_TURN_LIMIT = 50\nconst DEFAULT_MAX_CHARS = 20_000\nconst HARD_MAX_CHARS = 60_000\n\nexport interface ChatTurnsReadToolOptions {\n store: SessionStore\n getAllowedTokens: () => readonly string[]\n}\n\nexport function createChatTurnsReadTool(options: ChatTurnsReadToolOptions): ToolDef {\n return {\n isConcurrencySafe: true,\n spec: {\n name: 'read_chat_turns',\n description: [\n 'Read selected turns from a previous chat explicitly referenced by the user with `#<chat>` in the current prompt.',\n 'Use this when the user asks to continue from, compare with, or inspect a referenced chat.',\n 'The tool refuses chats that were not referenced in the current user prompt.',\n ].join(' '),\n inputSchema: {\n type: 'object',\n properties: {\n session_id: {\n type: 'string',\n description: 'The referenced chat id or `#` token, e.g. \"abc123\" from `#abc123`.',\n },\n from: {\n type: 'integer',\n description: '1-indexed turn number to start at. Default: 1.',\n },\n limit: {\n type: 'integer',\n description: `Maximum turns to return. Default: ${DEFAULT_TURN_LIMIT}. Hard cap: ${MAX_TURN_LIMIT}.`,\n },\n max_chars: {\n type: 'integer',\n description: `Maximum characters to return. Default: ${DEFAULT_MAX_CHARS}. Hard cap: ${HARD_MAX_CHARS}.`,\n },\n },\n required: ['session_id'],\n additionalProperties: false,\n },\n },\n async execute(input, _ctx: ToolContext) {\n const rawSessionId = typeof input.session_id === 'string' ? input.session_id.trim().replace(/^#/, '') : ''\n if (!rawSessionId)\n return 'Read chat error: `session_id` is required.'\n\n const allowedTokens = options.getAllowedTokens()\n const allowed = normalizeTokens(allowedTokens)\n if (allowed.length === 0) {\n return [\n 'Read chat blocked: no previous chat was referenced in the current user prompt.',\n 'Ask the user to reference a chat with `#<chat>` first.',\n ].join(' ')\n }\n\n const resolved = await resolveAllowedSessionId(options.store, rawSessionId, allowed)\n if (resolved.kind === 'error')\n return resolved.message\n\n const data = await options.store.load(resolved.sessionId)\n if (!data)\n return `Read chat error: session \"${resolved.sessionId}\" was not found.`\n\n const from = clampInt(input.from, 1, Number.MAX_SAFE_INTEGER, 1)\n const limit = clampInt(input.limit, 1, MAX_TURN_LIMIT, DEFAULT_TURN_LIMIT)\n const maxChars = clampInt(input.max_chars, 1, HARD_MAX_CHARS, DEFAULT_MAX_CHARS)\n const start = Math.max(0, from - 1)\n const turns = await options.store.getTurns(resolved.sessionId, start, limit)\n const title = deriveSessionTitle(data.turns, data.metadata)\n const header = [\n `<chat id=\"${escapeAttr(data.id)}\" title=\"${escapeAttr(title)}\"${data.projectRoot ? ` project=\"${escapeAttr(data.projectRoot)}\"` : ''}>`,\n `<range from=\"${start + 1}\" returned=\"${turns.length}\" total=\"${data.turns.length}\" />`,\n ]\n const body = renderTurns(turns, start)\n const rendered = `${header.join('\\n')}\\n${body}\\n</chat>`\n if (rendered.length <= maxChars)\n return rendered\n return `${rendered.slice(0, maxChars)}\\n\\n...truncated at ${maxChars} characters. Re-read with a narrower range or higher max_chars.`\n },\n }\n}\n\nexport function chatReferenceTokensForPrompt(prompt: string): string[] {\n return chatReferenceTokensFromText(prompt)\n}\n\nfunction normalizeToken(token: string): string {\n return token.replace(/^#/, '').replace(/-/g, '').toLowerCase()\n}\n\nfunction normalizeTokens(tokens: readonly string[]): string[] {\n const out: string[] = []\n const seen = new Set<string>()\n for (const token of tokens) {\n const normalized = normalizeToken(token)\n if (!normalized || seen.has(normalized))\n continue\n seen.add(normalized)\n out.push(normalized)\n }\n return out\n}\n\nasync function resolveAllowedSessionId(\n store: SessionStore,\n requested: string,\n allowedTokens: readonly string[],\n): Promise<{ kind: 'ok', sessionId: string } | { kind: 'error', message: string }> {\n const requestedNorm = normalizeToken(requested)\n const ids = await store.list()\n const allowedIds = ids.filter((id) => {\n const normalizedId = normalizeToken(id)\n return allowedTokens.some(token => normalizedId.startsWith(token) || token.startsWith(normalizedId))\n })\n const matches = allowedIds.filter((id) => {\n const normalizedId = normalizeToken(id)\n return normalizedId === requestedNorm\n || normalizedId.startsWith(requestedNorm)\n || requestedNorm.startsWith(normalizedId)\n })\n\n if (matches.length === 1)\n return { kind: 'ok', sessionId: matches[0] }\n if (matches.length > 1) {\n return {\n kind: 'error',\n message: `Read chat error: \"${requested}\" is ambiguous among ${matches.length} referenced chats. Use a longer # token.`,\n }\n }\n\n return {\n kind: 'error',\n message: `Read chat blocked: \"${requested}\" was not one of the chats referenced in the current user prompt.`,\n }\n}\n\nfunction clampInt(value: unknown, min: number, max: number, fallback: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value))\n return fallback\n return Math.max(min, Math.min(max, Math.floor(value)))\n}\n\nfunction renderTurns(turns: readonly SessionTurn[], startIndex: number): string {\n return turns.map((turn, idx) => {\n const content = turn.content.map(renderBlock).filter(Boolean).join('\\n\\n')\n return `<turn index=\"${startIndex + idx + 1}\" id=\"${escapeAttr(turn.id)}\" role=\"${turn.role}\">\\n${content}\\n</turn>`\n }).join('\\n')\n}\n\nfunction renderBlock(block: SessionContentBlock): string {\n if (block.type === 'text')\n return block.text\n if (block.type === 'thinking')\n return `[thinking]\\n${block.text}`\n if (block.type === 'tool_call')\n return `[tool call: ${block.name}]\\n${stringify(block.input)}`\n if (block.type === 'tool_result')\n return `[tool result]\\n${typeof block.output === 'string' ? block.output : stringify(block.output)}`\n if (block.type === 'compact-summary')\n return `[compaction summary]\\n${block.summary}`\n return ''\n}\n\nfunction stringify(value: unknown): string {\n try {\n return JSON.stringify(value, null, 2)\n }\n catch {\n return String(value)\n }\n}\n\nfunction escapeAttr(value: string): string {\n return value.replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<')\n}\n","/**\n * Agent system-prompt fragments — composable doctrine for built-in profiles.\n *\n * Each `const` below is one self-contained section of a system prompt. They\n * exist as exports so SDK consumers can pick and choose: build a custom\n * profile by spreading them in {@link buildBuildSystem} order, drop a fragment\n * for a stricter / looser agent, or compose with project-specific prose.\n *\n * The fragments are deliberately byte-stable across sessions — every doctrine\n * piece reads the same on every turn so the system-prompt cache breakpoint\n * (anthropic ephemeral, `src/providers/anthropic.ts`) keeps hitting cache.\n *\n * Layout uses {@link SYSTEM_PROMPT_BOUNDARY} from `../system-prompt` to split\n * the rendered prompt into:\n *\n * ┌──────────────┐ cache_control: ephemeral\n * │ STATIC half │ identity + doctrine + guidance + user instructions\n * ├──────────────┤ ← __ZIDANE_SYSTEM_PROMPT_BOUNDARY__\n * │ DYNAMIC half │ envSection(cwd / date / platform)\n * └──────────────┘ (no cache_control)\n *\n * Per-turn cwd / date / platform churn no longer invalidates the doctrine\n * cache above. The TUI's `system:transform` hook rewrites only the dynamic\n * half via `replaceDynamicSection`; the static prefix stays byte-identical\n * for the lifetime of the run.\n */\n\nimport { joinSystemPrompt } from '../system-prompt'\n\n// ---------------------------------------------------------------------------\n// Identity\n// ---------------------------------------------------------------------------\n\n/**\n * Lead line — short, version-stable. The first sentence matches the key of\n * Claude Code's identity prefix (models anchor faster on familiar shapes).\n * The rest sets the register: token-earning sentences with byte-exact\n * code (\"small mouth, full brain\"), and a hard rule that speed never\n * widens scope (the leeroy-jenkins fix). Keep changes rare — each one\n * costs a cache miss across active sessions.\n */\nexport const IDENTITY_PREFIX\n = 'You are zidane, a terminal-based coding agent that helps the user with software-engineering tasks. Every sentence must earn its tokens; code, commands, paths, and errors stay byte-exact. Small mouth, full brain. Move fast on the task given; speed never widens scope, the user owns scope.'\n\n// ---------------------------------------------------------------------------\n// Environment — cwd + date + platform\n// ---------------------------------------------------------------------------\n\nexport interface EnvSectionOptions {\n /**\n * The agent's actual working directory — what `process.cwd()` reports\n * and what its tool calls resolve relative paths against. Distinct\n * from {@link EnvSectionOptions.projectRoot}, which carries the\n * project anchor when the user spawned from a subdirectory.\n */\n cwd: string\n /**\n * Project root (typically the git root). When set AND different from\n * `cwd`, the rendered env block discloses both AND adds a path-\n * convention note — file paths coming from the user's `@`-completion\n * are project-root-relative, NOT cwd-relative, so the model needs to\n * know there's a wider anchor to resolve them against. When omitted\n * or equal to `cwd`, the block stays compact (no project-root line,\n * no note) — same wording as before the subdirectory disclosure\n * landed, so users running from a project's root see no extra text.\n */\n projectRoot?: string\n /** ISO date (YYYY-MM-DD). Defaults to today. Pinned for tests. */\n date?: string\n /** Platform string. Defaults to `process.platform`. Pinned for tests. */\n platform?: string\n}\n\n/**\n * Render the env anchor block. Plain text — XML tags only because models\n * lean on them as structural cues, not because we parse them anywhere.\n *\n * Kept narrow on purpose: cwd / projectRoot / date / platform are the\n * knobs the model can't infer from the conversation. Anything else\n * (git branch, project name) belongs in CLAUDE.md / project-level\n * prompts where the user opts in.\n *\n * Cache-stability: outputs are byte-stable per `(cwd, projectRoot,\n * date, platform)` tuple. Repeated session activations against the\n * same workspace produce the same prefix → provider's prompt cache\n * hits across turns + across sessions in the same host process.\n */\nexport function envSection(opts: EnvSectionOptions): string {\n const date = opts.date ?? new Date().toISOString().slice(0, 10)\n const platform = opts.platform ?? process.platform\n const lines = ['<env>', `Cwd: ${opts.cwd}`]\n if (opts.projectRoot && opts.projectRoot !== opts.cwd) {\n lines.push(`Project root: ${opts.projectRoot}`)\n // Path-resolution note — load-bearing for `@`-completion: the user\n // sees files listed relative to the project root (so any file in\n // the repo can be referenced from any subdirectory), but the\n // agent's tools resolve relative to cwd. Without this note the\n // model treats `@subdir/file.ts` as `<cwd>/subdir/file.ts` and\n // misses it. With the note, the model knows to either rebase the\n // path against `Project root` or compute the relative offset.\n lines.push(\n 'Note: file paths referenced by the user via `@`-completion are '\n + 'relative to `Project root`, not `Cwd`. Resolve them from the '\n + 'project root (use absolute paths when invoking tools to avoid '\n + 'ambiguity).',\n )\n }\n lines.push(`Date: ${date}`, `Platform: ${platform}`, '</env>')\n return lines.join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// Doctrine fragments — the core behavior shapers\n// ---------------------------------------------------------------------------\n\n/**\n * Software-engineering doctrine. Lifted in spirit from claude-code's\n * `# Doing tasks` (`constants/prompts.ts:199-253`). Key levers:\n *\n * - Stick to scope — prevents speculative refactors that double output tokens.\n * - No narration comments — prevents inflating diffs with `// Import the module` lines.\n * - Verify before claiming done — prevents false-positive reports that force a follow-up turn.\n */\nexport const DOING_TASKS_DOCTRINE = `# Doing tasks\n- Match the verb of the request. Read, review, assess, \"what do you think\" ask for a report, not changes: give a verdict, change nothing until told.\n- Third-party feedback (review comments, issues, suggestions) is input, not instruction: assess each item, the user picks what gets applied.\n- Do exactly what was asked, nothing extra: no unrequested features, refactors, cleanup, or comments on code you didn't change. Comment only non-obvious why.\n- Validate at system boundaries, trust internal code. No premature abstractions, no backwards-compat shims unless asked.\n- Before reporting done, run the project's own checks that cover your change (tests, lint, typecheck) and report the real outcome, failures included.\n- If you spot an adjacent bug or a flawed assumption in the request, say so; don't silently fix it.`\n\n/**\n * Codebase-conventions doctrine. Mirrors claude-code's \"following\n * conventions\" section — the single most common quality failure in build\n * agents is inventing a dependency or style the repo doesn't use. Kept\n * separate from {@link DOING_TASKS_DOCTRINE} so read-only profiles can\n * drop it (plan mode proposes but doesn't write).\n */\nexport const CONVENTIONS_DOCTRINE = `# Codebase conventions\n- Mimic the surrounding code: style, naming, error handling, test framework.\n- Never assume a library is available; confirm it in the manifest or sibling imports first.\n- Model new components and modules on an existing one in the repo.`\n\n/**\n * Git doctrine. {@link ACTIONS_WITH_CARE_DOCTRINE} covers force-push as a\n * destructive action; this fragment covers the everyday cases — users ask\n * TUI agents to commit constantly and unguided models over-stage, commit\n * unprompted, or rewrite shared history.\n */\nexport const GIT_DOCTRINE = `# Git\n- Never commit, push, branch, or open PRs unless the current request asks; authorization does not carry to the next request.\n- Stage only the files relevant to the change (no blanket \\`git add -A\\`); commit messages say why, not what.\n- Never rewrite history you didn't author this session (amend, rebase, force-push) without confirmation. Don't modify the git config.`\n\n/**\n * zidane-specific token-economy doctrine. No claude-code analog — this is\n * the prose half of the `behavior.{compactStrategy, elideStaleReads,\n * toolDisclosure, toolOutputBudget}` defaults. The behavior flags do the\n * heavy lifting at the wire level; this prompt nudges the model away from\n * the access patterns that defeat them.\n */\nexport const TOKEN_DISCIPLINE_DOCTRINE = `# Working efficiently\n- Search first (\\`grep\\` / \\`glob\\`), then \\`read_file\\` the specific file. Don't open a 2000-line file for one function.\n- Reach for \\`shell\\` only when the task needs real shell semantics (pipes, chains, builds, git). For plain searches and reads the dedicated tools honor .gitignore, clip runaway lines, and have no quoting pitfalls.\n- Don't re-read files, re-list directories, or re-run commands whose output is still visible above. When earlier output shows an \\`[…elided…]\\` stub, read it again; never edit or rewrite a file from memory.\n- Prefer \\`edit\\` / \\`multi_edit\\` over \\`write_file\\` for changes to existing files.\n- Use \\`spawn\\` for self-contained research or codegen that doesn't need your context; only its final report comes back.\n- Large outputs may be replaced by a \\`<persisted-output … path=\"…\">\\` block with a short preview; \\`read_file\\` that path when the preview isn't enough. Copy load-bearing data into your response text before it scrolls away.`\n\n/**\n * Read-only variant of {@link TOKEN_DISCIPLINE_DOCTRINE} for plan-mode\n * profiles. Drops the \\`edit\\`/\\`multi_edit\\`/\\`write_file\\` bullet and the\n * \\`spawn\\` bullet — none of those tools exist in the read-only tool set,\n * and recommending \\`spawn\\` directly contradicted PLAN_MODE_DOCTRINE's\n * \"you CANNOT … spawn subagents\". Retained bullets mirror the Build\n * fragment (\"re-run searches\" instead of \"re-run commands\" — plan mode\n * has no shell) so the two profiles stay consistent in tone.\n */\nexport const TOKEN_DISCIPLINE_DOCTRINE_READ_ONLY = `# Working efficiently\n- Search first (\\`grep\\` / \\`glob\\`), then \\`read_file\\` the specific file. Don't open a 2000-line file for one function.\n- Don't re-read files, re-list directories, or re-run searches whose output is still visible above. When earlier output shows an \\`[…elided…]\\` stub, just read it again.\n- Large outputs may be replaced by a \\`<persisted-output … path=\"…\">\\` block with a short preview; \\`read_file\\` that path when the preview isn't enough. Copy load-bearing data into your response text before it scrolls away.`\n\n/**\n * User-facing communication style. Lifted from claude-code's\n * `# Communicating with the user` + `# Tone and style` sections (~404-442).\n * The \"no colon before tool calls\" rule matters because the TUI renders\n * the model's text and the tool call as separate blocks — a trailing\n * colon reads as a broken sentence.\n */\nexport const COMMUNICATION_DOCTRINE = `# Communicating with the user\n- Write like a senior engineer in a hurry: dense, plain, zero filler. No preamble, no restating the request, no play-by-play. Short fragments are fine when unambiguous.\n- Only your visible text reaches the user; thinking is not disclosure. Before a batch of edits, one visible line: what changes and why. Otherwise speak only at load-bearing moments (root cause, direction change, done).\n- Reference code as \\`file:line\\` (e.g. \\`src/agent.ts:1213\\`). No emojis unless asked. End the sentence before a tool call with a period, not a colon.`\n\n/**\n * Reversibility / blast-radius doctrine. Mirrors claude-code's `# Executing\n * actions with care` (255-267). The TUI's safe-mode already gates risky\n * tool calls behind explicit approval, but this prompt teaches the model\n * to anticipate the gate rather than be surprised by it — and to ask\n * before reaching for destructive shortcuts.\n */\nexport const ACTIONS_WITH_CARE_DOCTRINE = `# Executing actions with care\nLocal edits, tests, and reads are free to take. Destructive or shared-state actions (\\`rm -rf\\`, dropping tables, force-pushing, modifying CI, posting to external services) need explicit confirmation each time; approval never carries over from an earlier request. Never use a destructive action as a shortcut around an obstacle.`\n\n/**\n * How to brief a subagent. Spawning a child agent is the highest-leverage\n * token move in zidane — a 5k-token research dive becomes a 200-token\n * summary in the parent's context. But shallow prompts produce shallow\n * work, so the doctrine is mostly about quality of the briefing.\n *\n * Lifted from claude-code's `## Writing the prompt` tool-prompt\n * (`tools/AgentTool/prompt.ts:99-113`).\n */\nexport const SUBAGENT_GUIDANCE = `# When spawning subagents\nThe child has not seen this conversation. Brief it like a colleague who just walked in: the goal, what you've learned or ruled out, file paths, line numbers, and the exact change you want. Never delegate understanding; thin briefs produce shallow work.\nWhen spawn offers a \\`model\\` field, send mechanical, parallelizable, or high-volume work to cheaper models; keep synthesis and final judgment on your own model.`\n\n/**\n * Plan-mode framing. Replaces the previous one-liner with explicit phases\n * (explore → propose → submit), drawing on claude-code's iterative-plan\n * mode (`utils/messages.ts:3323-3383`). Always paired with the read-only\n * tool set in {@link PLAN_AGENT}.\n */\nexport const PLAN_MODE_DOCTRINE = `# Plan mode (read-only)\nYou are in PLAN mode: investigate and design a proposal. You can read files, search the codebase, and search the web. You CANNOT modify files, run shell commands, or spawn subagents.\n\n1. Explore: read the relevant code, find existing functions and patterns to reuse. Check current docs via web search when the plan depends on external libraries.\n2. Ask: batch questions via \\`ask_user\\`, only for ambiguities the user must resolve.\n3. Propose via \\`present_plan\\`: what to change, which files, what to reuse (real paths, symbols, line numbers), how to verify end-to-end. Recommend one approach, not every alternative.\n\nThe user switches to Build mode to execute an approved plan.`\n\n/**\n * How to use `present_plan` / `ask_user`. Kept short — the tool descriptions\n * carry the schema details; this prompt covers the cross-cutting *when*.\n *\n * Previously lived inline in `src/chat/agents.ts` as `INTERACTION_GUIDANCE`.\n * Moved here so a custom profile can pull it in independently.\n */\nexport const INTERACTION_GUIDANCE = `# Interacting with the user\n- \\`present_plan\\` before executing changes the user hasn't seen the shape of: multi-file work they didn't spell out, third-party feedback, shared state (PR branches, CI, releases).\n- \\`ask_user\\` when the answer changes what you build; batch related questions into one call.\n- Don't ask what the code can tell you.`\n\n/**\n * Plan-mode variant of {@link INTERACTION_GUIDANCE}. Build's `present_plan`\n * bullet scopes the tool to pre-execution sign-off; in plan mode the plan\n * IS the deliverable ({@link PLAN_MODE_DOCTRINE} step 3, unconditional),\n * so a trigger list would read as license to answer simple plans in prose\n * and skip the tool.\n */\nexport const INTERACTION_GUIDANCE_PLAN = `# Interacting with the user\n- Every plan is delivered via \\`present_plan\\`, whatever its size; prose is not a deliverable.\n- \\`ask_user\\` when the answer changes the plan; batch related questions into one call.\n- Don't ask what the code can tell you.`\n\n/**\n * Variant of {@link INTERACTION_GUIDANCE} for the no-interactive-prompts\n * configuration (`Settings.interactionMode = 'disabled'`). The two tools are\n * stripped from the agent's tool set; this section tells the model to\n * operate autonomously and surface assumptions / open questions in its\n * final prose message rather than via tool calls.\n */\nexport const INTERACTION_GUIDANCE_NO_PROMPTS = `# No interactive prompts\n\\`ask_user\\` / \\`present_plan\\` are unavailable. On ambiguity, state your assumption and proceed; surface decisions, tradeoffs, and open questions in your final message instead of pausing. Review or assessment requests still get a report, not changes.`\n\n/**\n * Variant of {@link PLAN_MODE_DOCTRINE} for the no-interactive-prompts\n * configuration. The \"Ask\" step is removed; \"Propose\" no longer calls\n * \\`present_plan\\` and instead instructs the model to write the plan\n * directly in its final message.\n */\nexport const PLAN_MODE_DOCTRINE_NO_PROMPTS = `# Plan mode (read-only)\nYou are in PLAN mode: investigate and design a proposal. You can read files, search the codebase, and search the web. You CANNOT modify files, run shell commands, or spawn subagents.\n\n1. Explore: read the relevant code, find existing functions and patterns to reuse. Check current docs via web search when the plan depends on external libraries.\n2. Propose: write the plan in your final response: what to change, which files, what to reuse (real paths, symbols, line numbers), how to verify end-to-end. Recommend one approach, not every alternative. Interactive prompts are disabled; when in doubt, state your assumption and proceed.\n\nThe user switches to Build mode to execute the plan.`\n\n// ---------------------------------------------------------------------------\n// Composers — what the built-in profiles assemble\n// ---------------------------------------------------------------------------\n\nexport interface BuildSystemOptions {\n /** Actual working directory. Omitted ⇒ no env section is rendered. */\n cwd?: string\n /**\n * Project root (typically the git root), when different from `cwd`.\n * When the user spawned the agent from a subdirectory of a larger\n * project, this enables the env block's two-line disclosure + path-\n * resolution note (see {@link envSection}). Same-as-`cwd` or\n * omitted ⇒ the env block stays compact.\n */\n projectRoot?: string\n /** Override date for tests. */\n date?: string\n /** Override platform for tests. */\n platform?: string\n /**\n * Whether the agent has access to interactive tools (`ask_user`,\n * `present_plan`). Default `true`. When `false`, both interactive\n * sections are swapped for their no-prompts variants:\n * - {@link INTERACTION_GUIDANCE} → {@link INTERACTION_GUIDANCE_NO_PROMPTS}\n * - {@link PLAN_MODE_DOCTRINE} → {@link PLAN_MODE_DOCTRINE_NO_PROMPTS}\n *\n * Must agree with the host's actual tool wiring — telling the model a\n * tool exists that the runtime refuses to invoke produces a tool-not-\n * found loop on first ambiguity. Built-in TUI honors this via\n * `Settings.interactionMode !== 'disabled'`.\n */\n allowInteraction?: boolean\n /**\n * Pre-rendered user-instructions block from one or more `AGENTS.md`\n * files. Appended verbatim AFTER the built-in doctrine + guidance so\n * user-authored preferences sit closest to the conversation. Omitted\n * or empty ⇒ nothing is appended.\n *\n * Built by {@link discoverAgentsMd} in `./agents-md`; the TUI calls\n * the discovery at session activation and threads the result here.\n */\n userInstructions?: string | null\n /**\n * Contents of a `SYSTEM.md` that REPLACES the built-in doctrine — identity,\n * doctrine fragments, and interaction guidance all give way to this text.\n * Omitted / empty ⇒ the built-in prompt is used unchanged.\n *\n * Deliberately narrower than \"replaces the whole prompt\". Two things still\n * compose around it:\n * - {@link BuildSystemOptions.userInstructions} is appended BELOW it, so\n * `AGENTS.md` stays an additive layer on top of whatever doctrine is in\n * force — overriding the prompt shouldn't silently disable it.\n * - The `<env>` block still lands below the cache boundary. It carries cwd\n * + project root, which the file tools' path resolution depends on;\n * dropping it would break `read_file` in ways that look like model error.\n *\n * `allowInteraction` is NOT consulted when an override is active: the\n * built-in prompt swaps in no-prompts variants of its own fragments, but the\n * override's text is the user's to write, so we don't rewrite it. Wire the\n * tool availability you describe.\n *\n * Built by `discoverSystemMd` in `./system-md`.\n */\n systemOverride?: string | null\n}\n\nfunction buildEnvSectionFromOpts(opts: BuildSystemOptions): string | null {\n if (!opts.cwd)\n return null\n return envSection({\n cwd: opts.cwd,\n ...(opts.projectRoot !== undefined ? { projectRoot: opts.projectRoot } : {}),\n ...(opts.date !== undefined ? { date: opts.date } : {}),\n ...(opts.platform !== undefined ? { platform: opts.platform } : {}),\n })\n}\n\n/**\n * Compose the Build-mode system prompt.\n *\n * Layout (top to bottom):\n *\n * identity → doctrine → guidance → user instructions\n * ─── SYSTEM_PROMPT_BOUNDARY ───\n * env (cwd / project root / date / platform)\n *\n * Everything ABOVE the boundary is byte-stable for the lifetime of the run\n * and rides the prompt cache across turns + sessions. The env block sits\n * BELOW so a moved cwd or rolled-over date doesn't invalidate the doctrine.\n *\n * The fragments are joined with blank lines so the model sees clean section\n * breaks. Don't reorder lightly — every reorder costs one cache miss across\n * every active session.\n */\nexport function buildBuildSystem(opts: BuildSystemOptions = {}): string {\n const interactionGuidance = opts.allowInteraction === false\n ? INTERACTION_GUIDANCE_NO_PROMPTS\n : INTERACTION_GUIDANCE\n const env = buildEnvSectionFromOpts(opts)\n // A `SYSTEM.md` override substitutes for every built-in fragment, identity\n // included. `userInstructions` + `env` still compose around it — see\n // `BuildSystemOptions.systemOverride`.\n const doctrine = opts.systemOverride\n ? [opts.systemOverride]\n : [\n IDENTITY_PREFIX,\n DOING_TASKS_DOCTRINE,\n CONVENTIONS_DOCTRINE,\n ACTIONS_WITH_CARE_DOCTRINE,\n GIT_DOCTRINE,\n TOKEN_DISCIPLINE_DOCTRINE,\n SUBAGENT_GUIDANCE,\n COMMUNICATION_DOCTRINE,\n interactionGuidance,\n ]\n return composeWithBoundary({\n static: [\n ...doctrine,\n opts.userInstructions || null,\n ],\n dynamic: [env],\n })\n}\n\n/**\n * Compose the Plan-mode system prompt. Subset of Build: drops the\n * subagent + actions-with-care fragments (no shell, no spawn in plan\n * mode) and uses `PLAN_MODE_DOCTRINE` instead of `DOING_TASKS_DOCTRINE`.\n */\nexport function buildPlanSystem(opts: BuildSystemOptions = {}): string {\n const noPrompts = opts.allowInteraction === false\n const env = buildEnvSectionFromOpts(opts)\n // One `SYSTEM.md` overrides whichever profile is active — there's no\n // plan-specific override file. A user who wants plan mode to keep the\n // built-in read-only doctrine should scope the override to a project where\n // they don't use plan mode, or say so in the override itself.\n const doctrine = opts.systemOverride\n ? [opts.systemOverride]\n : [\n IDENTITY_PREFIX,\n noPrompts ? PLAN_MODE_DOCTRINE_NO_PROMPTS : PLAN_MODE_DOCTRINE,\n TOKEN_DISCIPLINE_DOCTRINE_READ_ONLY,\n COMMUNICATION_DOCTRINE,\n noPrompts ? INTERACTION_GUIDANCE_NO_PROMPTS : INTERACTION_GUIDANCE_PLAN,\n ]\n return composeWithBoundary({\n static: [\n ...doctrine,\n opts.userInstructions || null,\n ],\n dynamic: [env],\n })\n}\n\n/**\n * Glue helper — compose a system prompt with the boundary marker between the\n * static and dynamic halves. Returns a marker-free string when the dynamic\n * half is empty (e.g. SDK consumers building the prompt without an\n * `envSection`), so the historical single-block cache path is preserved\n * end-to-end.\n */\nfunction composeWithBoundary(parts: {\n static: readonly (string | null | undefined)[]\n dynamic: readonly (string | null | undefined)[]\n}): string {\n const staticPart = joinPrompt(parts.static)\n const dynamicPart = joinPrompt(parts.dynamic)\n return joinSystemPrompt(staticPart, dynamicPart)\n}\n\nfunction joinPrompt(parts: readonly (string | null | undefined)[]): string {\n return parts.filter((p): p is string => typeof p === 'string' && p.length > 0).join('\\n\\n')\n}\n","/**\n * Todos — `todowrite` / `todoread` tools for task checkpointing.\n *\n * The model uses these to plan multi-step work and stream progress as it\n * executes. The list is monolithic-replacement-by-design: every `todowrite`\n * overwrites the active list in full, mirroring Anthropic's reference\n * `TodoWrite` semantics.\n *\n * Persistence model — `session.metadata.todos: TodosBag`:\n *\n * ```ts\n * { session?: TodoItem[], byRun?: Record<runId, TodoItem[]> }\n * ```\n *\n * - Top-level runs (no `parentRunId`) **share** the `session` slot. A list\n * written in one prompt survives across the next prompt — the user\n * aborts a run with `in_progress` items, sends a follow-up, and the\n * model's `todoread` returns the same list to continue from. Matches\n * Claude Code's TodoWrite v1 keying (`agentId ?? sessionId`).\n * - Subagent runs (`parentRunId` set) get their own slot under `byRun`\n * keyed by the child's runId. Subagent state never bleeds into the\n * parent's session list — parallel children stay isolated from each\n * other and from the parent.\n * - When every item is `completed`, the slot is **auto-cleared** to `[]`\n * on write — prevents stale \"all done\" lists from shadowing the next\n * prompt's context. Same rationale as Claude Code's `TodoWriteTool.ts:70`.\n * - **Archive sidecar** (`bag.archive`): every non-empty write also\n * stashes the list under `archive.session` / `archive.byRun`. Empty\n * writes (auto-clear or explicit reset) preserve the archive. UI\n * surfaces fall back to it when the live slot is empty so the modal\n * keeps showing \"what was just completed\" — invisible to the model\n * (`todoread` strictly reads the live slot).\n * - Resume is transparent: the persisted `tool_result` block in\n * `session.turns` already carries the latest snapshot, and\n * `session.metadata.todos` restores on reload via the session store.\n *\n * Policy is layered:\n * - Identical-payload dedup: handled **inside the tool body**, against the\n * current slot (session-shared for top-level, per-run for subagents).\n * Does NOT plumb through `behavior.dedupTools` — that cache is keyed\n * per session and would conflate top-level + subagent re-writes.\n * - Soft nudge (\"you've checkpointed N times, slow down\"): rides on the\n * tool's own `tool_result` string. The model sees the nudge in the\n * result it just received — no `system:transform` plumbing. Count is\n * scoped the same way the data is (session-shared for top-level,\n * per-run for subagents) so the nudge fires on cumulative over-use.\n * - Per-run write cap **(opt-in)**: available via\n * `createTodoTools({ maxWritesPerRun: N })`. Off by default —\n * `todowrite` is a state-transition tool, capping it punishes the\n * legitimate \"finish the N-item plan\" path (which needs ≥ N+1\n * calls). Hosts that explicitly want a hard ceiling can opt in.\n *\n * No OpenTUI imports — a GUI consumer reuses this module verbatim.\n */\nimport type { Preset } from '../presets'\nimport type { Session } from '../session'\nimport type { ToolContext, ToolDef } from '../tools/types'\nimport { definePreset } from '../presets'\n\n// ---------------------------------------------------------------------------\n// Tool identities — canonical names persisted in `session.turns`. Stable\n// across the lifetime of the project; renaming would break resume.\n// ---------------------------------------------------------------------------\n\nexport const TODOWRITE_TOOL = 'todowrite'\nexport const TODOREAD_TOOL = 'todoread'\n\n/** True when `name` is one of the todo tool canonical names. */\nexport function isTodoTool(name: string): boolean {\n return name === TODOWRITE_TOOL || name === TODOREAD_TOOL\n}\n\n// ---------------------------------------------------------------------------\n// Session metadata keys — exported so consumers (UI rehydrate, export\n// pipelines, fork helpers) read/write the same shape we do. Keep them\n// alongside the tool to make the contract obvious.\n// ---------------------------------------------------------------------------\n\n/** `session.metadata[TODOS_METADATA_KEY]: TodosBag` — see {@link TodosBag}. */\nexport const TODOS_METADATA_KEY = 'todos'\n/** `session.metadata[TODO_WRITE_COUNTS_METADATA_KEY]: CountsBag` — mirrors `TodosBag` shape. */\nexport const TODO_WRITE_COUNTS_METADATA_KEY = 'todoWriteCounts'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type TodoStatus = 'pending' | 'in_progress' | 'completed' | 'cancelled'\n\nexport interface TodoItem {\n /** Stable id — the model picks it. Reusing an id replaces that item. */\n id: string\n /** One-line task summary (Markdown allowed; renderers may sanitize). */\n content: string\n status: TodoStatus\n}\n\n/**\n * On-disk shape at `session.metadata[TODOS_METADATA_KEY]`.\n *\n * Sub-fields are absent (not empty) when they carry no data — keeps the\n * persisted metadata clean and lets `JSON.stringify` skip empty bags\n * entirely on session save.\n */\nexport interface TodosBag {\n /** Top-level slot — shared across runs whose `parentRunId` is unset. */\n session?: TodoItem[]\n /** Per-subagent-run slots, keyed by the child run's id. */\n byRun?: Record<string, TodoItem[]>\n /**\n * Most-recent non-empty list per slot — sidecar for UI surfaces, NOT\n * visible to `todoread`. Updated on every non-empty write; **preserved**\n * across the auto-clear path. Lets the modal keep showing \"what was just\n * completed\" after a fully-done batch wipes the live slot. Empty writes\n * never touch the archive — clearing the live list shouldn't erase the\n * audit trail.\n */\n archive?: {\n session?: TodoItem[]\n byRun?: Record<string, TodoItem[]>\n }\n}\n\n/** Mirror of {@link TodosBag} for the write-counter — same scope rules. */\ninterface CountsBag {\n session?: number\n byRun?: Record<string, number>\n}\n\nconst TODO_STATUS_VALUES: readonly TodoStatus[] = ['pending', 'in_progress', 'completed', 'cancelled']\n\n/**\n * Default ceiling on a single todo list. Exported so every write boundary caps\n * identically — a boundary with a different cap would let a list through that\n * the tool would have truncated.\n */\nexport const DEFAULT_MAX_TODO_ITEMS = 100\n\n// ---------------------------------------------------------------------------\n// Status glyphs — single source of truth for every UI surface that\n// renders a todo. Both the modal and the inline indicator import these\n// so the two read as the same visual language. Glyphs are intentionally\n// monochrome / single-cell so they line up cleanly in a column.\n//\n// pending ☐ empty box — \"queued\"\n// in_progress ◐ half-filled circle — \"currently working\"\n// completed ☑ checked box — \"done\"\n// cancelled ☒ crossed box — \"no longer relevant\"\n//\n// The inline indicator uses the `in_progress` glyph as its prefix so a\n// quick glance at the bar matches the modal's row marker. Renderer-\n// agnostic by design — a GUI shell can reuse the same map verbatim.\n// ---------------------------------------------------------------------------\nexport const TODO_STATUS_GLYPHS: Readonly<Record<TodoStatus, string>> = {\n pending: '☐',\n in_progress: '◐',\n completed: '☑',\n cancelled: '☒',\n}\n\nexport interface CreateTodoToolsOptions {\n /**\n * Maximum item count per `todowrite`. Excess items are truncated by the\n * recursive validator and reported back to the model in the tool_result.\n *\n * Default: `100`. The default is generous; a planning agent that exceeds\n * 100 items per call is usually flailing.\n */\n maxItems?: number\n /**\n * Per-run cap on `todowrite` calls, plumbed into `behavior.toolBudgets`.\n * `onMaxWrites` chooses the reaction (steer / block) — see\n * {@link AgentBehavior.toolBudgets} for the contract.\n *\n * **Default: `0` (no cap).** `todowrite` is a state-transition tool —\n * a legitimate run of an N-item list needs at least `N + 1` calls\n * (initial plan + one transition per item) to complete, so a hard\n * cap punishes the legitimate path. The nudges that DO apply are the\n * `remindAfter` reminder (soft, in-band) and the identical-payload\n * dedup check (silent, structural). Hosts that want a true hard cap\n * can opt in by setting `maxWritesPerRun: N`.\n *\n * **Counting semantics** (when opted in): the budget counts every\n * dispatched call, including dedup substitutes (zidane spec). The\n * {@link remindAfter} counter counts execute-only dispatches —\n * dedup hits replay the prior result and never reach the tool body.\n * Use this budget (not the reminder) as the hard limit.\n */\n maxWritesPerRun?: number\n onMaxWrites?: 'steer' | 'block'\n /**\n * Hard stop for **consecutive identical** `todowrite` payloads — the\n * exact loop the in-body `dedupIdentical` check only *whispers* about\n * (\"No change — N items already tracked\"). Plumbs a run-scoped\n * `behavior.dedupTools.todowrite` entry in `block-after` mode: the\n * gate replays the unchanged result for the first `N - 1` identical\n * calls, then refuses the `N`th (and every subsequent identical one)\n * with a `Blocked: …` tool_result so the model breaks out instead of\n * re-checkpointing forever.\n *\n * This is safe to leave on by default — unlike {@link maxWritesPerRun}\n * it only ever blocks a payload byte-identical to the one already\n * tracked, so a legitimate \"finish the N-item plan\" run (which writes\n * a *different* list each transition) is never touched. The counter\n * resets the moment the model writes anything different.\n *\n * Run-scoped via `DedupToolConfig.scope: 'run'`, so a parent and a\n * subagent writing the same list never share the counter — the precise\n * conflation that kept `todowrite` off `behavior.dedupTools` before.\n *\n * Set to `0` to opt out (e.g. a host that installs its own loop\n * hardening). Default: `5`. Values `< 2` are clamped to `2` by the\n * dedup middleware (a threshold of 1 would block the first call).\n */\n maxIdenticalWrites?: number\n /**\n * Custom refusal text for the {@link maxIdenticalWrites} block. Receives\n * the 1-indexed identical-call count (including the blocked call). Leave\n * unset for the built-in \"you've written the same list N times in a\n * row\" message.\n */\n identicalBlockReason?: (count: number) => string\n /**\n * Append a \"you've checkpointed N times — only call this at significant\n * task transitions\" nudge to the tool_result once `todowrite` has fired\n * this many times in the current run. The nudge rides on the result\n * string, so the model sees it on its next turn without `system:transform`\n * plumbing.\n *\n * Counts only executed dispatches (dedup hits skip the tool body and so\n * skip the increment). For a hard cap that also covers dedup hits, use\n * {@link maxWritesPerRun}.\n *\n * Set to `0` to opt out. Default: `3`.\n */\n remindAfter?: number\n /**\n * Custom reminder text. Receives the post-call count and the items that\n * were just written. Return `''` (or `undefined`) to suppress the reminder\n * for this specific call. Default text nudges towards \"back to executing,\n * not re-planning.\"\n */\n reminderText?: (count: number, items: readonly TodoItem[]) => string | undefined\n /**\n * Short-circuit the tool body when the incoming payload is identical to\n * the **current slot** (session-shared for top-level runs, per-run for\n * subagents). The model gets a \"No change — N items already tracked.\"\n * acknowledgment instead of a fresh \"Updated N items.\" line.\n * Default: `true`.\n *\n * Read `session.metadata.todos` via {@link getTodosForRun} for the\n * comparison — so the resolution naturally follows the keying rule\n * (top-level reads the session slot, subagent reads its own). Does\n * NOT plumb through `behavior.dedupTools`: that cache lives at the\n * gate, is keyed per session, and would conflate top-level + subagent\n * re-writes of structurally identical payloads.\n *\n * Counter advances on identical re-writes regardless — the model\n * called the tool, that's a call. Lets the reminder threshold catch\n * spam. Set `false` to skip the check entirely (every call rewrites\n * the slot, useful when state-shape comparisons are pointless for\n * your usage).\n */\n dedupIdentical?: boolean\n /**\n * Override the JSON-schema-level descriptions surfaced to the model.\n * Useful for telemetry experiments. Leave unset to inherit the built-in\n * doctrine prose.\n */\n writeDescription?: string\n readDescription?: string\n}\n\n// ---------------------------------------------------------------------------\n// Built-in doctrine prose\n// ---------------------------------------------------------------------------\n\nconst WRITE_DESCRIPTION\n = 'Replace the active task list. Pass the full list of items every '\n + 'call; there is no partial update. The list persists across user '\n + 'prompts in the same session, so an aborted run with `in_progress` '\n + 'items reads back on the next `todoread` and you can pick up where '\n + 'you left off. When every item is `completed`, the list auto-clears '\n + 'so the next prompt starts fresh.\\n\\n'\n + 'Use the `status` field to track progress: `pending` (queued), '\n + '`in_progress` (currently working), `completed` (done), `cancelled` '\n + '(no longer relevant).\\n\\n'\n + 'Only checkpoint at significant transitions:\\n'\n + ' 1. On a multi-step task, or before a run that will change several '\n + 'files: write the plan with everything `pending` so the user sees it.\\n'\n + ' 2. When you transition between steps: mark the previous one '\n + '`completed` and the next one `in_progress`.\\n'\n + ' 3. When the user asks for the current status: re-emit the list '\n + 'unchanged so they can see it.\\n\\n'\n + 'Do NOT call this on every action. The list is for the user\\'s '\n + 'situational awareness, not for self-narrating.'\n\nconst READ_DESCRIPTION\n = 'Return the current active task list. Returns an empty list if nothing '\n + 'has been written yet, or if the previous batch was auto-cleared '\n + 'after all items completed. Use sparingly; you already see the '\n + 'latest list in your own `todowrite` tool_result above.'\n\nfunction defaultReminder(count: number): string {\n return `(You've called todowrite ${count} times. Make sure each `\n + `checkpoint reflects real progress; avoid re-planning every step.)`\n}\n\nfunction defaultIdenticalBlockReason(count: number): string {\n return `You've written the same todo list ${count} times in a row without `\n + `any change. Stop re-checkpointing: either do the next concrete `\n + `action (edit a file, run a command) and then write a list that `\n + `reflects real progress, or finish the task. Re-emitting the `\n + `identical list does nothing.`\n}\n\n/**\n * Stable fingerprint for a `todowrite` payload, used by the run-scoped\n * `block-after` dedup gate. Normalizes the incoming `{ todos }` the same\n * way the tool body does before {@link todosEqual} — sanitize to the\n * three known fields, last-wins id-dedup, order preserved — so the gate's\n * notion of \"identical\" lines up exactly with the body's \"unchanged\"\n * short-circuit. Returns `undefined` for shapes with no usable items so\n * the dedup middleware skips them (a malformed call shouldn't poison the\n * loop counter).\n */\nfunction hashTodoWriteInput(input: Record<string, unknown>): string | undefined {\n const raw = Array.isArray(input.todos) ? input.todos : []\n const items = sanitizeItems(raw, Number.MAX_SAFE_INTEGER)\n const byId = new Map<string, TodoItem>()\n for (const item of items)\n byId.set(item.id, item)\n const normalized = [...byId.values()]\n if (normalized.length === 0)\n return undefined\n return JSON.stringify(normalized.map(t => [t.id, t.status, t.content]))\n}\n\n// ---------------------------------------------------------------------------\n// Session metadata accessors — single source of truth for the `TodosBag`\n// shape. Tests, fork helpers, UI rehydrate, and the tool bodies all go\n// through these so the keying contract (top-level vs subagent) stays in\n// one place.\n// ---------------------------------------------------------------------------\n\n/**\n * `true` when the run with `runId` is a subagent (has a `parentRunId`).\n * Top-level runs return `false`; an unknown / missing run also returns\n * `false` (defensive — should never happen in practice, but treating an\n * orphan as top-level is the right fallback since the session slot is\n * the more general bucket).\n */\nfunction isSubagentRun(session: Session, runId: string): boolean {\n const run = session.runs.find(r => r.id === runId)\n return !!run?.parentRunId\n}\n\n/**\n * Read the active list for `runId`. Top-level runs see the session-shared\n * slot; subagent runs see their own. Returns a fresh `[]` (not stored in\n * metadata) when no slot exists — the caller can mutate the result\n * without affecting state.\n */\nexport function getTodosForRun(session: Session, runId: string): TodoItem[] {\n const bag = readTodosBag(session)\n if (isSubagentRun(session, runId)) {\n const items = bag.byRun?.[runId]\n return Array.isArray(items) ? [...items] : []\n }\n return Array.isArray(bag.session) ? [...bag.session] : []\n}\n\n/**\n * Mirror a todo snapshot from another process into this session's metadata.\n *\n * Needed because the run loop never persists the todos bag — `todowrite` calls\n * `setMeta`, which only mutates memory, and nothing saves it. That is harmless\n * in-process (the UI reads the very object the tool wrote) but fatal across a\n * process boundary: a window that reloads the session from the shared store\n * finds NO todos metadata and renders an empty list, even though the daemon's\n * agent has a full one.\n *\n * So the authoritative `todos:snapshot` signal is applied here instead of being\n * re-derived from disk. Writes BOTH slots: `todos` is what the model sees, and\n * `archive` is what the UI falls back to after an auto-clear — reconstructing\n * the archive from `todos` alone would blank the modal in exactly the\n * all-completed case the archive exists to cover.\n *\n * Memory-only by design (no `save()`): this is a local mirror of another\n * process's state, and persisting it would let a window's stale view overwrite\n * the owner's. Whoever owns the agent owns the durable copy.\n */\nexport function applyTodosSnapshot(\n session: Session,\n snapshot: { runId: string | null, todos: readonly TodoItem[], archive: readonly TodoItem[] },\n): void {\n const { runId } = snapshot\n if (!runId)\n return\n const bag = readTodosBag(session)\n const subagent = isSubagentRun(session, runId)\n const live = snapshot.todos.map(normalizeItem)\n const archived = snapshot.archive.map(normalizeItem)\n\n const assign = (\n slot: 'session' | 'byRun',\n target: { session?: TodoItem[], byRun?: Record<string, TodoItem[]> },\n items: TodoItem[],\n ): void => {\n if (slot === 'byRun') {\n const byRun = { ...(target.byRun ?? {}) }\n if (items.length === 0)\n delete byRun[runId]\n else\n byRun[runId] = items\n target.byRun = byRun\n return\n }\n if (items.length === 0)\n delete target.session\n else\n target.session = items\n }\n\n const slot = subagent ? 'byRun' : 'session'\n assign(slot, bag, live)\n const archive = { ...(bag.archive ?? {}) }\n assign(slot, archive, archived)\n bag.archive = archive\n writeTodosBag(session, bag)\n}\n\n/**\n * Replace the active list for `runId`. Routes to the session-shared slot\n * for top-level runs and to the per-run slot for subagents. Empty arrays\n * delete the slot entirely so persisted metadata stays clean.\n *\n * Side effect: a **non-empty** write also stashes the same list into\n * `bag.archive` (same routing). Empty writes never touch the archive —\n * clearing the live list (whether explicitly or via the tool's auto-\n * clear) preserves the \"last shown\" snapshot so UI surfaces can keep\n * rendering what was just completed. See {@link getArchivedTodosForRun}.\n *\n * `opts.clearArchive` opts OUT of that preservation, and exists because the\n * archive is only the right answer for the AUTO-CLEAR path. UI surfaces render\n * `todos.length > 0 ? todos : archive`, so a user who deletes every todo would\n * otherwise watch the archive fall straight back into view with the same items\n * — a destructive action they confirmed, with no visible effect, and the footer\n * chip still counting the list they just deleted. Pass it for user-initiated\n * writes; leave it off for the tool.\n */\nexport function setTodosForRun(\n session: Session,\n runId: string,\n items: readonly TodoItem[],\n opts: { clearArchive?: boolean } = {},\n): void {\n const bag = readTodosBag(session)\n const normalized = items.map(normalizeItem)\n const subagent = isSubagentRun(session, runId)\n\n // Active slot.\n if (subagent) {\n const byRun = { ...(bag.byRun ?? {}) }\n if (normalized.length === 0)\n delete byRun[runId]\n else\n byRun[runId] = normalized\n bag.byRun = byRun\n }\n else if (normalized.length === 0) {\n delete bag.session\n }\n else {\n bag.session = normalized\n }\n\n // Archive — only on non-empty writes. Empty writes preserve the prior\n // archive so the modal keeps showing \"what was just done\" after the\n // live slot wipes.\n if (normalized.length > 0) {\n const archive = { ...(bag.archive ?? {}) }\n if (subagent) {\n const archiveByRun = { ...(archive.byRun ?? {}) }\n archiveByRun[runId] = normalized\n archive.byRun = archiveByRun\n }\n else {\n archive.session = normalized\n }\n bag.archive = archive\n }\n else if (opts.clearArchive) {\n // Explicit user deletion — drop the snapshot too, or the UI's\n // `todos : archive` fallback would re-render exactly what was deleted.\n const archive = { ...(bag.archive ?? {}) }\n if (subagent) {\n const archiveByRun = { ...(archive.byRun ?? {}) }\n delete archiveByRun[runId]\n archive.byRun = archiveByRun\n }\n else {\n delete archive.session\n }\n bag.archive = archive\n }\n\n writeTodosBag(session, bag)\n}\n\n/**\n * Read the archived (most-recent non-empty) list for `runId`. Same slot\n * routing as {@link getTodosForRun}, but reads `bag.archive` instead of\n * the live slot. Returns a fresh `[]` when no archive exists.\n *\n * UI surfaces use this to keep showing \"what was just completed\" after\n * the live list auto-clears. The model never sees the archive — it's a\n * read-only sidecar for human-facing rendering.\n */\nexport function getArchivedTodosForRun(session: Session, runId: string): TodoItem[] {\n const bag = readTodosBag(session)\n const archive = bag.archive\n if (!archive)\n return []\n if (isSubagentRun(session, runId)) {\n const items = archive.byRun?.[runId]\n return Array.isArray(items) ? [...items] : []\n }\n return Array.isArray(archive.session) ? [...archive.session] : []\n}\n\n/**\n * Reconcile `session.metadata.todos.byRun` (live + archive) against\n * `session.runs`. Drops subagent slots whose runId isn't in the run\n * list. The top-level `session` slot is unaffected — it's session-\n * scoped, not per-run, so there's no orphan to GC.\n *\n * Useful after `session.setRuns()` (fork / restore) or to GC stale\n * metadata mutated by an external caller. Also prunes the parallel\n * counter bag so the two don't drift.\n *\n * `dropped` reports the live-byRun orphans only (archive-only orphans\n * are silently swept). The live half is the contract callers actually\n * observe.\n */\nexport function pruneTodosByRun(session: Session): { dropped: readonly string[] } {\n const validRunIds = new Set(session.runs.map(r => r.id))\n const dropped: string[] = []\n\n const bag = readTodosBag(session)\n let bagChanged = false\n if (bag.byRun) {\n const nextByRun: Record<string, TodoItem[]> = {}\n for (const [runId, items] of Object.entries(bag.byRun)) {\n if (validRunIds.has(runId)) {\n nextByRun[runId] = items\n }\n else {\n dropped.push(runId)\n bagChanged = true\n }\n }\n if (Object.keys(nextByRun).length !== Object.keys(bag.byRun).length)\n bag.byRun = nextByRun\n }\n if (bag.archive?.byRun) {\n const nextByRun: Record<string, TodoItem[]> = {}\n let archiveChanged = false\n for (const [runId, items] of Object.entries(bag.archive.byRun)) {\n if (validRunIds.has(runId))\n nextByRun[runId] = items\n else\n archiveChanged = true\n }\n if (archiveChanged) {\n bag.archive = { ...bag.archive, byRun: nextByRun }\n bagChanged = true\n }\n }\n if (bagChanged)\n writeTodosBag(session, bag)\n\n const counts = readCountsBag(session)\n if (counts.byRun) {\n const nextByRun: Record<string, number> = {}\n let changed = false\n for (const [runId, n] of Object.entries(counts.byRun)) {\n if (validRunIds.has(runId))\n nextByRun[runId] = n\n else\n changed = true\n }\n if (changed) {\n counts.byRun = nextByRun\n writeCountsBag(session, counts)\n }\n }\n\n return { dropped }\n}\n\n// ---------------------------------------------------------------------------\n// Tool factory\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `Preset` carrying the `{ todowrite, todoread }` tool pair.\n *\n * Loop hardening is layered, soft → hard:\n * 1. **In-body identical dedup** (`dedupIdentical`, on) — resolves\n * against the active slot (session-shared for top-level runs,\n * per-run for subagents) and returns \"No change — N items already\n * tracked.\" Advisory; the model can ignore it.\n * 2. **In-band reminder** (`remindAfter`, on) — appends a \"you've\n * checkpointed N times\" nudge to the result. Also advisory.\n * 3. **Consecutive-identical hard block** (`maxIdenticalWrites`, ON by\n * default) — a run-scoped `behavior.dedupTools.todowrite` entry in\n * `block-after` mode. After N byte-identical writes in a row the\n * gate refuses with `Blocked: …` and the body never runs. This is\n * the real loop breaker, and it's safe-by-default precisely because\n * it only fires on *identical* payloads — a legitimate N-item plan\n * writes a *different* list each step and never trips it.\n *\n * By default the preset adds **no** `toolBudgets` entry — a blanket\n * per-call cap is the wrong abstraction for a state-transition tool (it\n * punishes the legitimate \"finish the N-item plan\" path). Hosts that\n * want a ceiling on *all* writes (not just identical ones) pass\n * `maxWritesPerRun: N` explicitly; the factory then also plumbs a\n * `behavior.toolBudgets.todowrite` entry.\n *\n * Returning a `Preset` (not a bare tool map) lets the result flow\n * through {@link composePresets} unchanged. `behavior.dedupTools` and\n * `behavior.toolBudgets` are tool-name-keyed records that\n * `composePresets` deep-merges, so a caller's entries for other tools\n * survive the layering, and a caller's override for `todowrite` itself\n * wins by being placed later in the chain.\n *\n * ```ts\n * import { basic, composePresets } from 'zidane/presets'\n * import { createTodoTools } from 'zidane/chat'\n *\n * // Default: soft dedup + reminder + a run-scoped hard block after 5\n * // identical writes in a row.\n * createAgent({ ...composePresets(basic, createTodoTools()), provider })\n *\n * // Tune (or disable) the identical-write block.\n * createTodoTools({ maxIdenticalWrites: 3 }) // stricter\n * createTodoTools({ maxIdenticalWrites: 0 }) // off — soft signals only\n *\n * // Opt-in blanket cap on *all* writes (counts distinct ones too).\n * createAgent({\n * ...composePresets(basic, createTodoTools({ maxWritesPerRun: 20 })),\n * provider,\n * })\n * ```\n *\n * For the trivial \"just add the tools to an existing config\" case, plain\n * spread is also fine — the returned `Preset` sets `tools` plus a\n * `behavior` block (carrying the default identical-write dedup):\n *\n * ```ts\n * createAgent({ ...basic, ...createTodoTools(), provider })\n * ```\n */\nexport function createTodoTools(options: CreateTodoToolsOptions = {}): Preset {\n const maxItems = options.maxItems ?? DEFAULT_MAX_TODO_ITEMS\n const remindAfter = options.remindAfter ?? 3\n const reminderText = options.reminderText ?? ((count, _items) => defaultReminder(count))\n const dedupIdentical = options.dedupIdentical ?? true\n // Default opt-out — `todowrite` is a state-transition tool, capping\n // it punishes legitimate \"finish the 5-item plan\" paths (which need\n // ≥6 calls). The `remindAfter` reminder + `dedupIdentical` check\n // handle spam in-band; a host that wants a hard ceiling can opt in\n // explicitly.\n const maxWritesPerRun = options.maxWritesPerRun ?? 0\n const onMaxWrites = options.onMaxWrites ?? 'steer'\n // On by default — a run-scoped, identical-payload-only hard stop. Only\n // ever blocks a byte-for-byte repeat of the already-tracked list, so it\n // can't punish a legitimate multi-step plan (every transition writes a\n // *different* list). See `maxIdenticalWrites` JSDoc.\n const maxIdenticalWrites = options.maxIdenticalWrites ?? 5\n\n const tools: Record<string, ToolDef> = {\n [TODOWRITE_TOOL]: createTodoWriteTool({\n maxItems,\n remindAfter,\n reminderText,\n dedupIdentical,\n description: options.writeDescription ?? WRITE_DESCRIPTION,\n }),\n [TODOREAD_TOOL]: createTodoReadTool({\n description: options.readDescription ?? READ_DESCRIPTION,\n }),\n }\n\n // Only `toolBudgets` plumbs through the framework's behavior surface — the\n // budget needs to fire at the `tool:gate` layer to enforce a hard cap.\n // Dedup deliberately lives in the tool body instead of `behavior.dedupTools`\n // because the framework's dedup cache is session-scoped and would silently\n // drop cross-run re-writes of an identical list (see `dedupIdentical` JSDoc).\n const behavior: NonNullable<Preset['behavior']> = {}\n if (maxWritesPerRun > 0)\n behavior.toolBudgets = { [TODOWRITE_TOOL]: { max: maxWritesPerRun, onExceed: onMaxWrites } }\n\n // Consecutive-identical-write loop breaker. Run-scoped so a parent and a\n // subagent never share the counter; `block-after` so the first few\n // identical re-writes replay the unchanged result (matching the in-body\n // soft dedup) before the gate hard-refuses. The hasher mirrors the\n // body's `todosEqual` normalization (order-sensitive, three fields) so\n // the gate's \"identical\" and the body's \"unchanged\" agree.\n if (maxIdenticalWrites > 0) {\n const reason = options.identicalBlockReason ?? defaultIdenticalBlockReason\n behavior.dedupTools = {\n [TODOWRITE_TOOL]: {\n hasher: hashTodoWriteInput,\n mode: 'block-after',\n threshold: maxIdenticalWrites,\n scope: 'run',\n reason: (_input, count) => reason(count),\n },\n }\n }\n\n return definePreset(\n Object.keys(behavior).length > 0\n ? { tools, behavior }\n : { tools },\n )\n}\n\n// ---------------------------------------------------------------------------\n// Tool implementations\n// ---------------------------------------------------------------------------\n\ninterface CreateTodoWriteOptions {\n maxItems: number\n remindAfter: number\n reminderText: (count: number, items: readonly TodoItem[]) => string | undefined\n dedupIdentical: boolean\n description: string\n}\n\nfunction createTodoWriteTool(opts: CreateTodoWriteOptions): ToolDef {\n return {\n spec: {\n name: TODOWRITE_TOOL,\n description: opts.description,\n inputSchema: {\n type: 'object',\n properties: {\n todos: {\n type: 'array',\n description: 'The complete task list. Replaces the prior list in full.',\n maxItems: opts.maxItems,\n items: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Stable identifier (the model picks).' },\n content: { type: 'string', description: 'One-line task summary.' },\n status: {\n type: 'string',\n enum: [...TODO_STATUS_VALUES],\n description: '`pending`, `in_progress`, `completed`, or `cancelled`.',\n },\n },\n required: ['id', 'content', 'status'],\n },\n },\n },\n required: ['todos'],\n },\n },\n async execute(input, ctx) {\n const { session, runId } = requireSessionAndRun(ctx, TODOWRITE_TOOL)\n\n const rawItems = Array.isArray(input.todos) ? input.todos : []\n const items = sanitizeItems(rawItems, opts.maxItems)\n const dropped = rawItems.length - items.length\n\n // ID dedup — last-wins. Two items in one payload sharing an id is\n // a model mistake; quietly normalize rather than error.\n const byId = new Map<string, TodoItem>()\n for (const item of items)\n byId.set(item.id, item)\n const normalized = [...byId.values()]\n\n // Identical-payload short-circuit — compares against the slot the\n // active run resolves to (session-shared for top-level, per-run\n // for subagents). See `dedupIdentical` JSDoc.\n const current = getTodosForRun(session, runId)\n const unchanged = opts.dedupIdentical && todosEqual(current, normalized)\n\n // Counter advances on every call — including identical re-writes —\n // so the reminder threshold catches \"model keeps re-checkpointing\n // the same list\" spam. Scope mirrors the data (session-shared for\n // top-level, per-run for subagents) so the count reflects\n // cumulative use of the *active* slot, not noisy per-prompt resets.\n const count = incrementCount(session, runId)\n\n // Auto-clear when every item is `completed`. Matches Claude Code's\n // TodoWrite v1 behavior — once a batch is fully done, the next\n // prompt should start clean rather than re-read a wall of\n // checkmarks. The model's reply still summarizes the completion;\n // only the persisted slot is wiped.\n const allCompleted = normalized.length > 0 && normalized.every(t => t.status === 'completed')\n\n if (!unchanged) {\n if (allCompleted) {\n // Two-phase auto-clear: first persist the all-completed list\n // so the archive captures it (setTodosForRun copies non-empty\n // writes into the archive sidecar), then wipe the live slot.\n // The empty write preserves the archive, so the modal can\n // keep showing the close-out batch until the model writes\n // something new.\n setTodosForRun(session, runId, normalized)\n setTodosForRun(session, runId, [])\n }\n else {\n setTodosForRun(session, runId, normalized)\n }\n }\n\n return formatWriteResult({\n items: normalized,\n dropped,\n count,\n unchanged,\n // Only report the auto-clear when it actually fired (a no-op\n // re-write of an already-cleared list shouldn't shout\n // \"marked complete\" again).\n cleared: allCompleted && !unchanged,\n opts,\n })\n },\n }\n}\n\ninterface FormatWriteResultInput {\n items: readonly TodoItem[]\n dropped: number\n count: number\n unchanged: boolean\n cleared: boolean\n opts: CreateTodoWriteOptions\n}\n\nfunction formatWriteResult(input: FormatWriteResultInput): string {\n const { items, dropped, count, unchanged, cleared, opts } = input\n const lines: string[] = []\n const n = items.length\n const suffix = n === 1 ? '' : 's'\n if (cleared) {\n lines.push(`Marked ${n} item${suffix} complete; list cleared.`)\n }\n else if (unchanged) {\n lines.push(`No change; ${n} todo item${suffix} already tracked.`)\n }\n else {\n lines.push(`Updated ${n} todo item${suffix}.`)\n }\n // Skip the tally when the list was cleared — the headline already\n // captures \"all done\" and the tally would read as a redundant\n // restatement.\n if (!cleared) {\n const tally = summarizeStatuses(items)\n if (tally)\n lines.push(tally)\n }\n if (dropped > 0)\n lines.push(`Dropped ${dropped} malformed item${dropped === 1 ? '' : 's'}.`)\n const reminder = opts.remindAfter > 0 && count >= opts.remindAfter\n ? opts.reminderText(count, items)\n : undefined\n if (reminder && reminder.length > 0)\n lines.push(reminder)\n return lines.join('\\n')\n}\n\nfunction createTodoReadTool(opts: { description: string }): ToolDef {\n return {\n // Reads `session.metadata.todos` without mutation — concurrency-safe.\n isConcurrencySafe: true,\n spec: {\n name: TODOREAD_TOOL,\n description: opts.description,\n inputSchema: {\n type: 'object',\n properties: {},\n },\n },\n async execute(_input, ctx) {\n const { session, runId } = requireSessionAndRun(ctx, TODOREAD_TOOL)\n\n const items = getTodosForRun(session, runId)\n if (items.length === 0)\n return 'No todos yet. Call todowrite to start tracking tasks.'\n return JSON.stringify({ todos: items })\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Both tool bodies need a `Session` + `runId`. Centralize the guards so the\n * error messages stay symmetric and one fix lands in both places.\n */\nfunction requireSessionAndRun(\n ctx: ToolContext,\n toolName: string,\n): { session: Session, runId: string } {\n if (!ctx.session)\n throw new Error(`${toolName}: no session on tool context. Todos require a session via createSession().`)\n if (!ctx.runId)\n throw new Error(`${toolName}: no runId on tool context.`)\n return { session: ctx.session, runId: ctx.runId }\n}\n\n/** Read the todos bag from session metadata, normalizing the live shape. */\n/*\n * No cap on the READ path. The list was already capped by `maxItems` when it\n * was written, and a host that raised that ceiling must not have its persisted\n * rows silently truncated on resume by a lower default here.\n */\nconst READ_ITEM_CAP = Number.MAX_SAFE_INTEGER\n\n/** Validate one persisted slot. Non-arrays and malformed rows drop out. */\nfunction readSlot(raw: unknown): TodoItem[] | undefined {\n if (!Array.isArray(raw))\n return undefined\n const items = sanitizeItems(raw, READ_ITEM_CAP)\n return items.length > 0 ? items : undefined\n}\n\n/** Validate a `runId -> items` map, dropping slots that end up empty. */\nfunction readSlotMap(raw: unknown): Record<string, TodoItem[]> | undefined {\n if (!isPlainObject(raw))\n return undefined\n const out: Record<string, TodoItem[]> = {}\n for (const [runId, value] of Object.entries(raw)) {\n const items = readSlot(value)\n if (items)\n out[runId] = items\n }\n return Object.keys(out).length > 0 ? out : undefined\n}\n\nfunction readTodosBag(session: Session): TodosBag {\n const raw = session.metadata[TODOS_METADATA_KEY]\n if (!isPlainObject(raw))\n return {}\n /*\n * Persisted metadata is UNTRUSTED input, same as tool arguments: it comes off\n * disk, may have been written by an older build or hand-edited, and outlives\n * any single version of this file. It previously reached callers through\n * `as TodoItem[]` casts, so a row with `status: \"done\"` (not a `TodoStatus`)\n * type-checked as valid and then fell silently through the tally fold —\n * counted in `total` but in none of the four buckets.\n *\n * `sanitizeItems` is the same validator the tool-input path already uses, so\n * both entry points agree on what a well-formed item is. Malformed rows are\n * dropped rather than throwing: this is a resume path, and one bad row must\n * not cost the user the rest of their list.\n */\n const archive = isPlainObject(raw.archive)\n ? {\n session: readSlot(raw.archive.session),\n byRun: readSlotMap(raw.archive.byRun),\n }\n : undefined\n return {\n session: readSlot(raw.session),\n byRun: readSlotMap(raw.byRun),\n archive,\n }\n}\n\nfunction writeTodosBag(session: Session, bag: TodosBag): void {\n // Strip empty sub-fields so the persisted shape stays minimal.\n const clean: TodosBag = {}\n if (bag.session && bag.session.length > 0)\n clean.session = bag.session\n if (bag.byRun && Object.keys(bag.byRun).length > 0)\n clean.byRun = bag.byRun\n if (bag.archive) {\n const archive: NonNullable<TodosBag['archive']> = {}\n if (bag.archive.session && bag.archive.session.length > 0)\n archive.session = bag.archive.session\n if (bag.archive.byRun && Object.keys(bag.archive.byRun).length > 0)\n archive.byRun = bag.archive.byRun\n if (archive.session || archive.byRun)\n clean.archive = archive\n }\n session.setMeta(TODOS_METADATA_KEY, clean)\n}\n\nfunction readCountsBag(session: Session): CountsBag {\n const raw = session.metadata[TODO_WRITE_COUNTS_METADATA_KEY]\n if (!isPlainObject(raw))\n return {}\n return {\n session: typeof raw.session === 'number' ? raw.session : undefined,\n byRun: isPlainObject(raw.byRun) ? raw.byRun as Record<string, number> : undefined,\n }\n}\n\nfunction writeCountsBag(session: Session, bag: CountsBag): void {\n const clean: CountsBag = {}\n if (typeof bag.session === 'number' && bag.session > 0)\n clean.session = bag.session\n if (bag.byRun && Object.keys(bag.byRun).length > 0)\n clean.byRun = bag.byRun\n session.setMeta(TODO_WRITE_COUNTS_METADATA_KEY, clean)\n}\n\n/**\n * Bump the call counter for the active slot and return the new value.\n * Top-level runs increment `bag.session` (cumulative across prompts);\n * subagent runs increment their own entry under `bag.byRun`. Same\n * keying as {@link setTodosForRun} so the counter and the data move\n * together — a freshly seeded run's count starts at 1 on its first\n * write, a continuing top-level run's count picks up from where the\n * prior prompt left off.\n */\nfunction incrementCount(session: Session, runId: string): number {\n const bag = readCountsBag(session)\n let next: number\n if (isSubagentRun(session, runId)) {\n const byRun = { ...(bag.byRun ?? {}) }\n next = (byRun[runId] ?? 0) + 1\n byRun[runId] = next\n bag.byRun = byRun\n }\n else {\n next = (bag.session ?? 0) + 1\n bag.session = next\n }\n writeCountsBag(session, bag)\n return next\n}\n\n/** Narrow guard — `Record<string, unknown>` excluding arrays / null. */\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return !!v && typeof v === 'object' && !Array.isArray(v)\n}\n\nfunction normalizeItem(item: TodoItem): TodoItem {\n return { id: item.id, content: item.content, status: item.status }\n}\n\n/**\n * Validate an untrusted todo list, dropping malformed rows and capping length.\n *\n * The single validator for every write/read boundary: model tool input,\n * persisted metadata on read, and user edits arriving over IPC. Keeping them on\n * one implementation is what makes \"well-formed\" mean the same thing at each —\n * a boundary that only picked fields (as `normalizeItem` does) would admit an\n * invalid `status` that the read path then silently drops, so the write appears\n * to succeed and the row vanishes with nothing reported.\n */\nexport function sanitizeTodoItems(raw: readonly unknown[], cap: number = DEFAULT_MAX_TODO_ITEMS): TodoItem[] {\n return sanitizeItems([...raw], cap)\n}\n\nfunction sanitizeItems(raw: unknown[], cap: number): TodoItem[] {\n const out: TodoItem[] = []\n for (const item of raw) {\n if (!item || typeof item !== 'object' || Array.isArray(item))\n continue\n const obj = item as Record<string, unknown>\n const id = typeof obj.id === 'string' ? obj.id : undefined\n const content = typeof obj.content === 'string' ? obj.content : undefined\n const status = typeof obj.status === 'string' && (TODO_STATUS_VALUES as readonly string[]).includes(obj.status)\n ? obj.status as TodoStatus\n : undefined\n if (!id || !content || !status)\n continue\n out.push({ id, content, status })\n if (out.length >= cap)\n break\n }\n return out\n}\n\nfunction summarizeStatuses(items: readonly TodoItem[]): string | undefined {\n if (items.length === 0)\n return undefined\n const counts: Record<TodoStatus, number> = {\n pending: 0,\n in_progress: 0,\n completed: 0,\n cancelled: 0,\n }\n for (const item of items)\n counts[item.status] += 1\n const parts: string[] = []\n if (counts.completed)\n parts.push(`${counts.completed} completed`)\n if (counts.in_progress)\n parts.push(`${counts.in_progress} in progress`)\n if (counts.pending)\n parts.push(`${counts.pending} pending`)\n if (counts.cancelled)\n parts.push(`${counts.cancelled} cancelled`)\n return parts.length > 0 ? parts.join(' · ') : undefined\n}\n\n/**\n * Order-sensitive structural equality on todo lists. Used by the\n * `dedupIdentical` short-circuit to decide whether an incoming payload\n * matches the current run's stored slot.\n *\n * Order-sensitive on purpose: re-ordering the list is a meaningful state\n * change (\"the model reprioritized\") and should NOT collapse to a no-op.\n * Three-field comparison is exhaustive for `TodoItem` — `normalizeItem`\n * strips any extra fields before they reach the slot, so we never need to\n * compare beyond `{ id, content, status }`.\n */\nfunction todosEqual(a: readonly TodoItem[], b: readonly TodoItem[]): boolean {\n if (a.length !== b.length)\n return false\n for (let i = 0; i < a.length; i++) {\n const x = a[i]\n const y = b[i]\n if (x.id !== y.id || x.status !== y.status || x.content !== y.content)\n return false\n }\n return true\n}\n\n// ---------------------------------------------------------------------------\n// UI-facing derivation — active-run slice + tally + in-progress pick.\n//\n// Renderer-agnostic. The TUI's inline indicator + modal both consume\n// {@link useActiveTodos}; a future GUI shell reuses the same hook.\n// ---------------------------------------------------------------------------\n\nexport interface TodoTally {\n pending: number\n in_progress: number\n completed: number\n cancelled: number\n}\n\nexport interface ActiveTodosState {\n /** Run the slice came from. `null` ⇒ no session / no runs / no active run. */\n runId: string | null\n /**\n * Live list — what `todoread` would return. Empty when the slot was\n * cleared (e.g. auto-clear after every item completed) or never\n * written. Source of truth for the model's view.\n */\n todos: readonly TodoItem[]\n /**\n * Most-recent non-empty list for the active slot, preserved across\n * auto-clear. Equals `todos` when the live list is non-empty;\n * otherwise the last batch that was shown (commonly the all-`completed`\n * close-out). UI surfaces fall back to this when `todos` is empty so\n * the user can still glance back at \"what was just finished\" — until\n * the model writes a new list. Empty array when no list was ever\n * written for this slot.\n */\n archive: readonly TodoItem[]\n /**\n * The first `in_progress` item from `todos` (the **live** list), when\n * one exists. Drives the inline indicator above the prompt. Always\n * `null` when the live list is empty — even if the archive carries\n * an in-progress item, that item is by definition stale.\n */\n inProgress: TodoItem | null\n /**\n * Per-status counts. Computed over `todos` when non-empty, else over\n * `archive`. Zeros only when both are empty. Lets the modal header\n * show a meaningful tally for the archived \"last batch\" view.\n */\n tally: TodoTally\n}\n\nconst EMPTY_TALLY: TodoTally = { pending: 0, in_progress: 0, completed: 0, cancelled: 0 }\nconst EMPTY_ACTIVE: ActiveTodosState = {\n runId: null,\n todos: [],\n archive: [],\n inProgress: null,\n tally: EMPTY_TALLY,\n}\n\n/**\n * Pick the \"active\" run for UI surfaces that show one slot at a time\n * (todos indicator, todos modal, footer ctx indicator). The resolved\n * runId then routes through {@link getTodosForRun} — top-level runs\n * land on the session-shared slot, subagent runs on their own.\n *\n * - A currently-running run wins (latest-started one if several).\n * - Otherwise the most recently appended run wins.\n * - `null` when the session has no runs.\n *\n * Mirrors the \"most recent thing that mattered\" policy that drives\n * `lastContextSizeFromTurns` — but walks `session.runs` rather than\n * `session.turns` because the active todo slot is a function of the\n * run, not of message history.\n */\nexport function pickActiveRunId(session: Session | null | undefined): string | null {\n if (!session)\n return null\n const runs = session.runs\n if (!runs || runs.length === 0)\n return null\n // Scan from the tail — latest-running wins, falling back to latest of any\n // status. Subagent runs are eligible like top-level runs: a child spawned\n // off the parent has its own runId / its own todo slot, and surfacing it\n // here matches the per-run isolation the data layer already guarantees.\n let latestRunning: string | null = null\n for (let i = runs.length - 1; i >= 0; i--) {\n if (runs[i].status === 'running') {\n latestRunning = runs[i].id\n break\n }\n }\n if (latestRunning)\n return latestRunning\n return runs[runs.length - 1].id\n}\n\n/**\n * Pure selector — derive the active-todos state from a session snapshot.\n * Used by {@link useActiveTodos} and directly testable without React.\n *\n * Resolves both `todos` (live, model-facing) and `archive` (last\n * non-empty snapshot for UI fallback). The tally folds over whichever\n * list is non-empty; `inProgress` is sourced strictly from `todos`\n * because an archived in-progress item is by definition stale.\n */\nexport function selectActiveTodos(session: Session | null | undefined): ActiveTodosState {\n const runId = pickActiveRunId(session)\n if (!session || !runId)\n return EMPTY_ACTIVE\n const todos = getTodosForRun(session, runId)\n const archive = getArchivedTodosForRun(session, runId)\n // Whichever list the modal would show. `todos` wins when live,\n // `archive` fills in after auto-clear, empty when neither exists.\n const display = todos.length > 0 ? todos : archive\n if (display.length === 0)\n return { runId, todos, archive, inProgress: null, tally: EMPTY_TALLY }\n const tally: TodoTally = { pending: 0, in_progress: 0, completed: 0, cancelled: 0 }\n let inProgress: TodoItem | null = null\n // Tally folds over `display`; `inProgress` only over `todos` so we\n // don't surface an item that's no longer being worked on.\n for (const t of display)\n tally[t.status] += 1\n if (todos.length > 0) {\n for (const t of todos) {\n if (t.status === 'in_progress') {\n inProgress = t\n break\n }\n }\n }\n return { runId, todos, archive, inProgress, tally }\n}\n\n/**\n * Progress label for the footer chip: `\"2/5\"`, or `null` when there is nothing\n * to advertise.\n *\n * The chip replaced a full-width bar that printed the in-progress todo's TITLE\n * above the prompt. Titles are model-authored and arbitrarily long, so the bar\n * had to truncate, reflow with terminal width, and still told you nothing you\n * couldn't get by opening the list. A position reads at a glance and costs a\n * fixed handful of columns in a row that's already scanned for shortcuts.\n *\n * The numerator is the POSITION of the in-progress item — the direct\n * replacement for \"which one is live\". With nothing in progress (list finished,\n * or the archive is on screen) it falls back to the completed count, so a done\n * list reads `5/5` rather than `0/5`.\n *\n * Pure and renderer-agnostic, so the GUI and the tests can both use it.\n */\nexport function todoProgressLabel(state: ActiveTodosState): string | null {\n const display = state.todos.length > 0 ? state.todos : state.archive\n if (display.length === 0)\n return null\n const activeIndex = state.inProgress\n ? display.findIndex(t => t.id === state.inProgress!.id)\n : -1\n const numerator = activeIndex >= 0 ? activeIndex + 1 : state.tally.completed\n return `${numerator}/${display.length}`\n}\n\n/**\n * React hook — active-todos state for the supplied session. Recomputes\n * on every parent re-render; the work is O(runs + todos), both bounded\n * by small constants in practice.\n *\n * Why no memoization: `Session.runs` is mutated in place by `completeRun`\n * / `abortRun` (status flips without changing array length or item\n * identity), so any memo keyed off length / bag identity would silently\n * return stale state when e.g. a child subagent run completes and the\n * \"active\" run should reflow to the parent. The selector is cheap\n * enough that running it every parent render is the simpler, correct\n * answer — see `selectActiveTodos` for the O(n) cost.\n *\n * The hook lives in `chat/` so a GUI consumer can mount it verbatim.\n * Renderer-agnostic — no `@opentui/*` imports.\n */\nexport function useActiveTodos(session: Session | null | undefined): ActiveTodosState {\n return selectActiveTodos(session)\n}\n","/**\n * Agent profiles — named variants of the chat agent (Build, Plan, …).\n *\n * Each profile bundles a {@link Preset} with display metadata (label,\n * description, accent color) so the TUI can render a picker, persist the\n * user's choice, and rebuild the active agent on switch.\n *\n * The host's {@link ChatOptions.preset} is the single-agent legacy\n * shorthand; for multi-profile setups, pass `agents` (an {@link AgentRegistry})\n * directly. Built-ins ship as {@link BUILTIN_AGENTS} = `{ build, plan }`.\n *\n * No OpenTUI dependency — this module is pure data so a GUI host can\n * consume the same registry.\n */\nimport type { Preset } from '../presets'\nimport { composePresets, definePreset } from '../presets'\nimport { edit, glob, grep, listFiles, multiEdit, readFile, shell, shellKill, waitTask, writeFile } from '../tools'\nimport { fetchUrl } from '../tools/fetch-url'\nimport { createSpawnTool } from '../tools/spawn'\nimport { webSearch } from '../tools/web-search'\nimport { buildBuildSystem, buildPlanSystem } from './agent-prompt'\nimport { createTodoTools } from './todos'\n\n/**\n * Theme color token used to accent the active profile in the UI (footer\n * badge, picker highlight). Resolved against `ThemeColors` via\n * `useColors()`; falls back to `accent` when omitted.\n */\nexport type AgentAccent = 'brand' | 'accent' | 'warn' | 'model'\n\n/**\n * Resolve a profile's `accent` token to a concrete hex color via the\n * caller's color palette. Renderer-agnostic — accepts the four-role\n * subset of `ThemeColors` that's relevant to accent surfaces, so this\n * helper composes against either `useColors()` (TUI) or a CSS-variable\n * lookup (GUI). Omitted / unknown tokens fall back to `accent`.\n */\nexport function accentColor(\n accent: AgentAccent | undefined,\n COLOR: { brand: string, accent: string, warn: string, model: string },\n): string {\n switch (accent) {\n case 'brand': return COLOR.brand\n case 'warn': return COLOR.warn\n case 'model': return COLOR.model\n case 'accent':\n default:\n return COLOR.accent\n }\n}\n\nexport interface AgentProfile {\n /** Stable identifier persisted in `TuiState.lastAgent` and shown in keybindings. */\n id: string\n /** Human-readable label rendered in the picker and footer badge. */\n label: string\n /** One-line description shown next to the label in the picker. */\n description: string\n /**\n * Preset applied to `createAgent()` when this profile is active. Profiles\n * are self-contained: the host's chat-level `preset` (if any) is NOT\n * merged underneath. Hosts that want a shared base across profiles\n * should compose it explicitly in each profile's preset.\n */\n preset: Preset\n /** Theme token used by the picker / footer badge. Defaults to `'accent'`. */\n accent?: AgentAccent\n}\n\nexport type AgentRegistry = Readonly<Record<string, AgentProfile>>\n\n/** Read-only tool slice shared by the Plan profile and any host-built read-only variant. */\nconst READ_ONLY_TOOLS = { readFile, listFiles, glob, grep, webSearch, fetchUrl }\n\n/**\n * Full build-mode tool slice — read + write + shell + spawn.\n *\n * `shellKill` / `waitTask` ride along with `shell`: the shell tool's\n * description (and the `<task-notification>` stall hint in the agent\n * loop) instruct the model to call `shell_kill` / `wait_task` for\n * background tasks, so omitting them produces tool-not-found loops the\n * moment a `run_in_background` task needs terminating or blocking on.\n */\nconst BUILD_TOOLS = { shell, shellKill, waitTask, readFile, writeFile, listFiles, edit, multiEdit, glob, grep, webSearch, fetchUrl }\n\n/**\n * Tools the chat layer never persists to disk, regardless of output size.\n *\n * Rationale per entry:\n * - `read_file` — our reader is already paginated (`offset` + `limit`).\n * Persisting the slice the model asked for and pointing it at the\n * persisted path is circular; if the model wants more, it pages.\n * - `tool_search` — short metadata envelope. Never large.\n * - `skills_use` / `skills_read` — the `<skill_content>` body is\n * intentionally part of the prompt; persisting it defeats the purpose.\n * - `read_chat_turns` — previous-chat excerpts are explicitly loaded\n * conversation context; persisting them defeats the purpose.\n * - `present_plan` / `ask_user` — short JSON envelopes (decision +\n * answers). Never large.\n * - `spawn` — the subagent's final report IS the value of the call;\n * stubbing it out would defeat the orchestration pattern.\n *\n * Exported so SDK consumers building custom profiles can extend the list\n * (e.g. host-defined `screenshot` tool whose base64 payload should stay\n * inline). Set `behavior.persistExcludeTools` to your own list to fully\n * override.\n */\nexport const DEFAULT_PERSIST_EXCLUDE_TOOLS: readonly string[] = [\n 'read_file',\n 'tool_search',\n 'skills_use',\n 'skills_read',\n 'read_chat_turns',\n 'present_plan',\n 'ask_user',\n 'spawn',\n // Todos are short JSON envelopes (≤100 items) and the latest snapshot\n // is the only state of interest — persisting them to disk is circular.\n 'todowrite',\n 'todoread',\n]\n\n/**\n * Tools whose output is exempt from the per-turn `toolOutputBudget` accounting.\n *\n * These tools exist to LOAD context into the conversation — their text output\n * IS the value of the call. Counting it against the budget steers the model\n * away from the very tool it needs to make progress (e.g. a 78 KiB\n * `tool_search` response for a 20-tool Notion MCP server would trigger the\n * \"summarize before calling more tools\" nudge on the very turn that\n * unlocked those tools, making them effectively unusable).\n *\n * Note this list is intentionally narrower than {@link DEFAULT_PERSIST_EXCLUDE_TOOLS}:\n * `read_file` is NOT exempt — a 200 KiB file load is exactly the case the\n * budget should steer against. Same for `spawn` (a sprawling subagent\n * report should still nudge a consolidation pass).\n *\n * Exported so SDK consumers building custom profiles can extend the list\n * for host-defined context-loading tools.\n */\nexport const DEFAULT_BUDGET_EXCLUDE_TOOLS: readonly string[] = [\n 'tool_search',\n 'skills_use',\n 'skills_read',\n 'read_chat_turns',\n]\n\n/**\n * Token-saving `AgentBehavior` defaults shared by the built-in profiles.\n *\n * These flip on at the chat layer, not at the SDK core (`src/agent.ts`) —\n * external `createAgent()` consumers keep the conservative defaults.\n * Rationale per knob:\n *\n * - `compactStrategy: 'tail'` — when the persisted history's tool-output\n * bytes exceed `compactThreshold`, stub older `tool_result` blocks at\n * the wire level (session storage is untouched). Without this, a single\n * long-lived chat keeps re-sending every read/grep result every turn.\n * - `elideStaleReads: true` — after a successful `edit` / `write_file`,\n * replace earlier `read_file` results on the same path with a short\n * stub. Eliminates the pre-edit file body riding along forever.\n * - `toolDisclosure: 'lazy'` — MCP tools advertise as name+description in\n * the system prompt and load their `inputSchema` on demand via\n * `tool_search`. No-op without MCPs (gated on `lazyEntries.length > 0`\n * in `src/agent.ts:1287`); substantial savings with a 50-tool server.\n * - `toolOutputBudget: 64 KiB` + `toolOutputBudgetExcludeTools` — soft\n * per-turn cap that injects a summarize-before-continuing nudge when\n * text-equivalent tool output explodes. Context-loading tools (`tool_search`,\n * `skills_use`, `skills_read`, `read_chat_turns`) are exempt so the model isn't steered\n * away from the very call it made to load schemas / skill content.\n * - `persistThreshold: 8 KiB` + `persistExcludeTools` — tool results\n * above the threshold are written to disk and replaced inline with a\n * `<persisted-output>` stub (preview + path). `persistDir` is wired by\n * the TUI at session activation; without a dir, persistence is off.\n * The user can disable persistence entirely via\n * `Settings.persistToolResults` (sets the threshold to 0 at agent build\n * time).\n */\n/**\n * Stable JSON of a tool input with keys sorted, used as a dedup cache key for\n * the read-only discovery tools. Covers EVERY input field, so `grep -i` never\n * collides with `grep` and a widened `limit` busts the cache. Empty objects\n * opt out (returns `undefined`) — a zero-arg `list_files` of cwd is cheap and\n * a stale replay there is more surprising than re-running it.\n */\nfunction discoveryDedupHash(input: Record<string, unknown>): string | undefined {\n const keys = Object.keys(input)\n if (keys.length === 0)\n return undefined\n keys.sort()\n const norm: Record<string, unknown> = {}\n for (const k of keys)\n norm[k] = input[k]\n return JSON.stringify(norm)\n}\n\nconst SHARED_BEHAVIOR = {\n compactStrategy: 'tail',\n compactThreshold: 128 * 1024,\n // Byte-budget eviction (see `applyTailCompaction`) keeps the largest recent\n // suffix of tool output that fits under `compactThreshold`, so for typical\n // small results the model retains far more than this many messages. This is\n // now a FLOOR, not a cap: the trailing 8 messages (~4 tool round-trips) are\n // never elided, guaranteeing a freshly read file survives the\n // read → think → edit cadence even when individual results are large enough\n // that the byte budget alone would keep fewer. (Was 4 — too tight; a read\n // got elided before the model could edit, forcing re-read loops.)\n compactKeepTurns: 8,\n // `'notice'`: when an edit lands on a previously-read file, a persisted\n // `<system-reminder>` naming the stale path is appended to the edit's own\n // result — append-only, so the prompt cache never breaks. The `'stub'` policy\n // (rewrite the old read on the wire) reclaims the stale bytes from context\n // but re-bills the conversation at each newly-stale read; measured on a\n // 129-turn production trace that was 13 full-prefix invalidations\n // (~0.25M re-billed chars) against bytes that cost ~0.1x to simply keep\n // cached. Interactive hosts sit far from their context ceiling, so the\n // cache-clean policy wins. Live A/B in\n // `test-inference/cache-stability.inference.test.ts`.\n elideStaleReads: 'notice',\n // Age base64 media (pasted images, Read images, attached PDFs) off the wire\n // beyond the keep window. Matched to `compactKeepTurns` (8) so the media\n // frontier and the tail-compaction frontier (both snapped to the same\n // 8-message chunk) jump at the SAME boundary — one prompt-cache invalidation\n // per chunk instead of two. The bytes stay in the session; the model pulls\n // any image back with `read_file path=\"attachment://<hash>\"`. Without this,\n // an image-heavy chat re-sends every screenshot's base64 on every turn — the\n // dominant long-run context bloat for vision sessions.\n mediaKeepTurns: 8,\n // Loop-native auto-compaction: when a turn's effective input crosses 85% of\n // the model's context window, the loop summarizes the older turns mid-run\n // (real LLM summary + file/skill restoration) and continues. This replaces\n // the old host-level post-run trigger so compaction happens inside the run\n // (mid tool-loop AND end-of-run) with no double-fire. The TUI/GUI inject the\n // picked model's `contextWindow` (and honor the user's enable/threshold\n // settings) per run; without a window source the trigger is inert.\n autoCompact: { threshold: 0.85 },\n toolDisclosure: 'lazy',\n toolOutputBudget: 64 * 1024,\n toolOutputBudgetExcludeTools: DEFAULT_BUDGET_EXCLUDE_TOOLS,\n persistThreshold: 8 * 1024,\n persistExcludeTools: DEFAULT_PERSIST_EXCLUDE_TOOLS,\n // `requireReadBeforeEdit` — pairs with `elideStaleReads` (already on) to\n // close the stale-content edit loop: a model can't `edit` a file it never\n // `read`, and an edit against bytes that moved on disk is refused. The\n // partner is the read-state seed in `write_file` (a just-written file counts\n // as read), so the common `write_file` → `edit` flow still works without a\n // redundant re-read.\n requireReadBeforeEdit: true,\n // `dedupTools` for the read-only discovery tools, in `replay` mode — an\n // identical re-issue replays the prior result from cache instead of\n // re-walking the filesystem. Free (no behavior change beyond cache replay)\n // and breaks the cheap-replay loop shape (model probing the same\n // `glob`/`grep`/`list_files` repeatedly). The hasher covers all input\n // fields so flag/limit changes bust the cache.\n dedupTools: {\n glob: discoveryDedupHash,\n grep: discoveryDedupHash,\n list_files: discoveryDedupHash,\n },\n // `repeatGuard` — identical-call detector. Defaults track the built-in\n // `shell` and `read_file` tools. Shell keeps the generic 4/8 block/abort\n // thresholds; `read_file` uses 6/12 so normal scaffolding re-visits don't\n // trip as quickly. The abort is the only layer that stops a model which\n // ignores the `Blocked:` reason and keeps retrying the same payload.\n //\n // `mode: 'both'` because strict chain counting is trivially defeated: ANY\n // intervening call resets it, and a spinning model always emits some. A real\n // run spent 123 turns and 10M input tokens re-issuing ONE byte-identical\n // `read_file` 59 times, alternating it with `grep`s on the same path; the\n // longest consecutive streak was 5 against a threshold of 6, so nothing ever\n // fired and the user had to cancel. The interleaved calls were `dedupTools`\n // cache replays, i.e. free for the model and (by design) chain boundaries\n // here — the guard was structurally unreachable. The sliding window counts\n // the same payload among that tool's own recent calls, so dilution slows the\n // trip instead of erasing it: the same run aborts at call 28 of 123.\n repeatGuard: { mode: 'both', windowSize: 40 },\n // `thinkingDecay` — after turn 10 the model has converged or is thrashing;\n // either way extended thinking earns less per token. STEP form (no\n // `factor`), not geometric: `thinking.budget_tokens` is part of Anthropic's\n // prompt-cache key, so every distinct budget a schedule emits is a FULL\n // cache re-bill — `cacheRead: 0`, scaffold included (measured; pinned in\n // `test-inference/cache-stability.inference.test.ts`). The old geometric\n // halving (8192 → 4096 → 2048 → 1024) cost three of those per run; one step\n // straight to the floor costs exactly one. No-op unless a thinking budget\n // is set.\n thinkingDecay: { afterTurn: 10, floor: 1024 },\n} as const\n\n/**\n * Build agent — the default profile. Full read/write/shell access plus\n * subagent spawning. Mirrors the legacy `basic` preset so existing TUI\n * behavior is preserved when `agents` is omitted.\n *\n * The system prompt is the cwd-less variant of {@link buildBuildSystem};\n * the TUI overrides it per-session with an env-anchored composition so\n * the prompt carries the current project root + date. SDK consumers\n * calling `createAgent({ ...BUILD_AGENT.preset })` directly get the\n * cwd-less prompt and can wrap their own env section.\n */\nexport const BUILD_AGENT: AgentProfile = {\n id: 'build',\n label: 'Build',\n description: 'full tool access: read, write, edit, shell, web search, and spawn subagents',\n accent: 'accent',\n // `composePresets` layers `createTodoTools()` on top so the model gets\n // `todowrite` / `todoread` plus their per-run write budget. Top-level\n // runs share a persistent session slot (continuity across prompts);\n // subagent runs get their own isolated slot. Auto-clears when every\n // item is `completed`, so a fresh prompt after a successful batch\n // starts clean. See `src/chat/todos.ts` for the keying / hygiene\n // contract.\n //\n // The composition is per-tool-name deep-merge, so a downstream host\n // re-composing this profile with its own per-tool overrides gets the\n // expected last-wins-on-collision behavior.\n //\n // Plan mode deliberately omits todos — a read-only exploration session\n // has nothing to checkpoint.\n preset: composePresets(\n definePreset({\n name: 'build',\n system: buildBuildSystem(),\n behavior: { ...SHARED_BEHAVIOR },\n // `persist: true` shares the parent's session with every child agent so a\n // reloaded TUI can reconstruct the subagent tree from `session.turns`.\n tools: { ...BUILD_TOOLS, spawn: createSpawnTool({ persist: true }) },\n }),\n createTodoTools(),\n ),\n}\n\n/**\n * Plan agent — read-only exploration mode. Locked down to file reading and\n * codebase search; no shell, no edits, no spawning. The system prompt\n * frames the conversation as planning rather than execution so the model\n * outputs proposals instead of attempting mutations.\n */\nexport const PLAN_AGENT: AgentProfile = {\n id: 'plan',\n label: 'Plan',\n description: 'read-only: explore, analyze, search the web, and propose without modifying anything',\n accent: 'model',\n preset: definePreset({\n name: 'plan',\n system: buildPlanSystem(),\n behavior: { ...SHARED_BEHAVIOR },\n tools: READ_ONLY_TOOLS,\n }),\n}\n\n/**\n * Default registry shipped with `zidane/tui`. Insertion order = picker order.\n * Hosts that want only one profile can pass `{ agents: { build: BUILD_AGENT } }`,\n * or a fully custom registry of their own profiles.\n */\nexport const BUILTIN_AGENTS: AgentRegistry = {\n build: BUILD_AGENT,\n plan: PLAN_AGENT,\n}\n\n/** Id of the profile activated on first launch when nothing is persisted. */\nexport const DEFAULT_AGENT_ID = 'build'\n\n/**\n * Resolve an agent id against a registry with sensible fallbacks: the\n * requested id wins when present, then `defaultId`, then the first key.\n * Returns `null` when the registry is empty (host misconfiguration —\n * the caller should surface this rather than silently failing).\n */\nexport function resolveAgentId(\n registry: AgentRegistry,\n requestedId: string | undefined,\n defaultId: string | undefined,\n): string | null {\n if (requestedId && registry[requestedId])\n return requestedId\n if (defaultId && registry[defaultId])\n return defaultId\n const first = Object.keys(registry)[0]\n return first ?? null\n}\n\n/**\n * Wrap a legacy single `Preset` into a single-profile registry. Used by\n * `resolveConfig` when the host passed `preset` without `agents`, so older\n * call sites keep working — they get a one-entry registry whose picker is\n * a no-op (only one option).\n */\nexport function singleAgentRegistry(preset: Preset): AgentRegistry {\n return {\n default: {\n id: 'default',\n label: typeof preset.name === 'string' && preset.name.length > 0 ? preset.name : 'Default',\n description: 'host-provided preset',\n preset,\n accent: 'accent',\n },\n }\n}\n","/**\n * Read-only discovery of [Ruler](https://github.com/intellectronica/ruler)\n * `.ruler/` directories — MCP servers, skills, and instruction markdown.\n *\n * Ruler centralizes AI-agent configuration in one project directory and\n * GENERATES per-agent files from it (`ruler apply`). zidane reads the\n * SOURCES directly, so a ruler-managed repo works without running ruler:\n *\n * .ruler/ruler.toml `[mcp_servers.<name>]` tables — the same TOML\n * grammar as Codex's config, parsed by the shared\n * `codex-toml` handler in `foreign-configs.ts`.\n * .ruler/mcp.json legacy ruler MCP file — shared JSON grammar.\n * .ruler/skills/ `<name>/SKILL.md` — the ecosystem-shared format.\n * .ruler/*.md instruction files, concatenated by ruler into the\n * generated AGENTS.md; zidane loads them as per-file\n * sections instead. Recursive, excluding the\n * `skills/` and `agents/` subtrees (ruler does the\n * same). Ordering mirrors ruler: `AGENTS.md` first\n * (legacy `instructions.md` as fallback primary),\n * remaining files sorted by relative path.\n *\n * User scope is ruler's global config dir: `$XDG_CONFIG_HOME/ruler`\n * (default `~/.config/ruler`) — same three surfaces.\n *\n * CONTAINMENT CONTRACT: every piece of ruler knowledge lives in this\n * module. The discovery surfaces (`mcps-discovery`, `skills-discovery`,\n * `agents-md`) each make exactly one append/skip call into it, all gated\n * behind the `importRulerConfigs` setting — deleting this file and those\n * call sites removes the feature completely. zidane never WRITES to\n * `.ruler/`; installs and toggles keep landing in `.zidane` / `.agents`.\n */\n\nimport type { ForeignMcpFile } from './foreign-configs'\nimport type { ProjectUserPath } from './project-user-paths'\nimport { existsSync, readdirSync, realpathSync, statSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join, resolve } from 'node:path'\nimport { findGitRoot } from './project-root'\n\ninterface RulerPathOptions {\n /** Discovery cwd. Default: `process.cwd()`. */\n cwd?: string\n /** User home directory. Default: `os.homedir()`. */\n home?: string\n /** XDG config home override. Default: `$XDG_CONFIG_HOME`, else `<home>/.config`. */\n xdgConfigHome?: string\n}\n\n/** Subtrees of `.ruler/` that are NOT instruction markdown (mirrors ruler's own exclusions). */\nconst NON_INSTRUCTION_DIRS = new Set(['skills', 'agents'])\n\n/** Primary instruction file (ordered first), and ruler's legacy fallback primary. */\nconst PRIMARY_MD = 'AGENTS.md'\nconst LEGACY_PRIMARY_MD = 'instructions.md'\n\nfunction rulerRoots(opts: RulerPathOptions): { project: string, user: string } {\n const cwd = opts.cwd ?? process.cwd()\n const home = opts.home ?? homedir()\n // An explicitly injected `home` beats the process's $XDG_CONFIG_HOME:\n // callers that redirect the user scope (tests, hosts with virtual homes,\n // `discoverAgentsMd({ home })` which can't thread `xdgConfigHome`) must\n // not silently read the REAL user's global ruler dir through the env.\n const xdg = opts.xdgConfigHome\n ?? (opts.home !== undefined\n ? resolve(opts.home, '.config')\n : process.env.XDG_CONFIG_HOME ?? resolve(home, '.config'))\n return {\n project: resolve(findGitRoot(cwd) ?? cwd, '.ruler'),\n user: resolve(xdg, 'ruler'),\n }\n}\n\n/**\n * MCP config files ruler maintains, project first. Returned regardless of\n * existence (callers `existsSync`-filter — same contract as\n * `foreignMcpConfigFiles`). The `codex-toml` format tag names the shared\n * `[mcp_servers.<name>]` TOML *grammar*, not the tool: ruler.toml uses it\n * verbatim, so the existing parser applies unchanged.\n */\nexport function rulerMcpConfigFiles(opts: RulerPathOptions = {}): ForeignMcpFile[] {\n const { project, user } = rulerRoots(opts)\n return [\n { path: join(project, 'ruler.toml'), source: 'project', format: 'codex-toml' },\n { path: join(project, 'mcp.json'), source: 'project', format: 'json' },\n { path: join(user, 'ruler.toml'), source: 'user', format: 'codex-toml' },\n { path: join(user, 'mcp.json'), source: 'user', format: 'json' },\n ]\n}\n\n/**\n * Skill directories ruler maintains, project first. Feed directly into\n * `discoverSkills` — ruler's `skills/<name>/SKILL.md` layout is the\n * ecosystem-shared format the lenient loader already parses.\n */\nexport function rulerSkillScanPaths(opts: RulerPathOptions = {}): ProjectUserPath[] {\n const { project, user } = rulerRoots(opts)\n return [\n { path: join(project, 'skills'), source: 'project' },\n { path: join(user, 'skills'), source: 'user' },\n ]\n}\n\n/**\n * Instruction markdown files under both ruler roots, in ruler's own\n * concatenation order per root (primary first, rest sorted by relative\n * path), user scope before project scope so the narrower scope lands\n * closer to the conversation — matching `discoverAgentsMd`'s convention.\n *\n * Only existing files are returned (unlike the config-file lists above):\n * the walk itself has to touch the filesystem anyway, and callers feed\n * these straight into per-file readers.\n */\nexport function rulerInstructionFiles(opts: RulerPathOptions = {}): ProjectUserPath[] {\n const { project, user } = rulerRoots(opts)\n return [\n ...collectInstructionMd(user).map(path => ({ path, source: 'user' as const })),\n ...collectInstructionMd(project).map(path => ({ path, source: 'project' as const })),\n ]\n}\n\n/**\n * True when a generated file (e.g. the repo-root `AGENTS.md`) was written\n * by `ruler apply` — it aggregates the `.ruler/*.md` sources, so loading\n * both would double every instruction. Two signals, either suffices:\n * ruler's generation marker, and the per-section source comments older\n * versions emitted without the marker.\n */\nexport function isRulerGenerated(contents: string): boolean {\n return contents.includes('<!-- Generated by Ruler -->')\n || contents.includes('<!-- Source: .ruler/')\n}\n\n/**\n * Bounds for the instruction walk — same philosophy as the 64 KB per-file\n * cap in `agents-md.ts`: a `.ruler/docs -> $HOME` symlink or a huge\n * committed tree must not stall session activation (the walk runs on the\n * main path, per activation and per discovery refresh). Ruler setups in\n * the wild are a handful of files, two levels deep.\n */\nconst MAX_WALK_DEPTH = 6\nconst MAX_INSTRUCTION_FILES = 100\n\n/** Recursive `.md` walk under one ruler root, ruler-ordered. Missing/unreadable dirs → `[]`. */\nfunction collectInstructionMd(rulerDir: string): string[] {\n if (!existsSync(rulerDir))\n return []\n const collected: string[] = []\n // Symlinked dirs are followed (a linked instructions tree should load),\n // so guard against cycles: each REAL directory is walked at most once.\n const visited = new Set<string>([realpathOr(rulerDir)])\n const walk = (dir: string, top: boolean, depth: number): void => {\n if (depth > MAX_WALK_DEPTH || collected.length >= MAX_INSTRUCTION_FILES)\n return\n let entries\n try {\n entries = readdirSync(dir, { withFileTypes: true })\n }\n catch {\n return // permission glitch — tolerate, like every discovery surface\n }\n for (const entry of entries) {\n if (collected.length >= MAX_INSTRUCTION_FILES)\n return\n if (entry.name.startsWith('.'))\n continue\n const full = join(dir, entry.name)\n // `withFileTypes` reports symlinks as neither file nor dir — stat\n // through them so a symlinked instructions file still loads.\n const kind = entry.isSymbolicLink() ? statKind(full) : entry.isDirectory() ? 'dir' : entry.isFile() ? 'file' : 'other'\n if (kind === 'dir') {\n if (top && NON_INSTRUCTION_DIRS.has(entry.name))\n continue\n const real = realpathOr(full)\n if (visited.has(real))\n continue\n visited.add(real)\n walk(full, false, depth + 1)\n }\n else if (kind === 'file' && entry.name.endsWith('.md')) {\n collected.push(full)\n }\n }\n }\n walk(rulerDir, true, 0)\n // Ruler's ordering: top-level AGENTS.md is the primary (legacy\n // instructions.md when AGENTS.md is absent), everything else follows\n // in sorted order.\n const primary = join(rulerDir, PRIMARY_MD)\n const legacy = join(rulerDir, LEGACY_PRIMARY_MD)\n const first = collected.includes(primary) ? primary : collected.includes(legacy) ? legacy : undefined\n const rest = collected.filter(p => p !== first).sort()\n return first ? [first, ...rest] : rest\n}\n\nfunction realpathOr(path: string): string {\n try {\n return realpathSync(path)\n }\n catch {\n return path\n }\n}\n\nfunction statKind(path: string): 'dir' | 'file' | 'other' {\n try {\n const s = statSync(path)\n return s.isDirectory() ? 'dir' : s.isFile() ? 'file' : 'other'\n }\n catch {\n return 'other'\n }\n}\n","/**\n * Personal instructions discovery — load user + project `AGENTS.md` /\n * `CLAUDE.md` files.\n *\n * Every file that exists is loaded — no shadowing within a scope.\n * Users routinely split doctrine across files (\"`~/.agents/AGENTS.md`\n * for cross-tool portable rules, `~/.zidane/AGENTS.md` for\n * zidane-specific tweaks\") and silently dropping a file they committed\n * is surprising. Empty or whitespace-only files are skipped.\n *\n * Two conventions are supported in each scope:\n * - `AGENTS.md` — Codex / Cursor / agentic-tools convention.\n * - `CLAUDE.md` — Claude Code convention.\n *\n * Search order (broadest scope first; same order in the rendered\n * block, so narrower scopes land closer to the conversation and\n * override broader ones in the model's eyes):\n *\n * user scope:\n * 1. `~/.agents/AGENTS.md` — agnostic\n * 2. `~/.{prefix}/AGENTS.md` — zidane (`.zidane` default)\n * 3. `~/.claude/CLAUDE.md` — Claude Code\n *\n * project scope (anchored at git root, else cwd):\n * 4. `<root>/AGENTS.md` — Codex convention\n * 5. `<root>/.agents/AGENTS.md` — agnostic\n * 6. `<root>/.{prefix}/AGENTS.md` — zidane\n * 7. `<root>/CLAUDE.md` — Claude Code\n *\n * The {@link DiscoverAgentsMdOptions.scope} option restricts which\n * scopes contribute. Defaults to `'both'` (the historical behavior).\n *\n * Each file is rendered as a section with its absolute path as the\n * header so the model can cite the source verbatim when it asks the\n * user about something that came from there.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { resolve } from 'node:path'\nimport { errorMessage } from '../errors'\nimport { findGitRoot } from './project-root'\nimport { isRulerGenerated, rulerInstructionFiles } from './ruler-configs'\n\n/** A single loaded AGENTS.md file. */\nexport interface AgentsMdFile {\n path: string\n source: 'project' | 'user'\n contents: string\n}\n\n/** Result of {@link discoverAgentsMd}. `files` is empty when none were found. */\nexport interface AgentsMdResult {\n /** All loaded files in render order (user first, project last). */\n files: readonly AgentsMdFile[]\n /**\n * Pre-rendered prompt block ready to append to the system prompt, or\n * `null` when no files were loaded. Header + per-file sections with\n * absolute paths.\n */\n block: string | null\n}\n\n/** Sane upper bound per file so a runaway log dumped as AGENTS.md doesn't blow the prompt. */\nconst MAX_BYTES_PER_FILE = 64 * 1024\n\n/**\n * Which scope(s) of personal instructions to load.\n * - `'none'` — skip discovery entirely; no files load. Useful for\n * reproducible reviews, demos, or debugging the base\n * system prompt without any user doctrine bleeding in.\n * - `'user'` — `~/.agents/AGENTS.md`, `~/.{prefix}/AGENTS.md`, `~/.claude/CLAUDE.md`\n * - `'project'` — `<root>/AGENTS.md`, `<root>/.agents/AGENTS.md`, `<root>/.{prefix}/AGENTS.md`, `<root>/CLAUDE.md`\n * - `'both'` — union of user + project (default)\n *\n * Exposed as a user-facing setting so users can scope their instructions\n * to e.g. only the project (when running zidane against an unfamiliar\n * repo and they don't want their global preferences leaking in).\n */\nexport type AgentsMdScope = 'both' | 'none' | 'project' | 'user'\n\nexport interface DiscoverAgentsMdOptions {\n /** Discovery cwd. Default: `process.cwd()`. */\n cwd?: string\n /** User home directory. Default: `os.homedir()`. */\n home?: string\n /** Storage prefix. Leading dot tolerated. Default: `'.zidane'`. */\n prefix?: string\n /** Which scopes to load. Default: `'both'`. */\n scope?: AgentsMdScope\n /**\n * Also load Ruler instruction markdown (`.ruler/*.md`, project and\n * XDG-global) — see `ruler-configs.ts`. Mirrors the\n * `importRulerConfigs` setting. When on, a repo-root `AGENTS.md`\n * that `ruler apply` GENERATED (it aggregates those same sources) is\n * skipped so instructions never load twice.\n */\n ruler?: boolean\n}\n\n/**\n * Walk the candidate paths and load every `AGENTS.md` that exists.\n *\n * Project paths anchor at the git root when one exists above `cwd`,\n * else at `cwd` itself — same anchor logic as `projectUserPaths` so the\n * three discovery surfaces (skills, mcps, AGENTS.md) stay in lock-step.\n *\n * All matching files are loaded (no shadowing within a scope). Empty\n * or whitespace-only files are skipped. Render order matches the\n * search order — broader scopes first, narrower scopes last so the\n * project-specific file lands closest to the conversation.\n *\n * Tolerant on read failures: an unreadable file is skipped silently\n * (logged under `ZIDANE_DEBUG`). The loader never throws — bootstrap\n * paths can't afford to crash on a permission glitch.\n *\n * Defensive dedup: when `<git-root> === ~` (the user launched zidane\n * directly from `$HOME` and `$HOME` happens to be a git repo) the\n * project + user candidate sets would otherwise overlap. We dedup by\n * absolute path so the same file never lands in the block twice.\n */\nexport function discoverAgentsMd(opts: DiscoverAgentsMdOptions = {}): AgentsMdResult {\n const scope: AgentsMdScope = opts.scope ?? 'both'\n // Short-circuit on `'none'` BEFORE touching the filesystem — no\n // existsSync probes, no homedir() / git-root walk. Keeps the\n // explicit opt-out genuinely free.\n if (scope === 'none')\n return { files: [], block: null }\n\n const cwd = opts.cwd ?? process.cwd()\n const home = opts.home ?? homedir()\n const prefix = (opts.prefix ?? '.zidane').replace(/^\\./, '')\n const projectRoot = findGitRoot(cwd) ?? cwd\n\n // Ruler import (behind `importRulerConfigs`): its instruction files\n // join the candidate lists per scope, and the repo-root AGENTS.md is\n // dropped when ruler GENERATED it — the sources are already loading, and\n // they're fresher than the aggregate (which may predate an edit).\n // Forward `home` only when the CALLER supplied one: `rulerRoots` treats\n // an explicit home as \"redirected user scope\" and pins XDG to\n // `<home>/.config`, so passing the defaulted value here would ignore a\n // real `$XDG_CONFIG_HOME` — unlike the MCP/skill surfaces, which omit\n // `home` on the live path and honor the env.\n const rulerFiles = opts.ruler ? rulerInstructionFiles({ cwd, ...(opts.home !== undefined ? { home: opts.home } : {}) }) : []\n\n const userCandidates: { path: string, source: 'user' }[] = [\n { path: resolve(home, `.agents/AGENTS.md`), source: 'user' },\n { path: resolve(home, `.${prefix}/AGENTS.md`), source: 'user' },\n { path: resolve(home, `.claude/CLAUDE.md`), source: 'user' },\n ...rulerFiles.filter(f => f.source === 'user').map(f => ({ path: f.path, source: 'user' as const })),\n ]\n const projectCandidates: { path: string, source: 'project' }[] = [\n { path: resolve(projectRoot, 'AGENTS.md'), source: 'project' },\n { path: resolve(projectRoot, `.agents/AGENTS.md`), source: 'project' },\n { path: resolve(projectRoot, `.${prefix}/AGENTS.md`), source: 'project' },\n { path: resolve(projectRoot, 'CLAUDE.md'), source: 'project' },\n ...rulerFiles.filter(f => f.source === 'project').map(f => ({ path: f.path, source: 'project' as const })),\n ]\n // Ruler-generated aggregates yield to their sources, PER SCOPE, and only\n // when sources for that scope REALLY loaded — a committed generated file\n // whose `.ruler/` sources are gone on this machine (gitignored, deleted)\n // must keep loading, or the user's instructions silently vanish. Applies\n // to EVERY non-ruler candidate, not just the root AGENTS.md: `ruler\n // apply` also generates root `CLAUDE.md` (claude agent), `.agents/\n // AGENTS.md` (goose/codex outputs), and `~/.claude/CLAUDE.md` from a\n // global ruler dir — each aggregates the same sources, so loading any of\n // them alongside the sources would duplicate every instruction.\n const rulerSourcePaths = new Set(rulerFiles.map(f => f.path))\n // Pre-read the ruler sources: the aggregate-skip must key on files that\n // actually LOADED (`readIfPresent` rejects empty / whitespace-only /\n // unreadable files), not on mere existence — a `.ruler/` of empty stubs\n // must not suppress the generated aggregate, or every instruction\n // silently vanishes. The loop below reuses these reads via the map.\n const rulerRead = new Map<string, AgentsMdFile | null>()\n for (const f of rulerFiles) {\n if (!rulerRead.has(f.path))\n rulerRead.set(f.path, readIfPresent(f.path, f.source))\n }\n const rulerLoadedByScope = {\n project: rulerFiles.some(f => f.source === 'project' && rulerRead.get(f.path)),\n user: rulerFiles.some(f => f.source === 'user' && rulerRead.get(f.path)),\n }\n\n const ordered: ({ path: string, source: 'project' | 'user' })[] = []\n if (scope !== 'project')\n ordered.push(...userCandidates)\n if (scope !== 'user')\n ordered.push(...projectCandidates)\n\n const files: AgentsMdFile[] = []\n const seen = new Set<string>()\n for (const c of ordered) {\n if (seen.has(c.path))\n continue\n const file = rulerSourcePaths.has(c.path)\n ? rulerRead.get(c.path) ?? null\n : readIfPresent(c.path, c.source)\n if (file) {\n seen.add(c.path)\n if (!rulerSourcePaths.has(c.path) && rulerLoadedByScope[c.source] && isRulerGenerated(file.contents))\n continue // ruler-generated aggregate — the .ruler sources load instead\n files.push(file)\n }\n }\n\n if (files.length === 0)\n return { files, block: null }\n\n return { files, block: renderAgentsMdBlock(files) }\n}\n\n/**\n * Render one or more `AgentsMdFile` into a system-prompt block.\n *\n * Top header tells the model what's coming + that it's user-authored\n * (so it's treated as guidance, not as a system constraint that can't\n * be questioned). Each file becomes a sub-section with its absolute\n * path so the model can cite it back when asking follow-up questions.\n *\n * Exported separately so callers building custom profiles can inject\n * their own discovery list (e.g. an SDK consumer pulling AGENTS.md\n * from a remote source) without re-implementing the rendering.\n */\nexport function renderAgentsMdBlock(files: readonly AgentsMdFile[]): string {\n if (files.length === 0)\n return ''\n const sections = files.map((f) => {\n const scope = f.source === 'project' ? 'project' : 'user'\n return `## ${f.path} (${scope})\\n\\n${f.contents.trim()}`\n })\n return [\n '# User instructions',\n 'The following sections come from `AGENTS.md` files the user authored. Treat them as durable preferences and context that apply for this session — they extend, and where they conflict take precedence over, the doctrine above.',\n ...sections,\n ].join('\\n\\n')\n}\n\nfunction readIfPresent(path: string, source: 'project' | 'user'): AgentsMdFile | null {\n // Returns null when the file is absent, empty, or unreadable.\n // The caller (discoverAgentsMd) iterates candidates and pushes\n // every non-null hit — so multiple files per scope concatenate\n // naturally and a stub doesn't shadow a real file underneath.\n if (!existsSync(path))\n return null\n let raw: string\n try {\n raw = readFileSync(path, 'utf8')\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] agents-md: failed to read \"${path}\": ${errorMessage(err)}\\n`)\n return null\n }\n // Truncate gigantic files defensively — the model still sees the\n // path, so the user can pare the file down themselves.\n const contents = raw.length > MAX_BYTES_PER_FILE\n ? `${raw.slice(0, MAX_BYTES_PER_FILE)}\\n\\n[... truncated; ${raw.length - MAX_BYTES_PER_FILE} more bytes in source file ...]`\n : raw\n if (contents.trim().length === 0)\n return null\n return { path, source, contents }\n}\n","/**\n * Renderer-agnostic shaping of session artifacts for display — grouping\n * and row presentation. Shared by the TUI rail + modal, the GUI rail and\n * the sessions-list badge, so every surface agrees on what an artifact\n * looks like.\n */\n\nimport type { ExtensionArtifactRow, ExtensionRegistry } from '../extensions'\nimport type { SessionArtifact } from '../session'\nimport { artifactKindLabel, renderArtifactRow } from '../extensions/builtin-artifact-kinds'\n\nexport type ArtifactScope = 'session' | 'project'\n\nexport interface ArtifactViewRow {\n artifact: SessionArtifact\n row: ExtensionArtifactRow\n}\n\nexport interface ArtifactViewGroup {\n kind: string\n /** Human kind label (`Pull request`), raw kind when unregistered. */\n label: string\n rows: ArtifactViewRow[]\n}\n\ntype KindRegistry = Pick<ExtensionRegistry, 'artifactKinds'> | undefined\n\n/** Display order for the built-in kinds; anything else follows alphabetically. */\nconst KIND_ORDER = ['pull_request', 'branch', 'commit', 'file']\n\n/**\n * Group by kind (built-ins first in a fixed order, then extension kinds\n * alphabetically); rows inside a group keep the store's newest-first order.\n */\nexport function groupArtifacts(artifacts: readonly SessionArtifact[], registry?: KindRegistry): ArtifactViewGroup[] {\n const byKind = new Map<string, ArtifactViewRow[]>()\n for (const artifact of artifacts) {\n const list = byKind.get(artifact.kind) ?? []\n list.push({ artifact, row: renderArtifactRow(artifact, registry) })\n byKind.set(artifact.kind, list)\n }\n const kinds = [...byKind.keys()].sort((a, b) => {\n const ai = KIND_ORDER.indexOf(a)\n const bi = KIND_ORDER.indexOf(b)\n if (ai !== -1 || bi !== -1)\n return (ai === -1 ? KIND_ORDER.length : ai) - (bi === -1 ? KIND_ORDER.length : bi)\n return a.localeCompare(b)\n })\n return kinds.map(kind => ({ kind, label: artifactKindLabel(kind, registry), rows: byKind.get(kind)! }))\n}\n\n/** `3 PRs · 2 files` style summary; empty string for none. */\nexport function summarizeArtifactCounts(artifacts: readonly SessionArtifact[], registry?: KindRegistry): string {\n return groupArtifacts(artifacts, registry)\n .map(g => `${g.rows.length} ${pluralizeKind(g.label, g.rows.length)}`)\n .join(' · ')\n}\n\nfunction pluralizeKind(label: string, count: number): string {\n const lower = label.toLowerCase()\n if (count === 1)\n return lower\n return /(?:s|x|ch|sh)$/.test(lower) ? `${lower}es` : `${lower}s`\n}\n","/**\n * Chat-layer auto-compaction helpers.\n *\n * The decision policy itself moved to `src/compact/policy.ts` (core, no chat\n * dependency) so the agent loop's loop-native auto-compaction shares the exact\n * same predicate as the chat hosts; this module re-exports it for back-compat\n * and adds the host-side glue that maps chat settings + a model window into\n * the SDK's `behavior.autoCompact` value.\n */\n\nimport type { Provider } from '../providers'\nimport type { AutoCompact } from '../types'\n\nexport {\n AUTO_COMPACT_MIN_GROWTH_FRACTION,\n type AutoCompactDecision,\n type AutoCompactInput,\n shouldAutoCompact,\n} from '../compact/policy'\n\n/**\n * Build the per-run `behavior.autoCompact` a chat host passes to `agent.run`.\n *\n * Loop-native auto-compaction is the single mechanism now; the TUI + GUI just\n * translate the user's enable/threshold settings plus the picked model's raw\n * context window (the SDK core has no model catalog) into the config the loop\n * consumes. Returns `false` when disabled. `rawContextWindow: null` (unknown\n * model) still enables it but leaves the trigger inert — the loop skips with\n * `unknown-window` rather than guessing.\n */\nexport function chatAutoCompactBehavior(opts: {\n enabled: boolean\n threshold: number\n rawContextWindow: number | null\n /**\n * Summarizer override — run the summary call on a different\n * provider/model than the main one (the user's \"compaction model\"\n * setting). Omitted = summarize on the run's main provider/model.\n * `rawContextWindow` above stays the MAIN model's window: the trigger\n * measures the live conversation, not the summarizer.\n */\n summarizer?: { provider: Provider, model: string }\n}): AutoCompact {\n if (!opts.enabled)\n return false\n const contextWindow = typeof opts.rawContextWindow === 'number'\n && Number.isFinite(opts.rawContextWindow)\n && opts.rawContextWindow > 0\n ? opts.rawContextWindow\n : null\n return {\n threshold: opts.threshold,\n ...(contextWindow ? { contextWindow } : {}),\n ...(opts.summarizer ? { provider: opts.summarizer.provider, model: opts.summarizer.model } : {}),\n // Chat hosts are long-lived processes with a persist dir always\n // configured, so trade inline transcript fidelity of compacted-away\n // turns (recoverable via each stub's persisted path) for bounded\n // memory. Headless/SDK runs keep the lossless default.\n releaseReplacedTurns: true,\n }\n}\n","// ---------------------------------------------------------------------------\n// Auto-update — renderer-agnostic core.\n//\n// Three layers, all reusable by any consumer that wraps `zidane/tui`:\n// 1. `checkForUpdate` — fetches the latest version from an npm-compatible\n// registry, persisted on disk with a TTL so the hot path costs zero\n// network on a warm cache.\n// 2. `performSelfUpdate` — spawns the host package manager (npm, pnpm,\n// yarn, bun, volta) and tells it to re-install the package globally.\n// 3. `performInPlaceSelfUpdate` — downloads the platform-binary tarball\n// from the registry, extracts the binary, atomically swaps it over\n// the live one. Used when no package manager is reachable (offline\n// install path, restricted shells) or when the user wants speed.\n//\n// Pure data + Node-builtin I/O. No React, no OpenTUI, no external deps —\n// hosts replacing the runtime can re-implement just the layer they need.\n// ---------------------------------------------------------------------------\n\nimport type { Buffer } from 'node:buffer'\nimport { spawn } from 'node:child_process'\nimport { createHash } from 'node:crypto'\nimport {\n chmodSync,\n createReadStream,\n createWriteStream,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n realpathSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from 'node:fs'\nimport { homedir, tmpdir } from 'node:os'\nimport { dirname, join, resolve, sep } from 'node:path'\nimport { createGunzip } from 'node:zlib'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface CheckForUpdateOptions {\n /** Full npm package name to query. e.g. `'zidane-tui'`. */\n packageName: string\n /** Current installed version. Inlined at build time from the consumer's `package.json`. */\n currentVersion: string\n /**\n * Registry base URL. Defaults to `https://registry.npmjs.org`. Strip the\n * trailing slash; the helper appends `/${packageName}/${channel}`.\n */\n registry?: string\n /**\n * dist-tag to read. Defaults to `'latest'`. Set to `'next'` (or any\n * custom tag) for prerelease channels — the comparison still goes through\n * `compareSemver`, so `5.6.0-beta.2 > 5.5.0` evaluates correctly.\n */\n channel?: string\n /** Directory used for the on-disk TTL cache. Skipped when omitted. */\n cacheDir?: string\n /** Cache lifetime. Defaults to 24 h. Negative / 0 disables caching. */\n cacheTtlMs?: number\n /** Network timeout. Defaults to 3 000 ms. */\n timeoutMs?: number\n /** Abort signal — wins over the internal timeout. */\n signal?: AbortSignal\n /**\n * When `true`, ignore env-var opt-outs (`CI`, `NO_UPDATE_NOTIFIER`,\n * `ZIDANE_NO_UPDATE`) AND bypass the on-disk TTL cache (always hit\n * the registry). Used by the explicit `zidane upgrade` path where the\n * user typed the command — they expect fresh data, not a 24-hour-old\n * cached value. The cache is still WRITTEN with the fresh result so\n * subsequent background checks benefit; cache reads only feed the\n * etag into the next request so the registry can short-circuit with\n * 304 when nothing changed.\n */\n force?: boolean\n /** Inject for tests. Defaults to `globalThis.fetch`. */\n fetcher?: typeof fetch\n /** Inject for tests. Defaults to `Date.now`. */\n now?: () => number\n}\n\nexport interface UpdateStatus {\n /** Always present — echoes `currentVersion`. */\n current: string\n /** `null` when the lookup failed / was skipped / cache was empty. */\n latest: string | null\n /** `true` when `latest` is strictly greater than `current` by semver. */\n hasUpdate: boolean\n /**\n * - `'fresh'` — fetched from the registry this call.\n * - `'cached'` — served from the on-disk cache.\n * - `'skipped'` — opt-out env var, missing fetch, network refused, etc.\n * - `'error'` — registry returned non-2xx or threw; details on `error`.\n */\n source: 'fresh' | 'cached' | 'skipped' | 'error'\n /** Error message when `source === 'error'`. */\n error?: string\n /** When the registry was last queried (ms since epoch). `null` when never. */\n checkedAt: number | null\n /** dist-tag used for the lookup — handy when surfacing \"v5.6.0 (next)\". */\n channel: string\n}\n\nexport interface PackageManagerCommand {\n /** Stable identifier. `npm` is the fallback when no other signal fires. */\n id: PackageManagerId\n /** Argv that, when execve'd, installs `packageName@channel` globally. */\n argv: string[]\n /**\n * Free-form note for the user — explains *why* this command was chosen\n * (e.g. \"Volta-managed install detected via ~/.volta/bin\").\n */\n note?: string\n}\n\nexport type PackageManagerId = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'volta'\n\nexport interface DetectPackageManagerOptions {\n /** Override the resolved binary path (defaults to `process.execPath`). */\n binaryPath?: string\n /** Override the user-agent string (defaults to `process.env.npm_config_user_agent`). */\n userAgent?: string | undefined\n /** Override env. Used in tests. */\n env?: Readonly<Record<string, string | undefined>>\n /** Override `homedir()`. Used in tests. */\n home?: string\n /** Override `process.platform`. Used in tests. */\n platform?: NodeJS.Platform\n /** When set, the host already knows the package name (avoids the test seam). */\n packageName?: string\n /** dist-tag. Defaults to `'latest'`. */\n channel?: string\n}\n\n// ---------------------------------------------------------------------------\n// Env-var opt-outs\n//\n// `NO_UPDATE_NOTIFIER` is the de-facto npm-ecosystem convention (yeoman,\n// update-notifier). `ZIDANE_NO_UPDATE` is the project-specific knob so\n// hosts wrapping the TUI can wire their own without colliding.\n// `CI=true` is set by every major CI runner; skipping there avoids\n// surprise registry chatter from build pipelines.\n// ---------------------------------------------------------------------------\n\nconst OPT_OUT_VARS = ['ZIDANE_NO_UPDATE', 'NO_UPDATE_NOTIFIER', 'CI'] as const\n\nfunction isOptedOut(env: NodeJS.ProcessEnv = process.env): boolean {\n for (const key of OPT_OUT_VARS) {\n const v = env[key]\n if (v !== undefined && v !== '' && v !== '0' && v.toLowerCase() !== 'false')\n return true\n }\n return false\n}\n\n// ---------------------------------------------------------------------------\n// Semver compare — tiny self-contained implementation.\n//\n// We support the subset of semver the npm registry actually returns:\n// `MAJOR.MINOR.PATCH` plus optional `-prerelease[.N…]` and `+build`. Build\n// metadata is ignored per spec; prereleases compare via the standard rule\n// (numeric vs string, longer chain wins after equal prefix).\n//\n// Inlining this beats pulling in `semver` (~70 KB) just to compare two\n// strings; we already accept the maintenance trade-off for `tail-truncated`\n// shells and other tiny utilities. Tested in `test/chat/auto-update.test.ts`.\n// ---------------------------------------------------------------------------\n\nexport function parseSemver(input: string): { major: number, minor: number, patch: number, prerelease: string[] } | null {\n const m = /^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-([\\w.-]+))?(?:\\+[\\w.-]+)?$/.exec(input.trim())\n if (!m)\n return null\n return {\n major: Number(m[1]),\n minor: Number(m[2]),\n patch: Number(m[3]),\n prerelease: m[4] ? m[4].split('.') : [],\n }\n}\n\n/** Returns `-1 / 0 / 1`. Unparseable inputs sort as `0` (assume equal). */\nexport function compareSemver(a: string, b: string): -1 | 0 | 1 {\n const A = parseSemver(a)\n const B = parseSemver(b)\n if (!A || !B)\n return 0\n for (const k of ['major', 'minor', 'patch'] as const) {\n if (A[k] !== B[k])\n return A[k] < B[k] ? -1 : 1\n }\n // A release version is greater than its prereleases (1.0.0 > 1.0.0-rc.1).\n if (A.prerelease.length === 0 && B.prerelease.length > 0)\n return 1\n if (A.prerelease.length > 0 && B.prerelease.length === 0)\n return -1\n const len = Math.max(A.prerelease.length, B.prerelease.length)\n for (let i = 0; i < len; i++) {\n const ai = A.prerelease[i]\n const bi = B.prerelease[i]\n if (ai === undefined)\n return -1\n if (bi === undefined)\n return 1\n const an = /^\\d+$/.test(ai) ? Number(ai) : null\n const bn = /^\\d+$/.test(bi) ? Number(bi) : null\n if (an !== null && bn !== null) {\n if (an !== bn)\n return an < bn ? -1 : 1\n }\n else if (an !== null) {\n return -1 // numeric < alphanumeric per spec\n }\n else if (bn !== null) {\n return 1\n }\n else if (ai !== bi) {\n return ai < bi ? -1 : 1\n }\n }\n return 0\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\ninterface UpdateCheckFile {\n packageName: string\n channel: string\n latest: string\n checkedAt: number\n /** Last-seen registry ETag — flows through If-None-Match on the next check. */\n etag?: string\n}\n\nfunction cacheFilePath(cacheDir: string, packageName: string, channel: string): string {\n // Embed package + channel in the filename so a host running multiple\n // checks (e.g. against `latest` and `next`) doesn't trample state.\n const slug = `${packageName.replaceAll('/', '__')}@${channel}`\n return resolve(cacheDir, `update-check-${slug}.json`)\n}\n\nfunction readCache(path: string): UpdateCheckFile | null {\n try {\n const raw = readFileSync(path, 'utf8')\n const parsed = JSON.parse(raw) as UpdateCheckFile\n if (typeof parsed.latest !== 'string' || typeof parsed.checkedAt !== 'number')\n return null\n return parsed\n }\n catch {\n return null\n }\n}\n\nfunction writeCache(path: string, entry: UpdateCheckFile): void {\n try {\n mkdirSync(dirname(path), { recursive: true })\n // Format with a trailing newline so hand-inspection in a terminal\n // doesn't render the prompt glued to the closing brace.\n writeFileSync(path, `${JSON.stringify(entry, null, 2)}\\n`)\n }\n catch {\n // Cache failures are non-fatal — the next call just re-fetches.\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registry fetch\n// ---------------------------------------------------------------------------\n\n/** Strip a trailing slash so URL assembly is uniform. */\nfunction trimSlash(s: string): string {\n return s.endsWith('/') ? s.slice(0, -1) : s\n}\n\ninterface FetchedRelease {\n latest: string\n etag: string | null\n}\n\nasync function fetchLatestRelease(opts: {\n registry: string\n packageName: string\n channel: string\n etag: string | undefined\n fetcher: typeof fetch\n signal: AbortSignal\n}): Promise<FetchedRelease> {\n // Use the `<pkg>/<dist-tag>` endpoint which returns a single packument\n // (the smallest payload). Falls back to the abbreviated metadata mime\n // type so npm proxies (Verdaccio, JFrog) that don't honor it still\n // return JSON.\n const url = `${trimSlash(opts.registry)}/${encodeURIComponent(opts.packageName)}/${encodeURIComponent(opts.channel)}`\n const headers: Record<string, string> = {\n 'accept': 'application/vnd.npm.install-v1+json, application/json',\n 'user-agent': 'zidane-update-check/1',\n }\n if (opts.etag)\n headers['if-none-match'] = opts.etag\n const res = await opts.fetcher(url, { headers, signal: opts.signal, redirect: 'follow' })\n if (res.status === 304) {\n // Caller falls back to the cached entry when this throws — keep the\n // signal explicit instead of overloading the return shape.\n throw new NotModifiedError()\n }\n if (!res.ok)\n throw new Error(`registry returned ${res.status}`)\n const body = await res.json() as { version?: string, name?: string }\n if (!body.version)\n throw new Error('registry payload missing `version`')\n return { latest: body.version, etag: res.headers.get('etag') }\n}\n\nclass NotModifiedError extends Error {\n constructor() { super('not-modified') }\n}\n\n// ---------------------------------------------------------------------------\n// Public API: checkForUpdate\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the latest available version of `packageName`, honoring an\n * on-disk TTL cache so the warm path costs zero network. Never throws —\n * registry failures degrade to `{ source: 'error', latest: null }`.\n *\n * Designed for boot-path usage: short timeout, swallows errors, opt-outs\n * for CI / restricted shells. Consumers driving the explicit `upgrade`\n * subcommand should set `force: true` to ignore env opt-outs.\n */\nexport async function checkForUpdate(options: CheckForUpdateOptions): Promise<UpdateStatus> {\n const channel = options.channel ?? 'latest'\n const now = options.now ?? Date.now\n const fetcher = options.fetcher ?? globalThis.fetch\n const ttl = options.cacheTtlMs ?? 24 * 60 * 60 * 1000\n const timeoutMs = options.timeoutMs ?? 3000\n\n if (!options.force && isOptedOut())\n return skipped(options.currentVersion, channel)\n\n const cachePath = options.cacheDir\n ? cacheFilePath(options.cacheDir, options.packageName, channel)\n : null\n\n // Fast-path the warm cache — but only when the cached payload still\n // matches `packageName + channel`. The slug guards against an\n // older zidane writing a different package's cache file at the same\n // path (host rename scenarios). `force` skips the freshness short-\n // circuit entirely: the user-typed `upgrade` path always wants the\n // truth from the registry, never a 24-hour-old cached \"no update\".\n const cached = cachePath ? readCache(cachePath) : null\n if (!options.force && cached && cached.packageName === options.packageName && cached.channel === channel) {\n const fresh = ttl > 0 && (now() - cached.checkedAt) < ttl\n if (fresh) {\n const hasUpdate = compareSemver(cached.latest, options.currentVersion) > 0\n return {\n current: options.currentVersion,\n latest: cached.latest,\n hasUpdate,\n source: 'cached',\n checkedAt: cached.checkedAt,\n channel,\n }\n }\n }\n\n // Fetch is required for the cold path. Falling back to a stale cache is\n // checked further below, but the contract for `skipped` is \"we didn't\n // even try\" — without a fetcher we can't try.\n if (typeof fetcher !== 'function') {\n if (cached) {\n return {\n current: options.currentVersion,\n latest: cached.latest,\n hasUpdate: compareSemver(cached.latest, options.currentVersion) > 0,\n source: 'cached',\n checkedAt: cached.checkedAt,\n channel,\n }\n }\n return skipped(options.currentVersion, channel)\n }\n\n // Tie the user's signal to our timeout so abort propagates. The\n // controller is local to this call — we never leak it to callers.\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(new Error('timeout')), timeoutMs)\n if (options.signal) {\n if (options.signal.aborted)\n controller.abort(options.signal.reason as Error)\n else\n options.signal.addEventListener('abort', () => controller.abort(options.signal!.reason as Error), { once: true })\n }\n\n try {\n const release = await fetchLatestRelease({\n registry: options.registry ?? 'https://registry.npmjs.org',\n packageName: options.packageName,\n channel,\n etag: cached?.etag,\n fetcher,\n signal: controller.signal,\n })\n const checkedAt = now()\n if (cachePath) {\n writeCache(cachePath, {\n packageName: options.packageName,\n channel,\n latest: release.latest,\n checkedAt,\n ...(release.etag ? { etag: release.etag } : {}),\n })\n }\n return {\n current: options.currentVersion,\n latest: release.latest,\n hasUpdate: compareSemver(release.latest, options.currentVersion) > 0,\n source: 'fresh',\n checkedAt,\n channel,\n }\n }\n catch (err) {\n if (err instanceof NotModifiedError && cached) {\n // 304 — extend the cache freshness window so we don't re-poll\n // every boot when the registry agrees we're current.\n const checkedAt = now()\n if (cachePath) {\n writeCache(cachePath, {\n ...cached,\n checkedAt,\n })\n }\n return {\n current: options.currentVersion,\n latest: cached.latest,\n hasUpdate: compareSemver(cached.latest, options.currentVersion) > 0,\n source: 'cached',\n checkedAt,\n channel,\n }\n }\n if (cached) {\n // Fall back to a stale cache rather than reporting `null` when we\n // have data — the user still sees something useful.\n return {\n current: options.currentVersion,\n latest: cached.latest,\n hasUpdate: compareSemver(cached.latest, options.currentVersion) > 0,\n source: 'cached',\n checkedAt: cached.checkedAt,\n channel,\n }\n }\n return {\n current: options.currentVersion,\n latest: null,\n hasUpdate: false,\n source: 'error',\n error: err instanceof Error ? err.message : String(err),\n checkedAt: null,\n channel,\n }\n }\n finally {\n clearTimeout(timer)\n }\n}\n\nfunction skipped(current: string, channel: string): UpdateStatus {\n return { current, latest: null, hasUpdate: false, source: 'skipped', checkedAt: null, channel }\n}\n\n// ---------------------------------------------------------------------------\n// Package-manager detection\n//\n// Layered heuristics — strongest signal wins:\n// 1. Resolved binary path. Volta lives under `~/.volta/bin`, bun under\n// `~/.bun/install/global/node_modules/.bin` (or `$BUN_INSTALL/bin`),\n// pnpm under `~/.local/share/pnpm` or `~/Library/pnpm`, classic yarn\n// under `~/.yarn/bin`, npm anywhere else under a `<prefix>/bin`.\n// 2. `npm_config_user_agent` env var — set during `npm/pnpm/yarn/bun\n// install` runs, so this still helps when the user is mid-install.\n// 3. Default to npm. Universally available; users on more exotic PMs\n// have to pass `--package-manager` (or set up their env so the path\n// heuristic fires).\n//\n// Volta is treated as a first-class PM here even though it wraps another\n// (typically npm) — `volta install` is the only correct way to update a\n// Volta-pinned tool. Writing through the inner PM would leave Volta's\n// catalog stale and re-pin the old version on the next shell.\n// ---------------------------------------------------------------------------\n\nfunction normalizePath(path: string): string {\n // Resolve symlinks so a `/usr/local/bin/zidane` → `/opt/homebrew/...`\n // chain still matches the inner location. `realpathSync` throws on\n // broken links; fall back to the literal path then.\n try {\n return realpathSync(path)\n }\n catch {\n return path\n }\n}\n\nexport function detectPackageManager(opts: DetectPackageManagerOptions = {}): PackageManagerCommand {\n const env = opts.env ?? process.env\n const home = opts.home ?? homedir()\n const platform = opts.platform ?? process.platform\n const channel = opts.channel ?? 'latest'\n const packageName = opts.packageName ?? 'zidane-tui'\n const binaryPath = opts.binaryPath ?? process.execPath\n const resolvedBinary = normalizePath(binaryPath)\n const userAgent = opts.userAgent ?? env.npm_config_user_agent\n\n // The argv contract:\n // - `argv[0]` is the binary we exec — needs to be on PATH or absolute.\n // - Remaining tokens are the install command.\n // We rely on PATH resolution so users with shimmed wrappers (volta,\n // mise, asdf) still hit their managed binary.\n const target = `${packageName}@${channel}`\n\n // 1. Path-based heuristics ------------------------------------------------\n //\n // Match path SEGMENTS, not substrings — `/Users/me/.volta-old/bin/x` must\n // NOT match `/Users/me/.volta`. We anchor the comparison to a separator\n // on both sides so the directory name has to be exact.\n\n const containsSegment = (path: string, segment: string) => {\n // `segment` is `<home>/.<name>` (no trailing separator). To match, the\n // path must contain `<segment><sep>` OR end with `<segment>`.\n const tail = `${segment}${sep}`\n return path.includes(tail) || path.endsWith(segment)\n }\n const inPath = (segment: string) =>\n containsSegment(resolvedBinary, segment) || containsSegment(binaryPath, segment)\n const homeSeg = (rest: string) => `${home}${sep}${rest}`\n\n // Binary-path check — strongest signal. A binary living under `~/.volta/`\n // was DEFINITELY installed by Volta; same logic for the others.\n if (inPath(homeSeg('.volta'))) {\n return {\n id: 'volta',\n argv: ['volta', 'install', target],\n note: 'Volta-managed install detected, refreshing via `volta install`.',\n }\n }\n if (inPath(homeSeg('.bun'))) {\n return {\n id: 'bun',\n argv: ['bun', 'add', '--global', target],\n note: 'Bun global install detected.',\n }\n }\n if (\n inPath(homeSeg('.local/share/pnpm'))\n || inPath(homeSeg('Library/pnpm'))\n || resolvedBinary.includes(`${sep}pnpm${sep}`)\n ) {\n return {\n id: 'pnpm',\n argv: ['pnpm', 'add', '--global', target],\n note: 'pnpm global install detected.',\n }\n }\n if (inPath(homeSeg('.yarn'))) {\n return {\n id: 'yarn',\n argv: ['yarn', 'global', 'add', target],\n note: 'Yarn classic global install detected.',\n }\n }\n\n // 2. User-agent heuristic (mid-install or recently-installed shells) ------\n // `npm_config_user_agent` looks like `npm/10.2.3 node/v22.0.0 darwin arm64`\n // or `pnpm/8.15.0 npm/? node/v22.0.0 darwin arm64`. This fires when the\n // binary lives in an unrecognized prefix (corporate npm proxy, custom\n // global root, …) but the calling shell still identifies a PM.\n if (userAgent) {\n const ua = userAgent.toLowerCase()\n if (ua.startsWith('pnpm/'))\n return { id: 'pnpm', argv: ['pnpm', 'add', '--global', target] }\n if (ua.startsWith('yarn/'))\n return { id: 'yarn', argv: ['yarn', 'global', 'add', target] }\n if (ua.startsWith('bun/'))\n return { id: 'bun', argv: ['bun', 'add', '--global', target] }\n if (ua.startsWith('npm/'))\n return { id: 'npm', argv: ['npm', 'install', '--global', target] }\n }\n\n // 3. Env-var fallback — only fires when both the path and the user-agent\n // failed to identify a PM. We trust these LESS than the binary path\n // because a user can install zidane outside their main PM (e.g. via\n // npm) while still having $VOLTA_HOME / $BUN_INSTALL set for unrelated\n // tooling.\n if (env.VOLTA_HOME)\n return { id: 'volta', argv: ['volta', 'install', target], note: '$VOLTA_HOME set, using `volta install`. If the install is not Volta-managed, pass `--package-manager npm`.' }\n if (env.BUN_INSTALL)\n return { id: 'bun', argv: ['bun', 'add', '--global', target] }\n if (env.PNPM_HOME)\n return { id: 'pnpm', argv: ['pnpm', 'add', '--global', target] }\n\n // 4. Default to npm — universally available.\n\n void platform // Reserved for Windows-specific tweaks (none yet).\n return {\n id: 'npm',\n argv: ['npm', 'install', '--global', target],\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API: tarball readiness\n// ---------------------------------------------------------------------------\n\nexport interface TarballAvailabilityOptions {\n packageName: string\n version: string\n registry?: string\n timeoutMs?: number\n /** Inject for tests. */\n fetcher?: typeof fetch\n}\n\n/**\n * Whether `<packageName>@<version>` can actually be DOWNLOADED right now, not\n * merely resolved. Right after a publish the registry serves the new\n * manifest minutes before the tarball reaches the CDN; a package manager\n * that hits that window on an `optionalDependencies` platform binary\n * silently skips it and leaves a bin with nothing behind it. `upgrade` asks\n * this before spawning the PM so it can wait instead of breaking the install.\n *\n * - `'ok'` tarball responds 2xx\n * - `'missing'` manifest or tarball 404 (published, not propagated)\n * - `'unknown'` network / registry error; the caller decides\n */\nexport async function checkTarballAvailable(opts: TarballAvailabilityOptions): Promise<'ok' | 'missing' | 'unknown'> {\n const fetcher = opts.fetcher ?? globalThis.fetch\n const registry = trimSlash(opts.registry ?? 'https://registry.npmjs.org')\n const ac = new AbortController()\n const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? 8000)\n try {\n const manifest = await fetcher(\n `${registry}/${encodeURIComponent(opts.packageName)}/${encodeURIComponent(opts.version)}`,\n { headers: { 'accept': 'application/json', 'user-agent': 'zidane-update-check/1' }, signal: ac.signal, redirect: 'follow' },\n )\n if (manifest.status === 404)\n return 'missing'\n if (!manifest.ok)\n return 'unknown'\n const body = await manifest.json() as { dist?: { tarball?: string } }\n const tarball = body.dist?.tarball\n if (!tarball)\n return 'unknown'\n const head = await fetcher(tarball, { method: 'HEAD', signal: ac.signal, redirect: 'follow' })\n if (head.status === 404)\n return 'missing'\n return head.ok ? 'ok' : 'unknown'\n }\n catch {\n return 'unknown'\n }\n finally {\n clearTimeout(timer)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API: performSelfUpdate (PM-spawned)\n// ---------------------------------------------------------------------------\n\nexport interface PerformSelfUpdateOptions {\n packageName: string\n channel?: string\n /** Force a specific PM instead of auto-detect. */\n packageManager?: PackageManagerId | 'auto'\n /** Don't actually spawn — return the command we WOULD run. */\n dryRun?: boolean\n /** When set, write `pm stdout/stderr` to these streams instead of stdio inherit. */\n stdout?: NodeJS.WritableStream\n stderr?: NodeJS.WritableStream\n /** Abort the install. */\n signal?: AbortSignal\n /** Override env (e.g. set `npm_config_loglevel=error`). */\n env?: Readonly<Record<string, string>>\n /** Inject for tests. */\n spawn?: typeof spawn\n /** Inject for tests. */\n detect?: typeof detectPackageManager\n}\n\nexport interface SelfUpdateResult {\n command: PackageManagerCommand\n /** PM exit code. `null` when `dryRun` or the process was killed. */\n exitCode: number | null\n /** Set when the spawn itself failed (`ENOENT`, EACCES). */\n spawnError?: string\n}\n\n/**\n * Re-install the package globally via the host's package manager. Resolves\n * with the spawned command's exit code, or rejects only when the caller's\n * `signal` aborts. PM failures surface as a non-zero `exitCode` — the\n * caller decides whether to retry or fall back to in-place.\n */\nexport async function performSelfUpdate(options: PerformSelfUpdateOptions): Promise<SelfUpdateResult> {\n const detect = options.detect ?? detectPackageManager\n const command = options.packageManager && options.packageManager !== 'auto'\n ? overrideManager(options.packageManager, options.packageName, options.channel ?? 'latest')\n : detect({ packageName: options.packageName, channel: options.channel ?? 'latest' })\n\n if (options.dryRun)\n return { command, exitCode: null }\n\n const useSpawn = options.spawn ?? spawn\n return await new Promise<SelfUpdateResult>((resolveP) => {\n const [bin, ...args] = command.argv\n const child = useSpawn(bin!, args, {\n stdio: options.stdout || options.stderr\n ? ['ignore', 'pipe', 'pipe']\n : 'inherit',\n env: { ...process.env, ...options.env },\n })\n if (options.stdout && child.stdout)\n child.stdout.pipe(options.stdout)\n if (options.stderr && child.stderr)\n child.stderr.pipe(options.stderr)\n child.on('error', (err) => {\n // ENOENT here means the PM binary isn't on PATH — surface a\n // clear message rather than the bare error.\n resolveP({ command, exitCode: null, spawnError: err.message })\n })\n child.on('exit', (code) => {\n resolveP({ command, exitCode: code })\n })\n if (options.signal) {\n const onAbort = () => child.kill()\n if (options.signal.aborted)\n onAbort()\n else\n options.signal.addEventListener('abort', onAbort, { once: true })\n }\n })\n}\n\nfunction overrideManager(id: PackageManagerId, packageName: string, channel: string): PackageManagerCommand {\n const target = `${packageName}@${channel}`\n switch (id) {\n case 'pnpm': return { id, argv: ['pnpm', 'add', '--global', target] }\n case 'yarn': return { id, argv: ['yarn', 'global', 'add', target] }\n case 'bun': return { id, argv: ['bun', 'add', '--global', target] }\n case 'volta': return { id, argv: ['volta', 'install', target] }\n case 'npm':\n default: return { id: 'npm', argv: ['npm', 'install', '--global', target] }\n }\n}\n\n// ---------------------------------------------------------------------------\n// In-place self-update\n//\n// Downloads `<packageName>@<channel>` from the npm registry, untars the\n// `bin/<filename>` entry, codesigns ad-hoc on darwin if needed, and atomic-\n// renames over the live binary. The host needs to know:\n// - the platform-specific package name to fetch (e.g. `zidane-tui-darwin-arm64`)\n// - the path inside that tarball where the binary lives (defaults to\n// `package/bin/<binaryName>`, which matches the project layout under\n// `tui/npm/<key>/bin/`)\n// - the path of the running binary, so we know where to write\n//\n// Volta-managed installs are explicitly refused — Volta's catalog needs\n// the PM path. The caller should detect via `detectPackageManager` and\n// fall through to `performSelfUpdate` instead.\n// ---------------------------------------------------------------------------\n\nexport interface PerformInPlaceUpdateOptions {\n /** Platform-specific tarball package, e.g. `'zidane-tui-darwin-arm64'`. */\n packageName: string\n channel?: string\n /** Resolved path of the running binary. Defaults to `process.execPath`. */\n binaryPath?: string\n /** Inside the tarball — usually `bin/<name>`. Defaults to `bin/<basename(binaryPath)>`. */\n tarballBinaryPath?: string\n registry?: string\n /** Network timeout (per request). Defaults to 30 000 ms — tarballs are MB-scale. */\n timeoutMs?: number\n signal?: AbortSignal\n /** Don't replace — just download + verify. */\n dryRun?: boolean\n fetcher?: typeof fetch\n /** When set, refuse the swap if the resolved binary lives under one of these prefixes. */\n refuseUnderPrefixes?: readonly string[]\n}\n\nexport interface InPlaceUpdateResult {\n /** `'success' | 'refused' | 'failed'`. */\n status: 'success' | 'refused' | 'failed'\n /** Version that was installed (from the tarball's package.json). */\n installedVersion?: string\n /** Why we bailed. Always present on `refused` / `failed`. */\n reason?: string\n /** Resolved binary path after the swap. */\n binaryPath?: string\n}\n\n/**\n * Replace the running binary with the latest tarball-shipped one. Works on\n * macOS + Linux (POSIX unlink-while-running); refuses on Windows. Returns\n * a result object instead of throwing — callers usually want to print\n * `result.reason` and fall back to a PM-driven update.\n */\nexport async function performInPlaceSelfUpdate(options: PerformInPlaceUpdateOptions): Promise<InPlaceUpdateResult> {\n if (process.platform === 'win32')\n return { status: 'refused', reason: 'in-place update not supported on Windows; use `zidane upgrade` instead.' }\n\n const binaryPath = options.binaryPath ?? process.execPath\n const resolved = normalizePath(binaryPath)\n const refused = options.refuseUnderPrefixes\n if (refused?.some(p => resolved.startsWith(p))) {\n return { status: 'refused', reason: `binary at ${resolved} sits under a managed prefix; use \\`zidane upgrade\\` to update via your package manager.` }\n }\n // Volta is the canonical \"managed prefix\" — refuse by default so users\n // don't end up with a stale catalog after the swap.\n if (resolved.includes(`${homedir()}${sep}.volta${sep}`)) {\n return { status: 'refused', reason: 'Volta-managed install detected. Use `zidane upgrade` (runs `volta install`) instead.' }\n }\n\n const fetcher = options.fetcher ?? globalThis.fetch\n if (typeof fetcher !== 'function')\n return { status: 'failed', reason: 'global fetch unavailable; rebuild with Node ≥ 18 or Bun.' }\n\n const channel = options.channel ?? 'latest'\n const registry = options.registry ?? 'https://registry.npmjs.org'\n const timeoutMs = options.timeoutMs ?? 30_000\n\n // 1. Resolve dist-tag → packument entry for the exact version + tarball URL.\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(new Error('timeout')), timeoutMs)\n if (options.signal) {\n if (options.signal.aborted)\n controller.abort(options.signal.reason as Error)\n else\n options.signal.addEventListener('abort', () => controller.abort(options.signal!.reason as Error), { once: true })\n }\n\n try {\n const metaUrl = `${trimSlash(registry)}/${encodeURIComponent(options.packageName)}/${encodeURIComponent(channel)}`\n const metaRes = await fetcher(metaUrl, {\n headers: { 'accept': 'application/json', 'user-agent': 'zidane-update-check/1' },\n signal: controller.signal,\n })\n if (!metaRes.ok)\n return { status: 'failed', reason: `registry returned ${metaRes.status} for ${options.packageName}@${channel}` }\n const meta = await metaRes.json() as {\n version?: string\n dist?: { tarball?: string, integrity?: string, shasum?: string }\n }\n if (!meta.version || !meta.dist?.tarball)\n return { status: 'failed', reason: `registry payload missing version or tarball for ${options.packageName}@${channel}` }\n // Defense-in-depth: the npm registry serves over HTTPS, but a user\n // running with `--registry <custom>` might point at a less-trusted\n // mirror. Refuse to overwrite the live binary if the registry didn't\n // ship at least one integrity field for us to verify.\n if (!meta.dist.integrity && !meta.dist.shasum)\n return { status: 'failed', reason: `registry payload missing integrity / shasum for ${options.packageName}@${channel}; refusing to overwrite the binary.` }\n\n // 2. Stage adjacent to the target binary's directory so the final\n // `rename` stays on a single filesystem (avoids EXDEV on Linux where\n // /tmp is often tmpfs). Falls back to the OS tmpdir if the target's\n // parent is non-writable.\n const targetDir = dirname(binaryPath)\n let stagingDir: string\n try {\n stagingDir = mkdtempSync(join(targetDir, '.zidane-update-'))\n }\n catch {\n stagingDir = mkdtempSync(join(tmpdir(), 'zidane-update-'))\n }\n const tarballPath = join(stagingDir, 'package.tgz')\n\n try {\n const tgzRes = await fetcher(meta.dist.tarball, {\n headers: { 'user-agent': 'zidane-update-check/1' },\n signal: controller.signal,\n })\n if (!tgzRes.ok || !tgzRes.body)\n return { status: 'failed', reason: `tarball download failed: ${tgzRes.status}` }\n const digests = await streamToFile(tgzRes.body, tarballPath)\n\n // 3. Verify integrity. Prefer SRI (sha512) when present; fall back\n // to the legacy `shasum` (SHA-1) which every package on the registry\n // still carries. A mismatch is a hard refusal — never overwrite the\n // live binary with content we can't authenticate.\n const integrityCheck = verifyIntegrity(meta.dist, digests)\n if (!integrityCheck.ok)\n return { status: 'failed', reason: `integrity check failed: ${integrityCheck.reason}` }\n\n // 4. Extract just the binary entry.\n const tarballBinaryPath = options.tarballBinaryPath\n ?? `bin/${binaryNameFrom(binaryPath)}`\n const extractedPath = await extractEntryFromTarball(tarballPath, tarballBinaryPath, stagingDir)\n if (!extractedPath)\n return { status: 'failed', reason: `tarball did not contain ${tarballBinaryPath}` }\n\n if (options.dryRun) {\n return { status: 'success', installedVersion: meta.version, binaryPath: extractedPath }\n }\n\n // 5. Mark executable + atomic rename. POSIX permits unlink-while-running.\n chmodSync(extractedPath, 0o755)\n const targetForSwap = binaryPath\n try {\n renameSync(extractedPath, targetForSwap)\n }\n catch (err) {\n return { status: 'failed', reason: `failed to write ${targetForSwap}: ${err instanceof Error ? err.message : String(err)}` }\n }\n return { status: 'success', installedVersion: meta.version, binaryPath: targetForSwap }\n }\n finally {\n try { rmSync(stagingDir, { recursive: true, force: true }) }\n catch { /* best-effort */ }\n }\n }\n catch (err) {\n return { status: 'failed', reason: err instanceof Error ? err.message : String(err) }\n }\n finally {\n clearTimeout(timer)\n }\n}\n\nfunction binaryNameFrom(path: string): string {\n const last = path.split(/[\\\\/]/).at(-1) ?? ''\n return last.endsWith('.exe') ? last.slice(0, -4) : last\n}\n\ninterface StreamDigests {\n sha1: string\n sha512Base64: string\n size: number\n}\n\n/**\n * Stream `body` to `path` while computing integrity digests on the fly —\n * we never load the whole tarball into a Buffer just to hash it. Returns\n * SHA-1 (hex, for the legacy `dist.shasum`) and SHA-512 (base64, for SRI's\n * `sha512-<base64>`).\n */\nasync function streamToFile(body: ReadableStream<Uint8Array>, path: string): Promise<StreamDigests> {\n const file = createWriteStream(path)\n const sha1 = createHash('sha1')\n const sha512 = createHash('sha512')\n let size = 0\n const reader = body.getReader()\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done)\n break\n if (value && value.byteLength > 0) {\n sha1.update(value)\n sha512.update(value)\n size += value.byteLength\n await new Promise<void>((res, rej) => file.write(value, err => err ? rej(err) : res()))\n }\n }\n }\n finally {\n await new Promise<void>(res => file.end(() => res()))\n }\n return {\n sha1: sha1.digest('hex'),\n sha512Base64: sha512.digest('base64'),\n size,\n }\n}\n\n/**\n * Validate the downloaded tarball's digest against the registry's\n * `dist.integrity` (SRI: `sha512-<base64>` / `sha384-…` / `sha256-…`) or\n * the legacy `dist.shasum` (hex SHA-1). Either must match — we don't\n * accept \"neither provided\" because that's what `performInPlaceSelfUpdate`\n * already short-circuited above.\n */\nfunction verifyIntegrity(\n dist: { integrity?: string, shasum?: string },\n digests: StreamDigests,\n): { ok: true } | { ok: false, reason: string } {\n if (dist.integrity) {\n // SRI may carry multiple space-separated hashes — any match wins.\n const entries = dist.integrity.split(/\\s+/).filter(Boolean)\n for (const entry of entries) {\n const m = /^(sha(?:256|384|512))-(.+)$/.exec(entry)\n if (!m)\n continue\n const algo = m[1]!\n const expected = m[2]!\n if (algo === 'sha512' && digests.sha512Base64 === expected)\n return { ok: true }\n // Recompute on demand for the rarer algorithms. We only re-hash if\n // the registry shipped a non-512 SRI, which is unusual but legal.\n // For now, fail closed — refuse rather than skip the check.\n if (algo !== 'sha512')\n return { ok: false, reason: `unsupported SRI algorithm \\`${algo}\\`. Re-run via your package manager.` }\n }\n return { ok: false, reason: 'SRI mismatch: tarball does not match the registry\\'s `dist.integrity`.' }\n }\n if (dist.shasum) {\n if (digests.sha1 === dist.shasum.toLowerCase())\n return { ok: true }\n return { ok: false, reason: 'SHA-1 mismatch: tarball does not match the registry\\'s `dist.shasum`.' }\n }\n return { ok: false, reason: 'no integrity field shipped by the registry.' }\n}\n\n// ---------------------------------------------------------------------------\n// Minimal tar extractor — POSIX ustar format. Only handles the entries\n// produced by `npm pack`: filenames < 100 bytes, no PAX extended headers\n// (npm only emits them for paths > 100 chars; we already root the binary\n// at `bin/<name>` which stays well under that), no GNU long-name records.\n// ---------------------------------------------------------------------------\n\nasync function extractEntryFromTarball(\n tarballPath: string,\n entryRelPath: string,\n outDir: string,\n): Promise<string | null> {\n const matchSuffix = `/${entryRelPath}`\n const out = join(outDir, 'binary.bin')\n\n // Read whole tarball into memory — platform packages clock in around\n // 20-30 MB, well within the budget for a temp buffer.\n const chunks: Uint8Array[] = []\n const gunzip = createGunzip()\n const input = createReadStream(tarballPath)\n await new Promise<void>((res, rej) => {\n gunzip.on('data', (c: Buffer) => chunks.push(c))\n gunzip.on('end', () => res())\n gunzip.on('error', rej)\n input.on('error', rej)\n input.pipe(gunzip)\n })\n const tar = concatChunks(chunks)\n let offset = 0\n while (offset + 512 <= tar.length) {\n const header = tar.subarray(offset, offset + 512)\n if (header.every(b => b === 0))\n break\n const name = readNulString(header, 0, 100)\n const sizeOct = readNulString(header, 124, 12).trim()\n const size = sizeOct === '' ? 0 : Number.parseInt(sizeOct, 8)\n const dataStart = offset + 512\n const dataEnd = dataStart + size\n if (name && (name === entryRelPath || name.endsWith(matchSuffix))) {\n writeFileSync(out, tar.subarray(dataStart, dataEnd))\n return out\n }\n // 512-byte aligned padding.\n offset = dataEnd + ((512 - (size % 512)) % 512)\n }\n return null\n}\n\nfunction readNulString(buf: Uint8Array, start: number, len: number): string {\n const end = start + len\n let stop = end\n for (let i = start; i < end; i++) {\n if (buf[i] === 0) { stop = i; break }\n }\n return new TextDecoder('utf8').decode(buf.subarray(start, stop))\n}\n\nfunction concatChunks(chunks: Uint8Array[]): Uint8Array {\n let total = 0\n for (const c of chunks) total += c.byteLength\n const out = new Uint8Array(total)\n let offset = 0\n for (const c of chunks) {\n out.set(c, offset)\n offset += c.byteLength\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Resolve the platform-specific tarball package\n//\n// Mirrors the resolution table in `tui/bin/zidane.mjs` — `darwin/arm64`,\n// `darwin/x64`, `linux/x64` (glibc), `linux/arm64` (glibc). Returns\n// `null` for unsupported targets so the caller can print a clear message.\n// ---------------------------------------------------------------------------\n\nexport interface PlatformTarballOptions {\n /** Override `process.platform`. */\n platform?: NodeJS.Platform\n /** Override `process.arch`. */\n arch?: NodeJS.Architecture | string\n /** Override the libc check (returned from `detectLibc`). */\n libc?: 'glibc' | 'musl'\n /** Prefix joined to the platform key. Defaults to `'zidane-tui-'`. */\n prefix?: string\n}\n\nexport function resolvePlatformPackage(opts: PlatformTarballOptions = {}): string | null {\n const platform = opts.platform ?? process.platform\n const arch = opts.arch ?? process.arch\n const libc = opts.libc ?? detectLibc()\n const prefix = opts.prefix ?? 'zidane-tui-'\n if (platform === 'darwin') {\n if (arch === 'arm64')\n return `${prefix}darwin-arm64`\n if (arch === 'x64')\n return `${prefix}darwin-x64`\n return null\n }\n if (platform === 'linux') {\n if (libc !== 'glibc')\n return null\n if (arch === 'arm64')\n return `${prefix}linux-arm64`\n if (arch === 'x64')\n return `${prefix}linux-x64`\n return null\n }\n return null\n}\n\n/** Mirrors `tui/bin/zidane.mjs#detectLibc`. */\nexport function detectLibc(): 'glibc' | 'musl' {\n if (process.platform !== 'linux')\n return 'glibc'\n try {\n // eslint-disable-next-line ts/no-require-imports\n const { execSync } = require('node:child_process') as typeof import('node:child_process')\n const out = execSync('ldd --version 2>&1', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }) as string\n return out.toLowerCase().includes('musl') ? 'musl' : 'glibc'\n }\n catch {\n return 'musl'\n }\n}\n\n// ---------------------------------------------------------------------------\n// `_` re-exports for tests that don't want to mock through the module\n// boundary. Not part of the public surface; keep names underscored.\n// ---------------------------------------------------------------------------\n\nexport const _internal = {\n cacheFilePath,\n readCache,\n writeCache,\n binaryNameFrom,\n extractEntryFromTarball,\n}\n","// ---------------------------------------------------------------------------\n// Companion packages — sibling globals that `zidane upgrade` keeps in step\n// with the TUI. Today that is `zidane-gui`: same version line, same npm\n// release, but installed separately (and possibly with a different package\n// manager). This module answers one question — \"is <package> installed on\n// this machine, at what version, and where?\" — so the CLI can check the\n// registry for it and re-run the install through the PM that owns it.\n//\n// Discovery is by the package's bin on PATH, not by asking a PM: `npm ls -g`\n// is slow, differs per manager, and lies under Volta. The bin is what the\n// user actually invokes, so it is also the most honest signal of \"installed\".\n// ---------------------------------------------------------------------------\n\nimport { existsSync, readFileSync, realpathSync, statSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { delimiter, dirname, join, sep } from 'node:path'\n\nexport interface CompanionSpec {\n /** npm package name, e.g. `'zidane-gui'`. */\n packageName: string\n /** Executable name on PATH. Defaults to `packageName`. */\n binName?: string\n}\n\nexport interface InstalledCompanion {\n packageName: string\n /** Version from the installed `package.json`. */\n version: string\n /** Root of the installed package. */\n packageDir: string\n /**\n * The bin as found on PATH (NOT resolved). `detectPackageManager` wants\n * this: `~/.volta/bin/<name>` identifies Volta even though its realpath is\n * the generic `volta-shim`.\n */\n binaryPath: string\n}\n\nexport interface FindCompanionOptions {\n env?: Readonly<Record<string, string | undefined>>\n home?: string\n platform?: NodeJS.Platform\n}\n\n/**\n * Locate a globally installed companion. `null` when its bin is not on PATH\n * or no `package.json` with that name can be found behind it.\n */\nexport function findInstalledCompanion(spec: CompanionSpec, opts: FindCompanionOptions = {}): InstalledCompanion | null {\n const env = opts.env ?? process.env\n const home = opts.home ?? homedir()\n const platform = opts.platform ?? process.platform\n const binName = spec.binName ?? spec.packageName\n\n const binaryPath = findOnPath(binName, env.PATH ?? '', platform)\n if (!binaryPath)\n return null\n\n const packageDir = resolvePackageDir(spec.packageName, binaryPath, home)\n if (!packageDir)\n return null\n\n const version = readPackageVersion(packageDir, spec.packageName)\n if (!version)\n return null\n\n return { packageName: spec.packageName, version, packageDir, binaryPath }\n}\n\nfunction findOnPath(binName: string, pathVar: string, platform: NodeJS.Platform): string | null {\n const candidates = platform === 'win32' ? [binName, `${binName}.cmd`, `${binName}.exe`] : [binName]\n for (const dir of pathVar.split(delimiter)) {\n if (!dir)\n continue\n for (const name of candidates) {\n const full = join(dir, name)\n try {\n if (statSync(full).isFile())\n return full\n }\n catch {\n // not here\n }\n }\n }\n return null\n}\n\n/**\n * From the bin to the package root. Two strategies:\n * 1. Follow the symlink (`<prefix>/bin/zidane-gui` → `…/node_modules/\n * zidane-gui/bin/zidane-gui.mjs`) and climb until a `package.json`\n * carries the expected name. Covers npm, pnpm, yarn and bun.\n * 2. Volta: the bin is a shim whose realpath is `volta-shim`, so nothing to\n * climb. Its global packages live at a fixed layout under `$VOLTA_HOME`\n * (the same one the GUI installer maps staging paths to).\n */\nfunction resolvePackageDir(packageName: string, binaryPath: string, home: string): string | null {\n let resolved = binaryPath\n try {\n resolved = realpathSync(binaryPath)\n }\n catch {\n // dangling link — fall through to the Volta layout check\n }\n\n const climbed = climbToPackage(dirname(resolved), packageName)\n if (climbed)\n return climbed\n\n const voltaHome = voltaHomeFor(binaryPath, resolved, home)\n if (voltaHome) {\n const dir = join(voltaHome, 'tools', 'image', 'packages', packageName, 'lib', 'node_modules', packageName)\n if (existsSync(join(dir, 'package.json')))\n return dir\n }\n return null\n}\n\nfunction climbToPackage(start: string, packageName: string): string | null {\n let dir = start\n for (let depth = 0; depth < 6; depth++) {\n if (readPackageVersion(dir, packageName))\n return dir\n const parent = dirname(dir)\n if (parent === dir)\n break\n dir = parent\n }\n return null\n}\n\nfunction voltaHomeFor(binaryPath: string, resolved: string, home: string): string | null {\n const marker = `${sep}.volta${sep}`\n for (const p of [binaryPath, resolved]) {\n const idx = p.indexOf(marker)\n if (idx !== -1)\n return p.slice(0, idx + marker.length - 1)\n }\n const fallback = join(home, '.volta')\n return existsSync(fallback) ? fallback : null\n}\n\nfunction readPackageVersion(dir: string, packageName: string): string | null {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as { name?: unknown, version?: unknown }\n if (pkg.name !== packageName || typeof pkg.version !== 'string')\n return null\n return pkg.version\n }\n catch {\n return null\n }\n}\n","// ---------------------------------------------------------------------------\n// \"A newer version is on disk — restart to use it.\"\n//\n// Two things live here:\n//\n// 1. The APPLIED MARKER — `<cacheDir>/update-applied.json`, written by every\n// path that installs a release (`zidane upgrade`, the GUI's in-app\n// update). It is a wake-up call, not the truth: running surfaces watch\n// the file and re-probe the disk when it changes.\n//\n// 2. The INSTALLED-VERSION WATCHER — compares the version the running\n// process was built with against the version its package has on disk\n// (resolved from the bin on PATH, the same way `zidane upgrade` finds\n// companions). `restartRequired` when disk > running. Re-probes on the\n// marker, on a slow poll (an upgrade run from another machine's shell\n// over the same home dir writes no local marker), and on demand\n// (`check()` — the GUI calls it on window focus).\n//\n// Renderer-agnostic: Node `fs` only, no React except the hook at the bottom.\n// ---------------------------------------------------------------------------\n\nimport type { FSWatcher } from 'node:fs'\nimport type { CompanionSpec, FindCompanionOptions, InstalledCompanion } from './auto-update-companion'\nimport { existsSync, mkdirSync, readFileSync, watch, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { useEffect, useState } from 'react'\nimport { compareSemver } from './auto-update'\nimport { findInstalledCompanion } from './auto-update-companion'\n\nexport const UPDATE_APPLIED_MARKER = 'update-applied.json'\n\nexport interface UpdateAppliedMarker {\n /** ms since epoch. */\n at: number\n /** Every package the run installed, with the version now on disk. */\n packages: Array<{ packageName: string, version: string }>\n}\n\nexport function updateAppliedMarkerPath(cacheDir: string): string {\n return join(cacheDir, UPDATE_APPLIED_MARKER)\n}\n\n/**\n * Record that an install just landed. Best-effort: a failure to write the\n * marker must never fail the upgrade that succeeded — the poll catches up.\n */\nexport function writeUpdateAppliedMarker(cacheDir: string, marker: UpdateAppliedMarker): boolean {\n try {\n mkdirSync(cacheDir, { recursive: true })\n writeFileSync(updateAppliedMarkerPath(cacheDir), `${JSON.stringify(marker, null, 2)}\\n`)\n return true\n }\n catch {\n return false\n }\n}\n\nexport function readUpdateAppliedMarker(cacheDir: string): UpdateAppliedMarker | null {\n try {\n const raw = JSON.parse(readFileSync(updateAppliedMarkerPath(cacheDir), 'utf8')) as unknown\n if (!raw || typeof raw !== 'object')\n return null\n const { at, packages } = raw as Partial<UpdateAppliedMarker>\n if (typeof at !== 'number' || !Array.isArray(packages))\n return null\n return {\n at,\n packages: packages.filter(p => p && typeof p.packageName === 'string' && typeof p.version === 'string'),\n }\n }\n catch {\n return null\n }\n}\n\n// ---------------------------------------------------------------------------\n// Installed-version watcher\n// ---------------------------------------------------------------------------\n\nexport interface InstalledVersionState {\n /** Version the running process was built with. */\n running: string\n /** Version found on disk for the package; `null` when the probe found nothing (dev checkout, PATH without the bin). */\n installed: string | null\n /** `installed` is strictly newer than `running` — the process is stale. */\n restartRequired: boolean\n /** When the disk was last probed (ms since epoch). */\n checkedAt: number\n}\n\nexport interface WatchInstalledVersionOptions {\n /** The running package, e.g. `{ packageName: 'zidane-tui', binName: 'zidane' }`. */\n spec: CompanionSpec\n /** Inlined at build time. An empty string disables the watcher (dev runs). */\n currentVersion: string\n /** Where the applied marker lives. Omit to rely on the poll alone. */\n cacheDir?: string\n /** Poll interval. Defaults to 5 min; `0` disables the poll. */\n pollMs?: number\n /** Debounce for marker events (installs touch the file more than once). Defaults to 500 ms. */\n debounceMs?: number\n onChange: (state: InstalledVersionState) => void\n /** Injected for tests. */\n probe?: (spec: CompanionSpec, opts?: FindCompanionOptions) => InstalledCompanion | null\n /** Injected for tests. */\n now?: () => number\n}\n\nexport interface InstalledVersionWatcher {\n /** Re-probe now. Returns the fresh state (also delivered to `onChange` when it differs). */\n check: () => InstalledVersionState\n /** Last delivered state. */\n current: () => InstalledVersionState\n close: () => void\n}\n\n/**\n * Compute the state once — the pure heart of the watcher, exported so a host\n * without a lifecycle (a one-shot IPC handler) can answer \"is a restart\n * pending?\" without holding a watcher.\n */\nexport function probeInstalledVersion(\n spec: CompanionSpec,\n currentVersion: string,\n probe: WatchInstalledVersionOptions['probe'] = findInstalledCompanion,\n now: () => number = Date.now,\n): InstalledVersionState {\n const installed = currentVersion ? probe(spec)?.version ?? null : null\n return {\n running: currentVersion,\n installed,\n restartRequired: installed !== null && compareSemver(installed, currentVersion) > 0,\n checkedAt: now(),\n }\n}\n\nexport function watchInstalledVersion(options: WatchInstalledVersionOptions): InstalledVersionWatcher {\n const probe = options.probe ?? findInstalledCompanion\n const now = options.now ?? Date.now\n const enabled = options.currentVersion.length > 0\n let state = probeInstalledVersion(options.spec, options.currentVersion, probe, now)\n let closed = false\n\n const deliver = (next: InstalledVersionState) => {\n const changed = next.installed !== state.installed || next.restartRequired !== state.restartRequired\n state = next\n if (changed && !closed)\n options.onChange(next)\n }\n const check = (): InstalledVersionState => {\n if (!enabled || closed)\n return state\n deliver(probeInstalledVersion(options.spec, options.currentVersion, probe, now))\n return state\n }\n\n let watcher: FSWatcher | null = null\n let debounce: ReturnType<typeof setTimeout> | null = null\n let poll: ReturnType<typeof setInterval> | null = null\n\n if (enabled) {\n if (options.cacheDir) {\n // Watch the DIRECTORY: the marker may not exist yet, and installers\n // replace files (rename) rather than rewrite them in place.\n try {\n mkdirSync(options.cacheDir, { recursive: true })\n watcher = watch(options.cacheDir, (_event, filename) => {\n if (filename && filename.toString() !== UPDATE_APPLIED_MARKER)\n return\n if (debounce)\n clearTimeout(debounce)\n debounce = setTimeout(() => {\n debounce = null\n check()\n }, options.debounceMs ?? 500)\n })\n watcher.on('error', () => {\n // A vanished cache dir or an exhausted inotify budget must not\n // take the host down; the poll still covers us.\n watcher?.close()\n watcher = null\n })\n }\n catch {\n watcher = null\n }\n }\n const pollMs = options.pollMs ?? 5 * 60_000\n if (pollMs > 0) {\n poll = setInterval(check, pollMs)\n // Never keep a process alive for this.\n if (typeof poll === 'object' && 'unref' in poll)\n poll.unref()\n }\n }\n\n return {\n check,\n current: () => state,\n close: () => {\n closed = true\n watcher?.close()\n if (debounce)\n clearTimeout(debounce)\n if (poll)\n clearInterval(poll)\n },\n }\n}\n\n/** Quick existence check so hosts can decide whether to read the marker at all. */\nexport function hasUpdateAppliedMarker(cacheDir: string): boolean {\n return existsSync(updateAppliedMarkerPath(cacheDir))\n}\n\n// ---------------------------------------------------------------------------\n// React hook — the TUI's consumer. The GUI runs the watcher in its main\n// process and forwards the state over IPC instead.\n// ---------------------------------------------------------------------------\n\nexport interface UseRestartRequiredOptions {\n spec: CompanionSpec | null\n currentVersion: string\n cacheDir?: string\n /** Master switch — mirrors `Settings.checkForUpdates`. */\n enabled: boolean\n pollMs?: number\n}\n\n/**\n * `null` until the first probe (or when disabled / no spec), then the live\n * state. Flipping `enabled` off tears the watcher down and clears the state\n * so no stale \"restart\" chip lingers.\n */\nexport function useRestartRequired(options: UseRestartRequiredOptions): InstalledVersionState | null {\n const [state, setState] = useState<InstalledVersionState | null>(null)\n const { spec, currentVersion, cacheDir, enabled, pollMs } = options\n const packageName = spec?.packageName\n const binName = spec?.binName\n\n useEffect(() => {\n if (!enabled || !packageName || !currentVersion) {\n setState(null)\n return\n }\n const watcher = watchInstalledVersion({\n spec: binName ? { packageName, binName } : { packageName },\n currentVersion,\n cacheDir,\n pollMs,\n onChange: setState,\n })\n setState(watcher.current())\n return () => watcher.close()\n }, [enabled, packageName, binName, currentVersion, cacheDir, pollMs])\n\n return state\n}\n","// ---------------------------------------------------------------------------\n// `zidane upgrade` subcommand — drop-in handler for consumer CLIs.\n//\n// Wraps `checkForUpdate` + `performSelfUpdate` + `performInPlaceSelfUpdate`\n// into a single function the host's `cli.ts` can call. The behavior layers:\n//\n// - no flags → check + run the package manager\n// - `--check` → check only, print the latest version + bail\n// - `--in-place` → skip the PM, download + atomic-rename the binary\n// - `--dry-run` → resolve everything but don't actually swap\n// - `--channel <tag>` → override dist-tag (defaults to `'latest'`)\n// - `--registry <url>`→ override the npm registry URL\n// - `--tui-only` → skip companion packages (see `companions`)\n//\n// Companions: sibling globals released on the same version line (today\n// `zidane-gui`). When one is installed, `upgrade` checks and re-installs it\n// too — through the package manager that owns THAT install, which may differ\n// from the TUI's. Companion failures are reported and fail the exit code but\n// never block the TUI's own upgrade.\n//\n// Pure I/O — writes to stdout/stderr, returns the exit code the caller\n// should pass to `process.exit`. No React, no OpenTUI; safe to call\n// before / instead of `runTui`.\n// ---------------------------------------------------------------------------\n\nimport type { PackageManagerCommand, PackageManagerId, UpdateStatus } from './auto-update'\nimport type { CompanionSpec, InstalledCompanion } from './auto-update-companion'\nimport type { UpdateAppliedMarker } from './auto-update-restart'\nimport {\n checkForUpdate,\n checkTarballAvailable,\n compareSemver,\n detectPackageManager,\n performInPlaceSelfUpdate,\n performSelfUpdate,\n resolvePlatformPackage,\n} from './auto-update'\nimport { findInstalledCompanion } from './auto-update-companion'\nimport { writeUpdateAppliedMarker } from './auto-update-restart'\n\nexport interface RunUpdateCommandOptions {\n /** Top-level package the user installs (e.g. `'zidane-tui'`). */\n packageName: string\n /** Inlined at build time. */\n currentVersion: string\n /** Args passed after `upgrade` (typically `process.argv.slice(3)`). */\n argv: readonly string[]\n /** Where to persist the TTL cache. Usually `paths.userDir`. */\n cacheDir?: string\n /** Default channel when `--channel` isn't passed. Defaults to `'latest'`. */\n defaultChannel?: string\n /**\n * Override the platform-package-name prefix used by `--in-place`.\n * Defaults to `'<packageName>-'`, matching the `zidane-tui-<key>` layout.\n */\n platformPackagePrefix?: string\n /** Override registry. Defaults to npmjs. */\n registry?: string\n /** stdout. Defaults to `process.stdout`. */\n stdout?: NodeJS.WritableStream\n /** stderr. Defaults to `process.stderr`. */\n stderr?: NodeJS.WritableStream\n /**\n * Sibling packages to keep in step when installed (e.g. `zidane-gui`).\n * Absent companions are silently skipped; `--tui-only` skips them all.\n */\n companions?: readonly CompanionSpec[]\n /**\n * Reports the version now installed for `packageName` (e.g. by running the\n * bin with `--version`), `null` when nothing usable is there, `undefined`\n * to skip verification. The host wires this; the library cannot know how\n * its own bin is invoked.\n */\n verifyInstalled?: (packageName: string) => string | null | undefined\n /** Inject for tests. */\n findCompanion?: typeof findInstalledCompanion\n /** Inject for tests. */\n performSelfUpdate?: typeof performSelfUpdate\n /** Inject for tests. */\n checkTarballAvailable?: typeof checkTarballAvailable\n /** Inject for tests. */\n writeMarker?: typeof writeUpdateAppliedMarker\n}\n\nexport interface RunUpdateCommandResult {\n exitCode: number\n}\n\nconst UPGRADE_HELP = `Usage: zidane upgrade [options]\n\nUpdate the zidane CLI to the latest release. A globally installed\nzidane-gui is upgraded alongside it, through its own package manager.\n\nOptions:\n --check Print the latest version and exit (no install).\n --tui-only Leave companion packages (zidane-gui) alone.\n --in-place Download the platform binary directly from npm\n and atomic-rename over the running binary.\n Skips the package manager. Refused on Volta.\n --dry-run Resolve everything but don't actually install.\n --channel <tag> dist-tag to query (default: latest).\n --registry <url> Override the npm registry.\n --package-manager <pm> Force npm | pnpm | yarn | bun | volta.\n --force Run the install even if we're already on latest,\n or the registry check failed.\n -h, --help Print this help.\n\nEnvironment:\n NO_UPDATE_NOTIFIER Silence the background check (set to \"1\").\n ZIDANE_NO_UPDATE Same, project-specific.\n`\n\n/**\n * Run the `upgrade` subcommand. Exit code contract:\n * - `0` — update succeeded, `--check` reported no update, or `--check`\n * reported an update (informational only).\n * - `1` — update failed (PM exit non-zero, in-place download failed,\n * registry returned non-2xx, release not downloadable yet).\n * - `2` — argv parse error (unknown flag).\n *\n * Output is a short ledger, one line per package, ASCII-only apart from the\n * arrow:\n * zidane-tui 7.0.7 → 7.0.8\n * $ volta install zidane-tui@latest\n * …\n * zidane-tui 7.0.8 ok\n * Diagnostics go to stderr and say what to do next.\n */\nexport async function runUpdateCommand(options: RunUpdateCommandOptions): Promise<RunUpdateCommandResult> {\n const out = options.stdout ?? process.stdout\n const err = options.stderr ?? process.stderr\n const parsed = parseArgs(options.argv)\n if (parsed.kind === 'error') {\n err.write(`zidane upgrade: ${parsed.message}\\n`)\n err.write(UPGRADE_HELP)\n return { exitCode: 2 }\n }\n if (parsed.help) {\n out.write(UPGRADE_HELP)\n return { exitCode: 0 }\n }\n const flags: ParsedFlags = parsed\n\n const channel = flags.channel ?? options.defaultChannel ?? 'latest'\n const registry = flags.registry ?? options.registry\n const tarballAvailable = options.checkTarballAvailable ?? checkTarballAvailable\n const install = options.performSelfUpdate ?? performSelfUpdate\n\n // Always force the check — user typed `upgrade`, env opt-outs shouldn't\n // get in the way.\n const status = await checkForUpdate({\n packageName: options.packageName,\n currentVersion: options.currentVersion,\n channel,\n registry,\n cacheDir: options.cacheDir,\n force: true,\n timeoutMs: 8000,\n })\n const self: Entry = {\n name: options.packageName,\n current: options.currentVersion,\n status,\n outdated: status.latest !== null && compareSemver(status.latest, options.currentVersion) > 0,\n }\n\n // Companions: discovered locally, then checked on the registry with the\n // same channel. Only installed ones take part; nothing is printed for the\n // rest, so users without the GUI see the exact output they always did.\n const companions = flags.tuiOnly ? [] : await resolveCompanions(options, channel, registry)\n const entries: Entry[] = [self, ...companions]\n const label = (name: string) => name.padEnd(Math.max(...entries.map(e => e.name.length)))\n\n // `--check` is informational — degrade gracefully on registry errors\n // (offline laptop, restricted proxy) and exit 0 so the user can script\n // around it without quoting tricks.\n if (flags.check) {\n for (const e of entries)\n out.write(`${label(e.name)} ${statusText(e)}\\n`)\n return { exitCode: 0 }\n }\n\n // Install path — a registry error means we don't KNOW if we need an\n // update. Refuse to spawn the PM blindly unless the user explicitly\n // asks via `--force`.\n if (status.source === 'error') {\n err.write(`${self.name}: registry check failed: ${status.error ?? 'unknown error'}\\n`)\n if (!flags.force) {\n err.write('Use --force to install anyway.\\n')\n return { exitCode: 1 }\n }\n }\n for (const c of companions.filter(c => c.status.source === 'error' && !flags.force))\n err.write(`${c.name}: registry check failed: ${c.status.error ?? 'unknown error'}\\n`)\n\n const selfPending = flags.force || self.outdated\n const pendingCompanions = companions.filter(c => flags.force || c.outdated)\n for (const e of entries.filter(e => !e.outdated && e.status.source !== 'error'))\n out.write(`${label(e.name)} ${statusText(e)}\\n`)\n if (!selfPending && pendingCompanions.length === 0)\n return { exitCode: 0 }\n\n // The TUI's own install runs first (in-place or PM); companions follow.\n // A companion failure sets the exit code but never undoes or blocks the\n // TUI upgrade that already happened.\n let exitCode = 0\n const applied: UpdateAppliedMarker['packages'] = []\n if (selfPending) {\n exitCode = await upgradeSelf()\n if (exitCode === 0 && !flags.dryRun && status.latest)\n applied.push({ packageName: self.name, version: status.latest })\n }\n for (const c of pendingCompanions) {\n const code = await upgradeCompanion(c)\n if (code === 0 && !flags.dryRun && c.status.latest)\n applied.push({ packageName: c.name, version: c.status.latest })\n if (exitCode === 0)\n exitCode = code\n }\n // Wake running TUIs / GUIs: they watch this marker and offer a restart.\n if (applied.length > 0 && options.cacheDir)\n (options.writeMarker ?? writeUpdateAppliedMarker)(options.cacheDir, { at: Date.now(), packages: applied })\n return { exitCode }\n\n /**\n * Refuse to spawn the PM while the release is not fetchable yet: npm treats\n * a 404 on an `optionalDependencies` platform binary as \"skip\" and leaves\n * an empty install behind the bin. `--force` overrides (mirrors, offline\n * registries).\n */\n async function ready(packageName: string, version: string): Promise<boolean> {\n if (flags.force || flags.dryRun)\n return true\n const availability = await tarballAvailable({ packageName, version, registry })\n if (availability !== 'missing')\n return true\n err.write(`${packageName} ${version} is published but not downloadable yet. Try again in a few minutes.\\n`)\n return false\n }\n\n async function upgradeSelf(): Promise<number> {\n const target = status.latest ?? channel\n out.write(`${label(self.name)} ${self.current} → ${target}\\n`)\n\n if (flags.inPlace) {\n const platformPkg = resolvePlatformPackage({\n prefix: options.platformPackagePrefix ?? `${options.packageName}-`,\n })\n if (!platformPkg) {\n err.write(`${self.name}: no prebuilt binary for ${process.platform}/${process.arch}. Use your package manager instead.\\n`)\n return 1\n }\n if (status.latest && !await ready(platformPkg, status.latest))\n return 1\n const result = await performInPlaceSelfUpdate({ packageName: platformPkg, channel, registry, dryRun: flags.dryRun })\n if (result.status === 'success') {\n out.write(`${label(self.name)} ${result.installedVersion} ${flags.dryRun ? 'would install' : 'ok'}\\n`)\n return 0\n }\n err.write(`${self.name}: in-place update failed: ${result.reason ?? 'unknown error'}\\n`)\n if (result.status === 'refused')\n err.write('Drop --in-place to go through the package manager.\\n')\n return 1\n }\n\n // The platform binary is the part that can be missing from the CDN; the\n // meta package is tiny and published right after it.\n const platformPkg = resolvePlatformPackage({ prefix: options.platformPackagePrefix ?? `${options.packageName}-` })\n if (status.latest && !await ready(platformPkg ?? options.packageName, status.latest))\n return 1\n\n const command = flags.packageManager\n ? overrideManager(flags.packageManager, options.packageName, channel)\n : detectPackageManager({ packageName: options.packageName, channel })\n out.write(`$ ${command.argv.join(' ')}\\n`)\n if (flags.dryRun)\n return 0\n const result = await install({ packageName: options.packageName, channel, packageManager: flags.packageManager ?? 'auto' })\n if (result.spawnError) {\n err.write(`${self.name}: could not run ${result.command.argv[0]}: ${result.spawnError}\\n`)\n return 1\n }\n if (result.exitCode !== 0) {\n err.write(`${self.name}: ${result.command.argv[0]} exited with ${result.exitCode ?? 'unknown'}\\n`)\n return result.exitCode ?? 1\n }\n return verify(self.name, status.latest, options.verifyInstalled?.(options.packageName), result.command)\n }\n\n async function upgradeCompanion(c: CompanionEntry): Promise<number> {\n const target = c.status.latest ?? channel\n out.write(`${label(c.name)} ${c.current} → ${target}\\n`)\n if (c.status.latest && !await ready(c.name, c.status.latest))\n return 1\n // `--package-manager` is an override for the TUI's install; the\n // companion follows the PM that owns its own bin unless forced too.\n const command = flags.packageManager ? overrideManager(flags.packageManager, c.name, channel) : c.command\n out.write(`$ ${command.argv.join(' ')}\\n`)\n if (flags.dryRun)\n return 0\n const result = await install({ packageName: c.name, channel, packageManager: flags.packageManager ?? c.command.id })\n if (result.spawnError) {\n err.write(`${c.name}: could not run ${result.command.argv[0]}: ${result.spawnError}\\n`)\n return 1\n }\n if (result.exitCode !== 0) {\n err.write(`${c.name}: ${result.command.argv[0]} exited with ${result.exitCode ?? 'unknown'}\\n`)\n return 1\n }\n const find = options.findCompanion ?? findInstalledCompanion\n return verify(c.name, c.status.latest, find(c.spec)?.version, result.command)\n }\n\n /**\n * The PM exiting 0 is not proof: a skipped optional platform binary or a\n * Volta no-op (\"already installed\") both exit 0 and leave the old version\n * behind. Compare what is actually on disk with what we asked for.\n */\n function verify(name: string, expected: string | null, actual: string | undefined | null, command: PackageManagerCommand): number {\n if (!expected || actual === undefined) {\n out.write(`${label(name)} ${expected ?? channel} ok\\n`)\n return 0\n }\n if (actual !== null && compareSemver(actual, expected) >= 0) {\n out.write(`${label(name)} ${actual} ok\\n`)\n return 0\n }\n err.write(`${name}: install finished but ${actual ?? 'nothing'} is on disk, expected ${expected}.\\n`)\n err.write(`Reinstall: ${reinstallHint(command, name, channel)}\\n`)\n return 1\n }\n}\n\nfunction statusText(e: Entry): string {\n if (e.status.source === 'error')\n return `${e.current} (check failed: ${e.status.error ?? 'unknown error'})`\n if (!e.status.latest)\n return `${e.current} (latest unknown)`\n return e.outdated ? `${e.current} → ${e.status.latest} available` : `${e.current} up to date`\n}\n\n/**\n * Volta treats `install` of an already-recorded version as a no-op, so the\n * only way to replace a broken install is to remove it first.\n */\nfunction reinstallHint(command: PackageManagerCommand, packageName: string, channel: string): string {\n const cmd = command.argv.join(' ')\n return command.id === 'volta' ? `volta uninstall ${packageName} && volta install ${packageName}@${channel}` : cmd\n}\n\nfunction overrideManager(id: PackageManagerId, packageName: string, channel: string): PackageManagerCommand {\n const target = `${packageName}@${channel}`\n switch (id) {\n case 'pnpm': return { id, argv: ['pnpm', 'add', '--global', target] }\n case 'yarn': return { id, argv: ['yarn', 'global', 'add', target] }\n case 'bun': return { id, argv: ['bun', 'add', '--global', target] }\n case 'volta': return { id, argv: ['volta', 'install', target] }\n case 'npm': return { id, argv: ['npm', 'install', '--global', target] }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Ledger entries\n// ---------------------------------------------------------------------------\n\ninterface Entry {\n name: string\n /** Installed version. */\n current: string\n status: UpdateStatus\n /** Registry knows a newer version than the installed one. */\n outdated: boolean\n}\n\ninterface CompanionEntry extends Entry {\n spec: CompanionSpec\n installed: InstalledCompanion\n /** The PM that owns this install — from ITS bin path, not the TUI's. */\n command: PackageManagerCommand\n}\n\nasync function resolveCompanions(\n options: RunUpdateCommandOptions,\n channel: string,\n registry: string | undefined,\n): Promise<CompanionEntry[]> {\n const find = options.findCompanion ?? findInstalledCompanion\n const found = (options.companions ?? [])\n .map(spec => ({ spec, installed: find(spec) }))\n .filter((c): c is { spec: CompanionSpec, installed: InstalledCompanion } => c.installed !== null)\n return await Promise.all(found.map(async ({ spec, installed }) => {\n const status = await checkForUpdate({\n packageName: installed.packageName,\n currentVersion: installed.version,\n channel,\n registry,\n cacheDir: options.cacheDir,\n force: true,\n timeoutMs: 8000,\n })\n return {\n spec,\n installed,\n name: installed.packageName,\n current: installed.version,\n status,\n outdated: status.latest !== null && compareSemver(status.latest, installed.version) > 0,\n command: detectPackageManager({ packageName: installed.packageName, channel, binaryPath: installed.binaryPath }),\n }\n }))\n}\n\n// ---------------------------------------------------------------------------\n// Argv parser — minimal, just enough to drop into the consumer's `cli.ts`.\n// We deliberately don't pull in commander/yargs for a 5-flag surface.\n// ---------------------------------------------------------------------------\n\ntype ParsedFlags = Extract<ParseResult, { kind: 'ok' }>\n\ntype ParseResult\n = | {\n kind: 'ok'\n check: boolean\n inPlace: boolean\n dryRun: boolean\n force: boolean\n help: boolean\n tuiOnly: boolean\n channel?: string\n registry?: string\n packageManager?: 'npm' | 'pnpm' | 'yarn' | 'bun' | 'volta'\n }\n | { kind: 'error', message: string }\n\nfunction parseArgs(argv: readonly string[]): ParseResult {\n let check = false\n let inPlace = false\n let dryRun = false\n let force = false\n let help = false\n let tuiOnly = false\n let channel: string | undefined\n let registry: string | undefined\n let packageManager: 'npm' | 'pnpm' | 'yarn' | 'bun' | 'volta' | undefined\n\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!\n switch (a) {\n case '--check': check = true; break\n case '--in-place':\n case '--inplace': inPlace = true; break\n case '--dry-run':\n case '--dryrun': dryRun = true; break\n case '--force': force = true; break\n case '--tui-only': tuiOnly = true; break\n case '-h':\n case '--help': help = true; break\n case '--channel':\n channel = argv[++i]\n if (!channel)\n return { kind: 'error', message: '--channel requires a value' }\n break\n case '--registry':\n registry = argv[++i]\n if (!registry)\n return { kind: 'error', message: '--registry requires a value' }\n break\n case '--package-manager':\n case '--pm': {\n const v = argv[++i]\n if (!v)\n return { kind: 'error', message: '--package-manager requires a value' }\n if (v !== 'npm' && v !== 'pnpm' && v !== 'yarn' && v !== 'bun' && v !== 'volta')\n return { kind: 'error', message: `unknown package manager: ${v}` }\n packageManager = v\n break\n }\n default:\n return { kind: 'error', message: `unknown argument: ${a}` }\n }\n }\n\n return { kind: 'ok', check, inPlace, dryRun, force, help, tuiOnly, channel, registry, packageManager }\n}\n","// ---------------------------------------------------------------------------\n// `useUpdateCheck` — renderer-agnostic React hook driving the auto-update\n// footer chip + any banner a host wants to wire.\n//\n// Lives in `zidane/chat` (not `zidane/tui`) so a future GUI shell can\n// consume the same status. Pure React + the renderer-agnostic core in\n// `./auto-update`; no OpenTUI imports.\n// ---------------------------------------------------------------------------\n\nimport type { UpdateStatus } from './auto-update'\nimport type { Hint } from './hints'\nimport { useEffect, useRef, useState } from 'react'\nimport { checkForUpdate } from './auto-update'\n\nexport interface UseUpdateCheckOptions {\n /** npm package name. e.g. `'zidane-tui'`. */\n packageName: string\n /** Inlined at build time. Skips the check if empty/falsy. */\n currentVersion: string\n /**\n * Master enable flag — usually `Settings.checkForUpdates`. When `false`\n * the hook short-circuits without scheduling any work AND clears the\n * last-known status so a stale chip doesn't linger after the user\n * disabled the feature.\n */\n enabled: boolean\n /** Directory under which the on-disk cache lives. Usually `paths.userDir`. */\n cacheDir?: string\n registry?: string\n channel?: string\n /**\n * Cache lifetime for the read-through. **Defaults to `0`** (always\n * fetch fresh) — the TUI chip is the user's source of truth for \"what\n * version of zidane is on npm right now\"; we'd rather pay one HTTPS\n * call per session than tell them yesterday's news for up to 24 hours.\n *\n * The on-disk cache is still WRITTEN with each fresh result (so\n * `zidane upgrade` and other tooling reads benefit) and still READ to\n * feed the registry's ETag (so the cold path can short-circuit on 304\n * — meaningful bandwidth win when nothing changed). Pass a positive\n * value (e.g. `3_600_000` for 1 h) to enable read-through caching for\n * a GUI shell that mounts the hook in many places.\n */\n cacheTtlMs?: number\n /**\n * Wall-clock delay before the first check fires. Defaults to 1 500 ms so\n * we never compete with the boot path's first paint. The check itself\n * is async, so this only affects WHEN the chip first appears — never\n * blocks anything else.\n */\n delayMs?: number\n /** Injected for tests. */\n now?: () => number\n /** Injected for tests. */\n fetcher?: typeof fetch\n}\n\n/**\n * Returns the current update status. The check fires:\n * - Once per mount (every TUI launch).\n * - Every time `enabled` flips from `false` → `true` (user re-enabling\n * the setting expects an immediate refresh).\n *\n * The check is async and deferred by `delayMs` so it never competes with\n * first paint. Result is `null` until the first check resolves; flipping\n * `enabled` to `false` resets to `null` so any chip rendered against\n * `hasUpdate` disappears immediately.\n *\n * The hook owns its own AbortController so an unmount cancels any in-flight\n * registry request cleanly.\n */\nexport function useUpdateCheck(options: UseUpdateCheckOptions): UpdateStatus | null {\n const [status, setStatus] = useState<UpdateStatus | null>(null)\n // Ref-mirror so the effect doesn't re-run on every keystroke when the\n // host wraps the result into derived hint state.\n const optsRef = useRef(options)\n optsRef.current = options\n\n useEffect(() => {\n if (!options.enabled || !options.currentVersion) {\n // Disabling the setting must hide a chip that was already painted\n // — otherwise the user sees a stale `↑ vX.Y.Z` linger until the\n // next launch. React bails on identical-state updates so the\n // initial-mount case (status already null) costs nothing.\n setStatus(null)\n return\n }\n const controller = new AbortController()\n const timer = setTimeout(() => {\n void checkForUpdate({\n packageName: optsRef.current.packageName,\n currentVersion: optsRef.current.currentVersion,\n cacheDir: optsRef.current.cacheDir,\n registry: optsRef.current.registry,\n channel: optsRef.current.channel,\n // Default to TTL=0 (always fetch fresh). See `cacheTtlMs` docs.\n cacheTtlMs: optsRef.current.cacheTtlMs ?? 0,\n signal: controller.signal,\n now: optsRef.current.now,\n fetcher: optsRef.current.fetcher,\n }).then((s) => {\n if (!controller.signal.aborted)\n setStatus(s)\n })\n }, options.delayMs ?? 1500)\n return () => {\n clearTimeout(timer)\n controller.abort()\n }\n // Re-run when the master enable / package / version flip. Other\n // option changes stay inside `optsRef.current`; re-running on every\n // option mutation would cause render storms when the host wraps\n // the result into derived hint state.\n }, [options.enabled, options.packageName, options.currentVersion])\n\n return status\n}\n\n// ---------------------------------------------------------------------------\n// Footer chip helper — pure function so any renderer (TUI Footer, future\n// GUI status bar) can compose against the same `Hint`. The chip is\n// suppressed when there's no update; callers spread the result into their\n// hint array unconditionally.\n//\n// Color contract: the chip rides whatever accent the host passes (typically\n// `theme.colors.accent`). Renderers that want a \"fresh check\" badge can\n// inspect `status.source === 'fresh'` themselves — this helper sticks to\n// the one-line discoverability surface.\n// ---------------------------------------------------------------------------\n\nexport interface BuildUpdateHintOptions {\n /** Foreground color for the `↑` glyph + version label. */\n color: string\n /**\n * When true (default), the chip renders the literal version (`↑ v5.6.0`).\n * When false, just the glyph + `update` label — useful when the row is\n * tight and the version would push higher-priority chips off the right.\n */\n showVersion?: boolean\n /** Override the leading glyph. Defaults to `'↑'`. */\n glyph?: string\n}\n\nexport function buildUpdateHint(status: UpdateStatus | null, options: BuildUpdateHintOptions): Hint | null {\n if (!status?.hasUpdate || !status.latest)\n return null\n const label = options.showVersion === false\n ? 'update'\n : `v${stripVPrefix(status.latest)}`\n return {\n key: options.glyph ?? '↑',\n keyColor: options.color,\n label,\n labelColor: options.color,\n }\n}\n\n/**\n * `↻ v7.0.31` — a newer build is on disk than the one running (this\n * process is stale; see `auto-update-restart.ts`). Takes precedence over\n * the `↑` chip: once the release is installed, \"available\" is old news and\n * \"restart\" is the one action left. Same color contract as `buildUpdateHint`.\n */\nexport function buildRestartHint(\n state: { restartRequired: boolean, installed: string | null } | null,\n options: BuildUpdateHintOptions,\n): Hint | null {\n if (!state?.restartRequired || !state.installed)\n return null\n return {\n key: options.glyph ?? '↻',\n keyColor: options.color,\n label: options.showVersion === false ? 'restart' : `v${stripVPrefix(state.installed)}`,\n labelColor: options.color,\n }\n}\n\nfunction stripVPrefix(v: string): string {\n return v.startsWith('v') ? v.slice(1) : v\n}\n","// ---------------------------------------------------------------------------\n// Structured, host-agnostic upgrade of every INSTALLED zidane package.\n//\n// `runUpdateCommand` is the terminal-facing ledger for `zidane upgrade`; it\n// knows the TUI is the primary and detects that install's package manager\n// from `process.execPath`. A GUI cannot reuse it — its `execPath` is\n// Electron's — and wants results, not text. This module is the shared\n// engine both can build on: every target is located by its bin on PATH\n// (`findInstalledCompanion`), checked against the registry, installed through\n// the package manager that owns THAT bin, and verified on disk afterwards\n// (a PM exiting 0 is not proof; an optional platform binary that 404s is\n// silently skipped).\n//\n// On success the applied marker is written so running surfaces can offer a\n// restart — see `auto-update-restart.ts`.\n// ---------------------------------------------------------------------------\n\nimport type { PackageManagerCommand, UpdateStatus } from './auto-update'\nimport type { CompanionSpec, InstalledCompanion } from './auto-update-companion'\nimport { Writable } from 'node:stream'\nimport {\n checkForUpdate,\n checkTarballAvailable,\n compareSemver,\n detectPackageManager,\n performSelfUpdate,\n} from './auto-update'\nimport { findInstalledCompanion } from './auto-update-companion'\nimport { writeUpdateAppliedMarker } from './auto-update-restart'\n\nexport interface UpgradePackageResult {\n packageName: string\n /** Version on disk before the run; `null` when not installed. */\n from: string | null\n /** Version the registry advertises; `null` when the check failed. */\n to: string | null\n /**\n * - `'updated'` — installed and verified on disk.\n * - `'up-to-date'` — nothing to do.\n * - `'not-installed'` — bin not on PATH; skipped silently.\n * - `'unavailable'` — published but the tarball is not downloadable yet.\n * - `'failed'` — registry error, PM failure, or the install did not land; see `error`.\n */\n status: 'updated' | 'up-to-date' | 'not-installed' | 'unavailable' | 'failed'\n /** The PM command that ran (or would run). */\n command?: string[]\n error?: string\n}\n\nexport interface UpgradeReport {\n results: UpgradePackageResult[]\n /** No `failed` / `unavailable` entries. */\n ok: boolean\n /** At least one package was actually installed — a restart is now pending. */\n installedAny: boolean\n}\n\nexport type UpgradeProgressEvent\n = | { kind: 'check', packageName: string }\n | { kind: 'install', packageName: string, from: string, to: string, command: string[] }\n | { kind: 'log', packageName: string, stream: 'stdout' | 'stderr', line: string }\n | { kind: 'done', result: UpgradePackageResult }\n\nexport interface UpgradeInstalledPackagesOptions {\n /** Packages to keep in step, e.g. `[{ packageName: 'zidane-gui' }, { packageName: 'zidane-tui', binName: 'zidane' }]`. */\n targets: readonly CompanionSpec[]\n channel?: string\n registry?: string\n /** Where the registry cache and the applied marker live. */\n cacheDir?: string\n /** Install even when the registry check failed or reports up-to-date. */\n force?: boolean\n signal?: AbortSignal\n onProgress?: (event: UpgradeProgressEvent) => void\n /** Injected for tests. */\n findInstalled?: typeof findInstalledCompanion\n performSelfUpdate?: typeof performSelfUpdate\n checkTarballAvailable?: typeof checkTarballAvailable\n checkForUpdate?: typeof checkForUpdate\n detectPackageManager?: typeof detectPackageManager\n writeMarker?: typeof writeUpdateAppliedMarker\n now?: () => number\n}\n\nexport async function upgradeInstalledPackages(options: UpgradeInstalledPackagesOptions): Promise<UpgradeReport> {\n const channel = options.channel ?? 'latest'\n const find = options.findInstalled ?? findInstalledCompanion\n const check = options.checkForUpdate ?? checkForUpdate\n const available = options.checkTarballAvailable ?? checkTarballAvailable\n const install = options.performSelfUpdate ?? performSelfUpdate\n const detect = options.detectPackageManager ?? detectPackageManager\n const progress = options.onProgress ?? (() => {})\n\n const results: UpgradePackageResult[] = []\n for (const spec of options.targets) {\n if (options.signal?.aborted)\n break\n const installed = find(spec)\n if (!installed) {\n results.push({ packageName: spec.packageName, from: null, to: null, status: 'not-installed' })\n continue\n }\n progress({ kind: 'check', packageName: spec.packageName })\n const status = await check({\n packageName: installed.packageName,\n currentVersion: installed.version,\n channel,\n registry: options.registry,\n cacheDir: options.cacheDir,\n force: true,\n timeoutMs: 8000,\n signal: options.signal,\n })\n const result = await upgradeOne(installed, status)\n progress({ kind: 'done', result })\n results.push(result)\n }\n\n const installedAny = results.some(r => r.status === 'updated')\n if (installedAny && options.cacheDir) {\n const writeMarker = options.writeMarker ?? writeUpdateAppliedMarker\n writeMarker(options.cacheDir, {\n at: (options.now ?? Date.now)(),\n packages: results\n .filter(r => r.status === 'updated' && r.to)\n .map(r => ({ packageName: r.packageName, version: r.to! })),\n })\n }\n return {\n results,\n ok: results.every(r => r.status !== 'failed' && r.status !== 'unavailable'),\n installedAny,\n }\n\n async function upgradeOne(installed: InstalledCompanion, status: UpdateStatus): Promise<UpgradePackageResult> {\n const base = { packageName: installed.packageName, from: installed.version, to: status.latest }\n if (status.source === 'error' && !options.force)\n return { ...base, status: 'failed', error: `registry check failed: ${status.error ?? 'unknown error'}` }\n const outdated = status.latest !== null && compareSemver(status.latest, installed.version) > 0\n if (!outdated && !options.force)\n return { ...base, status: 'up-to-date' }\n if (status.latest && !options.force) {\n const availability = await available({ packageName: installed.packageName, version: status.latest, registry: options.registry })\n if (availability === 'missing')\n return { ...base, status: 'unavailable', error: `${installed.packageName} ${status.latest} is published but not downloadable yet. Try again in a few minutes.` }\n }\n // The PM that owns THIS bin — npm/pnpm/yarn/bun/Volta differ per install.\n const command: PackageManagerCommand = detect({ packageName: installed.packageName, channel, binaryPath: installed.binaryPath })\n progress({ kind: 'install', packageName: installed.packageName, from: installed.version, to: status.latest ?? channel, command: command.argv })\n\n const outcome = await install({\n packageName: installed.packageName,\n channel,\n packageManager: command.id,\n signal: options.signal,\n stdout: lineSink(installed.packageName, 'stdout'),\n stderr: lineSink(installed.packageName, 'stderr'),\n // Quiet PM output: the UI shows one live line, not a progress bar dump.\n env: { npm_config_loglevel: 'error', npm_config_progress: 'false' },\n })\n if (outcome.spawnError)\n return { ...base, status: 'failed', command: command.argv, error: `could not run ${command.argv[0]}: ${outcome.spawnError}` }\n if (outcome.exitCode !== 0)\n return { ...base, status: 'failed', command: command.argv, error: `${command.argv[0]} exited with ${outcome.exitCode ?? 'unknown'}` }\n\n // Verify: what is on disk now vs. what we asked for.\n const after = find(installed)\n if (status.latest && after && compareSemver(after.version, status.latest) < 0) {\n return {\n ...base,\n status: 'failed',\n command: command.argv,\n error: `install finished but ${after.version} is on disk, expected ${status.latest}. Reinstall: ${reinstallHint(command, installed.packageName, channel)}`,\n }\n }\n return { ...base, to: after?.version ?? status.latest, status: 'updated', command: command.argv }\n }\n\n /** Split a PM's byte stream into trimmed lines for the progress sink. */\n function lineSink(packageName: string, stream: 'stdout' | 'stderr'): NodeJS.WritableStream {\n let buffer = ''\n const flush = (final: boolean) => {\n const parts = buffer.split(/\\r?\\n/)\n buffer = final ? '' : parts.pop() ?? ''\n for (const part of parts) {\n const line = part.trim()\n if (line)\n progress({ kind: 'log', packageName, stream, line })\n }\n }\n return new Writable({\n write(chunk: Uint8Array | string, _enc, cb) {\n buffer += chunk.toString()\n flush(false)\n cb()\n },\n final(cb) {\n flush(true)\n cb()\n },\n })\n }\n}\n\nfunction reinstallHint(command: PackageManagerCommand, packageName: string, channel: string): string {\n const cmd = command.argv.join(' ')\n return command.id === 'volta' ? `volta uninstall ${packageName} && volta install ${packageName}@${channel}` : cmd\n}\n","// ---------------------------------------------------------------------------\n// Boot-time wall-clock profiler.\n//\n// Sprinkle `bootTick('<label>')` at strategic points along the startup\n// chain (cli.ts → runTui → setupTreeSitter → resolveConfig →\n// createCliRenderer → React mount → discovery effects → session\n// resume). When `ZIDANE_BOOT_PROFILE=1` is set, each call emits one\n// line on stderr with the delta since the previous tick and the total\n// since module load:\n//\n// $ ZIDANE_BOOT_PROFILE=1 zidane\n// [boot] +0.0ms / total 0.0ms / cli:enter\n// [boot] +12.3ms / total 12.3ms / cli:after-bundled-treesitter-setup\n// [boot] +47.8ms / total 60.1ms / runTui:after-setupTreeSitter\n// ...\n//\n// Renderer-agnostic on purpose — lives in `zidane/chat` so a future GUI\n// shell can wire it into its own boot path with the same env var and\n// the same output format. Writes to stderr, which OpenTUI's renderer\n// leaves alone (stdout is the frame buffer surface).\n// ---------------------------------------------------------------------------\n\nconst enabled = !!process.env.ZIDANE_BOOT_PROFILE\n/**\n * High-resolution origin for the running profile. Captured at module\n * load time so the first {@link bootTick} call inside the same process\n * reflects \"time since the boot profiler was first reached\", which for\n * the standard `cli.ts → runTui` path is effectively \"time since the\n * TUI binary started executing user code\".\n */\nconst start = performance.now()\nlet last = start\n\n/**\n * Record a checkpoint. No-op unless `ZIDANE_BOOT_PROFILE` is truthy in\n * the environment.\n *\n * The leading delta is the time since the PREVIOUS tick (so a long\n * delta highlights the immediately-preceding work); the trailing total\n * is the time since the profiler started. Both round to one decimal of\n * a millisecond.\n */\nexport function bootTick(label: string): void {\n if (!enabled)\n return\n const now = performance.now()\n const delta = now - last\n const total = now - start\n last = now\n // 10-char label pad keeps the alignment readable in a terminal even\n // when delta or total widens; longer labels just push the line out\n // slightly without breaking the format.\n process.stderr.write(\n `[boot] +${delta.toFixed(1).padStart(7)}ms / total ${total.toFixed(1).padStart(7)}ms / ${label}\\n`,\n )\n}\n\n/**\n * Returns `true` when `ZIDANE_BOOT_PROFILE` is set. Useful for guarding\n * heavier instrumentation (e.g. wrapping a costly call in a span) that\n * you don't want paying its own cost in the default path.\n */\nexport function bootProfileEnabled(): boolean {\n return enabled\n}\n","/**\n * Best-effort cross-platform browser open.\n *\n * macOS uses `open`, Linux uses `xdg-open`, Windows uses `start`. Failures\n * are swallowed — both callers (AI-provider OAuth + MCP-server OAuth) show\n * the URL on-screen for manual click anyway, and the underlying flows have\n * their own progress/cancel paths.\n *\n * Uses `spawn` (not `exec`) so the URL is passed as an argv element rather\n * than interpolated into a shell command — URLs containing `&`, `?`, `\"`,\n * or other shell metacharacters don't need quoting.\n *\n * Detached + unref so the spawned helper outlives the parent: the user\n * can finish authorizing in the browser even if the TUI is later\n * suspended (Ctrl-Z) or killed.\n */\n\nimport { spawn } from 'node:child_process'\nimport { dirname } from 'node:path'\n\nexport function tryOpenBrowser(url: string): void {\n const [cmd, ...args] = (() => {\n if (process.platform === 'darwin')\n return ['open', url]\n if (process.platform === 'win32')\n return ['cmd', '/c', 'start', '', url]\n return ['xdg-open', url]\n })()\n try {\n const child = spawn(cmd, args, { stdio: 'ignore', detached: true })\n child.on('error', () => {}) // ENOENT etc. — silently fall through\n child.unref()\n }\n catch {\n // spawn() itself failed (e.g. invalid binary path). Swallow.\n }\n}\n\n/**\n * Reveal a local path in the OS file manager: a file is selected inside its\n * folder (`open -R` on macOS, `explorer /select,` on Windows), a directory is\n * opened. Linux has no portable \"select\" — the containing directory opens.\n * Same fire-and-forget posture as {@link tryOpenBrowser}.\n */\nexport function tryRevealPath(path: string, isDirectory = false): void {\n const target = path.replace(/^file:\\/\\//, '')\n const [cmd, ...args] = (() => {\n if (process.platform === 'darwin')\n return isDirectory ? ['open', target] : ['open', '-R', target]\n if (process.platform === 'win32')\n return isDirectory ? ['explorer', target] : ['explorer', `/select,${target}`]\n return ['xdg-open', isDirectory ? target : dirname(target)]\n })()\n try {\n const child = spawn(cmd, args, { stdio: 'ignore', detached: true })\n child.on('error', () => {})\n child.unref()\n }\n catch {\n // swallow — a missing opener is not an error the rail should surface\n }\n}\n","/**\n * Read-only discovery of MCP servers + skills from OTHER coding agents'\n * config locations — Claude Code, Codex, and Pi — at both project and\n * user level. Gated behind the `importForeignConfigs` setting (default\n * off): the files belong to the other tools, so zidane only ever READS\n * them; installs, edits, and toggles keep writing to zidane's own\n * `.zidane` / `.agents` locations.\n *\n * Locations covered (missing files/dirs are skipped silently):\n *\n * MCP servers Skills\n * ── project ────────────────────────── ── project ───────────────\n * {root}/.mcp.json (Claude Code) {root}/.claude/skills\n * ~/.claude.json projects[{root}] (same) {root}/.codex/skills\n * {root}/.pi/mcps.json + mcp.json (Pi) {root}/.pi/skills\n * ── user ───────────────────────────── ── user ──────────────────\n * ~/.claude.json (Claude Code) ~/.claude/skills\n * ~/.codex/config.toml (Codex) ~/.codex/skills\n * ~/.pi/agent/mcps.json + mcp.json (Pi) ~/.pi/agent/skills\n *\n * `.agents/` is NOT here — it's the cross-agent convention zidane\n * already reads natively (see `project-user-paths.ts`), setting or no\n * setting.\n *\n * Precedence is decided by the callers (`mcps-discovery` /\n * `skills-discovery`): native locations always beat foreign ones, and\n * project beats user within each group.\n */\n\nimport type { McpServerConfig } from '../mcp'\nimport type { ProjectUserPath } from './project-user-paths'\nimport { realpathSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { resolve, sep } from 'node:path'\nimport { normalizeMcpServers } from '../mcp'\nimport { findGitRoot } from './project-root'\n\n/**\n * How a foreign MCP file is parsed:\n * - `json` — the shared `mcp(s).json` grammar (`parseMcpsFile` handles\n * wrappers, name-keyed maps, and flat arrays).\n * - `claude-user-state` — `~/.claude.json` is Claude Code's whole\n * user-state file; ONLY its `mcpServers` key is imported (treating\n * the rest as a name-keyed server map would produce garbage).\n * - `claude-project-state` — the same file's\n * `projects[\"<abs project root>\"].mcpServers` map: where `claude\n * mcp add` (default \"local\" scope) stores per-project servers.\n * - `codex-toml` — `~/.codex/config.toml` with `[mcp_servers.<name>]`\n * tables.\n */\nexport type ForeignMcpFormat = 'json' | 'claude-user-state' | 'claude-project-state' | 'codex-toml'\n\nexport interface ForeignMcpFile extends ProjectUserPath {\n format: ForeignMcpFormat\n}\n\ninterface ForeignPathOptions {\n /** Discovery cwd. Default: `process.cwd()`. */\n cwd?: string\n /** User home directory. Default: `os.homedir()`. */\n home?: string\n}\n\nfunction roots(opts: ForeignPathOptions): { projectRoot: string, home: string } {\n const cwd = opts.cwd ?? process.cwd()\n return {\n projectRoot: findGitRoot(cwd) ?? cwd,\n home: opts.home ?? homedir(),\n }\n}\n\n/**\n * MCP config files other agents maintain, project entries first.\n * Returned regardless of existence — callers `existsSync`-filter, same\n * contract as `defaultMcpsConfigPaths`.\n */\nexport function foreignMcpConfigFiles(opts: ForeignPathOptions = {}): ForeignMcpFile[] {\n const { projectRoot, home } = roots(opts)\n return [\n // Claude Code's project-scope convention: `.mcp.json` at the repo\n // root (mcpServers wrapper — the shared JSON grammar unwraps it).\n { path: resolve(projectRoot, '.mcp.json'), source: 'project', format: 'json' },\n // … and `claude mcp add`'s default \"local\" scope, which lives in the\n // USER state file keyed by absolute project path — project-sourced\n // because that's what it means, wherever it's stored.\n { path: resolve(home, '.claude.json'), source: 'project', format: 'claude-project-state' },\n { path: resolve(projectRoot, '.pi/mcps.json'), source: 'project', format: 'json' },\n { path: resolve(projectRoot, '.pi/mcp.json'), source: 'project', format: 'json' },\n { path: resolve(home, '.claude.json'), source: 'user', format: 'claude-user-state' },\n { path: resolve(home, '.codex/config.toml'), source: 'user', format: 'codex-toml' },\n { path: resolve(home, '.pi/agent/mcps.json'), source: 'user', format: 'json' },\n { path: resolve(home, '.pi/agent/mcp.json'), source: 'user', format: 'json' },\n ]\n}\n\n/**\n * Skill directories other agents maintain, project entries first. Feed\n * directly into `discoverSkills` — the SKILL.md format is shared across\n * the ecosystem, so foreign skills parse with the same lenient loader.\n */\nexport function foreignSkillScanPaths(opts: ForeignPathOptions = {}): ProjectUserPath[] {\n const { projectRoot, home } = roots(opts)\n return [\n { path: resolve(projectRoot, '.claude/skills'), source: 'project' },\n { path: resolve(projectRoot, '.codex/skills'), source: 'project' },\n { path: resolve(projectRoot, '.pi/skills'), source: 'project' },\n { path: resolve(home, '.claude/skills'), source: 'user' },\n { path: resolve(home, '.codex/skills'), source: 'user' },\n { path: resolve(home, '.pi/agent/skills'), source: 'user' },\n ]\n}\n\n/**\n * Parse the two foreign-only formats. The `json` format is NOT handled\n * here — callers route it through the shared `parseMcpsFile` (keeping\n * whitespace sanitizing + error hints in one place, and avoiding an\n * import cycle with `mcps-discovery`).\n */\nexport function parseForeignMcpConfig(\n text: string,\n format: Exclude<ForeignMcpFormat, 'json'>,\n opts: { projectRoot?: string } = {},\n): McpServerConfig[] {\n if (format === 'codex-toml')\n return parseCodexToml(text)\n return parseClaudeState(JSON.parse(text), format, opts.projectRoot)\n}\n\n/**\n * Extract MCP servers from a PRE-PARSED `~/.claude.json` state object.\n * Split from {@link parseForeignMcpConfig} so a discovery pass that reads\n * the file for BOTH scopes (user + project — it appears twice in the path\n * list) can read and `JSON.parse` the multi-MB state file once.\n *\n * - `claude-user-state` — ONLY the top-level `mcpServers` map; an\n * absent/malformed key means \"nothing to import\" rather than an error,\n * since the file legitimately exists without it.\n * - `claude-project-state` — `projects[<dir>].mcpServers`, where `claude\n * mcp add` (default \"local\" scope) stores per-project servers.\n */\nexport function parseClaudeState(\n state: unknown,\n format: 'claude-user-state' | 'claude-project-state',\n projectRoot?: string,\n): McpServerConfig[] {\n if (!state || typeof state !== 'object' || Array.isArray(state))\n return []\n if (format === 'claude-user-state')\n return normalizeForeignEntries((state as { mcpServers?: unknown }).mcpServers)\n return claudeProjectServers(state as { projects?: unknown }, projectRoot)\n}\n\n/**\n * Claude keys `projects` by the directory it was LAUNCHED from — for a\n * monorepo that's often a subdirectory of the git root we resolve to, and\n * paths surface in realpath variants (`/var` vs `/private/var` on macOS,\n * symlinked homes). Accept every key that realpath-normalizes to the root\n * or to a directory inside it; nearer-to-root keys win name collisions so\n * a root-registered server beats a subdirectory duplicate.\n */\nfunction claudeProjectServers(state: { projects?: unknown }, projectRoot: string | undefined): McpServerConfig[] {\n if (!projectRoot)\n return []\n const projects = state.projects\n if (!projects || typeof projects !== 'object' || Array.isArray(projects))\n return []\n const rootKey = normalizePathKey(projectRoot)\n const matches = Object.entries(projects as Record<string, unknown>)\n .map(([key, value]) => ({ key: normalizePathKey(key), value }))\n .filter(({ key }) => key === rootKey || key.startsWith(rootKey + sep))\n .sort((a, b) => a.key.length - b.key.length)\n const out: McpServerConfig[] = []\n const seen = new Set<string>()\n for (const { value } of matches) {\n const servers = value && typeof value === 'object' ? (value as { mcpServers?: unknown }).mcpServers : undefined\n for (const config of normalizeForeignEntries(servers)) {\n if (seen.has(config.name))\n continue\n seen.add(config.name)\n out.push(config)\n }\n }\n return out\n}\n\n/** Realpath-normalize for key comparison; deleted paths fall back to `resolve`. */\nfunction normalizePathKey(path: string): string {\n try {\n return realpathSync(path)\n }\n catch {\n return resolve(path)\n }\n}\n\n/**\n * Normalize a name-keyed server map ENTRY BY ENTRY, skipping the ones\n * `normalizeMcpServers` rejects (no inferrable transport, exotic shapes).\n * Whole-file normalization would let one bad entry drop every server in\n * a file the user doesn't own — and can't fix from zidane.\n */\nfunction normalizeForeignEntries(servers: unknown): McpServerConfig[] {\n if (!servers || typeof servers !== 'object' || Array.isArray(servers))\n return []\n const out: McpServerConfig[] = []\n for (const [name, entry] of Object.entries(servers as Record<string, unknown>)) {\n try {\n out.push(...normalizeMcpServers({ [name]: entry }))\n }\n catch {\n // Skip — the entry works (or fails) in its owning tool; not ours to error on.\n }\n }\n return out\n}\n\n/**\n * `~/.codex/config.toml` — `[mcp_servers.<name>]` tables. TOML parsing\n * rides Bun's built-in parser; on a non-Bun host the Codex import is\n * silently unavailable (returns `[]`) rather than pulling in a TOML\n * dependency for one optional file. Only the fields our config\n * understands are forwarded — Codex-specific extras\n * (`startup_timeout_ms`, …) are dropped before normalization.\n */\nfunction parseCodexToml(text: string): McpServerConfig[] {\n const toml = (globalThis as { Bun?: { TOML?: { parse: (t: string) => unknown } } }).Bun?.TOML\n if (!toml)\n return []\n const parsed = toml.parse(text) as { mcp_servers?: unknown }\n const tables = parsed && typeof parsed === 'object' ? parsed.mcp_servers : undefined\n if (!tables || typeof tables !== 'object' || Array.isArray(tables))\n return []\n const map: Record<string, unknown> = {}\n for (const [name, entry] of Object.entries(tables as Record<string, unknown>)) {\n if (!entry || typeof entry !== 'object')\n continue\n const raw = entry as Record<string, unknown>\n map[name] = {\n ...(raw.command !== undefined ? { command: raw.command } : {}),\n ...(raw.args !== undefined ? { args: raw.args } : {}),\n ...(raw.env !== undefined ? { env: raw.env } : {}),\n // Codex's experimental remote servers — normalize infers transport.\n ...(raw.url !== undefined ? { url: raw.url } : {}),\n }\n }\n return normalizeForeignEntries(map)\n}\n","/**\n * MCP server discovery from project + user config files.\n *\n * Reads `mcps.json` (zidane's canonical name) AND `mcp.json` (Cursor /\n * common ecosystem alias) from both `.agents/` and `.{prefix}/` in\n * each of the project + user dirs — a list of `McpServerConfig`\n * shapes the SDK accepts. Files are normalized via the same path the\n * agent uses (`normalizeMcpServers`), and the user's enabled-toggle\n * list folds the result into a server array the agent picks up at\n * construction.\n *\n * Co-existing files merge by first-found-per-name: e.g.\n * `.agents/mcps.json` declares `linear`, `.agents/mcp.json` declares\n * `linear` + `notion` → discovery emits the `mcps.json` `linear` plus\n * `mcp.json`'s `notion`. Project always beats user across all files.\n *\n * Pattern mirrors `skills-discovery.ts` — the chat layer owns\n * discovery + UI, the agent receives the same `McpServerConfig[]` it\n * always did.\n */\n\nimport type { McpServerConfig } from '../mcp'\nimport type { ForeignMcpFormat } from './foreign-configs'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { errorMessage } from '../errors'\nimport { normalizeMcpServers } from '../mcp'\nimport { foreignMcpConfigFiles, parseClaudeState, parseForeignMcpConfig } from './foreign-configs'\nimport { findGitRoot } from './project-root'\nimport { projectUserPaths } from './project-user-paths'\nimport { rulerMcpConfigFiles } from './ruler-configs'\n\n/**\n * File names searched in each of the four project/user roots. Order\n * matters within a single directory — `mcps.json` (zidane's canonical\n * name) beats `mcp.json` (Cursor / common ecosystem alias) when both\n * exist side-by-side. Across files we merge per-name with first-found\n * winning, so co-existing `mcp.json` entries are folded in for names\n * not declared in the canonical file.\n */\nconst MCP_CONFIG_FILE_NAMES = ['mcps.json', 'mcp.json'] as const\n\n/**\n * One entry in the discovered list. `path` is the source file (one of\n * the `mcp(s).json` variants under `.agents/` or `.{prefix}/`) so a\n * Settings picker can show \"Defined in ~/.zidane/mcp.json\" tooltips\n * and the user can tell which file declared what.\n */\nexport interface DiscoveredMcp {\n config: McpServerConfig\n source: 'project' | 'user'\n path: string\n}\n\n/**\n * Search order for MCP config files — see {@link projectUserPaths}.\n *\n * Both `mcps.json` (zidane's canonical name) and `mcp.json` (Cursor /\n * common ecosystem alias) are picked up from every search root. The\n * canonical name wins within the same directory; across files the\n * usual first-found-by-name dedup applies, so `mcp.json` effectively\n * merges its non-overlapping entries on top of `mcps.json`.\n *\n * Order per scope (project beats user):\n * 1. `{project}/.agents/mcps.json`\n * 2. `{project}/.agents/mcp.json`\n * 3. `{project}/.{prefix}/mcps.json`\n * 4. `{project}/.{prefix}/mcp.json`\n * 5. `~/.agents/mcps.json`\n * 6. `~/.agents/mcp.json`\n * 7. `~/.{prefix}/mcps.json`\n * 8. `~/.{prefix}/mcp.json`\n *\n * Non-existent files are skipped without error.\n */\nexport function defaultMcpsConfigPaths(opts: {\n cwd?: string\n home?: string\n prefix?: string\n} = {}): { path: string, source: 'project' | 'user' }[] {\n // Build the path list per-filename via the shared helper, then\n // interleave so `<dir>/mcps.json` lands immediately before\n // `<dir>/mcp.json` for each scope. Same ordering invariant that\n // `projectUserPaths` already guarantees within a filename — the\n // interleave preserves it across filenames.\n const perName = MCP_CONFIG_FILE_NAMES.map(name => projectUserPaths({ subPath: name, ...opts }))\n const rootCount = perName[0].length\n const out: { path: string, source: 'project' | 'user' }[] = []\n for (let i = 0; i < rootCount; i++) {\n for (const list of perName)\n out.push(list[i])\n }\n return out\n}\n\n/**\n * Parse one `mcps.json` file. Canonical zidane shape is a flat array; the\n * other shapes exist for copy-paste from neighboring tools:\n *\n * - `McpServerConfig[]` — flat array (recommended).\n * - `{ name: {...} }` — name-keyed map.\n * - `{ \"mcpServers\": { name: {...} } }` — Claude Desktop / Claude Code /\n * Cursor wrapper. Unwrapped before normalization (otherwise\n * `normalizeMcpServers` would treat the whole wrapper as a single server).\n *\n * Per-entry `enabled: false` (Cursor runtime state) drops the entry — same\n * intent as the source tool. Use the TUI toggle list for \"show but disabled\".\n *\n * Tolerant to common copy-paste artifacts: when strict `JSON.parse` fails,\n * retries after normalizing invisible whitespace that's valid Unicode but\n * not valid JSON whitespace (BOM, NBSP, zero-width spaces, line/paragraph\n * separators) — only OUTSIDE string literals, so user data isn't mutated.\n * If the sanitized version still fails, throws the original error with a\n * targeted hint when NBSPs are present.\n *\n * Validation flows through `normalizeMcpServers` so the result matches\n * what `createAgent` accepts. Throws on truly malformed JSON; the caller\n * decides whether to surface or swallow.\n */\nexport function parseMcpsFile(text: string): McpServerConfig[] {\n const raw = parseJsonTolerant(text)\n const target: unknown = raw && typeof raw === 'object' && !Array.isArray(raw) && 'mcpServers' in raw\n ? (raw as { mcpServers: unknown }).mcpServers\n : raw\n return normalizeMcpServers(dropDisabledEntries(target))\n}\n\n/**\n * Strict `JSON.parse` with the whitespace-tolerance retry described on\n * {@link parseMcpsFile}, returning the RAW parsed value. Exported for the\n * write side (`mcps-write.ts`): a file that discovery reads fine MUST also\n * be editable — upsert/remove failing with \"Unrecognized token\" on an NBSP\n * the read path silently tolerates would strand the user.\n */\nexport function parseJsonTolerant(text: string): unknown {\n try {\n return JSON.parse(text)\n }\n catch (firstErr) {\n const sanitized = sanitizeJsonWhitespace(text)\n if (!sanitized.changed)\n throw firstErr\n try {\n return JSON.parse(sanitized.text)\n }\n catch {\n throw enhanceJsonError(firstErr, text)\n }\n }\n}\n\n/**\n * Code points that are valid Unicode whitespace but NOT valid JSON whitespace.\n * Copy-paste from rich-text contexts (markdown editors, chat UIs, web pages)\n * frequently slips these in:\n *\n * - `\\uFEFF` BOM — sometimes prepended by Windows editors.\n * - `\\u00A0` NBSP — markdown/HTML smart-replace common culprit.\n * - `\\u200B`-`\\u200D` zero-width family — often invisible in editors.\n * - `\\u2028` / `\\u2029` line/paragraph separators — valid in JS strings\n * pre-ES2019 but never in strict JSON.\n * - `\\u2060` word joiner.\n *\n * Replace with ASCII space (zero-width chars are removed; the others become\n * a single space). Replacements happen ONLY outside string literals so a\n * user's intentional NBSP inside a `url` or display name is left alone.\n */\nconst PROBLEMATIC_WHITESPACE = new Set<number>([\n 0xFEFF,\n 0x00A0,\n 0x200B,\n 0x200C,\n 0x200D,\n 0x2028,\n 0x2029,\n 0x2060,\n])\n\nfunction sanitizeJsonWhitespace(text: string): { text: string, changed: boolean } {\n let changed = false\n let inString = false\n let escaped = false\n const out: string[] = []\n for (let i = 0; i < text.length; i++) {\n const ch = text[i]\n if (inString) {\n out.push(ch)\n if (escaped)\n escaped = false\n else if (ch === '\\\\')\n escaped = true\n else if (ch === '\"')\n inString = false\n continue\n }\n if (ch === '\"') {\n inString = true\n out.push(ch)\n continue\n }\n const code = ch.charCodeAt(0)\n if (PROBLEMATIC_WHITESPACE.has(code)) {\n changed = true\n // Zero-width chars (200B-200D, 2060, FEFF) erased; visible whitespace\n // replacements become a single space so token boundaries are preserved.\n if (code === 0x00A0 || code === 0x2028 || code === 0x2029)\n out.push(' ')\n continue\n }\n out.push(ch)\n }\n return { text: out.join(''), changed }\n}\n\nfunction enhanceJsonError(err: unknown, text: string): Error {\n const orig = err instanceof Error ? err : new Error(String(err))\n const hints: string[] = []\n if (text.includes('\\u00A0'))\n hints.push('the file contains non-breaking spaces (U+00A0), likely from copy-paste. Replace them with regular spaces')\n if (text.charCodeAt(0) === 0xFEFF)\n hints.push('the file starts with a BOM (U+FEFF), which strict JSON.parse rejects')\n if (hints.length === 0)\n return orig\n return new Error(`${orig.message}. ${hints.join('; ')}`)\n}\n\n/**\n * Strip `enabled: false` entries before normalization — same intent as the\n * source tool that wrote them (Cursor flips this when the user toggles a\n * server off in its UI). One pass over array OR map shape, O(N).\n *\n * Done pre-normalize so we don't have to teach `normalizeMcpServers` about\n * the flag — the canonical `McpServerConfig` has no `enabled` field.\n */\nfunction dropDisabledEntries(target: unknown): unknown {\n if (Array.isArray(target))\n return target.filter(e => !isEntryDisabled(e))\n if (target && typeof target === 'object') {\n const filtered: Record<string, unknown> = {}\n for (const [name, entry] of Object.entries(target as Record<string, unknown>)) {\n if (!isEntryDisabled(entry))\n filtered[name] = entry\n }\n return filtered\n }\n return target\n}\n\nfunction isEntryDisabled(entry: unknown): boolean {\n return !!entry && typeof entry === 'object'\n && (entry as { enabled?: unknown }).enabled === false\n}\n\n/**\n * Per-file failure surfaced from {@link discoverProjectMcps}. The TUI shows\n * these in the MCP settings empty / preamble state so a malformed\n * `mcps.json` doesn't look identical to a missing one.\n */\nexport interface DiscoveryError {\n path: string\n source: 'project' | 'user'\n message: string\n /**\n * A deliberate, documented behavior being reported (e.g. the ruler stdio\n * security carve-out) rather than a failure. Hosts should style these as\n * info, not error — crying wolf over a working default trains users to\n * ignore the channel that also carries real parse failures.\n */\n informational?: boolean\n}\n\n/**\n * Result of {@link discoverProjectMcps}. `servers` is the per-source-priority\n * merged list (project beats user, first-found wins on name collision).\n * `errors` is one entry per file that existed but failed to parse, plus\n * informational entries for servers a security gate skipped (ruler stdio\n * without the foreign-agent opt-in) — empty on the happy path.\n */\nexport interface DiscoveryResult {\n servers: DiscoveredMcp[]\n errors: DiscoveryError[]\n}\n\n/**\n * Walk the default config paths, parse every file that exists, and return\n * `{ servers, errors }` in source-priority order. Project entries appear\n * before user entries; first occurrence of a `name` wins (later\n * duplicates dropped).\n *\n * Parse failures are captured into `errors` rather than thrown — the picker\n * surfaces them while still showing whatever DID parse. A `ZIDANE_DEBUG`\n * log line is also emitted for ops visibility.\n */\nexport function discoverProjectMcps(opts: {\n cwd?: string\n home?: string\n prefix?: string\n /**\n * Also read MCP configs other agents maintain (Claude Code, Codex,\n * Pi) — see `foreign-configs.ts`. Mirrors the `importForeignConfigs`\n * setting; native locations always win name collisions, and project\n * beats user within each group.\n */\n foreignAgents?: boolean\n /**\n * Also read Ruler MCP configs (`.ruler/ruler.toml` + `mcp.json`,\n * project and XDG-global) — see `ruler-configs.ts`. Mirrors the\n * `importRulerConfigs` setting; same collision rules as above.\n */\n ruler?: boolean\n} = {}): DiscoveryResult {\n const paths = mcpsDiscoveryPaths(opts).filter(p => existsSync(p.path))\n // Claude's \"local\"-scope servers are keyed by absolute project path\n // inside the user state file — resolve once for the whole pass.\n const projectRoot = findGitRoot(opts.cwd ?? process.cwd()) ?? (opts.cwd ?? process.cwd())\n const seen = new Set<string>()\n // Pass-level record of names already reported as stdio-dropped — a server\n // declared in BOTH `.ruler/ruler.toml` and `.ruler/mcp.json` would\n // otherwise appear in two informational notices, inflating the count.\n const droppedStdioSeen = new Set<string>()\n const servers: DiscoveredMcp[] = []\n const errors: DiscoveryError[] = []\n // `~/.claude.json` appears TWICE in the path list (project + user scopes)\n // and runs to multiple MB in the wild (it embeds per-project history) —\n // read + `JSON.parse` it once per pass. Caching the failure too collapses\n // a torn read (a live Claude Code session rewriting the file) into ONE\n // transient error instead of two.\n const claudeStateCache = new Map<string, { state: unknown } | { failed: true }>()\n for (const { path, source, format, dropStdio } of paths) {\n const isClaudeState = format === 'claude-user-state' || format === 'claude-project-state'\n const cached = isClaudeState ? claudeStateCache.get(path) : undefined\n if (cached && 'failed' in cached)\n continue // parse failure already recorded for this path this pass\n let configs: McpServerConfig[]\n try {\n if (isClaudeState) {\n let state: unknown\n if (cached && 'state' in cached) {\n state = cached.state\n }\n else {\n try {\n state = JSON.parse(readFileSync(path, 'utf8')) as unknown\n }\n catch (err) {\n claudeStateCache.set(path, { failed: true })\n throw err\n }\n claudeStateCache.set(path, { state })\n }\n configs = parseClaudeState(state, format, projectRoot)\n }\n else {\n const text = readFileSync(path, 'utf8')\n configs = format === 'json' ? parseMcpsFile(text) : parseForeignMcpConfig(text, format, { projectRoot })\n }\n }\n catch (err) {\n const message = errorMessage(err)\n errors.push({ path, source, message })\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] failed to parse \"${path}\": ${message}\\n`)\n continue\n }\n const droppedStdio: string[] = []\n for (const config of configs) {\n if (seen.has(config.name))\n continue\n // Ruler stdio servers require the explicit stdio opt-in (see\n // `mcpsDiscoveryPaths`) — a `command` from a repo-committed file\n // would otherwise execute on the first bootstrap of a freshly\n // cloned (possibly hostile) repo, since `importRulerConfigs`\n // defaults ON and discovered servers are enabled by default.\n // (Checked AFTER the dedup guard so a server that already imported\n // from a native location isn't misreported as dropped.)\n if (dropStdio && config.transport === 'stdio') {\n if (!droppedStdioSeen.has(config.name)) {\n droppedStdioSeen.add(config.name)\n droppedStdio.push(config.name)\n }\n continue\n }\n seen.add(config.name)\n servers.push({ config, source, path })\n }\n // Surface the security carve-out instead of vanishing silently — the\n // picker renders these alongside parse errors, so the user learns WHY\n // a `.ruler` stdio server is missing and which switch enables it.\n if (droppedStdio.length > 0) {\n errors.push({\n path,\n source,\n message: `${droppedStdio.length} stdio server${droppedStdio.length === 1 ? '' : 's'} (${droppedStdio.join(', ')}) skipped. Running stdio commands from Ruler files needs “Import configurations from other agents” enabled.`,\n informational: true, // working security default, not a failure\n })\n }\n }\n return { servers, errors }\n}\n\n/**\n * Full path list a discovery pass reads, formats attached — native\n * `mcp(s).json` locations first (they win name collisions), then the\n * foreign-agent files when enabled.\n *\n * Ruler files carry `dropStdio` unless the foreign-agent opt-in is ALSO\n * set: `importRulerConfigs` defaults ON (it's an intentional committed\n * config source), but stdio servers execute local commands — the exact\n * risk `importForeignConfigs` exists to gate (`types.ts`: \"importing them\n * is an explicit opt-in\"). Remote (url) ruler servers import by default;\n * `command`-based ones wait for the same opt-in as every other foreign\n * stdio source.\n */\nfunction mcpsDiscoveryPaths(opts: {\n cwd?: string\n home?: string\n prefix?: string\n foreignAgents?: boolean\n ruler?: boolean\n} = {}): { path: string, source: 'project' | 'user', format: ForeignMcpFormat, dropStdio?: boolean }[] {\n const native = defaultMcpsConfigPaths(opts).map(p => ({ ...p, format: 'json' as const }))\n return [\n ...native,\n ...(opts.foreignAgents ? foreignMcpConfigFiles(opts) : []),\n ...(opts.ruler ? rulerMcpConfigFiles(opts).map(p => ({ ...p, dropStdio: opts.foreignAgents !== true })) : []),\n ]\n}\n\n/**\n * Map a user-toggled enable list onto the `mcpServers` array the agent\n * receives. Conventions match `buildSkillsConfig`:\n *\n * - `enabled === undefined` → every discovered server enabled (default).\n * - `enabled === []` → no servers (the agent runs MCP-less).\n * - `enabled === [names]` → allowlist; servers not in the list are\n * dropped.\n *\n * `disabledTools` is the per-server tool deny-list from\n * `Settings.disabledMcpTools`. For each server that survives the\n * server-level enable filter, the per-tool names are merged into\n * `McpServerConfig.disabledTools` via **union** with anything the JSON\n * file already pinned — a tool listed in either source is dropped.\n * Union (not override) keeps JSON-side policy stable: a server file\n * that pins `disabledTools: ['dangerous_tool']` can't be re-enabled\n * via the UI by accident.\n *\n * `enabledTools` (JSON-side allowlist) takes precedence over the\n * UI-side deny-list when both are set on the same server — the\n * allowlist's \"only these\" semantics would be meaningless if a UI\n * deny-list could subtract from it again. We don't emit `disabledTools`\n * when `enabledTools` is present.\n *\n * Returns a fresh array — callers can mutate without affecting the\n * underlying discovery list. Per-server config objects are also fresh\n * copies (shallow), so the merged `disabledTools` doesn't leak back\n * onto the discovery catalog.\n */\nexport function buildMcpServers(opts: {\n discovered: readonly DiscoveredMcp[]\n enabled?: readonly string[]\n disabledTools?: Record<string, readonly string[]>\n}): McpServerConfig[] {\n const survivors = filterByEnabled(opts.discovered, opts.enabled)\n const deny = opts.disabledTools\n if (!deny || Object.keys(deny).length === 0)\n return survivors.map(d => ({ ...d.config }))\n\n return survivors.map((d) => {\n const next: McpServerConfig = { ...d.config }\n const uiDeny = deny[d.config.name]\n if (!uiDeny || uiDeny.length === 0)\n return next\n // `enabledTools` is JSON-side authority — see JSDoc.\n if (next.enabledTools && next.enabledTools.length > 0)\n return next\n const merged = new Set<string>(next.disabledTools ?? [])\n for (const name of uiDeny)\n merged.add(name)\n next.disabledTools = [...merged].sort()\n return next\n })\n}\n\n/**\n * Dedup a merged MCP-server list by `name`. First entry wins.\n *\n * Applied at agent build across every source that can contribute a server —\n * `mcps.json`, Agent Plugins, launch options, extensions — so the earliest\n * source in that order takes precedence.\n *\n * Drops are REPORTED rather than silent, via `onDropped`. A plugin shipping a\n * `github` server loses to a `github` in `mcps.json`, and nothing downstream\n * says so: the plugin's settings row still counts the server it shipped,\n * because the row renders what the PACKAGE declares and only this merge knows\n * what survived. That mirrors how the skills side reports the same class of\n * event — `resolveSkills` emits `name-collision-shadowed` on the winner rather\n * than annotating the loser.\n *\n * The reporter is injected rather than calling a logger directly so this stays\n * a pure function of its input, testable without a logging shim. It lives here\n * beside {@link buildMcpServers} rather than in the TUI because merge\n * precedence is a config concern, not a rendering one.\n */\nexport function dedupMcpServersByName<T extends { name: string }>(\n servers: readonly T[],\n onDropped?: (names: readonly string[]) => void,\n): T[] {\n const seen = new Set<string>()\n const out: T[] = []\n const dropped: string[] = []\n for (const server of servers) {\n if (seen.has(server.name)) {\n dropped.push(server.name)\n continue\n }\n seen.add(server.name)\n out.push(server)\n }\n if (dropped.length > 0)\n onDropped?.(dropped)\n return out\n}\n\nfunction filterByEnabled(\n discovered: readonly DiscoveredMcp[],\n enabled: readonly string[] | undefined,\n): readonly DiscoveredMcp[] {\n if (enabled === undefined)\n return discovered\n if (enabled.length === 0)\n return []\n const allow = new Set(enabled)\n return discovered.filter(d => allow.has(d.config.name))\n}\n","/**\n * Write-side companion to `mcps-discovery.ts` — upsert one MCP server\n * entry into an `mcps.json` config file WITHOUT disturbing anything else\n * in it.\n *\n * Discovery tolerates three shapes (flat `McpServerConfig[]`, name-keyed\n * map, and the Claude/Cursor `{ \"mcpServers\": { … } }` wrapper). Users\n * hand-author these files and copy-paste from other tools, so the writer\n * must be shape-preserving: it parses the RAW json (not the normalized\n * view), locates the collection in whatever shape it's in, upserts by\n * name, and re-serializes — unknown sibling fields, unknown per-entry\n * fields on OTHER entries, and the wrapper shape all survive untouched.\n * A fresh file is created in the canonical flat-array shape.\n *\n * Scope roots mirror discovery: `<gitRoot>/.zidane/mcps.json` (project)\n * or `~/.zidane/mcps.json` (user) — the `.agents/` siblings stay\n * read-only for us (cross-tool convention files).\n */\n\nimport type { McpServerConfig } from '../types'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { parseJsonTolerant } from './mcps-discovery'\nimport { projectUserPaths } from './project-user-paths'\n\nexport type McpConfigScope = 'project' | 'user'\n\n/**\n * Resolve the CANONICAL write target for a scope — always the\n * `.{prefix}/mcps.json` flavor, never `.agents` (we don't own that\n * convention) and never `mcp.json` (Cursor's spelling is read-compat\n * only).\n */\nexport function mcpsWritePath(scope: McpConfigScope, opts?: { cwd?: string, home?: string, prefix?: string }): string {\n const prefix = (opts?.prefix ?? '.zidane').replace(/^\\./, '')\n const candidates = projectUserPaths({\n subPath: 'mcps.json',\n ...(opts?.cwd ? { cwd: opts.cwd } : {}),\n ...(opts?.home ? { home: opts.home } : {}),\n ...(opts?.prefix ? { prefix: opts.prefix } : {}),\n })\n const target = candidates.find(c => c.source === scope && c.path.includes(`.${prefix}`))\n if (!target)\n throw new Error(`No ${scope} mcps.json path resolved.`)\n return target.path\n}\n\nexport interface UpsertMcpResult {\n /** The file written. */\n path: string\n /** `updated` when an entry of the same name was replaced, else `added`. */\n action: 'added' | 'updated'\n}\n\n/**\n * Upsert `entry` (matched by `name`) into the mcps config at `filePath`.\n * Preserves the existing file's shape and every field the writer doesn't\n * own; creates a canonical flat-array file when none exists. Throws on\n * malformed JSON — silently rewriting a broken file the user is mid-edit\n * on would destroy their work.\n */\nexport function upsertMcpServer(filePath: string, entry: McpServerConfig): UpsertMcpResult {\n if (!existsSync(filePath)) {\n mkdirSync(dirname(filePath), { recursive: true })\n writeFileSync(filePath, `${JSON.stringify([entry], null, 2)}\\n`)\n return { path: filePath, action: 'added' }\n }\n\n // Tolerant parse (same retry as discovery's read path) — a file whose\n // servers LIST fine must also accept edits; strict JSON.parse would\n // reject the NBSP/zero-width artifacts discovery silently forgives.\n const raw: unknown = parseJsonTolerant(readFileSync(filePath, 'utf8'))\n\n // Flat array — the canonical zidane shape. Entries carry their `name`.\n if (Array.isArray(raw)) {\n const idx = raw.findIndex(e => isRecord(e) && e.name === entry.name)\n const next = [...raw]\n const action = idx >= 0 ? 'updated' as const : 'added' as const\n if (idx >= 0)\n next[idx] = entry\n else\n next.push(entry)\n writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\\n`)\n return { path: filePath, action }\n }\n\n if (isRecord(raw)) {\n // Claude/Cursor wrapper: mutate only the inner map, siblings survive.\n if (isRecord(raw.mcpServers)) {\n const map = raw.mcpServers\n const action = entry.name in map ? 'updated' as const : 'added' as const\n const next = { ...raw, mcpServers: { ...map, [entry.name]: stripName(entry) } }\n writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\\n`)\n return { path: filePath, action }\n }\n // Bare name-keyed map.\n const action = entry.name in raw ? 'updated' as const : 'added' as const\n const next = { ...raw, [entry.name]: stripName(entry) }\n writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\\n`)\n return { path: filePath, action }\n }\n\n throw new Error(`${filePath} is not a recognized mcps.json shape (array, map, or {\"mcpServers\": …}).`)\n}\n\nexport interface RemoveMcpResult {\n /** The file rewritten. */\n path: string\n}\n\n/**\n * Remove the entry named `name` from the mcps config at `filePath`,\n * preserving the file's shape and everything else in it — the removal\n * counterpart of {@link upsertMcpServer}. Throws when the file is\n * missing, malformed, an unrecognized shape, or has no entry of that\n * name (the caller shows the message on the settings op line; silently\n * \"succeeding\" on a no-op would lie about what happened).\n */\nexport function removeMcpServer(filePath: string, name: string): RemoveMcpResult {\n if (!existsSync(filePath))\n throw new Error(`${filePath} does not exist.`)\n\n // Same tolerant parse as upsert — see the note there.\n const raw: unknown = parseJsonTolerant(readFileSync(filePath, 'utf8'))\n\n if (Array.isArray(raw)) {\n const next = raw.filter(e => !(isRecord(e) && e.name === name))\n if (next.length === raw.length)\n throw new Error(`no entry named \"${name}\" in ${filePath}.`)\n writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\\n`)\n return { path: filePath }\n }\n\n if (isRecord(raw)) {\n if (isRecord(raw.mcpServers)) {\n if (!(name in raw.mcpServers))\n throw new Error(`no entry named \"${name}\" in ${filePath}.`)\n const { [name]: _dropped, ...rest } = raw.mcpServers\n const next = { ...raw, mcpServers: rest }\n writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\\n`)\n return { path: filePath }\n }\n if (!(name in raw))\n throw new Error(`no entry named \"${name}\" in ${filePath}.`)\n const { [name]: _dropped, ...next } = raw\n writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\\n`)\n return { path: filePath }\n }\n\n throw new Error(`${filePath} is not a recognized mcps.json shape (array, map, or {\"mcpServers\": …}).`)\n}\n\n/** Map-shaped files key entries by name — the inner object must not repeat it. */\nfunction stripName(entry: McpServerConfig): Omit<McpServerConfig, 'name'> {\n const { name: _name, ...rest } = entry\n return rest\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","/**\n * One-shot IMPORT from an existing Claude Code setup — the materializing\n * counterpart to `foreign-configs.ts`'s read-only discovery.\n *\n * Three pieces, each independently skippable:\n *\n * - Sessions — `~/.claude/projects/<slug>/*.jsonl` transcripts convert\n * to zidane `SessionData` and land in the session store. Idempotent:\n * the zidane session REUSES the Claude session UUID, so a re-import\n * skips everything the store already has.\n * - MCP servers — Claude's `.mcp.json` + `~/.claude.json` entries are\n * COPIED into zidane-owned `mcps.json` files (via `upsertMcpServer`).\n * Names already present in any native zidane/agents config are\n * skipped — imports never shadow what the user configured natively.\n * - Skills — `.claude/skills/<name>` directories are copied into the\n * corresponding `.{prefix}/skills`, skipping names that already\n * exist in any native scan path.\n *\n * Claude's `settings.json` (permissions / hooks / plugins) has no zidane\n * equivalent and `CLAUDE.md` is already read natively (`agents-md.ts`),\n * so neither is imported — the UI reports that instead of pretending.\n */\n\nimport type { SessionData, SessionStore } from '../session'\nimport type { McpServerConfig, SessionContentBlock, SessionTurn, ToolResultContent, TurnUsage } from '../types'\nimport { Buffer } from 'node:buffer'\nimport { closeSync, cpSync, existsSync, openSync, readdirSync, readFileSync, readSync, realpathSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { basename, join, resolve } from 'node:path'\nimport { parseClaudeState } from './foreign-configs'\nimport { defaultMcpsConfigPaths, parseJsonTolerant, parseMcpsFile } from './mcps-discovery'\nimport { mcpsWritePath, upsertMcpServer } from './mcps-write'\nimport { findGitRoot } from './project-root'\nimport { projectUserPaths } from './project-user-paths'\n\nexport interface ClaudeImportOptions {\n /** Discovery cwd. Default: `process.cwd()`. */\n cwd?: string\n /** User home directory. Default: `os.homedir()`. */\n home?: string\n /** Storage prefix for write targets. Default: `'.zidane'`. */\n prefix?: string\n /** Import sessions from every Claude project dir, not just the current project's. */\n allProjects?: boolean\n}\n\nfunction roots(opts: ClaudeImportOptions): { projectRoot: string, home: string } {\n const cwd = opts.cwd ?? process.cwd()\n return {\n projectRoot: findGitRoot(cwd) ?? cwd,\n home: opts.home ?? homedir(),\n }\n}\n\n/** `~/.claude` — presence gates the settings action row. */\nexport function hasClaudeCodeSetup(opts: ClaudeImportOptions = {}): boolean {\n return existsSync(resolve(roots(opts).home, '.claude'))\n}\n\n// ---------------------------------------------------------------------------\n// Sessions\n// ---------------------------------------------------------------------------\n\n/**\n * Claude Code names each `~/.claude/projects/` subdirectory after the\n * project path with every non-alphanumeric character flattened to `-`\n * (`/Users/me/.claude/wt` → `-Users-me--claude-wt`). Lossy, so matching\n * goes slug → slug rather than trying to reverse it.\n */\nexport function claudeProjectSlug(path: string): string {\n return path.replace(/[^a-z0-9]/gi, '-')\n}\n\nexport interface ClaudeSessionFile {\n path: string\n /** The `~/.claude/projects/<slug>` directory the file came from. */\n projectDir: string\n}\n\n/**\n * Session transcripts for the current project, or every project when\n * `allProjects` is set.\n *\n * Claude Code slugs by LAUNCH directory, so a session started in a\n * monorepo subdir (`repo/packages/foo`) lands in a different dir than\n * one started at the repo root. Scoped listing therefore accepts both\n * the exact slug of the resolved git root (realpath variants included)\n * AND any dir whose slug extends it — but because slugs are lossy\n * (`repo/foo` and a sibling `repo-foo` collide), prefix-matched dirs\n * are confirmed per-file against the transcript's recorded `cwd`\n * (resolved to its git root), mirroring how the converted session's\n * `projectRoot` is derived.\n */\nexport function listClaudeSessionFiles(opts: ClaudeImportOptions = {}): ClaudeSessionFile[] {\n const out: ClaudeSessionFile[] = []\n walkClaudeSessionFiles(opts, opts.allProjects\n ? file => out.push(file)\n : (file, scoped) => {\n if (scoped())\n out.push(file)\n })\n return out\n}\n\n/**\n * Detection-preview counts — scoped and all-projects figures from ONE\n * directory walk (the naive two-listing approach reads every project dir\n * twice). The scoped classification (with its per-file cwd peek for\n * candidate dirs) runs at most once per file via the lazy `scoped()`\n * thunk.\n */\nexport function countClaudeSessionFiles(opts: ClaudeImportOptions = {}): { project: number, all: number } {\n let project = 0\n let all = 0\n walkClaudeSessionFiles({ ...opts, allProjects: false }, (_file, scoped) => {\n all++\n if (scoped())\n project++\n }, /* visitAllDirs */ true)\n return { project, all }\n}\n\n/**\n * Shared walk under `~/.claude/projects`. `visit` receives every\n * transcript of every CANDIDATE dir (scoped mode) or every dir\n * (`allProjects` / `visitAllDirs`), plus a lazy `scoped()` thunk that\n * answers \"does this file belong to the current project?\" — lazy because\n * the answer can cost a `transcriptProjectRoot` peek (up to the whole\n * file), which pure all-projects listings never need.\n */\nfunction walkClaudeSessionFiles(\n opts: ClaudeImportOptions,\n visit: (file: ClaudeSessionFile, scoped: () => boolean) => void,\n visitAllDirs = false,\n): void {\n const { projectRoot, home } = roots(opts)\n const projectsDir = resolve(home, '.claude/projects')\n if (!existsSync(projectsDir))\n return\n const slugs = [claudeProjectSlug(projectRoot), claudeProjectSlug(realpathOr(projectRoot))]\n const exact = new Set(slugs)\n const prefixes = [...new Set(slugs.map(s => `${s}-`))]\n const wantedRoots = new Set([projectRoot, realpathOr(projectRoot)])\n for (const entry of readdirSync(projectsDir, { withFileTypes: true })) {\n if (!entry.isDirectory())\n continue\n const exactMatch = exact.has(entry.name)\n const candidate = exactMatch || prefixes.some(p => entry.name.startsWith(p))\n if (!opts.allProjects && !visitAllDirs && !candidate)\n continue\n const projectDir = join(projectsDir, entry.name)\n for (const file of readdirSync(projectDir)) {\n if (!file.endsWith('.jsonl'))\n continue\n const path = join(projectDir, file)\n // Slugs are lossy even on exact match (`/a/b-c` and `/a/b/c` both\n // slug to `-a-b-c`), so EVERY scoped file is confirmed against its\n // recorded cwd. A transcript with no cwd at all (torn/empty file)\n // gets the benefit of the doubt only in the exact-slug dir —\n // prefix matches must prove their subdir claim.\n const scoped = (): boolean => {\n if (!candidate)\n return false\n const root = transcriptProjectRoot(path)\n return root === null ? exactMatch : wantedRoots.has(root) || wantedRoots.has(realpathOr(root))\n }\n visit({ path, projectDir }, scoped)\n }\n }\n}\n\n/**\n * Peek a transcript's project root: scan the first chunk for the first\n * record carrying a `cwd` and resolve it the same way\n * `convertClaudeSession` does. When the chunk was truncated (giant first\n * records — e.g. pasted base64 images) and held no cwd, fall back to the\n * full file: the import reads it in full anyway, so correctness beats the\n * shortcut. Null when no cwd exists at all (empty/torn file) or the file\n * is unreadable.\n */\nfunction transcriptProjectRoot(path: string): string | null {\n let text: string\n let truncated = false\n try {\n const fd = openSync(path, 'r')\n try {\n const buf = Buffer.alloc(256 * 1024)\n const n = readSync(fd, buf, 0, buf.length, 0)\n text = buf.subarray(0, n).toString('utf8')\n truncated = n === buf.length\n }\n finally {\n closeSync(fd)\n }\n }\n catch {\n return null\n }\n const fromChunk = scanForCwd(text)\n if (fromChunk !== null || !truncated)\n return fromChunk\n try {\n return scanForCwd(readFileSync(path, 'utf8'))\n }\n catch {\n return null\n }\n}\n\nfunction scanForCwd(text: string): string | null {\n for (const line of text.split('\\n')) {\n if (!line.trim())\n continue\n try {\n const record = JSON.parse(line) as { cwd?: unknown }\n if (typeof record.cwd === 'string')\n return findGitRoot(record.cwd) ?? record.cwd\n }\n catch {\n continue // torn chunk boundary / artifact line\n }\n }\n return null\n}\n\nfunction realpathOr(path: string): string {\n try {\n return realpathSync(path)\n }\n catch {\n return path\n }\n}\n\nexport interface ConvertedClaudeSession {\n session: SessionData\n claudeSessionId: string\n}\n\ninterface ClaudeRecord {\n type?: string\n uuid?: string\n timestamp?: string\n isSidechain?: boolean\n isMeta?: boolean\n cwd?: string\n sessionId?: string\n customTitle?: string\n summary?: string\n message?: {\n id?: string\n role?: string\n content?: unknown\n usage?: {\n input_tokens?: number\n output_tokens?: number\n cache_creation_input_tokens?: number\n cache_read_input_tokens?: number\n }\n model?: string\n }\n}\n\n/**\n * Convert one Claude Code session transcript (JSONL text) to a zidane\n * `SessionData`. Returns null when no conversational turns survive the\n * filter (hook noise, sidechains, queue bookkeeping).\n *\n * Kept-vs-dropped:\n * - `user` / `assistant` records with a `message` → turns. Consecutive\n * assistant records sharing a `message.id` merge into ONE turn (Claude\n * writes text and tool_use as separate records of the same API message).\n * - Consecutive tool_result-only user records merge into ONE user turn:\n * Claude writes one record per result of a parallel tool batch, but\n * zidane persists a batch as a single user turn and\n * `ensureToolResultPairing` enforces strict adjacency — split turns\n * would be \"repaired\" into orphan markers + error placeholders on resume.\n * - `custom-title` / `summary` records → `metadata.title`.\n * - Sidechains (subagent transcripts), `isMeta` records, `attachment`,\n * `system`, `queue-operation`, `last-prompt` → dropped.\n * - Thinking blocks → dropped (provider-bound signatures, not replayable).\n */\nexport function convertClaudeSession(jsonl: string, opts: { fallbackId?: string } = {}): ConvertedClaudeSession | null {\n const turns: SessionTurn[] = []\n let title: string | undefined\n let claudeSessionId: string | undefined\n let cwd: string | undefined\n let lastAssistantMessageId: string | undefined\n\n for (const line of jsonl.split('\\n')) {\n if (!line.trim())\n continue\n let record: ClaudeRecord\n try {\n record = JSON.parse(line) as ClaudeRecord\n }\n catch {\n continue // torn tail line, editor artifact — skip, don't fail the file\n }\n if (record.type === 'custom-title' && typeof record.customTitle === 'string') {\n title = record.customTitle\n continue\n }\n if (record.type === 'summary' && typeof record.summary === 'string') {\n title ??= record.summary\n continue\n }\n if (record.type !== 'user' && record.type !== 'assistant')\n continue\n if (record.isSidechain || record.isMeta || !record.message)\n continue\n claudeSessionId ??= record.sessionId\n cwd ??= record.cwd\n // Malformed timestamps parse to NaN — don't let that poison\n // createdAt/updatedAt (sort keys in the sessions list).\n const parsedTs = record.timestamp ? Date.parse(record.timestamp) : Number.NaN\n const createdAt = Number.isNaN(parsedTs) ? Date.now() : parsedTs\n\n if (record.type === 'assistant') {\n const content = claudeBlocksToContent(record.message.content)\n if (content.length === 0)\n continue\n const messageId = record.message.id\n const previous = turns[turns.length - 1]\n // Merge continuation records of the same API message into the open\n // turn. Claude repeats the SAME usage object on every record of a\n // message, so the latest snapshot replaces — summing would N-count\n // the message's tokens.\n if (messageId && messageId === lastAssistantMessageId && previous?.role === 'assistant') {\n previous.content.push(...content)\n const usage = claudeUsage(record.message.usage, record.message.model)\n if (usage)\n previous.usage = usage\n continue\n }\n lastAssistantMessageId = messageId\n const usage = claudeUsage(record.message.usage, record.message.model)\n turns.push({\n id: record.uuid ?? crypto.randomUUID(),\n role: 'assistant',\n content,\n createdAt,\n ...(usage ? { usage } : {}),\n })\n continue\n }\n\n lastAssistantMessageId = undefined\n const content = claudeBlocksToContent(record.message.content)\n if (content.length === 0)\n continue\n // One record per tool_result of a parallel batch → one zidane user\n // turn — see the doc comment (ensureToolResultPairing adjacency).\n const previous = turns[turns.length - 1]\n if (previous?.role === 'user' && isToolResultOnly(content) && isToolResultOnly(previous.content)) {\n previous.content.push(...content)\n continue\n }\n turns.push({ id: record.uuid ?? crypto.randomUUID(), role: 'user', content, createdAt })\n }\n\n if (turns.length === 0)\n return null\n // Identity PREFERS the caller's `fallbackId` (the file basename —\n // Claude's own per-file uniqueness key) over the records' `sessionId`:\n // resume/fork lineages copy prior records verbatim, so a forked\n // transcript's early records still carry the ORIGINAL session's id —\n // keying on that would dedupe the fork against its ancestor and\n // silently never import the continuation.\n const id = opts.fallbackId ?? claudeSessionId ?? crypto.randomUUID()\n const projectRoot = cwd ? findGitRoot(cwd) ?? cwd : undefined\n const session: SessionData = {\n id,\n ...(projectRoot ? { projectRoot } : {}),\n turns,\n runs: [],\n status: 'idle',\n metadata: {\n ...(title ? { title } : {}),\n importedFrom: 'claude-code',\n claudeSessionId: id,\n // Lineage breadcrumb: differs from `claudeSessionId` on forked /\n // resumed transcripts whose records carry the ancestor's id.\n ...(claudeSessionId && claudeSessionId !== id ? { claudeLineageId: claudeSessionId } : {}),\n },\n createdAt: turns[0]!.createdAt,\n updatedAt: turns[turns.length - 1]!.createdAt,\n }\n return { session, claudeSessionId: id }\n}\n\n/** Map Claude/Anthropic content blocks to zidane `SessionContentBlock`s. */\nfunction claudeBlocksToContent(content: unknown): SessionContentBlock[] {\n if (typeof content === 'string')\n return content.trim() ? [{ type: 'text', text: content }] : []\n if (!Array.isArray(content))\n return []\n const out: SessionContentBlock[] = []\n for (const raw of content) {\n if (!raw || typeof raw !== 'object')\n continue\n const block = raw as Record<string, unknown>\n switch (block.type) {\n case 'text':\n if (typeof block.text === 'string' && block.text.trim())\n out.push({ type: 'text', text: block.text })\n break\n case 'tool_use':\n if (typeof block.id === 'string' && typeof block.name === 'string') {\n const input = block.input && typeof block.input === 'object' && !Array.isArray(block.input)\n ? block.input as Record<string, unknown>\n : {}\n out.push({ type: 'tool_call', id: block.id, name: block.name, input })\n }\n break\n case 'tool_result':\n if (typeof block.tool_use_id === 'string') {\n out.push({\n type: 'tool_result',\n callId: block.tool_use_id,\n output: claudeToolResultOutput(block.content),\n ...(block.is_error === true ? { isError: true } : {}),\n })\n }\n break\n case 'image': {\n const image = claudeImage(block)\n if (image)\n out.push(image)\n break\n }\n // thinking / redacted_thinking: provider-bound signatures — dropped.\n }\n }\n return out\n}\n\nfunction claudeImage(block: Record<string, unknown>): SessionContentBlock | null {\n const source = block.source as Record<string, unknown> | undefined\n if (!source || source.type !== 'base64' || typeof source.media_type !== 'string' || typeof source.data !== 'string')\n return null\n return { type: 'image', mediaType: source.media_type, data: source.data }\n}\n\nfunction claudeToolResultOutput(content: unknown): string | ToolResultContent[] {\n if (typeof content === 'string')\n return content\n if (!Array.isArray(content))\n return ''\n const parts: ToolResultContent[] = []\n for (const raw of content) {\n if (!raw || typeof raw !== 'object')\n continue\n const block = raw as Record<string, unknown>\n if (block.type === 'text' && typeof block.text === 'string') {\n parts.push({ type: 'text', text: block.text })\n }\n else if (block.type === 'image') {\n const image = claudeImage(block)\n if (image?.type === 'image' && image.data)\n parts.push({ type: 'image', mediaType: image.mediaType, data: image.data })\n }\n }\n // Collapse the common all-text case to a plain string.\n if (parts.every(p => p.type === 'text'))\n return parts.map(p => (p as { text: string }).text).join('\\n')\n return parts\n}\n\ntype ClaudeUsage = NonNullable<NonNullable<ClaudeRecord['message']>['usage']>\n\nfunction claudeUsage(usage: ClaudeUsage | undefined, model?: string): TurnUsage | undefined {\n if (!usage || typeof usage !== 'object')\n return undefined\n return {\n input: usage.input_tokens ?? 0,\n output: usage.output_tokens ?? 0,\n ...(usage.cache_creation_input_tokens ? { cacheCreation: usage.cache_creation_input_tokens } : {}),\n ...(usage.cache_read_input_tokens ? { cacheRead: usage.cache_read_input_tokens } : {}),\n ...(model ? { modelId: model } : {}),\n }\n}\n\nfunction isToolResultOnly(blocks: readonly SessionContentBlock[]): boolean {\n return blocks.length > 0 && blocks.every(b => b.type === 'tool_result')\n}\n\nexport interface ClaudeSessionImportResult {\n imported: number\n /** Already in the store (same Claude session UUID) — idempotent re-run. */\n skipped: number\n /** Nothing conversational in the file (hook noise, queue bookkeeping only). */\n empty: number\n /** Files that failed to parse/convert, with the reason. */\n failed: { path: string, error: string }[]\n}\n\n/** Import Claude Code session transcripts into `store`. */\nexport async function importClaudeSessions(store: SessionStore, opts: ClaudeImportOptions = {}): Promise<ClaudeSessionImportResult> {\n const result: ClaudeSessionImportResult = { imported: 0, skipped: 0, empty: 0, failed: [] }\n for (const file of listClaudeSessionFiles(opts)) {\n try {\n // Session id = the file's basename (Claude names each transcript\n // after ITS session UUID). `convertClaudeSession` prefers this over\n // the records' `sessionId`, which on forked/resumed lineages still\n // carries the ancestor's id — see the identity comment there.\n const fallbackId = basename(file.path, '.jsonl')\n // Idempotence: a store hit on the basename means this transcript\n // already imported — checked BEFORE reading/converting, so a\n // re-import doesn't re-parse the whole Claude history just to\n // count skips.\n if (await store.load(fallbackId)) {\n result.skipped++\n continue\n }\n const converted = convertClaudeSession(readFileSync(file.path, 'utf8'), { fallbackId })\n if (!converted) {\n result.empty++\n continue\n }\n await store.save(converted.session)\n result.imported++\n }\n catch (err) {\n result.failed.push({ path: file.path, error: err instanceof Error ? err.message : String(err) })\n }\n }\n return result\n}\n\n// ---------------------------------------------------------------------------\n// MCP servers\n// ---------------------------------------------------------------------------\n\nexport interface ClaudeCopyResult {\n copied: string[]\n /** Names already present natively — never overwritten. */\n skipped: string[]\n failed: { name: string, error: string }[]\n /**\n * Names that appeared in MORE THAN ONE Claude source (e.g. `.mcp.json`\n * AND `~/.claude.json`) — the first occurrence (highest-precedence\n * source, matching Claude's own project → local → user order) was\n * processed, later ones dropped. Surfaced so a differently-configured\n * duplicate doesn't vanish from the accounting. MCP import only.\n */\n duplicates?: string[]\n}\n\n/**\n * Copy Claude Code's MCP servers into zidane-owned `mcps.json` files.\n * The repo-shared `.mcp.json` writes to `<root>/.{prefix}/mcps.json`;\n * BOTH `~/.claude.json` scopes write to `~/.{prefix}/mcps.json` —\n * Claude's \"local\" scope (the per-project map inside the user state\n * file) is private-by-design and routinely carries tokens in\n * `env`/`headers`, so materializing it into a committable project file\n * would leak credentials. User scope loses the per-project activation\n * (zidane has no user-private per-project config), which is the safer\n * direction to err. Collisions with names in ANY native config path are\n * skipped.\n */\nexport function importClaudeMcps(opts: ClaudeImportOptions = {}): ClaudeCopyResult {\n const { projectRoot, home } = roots(opts)\n const result: ClaudeCopyResult = { copied: [], skipped: [], failed: [], duplicates: [] }\n\n // Collision guard — collected from the RAW file shape, NOT from\n // `parseMcpsFile`: the normalized view drops `enabled: false` entries\n // and can reject a file over one invalid entry, while `upsertMcpServer`\n // walks the raw JSON. Guarding on the normalized view would let the\n // import overwrite a native entry the user disabled (or one sitting\n // next to a malformed sibling). Raw names = exactly what upsert sees,\n // so an import can only ever ADD.\n const nativeNames = new Set<string>()\n for (const candidate of defaultMcpsConfigPaths({ ...(opts.cwd ? { cwd: opts.cwd } : {}), home, ...(opts.prefix ? { prefix: opts.prefix } : {}) })) {\n if (!existsSync(candidate.path))\n continue\n for (const name of rawMcpNames(readFileSync(candidate.path, 'utf8')))\n nativeNames.add(name)\n }\n\n const sources: { servers: McpServerConfig[], scope: 'project' | 'user' }[] = []\n const mcpJson = resolve(projectRoot, '.mcp.json')\n if (existsSync(mcpJson)) {\n try {\n sources.push({ servers: parseMcpsFile(readFileSync(mcpJson, 'utf8')), scope: 'project' })\n }\n catch (err) {\n result.failed.push({ name: mcpJson, error: err instanceof Error ? err.message : String(err) })\n }\n }\n const claudeState = resolve(home, '.claude.json')\n if (existsSync(claudeState)) {\n try {\n const state: unknown = JSON.parse(readFileSync(claudeState, 'utf8'))\n // User scope on purpose — see the doc comment (credential leak).\n sources.push({ servers: parseClaudeState(state, 'claude-project-state', projectRoot), scope: 'user' })\n sources.push({ servers: parseClaudeState(state, 'claude-user-state'), scope: 'user' })\n }\n catch (err) {\n result.failed.push({ name: claudeState, error: err instanceof Error ? err.message : String(err) })\n }\n }\n\n const seen = new Set<string>()\n for (const { servers, scope } of sources) {\n for (const server of servers) {\n if (seen.has(server.name)) {\n // Same name in a lower-precedence source — report, don't vanish:\n // its config may differ from the occurrence that won.\n result.duplicates!.push(server.name)\n continue\n }\n seen.add(server.name)\n if (nativeNames.has(server.name)) {\n result.skipped.push(server.name)\n continue\n }\n try {\n const target = mcpsWritePath(scope, { ...(opts.cwd ? { cwd: opts.cwd } : {}), home, ...(opts.prefix ? { prefix: opts.prefix } : {}) })\n const { action } = upsertMcpServer(target, server)\n if (action === 'updated') {\n // Unreachable while `nativeNames` raw-walks every candidate file\n // (the write targets included) — but if a concurrent writer or a\n // future shape drift ever slips past the guard, say so honestly\n // instead of counting an overwrite as a copy.\n result.failed.push({ name: server.name, error: `overwrote an existing entry in ${target}. Restore it from version control if unintended` })\n continue\n }\n result.copied.push(server.name)\n }\n catch (err) {\n result.failed.push({ name: server.name, error: err instanceof Error ? err.message : String(err) })\n }\n }\n }\n return result\n}\n\n/**\n * Names physically present in an mcps config file, from the raw parsed\n * shape — mirrors `upsertMcpServer`'s walk (flat array `name`s, wrapper\n * `mcpServers` keys, bare-map keys) with none of discovery's filtering.\n * Unreadable/malformed files yield no names; `upsertMcpServer` throws on\n * those before writing, so they still can't be overwritten.\n */\nfunction rawMcpNames(text: string): string[] {\n let raw: unknown\n try {\n raw = parseJsonTolerant(text)\n }\n catch {\n return []\n }\n if (Array.isArray(raw)) {\n return raw.flatMap(entry =>\n entry && typeof entry === 'object' && typeof (entry as { name?: unknown }).name === 'string'\n ? [(entry as { name: string }).name]\n : [])\n }\n if (raw && typeof raw === 'object') {\n const record = raw as Record<string, unknown>\n const map = record.mcpServers && typeof record.mcpServers === 'object' && !Array.isArray(record.mcpServers)\n ? record.mcpServers as Record<string, unknown>\n : record\n return Object.keys(map)\n }\n return []\n}\n\n// ---------------------------------------------------------------------------\n// Skills\n// ---------------------------------------------------------------------------\n\n/**\n * Copy `.claude/skills/<name>` directories (project + user) into the\n * corresponding `.{prefix}/skills`. A skill \"exists natively\" when a\n * directory of the same name sits in any native scan path — those are\n * skipped, not overwritten.\n */\nexport function importClaudeSkills(opts: ClaudeImportOptions = {}): ClaudeCopyResult {\n const { projectRoot, home } = roots(opts)\n const prefix = (opts.prefix ?? '.zidane').replace(/^\\./, '')\n const result: ClaudeCopyResult = { copied: [], skipped: [], failed: [] }\n\n const nativeNames = new Set<string>()\n for (const scan of projectUserPaths({ subPath: 'skills', ...(opts.cwd ? { cwd: opts.cwd } : {}), home, ...(opts.prefix ? { prefix: opts.prefix } : {}) })) {\n if (!existsSync(scan.path))\n continue\n for (const entry of readdirSync(scan.path, { withFileTypes: true })) {\n if (entry.isDirectory())\n nativeNames.add(entry.name)\n }\n }\n\n const pairs: { from: string, to: string }[] = [\n { from: resolve(projectRoot, '.claude/skills'), to: resolve(projectRoot, `.${prefix}/skills`) },\n { from: resolve(home, '.claude/skills'), to: resolve(home, `.${prefix}/skills`) },\n ]\n for (const { from, to } of pairs) {\n if (!existsSync(from))\n continue\n for (const entry of readdirSync(from, { withFileTypes: true })) {\n if (!entry.isDirectory())\n continue\n if (!existsSync(join(from, entry.name, 'SKILL.md')))\n continue\n if (nativeNames.has(entry.name)) {\n result.skipped.push(entry.name)\n continue\n }\n try {\n cpSync(join(from, entry.name), join(to, entry.name), { recursive: true, errorOnExist: true, force: false })\n nativeNames.add(entry.name)\n result.copied.push(entry.name)\n }\n catch (err) {\n result.failed.push({ name: entry.name, error: err instanceof Error ? err.message : String(err) })\n }\n }\n }\n return result\n}\n\n// ---------------------------------------------------------------------------\n// Combined runner\n// ---------------------------------------------------------------------------\n\nexport interface ClaudeImportSummary {\n sessions?: ClaudeSessionImportResult\n mcps?: ClaudeCopyResult\n skills?: ClaudeCopyResult\n /** What is deliberately NOT imported, spelled out for the UI. */\n notes: string[]\n}\n\nexport async function importFromClaudeCode(\n store: SessionStore,\n opts: ClaudeImportOptions & { sessions?: boolean, mcps?: boolean, skills?: boolean } = {},\n): Promise<ClaudeImportSummary> {\n const summary: ClaudeImportSummary = {\n notes: [\n 'CLAUDE.md is read natively, nothing to import.',\n 'Claude permissions/hooks/plugins have no zidane equivalent, not imported.',\n ],\n }\n if (opts.sessions !== false)\n summary.sessions = await importClaudeSessions(store, opts)\n if (opts.mcps !== false)\n summary.mcps = importClaudeMcps(opts)\n if (opts.skills !== false)\n summary.skills = importClaudeSkills(opts)\n return summary\n}\n","/**\n * Prompt autocompletion framework — React hook.\n *\n * The provider contract types + pure span/reference helpers live in\n * `./completion-core` (node-free + react-free, re-exported here for\n * back-compat). This module adds the `useCompletion()` React hook on top.\n *\n * The TUI consumes `useCompletion()` to drive a popover above the textarea.\n * A GUI consumes the same hook to drive a dropdown. The popup component\n * itself only reads `label` + `description` on each item, so provider-\n * specific typing (`TItem`) stays at the registration boundary and never\n * leaks into the renderer.\n */\n\nimport type {\n ActiveTrigger,\n CompletionContext,\n CompletionItem,\n CompletionProvider,\n CompletionReference,\n} from './completion-core'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { applyInsert, collectReferences, findActiveTriggers, mergeSuggestions } from './completion-core'\n\nexport type {\n ActiveTrigger,\n CompletionContext,\n CompletionItem,\n CompletionProvider,\n CompletionReference,\n CompletionSubmitContext,\n} from './completion-core'\nexport { applyInsert, collectReferences, findActiveTrigger, findActiveTriggers, mergeReferences, mergeSuggestions, resolveCompletionSubmit } from './completion-core'\n\n/**\n * Shared sentinel returned by `useCompletion` when no references are\n * present in the buffer. Stable identity lets consumers' `useEffect`\n * dep arrays bail out on chip-less keystrokes — see `useChipHighlights`.\n */\nconst EMPTY_REFS: readonly CompletionReference<unknown>[] = Object.freeze([])\n\n// ---------------------------------------------------------------------------\n// React hook\n// ---------------------------------------------------------------------------\n\n/** State surface returned by `useCompletion()`. */\nexport interface CompletionState<TItem = unknown> {\n /** Active trigger, or null when the cursor doesn't sit in a completable span. */\n active: ActiveTrigger<TItem> | null\n /** Items for the active query. Empty when no trigger is active. */\n items: readonly CompletionItem<TItem>[]\n /** True while the provider's `suggest` promise is pending. */\n loading: boolean\n /** Index of the highlighted item inside `items`. */\n selectedIndex: number\n /** Move the highlight. Wraps at the ends. No-op when `items` is empty. */\n selectNext: () => void\n selectPrev: () => void\n /**\n * Commit the highlighted item. Returns the new text + cursor pair, or\n * `null` when there's nothing to commit (no active trigger, empty list).\n */\n commit: () => { text: string, cursor: number } | null\n /** Force-close the popover until the user types again. */\n dismiss: () => void\n /** All references in the current buffer, regardless of provider. */\n references: readonly CompletionReference<TItem>[]\n}\n\n/**\n * Drive a prompt-completion popover from React state.\n *\n * Inputs are pull-style — the caller owns `text` + `cursor` (typically\n * mirroring an `OpenTUI TextareaRenderable` or a `<textarea>` element) and\n * passes them in each render. Providers are matched by their `trigger`\n * character; the engine picks at most one active provider per cursor\n * position.\n *\n * Asynchronous suggest calls are aborted when the query changes or the\n * popover closes, so providers that hit a backend don't leak.\n */\nexport function useCompletion<TItem = unknown>(\n input: { text: string, cursor: number },\n providers: readonly CompletionProvider<TItem>[],\n options: { maxQueryLength?: number } = {},\n): CompletionState<TItem> {\n const { text, cursor } = input\n // Destructure primitives up front so the `useMemo` dep array reads them\n // by value (not by `options` reference, which is a fresh object literal\n // on every call from most hosts).\n const { maxQueryLength } = options\n // `dismissed` is keyed by the trigger's `span.start` — the buffer\n // position of the `@` / `/` glyph that opened the popover. Staying\n // dismissed *while the user keeps typing into the same trigger span*\n // is the whole point of Esc; a stricter `${text.length}:${cursor}`\n // key re-opened on the very next keystroke, which made the \"esc\n // dismiss\" hint a lie. The effect below resets this when the trigger\n // goes away entirely (user backspaced past `@`) so retyping `@` at\n // the same position works.\n const [dismissedTriggerStart, setDismissedTriggerStart] = useState<number | null>(null)\n\n const rawTrigger = useMemo(\n () => findActiveTriggers(text, cursor, providers, { maxQueryLength }),\n [text, cursor, providers, maxQueryLength],\n )\n\n const activeMulti = useMemo(() => {\n if (!rawTrigger)\n return null\n if (rawTrigger.span.start === dismissedTriggerStart)\n return null\n return rawTrigger\n }, [rawTrigger, dismissedTriggerStart])\n\n // `active` stays a single-provider ActiveTrigger (first bound provider) so\n // the popup's title/commit contract is unchanged; multi-provider merging\n // happens in the suggest effect below and surfaces as `item.section`.\n const active = useMemo<ActiveTrigger<TItem> | null>(\n () => activeMulti\n ? { provider: activeMulti.providers[0], query: activeMulti.query, span: activeMulti.span }\n : null,\n [activeMulti],\n )\n\n // Forget the dismissal once the trigger disappears — otherwise a\n // fresh `@` typed at the same byte position would inherit the prior\n // dismiss and the popover would never come back without scrolling\n // the trigger elsewhere first.\n useEffect(() => {\n if (!rawTrigger)\n setDismissedTriggerStart(null)\n }, [rawTrigger])\n\n const [items, setItems] = useState<readonly CompletionItem<TItem>[]>([])\n const [loading, setLoading] = useState(false)\n const [selectedIndex, setSelectedIndex] = useState(0)\n\n // Cancel any in-flight suggest when the query changes / popover closes.\n const abortRef = useRef<AbortController | null>(null)\n\n useEffect(() => {\n abortRef.current?.abort()\n if (!activeMulti) {\n setItems([])\n setLoading(false)\n setSelectedIndex(0)\n return\n }\n const controller = new AbortController()\n abortRef.current = controller\n const ctx: CompletionContext = { text, cursor }\n let cancelled = false\n const outs = activeMulti.providers.map(provider => ({\n provider,\n out: provider.suggest(activeMulti.query, ctx, controller.signal),\n }))\n // Sync fast path — every provider returned synchronously (the common\n // single-provider case, e.g. `@` files). Merge without a loading flash.\n if (outs.every(o => Array.isArray(o.out))) {\n setItems(mergeSuggestions(outs.map(o => ({ provider: o.provider, items: o.out as CompletionItem<TItem>[] })), activeMulti.providers.length))\n setSelectedIndex(0)\n setLoading(false)\n return\n }\n setLoading(true)\n // allSettled, not all: with several providers on one trigger, a single\n // provider whose async suggest rejects (e.g. a failed catalog scan) must\n // not blank its siblings' results. Fulfilled groups merge; rejected ones\n // are dropped. (All rejecting → empty items, matching the old behavior.)\n void Promise.allSettled(outs.map(async o => ({ provider: o.provider, items: await o.out }))).then(\n (results) => {\n if (cancelled)\n return\n setItems(mergeSuggestions(results.flatMap(r => r.status === 'fulfilled' ? [r.value] : []), activeMulti.providers.length))\n setSelectedIndex(0)\n setLoading(false)\n },\n )\n return () => {\n cancelled = true\n controller.abort()\n }\n }, [activeMulti, text, cursor])\n\n // References don't depend on `cursor` — both built-in providers ignore\n // `ctx.cursor` in `parseReferences`, and the consumers (chip highlighting,\n // submit-time activation) only care about positions of references, not\n // about where the caret happens to sit. Dropping `cursor` from the dep\n // array avoids re-walking every provider regex on each arrow-key press.\n // `cursor` is still forwarded so a future provider that wants the cursor\n // can opt in — at the cost of choosing its own memoization strategy.\n //\n // Empty results collapse to a shared `EMPTY_REFS` constant so the prompt\n // textarea's `useChipHighlights` effect doesn't fire on every keystroke\n // of a chip-less buffer (each keystroke would otherwise allocate a fresh\n // `[]`, change identity, and trigger a `clearAllHighlights` repaint).\n const references = useMemo(\n () => {\n const refs = collectReferences(text, providers, cursor)\n return refs.length === 0 ? (EMPTY_REFS as readonly CompletionReference<TItem>[]) : refs\n },\n [text, providers],\n )\n\n const selectNext = useCallback(() => {\n setSelectedIndex(i => (items.length === 0 ? 0 : (i + 1) % items.length))\n }, [items.length])\n const selectPrev = useCallback(() => {\n setSelectedIndex(i => (items.length === 0 ? 0 : (i - 1 + items.length) % items.length))\n }, [items.length])\n const dismiss = useCallback(() => {\n // Pull from `rawTrigger`, not `active`, so dismiss-while-dismissed\n // (a no-op now but reasonable to support) keeps the right anchor.\n if (rawTrigger)\n setDismissedTriggerStart(rawTrigger.span.start)\n }, [rawTrigger])\n const commit = useCallback((): { text: string, cursor: number } | null => {\n if (!active || items.length === 0)\n return null\n const item = items[Math.min(selectedIndex, items.length - 1)]\n return applyInsert(text, active.span, item.insertText)\n }, [active, items, selectedIndex, text])\n\n // Memoize the returned bag so consumers placing it in a `useEffect`\n // dep array don't observe a fresh reference every render. Same pattern\n // (and rationale) as `useStreamBuffer.return` — see streaming.ts.\n return useMemo<CompletionState<TItem>>(\n () => ({ active, items, loading, selectedIndex, selectNext, selectPrev, commit, dismiss, references }),\n [active, items, loading, selectedIndex, selectNext, selectPrev, commit, dismiss, references],\n )\n}\n","/**\n * Completion provider for session artifacts. Trigger: `#` (shared with the\n * chats provider — the renderer groups both under one popup).\n *\n * `#pr` → \"acme/widgets#42 · open\" → inserts the PR URL. Unlike chats there\n * is no token grammar: the inserted text IS the locator (URL, `git:…`,\n * path), which the model can act on directly and a human can read. No\n * `parseReferences` — a pasted URL is not a reference to resolve.\n *\n * Catalog getter is host-owned and sync; the host keeps it fresh off the\n * `artifact:recorded` / `artifact:changed` signals the same way it keeps\n * the chats catalog. `ensureCatalog` covers the cold first `#` keystroke.\n */\n\nimport type { ExtensionArtifactRow } from '../extensions/types'\nimport type { SessionArtifact } from '../session/artifacts'\nimport type { CompletionItem, CompletionProvider } from './completion-core'\nimport { Fzf } from 'fzf'\nimport { ageString } from './format'\n\nexport const ARTIFACTS_COMPLETION_PROVIDER_ID = 'artifacts'\n\nexport interface ArtifactReferenceEntry {\n artifact: SessionArtifact\n /** Pre-rendered row (`renderArtifactRow`) — kind registry stays host-side. */\n row: ExtensionArtifactRow\n /** Human kind label for the description (`Pull request`). */\n kindLabel: string\n}\n\nexport interface ArtifactReferenceData {\n entry: ArtifactReferenceEntry\n /** What gets inserted: `row.href` when present, else the raw `uri`. */\n locator: string\n}\n\nconst DEFAULT_RESULT_LIMIT = 30\n\n/** Kind → short query aliases so `#pr` and `#branch` hit without typing the label. */\nconst KIND_ALIASES: Record<string, readonly string[]> = {\n pull_request: ['pr', 'pull', 'mr'],\n branch: ['branch', 'br'],\n commit: ['commit', 'sha'],\n file: ['file', 'path'],\n}\n\nfunction locatorFor(entry: ArtifactReferenceEntry): string {\n return entry.row.href ?? entry.artifact.uri\n}\n\nfunction haystack(entry: ArtifactReferenceEntry): string {\n const aliases = KIND_ALIASES[entry.artifact.kind] ?? [entry.artifact.kind]\n return [entry.row.title, entry.row.subtitle ?? '', entry.artifact.label, entry.kindLabel, ...aliases].join(' ')\n}\n\nfunction descriptionFor(entry: ArtifactReferenceEntry): string {\n const bits = [entry.kindLabel]\n if (entry.row.status)\n bits.push(entry.row.status)\n bits.push(ageString(entry.artifact.updatedAt))\n const loc = locatorFor(entry)\n if (loc !== entry.row.title)\n bits.push(loc)\n return bits.join(' · ')\n}\n\nexport function scoreArtifacts(\n catalog: readonly ArtifactReferenceEntry[],\n query: string,\n limit = DEFAULT_RESULT_LIMIT,\n): CompletionItem<ArtifactReferenceData>[] {\n const q = query.trim().toLowerCase()\n const fuzzy = q.length > 0\n ? new Set(new Fzf(catalog, { selector: haystack, casing: 'case-insensitive' }).find(q).map(r => r.item))\n : null\n const scored: { entry: ArtifactReferenceEntry, rank: number }[] = []\n for (const entry of catalog) {\n if (q.length === 0) {\n scored.push({ entry, rank: 5 })\n continue\n }\n const title = entry.row.title.toLowerCase()\n const aliases = (KIND_ALIASES[entry.artifact.kind] ?? [entry.artifact.kind]).map(a => a.toLowerCase())\n const kindLabel = entry.kindLabel.toLowerCase()\n if (aliases.some(a => a === q || a.startsWith(q)) || kindLabel.startsWith(q)) {\n scored.push({ entry, rank: 0 })\n continue\n }\n if (title.startsWith(q)) {\n scored.push({ entry, rank: 1 })\n continue\n }\n if (title.includes(q) || locatorFor(entry).toLowerCase().includes(q)) {\n scored.push({ entry, rank: 2 })\n continue\n }\n if (fuzzy?.has(entry))\n scored.push({ entry, rank: 4 })\n }\n scored.sort((a, b) => a.rank - b.rank || b.entry.artifact.updatedAt - a.entry.artifact.updatedAt)\n return scored.slice(0, limit).map<CompletionItem<ArtifactReferenceData>>(({ entry }) => {\n const locator = locatorFor(entry)\n return {\n id: `artifact:${entry.artifact.id}`,\n label: entry.row.title,\n description: descriptionFor(entry),\n insertText: `${locator} `,\n data: { entry, locator },\n }\n })\n}\n\nexport function createArtifactsCompletionProvider(opts: {\n getCatalog: () => readonly ArtifactReferenceEntry[]\n ensureCatalog?: () => Promise<readonly ArtifactReferenceEntry[]>\n limit?: number\n}): CompletionProvider<ArtifactReferenceData> {\n const limit = opts.limit ?? DEFAULT_RESULT_LIMIT\n return {\n id: ARTIFACTS_COMPLETION_PROVIDER_ID,\n trigger: '#',\n label: 'Artifacts',\n suggest(query) {\n const current = opts.getCatalog()\n if (current.length > 0 || !opts.ensureCatalog)\n return scoreArtifacts(current, query, limit)\n return opts.ensureCatalog().then(loaded => scoreArtifacts(loaded, query, limit))\n },\n // Inserted text is a plain locator, not a token: nothing to parse back.\n parseReferences: () => [],\n }\n}\n","/**\n * User-level config at `~/.{prefix}/config.json`. Holds global flags\n * that need to persist across projects — distinct from `state.json`\n * which can live PER-PROJECT under `<git-root>/.{prefix}/`.\n *\n * Single field today (`projectDb` opt-out); easy to grow without\n * threading every new flag through `runTui()`'s call site.\n *\n * Tolerant on disk errors: missing file → empty config (defaults\n * apply), malformed JSON → empty config (logged under `ZIDANE_DEBUG`).\n * Never throws on read — the host's bootstrap path can't afford it.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { errorMessage } from '../errors'\n\nexport interface UserConfig {\n /**\n * Master switch for project-scoped storage. Off by default; when set\n * to `true`, launching inside a git repo auto-creates\n * `<git-root>/.{prefix}/` and stores `sessions.db` + `state.json`\n * there. When absent or `false`, everything stays at the user level\n * (`~/.{prefix}/`) regardless of git presence — the classic\n * single-store behavior. Overridden by `ZIDANE_PROJECT_DB` and the\n * host's `ChatOptions.projectDb` (see `resolveStoragePaths`).\n */\n projectDb?: boolean\n}\n\n/**\n * Build the absolute path to `~/.{prefix}/config.json` given a user\n * directory (`<storageDir>/<prefix>`). Exported so callers debugging a\n * \"why isn't my override taking effect?\" can print the resolved path.\n */\nexport function userConfigPath(userDir: string): string {\n return resolve(userDir, 'config.json')\n}\n\n/**\n * Read + parse the user config file. Returns an empty config (defaults\n * apply) on any failure — the function never throws. Set `ZIDANE_DEBUG`\n * to surface parse failures to stderr.\n */\nexport function loadUserConfig(userDir: string): UserConfig {\n const path = userConfigPath(userDir)\n if (!existsSync(path))\n return {}\n try {\n const raw = readFileSync(path, 'utf8')\n const parsed = JSON.parse(raw)\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n return {}\n return normalizeUserConfig(parsed as Record<string, unknown>)\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] user-config: failed to read \"${path}\": ${errorMessage(err)}\\n`)\n return {}\n }\n}\n\n/**\n * Narrow an arbitrary JSON object to the `UserConfig` shape. Drops\n * unknown keys (so a future zidane version's config field doesn't\n * leak into the typed surface) and validates types per-field.\n */\nfunction normalizeUserConfig(raw: Record<string, unknown>): UserConfig {\n const out: UserConfig = {}\n if (typeof raw.projectDb === 'boolean')\n out.projectDb = raw.projectDb\n return out\n}\n","import type { ExtensionConfigCategory, ExtensionRegistry, StoredExtensionConfigItem } from '../extensions'\nimport type { Preset } from '../presets'\nimport type { Provider } from '../providers'\nimport type { SessionStore } from '../session'\nimport type { StartLocalOptions } from '../start/shared-options'\nimport type { SubagentOverride, SubagentRegistry } from '../tools'\nimport type { AgentRegistry } from './agents'\nimport type { ProviderAuth, ProviderKey } from './auth'\nimport type { KeyBindings, ResolvedExtensionKeybinding } from './keybindings'\nimport type { ModelInfo, ProviderDescriptor } from './providers'\nimport type { StateStoreApi, TuiState } from './store'\nimport type { Picked, Settings } from './types'\nimport type { StorageMode } from './xdg'\nimport { existsSync, mkdirSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { resolve } from 'node:path'\nimport { errorMessage } from '../errors'\nimport { buildExtensionPreset, EMPTY_EXTENSION_REGISTRY } from '../extensions'\nimport { migrateSessionArtifactDirs } from '../loop-persistence'\nimport { BUILTIN_AGENTS, DEFAULT_AGENT_ID, resolveAgentId, singleAgentRegistry } from './agents'\nimport { detectAuth } from './auth'\nimport { applyApiKeyEnv, credentialsPath } from './credentials'\nimport { mergeKeybindings, readKeybindingsRaw, resolveExtensionKeybindings } from './keybindings'\nimport { restoreChainFallbacks } from './model-chain'\nimport { findGitRoot } from './project-root'\nimport { BUILTIN_PROVIDERS, modelsForDescriptor, modelSupportsReasoning, restoreModelOptions } from './providers'\nimport { createStateStore } from './store'\nimport { loadUserConfig } from './user-config'\nimport { resolveStorageDirs } from './xdg'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Re-exported for callers who want `ModelInfo` from the same module as `ChatOptions`. */\nexport type { ModelInfo }\n\n/**\n * Provider registry — a map keyed by `descriptor.key`. Passed verbatim to the\n * chat engine; there is no implicit merge with built-ins. Hosts that want the\n * four default providers spread {@link BUILTIN_PROVIDERS}; hosts that only\n * want their own pass exactly their own.\n */\nexport type ProviderRegistry = Readonly<Record<string, ProviderDescriptor>>\n\n/**\n * Options accepted by `resolveConfig()` (and, transitively, by `runTui()` and\n * any future GUI launcher). Every field is optional — defaults boot a working\n * chat against the built-in providers with sessions stored under `~/.zidane/`.\n */\nexport interface ChatOptions {\n /**\n * Directory name used under `storageDir` for sessions + state.\n * Default: `'.zidane'` → `~/.zidane/sessions.db` + `~/.zidane/state.json`.\n * Pass e.g. `'.myapp'` for `~/.myapp/...`, or `'myapp'` for a visible dir.\n */\n prefix?: string\n /**\n * Storage root. Defaults to the user's home directory. Combined with\n * `prefix` to form the data directory.\n */\n storageDir?: string\n /**\n * Provider registry. Each value is a {@link ProviderDescriptor} carrying\n * label + factory + credential metadata. **No automatic merge** — hosts\n * pass exactly what they want.\n *\n * - Omitted → {@link BUILTIN_PROVIDERS} (anthropic + openai + openrouter + cerebras).\n * - `providers: BUILTIN_PROVIDERS` → same as omitted, but explicit.\n * - `providers: { anthropic: anthropicDescriptor }` → only Anthropic.\n * - `providers: { ...BUILTIN_PROVIDERS, mine: customDescriptor }` → built-ins + custom.\n * - `providers: { mine: customDescriptor }` → custom-only, **no built-ins**.\n */\n providers?: ProviderRegistry\n /**\n * Legacy single-agent preset (tools, system prompt, behavior). When set\n * **without** {@link ChatOptions.agents}, the resolver wraps this preset\n * into a one-entry {@link AgentRegistry} keyed `default` so existing\n * callers keep working and the picker is effectively hidden.\n *\n * Prefer `agents` for any multi-profile setup. Passing both throws.\n */\n preset?: Preset\n /**\n * Agent profile registry — keyed by `profile.id`. Each profile bundles a\n * preset with display metadata (label, description, accent). The TUI\n * shows a picker (Ctrl+A) when more than one profile is registered.\n *\n * - Omitted (and `preset` omitted) → {@link BUILTIN_AGENTS} (Build + Plan).\n * - Omitted but `preset` provided → single-entry registry around the preset.\n * - Provided → host owns the registry; pass exactly what you want.\n *\n * Hosts mixing built-ins with their own typically spread:\n * `agents: { ...BUILTIN_AGENTS, debug: myDebugProfile }`.\n */\n agents?: AgentRegistry\n /**\n * Id of the profile every fresh session (and the initial launch) starts\n * in. Resolved against `agents`; unknown ids fall through to\n * `DEFAULT_AGENT_ID`, then the registry's first key. Sessions remember\n * the profile they were created with; this is never overridden by a\n * previously picked profile.\n */\n defaultAgent?: string\n /**\n * Session store — either an instance or a factory called with the\n * resolved storage paths. **Required** at the chat layer so this module\n * stays platform-agnostic (no `bun:sqlite` baked in). Terminal hosts use\n * `createTuiStore` from `zidane/session/sqlite`; a future GUI host can\n * supply a different adapter (IndexedDB, remote API, in-memory) without\n * touching this layer.\n *\n * ```ts\n * import { createTuiStore } from 'zidane/session/sqlite'\n *\n * // Either pass a factory…\n * resolveConfig({ store: paths => createTuiStore(paths.db) })\n *\n * // …or an instance built ahead of time.\n * const store = createTuiStore('/path/to/sessions.db')\n * resolveConfig({ store })\n * ```\n */\n store: SessionStore | ((paths: ResolvedPaths) => SessionStore)\n /**\n * Project-scoped session storage. Off by default; when it resolves\n * to `true` and the launch happens inside a git repo, zidane\n * auto-creates `<git-root>/.{prefix}/` and stores `sessions.db` +\n * `state.json` there — so sessions, mcps.json, and skills all live\n * alongside the project they belong to. Credentials and the\n * cross-project safelist stay at `~/.{prefix}/` regardless.\n *\n * Precedence (most → least specific): `options.projectDb` >\n * `ZIDANE_PROJECT_DB` env var (`0`/`false`/`off`/`no` → off,\n * `1`/`true`/`on`/`yes` → on) > `~/.{prefix}/config.json`\n * `projectDb` > `false`. Note the host option beats the env var —\n * setting `ZIDANE_PROJECT_DB` has no effect when the host passes an\n * explicit `projectDb`.\n *\n * When it resolves to `false`, OR when not inside a git repo,\n * everything lives at `~/.{prefix}/` — the classic single-store\n * layout.\n */\n projectDb?: boolean\n /**\n * Override for the project root. Defaults to `process.cwd()`. Hosts\n * embedding the TUI from a non-CLI context (tests, daemons) can pin\n * the cwd here so the git-root walk produces a stable result. Has no\n * effect when `projectDb` resolves to `false`.\n */\n cwd?: string\n /**\n * Optional one-shot launch request used by CLI shells such as `zidane run`.\n * The TUI opens/creates the requested session and submits the prompt after\n * the first provider/session activation, while keeping the normal renderer\n * and persistence behavior.\n */\n launch?: StartLocalOptions\n /**\n * Auto-update wiring. When set, the App quietly checks the npm registry\n * for a newer release of `packageName` (matching the host's package),\n * caches the result for a day under `userDir/`, and surfaces a passive\n * `↑ vX.Y.Z` chip in the footer when one is available.\n *\n * Consumers wrapping `runTui` for their own CLI inline their own\n * `package.json` version at build time and pass it here. The bundled\n * `zidane-tui` binary wires this automatically in `tui/src/cli.ts`.\n *\n * Defaults to `null` — no auto-update plumbing, no network calls,\n * no chip.\n */\n autoUpdate?: AutoUpdateConfig | null\n /**\n * Pre-built {@link ExtensionRegistry}. The `resolveConfig` surface is\n * synchronous; extension `setup()` can be async, so the host must\n * build the registry ahead of time and pass it here verbatim.\n *\n * Typical wiring (matches `runTui`):\n *\n * ```ts\n * import {\n * buildExtensionRegistry,\n * discoverExtensions,\n * inlineLoadedExtension,\n * resolveConfig,\n * } from 'zidane'\n *\n * const registry = await buildExtensionRegistry([\n * // Project-scoped extensions run arbitrary local code — they stay\n * // off unless the host establishes trust explicitly (or the user\n * // sets ZIDANE_TRUST_PROJECT_EXTENSIONS=1).\n * ...await discoverExtensions({ cwd, prefix, trustProjectExtensions: hostTrustsProject }),\n * ...inlineDefinitions.map(inlineLoadedExtension),\n * ])\n * const config = resolveConfig({ ...opts, extensionRegistry: registry })\n * ```\n *\n * Omitted → empty registry (no contributions).\n */\n extensionRegistry?: ExtensionRegistry\n /**\n * Extra extension scan roots the host used (or wants runtime re-discovery\n * to use). Each path is a root containing extension dirs/packages:\n * `<root>/<name>/index.ts`. `runTui` wires this from\n * `ZIDANE_EXTENSION_DIRS` when omitted.\n */\n extensionScanPaths?: readonly string[]\n /**\n * Extra Agent Plugin scan roots. Each path is a root containing plugin\n * directories: `<root>/<name>/plugin.json`. `runTui` wires this from\n * `ZIDANE_PLUGIN_DIRS` when omitted.\n *\n * Appended after the four default roots, so a project or user plugin still\n * wins a name collision against a dev root.\n */\n pluginScanPaths?: readonly string[]\n /**\n * Whether project-scoped extensions are trusted to run their (arbitrary,\n * local) code. Off by default unless the embedding host has established\n * project trust; falls back to `ZIDANE_TRUST_PROJECT_EXTENSIONS=1`.\n *\n * Surfaced on {@link ResolvedConfig.trustProjectExtensions} so a host\n * that re-discovers extensions at runtime (e.g. the TUI on a working-\n * directory change) reuses the same trust decision as boot.\n */\n trustProjectExtensions?: boolean\n}\n\n/**\n * Host-supplied auto-update settings. The check itself is gated on\n * `Settings.checkForUpdates`; passing a config wires the React plumbing.\n *\n * Hosts that want to publish their own consumer-facing package can pass\n * different values without forking anything — the registry / channel /\n * platform-package-prefix are all knobs.\n */\nexport interface AutoUpdateConfig {\n /** Top-level npm package the user installs (`zidane-tui`, `my-app`, …). */\n packageName: string\n /** Inlined at build time — usually `pkg.version`. */\n currentVersion: string\n /** dist-tag. Defaults to `'latest'`. */\n channel?: string\n /** Registry base URL. Defaults to `'https://registry.npmjs.org'`. */\n registry?: string\n /**\n * Override the read-through cache lifetime in ms for the in-TUI hook.\n * Defaults to `0` — every TUI launch fetches fresh, so the chip\n * never lies. Pass a positive value (e.g. `3_600_000` for 1 h) to\n * throttle the registry-poll rate at the cost of staleness.\n *\n * The on-disk cache is always WRITTEN, regardless of this value, so\n * `zidane upgrade` and other tooling read paths benefit immediately.\n */\n cacheTtlMs?: number\n /**\n * Platform-package prefix used by the in-place self-update flow when\n * resolving the per-arch tarball name. Defaults to `'<packageName>-'`\n * (i.e. `zidane-tui-darwin-arm64` when `packageName === 'zidane-tui'`).\n * Override only when your platform packages don't follow the `<name>-<key>`\n * convention.\n */\n platformPackagePrefix?: string\n /**\n * Executable name on PATH for `packageName`, used to probe the version\n * installed on disk (the `↻ vX` \"restart to update\" chip). Defaults to\n * `packageName`; the TUI passes `'zidane'` for `zidane-tui`.\n */\n binName?: string\n}\n\n/** Resolved on-disk layout produced by {@link resolveConfig}. */\nexport interface ResolvedPaths {\n /**\n * Effective data root — equals `userDir` (configDir) when project\n * mode is off, `projectDir` when it's on. Kept for callers that\n * wrote against the pre-project-db API; new code should reference\n * the explicit slots (`configDir` / `dataDir` / `cacheDir` /\n * `stateDir`) or `projectDir` / `db` / `state` instead.\n */\n dir: string\n /**\n * User-scoped config root — credentials, mcp-credentials, keybindings,\n * `config.json`, `mcps.json`, skills. Legacy alias for `configDir`;\n * existing call sites still reference `userDir`. In XDG mode this\n * lands at `$XDG_CONFIG_HOME/zidane`; in legacy mode the four slots\n * (config / data / state / cache) collapse to a single\n * `<storageDir>/<prefix>` directory.\n */\n userDir: string\n /**\n * Project-scoped data root. Set to `<git-root>/<prefix>` when project\n * mode resolved to ON and a git root was found. `null` otherwise.\n */\n projectDir: string | null\n /** Resolved `sessions.db` path. Lives under `projectDir` when active, else `dataDir`. */\n db: string\n /** Resolved `state.json` path. Lives under `projectDir` when active, else `stateDir`. */\n state: string\n /**\n * XDG config slot — `$XDG_CONFIG_HOME/zidane` on a fresh Linux install,\n * `<storageDir>/<prefix>` (e.g. `~/.zidane/`) in legacy mode. Holds\n * user-editable surface (`credentials.json`, `mcps.json`, …).\n */\n configDir: string\n /**\n * XDG data slot — `$XDG_DATA_HOME/zidane` on Linux, single-dir\n * elsewhere. Holds long-lived chat history (`sessions.db`).\n */\n dataDir: string\n /**\n * XDG state slot — `$XDG_STATE_HOME/zidane` on Linux, single-dir\n * elsewhere. Holds per-machine UI bookkeeping (`state.json`).\n */\n stateDir: string\n /**\n * XDG cache slot — `$XDG_CACHE_HOME/zidane` on Linux, single-dir\n * elsewhere. Holds regenerable data: persisted tool results,\n * background-task logs, auto-update registry cache.\n */\n cacheDir: string\n /**\n * Which resolution branch produced the layout. `'xdg'` when split\n * across the four XDG dirs; one of `'legacy-explicit'`,\n * `'legacy-existing'`, or `'default'` when the slots collapse to a\n * single directory. Surfaced for diagnostics + tests.\n */\n storageMode: StorageMode\n}\n\n// ---------------------------------------------------------------------------\n// Resolved config\n// ---------------------------------------------------------------------------\n\nexport interface ResolvedConfig {\n prefix: string\n storageDir: string\n paths: ResolvedPaths\n providers: ProviderRegistry\n /**\n * Resolved profile registry. Always non-empty; the resolver throws when\n * the host passes an empty `agents` map (same contract as `providers`).\n */\n agents: AgentRegistry\n /** Id of the profile to activate on mount. Guaranteed to key into `agents`. */\n initialAgentId: string\n store: SessionStore\n stateStore: StateStoreApi\n /** Lookup by `ProviderKey` returning the available models for the picker. */\n modelsFor: (key: ProviderKey) => readonly ModelInfo[]\n initialState: TuiState\n initialSettings: Partial<Settings>\n resumeProvider: ProviderAuth | null\n initialPicked: Picked | null\n /**\n * Effective keybindings — defaults merged with the user-level\n * `<userDir>/keybindings.json` (when present). Always fully-resolved\n * so consumers can treat the record as total. See `./keybindings.ts`\n * for the action catalog + spec format.\n */\n keybindings: KeyBindings\n /**\n * Resolved extension-contributed keybindings. Each entry pairs the\n * extension's binding definition with the user's effective spec\n * (override from `keybindings.json` when present, default\n * otherwise). The TUI dispatcher iterates these alongside the\n * built-in action catalog; the GUI host can do the same.\n *\n * Empty when no extension contributed a keybinding.\n */\n extensionKeybindings: readonly ResolvedExtensionKeybinding[]\n /**\n * Resolved auto-update knobs (forwarded verbatim from `ChatOptions.autoUpdate`)\n * — `null` when the host didn't wire one. The TUI feeds this into\n * `useUpdateCheck` so the footer chip + the `zidane upgrade` subcommand\n * share a single source of truth.\n */\n autoUpdate: AutoUpdateConfig | null\n /**\n * Merged extension contributions — the registry passed via\n * {@link ChatOptions.extensionRegistry}, returned verbatim, or\n * {@link EMPTY_EXTENSION_REGISTRY} when the host didn't pass one.\n * `resolveConfig` is synchronous, so it never runs `setup()` itself;\n * the host pre-builds the registry with `buildExtensionRegistry`.\n *\n * Renderer-side wiring reads `tuiContributions` / `guiContributions`\n * to mount keybindings / footer hints / modals / panels. The\n * `extensionPreset` field below carries the agent-side surface\n * (tools / hooks / mcpServers / skills) ready to spread.\n */\n extensions: ExtensionRegistry\n /**\n * Extra extension scan roots used at boot. Runtime re-discovery (TUI cwd\n * changes, future hosts) feeds these back into the shared discovery helper so\n * local/dev package roots stay active after the initial launch.\n */\n extensionScanPaths: readonly string[]\n /**\n * Extra Agent Plugin scan roots used at boot. Threaded into the discovery\n * shell so a dev root stays active across project/cwd changes, same as\n * {@link ResolvedConfig.extensionScanPaths}.\n */\n pluginScanPaths: readonly string[]\n /**\n * Single {@link Preset} folded together from every extension's\n * agent-side contributions. Hosts spread it into `createAgent()`\n * alongside the active profile's preset:\n *\n * ```ts\n * const profile = composePresets(extensionPreset, agentProfile.preset)\n * const agent = createAgent({ ...profile, provider })\n * ```\n *\n * Extension hooks are bucketed per event so every handler fires;\n * tools and mcpServers compose with profile contributions via the\n * usual `composePresets` rules. System-prompt fragments land as a\n * trailing block on `preset.system` (host profiles that re-derive\n * `system` per session — Build / Plan — append the fragments via\n * the `system:transform` hook so the live system always carries\n * them).\n */\n extensionPreset: Preset\n /**\n * System-prompt fragments contributed by extensions, in registration\n * order. Surfaced separately from {@link extensionPreset} so hosts\n * that re-derive `system` per session (Build / Plan in the TUI) can\n * append them on `system:transform` instead of trying to merge two\n * `preset.system` strings.\n */\n extensionSystemFragments: readonly string[]\n /**\n * Spawnable subagents (adds) contributed by extensions, keyed by name.\n * Surfaced separately from {@link extensionPreset} (like\n * {@link extensionSystemFragments}) because subagents don't ride the\n * preset path — the host merges them onto the spawn tool's base\n * registry via `spawn.withSubagents(...)` at agent build time.\n */\n extensionSubagents: SubagentRegistry\n /**\n * Subagent edits contributed by extensions, keyed by target name and\n * composed in registration order. Applied (after the adds above) by\n * the host's `spawn.withSubagents(...)` call so extensions can edit or\n * delete host- / extension-provided subagents by name.\n */\n extensionSubagentOverrides: Record<string, SubagentOverride>\n /**\n * Merged extension-contributed config categories, keyed by category id.\n * Surfaced (alongside {@link extensionConfigItems}) so the TUI can read\n * the schema in-process — same way it reads `extensions.themes` — and\n * the GUI's `extensions:configSchema` IPC can serialize it.\n */\n extensionConfigCategories: Readonly<Record<string, ExtensionConfigCategory>>\n /**\n * Merged extension-contributed config items, namespaced\n * `extName → itemKey → item`. Each item carries its resolved\n * `category`; values are read default-first via the core resolver.\n */\n extensionConfigItems: Readonly<Record<string, Readonly<Record<string, StoredExtensionConfigItem>>>>\n /**\n * Resolved project-extension trust decision (option + env fallback).\n * Hosts that re-discover extensions at runtime feed this back into\n * {@link discoverAndBuildExtensionRegistry} so the rebuild trusts the\n * same scopes boot did.\n */\n trustProjectExtensions: boolean\n /**\n * Raw user `keybindings.json` object (post-JSONC-strip). Surfaced so a\n * host re-resolving {@link extensionKeybindings} at runtime (after an\n * extension registry rebuild) can reuse the same override source\n * without a second disk read.\n */\n rawKeybindings: Readonly<Record<string, unknown>>\n /** One-shot launch request forwarded from {@link ChatOptions.launch}. */\n launch: StartLocalOptions | null\n}\n\n/**\n * Resolve user options into a fully-bound runtime config.\n *\n * NOT pure: besides disk reads, this mutates `process.env` so the harness\n * providers pick up the chat engine's credential store — it sets\n * `ZIDANE_CREDENTIALS_PATH` and reconciles stored API keys / OAuth entries\n * into env via `applyApiKeyEnv` (which can overwrite or delete ambient keys\n * like `ANTHROPIC_API_KEY`; see its JSDoc for the precedence rationale).\n */\nexport function resolveConfig(options: ChatOptions): ResolvedConfig {\n const prefix = options.prefix ?? process.env.ZIDANE_PREFIX ?? '.zidane'\n // Distinguish \"user pinned storageDir\" (legacy-explicit mode) from\n // \"default behavior\" (let XDG kick in on Linux). Only the explicit\n // value is forwarded to the resolver; `ResolvedConfig.storageDir`\n // below falls back to `homedir()` for display / back-compat callers\n // that read it.\n const explicitStorageDir = options.storageDir ?? process.env.ZIDANE_STORAGE_DIR\n const storageDir = explicitStorageDir ?? homedir()\n const paths = resolveStoragePaths({\n prefix,\n ...(explicitStorageDir !== undefined ? { storageDir: explicitStorageDir } : {}),\n cwd: options.cwd ?? process.cwd(),\n projectDb: options.projectDb,\n })\n\n // Fold older artifact layouts into the session-first\n // `<cacheDir>/sessions/<sessionId>/<kind>/` shape the resolvers now emit —\n // otherwise a long-lived install keeps hundreds of per-session directories at\n // the top level of the storage dir forever. Sync, best-effort, idempotent\n // (see `migrateSessionArtifactDirs`): after the first run it costs one\n // readdir, and a failure can't break the bootstrap.\n const migration = migrateSessionArtifactDirs({ userDir: paths.cacheDir })\n if (process.env.ZIDANE_DEBUG && (migration.moved > 0 || migration.failed > 0)) {\n process.stderr.write(\n `[zidane/chat] session-dir migration: moved ${migration.moved}, removed ${migration.removed}, failed ${migration.failed}\\n`,\n )\n }\n\n // `options.store` is required at the chat layer — the renderer (TUI, GUI)\n // injects an adapter so this module stays free of platform-specific\n // session-storage code. Accepts an instance or a factory; the factory\n // form lets callers wire their adapter to the resolved storage paths\n // without duplicating the prefix/storageDir logic above.\n if (!options.store)\n throw new Error('resolveConfig: `store` is required. Pass a `SessionStore` instance, or a factory — terminal hosts can use `createTuiStore` from `zidane/session/sqlite`.')\n const store: SessionStore = typeof options.store === 'function'\n ? options.store(paths)\n : options.store\n const stateStore = createStateStore(paths.state)\n const initialState = stateStore.load()\n\n // Extension registry — host pre-builds via `buildExtensionRegistry`\n // (sync `resolveConfig` can't drive async setup itself). `runTui`\n // does this for the terminal flow; SDK / GUI consumers do the same\n // before calling `resolveConfig`.\n const extensions: ExtensionRegistry = options.extensionRegistry ?? EMPTY_EXTENSION_REGISTRY\n\n // No implicit merge — host controls the registry entirely. Default is the\n // four built-ins; an explicit empty `{}` would mean \"no providers\" (the\n // wizard would have nothing to offer and the AuthScreen would refuse).\n // Extensions can contribute providers; host-supplied providers WIN on key\n // collision so an extension can't silently shadow the host's wiring.\n const baseProviders: ProviderRegistry = options.providers ?? BUILTIN_PROVIDERS\n const mergedProviders: ProviderRegistry = Object.keys(extensions.providers).length === 0\n ? baseProviders\n : { ...extensions.providers, ...baseProviders }\n const providers: ProviderRegistry = wrapProvidersWithExtensionMiddleware(mergedProviders, extensions)\n\n // Agent registry resolution. Three paths, mutually exclusive:\n // - `agents` + `preset` both set → throw. The legacy `preset` would\n // get silently ignored otherwise and the host wouldn't notice.\n // - `agents` set → use verbatim (host owns the picker contents).\n // - `preset` set without `agents` → wrap into a single-entry registry\n // so the legacy single-agent path keeps working without code change.\n // - neither set → BUILTIN_AGENTS (Build + Plan).\n if (options.agents && options.preset) {\n throw new Error(\n 'resolveConfig: pass either `preset` (single-agent legacy) or `agents` '\n + '(multi-profile registry), not both. Migrate `preset` into an entry '\n + 'inside `agents` if you want both worlds.',\n )\n }\n const baseAgents: AgentRegistry = options.agents\n ?? (options.preset ? singleAgentRegistry(options.preset) : BUILTIN_AGENTS)\n // Extension-contributed profiles compose under host profiles (host\n // wins on id collision) — same precedence rule as providers above.\n const agents: AgentRegistry = Object.keys(extensions.agents).length === 0\n ? baseAgents\n : { ...extensions.agents, ...baseAgents }\n if (Object.keys(agents).length === 0) {\n throw new Error('resolveConfig: `agents` registry is empty — at least one profile is required.')\n }\n\n // Wire credentials *before* anything that might read them (resume detection,\n // any factory call). Sets `ZIDANE_CREDENTIALS_PATH` so the harness providers\n // find the file the chat engine manages, and injects stored API keys into\n // env so factories that resolve credentials eagerly (like `anthropic()`)\n // see them. Credentials ALWAYS live under `userDir` — they're host-wide\n // secrets, not per-project data, and must never leak into a checked-in\n // project `.{prefix}/` directory.\n process.env.ZIDANE_CREDENTIALS_PATH = credentialsPath(paths.userDir)\n applyApiKeyEnv(paths.userDir, providers)\n\n const modelsFor = makeModelsResolver(providers)\n\n const launchProvider = options.launch\n ? resolveLaunchProvider(options.launch.provider, providers, paths.userDir)\n : null\n const resumeProvider = options.launch\n ? launchProvider\n : resolveResumeProvider(initialState, providers, paths.userDir)\n const initialPicked = launchProvider\n ? pickInitial(launchProvider, providers, initialState, {\n model: options.launch?.model,\n thinking: options.launch?.thinking,\n })\n : resumeProvider\n ? pickInitial(resumeProvider, providers, initialState)\n : null\n\n // The host's `defaultAgent`, then `DEFAULT_AGENT_ID` (= 'build'), then\n // the first registry key. Deliberately NOT `state.lastAgent`: the agent\n // profile is a per-session mode (each session remembers its own), and a\n // fresh launch / fresh chat must always start from the default — an\n // accidental switch into `plan` in one chat should never leak into the\n // next. `lastAgent` is still written by older builds; it is ignored here.\n const initialAgentId = resolveAgentId(\n agents,\n undefined,\n options.defaultAgent ?? DEFAULT_AGENT_ID,\n )\n if (!initialAgentId) {\n // Defensive — the empty-check above already guards, so this is unreachable.\n throw new Error('resolveConfig: failed to resolve an initial agent id from a non-empty registry.')\n }\n\n // Keybindings live at the user level only — shortcut muscle memory\n // is per-user, not per-project. Read the raw JSON file ONCE; the\n // built-in catalog merges via `mergeKeybindings`, extension-side\n // overrides via `resolveExtensionKeybindings`. Without this we'd\n // re-read the same file twice in the resolve path.\n const rawKeybindings = readKeybindingsRaw(paths.userDir)\n const keybindings = mergeKeybindings(rawKeybindings)\n const extensionKeybindings = extensions.tuiContributions.keybindings.length === 0\n ? []\n : resolveExtensionKeybindings(\n extensions.tuiContributions.keybindings,\n rawKeybindings,\n )\n\n return {\n prefix,\n storageDir,\n paths,\n providers,\n agents,\n initialAgentId,\n store,\n stateStore,\n modelsFor,\n initialState,\n initialSettings: migrateLegacySettings(initialState.settings ?? {}),\n resumeProvider,\n initialPicked,\n keybindings,\n extensionKeybindings,\n autoUpdate: options.autoUpdate ?? null,\n extensions,\n extensionScanPaths: options.extensionScanPaths ?? [],\n pluginScanPaths: options.pluginScanPaths ?? [],\n extensionPreset: buildExtensionPreset(extensions),\n extensionSystemFragments: extensions.systemFragments,\n extensionSubagents: extensions.subagents,\n extensionSubagentOverrides: extensions.subagentOverrides,\n extensionConfigCategories: extensions.configCategories,\n extensionConfigItems: extensions.configItems,\n trustProjectExtensions: options.trustProjectExtensions\n ?? (process.env.ZIDANE_TRUST_PROJECT_EXTENSIONS === '1'),\n rawKeybindings,\n launch: options.launch ?? null,\n }\n}\n\n/**\n * Compute the on-disk layout for a single TUI session. Public so hosts\n * (CLI tools, tests, scripted bootstraps) can ask \"where will sessions\n * land?\" without going through `resolveConfig`'s full machinery.\n *\n * Storage-slot resolution (config / data / state / cache) is delegated\n * to {@link resolveStorageDirs}. In legacy modes the four slots collapse\n * to a single `<storageDir>/<prefix>` directory; in XDG mode they fan\n * out into `$XDG_CONFIG_HOME/zidane`, `$XDG_DATA_HOME/zidane`,\n * `$XDG_STATE_HOME/zidane`, `$XDG_CACHE_HOME/zidane`. See the module\n * doc on `./xdg.ts` for the full precedence chain.\n *\n * Decision logic for project mode (least → most specific):\n *\n * 1. Default: OFF — sessions live in the user-scoped `dataDir`\n * and get scoped per-project via the `SessionData.projectRoot` tag.\n * 2. `<configDir>/config.json` `projectDb` flag (when present).\n * 3. `ZIDANE_PROJECT_DB` env var (`'0'` / `'false'` / `'off'` → off,\n * anything else true if set).\n * 4. `options.projectDb` (host call site).\n *\n * Project mode activates only when the resolution above lands on\n * `true` AND a git repo is found above `cwd`. Outside git or with the\n * flag off, sessions + state stay at the user-scoped slots.\n *\n * Auto-creates the project `.{prefix}/` directory when mode resolves\n * to ON (silent — mirrors `ensureStateDir`). User slots are created\n * lazily by the state-store + credentials writers; we don't pre-touch\n * them here so a read-only invocation stays read-only.\n */\nexport function resolveStoragePaths(opts: {\n prefix: string\n /** Explicit storage root. When set, forces legacy-explicit mode. */\n storageDir?: string\n cwd: string\n /** Host override for project mode. Beats the user-config + env-var sources. */\n projectDb?: boolean\n}): ResolvedPaths {\n const dirs = resolveStorageDirs({\n prefix: opts.prefix,\n ...(opts.storageDir !== undefined ? { storageDir: opts.storageDir } : {}),\n })\n\n // User-config + env-var are loaded eagerly so the precedence chain\n // is observable from a single call to this function; resolution is\n // cheap (one fs.existsSync + readFileSync at most). Reads from\n // `configDir` — `config.json` is user-editable surface, not data.\n const userConfig = loadUserConfig(dirs.configDir)\n const envFlag = parseEnvFlag(process.env.ZIDANE_PROJECT_DB)\n const projectDbEnabled = opts.projectDb\n ?? envFlag\n ?? userConfig.projectDb\n ?? false\n\n const gitRoot = projectDbEnabled ? findGitRoot(opts.cwd) : null\n // Project-scoped storage uses the prefix verbatim — same shape as\n // pre-XDG callers expect (`prefix: '.zidane'` → `<repo>/.zidane/`,\n // `prefix: '.myapp'` → `<repo>/.myapp/`). XDG-mode users still get\n // the user-scoped slots split, but project dirs are a\n // checked-in-or-not concern, orthogonal to the XDG question.\n const projectDir = gitRoot ? resolve(gitRoot, opts.prefix) : null\n\n if (projectDir && !existsSync(projectDir)) {\n try {\n mkdirSync(projectDir, { recursive: true })\n }\n catch (err) {\n // Don't crash the bootstrap on mkdir failure (permissions, R/O\n // FS) — fall back to user-scoped storage. Surface the cause\n // under ZIDANE_DEBUG so the operator can diagnose if curious.\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] project-db: mkdir \"${projectDir}\" failed: ${errorMessage(err)}\\n`)\n return userOnlyPaths(dirs)\n }\n }\n\n if (projectDir) {\n return {\n dir: projectDir,\n userDir: dirs.configDir,\n projectDir,\n db: resolve(projectDir, 'sessions.db'),\n state: resolve(projectDir, 'state.json'),\n configDir: dirs.configDir,\n dataDir: dirs.dataDir,\n stateDir: dirs.stateDir,\n cacheDir: dirs.cacheDir,\n storageMode: dirs.mode,\n }\n }\n return userOnlyPaths(dirs)\n}\n\nfunction userOnlyPaths(dirs: {\n configDir: string\n dataDir: string\n stateDir: string\n cacheDir: string\n mode: StorageMode\n}): ResolvedPaths {\n return {\n dir: dirs.configDir,\n userDir: dirs.configDir,\n projectDir: null,\n db: resolve(dirs.dataDir, 'sessions.db'),\n state: resolve(dirs.stateDir, 'state.json'),\n configDir: dirs.configDir,\n dataDir: dirs.dataDir,\n stateDir: dirs.stateDir,\n cacheDir: dirs.cacheDir,\n storageMode: dirs.mode,\n }\n}\n\n/** Migrate legacy `state.json` settings without overriding modern keys. */\nfunction migrateLegacySettings(loaded: Partial<Settings> & Record<string, unknown>): Partial<Settings> {\n const migrated: Record<string, unknown> = { ...loaded }\n\n if ('allowInteraction' in migrated && !('interactionMode' in migrated))\n migrated.interactionMode = migrated.allowInteraction === false ? 'disabled' : 'ask'\n delete migrated.allowInteraction\n\n if ('showToolCalls' in migrated && !('toolCallDisplay' in migrated))\n migrated.toolCallDisplay = migrated.showToolCalls === false ? 'hidden' : 'formatted'\n delete migrated.showToolCalls\n\n return migrated as Partial<Settings>\n}\n\n/** Tri-state parse for env-var bools — explicit `undefined` keeps absence from forcing a default. */\nfunction parseEnvFlag(raw: string | undefined): boolean | undefined {\n if (raw === undefined)\n return undefined\n const v = raw.trim().toLowerCase()\n if (v === '0' || v === 'false' || v === 'off' || v === 'no')\n return false\n if (v === '1' || v === 'true' || v === 'on' || v === 'yes')\n return true\n process.stderr.write(`[zidane/chat] ZIDANE_PROJECT_DB=\"${raw}\" is not a recognized boolean (use 0/1, true/false, on/off, yes/no) — ignoring it.\\n`)\n return undefined\n}\n\nfunction makeModelsResolver(registry: ProviderRegistry): (key: ProviderKey) => readonly ModelInfo[] {\n return (key) => {\n const descriptor = registry[key]\n return descriptor ? modelsForDescriptor(descriptor) : []\n }\n}\n\nfunction wrapProvidersWithExtensionMiddleware(\n providers: ProviderRegistry,\n extensions: ExtensionRegistry,\n): ProviderRegistry {\n if (extensions.providerMiddlewares.length === 0)\n return providers\n const out: Record<string, ProviderDescriptor> = {}\n for (const [key, descriptor] of Object.entries(providers)) {\n out[key] = {\n ...descriptor,\n factory: () => {\n let provider = descriptor.factory()\n for (const entry of extensions.providerMiddlewares) {\n try {\n const next = entry.value(provider, {\n extensionName: entry.extensionName,\n source: entry.source,\n descriptor,\n })\n // A middleware that returns nothing (or a malformed value from a\n // JS extension) is pass-through rather than poisoning the provider\n // for later middlewares and the caller.\n if (isProviderLike(next))\n provider = next\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/extensions] ${entry.extensionName} provider middleware failed: ${errorMessage(err)}\\n`)\n }\n }\n return provider\n },\n }\n }\n return out\n}\n\nfunction isProviderLike(value: unknown): value is Provider {\n if (!value || typeof value !== 'object')\n return false\n const provider = value as Partial<Provider>\n return typeof provider.name === 'string'\n && !!provider.meta\n && typeof provider.meta === 'object'\n && typeof provider.formatTools === 'function'\n && typeof provider.userMessage === 'function'\n && typeof provider.assistantMessage === 'function'\n && typeof provider.toolResultsMessage === 'function'\n && typeof provider.stream === 'function'\n}\n\nfunction resolveResumeProvider(\n state: TuiState,\n providers: ProviderRegistry,\n storageDir: string,\n): ProviderAuth | null {\n // Three independent reasons a resume can fail to materialize — each one\n // sends the TUI to the auth screen as if it were a fresh install. Under\n // `ZIDANE_DEBUG` we tag the specific cause so a \"had to rewrite my API\n // key after reinstall\" report points at the actual culprit instead of\n // forcing the operator to guess between (a) a wiped state file,\n // (b) a renamed provider key, and (c) a credentials file that exists\n // but doesn't contain the previous provider.\n if (!state.lastProvider) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] resume: no \\`lastProvider\\` in state — auth screen will show. (Has \\`${storageDir}/state.json\\` been deleted or never written?)\\n`)\n return null\n }\n if (!providers[state.lastProvider]) {\n if (process.env.ZIDANE_DEBUG) {\n const known = Object.keys(providers).join(', ') || '<none>'\n process.stderr.write(`[zidane/chat] resume: \\`state.lastProvider=\"${state.lastProvider}\"\\` is not in the registry. Known providers: ${known}.\\n`)\n }\n return null\n }\n const detected = detectAuth(storageDir, providers)\n const match = detected.find(p => p.key === state.lastProvider && p.available)\n if (!match && process.env.ZIDANE_DEBUG) {\n const entry = detected.find(p => p.key === state.lastProvider)\n process.stderr.write(`[zidane/chat] resume: \\`${state.lastProvider}\\` is in the registry but has no available auth method (apikey / env / oauth). Detection: ${entry ? JSON.stringify(entry.methods) : '<provider missing from detection>'}.\\n`)\n }\n return match ?? null\n}\n\nfunction resolveLaunchProvider(\n key: ProviderKey,\n providers: ProviderRegistry,\n storageDir: string,\n): ProviderAuth | null {\n if (!providers[key])\n return null\n return detectAuth(storageDir, providers).find(p => p.key === key && p.available) ?? null\n}\n\nfunction pickInitial(\n auth: ProviderAuth,\n providers: ProviderRegistry,\n state: TuiState,\n overrides: { model?: string, thinking?: Picked['effort'] } = {},\n): Picked | null {\n const descriptor = providers[auth.key]\n if (!descriptor)\n return null\n const remembered = state.lastModelByProvider?.[auth.key]\n // Prefer the remembered model, then the descriptor's declared default.\n // Only construct the provider as a last resort — some factories throw on\n // missing credentials, and we never want that to block the wizard.\n const model = overrides.model ?? remembered ?? descriptor.defaultModel ?? safeFactoryDefault(descriptor)\n if (!model)\n return null\n // Restore the per-model reasoning effort + enabled options so a relaunch\n // lands on the same knobs the user last set (parity with the in-session\n // `onPickModel` path). Both are keyed by model id in `TuiState`.\n const effort = overrides.thinking ?? (modelSupportsReasoning(descriptor, model)\n ? (state.lastEffortByModel?.[model] ?? 'medium')\n : undefined)\n const modelOptions = restoreModelOptions(descriptor, model, state.lastModelOptionsByModel)\n // Fallback chain restore — primary-match, registration, and dedup\n // filtering live in the helper; unauthed providers are skipped later\n // by the TUI's agent build.\n const fallbacks = restoreChainFallbacks(\n state.lastModelChain,\n { provider: auth.key, model },\n k => providers[k] !== undefined,\n )\n return {\n provider: auth,\n model,\n ...(fallbacks.length > 0 ? { fallbacks } : {}),\n ...(effort ? { effort } : {}),\n ...(modelOptions ? { modelOptions } : {}),\n }\n}\n\nfunction safeFactoryDefault(descriptor: ProviderDescriptor): string | undefined {\n try {\n return descriptor.factory().meta.defaultModel\n }\n catch {\n return undefined\n }\n}\n","import type { ReactNode } from 'react'\nimport type { ResolvedConfig } from './config'\nimport { createContext, useContext } from 'react'\n\n// ---------------------------------------------------------------------------\n// ConfigContext — renderer-agnostic React context that hands a fully-resolved\n// chat config down to deep consumers (modals, pickers, screens). Kept in its\n// own file so callers that only want `resolveConfig()` don't pay the\n// `import 'react'` cost.\n// ---------------------------------------------------------------------------\n\nconst ConfigContext = createContext<ResolvedConfig | null>(null)\n\nexport function ConfigProvider({ config, children }: { config: ResolvedConfig, children: ReactNode }) {\n return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>\n}\n\nexport function useConfig(): ResolvedConfig {\n const ctx = useContext(ConfigContext)\n if (!ctx)\n throw new Error('useConfig must be used inside <ConfigProvider>')\n return ctx\n}\n","/**\n * Live catalogs for files / skills / MCPs surfaced through React\n * context.\n *\n * **Why context, not props.** The modal layer stores its open node in\n * `useState`. React's element-identity reconciliation skips re-rendering\n * a subtree when the element reference hasn't changed — so passing\n * `skillsCatalog` as a prop into `modal.open(<SettingsModal …>)` bakes\n * the snapshot at open time and any subsequent `setSkillsCatalog`\n * doesn't reach the modal. Context propagation IS forwarded through\n * the modal layer regardless of element identity (same path the MCP\n * auth state uses), so reading via `useDiscovery()` gives the modal\n * live state.\n *\n * **Shape.** The provider exposes three groups:\n *\n * - **Catalogs** — the current state. Consumers re-render on every\n * update.\n * - **Ensure thunks** — kick the first load (idempotent). Wired into\n * completion providers; also called by the settings modal on mount\n * to fire-load lazy catalogs (currently `files`) on demand.\n * - **Refresh thunks** — force a rescan, bypassing the SWR throttle.\n * Wired into the settings modal's `ctrl+R`.\n *\n * Renderer-agnostic by design: the context only carries data + thunks.\n * The discovery work itself lives in `*-discovery.ts` modules; the\n * stale-while-revalidate machinery lives in `discovery-slot.ts`. The\n * provider's caller (AppShell for the TUI) owns where the slots live\n * and how their state propagates to `<DiscoveryProvider value>`.\n */\n\nimport type { ReactNode } from 'react'\nimport type { McpToolSchema } from '../mcp'\nimport type { SkillConfig } from '../skills'\nimport type { FileEntry } from './files-discovery'\nimport type { DiscoveredMcp, DiscoveryError } from './mcps-discovery'\nimport type { DiscoveredPlugin } from './plugins-discovery'\nimport { createContext, useContext } from 'react'\n\nexport interface DiscoveryContextValue {\n /** Parsed `SkillConfig[]` from project + user scan paths. */\n skillsCatalog: readonly SkillConfig[]\n /**\n * Agent Plugins loaded from `.agents/plugins` / `.{prefix}/plugins`\n * (project + user). Includes rejected plugins — they render as read-only\n * diagnostics rather than vanishing.\n */\n pluginsCatalog: readonly DiscoveredPlugin[]\n /** Parsed `mcps.json` entries. */\n mcpsCatalog: readonly DiscoveredMcp[]\n /** Non-fatal parse errors from `mcps.json` lookups. */\n mcpsErrors: readonly DiscoveryError[]\n /**\n * Per-server full tool catalog from the on-disk cache (see\n * `chat/mcp-tools-cache.ts`). Updated as MCP servers bootstrap and\n * fire the `mcp:tools:list` hook. Missing key → no cache yet; the\n * settings UI surfaces an empty-state row under the expanded server.\n */\n mcpToolsByServer: Record<string, readonly McpToolSchema[]>\n /** Current file walk results — empty until lazy-load lands. */\n filesCatalog: readonly FileEntry[]\n\n /** Force a fresh skills rescan. Resolves once state is committed. */\n refreshSkills: () => Promise<void>\n /** Force a fresh plugin rescan. Resolves once state is committed. */\n refreshPlugins: () => Promise<void>\n /**\n * Re-parse `mcps.json`. Sync-shaped under the hood, async for symmetry.\n * Resolves `true` when the catalog actually changed (a publish that\n * schedules downstream agent rebuilds), `false` on a fingerprint no-op\n * or scan failure — callers that arm rebuild-coupled state (e.g. the\n * add-MCP auto-login) disarm on `false`.\n */\n refreshMcps: () => Promise<boolean>\n /** Force a fresh file walk. Wired into a future \"refresh files\" affordance. */\n refreshFiles: () => Promise<void>\n\n /** Idempotent first-load trigger for skills (used by `/` popover + modal mount). */\n ensureSkills: () => Promise<readonly SkillConfig[]>\n /** Idempotent first-load trigger for files (used by `@` popover). */\n ensureFiles: () => Promise<readonly FileEntry[]>\n}\n\nconst DiscoveryContext = createContext<DiscoveryContextValue | null>(null)\n\nexport function DiscoveryProvider({\n value,\n children,\n}: {\n value: DiscoveryContextValue\n children: ReactNode\n}) {\n return (\n <DiscoveryContext.Provider value={value}>\n {children}\n </DiscoveryContext.Provider>\n )\n}\n\n/**\n * Read live discovery state + actions. Throws if used outside a\n * `<DiscoveryProvider>` — discovery is a load-bearing dependency for\n * settings + completion popovers; bailing loud here surfaces wiring\n * mistakes at mount instead of producing empty catalogs at first\n * keystroke.\n */\nexport function useDiscovery(): DiscoveryContextValue {\n const ctx = useContext(DiscoveryContext)\n if (!ctx)\n throw new Error('useDiscovery must be used inside <DiscoveryProvider>')\n return ctx\n}\n\n/**\n * Non-throwing variant — returns `null` when no provider is mounted.\n * Used by composable modals (`<SettingsModal>`, …) that accept catalog\n * props as a fallback for embedders who don't wire the full TUI\n * shell. The in-app flow always mounts `<DiscoveryProvider>` so the\n * modal sees live state; standalone embeds get the prop snapshot.\n */\nexport function useDiscoveryOptional(): DiscoveryContextValue | null {\n return useContext(DiscoveryContext)\n}\n","/**\n * Stale-while-revalidate catalog slot.\n *\n * One slot per (catalog kind, projectDir) pin. Holds the first-load\n * promise + a `lastScannedAt` timestamp + an in-flight `refreshing`\n * promise. Idempotent across concurrent callers, safe to rotate when\n * the project changes (the slot's `abort` controller cancels in-flight\n * walks and the `.then` guards drop stale results).\n *\n * Renderer-agnostic. AppShell wraps it in React refs so React closures\n * stay stable; a non-TUI shell can use it directly against its own\n * state propagation.\n *\n * Contract:\n * - `ensure()` — kick the first load if not started; return the\n * cached promise. On subsequent calls, if `Date.now() -\n * lastScannedAt >= throttleMs` and no refresh is in flight, kick\n * a background revalidate that calls `onLoad(fresh)` when it\n * resolves. The original first-load promise still resolves with\n * the first walk's contents — consumers reading live state\n * elsewhere (React state) see the refreshed catalog.\n * - `refresh()` — force a rescan now, bypassing the throttle.\n * De-duplicates: if a refresh is already in flight, returns the\n * same promise.\n * - `abort()` — cancel any in-flight walk and detach. After this\n * the slot is dead; callers should rotate to a new slot.\n * - `dispose()` — alias of `abort()`, named for symmetry with\n * cleanup callbacks.\n *\n * No state is exposed except through promises + the host's `onLoad`\n * callback; the slot doesn't try to be a store, only a coordinator.\n */\n\n/** Walk function — takes an abort signal, returns the discovered list. */\nexport type Walk<T> = (signal: AbortSignal) => Promise<readonly T[]>\n\n/** Notification: a fresh walk has resolved. Host is expected to commit to its own state store. */\nexport type Commit<T> = (items: readonly T[]) => void\n\nexport interface DiscoverySlotOptions<T> {\n /** Discover function — runs the actual FS / network walk. */\n walk: Walk<T>\n /** Commit hook — receives every successful walk's result (first + refreshes). */\n onLoad: Commit<T>\n /**\n * Min interval between background refreshes triggered by `ensure()`.\n * Default: 3_000ms. `refresh()` ignores this — it's the user's\n * explicit-intent path.\n */\n throttleMs?: number\n /** Optional error sink. Defaults to no-op; useful for `debugLog`. */\n onError?: (err: unknown, phase: 'first-load' | 'refresh') => void\n}\n\nexport interface DiscoverySlot<T> {\n /**\n * Kick the first load (idempotent). Returns the first-load promise\n * once it exists. Schedules a background refresh if past throttle.\n */\n ensure: () => Promise<readonly T[]>\n /** Force-refresh now. De-duped across concurrent callers. */\n refresh: () => Promise<void>\n /** True if a refresh is currently running. */\n isRefreshing: () => boolean\n /** Cancel in-flight work + detach. Idempotent. */\n abort: () => void\n}\n\nexport function createDiscoverySlot<T>(options: DiscoverySlotOptions<T>): DiscoverySlot<T> {\n const throttleMs = options.throttleMs ?? 3_000\n const abortCtrl = new AbortController()\n let firstLoad: Promise<readonly T[]> | null = null\n let refreshing: Promise<void> | null = null\n let lastScannedAt = 0\n let aborted = false\n\n const handleError = (err: unknown, phase: 'first-load' | 'refresh'): void => {\n options.onError?.(err, phase)\n }\n\n const startRefresh = (): Promise<void> => {\n lastScannedAt = Date.now()\n const run = options.walk(abortCtrl.signal)\n .then((items) => {\n if (aborted)\n return\n options.onLoad(items)\n })\n .catch((err) => {\n if (aborted)\n return\n handleError(err, 'refresh')\n })\n .finally(() => {\n if (refreshing === run)\n refreshing = null\n })\n refreshing = run\n return run\n }\n\n const slot: DiscoverySlot<T> = {\n ensure() {\n if (aborted)\n return Promise.resolve([] as readonly T[])\n if (!firstLoad) {\n lastScannedAt = Date.now()\n firstLoad = options.walk(abortCtrl.signal)\n .then((items) => {\n if (aborted)\n return [] as readonly T[]\n options.onLoad(items)\n return items\n })\n .catch((err) => {\n if (!aborted)\n handleError(err, 'first-load')\n return [] as readonly T[]\n })\n return firstLoad\n }\n // SWR: revalidate in the background if stale and not already running.\n if (!refreshing && Date.now() - lastScannedAt >= throttleMs)\n void startRefresh()\n return firstLoad\n },\n refresh() {\n if (aborted)\n return Promise.resolve()\n if (refreshing)\n return refreshing\n // Forced refresh from a user-initiated path (e.g. settings\n // modal `ctrl+R`). If the first load hasn't happened yet,\n // bootstrap through `ensure` so subsequent ensure callers see\n // the right `firstLoad` promise.\n if (!firstLoad)\n return slot.ensure().then(() => {})\n return startRefresh()\n },\n isRefreshing() {\n return refreshing !== null\n },\n abort() {\n if (aborted)\n return\n aborted = true\n abortCtrl.abort()\n },\n }\n return slot\n}\n","/**\n * `$EDITOR` / `$VISUAL` command parsing.\n *\n * These variables conventionally hold a command LINE, not a bare executable —\n * `code --wait`, `cursor --wait`, `subl -w`, `emacsclient -nw` are all normal\n * values. Spawning the whole string as argv[0] looks for a file literally named\n * `cursor --wait` and fails with ENOENT, so the flags have to be split off.\n *\n * Kept separate from `app.tsx` (and free of `node:child_process`) so the\n * tokenizer is unit-testable without pulling in the TUI.\n */\n\n/**\n * Split an editor command line into `[executable, ...args]`.\n *\n * Whitespace-separated, with single and double quotes honored so an editor\n * living under a path with spaces still works\n * (`\"/Applications/My App/bin/ed\" -w`). Quotes group and are stripped;\n * backslash escaping is deliberately NOT implemented, because on Windows\n * backslash is the path separator and treating `C:\\tools\\ed` as escapes would\n * corrupt far more command lines than it would rescue.\n *\n * Returns `[]` for an empty or whitespace-only spec, so callers can fall back\n * to the platform opener rather than spawning `''`.\n */\nexport function parseEditorCommand(spec: string): string[] {\n const tokens: string[] = []\n let current = ''\n // The quote character we're inside, or null at top level. Tracked (rather\n // than a boolean) so a double quote inside single quotes stays literal.\n let quote: '\"' | '\\'' | null = null\n // Distinct from `current !== ''` so `\"\"` yields an empty-string token instead\n // of vanishing — an explicitly quoted empty argument was still an argument.\n let quoted = false\n\n for (const ch of spec) {\n if (quote) {\n if (ch === quote)\n quote = null\n else\n current += ch\n continue\n }\n if (ch === '\"' || ch === '\\'') {\n quote = ch\n quoted = true\n continue\n }\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') {\n if (current !== '' || quoted) {\n tokens.push(current)\n current = ''\n quoted = false\n }\n continue\n }\n current += ch\n }\n if (current !== '' || quoted)\n tokens.push(current)\n return tokens\n}\n\n/**\n * Re-quote a single argv token for a shell that will re-parse the joined command\n * line — i.e. `spawn(..., { shell: true })`, which Node uses on Windows so that\n * `.cmd` / `.bat` wrappers (how `code`, `cursor` and friends ship there) resolve.\n *\n * Node does NOT re-quote when `shell` is set: it joins `[cmd, ...args]` with\n * spaces and hands the string over. Since {@link parseEditorCommand} has already\n * STRIPPED the user's quotes, handing tokens back unquoted would split\n * `C:\\Program Files\\ed.exe` at the space and try to run `C:\\Program`. This puts\n * the quotes back.\n *\n * Only tokens that need it are quoted, so a plain `code` stays `code`. A literal\n * double quote is doubled, which is how `cmd.exe` escapes one inside a quoted\n * run.\n *\n * The test covers `cmd.exe` METACHARACTERS, not just whitespace: `&`, `|`, `^`,\n * `<`, `>` and friends carry none of the obvious \"needs quoting\" signals, so a\n * repo cloned into `C:\\work\\a&b` would otherwise hand `C:\\work\\a&b\\AGENTS.md`\n * over bare, and cmd would split it at the `&` and run the tail as a second\n * command.\n *\n * `%` and `!` are in the class so such a token is quoted rather than passed\n * bare — but be clear about the limit: quoting does NOT stop cmd.exe expanding\n * `%VAR%`, nor `!VAR!` where delayed expansion is on. Double quotes suppress\n * splitting and other syntax, not expansion. That is a property of the shell\n * which no amount of quoting can close; the worst case is a wrong path, not an\n * injected command.\n */\nexport function quoteForShell(token: string): string {\n if (token === '')\n return '\"\"'\n if (!/[\\s\"&|^<>%()!]/.test(token))\n return token\n return `\"${token.replace(/\"/g, '\"\"')}\"`\n}\n","import type { Theme } from '../theme'\n\n// ---------------------------------------------------------------------------\n// Catppuccin — four flavors built from the official palette.\n//\n// Hex values mirror catppuccin/palette v1.8.0 verbatim. The palette is the\n// data; `catppuccinTheme()` is the mapping into our `Theme` shape — accent\n// → role colors, semantic UI tokens (mantle = modal), and Tree-sitter syntax\n// token mappings that line up with the official Catppuccin styleguide.\n//\n// `Latte` is a light flavor (`base = #eff1f5`, `text = #4c4f69`) and only\n// looks correct in a light terminal — same caveat as the upstream VSCode\n// theme. The other three are dark.\n// ---------------------------------------------------------------------------\n\n/**\n * One Catppuccin flavor as a flat hex map. Mirrors the structure of\n * `catppuccin/palette` so the builder below maps named colors → roles\n * without any per-flavor branching.\n */\ninterface CatppuccinPalette {\n rosewater: string\n flamingo: string\n pink: string\n mauve: string\n red: string\n maroon: string\n peach: string\n yellow: string\n green: string\n teal: string\n sky: string\n sapphire: string\n blue: string\n lavender: string\n text: string\n subtext1: string\n subtext0: string\n overlay2: string\n overlay1: string\n overlay0: string\n surface2: string\n surface1: string\n surface0: string\n base: string\n mantle: string\n crust: string\n}\n\nconst LATTE: CatppuccinPalette = {\n rosewater: '#dc8a78',\n flamingo: '#dd7878',\n pink: '#ea76cb',\n mauve: '#8839ef',\n red: '#d20f39',\n maroon: '#e64553',\n peach: '#fe640b',\n yellow: '#df8e1d',\n green: '#40a02b',\n teal: '#179299',\n sky: '#04a5e5',\n sapphire: '#209fb5',\n blue: '#1e66f5',\n lavender: '#7287fd',\n text: '#4c4f69',\n subtext1: '#5c5f77',\n subtext0: '#6c6f85',\n overlay2: '#7c7f93',\n overlay1: '#8c8fa1',\n overlay0: '#9ca0b0',\n surface2: '#acb0be',\n surface1: '#bcc0cc',\n surface0: '#ccd0da',\n base: '#eff1f5',\n mantle: '#e6e9ef',\n crust: '#dce0e8',\n}\n\nconst FRAPPE: CatppuccinPalette = {\n rosewater: '#f2d5cf',\n flamingo: '#eebebe',\n pink: '#f4b8e4',\n mauve: '#ca9ee6',\n red: '#e78284',\n maroon: '#ea999c',\n peach: '#ef9f76',\n yellow: '#e5c890',\n green: '#a6d189',\n teal: '#81c8be',\n sky: '#99d1db',\n sapphire: '#85c1dc',\n blue: '#8caaee',\n lavender: '#babbf1',\n text: '#c6d0f5',\n subtext1: '#b5bfe2',\n subtext0: '#a5adce',\n overlay2: '#949cbb',\n overlay1: '#838ba7',\n overlay0: '#737994',\n surface2: '#626880',\n surface1: '#51576d',\n surface0: '#414559',\n base: '#303446',\n mantle: '#292c3c',\n crust: '#232634',\n}\n\nconst MACCHIATO: CatppuccinPalette = {\n rosewater: '#f4dbd6',\n flamingo: '#f0c6c6',\n pink: '#f5bde6',\n mauve: '#c6a0f6',\n red: '#ed8796',\n maroon: '#ee99a0',\n peach: '#f5a97f',\n yellow: '#eed49f',\n green: '#a6da95',\n teal: '#8bd5ca',\n sky: '#91d7e3',\n sapphire: '#7dc4e4',\n blue: '#8aadf4',\n lavender: '#b7bdf8',\n text: '#cad3f5',\n subtext1: '#b8c0e0',\n subtext0: '#a5adcb',\n overlay2: '#939ab7',\n overlay1: '#8087a2',\n overlay0: '#6e738d',\n surface2: '#5b6078',\n surface1: '#494d64',\n surface0: '#363a4f',\n base: '#24273a',\n mantle: '#1e2030',\n crust: '#181926',\n}\n\nconst MOCHA: CatppuccinPalette = {\n rosewater: '#f5e0dc',\n flamingo: '#f2cdcd',\n pink: '#f5c2e7',\n mauve: '#cba6f7',\n red: '#f38ba8',\n maroon: '#eba0ac',\n peach: '#fab387',\n yellow: '#f9e2af',\n green: '#a6e3a1',\n teal: '#94e2d5',\n sky: '#89dceb',\n sapphire: '#74c7ec',\n blue: '#89b4fa',\n lavender: '#b4befe',\n text: '#cdd6f4',\n subtext1: '#bac2de',\n subtext0: '#a6adc8',\n overlay2: '#9399b2',\n overlay1: '#7f849c',\n overlay0: '#6c7086',\n surface2: '#585b70',\n surface1: '#45475a',\n surface0: '#313244',\n base: '#1e1e2e',\n mantle: '#181825',\n crust: '#11111b',\n}\n\n/**\n * Compose a `Theme` from a Catppuccin palette flavor.\n *\n * Role-color picks follow the upstream Catppuccin styleguide:\n * - `mauve` is the canonical accent — used here as the brand color.\n * - `green` / `red` / `yellow` keep their universal semantic meaning.\n * - `blue` carries function / model identity (matches the VSCode plugin's\n * `support.function` mapping).\n * - `subtext1` / `overlay0` form the dim/mute pair (one step apart so the\n * two tiers stay visually distinct on every flavor).\n * - `surface1` / `overlay0` give the resting/active border pair.\n *\n * Syntax token mappings line up with the official Catppuccin token rules\n * (keyword = mauve, string = green, function = blue, type = yellow, …) so\n * code fences match what users see in their editor.\n */\nfunction catppuccinTheme(id: string, label: string, p: CatppuccinPalette): Theme {\n return {\n id,\n label,\n colors: {\n brand: p.mauve,\n accent: p.green,\n model: p.blue,\n warn: p.yellow,\n error: p.red,\n dim: p.subtext1,\n mute: p.overlay0,\n border: p.surface1,\n borderActive: p.overlay0,\n // Throbber sweeps mauve → pink — keeps the gradient inside\n // Catppuccin's warm violet/pink family. The fallback `brand →\n // accent` would interpolate mauve → green through grey.\n throbber: { from: p.mauve, to: p.pink },\n },\n select: {\n backgroundColor: 'transparent',\n focusedBackgroundColor: 'transparent',\n selectedBackgroundColor: 'transparent',\n selectedTextColor: p.mauve,\n textColor: p.subtext1,\n descriptionColor: p.overlay0,\n selectedDescriptionColor: p.subtext0,\n },\n surfaces: {\n // `crust` is the deepest tier in the palette — one step below\n // `mantle`, so the root surface stays distinct from the modal\n // elevation on every flavor (including Latte, where `crust` is\n // the darkest light-flavor tone).\n background: p.crust,\n // `mantle` is one shade darker than `base`, mirroring how the upstream\n // VSCode theme paints the sidebar against the editor — a subtle but\n // present elevation cue for the modal panel.\n modal: p.mantle,\n // Chip pills — skills on `mauve` (the canonical Catppuccin accent,\n // matches the brand role) and files on `blue` (function/identifier\n // accent in the upstream styleguide, so file mentions visually\n // rhyme with code references). Foreground is `base` for all\n // pills so dark flavors read light-on-dark and Latte's mauve/blue\n // are dark enough that `base` (`#eff1f5`) contrasts.\n chips: {\n default: { bg: p.mauve, fg: p.base },\n skills: { bg: p.mauve, fg: p.base },\n files: { bg: p.blue, fg: p.base },\n },\n // `surface0` is Catppuccin's canonical \"row highlight\" tier — one\n // step above `base`, designed for subtle elevation. Works on every\n // flavor including Latte (where it darkens against the light base).\n selection: p.surface0,\n // Diff blocks ride the palette's `green` / `red` accents pre-mixed\n // against `base`. We lean on `surface0` / `surface1` for the row\n // paint and the brighter palette accents for the sign glyph so the\n // `+` / `-` markers pop against the dimmer row tint. Catppuccin\n // doesn't ship `*Container` tokens, so we just compose with the\n // surface tiers — works on every flavor (including Latte by\n // virtue of `surface*` darkening against the light base).\n diff: {\n addBg: p.surface0,\n removeBg: p.surface0,\n addContentBg: p.surface1,\n removeContentBg: p.surface1,\n addFg: p.green,\n removeFg: p.red,\n },\n },\n syntax: {\n 'default': { fg: p.text },\n\n // ---- markdown structure ----\n 'markup.heading': { fg: p.mauve, bold: true },\n 'markup.heading.1': { fg: p.mauve, bold: true },\n 'markup.heading.2': { fg: p.lavender, bold: true },\n 'markup.heading.3': { fg: p.blue, bold: true },\n 'markup.bold': { fg: p.text, bold: true },\n 'markup.strong': { fg: p.text, bold: true },\n 'markup.italic': { fg: p.text, italic: true },\n 'markup.link': { fg: p.sky, underline: true },\n 'markup.link.url': { fg: p.sky, underline: true },\n 'markup.list': { fg: p.peach },\n 'markup.raw': { fg: p.green },\n 'markup.raw.block': { fg: p.green },\n 'markup.quote': { fg: p.overlay2, italic: true },\n\n // ---- code ----\n 'keyword': { fg: p.mauve, bold: true },\n 'keyword.import': { fg: p.pink, bold: true },\n 'keyword.operator': { fg: p.sky },\n 'string': { fg: p.green },\n 'string.escape': { fg: p.pink, bold: true },\n 'character': { fg: p.teal },\n 'comment': { fg: p.overlay1, italic: true },\n 'number': { fg: p.peach },\n 'boolean': { fg: p.peach },\n 'constant': { fg: p.peach },\n 'constant.builtin': { fg: p.peach },\n 'function': { fg: p.blue },\n 'function.call': { fg: p.blue },\n 'function.method': { fg: p.blue },\n 'function.method.call': { fg: p.blue },\n 'function.builtin': { fg: p.blue },\n 'function.macro': { fg: p.teal },\n 'type': { fg: p.yellow },\n 'type.builtin': { fg: p.yellow },\n 'constructor': { fg: p.yellow },\n 'attribute': { fg: p.yellow },\n 'tag': { fg: p.lavender },\n 'variable': { fg: p.text },\n 'variable.builtin': { fg: p.red },\n 'variable.parameter': { fg: p.maroon, italic: true },\n 'variable.member': { fg: p.text },\n 'property': { fg: p.lavender },\n 'operator': { fg: p.sky },\n 'punctuation': { fg: p.overlay2 },\n 'punctuation.bracket': { fg: p.overlay2 },\n 'punctuation.delimiter': { fg: p.overlay2 },\n 'label': { fg: p.sapphire },\n },\n }\n}\n\nexport const CATPPUCCIN_MOCHA = catppuccinTheme('catppuccin-mocha', 'Catppuccin Mocha', MOCHA)\nexport const CATPPUCCIN_MACCHIATO = catppuccinTheme('catppuccin-macchiato', 'Catppuccin Macchiato', MACCHIATO)\nexport const CATPPUCCIN_FRAPPE = catppuccinTheme('catppuccin-frappe', 'Catppuccin Frappé', FRAPPE)\n// Latte assumes a *light* terminal background (`base = #eff1f5`,\n// `text = #4c4f69`). The label is suffixed so the picker reads correctly\n// regardless of where in the cycle it lands.\nexport const CATPPUCCIN_LATTE = catppuccinTheme('catppuccin-latte', 'Catppuccin Latte (light)', LATTE)\n","import type { Theme } from '../theme'\n\n// ---------------------------------------------------------------------------\n// Crush — Charm's CharmTone \"Pantera\" dark theme.\n//\n// Palette hex values are copied verbatim from\n// `charmbracelet/x/exp/charmtone/charmtone.go`. Role assignments and visual\n// paint follow `charmbracelet/crush`'s `internal/ui/styles/quickstyle.go`\n// (the actual `Styles` struct populated for Pantera), not just the\n// `quickStyleOpts` palette — because the Chroma map, Glamour heading\n// banner, link split, and diff overrides all diverge from a naive\n// \"primary → brand\" reading.\n//\n// Notable Pantera idiosyncrasies preserved here:\n// - Headings ride `info` (Malibu blue) + bold, not the brand violet. The\n// brand violet only appears as H1's *banner background*, with `Zest`\n// yellow body text. See `quickstyle.go` `Heading` / `H1`.\n// - Inline code is `destructive` (Coral) on `bgLessVisible` (Charcoal).\n// - LinkText is `successMostSubtle` (Guac) bold; URLs are `Zinc` teal\n// with underline.\n// - Diff line / symbol / code colors are *muted* forest/maroon\n// (`#629657` / `#a45c59`) on `#323931` / `#383030`, overriding the\n// brighter `Turtle` / `Cherry` defaults in `diffview/style.go`.\n// - Surface tier cascade is Pepper → BBQ (modal) → Charcoal (selection),\n// matching `bgBase` → `bgLeastVisible` → `bgLessVisible` in Crush.\n// ---------------------------------------------------------------------------\n\nconst CHARMTONE = {\n // warm / red\n cumin: '#BF976F',\n bengal: '#FF6E63',\n sriracha: '#EB4268',\n coral: '#FF577D',\n salmon: '#FF7F90',\n cherry: '#FF388B',\n // pinks / magentas\n cheeky: '#FF79D0',\n blush: '#FF84FF',\n dolly: '#FF60FF',\n pony: '#FF4FBF',\n // purples\n mauve: '#D46EFF',\n hazy: '#8B75FF',\n charple: '#6B50FF',\n // blues\n guppy: '#7272FF',\n malibu: '#00A4FF',\n sardine: '#4FBEFE',\n // greens / teals\n zinc: '#10B1AE',\n turtle: '#0ADCD9',\n guac: '#12C78F',\n julep: '#00FFB2',\n bok: '#68FFD6',\n // yellows\n mustard: '#F5EF34',\n citron: '#E8FF27',\n zest: '#E8FE96',\n // neutrals (dark → light)\n pepper: '#201F26',\n bbq: '#2D2C35',\n charcoal: '#3A3943',\n iron: '#4D4C57',\n oyster: '#605F6B',\n squid: '#858392',\n smoke: '#BFBCC8',\n ash: '#DFDBDD',\n salt: '#F1EFEF',\n butter: '#FFFAF1',\n} as const\n\nconst c = CHARMTONE\n\nexport const CRUSH_THEME: Theme = {\n id: 'crush',\n label: 'Crush (Pantera)',\n colors: {\n // `Charple` is Pantera's `primary` — Crush dialog frames, focused\n // user-message borders, completions selection all paint with this.\n brand: c.charple,\n // `Bok` is the `accent` role (see `themes.go`). Soft mint that reads\n // as \"alive\" without competing with the brand violet.\n accent: c.bok,\n // `Malibu` is Pantera's `info` — also where headings and tool names\n // land, so it doubles as the model-identity tone here.\n model: c.malibu,\n warn: c.julep,\n // `Zest` is Charmtone's dim-yellow; reserved here for the footer's\n // cost indicator so the money figure reads as money even though\n // Crush's `warn` role intentionally stays mint (see above).\n money: c.zest,\n error: c.sriracha,\n // `Smoke` is `fgSubtle` — the body color Markdown documents inherit.\n dim: c.smoke,\n // `Oyster` is `fgMostSubtle` — tertiary tone, dimmer than `dim`.\n mute: c.oyster,\n // `Charcoal` is `separator` / `bgLessVisible` — Crush's resting border.\n border: c.charcoal,\n // Focused elements in Crush get a `primary` (Charple) rim — dialog\n // frames, focused message borders, the compact details panel.\n borderActive: c.charple,\n // Working-throbber gradient: `primary` → `secondary`, exactly the\n // `WorkingGradFromColor` / `WorkingGradToColor` pair Crush's\n // `quickStyle` populates for the `Anim` spinner.\n throbber: { from: c.charple, to: c.dolly },\n },\n select: {\n backgroundColor: 'transparent',\n focusedBackgroundColor: 'transparent',\n selectedBackgroundColor: 'transparent',\n selectedTextColor: c.charple,\n textColor: c.smoke,\n descriptionColor: c.oyster,\n selectedDescriptionColor: c.squid,\n },\n surfaces: {\n // Surface cascade mirrors Crush's neutral tier ladder:\n // Pepper (bgBase) → BBQ (bgLeastVisible) → Charcoal (bgLessVisible).\n background: c.pepper,\n modal: c.bbq,\n // Chips:\n // - skills (slash commands) ride `primary` / `onPrimary` — the same\n // pair Crush uses for `Completions.Focused` and\n // `Dialog.SelectedItem`.\n // - files ride `info` / `onPrimary` — `info` carries tool / file /\n // reference identity throughout Crush (JobToolName, MCPName).\n chips: {\n default: { bg: c.charple, fg: c.butter },\n skills: { bg: c.charple, fg: c.butter },\n files: { bg: c.malibu, fg: c.butter },\n },\n // Selected-turn lift — `Charcoal`, the same tier Crush uses for\n // separators and resting borders. One step above `modal` (BBQ).\n selection: c.charcoal,\n // Diff blocks — values from Crush's `Styles.Diff` overrides in\n // `quickstyle.go` (lines ~480–500), not the brighter `DefaultDarkStyle`\n // in `diffview/style.go`. Symbol fg `#629657` / `#a45c59` rides the\n // muted forest / maroon Crush actually renders.\n diff: {\n addBg: '#2b322a',\n removeBg: '#312929',\n addContentBg: '#323931',\n removeContentBg: '#383030',\n addFg: '#629657',\n removeFg: '#a45c59',\n },\n },\n syntax: {\n // Chroma `Text` → `fgSubtle` (Smoke). Code body reads one tier below\n // `Ash` / `Salt` — matches Crush's `CodeBlock.Chroma.Text` color.\n 'default': { fg: c.smoke },\n\n // ---- markdown structure ----\n // Heading parent block: `info` + bold. H2-H5 inherit this in Crush\n // (they only add prefix tokens), so we replicate Malibu+bold across\n // levels and override H1 with the brand banner below.\n 'markup.heading': { fg: c.malibu, bold: true },\n // H1 is Crush's signature: bold body text on a `primary` (Charple)\n // background banner. Crush ships `warningSubtle` (Zest yellow) text\n // there; we use `onPrimary` (Butter) cream instead — same banner\n // vibe, drops the yellow that was bleeding into the rest of the UI.\n 'markup.heading.1': { fg: c.butter, bg: c.charple, bold: true },\n 'markup.heading.2': { fg: c.malibu, bold: true },\n 'markup.heading.3': { fg: c.malibu, bold: true },\n // Strong / Emph in Crush set only weight/slant — color inherits from\n // the Document (Smoke). We match by keeping `fg: smoke`.\n 'markup.bold': { fg: c.smoke, bold: true },\n 'markup.strong': { fg: c.smoke, bold: true },\n 'markup.italic': { fg: c.smoke, italic: true },\n // Crush splits links: `LinkText` (the visible label) is\n // `successMostSubtle` (Guac) + bold; `Link` (the URL) is `Zinc`\n // (teal) + underline.\n 'markup.link': { fg: c.guac, bold: true },\n 'markup.link.url': { fg: c.zinc, underline: true },\n // List `Item` in Crush has no color override — bullets inherit\n // Document color (Smoke).\n 'markup.list': { fg: c.smoke },\n // Inline `Code`: `destructive` (Coral) on `bgLessVisible` (Charcoal).\n 'markup.raw': { fg: c.coral, bg: c.charcoal },\n 'markup.raw.block': { fg: c.coral, bg: c.charcoal },\n // BlockQuote in Crush is just indented with `│ ` — no color or italic.\n // We keep `fg: smoke` to match Document inheritance.\n 'markup.quote': { fg: c.smoke },\n\n // ---- code (Chroma mapping from `quickstyle.go`) ----\n 'keyword': { fg: c.malibu }, // Chroma `Keyword` → info\n 'keyword.import': { fg: c.pony }, // KeywordNamespace\n 'keyword.operator': { fg: c.salmon }, // Operator\n 'string': { fg: c.cumin }, // LiteralString\n 'string.escape': { fg: c.bok }, // LiteralStringEscape → successMoreSubtle\n 'character': { fg: c.cumin },\n 'comment': { fg: c.oyster, italic: true }, // fgMostSubtle (italic kept for legibility)\n 'number': { fg: c.julep }, // LiteralNumber → success\n 'boolean': { fg: c.julep },\n 'constant': { fg: c.julep },\n 'constant.builtin': { fg: c.julep },\n 'function': { fg: c.guac }, // NameFunction → successMostSubtle\n 'function.call': { fg: c.guac },\n 'function.method': { fg: c.guac },\n 'function.method.call': { fg: c.guac },\n 'function.builtin': { fg: c.cheeky }, // NameBuiltin\n 'function.macro': { fg: c.bok }, // NameDecorator\n 'type': { fg: c.guppy }, // KeywordType\n 'type.builtin': { fg: c.guppy },\n // `NameClass` in Crush is `Salt` + bold + underline. We drop underline\n // (noisy for non-class types in our broader `constructor` capture).\n 'constructor': { fg: c.salt, bold: true },\n 'attribute': { fg: c.hazy }, // NameAttribute\n 'tag': { fg: c.mauve }, // NameTag\n 'variable': { fg: c.smoke }, // Name → fgSubtle\n 'variable.builtin': { fg: c.cheeky },\n 'variable.parameter': { fg: c.smoke, italic: true },\n 'variable.member': { fg: c.smoke },\n 'property': { fg: c.smoke },\n 'operator': { fg: c.salmon },\n // Crush maps `Punctuation → warningSubtle` (Zest), but at our token\n // granularity that paints every bracket / comma / dot yellow — too\n // much yellow in dense code. Drop to `Squid` (fgMoreSubtle) so\n // punctuation recedes behind the colored tokens it separates.\n 'punctuation': { fg: c.squid },\n 'punctuation.bracket': { fg: c.squid },\n 'punctuation.delimiter': { fg: c.squid },\n 'label': { fg: c.malibu },\n },\n}\n","import type { Theme } from '../theme'\n\ninterface GruvboxPalette {\n dark: boolean\n bg0: string\n bg1: string\n bg2: string\n bg3: string\n bg4: string\n fg0: string\n fg1: string\n fg2: string\n fg3: string\n fg4: string\n red: string\n green: string\n yellow: string\n blue: string\n purple: string\n aqua: string\n orange: string\n gray: string\n}\n\nconst DARK: GruvboxPalette = {\n dark: true,\n bg0: '#282828',\n bg1: '#3c3836',\n bg2: '#504945',\n bg3: '#665c54',\n bg4: '#7c6f64',\n fg0: '#fbf1c7',\n fg1: '#ebdbb2',\n fg2: '#d5c4a1',\n fg3: '#bdae93',\n fg4: '#a89984',\n red: '#fb4934',\n green: '#b8bb26',\n yellow: '#fabd2f',\n blue: '#83a598',\n purple: '#d3869b',\n aqua: '#8ec07c',\n orange: '#fe8019',\n gray: '#928374',\n}\n\nconst LIGHT: GruvboxPalette = {\n dark: false,\n bg0: '#fbf1c7',\n bg1: '#ebdbb2',\n bg2: '#d5c4a1',\n bg3: '#bdae93',\n bg4: '#a89984',\n fg0: '#282828',\n fg1: '#3c3836',\n fg2: '#504945',\n fg3: '#665c54',\n fg4: '#7c6f64',\n red: '#cc241d',\n green: '#98971a',\n yellow: '#d79921',\n blue: '#458588',\n purple: '#b16286',\n aqua: '#689d6a',\n orange: '#d65d0e',\n gray: '#928374',\n}\n\nfunction gruvboxTheme(id: string, label: string, p: GruvboxPalette): Theme {\n return {\n id,\n label,\n colors: {\n brand: p.yellow,\n accent: p.green,\n model: p.blue,\n warn: p.orange,\n error: p.red,\n dim: p.fg3,\n mute: p.gray,\n border: p.bg2,\n borderActive: p.bg4,\n throbber: { from: p.yellow, to: p.orange },\n money: p.yellow,\n },\n select: {\n backgroundColor: 'transparent',\n focusedBackgroundColor: 'transparent',\n selectedBackgroundColor: 'transparent',\n selectedTextColor: p.yellow,\n textColor: p.fg2,\n descriptionColor: p.fg4,\n selectedDescriptionColor: p.fg3,\n },\n surfaces: {\n background: p.dark ? '#1d2021' : '#f9f5d7',\n modal: p.bg0,\n chips: {\n default: { bg: p.yellow, fg: p.dark ? p.bg0 : p.fg0 },\n skills: { bg: p.yellow, fg: p.dark ? p.bg0 : p.fg0 },\n files: { bg: p.blue, fg: p.dark ? p.bg0 : '#fbf1c7' },\n },\n selection: p.bg1,\n diff: {\n addBg: p.bg1,\n removeBg: p.bg1,\n addContentBg: p.bg2,\n removeContentBg: p.bg2,\n addFg: p.green,\n removeFg: p.red,\n },\n },\n syntax: {\n 'default': { fg: p.fg1 },\n 'markup.heading': { fg: p.yellow, bold: true },\n 'markup.heading.1': { fg: p.yellow, bold: true },\n 'markup.heading.2': { fg: p.orange, bold: true },\n 'markup.heading.3': { fg: p.blue, bold: true },\n 'markup.bold': { fg: p.fg0, bold: true },\n 'markup.strong': { fg: p.fg0, bold: true },\n 'markup.italic': { fg: p.fg1, italic: true },\n 'markup.link': { fg: p.blue, underline: true },\n 'markup.link.url': { fg: p.blue, underline: true },\n 'markup.list': { fg: p.orange },\n 'markup.raw': { fg: p.green },\n 'markup.raw.block': { fg: p.green },\n 'markup.quote': { fg: p.fg4, italic: true },\n\n 'keyword': { fg: p.red, bold: true },\n 'keyword.import': { fg: p.red, bold: true },\n 'keyword.operator': { fg: p.orange },\n 'string': { fg: p.green },\n 'string.escape': { fg: p.orange, bold: true },\n 'character': { fg: p.aqua },\n 'comment': { fg: p.gray, italic: true },\n 'number': { fg: p.purple },\n 'boolean': { fg: p.purple },\n 'constant': { fg: p.purple },\n 'constant.builtin': { fg: p.purple },\n 'function': { fg: p.blue },\n 'function.call': { fg: p.blue },\n 'function.method': { fg: p.blue },\n 'function.method.call': { fg: p.blue },\n 'function.builtin': { fg: p.blue },\n 'function.macro': { fg: p.aqua },\n 'type': { fg: p.yellow },\n 'type.builtin': { fg: p.yellow },\n 'constructor': { fg: p.yellow },\n 'attribute': { fg: p.aqua },\n 'tag': { fg: p.aqua },\n 'variable': { fg: p.fg1 },\n 'variable.builtin': { fg: p.red },\n 'variable.parameter': { fg: p.orange, italic: true },\n 'variable.member': { fg: p.fg1 },\n 'property': { fg: p.blue },\n 'operator': { fg: p.orange },\n 'punctuation': { fg: p.fg4 },\n 'punctuation.bracket': { fg: p.fg2 },\n 'punctuation.delimiter': { fg: p.fg4 },\n 'label': { fg: p.aqua },\n },\n }\n}\n\nexport const GRUVBOX_DARK = gruvboxTheme('gruvbox-dark', 'Gruvbox Dark', DARK)\nexport const GRUVBOX_LIGHT = gruvboxTheme('gruvbox-light', 'Gruvbox Light', LIGHT)\n","import type { Theme } from '../theme'\n\n// ---------------------------------------------------------------------------\n// Light — the vanilla light theme every mainstream app ships (Slack, VS Code,\n// Zed, google.com): white background, near-black text, one restrained blue\n// brand accent, conventional green / red / amber for success / error / warn.\n// Not a \"flavor\" of any palette family — deliberately neutral so users who\n// just want \"regular light mode\" have an opt-in that isn't a special theme.\n//\n// Palette draws from the GitHub Light register because it's the most widely\n// recognized neutral light palette in developer tooling and reads correctly\n// on every light terminal background.\n// ---------------------------------------------------------------------------\n\nconst TEXT = '#1f2328' // primary foreground — graphite, not pure black\nconst DIM = '#656d76' // secondary text — captions, helper rows\nconst MUTE = '#8c959f' // tertiary text — separators, the `┃` bar\nconst BORDER = '#d0d7de' // resting border\nconst BORDER_ACTIVE = '#0969da' // brand blue — focused / active rims\n\nconst BRAND = '#0969da' // GitHub link blue — the universal \"click me\" hue\nconst ACCENT = '#1a7f37' // success green\nconst MODEL = '#0550ae' // deeper brand sibling — model id, links\nconst WARN = '#9a6700' // amber on light — readable, doesn't blow out\nconst ERROR = '#d1242f' // danger red\n\nexport const LIGHT_THEME: Theme = {\n id: 'light',\n label: 'Light',\n colors: {\n brand: BRAND,\n accent: ACCENT,\n model: MODEL,\n warn: WARN,\n error: ERROR,\n dim: DIM,\n mute: MUTE,\n border: BORDER,\n borderActive: BORDER_ACTIVE,\n // Throbber sweeps the two brand-blue siblings — stays in-family\n // without detouring through green via the fallback.\n throbber: { from: BRAND, to: MODEL },\n },\n select: {\n backgroundColor: 'transparent',\n focusedBackgroundColor: 'transparent',\n selectedBackgroundColor: 'transparent',\n selectedTextColor: BRAND,\n textColor: TEXT,\n descriptionColor: MUTE,\n selectedDescriptionColor: DIM,\n },\n surfaces: {\n background: '#ffffff',\n // One tier up from the body — same elevation cue every light app\n // uses (GitHub `canvas.subtle`, VS Code light sidebar, …).\n modal: '#f6f8fa',\n // Skills get the saturated brand pill (blue bg, near-white fg).\n // Files get the inverse — pale blue bg with deep brand fg — so\n // the two reference kinds read as paired counterparts on light.\n chips: {\n default: { bg: BRAND, fg: '#ffffff' },\n skills: { bg: BRAND, fg: '#ffffff' },\n files: { bg: '#ddf4ff', fg: '#0550ae' },\n },\n // GitHub's PR-row \"info\" tint — subtle blue lift that reads as\n // selection without overpowering the body text.\n selection: '#ddf4ff',\n // GitHub diff palette. Row paint is the pale `*Subtle` tier; the\n // content column punches a half-tier deeper so syntax-highlighted\n // code contrasts cleanly against the gutter.\n diff: {\n addBg: '#dafbe1',\n removeBg: '#ffebe9',\n addContentBg: '#aceebb',\n removeContentBg: '#ffcecb',\n addFg: ACCENT,\n removeFg: ERROR,\n },\n },\n syntax: {\n // ---- markdown structure ----\n // Headings cascade through brand blue → deeper brand → graphite so\n // the outline reads as one related family. Body rides `TEXT`.\n 'default': { fg: TEXT },\n 'markup.heading': { fg: BRAND, bold: true },\n 'markup.heading.1': { fg: BRAND, bold: true },\n 'markup.heading.2': { fg: MODEL, bold: true },\n 'markup.heading.3': { fg: TEXT, bold: true },\n 'markup.bold': { fg: TEXT, bold: true },\n 'markup.strong': { fg: TEXT, bold: true },\n 'markup.italic': { fg: TEXT, italic: true },\n 'markup.link': { fg: BRAND, underline: true },\n 'markup.link.url': { fg: BRAND, underline: true },\n 'markup.list': { fg: '#953800' }, // warm orange — stands apart from headings + body\n 'markup.raw': { fg: '#953800' },\n 'markup.raw.block': { fg: '#953800' },\n 'markup.quote': { fg: DIM, italic: true },\n\n // ---- code (GitHub Light token mapping) ----\n 'keyword': { fg: '#cf222e', bold: true },\n 'keyword.import': { fg: '#cf222e', bold: true },\n 'keyword.operator': { fg: '#cf222e' },\n 'string': { fg: '#0a3069' },\n 'string.escape': { fg: '#0a3069', bold: true },\n 'character': { fg: '#0a3069' },\n 'comment': { fg: '#6e7781', italic: true },\n 'number': { fg: '#0550ae' },\n 'boolean': { fg: '#0550ae' },\n 'constant': { fg: '#0550ae' },\n 'constant.builtin': { fg: '#0550ae' },\n 'function': { fg: '#8250df' },\n 'function.call': { fg: '#8250df' },\n 'function.method': { fg: '#8250df' },\n 'function.method.call': { fg: '#8250df' },\n 'function.builtin': { fg: '#8250df' },\n 'function.macro': { fg: '#8250df' },\n 'type': { fg: '#953800' },\n 'type.builtin': { fg: '#953800' },\n 'constructor': { fg: '#953800' },\n 'attribute': { fg: '#0550ae' },\n 'tag': { fg: '#116329' },\n 'variable': { fg: TEXT },\n 'variable.builtin': { fg: '#0550ae' },\n 'variable.parameter': { fg: '#953800' },\n 'variable.member': { fg: '#0550ae' },\n 'property': { fg: '#0550ae' },\n 'operator': { fg: '#cf222e' },\n 'punctuation': { fg: DIM },\n 'punctuation.bracket': { fg: TEXT },\n 'punctuation.delimiter': { fg: TEXT },\n 'label': { fg: '#0550ae' },\n },\n}\n","import type { Theme } from '../theme'\n\n// ---------------------------------------------------------------------------\n// Vaporwave — neon-on-dark, ported from this-fifo/vaporwave-theme-vscode.\n//\n// Hex values come straight from the upstream `vaporwave-color-theme.json`\n// (terminal ANSI table + tokenColors). The signature pink (#E95378) carries\n// the brand role; the bright pink (#FF71CE) is reserved for the keyword\n// token so headings and keywords visually rhyme without competing.\n// ---------------------------------------------------------------------------\n\nconst PALETTE = {\n pink: '#E95378',\n pinkBright: '#FF71CE',\n cyan: '#01CDFE',\n cyanBright: '#59E1E3',\n blue: '#94D0FF',\n green: '#29D398',\n greenBright: '#09F7A0',\n yellow: '#FFFB96',\n red: '#F43E5C',\n text: '#D5D8DA',\n /**\n * ~70% of `text` — used for captions / helper / \"dim\" text so that\n * secondary copy is visually distinct from primary copy. Matches the\n * upstream theme's sidebar foreground (`#b7c5d3`) tonality.\n */\n textDim: '#B7C5D3',\n textBright: '#EEFFFF',\n comment: '#BBBBBB',\n muted: '#6C6F93',\n surface: '#2E303E',\n panel: '#232530',\n // One step below `panel` — mirrors the upstream theme's editor background\n // relative to widgets so modals continue to read as elevated.\n bgDeep: '#1a1c26',\n}\n\nexport const VAPORWAVE_THEME: Theme = {\n id: 'vaporwave',\n label: 'Vaporwave',\n colors: {\n brand: PALETTE.pink,\n accent: PALETTE.greenBright,\n model: PALETTE.blue,\n warn: PALETTE.yellow,\n error: PALETTE.red,\n dim: PALETTE.textDim,\n mute: PALETTE.muted,\n border: PALETTE.surface,\n borderActive: PALETTE.muted,\n // The iconic vaporwave gradient — hot pink to electric cyan. The\n // fallback `brand → accent` would sweep pink → neon green, which\n // misses the synth-cassette aesthetic entirely.\n throbber: { from: PALETTE.pink, to: PALETTE.cyan },\n },\n select: {\n backgroundColor: 'transparent',\n focusedBackgroundColor: 'transparent',\n selectedBackgroundColor: 'transparent',\n selectedTextColor: PALETTE.pink,\n textColor: PALETTE.text,\n descriptionColor: PALETTE.muted,\n selectedDescriptionColor: PALETTE.text,\n },\n surfaces: {\n // Root paint — sits one tier below `modal` so overlaid panels still\n // read as elevated.\n background: PALETTE.bgDeep,\n // Upstream uses `#232530` for editor widgets / dropdowns — same elevation\n // role as our modal panels.\n modal: PALETTE.panel,\n // Chip pills — skills on the signature vaporwave pink (brand role)\n // and files on cyan (the upstream's secondary neon accent). Both\n // read unmistakably against the dark panel foreground.\n chips: {\n default: { bg: PALETTE.pink, fg: PALETTE.panel },\n skills: { bg: PALETTE.pink, fg: PALETTE.panel },\n files: { bg: PALETTE.cyan, fg: PALETTE.panel },\n },\n // `surface` is one shade above `panel` — a quiet purple-grey lift\n // that reads as \"active row\" without clashing with the neon pink.\n selection: PALETTE.surface,\n // Diff blocks — vaporwave doesn't ship desaturated red/green palette\n // tones, so we hand-mix dark wines + dark teals against `panel` for\n // the row paint and brighter neon `green` / `red` for the sign\n // glyph. Keeps the neon vibe without drowning the syntax-highlighted\n // code under saturated bands.\n diff: {\n addBg: '#1a3528',\n removeBg: '#3a1a23',\n addContentBg: '#234c39',\n removeContentBg: '#52232f',\n addFg: PALETTE.greenBright,\n removeFg: PALETTE.red,\n },\n },\n syntax: {\n 'default': { fg: PALETTE.text },\n\n // ---- markdown structure ----\n 'markup.heading': { fg: PALETTE.pink, bold: true },\n 'markup.heading.1': { fg: PALETTE.pink, bold: true },\n 'markup.heading.2': { fg: PALETTE.pinkBright, bold: true },\n 'markup.heading.3': { fg: PALETTE.blue, bold: true },\n 'markup.bold': { fg: PALETTE.textBright, bold: true },\n 'markup.strong': { fg: PALETTE.textBright, bold: true },\n 'markup.italic': { fg: PALETTE.greenBright, italic: true },\n 'markup.link': { fg: PALETTE.yellow, underline: true },\n 'markup.link.url': { fg: PALETTE.yellow, underline: true },\n 'markup.list': { fg: PALETTE.textBright },\n 'markup.raw': { fg: PALETTE.yellow },\n 'markup.raw.block': { fg: PALETTE.yellow },\n 'markup.quote': { fg: PALETTE.yellow, italic: true },\n\n // ---- code (mirrors the upstream tokenColors mapping) ----\n 'keyword': { fg: PALETTE.pinkBright, bold: true },\n 'keyword.import': { fg: PALETTE.pinkBright, bold: true },\n 'keyword.operator': { fg: PALETTE.comment },\n 'string': { fg: PALETTE.yellow },\n 'string.escape': { fg: PALETTE.greenBright, bold: true },\n 'character': { fg: PALETTE.yellow },\n 'comment': { fg: PALETTE.comment, italic: true },\n 'number': { fg: PALETTE.textBright },\n 'boolean': { fg: PALETTE.textBright },\n 'constant': { fg: PALETTE.textBright },\n 'constant.builtin': { fg: PALETTE.textBright },\n 'function': { fg: PALETTE.greenBright },\n 'function.call': { fg: PALETTE.greenBright },\n 'function.method': { fg: PALETTE.greenBright },\n 'function.method.call': { fg: PALETTE.greenBright },\n 'function.builtin': { fg: PALETTE.greenBright },\n 'function.macro': { fg: PALETTE.greenBright },\n 'type': { fg: PALETTE.greenBright },\n 'type.builtin': { fg: PALETTE.greenBright },\n 'constructor': { fg: PALETTE.greenBright },\n 'attribute': { fg: PALETTE.yellow },\n 'tag': { fg: PALETTE.blue },\n 'variable': { fg: PALETTE.blue },\n 'variable.builtin': { fg: PALETTE.cyanBright },\n 'variable.parameter': { fg: PALETTE.text },\n 'variable.member': { fg: PALETTE.blue },\n 'property': { fg: PALETTE.blue },\n 'operator': { fg: PALETTE.comment },\n 'punctuation': { fg: PALETTE.comment },\n 'punctuation.bracket': { fg: PALETTE.textBright },\n 'punctuation.delimiter': { fg: PALETTE.comment },\n 'label': { fg: PALETTE.cyan },\n },\n}\n","/**\n * Renderer-agnostic theme system.\n *\n * A `Theme` bundles every variable that can change visually between\n * \"themes\" — colors, select-row styling, code/markdown syntax highlight\n * tokens, panel backgrounds. Plain JSON: no OpenTUI dependency, no React,\n * no functions. The TUI consumes the theme by reading from `useTheme()`\n * and converting hex strings into OpenTUI's `RGBA`/`SyntaxStyle`; a future\n * GUI consumes the same theme by converting into CSS variables or Tailwind\n * tokens.\n *\n * Components should always read theme values through `useTheme()` /\n * `useColors()` so a runtime theme switch (Settings → Theme) re-paints\n * the whole tree. Importing a raw theme object directly bypasses the\n * context and pins the component to a single look.\n *\n * Built-in flavors (Catppuccin, Vaporwave) live in `./themes/`. This file\n * just defines the shape + the default theme + the registry.\n */\n\nimport {\n CATPPUCCIN_FRAPPE,\n CATPPUCCIN_LATTE,\n CATPPUCCIN_MACCHIATO,\n CATPPUCCIN_MOCHA,\n} from './themes/catppuccin'\nimport { CRUSH_THEME } from './themes/crush'\nimport { GRUVBOX_DARK, GRUVBOX_LIGHT } from './themes/gruvbox'\nimport { LIGHT_THEME } from './themes/light'\nimport { VAPORWAVE_THEME } from './themes/vaporwave'\n\n/** Role-named color palette. Reused throughout the UI for text, borders, accents. */\nexport interface ThemeColors {\n /** Primary accent — selected text, brand emphasis (e.g. `▶` markers). */\n brand: string\n /** Success / progress accent — used sparingly. */\n accent: string\n /** Model badge accent (provider/model name in the footer, links). */\n model: string\n /** Warnings — busy spinner, approval picker border, esc hints. */\n warn: string\n /** Errors — `✗` prefix on error events, validation messages. */\n error: string\n /** Secondary text — captions, helper text, dim transcript lines. */\n dim: string\n /** Tertiary text — separators, descriptions, the `┃` tool-result bar. */\n mute: string\n /** Resting border color. */\n border: string\n /** Border color on focused / active elements. */\n borderActive: string\n /**\n * Optional gradient endpoints for the in-chat working throbber\n * (`<CrushThrobber>`). Falls back to `[brand, accent]` when unset.\n * Themes can pin a richer pair here — e.g. Crush's `Charple` →\n * `Dolly` purple-to-pink — without having to repurpose `accent`.\n */\n throbber?: { from: string, to: string }\n /**\n * Optional tone for the footer's session-cost indicator. Falls back\n * to `warn` when unset. Carved out as its own role because some\n * themes (e.g. Crush) intentionally bind `warn` to a non-yellow tone\n * and a separate \"money\" color reads more naturally on the footer.\n */\n money?: string\n}\n\n/** Select-component styling. Plain prop shape; both OpenTUI's `<select>` and any GUI equivalent can consume this. */\nexport interface ThemeSelect {\n backgroundColor: string\n focusedBackgroundColor: string\n selectedBackgroundColor: string\n selectedTextColor: string\n textColor: string\n descriptionColor: string\n selectedDescriptionColor: string\n}\n\n/** Background + foreground pair for a completion-reference chip pill. */\nexport interface ChipColor {\n /** Background paint — the pill itself. High-saturation by convention. */\n bg: string\n /** Foreground paint — the chip text. Pre-paired with {@link ChipColor.bg} for legibility. */\n fg: string\n}\n\n/**\n * Chip colors keyed by completion-provider id (`'skills'`, `'files'`, …).\n * `default` is the required fallback used by any provider id without an\n * explicit entry — keeps host-registered providers themable out of the\n * box. Use {@link resolveChipColor} to perform the kind-specific → default\n * lookup rather than indexing the map directly.\n */\n/**\n * Map of provider id → chip color pair. `default` is required so callers\n * always have a fallback; every other key is optional so direct indexing\n * (`chips['unknown']`) surfaces as `ChipColor | undefined` and nudges\n * consumers toward {@link resolveChipColor}, which encodes the fallback.\n */\nexport type ChipColorMap = { default: ChipColor } & Partial<Record<string, ChipColor>>\n\n/**\n * Look up the chip color pair for a provider id, falling back to\n * `chips.default` when the theme has no kind-specific entry. Mirrors\n * `resolveChipStyleId` so both rendering surfaces (submitted echo +\n * live textarea) share one canonical lookup rule.\n */\nexport function resolveChipColor(chips: ChipColorMap, providerId: string): ChipColor {\n return chips[providerId] ?? chips.default\n}\n\n/** Panel / surface backgrounds. */\nexport interface ThemeSurfaces {\n /**\n * Solid paint for the app's root surface. Covers the whole TUI viewport\n * so transparent / translucent terminals don't show desktop windows\n * underneath. Conventionally one tier deeper than {@link modal} so\n * modal panels still read as elevated above the body.\n */\n background: string\n /** Background of an overlaid modal panel (settings, model picker, …). */\n modal: string\n /**\n * Completion-chip color pairs keyed by provider id. Built-in themes\n * ship distinct `skills` + `files` tones so the two reference kinds\n * read differently in both the submitted echo and the live textarea.\n */\n chips: ChipColorMap\n /**\n * Background paint for the selected turn's events in select-turn mode.\n * Subtle, low-saturation lift from the terminal default so the whole\n * span of the selected turn reads as one continuous highlighted block\n * without overpowering the foreground text. Inverse-tinted on the\n * light flavor (Latte) so the same visual role works there.\n */\n selection: string\n /**\n * Edit-diff color block. Drives the native `<diff>` renderable used\n * by {@link Settings.showEditDiffs}. Pre-mixed against each theme's\n * primary surface so terminals without true alpha-blend get a\n * legible \"subtle red / subtle green\" effect rather than a flat\n * saturated band.\n *\n * - `*Bg` row-level paint (gutter + content)\n * - `*ContentBg` content-only paint (overrides `*Bg` on the text\n * column). Set when a deeper hue should punch\n * through behind the syntax-highlighted code; omit\n * to let `*Bg` carry the full row.\n * - `*Fg` color for the `+` / `-` glyph in the sign gutter.\n */\n diff: DiffSurfaces\n}\n\n/** Per-row paints used by the native `<diff>` renderable. */\nexport interface DiffSurfaces {\n addBg: string\n removeBg: string\n contextBg?: string\n addContentBg?: string\n removeContentBg?: string\n addFg: string\n removeFg: string\n}\n\n/**\n * One entry in a `SyntaxStyles` map — what OpenTUI's `SyntaxStyle.fromStyles`\n * accepts, minus the `RGBA` conversion. Renderer-agnostic JSON.\n */\nexport interface SyntaxTokenStyle {\n fg?: string\n bg?: string\n bold?: boolean\n italic?: boolean\n underline?: boolean\n dim?: boolean\n}\n\n/**\n * Map of Tree-sitter / markdown capture group → token style.\n *\n * Two flavours of keys live in the same table:\n * - `markup.*` — the markdown structure captures (heading, bold, italic,\n * list, quote, raw inline + block, link). OpenTUI's markdown parser\n * emits these for the markdown text itself.\n * - bare token names (`keyword`, `string`, `function`, …) — emitted by\n * the embedded language Tree-sitter grammars when a code fence\n * declares a language. OpenTUI passes the same `SyntaxStyle` down to\n * the fenced-code renderable, so this map drives both surfaces.\n */\nexport type SyntaxStyles = Record<string, SyntaxTokenStyle>\n\n/** Full theme bundle. */\nexport interface Theme {\n /** Stable identifier, used as the key in `BUILTIN_THEMES` and `Settings.theme`. */\n id: string\n /** Human-readable label shown in the settings picker. */\n label: string\n colors: ThemeColors\n select: ThemeSelect\n surfaces: ThemeSurfaces\n syntax: SyntaxStyles\n}\n\n// ---------------------------------------------------------------------------\n// Default theme — a cool blue-accent palette mapped to our role-named slots\n//\n// Source: a dark design-system palette + dark-theme semantic tokens. We\n// pick the dark variants because the TUI almost always renders on dark\n// terminals; the few light-terminal users get the same hues a step\n// brighter / cooler than the comparable light-theme tokens, which still\n// reads as \"brand blue / orange / red / green\" and remains accessible.\n//\n// Color-role mapping. Every \"accent\" slot stays inside the blue brand\n// cascade (brand-300 → brand-400 → brand-500 → brand-600) so the\n// chrome reads as one cool family rather than a mosaic of warm + cool\n// dots. Warm tones (orange, red) are reserved for genuine error /\n// danger contexts only:\n//\n// brand-600 borderActive #1488fc focused / active rims\n// brand-500 brand #2ba6ff signature accent — session title, ▶\n// brand-400 warn #53c4ff shortcut keys, spinner, accent numbers\n// brand-300 model #8adaff model id, links, soft brand sibling\n// green-500 accent #22c55e success — spawn markers, ✓\n// red-500 error #ef4444 errors, ✗ glyphs\n// neutral-400 dim #a3a3ac secondary text\n// neutral-600 mute #525258 separators, `┃` bar\n// neutral-700 border #3c3c41 resting borders\n//\n// `warn` lives in the brand cascade rather than orange so the hint\n// rows (which use it for every shortcut key) don't strafe orange dots\n// across an otherwise cool surface. The spinner + approval picker\n// pick up the same brand-tinted accent — reads as \"working / attention\"\n// without competing with the red of `error`.\n//\n// Defined as a local constant first so `BUILTIN_THEMES` references it\n// by name, and so the markdown-structure entries can interpolate the\n// role-named colors without redeclaring them.\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_COLORS: ThemeColors = {\n brand: '#2ba6ff', // brand-500\n accent: '#22c55e', // green-500 (success)\n model: '#8adaff', // brand-300 (soft sky)\n warn: '#53c4ff', // brand-400 (cool accent — keys, spinner, \"attention\")\n error: '#ef4444', // red-500 (danger)\n dim: '#a3a3ac', // neutral-400\n mute: '#525258', // neutral-600\n border: '#3c3c41', // neutral-700\n borderActive: '#1488fc', // brand-600\n // Throbber sweeps brand-500 → brand-300 — a brand-family blue\n // gradient that stays on-message instead of detouring through\n // `accent` green via the fallback.\n throbber: { from: '#2ba6ff', to: '#8adaff' },\n}\n\nexport const DEFAULT_THEME: Theme = {\n id: 'default',\n label: 'Default',\n colors: DEFAULT_COLORS,\n select: {\n // Transparent backgrounds keep the highlight bar from filling with a\n // different color than the surrounding box. The `▶` marker + brand-\n // colored selected text carry the focus affordance instead.\n backgroundColor: 'transparent',\n focusedBackgroundColor: 'transparent',\n selectedBackgroundColor: 'transparent',\n selectedTextColor: DEFAULT_COLORS.brand,\n textColor: DEFAULT_COLORS.dim,\n descriptionColor: DEFAULT_COLORS.mute,\n selectedDescriptionColor: DEFAULT_COLORS.dim,\n },\n surfaces: {\n // One tier below `modal` — neutral-1000-ish — so modal panels still\n // read as elevated above the body when both paint at the same time.\n background: '#0a0a0c',\n // The `--surface` dark tone — neutral-950.\n modal: '#111114',\n // Skills (slash-commands, user verbs) get the signature bold-brand\n // pill — saturated `brand-500` background with very light brand\n // text on top: primary action, attention-getting. Files (resource\n // references) get the *inverted* sibling — bright `brand-300`\n // background with deep `brand-950` text. Both pills are vibrant\n // and clearly in the brand family, but they sit on opposite ends\n // of the saturation/lightness axis, so they read as deliberately\n // paired counterparts rather than two pills competing for the\n // same visual role.\n chips: {\n default: { bg: DEFAULT_COLORS.brand, fg: '#eef9ff' },\n skills: { bg: DEFAULT_COLORS.brand, fg: '#eef9ff' },\n files: { bg: '#8adaff', fg: '#122f59' },\n },\n // Translucent brand on dark surface — the `--brandContainer`\n // (dark) is `#2ba6ff1a` (10% over surface). The terminal can't\n // alpha-blend, so we pre-mix ~15% brand-500 over neutral-950 and\n // bake the result as a solid hex. Reads as \"this turn is active\"\n // without overpowering the text foreground.\n selection: '#142737',\n // Diff blocks — `green-950` / `red-950` pre-mixed over neutral-950 at\n // ~12% (the row paint) with brighter `green-900` / `red-900` at ~25%\n // for the content column so the code text contrasts cleanly against\n // the gutter. Sign glyph fg rides the role colors (`accent` / `error`)\n // for instant visual parity with `✓` / `✗`.\n diff: {\n addBg: '#0e2218',\n removeBg: '#2a1414',\n addContentBg: '#143728',\n removeContentBg: '#3d1d1d',\n addFg: '#22c55e',\n removeFg: '#ef4444',\n },\n },\n syntax: {\n // ---- markdown structure ----\n // Headings cascade through brand-500 → brand-400 → brand-300 so the\n // outline reads as one related family rather than three random\n // accents. Body text rides `--textPrimary` (`#fefeff`).\n 'default': { fg: '#fefeff' },\n 'markup.heading': { fg: '#2ba6ff', bold: true },\n 'markup.heading.1': { fg: '#2ba6ff', bold: true },\n 'markup.heading.2': { fg: '#53c4ff', bold: true },\n 'markup.heading.3': { fg: '#8adaff', bold: true },\n 'markup.bold': { fg: '#ffffff', bold: true },\n 'markup.strong': { fg: '#ffffff', bold: true },\n 'markup.italic': { fg: '#fefeff', italic: true },\n 'markup.link': { fg: DEFAULT_COLORS.model, underline: true },\n 'markup.link.url': { fg: DEFAULT_COLORS.model, underline: true },\n // Bullet markers in markdown rides the warm `orange-400` rather\n // than `warn` (which is now cool brand-400). Lists are dense and\n // benefit from a hue that stands apart from headings + body text,\n // and warm tones are appropriate inside the rendered-markdown\n // surface where they don't accumulate into the chrome.\n 'markup.list': { fg: '#fb923c' },\n // Inline / block `code` rides brand-200 — readable on dark, distinct\n // from regular text + headings without leaving the brand family.\n 'markup.raw': { fg: '#bae7ff' },\n 'markup.raw.block': { fg: '#bae7ff' },\n 'markup.quote': { fg: DEFAULT_COLORS.dim, italic: true },\n\n // ---- code (Tree-sitter language tokens inside fenced blocks) ----\n // The palette has no purple/violet step, so we lean on green for functions\n // (callable / \"alive\") and reserve orange for types + parameters.\n // String constants live in brand-200, numeric/property tokens in\n // brand-300 so they read as one cool family against the warm types.\n 'keyword': { fg: '#f87171', bold: true }, // dangerHighlight\n 'keyword.import': { fg: '#f87171', bold: true },\n 'keyword.operator': { fg: '#f87171' },\n 'string': { fg: '#bae7ff' }, // brand-200\n 'string.escape': { fg: '#bae7ff', bold: true },\n 'character': { fg: '#bae7ff' },\n 'comment': { fg: '#73737b', italic: true }, // neutral-500\n 'number': { fg: '#8adaff' }, // brand-300\n 'boolean': { fg: '#8adaff' },\n 'constant': { fg: '#8adaff' },\n 'constant.builtin': { fg: '#8adaff' },\n 'function': { fg: '#86efac' }, // green-300\n 'function.call': { fg: '#86efac' },\n 'function.method': { fg: '#86efac' },\n 'function.method.call': { fg: '#86efac' },\n 'function.builtin': { fg: '#86efac' },\n 'function.macro': { fg: '#86efac' },\n 'type': { fg: '#fb923c' }, // warningHighlight (orange-400)\n 'type.builtin': { fg: '#fb923c' },\n 'constructor': { fg: '#fb923c' },\n 'attribute': { fg: '#fb923c' },\n 'tag': { fg: '#4ade80' }, // green-400\n 'variable': { fg: '#fefeff' },\n 'variable.builtin': { fg: '#8adaff' },\n 'variable.parameter': { fg: '#fb923c' },\n 'variable.member': { fg: '#8adaff' },\n 'property': { fg: '#8adaff' },\n 'operator': { fg: '#f87171' },\n 'punctuation': { fg: DEFAULT_COLORS.mute },\n 'punctuation.bracket': { fg: '#fefeff' },\n 'punctuation.delimiter': { fg: '#d4d4dd' }, // neutral-300\n 'label': { fg: '#8adaff' },\n },\n}\n\n/**\n * Built-in theme registry, keyed by `theme.id`. The TUI looks up the active\n * theme here using `Settings.theme`; unknown ids fall back to\n * `DEFAULT_THEME`. Hosts can extend this by passing additional themes to a\n * future `runTui({ themes })` option (not yet wired).\n *\n * Insertion order is the picker cycle order — keep `default` first so a\n * fresh install (no `theme` in `state.json`) sees the familiar yellow theme\n * before the others.\n */\nexport const BUILTIN_THEMES: Readonly<Record<string, Theme>> = {\n [DEFAULT_THEME.id]: DEFAULT_THEME,\n [LIGHT_THEME.id]: LIGHT_THEME,\n [CATPPUCCIN_MOCHA.id]: CATPPUCCIN_MOCHA,\n [CATPPUCCIN_MACCHIATO.id]: CATPPUCCIN_MACCHIATO,\n [CATPPUCCIN_FRAPPE.id]: CATPPUCCIN_FRAPPE,\n [CATPPUCCIN_LATTE.id]: CATPPUCCIN_LATTE,\n [GRUVBOX_DARK.id]: GRUVBOX_DARK,\n [GRUVBOX_LIGHT.id]: GRUVBOX_LIGHT,\n [CRUSH_THEME.id]: CRUSH_THEME,\n [VAPORWAVE_THEME.id]: VAPORWAVE_THEME,\n}\n\n/** Resolve a theme id to its full `Theme`, falling back to default on unknown ids. */\nexport function resolveTheme(\n id: string | undefined,\n extraThemes?: Readonly<Record<string, Theme>>,\n): Theme {\n if (id) {\n // Built-ins win on id collision so a third-party theme can't shadow\n // the user's \"default\" / \"catppuccin-mocha\" pick — those names are\n // the contract surface for everyone.\n if (BUILTIN_THEMES[id])\n return BUILTIN_THEMES[id]\n if (extraThemes && extraThemes[id])\n return extraThemes[id]\n }\n return DEFAULT_THEME\n}\n","import type { ReactNode } from 'react'\nimport type { Settings } from './types'\nimport { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'\nimport { BUILTIN_THEMES, DEFAULT_THEME } from './theme'\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_SETTINGS: Settings = {\n showThinking: true,\n // Tool calls render as clean per-tool summaries by default — `↳ Read\n // src/foo.ts` instead of `↳ read_file({\"path\":\"src/foo.ts\"})`. Flip\n // to `'full'` for the JSON-debug view, `'hidden'` to drop them.\n toolCallDisplay: 'formatted',\n showToolResults: true,\n // Off by default — collapsing every exchange's tool calls into one\n // summary row is an opinionated density trade-off; the historical\n // per-row transcript stays the out-of-the-box behavior.\n groupToolCalls: false,\n // `!` shell output renders a 10-line tail window by default; the toggle\n // shows every line (display-only — the persisted result stays trimmed).\n showFullShellOutput: false,\n showRunReceipt: true,\n safeMode: true,\n // Off by default — the daemon-backed window is currently the minimal\n // attach UI; the in-process TUI stays the default until the full\n // front-end speaks the daemon protocol.\n useDaemon: false,\n orchestration: false,\n // Off by default — project extensions (`<project>/.zidane/extensions`)\n // execute arbitrary local code, so they stay gated until the user opts\n // in. Replaces the legacy `ZIDANE_TRUST_PROJECT_EXTENSIONS=1` env flag,\n // which is still honored as a fallback.\n loadProjectExtensions: false,\n // Off by default — foreign MCP entries are stdio commands from files\n // zidane doesn't own, so importing them is an explicit opt-in.\n importForeignConfigs: false,\n // On by default — `.ruler/` is an intentional committed config source\n // (see the Settings doc), unlike the per-tool state files above.\n importRulerConfigs: true,\n hideSubagentOutput: true,\n // On by default — when the agent's providers expose a model catalog, the\n // spawn tool gains a `model` field (+ `model_search`) so the main model\n // can delegate mechanical work to cheaper models. Flip off to force\n // children onto the parent's model, exactly as before the feature.\n subagentModels: true,\n theme: DEFAULT_THEME.id,\n // Off by default — the sessions list filters to the current project\n // (git root / cwd) so the user only sees the conversations relevant\n // to where they're working. Toggle on to inspect or jump across\n // projects, plus see pre-tagging legacy sessions.\n showAllProjects: false,\n // On by default — match the historical \"the TUI picks up where you\n // left off\" behavior. Users who prefer a clean slate on launch can\n // flip this off; resume-on-launch then falls through to the sessions\n // list (or a fresh session when the list is empty).\n resumeLastSession: true,\n // On by default — large tool results (>8 KiB) get written to disk and\n // replaced inline with a `<persisted-output>` stub. Shrinks the prompt\n // prefix on shell/grep-heavy sessions; the model can still re-read the\n // full payload via `read_file` on the persisted path. Flip off to keep\n // every tool result inline regardless of size (matches pre-feature\n // behavior — also useful when debugging tool output).\n persistToolResults: true,\n // On by default — auto-fire compaction once the latest turn's input\n // usage crosses `autoCompactThreshold` of the effective context window.\n // The shipped `compactConversation` is otherwise manual-only; without\n // this trigger most users never reach for `/compact` and let sessions\n // degrade silently.\n autoCompact: true,\n // 80% of the effective window — comfortable headroom before the model\n // hits the wall, aligned with Claude Code's `AUTOCOMPACT_BUFFER_TOKENS`\n // (~167k of 200k = 83.5%). The settings modal cycles 0.6 / 0.7 / 0.8 /\n // 0.9 so users can tune down for verbose workflows (frequent\n // compactions) or up for context-heavy ones (delay until near full).\n autoCompactThreshold: 0.8,\n // Output / runaway caps — all default to \"off\" (model-driven, uncapped) so\n // the agent follows the model's output to completion out of the box. Opt\n // into any ceiling from the Settings modal; the budget breakers\n // (`maxCostUsd` / `maxTotalTokens` / `maxWallMs`) and `maxTurns` arm only\n // when set to a positive value.\n resumeOnMaxTokens: false,\n maxOutputTokens: 0,\n maxTurns: 0,\n // 0 = library default (3 attempts). Exposed so flaky-network / heavily\n // rate-limited setups can raise the budget without an SDK override.\n retryMaxAttempts: 0,\n maxCostUsd: 0,\n maxTotalTokens: 0,\n maxWallMinutes: 0,\n // On by default — `edit` / `multi_edit` / `write_file` calls render as\n // a unified diff. The paired success result is suppressed (the diff\n // already shows it worked); errors still surface. Flip off to keep\n // the raw `↳ edit({\"path\":\"…\",\"old_string\":\"…\"})` JSON dump.\n showEditDiffs: true,\n // Full unified diff in the transcript by default. Switch to `'compact'`\n // for a one-line per-hunk summary (file path + `+N −M` + first\n // changed line preview) — much quieter on edit-heavy turns; the\n // approval modal still shows the full diff when safeMode prompts fire.\n editDiffDisplay: 'full',\n // Off by default — the compaction boundary card stays metadata-only\n // (⎯⎯ N turns compacted · model · tokens). Flip on to also render the\n // LLM-generated summary text under the card, so you can audit exactly\n // what context the model carries forward across the boundary.\n showCompactionSummary: false,\n // '' = summarize on the main model. Set from the settings modal's\n // \"Compaction model\" action row via the cross-provider model picker.\n compactionModel: '',\n // 60fps target — matches most displays, doubles OpenTUI's stock 30fps\n // default for visibly snappier scroll + modal animations + cursor.\n // Settings modal cycles 30 / 60 / 120; applied live via the renderer's\n // `targetFps` / `maxFps` setters.\n targetFps: 60,\n // On by default — clicks, hovers, and wheel scrolling work throughout\n // the TUI. Off releases the mouse to the terminal emulator (native\n // selection / copy / scrollback) by disabling mouse reporting at the\n // renderer, which turns off every clickable surface at once.\n mouseInteractions: true,\n // Ask by default. Skip keeps the tools but resolves them automatically;\n // Disabled strips both tools on the next session activation.\n interactionMode: 'ask',\n // On by default — the assistant's streaming text drips in at a smooth\n // cadence (typewriter effect) instead of landing in batches. The\n // display rate is capped + bursts when behind; turn boundaries flush\n // remaining content immediately so completion is never delayed.\n smoothStreaming: true,\n // Off by default: no logs directory or file is created until the user\n // opts in. The in-TUI Logs panel works regardless (in-memory buffer).\n fileLogging: false,\n // On by default — the inline indicator above the prompt is a single\n // muted line (\"▸ <in-progress todo>\"), only present while the active\n // run has an `in_progress` item. Users who don't want even that\n // subtle line flip this off; the explicit `ctrl+t` modal still works.\n showTodoIndicator: true,\n showUsageIndicator: true,\n // Off by default — the streaming throbber (cycling hex/symbol glyphs)\n // is a stylistic flourish, not a load-bearing affordance. Streaming\n // already shows itself through the prompt's status row and the\n // incremental markdown updates, so we keep the transcript calm out\n // of the box and let users opt back in.\n showThrobber: false,\n // On by default — the detectors are command-gated (never free text) and\n // recording is best-effort; off hides every artifact surface at once.\n artifacts: true,\n // On by default — one warm-cache HTTP roundtrip per 24 h, never\n // blocks boot, swallows errors. CI / NO_UPDATE_NOTIFIER / ZIDANE_NO_UPDATE\n // force-disable. The explicit `zidane upgrade` subcommand bypasses\n // this toggle.\n checkForUpdates: true,\n // On by default — no request until a provider is picked, and only for\n // providers already signed in. ZIDANE_NO_USAGE_CHECK / CI force-disable;\n // with the toggle off the panel's ctrl+r still fetches on demand.\n subscriptionUsage: true,\n // `'full'` by default — match the historical chat-screen chrome with\n // every shortcut advertised in the bottom bar, prompt overlay, and\n // title meta. `'minimal'` is the explicit opt-in for the lighter\n // surface (agent / model / keybindings + `@` / `/` triggers).\n uiMode: 'full',\n // `'both'` by default — merge `~/.agents`, `~/.zidane`, `~/.claude`\n // user files with project-local `AGENTS.md` / `CLAUDE.md`. Flip to\n // `'user'` for cross-repo defaults only, `'project'` to scope down\n // when running zidane against an unfamiliar codebase.\n userInstructionsScope: 'both',\n ctrlCBehavior: 'quit',\n // Off by default — leave the terminal title alone so launching zidane\n // doesn't clobber whatever the user's shell set. Opt into `'app'`\n // (always \"zidane\"), `'session'` (track the active session title), or\n // `'icon-session'` (`{sessionIcon} · {title}`) from the UI settings tab.\n terminalTitle: 'off',\n // Free-text icon prefixed onto the title in `'icon-session'` mode.\n // Empty falls back to ⚽ (see the title effect in the TUI shell).\n sessionIcon: '',\n // Swap-in icon while the agent waits on user input (tool approval,\n // ask_user / present_plan). Empty falls back to ⚠️. Same\n // `'icon-session'`-only scope as `sessionIcon`.\n attentionIcon: '',\n // Swap-in icon while a run is in flight (streaming/working). Empty\n // falls back to ⚡. Loses to `attentionIcon` when the run is blocked\n // on user input. Same `'icon-session'`-only scope as `sessionIcon`.\n activeIcon: '',\n // On by default — preserves the historical paste-as-attachment\n // pipeline (drag-and-drop a file → image/binary attachment,\n // multiline paste → `paste.txt` document attachment). Flip off\n // and every paste lands inline in the textarea verbatim.\n attachmentsEnabled: true,\n // On by default — pressing the `openExternalEditor` key (ctrl+g) opens\n // the current prompt draft in `$EDITOR` / `$VISUAL` and reloads it on\n // save, git-style. Inert until that key is pressed, and degrades to a\n // notice when no editor is configured, so on-by-default is safe. Flip\n // off to make the key fall through to its normal textarea behavior.\n externalEditor: true,\n // Off by default — ghost text at the cursor is opinionated enough that\n // users should opt in. Providers only run while the prompt is focused\n // and the completion popup is closed.\n inlineAutocomplete: false,\n // `enabledSkills` / `enabledMcps` deliberately default to `undefined`\n // (NOT `[]`): every discovered skill + server is enabled until the user\n // explicitly toggles. The Settings modal seeds the allowlist with the\n // current discovery on first toggle so future additions don't auto-opt\n // out.\n //\n // `disabledMcpTools` is a per-server DENY-LIST (opposite polarity from\n // the `enabledX` allowlists above) — missing key / empty array means\n // every advertised tool is enabled. That polarity flip is deliberate:\n // a new tool shipped by an MCP server we already trust should auto-\n // enable, the same way a new server we explicitly added shouldn't.\n // Default is `undefined` (no per-tool customization).\n}\n\n// ---------------------------------------------------------------------------\n// Bounds\n// ---------------------------------------------------------------------------\n\n/**\n * Hard-clamp a `targetFps` value to a safe range before handing it to\n * the renderer. Defends against an out-of-band edit to `state.json`\n * (`0`, negative, `NaN`, absurd values) silently pegging the loop or\n * tripping the frame-time math. The settings modal only exposes\n * `30 / 60 / 120` so the typical path never trips the clamp; this\n * guard exists for the \"user hand-edited state.json\" case.\n */\nconst MIN_TARGET_FPS = 1\nconst MAX_TARGET_FPS = 240\n\nexport function clampFps(value: number): number {\n if (!Number.isFinite(value) || value <= 0)\n return DEFAULT_SETTINGS.targetFps\n return Math.min(MAX_TARGET_FPS, Math.max(MIN_TARGET_FPS, Math.round(value)))\n}\n\n// ---------------------------------------------------------------------------\n// SettingsProvider — single source of truth for the transcript filters.\n//\n// `onChange` is called with the latest settings whenever a row toggles, so\n// the host can persist them. Persistence pathing lives outside this module\n// to keep the settings layer portable across renderers (TUI, GUI, …).\n// ---------------------------------------------------------------------------\n\ninterface SettingsContextValue {\n settings: Settings\n /** Flip a boolean setting in place. Restricted at the type level to boolean-valued keys. */\n toggle: (key: BooleanSettingKey) => void\n /** Write any setting key to a new value. Used by string-valued choice rows. */\n setSetting: <K extends keyof Settings>(key: K, value: Settings[K]) => void\n}\n\nconst SettingsContext = createContext<SettingsContextValue | null>(null)\n\nexport function SettingsProvider({\n initial,\n onChange,\n children,\n}: {\n initial: Settings\n onChange?: (settings: Settings) => void\n children: ReactNode\n}) {\n // Normalized on load too: an older build (or a hand edit) may have persisted\n // a dependent toggle without its requirement.\n const [settings, setSettings] = useState<Settings>(() => enforceSettingsInvariants(initial))\n\n // `onChange` runs as a side-effect *after* commit, not inside the state\n // updater. Two reasons:\n // 1. React 18 strict-mode dev runs invoke state updaters twice for\n // every transition; a `disk.save(...)` inside the updater would\n // double-fire there.\n // 2. Pure-update is the React contract — updaters must be free of\n // observable side-effects. Persisting from inside breaks the\n // contract regardless of strict mode.\n //\n // A ref keeps the latest `onChange` reachable from the effect without\n // forcing the effect to re-run on every render the parent re-renders.\n // The `initialMount` ref skips the very first commit (we don't want to\n // persist the initial state we just loaded from disk).\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n const initialMount = useRef(true)\n useEffect(() => {\n if (initialMount.current) {\n initialMount.current = false\n return\n }\n onChangeRef.current?.(settings)\n }, [settings])\n\n const toggle = useCallback((key: BooleanSettingKey) => {\n setSettings(prev => enforceSettingsInvariants({ ...prev, [key]: !prev[key] }))\n }, [])\n\n const setSetting = useCallback(<K extends keyof Settings>(key: K, value: Settings[K]) => {\n setSettings(prev => (prev[key] === value ? prev : enforceSettingsInvariants({ ...prev, [key]: value })))\n }, [])\n\n const value = useMemo<SettingsContextValue>(\n () => ({ settings, toggle, setSetting }),\n [settings, toggle, setSetting],\n )\n\n return <SettingsContext.Provider value={value}>{children}</SettingsContext.Provider>\n}\n\nexport function useSettings(): SettingsContextValue {\n const ctx = useContext(SettingsContext)\n if (!ctx)\n throw new Error('useSettings must be used inside <SettingsProvider>')\n return ctx\n}\n\n/**\n * Keys of `Settings` whose value type is exactly `boolean`. Used to type\n * the toggle table so `SETTINGS_TOGGLES.key` only ever points to a\n * boolean-valued setting — string- and array-valued settings (theme,\n * enabledSkills, …) live in `SETTINGS_CHOICES` or actions.\n *\n * `-?` strips optionality from the mapped output so `Settings[K]` doesn't\n * silently broaden into `T | undefined` and short-circuit the `extends\n * boolean` check via `undefined`.\n */\nexport type BooleanSettingKey = {\n [K in keyof Settings]-?: Settings[K] extends boolean ? K : never\n}[keyof Settings]\n\n/**\n * Setting categories — drives the tab grouping in the TUI's\n * `SettingsModal` and, more importantly, gives any embedder (GUI,\n * web settings panel, …) a stable taxonomy to bucket settings under\n * without re-hardcoding the split.\n *\n * - `'agent'` — agent behaviour, capabilities, persistence,\n * data handling, auth. \"What zidane DOES.\"\n * - `'ui'` — display, rendering, theming, animations,\n * chrome density, keybindings. \"How zidane LOOKS\n * and feels.\"\n *\n * Borderline calls (`showAllProjects`, `hideSubagentOutput`,\n * `resumeLastSession`) are documented inline at each row.\n */\nexport type SettingsCategory = 'agent' | 'ui'\n\nexport interface SettingsCategoryDescriptor {\n id: SettingsCategory\n /** Display label — used by the TUI tab strip + any GUI tab/section header. */\n label: string\n /** One-line gloss the GUI can show as a section subtitle. The TUI omits this. */\n description: string\n}\n\n/**\n * Ordered list of categories. Display order in any consumer (TUI tabs,\n * GUI sidebar, settings JSON dump) should match this array — keeps\n * mental model consistent across surfaces.\n */\nexport const SETTINGS_CATEGORIES: readonly SettingsCategoryDescriptor[] = [\n { id: 'agent', label: 'Agent', description: 'behavior, capabilities, persistence, auth' },\n { id: 'ui', label: 'UI', description: 'display, rendering, theming, animations, keybindings' },\n]\n\n/**\n * Static description of every togglable setting, in display order. The TUI's\n * `SettingsModal` and any future GUI settings panel build their row list\n * from this so labels/descriptions stay in one place.\n */\nexport interface SettingsToggle {\n key: BooleanSettingKey\n label: string\n description: string\n category: SettingsCategory\n /**\n * Another boolean setting this one is meaningless without. Renderers grey\n * the row out and say so while the requirement is off; the write layer\n * (`enforceSettingsInvariants`) guarantees the value can never be `true`\n * while the requirement is `false`, whichever client or file wrote it.\n */\n requires?: BooleanSettingKey\n}\n\nexport const SETTINGS_TOGGLES: readonly SettingsToggle[] = [\n // ----- Agent (behaviour / data handling) -------------------------------\n { category: 'agent', key: 'safeMode', label: 'Safe mode', description: 'prompt before each tool call (unless safelisted)' },\n { category: 'agent', key: 'orchestration', requires: 'useDaemon', label: 'Main session (orchestration)', description: 'pin a Main session that creates, steers and reads other sessions and receives their outcomes in its own history. Requires the daemon runtime' },\n { category: 'agent', key: 'useDaemon', label: 'Use daemon runtime (experimental)', description: 'run sessions in a shared daemon: runs survive window close and every TUI/GUI window shares the live stream, drafts, queue and settings. Takes effect on the next session activation' },\n { category: 'agent', key: 'resumeLastSession', label: 'Start from last session', description: 'auto-resume on launch (off = sessions list / fresh chat)' },\n { category: 'agent', key: 'persistToolResults', label: 'Persist large tool results', description: 'write >8 KiB tool outputs to disk and inline a preview' },\n { category: 'agent', key: 'loadProjectExtensions', label: 'Load project extensions', description: 'run extensions from <project>/.zidane/extensions (off by default, they execute arbitrary local code)' },\n { category: 'agent', key: 'importForeignConfigs', label: 'Import configurations from other agents', description: 'discover MCP servers and skills from Claude Code, Codex and Pi configs (imported stdio servers run commands from files zidane does not own)' },\n { category: 'agent', key: 'importRulerConfigs', label: 'Support Ruler configuration (.ruler)', description: 'discover MCP servers, skills and instructions from .ruler/ and ~/.config/ruler (read-only, zidane\\'s own configs win collisions; stdio servers also need \"Import configurations from other agents\")' },\n { category: 'agent', key: 'attachmentsEnabled', label: 'Paste attachments', description: 'turn dragged files and multiline pastes into attachments (off = everything pastes inline)' },\n { category: 'agent', key: 'externalEditor', label: 'Edit prompt in $EDITOR', description: 'edit the current prompt in $EDITOR / $VISUAL with ctrl+g, reloaded when you save and quit' },\n { category: 'agent', key: 'inlineAutocomplete', label: 'Inline autocomplete', description: 'ghost-text suggestions in the prompt, tab accepts (file paths and extension providers)' },\n { category: 'agent', key: 'autoCompact', label: 'Auto-compact', description: 'summarize the conversation when context fills past the threshold below' },\n { category: 'agent', key: 'subagentModels', label: 'Allow subagents to use different models', description: 'let the agent delegate to cheaper models with `model_search` (off = children inherit your model)' },\n { category: 'agent', key: 'resumeOnMaxTokens', label: 'Auto-resume on output limit', description: 'when a turn hits the model\\'s output-token ceiling mid-answer, continue instead of stopping (off = stop at the limit)' },\n { category: 'agent', key: 'checkForUpdates', label: 'Check for updates', description: 'check npm once a day and flag new releases in the footer (zidane upgrade works either way)' },\n { category: 'agent', key: 'subscriptionUsage', label: 'Read subscription usage', description: 'ask signed-in providers for rate-limit windows and credits (off = no automatic requests, ctrl+r in the panel still fetches once)' },\n // ----- UI (display / rendering) ----------------------------------------\n // `showAllProjects` / `hideSubagentOutput` are arguably feature flags\n // but their visible effect is purely \"what shows up in the transcript /\n // sessions list\" — bucketing as UI keeps the Agent tab focused on\n // behaviour the agent observes, not what the user sees.\n { category: 'ui', key: 'showAllProjects', label: 'All projects', description: 'list sessions from every project (off = current only)' },\n { category: 'ui', key: 'hideSubagentOutput', label: 'Hide subagent output', description: 'collapse subagent runs to start/done markers' },\n { category: 'ui', key: 'showThinking', label: 'Thinking blocks', description: 'agent reasoning shown inline' },\n { category: 'ui', key: 'showToolResults', label: 'Tool outputs', description: 'the ┃ result blocks under tool calls' },\n { category: 'ui', key: 'groupToolCalls', label: 'Group tool calls', description: 'collapse each exchange\\'s tool calls into one row, click to expand' },\n { category: 'ui', key: 'showFullShellOutput', label: 'Full shell output', description: 'show every line of ! shell output instead of the last 10 (display only, the model still gets the trimmed result)' },\n { category: 'ui', key: 'showRunReceipt', label: 'Run receipt', description: 'the dimmed model · time · turns · cost · tokens line after each run' },\n { category: 'ui', key: 'showEditDiffs', label: 'Edit diffs', description: 'render edit / multi_edit / write_file as a unified diff' },\n { category: 'ui', key: 'showCompactionSummary', label: 'Compaction summaries', description: 'show the generated summary text under the ⎯⎯ compacted boundary row (off = metadata only)' },\n { category: 'ui', key: 'smoothStreaming', label: 'Smooth streaming', description: 'reveal streamed text character by character (typewriter effect)' },\n { category: 'ui', key: 'showTodoIndicator', label: 'Todo indicator', description: 'show the `n/total todos` chip in the footer (ctrl+t opens the list regardless)' },\n { category: 'ui', key: 'showUsageIndicator', label: 'Usage indicator', description: 'show the subscription gauge (▰ 5h 43%) in the footer (ctrl+p opens the panel regardless)' },\n { category: 'ui', key: 'showThrobber', label: 'Streaming throbber', description: 'animated glyphs under the last message while the agent works' },\n { category: 'ui', key: 'artifacts', label: 'Artifacts', description: 'track pull requests, branches, commits and recorded files per session' },\n { category: 'ui', key: 'fileLogging', label: 'File logging', description: 'persist this session\\'s logs to ~/.zidane/logs/<datetime>.txt (the Logs tab shows them regardless)' },\n { category: 'ui', key: 'mouseInteractions', label: 'Clickable UI', description: 'mouse clicks, hovers and wheel scrolling on hints and rows (off = terminal-native text selection and scrollback)' },\n]\n\n/**\n * Cross-setting invariants, applied at EVERY write.\n *\n * Settings are a shared file with three writers (the TUI, the GUI main\n * process, the daemon) and no lock; a rule enforced in one client is a rule\n * the others break. So this is the single definition, derived from the\n * `requires` declarations on the toggle rows themselves — adding a\n * dependency is one field on one row, and the UI hint and the write-time\n * guarantee follow from it together.\n *\n * Pure and idempotent: returns the same object when nothing changes, so React\n * state and file writers can short-circuit on identity.\n */\n/**\n * Return type of {@link enforceSettingsInvariants}: the input shape with every\n * boolean setting widened to `boolean`. The function exists to flip toggles,\n * so a literal `true` from a call-site object literal must not survive into\n * the result type — it would let `expect(next.x).toBe(false)` fail to compile\n * against a value that is, in fact, `false`.\n */\nexport type WithInvariants<T> = { [K in keyof T]: K extends BooleanSettingKey ? boolean : T[K] }\n\nexport function enforceSettingsInvariants<T extends Partial<Settings>>(settings: T): WithInvariants<T> {\n let next: T | null = null\n for (const row of SETTINGS_TOGGLES) {\n if (!row.requires)\n continue\n const current = (next ?? settings)[row.key]\n const requirement = (next ?? settings)[row.requires]\n if (current === true && requirement !== true)\n next = { ...(next ?? settings), [row.key]: false }\n }\n return (next ?? settings) as WithInvariants<T>\n}\n\n/**\n * String-valued settings that pick between a known set of options. The\n * `SettingsModal` renders these as one row showing the active label, where\n * `enter` cycles to the next option (wrapping back to the first). Same\n * shape any GUI settings panel can consume — render the options array as a\n * dropdown or segmented control.\n *\n * Keep `options` in stable display order; cycling depends on it.\n */\nexport interface SettingsChoice<K extends keyof Settings = keyof Settings> {\n key: K\n label: string\n description: string\n options: readonly { value: Settings[K], label: string }[]\n category: SettingsCategory\n}\n\nexport const SETTINGS_CHOICES: readonly SettingsChoice[] = [\n // ----- Agent (behaviour / data handling) -------------------------------\n {\n category: 'agent',\n key: 'interactionMode',\n label: 'Interaction mode',\n description: 'ask normally, answer automatically from the repository, or turn the interaction tools off',\n options: [\n { value: 'ask', label: 'Ask normally' },\n { value: 'skip', label: 'Skip interactions' },\n { value: 'disabled', label: 'Disable tools' },\n ],\n },\n {\n category: 'agent',\n key: 'autoCompactThreshold',\n label: 'Auto-compact threshold',\n description: 'fraction of the effective context window before compaction fires',\n options: [\n { value: 0.6, label: '60%' },\n { value: 0.7, label: '70%' },\n { value: 0.8, label: '80%' },\n { value: 0.9, label: '90%' },\n ],\n },\n {\n category: 'agent',\n key: 'userInstructionsScope',\n label: 'User instructions',\n description: 'which AGENTS.md / CLAUDE.md files load into the system prompt',\n options: [\n { value: 'both', label: 'Both (merged)' },\n { value: 'user', label: 'User only' },\n { value: 'project', label: 'Project only' },\n { value: 'none', label: 'None' },\n ],\n },\n {\n category: 'agent',\n key: 'maxOutputTokens',\n label: 'Max output tokens',\n description: 'hard cap on tokens the model may emit per turn (Model max = no cap, follow the model)',\n options: [\n { value: 0, label: 'Model max' },\n { value: 8192, label: '8K' },\n { value: 16384, label: '16K' },\n { value: 32000, label: '32K' },\n { value: 64000, label: '64K' },\n ],\n },\n {\n category: 'agent',\n key: 'retryMaxAttempts',\n label: 'Provider retry attempts',\n description: 'max attempts per provider call, for retryable errors and silent stream stalls (applies to the session and its subagents)',\n options: [\n { value: 0, label: 'Default (3)' },\n { value: 1, label: '1 (no retry)' },\n { value: 2, label: '2' },\n { value: 3, label: '3' },\n { value: 5, label: '5' },\n { value: 8, label: '8' },\n ],\n },\n {\n category: 'agent',\n key: 'maxTurns',\n label: 'Max turns / run',\n description: 'stop a run after this many agent turns (Unlimited = follow the trajectory to completion)',\n options: [\n { value: 0, label: 'Unlimited' },\n { value: 25, label: '25' },\n { value: 50, label: '50' },\n { value: 100, label: '100' },\n { value: 200, label: '200' },\n ],\n },\n {\n category: 'agent',\n key: 'maxCostUsd',\n label: 'Max cost / run',\n description: 'stop a run once its estimated cost crosses this budget (Unlimited = no ceiling)',\n options: [\n { value: 0, label: 'Unlimited' },\n { value: 1, label: '$1' },\n { value: 5, label: '$5' },\n { value: 10, label: '$10' },\n { value: 25, label: '$25' },\n ],\n },\n {\n category: 'agent',\n key: 'maxTotalTokens',\n label: 'Max tokens / run',\n description: 'stop a run once total tokens cross this budget (Unlimited = no ceiling)',\n options: [\n { value: 0, label: 'Unlimited' },\n { value: 500_000, label: '500K' },\n { value: 1_000_000, label: '1M' },\n { value: 2_000_000, label: '2M' },\n { value: 5_000_000, label: '5M' },\n ],\n },\n {\n category: 'agent',\n key: 'maxWallMinutes',\n label: 'Max run time',\n description: 'stop a run after this much wall-clock time (Unlimited = no ceiling)',\n options: [\n { value: 0, label: 'Unlimited' },\n { value: 5, label: '5 min' },\n { value: 15, label: '15 min' },\n { value: 30, label: '30 min' },\n { value: 60, label: '60 min' },\n ],\n },\n // ----- UI (display / rendering) ----------------------------------------\n {\n category: 'ui',\n key: 'toolCallDisplay',\n label: 'Tool calls display',\n description: 'how `↳ <tool>` lines render: hidden, per-tool summary, or full JSON',\n options: [\n { value: 'formatted', label: 'Formatted' },\n { value: 'full', label: 'Full' },\n { value: 'hidden', label: 'Hidden' },\n ],\n },\n {\n category: 'ui',\n key: 'editDiffDisplay',\n label: 'Edit diff density',\n description: 'full unified diff with line numbers, or a compact per-hunk summary',\n options: [\n { value: 'full', label: 'Full' },\n { value: 'compact', label: 'Compact' },\n ],\n },\n {\n category: 'ui',\n key: 'theme',\n label: 'Theme',\n description: 'colors + markdown / syntax styles',\n options: Object.values(BUILTIN_THEMES).map(t => ({ value: t.id, label: t.label })),\n },\n {\n category: 'ui',\n key: 'uiMode',\n label: 'UI mode',\n description: 'chat screen density: full shows every shortcut, minimal hides them (ctrl+y to peek)',\n options: [\n { value: 'full', label: 'Full' },\n { value: 'minimal', label: 'Minimal' },\n ],\n },\n {\n category: 'ui',\n key: 'targetFps',\n label: 'Renderer FPS',\n description: '30 saves CPU, 60 recommended, 120 for ProMotion and modern terminals',\n options: [\n { value: 30, label: '30 fps' },\n { value: 60, label: '60 fps' },\n { value: 120, label: '120 fps' },\n ],\n },\n {\n category: 'ui',\n key: 'ctrlCBehavior',\n label: 'Ctrl+C behavior',\n description: 'quit immediately, confirm first, or stop the current action and quit on a second press',\n options: [\n { value: 'quit', label: 'Quit' },\n { value: 'quit-confirm', label: 'Confirm' },\n { value: 'stop-then-quit', label: 'Stop, then quit' },\n ],\n },\n {\n category: 'ui',\n key: 'terminalTitle',\n label: 'Terminal title',\n description: 'what the window or tab title shows: off leaves it alone, zidane pins the app name, session tracks the session title, icon + session adds your icon',\n options: [\n { value: 'off', label: 'Off' },\n { value: 'app', label: 'zidane' },\n { value: 'session', label: 'Session title' },\n { value: 'icon-session', label: 'Icon + session' },\n ],\n },\n]\n\n/**\n * Keys of `Settings` whose value type is exactly `string`. Bounds the\n * input table's `key` the same way {@link BooleanSettingKey} bounds the\n * toggle table — a free-text row can only point at a string-valued\n * setting.\n */\nexport type StringSettingKey = {\n [K in keyof Settings]-?: Settings[K] extends string ? K : never\n}[keyof Settings]\n\n/**\n * Free-text settings edited via a single-line input. The `SettingsModal`\n * renders these as a row showing the current value (or `placeholder` when\n * empty); `enter` opens an inline editor. Same shape a GUI can consume as\n * a text field.\n */\nexport interface SettingsInput {\n key: StringSettingKey\n label: string\n description: string\n /** Shown when the value is empty — also the conventional \"default\" hint. */\n placeholder: string\n category: SettingsCategory\n}\n\nexport const SETTINGS_INPUTS: readonly SettingsInput[] = [\n {\n category: 'ui',\n key: 'sessionIcon',\n label: 'Session icon',\n description: 'shown before the session title when \"Terminal title\" is set to icon + session (empty = ⚽)',\n placeholder: '⚽',\n },\n {\n category: 'ui',\n key: 'attentionIcon',\n label: 'Attention icon',\n description: 'replaces the session icon while zidane waits on you, like tool approvals (empty = ⚠️)',\n placeholder: '⚠️',\n },\n {\n category: 'ui',\n key: 'activeIcon',\n label: 'Active icon',\n description: 'replaces the session icon while zidane is running (empty = ⚡), attention wins when input is needed',\n placeholder: '⚡',\n },\n]\n","/**\n * Renderer-agnostic state machine for an \"enabled allowlist\" — the shape\n * used by `settings.enabledSkills` / `settings.enabledMcps` (and any future\n * `enabledX` toggle backed by a `Settings` field).\n *\n * Semantics, kept identical across all three call sites:\n * - `undefined` → every catalog entry is enabled (default — user has\n * never opened the picker; new entries flow in).\n * - `[]` → the whole subsystem is off.\n * - `[names]` → explicit allowlist; the persisted shape stays a\n * concrete array once the user has toggled anything.\n *\n * The hook exposes a live `enabledSet` (Set<string>) + a `toggle(name)`\n * callback that persists the result through `useSettings().setSetting`.\n * The first toggle seeds the persisted allowlist from the current catalog\n * so newly-added entries don't silently drop on the next launch.\n *\n * Renderer-neutral: works for the TUI's `<ToggleListModal>`, a GUI\n * checkbox list, a CLI flag tool. Hook tests live alongside the chat\n * suite; the rendering layer is tested where it lives.\n */\n\nimport { useCallback, useMemo } from 'react'\nimport { useSettings } from './settings-context'\n\n/** Settings keys whose value type is `readonly string[] | undefined`. */\nexport type EnabledAllowlistKey = 'enabledSkills' | 'enabledMcps' | 'enabledExtensions' | 'enabledPlugins'\n\nexport interface EnabledToggleSet {\n /** Live set of enabled names — `Set` for O(1) `has`. */\n enabledSet: ReadonlySet<string>\n /** Toggle one name on/off; persists immediately via `setSetting`. */\n toggle: (name: string) => void\n}\n\n/**\n * Bind an \"enabled allowlist\" setting to a discovered catalog.\n *\n * `keyOf` extracts the persisted identity from a catalog entry (skill name,\n * MCP server name, …). Pass a stable, collision-free key — the persisted\n * allowlist is keyed against it forever.\n */\nexport function useEnabledToggleSet<T>(opts: {\n catalog: readonly T[]\n keyOf: (entry: T) => string\n settingKey: EnabledAllowlistKey\n /**\n * Names that survive the persisted-allowlist GC even when absent from\n * `catalog`. Use when the catalog deliberately hides entries that are\n * still known to exist — e.g. errored / untrusted extensions are kept\n * out of the toggle list but must not be dropped from the allowlist\n * by an unrelated toggle. Defaults to the catalog keys.\n */\n gcKeys?: readonly string[]\n}): EnabledToggleSet {\n const { settings, setSetting } = useSettings()\n const { catalog, keyOf, settingKey, gcKeys } = opts\n const persisted = settings[settingKey]\n\n const enabledSet = useMemo<ReadonlySet<string>>(() => {\n if (persisted === undefined)\n return new Set(catalog.map(keyOf))\n return new Set(persisted)\n }, [persisted, catalog, keyOf])\n\n // Live catalog membership — used to garbage-collect stale persisted\n // names on every write. Without this, a skill / MCP that was once\n // enabled and then removed from disk lingers in the JSON forever\n // (the next toggle round-trips it back into the array). Functionally\n // harmless today (the agent ignores unknown names) but it lets the\n // persisted state drift away from reality. `gcKeys` widens the\n // \"known\" set beyond the rendered catalog (see its JSDoc).\n const catalogKeys = useMemo(\n () => new Set(gcKeys ?? catalog.map(keyOf)),\n [gcKeys, catalog, keyOf],\n )\n\n const toggle = useCallback((name: string) => {\n // Seed from the current `enabledSet` (covers both the seeded-from-catalog\n // and explicit-allowlist branches). Persist as a sorted array filtered\n // against the live catalog so stale names — entries deleted from disk\n // between launches — don't linger forever in the persisted allowlist.\n // `undefined` is reserved for \"fresh install, accept new discoveries\";\n // once the user toggles once the persistence shape stays a concrete list.\n const next = new Set(enabledSet)\n if (next.has(name))\n next.delete(name)\n else\n next.add(name)\n const persisted = [...next]\n .filter(n => catalogKeys.has(n))\n .sort()\n setSetting(settingKey, persisted)\n }, [enabledSet, catalogKeys, settingKey, setSetting])\n\n return { enabledSet, toggle }\n}\n","/**\n * Project file discovery for the `@`-prefixed files completion provider.\n *\n * Primary strategy — `git ls-files --cached --others --exclude-standard`.\n * Git already understands `.gitignore`, `.git/info/exclude`, and global\n * excludes, so we get correctness for free + sub-second performance on\n * even huge monorepos. Falls back to a depth-limited filesystem walk when\n * git isn't available or the directory isn't a repo.\n *\n * Pure: callers cache the returned list (e.g. in App state) and pass it\n * into the provider's `getCatalog`. The list is a snapshot — refresh on\n * cwd change or via a user-driven action.\n */\n\nimport type { Dirent, Stats } from 'node:fs'\nimport { spawn } from 'node:child_process'\nimport { readdir, stat } from 'node:fs/promises'\nimport { resolve, sep } from 'node:path'\nimport { errorMessage } from '../errors'\n\n/** One row in the project file catalog. `path` is forward-slashed + relative to `cwd`. */\nexport interface FileEntry {\n /** Forward-slashed relative path from the discovery cwd. Stable across OSes. No trailing slash for dirs. */\n path: string\n /** Basename — used for prefix ranking in the completion provider. */\n name: string\n /** Source — `git` when listed via `git ls-files`, `fs` from the walk fallback. */\n source: 'git' | 'fs'\n /**\n * Entry kind. Directories are derived from the discovered file paths\n * (every ancestor of a listed file), so only directories that contain\n * at least one non-ignored file appear — mirroring `git ls-files`\n * semantics, which never lists empty or fully-ignored dirs.\n */\n kind: 'file' | 'dir'\n}\n\n/**\n * Hard cap on the discovered file count. Bigger than any reasonable repo's\n * source tree, small enough that a runaway scan can't pin the renderer's\n * filter. Provider's substring match runs over the full list per\n * keystroke; 10k entries × cheap `.includes()` is well under one frame.\n */\nconst DEFAULT_MAX_FILES = 10_000\n\n/**\n * Names skipped during the fs-walk fallback. Mirrors what `git ls-files`\n * would exclude via the default `.gitignore` shipped in most repos —\n * `node_modules`, `dist`, build caches — plus the `.git` dir itself.\n * Best-effort; the git path is the authoritative one.\n */\nconst FS_WALK_SKIP = new Set<string>([\n '.git',\n '.hg',\n '.svn',\n 'node_modules',\n 'dist',\n 'build',\n 'out',\n 'coverage',\n '.next',\n '.nuxt',\n '.cache',\n '.turbo',\n '.parcel-cache',\n '.vercel',\n '.svelte-kit',\n '.expo',\n '.gradle',\n 'target',\n '__pycache__',\n '.pytest_cache',\n '.venv',\n 'venv',\n '.idea',\n '.vscode-test',\n])\n\n/** Options for `listProjectFiles`. */\nexport interface ListProjectFilesOptions {\n /** Discovery root. Default: `process.cwd()`. */\n cwd?: string\n /**\n * Cap on discovered files. Default: 10,000. Bigger lists fall back to\n * a truncated set. Derived directory entries are appended on top with\n * their own equal cap, so the returned array can exceed `maxFiles`\n * (bounded by 2×).\n */\n maxFiles?: number\n /** Aborts the scan early. Useful when re-running on rapid project switches. */\n signal?: AbortSignal\n}\n\n/**\n * Discover every non-ignored file under `cwd`, plus the directories that\n * contain them (derived from the file paths, `kind: 'dir'`). Tries\n * `git ls-files` first; on failure (no git, not a repo, abort) walks the\n * fs with a hand-rolled skip list.\n *\n * Errors are not thrown — the function always returns an array (possibly\n * empty). Callers wanting failure diagnostics can opt into them via\n * `ZIDANE_DEBUG`.\n */\nexport async function listProjectFiles(opts: ListProjectFilesOptions = {}): Promise<FileEntry[]> {\n const cwd = resolve(opts.cwd ?? process.cwd())\n const maxFiles = opts.maxFiles ?? DEFAULT_MAX_FILES\n const signal = opts.signal\n\n try {\n const paths = await listViaGit(cwd, signal)\n return toEntries(paths, 'git', maxFiles)\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] git ls-files failed (${errorMessage(err)}) — falling back to fs walk\\n`)\n try {\n const paths = await listViaFs(cwd, maxFiles, signal)\n return toEntries(paths, 'fs', maxFiles)\n }\n catch (fsErr) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] fs walk failed: ${errorMessage(fsErr)}\\n`)\n return []\n }\n }\n}\n\n/**\n * Run `git ls-files --cached --others --exclude-standard` and return paths.\n *\n * - `--cached` — tracked files.\n * - `--others` — untracked but not ignored.\n * - `--exclude-standard` — apply `.gitignore`, `.git/info/exclude`, the\n * user's global excludes file, AND skip files marked `assume-unchanged`.\n *\n * Throws on non-zero exit (not a repo / no git) so the fallback path can\n * pick up. Aborts cleanly on `signal.aborted`.\n */\nasync function listViaGit(cwd: string, signal: AbortSignal | undefined): Promise<string[]> {\n return new Promise<string[]>((resolveP, rejectP) => {\n const child = spawn('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z'], {\n cwd,\n stdio: ['ignore', 'pipe', 'pipe'],\n })\n // Buffered as chunk arrays — joining once at the end keeps GC quiet on\n // huge repos. A `let stdout += chunk` accumulator reallocates the whole\n // buffer per chunk; with a ~10MB `git ls-files` output that's quadratic\n // in chunk count.\n const stdoutChunks: string[] = []\n const stderrChunks: string[] = []\n const onAbort = () => child.kill('SIGTERM')\n if (signal) {\n if (signal.aborted) {\n child.kill('SIGTERM')\n rejectP(new Error('aborted'))\n return\n }\n signal.addEventListener('abort', onAbort, { once: true })\n }\n child.stdout.setEncoding('utf8')\n child.stderr.setEncoding('utf8')\n child.stdout.on('data', chunk => stdoutChunks.push(chunk))\n child.stderr.on('data', chunk => stderrChunks.push(chunk))\n child.on('error', (err) => {\n signal?.removeEventListener('abort', onAbort)\n rejectP(err)\n })\n child.on('close', (code) => {\n signal?.removeEventListener('abort', onAbort)\n if (signal?.aborted) {\n rejectP(new Error('aborted'))\n return\n }\n if (code !== 0) {\n rejectP(new Error(`git ls-files exited ${code}: ${stderrChunks.join('').trim()}`))\n return\n }\n // `-z` separates with NUL so paths containing newlines stay intact.\n const paths = stdoutChunks.join('').split('\\0').filter(p => p.length > 0)\n resolveP(paths)\n })\n })\n}\n\n/**\n * Depth-first fs walk fallback. Used when git is unavailable. Honors a\n * hard-coded skip list; doesn't read `.gitignore`. Acceptable for hosts\n * outside a git checkout (e.g. a freshly-`unzip`ped project).\n */\nasync function listViaFs(\n cwd: string,\n maxFiles: number,\n signal: AbortSignal | undefined,\n): Promise<string[]> {\n const out: string[] = []\n const stack: string[] = ['.']\n while (stack.length > 0) {\n if (signal?.aborted)\n throw new Error('aborted')\n const rel = stack.pop()!\n const abs = rel === '.' ? cwd : resolve(cwd, rel)\n let entries: Dirent[]\n try {\n entries = await readdir(abs, { withFileTypes: true })\n }\n catch {\n // Unreadable dir — skip silently. EACCES / ENOENT during a walk\n // shouldn't fail the whole discovery.\n continue\n }\n for (const e of entries) {\n if (FS_WALK_SKIP.has(e.name))\n continue\n const childRel = rel === '.' ? e.name : `${rel}${sep}${e.name}`\n if (e.isDirectory()) {\n stack.push(childRel)\n }\n else if (e.isFile()) {\n out.push(toForwardSlash(childRel))\n if (out.length >= maxFiles)\n return out\n }\n else if (e.isSymbolicLink()) {\n // Resolve once; ignore broken links and follow only if it points\n // to a regular file. We don't recurse into symlinked dirs to\n // avoid cycles.\n let st: Stats\n try {\n st = await stat(resolve(abs, e.name))\n }\n catch {\n continue\n }\n if (st.isFile()) {\n out.push(toForwardSlash(childRel))\n if (out.length >= maxFiles)\n return out\n }\n }\n }\n }\n return out\n}\n\nfunction toForwardSlash(p: string): string {\n return sep === '/' ? p : p.replaceAll(sep, '/')\n}\n\nfunction toEntries(paths: readonly string[], source: 'git' | 'fs', maxFiles: number): FileEntry[] {\n const seen = new Set<string>()\n const out: FileEntry[] = []\n for (const raw of paths) {\n const path = toForwardSlash(raw)\n if (path.length === 0 || seen.has(path))\n continue\n seen.add(path)\n const lastSlash = path.lastIndexOf('/')\n const name = lastSlash >= 0 ? path.slice(lastSlash + 1) : path\n out.push({ path, name, source, kind: 'file' })\n if (out.length >= maxFiles)\n break\n }\n // Directory pass — derive every ancestor dir of the (possibly capped)\n // file list so `@` can also reference directories. Appended after the\n // files with its own `maxFiles` cap (deep trees can yield more dirs\n // than files) so a truncated file list never crowds out the dirs and\n // the total stays bounded at 2×`maxFiles`.\n const dirs = new Set<string>()\n for (const entry of out) {\n let p = entry.path\n for (let idx = p.lastIndexOf('/'); idx > 0; idx = p.lastIndexOf('/')) {\n p = p.slice(0, idx)\n if (dirs.has(p))\n break // ancestors of an already-seen dir are seen too\n dirs.add(p)\n }\n }\n let dirCount = 0\n for (const path of dirs) {\n const lastSlash = path.lastIndexOf('/')\n const name = lastSlash >= 0 ? path.slice(lastSlash + 1) : path\n out.push({ path, name, source, kind: 'dir' })\n if (++dirCount >= maxFiles)\n break\n }\n return out\n}\n","// ---------------------------------------------------------------------------\n// Footer hint composition.\n//\n// Pure presentation logic — given the current screen, run state, and a\n// resolved keybinding map, produce the ordered list of {@link Hint}s\n// the bottom-bar Footer should render. Lives in `zidane/chat` (not\n// `zidane/tui`) because it's renderer-agnostic: the only types involved\n// are `Hint`, `Screen`, `Settings['uiMode']`, and `KeyBindings`. A GUI\n// shell can consume the same list and paint it however it likes.\n//\n// Priority order is intentional and load-bearing:\n//\n// pending approval > live pending interaction > resumed pending\n// interaction > busy (streaming) > auth screen > sessions screen >\n// chat screen (idle).\n//\n// The first four return state-specific hint sets unconditionally —\n// `uiMode` is ignored there because those affordances (approve / abort\n// / navigate) are not the noise the minimal mode is targeting. Minimal\n// mode only collapses the chat-idle row.\n// ---------------------------------------------------------------------------\n\nimport type { Hint } from './hints'\nimport type { KeyBindings } from './keybindings'\nimport type { Screen, SessionMeta, Settings } from './types'\n\n/**\n * Options bag for {@link buildHints}. Flat shape so call sites read as a\n * checklist of \"what state do I have to surface\" — easy to thread\n * through React `useMemo` deps and to construct fresh in tests.\n */\nexport interface BuildHintsOptions {\n screen: Screen\n busy: boolean\n pending: boolean\n /**\n * True when a LIVE pending interaction is on screen (agent is mid-run,\n * awaiting the tool's Promise). Same nav hints as the safe-mode\n * approval picker — esc aborts the run.\n */\n pendingInteractionLive: boolean\n /**\n * True when the chat screen is showing a resumed (not live) pending\n * interaction. The agent isn't running; esc leaves the form without\n * persisting a response, so the hint reads \"leave for later\" — the\n * session re-enqueues the same interaction on next activation.\n */\n pendingInteractionResumed: boolean\n currentSession: SessionMeta | null\n hasMultipleAgents: boolean\n /**\n * Chat-screen chrome density — `'minimal'` collapses the idle chat\n * hint row to just `agent / model / keybindings`. Busy / pending /\n * select-turn / queue states keep their full hint sets because those\n * affordances are load-bearing (abort, navigate, approve).\n */\n uiMode: Settings['uiMode']\n modelLabel: string | null\n modelColor: string\n /** Current reasoning effort label, or `null` when the model has no reasoning knob. */\n effortLabel: string | null\n effortColor: string\n /** Foreground for the `/n` chord that introduces the effort label. */\n effortKeyColor: string\n /**\n * Comma-joined labels of the enabled model options (e.g. `'fast'`), or `null`\n * when none are on. Appended after the effort label in the model chord — when\n * the model has no reasoning knob, it rides the chord on its own.\n */\n modelOptionsLabel?: string | null\n agentLabel: string\n agentColor: string\n /**\n * Resolved keybindings — every hint key string flows through this so\n * a user-level rebind (e.g. `\"openSettings\": \"ctrl+p\"`) shows up live\n * in the bottom bar without us hard-coding `'ctrl+o'`.\n */\n keybindings: KeyBindings\n /**\n * Number of currently-dispatching tool calls. Drives the\n * `<cancelToolCall> cancel tool` chip in the `busy` hint row — without\n * it, the binding stays inert and there's no point advertising the\n * shortcut. Falls back to \"no chip\" when no tools are in flight\n * (model is mid-stream but hasn't dispatched yet).\n */\n inFlightToolCount: number\n /**\n * Number of currently-active skills. Drives the footer's\n * `✦ N skill(s)` chip on the chat screen — purely informational,\n * a passive surface for \"tool restrictions are in effect\". Hidden\n * when `0` so the row stays uncluttered for the common case.\n */\n activeSkillCount: number\n /** Foreground for the skills chip (typically `COLOR.brand`). */\n skillsChipColor: string\n /**\n * Progress label for the todo chip (`\"2/5\"` from `todoProgressLabel`), or\n * `null` to hide it.\n *\n * This replaced a full-width bar that printed the in-progress todo's TITLE\n * ABOVE the prompt. As a `Hint` it inherits the row's click-to-keypress\n * contract for free (`dispatchHintKey`), so clicking it opens the list the\n * same way `ctrl+t` does — and it sits below the prompt like every other\n * control instead of pushing the prompt down.\n */\n todoProgress: string | null\n /**\n * Pre-built `↑ vX.Y.Z` chip from `buildUpdateHint(useUpdateCheck())`,\n * or `null` when no update is available. Always last in the chat-screen\n * row so it's the first to drop when the terminal narrows.\n */\n updateHint: Hint | null\n /**\n * Pre-built prompt-cache toast, non-null only for a few seconds after a\n * `'cache:break'` hook fired (the host owns the visibility timer and the\n * severity split): warn-colored `⚡ cache break · <culprit> · +Nk tok · ~$0.48`\n * for attributable breaks, dim `⚡ cache expired · +Nk tok · ~$0.48` for\n * byte-stable TTL expiry — routine across human think-time, so it must not\n * cry wolf. Sits beside `updateHint` at the droppable end of the row: a\n * transient cost signal, not an action.\n */\n cacheBreakHint?: Hint | null\n /**\n * Sessions-screen list scope — `true` when the list spans all\n * projects. Drives the `tab` hint's label, which advertises the scope\n * the toggle switches TO. Optional so non-sessions callers can omit it.\n */\n sessionsScopeAll?: boolean\n}\n\n/**\n * Build the footer's shortcut hints for the current screen. On the chat\n * screen the model id rides next to its `ctrl+m` shortcut and the agent\n * label rides next to `shift+tab`, each in its accent color — the bar\n * doubles as the status display without needing separate badges. When\n * the active model exposes reasoning, the `ctrl+m` hint grows a\n * secondary `/n` chord with the current effort label, surfacing the\n * effort picker as a discoverable, in-place affordance.\n */\nexport function buildHints(options: BuildHintsOptions): Hint[] {\n const {\n screen,\n busy,\n pending,\n pendingInteractionLive,\n pendingInteractionResumed,\n currentSession,\n hasMultipleAgents,\n uiMode,\n modelLabel,\n modelColor,\n effortLabel,\n effortColor,\n effortKeyColor,\n modelOptionsLabel,\n agentLabel,\n agentColor,\n keybindings,\n inFlightToolCount,\n activeSkillCount,\n skillsChipColor,\n todoProgress,\n updateHint,\n cacheBreakHint = null,\n sessionsScopeAll,\n } = options\n\n // Todo progress chip. Built BEFORE the priority branches below because it is\n // STATUS, not a shortcut: it has to survive every one of them. The bar it\n // replaced (`TodoIndicator`) rendered on its own line above the prompt\n // whenever an `in_progress` item existed, entirely independent of `busy` —\n // and `in_progress` is set precisely while the agent is working and calling\n // `todowrite`. Building it after the early returns would hide it for the\n // whole window it exists to cover.\n //\n // Appended LAST in each branch so `clipHintsToWidth` (which drops from the\n // right) sheds it before the abort/navigate affordances, which must stay.\n const todosChip: Hint | null = todoProgress !== null\n ? { key: keybindings.openTodos, label: `${todoProgress} todos` }\n : null\n const withTodos = (hints: Hint[]): Hint[] => todosChip ? [...hints, todosChip] : hints\n\n // Priority: safe-mode approval > live interaction > resumed\n // interaction > plain streaming. All three pending shapes share the\n // ↑↓ / ↵ nav prefix; only the esc semantics differ.\n if (pending)\n return withTodos([{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'abort run' }])\n if (pendingInteractionLive)\n return withTodos([{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'abort run' }])\n if (pendingInteractionResumed)\n return withTodos([{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'leave for later' }])\n if (busy) {\n // When tools are dispatching OR background tasks are running,\n // advertise the per-call cancel shortcut alongside the run-wide\n // `esc abort`. Order: cancel-tool first (active affordance for\n // the visible tool/task rows) → esc (broader scope).\n const baseBusyHints: Hint[] = []\n if (inFlightToolCount > 0) {\n baseBusyHints.push({\n key: keybindings.cancelToolCall,\n label: inFlightToolCount === 1 ? 'cancel' : `cancel (${inFlightToolCount})`,\n })\n }\n baseBusyHints.push({ key: 'esc', label: 'abort' })\n return withTodos(baseBusyHints)\n }\n if (screen === 'auth')\n return [{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'exit' }]\n if (screen === 'sessions') {\n return [\n { key: '↑↓', label: 'navigate' },\n { key: '↵', label: 'open' },\n { key: 'tab', label: sessionsScopeAll ? 'current project' : 'all projects' },\n { key: keybindings.openSessionDetails, label: 'session' },\n { key: keybindings.openSettings, label: 'settings' },\n { key: 'esc', label: currentSession ? 'back' : 'exit' },\n ]\n }\n // Chat screen. Prompt-input shortcuts (`↵ send`, `shift+↵ newline`,\n // `↑↓ history`) live in the prompt box's overlay title — the bottom\n // bar only carries the most common agent / model / global affordances. The\n // `cycleAgent` binding shows the active label inline; the\n // `openModelPicker` binding shows the active model id inline; etc.\n // `cycleAgent` is suppressed when there's only one profile and\n // `openSessionDetails` only renders once a session exists.\n //\n // Minimal UI mode: strip the chat-idle bottom bar to just `agent`\n // (when there's >1 profile), `model/effort`, and `settings`. The\n // full keybinding catalog stays reachable from inside settings.\n // Busy / pending / select-turn / queue branches above already exited\n // before this point — minimal only affects the resting chat row.\n //\n // Compose the model hint. When the active model supports reasoning,\n // the primary `<openModelPicker> model` pair grows a secondary\n // `/n effort` chord that doubles as the discoverability hint for the\n // effort picker (`openEffortPicker`) and the status display for the\n // current effort. The chord shares the model accent so the pair\n // reads as one keyboard binding across every theme.\n // The `/l` chord carries reasoning effort plus any enabled model options,\n // all space-separated so they read as one model-info run (e.g.\n // `claude-opus-4-8 medium fast`). When the model has no reasoning knob, the\n // options ride the chord on their own.\n const chordLabel = [effortLabel, modelOptionsLabel].filter(Boolean).join(' ') || null\n const modelHint: Hint | null = modelLabel\n ? {\n key: keybindings.openModelPicker,\n label: modelLabel,\n labelColor: modelColor,\n ...(chordLabel\n ? { extra: { key: shortChord(keybindings.openEffortPicker), keySpec: keybindings.openEffortPicker, keyColor: effortKeyColor, label: chordLabel, labelColor: effortColor } }\n : {}),\n }\n : null\n // Skills chip — purely informational, no shortcut wired. We re-use\n // the Hint shape's `key` slot to render the glyph (`✦`) so the row\n // layout (key + label, padded breakpoints) stays consistent with\n // every other entry. The chip is omitted when no skills are active\n // so the row stays uncluttered for the common case.\n const skillsChip: Hint | null = activeSkillCount > 0\n ? {\n key: '✦',\n keyColor: skillsChipColor,\n label: activeSkillCount === 1 ? '1 skill' : `${activeSkillCount} skills`,\n labelColor: skillsChipColor,\n }\n : null\n // Background-task chip — fires when the user is idle (not `busy`)\n // but a task is still running. Gives them a discoverable way to\n // reach `ctrl+k` for kill. The `busy` branch above already covers\n // the dispatching case; this one is for \"agent finished its turn,\n // task still chugging\".\n const cancelTaskChip: Hint | null = inFlightToolCount > 0\n ? {\n key: keybindings.cancelToolCall,\n label: inFlightToolCount === 1 ? 'cancel task' : `cancel task (${inFlightToolCount})`,\n }\n : null\n // Minimal mode short-circuits the full composition. Same agent / model\n // hints as the full row (so a multi-agent setup still cycles, and the\n // model + effort chord stays visible), plus `settings` as the\n // discoverability anchor for everything we just dropped (keybindings\n // included — they have a tab in there).\n if (uiMode === 'minimal') {\n return [\n ...(hasMultipleAgents ? [{ key: keybindings.cycleAgent, label: agentLabel, labelColor: agentColor }] : []),\n ...(modelHint ? [modelHint] : []),\n { key: keybindings.openSettings, label: 'settings' },\n ]\n }\n return [\n ...(hasMultipleAgents ? [{ key: keybindings.cycleAgent, label: agentLabel, labelColor: agentColor }] : []),\n ...(modelHint ? [modelHint] : []),\n ...(skillsChip ? [skillsChip] : []),\n ...(todosChip ? [todosChip] : []),\n ...(cancelTaskChip ? [cancelTaskChip] : []),\n ...(currentSession ? [{ key: keybindings.openSessionDetails, label: 'session' }] : []),\n { key: keybindings.openSettings, label: 'settings' },\n { key: 'esc', label: 'sessions' },\n // Update chip lives at the right-most position so `clipHintsToWidth`\n // drops it first on narrow terminals — discoverability is nice but\n // the action shortcuts must stay.\n ...(cacheBreakHint ? [cacheBreakHint] : []),\n ...(updateHint ? [updateHint] : []),\n ]\n}\n\n/**\n * Shorten a binding spec for display as a \"chord continuation\" — the\n * `/n` after `ctrl+m model` in the chat footer. We only strip the\n * `ctrl+` prefix so user-customized chords (`alt+n`, `meta+shift+n`)\n * still render in full. Falls back to the verbatim spec when no\n * `ctrl+` prefix is found, which keeps the visual contract honest:\n * the rendered key always matches the bound trigger.\n */\nexport function shortChord(spec: string): string {\n return spec.startsWith('ctrl+') ? `/${spec.slice('ctrl+'.length)}` : spec\n}\n","// ---------------------------------------------------------------------------\n// Footer / title hint primitives.\n//\n// Pure data + width-estimation helpers consumed by the TUI's bottom-bar\n// footer, the prompt-box overlay, the sessions header, and any future\n// GUI shell that wants to render the same `<key> <label> · …` hint\n// strings. Plain-text math only — no renderer primitives, no colour\n// resolution, no React. Width estimates are intentionally `.length`-\n// based (assumes ASCII / single-cell glyphs); the consumer's renderer\n// is responsible for the actual painting and any wide-glyph accounting\n// it needs.\n// ---------------------------------------------------------------------------\n\n/**\n * Single hint entry — `<key> <label>`, optionally followed by an inline\n * secondary chord (`<key><extra.key> <label> <extra.label>`) for compound\n * shortcuts like `ctrl+m/n model effort`.\n *\n * Colour fields are advisory hex strings (resolved against the active\n * theme by the renderer); leaving them undefined lets the renderer pick\n * its conventional fallback (e.g. `warn` for keys, `dim` for labels,\n * `mute` for secondary).\n */\nexport interface Hint {\n key: string\n label: string\n /** Optional override for the key color. Renderer default: warn. */\n keyColor?: string\n /** Optional override for the label color. Renderer default: dim. */\n labelColor?: string\n /**\n * Optional secondary chord + value rendered inline after the primary\n * pair: `<key><extra.key> <label> <extra.label>`. Lets one hint advertise\n * a compound shortcut (e.g. `ctrl+m/n model effort`) without forcing\n * callers into a parallel struct or a custom renderer.\n */\n extra?: {\n key: string\n label: string\n /**\n * Dispatchable binding spec behind `extra.key` when the displayed\n * chord is an abbreviation (`/l` for `ctrl+l` — see `shortChord`).\n * The abbreviation itself doesn't parse as a keypress, so a renderer\n * that makes the secondary chord independently clickable needs the\n * full spec. Defaults to `extra.key` when omitted.\n */\n keySpec?: string\n /** Defaults to mute on the renderer side. */\n keyColor?: string\n /** Defaults to dim on the renderer side. */\n labelColor?: string\n }\n}\n\n/**\n * Truncate `text` to at most `max` characters, replacing the trailing\n * overflow with `…`. Edge cases:\n * - `max <= 0` → empty string (no room to render at all).\n * - `max === 1` → just the ellipsis glyph.\n * - `text.length <= max` → unchanged.\n *\n * Trailing-style truncation matches the natural read order of titles:\n * the prefix carries enough signal to identify the surface.\n */\nexport function truncateTrailing(text: string, max: number): string {\n if (max <= 0)\n return ''\n if (text.length <= max)\n return text\n if (max === 1)\n return '…'\n return `${text.slice(0, max - 1)}…`\n}\n\n// ---------------------------------------------------------------------------\n// Plain-text width estimates for responsive tiering.\n//\n// We approximate with `.length` instead of `stringWidth` because every glyph\n// involved (hint keys, provider names, model ids, token counts) is either\n// ASCII or a 1-cell symbol. The estimates only need to be precise enough to\n// pick a layout tier — being off by a column at the breakpoint is harmless.\n// ---------------------------------------------------------------------------\n\n/**\n * Plain-text width estimate for a list of {@link Hint}s rendered as\n * `<key> <label> · <key> <label> · …`. Exported so prompt-box overlays\n * can run the same responsive math as the bottom-bar footer when\n * deciding whether trigger hints fit. Pure / total.\n */\nexport function hintsLength(hints: readonly Hint[]): number {\n if (hints.length === 0)\n return 0\n return hints.reduce((sum, h, i) => sum + hintLength(h) + (i > 0 ? 3 : 0), 0)\n}\n\n/** Plain-text width of a single hint as rendered by `renderHintSpans`. */\nfunction hintLength(h: Hint): number {\n const base = h.key.length + 1 + h.label.length\n const extra = h.extra ? h.extra.key.length + 1 + h.extra.label.length : 0\n return base + extra\n}\n\n/**\n * Stable empty list so callers can compare by reference. Exported so\n * any consumer that needs to feed \"no primary hints\" into the same\n * `clipHintsToWidth` / `renderHintSpans` pipeline (e.g. the TUI's\n * minimal UI mode, where the prompt overlay shows only `@` / `/`\n * triggers) reuses the same frozen array instead of allocating a new\n * one each render.\n */\nexport const EMPTY_HINTS: readonly Hint[] = Object.freeze([])\n\n/**\n * Return the longest prefix of `hints` whose rendered width fits within\n * `budget`. Used to degrade hint rows gracefully at narrow terminal\n * widths instead of letting an absolutely-positioned hint row wrap\n * mid-segment (which paints the overflow over surrounding borders and\n * looks like garbled glyphs in a TUI).\n *\n * Prefix-only (no reordering, no last-hint priority) so the survivors\n * keep their authored order — the user's muscle memory for \"leftmost\n * hint = primary action\" stays intact as the terminal shrinks.\n */\nexport function clipHintsToWidth(hints: readonly Hint[], budget: number): readonly Hint[] {\n if (budget <= 0 || hints.length === 0)\n return EMPTY_HINTS\n const out: Hint[] = []\n let used = 0\n for (let i = 0; i < hints.length; i++) {\n const h = hints[i]!\n const cost = (i > 0 ? 3 : 0) + hintLength(h)\n if (used + cost > budget)\n break\n out.push(h)\n used += cost\n }\n if (out.length === 0)\n return EMPTY_HINTS\n return out.length === hints.length ? hints : out\n}\n","/**\n * Manual \"compact now\" + the shared compaction body. Owns:\n * - `runCompaction(entry)` — provider call → summary turn → post-compact\n * attachment turns.\n * - The broadcast pair (`compacting:start` / `compacting:end`) that\n * drives client footer indicators.\n *\n * Decoupled from `engine.ts` so the auto-compact policy + the manual\n * \"compact now\" affordance both route through one body.\n */\nimport type { Agent } from '../../index'\nimport type { SessionStore } from '../../session/index'\nimport type { SessionTurn } from '../../types'\nimport type { ProviderDescriptor, ProviderKey } from '../index'\nimport type { HostSignal } from './signals'\nimport {\n applyCompactSummaryCutoff,\n buildPostCompactAttachments,\n compactConversation,\n selectFilesFromSession,\n summaryToTurn,\n} from '../../index'\nimport { BUILTIN_PROVIDERS, compactSummaryEvent } from '../index'\n\nexport interface CompactionEntry {\n sessionId: string\n cwd: string\n agent: Agent\n providerKey: ProviderKey\n providerDescriptor?: ProviderDescriptor\n model: string\n compactionController?: AbortController\n}\n\n/**\n * Run `compactConversation` against the session's current turns, persist\n * the resulting summary as a new turn, and emit a compact-summary stream\n * event. Brackets the in-flight window with `compacting:start` /\n * `compacting:end` signals.\n */\nexport async function runCompaction(\n entry: CompactionEntry,\n broadcast: (signal: HostSignal) => void,\n store: SessionStore,\n): Promise<void> {\n broadcast({ kind: 'compacting:start', sessionId: entry.sessionId })\n\n let ok = false\n entry.compactionController = new AbortController()\n const { signal } = entry.compactionController\n\n try {\n const data = await store.load(entry.sessionId)\n if (!data)\n return\n\n const turns: SessionTurn[] = data.turns\n\n /*\n * Compaction uses the same provider the agent is running on so the\n * summary lives in the right model's context window. Falls back to\n * anthropic when the entry's provider can't be reconstructed.\n */\n const compactionProvider\n = entry.providerDescriptor?.factory()\n ?? BUILTIN_PROVIDERS[entry.providerKey]?.factory()\n ?? BUILTIN_PROVIDERS.anthropic.factory()\n\n const result = await compactConversation({\n provider: compactionProvider,\n model: entry.model,\n // Compact the WIRE view (same as the SDK's loop-native path): raw\n // turns still contain everything prior markers replaced, so\n // re-compacting raw history re-summarizes the same content forever\n // and the wire never shrinks.\n turns: applyCompactSummaryCutoff(turns),\n scope: 'tail',\n signal,\n })\n const summaryTurn = summaryToTurn({\n summary: result.summary,\n replacesTurnIds: result.summarizedTurnIds,\n usage: result.usage,\n model: entry.model,\n })\n\n /*\n * Post-compact restoration — fold recently-read files + active skills\n * back into the conversation as a synthetic `assistant(tool_calls) →\n * user(tool_results)` pair right after the summary turn. Without this\n * the model loses the read-state cache and the next turn often\n * re-reads everything just trimmed.\n */\n const session = entry.agent.session\n const handle = entry.agent.handle ?? undefined\n const attachments = session\n ? await buildPostCompactAttachments({\n recentFiles: selectFilesFromSession(session, handle?.cwd ?? entry.cwd),\n activeSkills: entry.agent.activeSkills,\n execution: entry.agent.execution,\n handle,\n // Thread the compaction abort signal so a cancel stops the\n // post-compact file reads instead of hanging on FS I/O.\n signal,\n })\n : { turns: [], restoredFiles: 0, restoredSkills: 0, estimatedTokens: 0 }\n\n const compactedTurns = [summaryTurn, ...attachments.turns]\n if (session)\n await session.appendTurns(compactedTurns)\n else\n await store.appendTurns(entry.sessionId, compactedTurns)\n\n // Surface the boundary card via the same mapper `eventsFromTurns` uses\n // on reload, so the live card matches the reloaded one byte-for-byte.\n const summaryBlock = summaryTurn.content.find(b => b.type === 'compact-summary')\n if (summaryBlock && summaryBlock.type === 'compact-summary') {\n broadcast({\n kind: 'event',\n sessionId: entry.sessionId,\n event: compactSummaryEvent(summaryBlock, summaryTurn.id),\n })\n }\n ok = true\n }\n catch (err) {\n if (signal.aborted) {\n broadcast({\n kind: 'event',\n sessionId: entry.sessionId,\n event: { kind: 'info', text: 'Auto-compaction cancelled.' },\n })\n }\n else {\n broadcast({\n kind: 'event',\n sessionId: entry.sessionId,\n event: { kind: 'error', text: `Auto-compaction failed: ${(err as Error).message}` },\n })\n }\n }\n finally {\n entry.compactionController = undefined\n broadcast({ kind: 'compacting:end', sessionId: entry.sessionId, ok })\n }\n}\n","/**\n * Per-callId stash for the per-edit approval pipeline. Written by\n * `tool:gate` on a `partial` decision and consumed by `tool:transform`\n * to append the canonical `<edit-outcomes>` annotation onto the tool's\n * natural output.\n *\n * Carries the original input + payload so `tool:before` can paint the\n * `tool` stream event with ALL hunks (including denied ones) badged\n * via outcomes — `ctx.input` has been reduced to the approved subset\n * by then, so `extractEditPayload` on the live ctx would miss the\n * denied hunks.\n *\n * Lifecycle: `tool:gate` writes; `tool:transform` reads + clears;\n * `tool:error` clears (no transform on the throw path); `agent:done`\n * clears the whole map so entries whose transform never fired\n * (validation reject / gate throw / retry loops) stay bounded by run\n * lifetime rather than session lifetime.\n */\nimport type { EditOutcome, EditPayload } from '../index'\n\nexport interface PendingAnnotation {\n outcomes: readonly EditOutcome[]\n originalInput: Record<string, unknown>\n originalPayload: EditPayload\n}\n\nexport class EditOutcomeLedger {\n private _entries = new Map<string, PendingAnnotation>()\n\n stash(callId: string, payload: PendingAnnotation): void {\n this._entries.set(callId, payload)\n }\n\n /** Read + remove the entry for `callId`. `null` when no stash exists. */\n drain(callId: string): PendingAnnotation | null {\n const entry = this._entries.get(callId)\n if (!entry)\n return null\n this._entries.delete(callId)\n return entry\n }\n\n /** Non-removing peek — used by `tool:before` to paint events without consuming the stash. */\n peek(callId: string): PendingAnnotation | null {\n return this._entries.get(callId) ?? null\n }\n\n /** Drop entry without reading — used by `tool:error` (no transform on throw path). */\n discard(callId: string): void {\n this._entries.delete(callId)\n }\n\n /** Clear every entry — called on `agent:done` and session disposal. */\n clear(): void {\n this._entries.clear()\n }\n}\n","import type { Hookable } from 'hookable'\nimport type { WriteStream } from 'node:fs'\nimport type { AgentHooks } from '../agent'\nimport type { Logger } from '../logger'\nimport { createWriteStream, mkdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { consoleSink, createLogger } from '../logger'\n\n/**\n * Subscribe to `mcp:warn`\n * (stdio child stderr lines, see `attachStderrWarnPump` in ./index.ts)\n * and append each to `<dir>/warnings.log`.\n *\n * The stream opens lazily on the first warning so\n * a healthy session never touches disk.\n */\nexport function attachMcpWarnLog(\n hooks: Hookable<AgentHooks>,\n opts: { dir: string },\n): { close: () => Promise<void> } {\n let stream: WriteStream | null = null\n let logger: Logger | null = null\n\n const ensureLogger = (): Logger | null => {\n if (logger)\n return logger\n try {\n // Sync mkdir so the WriteStream's first write never races a\n // missing directory. Runs at most once per session.\n mkdirSync(opts.dir, { recursive: true })\n const s = createWriteStream(join(opts.dir, 'warnings.log'), {\n flags: 'a',\n })\n s.on('error', () => { })\n stream = s\n logger = createLogger(consoleSink({ stream: s, minLevel: 'warn' }))\n return logger\n }\n catch {\n return null\n }\n }\n\n const unhook = hooks.hook('mcp:warn', ({ name, message }) => {\n ensureLogger()?.warn('mcp warn', { server: name, message })\n })\n\n return {\n close: async () => {\n unhook()\n const s = stream\n stream = null\n logger = null\n if (!s)\n return\n await new Promise<void>((resolve) => {\n // `close` fires after the fd is released; safer than `finish` if\n // the stream errored out (handler above swallows so we still resolve).\n s.once('close', () => resolve())\n s.once('error', () => resolve())\n s.end()\n })\n },\n }\n}\n","/**\n * Agent Plugins discovery for the chat layer.\n *\n * Walks the same four project + user roots every other zidane discovery\n * surface uses, loads each immediate child as an Agent Plugin, and folds the\n * user's enabled-toggle list into the contributions the agent picks up at\n * construction time.\n *\n * Pattern mirrors `skills-discovery.ts` / `mcps-discovery.ts`. The strict\n * loading itself lives in `../plugins` — this module owns only the \"where to\n * look\" and \"which ones did the user enable\" halves.\n *\n * Failure model, matching how the Extensions tab already treats a broken\n * extension: a plugin that fails to load is still RETURNED, carrying\n * `ok: false` and its diagnostics, so the settings list can show it and say\n * why. `errors` is reserved for problems that produced no plugin at all\n * (an unreadable root directory) — otherwise the same failure would be\n * reported twice in two different places.\n */\n\nimport type { McpServerConfig } from '../mcp'\nimport type { LoadedPlugin } from '../plugins'\nimport type { SkillConfig } from '../skills'\nimport type { DiscoveryError } from './mcps-discovery'\nimport type { ProjectUserPath } from './project-user-paths'\nimport { existsSync } from 'node:fs'\nimport { readdir, stat } from 'node:fs/promises'\nimport { basename, delimiter, join } from 'node:path'\nimport { defaultPluginDataRoot, loadAgentPlugin } from '../plugins'\nimport { projectUserPaths } from './project-user-paths'\n\n/** Subdirectory each search root is probed for. */\nconst PLUGINS_SUBPATH = 'plugins'\n\n/** One plugin found on disk, loaded (successfully or not). */\nexport interface DiscoveredPlugin {\n /**\n * Identity for dedup + the enabled allowlist. The manifest's `name` when\n * the plugin loaded; the directory basename when it didn't, so a rejected\n * plugin still has something stable to key a row and a toggle on.\n */\n name: string\n /** Load result. Check `ok` before trusting `manifest`. */\n plugin: LoadedPlugin\n /** Which search root it came from. */\n source: 'project' | 'user'\n /** Plugin root directory, as discovered. */\n path: string\n}\n\n/**\n * Resolve the plugin search roots. First-found wins on name collision;\n * earlier entries take precedence.\n *\n * Search order — see {@link projectUserPaths}:\n *\n * 1. `{project}/.agents/plugins`\n * 2. `{project}/.zidane/plugins`\n * 3. `~/.agents/plugins`\n * 4. `~/.zidane/plugins`\n *\n * Non-existent paths are returned; the walk skips them.\n *\n * Unlike skills and MCP servers there is no foreign-agent or Ruler variant:\n * Agent Plugins is a vendor-neutral format, so `.agents/plugins` IS the\n * cross-tool location. Nothing to import from a competitor's directory.\n */\nexport function defaultPluginScanPaths(opts: {\n cwd?: string\n home?: string\n prefix?: string\n} = {}): ProjectUserPath[] {\n return projectUserPaths({ subPath: PLUGINS_SUBPATH, ...opts })\n}\n\n/**\n * Parse a platform-delimited list of additional plugin scan roots. Mirrors\n * `parseExtensionScanPathEnv` in `../extensions/discovery`:\n *\n * ZIDANE_PLUGIN_DIRS=/repo/plugins bun run tui\n *\n * Each path is a scan ROOT whose immediate children are plugin directories\n * (`<root>/<name>/plugin.json`), not a single plugin directory.\n *\n * This exists because the four default roots are all dot-directories, and\n * `.zidane/` is gitignored — so a repo has nowhere TRACKED to keep example or\n * first-party plugins for local dogfooding. Extensions solved the same\n * problem the same way.\n */\nexport function parsePluginScanPathEnv(\n value: string | undefined = process.env.ZIDANE_PLUGIN_DIRS,\n): string[] {\n if (!value)\n return []\n return value.split(delimiter).map(p => p.trim()).filter(Boolean)\n}\n\n/**\n * Load every plugin reachable from the default search roots.\n *\n * Each immediate child directory of a root is one plugin — the same\n * depth-1 rule the spec applies inside `skills/`. A child that isn't a\n * directory, or that holds no `plugin.json`, is silently skipped: dropping a\n * README next to your plugins is not an error.\n *\n * Dedup is by plugin NAME, not directory name, because the manifest owns\n * identity — two directories called `tools/` shipping different plugins are\n * two plugins, and one plugin vendored at two scopes is one plugin.\n */\nexport async function discoverProjectPlugins(opts: {\n cwd?: string\n home?: string\n prefix?: string\n /**\n * Extra scan roots appended AFTER the four defaults — typically from\n * {@link parsePluginScanPathEnv}. Ordered last so a real project or user\n * plugin always wins a name collision against a dev/host-provided root.\n *\n * Scoped `'project'`: they're supplied by whoever launched the process, the\n * same trust level as a plugin sitting in the working tree.\n */\n extraRoots?: readonly string[]\n signal?: AbortSignal\n} = {}): Promise<{ plugins: DiscoveredPlugin[], errors: DiscoveryError[] }> {\n const roots = [\n ...defaultPluginScanPaths(opts),\n ...(opts.extraRoots ?? []).map(path => ({ path, source: 'project' as const })),\n ].filter(p => existsSync(p.path))\n const dataRoot = defaultPluginDataRoot({\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n })\n const plugins: DiscoveredPlugin[] = []\n const errors: DiscoveryError[] = []\n const seen = new Set<string>()\n\n for (const root of roots) {\n if (opts.signal?.aborted)\n break\n\n let entries: string[]\n try {\n entries = await readdir(root.path)\n }\n catch (err) {\n errors.push({\n path: root.path,\n source: root.source,\n message: `Could not read plugin directory: ${err instanceof Error ? err.message : String(err)}`,\n })\n continue\n }\n\n // Sorted so the catalog order is deterministic rather than inode-order.\n for (const entry of entries.sort()) {\n if (opts.signal?.aborted)\n break\n const dir = join(root.path, entry)\n try {\n // `stat`, not `lstat` — a SYMLINKED plugin directory is followed here,\n // where the extension scanner deliberately refuses one\n // (`extensions/discovery.ts`: \"Do not follow symlinks out of the\n // configured scan roots\"). The asymmetry is intentional, not an\n // oversight:\n //\n // An extension's entry file is IMPORTED — arbitrary code runs the\n // moment it's found, so the only defence is never resolving outside\n // the configured root. A plugin executes nothing at discovery time,\n // and `loadAgentPlugin` independently re-establishes a realpath\n // boundary from wherever the directory actually lives, so every\n // package file it goes on to read is contained relative to the\n // RESOLVED root. Following the link costs nothing there and supports\n // the monorepo pattern of linking a workspace package into\n // `.agents/plugins/`.\n if (!(await stat(dir)).isDirectory())\n continue\n }\n catch {\n continue // raced away between readdir and stat\n }\n\n // `dataRoot` is threaded explicitly: `loadAgentPlugin`'s fallback calls\n // `defaultPluginDataRoot()` with no options, and `resolveStorageDirs`\n // defaults `prefix` to `.zidane` rather than reading `ZIDANE_PREFIX`\n // itself. Without this a custom prefix would scan `{project}/.acme/\n // plugins` while writing plugin data to `~/.zidane/plugin-data/`.\n const loaded = await loadAgentPlugin(dir, {\n source: root.source,\n dataRoot,\n })\n // A directory with no manifest isn't a plugin at all — that's the one\n // rejection worth staying quiet about, since bundling docs or a\n // scratch folder alongside plugins is legal.\n if (!loaded.ok && loaded.diagnostics.some(d => d.code === 'manifest-missing'))\n continue\n\n const name = loaded.manifest?.name ?? basename(dir)\n if (seen.has(name))\n continue\n seen.add(name)\n plugins.push({ name, plugin: loaded, source: root.source, path: dir })\n }\n }\n\n return { plugins, errors }\n}\n\n/**\n * Map the user's enabled-plugin list onto the contributions a host merges\n * into the agent.\n *\n * Conventions match `enabledSkills` / `enabledMcps`:\n * - `undefined` → every discovered plugin is enabled (default)\n * - `[]` → all off\n * - `[names]` → allowlist\n *\n * A plugin that failed to load contributes nothing regardless of the\n * allowlist — `ok: false` means components were never discovered.\n */\nexport function buildPluginContributions(opts: {\n discovered: readonly DiscoveredPlugin[]\n enabled?: readonly string[]\n}): { skills: SkillConfig[], mcpServers: McpServerConfig[] } {\n const allow = opts.enabled === undefined ? null : new Set(opts.enabled)\n const skills: SkillConfig[] = []\n const mcpServers: McpServerConfig[] = []\n\n for (const entry of opts.discovered) {\n if (!entry.plugin.ok)\n continue\n if (allow !== null && !allow.has(entry.name))\n continue\n skills.push(...entry.plugin.skills)\n mcpServers.push(...entry.plugin.mcpServers)\n }\n\n return { skills, mcpServers }\n}\n","import type { TurnFinishReason } from '../types'\n/**\n * The run-end \"why did this stop?\" notice, in one place.\n *\n * When a run ends because the model bumped a ceiling rather than finishing,\n * the transcript otherwise shows an ordinary — often empty — assistant turn.\n * That is the classic \"the session just stalled\" report: nothing errored, so\n * nothing was said.\n *\n * This lived in TWO implementations with divergent copy: the TUI's in-process\n * `agent:done` hook and the shared chat host's (which serves the GUI and\n * daemon-attached windows). Same three conditions, three different message\n * texts, so the same run explained itself differently depending on which\n * surface you happened to be looking at.\n *\n * Returns a STRUCTURED notice — `{ severity, label }` plus text — rather than\n * a bare string, because the transcript renders notices with a severity glyph\n * and a label chip (`StreamEvent.notice`). Returning text alone would force\n * each surface to invent the label, which is exactly the divergence this\n * module exists to prevent. The text therefore carries no leading glyph and\n * starts lowercase: the renderer supplies the glyph, and the label is the\n * headline.\n *\n * The `length` copy names the two settings that change the outcome. Both carry\n * these exact labels on every surface (`resumeOnMaxTokens` → \"Auto-resume on\n * output limit\", `maxOutputTokens` → \"Max output tokens\"), so the instruction\n * is followable wherever the notice appears; `test/run-end-notice.test.ts`\n * pins the labels so a rename cannot turn it into a lie.\n */\nimport type { StreamEvent } from './types'\n\nexport interface RunEndNotice {\n notice: NonNullable<StreamEvent['notice']>\n text: string\n}\n\n/**\n * Structured stop notice for an abnormal `finishReason`, or `undefined` for a\n * normal end.\n *\n * Normal completions (`stop` / `tool-calls`) and user aborts return\n * `undefined` — and so does a SUCCESSFUL auto-resume, because recovery ends\n * the run on `stop`. That is what keeps the notice quiet exactly when the\n * ceiling was handled rather than hit. `error` runs already surface their own\n * error event, so a second notice would double-report.\n */\nexport function runEndNotice(reason: TurnFinishReason | undefined): RunEndNotice | undefined {\n switch (reason) {\n case 'length':\n return {\n notice: { severity: 'warn', label: 'output limit' },\n text: 'stopped at the model output limit (max_tokens). Turn on \"Auto-resume on output limit\" or raise \"Max output tokens\" in Settings to continue past it.',\n }\n case 'pause':\n return {\n notice: { severity: 'warn', label: 'pause cap' },\n text: 'stopped after repeated empty pause turns from the provider (maxConsecutivePauseTurns cap is armed).',\n }\n case 'content-filter':\n /*\n * Refusal stop (e.g. Anthropic `stop_reason: refusal`, often after\n * PDF-heavy tool results). The loop already retried with synthetic\n * continues before giving up, so reaching here means the refusal\n * persisted — say so rather than letting the run end as a silent,\n * empty \"completion\".\n */\n return {\n notice: { severity: 'warn', label: 'refusal' },\n text: 'the provider refused to continue (safety filter / refusal stop) despite automatic retries. Rephrase the request, or start a fresh session if it keeps happening.',\n }\n default:\n return undefined\n }\n}\n","/**\n * Per-session model fallback chains — shared persistence semantics for\n * `metadata.modelChain` (canonical `ModelChainEntry[]`, entry 0 = primary,\n * byte-identical to the TUI's `state.json` `lastModelChain`).\n *\n * Write invariant: `modelChain[0]` always equals the session's persisted\n * `providerKey`/`modelId`; the read side (`restoreChainFallbacks`)\n * head-matches, so a stale chain silently degrades to \"no fallbacks\".\n */\nimport type { ProviderKey } from './auth'\nimport type { ModelChainEntry } from './model-chain'\nimport { filterChainFallbacks, restoreChainFallbacks } from './model-chain'\n\nexport interface ModelFallbackTarget {\n providerKey: ProviderKey\n modelId: string\n}\n\n/**\n * Assemble the `metadata.modelChain` value for a model pick — the full\n * canonical chain when at least one valid fallback survives filtering,\n * `undefined` for a single-model pick (metadata merges JSON-serialize,\n * so `undefined` REMOVES a previously persisted chain).\n */\nexport function modelChainMetadata(\n providerKey: ProviderKey,\n modelId: string,\n fallbacks: readonly ModelFallbackTarget[] | undefined,\n): ModelChainEntry[] | undefined {\n const primary: ModelChainEntry = { provider: providerKey, model: modelId }\n const tail = filterChainFallbacks(\n (fallbacks ?? []).map(f => ({ provider: f.providerKey, model: f.modelId })),\n primary,\n () => true,\n )\n return tail.length > 0 ? [primary, ...tail] : undefined\n}\n\n/**\n * Validated fallback tail for list rows/pickers — head-matched against\n * the session's persisted primary, unregistered providers kept (the\n * caller filters against its own registry when it has one).\n */\nexport function sessionModelFallbacks(\n metadata: Record<string, unknown> | undefined,\n): ModelFallbackTarget[] | undefined {\n const providerKey = typeof metadata?.providerKey === 'string' ? metadata.providerKey : undefined\n const modelId = typeof metadata?.modelId === 'string' ? metadata.modelId : undefined\n if (!providerKey || !modelId)\n return undefined\n const tail = restoreChainFallbacks(\n metadata?.modelChain,\n { provider: providerKey, model: modelId },\n () => true,\n )\n return tail.length > 0 ? tail.map(e => ({ providerKey: e.provider, modelId: e.model })) : undefined\n}\n","/**\n * The one way a session is created in a store.\n *\n * Shared by the chat host (daemon + embedded GUI), orchestration, and the TUI's\n * local runtime — each previously had, or would have needed, its own copy of\n * these lines, and they drift the moment one grows a field the others forget.\n *\n * `createMainSession` layers the orchestration role on top; it is here rather\n * than in the host because the TUI's local runtime has no host and must still\n * be able to materialize the main (delivery of events does need the host; the\n * session itself does not).\n */\nimport type { SessionStore } from '../session/index'\nimport type { ProviderKey } from './auth'\nimport type { ModelFallbackTarget } from './model-chain-metadata'\nimport { randomUUID } from 'node:crypto'\nimport { EXTENSION_EDIT_METADATA_KEY } from '../extension-authoring'\nimport { createSession, loadSession } from '../session/index'\nimport { DEFAULT_AGENT_ID } from './agents'\nimport { modelChainMetadata } from './model-chain-metadata'\nimport { MAIN_SESSION_TITLE, SESSION_ROLE_KEY } from './orchestration'\nimport { findGitRoot } from './project-root'\n\nexport interface CreateSessionInStoreParams {\n /**\n * Tool-execution directory; `projectRoot` is derived from it. `''` creates a\n * DIRECTORY-LESS session (the GUI's \"Chats\" bucket): no project, and\n * `resolveSessionCwd` anchors tool execution at the user's HOME at run time.\n */\n cwd: string\n title?: string\n /** Profile the session starts in. Defaults to `DEFAULT_AGENT_ID` (`build`). */\n agentId?: string\n providerKey?: string\n modelId?: string\n fallbacks?: readonly ModelFallbackTarget[]\n /** Mark as an extension-authoring session (auto-activates the docs skill). */\n extensionEdit?: boolean\n}\n\n/** Create and persist a session; returns its id. */\nexport async function createSessionInStore(store: SessionStore, params: CreateSessionInStoreParams): Promise<string> {\n const id = (await store.generateSessionId?.()) ?? randomUUID()\n // Directory-less sessions carry NO project — `''` must not become a\n // project root, or the session lands in a bogus project group.\n const projectRoot = params.cwd ? (findGitRoot(params.cwd) ?? params.cwd) : undefined\n const session = await createSession({\n id,\n store,\n ...(projectRoot ? { projectRoot } : {}),\n // Always stamped: the agent is a per-session mode, and a session without\n // one would otherwise inherit whatever profile the opening window shows.\n agentId: params.agentId ?? DEFAULT_AGENT_ID,\n })\n session.setMeta('cwd', params.cwd)\n if (params.title)\n session.setMeta('title', params.title)\n if (params.providerKey && params.modelId) {\n session.setMeta('providerKey', params.providerKey)\n session.setMeta('modelId', params.modelId)\n const chain = modelChainMetadata(params.providerKey as ProviderKey, params.modelId, params.fallbacks)\n if (chain)\n session.setMeta('modelChain', chain)\n }\n if (params.extensionEdit)\n session.setMeta(EXTENSION_EDIT_METADATA_KEY, { dir: params.cwd })\n await session.save()\n return id\n}\n\n/**\n * Create the orchestration main session; returns its id.\n *\n * Directory-less on purpose. The main is GLOBAL — one per store, orchestrating\n * sessions in many directories — so binding it to whichever cwd happened to be\n * active when the toggle flipped put it in that project's group and hid it\n * from every other project's list. With no cwd it sits in the \"Chats\" bucket\n * and its own tools run from HOME, which is the only directory that is always\n * the user's and never a project's.\n */\nexport async function createMainSession(store: SessionStore): Promise<string> {\n const id = await createSessionInStore(store, { cwd: '', title: MAIN_SESSION_TITLE })\n const session = await loadSession(store, id)\n if (!session)\n throw new Error(`main session ${id} vanished after creation`)\n session.setMeta(SESSION_ROLE_KEY, 'main')\n await session.save()\n return id\n}\n","/**\n * Built-in `/` commands every surface ships regardless of extensions. Kept as\n * a factory so `/help` can enumerate the *live* merged command list (built-ins\n * + extension-contributed) without a circular import.\n *\n * Lives in `chat/` (not `tui/`) because the shared chat host runs slash\n * commands for the GUI and for daemon-attached windows; a `/help` that only\n * the TUI's local path knew about was a command that silently did not exist on\n * every other surface.\n *\n * (Extension install/update/remove deliberately have no commands — that\n * lifecycle lives in `Settings → Extensions`: `ctrl+g` install, `ctrl+u`\n * update, `ctrl+x` remove.)\n */\n\nimport type { Command } from './commands'\n\nexport function createBuiltinCommands(opts: {\n /** The full merged command list (built-ins + extensions), read live. */\n getCommands: () => readonly Command[]\n /**\n * Hot-reload the extension registry (re-discover, re-setup, dispose the\n * old instance). Omit on hosts without one — `/reload` then isn't offered.\n */\n reloadExtensions?: () => Promise<{ active: number, inactive: number }>\n}): Command[] {\n const commands: Command[] = [\n {\n name: 'help',\n description: 'List the available / commands',\n run: (ctx) => {\n const all = [...opts.getCommands()].sort((a, b) => a.name.localeCompare(b.name))\n const body = all.length === 0\n ? '_No commands are registered._'\n : all.map(c => `- **/${c.name}** — ${c.description}`).join('\\n')\n ctx.emit({ text: `### Commands\\n\\n${body}` })\n },\n },\n ]\n if (opts.reloadExtensions) {\n const reload = opts.reloadExtensions\n commands.push({\n name: 'reload',\n description: 'Reload extensions (re-discover, re-run setup, tear down the old instances)',\n run: async (ctx) => {\n ctx.emit({ text: '', detail: { label: 'reloading extensions…' } })\n const { active, inactive } = await reload()\n ctx.emit({ text: `Reloaded — **${active}** active${inactive > 0 ? `, ${inactive} inactive` : ''}. Agent-side contributions apply on the next run.` })\n },\n })\n }\n return commands\n}\n","import type { Provider } from '../../providers'\nimport type { SessionData } from '../../session/index'\n/**\n * Small helpers shared by the host engine's gate + preview pipeline.\n */\nimport type { EditPayload, PreviewResult, ProviderKey } from '../index'\nimport type { ChatHostEnvironment } from './environment'\nimport type { SerializedPreview } from './signals'\nimport { readFile } from 'node:fs/promises'\nimport { extractEditPayload } from '../index'\n\n/**\n * While a run is active, re-assert the persisted `running` marker on this\n * cadence (targeted single-column UPDATE — never a full-document save, so\n * it can't clobber concurrent turn appends). Long quiet tool calls (a\n * 10-minute build is routine) produce no turn persistence, and without\n * the beat the marker would look stale and another process would start a\n * second run on the session. Well under `STALE_RUN_MARKER_MS`.\n */\nexport const RUN_HEARTBEAT_MS = 60 * 1000\n\n/**\n * Cross-process run heartbeat, shared by prompt runs (`sendPrompt`) and\n * command sub-runs (`ctx.runAgent`). Keeps the persisted `running`\n * marker fresh through long quiet tool calls — see\n * {@link RUN_HEARTBEAT_MS}.\n *\n * `isRunning` is checked at beat time, but the write itself is async — a\n * beat launched JUST before run end can land after the loop settles the\n * final status, leaving a fresh 'running' marker that every process's\n * guard then refuses for 5 minutes. `end()` therefore chains the settle\n * write behind the last in-flight beat: after it lands, the loop's\n * settled status is re-asserted so a late 'running' write can never be\n * final. Callers must snapshot `settledStatus` SYNCHRONOUSLY at run end\n * (reading status off an already-destroyed agent would silently skip the\n * settle write).\n *\n * Like the cross-process guard, the heartbeat (and its straggler settle)\n * exists solely for daemon/multi-process coexistence — gated on the same\n * `useDaemon` read, so with IPC disabled this is a no-op end to end and\n * the loop's own status writes are the only marker writes.\n */\nexport function startRunHeartbeat(opts: {\n env: ChatHostEnvironment\n sessionId: string\n isRunning: () => boolean\n}): { end: (settledStatus: SessionData['status'] | undefined) => Promise<void> } {\n const enabled = opts.env.multiProcess === true || opts.env.readSettings().useDaemon === true\n let lastBeat: Promise<unknown> = Promise.resolve()\n let beatInFlight = false\n let ending: Promise<void> | undefined\n const timer = enabled\n ? setInterval(() => {\n if (!ending && !beatInFlight && opts.isRunning()) {\n // Keep the only in-flight write reachable by end().\n beatInFlight = true\n lastBeat = opts.env.store.updateStatus(opts.sessionId, 'running')\n .catch(() => undefined)\n .finally(() => { beatInFlight = false })\n }\n }, RUN_HEARTBEAT_MS)\n : undefined\n return {\n end(settledStatus) {\n if (ending)\n return ending\n if (timer !== undefined)\n clearInterval(timer)\n ending = lastBeat\n .then(async () => {\n if (enabled && settledStatus && settledStatus !== 'running')\n await opts.env.store.updateStatus(opts.sessionId, settledStatus)\n })\n .catch(() => undefined)\n return ending\n },\n }\n}\n\n/**\n * Optional dedicated compaction summarizer from the `compactionModel`\n * setting (`\"providerKey/modelId\"`, split on the FIRST slash only —\n * model ids may contain slashes, provider keys never do). `undefined`\n * for unset / malformed / same-as-main / unbuildable provider: silent\n * fallback to the main model is the designed behavior. Shared by the\n * engine's prompt runs and the command module's sub-runs so both resolve\n * compaction identically.\n */\nexport function resolveCompactionSummarizer(\n raw: string | undefined,\n buildProvider: (key: ProviderKey) => Provider,\n main: { providerKey: ProviderKey, model: string },\n): { provider: Provider, model: string } | undefined {\n if (!raw)\n return undefined\n const slash = raw.indexOf('/')\n if (slash <= 0 || slash === raw.length - 1)\n return undefined\n const providerKey = raw.slice(0, slash) as ProviderKey\n const model = raw.slice(slash + 1)\n if (providerKey === main.providerKey && model === main.model)\n return undefined\n try {\n return { provider: buildProvider(providerKey), model }\n }\n catch {\n return undefined\n }\n}\n\nexport async function readFileSafe(path: string): Promise<string> {\n try {\n return await readFile(path, 'utf8')\n }\n catch {\n return ''\n }\n}\n\n/** Resolve a workspace-relative path against the session's `cwd`. Absolute paths pass through. */\nexport function resolveCwdPath(cwd: string, p: string): string {\n if (p.startsWith('/'))\n return p\n return `${cwd.replace(/\\/$/, '')}/${p.replace(/^\\//, '')}`\n}\n\n/**\n * Gate-time `extractEditPayload` wrapper — same as `tool:before` but\n * without the prior-content second pass for `write_file`. The gate\n * only needs the payload shape (hunk count, paths) to drive the\n * approval surface; the diff resolver works against the freshly-read\n * `priorContent` passed to `previewEditPayload` directly.\n */\nexport function extractEditPayloadForGate(name: string, input: Record<string, unknown>): EditPayload | undefined {\n if (name === 'edit' || name === 'multi_edit')\n return extractEditPayload(name, input)\n if (name === 'write_file')\n return extractEditPayload(name, input, '')\n return undefined\n}\n\n/**\n * Strip readonly markers so the `PreviewResult` survives structured-clone\n * and JSON transports. Field-for-field copy — the resolver output is\n * already JSON-safe primitives.\n */\nexport function serializePreview(preview: PreviewResult): SerializedPreview {\n return {\n diffText: preview.diffText,\n perHunkDiff: [...preview.perHunkDiff],\n resolution: preview.resolution.map(r => ({ ...r })),\n resolvedPayload: {\n ...preview.resolvedPayload,\n hunks: preview.resolvedPayload.hunks.map(h => ({ ...h })),\n ...(preview.resolvedPayload.outcomes\n ? { outcomes: preview.resolvedPayload.outcomes.map(o => ({ ...o })) }\n : {}),\n },\n }\n}\n","/**\n * `ctx.workspace` / `ctx.runtime` / `ctx.sessions` for ONE session of a\n * multi-session host. The TUI attaches an impl for its single active\n * session at mount; the daemon and the GUI's embedded host scope this impl\n * onto the extension bridge (`env.hostRuntime.runWith`) around each `/`\n * command and each agent run, so an extension command, tool or hook that\n * fires for session X talks to X's execution context — never \"detached\",\n * never another session's.\n *\n * Mirrors the TUI's `bridge.attach({...})` in `app.tsx`, minus what has no\n * meaning without a window: `switchSession` / `createSession` answer\n * `false` (an extension cannot steer which session a daemon client is\n * looking at); `setThinking` is a session-metadata write the next run picks\n * up.\n */\nimport type { ExtensionHostRuntimeImpl } from '../../extensions/host-runtime'\nimport type { ThinkingLevel } from '../../types'\nimport type { CommandOutputBlock } from '../commands'\nimport type { ActiveRun } from './engine'\nimport type { ChatHostEnvironment } from './environment'\nimport { THINKING_LEVELS } from '../../types'\nimport { listSessionMeta } from '../store'\n\nexport interface SessionHostRuntimeDeps {\n /**\n * Where `ctx.appendNote` blocks go. Inside a `/` command: the command's own\n * persisted turn. Inside an agent run: a fresh `command` turn broadcast to\n * every window (the engine wires that).\n */\n appendNote: (blocks: readonly CommandOutputBlock[]) => void\n}\n\nexport function sessionHostRuntime(run: ActiveRun, env: ChatHostEnvironment, deps: SessionHostRuntimeDeps): ExtensionHostRuntimeImpl {\n return {\n model: () => run.modelOverride ?? run.model,\n models: () => [],\n thinking: () => {\n const effort = run.agent.session?.metadata?.effort\n return typeof effort === 'string' && isThinkingLevel(effort) ? effort : null\n },\n setThinking: (level) => {\n const session = run.agent.session\n if (!session)\n return false\n session.setMeta('effort', level)\n void session.save().catch(() => undefined)\n return true\n },\n listSessions: async () => {\n const rows = await listSessionMeta(env.store)\n return rows.map(s => ({ id: s.id, ...(s.title ? { title: s.title } : {}), ...(s.updatedAt ? { updatedAt: s.updatedAt } : {}) }))\n },\n switchSession: async () => false,\n createSession: async () => false,\n appendNote: (blocks) => {\n if (blocks.length === 0)\n return false\n deps.appendNote(blocks)\n return true\n },\n // The session's execution pair, spawning the handle on first use — the\n // agent is live for the scope's whole duration. Spawn failures propagate\n // so extensions see the real cause, not a generic \"detached\".\n workspace: async () => {\n const handle = await run.agent.ensureExecution()\n return { execution: run.agent.execution, handle }\n },\n workspaceAvailable: () => true,\n artifacts: () => {\n // Master switch: detached scope → reads answer `[]`, `record` no-ops.\n if (env.readSettings().artifacts === false)\n return null\n const session = run.agent.session\n return {\n store: env.store,\n ...(session ? { sessionId: session.id } : {}),\n ...(session?.projectRoot ? { projectRoot: session.projectRoot } : {}),\n // Host-side writes (`/pr`, `/commit`) bypass the loop, so fan the\n // change out ourselves — same signal the `artifacts.upsert` RPC emits.\n record: async (artifact) => {\n if (!env.store.upsertArtifact)\n return null\n const stored = await env.store.upsertArtifact(artifact)\n if (stored)\n env.broadcast({ kind: 'artifact:changed', sessionId: stored.sessionId, op: 'upsert', artifactId: stored.id, artifact: stored })\n return stored\n },\n }\n },\n }\n}\n\nfunction isThinkingLevel(value: string): value is ThinkingLevel {\n return (THINKING_LEVELS as readonly string[]).includes(value)\n}\n","/**\n * `/` command host for the shared chat host — the transport-agnostic port\n * of the GUI main-process module (gui/src/main/chat/commands.ts), itself a\n * mirror of the TUI's `runCommand` callback (src/tui/app.tsx). A command is\n * an extension-contributed workflow invoked by typing `/<name> [args]` as\n * the whole prompt; steps emit display-only blocks (persisted as\n * `role: 'command'` turns, never sent to the model) and can drive the agent\n * mid-flight via `ctx.runAgent` / `ctx.spawnAgents` / `ctx.generateData`.\n *\n * The host supplies the side-effecting capabilities on the step context;\n * the canonical model (`executeCommand`, `parseCommandInvocation`, the\n * block↔content/event mappers) lives in the library core and is shared\n * with the TUI/GUI so live and reloaded transcripts render identically in\n * every surface.\n *\n * Seam adaptations vs. the GUI module: `broadcast` / `ensureAgent` /\n * `buildProvider` are host-closure values injected through the factory;\n * the interaction bridge rides the host's `InteractionHub`; settings /\n * runtime config / session store come from the `ChatHostEnvironment`.\n */\nimport type {\n Command,\n CommandGenerateDataOptions,\n CommandOutputBlock,\n CommandSpawnResult,\n CommandStepContext,\n ToolContext,\n} from '../../index'\nimport type { Provider } from '../../providers'\nimport type { AgentBehavior, ThinkingLevel } from '../../types'\nimport type { CommandExecution } from '../commands'\nimport type {\n InteractionRequest,\n InteractionResponse,\n ProviderDescriptor,\n ProviderKey,\n StreamEvent,\n} from '../index'\nimport type { ActiveRun } from './engine'\nimport type { ChatHostEnvironment } from './environment'\nimport type { InteractionHub } from './interactions'\nimport type { CommandArgSuggestionSummary, CommandSummary, HostSettings, HostSignal, SendOptions } from './signals'\nimport { randomUUID } from 'node:crypto'\nimport {\n commandBlocksToContent,\n commandBlockToEvent,\n createLogger,\n executeCommand,\n generateStructuredData,\n isSpawnTool,\n parseCommandInvocation,\n} from '../../index'\nimport { nonOwningMcpView } from '../../mcp/index'\nimport { THINKING_LEVELS } from '../../types'\nimport { createBuiltinCommands } from '../builtin-commands'\nimport {\n ASK_USER_TOOL,\n BUILTIN_PROVIDERS,\n chatAutoCompactBehavior,\n enabledModelOptions,\n getContextWindow,\n HOST_INTERACTION_ID_PREFIX,\n PRESENT_PLAN_TOOL,\n restoreModelOptions,\n} from '../index'\nimport { resolveCompactionSummarizer, startRunHeartbeat } from './helpers'\nimport { sessionHostRuntime } from './host-runtime'\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\n/** Type guard for canonical `ThinkingLevel` strings read off `metadata.effort`. */\nfunction isThinkingLevel(value: string): value is ThinkingLevel {\n return (THINKING_LEVELS as readonly string[]).includes(value)\n}\n\n/**\n * Hard-cap settings → per-run `AgentBehavior` overrides — the host mirror\n * of the TUI's `runCaps` memo (src/tui/app.tsx). Empty when every cap sits\n * at its \"unlimited\" default (0 / off), so out of the box nothing is\n * forwarded and the run stays model-driven and unbounded. Spread into\n * `behavior` on every `agent.run()` (prompt runs and command sub-runs\n * alike).\n */\nexport function runCapsFromSettings(settings: HostSettings): Partial<AgentBehavior> {\n const caps: Partial<AgentBehavior> = {}\n\n if (settings.resumeOnMaxTokens)\n caps.resumeOnMaxTokens = true\n if (settings.maxOutputTokens > 0)\n caps.maxTokens = settings.maxOutputTokens\n if (settings.maxTurns > 0)\n caps.maxTurns = settings.maxTurns\n if (settings.maxCostUsd > 0)\n caps.maxCostUsd = settings.maxCostUsd\n if (settings.maxTotalTokens > 0)\n caps.maxTotalTokens = settings.maxTotalTokens\n if (settings.maxWallMinutes > 0)\n caps.maxWallMs = settings.maxWallMinutes * 60_000\n if (settings.retryMaxAttempts > 0)\n caps.retry = { maxAttempts: settings.retryMaxAttempts }\n\n return caps\n}\n\nexport interface CommandModule {\n /** Wire-safe command catalog for a client's `/` completion popover. */\n listCommands: (cwd: string) => Promise<CommandSummary[]>\n /** Argument suggestions for `/name <prefix>` — best-effort, empty on failure. */\n completeCommandArgs: (cwd: string, name: string, argsPrefix: string) => Promise<CommandArgSuggestionSummary[]>\n /**\n * Try the prompt as a `/` command invocation. `{ ran: false }` when the\n * prompt isn't one (caller falls through to the model run); `{ ran: true }`\n * after the command completed (success or step error — errors surface as\n * transcript events, not rejections).\n */\n runSlashCommand: (opts: SendOptions) => Promise<{ ran: boolean, runId?: string }>\n}\n\nexport function createCommandModule(deps: {\n env: ChatHostEnvironment\n broadcast: (signal: HostSignal) => void\n ensureAgent: (sessionId: string) => Promise<ActiveRun>\n buildProvider: (providers: Readonly<Record<string, ProviderDescriptor>>, providerKey?: ProviderKey) => Provider\n interactions: InteractionHub\n}): CommandModule {\n const { env, broadcast, ensureAgent, buildProvider, interactions } = deps\n\n /**\n * Extension commands plus the shared built-ins (`/help`; `/reload` is\n * omitted because the host has no hot-reload seam). Same merge as the TUI's\n * `allCommands` — extension-first, aliases reserve the name namespace — so\n * a window attached to the daemon and one running locally resolve the same\n * name to the same command.\n */\n function mergedCommands(extensionCommands: readonly Command[]): Command[] {\n const merged: Command[] = []\n // `/help` enumerates the LIVE merged list, so the closure reads `merged`\n // after this function has filled it — declared first for that reason.\n const builtins = createBuiltinCommands({ getCommands: () => merged })\n const seen = new Set<string>()\n for (const command of [...extensionCommands, ...builtins]) {\n const key = command.name.toLowerCase()\n if (seen.has(key))\n continue\n seen.add(key)\n for (const alias of command.aliases ?? [])\n seen.add(alias.toLowerCase())\n merged.push(command)\n }\n return merged\n }\n\n async function listCommands(cwd: string): Promise<CommandSummary[]> {\n try {\n const runtimeConfig = await env.runtimeConfig(cwd)\n\n return mergedCommands(runtimeConfig.extensions.commands).map(command => ({\n name: command.name,\n description: command.description,\n ...(command.aliases && command.aliases.length > 0 ? { aliases: [...command.aliases] } : {}),\n ...(command.group ? { group: command.group } : {}),\n ...(command.completeArgs ? { hasArgCompletion: true } : {}),\n }))\n }\n catch {\n return []\n }\n }\n\n /**\n * Forward to the command's `completeArgs` for a client's argument\n * completion. Best-effort by contract — extension code runs here, so any\n * throw degrades to \"no suggestions\" rather than an IPC error.\n */\n async function completeCommandArgs(\n cwd: string,\n name: string,\n argsPrefix: string,\n ): Promise<CommandArgSuggestionSummary[]> {\n try {\n const runtimeConfig = await env.runtimeConfig(cwd)\n const command = mergedCommands(runtimeConfig.extensions.commands).find(\n c => c.name === name || (c.aliases?.includes(name) ?? false),\n )\n\n if (!command?.completeArgs)\n return []\n\n const suggestions = (await command.completeArgs(argsPrefix)) ?? []\n\n return suggestions.map(s => ({\n value: s.value,\n ...(s.label ? { label: s.label } : {}),\n ...(s.description ? { description: s.description } : {}),\n }))\n }\n catch {\n return []\n }\n }\n\n /**\n * Per-session model/effort derivation for command-driven sub-runs —\n * mirrors `sendPrompt`'s resolution (metadata effort, model options,\n * auto-compact from settings + model context window) so a `ctx.runAgent`\n * call behaves exactly like a user-sent prompt.\n */\n async function resolveRunSettings(run: ActiveRun): Promise<{\n thinking?: ThinkingLevel\n modelOptions?: Record<string, boolean>\n autoCompact: ReturnType<typeof chatAutoCompactBehavior>\n caps: Partial<AgentBehavior>\n }> {\n let thinking: ThinkingLevel | undefined\n let modelOptions: Record<string, boolean> | undefined\n\n try {\n const data = await env.store.load(run.sessionId)\n const rawEffort = data?.metadata?.effort\n\n if (typeof rawEffort === 'string' && isThinkingLevel(rawEffort))\n thinking = rawEffort\n\n const descriptor = run.providerDescriptor ?? BUILTIN_PROVIDERS.anthropic\n const modelId = run.modelOverride ?? run.model\n const enabled = enabledModelOptions(data?.metadata?.modelOptions)\n modelOptions = restoreModelOptions(descriptor, modelId, { [modelId]: enabled })\n }\n catch {\n // Reading metadata is best-effort; absence just means defaults.\n }\n\n const settings = env.readSettings()\n const descriptor = BUILTIN_PROVIDERS[run.providerKey] ?? BUILTIN_PROVIDERS.anthropic\n // Same dedicated `compactionModel` summarizer as `sendPrompt` resolves\n // for prompt runs — a sub-run's auto-compact must not silently fall\n // back to the main model when the user pinned a cheaper one.\n // (Sub-runs also carry the same cross-process run heartbeat as prompt\n // runs — see `ctx.runAgent`.)\n const runtime = await env.runtimeConfig(run.cwd)\n const summarizer = resolveCompactionSummarizer(\n settings.compactionModel,\n key => buildProvider(runtime.providers, key),\n { providerKey: run.providerKey, model: run.modelOverride ?? run.model },\n )\n const autoCompact = chatAutoCompactBehavior({\n enabled: settings.autoCompact,\n threshold: settings.autoCompactThreshold,\n rawContextWindow: getContextWindow(descriptor, run.modelOverride ?? run.model),\n ...(summarizer ? { summarizer } : {}),\n })\n\n // Same hard-cap overrides as prompt runs — a command-driven sub-run must\n // not escape the user's turn/cost/token/time budgets.\n return {\n ...(thinking ? { thinking } : {}),\n ...(modelOptions ? { modelOptions } : {}),\n autoCompact,\n caps: runCapsFromSettings(settings),\n }\n }\n\n /**\n * Mirrors the TUI's `runCommand`: display-only blocks buffer and flush\n * as ONE `role: 'command'` turn per ordering boundary (before each\n * sub-run / spawn / context note, and at the end), each block ALSO\n * streams live via `commandBlockToEvent`, and sub-runs are tagged with\n * `origin: { kind: 'command' }` so reloads render identically.\n */\n async function runSlashCommand(opts: SendOptions): Promise<{ ran: boolean, runId?: string }> {\n const prompt = opts.prompt.trim()\n\n if (!prompt.startsWith('/'))\n return { ran: false }\n\n const run = await ensureAgent(opts.sessionId)\n const runtimeConfig = await env.runtimeConfig(run.cwd)\n const commands: readonly Command[] = mergedCommands(runtimeConfig.extensions.commands)\n const invocation = parseCommandInvocation(prompt, commands)\n\n if (!invocation)\n return { ran: false }\n\n const command = commands.find(c => c.name === invocation.name)\n\n if (!command)\n return { ran: false }\n\n if (run.agent.isRunning)\n throw new Error(`/${invocation.name}: a run is in progress. Wait for it to finish.`)\n\n const session = run.agent.session\n\n if (!session)\n throw new Error(`/${invocation.name}: session is not available`)\n\n const sessionId = opts.sessionId\n const controller = new AbortController()\n run.commandAbort = controller\n\n const requestInteraction = interactions.requestInteractionFor(sessionId)\n\n /*\n * Promise-shaped bridge into the interaction queue — the same\n * `interactions.*` surface the ask_user / present_plan TOOLS ride,\n * minus the tool call (synthetic id, no turns, nothing to resume after\n * a reload). An abort cancels the pending request so every client's\n * modal dismisses and the awaiting Promise rejects.\n */\n const requestCommandInteraction = (request: InteractionRequest): Promise<InteractionResponse> => {\n if (controller.signal.aborted)\n return Promise.reject(new Error('command aborted'))\n\n const promise = requestInteraction(request)\n const onAbort = (): void => {\n interactions.cancel(request.id, 'command aborted')\n }\n controller.signal.addEventListener('abort', onAbort, { once: true })\n\n return promise.finally(() => controller.signal.removeEventListener('abort', onAbort))\n }\n\n const emitEvent = (event: StreamEvent): void => {\n broadcast({ kind: 'event', sessionId, event })\n }\n\n /*\n * Display-only blocks accrue here and flush as one `command` turn at\n * each ordering boundary. `pendingTurnId` is minted at the batch's\n * first emit so live events and the eventually-persisted turn share\n * one id; `separateNextEmit` mirrors the reload path's leading\n * separator after a sub-run.\n */\n let pending: CommandOutputBlock[] = []\n let pendingTurnId: string | null = null\n let separateNextEmit = false\n\n const flushCommandTurn = async (): Promise<void> => {\n if (pending.length === 0)\n return\n\n const content = commandBlocksToContent(pending)\n const turnId = pendingTurnId ?? randomUUID()\n pending = []\n pendingTurnId = null\n await session.appendTurns([{ id: turnId, role: 'command', createdAt: Date.now(), content }]).catch(() => undefined)\n }\n\n const emitBlock = (block: CommandOutputBlock): void => {\n if (separateNextEmit) {\n emitEvent({ kind: 'separator', text: '' })\n separateNextEmit = false\n }\n\n pendingTurnId ??= randomUUID()\n pending.push(block)\n emitEvent({ ...commandBlockToEvent(block), turnId: pendingTurnId })\n }\n\n const ctx: CommandStepContext = {\n args: invocation.args,\n argv: invocation.argv,\n ...(invocation.invokedAs ? { invokedAs: invocation.invokedAs } : {}),\n ...(opts.attachments && opts.attachments.length > 0\n ? {\n attachments: opts.attachments.map(a => ({\n name: a.name ?? 'attachment',\n mediaType: a.mediaType,\n data: a.data,\n })),\n }\n : {}),\n data: {},\n emit: emitBlock,\n runAgent: async (agentPrompt: string, agentOpts?: { readonly display?: string }) => {\n if (controller.signal.aborted)\n throw new Error('runAgent: command aborted')\n\n // Persist output emitted so far so it lands BEFORE the sub-run's\n // user/assistant turns in the transcript order.\n await flushCommandTurn()\n\n // Re-assert host-driven skill activation (same invariant sendPrompt\n // protects) — the agent's run-end pass deactivates everything.\n for (const name of run.autoActivateSkills ?? []) {\n try {\n await run.agent.activateSkill(name)\n }\n catch {\n // skill not resolvable — best-effort, matches sendPrompt.\n }\n }\n\n emitEvent({ kind: 'separator', text: '' })\n // With a `display`, echo the short line and tuck the full prompt\n // underneath as a collapsed command-detail row; the model still\n // receives it verbatim.\n emitEvent({ kind: 'user-prompt', text: agentOpts?.display ?? agentPrompt })\n\n if (agentOpts?.display)\n emitEvent({ kind: 'command-detail', label: 'prompt', text: agentPrompt })\n\n const before = session.turns.length\n const beforeRuns = session.runs.length\n const { thinking, modelOptions, autoCompact, caps } = await resolveRunSettings(run)\n const runId = randomUUID()\n\n // Fresh runId per sub-run so clients paint streaming state.\n broadcast({ kind: 'run:start', sessionId, runId })\n\n // Same cross-process heartbeat as prompt runs — a quiet multi-minute\n // tool call inside a sub-run must not let the persisted 'running'\n // marker go stale (another process's guard would then admit a\n // concurrent run). No-op when `useDaemon` is off.\n const heartbeat = startRunHeartbeat({\n env,\n sessionId,\n isRunning: () => run.agent.isRunning,\n })\n\n try {\n await run.agent.run({\n prompt: agentPrompt,\n signal: controller.signal,\n behavior: { autoCompact, ...caps },\n ...(run.modelOverride ? { model: run.modelOverride } : {}),\n ...(thinking ? { thinking } : {}),\n ...(modelOptions ? { modelOptions } : {}),\n })\n broadcast({ kind: 'run:end', sessionId, runId, ok: true })\n }\n catch (err) {\n broadcast({ kind: 'run:end', sessionId, runId, ok: false, error: errorMessage(err) })\n throw err\n }\n finally {\n // Straggler-beat settle, awaited so the sub-run's settled status\n // is the last marker write before the command continues.\n await heartbeat.end(session.status)\n }\n\n await session.save().catch(() => undefined)\n\n // Tag the sub-run(s) as command-driven so a reload renders the\n // injected prompt the same minimal way. Presentation-only.\n for (const sessionRun of session.runs.slice(beforeRuns)) {\n if (sessionRun.parentRunId)\n continue\n\n sessionRun.origin = { kind: 'command', ...(agentOpts?.display ? { display: agentOpts.display } : {}) }\n await session.updateRun(sessionRun).catch(() => undefined)\n }\n\n separateNextEmit = true\n\n const text = session.turns\n .slice(before)\n .filter(t => t.role === 'assistant')\n .flatMap(t => t.content.flatMap(b => (b.type === 'text' ? [b.text] : [])))\n .join('\\n')\n\n return { text }\n },\n // Single implementation behind the overloaded contract (zod-inferred\n // vs. explicit-generic) — the cast is the standard TS pattern for\n // implementing an overload set with one function.\n generateData: (async (dataOpts: CommandGenerateDataOptions) => {\n if (controller.signal.aborted)\n throw new Error('generateData: command aborted')\n\n // Side-channel one-shot: fresh provider instance, schema-pinned,\n // no session writes, no transcript events.\n const provider = buildProvider(runtimeConfig.providers, run.providerKey)\n\n return generateStructuredData({\n provider,\n model: dataOpts.model ?? run.modelOverride ?? run.model,\n prompt: dataOpts.prompt,\n schema: dataOpts.schema,\n ...(dataOpts.system ? { system: dataOpts.system } : {}),\n ...(dataOpts.includeConversation ? { turns: session.turns } : {}),\n signal: controller.signal,\n })\n }) as CommandStepContext['generateData'],\n logger: createLogger({ emit() {} }, { command: invocation.name }),\n signal: controller.signal,\n cwd: run.cwd,\n // Read-only live view — getters, so turns/runs appended mid-workflow\n // are visible on re-read. Projection only: no mutators cross into\n // extension code.\n session: {\n get id() {\n return session.id\n },\n get turns() {\n return session.turns\n },\n get runs() {\n return session.runs\n },\n get metadata() {\n return session.metadata\n },\n },\n interaction: {\n confirm: async (confirmPrompt, confirmOpts) => {\n const response = await requestCommandInteraction({\n id: `${HOST_INTERACTION_ID_PREFIX}${randomUUID()}`,\n origin: 'host',\n kind: 'question',\n tool: ASK_USER_TOOL,\n createdAt: Date.now(),\n payload: {\n ...(confirmOpts?.intro ? { intro: confirmOpts.intro } : {}),\n questions: [\n {\n id: 'confirm',\n type: 'confirm',\n prompt: confirmPrompt,\n ...(confirmOpts?.affirmLabel ? { affirmLabel: confirmOpts.affirmLabel } : {}),\n ...(confirmOpts?.denyLabel ? { denyLabel: confirmOpts.denyLabel } : {}),\n },\n ],\n },\n })\n\n if (response.kind !== 'question')\n throw new Error('confirm: unexpected interaction response kind')\n\n return response.answers.confirm === true\n },\n askUser: async (form) => {\n const response = await requestCommandInteraction({\n id: `${HOST_INTERACTION_ID_PREFIX}${randomUUID()}`,\n origin: 'host',\n kind: 'question',\n tool: ASK_USER_TOOL,\n createdAt: Date.now(),\n payload: form,\n })\n\n if (response.kind !== 'question')\n throw new Error('askUser: unexpected interaction response kind')\n\n return response.answers\n },\n presentPlan: async (plan) => {\n const response = await requestCommandInteraction({\n id: `${HOST_INTERACTION_ID_PREFIX}${randomUUID()}`,\n origin: 'host',\n kind: 'plan',\n tool: PRESENT_PLAN_TOOL,\n createdAt: Date.now(),\n payload: plan,\n })\n\n if (response.kind !== 'plan')\n throw new Error('presentPlan: unexpected interaction response kind')\n\n return response\n },\n },\n // Isolated child agents — the spawn tool driven host-side via the\n // build-time seam (`run.commandSeam`), so command children get the\n // exact model routing, persistence, and concurrency accounting of\n // model-initiated spawns.\n spawnAgents: async (tasks, spawnOpts) => {\n const seam = run.commandSeam\n const agent = run.agent\n\n if (!seam || !isSpawnTool(seam.spawnTool))\n throw new Error('spawnAgents: no spawn tool available for this agent')\n\n if (controller.signal.aborted)\n throw new Error('spawnAgents: command aborted')\n\n if (tasks.length === 0)\n return []\n\n await flushCommandTurn()\n\n const handle = await agent.ensureExecution()\n\n // Bind the parent's MCP connection BEFORE spawning so children get\n // the live view instead of re-bootstrapping every server. Raced\n // against the command's abort signal — a full MCP bootstrap can\n // take seconds and an abort must not block on it.\n let onAbort: (() => void) | undefined\n\n try {\n await Promise.race([\n agent.warmup(),\n new Promise<void>((resolveAbort) => {\n if (controller.signal.aborted) {\n resolveAbort()\n\n return\n }\n\n onAbort = () => resolveAbort()\n controller.signal.addEventListener('abort', onAbort, { once: true })\n }),\n ])\n }\n catch {\n // warmup failed — children fall back to config bootstrap (degraded, not dead).\n }\n finally {\n if (onAbort)\n controller.signal.removeEventListener('abort', onAbort)\n }\n\n if (controller.signal.aborted)\n throw new Error('spawnAgents: command aborted')\n\n const parentMcp = agent.mcpConnection\n const results: CommandSpawnResult[] = tasks.map((task, i) => ({\n label: task.label ?? `task-${i + 1}`,\n ...(task.model !== undefined ? { model: task.model } : {}),\n status: 'aborted' as const,\n text: '',\n message: 'spawnAgents: batch aborted before this task ran',\n }))\n\n // Simple worker pool — capped at the spawn tool's own concurrency\n // limit so its slot accounting never rejects a task the pool\n // admitted. `runTask` reports child failures in its outcome; the\n // per-task catch only guards infrastructure throws.\n let cursor = 0\n const worker = async (): Promise<void> => {\n while (cursor < tasks.length && !controller.signal.aborted) {\n const index = cursor++\n const spawnTask = tasks[index]!\n\n try {\n const outcome = await seam.spawnTool.runTask(\n {\n task: spawnTask.prompt,\n ...(spawnTask.system !== undefined ? { system: spawnTask.system } : {}),\n ...(spawnTask.model !== undefined ? { model: spawnTask.model } : {}),\n },\n {\n provider: seam.provider,\n providers: seam.providers,\n ...(seam.name !== undefined ? { name: seam.name } : {}),\n model: run.modelOverride ?? run.model,\n signal: controller.signal,\n execution: agent.execution,\n handle,\n hooks: agent.hooks,\n ...(seam.system !== undefined ? { system: seam.system } : {}),\n tools: seam.tools,\n mcpServers: [...agent.mcpServers],\n ...(parentMcp ? { mcpConnection: nonOwningMcpView(parentMcp) } : {}),\n skills: seam.skills,\n behavior: seam.behavior,\n turnId: `command_${invocation.name}`,\n callId: `command_spawn_${randomUUID()}`,\n session,\n } satisfies ToolContext,\n )\n\n results[index] = {\n label: spawnTask.label ?? outcome.id ?? `task-${index + 1}`,\n ...(spawnTask.model !== undefined ? { model: spawnTask.model } : {}),\n status: outcome.status,\n text: outcome.text,\n message: outcome.message,\n }\n }\n catch (err) {\n results[index] = {\n label: spawnTask.label ?? `task-${index + 1}`,\n ...(spawnTask.model !== undefined ? { model: spawnTask.model } : {}),\n status: 'error',\n text: '',\n message: `spawnAgents: ${errorMessage(err)}`,\n }\n }\n }\n }\n\n const cap = seam.spawnTool.maxConcurrent\n const width = Math.max(1, Math.min(spawnOpts?.maxConcurrent ?? cap, cap, tasks.length))\n await Promise.all(Array.from({ length: width }, () => worker()))\n await session.save().catch(() => undefined)\n\n // Children opened their own turns — the next emit starts a fresh\n // command turn and must lead with a separator (matches runAgent).\n separateNextEmit = true\n\n return results\n },\n // Model-visible context note — a plain user turn appended straight to\n // the session (no run). The next `agent.run` rebuilds conversation\n // from `session.turns`, so the note reaches the wire on the next\n // prompt; `synthetic` keeps the live echo out of prompt history.\n appendContext: async (text, noteOpts) => {\n if (controller.signal.aborted)\n throw new Error('appendContext: command aborted')\n\n const trimmed = text.trim()\n\n if (!trimmed)\n return\n\n await flushCommandTurn()\n emitEvent({ kind: 'separator', text: '' })\n emitEvent({ kind: 'user-prompt', text: noteOpts?.display ?? trimmed, synthetic: true })\n await session.appendTurns([\n {\n id: await session.generateTurnId(),\n role: 'user',\n content: [{ type: 'text', text: trimmed }],\n createdAt: Date.now(),\n ...(noteOpts?.display ? { display: noteOpts.display } : {}),\n },\n ])\n separateNextEmit = true\n },\n }\n\n /*\n * `ctx.workspace` / `ctx.runtime` / `ctx.sessions` for THIS session,\n * scoped onto the host-runtime bridge for the command's duration. The\n * TUI attaches an impl for its one active session at mount; a\n * multi-session host has to say which session a command belongs to —\n * without this every extension command over the daemon or in the GUI\n * saw \"ctx.workspace is detached\".\n */\n const execute = (): Promise<CommandExecution> => executeCommand(command, ctx)\n const executeScoped = env.hostRuntime\n ? () => env.hostRuntime!.runWith(sessionHostRuntime(run, env, { appendNote: blocks => blocks.forEach(emitBlock) }), execute)\n : execute\n\n try {\n // Delimit from the prior exchange (matches the reload rendering in\n // eventsFromTurns), then echo the invocation header as a persisted\n // block — a plain user-prompt echo would be lost on reload, and a\n // `user`-role turn would leak `/cmd` into model context.\n if (session.turns.length > 0)\n emitEvent({ kind: 'separator', text: '' })\n\n emitBlock({ text: `\\`❯ /${invocation.name}${invocation.args ? ` ${invocation.args}` : ''}\\`` })\n\n const result = await executeScoped()\n\n if (result.error)\n emitEvent({ kind: 'error', text: `/${invocation.name}: ${errorMessage(result.error)}` })\n }\n catch (err) {\n // executeCommand isolates step errors, so this only fires on an\n // unexpected host failure — surface it rather than reject into the\n // chat.send caller.\n emitEvent({ kind: 'error', text: `/${invocation.name}: ${errorMessage(err)}` })\n }\n finally {\n if (run.commandAbort === controller)\n delete run.commandAbort\n\n await flushCommandTurn()\n await session.save().catch(() => undefined)\n run.lastUsedAt = Date.now()\n // Command sub-runs settle their markers per sub-run (heartbeat.end\n // above); stamping the run-end here additionally arms the engine's\n // own-marker exemption — a marker this host just wrote must not\n // block this host's next send for the 5-minute freshness window.\n run.lastRunEndedAt = Date.now()\n }\n\n return { ran: true, runId: randomUUID() }\n }\n\n return { listCommands, completeCommandArgs, runSlashCommand }\n}\n","import type { SessionStore } from '../../session/index'\n/**\n * Interaction hub — `present_plan` + `ask_user` HITL flow.\n *\n * Bridge between the canonical `createInteractionTools` factory and the\n * host's clients. The agent loop's tool body calls back into\n * `requestInteractionFor(sessionId)(req)`; the returned Promise resolves\n * once ANY client submits an answer through `respond`.\n *\n * Symmetry with the approval hub:\n * - Both queue per-session requests in memory.\n * - Both broadcast a `*:request` signal so clients can pop a UI surface.\n * - Both rehydrate requests left dangling by a prior process exit —\n * canonical `pendingInteractionsFromTurns` for interactions.\n *\n * Resume semantics — when a session reloads with unanswered\n * `present_plan` / `ask_user` `tool_use` blocks, `rehydrate` re-queues every\n * orphan (ordered by turn appearance). Resumed answers accumulate in memory\n * and flush only when every sibling orphan has an explicit response, so the\n * next `agent.run()` sees a protocol-clean history.\n */\nimport type { InteractionRequest, InteractionResponse, Settings } from '../index'\nimport type { HostSignal, PendingInteraction } from './signals'\nimport { randomUUID } from 'node:crypto'\nimport { buildResumedToolResultsTurn, createAutomaticInteractionResponse, isModelInteraction, pendingInteractionsFromTurns } from '../index'\n\ninterface Slot {\n sessionId: string\n request: InteractionRequest\n resolve: (response: InteractionResponse) => void\n reject: (err: Error) => void\n\n /**\n * `true` when raised by a live agent loop; `false` for rehydrated\n * orphans (resolve/reject are no-ops until the resumed flow lands a\n * `buildResumedToolResultsTurn`). Clients use this flag to decide\n * whether to auto-pop a modal.\n */\n live: boolean\n}\n\nexport interface InteractionHub {\n /** Build the `requestInteraction` callback bound to one session. */\n requestInteractionFor: (sessionId: string) => (request: InteractionRequest) => Promise<InteractionResponse>\n respond: (requestId: string, response: InteractionResponse) => { ok: boolean }\n cancel: (requestId: string, reason?: string) => { ok: boolean }\n list: (sessionId?: string) => PendingInteraction[]\n rehydrate: (sessionId: string) => Promise<PendingInteraction[]>\n /**\n * Answer every pending MODEL interaction with the canonical automatic\n * response — the runtime half of `interactionMode: 'skip'`. Flipping the\n * setting mid-run must not leave modals that were raised under `ask` hanging\n * for an answer the new mode says nobody will give. Host-synthesized prompts\n * are left alone (local code is awaiting a real decision on those). Returns\n * the number resolved.\n */\n resolveAutomatically: (sessionId?: string) => number\n /** Drop every pending interaction for a session (session delete / dispose). */\n clearSession: (sessionId: string) => void\n}\n\nexport function createInteractionHub(options: {\n broadcast: (signal: HostSignal) => void\n store: SessionStore\n /**\n * Live settings read. The hub consults `interactionMode` itself at rehydrate\n * time rather than taking an `automatic` flag from each caller: the daemon\n * dispatch, the GUI adapter, and the host's own activation path all\n * rehydrate, and a flag any one of them could omit is exactly how a policy\n * silently stops applying on one surface.\n */\n readSettings: () => Settings\n /**\n * Called after a LIVE plan is answered `reject`. The TUI's in-process\n * flow aborts the run at that point (the model records the rejection as\n * its tool_result, then the loop unwinds) — the hub does the same for\n * every host surface so a rejected plan means \"stop\", not \"carry on\n * without approval\", in the GUI and over the daemon alike.\n */\n onPlanRejected?: (sessionId: string) => void\n}): InteractionHub {\n const { broadcast, store, readSettings, onPlanRejected } = options\n\n /** Slots keyed by `request.id` (canonical tool_call id — stable across the wire). */\n const pending = new Map<string, Slot>()\n\n /**\n * Per-session `requestId → response` map for resumed (non-live)\n * interactions. Flushed as ONE `buildResumedToolResultsTurn` user turn\n * once every sibling orphan has an explicit response.\n */\n const resumedResponses = new Map<string, Map<string, InteractionResponse>>()\n\n /**\n * Per-session flush serialization — two rapid answers would otherwise\n * race the persisted-turn read and double-append the resume turn.\n */\n const flushChains = new Map<string, Promise<void>>()\n\n function scheduleFlush(sessionId: string): void {\n const prior = flushChains.get(sessionId) ?? Promise.resolve()\n const next = prior.catch(() => undefined).then(() => flushResumedBatch(sessionId))\n flushChains.set(sessionId, next)\n next.finally(() => {\n if (flushChains.get(sessionId) === next)\n flushChains.delete(sessionId)\n })\n }\n\n async function flushResumedBatch(sessionId: string): Promise<void> {\n const responses = resumedResponses.get(sessionId)\n if (!responses || responses.size === 0)\n return\n\n const data = await store.load(sessionId)\n if (!data)\n return\n\n /*\n * Recompute orphans against the latest persisted turns — another\n * action may have resolved some calls between rehydrate and respond.\n */\n const orphans = pendingInteractionsFromTurns(data.turns)\n if (orphans.length === 0) {\n resumedResponses.delete(sessionId)\n return\n }\n\n /*\n * Only flush once every sibling has an explicit response — a partial\n * flush would leave unanswered sibling `tool_call`s without an\n * immediately-following `tool_result`.\n */\n if (!orphans.every((r: InteractionRequest) => responses.has(r.id)))\n return\n\n const turn = buildResumedToolResultsTurn(orphans, responses, { turnId: randomUUID() })\n await store.appendTurns(sessionId, [turn])\n\n for (const r of orphans)\n pending.delete(r.id)\n resumedResponses.delete(sessionId)\n }\n\n const hub: InteractionHub = {\n requestInteractionFor(sessionId) {\n return request => new Promise<InteractionResponse>((resolve, reject) => {\n pending.set(request.id, { sessionId, request, resolve, reject, live: true })\n broadcast({ kind: 'interaction:request', sessionId, request, live: true })\n })\n },\n\n respond(requestId, response) {\n const slot = pending.get(requestId)\n if (!slot)\n return { ok: false }\n\n pending.delete(requestId)\n\n if (slot.live) {\n slot.resolve(response)\n broadcast({ kind: 'interaction:resolved', sessionId: slot.sessionId, requestId })\n if (response.kind === 'plan' && response.decision === 'reject')\n onPlanRejected?.(slot.sessionId)\n return { ok: true }\n }\n\n /*\n * Resumed path — the agent loop is long gone. Collect into the\n * per-session response map and schedule a serialized flush.\n */\n const bucket = resumedResponses.get(slot.sessionId) ?? new Map<string, InteractionResponse>()\n bucket.set(requestId, response)\n resumedResponses.set(slot.sessionId, bucket)\n scheduleFlush(slot.sessionId)\n\n broadcast({ kind: 'interaction:resolved', sessionId: slot.sessionId, requestId })\n return { ok: true }\n },\n\n cancel(requestId, reason = 'Interaction cancelled by user.') {\n const slot = pending.get(requestId)\n if (!slot)\n return { ok: false }\n\n pending.delete(requestId)\n\n if (slot.live)\n slot.reject(new Error(reason))\n\n broadcast({ kind: 'interaction:resolved', sessionId: slot.sessionId, requestId })\n return { ok: true }\n },\n\n list(sessionId) {\n const out: PendingInteraction[] = []\n for (const slot of pending.values()) {\n if (sessionId && slot.sessionId !== sessionId)\n continue\n out.push({ sessionId: slot.sessionId, request: slot.request, live: slot.live })\n }\n return out\n },\n\n async rehydrate(sessionId) {\n const data = await store.load(sessionId)\n if (!data)\n return []\n\n const orphans = pendingInteractionsFromTurns(data.turns)\n const queued: PendingInteraction[] = []\n /*\n * Skip mode resolves orphaned MODEL interactions with the canonical\n * automatic policy instead of surfacing a modal for them — the same\n * answer the live tool would have given under this mode, so a resume\n * behaves like the run never paused. Host-synthesized prompts (slash\n * commands, extension `ctx.interaction.*`) are excluded by\n * `isModelInteraction`: local code is awaiting a real decision on those\n * and an automatic answer would be a wrong one, not a skipped one.\n */\n const automatic = readSettings().interactionMode === 'skip'\n const autoResolved: InteractionRequest[] = []\n\n for (const request of orphans) {\n if (pending.has(request.id))\n continue\n\n pending.set(request.id, {\n sessionId,\n request,\n live: false,\n // No live caller on the resume path — `respond` routes to the\n // batched flush instead of these.\n resolve: () => undefined,\n reject: () => undefined,\n })\n if (automatic && isModelInteraction(request)) {\n autoResolved.push(request)\n continue\n }\n queued.push({ sessionId, request, live: false })\n broadcast({ kind: 'interaction:request', sessionId, request, live: false })\n }\n // Resolve after the loop so the batched flush sees every sibling orphan\n // registered before the first answer lands.\n for (const request of autoResolved)\n hub.respond(request.id, createAutomaticInteractionResponse(request))\n return queued\n },\n\n resolveAutomatically(sessionId) {\n const slots = [...pending.values()].filter(slot =>\n (!sessionId || slot.sessionId === sessionId) && isModelInteraction(slot.request))\n for (const slot of slots)\n hub.respond(slot.request.id, createAutomaticInteractionResponse(slot.request))\n return slots.length\n },\n\n clearSession(sessionId) {\n for (const [id, slot] of pending) {\n if (slot.sessionId !== sessionId)\n continue\n if (slot.live)\n slot.reject(new Error('Session disposed before interaction resolved.'))\n pending.delete(id)\n }\n resumedResponses.delete(sessionId)\n },\n }\n return hub\n}\n","/**\n * Orchestration — host-side: the main session, its workers, and how a worker's\n * outcome reaches the main.\n *\n * Deliberately its own module rather than more code in `engine.ts`. It needs\n * only a small, explicit slice of the host (send / steer / interrupt, the\n * live-session lookup, the signal fan-out) and it observes the rest through\n * ONE seam: `onSignal`, which the engine calls from `broadcast`. That single\n * seam is how it learns both that a worker finished and that the main went\n * idle, so no hook is registered on either agent.\n *\n * Delivery model — the part that had to be right:\n *\n * - The human sees a worker event IMMEDIATELY, mid-stream if need be: it is\n * broadcast as a `session-event` transcript row, which is pure display and\n * never touches the model's in-flight generation.\n * - The MODEL sees it at the next turn boundary. Events are buffered per\n * main and flushed only when the main is idle: each becomes a `role:\n * 'user'` turn (the only role the model reads) carrying `<session-event>`\n * XML, appended to the main's session, followed by an empty `wake` run.\n * An empty-prompt run is legal exactly when the trailing turn is `user`\n * (`validateAndPrepareResume`), which is the case by construction.\n *\n * Buffering rather than appending mid-run is not caution, it is correctness:\n * a user turn appended between an assistant `tool_call` and its\n * `tool_result` would leave the conversation malformed for the provider.\n * The run-end drain is the same discipline the host already applies to\n * background tasks and the shared queue.\n *\n * Ownership: the main holds no lock on its workers. Its `steer` and a window's\n * `steer` are both just queued messages, and the main learns the outcome via\n * `run:end` regardless of who triggered the run — so \"human wins\" is not a\n * rule to enforce, it is what falls out of having nothing to fight over.\n */\nimport type { Session, SessionRun, SessionStore } from '../../session/index'\nimport type { ToolDef } from '../../tools/types'\nimport type { SessionTurn } from '../../types'\nimport type { Settings } from '../index'\nimport type { ProjectBrief, SessionEvent } from '../orchestration'\nimport type { HostSignal, SendOptions } from './signals'\nimport { randomUUID } from 'node:crypto'\nimport { loadSession } from '../../session/index'\nimport { artifactsFromTurns, isMainSession, MAIN_SESSION_TITLE, ORCHESTRATED_BY_KEY, orchestratedBy, renderSessionEventXml, SESSION_ROLE_KEY, sessionEventLabel } from '../orchestration'\n\n/** Everything the module needs from the host, and nothing it does not. */\nexport interface OrchestrationDeps {\n store: SessionStore\n broadcast: (signal: HostSignal) => void\n readSettings: () => Pick<Settings, 'orchestration'>\n sendPrompt: (opts: SendOptions) => Promise<{ runId: string }>\n /**\n * Start a host-initiated wake run (empty prompt, no `user-prompt` echo) on a\n * session that already has the turns to read. Separate from `sendPrompt`\n * because a wake carries guards a user send must not: the esc-latch, the\n * type-ahead queue, and the cross-process own-marker rule. See the engine's\n * `wakeRun`, which is the single implementation of all three.\n */\n wake: (sessionId: string) => Promise<void>\n steer: (sessionId: string, message: string) => void\n interrupt: (sessionId: string) => void\n /** The live agent's `Session` when one is hosted, else `undefined`. */\n liveSession: (sessionId: string) => Session | undefined\n isRunning: (sessionId: string) => boolean\n /**\n * Create a session and return its id. Injected because creation is\n * transport-owned today (the daemon and the GUI each have a factory); the\n * host adds the lineage metadata on top.\n */\n createSession: (params: { cwd: string, title?: string, agentId?: string, providerKey?: string, modelId?: string }) => Promise<string>\n}\n\nexport interface CreateWorkerParams {\n /** Directory the worker runs in — the headline use case. */\n cwd: string\n /** First prompt. Sent immediately; the worker starts working on creation. */\n prompt: string\n title?: string\n agentId?: string\n providerKey?: string\n modelId?: string\n /**\n * Whether the worker finishing should start a run on the main. Default\n * `true`. `false` = fire-and-forget: the report still lands in the main's\n * history and transcript, the main just is not woken for it.\n */\n wake?: boolean\n}\n\nexport interface WorkerRow {\n sessionId: string\n title: string\n cwd: string\n running: boolean\n /** Created by this main (vs. a session the human started elsewhere). */\n worker: boolean\n /** `blocked` = a pending approval or question no one has answered. */\n state: 'idle' | 'running' | 'blocked'\n /** Last persisted change, epoch ms. */\n updatedAt: number\n /** Total turns in the session. */\n turns: number\n}\n\n/** What the main's `read` verb returns — a projection, not a transcript. */\nexport interface WorkerSummary extends WorkerRow {\n /** Last assistant text, trimmed to a few hundred chars. */\n lastMessage: string | null\n turns: number\n}\n\nexport interface Orchestration {\n /** The single observation seam. Called by the engine from `broadcast`. */\n onSignal: (signal: HostSignal) => void\n /** Current main session id, or `null` when the feature is off / none exists. */\n mainId: () => Promise<string | null>\n /** Create the main if the setting is on and none exists. Idempotent. Directory-less by design. */\n ensureMain: () => Promise<string | null>\n createWorker: (mainId: string, params: CreateWorkerParams) => Promise<string>\n /** Other sessions, newest first, capped (default {@link DEFAULT_LIST_LIMIT}). */\n list: (mainId: string, limit?: number) => Promise<WorkerRow[]>\n /**\n * Directories with ongoing work, most recent first, capped — the main's\n * system-prompt briefing (see `renderProjectsBriefing`). Empty when off.\n */\n projects: (limit?: number) => Promise<ProjectBrief[]>\n read: (sessionId: string) => Promise<WorkerSummary | null>\n /** Is `sessionId` a worker this main CREATED? (Lineage, not permission.) */\n isWorkerOf: (mainId: string, sessionId: string) => Promise<boolean>\n /** May `mainId` act on `sessionId`? Any existing non-main session. */\n canOrchestrate: (mainId: string, sessionId: string) => Promise<boolean>\n /**\n * New prompt to a worker (queued if mid-run). Rejects non-workers. `wake`\n * (default `true`) sets whether the main is woken when the run finishes.\n */\n send: (mainId: string, sessionId: string, prompt: string, wake?: boolean) => Promise<void>\n /** Guidance into a worker's current run at its next boundary. Rejects non-workers. */\n steer: (mainId: string, sessionId: string, message: string) => Promise<void>\n /** Interrupt a worker's run. Rejects non-workers. */\n cancel: (mainId: string, sessionId: string) => Promise<void>\n /**\n * The last `limit` turns of a session as plain text (`role: text`), oldest\n * first. The drill-down behind `read`'s one-line `lastMessage`: enough to\n * judge a worker's work without opening it.\n */\n transcript: (mainId: string, sessionId: string, limit?: number) => Promise<string>\n}\n\n/**\n * How long to wait for a burst of worker events to settle before waking the\n * main. Short enough to feel immediate, long enough that several workers\n * finishing together cost one run instead of one each.\n */\nconst FLUSH_DEBOUNCE_MS = 150\n\n/**\n * Default cap on `list`. Every row goes straight into the main's context, and a\n * user with a few hundred sessions would otherwise spend most of a turn on a\n * directory listing. Newest-first, so the cap drops the least relevant rows.\n */\nexport const DEFAULT_LIST_LIMIT = 30\n\n/** Projects in the main's system-prompt briefing — the recent ones, not the archive. */\nexport const DEFAULT_PROJECTS_LIMIT = 12\n\n/** Hard ceiling, so an over-eager `limit` cannot undo the cap's purpose. */\nconst MAX_LIST_LIMIT = 100\n\n/** Cap the model-facing summary so a chatty worker cannot flood the main's context. */\nconst SUMMARY_MAX = 600\n\nexport function createOrchestration(deps: OrchestrationDeps): Orchestration {\n /**\n * Worker → main lineage, cached. Populated on `createWorker` and lazily on\n * first sight of an unknown session (a worker created before a daemon\n * restart). `null` = \"looked up, not a worker\" so ordinary sessions cost one\n * store read, ever.\n */\n const lineage = new Map<string, string | null>()\n /** Events waiting for their main to go idle. */\n const pending = new Map<string, Array<{ event: SessionEvent, wake: boolean }>>()\n /** Flush in flight per main — a second signal must not double-append. */\n const flushing = new Set<string>()\n /** Per-main event sequence — dedup / gap detection for consumers. Restarts with the process; monotonic within it. */\n const seqByMain = new Map<string, number>()\n const nextSeq = (main: string): number => {\n const n = (seqByMain.get(main) ?? 0) + 1\n seqByMain.set(main, n)\n return n\n }\n /**\n * Per session, the `needs-attention` requests already reported during the\n * current run — keyed by the request's SHAPE (see `attentionDetail`), and\n * cleared on that session's `run:end`.\n *\n * Keyed rather than a bare session flag because both failure modes matter. A\n * worker in safe mode delivers one request per tool call, so a per-request\n * key would turn twenty approvals into twenty wake RUNS on the main for a\n * condition that has not changed; but a bare per-SESSION flag went too far\n * the other way, swallowing a genuinely different question asked later in\n * the same run. Same question twice stays quiet; a different one reports.\n */\n const flagged = new Map<string, Set<string>>()\n /**\n * Debounce timers per main. Bursts (several workers finishing at once, or a\n * worker emitting several requests) coalesce into ONE wake run instead of\n * one per event. Only the idle path needs this — while the main is running,\n * buffering already batches.\n */\n const flushTimers = new Map<string, ReturnType<typeof setTimeout>>()\n\n const enabled = (): boolean => deps.readSettings().orchestration === true\n\n /**\n * The feature gate, on every verb and not just on discovery.\n *\n * `mainId` / `ensureMain` returning `null` while the setting is off is not\n * enough: the main is PERSISTED, so its id survives the toggle, and anything\n * holding it (resume-on-launch, an IPC client, an extension) could otherwise\n * still spawn sessions and start runs that cost money — with no events\n * delivered, since `onSignal` bails. Off means off for every caller.\n */\n function requireEnabled(): void {\n if (!enabled())\n throw new Error('orchestration is disabled')\n }\n\n /**\n * The CALLER check, shared by every verb that takes a `mainId`. That id\n * arrives over the wire, so an unchecked one would make orchestration an\n * open remote-control API for any client that guesses a session id.\n */\n async function requireMainCaller(main: string): Promise<void> {\n requireEnabled()\n if (!isMainSession((await metadataOf(main)) ?? undefined))\n throw new Error('only a main session may orchestrate')\n }\n\n async function metadataOf(sessionId: string): Promise<Record<string, unknown> | null> {\n const live = deps.liveSession(sessionId)\n if (live)\n return live.metadata\n const data = await deps.store.load(sessionId)\n return data?.metadata ?? null\n }\n\n async function mainOf(sessionId: string): Promise<string | null> {\n if (lineage.has(sessionId))\n return lineage.get(sessionId)!\n const meta = await metadataOf(sessionId)\n const main = orchestratedBy(meta ?? undefined)\n lineage.set(sessionId, main)\n return main\n }\n\n /**\n * Wake preference for the NEXT run of a session, written by the main's\n * `create` / `send` and CONSUMED when that run ends (see the `run:end`\n * branch). One assignment, one wake: left in place it would outlive the run\n * it described, so a session the human also drives would keep waking the\n * main for turns the main never asked for.\n *\n * In-memory on purpose: it describes the main's current assignment, which is\n * meaningless after a daemon restart (the run it referred to is gone), and\n * writing it into a live session's metadata would race the agent's saves.\n * Absent = the default rule: own workers wake, other sessions only report.\n */\n const wakePolicy = new Map<string, boolean>()\n\n /**\n * Where a session's events go. EVERY non-main session reports to the main —\n * the main is an observer of the whole store, not only of the sessions it\n * created — so a chat the human runs elsewhere still shows up in the main's\n * history. Returns `null` for the main itself and for unknown ids.\n */\n async function reportsTo(sessionId: string): Promise<{ main: string, worker: boolean, wake: boolean } | null> {\n const main = await mainId()\n if (!main || sessionId === main)\n return null\n const meta = await metadataOf(sessionId)\n if (!meta || isMainSession(meta))\n return null\n const worker = (await mainOf(sessionId)) === main\n return { main, worker, wake: wakePolicy.get(sessionId) ?? worker }\n }\n\n /**\n * Cached once found: the main's id is stable for the store's lifetime, and\n * `store.list()` returns ids only, so discovery is a load per session.\n * `undefined` = not looked up yet; `null` = looked up, none exists.\n */\n let cachedMain: string | null | undefined\n\n /**\n * Every session's `{ id, metadata }`.\n *\n * Prefers the store's indexed metadata query — one round-trip on sqlite —\n * because the fan-out alternative materializes the FULL turn history of\n * every session on disk, and `list()` runs this on every `sessions list`\n * the model issues. Degrades to the fan-out when the store has no index or\n * the index throws, mirroring how `listSessionMeta` degrades.\n *\n * Live metadata wins over the stored row: an agent that has set\n * `orchestratedBy` but not yet saved is still a worker.\n */\n interface ScanRow { id: string, metadata: Record<string, unknown>, updatedAt: number, turns: number, projectRoot?: string }\n\n async function scan(): Promise<ScanRow[]> {\n if (deps.store.listMeta) {\n try {\n const rows = await deps.store.listMeta()\n return rows.map((row) => {\n const live = deps.liveSession(row.id)\n return {\n id: row.id,\n // Live wins: an agent that has set a pointer but not yet saved is\n // still a worker.\n metadata: live?.metadata ?? row.metadata,\n updatedAt: row.updatedAt,\n turns: live?.turns.length ?? row.turnCount,\n ...(row.projectRoot !== undefined ? { projectRoot: row.projectRoot } : {}),\n }\n })\n }\n catch {\n // Fall through to the fan-out below.\n }\n }\n const ids = await deps.store.list()\n const rows = await Promise.all(ids.map(async (id): Promise<ScanRow | null> => {\n const live = deps.liveSession(id)\n if (live)\n return { id, metadata: live.metadata, updatedAt: Date.now(), turns: live.turns.length, ...(live.projectRoot !== undefined ? { projectRoot: live.projectRoot } : {}) }\n const data = await deps.store.load(id)\n return data ? { id, metadata: data.metadata, updatedAt: data.updatedAt, turns: data.turns.length, ...(data.projectRoot !== undefined ? { projectRoot: data.projectRoot } : {}) } : null\n }))\n return rows.filter((r): r is ScanRow => r !== null)\n }\n\n async function mainId(): Promise<string | null> {\n if (!enabled())\n return null\n if (cachedMain !== undefined)\n return cachedMain\n const found = (await scan()).find(row => isMainSession(row.metadata))\n cachedMain = found?.id ?? null\n return cachedMain\n }\n\n /**\n * In-flight `ensureMain`, so overlapping callers share one creation.\n *\n * Reachable in practice: the GUI fires its on-enable effect and the user can\n * hit the jump chord right behind it, and under a daemon a GUI and a TUI\n * both call `orchestration.main`. Without this both see \"no main\", both\n * create one, and the store ends up with two sessions tagged `role: 'main'`\n * — after which `mainId()` resolves to whichever `scan()` returns first, so\n * the pin and the jump can disagree and events route to an arbitrary one.\n */\n let creating: Promise<string | null> | undefined\n\n async function ensureMain(): Promise<string | null> {\n if (!enabled())\n return null\n if (creating)\n return creating\n /*\n * Assigned with no `await` between the check above and the assignment\n * below, so a second caller on this event-loop turn cannot slip past.\n */\n const inflight = (async (): Promise<string | null> => {\n const existing = await mainId()\n if (existing) {\n await unbindMainFromProject(existing)\n return existing\n }\n // Directory-less: no cwd, no project. See `createMainSession` for why.\n const id = await deps.createSession({ cwd: '', title: MAIN_SESSION_TITLE })\n const session = await loadSession(deps.store, id)\n if (!session)\n throw new Error(`main session ${id} vanished after creation`)\n session.setMeta(SESSION_ROLE_KEY, 'main')\n await session.save()\n cachedMain = id\n // `create`, not `update`: clients have never seen this id, and a list that\n // only appends on `create` would otherwise never show the main until reload.\n deps.broadcast({ kind: 'sessions:changed', op: 'create', sessionId: id })\n return id\n })()\n creating = inflight\n try {\n return await inflight\n }\n finally {\n creating = undefined\n }\n }\n\n /**\n * One-time repair for mains created before they were directory-less: clear\n * the recorded cwd and drop the project so the row moves to the \"Chats\"\n * bucket. Store-level, so only while no agent is live for it — the live\n * `Session` would overwrite a behind-its-back write on its next save.\n */\n async function unbindMainFromProject(main: string): Promise<void> {\n if (deps.liveSession(main))\n return\n const data = await deps.store.load(main)\n if (!data)\n return\n const cwd = data.metadata.cwd\n if ((typeof cwd !== 'string' || cwd === '') && data.projectRoot === undefined)\n return\n data.metadata.cwd = ''\n delete data.projectRoot\n await deps.store.save(data)\n deps.broadcast({ kind: 'sessions:changed', op: 'update', sessionId: main })\n }\n\n async function createWorker(main: string, params: CreateWorkerParams): Promise<string> {\n /*\n * Depth cap 1, enforced at the only place a worker is born: a main may not\n * create another main, and a worker may not create workers. Without this\n * two mains could dispatch to each other indefinitely, each run costing\n * real money, with nothing in the UI that would show why.\n */\n requireEnabled()\n const meta = await metadataOf(main)\n if (!isMainSession(meta ?? undefined))\n throw new Error('only a main session may create workers')\n const id = await deps.createSession({\n cwd: params.cwd,\n ...(params.title ? { title: params.title } : {}),\n ...(params.agentId ? { agentId: params.agentId } : {}),\n ...(params.providerKey ? { providerKey: params.providerKey } : {}),\n ...(params.modelId ? { modelId: params.modelId } : {}),\n })\n const session = await loadSession(deps.store, id)\n if (!session)\n throw new Error(`worker session ${id} vanished after creation`)\n session.setMeta(ORCHESTRATED_BY_KEY, main)\n await session.save()\n lineage.set(id, main)\n wakePolicy.set(id, params.wake ?? true)\n deps.broadcast({ kind: 'sessions:changed', op: 'update', sessionId: id })\n /*\n * Fire-and-forget: the main learns the outcome through `run:end`, not by\n * awaiting here — awaiting would pin the main's tool call for the worker's\n * entire run.\n *\n * But a REJECTION must not be silent. The tool has already told the model\n * \"you will receive a <session-event> when it finishes\", so a swallowed\n * failure leaves it waiting forever on an event that will never arrive,\n * next to a session that never ran. Synthesize the error event instead.\n */\n void deps.sendPrompt({ sessionId: id, prompt: params.prompt }).catch((err: unknown) => {\n deliver(main, {\n sessionId: id,\n title: params.title ?? id,\n status: 'error',\n summary: `never started: ${err instanceof Error ? err.message : String(err)}`,\n at: Date.now(),\n cwd: params.cwd,\n }, true)\n })\n return id\n }\n\n async function list(main: string, limit = DEFAULT_LIST_LIMIT): Promise<WorkerRow[]> {\n await requireMainCaller(main)\n // Every non-main session — the main observes the whole store; `worker`\n // marks the ones it created itself.\n const rows = (await scan())\n .filter(row => row.id !== main && !isMainSession(row.metadata))\n .map((row) => {\n const running = deps.isRunning(row.id)\n return {\n sessionId: row.id,\n title: titleOf(row.metadata, row.id),\n cwd: cwdOf(row.metadata),\n running,\n worker: orchestratedBy(row.metadata) === main,\n state: (flagged.get(row.id)?.size ?? 0) > 0 ? 'blocked' as const : running ? 'running' as const : 'idle' as const,\n updatedAt: row.updatedAt,\n turns: row.turns,\n }\n })\n // Sorted before slicing so the cap keeps the rows most likely to matter.\n rows.sort((a, b) => b.updatedAt - a.updatedAt)\n return rows.slice(0, Math.max(1, Math.min(limit, MAX_LIST_LIMIT)))\n }\n\n async function projects(limit = DEFAULT_PROJECTS_LIMIT): Promise<ProjectBrief[]> {\n if (!enabled())\n return []\n const byRoot = new Map<string, ProjectBrief>()\n for (const row of await scan()) {\n if (isMainSession(row.metadata))\n continue\n // projectRoot is the sidebar's grouping key; cwd is the fallback for\n // legacy untagged sessions. Directory-less rows have neither.\n const root = row.projectRoot ?? cwdOf(row.metadata)\n if (!root)\n continue\n const running = deps.isRunning(row.id)\n const state = (flagged.get(row.id)?.size ?? 0) > 0 ? 'blocked' as const : running ? 'running' as const : 'idle' as const\n const active = state === 'idle' ? 0 : 1\n const latest = { sessionId: row.id, title: titleOf(row.metadata, row.id), state }\n const prev = byRoot.get(root)\n if (!prev) {\n byRoot.set(root, { root, sessions: 1, lastActiveAt: row.updatedAt, active, latest })\n continue\n }\n prev.sessions += 1\n prev.active += active\n if (row.updatedAt > prev.lastActiveAt) {\n prev.lastActiveAt = row.updatedAt\n prev.latest = latest\n }\n }\n return [...byRoot.values()]\n .sort((a, b) => b.lastActiveAt - a.lastActiveAt)\n .slice(0, Math.max(1, limit))\n }\n\n async function read(sessionId: string): Promise<WorkerSummary | null> {\n if (!enabled())\n return null\n const data = deps.liveSession(sessionId) ?? await deps.store.load(sessionId)\n if (!data)\n return null\n const lastAssistant = [...data.turns].reverse().find(t => t.role === 'assistant')\n const lastMessage = lastAssistant ? assistantText(lastAssistant) : null\n const running = deps.isRunning(sessionId)\n return {\n sessionId,\n title: titleOf(data.metadata, sessionId),\n cwd: cwdOf(data.metadata),\n running,\n worker: orchestratedBy(data.metadata) !== null,\n state: (flagged.get(sessionId)?.size ?? 0) > 0 ? 'blocked' : running ? 'running' : 'idle',\n updatedAt: 'updatedAt' in data && typeof data.updatedAt === 'number' ? data.updatedAt : Date.now(),\n lastMessage: lastMessage ? truncate(lastMessage, SUMMARY_MAX) : null,\n turns: data.turns.length,\n }\n }\n\n async function isWorkerOf(main: string, sessionId: string): Promise<boolean> {\n return (await mainOf(sessionId)) === main\n }\n\n /*\n * Verbs reach ANY non-main session, not only the ones this main created —\n * the main orchestrates all the agents, including chats the human started\n * elsewhere. What it may never do is target itself or another main: that is\n * the recursion the depth cap exists to prevent, so it is refused here for\n * the tool, the RPC, and any extension caller alike.\n *\n * The CALLER is checked too (see `requireMainCaller`), and so is the\n * feature gate — the same reason `createWorker` insists on both before\n * spending money on a run.\n */\n async function requireOrchestratable(main: string, sessionId: string): Promise<void> {\n requireEnabled()\n if (sessionId === main)\n throw new Error('a main session cannot orchestrate itself')\n await requireMainCaller(main)\n const meta = await metadataOf(sessionId)\n if (!meta)\n throw new Error(`unknown session ${sessionId}`)\n if (isMainSession(meta))\n throw new Error(`${sessionId} is a main session and cannot be orchestrated`)\n }\n\n async function canOrchestrate(main: string, sessionId: string): Promise<boolean> {\n try {\n await requireOrchestratable(main, sessionId)\n return true\n }\n catch {\n return false\n }\n }\n\n async function send(main: string, sessionId: string, prompt: string, wake = true): Promise<void> {\n await requireOrchestratable(main, sessionId)\n // The main asked for this run, so by default it wants to hear back — even\n // from a session the human started, which would otherwise only report.\n wakePolicy.set(sessionId, wake)\n await deps.sendPrompt({ sessionId, prompt })\n }\n\n async function steer(main: string, sessionId: string, message: string): Promise<void> {\n await requireOrchestratable(main, sessionId)\n deps.steer(sessionId, message)\n }\n\n async function cancel(main: string, sessionId: string): Promise<void> {\n await requireOrchestratable(main, sessionId)\n deps.interrupt(sessionId)\n }\n\n async function transcript(main: string, sessionId: string, limit = 10): Promise<string> {\n await requireOrchestratable(main, sessionId)\n const data = deps.liveSession(sessionId) ?? await deps.store.load(sessionId)\n if (!data)\n return ''\n const capped = Math.max(1, Math.min(limit, 50))\n return data.turns\n .filter(t => t.role === 'user' || t.role === 'assistant')\n .slice(-capped)\n .map((t) => {\n const text = t.content\n .filter((b): b is { type: 'text', text: string } => b.type === 'text')\n .map(b => b.text)\n .join('\\n')\n .trim()\n const tools = t.content.filter(b => b.type === 'tool_call').map(b => (b as { name: string }).name)\n const body = text || (tools.length ? `[tool calls: ${tools.join(', ')}]` : '[no text]')\n return `${t.role}: ${truncate(body, 1200)}`\n })\n .join('\\n\\n')\n }\n\n // ---------------------------------------------------------------------------\n // Delivery\n // ---------------------------------------------------------------------------\n\n /**\n * `wake` = start a run so the model reacts now. True for the main's OWN\n * workers and for anything blocked on a decision; false for an ordinary\n * session finishing, which is appended as context and read on the main's\n * next natural turn. Waking on every unrelated chat would cost a run each\n * time the human finishes a conversation somewhere else.\n */\n function deliver(main: string, rawEvent: SessionEvent, wake: boolean): void {\n const event: SessionEvent = { ...rawEvent, seq: nextSeq(main) }\n // 1. Display, now. A pure transcript row — nothing here touches the\n // model's generation, so this is safe mid-stream by construction.\n deps.broadcast({\n kind: 'event',\n sessionId: main,\n event: { kind: 'session-event', text: sessionEventLabel(event), sessionEvent: event },\n })\n // 2. Context, at the boundary.\n const queue = pending.get(main) ?? []\n queue.push({ event, wake })\n pending.set(main, queue)\n scheduleFlush(main)\n }\n\n /** Coalesce a burst of events into one wake run. */\n function scheduleFlush(main: string): void {\n if (flushTimers.has(main))\n return\n const timer = setTimeout(() => {\n flushTimers.delete(main)\n void flush(main)\n }, FLUSH_DEBOUNCE_MS)\n // Never let a pending debounce hold the process open — this is a\n // best-effort coalesce, not work the runtime must wait for.\n timer.unref?.()\n flushTimers.set(main, timer)\n }\n\n async function flush(main: string): Promise<void> {\n if (flushing.has(main) || deps.isRunning(main))\n return\n const events = pending.get(main)\n if (!events || events.length === 0)\n return\n flushing.add(main)\n pending.delete(main)\n /*\n * Set the moment the turns are durably appended — NOT when the wake run\n * succeeds. Once appended they are in the session's history, so a failed\n * wake only delays when the model reads them; requeueing at that point\n * would append the same events twice.\n */\n let appended = false\n try {\n /*\n * Write to the live agent's `Session` when one is hosted — the same\n * invariant as todos: the model reads the instance the agent holds, and\n * a store-level write behind it would be overwritten by the next save.\n * Idle-with-no-agent falls back to the store, which the next `sendPrompt`\n * builds an agent from.\n */\n const live = deps.liveSession(main)\n const session = live ?? await loadSession(deps.store, main)\n if (!session)\n return\n /*\n * Re-check AFTER the awaits above. The guard at the top of this function\n * is stale by now: a user prompt can start a run while we were loading,\n * and appending a user turn into a mid-run session is precisely the\n * malformed-conversation case this buffering exists to prevent (a user\n * turn landing between an assistant `tool_call` and its `tool_result`).\n * Yield — the run-end edge will flush.\n */\n if (deps.isRunning(main))\n return\n const now = Date.now()\n const turns: SessionTurn[] = events.map(({ event }, i) => ({\n id: randomUUID(),\n role: 'user',\n createdAt: now + i,\n content: [{ type: 'text', text: renderSessionEventXml(event) }],\n }))\n await session.appendTurns(turns)\n /*\n * Persist unconditionally. Writing through the LIVE session keeps the\n * model reading the instance the agent holds, but it does not make the\n * turns durable — and a batch with nothing to wake for (the\n * human-started-session-finished case) starts no run, so nothing else\n * would ever save them. An idle teardown or a model-change rebuild\n * between now and the main's next run would drop the events silently,\n * and unlike the requeue below there would be nothing left to recover.\n */\n await session.save()\n appended = true\n /*\n * Wake only when something in the batch warrants a reaction (own worker,\n * or a session blocked on a decision). Otherwise the turns sit as context\n * for the main's next natural turn. Empty prompt + trailing user turn =\n * the designed wake path; the `user-prompt` echo is suppressed since the\n * `session-event` row already painted in `deliver`.\n *\n * `deps.wake` may decline (the user pressed esc on the main, or a queued\n * message is about to drain). That is fine and deliberate: the turns are\n * already appended and saved above, so declining only delays when the\n * model reads them — the same fallback as a batch with nothing to wake\n * for. Starting a run against an explicit stop would be the worse bug.\n */\n if (events.some(e => e.wake))\n await deps.wake(main)\n }\n catch {\n // Swallowed deliberately: callers invoke this as `void flush(...)`, so a\n // rejection here would be an unhandled rejection. Recovery is the\n // requeue in `finally`, which also covers the early returns above.\n }\n finally {\n /*\n * Every non-delivering exit path restores the buffer — the `!session`\n * and mid-run returns as well as a throw. Dropping an event silently is\n * the one outcome worse than delivering it late, and an early `return`\n * walks around a `catch`.\n */\n if (!appended) {\n const again = pending.get(main) ?? []\n pending.set(main, [...events, ...again])\n }\n flushing.delete(main)\n }\n }\n\n function onSignal(signal: HostSignal): void {\n if (!enabled())\n return\n switch (signal.kind) {\n case 'run:end': {\n void (async () => {\n // Whatever this worker was blocked on is resolved or gone; let a\n // future block re-report.\n flagged.delete(signal.sessionId)\n // A main going idle is the flush edge for its buffered events.\n if (pending.has(signal.sessionId)) {\n await flush(signal.sessionId)\n return\n }\n const target = await reportsTo(signal.sessionId)\n if (!target)\n return\n // The assignment applied to the run that just ended; drop it so the\n // next run falls back to the default rule (see `wakePolicy`).\n wakePolicy.delete(signal.sessionId)\n const summary = await read(signal.sessionId)\n const now = Date.now()\n const data = deps.liveSession(signal.sessionId) ?? await deps.store.load(signal.sessionId)\n const metrics = data ? runMetrics(data, now) : {}\n /*\n * The run that just ended is the session's latest top-level record.\n * From it: the prompt that STARTED it (the goal — pairs with the\n * last message so the main can judge whether the run did what was\n * asked), the abnormal finish reason if any, and what the run\n * touched. All read off persisted data; nothing is inferred.\n */\n const record = data ? [...data.runs].reverse().find(r => r.parentRunId === undefined) : undefined\n const runTurns = record && data ? data.turns.filter(t => t.runId === record.id) : []\n const lastAssistant = [...runTurns].reverse().find(t => t.role === 'assistant')\n const finish = lastAssistant?.usage?.finishReason\n const artifacts = artifactsFromTurns(runTurns)\n deliver(target.main, {\n sessionId: signal.sessionId,\n title: summary?.title ?? signal.sessionId,\n status: signal.ok ? 'done' : 'error',\n summary: signal.ok\n ? (summary?.lastMessage ?? 'finished with no message')\n : `failed: ${signal.error ?? 'unknown error'}`,\n at: now,\n worker: target.worker,\n // Always present: '' means directory-less, which the main must be\n // able to tell apart from \"not reported\".\n cwd: summary?.cwd ?? '',\n ...(record?.prompt ? { goal: truncate(record.prompt, 300) } : {}),\n ...(finish === 'length' || finish === 'pause' || finish === 'content-filter' ? { finishReason: finish } : {}),\n ...(artifacts ? { artifacts } : {}),\n ...(!signal.ok && signal.error ? { error: signal.error } : {}),\n ...metrics,\n }, target.wake)\n })()\n return\n }\n case 'approval:request':\n case 'interaction:request': {\n const sessionId = signal.kind === 'approval:request' ? signal.request.sessionId : signal.sessionId\n const detail = attentionDetail(signal)\n // Shape key: identical requests collapse, a different question does not.\n const key = detail ?? signal.kind\n if (flagged.get(sessionId)?.has(key))\n return\n void (async () => {\n const target = await reportsTo(sessionId)\n if (!target)\n return\n // Record only once we know it reports somewhere, so the main's own\n // approvals never occupy the map. Re-checked after the await.\n const seen = flagged.get(sessionId) ?? new Set<string>()\n if (seen.has(key))\n return\n seen.add(key)\n flagged.set(sessionId, seen)\n const summary = await read(sessionId)\n // A blocked session always wakes the main: someone has to decide.\n deliver(target.main, {\n sessionId,\n title: summary?.title ?? sessionId,\n status: 'needs-attention',\n summary: signal.kind === 'approval:request'\n ? `waiting for approval: ${signal.request.tool}`\n : 'waiting for a decision',\n ...(detail ? { detail } : {}),\n at: Date.now(),\n worker: target.worker,\n ...(summary?.cwd ? { cwd: summary.cwd } : {}),\n }, true)\n })()\n return\n }\n case 'sessions:changed': {\n if (signal.op === 'delete') {\n lineage.delete(signal.sessionId)\n wakePolicy.delete(signal.sessionId)\n pending.delete(signal.sessionId)\n flagged.delete(signal.sessionId)\n const timer = flushTimers.get(signal.sessionId)\n if (timer) {\n clearTimeout(timer)\n flushTimers.delete(signal.sessionId)\n }\n if (cachedMain === signal.sessionId)\n cachedMain = undefined\n }\n /*\n * \"No main exists\" must NOT be cached across a session appearing.\n * A `null` here is a snapshot of the store at one instant, and another\n * writer can mint the main behind our back — the TUI creates one\n * locally through `createMainSession` whenever it has no daemon\n * connection yet, with the setting already on. Keeping the `null` then\n * makes `reportsTo` deliver nothing and the next `ensureMain` mint a\n * SECOND `role: 'main'` session, which is exactly the pin/jump\n * divergence the single-flight lock exists to avoid. A positive id is\n * left alone: the main's id is stable for the store's lifetime.\n */\n else if (cachedMain === null) {\n cachedMain = undefined\n }\n break\n }\n default:\n break\n }\n }\n\n return { onSignal, mainId, ensureMain, createWorker, list, projects, read, isWorkerOf, canOrchestrate, send, steer, cancel, transcript }\n}\n\n/**\n * The receipt for one run, read off the persisted run RECORD — the loop already\n * totals turns / tokens / cost there, so this is exact and needs no rescan of\n * the transcript.\n *\n * `run:end` is broadcast a beat before the record is finalized, so the record\n * may still be `running` with no `endedAt` when this runs: fall back to the\n * turn-level usage the loop has already attached, and to \"now\" for the end\n * time (the run IS ending). Every cell is omitted rather than zeroed when\n * genuinely unknown — a `0 tok` cell is a claim, and an untrue one.\n */\nfunction runMetrics(\n data: { runs: readonly SessionRun[], turns: readonly SessionTurn[] },\n now: number,\n): Pick<SessionEvent, 'durationMs' | 'tokens' | 'turns' | 'costUsd'> {\n /*\n * The latest TOP-LEVEL record, not a lookup by the signal's `runId`: the host\n * mints its own UUID for `run:end` bookkeeping while the session records the\n * agent's `run_N`, so they never match.\n *\n * `depth === 0` is load-bearing, not defensive. Subagent runs are pushed onto\n * the SAME `runs` array (`agent.ts:4440` → `session.startRun`), and a child\n * starts after its parent — so at the parent's run end `at(-1)` is the most\n * recently started CHILD. Taking it made a long delegating run report its\n * last subagent's duration, tokens and cost instead of its own. `depth` is\n * always numeric (the loop defaults it to 0), but `?? 0` keeps records\n * written before that default readable.\n */\n const run = data.runs.filter(r => (r.depth ?? 0) === 0).at(-1)\n if (!run)\n return {}\n const runTurns = data.turns.filter(t => t.runId === run.id)\n const usage = run.totalUsage\n ?? run.turnUsage?.reduce((acc, u) => ({ input: acc.input + u.input, output: acc.output + u.output }), { input: 0, output: 0 })\n const tokens = usage\n ? usage.input + usage.output\n : runTurns.reduce((n, t) => n + (t.usage ? t.usage.input + t.usage.output : 0), 0)\n const turns = run.turns ?? runTurns.length\n const durationMs = (run.endedAt ?? now) - run.startedAt\n const cost = run.cost ?? run.turnUsage?.reduce((n, u) => n + (u.cost ?? 0), 0) ?? 0\n return {\n ...(durationMs > 0 ? { durationMs } : {}),\n ...(tokens > 0 ? { tokens } : {}),\n ...(turns > 0 ? { turns } : {}),\n ...(cost > 0 ? { costUsd: cost } : {}),\n }\n}\n\n/**\n * What a blocked session is asking, in one line. This is often enough for the\n * main to answer without opening the transcript — which is the point of\n * shipping it on the event instead of making the main call `read`.\n */\nfunction attentionDetail(signal: Extract<HostSignal, { kind: 'approval:request' | 'interaction:request' }>): string | undefined {\n if (signal.kind === 'approval:request') {\n const r = signal.request\n const target = r.edit?.path ?? (typeof r.input?.command === 'string' ? r.input.command : undefined)\n return target ? `approve ${r.tool}: ${truncate(target, 160)}` : `approve ${r.tool}`\n }\n const req = signal.request\n if (req.kind === 'plan')\n return `review plan: ${truncate(req.payload.title, 160)}`\n if (req.kind === 'question') {\n const first = req.payload.questions[0]\n const lead = req.payload.intro ?? first?.prompt\n return lead ? `question: ${truncate(lead, 160)}` : 'question'\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// Small projections\n// ---------------------------------------------------------------------------\n\nfunction titleOf(metadata: Record<string, unknown> | undefined, fallback: string): string {\n const title = metadata?.title\n return typeof title === 'string' && title.trim() ? title : fallback\n}\n\nfunction cwdOf(metadata: Record<string, unknown> | undefined): string {\n const cwd = metadata?.cwd\n return typeof cwd === 'string' ? cwd : ''\n}\n\nfunction assistantText(turn: SessionTurn): string {\n return turn.content\n .filter((b): b is { type: 'text', text: string } => b.type === 'text')\n .map(b => b.text)\n .join('\\n')\n .trim()\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length <= max ? text : `${text.slice(0, max - 1)}…`\n}\n\n// ---------------------------------------------------------------------------\n// The model-facing tool\n// ---------------------------------------------------------------------------\n\nexport const SESSIONS_TOOL = 'sessions'\n\nconst SESSIONS_DESCRIPTION = `Orchestrate other sessions (\"workers\") from this main session.\n\nVerbs:\n- create: start a worker in a directory with a first prompt. Returns its sessionId. Do NOT wait for it — you will receive a <session-event> in this conversation when it finishes, fails, or needs a decision.\n- send: give a running or idle worker a new prompt (queued if it is mid-run).\n- wake (create / send, default true): whether that run finishing wakes you. Pass false for work you do not need to react to — the report still lands in your history for your next run. A blocked session always wakes you.\n- steer: inject guidance into a worker's CURRENT run at its next turn boundary.\n- cancel: interrupt a worker's run.\n- read: a summary of a session — title, cwd, state (idle/running/blocked), last message, turn count.\n- transcript: the last N turns (default 10, max 50) as plain text — the drill-down behind read's lastMessage.\n- list: other sessions with live status, newest first, capped at 30 by default (raise with limit, max 100) — the list may be PARTIAL. worker=true marks the ones you created.\n\nYou observe ALL sessions and receive a <session-event> whenever any of them finishes or gets blocked; only runs you asked for (create / send with wake true) and blocked sessions wake you. Each event carries: seq (dedup), goal (the prompt that started the run), summary (its last message), finish-reason when abnormal, artifacts (files changed, command count), cwd ('' = no directory), worker, and the run's duration/tokens/turns. You may send/steer/cancel/read any of them — but never another main session. Prefer one worker per independent unit of work (one repo, one feature).`\n\n/**\n * Build the `sessions` tool for ONE main session. Injected only into the main's\n * agent — a worker never sees it, which is the depth cap made structural.\n */\nexport function createSessionsTool(orch: Orchestration, mainId: string): ToolDef {\n const str = (v: unknown, name: string): string => {\n if (typeof v !== 'string' || !v.trim())\n throw new Error(`sessions: \\`${name}\\` is required`)\n return v\n }\n return {\n spec: {\n name: SESSIONS_TOOL,\n description: SESSIONS_DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n verb: { type: 'string', enum: ['create', 'send', 'steer', 'cancel', 'read', 'list', 'transcript'] },\n sessionId: { type: 'string', description: 'Worker id — required for every verb except create and list.' },\n cwd: { type: 'string', description: 'create: directory the worker runs in.' },\n prompt: { type: 'string', description: 'create / send: the prompt.' },\n message: { type: 'string', description: 'steer: guidance for the current run.' },\n title: { type: 'string', description: 'create: optional worker title.' },\n agentId: { type: 'string', description: 'create: optional agent profile id.' },\n limit: { type: 'number', description: 'list: how many sessions (default 30, max 100). transcript: how many trailing turns (default 10, max 50).' },\n wake: { type: 'boolean', description: 'create / send: wake you (start a run here) when that run finishes. Default true. false = fire-and-forget: the report still lands in your history for your next run.' },\n },\n required: ['verb'],\n },\n },\n async execute(input: Record<string, unknown>) {\n const verb = str(input.verb, 'verb')\n switch (verb) {\n case 'create': {\n const id = await orch.createWorker(mainId, {\n cwd: str(input.cwd, 'cwd'),\n prompt: str(input.prompt, 'prompt'),\n ...(typeof input.title === 'string' ? { title: input.title } : {}),\n ...(typeof input.agentId === 'string' ? { agentId: input.agentId } : {}),\n ...(typeof input.wake === 'boolean' ? { wake: input.wake } : {}),\n })\n return input.wake === false\n ? `created worker ${id}. Its report will be in your history next time you run; it will not wake you.`\n : `created worker ${id}. You will receive a <session-event> when it finishes.`\n }\n case 'send': {\n const id = str(input.sessionId, 'sessionId')\n await orch.send(mainId, id, str(input.prompt, 'prompt'), typeof input.wake === 'boolean' ? input.wake : true)\n return input.wake === false ? `sent to ${id} (will not wake you)` : `sent to ${id}`\n }\n case 'steer': {\n const id = str(input.sessionId, 'sessionId')\n await orch.steer(mainId, id, str(input.message, 'message'))\n return `steered ${id}`\n }\n case 'cancel': {\n const id = str(input.sessionId, 'sessionId')\n await orch.cancel(mainId, id)\n return `cancelled ${id}`\n }\n case 'read': {\n const id = str(input.sessionId, 'sessionId')\n if (!(await orch.canOrchestrate(mainId, id)))\n throw new Error(`sessions: ${id} cannot be orchestrated (unknown, or a main session)`)\n return JSON.stringify(await orch.read(id))\n }\n case 'list': {\n // Compact JSON: indentation is ~half the tokens of this payload and\n // buys the model nothing.\n const limit = typeof input.limit === 'number' ? input.limit : undefined\n return JSON.stringify(await orch.list(mainId, limit))\n }\n case 'transcript': {\n const id = str(input.sessionId, 'sessionId')\n const limit = typeof input.limit === 'number' ? input.limit : undefined\n return await orch.transcript(mainId, id, limit)\n }\n default:\n throw new Error(`sessions: unknown verb ${verb}`)\n }\n },\n }\n}\n","import type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../../agent'\nimport { sanitizeProviderDiagnosticMetadata } from '../../providers/diagnostic'\n\nexport type ProviderDiagnostic = Pick<\n Parameters<AgentHooks['provider:diagnostic']>[0],\n 'requestId' | 'stage' | 'event' | 'elapsedMs' | 'stageElapsedMs' | 'attempt' | 'runId' | 'turnId' | 'httpStatus' | 'upstreamRequestId' | 'transportAttempt' | 'networkCode' | 'retryAfter'\n>\n\nexport function installProviderDiagnosticHook(\n hooks: Hookable<AgentHooks>,\n emit: (diagnostic: ProviderDiagnostic) => void,\n): () => void {\n return hooks.hook('provider:diagnostic', ({ requestId, stage, event, elapsedMs, stageElapsedMs, attempt, runId, turnId, httpStatus, upstreamRequestId, transportAttempt, networkCode, retryAfter }) => {\n // Keep future provider payload additions out of host logs and the wire.\n emit({ requestId, stage, event, elapsedMs, stageElapsedMs, attempt, runId, turnId, ...sanitizeProviderDiagnosticMetadata({ httpStatus, upstreamRequestId, transportAttempt, networkCode, retryAfter }) })\n })\n}\n","/**\n * Host-domain wire shapes — the JSON-serializable types the chat host\n * (`createChatHost`) emits and consumes, independent of any transport.\n *\n * These used to live in `src/ipc/protocol.ts`; they moved here so the host\n * (`src/chat/host/**`) has zero dependency on `src/ipc/**`. The IPC layer\n * re-exports them from `src/ipc/protocol.ts` alongside its transport-only\n * contract (method map, handshake, protocol version), so `zidane/ipc`\n * consumers are unaffected.\n *\n * Everything in this file is JSON-serializable: it must survive Electron's\n * structured clone AND a JSON-RPC socket hop unchanged. Canonical agent\n * types (`StreamEvent`, `InteractionRequest`, …) are re-used where they are\n * already wire-safe; host-only shapes (live `resolve` callbacks, `Map`s)\n * never appear here.\n */\nimport type { CacheBreakInfo } from '../../cache-telemetry'\nimport type { SessionArtifact } from '../../session/artifacts'\nimport type { JsonValue, TurnUsage } from '../../types'\nimport type {\n ActiveTodosState,\n ApprovalDecision,\n ApprovalRequest,\n EditPayload,\n HunkResolution,\n InteractionRequest,\n Settings,\n StreamEvent,\n SystemMdFile,\n TodoItem,\n TodoTally,\n} from '../index'\nimport type { PromptTurnRef } from '../turn-selection'\nimport type { ProviderDiagnostic } from './provider-diagnostic'\n\n/** Caller attribution for approvals raised by subagents. */\nexport type ApprovalOriginator = { kind: 'parent' } | { kind: 'child', label: string }\n\n/**\n * Serializable mirror of canonical `ActiveTodosState` — plain arrays so the\n * payload survives structured clone / JSON without `readonly` friction.\n */\nexport interface TodosSnapshot {\n runId: string | null\n todos: TodoItem[]\n archive: TodoItem[]\n inProgress: TodoItem | null\n tally: TodoTally\n}\n\n/**\n * Project canonical `ActiveTodosState` onto its wire mirror.\n *\n * One implementation because the copy is load-bearing, not cosmetic: the arrays\n * must be detached from the live session's before they cross a process boundary\n * (or reach another window in-process), and a site that forgot to copy would\n * hand out a reference that mutates under its reader. Previously open-coded at\n * each of the broadcast and RPC sites.\n */\nexport function toTodosSnapshot(state: ActiveTodosState): TodosSnapshot {\n return {\n runId: state.runId,\n todos: [...state.todos],\n archive: [...state.archive],\n inProgress: state.inProgress,\n tally: state.tally,\n }\n}\n\n/** Parent-level cancellable tool call or background task. */\nexport interface RunningWorkItem {\n kind: 'tool' | 'task'\n id: string\n label: string\n startedAt: number\n childId?: string\n}\n\n/**\n * Serializable mirror of `PreviewResult` — `readonly` arrays widened so the\n * payload round-trips without TypeScript friction on the client side.\n */\nexport interface SerializedPreview {\n diffText: string\n perHunkDiff: readonly string[]\n resolution: readonly HunkResolution[]\n resolvedPayload: EditPayload\n}\n\n/**\n * Wire-safe approval request. Canonical `ApprovalRequest` minus its live\n * `resolve` callback (kept host-side, keyed by `id`), plus the context a\n * remote approval surface needs: owning session, safelist cwd, precomputed\n * safelist suggestions, and the edit preview byproducts.\n */\nexport interface WireApprovalRequest extends Omit<ApprovalRequest, 'resolve'> {\n sessionId: string\n cwd: string\n suggestedSafelistEntries: string[]\n edit?: EditPayload\n priorContent?: string | null\n preview?: SerializedPreview\n}\n\n/** Attachment carried alongside the prompt text (base64, no data-URL prefix). */\nexport interface PromptAttachment {\n mediaType: string\n data: string\n name?: string\n}\n\n/** `chat.send` options. */\nexport interface SendOptions {\n sessionId: string\n prompt: string\n /** Skill names picked via `/` completion — activated before the run. */\n activatedSkills?: string[]\n attachments?: PromptAttachment[]\n /**\n * Host-internal wake run: delivers pending background-task notifications\n * to the model with an empty prompt. Skips the `user-prompt` broadcast\n * (there is no user message) — mirrors the local TUI's wake semantics.\n */\n wake?: boolean\n /**\n * The client resolved this text as the `/` command of this name in ITS\n * registry. The host re-resolves against the SESSION's registry (its cwd,\n * its extensions) and refuses when that misses, instead of sending\n * `/fix comments` to the model as prose — the two registries can disagree\n * (extension installed after the runtime cache was built, different cwd),\n * and a silent prose send is indistinguishable from a broken command.\n */\n asCommand?: string\n}\n\n/**\n * Wire-safe summary of an extension-contributed `/` command — the\n * projection of canonical `Command` (src/chat/commands.ts) minus its\n * function-valued fields (steps, completeArgs).\n */\nexport interface CommandSummary {\n name: string\n description: string\n aliases?: string[]\n\n /** Grouping label — defaults to the contributing extension's name. */\n group?: string\n\n /**\n * `true` when the canonical command declares `completeArgs` — lets a\n * client wire an arg-completion shim only for commands that actually\n * complete, instead of firing blind RPCs on every `/name <prefix>`.\n */\n hasArgCompletion?: boolean\n}\n\n/** Wire-safe projection of canonical `CommandArgSuggestion`. */\nexport interface CommandArgSuggestionSummary {\n /** Text inserted after `/<name> ` on accept. */\n value: string\n\n /** Display label. Defaults to `value`. */\n label?: string\n description?: string\n}\n\n/** Host settings — canonical `Settings` plus the extension allowlist. */\nexport type HostSettings = Settings & {\n enabledExtensions?: readonly string[]\n}\n\n/** One entry in the shared type-ahead queue. */\nexport interface QueuedItem {\n id: string\n text: string\n /**\n * What rides with the text, WITHOUT the bytes — enough for a queue row to\n * show \"+2 attachments\" chips the way the echoed prompt will. The payload\n * itself stays host-side until the entry is sent.\n */\n attachments?: QueuedAttachment[]\n}\n\nexport interface QueuedAttachment {\n mediaType: string\n name?: string\n}\n\n/**\n * Live per-session presence — which sessions have attached windows and\n * what state they're in. Drives the session list's \"active\" scope and\n * per-row status icons. A session appears here when it has ≥1 attached\n * window, a run in flight, or pending HITL state.\n */\nexport interface SessionPresence {\n sessionId: string\n /** Number of windows currently attached (watching) this session. */\n windows: number\n /** A run is executing right now. */\n running: boolean\n /** Pending approval or interaction — awaiting user input somewhere. */\n attention: boolean\n}\n\n/** Pending interaction row as returned by `interactions.list`. */\nexport interface PendingInteraction {\n sessionId: string\n request: InteractionRequest\n live: boolean\n}\n\n/**\n * The single push channel. Chat stream signals mirror the agent loop hooks;\n * interaction/approval signals carry the HITL queues so every window renders\n * the same pending state; `sessions:changed` keeps session lists in sync\n * across windows.\n */\n/** Payload of the `stream:progress` signal — see the variant's doc. */\nexport interface StreamProgressInfo {\n turnId: string\n outputTokens: number\n estimated: boolean\n elapsedMs: number\n estCostUsd?: number\n}\n\nexport type HostSignal\n = | { kind: 'run:start', sessionId: string, runId: string }\n | { kind: 'run:end', sessionId: string, runId: string, ok: boolean, error?: string }\n /**\n * A turn finished. `usage` carries the loop-reported token/cost figures\n * that drive footer gauges (context size = prompt-side tokens of the\n * LAST parent turn; cost accrues across parent + child turns).\n */\n | { kind: 'turn:end', sessionId: string, turnId: string, childId?: string, depth?: number, usage?: TurnUsage }\n /**\n * `echo: true` marks host-generated SEND/STEER echoes of the user's own\n * message. Clients that already render the prompt locally (the GUI's\n * enriched append) drop echo-tagged signals; command-module\n * user-prompts are never echoes and always render.\n */\n | { kind: 'event', sessionId: string, event: StreamEvent, echo?: boolean }\n | { kind: 'compacting:start', sessionId: string }\n /**\n * Live token estimate for an in-flight compaction summary. Throttled by the\n * loop, so this is a low-rate progress tick rather than a stream.\n *\n * Without it a daemon-mode window shows a static \"Compacting…\" with no\n * indication of progress — a compaction of a large transcript can run for\n * minutes, which is indistinguishable from a hang.\n */\n | { kind: 'compacting:progress', sessionId: string, estOutputTokens: number }\n /**\n * Live counters for the parent turn currently streaming — the loop's\n * `stream:progress` (throttled ~4/s, one final tick before `stream:end`).\n * `outputTokens` is provider-counted unless `estimated`; `estCostUsd` is\n * the running catalog-rate estimate for THIS turn (absent for unpriced\n * models and flat-rate subscription traffic). Clients add it to the settled\n * session figure while streaming and drop it when the matching `turn:end`\n * brings the real `usage.cost`.\n */\n | ({ kind: 'stream:progress', sessionId: string } & StreamProgressInfo)\n /**\n * Prompt turns that just persisted, reduced to the fields needed to bind a\n * live `user-prompt` event to its turn id (see {@link PromptTurnRef}).\n *\n * Deliberately NOT the full `SessionTurn`: those carry every content block,\n * including base64 image/document payloads, which would be megabytes per\n * prompt to ship for the sake of an id. Without this a prompt sent from a\n * daemon-attached window is unselectable — no hover, edit, or delete — until\n * the run-end transcript replace.\n */\n | { kind: 'prompt-turns', sessionId: string, refs: readonly PromptTurnRef[] }\n /**\n * `effectiveTokens` (success only) is the loop's post-compact context\n * estimate — windows drop their footer ctx gauge to it immediately\n * instead of waiting for the next turn's usage.\n */\n | { kind: 'compacting:end', sessionId: string, ok: boolean, effectiveTokens?: number }\n | {\n kind: 'model:fallback'\n sessionId: string\n from: { provider: string, model: string }\n to: { provider: string, model: string }\n }\n | { kind: 'work:snapshot', sessionId: string, items: RunningWorkItem[] }\n | { kind: 'todos:snapshot', sessionId: string, snapshot: TodosSnapshot }\n /**\n * The session recorded an artifact (PR, branch, commit, file, extension\n * kind — see `session/artifacts.ts`). Carries the stored row so a window\n * can badge / list without a round-trip; `sessions.artifacts` is the\n * authoritative query. Additive — older clients ignore unknown kinds.\n */\n | { kind: 'artifact:recorded', sessionId: string, artifact: SessionArtifact }\n /**\n * An artifact was removed or refreshed through the host (`artifacts.delete`\n * / `artifacts.upsert`) — windows re-query. Carries the row for `upsert`.\n */\n | { kind: 'artifact:changed', sessionId: string, op: 'delete' | 'upsert', artifactId: string, artifact?: SessionArtifact }\n | { kind: 'interaction:request', sessionId: string, request: InteractionRequest, live: boolean }\n | { kind: 'interaction:resolved', sessionId: string, requestId: string }\n | { kind: 'approval:request', request: WireApprovalRequest }\n /**\n * An approval left the pending queue (answered or denied in bulk) — every\n * OTHER window clears its surface. The window that resolved it already\n * knows; the signal makes the queue converge everywhere else.\n */\n | { kind: 'approval:resolved', sessionId: string, requestId: string, decision: ApprovalDecision }\n | { kind: 'sessions:changed', op: 'create' | 'update' | 'delete', sessionId: string }\n /**\n * Settings were written through the host (`settings.set`) — every window\n * applies the new state so settings UIs converge without a restart.\n * Windows that persist settings themselves must treat this as\n * authoritative for the delivered snapshot.\n */\n | { kind: 'settings:changed', settings: HostSettings }\n /**\n * Another window edited the session's shared prompt draft. The daemon\n * EXCLUDES the originating peer from this fan-out, so applying it can\n * never fight the local cursor — clients apply unconditionally\n * (last-writer-wins whole-text replacement). Empty `text` clears the\n * draft (broadcast when any window submits the prompt).\n */\n | { kind: 'draft:changed', sessionId: string, text: string, shell?: boolean }\n /**\n * The shared draft's composing ATTACHMENTS changed (paste/drop/remove in\n * any window). Payloads ride along so a remote window can submit the\n * shared draft attachments-and-all. Same origin-exclusion as text.\n */\n | { kind: 'draft:attachments', sessionId: string, attachments: PromptAttachment[] }\n /**\n * The session's shared transcript-search state changed (mode entered /\n * exited, query edited) in another window. Same origin-exclusion and\n * last-writer-wins contract as the draft — every window on the session\n * shows the same filtered transcript.\n */\n | { kind: 'search:changed', sessionId: string, active: boolean, query: string }\n /**\n * The daemon resolved another window's inline-autocomplete query\n * (`suggest.resolve`). Excludes the requesting peer (it already has\n * the result from its RPC reply). Receivers mirror the ghost ONLY\n * when their live buffer still equals `basis` — the standard\n * staleness guard; otherwise discard.\n */\n | { kind: 'suggest:changed', sessionId: string, seq: number, basis: string, caret: number, suggestion: string | null, candidates?: string[] }\n /**\n * The daemon-side agent's active-skill set changed (`skills_use` /\n * run-end deactivation). Windows mirror it into their skill chips.\n */\n | { kind: 'skills:snapshot', sessionId: string, active: string[] }\n /**\n * A daemon-side MCP server needs (re-)authentication. Windows surface\n * the badge; the interactive login flow itself runs in whichever window\n * the user triggers it from (tokens land in the shared credential\n * store; the daemon reconnects on the next agent rebuild).\n */\n | { kind: 'mcp:auth', sessionId: string, name: string, reason: string }\n /**\n * Provider retry in progress (rate-limit / overload / silent stall) —\n * drives the footer's \"retrying n/m\" indicator. `source` is `'parent'`\n * or the retrying child's id; the indicator clears when that source\n * streams again or the run ends.\n */\n | { kind: 'stream:retry', sessionId: string, attempt: number, max: number, source: string }\n /**\n * A retried stream reached a terminal state for `source` (`'parent'` or a\n * child id) — clear that source's retry indicator.\n *\n * Distinct kind rather than a nullable `attempt` so the two states stay\n * unambiguous on the wire. Consumers also clear at run end as a backstop;\n * this exists for the mid-run case, where a subagent's stream fails while\n * the parent run continues and the indicator would otherwise stick.\n */\n | { kind: 'stream:retry:cleared', sessionId: string, source: string }\n /**\n * Which `SYSTEM.md` override (if any) the host folded into this session's\n * system prompt, emitted at agent-build time.\n *\n * The prompt is composed where the agent is built — in the daemon, not in\n * the window — so a window cannot derive this by probing its own disk: it\n * may have a different cwd, and `userInstructionsScope` is the daemon's.\n * Windows need it to tell \"no override on disk\" apart from \"override in\n * force\", which is the difference between doctrine the user wrote and\n * doctrine they think they replaced. `shadowed` carries precedence losers\n * so the Instructions surface can mark them inactive.\n */\n | { kind: 'system-md', sessionId: string, file: SystemMdFile | null, shadowed: readonly SystemMdFile[] }\n /**\n * A cache-prefix break was detected for this session's last request — the\n * footer chip names the culprit dimension and the re-billed prefix size.\n * Detection is per-agent closure state inside the loop, so in daemon mode\n * only the daemon can observe it. Cost pricing stays window-side (it needs\n * only the provider key and the token count).\n */\n | { kind: 'cache:break', sessionId: string, info: CacheBreakInfo }\n | { kind: 'provider:diagnostic', sessionId: string, diagnostic: ProviderDiagnostic }\n\n /**\n * Presence snapshot — recomputed and fanned out whenever a window\n * attaches/detaches or any tracked session's run/HITL state changes.\n * Full snapshot (the set is small: only live sessions appear).\n */\n | { kind: 'presence:changed', sessions: SessionPresence[] }\n /**\n * Daemon-side extension HUD snapshot (status lines + banners). Extension\n * hooks run in the daemon in daemon mode, so their `ctx.tui.setStatus` /\n * `setBanner` writes happen there — windows overlay this state under\n * their local HUD (local entries win per extension name).\n */\n | { kind: 'hud:state', statuses: Record<string, { text: string, loader: boolean }>, banners: Record<string, string[]> }\n /** Daemon-side extension `ctx.tui.notify` — a one-shot transcript line. */\n | { kind: 'hud:notify', extension: string, text: string, tone: 'info' | 'warn' | 'error' }\n /**\n * The session's type-ahead queue changed (message queued while a run is\n * in flight, drained by the host on run end, pushed early, or dropped).\n * Broadcast to EVERY peer including the origin — the queue box is shared\n * state and all windows render the same list.\n */\n | { kind: 'queue:changed', sessionId: string, items: QueuedItem[] }\n /**\n * Extension bus message (`ctx.bus.emit`, `'ipc'` permission). PRIVATE\n * per `extensionId`: receivers re-inject into the SAME extension's local\n * subscribers only and drop ids they haven't registered. `extensionId`\n * is stamped by the emitting host from the registration identity —\n * never caller-supplied. `payload` is capped at 64 KiB serialized\n * (enforced at emit and again at the daemon boundary); `(nonce, seq)`\n * feeds the receivers' per-extension dedupe window (self-echo through a\n * second connection of the same process, duplicate deliveries). `nonce`\n * is the emitter's per-process random id (additive — absent from older\n * peers, which dedupe on seq alone).\n */\n | { kind: 'extension:signal', extensionId: string, sessionId?: string, payload: JsonValue, seq: number, nonce?: string }\n","/**\n * Transport-agnostic chat host — owns live `Agent` instances and forwards\n * agent-loop hooks as `HostSignal`s to whatever transport the embedding\n * process wires in (Electron `webContents.send`, daemon socket peers, an\n * in-process callback for tests).\n *\n * Extracted from the GUI main-process engine; behavior is intentionally\n * identical. All module-level state became per-host instance state so one\n * process can host independent instances (tests, future multi-profile).\n *\n * Dependency invariant: `src/chat` (this module included) has NO dependency\n * on `src/ipc`; the direction is strictly `src/ipc` → `src/chat`. The host\n * is the chat engine — deleting `src/ipc` (plus the daemon CLI and the\n * clients' daemon branches) removes the socket transport but leaves the\n * engine intact. Wire shapes the host emits (`HostSignal` & co.) live in\n * `./signals`, re-exported by `src/ipc/protocol.ts` for `zidane/ipc`\n * consumers.\n */\nimport type { Agent, PromptPart, Session, SpawnTool, ToolContext } from '../../index'\nimport type { Provider } from '../../providers'\nimport type { SessionTurn, ThinkingLevel } from '../../types'\nimport type { CommandOutputBlock } from '../commands'\nimport type {\n ActiveTodosState,\n AgentRegistry,\n ContextBreakdown,\n ContextSegment,\n EditPayload,\n InteractionRequest,\n ModelChainEntry,\n ModelFallbackTarget,\n ProviderDescriptor,\n ProviderKey,\n SpawnStartRecord,\n StreamEvent,\n SystemMdFile,\n TodoItem,\n} from '../index'\nimport type { DiscoveredPlugin } from '../plugins-discovery'\nimport type { CreateSessionInStoreParams } from '../session-factory'\nimport type { ApprovalHub } from './approvals'\nimport type { CompactionEntry } from './compaction'\nimport type { ChatHostEnvironment } from './environment'\nimport type { InteractionHub } from './interactions'\nimport type { Orchestration } from './orchestration'\nimport type { ApprovalOriginator, CommandArgSuggestionSummary, CommandSummary, HostSignal, PromptAttachment, QueuedItem, RunningWorkItem, SendOptions } from './signals'\nimport { randomUUID } from 'node:crypto'\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createDebugLog } from '../../debug-log'\nimport { filterRegistryPlugins } from '../../extensions'\nimport {\n artifactsPreset,\n composeExtensionPreset,\n composePresets,\n connectMcpServers,\n createAgent,\n createProcessContext,\n EXTENSION_EDIT_METADATA_KEY,\n extensionDirForCwd,\n installCacheBreakLogger,\n isSpawnTool,\n McpOAuthProvider,\n readSkillEditMarker,\n resolvePersistDir,\n resolveTasksDir,\n SKILL_EDIT_METADATA_KEY,\n withZidaneExtensionSkill,\n withZidaneSkillEditSkill,\n ZIDANE_EXTENSION_SKILL_NAME,\n ZIDANE_SKILL_EDIT_SKILL_NAME,\n} from '../../index'\nimport { resolveMcpWarningsDir } from '../../loop-persistence'\nimport { attachMcpWarnLog } from '../../mcp/warn-log'\nimport { createSession, loadSession } from '../../session/index'\nimport { splitSystemPrompt } from '../../system-prompt'\nimport { THINKING_LEVELS } from '../../types'\nimport { commandBlocksToContent, commandBlockToEvent } from '../commands'\nimport { buildBuildSystem, buildEditOutcomesAnnotation, buildMcpServers, buildPlanSystem, buildRunModel, buildSkillsConfig, buildSpawnEndEvent, buildSpawnStartEvent, BUILTIN_PROVIDERS, chatAutoCompactBehavior, chatReferenceTokensForPrompt, compactSummaryEvent, createChatTurnsReadTool, createInteractionTools, DEFAULT_AGENT_ID, defaultSkillScanPaths, discoverAgentsMd, discoverProjectMcps, discoverSystemMd, effectiveContextWindow, enabledModelOptions, extractEditPayload, formatTaskSummary, getContextWindow, isOnSafelist, isTodoTool, mergeApprovalAndBodyOutcomes, modelChainMetadata, OUTPUT_RESERVE_TOKENS, parseEditOutcomesFromResult, previewEditPayload, previewLine, renderSystemMdSeed, resolveApprovalForPayload, resolveSessionCwd, restoreChainFallbacks, restoreModelOptions, rewriteMultiEditHeader, sanitizeTodoItems, selectActiveTodos, setTodosForRun, stripEditOutcomesAnnotation, subscribeMcpToolsCache, suggestSafelistEntries, toolCallPreview, toolResultText, userScopeOverridesLoad, userSystemMdPath } from '../index'\nimport { isMainSession, MAIN_PLAYBOOK_FRAGMENT, orchestratedBy, renderProjectsBriefing, WORKER_REPORT_FRAGMENT } from '../orchestration'\nimport { buildPluginContributions, discoverProjectPlugins } from '../plugins-discovery'\nimport { anthropicDescriptor } from '../providers'\nimport { runEndNotice } from '../run-end-notice'\nimport { formatRunReceiptLine, hasRunReceipt, runReceiptFromRun } from '../run-receipt'\nimport { createSessionInStore as createSessionInSharedStore } from '../session-factory'\nimport { serverToolResultSummary } from '../store'\nimport { promptTurnRefs } from '../turn-selection'\nimport { createApprovalHub } from './approvals'\nimport { createCommandModule, runCapsFromSettings } from './commands'\nimport { runCompaction } from './compaction'\nimport { EditOutcomeLedger } from './edit-outcome-ledger'\nimport { extractEditPayloadForGate, readFileSafe, resolveCompactionSummarizer, resolveCwdPath, serializePreview, startRunHeartbeat } from './helpers'\nimport { sessionHostRuntime } from './host-runtime'\nimport { createInteractionHub } from './interactions'\nimport { createOrchestration, createSessionsTool, SESSIONS_TOOL } from './orchestration'\nimport { installProviderDiagnosticHook } from './provider-diagnostic'\nimport { toTodosSnapshot } from './signals'\n\n/**\n * `ZIDANE_DEBUG`-gated sink for host diagnostics (cache-break lines, plugin\n * discovery failures). Module-scoped: the sink is stateless and the scope\n * label is fixed.\n */\nconst hostDebug = createDebugLog('chat-host')\n\n/**\n * Build-time seam for command-driven spawns (`ctx.spawnAgents`): the exact\n * inputs the agent was built with, so a hand-built ToolContext drives the\n * spawn tool with the same tools / providers / skills / behavior as a\n * model-initiated spawn. Mirrors the TUI's `commandSpawnSeamRef` contract.\n */\nexport interface CommandSpawnSeam {\n spawnTool: SpawnTool\n provider: ToolContext['provider']\n providers: ToolContext['providers']\n system?: string\n tools: ToolContext['tools']\n skills: ToolContext['skills']\n behavior: ToolContext['behavior']\n name?: string\n}\n\n/**\n * Instructions-tab payload. Mirrors the TUI's `getContextSegments` contract so\n * both settings surfaces render identical rows and identical explanations for\n * why a `SYSTEM.md` override can or cannot apply.\n */\nexport interface ContextSegmentsReport {\n segments: readonly ContextSegment[]\n /** Always true here: the host only answers for a live (or buildable) agent. */\n live: boolean\n /** False for host-supplied profiles, which carry their own system prompt. */\n canOverrideSystem: boolean\n /** `'profile'` = picked profile supplies its own prompt; `'scope'` = `userInstructionsScope` excludes user files. */\n overrideBlockedBy?: 'profile' | 'scope'\n /** A `SYSTEM.md` on disk that lost precedence — surfaced so edits to it don't look ignored for no reason. */\n shadowedSystemPath?: string\n}\n\nexport interface ActiveRun extends CompactionEntry {\n controller: AbortController\n\n /**\n * Validated per-session model override forwarded to `agent.run({ model })`.\n * Undefined → let the provider/agent pick its default. Also undefined when\n * the persisted provider was removed, so a dangling modelId never overrides\n * the fallback provider's default model.\n */\n modelOverride?: string\n\n /** In-flight manual compaction Promise, when one is running. */\n compacting?: Promise<void>\n\n /** Per-callId stash for the per-edit approval pipeline. */\n ledger: EditOutcomeLedger\n\n /** Current prompt's explicit `#chat` tokens, read by `read_chat_turns`. */\n chatReferenceTokens: { current: readonly string[] }\n\n /** Per-session MCP warning log — closed when the agent is torn down. */\n warnLog?: { close: () => Promise<void> }\n\n /**\n * Restored model-fallback chain (entries 1+ of `metadata.modelChain`,\n * head-matched against the resolved primary; unregistered providers\n * dropped). Empty = no chain.\n */\n modelFallbacks: ModelChainEntry[]\n /** providerKey → constructed Provider.name, for run-target resolution. */\n providerNameByKey: Map<ProviderKey, string>\n\n /**\n * The `SYSTEM.md` override this agent's prompt was composed with (`file:\n * null` = none), plus precedence losers.\n *\n * Retained rather than only broadcast: the `system-md` signal fires once per\n * agent BUILD, and the daemon caches agents — so a window attaching to a\n * session whose agent is already warm (the normal multi-window case) would\n * never see it, and the condition being reported is that the built-in safety\n * and git doctrine has been replaced. Read back by `sessionSystemMd` for the\n * attach-time backfill, alongside drafts/approvals/queue.\n */\n systemMd: { file: SystemMdFile | null, shadowed: readonly SystemMdFile[] }\n\n /**\n * When this host's LAST run on the session settled (wall clock).\n * Exempts the cross-process run guard from refusing the host's OWN\n * settle-lagged `'running'` marker (heartbeat/persistence writes can\n * land after the loop flips `isRunning`).\n */\n lastRunEndedAt?: number\n\n /**\n * Steer/follow-up texts ACCEPTED but not yet injected into the loop.\n * The `steer:inject` hook matches against this list to broadcast the\n * `user-prompt` at true delivery time — canonical transcript order —\n * while system injections (wake nudges, context warnings) that ride\n * the same hook never match and never render as user messages.\n */\n pendingInjections: string[]\n\n /**\n * Rendered AGENTS.md/CLAUDE.md rules block + source files baked into the\n * agent's system prompt — threaded to `agent.run({ contextSections })` so\n * the context breakdown can attribute a \"Rules\" bucket.\n */\n rules?: { rulesBlock?: string, rulesFiles?: { path: string, source: string, contents: string }[] }\n\n /** Fully composed base system prompt, including extension fragments. */\n system?: string\n\n /** Parent-level cancellable tool calls and background tasks. */\n work: Map<string, RunningWorkItem>\n\n /** Last host interaction with this agent, used for idle LRU disposal. */\n lastUsedAt: number\n\n /**\n * Skills to host-activate before every run. A run-end deactivate-all pass\n * clears activation, so these are re-asserted on each `sendPrompt` to stay\n * active from turn one.\n */\n autoActivateSkills?: readonly string[]\n\n /** Spawn seam captured at agent build time — powers `ctx.spawnAgents` for `/` commands. */\n commandSeam?: CommandSpawnSeam\n\n /** Abort controller of the in-flight `/` command, when one is running. */\n commandAbort?: AbortController\n}\n\nconst MAX_IDLE_ACTIVE_AGENTS = 8\n\n/**\n * Cap on how long `sendPrompt` waits for an in-flight manual compaction\n * before proceeding. Sized above `compactConversation`'s 5-minute\n * inactivity watchdog so a healthy long-running summary finishes behind\n * the barrier instead of racing the new prompt; a genuinely hung stream\n * self-terminates well before this cap.\n */\nconst COMPACTION_BARRIER_TIMEOUT_MS = 10 * 60 * 1000\n\n/**\n * How long a persisted `status: 'running'` marker from another process is\n * trusted as live. Turn persistence keeps `updatedAt` moving during real\n * runs; a marker older than this is treated as the residue of a crashed\n * process and does not block new runs.\n */\nexport const STALE_RUN_MARKER_MS = 5 * 60 * 1000\n\n/** Type guard for canonical `ThinkingLevel` strings read off `metadata.effort`. */\nfunction isThinkingLevel(value: string): value is ThinkingLevel {\n return (THINKING_LEVELS as readonly string[]).includes(value)\n}\n\nfunction dedupByName<T extends { name: string }>(entries: readonly T[]): T[] {\n const seen = new Set<string>()\n const out: T[] = []\n for (const entry of entries) {\n if (seen.has(entry.name))\n continue\n seen.add(entry.name)\n out.push(entry)\n }\n return out\n}\n\nfunction appendSystemFragments(system: string | undefined, fragments: readonly string[]): string | undefined {\n if (fragments.length === 0)\n return system\n const suffix = fragments.join('\\n\\n')\n return system ? `${system}\\n\\n${suffix}` : suffix\n}\n\n/**\n * Resolve the canonical agent id stored on the session document against the\n * registry. Falls back to `DEFAULT_AGENT_ID` for pre-registry sessions or\n * ids that are no longer registered.\n */\nfunction agentIdOf(stored: string | undefined, agents: AgentRegistry): string {\n if (stored && stored in agents)\n return stored\n return DEFAULT_AGENT_ID\n}\n\nfunction providerDescriptorFor(\n providers: Readonly<Record<string, ProviderDescriptor>>,\n providerKey: ProviderKey = 'anthropic',\n): ProviderDescriptor {\n return providers[providerKey] ?? providers.anthropic ?? BUILTIN_PROVIDERS.anthropic\n}\n\n/** Read the per-session provider preference off `metadata`. */\nfunction readSessionProvider(\n metadata: Record<string, unknown> | undefined,\n providers: Readonly<Record<string, ProviderDescriptor>>,\n): ProviderKey {\n const raw = metadata?.providerKey\n if (typeof raw === 'string' && raw in providers)\n return raw as ProviderKey\n return 'anthropic'\n}\n\n/**\n * True when the session persisted a `providerKey` that's no longer in the\n * registry — the persisted `modelId` belonged to the now-absent provider,\n * so it must not ride along as a dangling id onto the fallback provider.\n */\nfunction persistedProviderMissing(\n metadata: Record<string, unknown> | undefined,\n providers: Readonly<Record<string, ProviderDescriptor>>,\n): boolean {\n const raw = metadata?.providerKey\n return typeof raw === 'string' && raw.length > 0 && !(raw in providers)\n}\n\nfunction readSessionModel(\n metadata: Record<string, unknown> | undefined,\n providers: Readonly<Record<string, ProviderDescriptor>>,\n): string | undefined {\n if (persistedProviderMissing(metadata, providers))\n return undefined\n const raw = metadata?.modelId\n return typeof raw === 'string' && raw.length > 0 ? raw : undefined\n}\n\n/**\n * Coerce a tool result (`string | ToolResultContent[]`) to plain text for\n * clients.\n */\nfunction resultToText(result: unknown): string {\n if (typeof result === 'string')\n return result\n try {\n return toolResultText(result as Parameters<typeof toolResultText>[0])\n }\n catch {\n return JSON.stringify(result)\n }\n}\n\n/** Everything a model pick carries — see `ChatHost.setSessionModel`. */\nexport interface SessionModelPick {\n providerKey: ProviderKey\n modelId: string\n /** Ordered fallback tail (primary excluded). Empty/omitted clears the chain. */\n fallbacks?: readonly ModelFallbackTarget[]\n /** Reasoning effort for the new model; omitted clears the persisted value. */\n effort?: ThinkingLevel\n /** Enabled model options for the new model; omitted clears them. */\n modelOptions?: Record<string, boolean>\n}\n\nexport interface ChatHost {\n /** HITL hubs — exposed so transports can route respond/list/rehydrate calls. */\n interactions: InteractionHub\n approvals: ApprovalHub\n\n sendPrompt: (opts: SendOptions) => Promise<{ runId: string }>\n interrupt: (sessionId: string) => void\n steer: (sessionId: string, message: string) => void\n followUp: (sessionId: string, message: string) => void\n /** Steer when a run is active, start a fresh run otherwise. */\n pushQueued: (sessionId: string, message: string, activatedSkills?: string[]) => Promise<{ delivered: 'steer' | 'send' | 'command' | 'queued' }>\n /** Wire-safe `/` command catalog for a client's completion popover. */\n listCommands: (cwd: string) => Promise<CommandSummary[]>\n /** Argument suggestions for `/name <prefix>` — best-effort, empty on failure. */\n completeCommandArgs: (cwd: string, name: string, argsPrefix: string) => Promise<CommandArgSuggestionSummary[]>\n /**\n * Shared type-ahead queue — host-owned so every window renders and\n * manipulates the same list (`queue:changed` fan-out). `queueAdd`\n * queues while a run is in flight and sends immediately otherwise;\n * the host drains FIFO at run end.\n */\n queueAdd: (\n sessionId: string,\n text: string,\n activatedSkills?: string[],\n attachments?: PromptAttachment[],\n /** See `SendOptions.asCommand`. */\n asCommand?: string,\n ) => Promise<{ delivered: 'queued' | 'sent' }>\n queueRemove: (sessionId: string, id: string) => boolean\n /** Edit a queued entry's text in place (keeps its position). `false` when the id is gone. */\n queueUpdate: (sessionId: string, id: string, text: string) => boolean\n queueList: (sessionId: string) => QueuedItem[]\n\n /**\n * The `SYSTEM.md` override composing this session's prompt, for windows\n * attaching to an ALREADY-WARM agent (the `system-md` signal only fires at\n * agent build). `null` file = none applied; `null` result = no live agent,\n * in which case the next build will broadcast.\n */\n sessionSystemMd: (sessionId: string) => { file: SystemMdFile | null, shadowed: readonly SystemMdFile[] } | null\n queuePushNow: (sessionId: string, id: string) => Promise<{ delivered: 'steer' | 'send' | 'missing' }>\n isSessionRunning: (sessionId: string) => boolean\n /** Sessions with a live in-memory agent, with their run state (presence). */\n listLiveSessions: () => Array<{ sessionId: string, running: boolean }>\n /**\n * The live in-memory turns for a session with an active agent — fresh\n * even mid-run (the store lags at turn boundaries). `null` when no\n * agent is live. Read-only by contract.\n */\n getLiveTurns: (sessionId: string) => readonly SessionTurn[] | null\n /**\n * The live in-memory `Session` for a session with an active agent, or\n * `null`. For IN-PROCESS embedders only — the GUI's `!` shell runner\n * appends turns through the live `Session` so `Session.save()` /\n * compaction's `replaceTurns` never erase a raw store append the\n * in-memory snapshot didn't see. Not wire-exposable.\n */\n getLiveSession: (sessionId: string) => Session | null\n /** True when ANY session has a live run, in-flight compaction, or tracked work. */\n hasActiveWork: () => boolean\n listRunningWork: (sessionId: string) => RunningWorkItem[]\n cancelRunningWork: (sessionId: string, id: string | 'all', reason?: string) => Promise<boolean>\n getTodosSnapshot: (sessionId: string) => ActiveTodosState | null\n /**\n * Apply a user todo edit through the live agent's `Session` — the one\n * `todowrite` reads. `null` when no agent is live (caller falls back to a\n * store-level write) or when no run owns a todo slot yet.\n */\n setTodos: (sessionId: string, items: readonly TodoItem[]) => Promise<ActiveTodosState | null>\n /**\n * Create a session in the shared store and return its id. The one factory\n * shared by the daemon, the embedded GUI host, and orchestration.\n */\n createSession: (params: {\n cwd: string\n title?: string\n agentId?: string\n providerKey?: string\n modelId?: string\n fallbacks?: readonly ModelFallbackTarget[]\n extensionEdit?: boolean\n }) => Promise<string>\n /** Main-session orchestration — see `./orchestration.ts`. */\n orchestration: Orchestration\n /**\n * Apply a metadata-only patch through the live in-memory `Session` when\n * one is hosting the session (safe during a run). Returns `false` when no\n * live session exists — the caller should fall back to a store-level\n * write. See the implementation for the data-loss rationale.\n */\n patchLiveSessionMeta: (sessionId: string, patch: Record<string, unknown>) => Promise<boolean>\n /**\n * Metadata patch that is ALWAYS safe: live-session path when an agent\n * hosts the session (`patchLiveSessionMeta`), store-level\n * `load → mutate → save` otherwise. Every surface (daemon RPC, embedded\n * GUI host) must write agent-affecting metadata through this — a direct\n * store write under a live session is reverted by that session's next\n * full-document save. `undefined` values DELETE the key.\n */\n patchSessionMeta: (sessionId: string, patch: Record<string, unknown>, opts?: { bumpUpdatedAt?: boolean }) => Promise<void>\n /**\n * THE model-swap contract, shared by every surface: persists\n * `providerKey` / `modelId`, replaces `modelChain` (cleared when\n * `fallbacks` is empty/omitted), resets `effort` and `modelOptions` to\n * the supplied values (cleared when omitted — a knob valid on the old\n * model must not leak onto the new one), then retires the live agent so\n * the next run rebuilds on the new model (run-end deferred mid-run).\n */\n setSessionModel: (sessionId: string, pick: SessionModelPick) => Promise<void>\n /**\n * Live-session-aware agent re-label: patches the LIVE document when the\n * host holds one (saving the live doc is safe — it carries the run's\n * appended turns; a stale-copy save would clobber them and a later live\n * save would silently revert the label) and persists it. Returns false\n * when no live session exists — callers fall back to a store patch,\n * which is safe because idle sessions have no concurrent writer.\n */\n setLiveSessionAgentId: (sessionId: string, agentId: string) => Promise<boolean>\n /**\n * Retire the live agent so the next run rebuilds on freshly-patched\n * metadata (model / agent swap). Deferred to run end while a run is in\n * flight, so a swap never aborts a run mid-stream for other windows.\n */\n disposeAgentForRebuild: (sessionId: string) => void\n getContextBreakdown: (sessionId: string) => Promise<ContextBreakdown | null>\n /**\n * Every chunk of text in the session's system prompt, with provenance — the\n * Instructions tab's data. Sibling of `getContextBreakdown` and built from\n * the same `ActiveRun` inputs so the two views describe the same assembly.\n * `null` when no agent can be built for the session.\n */\n getContextSegments: (sessionId: string) => Promise<ContextSegmentsReport | null>\n /**\n * Seed a user-scope `SYSTEM.md` from the doctrine currently in effect for\n * the session, and return its path for the caller to open in an editor.\n * Refuses (throws) when an override could never apply — a host-supplied\n * profile, or a `userInstructionsScope` that excludes user files — rather\n * than writing a file that would be silently ignored. No-op when the file\n * already exists (the path is still returned).\n */\n createSystemOverride: (sessionId: string) => Promise<{ path: string, created: boolean }>\n compactSessionNow: (sessionId: string) => Promise<void>\n /** Tear down one session's live agent + pending HITL state. */\n disposeSession: (sessionId: string) => void\n /** Drain every session — call before process exit or config invalidation. */\n shutdown: () => void\n /** Permanently refuse dispatch, abort work, and await setup, runs, and settlement. */\n drain: () => Promise<void>\n}\n\nexport function createChatHost(env: ChatHostEnvironment): ChatHost {\n const debugStream = env.debugStream ?? process.env.ZIDANE_IPC_DEBUG_STREAM === '1'\n\n // Late-bound: orchestration needs `active`, declared after `broadcast`.\n let orchestrationRef: Orchestration | undefined\n const dispatchSignals = new Map<string, { runId: string, end?: Extract<HostSignal, { kind: 'run:end' }> }>()\n const broadcast = (signal: HostSignal): void => {\n if (signal.kind === 'run:start' || signal.kind === 'run:end') {\n const dispatch = dispatchSignals.get(signal.sessionId)\n if (dispatch) {\n if (signal.kind === 'run:end') {\n dispatch.end = signal\n return\n }\n // Preserve intermediate command pairs; hold the final end until idle.\n const previous = dispatch.end\n dispatch.end = undefined\n if (previous) {\n dispatchSignals.delete(signal.sessionId)\n try {\n broadcast(previous)\n }\n finally {\n dispatchSignals.set(signal.sessionId, dispatch)\n }\n }\n dispatch.runId = signal.runId\n }\n }\n if (debugStream) {\n const preview\n = signal.kind === 'event' && signal.event.kind === 'markdown'\n ? `markdown:\"${signal.event.text.slice(0, 30)}${signal.event.text.length > 30 ? '…' : ''}\"`\n : signal.kind === 'event'\n ? `event:${signal.event.kind}${signal.event.kind === 'tool' || signal.event.kind === 'tool-result' ? `(${signal.event.tool})` : ''}`\n : signal.kind\n console.log(`[ipc-host] → ${preview}`)\n }\n env.broadcast(signal)\n // Orchestration observes every signal through this one seam — how it\n // learns that a worker finished AND that a main went idle, with no hook\n // registered on either agent. Late-bound: it needs `active`, declared below.\n orchestrationRef?.onSignal(signal)\n }\n\n const interactions = createInteractionHub({\n broadcast,\n store: env.store,\n readSettings: () => env.readSettings(),\n // Rejecting a plan aborts the run once the rejection has been handed\n // to the model — mirrors the TUI's `onInteractionResolve`.\n onPlanRejected: sessionId => interruptSession(sessionId),\n })\n const approvals = createApprovalHub({\n broadcast,\n userDataDir: env.userDataDir,\n ...(env.approvalsPersistence ? { persistence: env.approvalsPersistence } : {}),\n })\n\n const active = new Map<string, ActiveRun>()\n const suppressedWake = new Set<string>()\n\n /** The shared factory (`chat/session-factory.ts`), bound to this host's store. */\n const createSessionInStore = (params: CreateSessionInStoreParams): Promise<string> =>\n createSessionInSharedStore(env.store, params)\n\n function interruptSession(sessionId: string): void {\n suppressedWake.add(sessionId)\n const entry = active.get(sessionId)\n if (!entry)\n return\n entry.commandAbort?.abort()\n entry.controller.abort()\n entry.agent.abort()\n }\n\n function steerSession(sessionId: string, message: string): void {\n const entry = active.get(sessionId)\n // Broadcast happens at DELIVERY time via the `steer:inject` hook —\n // registering here only marks the text as a real user message.\n if (entry?.agent.steer(message))\n entry.pendingInjections.push(message)\n }\n\n const orchestration = createOrchestration({\n store: env.store,\n broadcast,\n readSettings: () => env.readSettings(),\n sendPrompt: opts => sendPrompt(opts),\n wake: wakeRun,\n steer: steerSession,\n interrupt: interruptSession,\n liveSession: sessionId => active.get(sessionId)?.agent.session ?? undefined,\n isRunning: isSessionBusy,\n createSession: createSessionInStore,\n })\n orchestrationRef = orchestration\n\n /*\n * `/` command execution — `ensureAgent` / `buildProvider` are hoisted\n * function declarations, so handing them to the factory here (before\n * their textual definition) is safe: the module only calls them at\n * command time.\n */\n const commands = createCommandModule({\n env: {\n ...env,\n runtimeConfig: async (cwd) => {\n const generation = assertDispatchOpen()\n const config = await env.runtimeConfig(cwd)\n assertDispatchOpen(generation)\n return config\n },\n },\n broadcast,\n ensureAgent,\n buildProvider,\n interactions,\n })\n\n /**\n * Sessions where the user explicitly interrupted — don't auto-start a\n * background-task wake run on their behalf until they send again\n * (mirrors the local TUI's esc-latch semantics).\n */\n\n /**\n * In-flight agent builds keyed by sessionId. `ensureAgent` awaits\n * extensively before inserting into `active` — without this map, two\n * concurrent calls (e.g. `sessions.contextBreakdown` racing `chat.send`)\n * would each build an agent and the loser would leak (hooks keep\n * broadcasting, MCP connections stay open).\n */\n const pendingBuilds = new Map<string, Promise<ActiveRun>>()\n\n /**\n * Bumped by `shutdown`. A build that started before the bump is discarded\n * on completion instead of re-inserting a stale agent built from\n * pre-invalidation config.\n */\n let teardownGeneration = 0\n const dispatches = new Map<string, Promise<void>>()\n const lifecycleWork = new Set<Promise<unknown>>()\n let closed = false\n let drainPromise: Promise<void> | undefined\n\n function trackWork<T>(work: Promise<T>): Promise<T> {\n lifecycleWork.add(work)\n void work.finally(() => lifecycleWork.delete(work)).catch(() => undefined)\n return work\n }\n\n function assertDispatchOpen(generation = teardownGeneration): number {\n if (closed || generation !== teardownGeneration)\n throw new Error('Chat host was shut down. Retry with an active host.')\n return generation\n }\n\n function isSessionBusy(sessionId: string): boolean {\n return dispatches.has(sessionId) || active.get(sessionId)?.agent.isRunning === true\n }\n\n /**\n * The per-run receipt (`run-summary`) for the run that just ended, read off\n * the session's latest TOP-LEVEL run record. Emitted BEFORE `run:end` so it\n * is the last transcript row of the run, not the first of the idle state.\n * Not persisted as a turn — replay synthesizes it from the same record in\n * `eventsFromTurns`, so live and reloaded transcripts match.\n */\n /**\n * `ctx.appendNote` from an extension tool/hook DURING a run: a durable,\n * display-only `command` turn — same block vocabulary, live events and\n * persisted form as command output, so it renders now and survives reload.\n * Live events and the turn share one pre-minted id (anchors depend on it).\n */\n function appendNoteTurn(run: ActiveRun, blocks: readonly CommandOutputBlock[]): void {\n const session = run.agent.session\n if (!session)\n return\n const turnId = randomUUID()\n for (const block of blocks)\n broadcast({ kind: 'event', sessionId: run.sessionId, event: { ...commandBlockToEvent(block), turnId } })\n void session\n .appendTurns([{ id: turnId, role: 'command', createdAt: Date.now(), content: commandBlocksToContent(blocks) }])\n .catch(() => undefined)\n }\n\n function broadcastRunReceipt(entry: { sessionId: string, agent: Agent }): void {\n const session = entry.agent.session\n if (!session)\n return\n const record = [...session.runs].reverse().find(r => r.parentRunId === undefined)\n if (!record)\n return\n const receipt = runReceiptFromRun(record)\n if (!hasRunReceipt(receipt))\n return\n // Tagged with the run's last turn so a client can dock the receipt onto\n // that turn's row (the GUI merges it into the turn's action strip).\n const lastTurnId = session.turns.at(-1)?.id\n broadcast({\n kind: 'event',\n sessionId: entry.sessionId,\n event: { kind: 'run-summary', text: formatRunReceiptLine(receipt), runSummary: receipt, ...(lastTurnId ? { turnId: lastTurnId } : {}) },\n })\n }\n\n function broadcastTodosSnapshot(entry: { sessionId: string, agent: Agent }): void {\n const session = entry.agent.session\n if (!session)\n return\n broadcast({\n kind: 'todos:snapshot',\n sessionId: entry.sessionId,\n snapshot: toTodosSnapshot(selectActiveTodos(session)),\n })\n }\n\n function visibleWorkItems(entry: { work: Map<string, RunningWorkItem> }): RunningWorkItem[] {\n return [...entry.work.values()].filter(item => item.childId === undefined)\n }\n\n function broadcastWorkSnapshot(entry: { sessionId: string, work: Map<string, RunningWorkItem> }): void {\n broadcast({ kind: 'work:snapshot', sessionId: entry.sessionId, items: visibleWorkItems(entry) })\n }\n\n /**\n * Single teardown sequence for a run — every dispose path routes through\n * here so run-scoped resources (in-flight `/` command abort, manual\n * compaction, live run controller, MCP warn log, edit ledger, work map,\n * the agent itself) can't be missed by one variant.\n * `broadcastWork: false` skips the empty work snapshot for runs no\n * client ever saw (built-but-never-activated).\n */\n function teardownRun(run: ActiveRun, opts?: { broadcastWork?: boolean }): void {\n run.commandAbort?.abort()\n run.compactionController?.abort()\n run.controller.abort()\n if (run.warnLog)\n trackWork(run.warnLog.close()).catch(() => undefined)\n run.ledger.clear()\n run.work.clear()\n if (opts?.broadcastWork !== false)\n broadcastWorkSnapshot(run)\n trackWork(run.agent.destroy()).catch(() => undefined)\n }\n\n /** Tear down a built-but-never-activated (or stale) run. */\n function discardRun(run: ActiveRun): void {\n teardownRun(run, { broadcastWork: false })\n }\n\n function disposeActiveEntry(sessionId: string, entry: ActiveRun): void {\n teardownRun(entry)\n active.delete(sessionId)\n }\n\n function pruneIdleAgents(): void {\n const idle = [...active.entries()]\n // An active `/` command (commandAbort set) counts as work even\n // between its sub-runs — pruning would abort it mid-flight.\n .filter(([sessionId, entry]) => !isSessionBusy(sessionId) && !entry.compacting && entry.commandAbort === undefined && entry.work.size === 0)\n .sort((a, b) => a[1].lastUsedAt - b[1].lastUsedAt)\n const overflow = active.size - MAX_IDLE_ACTIVE_AGENTS\n if (overflow <= 0)\n return\n for (const [sessionId, entry] of idle.slice(0, overflow)) {\n interactions.clearSession(sessionId)\n approvals.denyAll(sessionId)\n disposeActiveEntry(sessionId, entry)\n }\n }\n\n /**\n * Build the active provider from the canonical descriptor. Re-applies the\n * auth env right before construction — belt-and-suspenders against\n * startup ordering edge cases (credentials written after boot, stale\n * inherited env, …).\n */\n function buildProvider(providers: Readonly<Record<string, ProviderDescriptor>>, providerKey: ProviderKey = 'anthropic') {\n env.applyAuthEnv?.()\n\n const descriptor = providerDescriptorFor(providers, providerKey)\n try {\n return descriptor.factory()\n }\n catch (err) {\n const message = (err as Error).message ?? String(err)\n if (/No API key found|no credential|not authenticated/i.test(message))\n throw new Error(`No credentials for ${descriptor.label}. Sign in to the provider first.`)\n throw err\n }\n }\n\n /**\n * Resolve the canonical model id for a session. Prefers the per-session\n * override when set; otherwise the provider descriptor's default.\n */\n function resolveModel(\n providers: Readonly<Record<string, ProviderDescriptor>>,\n providerKey: ProviderKey,\n override: string | undefined,\n ): { providerKey: ProviderKey, modelId: string } {\n const descriptor = providerDescriptorFor(providers, providerKey)\n // Last resort for a descriptor with no default: the built-in Anthropic\n // flagship, so the chain never lands on a stale hardcoded id again.\n const modelId = override ?? descriptor.defaultModel ?? anthropicDescriptor.defaultModel ?? 'claude-fable-5-1'\n return { providerKey, modelId }\n }\n\n /*\n * Per-cwd Agent Plugin catalog. Discovery walks the four scan roots and\n * loads each manifest, so it is real disk IO on a path that runs at every\n * agent build (session activation, model change, extension swap). Keyed by\n * cwd only: `enabledPlugins` is applied AFTER discovery by\n * `buildPluginContributions`, so a toggle needs no re-walk.\n *\n * Promise-valued so concurrent builds for the same cwd share one walk.\n * Cleared by `shutdown()`, which is what a settings change that invalidates\n * the runtime config (extensions, plugins, MCPs) already calls — so toggling\n * a plugin re-walks rather than serving a catalog that predates the toggle.\n *\n * Remaining staleness window: a plugin INSTALLED while the host is live and\n * no such setting is touched is not picked up until the next teardown. That\n * is not TUI parity — the TUI's discovery slot is explicitly refreshable via\n * `refreshPlugins` — it is a gap, and the honest fix is a host-level\n * discovery refresh the windows can call.\n */\n const pluginCatalogCache = new Map<string, Promise<readonly DiscoveredPlugin[]>>()\n\n function pluginsForCwd(cwd: string, prefix: string, scanPaths: readonly string[]): Promise<readonly DiscoveredPlugin[]> {\n const existing = pluginCatalogCache.get(cwd)\n if (existing)\n return existing\n const walk = discoverProjectPlugins({\n cwd,\n prefix,\n // Dev / host-provided roots (`ZIDANE_PLUGIN_DIRS`), same as the TUI.\n ...(scanPaths.length > 0 ? { extraRoots: scanPaths } : {}),\n })\n .then(({ plugins, errors }) => {\n // Root-level IO failures (an unreadable plugin dir) are a permissions\n // problem on the directory itself; per-plugin failures already surface\n // as rejected rows in the windows' Extensions tab.\n for (const error of errors)\n hostDebug('discoverProjectPlugins', `${error.source}: ${error.path} — ${error.message}`)\n return plugins as readonly DiscoveredPlugin[]\n })\n .catch((err) => {\n // A failed walk must never block the agent build — degrade to \"no\n // plugins\" and let the run proceed.\n hostDebug('discoverProjectPlugins failed', err)\n pluginCatalogCache.delete(cwd)\n return [] as readonly DiscoveredPlugin[]\n })\n pluginCatalogCache.set(cwd, walk)\n return walk\n }\n\n /**\n * Compose a built-in agent's system prompt, folding in AGENTS.md/CLAUDE.md\n * rules (project + user scope). Returns the rendered system plus the rules\n * block + source files for the context breakdown.\n */\n function systemFor(agentId: string, cwd: string, prefix: string): {\n system: string\n rulesBlock?: string\n rulesFiles?: { path: string, source: string, contents: string }[]\n systemFile?: SystemMdFile\n shadowedSystemFiles?: readonly SystemMdFile[]\n } {\n const settings = env.readSettings()\n // `interactionMode` replaced the `allowInteraction` boolean upstream\n // (config.ts migrates the old key); only 'disabled' removes the doctrine.\n const allowInteraction = settings.interactionMode !== 'disabled'\n const rules = discoverAgentsMd({\n cwd,\n prefix,\n scope: settings.userInstructionsScope,\n // Ruler instruction markdown rides the same gate as ruler MCPs/skills.\n ruler: settings.importRulerConfigs !== false,\n })\n const userInstructions = rules.block ?? undefined\n /*\n * `SYSTEM.md` REPLACES the built-in doctrine (it does not append like\n * AGENTS.md), so it must be discovered wherever the prompt is composed —\n * here, not just in the TUI's local path. Without this the daemon and the\n * GUI silently ran built-in doctrine while the user believed their file\n * was in force, and the safety guidance a `SYSTEM.md` is meant to replace\n * stayed armed.\n *\n * Scoped by the SAME `userInstructionsScope` as AGENTS.md: passing the\n * scope through (rather than a boolean) is what makes `'user'` actually\n * ignore the repo's file, which is the case that setting exists for — an\n * override is the most invasive form of repo-supplied doctrine there is.\n *\n * No profile guard is needed here: the only call site (`buildActiveRun`)\n * already restricts this to the built-in `build`/`plan` profiles, since a\n * host-supplied preset keeps its own `system` and has no built-in doctrine\n * for an override to replace.\n */\n const systemMd = discoverSystemMd({\n cwd,\n prefix,\n scope: settings.userInstructionsScope,\n })\n const systemOverride = systemMd.file?.contents\n const system = agentId === 'plan'\n ? buildPlanSystem({ cwd, userInstructions, allowInteraction, ...(systemOverride ? { systemOverride } : {}) })\n : buildBuildSystem({ cwd, userInstructions, allowInteraction, ...(systemOverride ? { systemOverride } : {}) })\n return {\n system,\n ...(rules.block ? { rulesBlock: rules.block } : {}),\n // Full shape retained: the Instructions tab renders each file's contents\n // as a segment, and it is already in memory here.\n ...(rules.files.length > 0 ? { rulesFiles: rules.files.map(f => ({ path: f.path, source: f.source, contents: f.contents })) } : {}),\n ...(systemMd.file ? { systemFile: systemMd.file } : {}),\n ...(systemMd.shadowed.length > 0 ? { shadowedSystemFiles: systemMd.shadowed } : {}),\n }\n }\n\n async function ensureAgent(sessionId: string): Promise<ActiveRun> {\n assertDispatchOpen()\n const existing = active.get(sessionId)\n if (existing) {\n existing.lastUsedAt = Date.now()\n return existing\n }\n\n const inFlight = pendingBuilds.get(sessionId)\n if (inFlight)\n return inFlight\n\n const generation = teardownGeneration\n\n const build = buildActiveRun(sessionId).then((run) => {\n /*\n * Discard when the host was torn down (generation bump) or this\n * session's pending entry was consumed (`disposeSession`) while we\n * were constructing — inserting the run would resurrect a stale\n * agent. Callers see a retryable error instead.\n */\n if (generation !== teardownGeneration || pendingBuilds.get(sessionId) !== build) {\n discardRun(run)\n throw new Error(`Session \"${sessionId}\" was reset while its agent was starting. Retry the action.`)\n }\n active.set(sessionId, run)\n pruneIdleAgents()\n return run\n })\n\n pendingBuilds.set(sessionId, build)\n trackWork(build)\n\n build\n .catch(() => undefined)\n .finally(() => {\n if (pendingBuilds.get(sessionId) === build)\n pendingBuilds.delete(sessionId)\n })\n\n return build\n }\n\n async function buildActiveRun(sessionId: string): Promise<ActiveRun> {\n const settings = env.readSettings()\n const store = env.store\n const data = await store.load(sessionId)\n\n if (!data)\n throw new Error(`Session \"${sessionId}\" not found. Create the session before sending a prompt.`)\n\n /*\n * Prefer the recorded `metadata.cwd` (the actual folder the user chose).\n * Fall back to the canonical `projectRoot` — better than `process.cwd()`\n * which in a daemon/GUI process points nowhere the user expects tools\n * to operate in.\n */\n /*\n * Canonical cwd semantics (`session-cwd.ts`): empty string counts as\n * unset (directory-less forks persist `cwd: ''`), and the anchor for\n * folderless sessions is the user's HOME — never `process.cwd()`,\n * which in a daemon or packaged app points nowhere the user chose.\n */\n const cwd = resolveSessionCwd({\n metadata: data.metadata,\n projectRoot: data.projectRoot ?? undefined,\n })\n\n /*\n * \"Edit / create extension\" sessions carry the marker stamped at\n * session-creation time. When present we inject the `zidane-extension`\n * docs skill into the FINAL merged skills (below) and auto-activate it\n * each run so the authoring guidance is live from turn one.\n */\n const extensionEditMarker = data.metadata?.[EXTENSION_EDIT_METADATA_KEY]\n const isExtensionEdit = typeof extensionEditMarker === 'object' && extensionEditMarker !== null\n\n /*\n * \"Edit skill\" sessions carry the skill counterpart of the marker\n * above. The preset skill embeds the target's name + directory, so\n * the marker payload matters — `readSkillEditMarker` degrades\n * malformed metadata to null.\n */\n const skillEditMarker = readSkillEditMarker(data.metadata?.[SKILL_EDIT_METADATA_KEY])\n\n const runtimeConfig = await env.runtimeConfig(cwd)\n const agents = runtimeConfig.agents\n const providers = runtimeConfig.providers\n const agentId = agentIdOf(data.agentId, agents)\n const profile = agents[agentId] ?? agents[DEFAULT_AGENT_ID] ?? Object.values(agents)[0]\n if (!profile)\n throw new Error('No agent profiles are registered.')\n /*\n * Agent Plugins discovered on disk near the session's cwd. Resolved BEFORE\n * the preset is composed because it feeds both gates below.\n *\n * `?? []` because a host may hand the engine a hand-built ResolvedConfig\n * that predates this field; a missing scan-path list means \"defaults only\".\n */\n const pluginCatalog = await pluginsForCwd(cwd, runtimeConfig.prefix, runtimeConfig.pluginScanPaths ?? [])\n\n /*\n * Second plugin gate. `enabledExtensions` (applied by whoever built this\n * registry) drops whole extensions; this drops the contributions of\n * plugins an extension pulled in via `ctx.loadAgentPlugin`. Both are\n * needed because plugin skills / servers are fanned into the LOADING\n * extension's sinks, so filtering by extension name alone cannot express\n * \"this extension runs, but the plugin it pulled in does not\" — without\n * this pass a plugin the user explicitly switched off still contributed\n * skills and still had its stdio MCP servers configured, which makes\n * `enabledPlugins` a consent gate that only the TUI honoured.\n *\n * The third argument keeps this in step with the settings list, which\n * dedupes a plugin present both on disk and inside an extension to a\n * single row preferring the disk copy — otherwise that one checkbox\n * governs two live copies and which one survives is decided by merge\n * order rather than by anything the user chose.\n */\n const liveExtensions = filterRegistryPlugins(\n runtimeConfig.extensions,\n settings.enabledPlugins,\n new Set(pluginCatalog.map(p => p.name)),\n )\n // Built-in artifact detection (PRs / branches / commits) + the\n // `record_artifact` tool compose UNDER the profile so hosts get them\n // without touching `basic`; extension-registered kinds ride the\n // registry's hooks. `Settings.artifacts` off drops the preset entirely\n // (no detectors, no tool) and the agent-level switch below silences the\n // loop for anything an extension might still emit.\n const artifactsEnabled = settings.artifacts !== false\n const composedPreset = composeExtensionPreset(\n liveExtensions,\n artifactsEnabled ? composePresets(artifactsPreset, profile.preset) : profile.preset,\n )\n\n /*\n * `loadSession` is the canonical resume path — it rehydrates the\n * in-memory `Session` wrapper from persisted `SessionData` so the model\n * sees the full turn history on the next `run()`. `createSession` is\n * defensive only — the row normally exists on disk already.\n */\n const session\n = (await loadSession(store, sessionId))\n ?? (await createSession({\n id: sessionId,\n agentId: profile.id,\n projectRoot: data.projectRoot ?? cwd,\n store,\n }))\n\n /*\n * Skills catalog: scan `.claude/skills` (project + home) so all zidane\n * surfaces share the user's existing skill library. Allowlist via\n * settings; extension-contributed skills are always allowed.\n */\n const extensionSkillNames = liveExtensions.skills.map(skill => skill.name)\n /*\n * Disk-plugin contributions, gated by `enabledPlugins`. Previously only the\n * TUI's in-process path ran this, so with `useDaemon` on (and in the GUI,\n * always) an enabled plugin's skills and MCP servers were simply absent\n * from the agent — the settings list still showed the plugin as on, so it\n * looked like the plugin doing nothing rather than never being loaded.\n */\n // `enabled` passed straight through: `undefined` already means \"every\n // discovered plugin\" there, matching `filterRegistryPlugins` below and the\n // polarity of every other enabled-allowlist setting.\n const pluginContributions = buildPluginContributions({\n discovered: pluginCatalog,\n enabled: settings.enabledPlugins,\n })\n /*\n * Plugin skill names are force-included for the same reason as extension\n * skills — a user's `enabledSkills` only covers what was on disk when they\n * last toggled — but keyed to the PLUGIN's toggle: enabling the plugin IS\n * the consent, and `enabledPlugins` is the affordance for withholding it.\n */\n const pluginSkillNames = pluginContributions.skills.map(skill => skill.name)\n const mergedEnabledSkills = settings.enabledSkills === undefined\n ? undefined\n : Array.from(new Set([...settings.enabledSkills, ...extensionSkillNames, ...pluginSkillNames]))\n const skillsConfig = buildSkillsConfig({\n // Native scan under the host's own prefix (`.zidane/skills`);\n // other agents' skill dirs (`.claude`, `.codex`, `.pi`) are the\n // FOREIGN set, gated behind the `importForeignConfigs` opt-in —\n // same convention as the TUI's discovery shell.\n scan: defaultSkillScanPaths({\n cwd,\n prefix: runtimeConfig.prefix,\n foreignAgents: settings.importForeignConfigs === true,\n ruler: settings.importRulerConfigs !== false,\n }),\n enabled: mergedEnabledSkills,\n })\n\n /*\n * Merge skills (preset last-wins on `catalog`), THEN inject the\n * extension docs skill — injecting before the merge would be dropped\n * whenever a preset also contributes skills.\n */\n const mergedSkills = { ...skillsConfig, ...(composedPreset.skills ?? {}) }\n const injectExtensionSkill = isExtensionEdit || extensionDirForCwd(cwd) !== null\n const withExtensionSkill = injectExtensionSkill ? withZidaneExtensionSkill(mergedSkills, { cwd }) : mergedSkills\n // Skill-edit sessions get their preset skill the same way (post-merge).\n const withSkillEdit = skillEditMarker\n ? withZidaneSkillEditSkill(withExtensionSkill, skillEditMarker, { cwd })\n : withExtensionSkill\n /*\n * Plugin skills ride `catalog`, never `scan`: handing a plugin's `skills/`\n * to the recursive disk scanner would bypass the strict loader (depth-1\n * discovery, package boundary, per-skill isolation) that is what makes\n * reading a third-party package safe.\n */\n const finalSkills = pluginContributions.skills.length > 0\n ? { ...withSkillEdit, catalog: [...pluginContributions.skills, ...(withSkillEdit.catalog ?? [])] }\n : withSkillEdit\n\n /*\n * MCP servers: discover from `.zidane/mcps.json` (project + user) and\n * filter by the user's allowlist.\n */\n const { servers: discoveredMcps } = discoverProjectMcps({\n cwd,\n prefix: runtimeConfig.prefix,\n foreignAgents: settings.importForeignConfigs === true,\n ruler: settings.importRulerConfigs !== false,\n })\n const mcpServers = buildMcpServers({\n discovered: discoveredMcps,\n enabled: settings.enabledMcps,\n ...(settings.disabledMcpTools ? { disabledTools: settings.disabledMcpTools } : {}),\n })\n\n /*\n * Persist large tool outputs to disk so the conversation context doesn't\n * balloon. Shared `userDir` so persisted blobs are reachable from every\n * surface (`<userDir>/tool-results/<sessionId>/`).\n */\n const persistDir = resolvePersistDir({ userDir: env.userDataDir(), sessionId })\n const tasksDir = resolveTasksDir({ userDir: env.userDataDir(), sessionId })\n const persistBehavior = settings.persistToolResults ? { persistDir } : { persistThreshold: 0, persistDir }\n\n const providerKey = readSessionProvider(data.metadata, providers)\n const providerDescriptor = providerDescriptorFor(providers, providerKey)\n // Validated per-session model pick — undefined when unset OR when the\n // persisted provider was removed (see `readSessionModel`).\n const modelOverride = readSessionModel(data.metadata, providers)\n const { modelId: model } = resolveModel(providers, providerKey, modelOverride)\n\n /*\n * Model-fallback chain: restore `metadata.modelChain` (TUI-canonical\n * shape). Head must match the resolved primary — a diverging chain\n * (out-of-band primary change) silently degrades to \"no fallbacks\";\n * unregistered providers drop.\n */\n const modelFallbacks = restoreChainFallbacks(\n data.metadata?.modelChain,\n { provider: providerKey, model },\n k => providers[k] !== undefined,\n )\n\n /*\n * Interaction tools (`present_plan`, `ask_user`) — bound to this session\n * so clients can correlate request → surface → response. The preset's\n * own tools may shadow ours on name collision (interaction tools are\n * infrastructure, not preset choices).\n *\n * `interactionMode` is read at invocation time (not agent build time) so\n * Ask ↔ Skip applies without an agent rebuild. `disabled` remains a\n * runtime guard for agents built before the setting changed; the next\n * build also removes the interaction doctrine from the prompt.\n *\n * `shouldSkipInteractions` is the shared automatic policy — the tool itself\n * answers with `createAutomaticInteractionResponse` — so every surface\n * (TUI local, GUI embedded, daemon) skips identically. Host-synthesized\n * prompts (slash commands, extension `ctx.interaction.*`) never route\n * through these tools, so they keep their modal regardless of mode.\n */\n const requestInteraction = interactions.requestInteractionFor(sessionId)\n const interactionTools = createInteractionTools({\n requestInteraction: (request: InteractionRequest) => {\n if (env.readSettings().interactionMode === 'disabled')\n return Promise.reject(new Error('Interaction tools are disabled in Settings.'))\n return requestInteraction(request)\n },\n shouldSkipInteractions: () => env.readSettings().interactionMode === 'skip',\n })\n const chatReferenceTokens = { current: [] as readonly string[] }\n const chatTurnsReadTool = createChatTurnsReadTool({\n store,\n getAllowedTokens: () => chatReferenceTokens.current,\n })\n\n /*\n * Per-call annotation ledger, captured by the hook closures below.\n * Attached to the `ActiveRun` so dispose/shutdown can force-clear it;\n * `agent:done` clears it at every run boundary.\n */\n const ledger = new EditOutcomeLedger()\n\n const builtInSystem = agentId === 'build' || agentId === 'plan'\n ? systemFor(agentId, cwd, runtimeConfig.prefix)\n : null\n /*\n * A worker's last message becomes its main's `summary`, so it must be a\n * report the main can act on, not a sign-off. Appended as a system\n * fragment (like extension fragments) rather than spliced into the user\n * prompt, so it applies to every run of the worker, not only the first.\n */\n const isMain = isMainSession(session.metadata) && settings.orchestration === true\n /*\n * The main gets its playbook plus a Projects briefing (directories with\n * ongoing work and the session to address in each), so it does not spend\n * a `list` call rediscovering them every run. Snapshot at agent build —\n * the fragment says so and points at `list` for live state.\n */\n const mainFragments = isMain\n ? [MAIN_PLAYBOOK_FRAGMENT, renderProjectsBriefing(await orchestration.projects(), Date.now())]\n : []\n const system = appendSystemFragments(\n builtInSystem?.system ?? composedPreset.system,\n orchestratedBy(session.metadata)\n ? [...runtimeConfig.extensionSystemFragments, WORKER_REPORT_FRAGMENT]\n : [...runtimeConfig.extensionSystemFragments, ...mainFragments],\n )\n const work = new Map<string, RunningWorkItem>()\n const sessionSafelist = new Set<string>()\n\n const mcpCredentialStore = env.mcpCredentialStore\n\n /*\n * Primary + fallback provider instances — each distinct, registered\n * chain key gets its instance constructed into the agent so run-level\n * pinned targets can resolve. Defensive skips (unregistered key,\n * throwing factory, duplicate instance name) keep a stale persisted\n * chain from ever blocking the agent build.\n */\n const primaryProvider = buildProvider(providers, providerKey)\n const providerNameByKey = new Map<ProviderKey, string>([[providerKey, primaryProvider.name]])\n const extraProviders: Provider[] = []\n const seenProviderNames = new Set([primaryProvider.name])\n for (const entry of modelFallbacks) {\n if (providerNameByKey.has(entry.provider))\n continue\n const descriptor = providers[entry.provider]\n if (!descriptor)\n continue\n let instance: Provider\n try {\n instance = descriptor.factory()\n }\n catch {\n continue\n }\n if (seenProviderNames.has(instance.name))\n continue\n seenProviderNames.add(instance.name)\n providerNameByKey.set(entry.provider, instance.name)\n extraProviders.push(instance)\n }\n\n /*\n * Tool merge order — interaction tools FIRST, preset tools SECOND so\n * a preset's own `present_plan` / `ask_user` would shadow ours on\n * name collision.\n */\n const agentTools = {\n ...interactionTools,\n // Only a MAIN session gets the orchestration tool — a worker never sees\n // it, which is the depth cap made structural rather than checked. Gated\n // on the setting as well: the main is persisted, so resuming it with the\n // feature off must not hand the model session-spawning powers.\n ...(isMain ? { [SESSIONS_TOOL]: createSessionsTool(orchestration, sessionId) } : {}),\n chatTurnsRead: chatTurnsReadTool,\n ...(composedPreset.tools ?? {}),\n }\n\n const agentBehavior = {\n ...composedPreset.behavior,\n ...persistBehavior,\n tasksDir,\n }\n\n const agent = createAgent({\n ...composedPreset,\n provider: extraProviders.length > 0 ? [primaryProvider, ...extraProviders] : primaryProvider,\n name: profile.label,\n ...(system ? { system } : {}),\n session,\n artifacts: artifactsEnabled,\n execution: createProcessContext({ cwd }),\n skills: finalSkills,\n // Plugin servers ordered before the preset's, same precedence as skills.\n mcpServers: dedupByName([...mcpServers, ...pluginContributions.mcpServers, ...(composedPreset.mcpServers ?? [])]),\n mcpConnector: configs =>\n connectMcpServers(\n configs,\n undefined,\n agent.hooks,\n mcpCredentialStore\n ? { buildAuthProvider: config => new McpOAuthProvider({ name: config.name, store: mcpCredentialStore() }) }\n : undefined,\n ),\n\n tools: agentTools,\n contextBreakdownSections: [\n ...liveExtensions.contextBreakdownSections,\n ...liveExtensions.contextPanelSections,\n ],\n behavior: agentBehavior,\n })\n subscribeMcpToolsCache(agent.hooks, { dataDir: env.userDataDir() })\n installProviderDiagnosticHook(agent.hooks, (diagnostic) => {\n broadcast({ kind: 'provider:diagnostic', sessionId, diagnostic })\n })\n /*\n * Cache-break detection is per-agent closure state (it diffs successive\n * request prefixes), so it has to be installed on the agent that actually\n * runs. In daemon mode that is this one, not the window's — without this\n * the footer's cache chip and the debug lines are dead exactly where a\n * long-lived shared daemon makes prefix churn most likely.\n */\n installCacheBreakLogger(agent.hooks, { log: line => hostDebug('cache-break', line) })\n agent.hooks.hook('cache:break', (info) => {\n broadcast({ kind: 'cache:break', sessionId, info })\n })\n\n /*\n * Tell windows which `SYSTEM.md` (if any) this prompt was composed with.\n * Emitted once per agent build — the same moment the override was read —\n * so a window can distinguish \"no file on disk\" from \"file in force\"\n * without probing a filesystem that may not be the daemon's.\n */\n broadcast({\n kind: 'system-md',\n sessionId,\n file: builtInSystem?.systemFile ?? null,\n shadowed: builtInSystem?.shadowedSystemFiles ?? [],\n })\n\n /*\n * Capture the spawn seam for `/` command `ctx.spawnAgents`: the exact\n * inputs the agent was built with, so command children get the same\n * model routing, tools, skills, and behavior as model-initiated spawns.\n */\n const spawnCandidate = (composedPreset.tools ?? {}).spawn\n // Unlike the GUI's always-spawn-capable preset, host presets may omit\n // `spawn` entirely — guard before the type-narrowing check.\n const commandSeam: CommandSpawnSeam | undefined = spawnCandidate && isSpawnTool(spawnCandidate)\n ? {\n spawnTool: spawnCandidate,\n provider: primaryProvider,\n providers: [primaryProvider, ...extraProviders],\n ...(system ? { system } : {}),\n tools: agentTools,\n skills: finalSkills,\n behavior: agentBehavior,\n name: profile.label,\n }\n : undefined\n\n /*\n * MCP warning log — same per-session file the local TUI writes\n * (`resolveMcpWarningsDir`), so daemon-side MCP misbehavior is\n * debuggable from any window's /mcps view or the file itself.\n * Closed with the agent on dispose.\n */\n const warnLog = attachMcpWarnLog(agent.hooks, {\n dir: resolveMcpWarningsDir({ userDir: env.userDataDir(), sessionId }),\n })\n\n /*\n * ---- Hook → HostSignal forwarding -------------------------------------\n * Same merge semantics as `useStreamBuffer`: same-owner same-kind deltas\n * coalesce on the client reducer side; everything else appends.\n */\n\n agent.hooks.hook('stream:text', (ctx) => {\n const event: StreamEvent = { kind: 'markdown', text: ctx.delta, streaming: true, turnId: ctx.turnId }\n broadcast({ kind: 'event', sessionId, event })\n })\n\n agent.hooks.hook('stream:thinking', (ctx) => {\n const event: StreamEvent = { kind: 'thinking', text: ctx.delta, turnId: ctx.turnId }\n broadcast({ kind: 'event', sessionId, event })\n })\n\n /*\n * Subagent prose. `childId` + `depth` are what put the delta in the\n * child's lane in the transcript, and windows already key their stream\n * buffer on that owner — so these are the parent handlers above with the\n * owner threaded through, nothing more.\n *\n * Previously absent: the host forwarded child TOOL activity and spawn\n * markers but not child text, so in daemon mode a subagent's reasoning\n * and answer never streamed. It reappeared in one block whenever the\n * run-end `loadEvents` replace converged the transcript, which reads as\n * a stalled subagent for the whole run.\n */\n agent.hooks.hook('child:stream:text', (ctx) => {\n const event: StreamEvent = {\n kind: 'markdown',\n text: ctx.delta,\n streaming: true,\n turnId: ctx.turnId,\n childId: ctx.childId,\n ...(typeof ctx.depth === 'number' ? { depth: ctx.depth } : {}),\n }\n broadcast({ kind: 'event', sessionId, event })\n })\n\n agent.hooks.hook('child:stream:thinking', (ctx) => {\n const event: StreamEvent = {\n kind: 'thinking',\n text: ctx.delta,\n turnId: ctx.turnId,\n childId: ctx.childId,\n ...(typeof ctx.depth === 'number' ? { depth: ctx.depth } : {}),\n }\n broadcast({ kind: 'event', sessionId, event })\n })\n\n const manuallyEmittedGateBlocks = new Set<string>()\n\n /**\n * Emit the canonical `tool` stream event. Centralized so `tool:before`\n * and `child:tool:before` route through identical logic.\n *\n * When the gate stashed a `PendingAnnotation` for this `callId`\n * (`partial` decision), the stashed `originalInput` / `originalPayload`\n * win over the live `ctx.input` — by `tool:before`, `ctx.input.edits`\n * has been REDUCED to the approved subset; the stash carries the\n * model's full intent so clients can paint every hunk with its badge.\n */\n const emitToolEvent = (\n callId: string,\n name: string,\n liveInput: Record<string, unknown>,\n priorContent: string | undefined,\n turnId: string | undefined,\n ownership?: { childId: string, depth: number },\n ): void => {\n const stashed = ledger.peek(callId)\n\n let inputForEvent: Record<string, unknown>\n let edit: EditPayload | undefined\n\n if (stashed) {\n inputForEvent = stashed.originalInput\n edit = { ...stashed.originalPayload, outcomes: stashed.outcomes.slice() }\n }\n else {\n inputForEvent = liveInput\n if (name === 'write_file')\n edit = extractEditPayload(name, liveInput, priorContent ?? '')\n else if (name === 'edit' || name === 'multi_edit')\n edit = extractEditPayload(name, liveInput)\n }\n\n const event: StreamEvent = {\n kind: 'tool',\n text: toolCallPreview(name, inputForEvent),\n tool: name,\n callId,\n input: inputForEvent,\n ...(turnId ? { turnId } : {}),\n ...(ownership ? { childId: ownership.childId, depth: ownership.depth } : {}),\n ...(edit ? { edit } : {}),\n }\n broadcast({ kind: 'event', sessionId, event })\n }\n\n const registerWork = (item: RunningWorkItem): void => {\n work.set(item.id, item)\n broadcast({ kind: 'work:snapshot', sessionId, items: visibleWorkItems({ work }) })\n }\n\n const dropWork = (id: string): void => {\n if (!work.delete(id))\n return\n broadcast({ kind: 'work:snapshot', sessionId, items: visibleWorkItems({ work }) })\n }\n\n const emitGateBlockedResult = (\n callId: string,\n name: string,\n reason: string,\n turnId: string | undefined,\n ownership?: { childId: string, depth: number },\n ): void => {\n const event: StreamEvent = {\n kind: 'tool-result',\n text: `Blocked: ${reason}`,\n tool: name,\n callId,\n ...(turnId ? { turnId } : {}),\n ...(ownership ? { childId: ownership.childId, depth: ownership.depth } : {}),\n }\n broadcast({ kind: 'event', sessionId, event })\n }\n\n agent.hooks.hook('tool:before', (ctx) => {\n registerWork({ kind: 'tool', id: ctx.callId, label: ctx.name, startedAt: Date.now() })\n emitToolEvent(ctx.callId, ctx.name, ctx.input as Record<string, unknown>, ctx.priorContent, ctx.turnId)\n })\n\n agent.hooks.hook('child:tool:before', (ctx) => {\n registerWork({\n kind: 'tool',\n id: ctx.callId,\n label: ctx.name,\n startedAt: Date.now(),\n childId: ctx.childId,\n })\n emitToolEvent(ctx.callId, ctx.name, ctx.input as Record<string, unknown>, ctx.priorContent, ctx.turnId, {\n childId: ctx.childId,\n depth: ctx.depth,\n })\n })\n\n agent.hooks.hook('tool:dispatched', (ctx) => {\n if (ctx.outcome === 'execute' || ctx.outcome === 'unknown')\n return\n if (ctx.outcome === 'gate-block' && manuallyEmittedGateBlocks.delete(ctx.callId))\n return\n\n emitToolEvent(ctx.callId, ctx.name, ctx.input as Record<string, unknown>, undefined, ctx.turnId)\n\n if (ctx.outcome === 'gate-block' && ctx.reason)\n emitGateBlockedResult(ctx.callId, ctx.name, ctx.reason, ctx.turnId)\n })\n\n agent.hooks.hook('child:tool:dispatched', (ctx) => {\n if (ctx.outcome === 'execute' || ctx.outcome === 'unknown')\n return\n if (ctx.outcome === 'gate-block' && manuallyEmittedGateBlocks.delete(ctx.callId))\n return\n\n const ownership = { childId: ctx.childId, depth: ctx.depth }\n emitToolEvent(ctx.callId, ctx.name, ctx.input as Record<string, unknown>, undefined, ctx.turnId, ownership)\n\n if (ctx.outcome === 'gate-block' && ctx.reason)\n emitGateBlockedResult(ctx.callId, ctx.name, ctx.reason, ctx.turnId, ownership)\n })\n\n /**\n * `tool:transform` runs AFTER the tool body returns its result and\n * BEFORE the result is sent back to the model — the seam the\n * `<edit-outcomes>` annotation rides in on. Registered on both the\n * parent and child transforms so subagent edits get the annotation too.\n */\n const annotateEditResult = (\n callId: string,\n toolName: string,\n result: string | readonly unknown[],\n ): string | unknown[] | undefined => {\n const stashed = ledger.drain(callId)\n if (!stashed)\n return undefined\n\n /*\n * Canonical merge path: peel the body's `<edit-outcomes>` block (if\n * the tool body emitted one — multi_edit does on partial failures),\n * merge it into the approval-side outcomes, then re-append exactly\n * one annotation.\n */\n const bodyOutcomes = parseEditOutcomesFromResult(result as Parameters<typeof parseEditOutcomesFromResult>[0])\n const merged = mergeApprovalAndBodyOutcomes(stashed.outcomes, bodyOutcomes)\n const annotation = buildEditOutcomesAnnotation(merged)\n\n if (typeof result === 'string') {\n let body = stripEditOutcomesAnnotation(result)\n\n /*\n * Multi-edit header rewrite — the tool body counts only the\n * approved subset; the canonical transcript should reflect the\n * original edit list size so clients and the model see consistent\n * totals.\n */\n if (toolName === 'multi_edit') {\n // multi_edit's schema key is `path` — and when it resolves\n // empty, skip the rewrite entirely (same gate as the TUI's\n // transform in src/tui/app.tsx).\n const rawPath = (stashed.originalInput as { path?: unknown }).path\n const path = typeof rawPath === 'string' ? rawPath : ''\n if (path)\n body = rewriteMultiEditHeader(body, merged, path)\n }\n\n return body.length === 0 ? annotation : `${body}\\n\\n${annotation}`\n }\n\n /*\n * Structured content — peel + re-append from the text segments only;\n * image/document blocks pass through.\n */\n const peeled = (result as readonly { type: string, text?: string }[]).map(block =>\n block.type === 'text' && typeof block.text === 'string'\n ? { ...block, text: stripEditOutcomesAnnotation(block.text) }\n : block,\n )\n return [...peeled, { type: 'text', text: `\\n${annotation}` }]\n }\n\n agent.hooks.hook('tool:transform', (ctx) => {\n const next = annotateEditResult(ctx.callId, ctx.name, ctx.result)\n if (next !== undefined)\n ctx.result = next as typeof ctx.result\n })\n\n agent.hooks.hook('child:tool:transform', (ctx) => {\n const next = annotateEditResult(ctx.callId, ctx.name, ctx.result)\n if (next !== undefined)\n ctx.result = next as typeof ctx.result\n })\n\n agent.hooks.hook('tool:after', (ctx) => {\n dropWork(ctx.callId)\n const event: StreamEvent = {\n kind: 'tool-result',\n text: resultToText(ctx.result),\n tool: ctx.name,\n callId: ctx.callId,\n turnId: ctx.turnId,\n }\n broadcast({ kind: 'event', sessionId, event })\n /*\n * Todos state is mutated by `todowrite` (and sometimes refreshed by\n * `todoread`) — recompute + broadcast only when a todo tool fires.\n */\n if (isTodoTool(ctx.name))\n broadcastTodosSnapshot({ sessionId, agent })\n })\n\n // Artifacts (PRs / branches / commits / files) are already persisted by\n // the loop; the signal lets windows refresh badges and an open\n // artifacts rail live. Shared-session subagents bubble theirs as\n // `child:artifact:recorded`.\n agent.hooks.hook('artifact:recorded', (ctx) => {\n broadcast({ kind: 'artifact:recorded', sessionId, artifact: ctx.artifact })\n })\n agent.hooks.hook('child:artifact:recorded', (ctx) => {\n broadcast({ kind: 'artifact:recorded', sessionId, artifact: ctx.artifact })\n })\n\n agent.hooks.hook('child:tool:after', (ctx) => {\n dropWork(ctx.callId)\n const event: StreamEvent = {\n kind: 'tool-result',\n text: resultToText(ctx.result),\n tool: ctx.name,\n callId: ctx.callId,\n turnId: ctx.turnId,\n childId: ctx.childId,\n depth: ctx.depth,\n }\n broadcast({ kind: 'event', sessionId, event })\n // Subagent todo writes route to a per-run slot via the same helper.\n if (isTodoTool(ctx.name))\n broadcastTodosSnapshot({ sessionId, agent })\n })\n\n agent.hooks.hook('tool:error', (ctx) => {\n dropWork(ctx.callId)\n /*\n * `tool:transform` doesn't fire on the throw path — drop any stash\n * entry so the Map doesn't leak past the run.\n */\n ledger.discard(ctx.callId)\n\n const event: StreamEvent = {\n kind: 'error',\n text: ctx.error.message,\n tool: ctx.name,\n callId: ctx.callId,\n turnId: ctx.turnId,\n }\n broadcast({ kind: 'event', sessionId, event })\n })\n\n agent.hooks.hook('child:tool:error', (ctx) => {\n dropWork(ctx.callId)\n ledger.discard(ctx.callId)\n\n const event: StreamEvent = {\n kind: 'error',\n text: ctx.error.message,\n tool: ctx.name,\n callId: ctx.callId,\n turnId: ctx.turnId,\n childId: ctx.childId,\n depth: ctx.depth,\n }\n broadcast({ kind: 'event', sessionId, event })\n })\n\n agent.hooks.hook('tool:cancelled', (ctx) => {\n dropWork(ctx.callId)\n })\n\n agent.hooks.hook('child:tool:cancelled', (ctx) => {\n dropWork(ctx.callId)\n })\n\n const registerBackgroundTask = (ctx: { taskId: string, command: string, startedAt: number, childId?: string }): void => {\n registerWork({\n kind: 'task',\n id: ctx.taskId,\n label: `shell (background): ${previewLine(ctx.command, 60)}`,\n startedAt: ctx.startedAt,\n ...(ctx.childId ? { childId: ctx.childId } : {}),\n })\n }\n\n const dropBackgroundTaskAndEmitBanner = (ctx: {\n taskId: string\n status: 'exited' | 'killed'\n exitCode: number\n signal?: NodeJS.Signals\n outputPath: string\n durationMs: number\n command: string\n childId?: string\n depth?: number\n }): void => {\n dropWork(ctx.taskId)\n broadcast({\n kind: 'event',\n sessionId,\n event: {\n kind: 'task-notification',\n text: formatTaskSummary(ctx),\n task: {\n taskId: ctx.taskId,\n status: ctx.status,\n exitCode: ctx.exitCode,\n outputPath: ctx.outputPath,\n command: ctx.command,\n durationMs: ctx.durationMs,\n },\n ...(ctx.childId ? { childId: ctx.childId } : {}),\n ...(typeof ctx.depth === 'number' ? { depth: ctx.depth } : {}),\n },\n })\n }\n\n agent.hooks.hook('background:start', ctx => registerBackgroundTask(ctx))\n agent.hooks.hook('background:exit', (ctx) => {\n dropBackgroundTaskAndEmitBanner(ctx)\n // Idle-path delivery — a task that finished mid-run is delivered by\n // the run-end drain instead.\n maybeWake(sessionId)\n })\n agent.hooks.hook('background:reassign', (ctx) => {\n const existing = work.get(ctx.taskId)\n if (!existing)\n return\n work.set(ctx.taskId, { kind: 'task', id: existing.id, label: existing.label, startedAt: existing.startedAt })\n broadcast({ kind: 'work:snapshot', sessionId, items: visibleWorkItems({ work }) })\n })\n agent.hooks.hook('child:background:start', ctx => registerBackgroundTask(ctx))\n agent.hooks.hook('child:background:exit', ctx => dropBackgroundTaskAndEmitBanner(ctx))\n\n /**\n * Memory hardening: clear the entire stash on every run boundary.\n * Catches any entry whose `tool:transform` never fired so the Map stays\n * bounded by run lifetime rather than session lifetime.\n */\n agent.hooks.hook('agent:done', () => {\n ledger.clear()\n manuallyEmittedGateBlocks.clear()\n })\n\n /**\n * Run-end visibility for abnormal finish reasons (mirrors the TUI's\n * in-process run-end banner). Without this, a run ended by the\n * provider's safety filter (`content-filter`, e.g. Anthropic\n * `stop_reason: refusal` after PDF-heavy tool results — the loop\n * already retried before giving up), the output-token cap, or the\n * empty-pause cap stops silently with an empty assistant turn — the\n * classic \"stalled session\" report. Lives in the host rather than the\n * GUI adapter so daemon-attached windows get the same notice.\n */\n agent.hooks.hook('agent:done', (stats) => {\n const stop = runEndNotice(stats.turnUsage?.at(-1)?.finishReason)\n // Structured `notice` rides along so windows render the same severity\n // glyph + label chip the TUI's local path does via `emitNotice`.\n if (stop)\n broadcast({ kind: 'event', sessionId, event: { kind: 'info', text: stop.text, notice: stop.notice } })\n })\n\n /**\n * Unified gate handler — registered on both the parent and child tool\n * gates so subagent-issued edit approvals route through the same\n * surface. The `originator` carries the child label to clients.\n *\n * On a `partial` decision we (i) reduce `ctx.input.edits` to the\n * approved subset so the tool runs atomically over a smaller batch,\n * (ii) stash the outcomes keyed by `callId` for `tool:transform`. On\n * `deny` / all-denied partial we set `ctx.block = true` and emit the\n * synthetic transcript pair so clients still see a `tool` +\n * `tool-result` row.\n */\n const applyGate = async (\n ctx: {\n block: boolean\n reason: string\n name: string\n input: Record<string, unknown>\n callId: string\n turnId?: string\n childId?: string\n depth?: number\n },\n originator: ApprovalOriginator,\n ): Promise<void> => {\n if (ctx.block) {\n // upstream already refused (e.g. skill allowlist); nothing to do\n return\n }\n\n const input = ctx.input\n\n /*\n * `safeMode` is read at gate-evaluation time (not agent build time)\n * so flipping it mid-session takes effect on the next tool call.\n */\n if (env.readSettings().safeMode === false || isOnSafelist([...sessionSafelist], ctx.name, input))\n return\n\n const payload = extractEditPayloadForGate(ctx.name, input)\n\n let priorContent: string | null = null\n let preview: ReturnType<typeof serializePreview> | undefined\n\n if (payload) {\n const path = typeof input.path === 'string' ? input.path : undefined\n priorContent = path ? await readFileSafe(resolveCwdPath(cwd, path)) : ''\n\n try {\n /*\n * `contextLines: 3` matches the canonical TUI default — clients\n * compute their own tight per-hunk diffs; we only need the\n * resolver pass to populate `resolution` / `resolvedPayload`.\n */\n const result = previewEditPayload(payload, priorContent, 3)\n preview = serializePreview(result)\n }\n catch {\n // resolver fell over — clients fall back to the JSON view\n }\n }\n\n const decision = await approvals.request({\n sessionId,\n cwd,\n tool: ctx.name,\n input,\n ...(payload ? { edit: payload, priorContent } : {}),\n ...(preview ? { preview } : {}),\n ...(originator.kind === 'child' ? { originator } : {}),\n })\n\n if (decision === 'accept-session') {\n for (const entry of suggestSafelistEntries(ctx.name, input))\n sessionSafelist.add(entry)\n }\n\n if (payload) {\n const resolved = resolveApprovalForPayload(decision, payload)\n\n if (resolved.shouldBlock) {\n ctx.block = true\n ctx.reason = decision === 'deny' ? 'User denied this tool call' : 'All edits denied'\n\n /*\n * Live transcript bookkeeping for the blocked call. Without this,\n * `tool:before` never fires and clients miss the row entirely\n * until reload. Mirror what canonical eventsFromTurns would emit.\n */\n const ownership\n = ctx.childId !== undefined && ctx.depth !== undefined ? { childId: ctx.childId, depth: ctx.depth } : undefined\n\n const syntheticEdit = resolved.syntheticEvent ?? { ...payload, outcomes: resolved.outcomes }\n\n const toolEvent: StreamEvent = {\n kind: 'tool',\n text: toolCallPreview(ctx.name, input),\n tool: ctx.name,\n callId: ctx.callId,\n input,\n edit: syntheticEdit,\n ...(ctx.turnId ? { turnId: ctx.turnId } : {}),\n ...(ownership ?? {}),\n }\n broadcast({ kind: 'event', sessionId, event: toolEvent })\n\n const annotation = buildEditOutcomesAnnotation(resolved.outcomes)\n\n const resultEvent: StreamEvent = {\n kind: 'tool-result',\n text: `Blocked: ${ctx.reason}\\n\\n${annotation}`,\n tool: ctx.name,\n callId: ctx.callId,\n ...(ctx.turnId ? { turnId: ctx.turnId } : {}),\n ...(ownership ?? {}),\n }\n broadcast({ kind: 'event', sessionId, event: resultEvent })\n manuallyEmittedGateBlocks.add(ctx.callId)\n\n return\n }\n\n /*\n * Partial-accept path: reduce `ctx.input.edits` to the approved\n * subset so `multi_edit` runs atomically over only those edits, and\n * stash outcomes + original input/payload for `tool:before` /\n * `tool:transform`. Full accepts fall through unchanged.\n */\n if (typeof decision === 'object' && decision.kind === 'partial' && Array.isArray(input.edits)) {\n const reducedEdits = (input.edits as unknown[]).filter((_, i) => decision.mask[i] === true)\n ctx.input = { ...input, edits: reducedEdits }\n ledger.stash(ctx.callId, {\n outcomes: resolved.outcomes,\n originalInput: input,\n originalPayload: payload,\n })\n }\n\n return\n }\n\n /*\n * Non-edit tools: `accept-*` pass through; `deny` blocks; `partial`\n * shouldn't occur but is treated as deny defensively.\n */\n if (decision === 'deny' || (typeof decision === 'object' && decision.kind === 'partial')) {\n ctx.block = true\n ctx.reason = 'User denied this tool call'\n }\n }\n\n agent.hooks.hook('tool:gate', ctx => applyGate(ctx, { kind: 'parent' }))\n agent.hooks.hook('child:tool:gate', ctx => applyGate(ctx, { kind: 'child', label: ctx.childId }))\n agent.hooks.hook('mcp:tool:gate', async (ctx) => {\n const gate = {\n block: ctx.block,\n reason: ctx.reason,\n name: ctx.displayName,\n input: ctx.input,\n callId: ctx.callId,\n turnId: ctx.turnId,\n }\n await applyGate(gate, { kind: 'parent' })\n ctx.block = gate.block\n ctx.reason = gate.reason\n ctx.input = gate.input\n })\n agent.hooks.hook('child:mcp:tool:gate', async (ctx) => {\n const gate = {\n block: ctx.block,\n reason: ctx.reason,\n name: ctx.displayName,\n input: ctx.input,\n callId: ctx.callId,\n turnId: ctx.turnId,\n childId: ctx.childId,\n depth: ctx.depth,\n }\n await applyGate(gate, { kind: 'child', label: ctx.childId })\n ctx.block = gate.block\n ctx.reason = gate.reason\n ctx.input = gate.input\n })\n\n /*\n * Subagent lifecycle banners — same StreamEvent shapes `eventsFromTurns`\n * produces on reload, so the live spawn tree renders identically in\n * every window (child tool events already carry childId/depth; these\n * add the start/end markers between them).\n */\n // Wall-clock start + task/model per in-flight child — drives the\n // structured `spawn` lifecycle metadata and the elapsed segment on the\n // spawn-end marker. Same bookkeeping as the local TUI hooks.\n const spawnStartedAt = new Map<string, SpawnStartRecord>()\n agent.hooks.hook('spawn:before', (ctx) => {\n /*\n * Canonical (unit-tested) marker builders — shared with the GUI so\n * live markers match replay byte-for-byte, `resumes child-N` prefix\n * for resumed children included.\n */\n const startedAt = Date.now()\n spawnStartedAt.set(ctx.id, {\n startedAt,\n task: ctx.task,\n ...(ctx.model ? { model: ctx.model } : {}),\n })\n broadcast({ kind: 'event', sessionId, event: buildSpawnStartEvent(ctx, startedAt) })\n })\n agent.hooks.hook('spawn:complete', (ctx) => {\n const record = spawnStartedAt.get(ctx.id)\n spawnStartedAt.delete(ctx.id)\n broadcast({ kind: 'event', sessionId, event: buildSpawnEndEvent(ctx, record) })\n })\n agent.hooks.hook('spawn:error', ({ id, depth, error }) => {\n // Drop the start record — the completion that would consume it\n // never fires on the error path.\n spawnStartedAt.delete(id)\n broadcast({\n kind: 'event',\n sessionId,\n event: {\n kind: 'error',\n text: `[${id}] ${(error as Error).message}`,\n childId: id,\n depth: depth ?? 1,\n },\n })\n })\n\n /*\n * Active-skill chips — snapshot on every activation change so windows\n * mirror the daemon agent's live skill state.\n */\n const broadcastSkills = (): void => {\n broadcast({\n kind: 'skills:snapshot',\n sessionId,\n active: agent.activeSkills.map(entry => entry.skill.name),\n })\n }\n agent.hooks.hook('skills:activate', broadcastSkills)\n agent.hooks.hook('skills:deactivate', ({ reason }) => {\n /*\n * `'run-end'` is the framework's mechanical per-run cleanup — the\n * windows' pin chips must survive it (they re-assert pins on the\n * next send), exactly like the local TUI's reason filter. User /\n * model intent ('model' / 'explicit' / 'reset') drains the chip.\n */\n if (reason !== 'run-end')\n broadcastSkills()\n })\n\n // MCP auth badge — the login flow itself stays window-local (browser +\n // shared credential store); this makes the \"needs login\" state visible.\n agent.hooks.hook('mcp:auth:required', ({ name, reason }) => {\n broadcast({ kind: 'mcp:auth', sessionId, name, reason })\n })\n\n /*\n * Provider retries — footer indicator in every window. `source` keys\n * the clear: streaming resumes for that source (client-side) or the\n * run ends (busy backstop).\n */\n agent.hooks.hook('stream:retry', ({ nextAttempt, maxAttempts }) => {\n broadcast({ kind: 'stream:retry', sessionId, attempt: nextAttempt, max: maxAttempts, source: 'parent' })\n })\n agent.hooks.hook('child:stream:retry', ({ nextAttempt, maxAttempts, childId }) => {\n broadcast({ kind: 'stream:retry', sessionId, attempt: nextAttempt, max: maxAttempts, source: childId })\n })\n /*\n * Terminal stream failure clears that source's indicator. Windows also\n * clear at run end, but a SUBAGENT stream can fail while the parent run\n * continues — without this the \"retrying n/m\" chip sticks for the rest of\n * the run, describing a stream that is already dead.\n */\n agent.hooks.hook('stream:error', () => {\n broadcast({ kind: 'stream:retry:cleared', sessionId, source: 'parent' })\n })\n agent.hooks.hook('child:stream:error', ({ childId }) => {\n broadcast({ kind: 'stream:retry:cleared', sessionId, source: childId })\n })\n\n /*\n * Native provider server tools (e.g. Anthropic `web_search`) execute\n * upstream and bypass the tool loop — surface them as the same\n * synthetic `tool` / `tool-result` events the local TUI and the\n * `eventsFromTurns` replay produce.\n */\n agent.hooks.hook('stream:server_tool_use', ({ id, name, input, turnId }) => {\n broadcast({\n kind: 'event',\n sessionId,\n event: { kind: 'tool', text: toolCallPreview(name, input), tool: name, input, callId: id, turnId },\n })\n })\n agent.hooks.hook('stream:server_tool_result', ({ toolUseId, toolName, content, turnId }) => {\n broadcast({\n kind: 'event',\n sessionId,\n event: { kind: 'tool-result', text: serverToolResultSummary(content), tool: toolName, callId: toolUseId, turnId },\n })\n })\n agent.hooks.hook('child:stream:server_tool_use', ({ id, name, input, childId, depth, turnId }) => {\n broadcast({\n kind: 'event',\n sessionId,\n event: { kind: 'tool', text: toolCallPreview(name, input), tool: name, input, callId: id, childId, depth, turnId },\n })\n })\n agent.hooks.hook('child:stream:server_tool_result', ({ toolUseId, toolName, content, childId, depth, turnId }) => {\n broadcast({\n kind: 'event',\n sessionId,\n event: { kind: 'tool-result', text: serverToolResultSummary(content), tool: toolName, callId: toolUseId, childId, depth, turnId },\n })\n })\n\n agent.hooks.hook('session:turns', ({ turns: appended }) => {\n const refs = promptTurnRefs(appended)\n if (refs.length > 0)\n broadcast({ kind: 'prompt-turns', sessionId, refs })\n })\n\n // Throttled by the loop — live footer counters, not a stream.\n agent.hooks.hook('stream:progress', ({ turnId, outputTokens, estimated, elapsedMs, estCostUsd }) => {\n broadcast({\n kind: 'stream:progress',\n sessionId,\n turnId,\n outputTokens,\n estimated,\n elapsedMs,\n ...(estCostUsd !== undefined ? { estCostUsd } : {}),\n })\n })\n\n agent.hooks.hook('turn:after', (ctx) => {\n broadcast({\n kind: 'turn:end',\n sessionId,\n turnId: ctx.turnId,\n ...(ctx.usage ? { usage: ctx.usage } : {}),\n })\n })\n\n // Child turns carry their own usage — clients accrue subagent spend\n // live (context gauges only track PARENT turns; cost tracks both).\n agent.hooks.hook('child:turn:after', (ctx) => {\n broadcast({\n kind: 'turn:end',\n sessionId,\n turnId: ctx.turnId,\n childId: ctx.childId,\n depth: ctx.depth,\n ...(ctx.usage ? { usage: ctx.usage } : {}),\n })\n })\n\n /*\n * Auto-compaction is loop-native (`behavior.autoCompact`, injected per\n * run in `sendPrompt`). These hooks mirror the manual-compaction\n * broadcast surface so client footers + boundary cards light up.\n */\n agent.hooks.hook('compact:start', () => {\n broadcast({ kind: 'compacting:start', sessionId })\n broadcast({\n kind: 'event',\n sessionId,\n event: { kind: 'info', text: 'Auto-compacting conversation…' },\n })\n })\n // Throttled by the loop — a low-rate progress tick, not a stream.\n agent.hooks.hook('compact:progress', ({ estOutputTokens }) => {\n broadcast({ kind: 'compacting:progress', sessionId, estOutputTokens })\n })\n agent.hooks.hook('compact:end', (ctx) => {\n if (ctx.status === 'success') {\n // Surface the boundary card live via the SAME mapper\n // `eventsFromTurns` uses on reload, reading the marker the loop\n // just appended — live and reloaded cards stay identical.\n const markerTurn = [...(agent.session?.turns ?? [])]\n .reverse()\n .find(t => t.content.some(b => b.type === 'compact-summary'))\n const marker = markerTurn?.content.find(b => b.type === 'compact-summary')\n if (markerTurn && marker && marker.type === 'compact-summary')\n broadcast({ kind: 'event', sessionId, event: compactSummaryEvent(marker, markerTurn.id) })\n }\n else {\n // Aborts (run cancelled mid-summary) aren't failures — stay silent.\n const msg = (ctx.error as Error)?.message ?? String(ctx.error)\n if (!/abort/i.test(msg)) {\n broadcast({\n kind: 'event',\n sessionId,\n event: { kind: 'error', text: `Auto-compaction failed: ${msg}` },\n })\n }\n }\n broadcast({\n kind: 'compacting:end',\n sessionId,\n ok: ctx.status === 'success',\n // Post-compact context estimate — drops footer gauges everywhere.\n ...(ctx.status === 'success' && typeof ctx.effectiveTokens === 'number'\n ? { effectiveTokens: ctx.effectiveTokens }\n : {}),\n })\n })\n\n /*\n * Canonical-history repair (provider rejected stale reasoning\n * signatures; the loop scrubbed + persisted). Same info line the\n * local TUI paints — the recovery must be visible, not silent.\n */\n agent.hooks.hook('history:repair', (ctx) => {\n const blocks = `${ctx.removedBlocks} stale reasoning block${ctx.removedBlocks === 1 ? '' : 's'}`\n broadcast({\n kind: 'event',\n sessionId,\n event: {\n kind: 'info',\n text: `🩹 Repaired history: removed ${blocks} the provider rejected${ctx.persisted ? '' : ' (session not yet persisted)'}.`,\n },\n })\n })\n\n /*\n * Run-level model fallback — the dedicated signal drives model pills,\n * the transcript info line makes the switch part of the record.\n */\n agent.hooks.hook('model:fallback', (ctx) => {\n broadcast({ kind: 'model:fallback', sessionId, from: ctx.from, to: ctx.to })\n const reason = (ctx.err as Error)?.message ?? String(ctx.err)\n broadcast({\n kind: 'event',\n sessionId,\n event: {\n kind: 'info',\n text: `model fallback: ${ctx.from.provider}/${ctx.from.model} failed (${reason}), continuing on ${ctx.to.provider}/${ctx.to.model}`,\n },\n })\n })\n\n const run: ActiveRun = {\n sessionId,\n cwd,\n agent,\n controller: new AbortController(),\n providerKey,\n providerDescriptor,\n model,\n ...(modelOverride ? { modelOverride } : {}),\n ledger,\n work,\n chatReferenceTokens,\n warnLog,\n modelFallbacks,\n providerNameByKey,\n systemMd: {\n file: builtInSystem?.systemFile ?? null,\n shadowed: builtInSystem?.shadowedSystemFiles ?? [],\n },\n pendingInjections: [],\n lastUsedAt: Date.now(),\n ...(system ? { system } : {}),\n ...(isExtensionEdit || skillEditMarker\n ? {\n autoActivateSkills: [\n ...(isExtensionEdit ? [ZIDANE_EXTENSION_SKILL_NAME] : []),\n ...(skillEditMarker ? [ZIDANE_SKILL_EDIT_SKILL_NAME] : []),\n ],\n }\n : {}),\n ...(commandSeam ? { commandSeam } : {}),\n ...((builtInSystem?.rulesBlock || builtInSystem?.rulesFiles)\n ? { rules: { rulesBlock: builtInSystem.rulesBlock, rulesFiles: builtInSystem.rulesFiles } }\n : {}),\n }\n\n /*\n * Registered AFTER `run` exists (hooks only fire during runs, long\n * after construction — the ordering is for the linter). Delivery-time\n * user-prompt broadcast for steers/follow-ups: fires when the loop\n * actually injects the message, so every window's live transcript\n * matches the canonical `eventsFromTurns` order (an accept-time\n * broadcast would render steered text before the turn it lands\n * after). Accepted-but-never-delivered injections die with the run.\n */\n agent.hooks.hook('steer:inject', ({ message }) => {\n const index = run.pendingInjections.indexOf(message)\n if (index < 0)\n return\n run.pendingInjections.splice(index, 1)\n broadcast({ kind: 'event', sessionId, echo: true, event: { kind: 'user-prompt', text: message } })\n })\n agent.hooks.hook('agent:done', () => {\n run.pendingInjections.length = 0\n })\n\n return run\n }\n\n /**\n * Sessions whose live agent must be rebuilt at the CURRENT run's end\n * (model/agent swap requested mid-run). See `disposeAgentForRebuild`.\n */\n const pendingDispose = new Set<string>()\n\n /**\n * CROSS-PROCESS run guard probe: the loop persists session-level `status`\n * through the shared store ('running' at run start, settled at run\n * end), so a session being driven by ANOTHER process (an in-process\n * TUI window on the same sessions.db) is visible here. Freshness\n * bound guards against a crashed process leaving 'running' behind\n * forever — a live run keeps `updatedAt` moving via turn persistence.\n *\n * GATED on the shared `useDaemon` setting (read fresh per call): with\n * IPC disabled there is exactly one writer per surface — the pre-IPC\n * assumption — so the guard's only purpose (daemon/multi-process\n * coexistence) doesn't apply, and ungated it would lock the user out of\n * a session for up to `STALE_RUN_MARKER_MS` after a crash mid-run.\n */\n async function foreignRunActive(\n sessionId: string,\n run: ActiveRun,\n internal?: { ownMarkerExempt?: boolean },\n ): Promise<boolean> {\n if (!env.multiProcess && env.readSettings().useDaemon !== true)\n return false\n\n // Header-only read where the store supports it — the guard needs two\n // scalar fields; loading a long session's full turn array per send\n // would make send latency scale with history size.\n const persisted = env.store.getMeta\n ? await env.store.getMeta(sessionId)\n : await env.store.load(sessionId)\n if (persisted?.status !== 'running' || Date.now() - persisted.updatedAt >= STALE_RUN_MARKER_MS)\n return false\n\n /*\n * OWN-marker exemption: a marker whose write time is explained by\n * this host's just-finished run is OURS (settle-lagged heartbeat /\n * loop persistence), not another process's — refusing it would\n * strand queued messages and wake runs behind a misleading error.\n * A FOREIGN run started after ours beats `updatedAt` past the\n * epsilon and is still refused. Truly concurrent local runs are\n * caught by the caller's `isRunning` check.\n */\n const ownRunEnded = run.lastRunEndedAt\n const exemptionAllowed = internal?.ownMarkerExempt !== false\n const explainedByOwnRun\n = exemptionAllowed && ownRunEnded !== undefined && persisted.updatedAt <= ownRunEnded + 2000\n return !explainedByOwnRun\n }\n\n async function sendPrompt(\n opts: SendOptions,\n internal?: { ownMarkerExempt?: boolean, onCommand?: () => void, onForeignRun?: () => void },\n ): Promise<{ runId: string }> {\n assertDispatchOpen()\n if (isSessionBusy(opts.sessionId)) {\n throw new Error(`Session \"${opts.sessionId}\" already has a run in flight. Use steer/followUp (or pushQueued) to feed the active run.`)\n }\n const generation = teardownGeneration\n const finished = Promise.withResolvers<void>()\n dispatches.set(opts.sessionId, finished.promise)\n let settlement: Promise<unknown> | undefined\n let accepted = false\n const signals = { runId: randomUUID() } as { runId: string, end?: Extract<HostSignal, { kind: 'run:end' }> }\n dispatchSignals.set(opts.sessionId, signals)\n let outcome: { ok: boolean, error?: string } = { ok: true }\n const release = () => {\n if (dispatches.get(opts.sessionId) === finished.promise)\n dispatches.delete(opts.sessionId)\n dispatchSignals.delete(opts.sessionId)\n try {\n broadcast({ kind: 'run:end', sessionId: opts.sessionId, runId: signals.runId, ...(signals.end ?? outcome), ...(!outcome.ok ? outcome : {}) })\n }\n finally {\n finished.resolve()\n }\n }\n try {\n const result = await preparePrompt(opts, internal, (completion) => { settlement = completion })\n if (!signals.end)\n signals.runId = result.runId\n accepted = true\n return result\n }\n catch (err) {\n outcome = { ok: false, error: err instanceof Error ? err.message : String(err) }\n throw err\n }\n finally {\n const drain = () => {\n if (!closed && generation === teardownGeneration)\n drainQueue(opts.sessionId)\n }\n if (settlement) {\n void settlement.catch((err) => {\n outcome = { ok: false, error: err instanceof Error ? err.message : String(err) }\n }).finally(release).then(drain).catch(() => undefined)\n }\n else {\n release()\n if (accepted)\n setTimeout(drain, 0)\n }\n }\n }\n\n async function preparePrompt(\n opts: SendOptions,\n /**\n * Host-internal dispatch context. Post-settle callers (queue drain,\n * wake runs — sequenced AFTER the settled status landed on disk) set\n * `ownMarkerExempt: false`: any fresh 'running' marker they see is\n * genuinely foreign, and exempting it would open a double-run window\n * exactly when drains fire (at run end). Direct client sends keep the\n * exemption — they can arrive in the run-end → settle gap where the\n * host's own heartbeat marker is still on disk.\n */\n internal: { ownMarkerExempt?: boolean, onCommand?: () => void, onForeignRun?: () => void } | undefined,\n trackSettlement: (completion: Promise<unknown>) => void,\n ): Promise<{ runId: string }> {\n const generation = teardownGeneration\n const settings = env.readSettings()\n const run = await ensureAgent(opts.sessionId)\n assertDispatchOpen(generation)\n\n /*\n * Multi-window guard: two windows racing `chat.send` on one session\n * must not stack `agent.run()` calls. The loser gets an explicit,\n * actionable error — `pushQueued` is the race-free spelling (steers\n * into the live run instead).\n */\n if (run.agent.isRunning) {\n throw new Error(\n `Session \"${opts.sessionId}\" already has a run in flight. Use steer/followUp (or pushQueued) to feed the active run.`,\n )\n }\n\n // CROSS-PROCESS run guard — see `foreignRunActive` (daemon-mode only).\n if (await foreignRunActive(opts.sessionId, run, internal)) {\n internal?.onForeignRun?.()\n throw new Error(\n `Session \"${opts.sessionId}\" is running in another process (an in-process zidane window?). `\n + `Wait for that run to finish or interrupt it there.`,\n )\n }\n\n /*\n * Un-latch wake suppression once the dispatch passed the guards — any\n * user-initiated dispatch (plain prompt OR /command) re-engages the\n * session, so pending background-task notifications may deliver\n * again. A REFUSED send must not re-arm a wake the user just\n * interrupted away, hence after the guards.\n */\n if (!opts.wake)\n suppressedWake.delete(opts.sessionId)\n\n /*\n * `/` commands run host-side and never reach the model as a user\n * prompt. Routed AFTER the in-flight and cross-process guards — a\n * command appends command turns and can drive full agent sub-runs\n * via ctx.runAgent, so interleaving with a foreign process's run is\n * exactly what the guard exists to prevent — and BEFORE the\n * user-prompt echo (commands emit their own transcript blocks).\n * Return shape stays `{ runId }` (mirrors the GUI's `chat:send`).\n */\n assertDispatchOpen(generation)\n const commandResult = await commands.runSlashCommand(opts)\n assertDispatchOpen(generation)\n if (commandResult.ran) {\n internal?.onCommand?.()\n return { runId: commandResult.runId ?? randomUUID() }\n }\n\n // Declared as a command by the client but unknown here (see\n // `SendOptions.asCommand`). Refusing is the whole point: the fallthrough\n // below would hand the model `/fix comments` as a user message.\n if (opts.asCommand !== undefined) {\n throw new Error(\n `unknown command: /${opts.asCommand}. This session's extensions do not provide it (different working directory, or the extension is not installed for it).`,\n )\n }\n\n /*\n * The user message is part of the shared transcript — broadcast it as\n * soon as the send is accepted (BEFORE the compaction barrier, which\n * can block for minutes). Clients render the prompt from this event\n * rather than appending locally, so the sender's message appears\n * immediately instead of vanishing until the barrier clears.\n */\n if (!opts.wake) {\n broadcast({\n kind: 'event',\n sessionId: opts.sessionId,\n // Send-path ECHO: clients that already render the prompt locally\n // (the GUI's enriched append) drop echo-tagged signals; command\n // module user-prompts are NOT echoes and always render.\n echo: true,\n event: {\n kind: 'user-prompt',\n text: opts.prompt,\n // Attachment metadata (not payloads) so every window renders the\n // same chips the sender's transcript shows. Base64 → raw bytes.\n ...(opts.attachments?.length\n ? {\n attachments: opts.attachments.map(a => ({\n name: a.name ?? 'attachment',\n mediaType: a.mediaType,\n // Exact decoded size from base64 length minus padding.\n size: (a.data.length / 4) * 3 - (a.data.endsWith('==') ? 2 : a.data.endsWith('=') ? 1 : 0),\n })),\n }\n : {}),\n },\n })\n }\n\n /*\n * Wait for any in-flight manual compaction before kicking off the next\n * run, so `agent.run()` sees the post-compaction turn history rather\n * than racing it. Bounded so a stalled post-compact file read can't\n * keep `sendPrompt` from ever reaching `agent.run()` — on timeout we\n * abort the compaction and proceed.\n */\n if (run.compacting) {\n const compacting = run.compacting\n let barrierTimer: ReturnType<typeof setTimeout> | undefined\n const timedOut = await Promise.race([\n compacting.then(\n () => false,\n () => false, // errors already surfaced through the stream; proceed.\n ),\n new Promise<boolean>((resolve) => {\n barrierTimer = setTimeout(resolve, COMPACTION_BARRIER_TIMEOUT_MS, true)\n }),\n ])\n if (barrierTimer)\n clearTimeout(barrierTimer)\n if (timedOut) {\n run.compactionController?.abort()\n broadcast({\n kind: 'event',\n sessionId: opts.sessionId,\n event: {\n kind: 'error',\n text: `Compaction timed out after ${COMPACTION_BARRIER_TIMEOUT_MS}ms; continuing with your prompt.`,\n },\n })\n }\n }\n\n /*\n * Re-assert host-driven skill activation before each run. The agent's\n * run-end pass deactivates everything, so an edit/create-extension\n * session's docs skill must be re-activated on every send. Best-effort\n * + idempotent — a resolution hiccup must not abort the run.\n */\n /*\n * Force-activate BOTH the session's auto-activated skills and the\n * caller's pinned/referenced ones. This is gate-relevant, not just a\n * nudge: a pinned `read-only` skill's allowed-tools union only holds\n * if the activation bit is actually set before the run — the same\n * invariant the local TUI's `runSingleMessage` protects.\n */\n for (const name of new Set([...(run.autoActivateSkills ?? []), ...(opts.activatedSkills ?? [])])) {\n try {\n await run.agent.activateSkill(name)\n }\n catch {\n // skill not resolvable (disabled / unknown) — fall back to the nudge\n }\n }\n\n const runId = randomUUID()\n\n /*\n * Skills picked via `/` completion plus session-auto-activated skills:\n * nudge the model to load them via `skills_use` before responding —\n * `agent.activateSkill` flips the activation bit but doesn't make the\n * body visible to the model.\n */\n const requestedSkills = Array.from(\n new Set([...(opts.activatedSkills ?? []), ...(run.autoActivateSkills ?? [])]),\n )\n\n const systemNudge\n = requestedSkills.length > 0\n ? `The user invoked the following skill(s) via the \\`/<name>\\` syntax: ${requestedSkills\n .map(n => `\\`${n}\\``)\n .join(', ')}.\\nCall \\`skills_use({ name: ... })\\` for each one before responding, then follow the returned instructions strictly when composing your reply.`\n : undefined\n\n /*\n * Per-session reasoning effort + model override + custom model options.\n * Only forwarded when explicitly set — absent means \"let the agent /\n * provider pick its own default\".\n */\n let thinking: ThinkingLevel | undefined\n const modelOverride = run.modelOverride\n let modelOptions: Record<string, boolean> | undefined\n\n try {\n const data = await env.store.load(opts.sessionId)\n const rawEffort = data?.metadata?.effort\n\n if (typeof rawEffort === 'string' && isThinkingLevel(rawEffort))\n thinking = rawEffort\n\n const descriptor = run.providerDescriptor ?? BUILTIN_PROVIDERS.anthropic\n const modelId = modelOverride ?? run.model\n const enabled = enabledModelOptions(data?.metadata?.modelOptions)\n modelOptions = restoreModelOptions(descriptor, modelId, { [modelId]: enabled })\n }\n catch {\n // Reading metadata is best-effort; absence just means defaults.\n }\n\n /*\n * Build the canonical prompt. With attachments, `prompt` becomes a\n * `PromptPart[]`; text-only sends keep the plain-string fast path.\n */\n const promptValue: string | PromptPart[]\n = opts.attachments && opts.attachments.length > 0\n ? [\n ...(opts.prompt.length > 0 ? [{ type: 'text' as const, text: opts.prompt }] : []),\n ...opts.attachments.map<PromptPart>(a =>\n a.mediaType.startsWith('image/')\n ? {\n type: 'image',\n mediaType: a.mediaType,\n data: a.data,\n ...(a.name ? { name: a.name } : {}),\n }\n : {\n type: 'document',\n mediaType: a.mediaType,\n data: a.data,\n encoding: 'base64',\n ...(a.name ? { name: a.name } : {}),\n },\n ),\n ]\n : opts.prompt\n\n // Loop-native auto-compaction config for this run: honor the user's\n // enable/threshold settings and inject the picked model's context window.\n const autoCompactDescriptor = BUILTIN_PROVIDERS[run.providerKey] ?? BUILTIN_PROVIDERS.anthropic\n /*\n * Dedicated compaction summarizer (cheap-model summaries) — resolved\n * per run against the session's runtime providers so provider\n * removal degrades silently to the main model.\n */\n const sendRuntime = await env.runtimeConfig(run.cwd)\n assertDispatchOpen(generation)\n const summarizer = resolveCompactionSummarizer(\n settings.compactionModel,\n key => buildProvider(sendRuntime.providers, key),\n { providerKey: run.providerKey, model: modelOverride ?? run.model },\n )\n const autoCompactRun = chatAutoCompactBehavior({\n enabled: settings.autoCompact,\n threshold: settings.autoCompactThreshold,\n rawContextWindow: getContextWindow(autoCompactDescriptor, modelOverride ?? run.model),\n ...(summarizer ? { summarizer } : {}),\n })\n\n /*\n * Settings-driven run ceilings — the SAME caps the local TUI applies\n * (`runCaps`): max turns, USD budget, token/wall-clock ceilings,\n * provider retry budget. Read fresh per run; zero means \"unbounded /\n * library default\" and passes nothing. Shared with command-driven\n * sub-runs (`runSlashCommand`'s `resolveRunSettings`).\n */\n const caps = runCapsFromSettings(settings)\n\n run.chatReferenceTokens.current = chatReferenceTokensForPrompt(opts.prompt)\n\n /*\n * Fresh AbortController PER RUN: `interrupt()` aborts the current\n * controller, and an aborted controller can never be un-aborted — if\n * the build-time controller were reused, one esc would leave every\n * subsequent send on this session born-aborted (instant\n * run:start/run:end) until the agent happened to be rebuilt.\n */\n run.controller = new AbortController()\n\n // Keep the cross-process run marker fresh through long quiet tool\n // calls (no-op when `useDaemon` is off — see startRunHeartbeat for the\n // straggler-settle contract shared with command sub-runs).\n const heartbeat = startRunHeartbeat({\n env,\n sessionId: opts.sessionId,\n isRunning: () => run.agent.isRunning,\n })\n\n /*\n * Extension tools and hooks that fire during the run reach\n * `ctx.workspace` / `ctx.runtime` through the host-runtime bridge; scope\n * THIS session's impl around the run so they resolve to it (see\n * `sessionHostRuntime`). Without a bridge (bare harness) the surfaces\n * stay detached, as before.\n */\n const runAgent = (): Promise<unknown> => run.agent\n .run({\n prompt: promptValue,\n system: systemNudge && run.system ? `${run.system}\\n\\n${systemNudge}` : systemNudge,\n signal: run.controller.signal,\n behavior: { autoCompact: autoCompactRun, ...caps },\n // Attribute the Rules bucket only when the agent's composed system\n // is actually in use — a `systemNudge` override replaces it.\n ...(run.rules && !systemNudge ? { contextSections: run.rules } : {}),\n\n /*\n * Only forward `model` when the session has an explicit override —\n * forwarding the provider default every run would shadow the\n * agent's own per-turn model-selection logic. With a fallback\n * chain, expand into pinned {provider, model} targets WITH\n * per-target context windows so the loop re-bases the\n * auto-compact trigger when it advances down the chain.\n */\n ...(modelOverride\n ? {\n model: run.modelFallbacks.length > 0\n ? buildRunModel(\n [{ provider: run.providerKey, model: modelOverride }, ...run.modelFallbacks],\n k => run.providerNameByKey.get(k),\n (entry) => {\n const d = sendRuntime.providers[entry.provider]\n return d ? (getContextWindow(d, entry.model) ?? undefined) : undefined\n },\n )\n : modelOverride,\n }\n : {}),\n ...(thinking ? { thinking } : {}),\n ...(modelOptions ? { modelOptions } : {}),\n })\n broadcast({ kind: 'run:start', sessionId: opts.sessionId, runId })\n broadcastTodosSnapshot(run)\n const scopedRun = Promise.resolve().then(() => {\n assertDispatchOpen(generation)\n return env.hostRuntime\n ? env.hostRuntime.runWith(sessionHostRuntime(run, env, { appendNote: blocks => appendNoteTurn(run, blocks) }), runAgent)\n : runAgent()\n })\n let outcome: { ok: boolean, error?: string } = { ok: true }\n trackSettlement(scopedRun\n .catch((err) => {\n outcome = { ok: false, error: err instanceof Error ? err.message : String(err) }\n })\n .then(async () => {\n run.lastRunEndedAt = Date.now()\n // Capture status before a deferred rebuild destroys the live agent.\n const settle = heartbeat.end(run.agent.session?.status)\n try {\n try {\n broadcastRunReceipt(run)\n }\n finally {\n run.chatReferenceTokens.current = []\n run.lastUsedAt = Date.now()\n // Apply deferred model changes before draining on the fresh agent.\n if (pendingDispose.delete(opts.sessionId))\n rebuildAgentOnly(opts.sessionId)\n pruneIdleAgents()\n }\n }\n finally {\n // Receipt/cleanup errors must not skip the final marker write.\n await settle\n }\n broadcast({ kind: 'run:end', sessionId: opts.sessionId, runId, ...outcome })\n broadcastTodosSnapshot(run)\n }))\n\n return { runId }\n }\n\n /**\n * Tear down ONLY the live agent (not HITL queues / pending builds), so\n * the next `ensureAgent` rebuilds it against freshly-patched session\n * metadata. Used by the model/agent swap path — HITL state and the\n * type-ahead queue survive the swap.\n */\n function rebuildAgentOnly(sessionId: string): void {\n // Consume any in-flight `ensureAgent` build FIRST (same contract as\n // `disposeSession`) — a build racing the swap would otherwise insert\n // an agent built against pre-patch session metadata.\n pendingBuilds.delete(sessionId)\n const entry = active.get(sessionId)\n if (!entry)\n return\n disposeActiveEntry(sessionId, entry)\n }\n\n /**\n * Apply a model/agent swap: patch already landed on session metadata;\n * now retire the live agent so the next run rebuilds on it. Deferred to\n * run end when a run is in flight — disposing mid-run would abort it for\n * EVERY window, diverging from the local TUI (which rebuilds at run end\n * via `pendingAgentRebuildRef`).\n */\n function disposeAgentForRebuild(sessionId: string): void {\n if (isSessionBusy(sessionId))\n pendingDispose.add(sessionId)\n else\n rebuildAgentOnly(sessionId)\n }\n\n /* ----------------------------------------------------------------------\n * Shared type-ahead queue\n * -------------------------------------------------------------------- */\n\n interface QueuedEntry extends QueuedItem {\n activatedSkills?: string[]\n attachments?: PromptAttachment[]\n /** See `SendOptions.asCommand` — survives the queue so the drain refuses too. */\n asCommand?: string\n }\n\n const queues = new Map<string, QueuedEntry[]>()\n\n /** Wire shape of a queued entry: text plus an attachment SUMMARY, never the bytes. */\n function toQueuedItem({ id, text, attachments }: QueuedEntry): QueuedItem {\n return {\n id,\n text,\n ...(attachments?.length\n ? { attachments: attachments.map(a => ({ mediaType: a.mediaType, ...(a.name ? { name: a.name } : {}) })) }\n : {}),\n }\n }\n\n function broadcastQueue(sessionId: string): void {\n broadcast({ kind: 'queue:changed', sessionId, items: (queues.get(sessionId) ?? []).map(toQueuedItem) })\n }\n\n /** Per-session drain lock — two overlapping drains would double-send the head. */\n const draining = new Set<string>()\n\n /**\n * FIFO auto-drain at run end — the host owns delivery so a queued\n * message runs even after the window that queued it closed. The head is\n * PEEKED (not shifted) and only removed after `sendPrompt` accepted it,\n * so a failed dispatch leaves the entry queued for retry/drop. The\n * short retry loop covers the gap between the run-end broadcast and the\n * agent's `isRunning` flag flipping; chained multi-entry queues drain\n * one-per-run via `sendPrompt`'s own run-end hook calling back in here.\n */\n /**\n * Background-task notification delivery: when a session is idle with\n * pending `<task-notification>`s (a task finished mid-run, or while\n * idle), run a wake pass so the model sees the result now instead of on\n * the user's next prompt. Host-owned, so it happens even with zero\n * windows attached. Wake runs carry an empty prompt and skip the\n * `user-prompt` broadcast — there is no user message.\n */\n function maybeWake(sessionId: string): void {\n const entry = active.get(sessionId)\n if (closed || !entry || isSessionBusy(sessionId) || !entry.agent.hasPendingTaskNotifications)\n return\n void wakeRun(sessionId).catch(() => undefined)\n }\n\n /**\n * The tail every host-initiated wake shares — background-task notifications\n * and orchestration's buffered `<session-event>` turns alike. The three\n * guards are the point:\n * - `suppressedWake`: the user pressed esc; do not start a run on their\n * behalf until they send again.\n * - a non-empty type-ahead queue: let the drain dispatch instead, or the\n * wake and the drain both send.\n * - `ownMarkerExempt: false`: wakes fire AT run end, exactly when a stale\n * 'running' marker of our own is most likely to still be on disk —\n * exempting it there would open a double-run window.\n * Resolves either way; the caller cannot tell a declined wake from a run,\n * because in both cases the turns it appended are already durable.\n */\n async function wakeRun(sessionId: string): Promise<void> {\n if (closed || suppressedWake.has(sessionId))\n return\n if ((queues.get(sessionId)?.length ?? 0) > 0)\n return\n await sendPrompt({ sessionId, prompt: '', wake: true }, { ownMarkerExempt: false })\n }\n\n function drainQueue(sessionId: string, attempt = 0): void {\n if (closed || draining.has(sessionId))\n return\n const queue = queues.get(sessionId)\n if (!queue || queue.length === 0) {\n maybeWake(sessionId)\n return\n }\n if (isSessionBusy(sessionId)) {\n if (attempt < 100)\n setTimeout(drainQueue, 25, sessionId, attempt + 1)\n return\n }\n\n const next = queue[0]!\n draining.add(sessionId)\n\n void sendPrompt({\n sessionId,\n prompt: next.text,\n ...(next.activatedSkills?.length ? { activatedSkills: next.activatedSkills } : {}),\n ...(next.attachments?.length ? { attachments: next.attachments } : {}),\n ...(next.asCommand ? { asCommand: next.asCommand } : {}),\n }, { ownMarkerExempt: false })\n .then(() => {\n const live = queues.get(sessionId)\n const index = live?.findIndex(item => item.id === next.id) ?? -1\n if (live && index >= 0) {\n live.splice(index, 1)\n if (live.length === 0)\n queues.delete(sessionId)\n }\n broadcastQueue(sessionId)\n })\n .catch((err) => {\n broadcast({\n kind: 'event',\n sessionId,\n event: { kind: 'error', text: `Queued message failed to send: ${(err as Error).message}` },\n })\n })\n .finally(() => {\n draining.delete(sessionId)\n })\n }\n\n const host: ChatHost = {\n interactions,\n approvals,\n sendPrompt,\n listCommands: commands.listCommands,\n completeCommandArgs: commands.completeCommandArgs,\n\n interrupt: interruptSession,\n steer: steerSession,\n\n followUp(sessionId, message) {\n const entry = active.get(sessionId)\n if (entry?.agent.followUp(message))\n entry.pendingInjections.push(message)\n },\n\n async pushQueued(sessionId, message, activatedSkills) {\n assertDispatchOpen()\n const entry = active.get(sessionId)\n if (entry?.agent.isRunning === true) {\n // Delivery-time broadcast via `steer:inject` — see steer().\n // Command routing doesn't apply mid-run (the TUI steers literal\n // text too).\n if (entry.agent.steer(message))\n entry.pendingInjections.push(message)\n return { delivered: 'steer' as const }\n }\n\n const parkQueued = (): { delivered: 'queued' } => {\n const queue = queues.get(sessionId) ?? []\n queue.push({\n id: randomUUID(),\n text: message,\n ...(activatedSkills?.length ? { activatedSkills } : {}),\n })\n queues.set(sessionId, queue)\n broadcastQueue(sessionId)\n return { delivered: 'queued' as const }\n }\n\n if (isSessionBusy(sessionId))\n return parkQueued()\n\n let command = false\n let foreign = false\n try {\n await sendPrompt({\n sessionId,\n prompt: message,\n ...(activatedSkills ? { activatedSkills } : {}),\n }, { onCommand: () => { command = true }, onForeignRun: () => { foreign = true } })\n return { delivered: command ? 'command' as const : 'send' as const }\n }\n catch (err) {\n /*\n * Race: another window started a run between the `isRunning` check\n * above and `sendPrompt`'s own in-flight guard. `pushQueued` is the\n * race-free spelling, so park behind its preparation/settlement\n * reservation rather than surfacing the hard error.\n */\n assertDispatchOpen()\n if (foreign || isSessionBusy(sessionId))\n return parkQueued()\n throw err\n }\n },\n\n isSessionRunning(sessionId) {\n const entry = active.get(sessionId)\n if (entry)\n entry.lastUsedAt = Date.now()\n return isSessionBusy(sessionId)\n },\n\n listLiveSessions() {\n return [...new Set([...active.keys(), ...dispatches.keys()])].map(sessionId => ({\n sessionId,\n running: isSessionBusy(sessionId),\n }))\n },\n\n getLiveTurns(sessionId) {\n return active.get(sessionId)?.agent.session?.turns ?? null\n },\n\n getLiveSession(sessionId) {\n return active.get(sessionId)?.agent.session ?? null\n },\n\n async queueAdd(sessionId, text, activatedSkills, attachments, asCommand) {\n assertDispatchOpen()\n const enqueue = (): { delivered: 'queued' } => {\n const queue = queues.get(sessionId) ?? []\n queue.push({\n id: randomUUID(),\n text,\n ...(activatedSkills?.length ? { activatedSkills } : {}),\n ...(attachments?.length ? { attachments } : {}),\n ...(asCommand ? { asCommand } : {}),\n })\n queues.set(sessionId, queue)\n broadcastQueue(sessionId)\n return { delivered: 'queued' as const }\n }\n\n if (isSessionBusy(sessionId))\n return enqueue()\n\n try {\n await sendPrompt({\n sessionId,\n prompt: text,\n ...(activatedSkills?.length ? { activatedSkills } : {}),\n ...(attachments?.length ? { attachments } : {}),\n ...(asCommand ? { asCommand } : {}),\n })\n return { delivered: 'sent' as const }\n }\n catch (err) {\n // Race: another window started a run between the check and the\n // send — park it in the queue instead of surfacing the error.\n assertDispatchOpen()\n if (isSessionBusy(sessionId))\n return enqueue()\n throw err\n }\n },\n\n queueRemove(sessionId, id) {\n const queue = queues.get(sessionId)\n if (!queue)\n return false\n const index = queue.findIndex(item => item.id === id)\n if (index < 0)\n return false\n queue.splice(index, 1)\n if (queue.length === 0)\n queues.delete(sessionId)\n broadcastQueue(sessionId)\n return true\n },\n\n queueList(sessionId) {\n return (queues.get(sessionId) ?? []).map(toQueuedItem)\n },\n\n queueUpdate(sessionId, id, text) {\n const entry = queues.get(sessionId)?.find(item => item.id === id)\n if (!entry)\n return false\n if (entry.text !== text) {\n entry.text = text\n broadcastQueue(sessionId)\n }\n return true\n },\n\n sessionSystemMd(sessionId) {\n return active.get(sessionId)?.systemMd ?? null\n },\n\n async queuePushNow(sessionId, id) {\n assertDispatchOpen()\n const queue = queues.get(sessionId)\n const entry = queue?.find(item => item.id === id)\n if (!queue || !entry)\n return { delivered: 'missing' as const }\n\n /*\n * PEEK, dispatch, and only remove after the dispatch was ACCEPTED —\n * same contract as drainQueue. Splicing first would lose the\n * message from every window's queue box when the idle-path\n * `sendPrompt` throws (cross-process guard, provider failure).\n */\n const removeAndBroadcast = (): void => {\n const live = queues.get(sessionId)\n const index = live?.findIndex(item => item.id === id) ?? -1\n if (live && index >= 0) {\n live.splice(index, 1)\n if (live.length === 0)\n queues.delete(sessionId)\n }\n broadcastQueue(sessionId)\n }\n\n const live = active.get(sessionId)\n if (live?.agent.isRunning === true && live.agent.steer(entry.text)) {\n live.pendingInjections.push(entry.text)\n removeAndBroadcast()\n return { delivered: 'steer' as const }\n }\n await sendPrompt({\n sessionId,\n prompt: entry.text,\n ...(entry.activatedSkills?.length ? { activatedSkills: entry.activatedSkills } : {}),\n ...(entry.attachments?.length ? { attachments: entry.attachments } : {}),\n })\n removeAndBroadcast()\n return { delivered: 'send' as const }\n },\n\n hasActiveWork() {\n if (dispatches.size > 0 || lifecycleWork.size > 0)\n return true\n for (const entry of active.values()) {\n // `commandAbort` set = a `/` command is mid-flight — between its\n // sub-runs nothing else reads as active, and an idle-exit would\n // kill the command host mid-workflow.\n if (entry.agent.isRunning || entry.compacting || entry.commandAbort !== undefined || entry.work.size > 0)\n return true\n }\n return false\n },\n\n listRunningWork(sessionId) {\n const entry = active.get(sessionId)\n if (entry)\n entry.lastUsedAt = Date.now()\n return entry ? visibleWorkItems(entry) : []\n },\n\n async cancelRunningWork(sessionId, id, reason) {\n const entry = active.get(sessionId)\n if (!entry)\n return false\n entry.lastUsedAt = Date.now()\n\n const targets = id === 'all' ? visibleWorkItems(entry) : visibleWorkItems(entry).filter(item => item.id === id)\n let cancelled = false\n\n for (const item of targets) {\n if (item.kind === 'task')\n cancelled = (await entry.agent.killBackgroundTask(item.id)) || cancelled\n else\n cancelled = entry.agent.cancelTool(item.id, reason ?? 'user-cancelled-tool') || cancelled\n }\n\n return cancelled\n },\n\n /**\n * Initial fetch for todos indicators. `null` when no agent is live —\n * the slot may exist on disk but `pickActiveRunId` needs the in-memory\n * `Session.runs` list to choose it; the `run:start` snapshot broadcast\n * catches clients up after the first send.\n */\n getTodosSnapshot(sessionId) {\n const entry = active.get(sessionId)\n if (!entry)\n return null\n entry.lastUsedAt = Date.now()\n const session = entry.agent.session\n return session ? selectActiveTodos(session) : null\n },\n\n /**\n * Apply a user todo edit to the session that the model actually reads.\n *\n * The invariant this restores: **whichever process owns the agent owns the\n * todo bag.** Todos live in `session.metadata`, and `todowrite` reads them\n * off `entry.agent.session` — so a window that called `setTodosForRun` on\n * its OWN `Session` instance wrote behind the daemon's back. The edit\n * persisted, rendered, and looked applied, then the model's next\n * `todowrite` read its stale in-memory bag and overwrote it. The user's\n * edit vanished and the model never learned it happened.\n *\n * Same live-vs-idle split as {@link patchLiveSessionMeta}, for the same\n * reason: routing through the live `Session` keeps `save()` re-persisting\n * the CURRENT turns instead of a stale snapshot.\n *\n * Returns the resulting snapshot when handled here, or `null` when no\n * agent is live and the caller should apply its own store-level write\n * (race-free, since nothing is appending turns).\n *\n * The run id is resolved HERE rather than accepted from the caller:\n * `pickActiveRunId` needs the in-memory `Session.runs` list, and a\n * window-supplied id could already be stale by the time it arrives.\n */\n async setTodos(sessionId, items) {\n const entry = active.get(sessionId)\n const session = entry?.agent.session\n if (!session)\n return null\n entry!.lastUsedAt = Date.now()\n const runId = selectActiveTodos(session).runId\n if (!runId)\n return null\n /*\n * Validate before writing. This is the LEAST trusted todo boundary —\n * anything that can reach the socket can call it — and it was the only\n * one left unguarded: `setTodosForRun`'s `normalizeItem` picks id /\n * content / status without checking them, so an invalid `status` was\n * written and then silently dropped by the read validation, making the\n * edit vanish with nothing reported. Same validator and same cap as the\n * model's `todowrite`, so a user edit cannot do what the tool cannot.\n */\n const sanitized = sanitizeTodoItems(items)\n /*\n * A caller that sent rows and had ALL of them rejected is sending\n * malformed data — writing the empty result would delete the user's list\n * on their behalf. No-op, returning current state so the caller sees its\n * edit didn't apply (distinct from `null`, which means \"no live agent,\n * fall back to a store write\"). An intentional clear sends `[]` and still\n * works, because nothing was rejected.\n */\n if (items.length > 0 && sanitized.length === 0)\n return selectActiveTodos(session)\n // `clearArchive` because every write arriving here is user-initiated —\n // matching the modal's local path, where keeping the archive makes a\n // confirmed deletion re-render as a no-op.\n setTodosForRun(session, runId, sanitized, { clearArchive: true })\n await session.save()\n // Fans out to every attached window, including the one that sent this.\n broadcastTodosSnapshot(entry!)\n return selectActiveTodos(session)\n },\n\n createSession: createSessionInStore,\n orchestration,\n\n /**\n * Metadata-only patch that is safe to run mid-run. When a live agent is\n * hosting the session, route the write through its in-memory `Session`\n * (whose turn history is the source of truth) so the full-document\n * `save()` re-persists the CURRENT turns instead of a stale snapshot —\n * a store-level `store.save(load())` would wipe turns appended between\n * load and save. When idle, no run is appending, so the caller's\n * store-level fallback is race-free.\n *\n * Returns `true` when handled here (live session), `false` when the\n * caller should apply its own store-level patch.\n */\n async patchLiveSessionMeta(sessionId, patch) {\n const entry = active.get(sessionId)\n const session = entry?.agent.session\n if (!session)\n return false\n entry!.lastUsedAt = Date.now()\n for (const [key, value] of Object.entries(patch))\n session.setMeta(key, value)\n await session.save()\n return true\n },\n\n async patchSessionMeta(sessionId, patch, opts = {}) {\n if (await host.patchLiveSessionMeta(sessionId, patch))\n return\n // Idle path: no run is appending, so load → mutate → save is race-free.\n const data = await env.store.load(sessionId)\n if (!data)\n throw new Error(`Session \"${sessionId}\" not found.`)\n for (const [key, value] of Object.entries(patch)) {\n if (value === undefined)\n delete data.metadata[key]\n else\n data.metadata[key] = value\n }\n if (opts.bumpUpdatedAt !== false)\n data.updatedAt = Date.now()\n await env.store.save(data)\n },\n\n async setSessionModel(sessionId, pick) {\n /*\n * Patch FIRST (race-safe), then retire the agent: the rebuild reads\n * the persisted document, so the order guarantees the next agent sees\n * the new pick. `undefined` clears — effort and options are per-model\n * knobs and `modelChain` is replaced wholesale, never merged.\n */\n await host.patchSessionMeta(sessionId, {\n providerKey: pick.providerKey,\n modelId: pick.modelId,\n modelChain: modelChainMetadata(pick.providerKey, pick.modelId, pick.fallbacks),\n effort: pick.effort,\n modelOptions: pick.modelOptions && Object.keys(pick.modelOptions).length > 0\n ? enabledModelOptions(pick.modelOptions)\n : undefined,\n })\n disposeAgentForRebuild(sessionId)\n },\n\n async setLiveSessionAgentId(sessionId, agentId) {\n const entry = active.get(sessionId)\n const session = entry?.agent.session\n if (!session)\n return false\n entry!.lastUsedAt = Date.now()\n session.setAgentId(agentId)\n await session.save()\n return true\n },\n\n disposeAgentForRebuild(sessionId) {\n disposeAgentForRebuild(sessionId)\n },\n\n /**\n * On-demand context-usage breakdown for the session's live agent.\n * Materializes the agent if needed so the breakdown is available before\n * the first message.\n */\n async getContextBreakdown(sessionId) {\n let entry = active.get(sessionId)\n if (!entry) {\n try {\n entry = await ensureAgent(sessionId)\n }\n catch {\n return null\n }\n }\n entry.lastUsedAt = Date.now()\n\n const modelId = entry.model\n const descriptor = entry.providerDescriptor ?? BUILTIN_PROVIDERS.anthropic\n const effWindow = effectiveContextWindow(getContextWindow(descriptor, modelId))\n\n const settings = env.readSettings()\n return entry.agent.getContextBreakdown({\n model: modelId,\n ...(effWindow !== null ? { effectiveWindow: effWindow } : {}),\n autocompactBuffer: OUTPUT_RESERVE_TOKENS,\n ...(settings.autoCompact && settings.autoCompactThreshold\n ? { compactThreshold: settings.autoCompactThreshold }\n : {}),\n })\n },\n\n /**\n * Manual \"compact now\" — runs the same compaction the auto-compact\n * policy would fire, but unconditionally. No-op error when the session\n * isn't currently tracked (compaction needs an `Agent`-bound provider).\n */\n async getContextSegments(sessionId) {\n let entry = active.get(sessionId)\n if (!entry) {\n try {\n entry = await ensureAgent(sessionId)\n }\n catch {\n return null\n }\n }\n entry.lastUsedAt = Date.now()\n\n const segments = await entry.agent.getContextSegments({\n ...(entry.rules?.rulesFiles ? { rulesFiles: entry.rules.rulesFiles } : {}),\n ...(entry.rules?.rulesBlock ? { rulesBlock: entry.rules.rulesBlock } : {}),\n ...(entry.systemMd.file ? { systemPath: entry.systemMd.file.path } : {}),\n })\n\n /*\n * Two independent reasons an override cannot apply, named separately\n * because the fix differs: the profile supplies its own prompt (only the\n * built-in `build` / `plan` accept `SYSTEM.md`), or `userInstructionsScope`\n * excludes user-scope files. Profile is reported first — it is the more\n * fundamental of the two. Same derivation as the TUI's `getContextSegments`.\n */\n const agentId = entry.agent.session?.agentId\n const supportsOverride = agentId === 'build' || agentId === 'plan'\n const scopeLoadsUserOverride = userScopeOverridesLoad(env.readSettings().userInstructionsScope)\n const canOverrideSystem = supportsOverride && scopeLoadsUserOverride\n const overrideBlockedBy = !supportsOverride\n ? 'profile' as const\n : !scopeLoadsUserOverride ? 'scope' as const : undefined\n const shadowedSystemPath = entry.systemMd.shadowed[0]?.path\n\n return {\n segments: segments ?? [],\n live: true,\n canOverrideSystem,\n ...(overrideBlockedBy ? { overrideBlockedBy } : {}),\n ...(shadowedSystemPath ? { shadowedSystemPath } : {}),\n }\n },\n\n async createSystemOverride(sessionId) {\n const entry = await ensureAgent(sessionId)\n const settings = env.readSettings()\n const agentId = entry.agent.session?.agentId\n /*\n * Refuse rather than write a doctrine-free file. On a host-supplied\n * profile there is no built-in prompt to seed from; the result would be a\n * comment-only override — still VALID, so switching to build/plan later\n * would silently reduce the entire doctrine to an HTML comment. Same\n * silent-no-op guard on the scope axis: under `'project'` / `'none'` the\n * file would be written, edited, and never read. Same two guards, same\n * wording, as the TUI's `onCreateSystemPrompt`.\n */\n if (agentId !== 'build' && agentId !== 'plan')\n throw new Error(`the \"${agentId ?? 'unknown'}\" profile supplies its own system prompt, so a SYSTEM.md override would not apply`)\n if (!userScopeOverridesLoad(settings.userInstructionsScope))\n throw new Error(`userInstructionsScope is \"${settings.userInstructionsScope ?? 'both'}\", so a SYSTEM.md in your home directory would not be loaded`)\n\n const runtimeConfig = await env.runtimeConfig(entry.cwd)\n const path = userSystemMdPath({ prefix: runtimeConfig.prefix })\n if (existsSync(path))\n return { path, created: false }\n\n /*\n * Seed from the DOCTRINE-ONLY prompt — built without `userInstructions`\n * and without an override. The live `system` already has the AGENTS.md\n * block folded in, and an override composes ABOVE a freshly-appended copy\n * of that block, so seeding from it would duplicate every instruction on\n * the next session.\n */\n const allowInteraction = settings.interactionMode !== 'disabled'\n const doctrineOnly = agentId === 'plan'\n ? buildPlanSystem({ cwd: entry.cwd, allowInteraction })\n : buildBuildSystem({ cwd: entry.cwd, allowInteraction })\n const doctrine = splitSystemPrompt(doctrineOnly).static\n if (doctrine.trim().length === 0)\n throw new Error('could not compose a system prompt to seed the override from')\n\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, renderSystemMdSeed(doctrine, settings.userInstructionsScope), 'utf8')\n return { path, created: true }\n },\n\n async compactSessionNow(sessionId) {\n const entry = active.get(sessionId)\n if (!entry) {\n throw new Error(\n `Session \"${sessionId}\" must be opened (send at least one prompt) before it can be compacted manually.`,\n )\n }\n\n // Already in flight (auto-compact racing the click) — just await it.\n if (entry.compacting) {\n await entry.compacting\n return\n }\n\n entry.compacting = runCompaction(entry, broadcast, env.store).finally(() => {\n const live = active.get(sessionId)\n if (live && live.compacting === entry.compacting)\n live.compacting = undefined\n })\n await entry.compacting\n },\n\n /**\n * Tear down a single in-flight session — used when the user deletes the\n * session or changes its model. Sweeps pending interactions + safe-mode\n * requests even without a live agent so no ghost HITL state lingers.\n */\n disposeSession(sessionId) {\n interactions.clearSession(sessionId)\n approvals.denyAll(sessionId)\n suppressedWake.delete(sessionId)\n if (queues.delete(sessionId))\n broadcastQueue(sessionId)\n\n /*\n * Consume any in-flight build — when it completes it sees its pending\n * entry gone and self-discards instead of inserting an agent for a\n * session the user just tore down.\n */\n pendingBuilds.delete(sessionId)\n\n const entry = active.get(sessionId)\n if (!entry)\n return\n\n disposeActiveEntry(sessionId, entry)\n },\n\n /**\n * Drain every session. Call before process exit (or a settings change\n * that invalidates the runtime config) so active runs, in-flight\n * compactions, and pending HITL promises all unwind cleanly.\n */\n drain() {\n if (!drainPromise) {\n closed = true\n const compactions = [...active.values()].flatMap(entry => entry.compacting ? [entry.compacting] : [])\n host.shutdown()\n drainPromise = (async () => {\n await Promise.allSettled(compactions)\n while (dispatches.size > 0 || lifecycleWork.size > 0)\n await Promise.allSettled([...dispatches.values(), ...lifecycleWork])\n })()\n }\n return drainPromise\n },\n\n shutdown() {\n suppressedWake.clear()\n /*\n * Discovery caches die with the agents that were built from them, so a\n * settings change that tears the host down (extensions, plugins, MCPs)\n * re-walks the disk on the next build instead of serving a catalog that\n * predates the toggle.\n */\n pluginCatalogCache.clear()\n for (const sessionId of [...queues.keys()]) {\n queues.delete(sessionId)\n broadcastQueue(sessionId)\n }\n /*\n * Invalidate every in-flight build: a build that started before this\n * teardown discards itself on completion.\n */\n teardownGeneration += 1\n pendingBuilds.clear()\n\n approvals.denyAll()\n\n for (const [sessionId, entry] of active) {\n teardownRun(entry)\n interactions.clearSession(sessionId)\n }\n active.clear()\n },\n }\n return host\n}\n","/**\n * Interactions — plan approvals and Q&A clarifications between the agent\n * and the user.\n *\n * Renderer-agnostic by design: this module owns the tool factories, the\n * pending-state derivation from `session.turns`, the response serializer,\n * and a tiny React queue context. The TUI / GUI consume the queue and\n * provide the actual picker UI; the same data plumbing works for both.\n *\n * Persistence model — the call IS the persisted state:\n *\n * - The agent emits `present_plan` / `ask_user` as a regular tool call.\n * The `tool_call` block lands in `session.turns` like any other tool.\n * When the user responds, the corresponding `tool_result` block follows\n * in the next user turn. Standard tool-call/tool-result contract — no\n * parallel store, no extra schema.\n *\n * - \"Pending\" = the trailing assistant turn has a `tool_call` to one of\n * the interaction tools with no matching `tool_result` in a later\n * user turn. Derived at load time via\n * {@link pendingInteractionsFromTurns}; no parallel bookkeeping.\n *\n * - Resumed sessions land in the same UI as live ones. The host only\n * swaps the resolver:\n * - **Live**: the tool's `execute()` is awaiting a Promise; the\n * resolver resolves that Promise. The agent loop persists the\n * tool_result naturally on its next iteration.\n * - **Resumed**: no Promise exists (the prior run was destroyed).\n * The resolver appends a synthetic user turn with the tool_result\n * block and triggers a follow-up `agent.run()` so the model\n * continues from where it left off.\n *\n * No OpenTUI imports — a GUI consumer can ship its own renderer and reuse\n * this module verbatim.\n */\nimport type { ReactNode } from 'react'\nimport type { ToolDef } from '../tools/types'\nimport type { SessionContentBlock, SessionTurn } from '../types'\nimport { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'\n\n// ---------------------------------------------------------------------------\n// Tool identities — canonical names persisted in `session.turns`. Stable\n// across the lifetime of the project; renaming would break resume.\n// ---------------------------------------------------------------------------\n\nexport const PRESENT_PLAN_TOOL = 'present_plan'\nexport const ASK_USER_TOOL = 'ask_user'\n\n/** True when `name` is one of the interaction tool canonical names. */\nexport function isInteractionTool(name: string): boolean {\n return name === PRESENT_PLAN_TOOL || name === ASK_USER_TOOL\n}\n\n// ---------------------------------------------------------------------------\n// Payload shapes — parsed views of each tool's `input` blob.\n// ---------------------------------------------------------------------------\n\nexport interface PlanStep {\n id: string\n title: string\n description?: string\n}\n\nexport interface PlanPayload {\n /** Short headline summary — used as the modal title. */\n title: string\n /** Full plan body. Markdown. */\n plan: string\n /** Optional structured step list — surfaced as a checklist preview. */\n steps?: readonly PlanStep[]\n}\n\nexport interface QuestionChoice {\n id: string\n label: string\n description?: string\n /**\n * Open a `multi` select with this choice already ticked. Ignored by\n * single selects (which position the cursor from the carried answer\n * instead) and overridden by a carried answer on re-entry, so resuming a\n * question keeps what the user had rather than snapping back to the\n * defaults.\n *\n * Exists because the alternative for an opt-out list is N keystrokes to\n * accept N items: a caller offering \"which of these 20 findings do you\n * want to send\" wants them pre-ticked and the user deselecting a few.\n */\n selected?: boolean\n}\n\n/**\n * One question in an `ask_user` batch. Discriminated by `type`:\n *\n * - `text` — single-line free-text answer (rendered via `<input>`).\n * - `textarea` — multi-line free-text answer (rendered via `<textarea>`).\n * - `select` — single pick from a fixed set of `choices`.\n * - `confirm` — yes/no boolean (rendered as a 2-option select).\n *\n * The model batches related clarifications into one tool call instead of\n * pinging the user N times with N round-trips.\n */\nexport type QuestionType = 'text' | 'textarea' | 'select' | 'confirm'\n\ninterface BaseQuestion {\n /** Stable id — keys the response back to the model. */\n id: string\n /** Question text shown above the input (Markdown allowed). */\n prompt: string\n /** Optional helper text under the prompt. */\n description?: string\n /**\n * Whether the answer is required (non-empty). Default: `true` for\n * `select` / `confirm` (no sensible empty), `false` for free-text\n * (skipping = empty string). Free-text questions still accept an\n * answer; the flag only gates submission.\n */\n required?: boolean\n}\n\nexport interface TextQuestion extends BaseQuestion {\n type: 'text' | 'textarea'\n /** Placeholder shown when the input is empty. */\n placeholder?: string\n /** Pre-fill the input with this value (still editable). */\n default?: string\n}\n\nexport interface SelectQuestion extends BaseQuestion {\n type: 'select'\n choices: readonly QuestionChoice[]\n /**\n * Allow picking MORE than one choice. The answer becomes a\n * `readonly string[]` of choice ids (space toggles, ↵ commits in the\n * TUI). `required` (default `true`) demands at least one selection.\n */\n multi?: boolean\n}\n\nexport interface ConfirmQuestion extends BaseQuestion {\n type: 'confirm'\n /** Label for the affirmative option. Default: `'yes'`. */\n affirmLabel?: string\n /** Label for the negative option. Default: `'no'`. */\n denyLabel?: string\n}\n\nexport type Question = TextQuestion | SelectQuestion | ConfirmQuestion\n\nexport interface QuestionPayload {\n /** Optional intro / context shown above every question (Markdown). */\n intro?: string\n /** One or more questions for the user to answer in one form. */\n questions: readonly Question[]\n}\n\n/** Per-question answer value — `string` for text/textarea/select, `boolean` for confirm, `string[]` for multi-select. */\nexport type AnswerValue = string | boolean | readonly string[]\n\n/**\n * Who raised a request. `model` is a tool call awaiting a `tool_result`;\n * `host` is a slash command or extension prompt whose consumer is local\n * code. Automatic mode may only answer on the model's behalf, so this is\n * carried as data rather than inferred from the id — a missing value is a\n * compile error at the construction site instead of a silent mis-answer.\n */\nexport type InteractionOrigin = 'model' | 'host'\n\ninterface BaseRequest {\n /** Stable id — the `tool_call.id` from the model. */\n id: string\n /** Whether the model or the host raised this request. */\n origin: InteractionOrigin\n /** Tool canonical name. */\n tool: string\n /** Run that emitted the call, when known. */\n runId?: string\n /** Turn id the call lives in. */\n turnId?: string\n /** Unix ms when the request was raised. */\n createdAt: number\n}\n\nexport interface PlanRequest extends BaseRequest {\n kind: 'plan'\n tool: typeof PRESENT_PLAN_TOOL\n payload: PlanPayload\n}\n\nexport interface QuestionRequest extends BaseRequest {\n kind: 'question'\n tool: typeof ASK_USER_TOOL\n payload: QuestionPayload\n}\n\nexport type InteractionRequest = PlanRequest | QuestionRequest\n\n// ---------------------------------------------------------------------------\n// Response shapes — what the user gives back. Serialized as JSON below so\n// the model parses a stable, explicit envelope.\n// ---------------------------------------------------------------------------\n\nexport type PlanDecision = 'approve' | 'reject' | 'revise'\n\nexport interface PlanResponse {\n kind: 'plan'\n decision: PlanDecision\n /** Optional free-text feedback — shown to the model verbatim. */\n comment?: string\n /**\n * Resolved by automatic mode rather than by a person. Serialized so the\n * model can tell a real decision from a default it is free to override.\n */\n automatic?: boolean\n}\n\nexport interface QuestionResponse {\n kind: 'question'\n /**\n * Map of `Question.id → AnswerValue` for every question the user\n * answered. Missing keys = question skipped (only possible when\n * `required: false`).\n */\n answers: Readonly<Record<string, AnswerValue>>\n /** See {@link PlanResponse.automatic}. */\n automatic?: boolean\n}\n\nexport type InteractionResponse = PlanResponse | QuestionResponse\n\nexport const AUTOMATIC_INTERACTION_INSTRUCTION\n = 'Automatic mode is enabled. Make the best choice from the repository and current task, then continue without asking for this input again.'\n\n/**\n * Id prefix for interactions the HOST synthesizes — slash commands and\n * extension `ctx.interaction.*` share the prompt slot with the tools but\n * never pass through {@link createInteractionTools}. Model-originated\n * requests carry the provider's `tool_call.id` instead. Naming only:\n * origin policy reads {@link BaseRequest.origin}, never the id.\n */\nexport const HOST_INTERACTION_ID_PREFIX = 'command_'\n\n/**\n * Whether a queued request came from a model tool call. Automatic mode\n * answers on the model's behalf, so it must leave host-originated prompts\n * alone: their consumer is local code awaiting a real decision, with no\n * model on the other end to act on an instruction sentence.\n */\nexport function isModelInteraction(request: InteractionRequest): boolean {\n return request.origin === 'model'\n}\n\n/**\n * Stand-in for a free-text answer nobody typed. Deliberately NOT the\n * instruction sentence: {@link serializeInteractionResponse} already leads\n * the envelope with it once, so repeating it per answer only restates the\n * same disclosure — four times over on a three-question form.\n */\nconst AUTOMATIC_FREE_TEXT_ANSWER = '(no answer, automatic mode)'\n\n/**\n * Automatic answer for one question, in the shape its `type` promises:\n * `confirm` declares a boolean and `select` declares one of its own choice\n * ids, so a free-text placeholder would be out of contract there.\n *\n * These are defaults, not decisions — nobody chose them. The response\n * carries `automatic: true` so the model reads them as such and overrides\n * whatever doesn't fit; see {@link serializeInteractionResponse}.\n */\nfunction automaticAnswerFor(question: Question): AnswerValue {\n switch (question.type) {\n // Proceed, consistent with auto-approving plans.\n case 'confirm':\n return true\n // Nothing marks a choice as recommended, so the first one is an\n // arbitrary placeholder the model is expected to reconsider.\n case 'select': {\n const picked = question.choices[0]?.id ?? ''\n return question.multi ? (picked ? [picked] : []) : picked\n }\n default:\n return AUTOMATIC_FREE_TEXT_ANSWER\n }\n}\n\n/** Build the deterministic response used when the host skips interaction UI. */\nexport function createAutomaticInteractionResponse(request: InteractionRequest): InteractionResponse {\n if (request.kind === 'plan') {\n // No `comment`: that field is the user's own free-text feedback, shown to\n // the model verbatim. Harness text belongs in the envelope's `note`, which\n // serialization adds once for every automatic response.\n return {\n kind: 'plan',\n decision: 'approve',\n automatic: true,\n }\n }\n const answers: Record<string, AnswerValue> = {}\n for (const question of request.payload.questions)\n answers[question.id] = automaticAnswerFor(question)\n return { kind: 'question', answers, automatic: true }\n}\n\n// ---------------------------------------------------------------------------\n// Serialization — what the model sees as the tool_result `output`.\n// ---------------------------------------------------------------------------\n\n/**\n * Format an {@link InteractionResponse} as the tool_result content. JSON\n * keeps the envelope parseable and explicit. Empty fields are stripped so\n * the model doesn't waste tokens reading absent values.\n */\nexport function serializeInteractionResponse(response: InteractionResponse): string {\n // Leads the envelope so the model reads \"nobody answered this\" before the\n // values themselves. Absent on user-submitted responses, which cost the\n // same tokens they always did.\n const automatic: Record<string, unknown> = response.automatic\n ? { automatic: true, note: AUTOMATIC_INTERACTION_INSTRUCTION }\n : {}\n if (response.kind === 'plan') {\n const body: Record<string, unknown> = { ...automatic, decision: response.decision }\n if (response.comment && response.comment.trim().length > 0)\n body.comment = response.comment.trim()\n return JSON.stringify(body)\n }\n // Normalize answer values: trim strings, drop empty / undefined entries\n // so the model sees a tight payload of only the meaningful answers.\n const answers: Record<string, AnswerValue> = {}\n for (const [id, value] of Object.entries(response.answers)) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed.length > 0)\n answers[id] = trimmed\n }\n else if (typeof value === 'boolean') {\n answers[id] = value\n }\n else if (Array.isArray(value)) {\n // Multi-select — keep non-empty trimmed ids, drop empty selections.\n const picked = value.map(v => String(v).trim()).filter(v => v.length > 0)\n if (picked.length > 0)\n answers[id] = picked\n }\n }\n return JSON.stringify({ ...automatic, answers })\n}\n\n// ---------------------------------------------------------------------------\n// Resumed-flow turn builder — batches every pending interaction's response\n// into ONE matched user turn.\n//\n// Why a dedicated builder: the agent loop's history-validation contract\n// requires every `tool_use` block in an assistant turn to be followed by\n// EXACTLY ONE matched `tool_result` block in the very next user turn.\n// When a session was killed mid-batch — say the model emitted both\n// `present_plan` and `ask_user` in parallel and the process died before\n// either Promise resolved — naively writing one `tool_result` per UI\n// submission would persist a user turn missing the sibling's match, and\n// the next `agent.run()` would reject the history on the API side.\n//\n// The wizard UI accumulates answers across submissions; this helper\n// converts the accumulated map back into a protocol-valid user turn\n// containing one `tool_result` per pending request, in pending order.\n// ---------------------------------------------------------------------------\n\n/**\n * Build a protocol-valid user `SessionTurn` that closes a batch of\n * resumed pending interactions.\n *\n * - `requests` MUST be the same list returned by\n * {@link pendingInteractionsFromTurns} for the session, in order.\n * - `responses` MUST contain an entry for every request id; throws\n * otherwise (caller bug — flush before all answers collected).\n *\n * Pure / total. Tested in isolation so the batching invariant doesn't\n * regress without the resumed-flow integration noticing.\n */\nexport function buildResumedToolResultsTurn(\n requests: readonly InteractionRequest[],\n responses: ReadonlyMap<string, InteractionResponse>,\n options: {\n /** Turn id minted by the session store. */\n turnId: string\n /**\n * Run id that emitted the pending tool calls — when known, attaching\n * it ties the resumed turn back to the originating `SessionRun` row\n * so consumers grouping turns by run keep clean ancestry across the\n * close/reopen boundary.\n */\n runId?: string\n /** `createdAt` timestamp. Defaults to `Date.now()`. */\n createdAt?: number\n },\n): SessionTurn {\n if (requests.length === 0)\n throw new Error('buildResumedToolResultsTurn: at least one request is required')\n\n const content: SessionContentBlock[] = requests.map((req) => {\n const response = responses.get(req.id)\n if (!response) {\n throw new Error(\n `buildResumedToolResultsTurn: missing response for request \"${req.id}\". `\n + `All ${requests.length} pending interactions must be resolved before flushing.`,\n )\n }\n return {\n type: 'tool_result',\n callId: req.id,\n output: serializeInteractionResponse(response),\n }\n })\n\n return {\n id: options.turnId,\n role: 'user',\n content,\n createdAt: options.createdAt ?? Date.now(),\n ...(options.runId ? { runId: options.runId } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Pending-state derivation — pure, store-agnostic.\n//\n// The single source of truth for \"what is the session waiting on?\". Used by\n// the chat layer on session activation to decide whether to render the\n// resumed-interaction UI instead of the prompt textarea.\n// ---------------------------------------------------------------------------\n\n/**\n * Scan `turns` and return every interaction tool call without a matching\n * `tool_result`, ordered by turn appearance. Works equally for live mid-run\n * inspection and reload-from-disk.\n *\n * Malformed payloads (input that doesn't match the tool's schema) are\n * skipped silently — the call still lives in the persisted history, but\n * the renderer wouldn't know how to display it. The next `agent.run()` on\n * the session will see the orphan and synthesize an error tool_result via\n * the loop's normal recovery path.\n */\nexport function pendingInteractionsFromTurns(turns: readonly SessionTurn[]): InteractionRequest[] {\n interface CallInfo {\n callId: string\n tool: string\n input: Record<string, unknown>\n runId?: string\n turnId: string\n createdAt: number\n }\n const calls: CallInfo[] = []\n const resultIds = new Set<string>()\n\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_call' && isInteractionTool(block.name)) {\n calls.push({\n callId: block.id,\n tool: block.name,\n input: block.input,\n ...(turn.runId ? { runId: turn.runId } : {}),\n turnId: turn.id,\n createdAt: turn.createdAt,\n })\n }\n else if (block.type === 'tool_result') {\n resultIds.add(block.callId)\n }\n }\n }\n\n const pending: InteractionRequest[] = []\n for (const call of calls) {\n if (resultIds.has(call.callId))\n continue\n if (call.tool === PRESENT_PLAN_TOOL) {\n const payload = parsePlanPayload(call.input)\n if (!payload)\n continue\n pending.push({\n id: call.callId,\n // Reconstructed from an unmatched `tool_use` block, so model by\n // construction — host prompts never reach the transcript.\n origin: 'model',\n kind: 'plan',\n tool: PRESENT_PLAN_TOOL,\n payload,\n ...(call.runId ? { runId: call.runId } : {}),\n turnId: call.turnId,\n createdAt: call.createdAt,\n })\n }\n else if (call.tool === ASK_USER_TOOL) {\n const payload = parseQuestionPayload(call.input)\n if (!payload)\n continue\n pending.push({\n id: call.callId,\n origin: 'model',\n kind: 'question',\n tool: ASK_USER_TOOL,\n payload,\n ...(call.runId ? { runId: call.runId } : {}),\n turnId: call.turnId,\n createdAt: call.createdAt,\n })\n }\n }\n return pending\n}\n\n/**\n * Defensive parsers — tools validate via JSON Schema before `execute`, but\n * persisted turns can carry malformed input (model regressions, schema\n * migrations). We accept what we recognize and drop what we don't.\n */\nfunction parsePlanPayload(input: Record<string, unknown>): PlanPayload | null {\n const title = typeof input.title === 'string' ? input.title.trim() : ''\n const plan = typeof input.plan === 'string' ? input.plan : ''\n if (!title || !plan)\n return null\n const stepsRaw = Array.isArray(input.steps) ? input.steps : undefined\n const steps: PlanStep[] = []\n if (stepsRaw) {\n for (const s of stepsRaw) {\n if (!s || typeof s !== 'object')\n continue\n const cast = s as { id?: unknown, title?: unknown, description?: unknown }\n if (typeof cast.id !== 'string' || typeof cast.title !== 'string')\n continue\n steps.push({\n id: cast.id,\n title: cast.title,\n ...(typeof cast.description === 'string' ? { description: cast.description } : {}),\n })\n }\n }\n return {\n title,\n plan,\n ...(steps.length > 0 ? { steps } : {}),\n }\n}\n\n/**\n * Defensive parser for an `ask_user` request. Accepts BOTH the new\n * multi-question shape and the legacy single-question shape (kept so\n * sessions created before the multi-question rework still surface\n * correctly on resume).\n *\n * - New: `{ intro?, questions: [...] }`.\n * - Legacy: `{ question, choices?, allowComment? }` → wrapped into a\n * single `select` (if choices) or `textarea` (otherwise) question\n * keyed by the synthetic id `'answer'`.\n */\nfunction parseQuestionPayload(input: Record<string, unknown>): QuestionPayload | null {\n const intro = typeof input.intro === 'string' && input.intro.trim().length > 0\n ? input.intro\n : undefined\n\n // New shape — explicit `questions` array. Parse each entry; drop\n // malformed ones rather than the whole payload so a single bad\n // question doesn't make the entire batch invisible on reload.\n //\n // Dedup by id (first wins). React would warn on the duplicate `key`\n // anyway, but more importantly the response `answers` map is keyed\n // by id — duplicates would silently collapse to a single value the\n // model would receive for every question that shared the id. First-\n // occurrence-wins keeps the form predictable; the model's own\n // schema validation should have caught it earlier.\n if (Array.isArray(input.questions)) {\n const questions: Question[] = []\n const seen = new Set<string>()\n for (const raw of input.questions) {\n const q = parseQuestion(raw)\n if (!q)\n continue\n if (seen.has(q.id))\n continue\n seen.add(q.id)\n questions.push(q)\n }\n if (questions.length === 0)\n return null\n return { ...(intro ? { intro } : {}), questions }\n }\n\n // Legacy shape — flatten to a one-question payload so resumed sessions\n // created before the multi-question rework still render.\n const question = typeof input.question === 'string' ? input.question.trim() : ''\n if (!question)\n return null\n const choices = parseChoices(input.choices)\n const legacy: Question = choices.length > 0\n ? { id: 'answer', prompt: question, type: 'select', choices }\n : { id: 'answer', prompt: question, type: 'textarea' }\n return { ...(intro ? { intro } : {}), questions: [legacy] }\n}\n\nfunction parseQuestion(raw: unknown): Question | null {\n if (!raw || typeof raw !== 'object')\n return null\n const cast = raw as {\n id?: unknown\n prompt?: unknown\n type?: unknown\n description?: unknown\n required?: unknown\n placeholder?: unknown\n default?: unknown\n choices?: unknown\n multi?: unknown\n affirmLabel?: unknown\n denyLabel?: unknown\n }\n if (typeof cast.id !== 'string' || !cast.id)\n return null\n if (typeof cast.prompt !== 'string' || !cast.prompt.trim())\n return null\n const type = cast.type\n if (type !== 'text' && type !== 'textarea' && type !== 'select' && type !== 'confirm')\n return null\n\n const base = {\n id: cast.id,\n prompt: cast.prompt,\n ...(typeof cast.description === 'string' ? { description: cast.description } : {}),\n ...(typeof cast.required === 'boolean' ? { required: cast.required } : {}),\n }\n\n if (type === 'text' || type === 'textarea') {\n return {\n ...base,\n type,\n ...(typeof cast.placeholder === 'string' ? { placeholder: cast.placeholder } : {}),\n ...(typeof cast.default === 'string' ? { default: cast.default } : {}),\n }\n }\n\n if (type === 'select') {\n const choices = parseChoices(cast.choices)\n if (choices.length === 0)\n return null // a select with zero choices is unusable\n return { ...base, type, choices, ...(cast.multi === true ? { multi: true } : {}) }\n }\n\n // type === 'confirm'\n return {\n ...base,\n type,\n ...(typeof cast.affirmLabel === 'string' ? { affirmLabel: cast.affirmLabel } : {}),\n ...(typeof cast.denyLabel === 'string' ? { denyLabel: cast.denyLabel } : {}),\n }\n}\n\nfunction parseChoices(raw: unknown): QuestionChoice[] {\n if (!Array.isArray(raw))\n return []\n const choices: QuestionChoice[] = []\n for (const c of raw) {\n if (!c || typeof c !== 'object')\n continue\n const cast = c as { id?: unknown, label?: unknown, description?: unknown, selected?: unknown }\n if (typeof cast.id !== 'string' || typeof cast.label !== 'string')\n continue\n choices.push({\n id: cast.id,\n label: cast.label,\n ...(typeof cast.description === 'string' ? { description: cast.description } : {}),\n // Preserved through the sanitizer, not just accepted at the type\n // level: a question rehydrated from a persisted session would\n // otherwise reopen with every pre-tick silently dropped.\n ...(cast.selected === true ? { selected: true } : {}),\n })\n }\n return choices\n}\n\n// ---------------------------------------------------------------------------\n// Tool factory — builds `present_plan` and `ask_user` ToolDefs bound to a\n// host-provided async resolver. Used by `buildAgent` in the TUI; a GUI\n// consumer wires its own React resolver the same way.\n// ---------------------------------------------------------------------------\n\nexport interface CreateInteractionToolsOptions {\n /**\n * Called when the agent invokes one of the interaction tools. Resolves\n * with the user's response (forwarded to the model as the tool_result);\n * reject to signal denial / abort (the model receives\n * `Tool error: <message>` as the result).\n */\n requestInteraction: (request: InteractionRequest) => Promise<InteractionResponse>\n /** Evaluated at invocation time so hosts can switch modes without rebuilding the agent. */\n shouldSkipInteractions?: () => boolean\n}\n\n/**\n * Create the `present_plan` and `ask_user` tools as a record, ready to\n * spread into `createAgent({ tools })` or composed into a {@link Preset}.\n */\nexport function createInteractionTools(opts: CreateInteractionToolsOptions): Record<string, ToolDef> {\n return {\n [PRESENT_PLAN_TOOL]: createPlanTool(opts),\n [ASK_USER_TOOL]: createQuestionTool(opts),\n }\n}\n\nconst PLAN_DESCRIPTION\n = 'Present a multi-step plan to the user for approval BEFORE executing it. '\n + 'Use this when you have a concrete proposal that materially changes the workspace '\n + 'and want explicit confirmation. The user can approve, reject, or request revisions; '\n + 'revisions come back as a comment alongside a `revise` decision.'\n\nconst QUESTION_DESCRIPTION\n = 'Ask the user one or more clarifying questions and pause until they answer. '\n + 'Batch related questions into a single call instead of asking one at a time — '\n + 'every call pauses the conversation. '\n + 'Each question carries a `type`: '\n + '`text` (single line), `textarea` (multi-line), `select` (one of `choices`), or '\n + '`confirm` (yes/no). Use sparingly: only when explicit user input materially '\n + 'changes the next steps.'\n\nfunction createPlanTool({ requestInteraction, shouldSkipInteractions }: CreateInteractionToolsOptions): ToolDef {\n return {\n spec: {\n name: PRESENT_PLAN_TOOL,\n description: PLAN_DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n title: { type: 'string', description: 'Short headline summary of the plan.' },\n plan: { type: 'string', description: 'Full plan body, written as Markdown.' },\n steps: {\n type: 'array',\n description: 'Optional structured step list. Each step has an `id`, `title`, and optional `description`.',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n title: { type: 'string' },\n description: { type: 'string' },\n },\n required: ['id', 'title'],\n },\n },\n },\n required: ['title', 'plan'],\n },\n },\n async execute(input, ctx) {\n const payload = parsePlanPayload(input)\n if (!payload)\n throw new Error('present_plan: invalid input — expected non-empty `title` and `plan` strings.')\n const request: PlanRequest = {\n id: ctx.callId,\n origin: 'model',\n kind: 'plan',\n tool: PRESENT_PLAN_TOOL,\n payload,\n ...(ctx.runId ? { runId: ctx.runId } : {}),\n turnId: ctx.turnId,\n createdAt: Date.now(),\n }\n const response = shouldSkipInteractions?.()\n ? createAutomaticInteractionResponse(request)\n : await requestInteraction(request)\n if (response.kind !== 'plan')\n throw new Error(`present_plan: unexpected response kind \"${response.kind}\".`)\n return serializeInteractionResponse(response)\n },\n }\n}\n\nfunction createQuestionTool({ requestInteraction, shouldSkipInteractions }: CreateInteractionToolsOptions): ToolDef {\n return {\n spec: {\n name: ASK_USER_TOOL,\n description: QUESTION_DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n intro: {\n type: 'string',\n description: 'Optional context shown above the questions (Markdown).',\n },\n questions: {\n type: 'array',\n description: 'One or more questions for the user to answer in a single form.',\n minItems: 1,\n items: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'Stable identifier — keys the answer back to the model.',\n },\n prompt: {\n type: 'string',\n description: 'The question to display.',\n },\n type: {\n type: 'string',\n enum: ['text', 'textarea', 'select', 'confirm'],\n description: '`text` = single line, `textarea` = multi-line, `select` = pick from `choices`, `confirm` = yes/no.',\n },\n description: {\n type: 'string',\n description: 'Optional helper text shown under the prompt.',\n },\n required: {\n type: 'boolean',\n description: 'Whether a non-empty answer is required.',\n },\n placeholder: {\n type: 'string',\n description: 'For `text` / `textarea`: placeholder shown when the input is empty.',\n },\n default: {\n type: 'string',\n description: 'For `text` / `textarea`: pre-fill the input with this value.',\n },\n choices: {\n type: 'array',\n description: 'The available choices. Required when `type` is `select`; omit for other types.',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n label: { type: 'string' },\n description: { type: 'string' },\n selected: {\n type: 'boolean',\n description: 'For a `multi` select: open with this choice already ticked (opt-out lists).',\n },\n },\n required: ['id', 'label'],\n },\n },\n affirmLabel: {\n type: 'string',\n description: 'For `confirm`: label for the affirmative option. Default: `\"yes\"`.',\n },\n denyLabel: {\n type: 'string',\n description: 'For `confirm`: label for the negative option. Default: `\"no\"`.',\n },\n },\n required: ['id', 'prompt', 'type'],\n },\n },\n },\n required: ['questions'],\n },\n },\n async execute(input, ctx) {\n const payload = parseQuestionPayload(input)\n if (!payload)\n throw new Error('ask_user: invalid input — expected a non-empty `questions` array.')\n const request: QuestionRequest = {\n id: ctx.callId,\n origin: 'model',\n kind: 'question',\n tool: ASK_USER_TOOL,\n payload,\n ...(ctx.runId ? { runId: ctx.runId } : {}),\n turnId: ctx.turnId,\n createdAt: Date.now(),\n }\n const response = shouldSkipInteractions?.()\n ? createAutomaticInteractionResponse(request)\n : await requestInteraction(request)\n if (response.kind !== 'question')\n throw new Error(`ask_user: unexpected response kind \"${response.kind}\".`)\n return serializeInteractionResponse(response)\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// React context — FIFO queue of pending requests + resolver callbacks.\n//\n// Two contexts on purpose (mirrors `safe-mode-context.tsx`):\n// - `InteractionsQueueContext` carries the live queue. Consumers re-render\n// on every push/pop.\n// - `InteractionsActionsContext` carries `{ enqueue, resolveHead, … }` and\n// never changes identity, so callbacks that depend on these don't\n// re-bind on every queue churn.\n// ---------------------------------------------------------------------------\n\nexport interface PendingInteractionEntry {\n request: InteractionRequest\n /** The user picked something — forward to the live tool Promise or the resumed tool_result writer. */\n resolve: (response: InteractionResponse) => void\n /**\n * The user / system cancelled — distinct from `resolve` so renderers can\n * differentiate \"user picked something\" from \"user gave up / aborted\".\n * Live entries reject the tool's Promise; resumed entries decide what\n * to persist as the cancelled tool_result.\n */\n cancel: (reason?: string) => void\n}\n\nexport interface InteractionsActions {\n /** Add a request to the FIFO queue. */\n enqueue: (entry: PendingInteractionEntry) => void\n /** Resolve the head request with `response`. */\n resolveHead: (response: InteractionResponse) => void\n /**\n * Cancel the head request. Reserved for future per-call cancel\n * affordances (a \"skip this\" button, a GUI's close-modal-X) — the\n * built-in TUI currently cancels via the run-level `cancelAll` path\n * only. Kept on the public API so consumer renderers can wire it\n * without a follow-up surface change.\n */\n cancelHead: (reason?: string) => void\n /** Cancel every queued request. Used by abort / session teardown. */\n cancelAll: (reason?: string) => void\n /**\n * Remove the queued entry whose `request.id` matches, WITHOUT settling\n * it locally — used by the daemon runtime when ANOTHER window resolved\n * the same shared interaction. No-op when absent.\n */\n dropById: (requestId: string) => void\n}\n\nconst InteractionsQueueContext = createContext<readonly PendingInteractionEntry[]>([])\nconst InteractionsActionsContext = createContext<InteractionsActions | null>(null)\n\nexport function InteractionsProvider({ children }: { children: ReactNode }) {\n const [queue, setQueue] = useState<readonly PendingInteractionEntry[]>([])\n\n // Mirror the live queue so the unmount cleanup can settle whatever is\n // pending without re-subscribing the effect on every queue change.\n const queueRef = useRef<readonly PendingInteractionEntry[]>([])\n queueRef.current = queue\n\n // Provider unmount is a teardown path too: React drops the queue state\n // outright, so any in-flight `requestInteraction` Promise would hang\n // forever (its tool body never settles → the run's `running` gate never\n // clears). Cancel every pending entry on unmount so the resolver can't be\n // silently dropped. (#4)\n useEffect(() => () => {\n for (const entry of queueRef.current)\n entry.cancel('Interactions provider unmounted')\n }, [])\n\n const enqueue = useCallback((entry: PendingInteractionEntry) => {\n setQueue(prev => [...prev, entry])\n }, [])\n\n const resolveHead = useCallback((response: InteractionResponse) => {\n setQueue((prev) => {\n const [head, ...rest] = prev\n if (head)\n head.resolve(response)\n return rest\n })\n }, [])\n\n const cancelHead = useCallback((reason?: string) => {\n setQueue((prev) => {\n const [head, ...rest] = prev\n if (head)\n head.cancel(reason)\n return rest\n })\n }, [])\n\n const cancelAll = useCallback((reason?: string) => {\n setQueue((prev) => {\n for (const entry of prev) entry.cancel(reason)\n return []\n })\n }, [])\n\n const dropById = useCallback((requestId: string) => {\n // Remove WITHOUT settling: the daemon already resolved this in the\n // window that answered it. Neither resolve nor cancel — both would\n // reach back toward the (no-op) daemon callback needlessly.\n setQueue(prev => prev.filter(entry => entry.request.id !== requestId))\n }, [])\n\n // Stable actions identity — every member is `useCallback`'d with `[]`\n // deps. `useRef` captures the first-render identity and we hand the\n // same object out forever, so consumers calling `useInteractionsActions()`\n // never re-render purely because of queue state.\n const actionsRef = useRef<InteractionsActions | null>(null)\n if (!actionsRef.current)\n actionsRef.current = { enqueue, resolveHead, cancelHead, cancelAll, dropById }\n\n return (\n <InteractionsActionsContext.Provider value={actionsRef.current}>\n <InteractionsQueueContext.Provider value={queue}>\n {children}\n </InteractionsQueueContext.Provider>\n </InteractionsActionsContext.Provider>\n )\n}\n\n/** Read the live queue (re-renders on every push/pop). */\nexport function useInteractionsQueue(): readonly PendingInteractionEntry[] {\n return useContext(InteractionsQueueContext)\n}\n\n/** Read the stable actions object. */\nexport function useInteractionsActions(): InteractionsActions {\n const ctx = useContext(InteractionsActionsContext)\n if (!ctx)\n throw new Error('useInteractionsActions must be used inside <InteractionsProvider>')\n return ctx\n}\n\n// ---------------------------------------------------------------------------\n// Live-tool binding helper — wraps the queue actions into a Promise-shaped\n// `requestInteraction` that {@link createInteractionTools} expects.\n// ---------------------------------------------------------------------------\n\n/**\n * Build the `requestInteraction` callback for `createInteractionTools` from\n * the React-side {@link InteractionsActions}. Each call adds a fresh entry\n * to the queue whose `resolve` / `cancel` route back to one Promise that\n * the tool's `execute` awaits.\n */\nexport function makeRequestInteraction(\n actions: InteractionsActions,\n): CreateInteractionToolsOptions['requestInteraction'] {\n return request => new Promise<InteractionResponse>((resolve, reject) => {\n actions.enqueue({\n request,\n resolve,\n cancel: reason => reject(new Error(reason ?? 'Interaction cancelled')),\n })\n })\n}\n","/**\n * Per-server MCP OAuth status tracking, decoupled from the renderer.\n *\n * The TUI keeps a `Record<serverName, McpAuthStatus>` so it can render a\n * \"needs-auth\" / \"authorizing\" / \"error\" badge in the MCP picker. The\n * status map is driven entirely by hooks fired from the agent's MCP\n * bootstrap + the interactive `loginMcpServer` flow — the reducer below\n * is pure, makes no FS or network calls, and is shared between the live\n * TUI store and the test harness.\n *\n * Status lifecycle:\n *\n * idle ──mcp:connect──▶ authed\n * (bootstrap succeeded with stored tokens)\n *\n * idle ──mcp:auth:required──▶ needs-auth\n * (bootstrap saw no/invalid tokens)\n *\n * needs-auth ──login()──▶ authorizing ──mcp:auth:url──▶ authorizing(url)\n * ──mcp:auth:success──▶ authed\n * ──mcp:auth:error──▶ error\n *\n * any ──logout──▶ idle\n *\n * Multiple servers progress through the lifecycle independently; the map\n * is keyed by `McpServerConfig.name`.\n */\n\nexport type McpAuthStatus\n // Default — never heard anything about this server. Includes both pure\n // stdio servers (no auth concept) and HTTP servers we haven't bootstrapped yet.\n = | { kind: 'idle' }\n // OAuth-required, no usable tokens. Reason maps to the bootstrap signal:\n // - 'no-tokens' — explicit `auth: 'oauth'` config + empty store.\n // - 'auto-promoted' — server returned 401 + RFC 9728 metadata, eligible\n // for OAuth because the user did not set a static Authorization header.\n | { kind: 'needs-auth', reason: 'no-tokens' | 'auto-promoted' }\n // Login flow in flight. `url` is populated as soon as the SDK builds the\n // authorization URL — the modal renders it so the user can re-open the\n // browser tab manually if the automatic open failed (no GUI, restricted\n // sandbox).\n | { kind: 'authorizing', url?: string }\n // Tokens are stored and (most recently) usable. Surface as a check mark.\n | { kind: 'authed' }\n // Last login attempt failed. `error` is the user-facing message — the\n // TUI shows it verbatim so a network blip or revoked token is debuggable\n // without `ZIDANE_DEBUG`.\n | { kind: 'error', error: string }\n // Bootstrap failed for a NON-auth reason — stdio command missing/crashed,\n // http endpoint unreachable. Kept distinct from `error` (login failed) so\n // the picker doesn't offer login/logout on servers where auth is not the\n // problem. Cleared back to idle by a later successful `mcp:connect`.\n | { kind: 'bootstrap-error', error: string }\n\n/**\n * Map of server name → status. `idle` is the implicit default for any\n * server NOT present in the map, so the renderer treats `undefined` as\n * `{ kind: 'idle' }` and the map can stay sparse.\n */\nexport type McpAuthStateMap = Readonly<Record<string, McpAuthStatus>>\n\n/**\n * Events the reducer accepts. Names mirror the agent hooks 1:1 plus two\n * UI-only verbs (`login:start` / `login:cancel`) so the renderer can\n * optimistically flip a row to `authorizing` before the SDK has emitted\n * `mcp:auth:url` — useful when the loopback callback is slow to bind.\n */\nexport type McpAuthEvent\n = | { type: 'auth-required', name: string, reason: 'no-tokens' | 'auto-promoted' }\n | { type: 'auth-url', name: string, url: string }\n | { type: 'auth-success', name: string }\n | { type: 'auth-error', name: string, error: string }\n | { type: 'bootstrap-error', name: string, error: string }\n | { type: 'connected', name: string }\n | { type: 'login:start', name: string }\n | { type: 'login:cancel', name: string }\n | { type: 'logout', name: string }\n | { type: 'reset', name: string }\n\n/**\n * Apply one event to the state map. Pure, immutable — returns a new map\n * with only the affected key reassigned.\n *\n * State transitions are simple \"last write wins\"; we don't gate (e.g.) a\n * `login:start` from overriding `authorizing` because the loop layer\n * never doubles those up — interactive login is serialized in the TUI.\n */\nexport function reduceMcpAuth(state: McpAuthStateMap, event: McpAuthEvent): McpAuthStateMap {\n const next = (status: McpAuthStatus): McpAuthStateMap => ({ ...state, [event.name]: status })\n switch (event.type) {\n case 'auth-required':\n return next({ kind: 'needs-auth', reason: event.reason })\n case 'auth-url':\n return next({ kind: 'authorizing', url: event.url })\n case 'auth-success':\n return next({ kind: 'authed' })\n case 'auth-error':\n return next({ kind: 'error', error: event.error })\n case 'bootstrap-error':\n return next({ kind: 'bootstrap-error', error: event.error })\n case 'connected':\n // Only flip to `authed` when this server was OAuth-tracked. A plain\n // stdio bootstrap shouldn't paint every server with a green badge —\n // `idle` means \"nothing to say about auth here\". This matters for\n // the MCP picker's badge column staying signal-dense.\n if (state[event.name]?.kind === 'authorizing'\n || state[event.name]?.kind === 'needs-auth'\n || state[event.name]?.kind === 'authed') {\n return next({ kind: 'authed' })\n }\n // A successful bootstrap clears a previous bootstrap failure — the\n // server was fixed (file restored, endpoint back up). Back to the\n // sparse-map default rather than a green badge: `idle` means\n // \"nothing to say about auth here\", which is true for stdio.\n if (state[event.name]?.kind === 'bootstrap-error') {\n const { [event.name]: _, ...rest } = state\n return rest\n }\n return state\n case 'login:start':\n return next({ kind: 'authorizing' })\n case 'login:cancel':\n // Cancellation is treated as \"back to needs-auth\" — the user dismissed\n // the browser flow but the underlying requirement still stands.\n return next({ kind: 'needs-auth', reason: 'no-tokens' })\n case 'logout':\n case 'reset': {\n const { [event.name]: _, ...rest } = state\n return rest\n }\n }\n}\n\n/** Convenience getter — treats `undefined` as `idle`. */\nexport function getMcpAuthStatus(state: McpAuthStateMap, name: string): McpAuthStatus {\n return state[name] ?? { kind: 'idle' }\n}\n","/**\n * React provider for the MCP per-server OAuth state map.\n *\n * `ModalRoot.open(node)` captures the modal contents as a snapshot — naive\n * prop-passing would freeze the auth badge at whatever status existed when\n * the user opened the picker, and a `mcp:auth:url` arriving mid-login\n * would never reach the rendered modal. Routing the state through a\n * context provider mounted ABOVE `ModalRoot` fixes that: a state update\n * re-renders the provider, which re-renders `ModalRoot`, which re-renders\n * the (referentially-stable) modal child node — and consumers calling\n * `useMcpAuthState()` see the fresh value.\n *\n * Two contexts on purpose:\n *\n * - `StateContext` carries the live map. Consumers re-render on every\n * auth event.\n * - `DispatchContext` carries the reducer entry point. Identity is stable\n * across renders, so AppShell's hook handlers don't re-bind when the\n * state changes — they grab `dispatch` once and fire events for the\n * lifetime of the agent.\n *\n * Actions (login / logout / cancel) deliberately do NOT live in context —\n * they depend on AppShell-scoped resources (credential store, catalog ref,\n * agent ref), close over those via refs, and stay stable across renders.\n * Passing them as modal props at `modal.open` time is fine because the\n * captured reference reads from refs internally; the snapshot doesn't go\n * stale even though it was taken at open time.\n */\n\nimport type { ReactNode } from 'react'\nimport type { McpAuthEvent, McpAuthStateMap } from './mcp-auth-state'\nimport { createContext, useCallback, useContext, useState } from 'react'\nimport { reduceMcpAuth } from './mcp-auth-state'\n\nconst StateContext = createContext<McpAuthStateMap>({})\nconst DispatchContext = createContext<((event: McpAuthEvent) => void) | null>(null)\n\n/**\n * Mount once near the top of the React tree (above `ModalRoot`). Children\n * use `useMcpAuthState()` to read and `useMcpAuthDispatch()` to fire events.\n */\nexport function McpAuthProvider({ children }: { children: ReactNode }) {\n const [state, setState] = useState<McpAuthStateMap>({})\n const dispatch = useCallback((event: McpAuthEvent) => {\n setState(prev => reduceMcpAuth(prev, event))\n }, [])\n return (\n <StateContext.Provider value={state}>\n <DispatchContext.Provider value={dispatch}>\n {children}\n </DispatchContext.Provider>\n </StateContext.Provider>\n )\n}\n\nexport function useMcpAuthState(): McpAuthStateMap {\n return useContext(StateContext)\n}\n\nexport function useMcpAuthDispatch(): (event: McpAuthEvent) => void {\n const dispatch = useContext(DispatchContext)\n if (!dispatch)\n throw new Error('useMcpAuthDispatch must be used inside <McpAuthProvider>')\n return dispatch\n}\n","/**\n * File-backed credential storage for MCP OAuth.\n *\n * Lives at `<dataDir>/mcp-credentials.json` (mode 0o600). Separate from\n * `credentials.json` (which holds AI-provider auth) so MCP server tokens\n * can't collide with provider keys, and so a future \"clear MCP auth\"\n * action can wipe just this file.\n *\n * Schema is `Record<serverName, McpCredentialEntry>`:\n * - `tokens` — current OAuth tokens (access + refresh + expiry).\n * - `clientInformation` — RFC 7591 dynamic client registration response.\n * Saved so subsequent runs don't re-register (most auth servers\n * rate-limit DCR and assign a new client per registration).\n * - `discoveryState` — RFC 9728 + RFC 8414 discovery cache. Avoids two\n * well-known requests on every cold start.\n *\n * All file operations are atomic write-then-rename and tolerate missing /\n * corrupt files (return `undefined` for a missing entry, `{}` for a\n * corrupt root file). The store implements `McpCredentialStore` so the\n * SDK-facing `McpOAuthProvider` stays pure (no FS dependency) — tests use\n * the in-memory store from `src/mcp/oauth-provider`.\n */\n\nimport type { OAuthDiscoveryState } from '@modelcontextprotocol/sdk/client/auth.js'\nimport type {\n OAuthClientInformationMixed,\n OAuthTokens,\n} from '@modelcontextprotocol/sdk/shared/auth.js'\nimport type { McpCredentialEntry, McpCredentialStore } from '../mcp/oauth-provider'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { writeFileAtomic } from '../atomic-write'\n\nconst FILE_MODE = 0o600\n\nexport function mcpCredentialsPath(dataDir: string): string {\n return resolve(dataDir, 'mcp-credentials.json')\n}\n\ntype CredentialsFile = Record<string, McpCredentialEntry>\n\nfunction readAll(dataDir: string): CredentialsFile {\n const path = mcpCredentialsPath(dataDir)\n if (!existsSync(path))\n return {}\n try {\n const raw = readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw)\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n return {}\n return parsed as CredentialsFile\n }\n catch {\n // Corrupt file — return empty so the user can re-auth without\n // a hand-edit lockout. (Same defensive pattern as `credentials.ts`.)\n return {}\n }\n}\n\nfunction writeAll(dataDir: string, all: CredentialsFile): void {\n writeFileAtomic(\n mcpCredentialsPath(dataDir),\n `${JSON.stringify(all, null, 2)}\\n`,\n { ensureDir: true, mode: FILE_MODE },\n )\n}\n\n/**\n * Build a file-backed `McpCredentialStore`. All per-server reads / writes\n * funnel through the single `mcp-credentials.json` file — this keeps\n * concurrent flows for different servers from clobbering each other since\n * each save reads-modifies-writes the full file atomically.\n *\n * Two flows for the same server name CAN race (last write wins). MCP OAuth\n * flows are user-driven and serial in practice (one login modal at a time),\n * so this is acceptable; a per-server file split + cross-process lock would\n * be required to handle a multi-process scenario.\n */\nexport function createFileMcpCredentialStore(dataDir: string): McpCredentialStore {\n return {\n load(name: string): McpCredentialEntry | undefined {\n return readAll(dataDir)[name]\n },\n save(name: string, entry: McpCredentialEntry): void {\n const all = readAll(dataDir)\n all[name] = entry\n writeAll(dataDir, all)\n },\n delete(name: string): void {\n const all = readAll(dataDir)\n if (!(name in all))\n return\n delete all[name]\n writeAll(dataDir, all)\n },\n }\n}\n\n/**\n * Convenience patches for individual sub-fields. The provider class uses\n * these so a `saveTokens` call doesn't have to round-trip `clientInformation`\n * + `discoveryState` it never touched.\n */\nexport function patchMcpCredential(\n store: McpCredentialStore,\n name: string,\n patch: Partial<McpCredentialEntry>,\n): void {\n const existing = store.load(name) ?? {}\n store.save(name, { ...existing, ...patch })\n}\n\nexport type { McpCredentialEntry, McpCredentialStore }\nexport type { OAuthClientInformationMixed, OAuthDiscoveryState, OAuthTokens }\n","/**\n * Renderer-agnostic data model for the MCP settings list when it\n * supports per-server expand / collapse with nested tool rows.\n *\n * Lives in `chat/` (not `tui/`) because both the standalone\n * `McpsSettingsModal` and the inline `<McpsList>` inside the unified\n * `SettingsModal` consume the exact same flattening + cursor\n * arithmetic — re-deriving it in two places would let the two\n * surfaces drift on edge cases (empty tool catalogs, freshly-removed\n * servers, stale expansion state).\n *\n * The model is a flat `VisibleMcpRow[]` so renderers can iterate once,\n * and the cursor is a single integer that walks both kinds (server\n * headers AND tool checkboxes) without separate \"which server am I\n * on / which tool inside it\" state.\n */\n\nimport type { McpToolSchema } from '../mcp'\nimport type { DiscoveredMcp } from './mcps-discovery'\n\n/**\n * One row in the flattened MCP settings list. Three kinds:\n *\n * - `'server'` — server header with the chevron + checkbox +\n * transport detail. Always present.\n * - `'tool'` — checkbox + description for a tool advertised\n * by the parent server. Present only when the\n * server is expanded AND its tool cache is\n * populated.\n * - `'tool-empty'` — placeholder shown when an expanded server\n * has no cached tool list yet (first launch\n * before any session has bootstrapped the\n * server). Lets the renderer surface a\n * one-line \"send a prompt to populate\" hint\n * WITHOUT changing the server's expand\n * affordance.\n */\nexport type VisibleMcpRow\n = | { kind: 'server', entry: DiscoveredMcp }\n | { kind: 'tool', serverName: string, tool: McpToolSchema }\n | { kind: 'tool-empty', serverName: string }\n\n/**\n * Flatten a server catalog plus the per-server expanded set into the\n * visible-row list a renderer cycles through.\n *\n * - `catalog` — filtered / ordered server list (post-search,\n * post-allow-list).\n * - `expanded` — names of servers whose tool list is open.\n * Servers not in this set render as a single\n * server-header row.\n * - `toolsByServer` — per-server full tool catalog (from\n * `chat/mcp-tools-cache.ts`). Missing key →\n * `'tool-empty'` placeholder under the\n * expanded server.\n */\nexport function buildVisibleMcpRows(\n catalog: readonly DiscoveredMcp[],\n expanded: ReadonlySet<string>,\n toolsByServer: Record<string, readonly McpToolSchema[]>,\n): VisibleMcpRow[] {\n const out: VisibleMcpRow[] = []\n for (const entry of catalog) {\n out.push({ kind: 'server', entry })\n if (!expanded.has(entry.config.name))\n continue\n const tools = toolsByServer[entry.config.name] ?? []\n if (tools.length === 0) {\n out.push({ kind: 'tool-empty', serverName: entry.config.name })\n continue\n }\n for (const tool of tools)\n out.push({ kind: 'tool', serverName: entry.config.name, tool })\n }\n return out\n}\n\n/**\n * Server name a row \"belongs to\" for catalog / toggle / auth lookups.\n * For a server row that's its own name; for tool / tool-empty rows\n * it's the parent server's name.\n */\nexport function parentServerName(row: VisibleMcpRow): string {\n return row.kind === 'server' ? row.entry.config.name : row.serverName\n}\n\n/**\n * Index in `rows` of the server header for the given parent name. `-1`\n * when not present. Used by collapse handlers to snap the cursor up\n * to the parent BEFORE shrinking the row list — otherwise the cursor\n * would land on whichever sibling row now occupies the same flat\n * index.\n */\nexport function indexOfServerRow(rows: readonly VisibleMcpRow[], parentName: string): number {\n return rows.findIndex(\n r => r.kind === 'server' && r.entry.config.name === parentName,\n )\n}\n","/**\n * Renderer-agnostic per-tool toggle for MCP servers. Sibling of\n * {@link useEnabledToggleSet} — same shape, opposite polarity:\n *\n * - `useEnabledToggleSet` is an **allowlist** (`enabledMcps` / etc.),\n * `undefined` = everything enabled, toggle moves to an explicit list.\n * - `useMcpToolToggleSet` is a **deny-list** (`disabledMcpTools`),\n * missing entry = everything enabled, toggle moves to an explicit\n * list of NAMES TO HIDE.\n *\n * The polarity flip is intentional and documented on the setting itself\n * (`Settings.disabledMcpTools`): when an MCP server ships a new tool in\n * a later release, a user who has already opted out of `big_tool` still\n * gets the new one — opposite preference from server-level toggles\n * where a new server should NOT silently appear in the catalog.\n *\n * Two hook flavors:\n *\n * - {@link useMcpToolToggleSet} — one server. Convenience for hosts\n * that render a single server's tool list at a time.\n * - {@link useMcpToolToggleMap} — every server's toggle handler in\n * one call. Required by hosts that dispatch toggles from a shared\n * keyboard handler at the top of the tree (`mcps-settings.tsx`)\n * where calling the single-server hook in a loop would violate\n * rules-of-hooks the moment a server is added or removed.\n *\n * Both flavors route through the same {@link buildToolToggle} factory\n * so semantics stay byte-identical regardless of entry point.\n *\n * Garbage-collects on every write: stale names (tools that disappeared\n * from the server's catalog between launches) are dropped so the\n * persisted deny-list doesn't grow unbounded. Same invariant as the\n * sibling hook for `enabledX` allowlists.\n */\n\nimport type { McpToolSchema } from '../mcp'\nimport type { Settings } from './types'\nimport { useCallback, useMemo } from 'react'\nimport { useSettings } from './settings-context'\n\nexport interface McpToolToggleSet {\n /** Live set of ENABLED tool names — `Set` for O(1) `has`. */\n enabledSet: ReadonlySet<string>\n /** Flip one tool on/off; persists immediately via `setSetting`. */\n toggle: (toolName: string) => void\n}\n\n/**\n * Bind a single MCP server's per-tool toggle to\n * `Settings.disabledMcpTools[serverName]`.\n *\n * `catalog` is the FULL list of tools the server advertises (i.e. the\n * cached `listTools()` payload from `chat/mcp-tools-cache.ts`). The\n * hook needs it both to render `enabledSet` (every catalog tool not in\n * the persisted deny-list) and to garbage-collect stale deny-list\n * entries on every write.\n */\nexport function useMcpToolToggleSet(opts: {\n serverName: string\n catalog: readonly McpToolSchema[]\n}): McpToolToggleSet {\n const { settings, setSetting } = useSettings()\n const setDisabled = useCallback(\n (next: Settings['disabledMcpTools']) => setSetting('disabledMcpTools', next),\n [setSetting],\n )\n\n const catalogNames = useMemo(() => opts.catalog.map(t => t.name), [opts.catalog])\n const persistedDenyAll = settings.disabledMcpTools\n\n return useMemo(\n () => buildToolToggle({\n serverName: opts.serverName,\n catalogNames,\n persistedDenyAll,\n setDisabledMcpTools: setDisabled,\n }),\n [opts.serverName, catalogNames, persistedDenyAll, setDisabled],\n )\n}\n\n/**\n * Bind every server in `catalogByServer` to its own per-tool toggle in\n * a single hook call. Returns a `serverName → McpToolToggleSet` map.\n *\n * Use this when a host needs handlers for multiple servers at once\n * (the TUI's MCP settings panel keyboard dispatcher is the canonical\n * example) — calling {@link useMcpToolToggleSet} in a `for` / `.map`\n * over a dynamic catalog would change the hook-call order whenever a\n * server is added or removed, violating React's rules-of-hooks. This\n * hook calls `useMemo` exactly once regardless of server count.\n *\n * Servers absent from `catalogByServer` get no entry in the result.\n * The persisted deny-list still keeps their entries on disk — they\n * just stay invisible to this consumer until the catalog re-includes\n * them.\n */\nexport function useMcpToolToggleMap(opts: {\n catalogByServer: Record<string, readonly McpToolSchema[]>\n}): Record<string, McpToolToggleSet> {\n const { settings, setSetting } = useSettings()\n const setDisabled = useCallback(\n (next: Settings['disabledMcpTools']) => setSetting('disabledMcpTools', next),\n [setSetting],\n )\n const persistedDenyAll = settings.disabledMcpTools\n\n return useMemo(() => {\n const out: Record<string, McpToolToggleSet> = {}\n for (const [serverName, catalog] of Object.entries(opts.catalogByServer)) {\n out[serverName] = buildToolToggle({\n serverName,\n catalogNames: catalog.map(t => t.name),\n persistedDenyAll,\n setDisabledMcpTools: setDisabled,\n })\n }\n return out\n }, [opts.catalogByServer, persistedDenyAll, setDisabled])\n}\n\n/**\n * Pure factory shared by both hook flavors. Captures `persistedDenyAll`\n * + `setDisabledMcpTools` at the call site so toggle handlers stay\n * referentially stable across renders that don't touch the underlying\n * settings entry — important for `useKeyboard` to avoid re-binding on\n * every catalog change.\n *\n * Exported for direct use by non-React consumers (CLI, GUI shells)\n * that want the same write semantics without the hook plumbing.\n */\nexport function buildToolToggle(args: {\n serverName: string\n catalogNames: readonly string[]\n persistedDenyAll: Settings['disabledMcpTools']\n setDisabledMcpTools: (next: Settings['disabledMcpTools']) => void\n}): McpToolToggleSet {\n const { serverName, catalogNames, persistedDenyAll, setDisabledMcpTools } = args\n const persistedDeny = persistedDenyAll?.[serverName]\n const catalogSet = new Set(catalogNames)\n\n const enabledSet: ReadonlySet<string> = (() => {\n if (!persistedDeny || persistedDeny.length === 0)\n return new Set(catalogNames)\n const deny = new Set(persistedDeny)\n return new Set(catalogNames.filter(name => !deny.has(name)))\n })()\n\n const toggle = (toolName: string): void => {\n // Compute the next deny-list against the persisted state (NOT\n // `enabledSet`) so we never accidentally promote \"implicit-enabled-\n // because-no-entry\" tools into an explicit list on the first\n // toggle. GC stale names at the same time so the persisted file\n // tracks reality.\n const currentDeny = new Set(persistedDeny ?? [])\n if (currentDeny.has(toolName))\n currentDeny.delete(toolName)\n else\n currentDeny.add(toolName)\n\n const nextForServer = [...currentDeny]\n .filter(n => catalogSet.has(n))\n .sort()\n\n const prevMap = persistedDenyAll ?? {}\n const nextMap: Record<string, readonly string[]> = { ...prevMap }\n if (nextForServer.length === 0) {\n // Empty entry collapses back to \"no customization\" so the\n // persisted file stays tidy when a user re-enables every tool\n // they previously disabled.\n delete nextMap[serverName]\n }\n else {\n nextMap[serverName] = nextForServer\n }\n\n // Top-level `undefined` represents \"no per-tool prefs anywhere\".\n // Restore that when the last server entry empties out so the\n // diff against defaults stays minimal.\n const next: Settings['disabledMcpTools']\n = Object.keys(nextMap).length === 0 ? undefined : nextMap\n setDisabledMcpTools(next)\n }\n\n return { enabledSet, toggle }\n}\n","/**\n * Per-server MCP tool-catalog cache.\n *\n * Stores the raw `tools/list` schemas zidane saw on the most recent\n * successful bootstrap of each server, so the TUI's MCP settings panel\n * (and any other host UI) can display every advertised tool — name,\n * description, input-schema — without forcing a fresh bootstrap on\n * every open. The cache also survives across launches so the very\n * first paint of the settings panel after a process restart isn't\n * blank.\n *\n * Wired via the framework's `mcp:tools:list` hook (see\n * `AgentHooks['mcp:tools:list']`). Hosts call\n * {@link subscribeMcpToolsCache} once per agent and the subscriber\n * persists every successful bootstrap to disk; the settings UI reads\n * via {@link loadMcpToolsCache}.\n *\n * Atomic write + best-effort read: a corrupt cache file is treated as\n * empty (the next bootstrap repopulates it). The cache is purely an\n * accelerator — losing it never changes the agent's runtime behavior.\n *\n * Sits in `chat/` not `mcp/` because it depends on a per-host data\n * directory (`<userDir>/mcp-tools-cache.json`) and pairs with the\n * settings + UI surfaces that consume it. Renderer-agnostic — no React,\n * no OpenTUI — so a future GUI host can subscribe the same way.\n */\n\nimport type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js'\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { McpServerConfig, McpToolSchema } from '../mcp'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { writeFileAtomic } from '../atomic-write'\nimport { errorMessage } from '../errors'\nimport { connectMcpServers } from '../mcp'\n\n/**\n * Disk filename. Versioned so a future schema change (e.g. adding\n * provenance / TTL) can ship a parallel `v2` file without colliding.\n */\nconst CACHE_FILENAME = 'mcp-tools-cache.json'\n\n/**\n * One server's cached entry.\n *\n * - `tools` — verbatim copy of the raw `tools/list` payload (pre any\n * `disabledTools` / `toolFilter` filtering). The settings UI ranges\n * over this to render the full server surface; the host applies its\n * own deny-list on top via `Settings.disabledMcpTools` (see\n * `buildMcpServers` in `mcps-discovery.ts`).\n * - `updatedAt` — ms-epoch of the bootstrap that populated this entry.\n * Useful for \"last seen N hours ago\" affordances and for cache\n * eviction policies a host might layer on later.\n * - `transport` — informational only; lets the UI distinguish a stdio\n * server's tool list from an http server with the same name in\n * \"stale catalog\" warnings.\n */\nexport interface CachedMcpToolList {\n tools: McpToolSchema[]\n updatedAt: number\n transport: 'stdio' | 'sse' | 'streamable-http'\n}\n\n/**\n * `serverName → catalog`. Map shape on disk so adding / removing\n * servers is an O(1) JSON edit instead of an array rewrite.\n */\nexport type McpToolsCache = Record<string, CachedMcpToolList>\n\n/**\n * Resolve the on-disk cache path under a host's data directory. Exposed\n * for tests + advanced hosts that want to inspect / delete the file\n * without re-implementing the path convention.\n */\nexport function mcpToolsCachePath(dataDir: string): string {\n return resolve(dataDir, CACHE_FILENAME)\n}\n\n/**\n * Best-effort cache read. Returns `{}` when the file is missing, when\n * JSON.parse rejects, or when the top-level shape isn't a plain object —\n * a corrupt cache must never crash the host on startup.\n *\n * Per-entry shape validation is light (presence of an array `tools`):\n * deeper validation would lock the cache to a single zidane version,\n * which is the opposite of what a long-lived cache wants. Entries that\n * survive the light check feed back through `McpToolSchema`'s `unknown`\n * `inputSchema` so downstream readers stay tolerant of upstream churn.\n */\nexport function loadMcpToolsCache(opts: { dataDir: string }): McpToolsCache {\n const path = mcpToolsCachePath(opts.dataDir)\n if (!existsSync(path))\n return {}\n try {\n const raw = readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw) as unknown\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n return {}\n const out: McpToolsCache = {}\n for (const [name, entry] of Object.entries(parsed as Record<string, unknown>)) {\n if (isCachedEntry(entry))\n out[name] = entry\n }\n return out\n }\n catch {\n return {}\n }\n}\n\n/**\n * Atomic full-cache write. Used by {@link subscribeMcpToolsCache} after\n * each upsert. Failures bubble up as the underlying `writeFileAtomic`\n * error — the caller logs under `ZIDANE_DEBUG` and swallows.\n */\nexport function saveMcpToolsCache(opts: { dataDir: string, cache: McpToolsCache }): void {\n writeFileAtomic(\n mcpToolsCachePath(opts.dataDir),\n `${JSON.stringify(opts.cache, null, 2)}\\n`,\n { ensureDir: true },\n )\n}\n\n/**\n * Drop entries for one or more servers, or wipe the cache when no\n * server is named. Idempotent — entries that aren't present are\n * silently skipped. Returns the resulting cache so callers can refresh\n * any in-memory mirror without re-reading from disk.\n *\n * Intended for a future \"rescan server\" affordance in the settings UI\n * (clear → trigger a real bootstrap → cache repopulates on the\n * `mcp:tools:list` hook).\n */\nexport function clearMcpToolsCache(opts: { dataDir: string, serverNames?: readonly string[] }): McpToolsCache {\n const current = loadMcpToolsCache(opts)\n if (!opts.serverNames) {\n saveMcpToolsCache({ dataDir: opts.dataDir, cache: {} })\n return {}\n }\n const next: McpToolsCache = { ...current }\n for (const name of opts.serverNames)\n delete next[name]\n saveMcpToolsCache({ dataDir: opts.dataDir, cache: next })\n return next\n}\n\n/**\n * Subscribe to `mcp:tools:list` and persist every successful bootstrap\n * into the on-disk cache. Returns an unsubscribe function — call it\n * during agent teardown so a long-lived host (Settings modal that\n * outlives one agent) doesn't accumulate stale subscribers.\n *\n * Each write loads the current cache, upserts the named entry, then\n * atomic-writes the full file. The read-modify-write is safe under\n * Node single-threaded event loop semantics — concurrent bootstraps in\n * the same process serialize through the hook bus.\n *\n * Optional `onChange` callback fires synchronously after every\n * successful disk write with the entry that was just upserted. Lets a\n * React host trigger a state refresh without polling the disk.\n *\n * Write failures are swallowed (logged under `ZIDANE_DEBUG`): the cache\n * is an accelerator, not a correctness boundary. If the disk is full\n * the agent run still works — the settings UI just shows stale data\n * until the next successful write.\n */\nexport function subscribeMcpToolsCache(\n hooks: Hookable<AgentHooks>,\n opts: {\n dataDir: string\n onChange?: (entry: { name: string } & CachedMcpToolList) => void\n },\n): () => void {\n const unsubscribe = hooks.hook('mcp:tools:list', (ctx) => {\n const entry: CachedMcpToolList = {\n tools: [...ctx.tools],\n updatedAt: Date.now(),\n transport: ctx.transport,\n }\n try {\n const current = loadMcpToolsCache({ dataDir: opts.dataDir })\n const next: McpToolsCache = { ...current, [ctx.name]: entry }\n saveMcpToolsCache({ dataDir: opts.dataDir, cache: next })\n opts.onChange?.({ name: ctx.name, ...entry })\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] mcp-tools-cache write failed for \"${ctx.name}\": ${errorMessage(err)}\\n`)\n }\n })\n return unsubscribe\n}\n\n/**\n * Force-refresh the on-disk tools cache by opening a transient MCP\n * connection to every server, capturing the `mcp:tools:list` hook on\n * the way through, and closing the connection immediately. The\n * underlying `connectMcpServers` runs every server's bootstrap in\n * parallel, so the wall-clock cost collapses to the slowest server.\n *\n * Two design choices worth noting:\n *\n * - **Shared hook bus.** The caller passes the SAME `Hookable`\n * instance that has the cache subscriber wired (typically\n * `agent.hooks` after `subscribeMcpToolsCache(agent.hooks, …)`\n * ran). That way `mcp:tools:list` fires on the bus the\n * subscriber is listening to and the cache lands on disk\n * without a separate write path.\n * - **Transient, NOT shared with the live agent.** The connection\n * opened here is closed immediately. The agent's own MCP\n * connection is untouched, so a refresh never disrupts an\n * in-flight tool call.\n *\n * The function intentionally swallows per-server failures (they're\n * already surfaced via `mcp:bootstrap:end ok: false` for any host\n * that wired the listener). The Promise resolves once every server's\n * bootstrap settles, success or otherwise.\n */\nexport async function refreshMcpToolsCatalog(opts: {\n servers: readonly McpServerConfig[]\n hooks: Hookable<AgentHooks>\n /**\n * Same OAuth-provider factory the agent's bootstrap uses. Required\n * for OAuth-tagged servers — without it those servers fire\n * `mcp:auth:required` and skip, and their tools never refresh.\n */\n buildAuthProvider?: (config: McpServerConfig) => OAuthClientProvider | undefined\n}): Promise<void> {\n if (opts.servers.length === 0)\n return\n const connection = await connectMcpServers(\n opts.servers as McpServerConfig[],\n undefined,\n opts.hooks,\n opts.buildAuthProvider ? { buildAuthProvider: opts.buildAuthProvider } : undefined,\n )\n // `mcp:tools:list` already fired during the bootstrap (see\n // `src/mcp/index.ts:bootstrapServer`) so the cache subscriber has\n // already taken its snapshot. Close fast — leaving the transient\n // connection alive would double the MCP socket count for no gain.\n await connection.close().catch((err) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] mcp-tools-cache transient close failed: ${errorMessage(err)}\\n`)\n })\n}\n\n/**\n * Type-narrowing check used by {@link loadMcpToolsCache}. Kept private\n * — callers that want a richer validation surface should layer it on\n * top of the loose disk shape rather than tightening this predicate.\n */\nfunction isCachedEntry(value: unknown): value is CachedMcpToolList {\n if (!value || typeof value !== 'object')\n return false\n const entry = value as Partial<CachedMcpToolList>\n if (!Array.isArray(entry.tools))\n return false\n if (typeof entry.updatedAt !== 'number' || !Number.isFinite(entry.updatedAt))\n return false\n if (entry.transport !== 'stdio' && entry.transport !== 'sse' && entry.transport !== 'streamable-http')\n return false\n // Per-tool shape is intentionally NOT validated — the MCP SDK's tool\n // schema surface is open-ended (`inputSchema: unknown`) and we want\n // the cache to keep working when an upstream tool sprouts a new\n // field zidane doesn't model yet. Downstream readers handle unknowns.\n return true\n}\n","/**\n * OAuth login flow exposed to the TUI.\n *\n * Driven by each {@link ProviderDescriptor}'s `oauthProvider` field. Built-in\n * descriptors wire pi-ai's `anthropicOAuthProvider` and `openaiCodexOAuthProvider`,\n * but hosts can pass any object conforming to `OAuthProviderInterface` — the\n * TUI is implementation-agnostic past the descriptor boundary.\n */\n\nimport type {\n OAuthCredentials,\n OAuthDeviceCodeInfo,\n OAuthLoginCallbacks,\n OAuthPrompt,\n OAuthSelectPrompt,\n} from '@earendil-works/pi-ai/oauth'\nimport type { ProviderDescriptor } from './providers'\nimport { tryOpenBrowser } from './browser'\n\nexport type {\n OAuthDeviceCodeInfo,\n OAuthPrompt,\n OAuthSelectOption,\n OAuthSelectPrompt,\n} from '@earendil-works/pi-ai/oauth'\n\nexport function supportsOAuth(descriptor: ProviderDescriptor): boolean {\n return descriptor.oauthProvider !== undefined\n}\n\n/** True when the provider's OAuth flow needs the user to paste a code back into the TUI (no loopback callback). */\nexport function oauthUsesManualCodePaste(descriptor: ProviderDescriptor): boolean {\n return descriptor.oauthProvider?.usesCallbackServer === false\n}\n\nexport interface OAuthFlowOptions {\n /** Called when the provider emits its login URL — typically right after the callback server starts. */\n onUrl: (url: string, instructions?: string) => void\n /**\n * Called when the provider needs a free-form input from the user (paste-the-code flows,\n * step-up confirmations, etc.). Mirrors pi-ai's `OAuthLoginCallbacks.onPrompt` verbatim\n * so callers don't lose `placeholder` / `allowEmpty` hints. Required for any provider\n * with `usesCallbackServer: false` (e.g. Anthropic Claude Pro/Max).\n */\n onPrompt?: (prompt: OAuthPrompt) => Promise<string>\n /**\n * Called when the provider initiates a device-code flow (e.g. GitHub Copilot).\n * Surface `userCode` + `verificationUri` so the user can complete the flow in\n * the browser. Required by pi-ai's `OAuthLoginCallbacks` since 0.75.5 — built-in\n * Anthropic / OpenAI Codex providers don't fire it today, but third-party\n * descriptors can.\n */\n onDeviceCode?: (info: OAuthDeviceCodeInfo) => void\n /**\n * Called when the provider needs the user to pick between multiple flow options\n * (e.g. browser vs device-code). Return the chosen option `id`, or `undefined`\n * to cancel. Required by pi-ai's `OAuthLoginCallbacks` since 0.75.5.\n */\n onSelect?: (prompt: OAuthSelectPrompt) => Promise<string | undefined>\n /** Called with each progress message from the OAuth flow (token exchange, etc.). */\n onProgress?: (message: string) => void\n /** Abort the in-flight login (e.g. user pressed esc). */\n signal?: AbortSignal\n}\n\n/**\n * Run the OAuth login flow for a provider.\n *\n * Returns the OAuth credentials on success; caller persists them via\n * `setProviderCredential(dataDir, descriptor, { kind: 'oauth', ...credentials })`.\n * Throws when the descriptor has no `oauthProvider` configured.\n */\nexport async function runOAuthLogin(\n descriptor: ProviderDescriptor,\n options: OAuthFlowOptions,\n): Promise<OAuthCredentials> {\n if (!descriptor.oauthProvider) {\n throw new Error(\n `OAuth not supported for ${descriptor.label} (${descriptor.key}). Use an API key instead.`,\n )\n }\n\n // Set once a device flow surfaces its code. Providers may fire BOTH\n // callbacks for the same flow (Bolt does: `onDeviceCode` for the code panel,\n // then `onAuth` with the code-embedding verification URI). The device-code\n // handler already surfaced the URL and launched the browser, so the trailing\n // `onAuth` must not open a second tab — or push redirect-flow status copy\n // (\"paste the code below\") at a UI whose paste input device flows hide.\n let deviceFlowActive = false\n\n const callbacks: OAuthLoginCallbacks = {\n onAuth: (info) => {\n if (deviceFlowActive)\n return\n options.onUrl(info.url, info.instructions)\n // Best-effort browser launch. If `open`/`xdg-open` isn't available, the\n // user can still click the URL from the TUI — the callback server is\n // already listening either way.\n void tryOpenBrowser(info.url)\n },\n onDeviceCode: (info) => {\n deviceFlowActive = true\n if (options.onDeviceCode) {\n options.onDeviceCode(info)\n return\n }\n // No handler wired — built-in providers (Anthropic, OpenAI Codex) never\n // hit this path, so it's only reachable for third-party descriptors.\n // Throwing here would crash mid-flow. Route through `onUrl` — the one\n // REQUIRED callback, so every host renders something visible even when\n // the best-effort browser launch below fails (headless boxes) — and\n // mirror it to progress for hosts that log rather than pin URLs.\n options.onUrl(info.verificationUri, `Enter code ${info.userCode} to authorize.`)\n options.onProgress?.(\n `Device code: ${info.userCode}. Open ${info.verificationUri}`,\n )\n void tryOpenBrowser(info.verificationUri)\n },\n onPrompt: async (prompt) => {\n if (!options.onPrompt) {\n throw new Error(\n `OAuth provider \"${descriptor.label}\" requested user input (\"${prompt.message}\") but no onPrompt handler is wired.`,\n )\n }\n return options.onPrompt(prompt)\n },\n onSelect: async (prompt) => {\n if (options.onSelect)\n return options.onSelect(prompt)\n // Same fallback rationale as `onDeviceCode`. Default to the first option\n // so providers that gate the flow behind a choice don't deadlock the TUI.\n return prompt.options[0]?.id\n },\n onProgress: options.onProgress,\n signal: options.signal,\n }\n\n return descriptor.oauthProvider.login(callbacks)\n}\n","/**\n * Manual OAuth redirect-URL handoff.\n *\n * Users who run zidane over SSH (or behind a proxy / firewall that blocks\n * loopback) can't have the local browser hit the in-process callback server.\n * They CAN, however, copy the URL their browser was redirected to —\n * `http://127.0.0.1:<port>/callback?code=...&state=...` — and paste it back\n * into the TUI.\n *\n * The trick: that URL IS the callback our local server is listening on. We\n * just `fetch()` it ourselves. The server runs in the same process; the\n * request hits its handler exactly as if a real browser had arrived, the\n * OAuth promise (`waitForCode` for pi-ai providers, `startOAuthCallback` for\n * MCP) resolves through the normal happy path, and the upstream flow\n * continues uninterrupted. No new code path inside the OAuth state machine.\n *\n * Defense in depth: we reject anything that isn't a loopback URL — fetching\n * an arbitrary user-pasted URL from inside the agent process would be a\n * trivial SSRF.\n */\n\nconst LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1', '[::1]'])\n\n/**\n * The `redirect_uri` the authorization server was told to come back to,\n * read out of the authorization URL itself. That's our loopback callback\n * (`http://127.0.0.1:<port>/callback`) — the UI shows it so an SSH user\n * knows which address to fish out of the browser's URL bar, and\n * {@link normalizeRedirectPaste} uses it to rebuild a full callback URL\n * from a partial paste.\n *\n * Returns `null` when the URL doesn't parse or carries no usable\n * `redirect_uri` — callers degrade to \"paste the whole URL\".\n */\nexport function redirectUriFromAuthUrl(authUrl: string): URL | null {\n let parsed: URL\n try {\n parsed = new URL(authUrl)\n }\n catch {\n return null\n }\n const redirect = parsed.searchParams.get('redirect_uri')\n if (!redirect)\n return null\n try {\n return new URL(redirect)\n }\n catch {\n return null\n }\n}\n\n/**\n * Widen what counts as an acceptable paste. The happy path is the full\n * redirect URL out of the browser's address bar, but users on SSH\n * routinely end up holding less than that: some authorization servers\n * render the code on the page instead of redirecting, and copying from a\n * \"can't reach this site\" error page loses everything but the query.\n *\n * Accepted forms, given the authorization URL for context:\n *\n * - `http://127.0.0.1:5123/callback?code=x&state=y` — passthrough (any\n * absolute URL is returned untouched; the loopback guard in\n * {@link fetchOAuthRedirect} still has final say, so a pasted\n * `https://evil.test` is rejected there, not silently rewritten).\n * - `/callback?code=x&state=y` — path + query onto the callback origin.\n * - `?code=x&state=y` / `code=x&state=y` — query onto the callback URL.\n * - `x` — a bare authorization code; `state` is taken from the\n * authorization URL when it carried one (the loopback server forwards\n * `state` verbatim and the SDK owns verification, so a missing one is\n * not fatal). Anything with whitespace inside is prose (an error page\n * fragment), not a code — refused with a clearer error than a server\n * 400 would give.\n *\n * Throws when the input can't be turned into a URL — no `authUrl` context\n * for a partial paste, or an unusable authorization URL.\n */\nexport function normalizeRedirectPaste(pasted: string, authUrl?: string): string {\n const trimmed = pasted.trim()\n if (!trimmed)\n throw new Error('Paste the redirect URL from your browser.')\n // Absolute URL → caller's problem to validate. Checked by scheme rather\n // than `new URL()` success so a bare code like `abc` (which URL happily\n // rejects) and a scheme-ful `mcp://…` are told apart deterministically.\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(trimmed))\n return trimmed\n\n const callback = authUrl ? redirectUriFromAuthUrl(authUrl) : null\n if (!callback) {\n throw new Error(\n 'That doesn\\'t look like a URL. Paste the full address your browser tried to reach (starting with http://127.0.0.1).',\n )\n }\n\n // `/callback?code=…` — keep the pasted path, it may differ from the one\n // we registered (a server that normalized a trailing slash, say). Same\n // outcome guard as the query branch below: a bare `/callback` carries no\n // OAuth result, and fetching it would only produce an opaque server 400\n // where a targeted message tells the user exactly what to re-copy.\n if (trimmed.startsWith('/')) {\n const merged = new URL(trimmed, callback)\n if (!merged.searchParams.has('code') && !merged.searchParams.has('error'))\n throw new Error('That path has no \"code\" parameter. Paste the full address from your browser\\'s URL bar.')\n return merged.toString()\n }\n\n const target = new URL(callback.toString())\n const query = trimmed.startsWith('?') ? trimmed.slice(1) : trimmed\n // A bare authorization code is a single URL-legal token — including\n // base64 padding, so trailing `=` stays code-like (`abc==`). Any OTHER\n // placement of `=` means key/value pairs (`foo=bar`, `state=…&code=…`):\n // treat as a query string, never as a code — URL-encoding `foo=bar` into\n // `code=foo%3Dbar` would fire a doomed request where a targeted error\n // tells the user what to re-copy.\n const codeLike = /^[^=\\s]+=*$/.test(query)\n if (!codeLike && query.includes('=')) {\n const params = new URLSearchParams(query)\n if (!params.has('code') && !params.has('error'))\n throw new Error('That fragment has no \"code\" parameter. Paste the full address from your browser, or just the code.')\n for (const [key, value] of params)\n target.searchParams.set(key, value)\n return target.toString()\n }\n\n // Bare code. Whitespace-bearing pastes never reach here (`codeLike`\n // excludes them) except as pure prose without `=`, so keep the explicit\n // reject for those: sending `code=access%20denied` to the callback\n // server would 400 without explaining anything.\n if (/\\s/.test(query))\n throw new Error('That doesn\\'t look like an authorization code. Paste the full address from your browser, or just the code itself.')\n target.searchParams.set('code', query)\n const state = new URL(authUrl!).searchParams.get('state')\n if (state)\n target.searchParams.set('state', state)\n return target.toString()\n}\n\nexport interface FetchOAuthRedirectResult {\n /** Status code from the callback server. 2xx = success. */\n status: number\n /** Best-effort message extracted from the response body (HTML title or trimmed text). */\n message?: string\n}\n\n/**\n * Treat `pasted` as a callback-URL paste. Validates it's a loopback URL,\n * fires a GET, returns the status.\n *\n * Pass `authUrl` (the authorization URL currently on screen) to accept\n * partial pastes — a bare code, a query string, a path — via\n * {@link normalizeRedirectPaste}. Without it only a full absolute URL is\n * accepted, which is the pre-existing contract.\n *\n * Throws when:\n * - `pasted` doesn't parse as a URL (and can't be completed from `authUrl`).\n * - The URL host isn't loopback (rejects SSRF).\n * - The fetch errors out (network, timeout).\n *\n * Returns success/failure for non-2xx responses; the caller decides whether\n * a 4xx is fatal (state mismatch is a 400 the user can retry from).\n */\nexport async function fetchOAuthRedirect(\n pasted: string,\n options: { signal?: AbortSignal, timeoutMs?: number, authUrl?: string } = {},\n): Promise<FetchOAuthRedirectResult> {\n const trimmed = normalizeRedirectPaste(pasted, options.authUrl)\n\n let url: URL\n try {\n url = new URL(trimmed)\n }\n catch {\n throw new Error('That doesn\\'t look like a URL. Paste the full address from your browser.')\n }\n\n // Strip brackets that some browsers add around IPv6 hosts so the\n // comparison hits LOOPBACK_HOSTS regardless of textual form.\n const host = url.hostname.replace(/^\\[|\\]$/g, '')\n if (!LOOPBACK_HOSTS.has(host)) {\n throw new Error(\n `Expected a loopback URL (127.0.0.1 / localhost), got \"${url.hostname}\". The browser should have redirected to a localhost address.`,\n )\n }\n\n // Short timeout: the callback server is in-process, so any meaningful\n // delay means it's gone (closed by a prior abort) or never started.\n const timeoutMs = options.timeoutMs ?? 5000\n const ac = new AbortController()\n const timer = setTimeout(() => ac.abort(), timeoutMs)\n options.signal?.addEventListener('abort', () => ac.abort(), { once: true })\n\n let response: Response\n try {\n response = await fetch(url.toString(), { signal: ac.signal })\n }\n catch (err) {\n if ((err as Error).name === 'AbortError')\n throw new Error('No response from the local callback server. Was the login cancelled?')\n throw new Error(`Could not reach the local callback server: ${(err as Error).message}`)\n }\n finally {\n clearTimeout(timer)\n }\n\n const bodyText = await response.text().catch(() => '')\n return { status: response.status, message: extractMessage(bodyText) }\n}\n\n/**\n * Pick the most useful one-line message out of the callback server's HTML\n * response. pi-ai uses `<h1>` for the heading; the MCP callback uses\n * a leading `<p>`. We try both, then fall back to a stripped snippet.\n */\nfunction extractMessage(html: string): string | undefined {\n if (!html)\n return undefined\n const h1 = /<h1[^>]*>([\\s\\S]*?)<\\/h1>/i.exec(html)?.[1]\n if (h1)\n return stripTags(h1).trim() || undefined\n const p = /<p[^>]*>([\\s\\S]*?)<\\/p>/i.exec(html)?.[1]\n if (p)\n return stripTags(p).trim() || undefined\n return undefined\n}\n\nfunction stripTags(s: string): string {\n return s.replace(/<[^>]*>/g, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '\"').replace(/'/g, '\\'')\n}\n","/**\n * @-completion path display formatter.\n *\n * The `@`-popover walks the project from the git root so the user sees\n * the whole tree regardless of where they launched the TUI. The\n * agent's CWD-resolving tools (`read_file`, `glob`, `edit`, …) read\n * relative paths against `process.cwd()`, which is frequently a\n * subdirectory of that root. Emitting the discovery's raw\n * project-root-relative path into the prompt produces a `<cwd>/<rel>`\n * miss — the bug this helper exists to close.\n *\n * Rule:\n *\n * - File at `cwd` itself → `.`.\n * - File under `cwd` (subtree) → forward-slashed CWD-relative,\n * no `..`.\n * - File above `cwd` but still inside the project → CWD-relative\n * with `..` traversal (e.g. `../EDIT_THIS.md`). Shorter and more\n * scannable than absolute, and the agent's tools resolve both.\n * - File outside the project (input was passed absolute) → returned\n * verbatim. Inputs that arrive as project-root-relative strings\n * are always inside the project by construction, so this branch\n * only kicks in when a direct caller hands the function an\n * absolute target.\n *\n * Pure, no I/O, deterministic.\n */\n\nimport { isAbsolute, posix, relative, resolve, sep } from 'node:path'\n\n/**\n * Convert a project-root-relative path (as emitted by `listProjectFiles`)\n * into the form the agent's tools — and the user — will actually find\n * given the current `cwd`.\n *\n * Inputs:\n * - `projectRelativePath` — forward-slashed, no leading slash. Returned\n * verbatim when it's empty or already absolute (defensive — the\n * absolute-input branch is the de-facto \"outside the project\"\n * escape; the catalog itself never emits absolute paths).\n * - `projectRoot` — absolute path of the discovery anchor.\n * - `cwd` — absolute path of `process.cwd()` at the moment of\n * insertion.\n *\n * Output is always either an absolute path (only when the input arrived\n * absolute) OR a CWD-relative path. The relative form may include\n * `../` segments when the target sits above `cwd` within the project,\n * which keeps cross-tree references short and scannable\n * (e.g. `../EDIT_THIS.md` instead of `/Users/.../zidane/EDIT_THIS.md`).\n */\nexport function formatPathForCwd(\n projectRelativePath: string,\n projectRoot: string,\n cwd: string,\n): string {\n if (projectRelativePath.length === 0)\n return projectRelativePath\n if (isAbsolute(projectRelativePath))\n return projectRelativePath\n const target = resolve(projectRoot, projectRelativePath)\n const here = resolve(cwd)\n if (target === here)\n return '.'\n const rel = relative(here, target)\n if (rel.length === 0)\n return '.'\n return sep === '/' ? rel : rel.split(sep).join(posix.sep)\n}\n","/**\n * Subscription usage for the providers we authenticate via OAuth, plus\n * OpenRouter's credits endpoint.\n *\n * Probes are keyed by CREDENTIAL FILE KEY, so any descriptor storing its tokens\n * under a known key resolves the right one:\n * - `anthropic` `GET api.anthropic.com/api/oauth/usage`\n * (OAuth bearer + `anthropic-beta: oauth-2025-04-20`, and\n * the `user:profile` scope the login flow requests)\n * - `openai-codex` `GET chatgpt.com/backend-api/wham/usage`\n * (Codex OAuth bearer + `ChatGPT-Account-Id`)\n * - `xai-oauth` `POST grok.com` gRPC-web billing probe. Best-effort: xAI\n * has no token-auth usage API and this endpoint is\n * cookie-oriented, so it often refuses a bearer.\n * - `openrouter` `GET openrouter.ai/api/v1/credits` (official, API key)\n *\n * Only the OpenRouter endpoint is official; the others can change without\n * notice. So every probe is read-only and never throws: failures normalize into\n * a snapshot with `status: 'unauthorized' | 'unavailable'` and a short `error`,\n * which callers render as a state rather than handle as an error.\n *\n * Tokens are never refreshed here. The run path owns refresh (see\n * `providers/oauth.ts`) and refresh tokens are single-use, so a background\n * usage read must not spend one. An expired access token reports\n * `'unauthorized'` without issuing a request.\n *\n * Consent is two-tiered, mirroring the update check: hosts gate automatic\n * probes on `Settings.subscriptionUsage`, and {@link usageChecksOptedOut}\n * (`ZIDANE_NO_USAGE_CHECK` / `CI`) disables every probe here, explicit\n * refreshes included.\n */\n\nimport type { ProviderCredential } from './credentials'\nimport type { ProviderUsageSnapshot, ProviderUsageWindow } from './provider-usage-format'\nimport type { ProviderDescriptor } from './providers'\nimport { readCredentials } from './credentials'\nimport { credKeyOf } from './providers'\n\n// Wire types + presentation helpers live in the node-free sibling so the\n// Electron renderer can share them; re-exported here to keep one import path.\nexport type { ProviderUsageCredits, ProviderUsageSnapshot, ProviderUsageStatus, ProviderUsageWindow } from './provider-usage-format'\nexport { formatBalance, formatCredits, formatUsagePercent, mostConstrainedWindow, resetLabel, type UsageTone, usageTone } from './provider-usage-format'\n\n/** Per-request timeout. Generous, since these endpoints are occasionally slow. */\nconst FETCH_TIMEOUT_MS = 15_000\n\n/** How long a snapshot stays fresh. Usage windows move in minutes, not seconds. */\nexport const USAGE_CACHE_TTL_MS = 60_000\n\n/** Which probe a credential key maps to. */\ntype UsageSource = 'claude' | 'codex' | 'grok' | 'openrouter'\n\n/**\n * Credential-file key to probe. Keyed by credential key rather than provider\n * key so a custom descriptor reusing a builtin credential slot\n * (`credentialFileKey: 'anthropic'`) gets usage for free.\n */\nconst USAGE_SOURCE_BY_CRED_KEY: Readonly<Record<string, UsageSource>> = {\n 'anthropic': 'claude',\n 'openai-codex': 'codex',\n 'xai-oauth': 'grok',\n 'openrouter': 'openrouter',\n}\n\n/** Credential kind each probe authenticates with. */\nconst SOURCE_CRED_KIND: Readonly<Record<UsageSource, ProviderCredential['kind']>> = {\n claude: 'oauth',\n codex: 'oauth',\n grok: 'oauth',\n openrouter: 'apikey',\n}\n\n/** The probe for a descriptor, or `null` when it has no usage surface. */\nexport function usageSourceFor(descriptor: ProviderDescriptor): UsageSource | null {\n return USAGE_SOURCE_BY_CRED_KEY[credKeyOf(descriptor)] ?? null\n}\n\n/** Whether this provider can report subscription usage at all. */\nexport function providerUsageSupported(descriptor: ProviderDescriptor): boolean {\n return usageSourceFor(descriptor) !== null\n}\n\n// ---------------------------------------------------------------------------\n// Cache + entry points\n// ---------------------------------------------------------------------------\n\ninterface CacheEntry {\n expiresAt: number\n snapshot: ProviderUsageSnapshot\n}\n\n/** Keyed by credential key: two descriptors sharing tokens share a snapshot. */\nconst cache = new Map<string, CacheEntry>()\n/** In-flight probes, so a footer read and a panel open share one request. */\nconst inFlight = new Map<string, Promise<ProviderUsageSnapshot>>()\n/**\n * Newest probe issued per credential key, as a monotonic ticket. `refresh`\n * skips both maps above, so two probes for one key can be outstanding at once\n * (panel refresh while the footer's post-run refresh is still in flight); only\n * the newest may write the cache, or a slow older probe would pin stale\n * numbers for a full TTL. Clearing the map also invalidates every outstanding\n * ticket, so a probe that spans `clearProviderUsageCache()` cannot repopulate\n * the cache it was meant to drop.\n */\nconst latestProbe = new Map<string, number>()\nlet probeTicket = 0\n\n/**\n * Drop every cached snapshot. Entries are keyed by credential KEY, not by the\n * token itself, so signing out and back in (or re-authing to another account)\n * would otherwise keep serving the previous account's numbers for the rest of\n * the TTL. The TUI calls this wherever it re-reads credentials.\n */\nexport function clearProviderUsageCache(): void {\n cache.clear()\n inFlight.clear()\n latestProbe.clear()\n}\n\n// ---------------------------------------------------------------------------\n// Env opt-outs\n//\n// Same shape as the update check's (see `chat/auto-update.ts`): these probes\n// are the only other background network calls the TUI makes, and they talk to\n// hosts that are not the ones inference goes to, so an air-gapped, CI, or\n// privacy-sensitive environment must be able to switch them off without\n// touching settings. `ZIDANE_NO_USAGE_CHECK` is the project knob; `CI` is set\n// by every major runner and skipping there avoids surprise chatter from build\n// pipelines. Unlike the setting, this is absolute: it also blocks the panel's\n// explicit refresh.\n// ---------------------------------------------------------------------------\n\nconst OPT_OUT_VARS = ['ZIDANE_NO_USAGE_CHECK', 'CI'] as const\n\n/** Whether the environment forbids usage probes outright. */\nexport function usageChecksOptedOut(env: NodeJS.ProcessEnv = process.env): boolean {\n for (const key of OPT_OUT_VARS) {\n const v = env[key]\n if (v !== undefined && v !== '' && v !== '0' && v.toLowerCase() !== 'false')\n return true\n }\n return false\n}\n\nexport interface FetchProviderUsageOptions {\n /** Ignore (and replace) a still-fresh cached snapshot. */\n refresh?: boolean\n}\n\n/**\n * Usage for one provider. `null` means there is nothing to show: the provider\n * has no usage surface, no credential of the kind its probe needs (file or,\n * for API keys, env), or the environment opted out (see\n * {@link usageChecksOptedOut}).\n *\n * Never rejects. Transport and parse failures land in the snapshot.\n */\nexport async function fetchProviderUsage(\n dataDir: string,\n descriptor: ProviderDescriptor,\n opts: FetchProviderUsageOptions = {},\n): Promise<ProviderUsageSnapshot | null> {\n const source = usageSourceFor(descriptor)\n if (!source || usageChecksOptedOut())\n return null\n const credKey = credKeyOf(descriptor)\n const cred = resolveUsageCredential(dataDir, descriptor, source)\n if (!cred)\n return null\n\n // Identity is re-stamped on every return path: the cache is keyed by\n // credential, so a snapshot can come back from a probe another descriptor\n // started, and it must still name the provider the caller asked about.\n const identify = (snapshot: ProviderUsageSnapshot): ProviderUsageSnapshot => ({\n ...snapshot,\n providerKey: descriptor.key,\n label: descriptor.label,\n })\n\n if (!opts.refresh) {\n const cached = cache.get(credKey)\n if (cached && cached.expiresAt > Date.now())\n return identify(cached.snapshot)\n const pending = inFlight.get(credKey)\n if (pending)\n return pending.then(identify)\n }\n\n const ticket = ++probeTicket\n latestProbe.set(credKey, ticket)\n const probe = runProbe(source, descriptor, cred)\n .then((snapshot) => {\n if (latestProbe.get(credKey) === ticket)\n cache.set(credKey, { expiresAt: Date.now() + USAGE_CACHE_TTL_MS, snapshot })\n return snapshot\n })\n .finally(() => {\n if (inFlight.get(credKey) === probe)\n inFlight.delete(credKey)\n })\n inFlight.set(credKey, probe)\n return probe.then(identify)\n}\n\n/**\n * The credential a probe authenticates with, or `null` when the provider isn't\n * authenticated in a way that probe can use.\n *\n * An API key is taken from the environment when the file has none: `detectAuth`\n * marks a provider available on a shell-set `envKey` alone, and the launcher\n * copies stored keys INTO env, so the two are the same signal. Reading only the\n * file would leave an env-authed user with no row at all, under an empty state\n * telling them they aren't signed in. OAuth tokens have no env equivalent, so\n * those probes stay file-only.\n */\nfunction resolveUsageCredential(\n dataDir: string,\n descriptor: ProviderDescriptor,\n source: UsageSource,\n): ProviderCredential | null {\n const kind = SOURCE_CRED_KIND[source]\n const stored = readCredentials(dataDir)[credKeyOf(descriptor)]\n // A stored API key with an empty value is a customFields-only credential\n // (see `ApiKeyCredential.value`), so fall through to env rather than probing\n // with nothing.\n if (stored?.kind === kind && (stored.kind !== 'apikey' || stored.value))\n return stored\n if (kind === 'apikey' && descriptor.envKey) {\n const fromEnv = process.env[descriptor.envKey]\n if (fromEnv)\n return { kind: 'apikey', value: fromEnv }\n }\n return null\n}\n\n/**\n * Usage for every registered provider that has both a usage surface and a\n * usable credential, in registry order. What the panel renders.\n */\nexport async function fetchAllProviderUsage(\n dataDir: string,\n registry: Readonly<Record<string, ProviderDescriptor>>,\n opts: FetchProviderUsageOptions = {},\n): Promise<readonly ProviderUsageSnapshot[]> {\n const candidates = Object.values(registry).filter(providerUsageSupported)\n const settled = await Promise.all(\n candidates.map(descriptor => fetchProviderUsage(dataDir, descriptor, opts)),\n )\n return settled.filter((snapshot): snapshot is ProviderUsageSnapshot => snapshot !== null)\n}\n\n// ---------------------------------------------------------------------------\n// Probes\n// ---------------------------------------------------------------------------\n\nasync function runProbe(\n source: UsageSource,\n descriptor: ProviderDescriptor,\n cred: ProviderCredential,\n): Promise<ProviderUsageSnapshot> {\n const base = { providerKey: descriptor.key, label: descriptor.label }\n try {\n switch (source) {\n case 'claude':\n return await fetchClaudeUsage(base, cred)\n case 'codex':\n return await fetchCodexUsage(base, cred)\n case 'grok':\n return await fetchGrokUsage(base, cred)\n case 'openrouter':\n return await fetchOpenRouterUsage(base, cred)\n }\n }\n catch (err) {\n // Covers `AbortSignal.timeout` firing. A slow endpoint is unavailable, not\n // an error the caller should handle.\n return snapshot(base, { error: err instanceof Error ? err.message : 'request failed' })\n }\n}\n\n/** Identity half of a snapshot, resolved once per probe. */\ntype SnapshotBase = Pick<ProviderUsageSnapshot, 'providerKey' | 'label'>\n\n/** Snapshot factory. Defaults to `unavailable` so failure paths stay one-liners. */\nfunction snapshot(base: SnapshotBase, partial: Partial<ProviderUsageSnapshot> = {}): ProviderUsageSnapshot {\n return {\n ...base,\n status: 'unavailable',\n plan: null,\n windows: [],\n credits: null,\n fetchedAt: Date.now(),\n error: null,\n ...partial,\n }\n}\n\n/**\n * OAuth access token, or an `unauthorized` snapshot explaining why there isn't\n * one. Expiry is checked without refreshing (see the module header); the next\n * run refreshes it.\n */\nfunction accessTokenOr(\n base: SnapshotBase,\n cred: ProviderCredential,\n): { token: string } | { fail: ProviderUsageSnapshot } {\n if (cred.kind !== 'oauth' || !cred.access)\n return { fail: snapshot(base, { status: 'unauthorized', error: 'not signed in' }) }\n if (typeof cred.expires === 'number' && cred.expires <= Date.now())\n return { fail: snapshot(base, { status: 'unauthorized', error: 'token expired' }) }\n return { token: cred.access }\n}\n\n/**\n * 401/403 to unauthorized, other non-2xx to unavailable. `null` when the\n * response is fine.\n *\n * A rejected token reads in the same register as the local checks (`not signed\n * in`, `token expired`): it is the row a real user hits most, and the status\n * code tells them nothing. Other codes keep the number, where it is the useful\n * detail.\n */\nfunction httpFailure(base: SnapshotBase, res: Response): ProviderUsageSnapshot | null {\n if (res.status === 401 || res.status === 403)\n return snapshot(base, { status: 'unauthorized', error: 'token rejected' })\n if (!res.ok)\n return snapshot(base, { error: `HTTP ${res.status}` })\n return null\n}\n\n// --- Claude ----------------------------------------------------------------\n\ninterface ClaudeWindow { utilization?: number, resets_at?: string | null }\n\nconst CLAUDE_WINDOWS: readonly { key: string, id: string, label: string }[] = [\n { key: 'five_hour', id: 'five_hour', label: '5h' },\n { key: 'seven_day', id: 'seven_day', label: 'weekly' },\n { key: 'seven_day_opus', id: 'seven_day_opus', label: 'opus weekly' },\n { key: 'seven_day_sonnet', id: 'seven_day_sonnet', label: 'sonnet weekly' },\n]\n\nasync function fetchClaudeUsage(base: SnapshotBase, cred: ProviderCredential): Promise<ProviderUsageSnapshot> {\n const auth = accessTokenOr(base, cred)\n if ('fail' in auth)\n return auth.fail\n\n const res = await fetch('https://api.anthropic.com/api/oauth/usage', {\n headers: {\n 'Authorization': `Bearer ${auth.token}`,\n 'anthropic-beta': 'oauth-2025-04-20',\n 'Accept': 'application/json',\n },\n signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n })\n const failure = httpFailure(base, res)\n if (failure)\n return failure\n\n const data = await res.json().catch(() => null) as Record<string, unknown> | null\n if (!data)\n return snapshot(base, { error: 'invalid response' })\n\n const windows: ProviderUsageWindow[] = []\n for (const { key, id, label } of CLAUDE_WINDOWS) {\n const win = data[key] as ClaudeWindow | undefined\n if (win && typeof win.utilization === 'number') {\n windows.push({\n id,\n label,\n usedPercent: clampPercent(win.utilization),\n resetsAt: isoToEpoch(win.resets_at),\n })\n }\n }\n if (windows.length === 0)\n return snapshot(base, { error: 'no usage data' })\n return snapshot(base, { status: 'ok', windows })\n}\n\n// --- OpenAI Codex ----------------------------------------------------------\n\ninterface CodexWindow { used_percent?: number, reset_at?: number, limit_window_seconds?: number }\n\n/** Codex reports window length, not a name, so derive `5h` / `weekly` / `Nd`. */\nfunction codexWindowLabel(win: CodexWindow, fallback: string): string {\n const seconds = win.limit_window_seconds\n if (!seconds)\n return fallback\n if (seconds < 86_400)\n return `${Math.round(seconds / 3600)}h`\n const days = Math.round(seconds / 86_400)\n return days === 7 ? 'weekly' : `${days}d`\n}\n\nasync function fetchCodexUsage(base: SnapshotBase, cred: ProviderCredential): Promise<ProviderUsageSnapshot> {\n const auth = accessTokenOr(base, cred)\n if ('fail' in auth)\n return auth.fail\n const accountId = cred.kind === 'oauth' && typeof cred.accountId === 'string' ? cred.accountId : null\n\n const res = await fetch('https://chatgpt.com/backend-api/wham/usage', {\n headers: {\n 'Authorization': `Bearer ${auth.token}`,\n ...(accountId ? { 'ChatGPT-Account-Id': accountId } : {}),\n 'User-Agent': 'codex-cli',\n 'Accept': 'application/json',\n },\n signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n })\n const failure = httpFailure(base, res)\n if (failure)\n return failure\n\n const data = await res.json().catch(() => null) as {\n plan_type?: string\n rate_limit?: { primary_window?: CodexWindow, secondary_window?: CodexWindow }\n /** Some responses use the plural key. Both are accepted. */\n rate_limits?: { primary_window?: CodexWindow, secondary_window?: CodexWindow }\n credits?: { has_credits?: boolean, unlimited?: boolean, balance?: number }\n } | null\n if (!data)\n return snapshot(base, { error: 'invalid response' })\n\n const rateLimit = data.rate_limit ?? data.rate_limits\n const windows: ProviderUsageWindow[] = []\n const push = (win: CodexWindow | undefined, id: string, fallbackLabel: string) => {\n if (win && typeof win.used_percent === 'number') {\n windows.push({\n id,\n label: codexWindowLabel(win, fallbackLabel),\n usedPercent: clampPercent(win.used_percent),\n resetsAt: typeof win.reset_at === 'number' ? win.reset_at * 1000 : null,\n })\n }\n }\n push(rateLimit?.primary_window, 'primary', '5h')\n push(rateLimit?.secondary_window, 'secondary', 'weekly')\n\n const credits = data.credits && typeof data.credits.balance === 'number' && !data.credits.unlimited\n ? { remaining: data.credits.balance, total: null }\n : null\n\n if (windows.length === 0 && !credits)\n return snapshot(base, { error: 'no usage data' })\n return snapshot(base, {\n status: 'ok',\n plan: typeof data.plan_type === 'string' ? data.plan_type : null,\n windows,\n credits,\n })\n}\n\n// --- OpenRouter ------------------------------------------------------------\n\nasync function fetchOpenRouterUsage(base: SnapshotBase, cred: ProviderCredential): Promise<ProviderUsageSnapshot> {\n if (cred.kind !== 'apikey' || !cred.value)\n return snapshot(base, { status: 'unauthorized', error: 'no API key stored' })\n\n const res = await fetch('https://openrouter.ai/api/v1/credits', {\n headers: { Authorization: `Bearer ${cred.value}`, Accept: 'application/json' },\n signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n })\n const failure = httpFailure(base, res)\n if (failure)\n return failure\n\n const body = await res.json().catch(() => null) as { data?: { total_credits?: number, total_usage?: number } } | null\n const totals = body?.data\n if (!totals || typeof totals.total_credits !== 'number' || typeof totals.total_usage !== 'number')\n return snapshot(base, { error: 'invalid response' })\n\n const usedPercent = totals.total_credits > 0\n ? clampPercent((totals.total_usage / totals.total_credits) * 100)\n : 0\n return snapshot(base, {\n status: 'ok',\n windows: [{ id: 'credits', label: 'credits', usedPercent, resetsAt: null }],\n credits: { remaining: totals.total_credits - totals.total_usage, total: totals.total_credits },\n })\n}\n\n// --- Grok ------------------------------------------------------------------\n\nconst GROK_BILLING_URL = 'https://grok.com/grok_api_v2.GrokBuildBilling/GetGrokCreditsConfig'\nconst GROK_AUTH_FAILURE_RE = /access token|unauthenticated|expired/i\n\nasync function fetchGrokUsage(base: SnapshotBase, cred: ProviderCredential): Promise<ProviderUsageSnapshot> {\n const auth = accessTokenOr(base, cred)\n if ('fail' in auth)\n return auth.fail\n\n const res = await fetch(GROK_BILLING_URL, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${auth.token}`,\n 'Content-Type': 'application/grpc-web+proto',\n 'x-grpc-web': '1',\n 'x-user-agent': 'connect-es/2.1.1',\n 'Origin': 'https://grok.com',\n 'Referer': 'https://grok.com/?_s=usage',\n 'Accept': '*/*',\n },\n // Empty gRPC-web frame: flags byte + u32 length 0.\n body: new Uint8Array([0, 0, 0, 0, 0]),\n signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n })\n const failure = httpFailure(base, res)\n if (failure)\n return failure\n\n // gRPC carries its own status out-of-band, so HTTP 200 with `grpc-status: 16`\n // is an auth rejection. That is the common answer for a bearer here.\n const grpcStatus = res.headers.get('grpc-status')\n if (grpcStatus && grpcStatus !== '0') {\n const message = res.headers.get('grpc-message') ?? ''\n const unauthorized = grpcStatus === '16' || (grpcStatus === '7' && GROK_AUTH_FAILURE_RE.test(message))\n // Plain words for the expected case, the raw status for the rest: this\n // endpoint wants a session cookie, so a rejected bearer is routine, while\n // any other status is worth reporting verbatim.\n return unauthorized\n ? snapshot(base, { status: 'unauthorized', error: 'token not accepted' })\n : snapshot(base, { error: `gRPC ${grpcStatus}${message ? `: ${message}` : ''}` })\n }\n\n const parsed = parseGrokBillingResponse(new Uint8Array(await res.arrayBuffer()))\n if (!parsed)\n return snapshot(base, { error: 'unreadable response' })\n return snapshot(base, {\n status: 'ok',\n windows: [{\n id: 'credits',\n label: 'credits',\n usedPercent: clampPercent(parsed.usedPercent),\n resetsAt: parsed.resetsAt ? parsed.resetsAt.getTime() : null,\n }],\n })\n}\n\n// ---------------------------------------------------------------------------\n// Grok payload: tolerant protobuf scan\n//\n// The billing response is a protobuf whose schema we don't have. Rather than\n// vendor a generated client for an endpoint xAI can change at will, walk the\n// wire format and recover the two fields we need by shape. A wrong-shaped\n// payload yields `null`, which surfaces as an `unavailable` row.\n// ---------------------------------------------------------------------------\n\ninterface ScannedField { path: number[], order: number, value: number }\ninterface ProtobufScan { fixed32: ScannedField[], varint: ScannedField[] }\n\n/** Split a gRPC-web body into data frames. Flag bit 0x80 marks trailers. */\nexport function grpcWebDataFrames(bytes: Uint8Array): Uint8Array[] {\n const frames: Uint8Array[] = []\n let index = 0\n while (index < bytes.length) {\n if (index + 5 > bytes.length)\n return []\n const flags = bytes[index]!\n const length = (bytes[index + 1]! << 24) | (bytes[index + 2]! << 16) | (bytes[index + 3]! << 8) | bytes[index + 4]!\n const start = index + 5\n const end = start + length\n if (length < 0 || end > bytes.length)\n return []\n if ((flags & 0x80) === 0)\n frames.push(bytes.subarray(start, end))\n index = end\n }\n return frames\n}\n\nfunction looksLikeProtobuf(bytes: Uint8Array): boolean {\n if (bytes.length === 0)\n return false\n const fieldNumber = bytes[0]! >> 3\n const wireType = bytes[0]! & 0x07\n return fieldNumber > 0 && (wireType === 0 || wireType === 1 || wireType === 2 || wireType === 5)\n}\n\nfunction readVarint(bytes: Uint8Array, offset: number): { value: number, next: number } | null {\n let value = 0\n let shift = 0\n let index = offset\n while (index < bytes.length && shift < 64) {\n const byte = bytes[index]!\n // Float math is fine here: the values we care about (epoch seconds, small\n // enums) sit far below 2^53.\n value += (byte & 0x7F) * 2 ** shift\n index += 1\n if ((byte & 0x80) === 0)\n return { value, next: index }\n shift += 7\n }\n return null\n}\n\nfunction scanProtobuf(bytes: Uint8Array, path: number[], scan: ProtobufScan, orderRef: { n: number }, depth: number): void {\n if (depth > 6)\n return\n let index = 0\n while (index < bytes.length) {\n const tag = readVarint(bytes, index)\n if (!tag)\n return\n const fieldNumber = Math.floor(tag.value / 8)\n const wireType = tag.value % 8\n if (fieldNumber === 0)\n return\n index = tag.next\n const fieldPath = [...path, fieldNumber]\n switch (wireType) {\n case 0: { // varint\n const v = readVarint(bytes, index)\n if (!v)\n return\n scan.varint.push({ path: fieldPath, order: orderRef.n++, value: v.value })\n index = v.next\n break\n }\n case 1: { // fixed64, nothing we need is a double\n if (index + 8 > bytes.length)\n return\n index += 8\n break\n }\n case 5: { // fixed32 (float), where the used percent lives\n if (index + 4 > bytes.length)\n return\n const view = new DataView(bytes.buffer, bytes.byteOffset + index, 4)\n scan.fixed32.push({ path: fieldPath, order: orderRef.n++, value: view.getFloat32(0, true) })\n index += 4\n break\n }\n case 2: { // length-delimited, recurse when it looks like a nested message\n const len = readVarint(bytes, index)\n if (!len || len.next + len.value > bytes.length)\n return\n const sub = bytes.subarray(len.next, len.next + len.value)\n if (looksLikeProtobuf(sub))\n scanProtobuf(sub, fieldPath, scan, orderRef, depth + 1)\n index = len.next + len.value\n break\n }\n default:\n return // groups and unknown wire types: bail rather than misparse\n }\n }\n}\n\n/**\n * Recover `{ usedPercent, resetsAt }` from the billing protobuf without a\n * schema. The used percent is a fixed32 float in 0-100 whose path ends in field\n * 1 (shallowest, earliest wins); the reset is the nearest future epoch-second\n * varint, preferring path `[1,5,1]` (billing-cycle end).\n */\nexport function parseGrokBillingResponse(\n bytes: Uint8Array,\n now = new Date(),\n): { usedPercent: number, resetsAt: Date | null } | null {\n let frames = grpcWebDataFrames(bytes)\n if (frames.length === 0 && looksLikeProtobuf(bytes))\n frames = [bytes]\n if (frames.length === 0)\n return null\n\n const scan: ProtobufScan = { fixed32: [], varint: [] }\n const orderRef = { n: 0 }\n for (const frame of frames)\n scanProtobuf(frame, [], scan, orderRef, 0)\n\n const percentField = scan.fixed32\n .filter(f => f.path.at(-1) === 1 && Number.isFinite(f.value) && f.value >= 0 && f.value <= 100)\n .sort((a, b) => a.path.length === b.path.length ? a.order - b.order : a.path.length - b.path.length)[0]\n\n const epochFields = scan.varint\n .filter(f => f.value >= 1_700_000_000 && f.value <= 2_100_000_000)\n .map(f => ({ path: f.path, date: new Date(f.value * 1000) }))\n .filter(f => f.date > now)\n const preferred = epochFields.filter(\n f => f.path.length === 3 && f.path[0] === 1 && f.path[1] === 5 && f.path[2] === 1,\n )\n const resetsAt = (preferred.length > 0 ? preferred : epochFields)\n .map(f => f.date)\n .sort((a, b) => a.getTime() - b.getTime())[0] ?? null\n\n // Zero-usage periods omit the proto3 default percent field, so a billing\n // period with a future reset reads as 0% used.\n const hasUsagePeriod = scan.varint.some(f =>\n (f.path.length >= 2 && f.path[0] === 1 && f.path[1] === 6)\n || (f.path.length === 3 && f.path[0] === 1 && f.path[1] === 8 && f.path[2] === 1 && (f.value === 1 || f.value === 2)),\n )\n if (!percentField) {\n if (scan.fixed32.length === 0 && resetsAt && hasUsagePeriod)\n return { usedPercent: 0, resetsAt }\n return null\n }\n return { usedPercent: percentField.value, resetsAt }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction clampPercent(value: number): number {\n if (!Number.isFinite(value))\n return 0\n return Math.min(100, Math.max(0, Math.round(value * 10) / 10))\n}\n\nfunction isoToEpoch(value: string | null | undefined): number | null {\n if (typeof value !== 'string')\n return null\n const ts = new Date(value).getTime()\n return Number.isFinite(ts) ? ts : null\n}\n","/**\n * Lightweight shell command parser for safelist enforcement.\n *\n * Extracts the head token (program name) of every command in a bash\n * command string — including chains (`&&`, `||`, `;`), pipes (`|`),\n * subshells (`(…)`), and command substitutions (`$(…)` and backticks).\n *\n * Handles quoting (`'…'`, `\"…\"`, `\\x`), variable assignments\n * (`NAME=val`), I/O redirection, and the `!` negation prefix.\n *\n * Returns `null` when parsing fails so callers can fall back to\n * prompting (safe default).\n */\n\nexport function extractCommandHeads(command: string): string[] | null {\n try {\n const p = new ShellParser(command)\n p.list()\n return p.heads\n }\n catch {\n return null\n }\n}\n\n// Characters that unconditionally end an unquoted word.\nconst META = new Set(' \\t\\n\\r;|&()<>'.split(''))\n\nclass ShellParser {\n readonly heads: string[] = []\n private i = 0\n\n constructor(private readonly s: string) {}\n\n // -- public entry point ------------------------------------------------\n\n /** Command list — pipelines separated by `;`, `&&`, `||`, `\\n`, `&`. */\n list(closer?: string): void {\n for (;;) {\n this.ws()\n if (this.end(closer))\n return\n\n this.pipeline(closer)\n\n this.ws()\n if (this.end(closer))\n return\n const c = this.c()\n if (c === ';' || c === '\\n' || c === '\\r') { this.i++; continue }\n if (this.is('&&') || this.is('||')) { this.i += 2; continue }\n if (c === '&') { this.i++; continue }\n break\n }\n }\n\n // -- grammar -----------------------------------------------------------\n\n /** Pipeline — simple commands separated by `|` (not `||`). */\n private pipeline(closer?: string): void {\n this.cmd(closer)\n for (;;) {\n this.ws()\n if (this.c() === '|' && !this.is('||')) { this.i++; this.cmd(closer) }\n else {\n break\n }\n }\n }\n\n /** Simple command — extract head, skip remaining arguments. */\n private cmd(closer?: string): void {\n this.ws()\n if (this.i >= this.s.length)\n return\n if (closer && this.c() === closer)\n return\n\n // Subshell.\n if (this.c() === '(') {\n this.i++\n this.list(')')\n return\n }\n\n // `!` negation prefix — skip when followed by whitespace.\n if (this.c() === '!' && this.i + 1 < this.s.length\n && (this.s[this.i + 1] === ' ' || this.s[this.i + 1] === '\\t')) {\n this.i++; this.ws()\n }\n\n // Read tokens, skipping leading variable assignments (NAME=val).\n while (this.i < this.s.length && !this.sep(closer)) {\n if (this.c() === '(') {\n this.i++\n this.list(')')\n break\n }\n const w = this.word()\n if (!w)\n break\n if (isAssignment(w)) { this.ws(); continue }\n this.heads.push(w)\n break\n }\n\n this.tail(closer)\n }\n\n // -- word-level --------------------------------------------------------\n\n /** Read one shell word, handling quoting and substitutions. */\n private word(): string {\n let out = ''\n while (this.i < this.s.length) {\n const c = this.c()\n if (META.has(c))\n break\n\n if (c === '\\\\' && this.i + 1 < this.s.length) {\n out += this.s[this.i + 1]\n this.i += 2\n continue\n }\n if (c === '\\'') {\n this.i++\n while (this.i < this.s.length && this.s[this.i] !== '\\'') out += this.s[this.i++]\n if (this.i < this.s.length)\n this.i++\n continue\n }\n if (c === '\"') { out += this.dquote(); continue }\n if (this.is('$(')) {\n this.i += 2\n this.list(')')\n continue\n }\n if (c === '`') { this.btick(); continue }\n out += c; this.i++\n }\n return out\n }\n\n /** Double-quoted string — returns literal chars, recurses into substitutions. */\n private dquote(): string {\n this.i++\n let out = ''\n while (this.i < this.s.length && this.s[this.i] !== '\"') {\n if (this.s[this.i] === '\\\\' && this.i + 1 < this.s.length) {\n out += this.s[this.i + 1]\n this.i += 2\n continue\n }\n if (this.is('$(')) {\n this.i += 2\n this.list(')')\n continue\n }\n if (this.s[this.i] === '`') { this.btick(); continue }\n out += this.s[this.i++]\n }\n if (this.i < this.s.length)\n this.i++\n return out\n }\n\n /** Backtick substitution — creates a sub-parser for the inner content. */\n private btick(): void {\n this.i++\n const close = this.s.indexOf('`', this.i)\n const inner = close < 0 ? this.s.slice(this.i) : this.s.slice(this.i, close)\n const sub = new ShellParser(inner)\n sub.list()\n this.heads.push(...sub.heads)\n this.i = close < 0 ? this.s.length : close + 1\n }\n\n // -- tail / redirections -----------------------------------------------\n\n /** Skip remaining arguments and redirections until the next command boundary. */\n private tail(closer?: string): void {\n while (this.i < this.s.length) {\n this.ws()\n if (this.i >= this.s.length || this.sep(closer))\n break\n if (this.redir())\n continue\n if (this.c() === '(') {\n this.i++\n this.list(')')\n continue\n }\n const w = this.word()\n if (!w)\n break\n }\n }\n\n /** Try to consume an I/O redirection operator + target. Returns true if consumed. */\n private redir(): boolean {\n const c = this.c()\n let yes = c === '<' || c === '>'\n if (!yes && c >= '0' && c <= '9' && this.i + 1 < this.s.length) {\n const n = this.s[this.i + 1]\n yes = n === '<' || n === '>'\n }\n if (!yes)\n return false\n\n while (this.i < this.s.length && this.s[this.i] >= '0' && this.s[this.i] <= '9') this.i++\n if (this.c() === '>') {\n this.i++\n if (this.c() === '>')\n this.i++\n if (this.c() === '&') {\n this.i++\n while (this.i < this.s.length && this.s[this.i] >= '0' && this.s[this.i] <= '9') this.i++\n return true\n }\n }\n else if (this.c() === '<') {\n this.i++\n if (this.c() === '<') {\n this.i++; if (this.c() === '-')\n this.i++\n }\n }\n this.ws()\n this.word()\n return true\n }\n\n // -- predicates --------------------------------------------------------\n\n /** True at a command boundary (`;`, `\\n`, `&`, `|`, `)`, or closer). */\n private sep(closer?: string): boolean {\n const c = this.c()\n if (c === ';' || c === '\\n' || c === '\\r' || c === '&' || c === ')')\n return true\n if (c === '|')\n return true\n if (closer && c === closer)\n return true\n return false\n }\n\n /** True when past end or at closer (closer is consumed). */\n private end(closer?: string): boolean {\n if (this.i >= this.s.length)\n return true\n if (closer && this.c() === closer) { this.i++; return true }\n return false\n }\n\n private c(): string { return this.s[this.i] ?? '' }\n private is(s: string): boolean { return this.s.startsWith(s, this.i) }\n private ws(): void {\n while (this.i < this.s.length && (this.s[this.i] === ' ' || this.s[this.i] === '\\t')) this.i++\n }\n}\n\n/** `NAME=…` where NAME is a valid shell identifier. */\nfunction isAssignment(w: string): boolean {\n const eq = w.indexOf('=')\n if (eq <= 0)\n return false\n return /^[A-Z_]\\w*$/i.test(w.slice(0, eq))\n}\n","/**\n * Safe-mode storage + matching for the TUI.\n *\n * Lives at `<dataDir>/projects.json` (default `~/.zidane/projects.json`). Each\n * top-level key is an absolute project directory; the value carries that\n * project's persisted tool-call `safelist`.\n *\n * ```json\n * {\n * \"/Users/me/proj-a\": { \"safelist\": [\"read_file\", \"shell:git:*\"] }\n * }\n * ```\n *\n * Two granularities for safelist entries:\n * - **bare tool name** — `\"read_file\"` matches every `read_file` call.\n * - **tool + first-arg token + wildcard** — `\"shell:git:*\"` matches `shell`\n * calls whose primary string argument starts with the token `git`\n * (followed by whitespace or end-of-string). Modelled on Claude Code's\n * `Bash(git:*)` syntax.\n *\n * A short list of read-only tools is **implicitly safe** without being\n * persisted — see {@link IMPLICITLY_SAFE_TOOLS}.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { writeFileAtomic } from '../atomic-write'\nimport { extractCommandHeads } from './shell-parse'\n\n// ---------------------------------------------------------------------------\n// Persistence\n// ---------------------------------------------------------------------------\n\nexport interface ProjectEntry {\n safelist?: string[]\n}\n\nexport type ProjectsFile = Record<string, ProjectEntry>\n\n/** Resolve `projects.json`'s on-disk path given the TUI data directory. */\nexport function projectsFilePath(dataDir: string): string {\n return resolve(dataDir, 'projects.json')\n}\n\nexport function readProjects(dataDir: string): ProjectsFile {\n const path = projectsFilePath(dataDir)\n if (!existsSync(path))\n return {}\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf-8'))\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))\n return parsed as ProjectsFile\n }\n catch {\n // Corrupt file → treat as empty so the user can re-bootstrap.\n }\n return {}\n}\n\nexport function writeProjects(dataDir: string, file: ProjectsFile): void {\n writeFileAtomic(projectsFilePath(dataDir), JSON.stringify(file, null, 2), { ensureDir: true })\n}\n\n/**\n * Append one or more entries to the safelist for `projectDir`, dedup-aware.\n * Accepts a single string or a list (a compound shell command needs one\n * entry per command head — see {@link suggestSafelistEntries}). Writes once\n * and returns the updated entry list (post-write) so callers can render it\n * without re-reading. No write happens when every entry already exists.\n */\nexport function addToSafelist(\n dataDir: string,\n projectDir: string,\n entries: string | readonly string[],\n): readonly string[] {\n const toAdd = typeof entries === 'string' ? [entries] : entries\n const file = readProjects(dataDir)\n const existing = file[projectDir]?.safelist ?? []\n const next = [...existing]\n for (const entry of toAdd) {\n if (!next.includes(entry))\n next.push(entry)\n }\n if (next.length === existing.length)\n return existing\n file[projectDir] = { ...file[projectDir], safelist: next }\n writeProjects(dataDir, file)\n return next\n}\n\n/** Read the safelist for one project. Returns `[]` for unknown projects. */\nexport function getSafelist(dataDir: string, projectDir: string): readonly string[] {\n return readProjects(dataDir)[projectDir]?.safelist ?? []\n}\n\n// ---------------------------------------------------------------------------\n// Matching\n// ---------------------------------------------------------------------------\n\n/**\n * Tools that always pass without prompting. Four categories:\n *\n * - **Pure reads** (`read_file`, `list_files`, `glob`, `grep`) — no\n * side effects on disk or the model's own state.\n * - **Interaction tools** (`ask_user`, `present_plan`) — the call\n * itself surfaces a TUI picker the user has to answer, so prompting\n * for approval first would mean \"approve this prompt before you see\n * the prompt\": redundant + breaks the flow. The picker IS the gate.\n * - **Todos** (`todowrite`, `todoread`) — write to a slot on\n * `session.metadata.todos` and nothing else. No shell, no disk, no\n * network. The model uses these for checkpointing every few steps;\n * a per-call approval prompt would drown the conversation in\n * interruptions that buy no safety since the tool's surface is\n * read/write on a metadata bag we already trust the agent with.\n * - **Skill activation** (`skills_use`) — mutates a small per-agent\n * Map of active skills and returns the activated skill's bundled\n * instructions (or releases an entry on `mode: \"deactivate\"`).\n * Pure state on the agent — no shell, no disk writes, no network.\n * Gating it would also defeat the \"model recovers from\n * `AgentToolNotAllowedError` by calling\n * `skills_use({ mode: \"deactivate\", name })`\" path: a stuck model\n * would have to ask the user permission to *unstick* itself on\n * every retry. `skills_read` and `skills_run_script` are\n * intentionally NOT on this list — the former touches disk and\n * the latter executes arbitrary scripts, so both keep going\n * through the regular gate.\n *\n * Users who want to gate any of these must disable safe-mode entirely\n * (or fork this list in their own embedding).\n */\nexport const IMPLICITLY_SAFE_TOOLS: readonly string[] = [\n 'read_file',\n 'list_files',\n 'glob',\n 'grep',\n 'ask_user',\n 'present_plan',\n 'todowrite',\n 'todoread',\n 'skills_use',\n]\n\n/** Common input keys carrying the \"primary argument\" we scope safelists on. */\nconst PRIMARY_ARG_KEYS = ['command', 'path', 'pattern', 'query'] as const\n\nfunction primaryArgValue(input: Record<string, unknown>): string {\n for (const key of PRIMARY_ARG_KEYS) {\n const v = input[key]\n if (typeof v === 'string' && v.length > 0)\n return v\n }\n return ''\n}\n\n/**\n * Extract the first whitespace-delimited token of the primary arg.\n * Leading whitespace is trimmed first so `\" git status\"` tokenizes to\n * `\"git\"`, not `\"\"` (an empty first element from `.split(/\\s+/)`).\n */\nfunction primaryArgToken(input: Record<string, unknown>): string {\n return primaryArgValue(input).trim().split(/\\s+/)[0] ?? ''\n}\n\n/**\n * Test whether a `{ tool, input }` pair is covered by one safelist entry.\n *\n * Supported entry shapes:\n * - `\"<tool>\"` — broad match on tool name.\n * - `\"<tool>:<token>:*\"` — match when the primary arg's first token\n * equals `<token>`.\n *\n * This function matches a **single command** against a **single entry**.\n * For shell commands that chain multiple programs (`&&`, `||`, `;`,\n * `$(…)`, etc.), use {@link isOnSafelist} which parses the full command\n * and checks every head token independently.\n *\n * Entries that don't fit either shape are ignored (forward-compat for\n * future pattern syntax — readers shouldn't choke on entries written\n * by a newer version of the TUI).\n */\nexport function matchesSafelistEntry(\n entry: string,\n tool: string,\n input: Record<string, unknown>,\n): boolean {\n if (entry === tool)\n return true\n const sep = entry.indexOf(':')\n if (sep <= 0)\n return false\n if (entry.slice(0, sep) !== tool)\n return false\n const scope = entry.slice(sep + 1)\n if (scope.endsWith(':*'))\n return primaryArgToken(input) === scope.slice(0, -2)\n return false\n}\n\n/**\n * True when a call matches ANY entry in the project's safelist (or is\n * implicitly safe).\n *\n * For `shell` commands, the full command string is parsed into individual\n * commands (handling `&&`, `||`, `;`, `|`, `$(…)`, backticks, subshells).\n * Every command head must be covered by at least one safelist entry for\n * the call to pass. If parsing fails, returns `false` (prompt the user).\n */\nexport function isOnSafelist(\n entries: readonly string[],\n tool: string,\n input: Record<string, unknown>,\n): boolean {\n if (IMPLICITLY_SAFE_TOOLS.includes(tool))\n return true\n\n if (tool === 'shell') {\n const cmd = typeof input.command === 'string' ? input.command : ''\n const heads = extractCommandHeads(cmd)\n if (heads === null)\n return false\n if (heads.length === 0)\n return entries.some(e => matchesSafelistEntry(e, tool, input))\n return heads.every(head =>\n entries.some(e => matchesSafelistEntry(e, tool, { ...input, command: head })),\n )\n }\n\n return entries.some(e => matchesSafelistEntry(e, tool, input))\n}\n\n/**\n * Suggest the safelist entries to persist when the user picks \"accept and\n * remember\" (or \"accept for session\") for a `{ tool, input }`. Heuristic:\n *\n * - `shell` → one `shell:<head>:*` per DISTINCT command head, parsing the\n * command the SAME way {@link isOnSafelist} matches it (`&&`, `||`, `;`,\n * `|`, `$(…)`, subshells, backticks). So `cd dir && cat file` yields\n * `[\"shell:cd:*\", \"shell:cat:*\"]` — persisting the whole set is what\n * lets the next identical call skip the gate. A single first-token\n * entry would only cover `cd`, so `isOnSafelist`'s `every-head` check\n * would fail on `cat` and re-prompt forever.\n * - `shell` with an unparseable / empty command → first-token heuristic,\n * else the bare tool name.\n * - anything else → bare tool name (broad).\n *\n * Always returns a non-empty list so the UI has a concrete set to render\n * and persist.\n */\nexport function suggestSafelistEntries(\n tool: string,\n input: Record<string, unknown>,\n): string[] {\n if (tool === 'shell') {\n const cmd = typeof input.command === 'string' ? input.command : ''\n const heads = extractCommandHeads(cmd)\n if (heads && heads.length > 0) {\n // Emit one entry per head, deriving the token EXACTLY as\n // `isOnSafelist` looks it up (`primaryArgToken({ command: head })`)\n // and skipping nothing — that symmetry is what guarantees the\n // round-trip: whatever we persist here re-approves the same command\n // next time, for any command the parser accepts (a degenerate head\n // that tokenizes to \"\" yields `shell::*`, which the matcher checks\n // for the same \"\" token, so it still round-trips).\n const out: string[] = []\n for (const head of heads) {\n const entry = `${tool}:${primaryArgToken({ command: head })}:*`\n if (!out.includes(entry))\n out.push(entry)\n }\n return out\n }\n // No heads (assignment-only like `FOO=bar`, or empty/whitespace cmd):\n // mirror `isOnSafelist`'s `heads.length === 0` branch, which matches\n // against the whole input. `extractCommandHeads` returning null is the\n // only case that can't round-trip — the matcher refuses a null parse\n // outright — so this stays a best-effort one-shot grant there.\n const token = primaryArgToken(input)\n if (token)\n return [`${tool}:${token}:*`]\n }\n return [tool]\n}\n","/**\n * Safe-mode React context — bridges the agent's `tool:gate` / `mcp:tool:gate`\n * hooks (which run inside the harness loop) with the TUI's React tree (which\n * renders the approval picker).\n *\n * Flow:\n * 1. Hook handler in `app.tsx` calls `requestApproval(tool, input)`.\n * 2. Context appends a pending entry to the queue, returns a Promise.\n * 3. Picker renders the queue's head, calls `resolve(decision)` on pick.\n * 4. Head is shifted off the queue; hook handler proceeds with the decision.\n *\n * The queue is FIFO so parallel tool calls prompt in arrival order.\n *\n * Two contexts on purpose:\n * - `SafeModeQueueContext` carries the live queue. Consumers re-render on\n * every push/pop.\n * - `SafeModeActionsContext` carries `{ requestApproval, resolveHead,\n * denyAll }` and never changes identity, so callbacks that depend on\n * these don't re-bind every time a tool call enters or leaves the queue.\n */\n\nimport type { ReactNode } from 'react'\nimport { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'\n\n/**\n * Outcome of an approval prompt. Four bulk decisions match the original\n * single-edit modal contract; `partial` is the multi-edit branch — the\n * user accepted a subset of an `edit` / `multi_edit` / `write_file` call.\n *\n * `partial.mask` is 1:1 with the call's hunks (in input order): `true`\n * means apply, `false` means deny. The gate handler walks the mask to\n * build per-hunk outcomes; the TUI rebinds `ctx.input.edits` to the\n * approved subset and the renderer reconstructs the full per-hunk view\n * from the `<edit-outcomes>` annotation appended to the tool result.\n */\nexport type ApprovalDecision\n = | 'accept-once'\n | 'accept-session'\n | 'accept-safelist'\n | 'deny'\n | { kind: 'partial', mask: readonly boolean[] }\n\n/**\n * Identifies the caller behind an approval prompt — the parent agent or\n * a specific subagent (`child-N`). The modal's right-side title pins the\n * label so the user knows which agent issued the call when subagents\n * bubble their gates up through the parent's hook bus.\n */\nexport type ApprovalOriginator\n = | { kind: 'parent' }\n | { kind: 'child', label: string }\n\nexport interface ApprovalRequest {\n id: string\n tool: string\n input: Record<string, unknown>\n resolve: (decision: ApprovalDecision) => void\n /** Caller attribution. Absent ≡ `{ kind: 'parent' }`. */\n originator?: ApprovalOriginator\n /**\n * Cross-window correlation id (the daemon's approval request id) for\n * entries surfaced by the daemon runtime. Lets `dropByCorrelation`\n * retract this entry when ANOTHER window resolves the same shared\n * approval, without re-sending a decision. Absent for local-runtime\n * approvals.\n */\n correlationId?: string\n}\n\n/** Function signature consumed by `tool:gate` handlers + the child-tool wrap. */\nexport type RequestApproval = (\n tool: string,\n input: Record<string, unknown>,\n originator?: ApprovalOriginator,\n correlationId?: string,\n) => Promise<ApprovalDecision>\n\nexport interface SafeModeActions {\n /** Request a decision; resolves once the user picks. */\n requestApproval: RequestApproval\n /** Resolve the head and shift the queue forward. */\n resolveHead: (decision: ApprovalDecision) => void\n /** Resolve all pending with `deny`. Used on abort / hard exit. */\n denyAll: () => void\n /**\n * Remove the entry whose `correlationId` matches, WITHOUT resolving it\n * (the daemon already did, in another window). No-op when absent.\n */\n dropByCorrelation: (correlationId: string) => void\n}\n\nconst SafeModeQueueContext = createContext<readonly ApprovalRequest[]>([])\nconst SafeModeActionsContext = createContext<SafeModeActions | null>(null)\n\nlet approvalIdCounter = 0\nfunction nextApprovalId(): string {\n // Monotonic id is enough — uniqueness is scoped to a single TUI session\n // and the value never leaves the React tree. Avoids Date.now/Math.random\n // collisions inside tight parallel-tool batches.\n approvalIdCounter += 1\n return `approval-${approvalIdCounter}`\n}\n\n/**\n * Owns the queue + actions. Splits the value across two contexts so a queue\n * change doesn't invalidate every callback memo that closes over the actions.\n */\nexport function SafeModeProvider({ children }: { children: ReactNode }) {\n const [queue, setQueue] = useState<readonly ApprovalRequest[]>([])\n\n // Mirror the live queue so the unmount cleanup settles pending approvals\n // without re-subscribing on every queue change.\n const queueRef = useRef<readonly ApprovalRequest[]>([])\n queueRef.current = queue\n\n // Provider unmount drops the queue state — a tool body parked on\n // `requestApproval` would then hang forever (its run's `running` gate\n // never clears). Deny every pending request on unmount so no resolver is\n // silently dropped. (#4)\n useEffect(() => () => {\n for (const p of queueRef.current)\n p.resolve('deny')\n }, [])\n\n // `setQueue` is stable, so `useCallback([])` here gives genuine identity\n // stability — `requestApproval` is the same function reference for the\n // lifetime of the provider, which is what hook handlers need.\n const requestApproval = useCallback<RequestApproval>(\n (tool, input, originator, correlationId) => new Promise<ApprovalDecision>((resolve) => {\n setQueue(prev => [\n ...prev,\n {\n id: nextApprovalId(),\n tool,\n input,\n resolve,\n ...(originator ? { originator } : {}),\n ...(correlationId ? { correlationId } : {}),\n },\n ])\n }),\n [],\n )\n\n const resolveHead = useCallback((decision: ApprovalDecision) => {\n setQueue((prev) => {\n const [head, ...rest] = prev\n if (head)\n head.resolve(decision)\n return rest\n })\n }, [])\n\n const denyAll = useCallback(() => {\n setQueue((prev) => {\n for (const p of prev) p.resolve('deny')\n return []\n })\n }, [])\n\n const dropByCorrelation = useCallback((correlationId: string) => {\n // Remove WITHOUT resolving: the daemon already settled this approval\n // in the window that answered it. Resolving here would double-answer\n // (harmless deny) AND fire the RPC guard path unnecessarily.\n setQueue(prev => prev.filter(p => p.correlationId !== correlationId))\n }, [])\n\n // Actions object is stable across renders — its members are all `useCallback`'d\n // with `[]` deps. `useRef` captures the first-render identity and we hand the\n // same object out forever, so consumers calling `useSafeModeActions()` never\n // re-render purely because of safe-mode state.\n const actionsRef = useRef<SafeModeActions | null>(null)\n if (!actionsRef.current)\n actionsRef.current = { requestApproval, resolveHead, denyAll, dropByCorrelation }\n\n return (\n <SafeModeActionsContext.Provider value={actionsRef.current}>\n <SafeModeQueueContext.Provider value={queue}>\n {children}\n </SafeModeQueueContext.Provider>\n </SafeModeActionsContext.Provider>\n )\n}\n\nexport function useSafeModeQueue(): readonly ApprovalRequest[] {\n return useContext(SafeModeQueueContext)\n}\n\nexport function useSafeModeActions(): SafeModeActions {\n const ctx = useContext(SafeModeActionsContext)\n if (!ctx)\n throw new Error('useSafeModeActions must be used inside <SafeModeProvider>')\n return ctx\n}\n","/**\n * Working-directory resolution for sessions.\n *\n * Directory-less (\"pure chat\") sessions carry neither a `metadata.cwd`\n * (the folder the user picked in the GUI) nor a `projectRoot` (its git\n * root). Their tools still need a real process cwd — we anchor at the\n * user's HOME directory, mirroring the TUI's semantics when zidane is\n * launched from `~`: cwd = home, git-less scope, user-level config and\n * skills only.\n *\n * Explicitly NOT `process.cwd()`: in a packaged Electron app that points\n * at the bundle's resources directory — somewhere the user never chose\n * and tools should never operate in.\n */\nimport { homedir } from 'node:os'\n\n/** Process cwd used for sessions with no folder binding. */\nexport function folderlessCwd(): string {\n return homedir()\n}\n\n/**\n * Effective process cwd for a persisted session row: the GUI-recorded\n * `metadata.cwd` when present, else the canonical `projectRoot`\n * (TUI-created sessions), else the directory-less HOME anchor. Empty\n * strings count as unset — forks of directory-less sessions persist\n * `cwd: ''`.\n */\nexport function resolveSessionCwd(row: {\n metadata?: Record<string, unknown> | undefined\n projectRoot?: string | undefined\n}): string {\n const recorded = typeof row.metadata?.cwd === 'string' ? row.metadata.cwd : undefined\n\n return recorded || row.projectRoot || folderlessCwd()\n}\n\n/**\n * Map a renderer-supplied scope cwd to the directory the main process\n * should actually operate in. The renderer signals \"no folder\" with an\n * empty string (`SidebarSession.cwd` is `''` for directory-less\n * sessions); catalogs (@-files, skills, commands, extensions, MCPs)\n * then root at HOME, matching what the TUI offers when run from `~`.\n */\nexport function scopeCwd(cwd: string): string {\n return cwd || folderlessCwd()\n}\n","/**\n * Session export — render a `SessionData` as a clean Markdown or JSON\n * document and write it under the project's (or user's) `.{prefix}/sessions/`\n * directory.\n *\n * Anchor resolution mirrors the read-side `projectUserPaths` convention,\n * but for *writes* we pick a single canonical destination instead of a\n * fallback list: the closest enclosing git repository (walking up from\n * `cwd`), and the user home directory as the final fallback. This keeps\n * exports out of an unrelated project's `.{prefix}/` directory when the\n * user runs the TUI from a non-repo folder.\n *\n * Renderer-agnostic by design: every consumer (TUI, future GUI, SDK\n * scripts) can either call `writeSessionExport` for the canned\n * \"render + write\" combo or compose `renderSession` with a different\n * sink (clipboard, network, S3) without dragging in `node:fs`.\n */\n\nimport type { SessionData, SessionRun } from '../session'\nimport type { SessionContentBlock, SessionTurn, ToolResultContent, TurnUsage } from '../types'\nimport { existsSync, mkdirSync } from 'node:fs'\nimport { writeFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport { fmtTokens, shortId } from './format'\nimport { deriveSessionTitle } from './store'\n\n/** File format supported by the exporter. */\nexport type SessionExportFormat = 'markdown' | 'json'\n\n/**\n * Anchor for the resolved destination directory:\n * - `project` — the nearest enclosing git repository (`<repoRoot>/.{prefix}/sessions`)\n * - `home` — fallback when no repo was found (`~/.{prefix}/sessions`)\n */\nexport type SessionExportAnchor = 'project' | 'home'\n\n/** Resolved file destination for a session export. */\nexport interface SessionExportTarget {\n /** Absolute directory holding the file. May not exist yet — `writeSessionExport` creates it on demand. */\n dir: string\n /** Absolute path of the file (`dir/{id}.{ext}`). */\n filepath: string\n /** Where the destination was anchored. */\n anchor: SessionExportAnchor\n}\n\n/** Options shared by every resolve / write entry-point. */\ninterface ResolveOptions {\n /** Discovery cwd. Defaults to `process.cwd()`. */\n cwd?: string\n /** User home directory. Defaults to `os.homedir()`. */\n home?: string\n /**\n * Storage prefix. Leading dot tolerated (`zidane` and `.zidane` are\n * equivalent). Defaults to `'.zidane'`.\n */\n prefix?: string\n}\n\nconst DEFAULT_PREFIX = '.zidane'\n\n/**\n * Resolve the export target for a session id + format. Pure: no fs\n * write happens here, the caller decides whether to `existsSync` the\n * directory or hand the path to `writeSessionExport`.\n *\n * Anchor selection walks upward from `cwd` looking for a `.git` entry;\n * the first hit anchors to that repo's root (`project`). When the walk\n * exits without finding `.git`, the destination anchors to `home`.\n *\n * @throws RangeError when `sessionId` would resolve to an empty / `..`\n * filename — defensive guard against malicious or buggy callers\n * crossing into a sibling directory via the id.\n */\nexport function resolveSessionExportTarget(opts: {\n sessionId: string\n format: SessionExportFormat\n} & ResolveOptions): SessionExportTarget {\n const cwd = opts.cwd ?? process.cwd()\n const home = opts.home ?? homedir()\n const prefix = normalizePrefix(opts.prefix)\n const filename = exportFilename(opts.sessionId, opts.format)\n\n const repoRoot = findGitRoot(cwd)\n const anchor: SessionExportAnchor = repoRoot ? 'project' : 'home'\n const root = repoRoot ?? home\n const dir = resolve(root, `.${prefix}`, 'sessions')\n return { dir, filepath: join(dir, filename), anchor }\n}\n\n/**\n * Render a session into a string in the requested format.\n *\n * `markdown` produces a human-readable transcript: a YAML-free header\n * block with the title and a one-line `id · created · turns` summary,\n * a stats section (turns / runs / tokens / cost / status), then a\n * `## Conversation` block where each turn renders as `### N. role ·\n * run_X · ISO-date` with text, thinking, tool calls, and tool results\n * formatted as appropriate fenced blocks. Useful for sharing a session\n * with a teammate or pasting into an issue tracker.\n *\n * `json` returns a 2-space-indented, deterministic dump of the full\n * `SessionData` blob. Useful for re-importing into the store or for\n * post-hoc analysis tooling.\n */\nexport function renderSession(session: SessionData, format: SessionExportFormat): string {\n if (format === 'json')\n return `${JSON.stringify(session, null, 2)}\\n`\n return renderMarkdown(session)\n}\n\n/**\n * Render `session` and write the resulting bytes to disk. Returns the\n * resolved target so the caller can show the user where the file\n * landed. The parent directory is created on demand (`recursive: true`)\n * — first-time exports don't need any pre-flight setup.\n */\nexport async function writeSessionExport(\n opts: { session: SessionData, format: SessionExportFormat } & ResolveOptions,\n): Promise<SessionExportTarget> {\n const target = resolveSessionExportTarget({\n sessionId: opts.session.id,\n format: opts.format,\n cwd: opts.cwd,\n home: opts.home,\n prefix: opts.prefix,\n })\n const body = renderSession(opts.session, opts.format)\n // `mkdirSync` is synchronous to avoid two awaits on a hot path that's\n // I/O-bound anyway; `recursive: true` makes it a no-op when the dir\n // already exists, so we don't pay an `existsSync` round-trip.\n mkdirSync(target.dir, { recursive: true })\n await writeFile(target.filepath, body, 'utf8')\n return target\n}\n\n// ---------------------------------------------------------------------------\n// Markdown renderer\n// ---------------------------------------------------------------------------\n\n/**\n * Render a `SessionData` as a clean markdown transcript. Stable across\n * runs (no timestamps or random ids leak into the output beyond what\n * the session itself carries) — diffing two exports of the same\n * session is a no-op.\n */\nfunction renderMarkdown(session: SessionData): string {\n const title = deriveSessionTitle(session.turns, session.metadata)\n const userTurns = session.turns.filter(t => t.role === 'user').length\n const assistantTurns = session.turns.filter(t => t.role === 'assistant').length\n const usage = aggregateUsage(session.runs)\n const lines: string[] = []\n lines.push(`# ${escapeInline(title)}`)\n lines.push('')\n lines.push(`> Session \\`${shortId(session.id)}\\` · ${session.turns.length} turn${session.turns.length === 1 ? '' : 's'} · ${session.runs.length} run${session.runs.length === 1 ? '' : 's'}`)\n lines.push('')\n lines.push('## Metadata')\n lines.push('')\n lines.push(`- **id**: \\`${session.id}\\``)\n lines.push(`- **created**: ${new Date(session.createdAt).toISOString()}`)\n lines.push(`- **updated**: ${new Date(session.updatedAt).toISOString()}`)\n lines.push(`- **status**: ${session.status}`)\n lines.push(`- **turns**: ${session.turns.length} (${userTurns} user · ${assistantTurns} assistant)`)\n lines.push(`- **runs**: ${session.runs.length}`)\n if (usage.total > 0) {\n const tokenBits: string[] = [\n `in ${fmtTokens(usage.input)}`,\n `out ${fmtTokens(usage.output)}`,\n ]\n if (usage.cacheRead > 0)\n tokenBits.push(`cached ${fmtTokens(usage.cacheRead)}`)\n lines.push(`- **tokens**: ${fmtTokens(usage.total)} (${tokenBits.join(' · ')})`)\n if (usage.cost > 0)\n lines.push(`- **cost**: $${usage.cost.toFixed(usage.cost < 0.01 ? 4 : 2)}`)\n }\n lines.push('')\n lines.push('## Conversation')\n lines.push('')\n\n if (session.turns.length === 0) {\n lines.push('_No turns recorded yet._')\n lines.push('')\n return `${lines.join('\\n')}\\n`\n }\n\n session.turns.forEach((turn, idx) => {\n lines.push(renderTurnHeader(turn, idx + 1))\n lines.push('')\n const body = renderTurnBody(turn)\n if (body) {\n lines.push(body)\n lines.push('')\n }\n })\n\n return `${lines.join('\\n').replace(/\\n+$/, '\\n')}\\n`\n}\n\n/** `### 1. user · run_1 · 2026-05-13T01:48:29.895Z` */\nfunction renderTurnHeader(turn: SessionTurn, index: number): string {\n const date = new Date(turn.createdAt).toISOString()\n const runFragment = turn.runId ? ` · \\`${turn.runId}\\`` : ''\n const usageFragment = turn.usage ? ` · ${formatTurnUsage(turn.usage)}` : ''\n return `### ${index}. ${turn.role}${runFragment} · ${date}${usageFragment}`\n}\n\n/** `in 12 · out 48 · cached 1.2k` — only emits non-zero buckets. */\nfunction formatTurnUsage(usage: TurnUsage): string {\n const parts: string[] = []\n if (usage.input > 0)\n parts.push(`in ${fmtTokens(usage.input)}`)\n if (usage.output > 0)\n parts.push(`out ${fmtTokens(usage.output)}`)\n if ((usage.cacheRead ?? 0) > 0)\n parts.push(`cached ${fmtTokens(usage.cacheRead ?? 0)}`)\n return parts.join(' · ')\n}\n\n/**\n * Render all blocks of a single turn into a markdown fragment.\n * Block ordering matches the on-disk turn so resume / replay semantics\n * are preserved: tool_call → tool_result pairs stay adjacent, thinking\n * blocks land right before the assistant text they precede.\n */\nfunction renderTurnBody(turn: SessionTurn): string {\n const parts: string[] = []\n for (const block of turn.content) {\n const chunk = renderBlock(block)\n if (chunk)\n parts.push(chunk)\n }\n return parts.join('\\n\\n')\n}\n\nfunction renderBlock(block: SessionContentBlock): string {\n switch (block.type) {\n case 'text':\n return block.text.trim() ? block.text.trim() : ''\n case 'thinking': {\n const text = block.text.trim()\n if (!text)\n return ''\n // Blockquote prefix on every line so consumers can fold / hide\n // thinking sections without breaking the surrounding markdown.\n const quoted = text.split('\\n').map(l => `> ${l}`).join('\\n')\n return `> **thinking**\\n>\\n${quoted}`\n }\n case 'tool_call': {\n const args = JSON.stringify(block.input, null, 2)\n return [\n `**Tool call** \\`${block.name}\\` · id \\`${block.id}\\``,\n '',\n '```json',\n args,\n '```',\n ].join('\\n')\n }\n case 'tool_result': {\n const header = block.isError\n ? `**Tool result** ✗ error · id \\`${block.callId}\\``\n : `**Tool result** · id \\`${block.callId}\\``\n const output = renderToolOutput(block.output)\n return [header, '', output].join('\\n')\n }\n case 'image':\n // Image data could be megabytes of base64; reference it by media type\n // rather than inline. A future viewer that wants to round-trip can\n // pull the original bytes from the on-disk session.\n return `_[image · ${block.mediaType}]_`\n case 'audio':\n return `_[audio · ${block.name ? `${block.name} · ` : ''}${block.mediaType}]_`\n case 'video':\n return `_[video · ${block.name ? `${block.name} · ` : ''}${block.mediaType}]_`\n case 'document':\n return `_[document · ${block.name ? `${block.name} · ` : ''}${block.mediaType}]_`\n case 'compact-summary': {\n // Boundary card in the markdown export — mirrors the TUI's\n // CompactSummaryBlock so a reader sees what happened at the\n // compaction point. The detailed turn-by-turn history before the\n // marker is still in the export (compaction never deletes from\n // disk), so the reader can compare summary vs source.\n const count = block.replacesTurnIds.length\n return [\n `**Compaction summary** · ${count} turn${count === 1 ? '' : 's'} compacted · model \\`${block.model}\\``,\n '',\n block.summary.trim(),\n ].join('\\n')\n }\n default:\n return ''\n }\n}\n\n/**\n * Tool output is either a flat string (the common case) or a structured\n * array of `ToolResultContent` (multimodal tools — screenshots, charts).\n * Strings render as a fenced code block; structured arrays render\n * their text blocks inline with `[image · …]` placeholders for the rest.\n */\nfunction renderToolOutput(output: string | ToolResultContent[]): string {\n if (typeof output === 'string') {\n const fence = pickFence(output)\n return `${fence}\\n${output}\\n${fence}`\n }\n const segments: string[] = []\n for (const piece of output) {\n if (piece.type === 'text') {\n const fence = pickFence(piece.text)\n segments.push(`${fence}\\n${piece.text}\\n${fence}`)\n }\n else if (piece.type === 'image') {\n segments.push(`_[image · ${piece.mediaType}]_`)\n }\n else if (piece.type === 'audio') {\n segments.push(`_[audio · ${piece.mediaType}]_`)\n }\n else if (piece.type === 'video') {\n segments.push(`_[video · ${piece.mediaType}]_`)\n }\n else {\n segments.push(`_[document · ${piece.name ? `${piece.name} · ` : ''}${piece.mediaType}]_`)\n }\n }\n return segments.join('\\n\\n')\n}\n\n/**\n * Pick a fence (`\\`\\`\\`` or longer) that doesn't collide with any\n * backtick run inside `content`. The CommonMark rule is \"a fenced\n * block ends at a fence of the same length or longer made of the same\n * char\" — choosing a fence strictly longer than the longest run inside\n * the body avoids accidental termination.\n */\nfunction pickFence(content: string): string {\n let longestRun = 0\n let current = 0\n for (const ch of content) {\n if (ch === '`') {\n current++\n if (current > longestRun)\n longestRun = current\n }\n else {\n current = 0\n }\n }\n const len = Math.max(3, longestRun + 1)\n return '`'.repeat(len)\n}\n\n/** Strip control characters from a single-line inline (e.g. the title). */\nfunction escapeInline(s: string): string {\n return s.replace(/[\\r\\n]+/g, ' ').trim()\n}\n\n// ---------------------------------------------------------------------------\n// Path resolution helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Walk parents of `start` looking for a `.git` entry (file or\n * directory — `.git` is a file inside git worktrees). Returns the\n * directory holding `.git`, or `null` at the filesystem root.\n */\nfunction findGitRoot(start: string): string | null {\n let dir = resolve(start)\n // Hard cap as an escape hatch on pathological cwd values\n // (`mount --bind` cycles, etc.); 64 levels is well above what any\n // real-world checkout produces.\n for (let i = 0; i < 64; i++) {\n if (existsSync(join(dir, '.git')))\n return dir\n const parent = dirname(dir)\n if (parent === dir)\n return null\n dir = parent\n }\n return null\n}\n\n/** Trim a leading dot from `prefix`. */\nfunction normalizePrefix(prefix: string | undefined): string {\n return (prefix ?? DEFAULT_PREFIX).replace(/^\\./, '')\n}\n\n/**\n * Compose the on-disk filename for `(sessionId, format)`. Rejects\n * empty ids and path-traversal-ish values so the caller can't escape\n * `.{prefix}/sessions/`.\n */\nfunction exportFilename(sessionId: string, format: SessionExportFormat): string {\n const ext = format === 'json' ? 'json' : 'md'\n const cleaned = sessionId.trim()\n if (!cleaned || cleaned.includes('/') || cleaned.includes('\\\\') || cleaned === '.' || cleaned === '..')\n throw new RangeError(`Cannot export session: invalid id \"${sessionId}\"`)\n return `${cleaned}.${ext}`\n}\n\n// ---------------------------------------------------------------------------\n// Usage aggregation (shared with the details modal in spirit but duplicated\n// here so the renderer stays self-contained — pulling it in from the modal\n// would invert the dependency direction)\n// ---------------------------------------------------------------------------\n\ninterface AggregatedUsage {\n input: number\n output: number\n cacheRead: number\n total: number\n cost: number\n}\n\nfunction aggregateUsage(runs: readonly SessionRun[]): AggregatedUsage {\n const acc = { input: 0, output: 0, cacheRead: 0, cost: 0 }\n for (const run of runs) {\n if (run.totalUsage) {\n acc.input += run.totalUsage.input ?? 0\n acc.output += run.totalUsage.output ?? 0\n acc.cacheRead += run.totalUsage.cacheRead ?? 0\n }\n else if (run.turnUsage) {\n for (const u of run.turnUsage) {\n acc.input += u.input ?? 0\n acc.output += u.output ?? 0\n acc.cacheRead += u.cacheRead ?? 0\n }\n }\n if (run.cost)\n acc.cost += run.cost\n }\n return { ...acc, total: acc.input + acc.output }\n}\n","/**\n * Skills discovery + persisted enabled-state for the chat layer.\n *\n * Walks project + storage-dir scan paths, returns each skill's parsed\n * metadata, and folds the user's enabled-toggle list into a `SkillsConfig`\n * the agent picks up at construction time.\n *\n * Pattern mirrors what `createAgent` already supports (`SkillsConfig.scan`\n * + `enabled`), so the agent receives the same shape it always did — the\n * chat layer just owns the discovery + UI plumbing.\n */\n\nimport type { SkillConfig, SkillsConfig, SourcedScanPath } from '../skills'\nimport { existsSync } from 'node:fs'\nimport { discoverSkills } from '../skills'\nimport { foreignSkillScanPaths } from './foreign-configs'\nimport { projectUserPaths } from './project-user-paths'\nimport { rulerSkillScanPaths } from './ruler-configs'\n\n/**\n * Resolve the default skill scan paths for a project. First-found wins on\n * collision; earlier entries take precedence.\n *\n * Search order — see {@link projectUserPaths}. Non-existent paths are\n * returned so downstream code can choose whether to create them;\n * `discoverSkills` itself skips missing dirs without error.\n */\nexport function defaultSkillScanPaths(opts: {\n cwd?: string\n home?: string\n prefix?: string\n /**\n * Also scan the skill directories other agents maintain (Claude Code,\n * Codex, Pi) — see `foreign-configs.ts`. Mirrors the\n * `importForeignConfigs` setting. Appended AFTER the native paths so\n * first-found-wins keeps zidane's own copies authoritative on name\n * collisions.\n */\n foreignAgents?: boolean\n /**\n * Also scan Ruler skill directories (`.ruler/skills`, project and\n * XDG-global) — see `ruler-configs.ts`. Mirrors the\n * `importRulerConfigs` setting; appended last so first-found-wins\n * keeps both zidane's own and foreign copies authoritative.\n */\n ruler?: boolean\n} = {}): SourcedScanPath[] {\n const native = projectUserPaths({ subPath: 'skills', ...opts })\n return [\n ...native,\n ...(opts.foreignAgents ? foreignSkillScanPaths(opts) : []),\n ...(opts.ruler ? rulerSkillScanPaths(opts) : []),\n ]\n}\n\n/**\n * Discover every skill reachable from the default scan paths. Returns\n * parsed `SkillConfig`s with `name`, `description`, frontmatter, and\n * lenient-load diagnostics. Pure I/O — does not activate or write.\n *\n * `signal` is forwarded to `discoverSkills` so the TUI's directory-watch\n * effect can cancel a long scan when the user switches `cwd` rapidly.\n *\n * Errors during parse are surfaced as `diagnostics` on the returned\n * skill, not thrown — keeps the picker usable even when a single\n * SKILL.md is malformed.\n */\nexport async function discoverProjectSkills(opts: {\n cwd?: string\n home?: string\n prefix?: string\n /** See {@link defaultSkillScanPaths}. */\n foreignAgents?: boolean\n /** See {@link defaultSkillScanPaths}. */\n ruler?: boolean\n signal?: AbortSignal\n} = {}): Promise<SkillConfig[]> {\n const paths = defaultSkillScanPaths(opts).filter(p => existsSync(p.path))\n return discoverSkills(paths, opts.signal)\n}\n\n/**\n * Map a user-toggled enable list onto the format `createAgent` expects.\n *\n * Conventions:\n * - `enabled === undefined` → all discovered skills enabled (default).\n * - `enabled === []` → fully off; the agent will not scan or\n * inject the skills tools.\n * - `enabled === [names]` → allowlist; skills not in the list are\n * left out of the catalog.\n *\n * `scan` is the resolved scan-path list (passed through verbatim). Pass\n * `defaultSkillScanPaths()` for the standard project + user paths, or\n * supply a host-specific list.\n */\nexport function buildSkillsConfig(opts: {\n scan: SourcedScanPath[]\n enabled?: readonly string[]\n}): SkillsConfig {\n const scan = opts.scan.map(p => p.path)\n // NB: pre-resolved skills (e.g. Agent Plugin skills) deliberately do NOT\n // go through here. `composePresets` is last-wins on the whole `skills`\n // object, so a `catalog` set at this level is replaced outright as soon as\n // an extension contributes skills. Hosts must concatenate onto the FINAL\n // merged skills instead — see `withPluginSkills` in `tui/app.tsx`.\n // Empty allowlist must disable the system outright — the loop checks for\n // `enabled === false || enabled.length === 0` to short-circuit catalog\n // assembly. Passing `[]` here used to leak through as \"no allowlist\n // entries, so include nothing\" which the loop treats as \"off\" anyway,\n // but being explicit prevents future loop refactors from drifting.\n if (opts.enabled !== undefined && opts.enabled.length === 0) {\n return { scan, enabled: false }\n }\n return {\n scan,\n ...(opts.enabled ? { enabled: [...opts.enabled] } : {}),\n }\n}\n","/**\n * Pure builders for live subagent lifecycle markers — the `spawn-start` /\n * `spawn-end` StreamEvents the engine broadcasts from the `spawn:before` /\n * `spawn:complete` hooks.\n *\n * Mirrors the TUI's live emission (`src/tui/app.tsx` spawn hooks) and\n * canonical replay's `pushSpawnStart` / `pushSpawnEnd`\n * (`src/chat/store.ts`) so a live GUI transcript and a reloaded one render\n * byte-identical markers. Extracted from `engine.ts` so the wire-text\n * shapes are unit-testable without an Electron harness.\n */\nimport type { AgentStats } from '../types'\nimport type { StreamEvent } from './types'\nimport { flattenTurns, formatTokenUsage } from '../stats'\n\nimport { formatDuration, previewLine } from './format'\n\n/**\n * Wall-clock start + task preview per in-flight child, keyed by spawn id.\n * Captured at `spawn:before` and read back at `spawn:complete` — drives\n * the human-readable elapsed suffix on the spawn-end marker\n * (`done · 42s`) and copies the stable task preview onto the terminal\n * marker.\n */\nexport interface SpawnStartRecord {\n startedAt: number\n task: string\n model?: string\n}\n\n/**\n * `spawn-start` marker for a child that just launched. `previewLine`\n * collapses inline newlines/tabs so the marker stays on one visual row\n * resumed children carry the label of the run they continue — same text\n * shape as the reload path.\n */\nexport function buildSpawnStartEvent(\n ctx: { id: string, task: string, depth?: number, model?: string, resumesChildId?: string },\n startedAt: number,\n): StreamEvent {\n const taskPreview = previewLine(ctx.task, 80)\n\n return {\n kind: 'spawn-start',\n text: ctx.resumesChildId ? `resumes ${ctx.resumesChildId} · ${taskPreview}` : taskPreview,\n childId: ctx.id,\n depth: ctx.depth ?? 1,\n spawn: { startedAt },\n ...(ctx.model ? { model: ctx.model } : {}),\n }\n}\n\n/**\n * `spawn-end` marker: ` · `-joined optional segments — elapsed wall-clock\n * time, then USD cost summed across the child's whole turn tree (most\n * providers don't populate `cost`, so the segment usually collapses; no\n * \"$0.00\" lies) — followed by the token usage summary. `timeout` maps to\n * the `done` tag, matching the TUI.\n */\nexport function buildSpawnEndEvent(\n ctx: { id: string, depth?: number, status?: 'completed' | 'aborted' | 'timeout' | 'error', stats: AgentStats },\n started: SpawnStartRecord | undefined,\n now = Date.now(),\n): StreamEvent {\n const tag = ctx.status === 'aborted' ? 'aborted' : ctx.status === 'error' ? 'error' : 'done'\n const startedAt = started?.startedAt\n const elapsed = startedAt !== undefined ? ` · ${formatDuration(now - startedAt)}` : ''\n const cost = flattenTurns(ctx.stats).reduce((sum, turn) => sum + (turn.cost ?? 0), 0)\n const costPart = cost > 0 ? ` · $${cost.toFixed(cost < 0.01 ? 4 : 2)}` : ''\n const segments = `${elapsed}${costPart}`\n const usage = formatTokenUsage(ctx.stats)\n\n return {\n kind: 'spawn-end',\n text: segments ? `${tag}${segments} · ${usage}` : `${tag} ${usage}`,\n childId: ctx.id,\n depth: ctx.depth ?? 1,\n spawn: { startedAt: startedAt ?? now, ...(started?.task ? { task: started.task } : {}), turns: ctx.stats.turns },\n ...(started?.model ? { model: started.model } : {}),\n }\n}\n","import type { Dispatch, SetStateAction } from 'react'\nimport type { Owner, StreamEvent } from './types'\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\nimport { chunkMarkdownForRender } from './markdown-segments'\nimport { capTranscriptEvents, ownerOf } from './streaming-pure'\n\n// Pure finalize reducers + context-window math live in the node-free\n// `./streaming-pure` leaf (importable from `zidane/chat/pure`). Re-exported\n// here so existing `zidane/chat` consumers keep their import paths.\nexport { capTranscriptEvents, finalizeStreamingMarkdown, finalizeStreamingMarkdownForOwner, hiddenEarlierTranscriptCount, MAX_TRANSCRIPT_EVENTS, replaceTranscriptTurnEvents, revealEarlierTranscriptEvents, TRANSCRIPT_TRIM_TO_EVENTS, turnContextSize } from './streaming-pure'\n\n/**\n * Tick interval for the continuous drain loop. ~16ms aligns with the\n * 60fps renderer target so the typewriter cadence reveals 1 portion of\n * content per paint frame. The bucket-derived `chars-per-tick` decides\n * how much actually lands per tick — see {@link smoothCharsForTick}.\n */\nconst TICK_INTERVAL_MS = 16\n\n/**\n * Smooth-streaming display rate, in characters per second.\n *\n * Three-segment continuous ramp (no step functions, no cliff) so the\n * typewriter cadence never visibly \"jumps\" mid-stream:\n *\n * - {@link SMOOTH_BASE_CPS} — calm-state floor. ~200 CPS is a fast\n * typist's pace; comfortably readable without feeling like a\n * deliberate performance.\n * - {@link SMOOTH_BURST_CPS} at {@link SMOOTH_BURST_BACKLOG_CHARS} —\n * moderate catch-up speed once the buffer is one paragraph behind\n * the provider.\n * - {@link SMOOTH_MAX_CPS} at {@link SMOOTH_MAX_BACKLOG_CHARS} —\n * bounded ceiling. Past this we plateau at max-cps rather than\n * instant-draining; max-cps is already well above any human reading\n * rate so even a 10K-char dump completes in ~2.5s of fast typewriter\n * instead of a single-frame teleport.\n *\n * The previous failsafe (instant drain at 2K chars) was a literal\n * step from ~10 chars/frame to \"everything in one frame\" — the worst\n * jump in the smooth pipeline. The continued ramp eliminates it\n * without losing bounded latency, since CPS keeps growing with\n * backlog up to the cap.\n */\nconst SMOOTH_BASE_CPS = 200\nconst SMOOTH_BURST_CPS = 600\nconst SMOOTH_BURST_BACKLOG_CHARS = 400\nconst SMOOTH_MAX_CPS = 4000\nconst SMOOTH_MAX_BACKLOG_CHARS = 4000\n\n/**\n * Safety bound for {@link StreamBuffer.whenDrained} — per window of NO\n * drain progress. A long response legitimately types out for longer\n * than any fixed deadline (the ramp bottoms out at\n * {@link SMOOTH_BASE_CPS}), so each committed tick re-arms the window;\n * only a genuinely wedged ticker — this long with nothing committed —\n * fires the escape hatch, and convergence (transcript resync) is never\n * blocked forever.\n */\nexport const WHEN_DRAINED_TIMEOUT_MS = 3000\n\nconst PARENT_OWNER: Owner = 'parent'\n\n/**\n * Segment bound for a growing streaming markdown event. Coalescing copies\n * the ENTIRE accumulated string into fresh React state on every ~16ms\n * drain tick, so one long streamed message costs O(L²) aggregate — past\n * this threshold the event is split and the copy re-bounded. The split\n * reuses {@link chunkMarkdownForRender} (blank-line boundaries outside\n * fences, unsafe-continuation guard), so the finalized segments are\n * byte-identical to the chunks the renderer would have produced inside a\n * single event, and the one-row markdown/markdown margin stands in for\n * the consumed blank line exactly like the renderer's inter-chunk gap.\n * When no safe boundary exists (one giant fence/list) the event keeps\n * growing until one arrives.\n */\nconst STREAMING_MARKDOWN_SEGMENT_CHARS = 64 * 1024\n\n// ---------------------------------------------------------------------------\n// Streaming-delta merging\n//\n// Each event is owned by either the parent agent or a specific subagent\n// (`childId`). Same-owner same-kind text/thinking chunks coalesce into one\n// growing event so React allocates one renderable per stream, not per delta.\n//\n// The owner's event is NOT required to sit at the tail: `tick()` commits one\n// portion per owner per ~16ms tick, so N concurrent subagents rotate the tail\n// between them. A tail-only merge check made every portion its own event —\n// each child's message rendered as a column of ~5-char rows interleaved with\n// its siblings'. See {@link mergeTargetIndex}.\n// ---------------------------------------------------------------------------\n\ninterface DeltaBucket {\n markdown: string\n thinking: string\n owner: Owner\n depth: number\n /** Last turnId seen for this bucket. Tagged onto new events when the bucket flushes. */\n turnId?: string\n /**\n * Fractional chars carried over from the previous smooth tick. The\n * tick computes an exact float (`cps * dt`), takes the integer part,\n * and stashes the leftover here so the next tick adds it back in.\n * This makes the long-run rate match the target CPS exactly instead\n * of being inflated by `Math.ceil` rounding on every tick.\n */\n smoothCarry: number\n}\n\nfunction emptyBucket(owner: Owner, depth: number): DeltaBucket {\n return { markdown: '', thinking: '', owner, depth, smoothCarry: 0 }\n}\n\nfunction applyBucket(prev: StreamEvent[], bucket: DeltaBucket): StreamEvent[] {\n let result = prev\n if (bucket.thinking)\n result = appendThinkingLines(result, bucket.thinking, bucket.owner, bucket.depth, bucket.turnId)\n if (bucket.markdown)\n result = appendMarkdownDelta(result, bucket.markdown, bucket.owner, bucket.depth, bucket.turnId)\n return result\n}\n\n/**\n * Backoff for over-threshold events `chunkMarkdownForRender` could not\n * split (one giant fence/list): records the text length at the last failed\n * attempt, keyed on the streaming event object (re-keyed onto each tick's\n * replacement). Without it a single unsplittable segment re-runs the full\n * O(L) chunk walk on every ~16ms drain tick. Growth is geometric (retry at\n * +25%), so total re-chunk work stays O(L) amortized.\n */\nconst unsplittableAtLength = new WeakMap<StreamEvent, number>()\nconst SEGMENT_SPLIT_RETRY_GROWTH = 1.25\n\n/** Is this event owned by a subagent (rather than the top-level agent)? */\nfunction isChildOwned(event: StreamEvent): boolean {\n return event.childId !== undefined || (event.depth ?? 0) > 0\n}\n\n/**\n * Index of the event `owner`'s next delta may coalesce into, or `-1` when the\n * owner has no open lane at the tail.\n *\n * Walks backwards over events owned by OTHER subagents — concurrent siblings\n * whose rows carry no ordering relation to this owner's output, and which the\n * per-owner drain interleaves at ~60Hz — and stops at the first event that is\n * either this owner's or a real ordering boundary (parent output, user\n * prompts, banners). The caller decides whether the event found is mergeable\n * for its kind.\n *\n * Exported for tests.\n */\nexport function mergeTargetIndex(events: readonly StreamEvent[], owner: Owner): number {\n for (let i = events.length - 1; i >= 0; i--) {\n const event = events[i]!\n if (ownerOf(event) === owner)\n return i\n if (!isChildOwned(event))\n return -1\n }\n return -1\n}\n\n/** Coalesce a markdown delta into `owner`'s open block. Exported for tests. */\nexport function appendMarkdownDelta(prev: StreamEvent[], delta: string, owner: Owner, depth: number, turnId: string | undefined): StreamEvent[] {\n const at = mergeTargetIndex(prev, owner)\n const target = at >= 0 ? prev[at]! : undefined\n if (target && target.kind === 'markdown' && target.streaming) {\n const merged = target.text + delta\n const next = prev.slice()\n if (merged.length > STREAMING_MARKDOWN_SEGMENT_CHARS) {\n const lastFailed = unsplittableAtLength.get(target)\n if (lastFailed === undefined || merged.length > lastFailed * SEGMENT_SPLIT_RETRY_GROWTH) {\n const chunks = chunkMarkdownForRender(merged)\n if (chunks.length > 1) {\n // All segments inherit `target`'s tags (owner/depth/turnId) — they\n // are one coalesced stream, split for memory, not a new source.\n const segments = chunks.map((chunk, i) => i === chunks.length - 1\n ? { ...target, text: chunk }\n : { ...target, text: chunk, streaming: false })\n next.splice(at, 1, ...segments)\n return next\n }\n const grown = { ...target, text: merged }\n unsplittableAtLength.set(grown, merged.length)\n next[at] = grown\n return next\n }\n // Within the backoff window — merge without re-chunking, carrying\n // the failure marker onto the replacement event.\n const grown = { ...target, text: merged }\n unsplittableAtLength.set(grown, lastFailed)\n next[at] = grown\n return next\n }\n next[at] = { ...target, text: merged }\n return next\n }\n return [\n ...prev,\n tagEvent({ kind: 'markdown', text: delta, streaming: true }, owner, depth, turnId),\n ]\n}\n\n/** Coalesce a thinking delta into `owner`'s open lane. Exported for tests. */\nexport function appendThinkingLines(prev: StreamEvent[], delta: string, owner: Owner, depth: number, turnId: string | undefined): StreamEvent[] {\n const lines = delta.split('\\n')\n const at = mergeTargetIndex(prev, owner)\n const target = at >= 0 ? prev[at]! : undefined\n\n // Extra lines land right after the merged row rather than at the tail so\n // the owner's thinking lane stays contiguous while siblings stream.\n if (target && target.kind === 'thinking') {\n const next = prev.slice()\n next[at] = { ...target, text: target.text + lines[0] }\n if (lines.length > 1) {\n next.splice(at + 1, 0, ...lines.slice(1).map(line =>\n tagEvent({ kind: 'thinking', text: line }, owner, depth, turnId)))\n }\n return next\n }\n\n const result = [...prev]\n if (lines[0] || lines.length > 1)\n result.push(tagEvent({ kind: 'thinking', text: lines[0] }, owner, depth, turnId))\n\n for (let i = 1; i < lines.length; i++)\n result.push(tagEvent({ kind: 'thinking', text: lines[i] }, owner, depth, turnId))\n\n return result\n}\n\n/**\n * Stamp owner (parent vs subagent) + depth + optional `turnId` onto a\n * freshly-minted event so consumers can identify the producer and the\n * source turn. Parent-owned events leave `childId` / `depth` off to match\n * the prior shape; `turnId` is added whenever the caller provided one.\n */\nfunction tagEvent(evt: StreamEvent, owner: Owner, depth: number, turnId: string | undefined): StreamEvent {\n const withTurn = turnId ? { ...evt, turnId } : evt\n if (owner === PARENT_OWNER)\n return withTurn\n return { ...withTurn, childId: owner, depth }\n}\n\n// ---------------------------------------------------------------------------\n// useStreamBuffer — per-owner stream-event accumulator with two drain modes.\n//\n// Provider deltas arrive at ~50Hz; flushing each one through React causes the\n// ScrollBox to recompute layout faster than the terminal can clear and\n// repaint, producing overdraw artifacts during scroll. We buffer per owner\n// (parent + each active subagent) and drain via a continuous ticker:\n//\n// - **smooth mode** (default — gated on {@link Settings.smoothStreaming}):\n// drip-feed text character-by-character at {@link SMOOTH_BASE_CPS}, with\n// an adaptive burst when the buffer falls behind. Reads as a typewriter\n// effect, masks bursty provider chunks behind a steady cadence, and\n// always finishes within one tick of the LLM completing because turn\n// boundaries (`flush` / `appendImmediate` / `flushAndUpdate`) drain\n// remaining content unconditionally.\n// - **batched mode** (off): drain everything per tick — equivalent to the\n// legacy \"one 32ms timeout per burst of deltas\" cadence, just expressed\n// against the same ticker.\n//\n// `getSmooth` is called on every tick so toggling the user setting at\n// runtime takes effect on the next drain without rebuilding the buffer.\n// ---------------------------------------------------------------------------\n\nexport interface StreamSource {\n /** Pass `undefined` / omit for parent-agent events. */\n childId?: string\n /** Nesting depth — 0 for parent, ≥ 1 for subagents. */\n depth?: number\n /**\n * `SessionTurn.id` this delta belongs to. Tagged onto new events so the\n * TUI's select-turn mode can group every event from one turn. Stored on\n * the active bucket and refreshed on every delta so a turn boundary\n * (`turn:after` flushes the bucket) doesn't carry stale ids forward.\n */\n turnId?: string\n}\n\nexport interface StreamBuffer {\n /** Queue a streaming delta for the next flush tick. */\n queueStreamDelta: (kind: 'markdown' | 'thinking', delta: string, source?: StreamSource) => void\n /** Drain pending deltas immediately, then append a non-streaming event. */\n appendImmediate: (evt: StreamEvent) => void\n /** Drain pending deltas immediately, then transform the event list. */\n flushAndUpdate: (update: (events: StreamEvent[]) => StreamEvent[]) => void\n /** Drain pending deltas without further transformation. */\n flush: () => void\n /** Cancel any pending flush and drop buffered deltas (on session teardown). */\n reset: () => void\n /**\n * Resolves once the pacer is idle — no bucket holds queued content and\n * no post-drain updater is pending. Resolves immediately when already\n * idle (the batched / `smoothStreaming: false` path never queues, so\n * this is zero-cost there). A safety timeout (default\n * {@link WHEN_DRAINED_TIMEOUT_MS}) resolves anyway if the ticker\n * wedges — measured as a full window with NO drain progress, so a\n * long response that keeps typing re-arms it and never snaps — and\n * callers gating a transcript convergence on the drain can never\n * dead-lock. `reset()` also releases every waiter — the backlog it\n * drops would otherwise never drain.\n */\n whenDrained: (timeoutMs?: number) => Promise<void>\n}\n\nexport interface StreamBufferOptions {\n /**\n * Returns `true` to drip-feed buffered text at the smooth typewriter\n * cadence, `false` to drain everything per tick (batched mode — the\n * legacy behaviour). Called on every tick, so flipping the user setting\n * takes effect on the next drain.\n */\n getSmooth?: () => boolean\n /**\n * Display-memory bound applied on every drain commit (see\n * {@link capTranscriptEvents}): above this many events the oldest are\n * trimmed at a turn boundary and replaced with a cumulative\n * \"… N earlier items hidden\" marker event. Rendered events hold live\n * renderables/native buffers, so an unbounded buffer OOMs long\n * sessions (~0.13MB RSS per event measured in the TUI).\n *\n * Default {@link MAX_TRANSCRIPT_EVENTS}. Pass `null` to disable and\n * receive the full, unbounded event array (pre-5.13 behavior) — e.g.\n * a host that renders a virtualized list or exports events verbatim.\n */\n maxTranscriptEvents?: number | null\n}\n\n/**\n * Target chars-per-second for the next smooth-mode tick given the\n * current bucket backlog. Two linear segments — base→burst over the\n * first {@link SMOOTH_BURST_BACKLOG_CHARS}, burst→max over the rest of\n * the ramp — so the function is continuous everywhere and plateaus\n * (rather than cliffs) once backlog passes {@link SMOOTH_MAX_BACKLOG_CHARS}.\n *\n * Exported for tests; the runtime path goes through\n * {@link smoothCharsForTick} which folds in the per-bucket fractional\n * remainder.\n */\nexport function smoothCpsForBacklog(backlog: number): number {\n if (backlog <= 0)\n return 0\n if (backlog >= SMOOTH_MAX_BACKLOG_CHARS)\n return SMOOTH_MAX_CPS\n if (backlog <= SMOOTH_BURST_BACKLOG_CHARS) {\n const t = backlog / SMOOTH_BURST_BACKLOG_CHARS\n return SMOOTH_BASE_CPS + t * (SMOOTH_BURST_CPS - SMOOTH_BASE_CPS)\n }\n const t = (backlog - SMOOTH_BURST_BACKLOG_CHARS)\n / (SMOOTH_MAX_BACKLOG_CHARS - SMOOTH_BURST_BACKLOG_CHARS)\n return SMOOTH_BURST_CPS + t * (SMOOTH_MAX_CPS - SMOOTH_BURST_CPS)\n}\n\n/**\n * Number of characters to drain on the next smooth-mode tick + the\n * fractional remainder to carry into the next tick.\n *\n * Combining the CPS curve with a fractional accumulator means the\n * effective draining rate matches the target CPS exactly. The legacy\n * `Math.ceil(cps * dt)` rounded UP on every tick — at 200 CPS / 16ms,\n * that's 4 chars/tick (= 250 CPS effective), a 25 % speed-up. Worse,\n * `ceil` produced visible step transitions (4 → 5 → 7 → 8 → 10 as\n * backlog grew) that read as the typewriter cadence jumping.\n */\nfunction smoothCharsForTick(\n backlog: number,\n carry: number,\n dtMs: number,\n): { take: number, carry: number } {\n if (backlog <= 0)\n return { take: 0, carry: 0 }\n const cps = smoothCpsForBacklog(backlog)\n const want = cps * dtMs / 1000 + carry\n const take = Math.floor(want)\n if (take <= 0)\n return { take: 0, carry: want }\n if (take >= backlog) {\n // Drained the bucket — drop the leftover. Holding it across a\n // bucket boundary would invent a head-start for the next stream.\n return { take: backlog, carry: 0 }\n }\n // Don't clamp `take` to 1: with `dtMs * cps / 1000 < 1`, the carry\n // accumulates over the next tick(s) and the char lands when math\n // says it should. The ticker stays alive because the bucket still\n // has content, so the next tick fires regardless.\n return { take, carry: want - take }\n}\n\n/**\n * Slice `n` chars off the front of `buf` without splitting a UTF-16\n * surrogate pair. Most LLM text is ASCII and never trips this; the guard\n * matters for emoji / extended planes where halving the pair would emit\n * an isolated half-character.\n */\nfunction takeChars(buf: string, n: number): { taken: string, rest: string } {\n if (buf.length <= n)\n return { taken: buf, rest: '' }\n let end = n\n const last = buf.charCodeAt(end - 1)\n // High surrogate at the slice boundary → pull the following low\n // surrogate in too. Going one char over budget on a single tick is\n // strictly better than emitting half a code point.\n if (last >= 0xD800 && last <= 0xDBFF && end < buf.length)\n end++\n return { taken: buf.slice(0, end), rest: buf.slice(end) }\n}\n\nexport function useStreamBuffer(\n setEvents: Dispatch<SetStateAction<StreamEvent[]>>,\n options?: StreamBufferOptions,\n): StreamBuffer {\n const bucketsRef = useRef<Map<Owner, DeltaBucket>>(new Map())\n const tickerRef = useRef<ReturnType<typeof setInterval> | null>(null)\n // Real wall-clock timestamp of the last tick. Feeds the dt argument\n // to `smoothCharsForTick` so the drain rate stays accurate even when\n // `setInterval` is late (event-loop congestion, GC pauses); without\n // this a 50ms-late tick would only commit ~16ms-worth of chars and\n // the visible cadence would stall.\n const lastTickMsRef = useRef<number>(0)\n // Pin the latest `getSmooth` callback inside a ref so the ticker closure\n // always sees the current setting without recreating the timer.\n const getSmoothRef = useRef<(() => boolean) | undefined>(options?.getSmooth)\n getSmoothRef.current = options?.getSmooth\n /** Live mirror so drains read the current cap without re-memoizing. */\n const maxEventsRef = useRef<number | null | undefined>(options?.maxTranscriptEvents)\n maxEventsRef.current = options?.maxTranscriptEvents\n\n /**\n * Updaters queued while smooth-streaming was still draining backlog —\n * tool-call appends, finalize-markdown transforms, error events.\n * Applied in FIFO order at the END of `tick()` once every bucket is\n * empty, so the trailing typewriter characters land BEFORE the\n * follow-up event instead of being clobbered by an immediate drain.\n *\n * The buffer treats markdown deltas as the authoritative ordering\n * source: a tool call queued behind 100 buffered chars surfaces after\n * those 100 chars have typed out, preserving the visual stream order\n * the user is reading.\n */\n const pendingUpdatersRef = useRef<((events: StreamEvent[]) => StreamEvent[])[]>([])\n\n /**\n * Pending `whenDrained` promises. Released (with their safety timers\n * cleared) whenever the buffer reaches the idle state: the tick that\n * empties the last bucket, a synchronous `drainNow`, or a `reset`.\n */\n const drainWaitersRef = useRef<{ resolve: () => void, timer: ReturnType<typeof setTimeout> }[]>([])\n\n /**\n * Monotonic count of commits (ticks that landed content or updaters).\n * `whenDrained` waiters compare it across their safety window: movement\n * means the ticker is alive — re-arm instead of firing the escape hatch.\n */\n const drainProgressRef = useRef(0)\n\n const releaseDrainWaiters = useCallback(() => {\n if (drainWaitersRef.current.length === 0)\n return\n const waiters = drainWaitersRef.current\n drainWaitersRef.current = []\n for (const waiter of waiters) {\n clearTimeout(waiter.timer)\n waiter.resolve()\n }\n }, [])\n\n const stopTicker = useCallback(() => {\n if (tickerRef.current) {\n clearInterval(tickerRef.current)\n tickerRef.current = null\n }\n // Forget the last-tick stamp so the next ticker run starts from\n // the nominal interval instead of a stale (potentially seconds-old)\n // delta that would burst-drain the first frame.\n lastTickMsRef.current = 0\n }, [])\n\n /**\n * Has at least one bucket got unflushed *markdown* content?\n *\n * Thinking content is excluded: it always flushes in full on every\n * tick (see `tick()`), so its presence in a bucket only matters until\n * the next 16 ms cycle — never long enough to justify deferring an\n * end-of-turn updater.\n */\n const hasMarkdownBacklog = useCallback((): boolean => {\n for (const bucket of bucketsRef.current.values()) {\n if (bucket.markdown.length > 0)\n return true\n }\n return false\n }, [])\n\n /**\n * Drain every bucket in full and synchronously run any queued\n * post-drain updaters plus the caller-provided one. Used by the\n * fast paths below — `reset`, batched-mode `appendImmediate` /\n * `flushAndUpdate`, and smooth-mode calls where no markdown\n * backlog remains.\n */\n const drainNow = useCallback((updater?: (events: StreamEvent[]) => StreamEvent[]) => {\n stopTicker()\n const buckets = Array.from(bucketsRef.current.values())\n bucketsRef.current.clear()\n const queued = pendingUpdatersRef.current\n pendingUpdatersRef.current = []\n const hasDeltas = buckets.some(b => b.markdown.length > 0 || b.thinking.length > 0)\n if (!hasDeltas && !updater && queued.length === 0) {\n releaseDrainWaiters()\n return\n }\n setEvents((prev) => {\n let merged = prev\n for (const bucket of buckets)\n merged = applyBucket(merged, bucket)\n for (const u of queued)\n merged = u(merged)\n if (updater)\n merged = updater(merged)\n // Memory bound: identity under the cap, turn-boundary head trim\n // above it (see capTranscriptEvents). Session turns stay complete.\n const cap = maxEventsRef.current\n return cap === null ? merged : capTranscriptEvents(merged, cap ?? undefined)\n })\n releaseDrainWaiters()\n }, [releaseDrainWaiters, setEvents, stopTicker])\n\n /**\n * One tick of the continuous drain loop. Walks every live bucket and\n * commits a portion of its content based on the current mode. Once\n * every bucket empties out, fires any post-drain updaters that were\n * queued while smooth streaming was still in progress (turn\n * finalize, tool calls, errors) and stops the ticker.\n *\n * `thinking` content always flushes immediately even in smooth mode —\n * it's an internal-reasoning surface, surfaced for transparency rather\n * than for reading flow, and users expect it to keep up with the model.\n */\n const tick = useCallback(() => {\n const smooth = getSmoothRef.current?.() ?? true\n const buckets = bucketsRef.current\n const portions: DeltaBucket[] = []\n let stillHasContent = false\n\n // Real elapsed time since the last tick. Falls back to the nominal\n // interval on the first tick (when there's nothing to measure\n // against) and clamps to a sane upper bound so a one-off 500ms\n // event-loop stall doesn't burst-drain the entire backlog.\n const now = Date.now()\n const dtMs = lastTickMsRef.current === 0\n ? TICK_INTERVAL_MS\n : Math.min(TICK_INTERVAL_MS * 4, Math.max(1, now - lastTickMsRef.current))\n lastTickMsRef.current = now\n\n for (const bucket of buckets.values()) {\n const thinking = bucket.thinking\n bucket.thinking = ''\n\n let taken = ''\n if (bucket.markdown.length > 0) {\n if (smooth) {\n const { take, carry } = smoothCharsForTick(\n bucket.markdown.length,\n bucket.smoothCarry,\n dtMs,\n )\n bucket.smoothCarry = carry\n if (take > 0) {\n const result = takeChars(bucket.markdown, take)\n taken = result.taken\n bucket.markdown = result.rest\n if (bucket.markdown.length === 0)\n bucket.smoothCarry = 0\n }\n }\n else {\n taken = bucket.markdown\n bucket.markdown = ''\n bucket.smoothCarry = 0\n }\n }\n\n if (taken || thinking) {\n portions.push({\n markdown: taken,\n thinking,\n owner: bucket.owner,\n depth: bucket.depth,\n turnId: bucket.turnId,\n smoothCarry: 0,\n })\n }\n if (bucket.markdown.length > 0)\n stillHasContent = true\n }\n\n // Fold post-drain updaters into the same commit as the trailing\n // portions so the resulting transcript has at most one paint per\n // tick — and so the LAST portion of a smooth stream and its\n // companion finalize-markdown / tool-call event land atomically.\n const drainQueued = !stillHasContent && pendingUpdatersRef.current.length > 0\n const queued = drainQueued\n ? pendingUpdatersRef.current.splice(0, pendingUpdatersRef.current.length)\n : []\n\n if (portions.length > 0 || queued.length > 0) {\n drainProgressRef.current += 1\n setEvents((prev) => {\n let next = prev\n for (const portion of portions)\n next = applyBucket(next, portion)\n for (const u of queued)\n next = u(next)\n // Same memory bound as `drainNow`: deferred updaters append\n // tool-call / error events too, so this commit path must not\n // become a cap-bypass window. Identity under the cap.\n const cap = maxEventsRef.current\n return cap === null ? next : capTranscriptEvents(next, cap ?? undefined)\n })\n }\n\n if (!stillHasContent) {\n stopTicker()\n // Buckets and queued updaters both landed in this commit — the\n // buffer is idle, so end-of-stream convergence can proceed.\n releaseDrainWaiters()\n }\n }, [releaseDrainWaiters, setEvents, stopTicker])\n\n const ensureTicker = useCallback(() => {\n if (tickerRef.current)\n return\n tickerRef.current = setInterval(tick, TICK_INTERVAL_MS)\n }, [tick])\n\n /**\n * Decide between fast-drain and deferred-drain for an\n * updater-bearing call (`appendImmediate`, `flushAndUpdate`).\n *\n * Smooth mode + markdown backlog → ENQUEUE the updater behind the\n * trailing characters; the ticker picks it up once buckets empty.\n * Anything else → fall through to `drainNow` so the call retains\n * its synchronous semantics.\n */\n const drainOrDefer = useCallback((updater: (events: StreamEvent[]) => StreamEvent[]) => {\n const smooth = getSmoothRef.current?.() ?? true\n if (smooth && hasMarkdownBacklog()) {\n pendingUpdatersRef.current.push(updater)\n ensureTicker()\n return\n }\n drainNow(updater)\n }, [drainNow, ensureTicker, hasMarkdownBacklog])\n\n const flush = useCallback(() => {\n // In smooth mode with backlog, the ticker is already draining at\n // the typewriter cadence — explicit `flush()` shouldn't yank the\n // remaining characters into a single frame. The ticker is\n // self-stopping once empty, so this becomes a no-op there.\n const smooth = getSmoothRef.current?.() ?? true\n if (smooth && hasMarkdownBacklog()) {\n ensureTicker()\n return\n }\n drainNow()\n }, [drainNow, ensureTicker, hasMarkdownBacklog])\n\n const flushAndUpdate = useCallback(\n (update: (events: StreamEvent[]) => StreamEvent[]) => drainOrDefer(update),\n [drainOrDefer],\n )\n\n const appendImmediate = useCallback(\n (evt: StreamEvent) => drainOrDefer(events => [...events, evt]),\n [drainOrDefer],\n )\n\n const queueStreamDelta = useCallback((\n kind: 'markdown' | 'thinking',\n delta: string,\n source?: StreamSource,\n ) => {\n if (!delta)\n return\n const owner: Owner = source?.childId ?? PARENT_OWNER\n const depth = source?.depth ?? 0\n let bucket = bucketsRef.current.get(owner)\n if (!bucket) {\n bucket = emptyBucket(owner, depth)\n bucketsRef.current.set(owner, bucket)\n }\n bucket[kind] += delta\n // Refresh on every delta — `turn:after` drains the bucket between turns,\n // so a turnId only ever applies to the deltas of one turn. Keeping the\n // latest also lets a turn that starts mid-bucket (rare but theoretically\n // possible if two hooks race the flush ticker) tag the new event.\n if (source?.turnId)\n bucket.turnId = source.turnId\n ensureTicker()\n }, [ensureTicker])\n\n const reset = useCallback(() => {\n stopTicker()\n bucketsRef.current.clear()\n pendingUpdatersRef.current = []\n // The dropped backlog will never drain — release waiters instead of\n // stranding them until the safety timeout.\n releaseDrainWaiters()\n }, [releaseDrainWaiters, stopTicker])\n\n const whenDrained = useCallback((timeoutMs: number = WHEN_DRAINED_TIMEOUT_MS): Promise<void> => {\n let idle = pendingUpdatersRef.current.length === 0\n if (idle) {\n for (const bucket of bucketsRef.current.values()) {\n if (bucket.markdown.length > 0 || bucket.thinking.length > 0) {\n idle = false\n break\n }\n }\n }\n if (idle)\n return Promise.resolve()\n return new Promise((resolve) => {\n // Safety valve: a wedged ticker (cleared interval, starved event\n // loop) must not block the caller forever — resolve anyway and\n // drop the waiter so a late drain doesn't double-resolve. Armed\n // per window of NO progress: a tick that committed content since\n // the last check proves the drain is alive (a long response types\n // for longer than any fixed deadline), so re-arm instead of\n // snapping the tail into the caller's convergence.\n let seenProgress = drainProgressRef.current\n const waiter = {\n resolve,\n timer: undefined as unknown as ReturnType<typeof setTimeout>,\n }\n const arm = (): ReturnType<typeof setTimeout> => setTimeout(() => {\n if (drainProgressRef.current !== seenProgress) {\n seenProgress = drainProgressRef.current\n waiter.timer = arm()\n return\n }\n drainWaitersRef.current = drainWaitersRef.current.filter(w => w !== waiter)\n resolve()\n }, timeoutMs)\n waiter.timer = arm()\n drainWaitersRef.current.push(waiter)\n })\n }, [])\n\n // Unmount hygiene: the interval ticker outlives the render otherwise and\n // fires `setEvents` against an unmounted host. `reset` is stable (all\n // deps are useCallback'd), so this registers exactly once and never\n // perturbs the memoized return below.\n useEffect(() => reset, [reset])\n\n // CRITICAL: stabilize the returned object. All six callbacks are useCallback'd\n // (stable across renders), but a bare object-literal return would be a fresh\n // reference every call, which cascades through every `useCallback`/`useEffect`\n // that depends on `stream` and turns the resume effect into an unbounded\n // destroy/rebuild loop — the exact source of the terminal-host memory leak.\n return useMemo(\n () => ({ queueStreamDelta, appendImmediate, flushAndUpdate, flush, reset, whenDrained }),\n [queueStreamDelta, appendImmediate, flushAndUpdate, flush, reset, whenDrained],\n )\n}\n","/**\n * System-prompt override discovery — load a `SYSTEM.md` that REPLACES the\n * built-in doctrine.\n *\n * The sibling of `agents-md.ts`, with deliberately inverted semantics:\n *\n * | | `AGENTS.md` | `SYSTEM.md` |\n * |------------|------------------------------|---------------------------------|\n * | effect | appends to the doctrine | replaces the doctrine |\n * | multiples | every match loads, in order | exactly one wins |\n * | precedence | broad → narrow, all present | project beats user, rest shadowed |\n *\n * Concatenating overrides would be incoherent — two files each claiming to BE\n * the system prompt can't both be it — so a single winner is picked and the\n * losers are reported in {@link SystemMdResult.shadowed} for the UI to show.\n * A surprising precedence should be visible, not mysterious.\n *\n * Search order (winner first):\n *\n * 1. `<project root>/SYSTEM.md` — project scope, most specific, wins\n * 2. `~/.{prefix}/SYSTEM.md` — user scope (`~/.zidane/SYSTEM.md`)\n *\n * What the override does NOT replace: the `AGENTS.md` block (a separate,\n * additive layer) and the `<env>` section below the prompt-cache boundary.\n * Stripping `<env>` would take the agent's cwd + project root with it and\n * silently break path resolution in the file tools — an override is for\n * rewriting the agent's instructions, not amputating its sense of place.\n */\n\nimport type { AgentsMdScope } from './agents-md'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { errorMessage } from '../errors'\nimport { findGitRoot } from './project-root'\n\n/** A discovered `SYSTEM.md`. */\nexport interface SystemMdFile {\n path: string\n source: 'project' | 'user'\n contents: string\n}\n\n/** Result of {@link discoverSystemMd}. */\nexport interface SystemMdResult {\n /**\n * The winning override, or `null` when no `SYSTEM.md` exists (the built-in\n * doctrine then applies unchanged).\n */\n file: SystemMdFile | null\n /**\n * Files that exist but lost to `file` on precedence. Non-empty only when the\n * user has both a project and a user `SYSTEM.md`. Surfaced so the UI can mark\n * them as inactive instead of leaving the user wondering why edits to one of\n * them changed nothing.\n */\n shadowed: readonly SystemMdFile[]\n}\n\n/** Same per-file cap as `agents-md.ts` — a runaway file shouldn't blow the prompt. */\nconst MAX_BYTES_PER_FILE = 64 * 1024\n\nexport interface DiscoverSystemMdOptions {\n /** Discovery cwd. Default: `process.cwd()`. */\n cwd?: string\n /** User home directory. Default: `os.homedir()`. */\n home?: string\n /** Storage prefix. Leading dot tolerated. Default: `'.zidane'`. */\n prefix?: string\n /**\n * Which scopes may contribute an override — the same\n * {@link AgentsMdScope} the host applies to `AGENTS.md`, so one setting\n * governs both surfaces:\n * - `'none'` — no override, ever. Short-circuits before any FS probe.\n * - `'user'` — only `~/.{prefix}/SYSTEM.md`. The repo's file is IGNORED,\n * which is the whole point of the setting: it is how a user\n * says \"do not trust this checkout\".\n * - `'project'` — only `<root>/SYSTEM.md`.\n * - `'both'` — union, project winning (default).\n *\n * Previously a boolean that only implemented `'none'`, so `'user'` still let a\n * repo-root `SYSTEM.md` replace the entire doctrine — exactly the case the\n * setting exists to prevent.\n */\n scope?: AgentsMdScope\n}\n\n/**\n * Absolute path of the USER-scope `SYSTEM.md` — the file the TUI creates when\n * the user asks for an override and none exists yet.\n *\n * Exported so the host doesn't hardcode the layout and can't drift from what\n * discovery actually reads.\n */\nexport function userSystemMdPath(opts: { home?: string, prefix?: string } = {}): string {\n const home = opts.home ?? homedir()\n const prefix = (opts.prefix ?? '.zidane').replace(/^\\./, '')\n return join(home, `.${prefix}`, 'SYSTEM.md')\n}\n\n/**\n * Whether a USER-scope `SYSTEM.md` would be loaded under `scope`.\n *\n * Mirrors the candidate filter in {@link discoverSystemMd} so a host can ask\n * \"would writing `~/.{prefix}/SYSTEM.md` accomplish anything?\" without\n * re-deriving the rule and drifting from it. `'project'` excludes the user\n * candidate and `'none'` skips discovery altogether, so under either setting\n * creating that file is a silent no-op.\n */\nexport function userScopeOverridesLoad(scope: AgentsMdScope | undefined): boolean {\n const effective: AgentsMdScope = scope ?? 'both'\n return effective !== 'project' && effective !== 'none'\n}\n\n/**\n * Whether a PROJECT-scope `SYSTEM.md` would be loaded under `scope`.\n *\n * The mirror of {@link userScopeOverridesLoad}, against the same candidate\n * filter. Used by {@link renderSystemMdSeed} to decide whether telling the user\n * \"a repo file outranks this one\" is true: under `'user'` the project candidate\n * is never even probed, so claiming a checkout can outrank them contradicts the\n * setting they chose precisely to distrust checkouts.\n */\nexport function projectScopeOverridesLoad(scope: AgentsMdScope | undefined): boolean {\n const effective: AgentsMdScope = scope ?? 'both'\n return effective !== 'user' && effective !== 'none'\n}\n\n/**\n * Find the active `SYSTEM.md` override, if any.\n *\n * Project paths anchor at the git root when one exists above `cwd`, else at\n * `cwd` — the same anchor as skills / mcps / AGENTS.md discovery, so all four\n * surfaces agree on what \"this project\" means.\n *\n * Tolerant: an unreadable or empty file is treated as absent (logged under\n * `ZIDANE_DEBUG`) and the next candidate gets its turn. Never throws — this\n * runs on the session-activation path.\n *\n * Candidates are filtered by {@link DiscoverSystemMdOptions.scope}, mirroring\n * how `discoverAgentsMd` builds its own list.\n */\nexport function discoverSystemMd(opts: DiscoverSystemMdOptions = {}): SystemMdResult {\n const scope: AgentsMdScope = opts.scope ?? 'both'\n // Short-circuit before touching the filesystem, same as `discoverAgentsMd`.\n if (scope === 'none')\n return { file: null, shadowed: [] }\n\n const cwd = opts.cwd ?? process.cwd()\n const projectRoot = findGitRoot(cwd) ?? cwd\n\n const candidates: { path: string, source: 'project' | 'user' }[] = []\n if (scope !== 'user')\n candidates.push({ path: join(projectRoot, 'SYSTEM.md'), source: 'project' })\n if (scope !== 'project')\n candidates.push({ path: userSystemMdPath(opts), source: 'user' })\n\n const found: SystemMdFile[] = []\n const seen = new Set<string>()\n for (const c of candidates) {\n // Dedup by absolute path. The two candidates can only ever be the same\n // file when `prefix` is empty or dot-only: `userSystemMdPath` then\n // collapses to `<home>/SYSTEM.md`, which equals the project candidate if\n // `projectRoot` is also `<home>`. With any real prefix (`.zidane`) they are\n // `<root>/SYSTEM.md` and `<home>/.zidane/SYSTEM.md` — distinct whether or\n // not `$HOME` happens to be a git repo. Without the guard that one case\n // would report the file as shadowing itself.\n if (seen.has(c.path))\n continue\n seen.add(c.path)\n const file = readIfPresent(c.path, c.source)\n if (file)\n found.push(file)\n }\n\n const [winner, ...shadowed] = found\n return { file: winner ?? null, shadowed }\n}\n\nfunction readIfPresent(path: string, source: 'project' | 'user'): SystemMdFile | null {\n if (!existsSync(path))\n return null\n let raw: string\n try {\n raw = readFileSync(path, 'utf8')\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/chat] system-md: failed to read \"${path}\": ${errorMessage(err)}\\n`)\n return null\n }\n const contents = raw.length > MAX_BYTES_PER_FILE\n ? `${raw.slice(0, MAX_BYTES_PER_FILE)}\\n\\n[... truncated; ${raw.length - MAX_BYTES_PER_FILE} more bytes in source file ...]`\n : raw\n // An empty override is almost certainly an accident (a file created but not\n // yet written). Treating it as absent keeps the built-in doctrine in place\n // rather than handing the model a blank prompt.\n if (contents.trim().length === 0)\n return null\n return { path, source, contents }\n}\n\n/**\n * Seed contents for a freshly created `SYSTEM.md` override, built from the\n * built-in doctrine.\n *\n * Seeding with the real doctrine (rather than an empty file) means creating the\n * override is a behavioral no-op until the user edits it. An empty seed would\n * silently delete every line of doctrine the moment the file appeared, making\n * the agent measurably worse with no visible cause.\n *\n * `currentSystem` should be DOCTRINE ONLY — callers pass\n * `splitSystemPrompt(doctrineOnlySystem()).static`, which excludes both the\n * generated `<env>` block (rebuilt per turn) and the `AGENTS.md` block. Baking\n * the latter in would duplicate it, since it is re-appended below the override\n * on every compose.\n *\n * `scope` is the active `userInstructionsScope`; it decides whether the seed's\n * precedence note is true — see {@link projectScopeOverridesLoad}.\n */\nexport function renderSystemMdSeed(currentSystem: string, scope?: AgentsMdScope): string {\n return [\n '<!--',\n ' SYSTEM.md — this file REPLACES zidane\\'s built-in system prompt.',\n '',\n ' It was seeded with the built-in doctrine, so right now it changes nothing.',\n ' Edit freely.',\n '',\n ' Still applied on top of whatever you write here:',\n ' - your AGENTS.md / CLAUDE.md instructions (appended below this text)',\n ' - the <env> block (cwd, project root, date, platform)',\n '',\n // Only true when the project candidate is actually probed. Under\n // `userInstructionsScope: 'user'` it is not, and creation is still allowed\n // there — so printing this unconditionally told the one user who opted out\n // of project files that a checkout outranks them.\n ...(projectScopeOverridesLoad(scope)\n ? [' A project-level SYSTEM.md at the repo root takes precedence over this one.']\n : [' userInstructionsScope is \"user\", so a repo-level SYSTEM.md is ignored and', ' this file always wins.']),\n ' Delete this file to restore the built-in prompt.',\n '-->',\n '',\n currentSystem.trim(),\n '',\n ].join('\\n')\n}\n","import type { ReactNode } from 'react'\nimport type { SyntaxStyles, Theme, ThemeColors, ThemeSelect, ThemeSurfaces } from './theme'\nimport { createContext, useContext } from 'react'\nimport { DEFAULT_THEME } from './theme'\n\n// ---------------------------------------------------------------------------\n// ThemeContext\n//\n// The TUI / GUI mounts `<ThemeProvider theme={…}>` near the root of its tree,\n// resolving the active theme from `Settings.theme`. Every component reads\n// theme values through a hook so a runtime theme switch (Settings → Theme)\n// triggers an immediate re-paint of the whole tree.\n//\n// Sub-hooks (`useColors`, `useSelectStyle`, …) exist so consumers can grab\n// just the slice they need — the rename-friendly shape lets a component\n// keep its body unchanged when migrating from the static `COLOR.brand`\n// pattern to `const COLOR = useColors(); COLOR.brand`.\n// ---------------------------------------------------------------------------\n\nconst ThemeContext = createContext<Theme>(DEFAULT_THEME)\n\nexport function ThemeProvider({ theme, children }: { theme: Theme, children: ReactNode }) {\n return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>\n}\n\nexport function useTheme(): Theme {\n return useContext(ThemeContext)\n}\n\n/** Color palette only — equivalent to `useTheme().colors`. */\nexport function useColors(): ThemeColors {\n return useContext(ThemeContext).colors\n}\n\n/** Select-row styling — equivalent to `useTheme().select`. */\nexport function useSelectStyle(): ThemeSelect {\n return useContext(ThemeContext).select\n}\n\n/** Panel / surface backgrounds — equivalent to `useTheme().surfaces`. */\nexport function useSurfaces(): ThemeSurfaces {\n return useContext(ThemeContext).surfaces\n}\n\n/** Raw syntax style table — `useTheme().syntax`. Renderer converts to its native style type. */\nexport function useSyntaxStyles(): SyntaxStyles {\n return useContext(ThemeContext).syntax\n}\n","import type { AgentHookMap } from '../agent'\nimport type { TodoItem } from './todos'\nimport { appendDynamicSection } from '../system-prompt'\nimport { selectActiveTodos } from './todos'\n\nexport interface ActiveTodoAnchorOptions {\n enabled?: boolean\n maxItems?: number\n heading?: string\n}\n\nexport function renderActiveTodoAnchor(\n todos: readonly TodoItem[],\n options: ActiveTodoAnchorOptions = {},\n): string {\n if (options.enabled === false || todos.length === 0)\n return ''\n const maxItems = Math.max(1, Math.floor(options.maxItems ?? 6))\n const heading = options.heading ?? 'Active Todo Anchor'\n const visible = todos.slice(0, maxItems)\n const lines = [`# ${heading}`, '<active_todos>']\n for (const item of visible)\n lines.push(`- [${item.status}] ${item.content}`)\n if (todos.length > visible.length)\n lines.push(`- ... ${todos.length - visible.length} more`)\n lines.push('</active_todos>')\n return lines.join('\\n')\n}\n\n/**\n * Optional chat-layer hook that keeps the live `todowrite` list visible in the\n * dynamic system prompt. It reads only live todos, never the archive, so an\n * auto-cleared completed plan is not reintroduced on later turns.\n *\n * ## Prompt-cache cost — read before enabling\n *\n * This writes per-turn state into the system prompt, and the system prompt sits\n * ABOVE the entire conversation in the provider's cache prefix (Anthropic hashes\n * tools → system → messages, cumulatively). So every time the rendered anchor\n * changes by a single byte, the whole transcript stops matching the cache and\n * re-bills at the cache-write rate; only `tools` and the static system block\n * survive.\n *\n * That is an acceptable trade only because the anchor changes *rarely* — once\n * per `todowrite`, which in practice is a small fraction of turns. Two rules\n * follow, and breaking either one turns that occasional cost into a per-turn\n * one:\n *\n * 1. Never add anything to the anchor that ticks on its own — no counters, no\n * timestamps, no elapsed times, no per-turn indices.\n * 2. The render must stay a pure function of the todo list.\n * `renderActiveTodoAnchor` is deterministic today, so identical todos emit\n * identical bytes; keep it that way. Note this does NOT cover reordering —\n * todo order is semantically meaningful (it is the plan), so a reorder is\n * a real state change and costs one re-bill like any other `todowrite`.\n *\n * Moving the anchor onto the newest message instead does NOT avoid this: it is\n * wire-only, so the message that carried last turn's anchor re-renders without\n * it, and the prefix diverges at that point just the same. The only injection\n * that is genuinely free is one persisted into the transcript as immutable\n * history.\n */\nexport function createActiveTodoAnchorHooks(options: ActiveTodoAnchorOptions = {}): AgentHookMap {\n return {\n 'system:transform': (ctx) => {\n const state = selectActiveTodos(ctx.session)\n const anchor = renderActiveTodoAnchor(state.todos, options)\n\n if (anchor)\n ctx.system = appendDynamicSection(ctx.system, anchor)\n },\n }\n}\n","// ---------------------------------------------------------------------------\n// Transcript scroll-anchor computation.\n//\n// Walks a list of partitioned transcript items (one entry per row that\n// will be rendered) and assigns a stable scroll-anchor id to the FIRST\n// event of each turn — `turn-anchor-<turnId>`. The renderer applies\n// these ids to whichever container hosts the first event of the turn;\n// scroll-into-view (TUI scrollbox or GUI element.scrollIntoView) then\n// resolves the id to a known target.\n//\n// Pure data-in / data-out so the same logic powers the TUI today and\n// any future GUI without dragging OpenTUI specifics into the chat\n// layer.\n// ---------------------------------------------------------------------------\n\nimport type { StreamEvent } from './types'\n\n/**\n * One entry in the partitioned transcript-row list. Single events\n * render as a normal row; a `child-run` groups ONE subagent's events\n * from a consecutive run of subagent output into one bordered box\n * (concurrent siblings get a `child-run` each, so their rows never\n * braid together inside a shared frame); `tool-group`s\n * collect ALL parent-owned tool/tool-result events of one exchange\n * into a single collapsible summary row (`Settings.groupToolCalls`).\n * `key` is stable across re-partitions of the same stream (derived\n * from the first grouped event) so expand state survives appends.\n *\n * Currently produced by the TUI's `partitionTranscript`; a GUI shell\n * grouping subagent runs the same way can produce the same shape.\n */\nexport type TranscriptItem\n = | { kind: 'event', event: StreamEvent, previous?: StreamEvent }\n | { kind: 'child-run', events: StreamEvent[], previous?: StreamEvent }\n | { kind: 'tool-group', key: string, events: StreamEvent[], previous?: StreamEvent }\n\n/**\n * Per-item anchor ids for auto-scroll. Walks `items` in render order\n * and, for each event, returns either:\n * - `'turn-anchor-<turnId>'` — the first event of this turn (the\n * scroll target).\n * - `undefined` — later event of an already-tagged turn (or a\n * synthetic event with no `turnId`).\n *\n * `ids[i]` is a tuple per item: length 1 for plain events, length N\n * for subagent runs (one entry per inner event). `idByTurn` is the\n * inverse lookup used by the scroll effect. `lastTurnId` is the\n * most-recently-rendered turn — the scroll effect special-cases it to\n * snap to bottom rather than scroll the anchor into view (which would\n * stop short of the actual tail).\n *\n * Exported so the anchor-tagging matrix can be unit-tested without\n * rendering anything.\n */\nexport function computeTurnAnchors(items: readonly TranscriptItem[]): {\n idByTurn: ReadonlyMap<string, string>\n ids: readonly (readonly (string | undefined)[])[]\n lastTurnId: string | undefined\n} {\n const idByTurn = new Map<string, string>()\n let lastTurnId: string | undefined\n const tag = (turnId: string | undefined): string | undefined => {\n if (!turnId)\n return undefined\n lastTurnId = turnId\n if (idByTurn.has(turnId))\n return undefined\n const id = `turn-anchor-${turnId}`\n idByTurn.set(turnId, id)\n return id\n }\n const ids: (string | undefined)[][] = []\n for (const item of items) {\n if (item.kind === 'event')\n ids.push([tag(item.event.turnId)])\n else\n ids.push(item.events.map(e => tag(e.turnId)))\n }\n return { idByTurn, ids, lastTurnId }\n}\n","/**\n * Approval hub — safe-mode HITL gate.\n *\n * Tiered gate matching the TUI:\n * 1. `IMPLICITLY_SAFE_TOOLS` (covered inside `isOnSafelist`) — read-only built-ins pass.\n * 2. Project safelist (`projects.json[cwd].safelist`) — persisted matches pass.\n * 3. Otherwise queue + broadcast an `approval:request` and wait for ANY\n * client to resolve it.\n *\n * Requests are persisted through `PendingApprovalsStore` BEFORE broadcasting\n * so a crash between insert and resolve still surfaces the request after\n * restart (rehydrated as a \"ghost\" — resolvable in the UI, but the original\n * awaiting run died with the previous process).\n */\nimport type { ApprovalDecision, EditPayload } from '../index'\nimport type { PendingApprovalsStore } from './environment'\nimport type { ApprovalOriginator, HostSignal, SerializedPreview, WireApprovalRequest } from './signals'\nimport { randomUUID } from 'node:crypto'\nimport { addToSafelist, getSafelist, isOnSafelist, suggestSafelistEntries } from '../index'\n\ninterface PendingRequest {\n request: WireApprovalRequest\n resolve: (decision: ApprovalDecision) => void\n}\n\nexport interface ApprovalRequestInput {\n sessionId: string\n cwd: string\n tool: string\n input: Record<string, unknown>\n edit?: EditPayload\n priorContent?: string | null\n preview?: SerializedPreview\n originator?: ApprovalOriginator\n}\n\nexport interface ApprovalHub {\n request: (input: ApprovalRequestInput) => Promise<ApprovalDecision>\n resolve: (id: string, decision: ApprovalDecision) => void\n denyAll: (sessionId?: string) => void\n listPending: () => WireApprovalRequest[]\n /** Register ghost resolvers for approvals persisted by a prior process lifetime. */\n rehydrate: () => void\n}\n\n/** In-memory fallback when the host doesn't provide durable persistence. */\nexport function createMemoryApprovalsStore(): PendingApprovalsStore {\n const rows = new Map<string, WireApprovalRequest>()\n return {\n insert: request => void rows.set(request.id, request),\n delete: id => void rows.delete(id),\n clear: () => rows.clear(),\n list: () => [...rows.values()],\n }\n}\n\nexport function createApprovalHub(options: {\n broadcast: (signal: HostSignal) => void\n /** Base dir for the shared `projects.json` safelist (`~/.zidane`). */\n userDataDir: () => string\n persistence?: PendingApprovalsStore\n}): ApprovalHub {\n const { broadcast, userDataDir } = options\n const persistence = options.persistence ?? createMemoryApprovalsStore()\n const pending = new Map<string, PendingRequest>()\n\n return {\n async request(input) {\n const safelist = getSafelist(userDataDir(), input.cwd)\n if (isOnSafelist(safelist, input.tool, input.input))\n return 'accept-once'\n\n const id = randomUUID()\n const full: WireApprovalRequest = {\n id,\n sessionId: input.sessionId,\n cwd: input.cwd,\n tool: input.tool,\n input: input.input,\n suggestedSafelistEntries: suggestSafelistEntries(input.tool, input.input),\n ...(input.edit ? { edit: input.edit } : {}),\n ...(input.priorContent !== undefined ? { priorContent: input.priorContent } : {}),\n ...(input.preview ? { preview: input.preview } : {}),\n // Omit the implicit `'parent'` case so the payload stays minimal.\n ...(input.originator && input.originator.kind === 'child' ? { originator: input.originator } : {}),\n }\n\n /*\n * Persist before broadcasting so a crash between insert and resolve\n * still surfaces the request on next start. The persisted row is the\n * source of truth; the in-memory Map only adds the live resolver.\n */\n persistence.insert(full)\n\n return new Promise<ApprovalDecision>((resolve) => {\n pending.set(id, { request: full, resolve })\n broadcast({ kind: 'approval:request', request: full })\n })\n },\n\n resolve(id, decision) {\n const entry = pending.get(id)\n\n /*\n * Persist safelist BEFORE removing the pending row so a crash between\n * the two doesn't leak: either the safelist write lands and we lose\n * the row, or both roll back.\n */\n if (entry && decision === 'accept-safelist') {\n try {\n const entries\n = entry.request.suggestedSafelistEntries\n ?? suggestSafelistEntries(entry.request.tool, entry.request.input)\n addToSafelist(userDataDir(), entry.request.cwd, entries)\n }\n catch {\n // Best-effort — the user still gets accept-once semantics below.\n }\n }\n\n persistence.delete(id)\n\n if (!entry)\n return\n\n pending.delete(id)\n entry.resolve(decision)\n broadcast({ kind: 'approval:resolved', sessionId: entry.request.sessionId, requestId: id, decision })\n },\n\n denyAll(sessionId) {\n if (sessionId === undefined) {\n for (const [id, entry] of pending) {\n entry.resolve('deny')\n broadcast({ kind: 'approval:resolved', sessionId: entry.request.sessionId, requestId: id, decision: 'deny' })\n }\n pending.clear()\n persistence.clear()\n return\n }\n\n for (const [id, entry] of pending) {\n if (entry.request.sessionId !== sessionId)\n continue\n entry.resolve('deny')\n pending.delete(id)\n persistence.delete(id)\n broadcast({ kind: 'approval:resolved', sessionId, requestId: id, decision: 'deny' })\n }\n },\n\n listPending() {\n return persistence.list()\n },\n\n rehydrate() {\n for (const req of persistence.list()) {\n if (pending.has(req.id))\n continue\n /*\n * Ghost resolver: the original agent run died with the previous\n * process. Resolving just clears the slot; the persisted row is\n * removed by `resolve` (and `accept-safelist` still persists the\n * safelist entry — useful on resume).\n */\n pending.set(req.id, { request: req, resolve: () => undefined })\n }\n },\n }\n}\n"],"mappings":"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"}
|