zidane 4.1.4 → 4.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +3 -3
  2. package/dist/{agent-BoV5Twdl.d.ts → agent-BAoqUvwA.d.ts} +27 -1
  3. package/dist/{agent-BoV5Twdl.d.ts.map → agent-BAoqUvwA.d.ts.map} +1 -1
  4. package/dist/{index-28otmfLX.d.ts → index-B8-yNSsk.d.ts} +2 -2
  5. package/dist/index-B8-yNSsk.d.ts.map +1 -0
  6. package/dist/{index-DPsd0qwm.d.ts → index-CqpNqjDy.d.ts} +2 -2
  7. package/dist/{index-DPsd0qwm.d.ts.map → index-CqpNqjDy.d.ts.map} +1 -1
  8. package/dist/index.d.ts +3 -3
  9. package/dist/index.js +4 -4
  10. package/dist/mcp.d.ts +1 -1
  11. package/dist/{presets-Cs7_CsMk.js → presets-BzkJDW1K.js} +3 -3
  12. package/dist/presets-BzkJDW1K.js.map +1 -0
  13. package/dist/presets.d.ts +1 -1
  14. package/dist/presets.js +1 -1
  15. package/dist/{providers-CX-R-Oy-.js → providers-CCDvIXGJ.js} +26 -5
  16. package/dist/providers-CCDvIXGJ.js.map +1 -0
  17. package/dist/providers.d.ts +1 -1
  18. package/dist/providers.js +1 -1
  19. package/dist/session/sqlite.d.ts +1 -1
  20. package/dist/session.d.ts +1 -1
  21. package/dist/skills.d.ts +2 -2
  22. package/dist/{stats-DoKUtF5T.js → stats-BT9l57RS.js} +34 -2
  23. package/dist/stats-BT9l57RS.js.map +1 -0
  24. package/dist/{tools-DpeWKzP1.js → tools-C8kDot0H.js} +73 -23
  25. package/dist/tools-C8kDot0H.js.map +1 -0
  26. package/dist/tools.d.ts +2 -2
  27. package/dist/tools.js +1 -1
  28. package/dist/tui.d.ts +423 -80
  29. package/dist/tui.d.ts.map +1 -1
  30. package/dist/tui.js +1604 -250
  31. package/dist/tui.js.map +1 -1
  32. package/dist/types.d.ts +2 -2
  33. package/dist/types.js +1 -1
  34. package/package.json +1 -1
  35. package/dist/index-28otmfLX.d.ts.map +0 -1
  36. package/dist/presets-Cs7_CsMk.js.map +0 -1
  37. package/dist/providers-CX-R-Oy-.js.map +0 -1
  38. package/dist/stats-DoKUtF5T.js.map +0 -1
  39. package/dist/tools-DpeWKzP1.js.map +0 -1
package/dist/tui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tui.js","names":["EmptyState","BUILTIN_FACTORIES","BUILTIN_PRESET","initMd4x"],"sources":["../src/tui/format.ts","../src/tui/theme.ts","../src/tui/components.tsx","../src/tui/auth.ts","../src/tui/providers.ts","../src/tui/store.ts","../src/tui/config.tsx","../src/tui/modal.tsx","../src/tui/model-picker.tsx","../src/tui/screens.tsx","../src/tui/settings.tsx","../src/tui/streaming.ts","../src/tui/app.tsx","../src/tui/index.tsx"],"sourcesContent":["/** Compact token formatter — 12_415 → \"12.4k\", 1_234_567 → \"1.23M\". */\nexport function fmtTokens(n: number): string {\n if (n < 1000)\n return String(n)\n if (n < 1_000_000)\n return `${(n / 1000).toFixed(n < 10_000 ? 2 : 1)}k`\n return `${(n / 1_000_000).toFixed(2)}M`\n}\n\n/** Compact relative-time formatter — \"just now / 5m / 3h / 2d\". */\nexport function ageString(ts: number, now: number = Date.now()): string {\n const m = Math.floor((now - ts) / 60_000)\n if (m < 1)\n return 'just now'\n if (m < 60)\n return `${m}m ago`\n const h = Math.floor(m / 60)\n if (h < 24)\n return `${h}h ago`\n return `${Math.floor(h / 24)}d ago`\n}\n\n/** Six-char short form of a session id for headers and lists. */\nexport function shortId(id: string): string {\n return id.replace(/-/g, '').slice(0, 6)\n}\n","import { RGBA, SyntaxStyle } from '@opentui/core'\n\n/**\n * Shared color palette. Kept as plain hex strings so it can be consumed by\n * OpenTUI props that accept `string | RGBA`. The names describe role, not\n * literal hue, so the theme can be swapped without touching call sites.\n */\nexport const COLOR = {\n brand: '#FFCC00',\n accent: '#00FF88',\n model: '#88CCFF',\n warn: '#FFAA66',\n error: '#FF6666',\n dim: '#888888',\n mute: '#555555',\n border: '#333333',\n borderActive: '#555555',\n} as const\n\n/**\n * Shared select styling — keeps the highlight bar from filling with a\n * different background than the surrounding box. The `▶` marker and the\n * brand-colored selected text carry the focus affordance.\n */\nexport const SELECT_THEME = {\n backgroundColor: 'transparent',\n focusedBackgroundColor: 'transparent',\n selectedBackgroundColor: 'transparent',\n selectedTextColor: COLOR.brand,\n textColor: COLOR.dim,\n descriptionColor: COLOR.mute,\n selectedDescriptionColor: COLOR.dim,\n} as const\n\n/**\n * Theme for markdown token highlighting. Token names map to Tree-sitter highlight\n * captures emitted by OpenTUI's markdown parser; the `default` entry is the\n * fallback for unstyled text.\n */\nexport const MD_STYLE = SyntaxStyle.fromStyles({\n 'default': { fg: RGBA.fromHex('#E6EDF3') },\n 'markup.heading': { fg: RGBA.fromHex(COLOR.brand), bold: true },\n 'markup.heading.1': { fg: RGBA.fromHex(COLOR.brand), bold: true },\n 'markup.heading.2': { fg: RGBA.fromHex('#FFD84D'), bold: true },\n 'markup.heading.3': { fg: RGBA.fromHex('#FFE680'), bold: true },\n 'markup.bold': { fg: RGBA.fromHex('#FFFFFF'), bold: true },\n 'markup.italic': { fg: RGBA.fromHex('#E6EDF3'), italic: true },\n 'markup.link': { fg: RGBA.fromHex(COLOR.model), underline: true },\n 'markup.link.url': { fg: RGBA.fromHex(COLOR.model), underline: true },\n 'markup.list': { fg: RGBA.fromHex(COLOR.warn) },\n 'markup.raw': { fg: RGBA.fromHex('#A5D6FF') },\n 'markup.raw.block': { fg: RGBA.fromHex('#A5D6FF') },\n 'markup.quote': { fg: RGBA.fromHex(COLOR.dim), italic: true },\n 'punctuation': { fg: RGBA.fromHex(COLOR.mute) },\n})\n","import type { Picked, Settings, StreamEvent } from './types'\nimport { heal } from 'md4x/wasm'\nimport { memo, useEffect, useMemo, useState } from 'react'\nimport { fmtTokens } from './format'\nimport { COLOR, MD_STYLE } from './theme'\n\n/**\n * Memoized so a flush that mutates only the trailing event doesn't force the\n * entire transcript to re-render. Each event holds a stable reference until\n * its content changes (we only ever recreate the streaming-markdown tail).\n *\n * The outer wrapper handles top-margin per kind (and per neighbor) so spacing\n * is the single source of truth for inter-event breathing room.\n */\nconst EventLine = memo(\n ({ event, previous }: { event: StreamEvent, previous?: StreamEvent }) => (\n <box style={{ marginTop: marginTopFor(event, previous) }}>\n <EventLineImpl event={event} />\n </box>\n ),\n)\n\n/**\n * `@opentui/react` extends `React.JSX.IntrinsicElements`, so `onSubmit` on `<input>`\n * gets intersected with the DOM `SubmitEvent` shape and demands an unhelpful overload.\n * The OpenTUI input runtime fires `(value: string) => void`; this helper isolates\n * the cast so each call site stays readable.\n */\nexport function onInputSubmit(handler: (value: string) => void): never {\n return handler as unknown as never\n}\n\n// ---------------------------------------------------------------------------\n// Footer — single status bar: hints on the left, provider info in the middle,\n// context-window indicator on the right.\n// ---------------------------------------------------------------------------\n\nexport interface Hint {\n key: string\n label: string\n}\n\nexport interface ContextUsage {\n used: number\n max: number\n}\n\nexport function Footer({\n hints,\n picked,\n context,\n}: {\n hints: Hint[]\n picked: Picked | null\n context: ContextUsage | null\n}) {\n return (\n <box style={{ flexDirection: 'row', height: 1, paddingLeft: 1, paddingRight: 1 }}>\n <text fg={COLOR.dim}>\n {hints.map((h, i) => (\n <span key={i}>\n {i > 0 && <span fg={COLOR.mute}> · </span>}\n <span fg={COLOR.warn}>{h.key}</span>\n <span fg={COLOR.dim}>{` ${h.label}`}</span>\n </span>\n ))}\n </text>\n {picked && <ProviderBadge picked={picked} />}\n <box style={{ flexGrow: 1 }} />\n {context && <ContextIndicator context={context} />}\n </box>\n )\n}\n\nfunction ProviderBadge({ picked }: { picked: Picked }) {\n const source = picked.provider.methods[0].source\n return (\n <text fg={COLOR.dim}>\n <span fg={COLOR.mute}> · </span>\n <span fg={COLOR.accent}>{picked.provider.label}</span>\n <span fg={COLOR.mute}> · </span>\n <span fg={COLOR.model}>{picked.model}</span>\n <span fg={COLOR.mute}> · </span>\n <span fg={source === 'oauth' ? COLOR.accent : COLOR.warn}>{source}</span>\n </text>\n )\n}\n\nfunction ContextIndicator({ context }: { context: ContextUsage }) {\n const ratio = context.max > 0 ? context.used / context.max : 0\n const pct = Math.round(ratio * 100)\n const color = ratio >= 0.85 ? COLOR.error : ratio >= 0.6 ? COLOR.warn : COLOR.dim\n return (\n <text fg={COLOR.dim}>\n <span fg={COLOR.mute}>ctx </span>\n <span fg={color}>{fmtTokens(context.used)}</span>\n <span fg={COLOR.mute}>{` / ${fmtTokens(context.max)} `}</span>\n <span fg={color}>{`(${pct}%)`}</span>\n </text>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Spinner — animated braille used while a run is streaming.\n// ---------------------------------------------------------------------------\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']\nconst SPINNER_INTERVAL_MS = 80\n\nexport function Spinner({ label }: { label: string }) {\n const [frame, setFrame] = useState(0)\n\n useEffect(() => {\n const id = setInterval(() => setFrame(f => (f + 1) % SPINNER_FRAMES.length), SPINNER_INTERVAL_MS)\n return () => clearInterval(id)\n }, [])\n\n return (\n <text fg={COLOR.warn}>\n {SPINNER_FRAMES[frame]}\n <span fg={COLOR.dim}>{` ${label}`}</span>\n </text>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Transcript — scrollbox with sticky-bottom and structured event rendering.\n// ---------------------------------------------------------------------------\n\nexport function Transcript({ events, settings }: { events: StreamEvent[], settings: Settings }) {\n const visible = events.filter(e => isVisible(e, settings))\n\n if (visible.length === 0)\n return <EmptyState />\n\n return (\n <scrollbox\n // Never claim keyboard focus: the textarea is the chat's primary input,\n // so up/down/page-up/page-down should stay with it (or with the modal\n // when one is open). Mouse-wheel scrolling still works.\n focusable={false}\n style={{ flexGrow: 1, paddingLeft: 1, paddingRight: 1 }}\n stickyScroll\n stickyStart=\"bottom\"\n >\n {visible.map((evt, i) => (\n <EventLine key={i} event={evt} previous={visible[i - 1]} />\n ))}\n </scrollbox>\n )\n}\n\nfunction isVisible(event: StreamEvent, settings: Settings): boolean {\n switch (event.kind) {\n case 'thinking': return settings.showThinking\n case 'tool': return settings.showToolCalls\n case 'tool-result': return settings.showToolResults\n default: return true\n }\n}\n\nfunction EmptyState() {\n return (\n <box style={{ flexGrow: 1, alignItems: 'center', justifyContent: 'center' }}>\n <text fg={COLOR.mute}>no messages yet — type below to start</text>\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// EventLine — dispatches per kind. Subagent events render via indented dim\n// blocks; the `depth` field drives the left indent.\n// ---------------------------------------------------------------------------\n\n/** Left-pad applied per depth level (in columns). */\nconst INDENT_PER_DEPTH = 2\n\nfunction indentFor(depth: number | undefined): number {\n return depth && depth > 0 ? depth * INDENT_PER_DEPTH : 0\n}\n\nfunction isChild(event: StreamEvent): boolean {\n return (event.depth ?? 0) > 0\n}\n\n/**\n * Default top-margin per kind. Spacing intent:\n * - `info` / `markdown` / `tool` / `error` / `spawn-start` open a new block\n * so they each get one row of breathing room above.\n * - `thinking` / `tool-result` / `spawn-end` continue the previous block\n * and stay flush.\n *\n * Context-aware overrides live in `marginTopFor` — e.g. consecutive tool\n * round-trips collapse to a tight list regardless of whether outputs are shown.\n */\nconst MARGIN_TOP: Record<StreamEvent['kind'], number> = {\n 'separator': 0,\n 'info': 1,\n 'thinking': 0,\n 'tool': 1,\n 'tool-result': 0,\n 'error': 1,\n 'markdown': 1,\n 'spawn-start': 1,\n 'spawn-end': 0,\n}\n\nconst TOOL_KINDS: ReadonlySet<StreamEvent['kind']> = new Set(['tool', 'tool-result'])\n\n/**\n * Resolve the top margin for an event given the one rendered just before it.\n *\n * The only context-aware rule today: a tool/tool-result event that follows\n * another tool/tool-result event collapses its margin to zero, so a chain of\n * tool calls reads as a tight list — whether the user has hidden tool outputs\n * or not, and whether the agent emits back-to-back calls or call→result pairs.\n *\n * Exported so the spacing matrix can be unit-tested without rendering.\n */\nexport function marginTopFor(event: StreamEvent, previous: StreamEvent | undefined): number {\n if (TOOL_KINDS.has(event.kind) && previous && TOOL_KINDS.has(previous.kind))\n return 0\n return MARGIN_TOP[event.kind] ?? 0\n}\n\nfunction EventLineImpl({ event }: { event: StreamEvent }) {\n const safeText = event.text === '' ? ' ' : event.text\n const paddingLeft = indentFor(event.depth)\n\n // Subagent text is dimmed across the board so the visual hierarchy is\n // obvious even before reading the indent.\n const child = isChild(event)\n\n switch (event.kind) {\n case 'separator':\n return <text> </text>\n case 'info':\n return <UserPromptBlock text={safeText} />\n case 'thinking':\n return (\n <box style={{ paddingLeft }}>\n <text fg={COLOR.dim}>{safeText}</text>\n </box>\n )\n case 'tool':\n return (\n <box style={{ paddingLeft }}>\n <text fg={child ? COLOR.dim : COLOR.model}>\n <span fg={COLOR.mute}>↳ </span>\n {safeText}\n </text>\n </box>\n )\n case 'tool-result':\n return <ToolResultBlock text={event.text} indent={paddingLeft} />\n case 'error':\n return (\n <box style={{ paddingLeft }}>\n <text fg={COLOR.error}>\n <span fg={COLOR.error}>✗ </span>\n {safeText}\n </text>\n </box>\n )\n case 'markdown':\n return (\n <box style={{ paddingLeft }}>\n <MarkdownBlock text={event.text} streaming={event.streaming ?? false} dim={child} />\n </box>\n )\n case 'spawn-start':\n return (\n <box style={{ paddingLeft }}>\n <text fg={COLOR.dim}>\n <span fg={COLOR.accent}>🌱 </span>\n <span fg={COLOR.dim}>{`[${event.childId ?? 'child'}] `}</span>\n <span fg={COLOR.dim}>{safeText}</span>\n </text>\n </box>\n )\n case 'spawn-end':\n return (\n <box style={{ paddingLeft }}>\n <text fg={COLOR.dim}>\n <span fg={COLOR.accent}>✓ </span>\n <span fg={COLOR.dim}>{`[${event.childId ?? 'child'}] `}</span>\n <span fg={COLOR.mute}>{safeText}</span>\n </text>\n </box>\n )\n default:\n return <text>{safeText}</text>\n }\n}\n\n/** User prompt — bordered to rhyme with the prompt input box below. */\nfunction UserPromptBlock({ text }: { text: string }) {\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n }}\n >\n <text fg={COLOR.brand}>{text}</text>\n </box>\n )\n}\n\n/**\n * Markdown block. While `streaming` is true, content is passed through\n * `md4x.heal()` so unclosed delimiters (bold, italic, code, link, table) render\n * as if already complete. OpenTUI's `streaming` prop keeps its parser from\n * committing to the final layout for the trailing block.\n */\nfunction MarkdownBlock({ text, streaming, dim }: { text: string, streaming: boolean, dim: boolean }) {\n // Memoize the heal pass so re-renders triggered by unrelated state changes\n // don't recompute it; only re-run when text/streaming flip.\n const content = useMemo(\n () => streaming ? heal(text) : text,\n [text, streaming],\n )\n return (\n <markdown\n content={content}\n syntaxStyle={MD_STYLE}\n streaming={streaming}\n fg={dim ? COLOR.dim : undefined}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Tool result — left-bar block, truncated. Caps at TOOL_RESULT_MAX_LINES so a\n// 200-line file doesn't drown the transcript.\n// ---------------------------------------------------------------------------\n\nconst TOOL_RESULT_MAX_LINES = 6\n\nfunction ToolResultBlock({ text, indent }: { text: string, indent: number }) {\n const lines = text.split('\\n')\n const visible = lines.slice(0, TOOL_RESULT_MAX_LINES)\n const omitted = Math.max(0, lines.length - TOOL_RESULT_MAX_LINES)\n\n return (\n <box style={{ paddingLeft: indent, flexDirection: 'column' }}>\n {visible.map((line, i) => (\n <text key={i} fg={COLOR.mute}>\n <span fg={COLOR.borderActive}>┃ </span>\n {line || ' '}\n </text>\n ))}\n {omitted > 0 && (\n <text fg={COLOR.mute}>\n <span fg={COLOR.borderActive}>┃ </span>\n {`… ${omitted} more line${omitted === 1 ? '' : 's'}`}\n </text>\n )}\n </box>\n )\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nexport type ProviderKey = 'anthropic' | 'openai' | 'openrouter' | 'cerebras'\n\ninterface OAuthEntry {\n access?: string\n refresh?: string\n expires?: number\n}\n\nexport interface AuthMethod {\n source: 'env' | 'oauth'\n /** Human-readable detail (env var name, expiry timestamp, …). */\n detail: string\n}\n\nexport interface ProviderAuth {\n key: ProviderKey\n label: string\n /** True when at least one credential source is present. */\n available: boolean\n methods: AuthMethod[]\n}\n\nconst ENV_KEYS: Record<ProviderKey, string> = {\n anthropic: 'ANTHROPIC_API_KEY',\n openai: 'OPENAI_CODEX_API_KEY',\n openrouter: 'OPENROUTER_API_KEY',\n cerebras: 'CEREBRAS_API_KEY',\n}\n\n/** Maps a provider to the credentials.json key written by `bun run auth`. */\nconst OAUTH_KEYS: Partial<Record<ProviderKey, string>> = {\n anthropic: 'anthropic',\n openai: 'openai-codex',\n}\n\nconst LABELS: Record<ProviderKey, string> = {\n anthropic: 'Anthropic',\n openai: 'OpenAI Codex',\n openrouter: 'OpenRouter',\n cerebras: 'Cerebras',\n}\n\nexport function envKeyFor(key: ProviderKey): string {\n return ENV_KEYS[key]\n}\n\n/**\n * Detect available auth across the providers the harness ships with.\n *\n * Mirrors the resolution order used by the providers at runtime:\n * - explicit env var (highest)\n * - OAuth credentials in `.credentials.json` (anthropic + openai-codex only)\n *\n * Pure read — never refreshes or rewrites the credentials file.\n */\nexport function detectAuth(env: Record<string, string | undefined> = process.env): ProviderAuth[] {\n const credsPath = resolve(process.cwd(), '.credentials.json')\n let creds: Record<string, OAuthEntry | undefined> = {}\n if (existsSync(credsPath)) {\n try {\n const parsed = JSON.parse(readFileSync(credsPath, 'utf-8'))\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))\n creds = parsed\n }\n catch {\n // Corrupt credentials file — treat as absent so the user can re-auth.\n }\n }\n\n return (Object.keys(LABELS) as ProviderKey[]).map((key) => {\n const methods: AuthMethod[] = []\n\n const envKey = ENV_KEYS[key]\n if (env[envKey])\n methods.push({ source: 'env', detail: envKey })\n\n const oauthKey = OAUTH_KEYS[key]\n if (oauthKey) {\n const entry = creds[oauthKey]\n if (entry?.access && entry.refresh) {\n const detail = entry.expires\n ? `oauth · expires ${new Date(entry.expires).toLocaleString()}`\n : 'oauth · .credentials.json'\n methods.push({ source: 'oauth', detail })\n }\n }\n\n return { key, label: LABELS[key], available: methods.length > 0, methods }\n })\n}\n","import type { Provider } from '../providers'\nimport type { ProviderKey } from './auth'\nimport { getModel } from '@mariozechner/pi-ai'\nimport { anthropic, cerebras, openai, openrouter } from '../providers'\n\n/**\n * Construct a fresh provider instance for a given key.\n *\n * Providers are cheap to build — credentials are resolved lazily at first\n * stream call — so we instantiate on demand rather than caching a singleton.\n * This also avoids leaking state across session/provider switches.\n */\nexport const FACTORIES: Record<ProviderKey, () => Provider> = {\n anthropic,\n openai,\n openrouter,\n cerebras,\n}\n\n/** zidane provider key → pi-ai provider id (some don't match 1:1). */\nexport const PI_PROVIDER_ID: Record<ProviderKey, string> = {\n anthropic: 'anthropic',\n openai: 'openai-codex',\n openrouter: 'openrouter',\n cerebras: 'cerebras',\n}\n\n/**\n * Look up the model's max context window via pi-ai's model registry.\n * Returns `null` when the model isn't known (e.g. a custom openrouter slug);\n * callers should hide the context indicator in that case.\n */\nexport function getContextWindow(key: ProviderKey, modelId: string): number | null {\n try {\n const providerId = PI_PROVIDER_ID[key] as never\n const model = getModel(providerId, modelId as never)\n return model?.contextWindow ?? null\n }\n catch {\n return null\n }\n}\n","import type { SessionStore } from '../session'\nimport type { SessionTurn, ToolResultContent } from '../types'\nimport type { ProviderKey } from './auth'\nimport type { SessionMeta, Settings, StreamEvent } from './types'\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createSqliteStore } from '../session/sqlite'\nimport { toolResultToText } from '../types'\n\nfunction ensureDir(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 const message = err instanceof Error ? err.message : String(err)\n throw new Error(\n `Could not create TUI storage directory at \"${dir}\". `\n + `Override the location via \\`runTui({ storageDir, prefix })\\` or the `\n + `\\`ZIDANE_STORAGE_DIR\\` env var. Original error: ${message}`,\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Session store — thin SQLite wrapper at a caller-supplied path.\n// ---------------------------------------------------------------------------\n\nexport function createTuiStore(dbPath: string): SessionStore {\n ensureDir(dbPath)\n return createSqliteStore({ path: dbPath })\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 /** 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 ensureDir(path)\n // Atomic write via tmp + rename so a crash mid-write never leaves a half file.\n const tmp = `${path}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify(state, null, 2))\n renameSync(tmp, path)\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 */\nexport async function listSessionMeta(store: SessionStore): Promise<SessionMeta[]> {\n const ids = await store.list()\n const metas = await Promise.all(ids.map(async (id) => {\n const data = await store.load(id)\n if (!data)\n return null\n return {\n id,\n title: titleFromTurns(data.turns) ?? 'untitled',\n turnCount: data.turns.length,\n updatedAt: data.updatedAt,\n }\n }))\n return metas.filter((m): m is SessionMeta => m !== 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 * Replay persisted turns as a viewable transcript. Mirrors the event shape produced\n * live by the agent hooks so loaded and streaming history render identically.\n *\n * Skips `tool_result` blocks (they're not user-visible by default), and inserts a\n * `separator` event between turn groups so the eye can parse turn boundaries.\n */\nexport function eventsFromTurns(turns: SessionTurn[]): StreamEvent[] {\n const events: StreamEvent[] = []\n\n for (let i = 0; i < turns.length; i++) {\n const turn = turns[i]\n if (i > 0)\n events.push({ kind: 'separator', text: '' })\n\n if (turn.role === 'user') {\n // User-role turns carry both raw prompts (text blocks) and the\n // tool_result echoes the loop sends back. Surfacing tool_results lets\n // the user audit what the agent actually saw.\n for (const block of turn.content) {\n if (block.type === 'text' && block.text.trim())\n events.push({ kind: 'info', text: `❯ ${block.text}` })\n else if (block.type === 'tool_result')\n events.push({ kind: 'tool-result', text: toolResultText(block.output) })\n }\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 })\n else if (block.type === 'tool_call')\n events.push({ kind: 'tool', text: toolCallPreview(block.name, block.input) })\n }\n }\n }\n\n return events\n}\n\n/** Shared formatter for the `↳ name(args)` line shown on tool calls. */\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/** 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/** Effective context size of the most recent assistant turn — drives the footer indicator. */\nexport function lastContextSizeFromTurns(turns: SessionTurn[]): number {\n for (let i = turns.length - 1; i >= 0; i--) {\n const turn = turns[i]\n if (turn.role === 'assistant' && turn.usage) {\n return (turn.usage.input ?? 0)\n + (turn.usage.cacheRead ?? 0)\n + (turn.usage.cacheCreation ?? 0)\n }\n }\n return 0\n}\n","import type { ReactNode } from 'react'\nimport type { Preset } from '../presets'\nimport type { Provider } from '../providers'\nimport type { SessionStore } from '../session'\nimport type { ProviderAuth, ProviderKey } from './auth'\nimport type { StateStoreApi, TuiState } from './store'\nimport type { Picked, Settings } from './types'\nimport { homedir } from 'node:os'\nimport { resolve } from 'node:path'\nimport { getModels } from '@mariozechner/pi-ai'\nimport { createContext, useContext } from 'react'\nimport BUILTIN_PRESET from '../presets/basic'\nimport { detectAuth } from './auth'\nimport { FACTORIES as BUILTIN_FACTORIES, PI_PROVIDER_ID } from './providers'\nimport { createStateStore, createTuiStore } from './store'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Structural model metadata — compatible with pi-ai's `Model` interface but\n * not coupled to it, so callers can pass either pi-ai-shaped objects or a\n * custom registry of their own.\n *\n * Deliberately a structural duplicate of `pi-ai`'s `Model` rather than a\n * re-export: keeps the public API stable when pi-ai bumps shape, and lets\n * hosts implement their own registry without depending on pi-ai's types.\n */\nexport interface ModelInfo {\n id: string\n name?: string\n contextWindow: number\n maxTokens?: number\n reasoning?: boolean\n input?: readonly ('text' | 'image')[]\n cost?: { input: number, output: number, cacheRead?: number, cacheWrite?: number }\n provider?: string\n}\n\nexport type ProviderRegistry = Partial<Record<ProviderKey, () => Provider>>\nexport type ModelRegistry = Partial<Record<ProviderKey, readonly ModelInfo[]>>\n\n/**\n * Options accepted by `runTui()` and `resolveConfig()`. Every field is optional\n * — sensible defaults boot a working chat against the four built-in providers\n * with sessions stored under `~/.zidane/`.\n */\nexport interface TuiOptions {\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 factory map. Override entries (or supply a whole new map) to\n * register custom providers. Default: anthropic/openai/openrouter/cerebras\n * from the built-in zidane factories.\n */\n providers?: ProviderRegistry\n /**\n * Agent preset (tools, system prompt, behavior). Default: `basic`.\n */\n preset?: Preset\n /**\n * Bring your own session store. Default: a SQLite store at\n * `<storageDir>/<prefix>/sessions.db`.\n */\n store?: SessionStore\n /**\n * Per-provider model registry for the in-app model picker. Each list should\n * be in pi-ai's `Model`-compatible shape (see `ModelInfo`). If omitted, the\n * picker falls back to pi-ai's built-in registry via `getModels()`.\n */\n models?: ModelRegistry\n}\n\n// ---------------------------------------------------------------------------\n// Resolved config\n// ---------------------------------------------------------------------------\n\nexport interface ResolvedConfig {\n prefix: string\n storageDir: string\n paths: { dir: string, db: string, state: string }\n providers: ProviderRegistry\n preset: Preset\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\n/** Resolve user options into a fully-bound runtime config. Pure aside from disk reads. */\nexport function resolveConfig(options: TuiOptions = {}): ResolvedConfig {\n const prefix = options.prefix ?? process.env.ZIDANE_PREFIX ?? '.zidane'\n const storageDir = options.storageDir ?? process.env.ZIDANE_STORAGE_DIR ?? homedir()\n const dir = resolve(storageDir, prefix)\n const paths = {\n dir,\n db: resolve(dir, 'sessions.db'),\n state: resolve(dir, 'state.json'),\n }\n\n const store: SessionStore = options.store ?? createTuiStore(paths.db)\n const stateStore = createStateStore(paths.state)\n const initialState = stateStore.load()\n\n const providers: ProviderRegistry = { ...BUILTIN_FACTORIES, ...(options.providers ?? {}) }\n const preset = options.preset ?? BUILTIN_PRESET\n\n const modelsFor = makeModelsResolver(options.models)\n\n const resumeProvider = resolveResumeProvider(initialState, providers)\n const initialPicked = resumeProvider ? pickInitial(resumeProvider, providers, initialState) : null\n\n return {\n prefix,\n storageDir,\n paths,\n providers,\n preset,\n store,\n stateStore,\n modelsFor,\n initialState,\n initialSettings: initialState.settings ?? {},\n resumeProvider,\n initialPicked,\n }\n}\n\nfunction makeModelsResolver(custom?: ModelRegistry): (key: ProviderKey) => readonly ModelInfo[] {\n return (key) => {\n const overridden = custom?.[key]\n if (overridden)\n return overridden\n try {\n const piId = PI_PROVIDER_ID[key] as never\n return getModels(piId) as readonly ModelInfo[]\n }\n catch {\n return []\n }\n }\n}\n\nfunction resolveResumeProvider(state: TuiState, providers: ProviderRegistry): ProviderAuth | null {\n if (!state.lastProvider)\n return null\n if (!providers[state.lastProvider])\n return null\n return detectAuth().find(p => p.key === state.lastProvider && p.available) ?? null\n}\n\nfunction pickInitial(auth: ProviderAuth, providers: ProviderRegistry, state: TuiState): Picked | null {\n const factory = providers[auth.key]\n if (!factory)\n return null\n const provider = factory()\n const remembered = state.lastModelByProvider?.[auth.key]\n return { provider: auth, model: remembered ?? provider.meta.defaultModel }\n}\n\n// ---------------------------------------------------------------------------\n// React-side: ConfigContext for deep consumers (modals, picker).\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","import type { ReactNode } from 'react'\nimport { useKeyboard, useTerminalDimensions } from '@opentui/react'\nimport { createContext, useContext, useMemo, useState } from 'react'\nimport { COLOR } from './theme'\n\n// ---------------------------------------------------------------------------\n// Modal layer\n//\n// A single, app-wide overlay slot. `ModalRoot` renders the active node on top\n// of its children via OpenTUI's `position: 'absolute'` so the host UI stays\n// mounted underneath (state preserved, no remount on close).\n//\n// Components anywhere in the tree call `useModal().open(...)` to push a node\n// and `.close()` to dismiss. Escape is captured by `Modal` itself when it's\n// open, so the App's top-level keyboard handler doesn't fight it.\n// ---------------------------------------------------------------------------\n\ninterface ModalApi {\n open: (node: ReactNode) => void\n close: () => void\n isOpen: boolean\n}\n\nconst ModalContext = createContext<ModalApi | null>(null)\n\nexport function ModalRoot({ children }: { children: ReactNode }) {\n const [active, setActive] = useState<ReactNode | null>(null)\n\n const api = useMemo<ModalApi>(() => ({\n open: node => setActive(node),\n close: () => setActive(null),\n get isOpen() { return active !== null },\n }), [active])\n\n return (\n <ModalContext.Provider value={api}>\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n {children}\n </box>\n {active && (\n // Transparent backdrop — host UI remains visible behind. The modal is\n // opaque (see <Modal/>) and sits centered; the dimming the eye perceives\n // comes from the modal's solid panel against the surrounding content,\n // not from a backdrop fill (OpenTUI cells have no alpha to blend with).\n <box\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 100,\n }}\n >\n {active}\n </box>\n )}\n </ModalContext.Provider>\n )\n}\n\nexport function useModal(): ModalApi {\n const ctx = useContext(ModalContext)\n if (!ctx)\n throw new Error('useModal must be used inside <ModalRoot>')\n return ctx\n}\n\n/**\n * Focus computed against the modal layer.\n *\n * Pass a component's preferred focus state and this returns `false` whenever a\n * modal is open — so focused inputs (textarea, selects) release their focus and\n * stop intercepting keys behind the overlay. Pair with `focusable={false}` on\n * \"passive\" focusables (scrollbox) so the renderer doesn't cycle focus into\n * them when the primary input blurs.\n */\nexport function useModalAwareFocus(preferred: boolean = true): boolean {\n const { isOpen } = useModal()\n return preferred && !isOpen\n}\n\n// ---------------------------------------------------------------------------\n// Modal — bordered content panel with built-in esc-to-close keyboard handling.\n//\n// Consumers pass their own content (forms, selects, lists). The modal owns:\n// - border + title + minimal padding\n// - sizing constraints (width caps at a comfortable column width)\n// - escape key → onClose\n// ---------------------------------------------------------------------------\n\nexport interface ModalProps {\n title?: string\n /** Called when the user presses esc. Defaults to `useModal().close()` if available. */\n onClose?: () => void\n children: ReactNode\n /** Preferred width in columns. Modal grows to this when the terminal allows. */\n maxWidth?: number\n /** Floor on the width, so content stays minimally legible. */\n minWidth?: number\n /** Columns of breathing room kept on each side of the modal. */\n horizontalMargin?: number\n}\n\n/**\n * Responsive modal — picks a width based on the live terminal size.\n *\n * - On a wide terminal, the modal grows to `maxWidth` so descriptions sit on\n * one line and don't wrap.\n * - On a narrow terminal, the modal shrinks down to `minWidth`, keeping a\n * small horizontal margin from the screen edges. Text inside wraps naturally.\n *\n * Uses `useTerminalDimensions()` so it reflows on `SIGWINCH` without remount.\n */\nexport function Modal({\n title,\n onClose,\n children,\n maxWidth = 92,\n minWidth = 44,\n horizontalMargin = 4,\n}: ModalProps) {\n const ctx = useContext(ModalContext)\n const dismiss = onClose ?? ctx?.close\n\n useKeyboard((key) => {\n if (key.name === 'escape')\n dismiss?.()\n })\n\n const { width: termWidth } = useTerminalDimensions()\n const width = Math.max(minWidth, Math.min(maxWidth, termWidth - horizontalMargin * 2))\n\n return (\n <box\n title={title ? ` ${title} ` : undefined}\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n backgroundColor: '#101010',\n paddingTop: 1,\n paddingBottom: 1,\n paddingLeft: 2,\n paddingRight: 2,\n width,\n flexDirection: 'column',\n gap: 1,\n }}\n >\n {children}\n </box>\n )\n}\n","import type { ModelInfo } from './config'\nimport { useMemo } from 'react'\nimport { fmtTokens } from './format'\nimport { Modal } from './modal'\nimport { COLOR, SELECT_THEME } from './theme'\n\n/** Cap the visible scroll window so a 30-model list doesn't push the modal off-screen. */\nconst VISIBLE_ROW_CAP = 12\n\n/**\n * Modal that lists the available models for the current provider and lets the\n * user pick one. Options come from `runTui({ models })` if supplied, otherwise\n * from pi-ai's built-in registry.\n *\n * Each row shows: `● selected · name (ctx N · reasoning · vision)`.\n */\nexport function ModelPickerModal({\n models,\n currentModelId,\n onPick,\n}: {\n models: readonly ModelInfo[]\n currentModelId: string\n onPick: (modelId: string) => void\n}) {\n // Hooks must always run in the same order — keep them above any conditional\n // return. The \"no models\" branch becomes JSX below.\n const initialIndex = useMemo(\n () => models.findIndex(m => m.id === currentModelId),\n [models, currentModelId],\n )\n\n const options = useMemo(\n () => models.map(m => ({\n name: `${m.id === currentModelId ? '● ' : ' '}${m.name ?? m.id}`,\n description: describeModel(m),\n value: m.id,\n })),\n [models, currentModelId],\n )\n\n if (models.length === 0)\n return <EmptyState />\n\n const visibleRows = Math.min(options.length, VISIBLE_ROW_CAP)\n const currentMissing = initialIndex < 0\n // `<select>` requires a non-negative index; clamp here and surface the\n // mismatch via a banner so the user knows their previously-saved model isn't\n // available in the current registry.\n const safeIndex = currentMissing ? 0 : initialIndex\n\n return (\n <Modal title=\"select model\">\n {currentMissing && (\n <text fg={COLOR.warn}>\n {`Current model \"${currentModelId}\" is not in this registry — pick one below to switch.`}\n </text>\n )}\n <select\n {...SELECT_THEME}\n focused\n options={options}\n wrapSelection\n selectedIndex={safeIndex}\n showScrollIndicator={options.length > visibleRows}\n style={{ height: visibleRows }}\n onSelect={(_idx, option) => {\n if (option)\n onPick(option.value as string)\n }}\n />\n <text fg={COLOR.mute}>\n <span fg={COLOR.warn}>↑↓</span>\n {' navigate · '}\n <span fg={COLOR.warn}>↵</span>\n {' select · '}\n <span fg={COLOR.warn}>esc</span>\n {' close'}\n </text>\n </Modal>\n )\n}\n\nfunction EmptyState() {\n return (\n <Modal title=\"select model\">\n <text fg={COLOR.dim}>No models available for this provider.</text>\n <text fg={COLOR.mute}>Pass a `models` registry to `runTui()` to populate this list.</text>\n </Modal>\n )\n}\n\n/** \"ctx 200k · reasoning · vision\" — compact per-model description. */\nfunction describeModel(m: ModelInfo): string {\n const parts: string[] = [`ctx ${fmtTokens(m.contextWindow)}`]\n if (m.reasoning)\n parts.push('reasoning')\n if (m.input?.includes('image'))\n parts.push('vision')\n return parts.join(' · ')\n}\n","import type { KeyEvent, TextareaRenderable } from '@opentui/core'\nimport type { ProviderAuth } from './auth'\nimport type { SessionMeta, Settings, StreamEvent } from './types'\nimport { defaultTextareaKeyBindings } from '@opentui/core'\nimport { useCallback, useMemo, useRef, useState } from 'react'\nimport { detectAuth, envKeyFor } from './auth'\nimport { Spinner, Transcript } from './components'\nimport { useConfig } from './config'\nimport { ageString, shortId } from './format'\nimport { useModalAwareFocus } from './modal'\nimport { COLOR, SELECT_THEME } from './theme'\n\n/**\n * Textarea bindings: plain `enter` submits; `shift+enter` inserts a newline.\n * All `return` defaults are stripped and replaced so the user's preferred\n * binding wins regardless of modifier state.\n */\nconst TEXTAREA_BINDINGS = [\n ...defaultTextareaKeyBindings.filter(b => b.name !== 'return'),\n { name: 'return', action: 'submit' as const },\n { name: 'return', shift: true, action: 'newline' as const },\n]\n\n// ---------------------------------------------------------------------------\n// AuthScreen — first-run provider picker.\n// ---------------------------------------------------------------------------\n\nexport function AuthScreen({ onPick }: { onPick: (p: ProviderAuth) => void }) {\n const { providers: registry } = useConfig()\n const focused = useModalAwareFocus()\n\n // Intersect detected auth with the configured provider registry: a host that\n // narrowed `providers` shouldn't see options it can't honor, even if env-var\n // credentials still exist for them.\n const providers = useMemo(\n () => detectAuth().filter(p => p.key in registry),\n [registry],\n )\n const available = useMemo(() => providers.filter(p => p.available), [providers])\n\n if (available.length === 0)\n return <NoAuthScreen providers={providers} />\n\n const options = available.map(p => ({\n name: p.label,\n description: p.methods.map(m => m.detail).join(' · '),\n value: p.key,\n }))\n\n return (\n <box\n title=\" pick a provider \"\n style={{\n border: true,\n borderColor: COLOR.border,\n padding: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n const provider = available.find(p => p.key === option.value)\n if (provider)\n onPick(provider)\n }}\n style={{ flexGrow: 1 }}\n />\n </box>\n )\n}\n\nfunction NoAuthScreen({ providers }: { providers: ProviderAuth[] }) {\n return (\n <box\n title=\" no authentication detected \"\n style={{\n border: true,\n borderColor: COLOR.error,\n padding: 1,\n flexDirection: 'column',\n gap: 1,\n flexGrow: 1,\n }}\n >\n <text fg={COLOR.error}>No provider credentials found.</text>\n <text fg={COLOR.dim}>Set one of these env vars (or run `bun run auth` for OAuth):</text>\n {providers.map(p => (\n <text key={p.key} fg={COLOR.dim}>\n {' · '}\n <span fg={COLOR.brand}>{p.label}</span>\n {' → '}\n <span fg={COLOR.model}>{envKeyFor(p.key)}</span>\n </text>\n ))}\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SessionsScreen — list of sessions with a New entry on top.\n// ---------------------------------------------------------------------------\n\nconst NEW_VALUE = '__new__'\n\nexport function SessionsScreen({\n sessions,\n currentId,\n onPick,\n onCreate,\n}: {\n sessions: SessionMeta[]\n currentId: string | null\n onPick: (id: string) => void\n onCreate: () => void\n}) {\n const focused = useModalAwareFocus()\n const options = useMemo(() => {\n const items: { name: string, description: string, value: string }[] = [\n { name: '+ new session', description: 'start fresh', value: NEW_VALUE },\n ]\n for (const s of sessions) {\n const marker = s.id === currentId ? '● ' : ' '\n const turnLabel = `${s.turnCount} turn${s.turnCount === 1 ? '' : 's'}`\n items.push({\n name: `${marker}${s.title}`,\n description: `#${shortId(s.id)} · ${turnLabel} · ${ageString(s.updatedAt)}`,\n value: s.id,\n })\n }\n return items\n }, [sessions, currentId])\n\n return (\n <box\n title=\" sessions \"\n style={{\n border: true,\n borderColor: COLOR.border,\n padding: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n if (option.value === NEW_VALUE)\n onCreate()\n else\n onPick(option.value)\n }}\n style={{ flexGrow: 1 }}\n />\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// ChatScreen — transcript + auto-growing multi-line input + (running) spinner.\n// Enter inserts a newline; shift+enter submits; ctrl+↑↓ cycles prompt history.\n// ---------------------------------------------------------------------------\n\n/** Visible content lines: 1 minimum, 5 maximum (textarea scrolls past 5). */\nconst MIN_CONTENT_LINES = 1\nconst MAX_CONTENT_LINES = 5\n\nexport function ChatScreen({\n events,\n busy,\n settings,\n onSubmit,\n session,\n}: {\n events: StreamEvent[]\n busy: boolean\n settings: Settings\n onSubmit: (prompt: string) => void\n session: SessionMeta | null\n}) {\n const title = useMemo(() => {\n if (!session)\n return ' untitled '\n const turns = `${session.turnCount} turn${session.turnCount === 1 ? '' : 's'}`\n return ` ${session.title} · #${shortId(session.id)} · ${turns} `\n }, [session])\n\n // Prior user prompts sourced from the transcript itself — kept in submit\n // order so the history-navigation feels like a regular shell.\n const userPrompts = useMemo(\n () => events.filter(e => e.kind === 'info').map(e => e.text.replace(/^❯ /, '')),\n [events],\n )\n\n return (\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n <box\n title={title}\n style={{\n border: true,\n borderColor: COLOR.border,\n flexGrow: 1,\n flexDirection: 'column',\n }}\n >\n <Transcript events={events} settings={settings} />\n </box>\n\n {busy\n ? <BusyBlock />\n : <PromptBlock userPrompts={userPrompts} onSubmit={onSubmit} />}\n </box>\n )\n}\n\nfunction BusyBlock() {\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.warn,\n paddingLeft: 1,\n paddingRight: 1,\n height: 3,\n }}\n >\n <Spinner label=\"streaming response — esc to abort\" />\n </box>\n )\n}\n\nfunction PromptBlock({\n userPrompts,\n onSubmit,\n}: {\n userPrompts: string[]\n onSubmit: (prompt: string) => void\n}) {\n const focused = useModalAwareFocus()\n const textareaRef = useRef<TextareaRenderable | null>(null)\n /** Auto-grow: visible content rows the textarea currently occupies (clamped 1..5). */\n const [contentLines, setContentLines] = useState(MIN_CONTENT_LINES)\n /**\n * History navigation state. `null` = not navigating (textarea owns its content).\n * Once the user enters history (up at top), we snapshot the draft and cycle.\n */\n const historyRef = useRef<{ idx: number, draft: string } | null>(null)\n\n const syncLines = useCallback(() => {\n // `lineCount` is logical (newline-separated) line count — what the user\n // means by \"jumping lines\". Wrap-aware visual lines (`virtualLineCount`)\n // would also grow on long single lines, which isn't what we want.\n const lines = textareaRef.current?.lineCount ?? MIN_CONTENT_LINES\n setContentLines(Math.max(MIN_CONTENT_LINES, lines))\n }, [])\n\n const submit = useCallback(() => {\n const value = textareaRef.current?.plainText ?? ''\n if (!value.trim())\n return\n onSubmit(value)\n textareaRef.current?.clear()\n historyRef.current = null\n setContentLines(MIN_CONTENT_LINES)\n }, [onSubmit])\n\n const cycleHistory = useCallback((direction: -1 | 1) => {\n if (userPrompts.length === 0 || !textareaRef.current)\n return\n\n if (historyRef.current === null) {\n // Entering history mode: snapshot the draft so we can restore on the\n // way forward past the most-recent entry.\n historyRef.current = {\n idx: userPrompts.length,\n draft: textareaRef.current.plainText,\n }\n }\n\n const nextIdx = historyRef.current.idx + direction\n\n if (nextIdx < 0)\n return // already at oldest\n\n if (nextIdx >= userPrompts.length) {\n // Walked off the end → restore the original draft, leave history mode.\n textareaRef.current.setText(historyRef.current.draft)\n textareaRef.current.gotoBufferEnd()\n historyRef.current = null\n }\n else {\n textareaRef.current.setText(userPrompts[nextIdx])\n textareaRef.current.gotoBufferEnd()\n historyRef.current.idx = nextIdx\n }\n syncLines()\n }, [userPrompts, syncLines])\n\n /**\n * Up/Down at the buffer boundary cycles prompt history (fish/zsh pattern).\n * Mid-buffer up/down move the cursor normally — handled by the default\n * `move-up` / `move-down` actions in `TEXTAREA_BINDINGS`.\n */\n const onKeyDown = useCallback((event: KeyEvent) => {\n if (event.ctrl || event.shift || event.meta)\n return\n if (event.name !== 'up' && event.name !== 'down')\n return\n const buffer = textareaRef.current\n if (!buffer)\n return\n const cursorRow = buffer.logicalCursor.row\n if (event.name === 'up' && cursorRow === 0)\n cycleHistory(-1)\n else if (event.name === 'down' && cursorRow === buffer.lineCount - 1)\n cycleHistory(1)\n }, [cycleHistory])\n\n // Content area + 2 lines of border = total box height.\n const contentHeight = Math.min(MAX_CONTENT_LINES, contentLines)\n const boxHeight = contentHeight + 2\n\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n height: boxHeight,\n flexDirection: 'column',\n }}\n >\n <textarea\n ref={textareaRef}\n // Release focus while a modal is open so its `handleKeyPress` stops\n // routing keys behind the overlay (see `useModalAwareFocus`).\n focused={focused}\n keyBindings={TEXTAREA_BINDINGS}\n placeholder=\"Ask zidane… (enter = send · shift+enter = newline · ↑↓ at edges = history)\"\n // Fill the box's inner area so the textarea's own scroll engine kicks\n // in once content exceeds the visible rows (past 5 logical lines).\n style={{ flexGrow: 1, height: '100%' }}\n onSubmit={submit}\n onContentChange={syncLines}\n onKeyDown={onKeyDown}\n />\n </box>\n )\n}\n","import type { ReactNode } from 'react'\nimport type { Settings } from './types'\nimport { useKeyboard } from '@opentui/react'\nimport { createContext, useCallback, useContext, useMemo, useState } from 'react'\nimport { Modal } from './modal'\nimport { COLOR } from './theme'\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_SETTINGS: Settings = {\n showThinking: true,\n showToolCalls: true,\n showToolResults: true,\n}\n\n// ---------------------------------------------------------------------------\n// SettingsProvider — single source of truth for the transcript filters.\n//\n// `onChange` is called with the latest settings whenever the user toggles a\n// row, so the host can persist them. Persistence pathing lives outside this\n// module to keep settings UI portable across hosting setups.\n// ---------------------------------------------------------------------------\n\ninterface SettingsContextValue {\n settings: Settings\n toggle: (key: keyof Settings) => 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 const [settings, setSettings] = useState<Settings>(initial)\n\n const toggle = useCallback((key: keyof Settings) => {\n setSettings((prev) => {\n const next = { ...prev, [key]: !prev[key] }\n onChange?.(next)\n return next\n })\n }, [onChange])\n\n const value = useMemo<SettingsContextValue>(() => ({ settings, toggle }), [settings, toggle])\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// SettingsModal — vertical toggle list. Up/Down navigates, Enter/Space toggles.\n// Esc dismisses (handled by the parent <Modal>).\n// ---------------------------------------------------------------------------\n\ninterface SettingRow {\n key: keyof Settings\n label: string\n description: string\n}\n\nconst ROWS: SettingRow[] = [\n { key: 'showThinking', label: 'Thinking blocks', description: 'agent reasoning shown inline' },\n { key: 'showToolCalls', label: 'Tool calls', description: 'the ↳ name(args) lines' },\n { key: 'showToolResults', label: 'Tool outputs', description: 'the ┃ result blocks under tool calls' },\n]\n\nexport function SettingsModal() {\n const { settings, toggle } = useSettings()\n const [cursor, setCursor] = useState(0)\n\n useKeyboard((key) => {\n if (key.name === 'up' || (key.ctrl && key.name === 'p'))\n setCursor(c => Math.max(0, c - 1))\n else if (key.name === 'down' || (key.ctrl && key.name === 'n'))\n setCursor(c => Math.min(ROWS.length - 1, c + 1))\n else if (key.name === 'return' || key.name === 'space')\n toggle(ROWS[cursor].key)\n })\n\n return (\n <Modal title=\"settings\">\n <box style={{ flexDirection: 'column' }}>\n {ROWS.map((row, i) => (\n <SettingRowView\n key={row.key}\n row={row}\n enabled={settings[row.key]}\n focused={i === cursor}\n />\n ))}\n </box>\n <text fg={COLOR.mute}>\n <span fg={COLOR.warn}>↑↓</span>\n {' navigate · '}\n <span fg={COLOR.warn}>↵</span>\n {' toggle · '}\n <span fg={COLOR.warn}>esc</span>\n {' close'}\n </text>\n </Modal>\n )\n}\n\n/**\n * A single setting row — `▶` marker · checkbox · label · description.\n *\n * Rendered as one `<text>` so OpenTUI's word-wrap handles narrow terminals\n * automatically: on wide screens everything sits on one line; on narrow ones\n * the trailing description wraps under the label without breaking the row's\n * structure.\n */\nfunction SettingRowView({\n row,\n enabled,\n focused,\n}: {\n row: SettingRow\n enabled: boolean\n focused: boolean\n}) {\n return (\n <text fg={focused ? COLOR.brand : COLOR.dim}>\n <span fg={focused ? COLOR.brand : COLOR.mute}>{focused ? '▶ ' : ' '}</span>\n <span fg={enabled ? COLOR.accent : COLOR.mute}>{enabled ? '[✓] ' : '[ ] '}</span>\n <span fg={focused ? COLOR.brand : COLOR.dim}>{row.label}</span>\n <span fg={COLOR.mute}>{` ${row.description}`}</span>\n </text>\n )\n}\n","import type { Dispatch, SetStateAction } from 'react'\nimport type { TurnUsage } from '../types'\nimport type { Owner, StreamEvent } from './types'\nimport { useCallback, useMemo, useRef } from 'react'\n\n/** Target one flush per ~33ms (one frame at the default renderer targetFps=30). */\nconst FLUSH_INTERVAL_MS = 33\n\nconst PARENT_OWNER: Owner = 'parent'\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\ninterface DeltaBucket {\n markdown: string\n thinking: string\n owner: Owner\n depth: number\n}\n\nfunction emptyBucket(owner: Owner, depth: number): DeltaBucket {\n return { markdown: '', thinking: '', owner, depth }\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)\n if (bucket.markdown)\n result = appendMarkdownDelta(result, bucket.markdown, bucket.owner, bucket.depth)\n return result\n}\n\nfunction appendMarkdownDelta(prev: StreamEvent[], delta: string, owner: Owner, depth: number): StreamEvent[] {\n const last = prev[prev.length - 1]\n if (last && last.kind === 'markdown' && last.streaming && ownerOf(last) === owner) {\n const next = prev.slice(0, -1)\n next.push({ ...last, text: last.text + delta })\n return next\n }\n return [\n ...prev,\n eventWithOwner({ kind: 'markdown', text: delta, streaming: true }, owner, depth),\n ]\n}\n\nfunction appendThinkingLines(prev: StreamEvent[], delta: string, owner: Owner, depth: number): StreamEvent[] {\n const lines = delta.split('\\n')\n const result = [...prev]\n const last = result[result.length - 1]\n\n if (last && last.kind === 'thinking' && ownerOf(last) === owner) {\n result[result.length - 1] = { ...last, text: last.text + lines[0] }\n }\n else if (lines[0] || lines.length > 1) {\n result.push(eventWithOwner({ kind: 'thinking', text: lines[0] }, owner, depth))\n }\n\n for (let i = 1; i < lines.length; i++)\n result.push(eventWithOwner({ kind: 'thinking', text: lines[i] }, owner, depth))\n\n return result\n}\n\nfunction ownerOf(evt: StreamEvent): Owner {\n return evt.childId ?? PARENT_OWNER\n}\n\nfunction eventWithOwner(evt: StreamEvent, owner: Owner, depth: number): StreamEvent {\n if (owner === PARENT_OWNER)\n return evt\n return { ...evt, childId: owner, depth }\n}\n\n/** Flip any trailing streaming markdown blocks (any owner) to finalized. */\nexport function finalizeStreamingMarkdown(events: StreamEvent[]): StreamEvent[] {\n let changed = false\n const next = events.map((e) => {\n if (e.kind === 'markdown' && e.streaming) {\n changed = true\n return { ...e, streaming: false }\n }\n return e\n })\n return changed ? next : events\n}\n\n/** Flip the trailing streaming markdown block for one specific owner. */\nexport function finalizeStreamingMarkdownForOwner(events: StreamEvent[], owner: Owner): StreamEvent[] {\n for (let i = events.length - 1; i >= 0; i--) {\n const e = events[i]\n if (e.kind !== 'markdown')\n continue\n if (!e.streaming)\n continue\n if (ownerOf(e) !== owner)\n continue\n const next = events.slice()\n next[i] = { ...e, streaming: false }\n return next\n }\n return events\n}\n\n// ---------------------------------------------------------------------------\n// Context-window math.\n// ---------------------------------------------------------------------------\n\n/**\n * Effective context size for a single turn.\n *\n * `usage.input` is misleading on its own when prompt caching is active: providers\n * (Anthropic, OpenRouter→Anthropic, Gemini) report `input` as the *new uncached*\n * tokens only — the cached prefix shows up in `cacheRead`, and newly-cached\n * tokens in `cacheCreation`. The model still saw all three buckets, so the real\n * context-window utilization is their sum.\n *\n * Non-caching providers leave `cacheRead`/`cacheCreation` undefined, so this\n * collapses to plain `input` for them.\n */\nexport function turnContextSize(usage: TurnUsage | undefined): number {\n if (!usage)\n return 0\n return (usage.input ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheCreation ?? 0)\n}\n\n// ---------------------------------------------------------------------------\n// useStreamBuffer — throttled stream-event accumulator with per-owner lanes.\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 flush at most once per ~33ms.\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\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\nexport function useStreamBuffer(setEvents: Dispatch<SetStateAction<StreamEvent[]>>): StreamBuffer {\n const bucketsRef = useRef<Map<Owner, DeltaBucket>>(new Map())\n const flushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const drainPendingInto = useCallback((updater?: (events: StreamEvent[]) => StreamEvent[]) => {\n if (flushTimerRef.current) {\n clearTimeout(flushTimerRef.current)\n flushTimerRef.current = null\n }\n const buckets = Array.from(bucketsRef.current.values())\n bucketsRef.current.clear()\n const hasDeltas = buckets.some(b => b.markdown.length > 0 || b.thinking.length > 0)\n if (!hasDeltas && !updater)\n return\n setEvents((prev) => {\n let merged = prev\n for (const bucket of buckets)\n merged = applyBucket(merged, bucket)\n return updater ? updater(merged) : merged\n })\n }, [setEvents])\n\n const flush = useCallback(() => drainPendingInto(), [drainPendingInto])\n\n const flushAndUpdate = useCallback(\n (update: (events: StreamEvent[]) => StreamEvent[]) => drainPendingInto(update),\n [drainPendingInto],\n )\n\n const appendImmediate = useCallback(\n (evt: StreamEvent) => drainPendingInto(events => [...events, evt]),\n [drainPendingInto],\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 if (!flushTimerRef.current)\n flushTimerRef.current = setTimeout(flush, FLUSH_INTERVAL_MS)\n }, [flush])\n\n const reset = useCallback(() => {\n if (flushTimerRef.current) {\n clearTimeout(flushTimerRef.current)\n flushTimerRef.current = null\n }\n bucketsRef.current.clear()\n }, [])\n\n // CRITICAL: stabilize the returned object. All five 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 }),\n [queueStreamDelta, appendImmediate, flushAndUpdate, flush, reset],\n )\n}\n","import type { Agent } from '../agent'\nimport type { Session } from '../session'\nimport type { ProviderAuth, ProviderKey } from './auth'\nimport type { ContextUsage, Hint } from './components'\nimport type { ResolvedConfig } from './config'\nimport type { Picked, Screen, SessionMeta, Settings, StreamEvent } from './types'\nimport { useKeyboard, useRenderer } from '@opentui/react'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createAgent } from '../agent'\nimport { createSession, loadSession } from '../session'\nimport { Footer } from './components'\nimport { ConfigProvider, useConfig } from './config'\nimport { ModalRoot, useModal } from './modal'\nimport { ModelPickerModal } from './model-picker'\nimport { getContextWindow } from './providers'\nimport { AuthScreen, ChatScreen, SessionsScreen } from './screens'\nimport { DEFAULT_SETTINGS, SettingsModal, SettingsProvider, useSettings } from './settings'\nimport {\n eventsFromTurns,\n lastContextSizeFromTurns,\n listSessionMeta,\n titleFromTurns,\n toolCallPreview,\n toolResultText,\n} from './store'\nimport {\n finalizeStreamingMarkdown,\n finalizeStreamingMarkdownForOwner,\n turnContextSize,\n useStreamBuffer,\n} from './streaming'\n\n/**\n * Surface failures that are normally silenced (teardown / save) when the\n * `ZIDANE_DEBUG` env var is set. Logging via `console.error` would otherwise\n * trigger OpenTUI's error console overlay and clutter the UI for end users.\n */\nconst debugLog: (label: string, err: unknown) => void\n = process.env.ZIDANE_DEBUG\n ? (label, err) => console.error(`[zidane/tui] ${label}:`, err)\n : () => {}\n\n/**\n * Top-level TUI component. Accepts a fully-resolved `ResolvedConfig` and wires\n * everything (settings, modal layer, screens, footer) underneath it.\n *\n * Hosts can either drive this via `runTui()` for the standard bootstrap or\n * mount `<App config={resolveConfig(...)} />` themselves inside a renderer\n * they already own.\n */\nexport function App({ config }: { config: ResolvedConfig }) {\n const initialSettings = useMemo(\n () => ({ ...DEFAULT_SETTINGS, ...config.initialSettings }),\n [config.initialSettings],\n )\n\n const onSettingsChange = useCallback(\n (settings: Settings) => config.stateStore.save({ ...config.stateStore.load(), settings }),\n [config.stateStore],\n )\n\n return (\n <ConfigProvider config={config}>\n <SettingsProvider initial={initialSettings} onChange={onSettingsChange}>\n <ModalRoot>\n <AppShell />\n </ModalRoot>\n </SettingsProvider>\n </ConfigProvider>\n )\n}\n\nfunction AppShell() {\n const renderer = useRenderer()\n const modal = useModal()\n const config = useConfig()\n const { settings } = useSettings()\n\n // Destructure stable identities up-front so callbacks/effects can depend on\n // them directly (avoids the \"whole config in deps\" footgun — see S1 review).\n const {\n providers: providerRegistry,\n preset,\n store,\n stateStore,\n modelsFor,\n resumeProvider,\n initialPicked,\n initialState,\n } = config\n const lastResumedSessionId = initialState.lastSessionId\n\n // Initial screen + picked seed from the resolved config so a returning user\n // lands straight on chat (or sessions) without an auth-screen flash.\n const [screen, setScreen] = useState<Screen>(() => {\n if (!resumeProvider)\n return 'auth'\n return lastResumedSessionId ? 'chat' : 'sessions'\n })\n const [picked, setPicked] = useState<Picked | null>(() => initialPicked)\n const [sessions, setSessions] = useState<SessionMeta[]>([])\n const [currentSession, setCurrentSession] = useState<SessionMeta | null>(null)\n const [events, setEvents] = useState<StreamEvent[]>([])\n const [busy, setBusy] = useState(false)\n /** Token count from the most recent assistant turn (caching-aware). */\n const [lastInputTokens, setLastInputTokens] = useState(0)\n\n const agentRef = useRef<Agent | null>(null)\n const sessionRef = useRef<Session | null>(null)\n\n const stream = useStreamBuffer(setEvents)\n\n const makePicked = useCallback((provider: ProviderAuth, modelId?: string): Picked | null => {\n // If the host narrowed the provider registry we may have a detected auth\n // with no factory to back it. Return null and let the caller bail; the\n // AuthScreen already filters these out, so this is a belt-and-braces guard.\n const factory = providerRegistry[provider.key]\n if (!factory)\n return null\n const remembered = initialState.lastModelByProvider?.[provider.key]\n const model = modelId ?? remembered ?? factory().meta.defaultModel\n return { provider, model }\n }, [providerRegistry, initialState])\n\n // -------------------------------------------------------------------------\n // Agent lifecycle: build a fresh Agent bound to the active Session and wire\n // streaming hooks. Re-created on every session switch so context never bleeds.\n // -------------------------------------------------------------------------\n\n const buildAgent = useCallback((session: Session, key: ProviderKey): Agent => {\n const factory = providerRegistry[key]\n if (!factory)\n throw new Error(`No provider registered for key \"${key}\"`)\n const agent = createAgent({ ...preset, provider: factory(), session })\n\n // Parent streams ------------------------------------------------------\n agent.hooks.hook('stream:thinking', ({ delta }) => stream.queueStreamDelta('thinking', delta))\n agent.hooks.hook('stream:text', ({ delta }) => stream.queueStreamDelta('markdown', delta))\n agent.hooks.hook('tool:before', ({ name, input }) => {\n stream.appendImmediate({ kind: 'tool', text: toolCallPreview(name, input) })\n })\n agent.hooks.hook('tool:after', ({ result }) => {\n stream.appendImmediate({ kind: 'tool-result', text: toolResultText(result) })\n })\n agent.hooks.hook('mcp:tool:after', ({ result }) => {\n stream.appendImmediate({ kind: 'tool-result', text: toolResultText(result) })\n })\n agent.hooks.hook('turn:after', ({ usage }) => {\n if (usage)\n setLastInputTokens(turnContextSize(usage))\n stream.flushAndUpdate(finalizeStreamingMarkdown)\n })\n\n // Subagent streams ----------------------------------------------------\n agent.hooks.hook('spawn:before', ({ id, task, depth }) => {\n const taskPreview = task.length > 80 ? `${task.slice(0, 80)}…` : task\n stream.appendImmediate({\n kind: 'spawn-start',\n text: taskPreview,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('spawn:complete', ({ id, depth, status, stats }) => {\n const tag = status === 'aborted' ? 'aborted' : status === 'error' ? 'error' : 'done'\n stream.appendImmediate({\n kind: 'spawn-end',\n text: `${tag} · ${stats.totalIn} in / ${stats.totalOut} out`,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('spawn:error', ({ id, depth, error }) => {\n stream.appendImmediate({\n kind: 'error',\n text: `[${id}] ${error.message}`,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('child:stream:thinking', ({ delta, childId, depth }) => {\n stream.queueStreamDelta('thinking', delta, { childId, depth })\n })\n agent.hooks.hook('child:stream:text', ({ delta, childId, depth }) => {\n stream.queueStreamDelta('markdown', delta, { childId, depth })\n })\n agent.hooks.hook('child:tool:before', ({ name, input, childId, depth }) => {\n stream.appendImmediate({\n kind: 'tool',\n text: toolCallPreview(name, input),\n childId,\n depth,\n })\n })\n agent.hooks.hook('child:tool:after', ({ result, childId, depth }) => {\n stream.appendImmediate({\n kind: 'tool-result',\n text: toolResultText(result),\n childId,\n depth,\n })\n })\n agent.hooks.hook('child:stream:end', ({ childId }) => {\n // Finalize the child's trailing markdown block so the next child event\n // (tool call, spawn-end) doesn't keep appending into it.\n stream.flushAndUpdate(prev => finalizeStreamingMarkdownForOwner(prev, childId))\n })\n\n return agent\n }, [providerRegistry, preset, stream])\n\n // -------------------------------------------------------------------------\n // Session activation — load (or create) a Session, rebuild the agent, replay\n // turns into the transcript, and persist the resume hint.\n // -------------------------------------------------------------------------\n\n const refreshSessions = useCallback(async () => {\n const list = await listSessionMeta(store)\n setSessions(list)\n return list\n }, [store])\n\n const teardown = useCallback(async () => {\n stream.reset()\n await agentRef.current?.destroy().catch(err => debugLog('agent.destroy failed', err))\n agentRef.current = null\n sessionRef.current = null\n }, [stream])\n\n const activateSession = useCallback(async (id: string | null, key: ProviderKey) => {\n await teardown()\n\n const loaded = id ? await loadSession(store, id) : null\n const session = loaded ?? await createSession({ store, ...(id ? { id } : {}) })\n\n sessionRef.current = session\n agentRef.current = buildAgent(session, key)\n\n setEvents(eventsFromTurns(session.turns))\n setLastInputTokens(lastContextSizeFromTurns(session.turns))\n setCurrentSession({\n id: session.id,\n title: titleFromTurns(session.turns) ?? 'untitled',\n turnCount: session.turns.length,\n updatedAt: Date.now(),\n })\n setScreen('chat')\n stateStore.save({\n ...stateStore.load(),\n lastProvider: key,\n lastSessionId: session.id,\n })\n }, [teardown, buildAgent, store, stateStore])\n\n // -------------------------------------------------------------------------\n // Resume on launch — when a previous provider was remembered, jump straight\n // into the last session (or the sessions list if it's been deleted).\n // -------------------------------------------------------------------------\n\n // Narrow the deps to exactly what the effect reads. Depending on `config`\n // as a whole would re-fire if any future refactor made the resolved config\n // non-stable — and re-firing here re-runs `activateSession`, destroying and\n // rebuilding the agent in a loop (this was the original leak).\n useEffect(() => {\n if (!resumeProvider)\n return\n let cancelled = false\n void (async () => {\n if (lastResumedSessionId) {\n const data = await store.load(lastResumedSessionId)\n if (cancelled)\n return\n if (data) {\n await activateSession(lastResumedSessionId, resumeProvider.key)\n return\n }\n }\n const list = await refreshSessions()\n if (cancelled)\n return\n if (list.length === 0)\n await activateSession(null, resumeProvider.key)\n else\n setScreen('sessions')\n })()\n return () => { cancelled = true }\n }, [activateSession, refreshSessions, resumeProvider, lastResumedSessionId, store])\n\n // -------------------------------------------------------------------------\n // Screen actions.\n // -------------------------------------------------------------------------\n\n const onPickProvider = useCallback(async (p: ProviderAuth) => {\n const next = makePicked(p)\n if (!next)\n return // provider isn't in the registry — guarded by AuthScreen, defensive here\n setPicked(next)\n stateStore.save({ ...stateStore.load(), lastProvider: p.key })\n const list = await refreshSessions()\n if (list.length === 0)\n await activateSession(null, p.key)\n else\n setScreen('sessions')\n }, [refreshSessions, activateSession, makePicked, stateStore])\n\n const onCreateSession = useCallback(async () => {\n if (picked)\n await activateSession(null, picked.provider.key)\n }, [picked, activateSession])\n\n const onSwitchSession = useCallback(async (id: string) => {\n if (picked)\n await activateSession(id, picked.provider.key)\n }, [picked, activateSession])\n\n const onOpenSessions = useCallback(async () => {\n await refreshSessions()\n setScreen('sessions')\n }, [refreshSessions])\n\n const onAbort = useCallback(() => {\n agentRef.current?.abort()\n }, [])\n\n const onPickModel = useCallback((modelId: string) => {\n setPicked((prev) => {\n if (!prev)\n return prev\n // Remember per-provider so the next launch resumes the right model.\n const prior = stateStore.load()\n stateStore.save({\n ...prior,\n lastModelByProvider: { ...prior.lastModelByProvider, [prev.provider.key]: modelId },\n })\n return { ...prev, model: modelId }\n })\n modal.close()\n }, [modal, stateStore])\n\n const onSubmitPrompt = useCallback(async (prompt: string) => {\n const agent = agentRef.current\n const session = sessionRef.current\n if (!agent || !session || !picked || !prompt.trim())\n return\n\n if (events.length > 0)\n stream.appendImmediate({ kind: 'separator', text: '' })\n stream.appendImmediate({ kind: 'info', text: `❯ ${prompt}` })\n setBusy(true)\n\n try {\n await agent.run({ model: picked.model, prompt })\n await session.save().catch(err => debugLog('session.save failed', err))\n setCurrentSession(prev => prev\n ? {\n ...prev,\n title: titleFromTurns(session.turns) ?? prev.title,\n turnCount: session.turns.length,\n updatedAt: Date.now(),\n }\n : prev)\n }\n catch (err) {\n stream.appendImmediate({ kind: 'error', text: err instanceof Error ? err.message : String(err) })\n }\n finally {\n stream.flushAndUpdate(finalizeStreamingMarkdown)\n setBusy(false)\n }\n }, [picked, events.length, stream])\n\n // -------------------------------------------------------------------------\n // Top-level keyboard. Each screen handles its own primary action; this only\n // routes esc + the modal shortcuts.\n //\n // The callback is recreated on every render, but OpenTUI's `useKeyboard`\n // wraps it with `useEffectEvent` internally — registration only happens once\n // and the handler always sees the latest closure. No leak.\n // -------------------------------------------------------------------------\n\n useKeyboard((key) => {\n if (modal.isOpen)\n return\n if (key.ctrl && key.name === ',' && screen !== 'auth') {\n modal.open(<SettingsModal />)\n return\n }\n if (key.ctrl && key.name === 'm' && screen === 'chat' && picked && !busy) {\n modal.open(\n <ModelPickerModal\n models={modelsFor(picked.provider.key)}\n currentModelId={picked.model}\n onPick={onPickModel}\n />,\n )\n return\n }\n if (key.name !== 'escape')\n return\n if (busy)\n return onAbort()\n if (screen === 'chat')\n return onOpenSessions()\n if (screen === 'sessions') {\n if (currentSession)\n setScreen('chat')\n else\n renderer.destroy()\n return\n }\n renderer.destroy() // auth screen\n })\n\n // -------------------------------------------------------------------------\n // Derived footer state.\n // -------------------------------------------------------------------------\n\n const hints: Hint[] = useMemo(\n () => buildHints(screen, busy, currentSession),\n [screen, busy, currentSession],\n )\n\n const contextUsage: ContextUsage | null = useMemo(() => {\n if (screen !== 'chat' || !picked)\n return null\n // Prefer the registry the host passed in (custom models can declare any\n // contextWindow); fall back to pi-ai's built-in lookup.\n const fromRegistry = modelsFor(picked.provider.key).find(m => m.id === picked.model)?.contextWindow\n const max = fromRegistry ?? getContextWindow(picked.provider.key, picked.model)\n return max ? { used: lastInputTokens, max } : null\n }, [screen, picked, lastInputTokens, modelsFor])\n\n // Drop the agent + clear timers when the app unmounts.\n useEffect(() => () => { void teardown() }, [teardown])\n\n return (\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n <box style={{ flexDirection: 'column', flexGrow: 1, paddingLeft: 1, paddingRight: 1 }}>\n {screen === 'auth' && <AuthScreen onPick={onPickProvider} />}\n {screen === 'sessions' && (\n <SessionsScreen\n sessions={sessions}\n currentId={currentSession?.id ?? null}\n onPick={onSwitchSession}\n onCreate={onCreateSession}\n />\n )}\n {screen === 'chat' && (\n <ChatScreen\n events={events}\n busy={busy}\n settings={settings}\n onSubmit={onSubmitPrompt}\n session={currentSession}\n />\n )}\n </box>\n <Footer hints={hints} picked={picked} context={contextUsage} />\n </box>\n )\n}\n\nfunction buildHints(\n screen: Screen,\n busy: boolean,\n currentSession: SessionMeta | null,\n): Hint[] {\n if (busy)\n return [{ key: 'esc', label: 'abort' }]\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: 'ctrl+,', label: 'settings' },\n { key: 'esc', label: currentSession ? 'back' : 'exit' },\n ]\n }\n return [\n { key: '↵', label: 'send' },\n { key: 'ctrl+m', label: 'model' },\n { key: 'ctrl+,', label: 'settings' },\n { key: 'esc', label: 'sessions' },\n ]\n}\n","import type { TuiOptions } from './config'\nimport { createCliRenderer } from '@opentui/core'\nimport { createRoot } from '@opentui/react'\nimport { init as initMd4x } from 'md4x/wasm'\nimport { App } from './app'\nimport { resolveConfig } from './config'\n\n// ---------------------------------------------------------------------------\n// Public API\n//\n// `runTui(options)` is the one-shot launcher. For consumers who want to embed\n// the App inside a renderer they already control, every building block below\n// is also exported individually.\n// ---------------------------------------------------------------------------\n\n/**\n * Tracks whether `runTui` has been invoked in this process. `createCliRenderer`\n * installs signal handlers + hijacks stdin; calling it a second time produces\n * undefined behavior. We bail loudly instead.\n */\nlet runTuiInvoked = false\n\n/**\n * Boot a full chat TUI with sensible defaults. **Does not return** under\n * normal use — it terminates the host process via `process.exit(0)` once\n * the user dismisses the renderer (Ctrl+C / Esc on the auth screen / a\n * non-zero exit code is mapped via the `catch` path below).\n *\n * **One-shot:** this function may only be invoked once per process. The\n * underlying OpenTUI renderer wires up global terminal state on init.\n *\n * **Why it exits the process:** after the renderer's `destroy()` restores\n * the terminal, React's reconciler and OpenTUI's internal listeners can\n * keep the Node/Bun event loop open indefinitely — the script appears to\n * hang in `bun run`, and under `bun --watch run` the watcher waits forever\n * for the child to exit. Forcing a clean exit here is the contract that\n * makes `runTui` a true one-shot launcher.\n *\n * Hosts that need post-renderer cleanup should mount `<App config={...} />`\n * against their own `createCliRenderer()` instead of calling `runTui()`.\n *\n * Env-var overrides (handy when launching from CI or restricted shells):\n * - `ZIDANE_STORAGE_DIR` — sets `storageDir`\n * - `ZIDANE_PREFIX` — sets `prefix`\n *\n * Hosts building on top of `zidane/tui` typically want their own env vars\n * (e.g. `MYAPP_STORAGE_DIR`); read them in your launch script and forward\n * to `runTui({ storageDir, prefix })`.\n *\n * ```ts\n * import { runTui } from 'zidane/tui'\n * import { createRemoteStore } from 'zidane/session' // for the `store` option\n *\n * await runTui() // ~/.zidane/sessions.db + state.json\n * await runTui({ prefix: '.myapp' }) // ~/.myapp/...\n * await runTui({ storageDir: '/data', prefix: 'myapp' })\n * await runTui({ providers: { custom: () => myProvider() } })\n * await runTui({ store: createRemoteStore({ url: '…' }) })\n * await runTui({ models: { anthropic: [{ id: 'claude-foo', contextWindow: 200_000 }] } })\n * ```\n */\nexport async function runTui(options: TuiOptions = {}): Promise<never> {\n if (runTuiInvoked) {\n throw new Error(\n 'runTui() can only be invoked once per process. '\n + 'Compose `<App config={resolveConfig(...)} />` against your own renderer '\n + 'if you need to run multiple TUIs in the same lifetime.',\n )\n }\n runTuiInvoked = true\n\n await initMd4x()\n const config = resolveConfig(options)\n\n // Promise + explicit resolver: avoid the `let done!: ...` non-null trick.\n let done: () => void = () => {}\n const exited = new Promise<void>((resolve) => { done = resolve })\n\n const renderer = await createCliRenderer({\n exitOnCtrlC: true,\n onDestroy: () => done(),\n })\n\n createRoot(renderer).render(<App config={config} />)\n\n await exited\n\n // Force a clean exit. See JSDoc above for rationale — without this the\n // process hangs after the renderer's `destroy()` returns, which manifests\n // as a stuck `bun --watch` parent and a terminal that needs a second Ctrl+C\n // to actually surrender control.\n process.exit(0)\n}\n\n// ---------------------------------------------------------------------------\n// Composition exports — for callers who want to build their own TUI shell.\n// ---------------------------------------------------------------------------\n\nexport { App } from './app'\nexport type { AuthMethod, ProviderAuth, ProviderKey } from './auth'\nexport { detectAuth, envKeyFor } from './auth'\nexport {\n type ContextUsage,\n Footer,\n type Hint,\n marginTopFor,\n onInputSubmit,\n Spinner,\n Transcript,\n} from './components'\nexport {\n ConfigProvider,\n type ModelInfo,\n type ModelRegistry,\n type ProviderRegistry,\n resolveConfig,\n type ResolvedConfig,\n type TuiOptions,\n useConfig,\n} from './config'\nexport { ageString, fmtTokens, shortId } from './format'\nexport { Modal, type ModalProps, ModalRoot, useModal, useModalAwareFocus } from './modal'\nexport { ModelPickerModal } from './model-picker'\nexport { FACTORIES, getContextWindow, PI_PROVIDER_ID } from './providers'\nexport { AuthScreen, ChatScreen, SessionsScreen } from './screens'\nexport {\n DEFAULT_SETTINGS,\n SettingsModal,\n SettingsProvider,\n useSettings,\n} from './settings'\nexport {\n createStateStore,\n createTuiStore,\n eventsFromTurns,\n lastContextSizeFromTurns,\n listSessionMeta,\n loadState,\n saveState,\n type StateStoreApi,\n titleFromTurns,\n toolCallPreview,\n toolResultText,\n type TuiState,\n} from './store'\nexport {\n finalizeStreamingMarkdown,\n finalizeStreamingMarkdownForOwner,\n type StreamBuffer,\n type StreamSource,\n turnContextSize,\n useStreamBuffer,\n} from './streaming'\nexport { COLOR, MD_STYLE, SELECT_THEME } from './theme'\nexport type {\n Owner,\n Picked,\n Screen,\n SessionMeta,\n Settings,\n StreamEvent,\n} from './types'\n"],"mappings":"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"}
1
+ {"version":3,"file":"tui.js","names":["initMd4x","EmptyState","ensureDir","BUILTIN_PRESET","initMd4x"],"sources":["../src/tui/format.ts","../src/tui/theme.ts","../src/tui/components.tsx","../src/tui/providers.ts","../src/tui/credentials.ts","../src/tui/auth.ts","../src/tui/store.ts","../src/tui/config.tsx","../src/tui/modal.tsx","../src/tui/model-picker.tsx","../src/tui/safe-mode.ts","../src/tui/safe-mode-context.tsx","../src/tui/oauth.ts","../src/tui/screens.tsx","../src/tui/settings.tsx","../src/tui/streaming.ts","../src/tui/app.tsx","../src/tui/index.tsx"],"sourcesContent":["/** Compact token formatter — 12_415 → \"12.4k\", 1_234_567 → \"1.23M\". */\nexport function fmtTokens(n: number): string {\n if (n < 1000)\n return String(n)\n if (n < 1_000_000)\n return `${(n / 1000).toFixed(n < 10_000 ? 2 : 1)}k`\n return `${(n / 1_000_000).toFixed(2)}M`\n}\n\n/** Compact relative-time formatter — \"just now / 5m / 3h / 2d\". */\nexport function ageString(ts: number, now: number = Date.now()): string {\n const m = Math.floor((now - ts) / 60_000)\n if (m < 1)\n return 'just now'\n if (m < 60)\n return `${m}m ago`\n const h = Math.floor(m / 60)\n if (h < 24)\n return `${h}h ago`\n return `${Math.floor(h / 24)}d ago`\n}\n\n/** Six-char short form of a session id for headers and lists. */\nexport function shortId(id: string): string {\n return id.replace(/-/g, '').slice(0, 6)\n}\n","import { RGBA, SyntaxStyle } from '@opentui/core'\n\n/**\n * Shared color palette. Kept as plain hex strings so it can be consumed by\n * OpenTUI props that accept `string | RGBA`. The names describe role, not\n * literal hue, so the theme can be swapped without touching call sites.\n */\nexport const COLOR = {\n brand: '#FFCC00',\n accent: '#00FF88',\n model: '#88CCFF',\n warn: '#FFAA66',\n error: '#FF6666',\n dim: '#888888',\n mute: '#555555',\n border: '#333333',\n borderActive: '#555555',\n} as const\n\n/**\n * Shared select styling — keeps the highlight bar from filling with a\n * different background than the surrounding box. The `▶` marker and the\n * brand-colored selected text carry the focus affordance.\n */\nexport const SELECT_THEME = {\n backgroundColor: 'transparent',\n focusedBackgroundColor: 'transparent',\n selectedBackgroundColor: 'transparent',\n selectedTextColor: COLOR.brand,\n textColor: COLOR.dim,\n descriptionColor: COLOR.mute,\n selectedDescriptionColor: COLOR.dim,\n} as const\n\n/**\n * Theme for markdown token highlighting. Token names map to Tree-sitter highlight\n * captures emitted by OpenTUI's markdown parser; the `default` entry is the\n * fallback for unstyled text.\n */\nexport const MD_STYLE = SyntaxStyle.fromStyles({\n 'default': { fg: RGBA.fromHex('#E6EDF3') },\n 'markup.heading': { fg: RGBA.fromHex(COLOR.brand), bold: true },\n 'markup.heading.1': { fg: RGBA.fromHex(COLOR.brand), bold: true },\n 'markup.heading.2': { fg: RGBA.fromHex('#FFD84D'), bold: true },\n 'markup.heading.3': { fg: RGBA.fromHex('#FFE680'), bold: true },\n 'markup.bold': { fg: RGBA.fromHex('#FFFFFF'), bold: true },\n 'markup.italic': { fg: RGBA.fromHex('#E6EDF3'), italic: true },\n 'markup.link': { fg: RGBA.fromHex(COLOR.model), underline: true },\n 'markup.link.url': { fg: RGBA.fromHex(COLOR.model), underline: true },\n 'markup.list': { fg: RGBA.fromHex(COLOR.warn) },\n 'markup.raw': { fg: RGBA.fromHex('#A5D6FF') },\n 'markup.raw.block': { fg: RGBA.fromHex('#A5D6FF') },\n 'markup.quote': { fg: RGBA.fromHex(COLOR.dim), italic: true },\n 'punctuation': { fg: RGBA.fromHex(COLOR.mute) },\n})\n","import type { Picked, Settings, StreamEvent } from './types'\nimport { heal, init as initMd4x } from 'md4x/wasm'\nimport { memo, useEffect, useMemo, useState } from 'react'\nimport { fmtTokens } from './format'\nimport { COLOR, MD_STYLE } from './theme'\n\n// Kick off md4x WASM init at module load. `runTui()` already awaits this\n// before rendering, but composition hosts that mount `<App>` against their\n// own renderer never call `runTui` — so without this, the first streaming\n// markdown delta would crash `heal()` with \"WASM not initialized\".\n// Idempotent (`init()` no-ops once `_hasInstance()` is true).\nconst md4xReady = initMd4x().catch(() => {\n // Swallow: heal() below is wrapped in try/catch and falls back to raw text.\n})\nvoid md4xReady\n\n/**\n * Heal incomplete streaming markdown, surviving any md4x failure.\n *\n * `md4x/wasm` can throw \"WASM not initialized\" in two cases we've seen:\n * 1. A host mounted `<App>` directly without calling `runTui()`/`init()`.\n * 2. A `bun build --compile` binary where the inlined `md4x.wasm` default\n * export resolved to `undefined` — `init()` silently calls\n * `_setInstance(undefined)` and every subsequent `heal()` throws.\n *\n * In both cases we'd rather render the raw streaming text (OpenTUI's\n * `<markdown streaming>` tolerates unclosed delimiters) than crash the\n * transcript.\n */\nfunction safeHeal(text: string): string {\n try {\n return heal(text)\n }\n catch {\n return text\n }\n}\n\n/**\n * Memoized so a flush that mutates only the trailing event doesn't force the\n * entire transcript to re-render. Each event holds a stable reference until\n * its content changes (we only ever recreate the streaming-markdown tail).\n *\n * The outer wrapper handles top-margin per kind (and per neighbor) so spacing\n * is the single source of truth for inter-event breathing room.\n */\nconst EventLine = memo(\n ({ event, previous, depthOffset = 0 }: {\n event: StreamEvent\n previous?: StreamEvent\n /**\n * Subtract from the event's depth before computing left indent. The\n * subagent box already provides one level of visual offset via its\n * border + padding, so events rendered inside it pass `depthOffset: 1`\n * to avoid double-indenting.\n */\n depthOffset?: number\n }) => (\n <box\n style={{\n marginTop: marginTopFor(event, previous),\n // Pin width to the scrollbox content area + lock the row against\n // shrinking. Without these, a streamed markdown growing taller can\n // make Yoga renegotiate widths for *every* sibling row, which is the\n // visible \"the text jiggles a column to the left, then back\" effect.\n alignSelf: 'stretch',\n flexShrink: 0,\n flexDirection: 'column',\n }}\n >\n <EventLineImpl event={event} depthOffset={depthOffset} />\n </box>\n ),\n)\n\n/**\n * `@opentui/react` extends `React.JSX.IntrinsicElements`, so `onSubmit` on `<input>`\n * gets intersected with the DOM `SubmitEvent` shape and demands an unhelpful overload.\n * The OpenTUI input runtime fires `(value: string) => void`; this helper isolates\n * the cast so each call site stays readable.\n */\nexport function onInputSubmit(handler: (value: string) => void): never {\n return handler as unknown as never\n}\n\n// ---------------------------------------------------------------------------\n// Footer — single status bar: hints on the left, provider info in the middle,\n// context-window indicator on the right.\n// ---------------------------------------------------------------------------\n\nexport interface Hint {\n key: string\n label: string\n}\n\nexport interface ContextUsage {\n used: number\n max: number\n}\n\nexport function Footer({\n hints,\n picked,\n context,\n}: {\n hints: Hint[]\n picked: Picked | null\n context: ContextUsage | null\n}) {\n return (\n <box style={{ flexDirection: 'row', height: 1, paddingLeft: 1, paddingRight: 1 }}>\n <text fg={COLOR.dim}>\n {hints.map((h, i) => (\n <span key={i}>\n {i > 0 && <span fg={COLOR.mute}> · </span>}\n <span fg={COLOR.warn}>{h.key}</span>\n <span fg={COLOR.dim}>{` ${h.label}`}</span>\n </span>\n ))}\n </text>\n {picked && <ProviderBadge picked={picked} />}\n <box style={{ flexGrow: 1 }} />\n {context && <ContextIndicator context={context} />}\n </box>\n )\n}\n\nfunction ProviderBadge({ picked }: { picked: Picked }) {\n const source = picked.provider.methods[0].source\n return (\n <text fg={COLOR.dim}>\n <span fg={COLOR.mute}> · </span>\n <span fg={COLOR.accent}>{picked.provider.label}</span>\n <span fg={COLOR.mute}> · </span>\n <span fg={COLOR.model}>{picked.model}</span>\n <span fg={COLOR.mute}> · </span>\n <span fg={source === 'oauth' ? COLOR.accent : COLOR.warn}>{source}</span>\n </text>\n )\n}\n\nfunction ContextIndicator({ context }: { context: ContextUsage }) {\n const ratio = context.max > 0 ? context.used / context.max : 0\n const pct = Math.round(ratio * 100)\n const color = ratio >= 0.85 ? COLOR.error : ratio >= 0.6 ? COLOR.warn : COLOR.dim\n return (\n <text fg={COLOR.dim}>\n <span fg={COLOR.mute}>ctx </span>\n <span fg={color}>{fmtTokens(context.used)}</span>\n <span fg={COLOR.mute}>{` / ${fmtTokens(context.max)} `}</span>\n <span fg={color}>{`(${pct}%)`}</span>\n </text>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Spinner — animated braille used while a run is streaming.\n// ---------------------------------------------------------------------------\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']\nconst SPINNER_INTERVAL_MS = 80\n\nexport function Spinner({ label }: { label: string }) {\n const [frame, setFrame] = useState(0)\n\n useEffect(() => {\n const id = setInterval(() => setFrame(f => (f + 1) % SPINNER_FRAMES.length), SPINNER_INTERVAL_MS)\n return () => clearInterval(id)\n }, [])\n\n return (\n <text fg={COLOR.warn}>\n {SPINNER_FRAMES[frame]}\n <span fg={COLOR.dim}>{` ${label}`}</span>\n </text>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Transcript — scrollbox with sticky-bottom and structured event rendering.\n// ---------------------------------------------------------------------------\n\nexport function Transcript({ events, settings }: { events: StreamEvent[], settings: Settings }) {\n const items = useMemo(() => partitionTranscript(events, settings), [events, settings])\n\n if (items.length === 0)\n return <EmptyState />\n\n return (\n <scrollbox\n // Never claim keyboard focus: the textarea is the chat's primary input,\n // so up/down/page-up/page-down should stay with it (or with the modal\n // when one is open). Mouse-wheel scrolling still works.\n focusable={false}\n style={{ flexGrow: 1, paddingLeft: 1, paddingRight: 1 }}\n stickyScroll\n stickyStart=\"bottom\"\n >\n {items.map((item, i) => (\n item.kind === 'event'\n ? <EventLine key={i} event={item.event} previous={item.previous} />\n : <SubagentBlock key={i} events={item.events} previous={item.previous} />\n ))}\n </scrollbox>\n )\n}\n\n/**\n * Per-event visibility — filters honor user toggles and the\n * `hideSubagentOutput` setting. When subagent output is hidden:\n * - Child-agent events are filtered down to the `spawn-start` /\n * `spawn-end` markers so the user still sees \"🌱 working… 🌳 done\".\n * - The parent's `tool-result` for `spawn` is hidden too. Its body\n * duplicates `spawn-end`'s stats line *and* the parent's next markdown\n * turn (\"Here's what the sub-agent found: …\"). Showing it again\n * produced an extra `┃ [sub-agent child-1] Completed …` block that\n * the user just wanted gone.\n *\n * Exported so the visibility matrix can be unit-tested without rendering.\n */\nexport function isVisible(event: StreamEvent, settings: Settings): boolean {\n if (settings.hideSubagentOutput) {\n if (isChild(event))\n return event.kind === 'spawn-start' || event.kind === 'spawn-end'\n if (event.kind === 'tool-result' && event.tool === 'spawn')\n return false\n }\n switch (event.kind) {\n case 'thinking': return settings.showThinking\n case 'tool': return settings.showToolCalls\n case 'tool-result': return settings.showToolResults\n default: return true\n }\n}\n\n/**\n * Output of `partitionTranscript`. Single events render as a normal\n * `EventLine`; child-event runs render as a bordered subagent box.\n */\ntype TranscriptItem\n = | { kind: 'event', event: StreamEvent, previous?: StreamEvent }\n | { kind: 'child-run', events: StreamEvent[], previous?: StreamEvent }\n\n/**\n * Walk the visible-event list once and group consecutive child events\n * (`depth > 0`) into runs so we can wrap each run in a single bordered\n * subagent box.\n *\n * When `hideSubagentOutput` is on, `isVisible` already filters most child\n * events out; the surviving `spawn-start` / `spawn-end` markers render as\n * plain entries (no boxing) — there's nothing meaningful to box.\n */\nfunction partitionTranscript(events: StreamEvent[], settings: Settings): TranscriptItem[] {\n const visible = events.filter(e => isVisible(e, settings))\n if (visible.length === 0)\n return []\n\n // Hide-mode: spawn-start/end are the only child events left. Don't box\n // them — they're already standalone \"subagent N is working/done\" lines.\n if (settings.hideSubagentOutput) {\n return visible.map((event, i) => ({ kind: 'event', event, previous: visible[i - 1] }))\n }\n\n const items: TranscriptItem[] = []\n let run: StreamEvent[] = []\n let runPrevious: StreamEvent | undefined\n\n const flush = () => {\n if (run.length > 0) {\n items.push({ kind: 'child-run', events: run, previous: runPrevious })\n run = []\n runPrevious = undefined\n }\n }\n\n for (let i = 0; i < visible.length; i++) {\n const event = visible[i]\n if (isChild(event)) {\n if (run.length === 0)\n runPrevious = visible[i - 1]\n run.push(event)\n }\n else {\n flush()\n items.push({ kind: 'event', event, previous: visible[i - 1] })\n }\n }\n flush()\n return items\n}\n\n/**\n * Bordered container for one run of subagent events. The box's border +\n * left padding give the visual \"this is a subagent\" affordance, so events\n * inside render with `depthOffset: 1` — a direct child of the parent\n * (depth 1) sits flush against the box's inner padding rather than being\n * indented twice. Grandchildren (depth ≥ 2) still indent further, so\n * nested subagents remain visually distinct.\n */\nfunction SubagentBlock({ events, previous }: { events: StreamEvent[], previous?: StreamEvent }) {\n const childIds = useMemo(() => {\n const set = new Set<string>()\n for (const e of events) {\n if (e.childId)\n set.add(e.childId)\n }\n return Array.from(set)\n }, [events])\n\n const title = childIds.length === 0\n ? ' subagent '\n : childIds.length === 1\n ? ` ${childIds[0]} `\n : ` subagents · ${childIds.join(', ')} `\n\n // Keep the same vertical breathing room a markdown/spawn-start event would\n // get standalone — without this the box looks glued to the parent's tool\n // call above it.\n const marginTop = previous ? 1 : 0\n\n return (\n <box\n title={title}\n style={{\n border: true,\n borderColor: COLOR.mute,\n paddingLeft: 1,\n paddingRight: 1,\n paddingTop: 0,\n paddingBottom: 0,\n marginTop,\n flexDirection: 'column',\n flexShrink: 0,\n alignSelf: 'stretch',\n }}\n >\n {events.map((evt, i) => (\n <EventLine key={i} event={evt} previous={events[i - 1]} depthOffset={1} />\n ))}\n </box>\n )\n}\n\nfunction EmptyState() {\n return (\n <box style={{ flexGrow: 1, alignItems: 'center', justifyContent: 'center' }}>\n <text fg={COLOR.mute}>no messages yet — type below to start</text>\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// EventLine — dispatches per kind. Subagent events render via indented dim\n// blocks; the `depth` field drives the left indent.\n// ---------------------------------------------------------------------------\n\n/** Left-pad applied per depth level (in columns). */\nconst INDENT_PER_DEPTH = 2\n\nfunction indentFor(depth: number | undefined): number {\n return depth && depth > 0 ? depth * INDENT_PER_DEPTH : 0\n}\n\nfunction isChild(event: StreamEvent): boolean {\n return (event.depth ?? 0) > 0\n}\n\n/**\n * Shared row geometry for every transcript event.\n *\n * `alignSelf: 'stretch'` + `flexShrink: 0` together pin each row to the\n * scrollbox's content width and prevent flex re-negotiation when neighboring\n * rows grow or shrink (streaming markdown, late-arriving tool results, etc.).\n * Without this, Yoga is free to re-compute widths every render and the\n * visible text appears to \"wiggle\" between columns as the stream advances.\n */\nfunction rowStyle(paddingLeft: number) {\n return {\n paddingLeft,\n flexDirection: 'column' as const,\n flexShrink: 0,\n alignSelf: 'stretch' as const,\n }\n}\n\n/**\n * Default top-margin per kind. Spacing intent:\n * - `info` / `markdown` / `tool` / `error` / `spawn-start` open a new block\n * so they each get one row of breathing room above.\n * - `thinking` / `tool-result` / `spawn-end` continue the previous block\n * and stay flush.\n *\n * Context-aware overrides live in `marginTopFor` — e.g. consecutive tool\n * round-trips collapse to a tight list regardless of whether outputs are shown.\n */\nconst MARGIN_TOP: Record<StreamEvent['kind'], number> = {\n 'separator': 0,\n 'info': 1,\n 'thinking': 0,\n 'tool': 1,\n 'tool-result': 0,\n 'error': 1,\n 'markdown': 1,\n 'spawn-start': 1,\n 'spawn-end': 0,\n}\n\nconst TOOL_KINDS: ReadonlySet<StreamEvent['kind']> = new Set(['tool', 'tool-result'])\n\n/**\n * Resolve the top margin for an event given the one rendered just before it.\n *\n * Context-aware rules:\n *\n * - A `tool` / `tool-result` event right after another `tool` / `tool-result`\n * collapses to a tight list — call→result pairs and back-to-back calls\n * read as one logical block.\n * - A parent-level event (`depth === 0`) right after a subagent event\n * (`depth > 0`) collapses too. The subagent's `🌳` end marker (and, in\n * show mode, the subagent box's bottom border) already provides the\n * separation; adding the event's default `marginTop` on top would\n * produce the visible \"line jump\" between a subagent's outcome and the\n * parent's follow-up. Either form of marker is enough — we don't want\n * both.\n *\n * Exported so the spacing matrix can be unit-tested without rendering.\n */\nexport function marginTopFor(event: StreamEvent, previous: StreamEvent | undefined): number {\n if (TOOL_KINDS.has(event.kind) && previous && TOOL_KINDS.has(previous.kind))\n return 0\n const eventDepth = event.depth ?? 0\n const previousDepth = previous?.depth ?? 0\n if (eventDepth === 0 && previousDepth > 0)\n return 0\n return MARGIN_TOP[event.kind] ?? 0\n}\n\nfunction EventLineImpl({ event, depthOffset = 0 }: { event: StreamEvent, depthOffset?: number }) {\n const safeText = event.text === '' ? ' ' : event.text\n const effectiveDepth = Math.max(0, (event.depth ?? 0) - depthOffset)\n const row = rowStyle(indentFor(effectiveDepth))\n\n // Subagent text is dimmed across the board so the visual hierarchy is\n // obvious even before reading the indent.\n const child = isChild(event)\n\n switch (event.kind) {\n case 'separator':\n return <text> </text>\n case 'info':\n return <UserPromptBlock text={safeText} />\n case 'thinking':\n return (\n <box style={row}>\n <text fg={COLOR.dim}>{safeText}</text>\n </box>\n )\n case 'tool':\n return (\n <box style={row}>\n <text fg={child ? COLOR.dim : COLOR.model}>\n <span fg={COLOR.mute}>↳ </span>\n {safeText}\n </text>\n </box>\n )\n case 'tool-result':\n return <ToolResultBlock text={event.text} indent={row.paddingLeft} />\n case 'error':\n return (\n <box style={row}>\n <text fg={COLOR.error}>\n <span fg={COLOR.error}>✗ </span>\n {safeText}\n </text>\n </box>\n )\n case 'markdown':\n return (\n <box style={row}>\n <MarkdownBlock text={event.text} streaming={event.streaming ?? false} dim={child} />\n </box>\n )\n case 'spawn-start':\n return (\n <box style={row}>\n <text fg={COLOR.dim}>\n <span fg={COLOR.accent}>🌱 </span>\n <span fg={COLOR.dim}>{`[${event.childId ?? 'child'}] `}</span>\n <span fg={COLOR.dim}>{safeText}</span>\n </text>\n </box>\n )\n case 'spawn-end':\n // Use a 2-cell emoji (like the 🌱 sprout on spawn-start) so the `[…]`\n // label lands in the same column on every row of a subagent's life\n // cycle — the previous `✓` glyph is 1 cell wide and shifted the\n // label one column left of the start marker.\n return (\n <box style={row}>\n <text fg={COLOR.dim}>\n <span fg={COLOR.accent}>🌳 </span>\n <span fg={COLOR.dim}>{`[${event.childId ?? 'child'}] `}</span>\n <span fg={COLOR.mute}>{safeText}</span>\n </text>\n </box>\n )\n default:\n return <text>{safeText}</text>\n }\n}\n\n/** User prompt — bordered to rhyme with the prompt input box below. */\nfunction UserPromptBlock({ text }: { text: string }) {\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n }}\n >\n <text fg={COLOR.brand}>{text}</text>\n </box>\n )\n}\n\n/**\n * Markdown block. While `streaming` is true, content is passed through\n * `md4x.heal()` so unclosed delimiters (bold, italic, code, link, table) render\n * as if already complete. OpenTUI's `streaming` prop keeps its parser from\n * committing to the final layout for the trailing block.\n *\n * `internalBlockMode: \"top-level\"` is the load-bearing knob for streaming\n * stability: in the default `\"coalesced\"` mode, OpenTUI fuses adjacent\n * top-level markdown blocks into one render block, so when a token streams\n * in the *entire* coalesced block re-flows and earlier paragraphs visibly\n * jump. With `\"top-level\"`, each top-level block (paragraph, heading, list,\n * code fence) is its own render block — they finalize as soon as the parser\n * moves past them, leaving only the trailing block reflowable.\n *\n * `alignSelf: 'stretch'` pins the markdown to the parent box's content\n * width so its wrap column doesn't drift between renders.\n */\nfunction MarkdownBlock({ text, streaming, dim }: { text: string, streaming: boolean, dim: boolean }) {\n // Memoize the heal pass so re-renders triggered by unrelated state changes\n // don't recompute it; only re-run when text/streaming flip.\n const content = useMemo(\n () => streaming ? safeHeal(text) : text,\n [text, streaming],\n )\n return (\n <markdown\n content={content}\n syntaxStyle={MD_STYLE}\n streaming={streaming}\n internalBlockMode=\"top-level\"\n fg={dim ? COLOR.dim : undefined}\n alignSelf=\"stretch\"\n flexShrink={0}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Tool result — left-bar block, truncated. Caps at TOOL_RESULT_MAX_LINES so a\n// 200-line file doesn't drown the transcript.\n// ---------------------------------------------------------------------------\n\nconst TOOL_RESULT_MAX_LINES = 6\n\nfunction ToolResultBlock({ text, indent }: { text: string, indent: number }) {\n const lines = text.split('\\n')\n const visible = lines.slice(0, TOOL_RESULT_MAX_LINES)\n const omitted = Math.max(0, lines.length - TOOL_RESULT_MAX_LINES)\n\n return (\n <box style={{ paddingLeft: indent, flexDirection: 'column' }}>\n {visible.map((line, i) => (\n <text key={i} fg={COLOR.mute}>\n <span fg={COLOR.borderActive}>┃ </span>\n {line || ' '}\n </text>\n ))}\n {omitted > 0 && (\n <text fg={COLOR.mute}>\n <span fg={COLOR.borderActive}>┃ </span>\n {`… ${omitted} more line${omitted === 1 ? '' : 's'}`}\n </text>\n )}\n </box>\n )\n}\n","/**\n * Provider registry — the customization seam for the TUI.\n *\n * A {@link ProviderDescriptor} carries everything the TUI needs to know about\n * a provider in one place: how to instantiate it, what to show in the UI,\n * how to detect credentials, how to OAuth (if applicable), and where to look\n * up its models. Hosts can ship their own descriptors instead of the built-in\n * four — see {@link BUILTIN_PROVIDERS}.\n */\n\nimport type { OAuthProviderInterface } from '@mariozechner/pi-ai/oauth'\nimport type { Provider } from '../providers'\nimport { getModel, getModels } from '@mariozechner/pi-ai'\nimport { anthropicOAuthProvider, openaiCodexOAuthProvider } from '@mariozechner/pi-ai/oauth'\nimport { anthropic, cerebras, openai, openrouter } from '../providers'\n\n/**\n * Structural model metadata — compatible with pi-ai's `Model` interface but\n * not coupled to it, so hosts can pass either pi-ai-shaped objects or a\n * custom registry of their own.\n *\n * Deliberately a structural duplicate of pi-ai's `Model` rather than a\n * re-export: keeps the public API stable when pi-ai bumps shape, and lets\n * hosts implement their own registry without depending on pi-ai's types.\n *\n * Lives here (rather than `./config`) because `ProviderDescriptor.models`\n * needs it — pushing it to `config.tsx` created an import cycle.\n */\nexport interface ModelInfo {\n id: string\n name?: string\n contextWindow: number\n maxTokens?: number\n reasoning?: boolean\n input?: readonly ('text' | 'image')[]\n cost?: { input: number, output: number, cacheRead?: number, cacheWrite?: number }\n provider?: string\n}\n\nexport interface ProviderDescriptor {\n /**\n * Unique identifier. Used as the registry key, persisted in `state.json`\n * as the resumed provider, and (by default) as the credential-file key.\n */\n key: string\n /** Display name shown in the TUI's provider picker and labels. */\n label: string\n /**\n * Factory that builds a fresh `Provider` instance. Called on every session\n * activation. Some factories (e.g. zidane's built-in `anthropic`) eagerly\n * resolve credentials at construction time and throw when none are\n * configured — set {@link defaultModel} on the descriptor to avoid the TUI\n * calling the factory before the user has picked + authed a provider.\n */\n factory: () => Provider\n /**\n * Default model id to seed the picker with. When omitted, the TUI falls\n * back to `descriptor.factory().meta.defaultModel`, which constructs the\n * provider — fine for lazy-credential factories, but breaks for factories\n * that throw on missing credentials before the wizard runs. Setting this\n * eagerly lets the TUI render the auth wizard without ever instantiating\n * the provider.\n */\n defaultModel?: string\n /**\n * Env var checked when detecting whether the user has an API key\n * configured for this provider. Optional — set to `undefined` when the\n * provider only supports OAuth (or only credentials via a custom path).\n */\n envKey?: string\n /**\n * Key under which credentials live in `credentials.json`. Defaults to\n * `key`. The only built-in that overrides this is OpenAI Codex\n * (`openai` → `openai-codex`) to stay compatible with the harness\n * provider's existing lookup.\n */\n credentialFileKey?: string\n /** Placeholder shown in the wizard's API-key input. */\n apiKeyPlaceholder?: string\n /**\n * pi-ai (or compat) OAuth provider. When present, the wizard offers an\n * OAuth option in addition to API key.\n */\n oauthProvider?: OAuthProviderInterface\n /**\n * Optional copy appended to the wizard's \"OAuth\" method description.\n * Use to communicate why a user might pick OAuth (e.g. subscription tier,\n * org-wide SSO). Shown after `browser-based sign-in`. Skip the leading\n * space — the wizard adds it. Example: `'Claude Pro/Max subscription'`.\n */\n oauthHint?: string\n /**\n * pi-ai provider id used to look up models in pi-ai's built-in registry.\n * Defaults to `key`. Only Codex differs (`openai` → `openai-codex`).\n */\n piProviderId?: string\n /**\n * Override the model list returned for this provider's picker. When set,\n * skips pi-ai's registry entirely. Useful for hosts maintaining their\n * own model catalogue or for custom providers pi-ai doesn't know about.\n */\n models?: readonly ModelInfo[]\n}\n\n/** Convenience accessor — returns `credentialFileKey ?? key`. */\nexport function credKeyOf(desc: ProviderDescriptor): string {\n return desc.credentialFileKey ?? desc.key\n}\n\n/** Convenience accessor — returns `piProviderId ?? key`. */\nexport function piIdOf(desc: ProviderDescriptor): string {\n return desc.piProviderId ?? desc.key\n}\n\n// ---------------------------------------------------------------------------\n// Built-in descriptors\n// ---------------------------------------------------------------------------\n\nexport const anthropicDescriptor: ProviderDescriptor = {\n key: 'anthropic',\n label: 'Anthropic',\n factory: anthropic,\n defaultModel: 'claude-opus-4-7',\n envKey: 'ANTHROPIC_API_KEY',\n apiKeyPlaceholder: 'sk-ant-…',\n oauthProvider: anthropicOAuthProvider,\n oauthHint: 'Claude Pro/Max subscription',\n}\n\nexport const openaiDescriptor: ProviderDescriptor = {\n key: 'openai',\n label: 'OpenAI Codex',\n factory: openai,\n defaultModel: 'gpt-5.4',\n envKey: 'OPENAI_CODEX_API_KEY',\n credentialFileKey: 'openai-codex',\n piProviderId: 'openai-codex',\n apiKeyPlaceholder: 'sk-… or eyJ… (Codex)',\n oauthProvider: openaiCodexOAuthProvider,\n}\n\nexport const openrouterDescriptor: ProviderDescriptor = {\n key: 'openrouter',\n label: 'OpenRouter',\n factory: openrouter,\n defaultModel: 'anthropic/claude-sonnet-4-6',\n envKey: 'OPENROUTER_API_KEY',\n apiKeyPlaceholder: 'sk-or-…',\n}\n\nexport const cerebrasDescriptor: ProviderDescriptor = {\n key: 'cerebras',\n label: 'Cerebras',\n factory: cerebras,\n defaultModel: 'zai-glm-4.7',\n envKey: 'CEREBRAS_API_KEY',\n apiKeyPlaceholder: 'csk-…',\n}\n\n/**\n * Default provider registry. Passed verbatim when `runTui` is invoked without\n * an explicit `providers` option. Hosts that want to override per-provider\n * metadata can spread this and replace specific entries:\n *\n * ```ts\n * runTui({ providers: { ...BUILTIN_PROVIDERS, anthropic: myOwnAnthropicDescriptor } })\n * ```\n */\nexport const BUILTIN_PROVIDERS: Readonly<Record<string, ProviderDescriptor>> = {\n anthropic: anthropicDescriptor,\n openai: openaiDescriptor,\n openrouter: openrouterDescriptor,\n cerebras: cerebrasDescriptor,\n}\n\n// ---------------------------------------------------------------------------\n// Model registry helper\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the model list for a given provider. Honors `descriptor.models`\n * when set; otherwise queries pi-ai via `descriptor.piProviderId`. Returns\n * `[]` for descriptors with no known mapping (custom providers without a\n * model list) — callers should hide the model picker in that case.\n */\nexport function modelsForDescriptor(descriptor: ProviderDescriptor): readonly ModelInfo[] {\n if (descriptor.models)\n return descriptor.models\n try {\n return getModels(piIdOf(descriptor) as never) as readonly ModelInfo[]\n }\n catch {\n return []\n }\n}\n\n/**\n * Look up the model's max context window via the descriptor's model source.\n * Returns `null` when the model isn't known (custom slugs, providers without\n * a registry); callers should hide the context indicator in that case.\n */\nexport function getContextWindow(descriptor: ProviderDescriptor, modelId: string): number | null {\n // Prefer the descriptor's own list — host overrides win.\n if (descriptor.models) {\n const found = descriptor.models.find(m => m.id === modelId)\n return found?.contextWindow ?? null\n }\n try {\n const model = getModel(piIdOf(descriptor) as never, modelId as never)\n return model?.contextWindow ?? null\n }\n catch {\n return null\n }\n}\n","/**\n * Credential storage for the TUI.\n *\n * Lives at `<dataDir>/credentials.json` (default `~/.zidane/credentials.json`,\n * overridable via `ZIDANE_STORAGE_DIR`). Owner-only (0o600) so refresh tokens\n * and API keys don't leak on shared boxes.\n *\n * Two credential kinds:\n * - `apikey` — a plain API key entered by the user (e.g. via the setup wizard).\n * - `oauth` — tokens obtained via a provider's OAuth flow (refresh-aware).\n *\n * **Schema note.** File keys come from {@link ProviderDescriptor.credentialFileKey}\n * (falling back to `descriptor.key`). They must match what the harness providers\n * look up at runtime — for built-ins this is `anthropic`, `openai-codex`,\n * `openrouter`, `cerebras`. Hosts that ship custom providers control the file\n * key via the descriptor.\n *\n * The legacy `cwd/.credentials.json` (written by `bun run auth` in older\n * versions) is migrated on first read if the new file doesn't exist yet. The\n * legacy file is left in place — we don't delete user data behind their back.\n */\n\nimport type { ProviderDescriptor } from './providers'\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { credKeyOf } from './providers'\n\n/** POSIX mode for the credentials file. Ignored on Windows. */\nconst FILE_MODE = 0o600\n\nexport interface ApiKeyCredential {\n kind: 'apikey'\n value: string\n}\nexport interface OAuthCredential {\n kind: 'oauth'\n access: string\n refresh?: string\n expires?: number\n /** Provider-specific extras (e.g. OpenAI Codex `accountId`). */\n [extra: string]: unknown\n}\nexport type ProviderCredential = ApiKeyCredential | OAuthCredential\n\n/** Top-level shape of `credentials.json` — keys are credential-file keys. */\nexport type CredentialsFile = Record<string, ProviderCredential>\n\n/**\n * Resolve the credentials file path given the resolved TUI data directory\n * (typically `~/.zidane`, i.e. `config.paths.dir`).\n *\n * Matches the convention used elsewhere in the TUI (sessions.db, state.json)\n * so a single `ZIDANE_STORAGE_DIR` override moves the entire data root.\n */\nexport function credentialsPath(dataDir: string): string {\n return resolve(dataDir, 'credentials.json')\n}\n\n/**\n * Read credentials from disk.\n *\n * Returns `{}` when the file is missing or corrupt (last-ditch tolerance —\n * a hand-edit gone wrong shouldn't lock the user out of re-authing). On first\n * call with no file present, attempts a migration from `cwd/.credentials.json`\n * (the legacy location used by `bun run auth`).\n */\nexport function readCredentials(dataDir: string): CredentialsFile {\n const path = credentialsPath(dataDir)\n\n if (!existsSync(path)) {\n const migrated = migrateLegacyFile(path)\n if (migrated)\n return migrated\n return {}\n }\n\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 return {}\n }\n}\n\n/** Read a single provider's credential (translating via the descriptor). */\nexport function readProviderCredential(dataDir: string, descriptor: ProviderDescriptor): ProviderCredential | undefined {\n return readCredentials(dataDir)[credKeyOf(descriptor)]\n}\n\n/**\n * Write credentials atomically (write-then-rename) with mode 0o600.\n *\n * Atomic on the same filesystem — readers either see the previous file or the\n * new one, never a half-written intermediate. Creates the parent dir if needed\n * (first launch on a fresh machine: `~/.zidane/` may not exist yet).\n */\nexport function writeCredentials(dataDir: string, creds: CredentialsFile): void {\n const path = credentialsPath(dataDir)\n mkdirSync(dirname(path), { recursive: true })\n const tmp = `${path}.${process.pid}.${Date.now()}.tmp`\n writeFileSync(tmp, `${JSON.stringify(creds, null, 2)}\\n`, { mode: FILE_MODE })\n renameSync(tmp, path)\n}\n\nexport function setProviderCredential(\n dataDir: string,\n descriptor: ProviderDescriptor,\n cred: ProviderCredential,\n): void {\n const all = readCredentials(dataDir)\n all[credKeyOf(descriptor)] = cred\n writeCredentials(dataDir, all)\n}\n\nexport function removeProviderCredential(dataDir: string, descriptor: ProviderDescriptor): void {\n const all = readCredentials(dataDir)\n const fileKey = credKeyOf(descriptor)\n if (!(fileKey in all))\n return\n delete all[fileKey]\n writeCredentials(dataDir, all)\n}\n\n/**\n * Inject API-key credentials into `process.env` so the harness providers pick\n * them up via their existing env-var resolution. Called once at TUI launch\n * after the credentials file has been resolved. OAuth credentials are NOT\n * injected — those reach providers via `ZIDANE_CREDENTIALS_PATH` + the file\n * reader in `src/providers/oauth.ts`.\n *\n * Does not overwrite env vars that are already set — explicit user-provided\n * env values win over stored API keys.\n *\n * Descriptors without an `envKey` (OAuth-only providers, custom providers\n * that bypass env-var resolution) are skipped silently.\n */\nexport function applyApiKeyEnv(\n dataDir: string,\n registry: Readonly<Record<string, ProviderDescriptor>>,\n): void {\n const creds = readCredentials(dataDir)\n for (const descriptor of Object.values(registry)) {\n if (!descriptor.envKey || process.env[descriptor.envKey])\n continue\n const cred = creds[credKeyOf(descriptor)]\n if (cred?.kind === 'apikey' && cred.value)\n process.env[descriptor.envKey] = cred.value\n }\n}\n\n// ---------------------------------------------------------------------------\n// Legacy migration\n// ---------------------------------------------------------------------------\n\n/**\n * `bun run auth` (pre-TUI) wrote `cwd/.credentials.json` with an entry per\n * provider mapping directly to an OAuthCredentials payload, e.g.:\n *\n * {\n * \"anthropic\": { \"access\": \"...\", \"refresh\": \"...\", \"expires\": 123 },\n * \"openai-codex\": { \"access\": \"...\", \"refresh\": \"...\", \"expires\": 123, \"accountId\": \"...\" }\n * }\n *\n * We don't delete the legacy file — it might still be used by a host that\n * imports the harness directly. We just copy its contents into the new\n * location under the kind-tagged shape so the TUI picks them up.\n *\n * Migration is provider-agnostic: any top-level entry with an `access` field\n * is preserved verbatim (extras included), under the same key. The TUI's\n * detection then looks them up via the matching descriptor's `credentialFileKey`.\n *\n * Returns the migrated credentials when the migration ran, or `null` when\n * there's no legacy file to migrate.\n */\nfunction migrateLegacyFile(targetPath: string): CredentialsFile | null {\n const legacyPath = resolve(process.cwd(), '.credentials.json')\n if (!existsSync(legacyPath))\n return null\n\n let legacy: Record<string, unknown>\n try {\n legacy = JSON.parse(readFileSync(legacyPath, 'utf-8'))\n }\n catch {\n return null\n }\n if (!legacy || typeof legacy !== 'object' || Array.isArray(legacy))\n return null\n\n const migrated: CredentialsFile = {}\n for (const [fileKey, value] of Object.entries(legacy)) {\n if (!isOAuthLegacy(value))\n continue\n const { access, refresh, expires, ...extras } = value\n migrated[fileKey] = {\n kind: 'oauth',\n access,\n ...(typeof refresh === 'string' ? { refresh } : {}),\n ...(typeof expires === 'number' ? { expires } : {}),\n ...extras,\n }\n }\n\n if (Object.keys(migrated).length === 0)\n return null\n\n // Persist immediately so subsequent reads use the new file directly.\n mkdirSync(dirname(targetPath), { recursive: true })\n const tmp = `${targetPath}.${process.pid}.${Date.now()}.tmp`\n writeFileSync(tmp, `${JSON.stringify(migrated, null, 2)}\\n`, { mode: FILE_MODE })\n renameSync(tmp, targetPath)\n\n return migrated\n}\n\nfunction isOAuthLegacy(value: unknown): value is { access: string, refresh?: string, expires?: number, [extra: string]: unknown } {\n return (\n typeof value === 'object'\n && value !== null\n && 'access' in value\n && typeof (value as { access: unknown }).access === 'string'\n )\n}\n","import type { ProviderCredential } from './credentials'\nimport type { ProviderDescriptor } from './providers'\nimport { readCredentials } from './credentials'\nimport { credKeyOf } from './providers'\n\n/**\n * Provider identifier as known to the TUI. Built-in keys are `anthropic`,\n * `openai`, `openrouter`, `cerebras`, but hosts can register additional\n * keys via {@link ProviderDescriptor} — so the type is open.\n */\nexport type ProviderKey = string\n\nexport interface AuthMethod {\n source: 'env' | 'oauth' | 'apikey'\n /** Human-readable detail (env var name, expiry timestamp, …). */\n detail: string\n}\n\nexport interface ProviderAuth {\n key: ProviderKey\n label: string\n /** True when at least one credential source is present. */\n available: boolean\n methods: AuthMethod[]\n}\n\n/**\n * Detect available auth for every registered provider.\n *\n * Resolution order per provider (a method appears in `methods` for each\n * layer that has a credential — the agent itself resolves them in the same\n * order via its provider factories):\n *\n * 1. `kind: 'apikey'` from `credentials.json` (injected into env at TUI launch)\n * 2. explicit env var (descriptor's `envKey`)\n * 3. `kind: 'oauth'` from `credentials.json` (or legacy `cwd/.credentials.json`)\n *\n * Pure read — never refreshes or rewrites the credentials file.\n */\nexport function detectAuth(\n dataDir: string,\n registry: Readonly<Record<string, ProviderDescriptor>>,\n env: Record<string, string | undefined> = process.env,\n): ProviderAuth[] {\n const creds = readCredentials(dataDir)\n\n return Object.values(registry).map((descriptor) => {\n const methods: AuthMethod[] = []\n const fileEntry = creds[credKeyOf(descriptor)] as ProviderCredential | undefined\n\n // 1. Stored API key\n if (fileEntry?.kind === 'apikey' && fileEntry.value)\n methods.push({ source: 'apikey', detail: 'credentials.json' })\n\n // 2. Env var (may be set by `applyApiKeyEnv` from a stored key, or by the\n // user's shell. Either way, treated as the same observable signal.)\n if (descriptor.envKey && env[descriptor.envKey])\n methods.push({ source: 'env', detail: descriptor.envKey })\n\n // 3. Stored OAuth\n if (fileEntry?.kind === 'oauth' && fileEntry.access) {\n const detail = typeof fileEntry.expires === 'number'\n ? `oauth · expires ${new Date(fileEntry.expires).toLocaleString()}`\n : 'oauth · credentials.json'\n methods.push({ source: 'oauth', detail })\n }\n\n return {\n key: descriptor.key,\n label: descriptor.label,\n available: methods.length > 0,\n methods,\n }\n })\n}\n","import type { SessionRun, SessionStore } from '../session'\nimport type { SessionTurn, ToolResultContent } from '../types'\nimport type { ProviderKey } from './auth'\nimport type { SessionMeta, Settings, StreamEvent } from './types'\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createSqliteStore } from '../session/sqlite'\nimport { formatTokenUsage } from '../stats'\nimport { toolResultToText } from '../types'\n\nfunction ensureDir(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 const message = err instanceof Error ? err.message : String(err)\n throw new Error(\n `Could not create TUI storage directory at \"${dir}\". `\n + `Override the location via \\`runTui({ storageDir, prefix })\\` or the `\n + `\\`ZIDANE_STORAGE_DIR\\` env var. Original error: ${message}`,\n )\n }\n}\n\n// ---------------------------------------------------------------------------\n// Session store — thin SQLite wrapper at a caller-supplied path.\n// ---------------------------------------------------------------------------\n\nexport function createTuiStore(dbPath: string): SessionStore {\n ensureDir(dbPath)\n return createSqliteStore({ path: dbPath })\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 /** 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 ensureDir(path)\n // Atomic write via tmp + rename so a crash mid-write never leaves a half file.\n const tmp = `${path}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify(state, null, 2))\n renameSync(tmp, path)\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 */\nexport async function listSessionMeta(store: SessionStore): Promise<SessionMeta[]> {\n const ids = await store.list()\n const metas = await Promise.all(ids.map(async (id) => {\n const data = await store.load(id)\n if (!data)\n return null\n return {\n id,\n title: titleFromTurns(data.turns) ?? 'untitled',\n turnCount: data.turns.length,\n updatedAt: data.updatedAt,\n }\n }))\n return metas.filter((m): m is SessionMeta => m !== 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 * 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 */\nexport function eventsFromTurns(\n turns: SessionTurn[],\n runs: readonly SessionRun[] = [],\n): StreamEvent[] {\n const runById = new Map<string, SessionRun>()\n for (const run of runs)\n runById.set(run.id, run)\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 const childLabelByRunId = new Map<string, string>()\n const childRuns = runs\n .filter(r => (r.depth ?? 0) > 0)\n .slice()\n .sort((a, b) => a.startedAt - b.startedAt)\n childRuns.forEach((r, i) => childLabelByRunId.set(r.id, `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 const events: StreamEvent[] = []\n let lastRunId: string | undefined\n let lastDepth = 0\n\n const closeRun = (runId: string | undefined, depth: number) => {\n if (!runId || depth <= 0)\n return\n const run = runById.get(runId)\n if (!run)\n return\n const tag = run.status === 'aborted' || run.status === 'error' ? run.status : 'done'\n // Same caching gotcha as the live path: `tokensIn` mirrors `AgentStats.totalIn`\n // (new uncached only). `formatTokenUsage` folds cache reads/creations\n // into the headline `in` number and surfaces the cached portion in parens.\n const usage = formatTokenUsage({\n totalIn: run.tokensIn ?? run.totalUsage?.input ?? 0,\n totalOut: run.tokensOut ?? run.totalUsage?.output ?? 0,\n totalCacheRead: run.totalUsage?.cacheRead ?? 0,\n totalCacheCreation: run.totalUsage?.cacheCreation ?? 0,\n })\n events.push({\n kind: 'spawn-end',\n text: `${tag} ${usage}`,\n childId: labelFor(runId),\n depth,\n })\n }\n\n const openRun = (runId: string, depth: number) => {\n if (depth <= 0)\n return\n const run = runById.get(runId)\n if (!run)\n return\n const taskPreview = run.prompt.length > 80 ? `${run.prompt.slice(0, 80)}…` : run.prompt\n events.push({\n kind: 'spawn-start',\n text: taskPreview,\n childId: labelFor(runId),\n depth,\n })\n }\n\n for (let i = 0; i < turns.length; i++) {\n const turn = turns[i]\n const run = turn.runId ? runById.get(turn.runId) : undefined\n const depth = run?.depth ?? 0\n const tag = depth > 0 && turn.runId ? { childId: labelFor(turn.runId), depth } : undefined\n\n // Run transitions — close the old child run, open the new one. Only\n // emit a top-level separator when we're staying at parent depth, so\n // the transcript reads as one tight subagent block without internal\n // turn-boundary lines.\n if (turn.runId !== lastRunId) {\n closeRun(lastRunId, lastDepth)\n if (depth === 0 && lastDepth === 0 && i > 0)\n events.push({ kind: 'separator', text: '' })\n if (turn.runId)\n openRun(turn.runId, depth)\n lastRunId = turn.runId\n lastDepth = depth\n }\n else if (i > 0 && depth === 0) {\n // Same parent run, new turn pair — keep the original separator.\n events.push({ kind: 'separator', text: '' })\n }\n\n if (turn.role === 'user') {\n for (const block of turn.content) {\n if (block.type === 'text' && block.text.trim()) {\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 if (depth === 0)\n events.push({ kind: 'info', text: `❯ ${block.text}` })\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 ...tag,\n })\n }\n }\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 === 'tool_call') {\n events.push({\n kind: 'tool',\n text: toolCallPreview(block.name, block.input),\n tool: block.name,\n ...tag,\n })\n }\n }\n }\n }\n\n // Flush a trailing open child run (e.g., last turn was a subagent's).\n closeRun(lastRunId, lastDepth)\n\n return events\n}\n\n/** Shared formatter for the `↳ name(args)` line shown on tool calls. */\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/** 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 return text.replace(/^Tokens:[^\\n]*\\n?/m, '')\n}\n\n/** Effective context size of the most recent assistant turn — drives the footer indicator. */\nexport function lastContextSizeFromTurns(turns: SessionTurn[]): number {\n for (let i = turns.length - 1; i >= 0; i--) {\n const turn = turns[i]\n if (turn.role === 'assistant' && turn.usage) {\n return (turn.usage.input ?? 0)\n + (turn.usage.cacheRead ?? 0)\n + (turn.usage.cacheCreation ?? 0)\n }\n }\n return 0\n}\n","import type { ReactNode } from 'react'\nimport type { Preset } from '../presets'\nimport type { SessionStore } from '../session'\nimport type { ProviderAuth, ProviderKey } from './auth'\nimport type { ModelInfo, ProviderDescriptor } from './providers'\nimport type { StateStoreApi, TuiState } from './store'\nimport type { Picked, Settings } from './types'\nimport { homedir } from 'node:os'\nimport { resolve } from 'node:path'\nimport { createContext, useContext } from 'react'\nimport BUILTIN_PRESET from '../presets/basic'\nimport { detectAuth } from './auth'\nimport { applyApiKeyEnv, credentialsPath } from './credentials'\nimport { BUILTIN_PROVIDERS, modelsForDescriptor } from './providers'\nimport { createStateStore, createTuiStore } from './store'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Re-exported for callers who want `ModelInfo` from the same module as `TuiOptions`. */\nexport type { ModelInfo }\n\n/**\n * Provider registry — a map keyed by `descriptor.key`. Passed verbatim to the\n * TUI; there is no implicit merge with built-ins. Hosts that want the four\n * default providers spread {@link BUILTIN_PROVIDERS}; hosts that only want\n * their own pass exactly their own.\n */\nexport type ProviderRegistry = Readonly<Record<string, ProviderDescriptor>>\n\n/**\n * Options accepted by `runTui()` and `resolveConfig()`. Every field is optional\n * — defaults boot a working chat against the built-in providers with sessions\n * stored under `~/.zidane/`.\n */\nexport interface TuiOptions {\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 * Agent preset (tools, system prompt, behavior). Default: `basic`.\n */\n preset?: Preset\n /**\n * Bring your own session store. Default: a SQLite store at\n * `<storageDir>/<prefix>/sessions.db`.\n */\n store?: SessionStore\n}\n\n// ---------------------------------------------------------------------------\n// Resolved config\n// ---------------------------------------------------------------------------\n\nexport interface ResolvedConfig {\n prefix: string\n storageDir: string\n paths: { dir: string, db: string, state: string }\n providers: ProviderRegistry\n preset: Preset\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\n/** Resolve user options into a fully-bound runtime config. Pure aside from disk reads. */\nexport function resolveConfig(options: TuiOptions = {}): ResolvedConfig {\n const prefix = options.prefix ?? process.env.ZIDANE_PREFIX ?? '.zidane'\n const storageDir = options.storageDir ?? process.env.ZIDANE_STORAGE_DIR ?? homedir()\n const dir = resolve(storageDir, prefix)\n const paths = {\n dir,\n db: resolve(dir, 'sessions.db'),\n state: resolve(dir, 'state.json'),\n }\n\n const store: SessionStore = options.store ?? createTuiStore(paths.db)\n const stateStore = createStateStore(paths.state)\n const initialState = stateStore.load()\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 const providers: ProviderRegistry = options.providers ?? BUILTIN_PROVIDERS\n const preset = options.preset ?? BUILTIN_PRESET\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 TUI manages, and injects stored API keys into env so\n // factories that resolve credentials eagerly (like `anthropic()`) see them.\n process.env.ZIDANE_CREDENTIALS_PATH = credentialsPath(dir)\n applyApiKeyEnv(dir, providers)\n\n const modelsFor = makeModelsResolver(providers)\n\n const resumeProvider = resolveResumeProvider(initialState, providers, dir)\n const initialPicked = resumeProvider ? pickInitial(resumeProvider, providers, initialState) : null\n\n return {\n prefix,\n storageDir,\n paths,\n providers,\n preset,\n store,\n stateStore,\n modelsFor,\n initialState,\n initialSettings: initialState.settings ?? {},\n resumeProvider,\n initialPicked,\n }\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 resolveResumeProvider(\n state: TuiState,\n providers: ProviderRegistry,\n storageDir: string,\n): ProviderAuth | null {\n if (!state.lastProvider)\n return null\n if (!providers[state.lastProvider])\n return null\n return detectAuth(storageDir, providers).find(p => p.key === state.lastProvider && p.available) ?? null\n}\n\nfunction pickInitial(auth: ProviderAuth, providers: ProviderRegistry, state: TuiState): 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 = remembered ?? descriptor.defaultModel ?? safeFactoryDefault(descriptor)\n return model ? { provider: auth, model } : null\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\n// ---------------------------------------------------------------------------\n// React-side: ConfigContext for deep consumers (modals, picker).\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","import type { ReactNode } from 'react'\nimport { useKeyboard, useTerminalDimensions } from '@opentui/react'\nimport { createContext, useContext, useMemo, useState } from 'react'\nimport { COLOR } from './theme'\n\n// ---------------------------------------------------------------------------\n// Modal layer\n//\n// A single, app-wide overlay slot. `ModalRoot` renders the active node on top\n// of its children via OpenTUI's `position: 'absolute'` so the host UI stays\n// mounted underneath (state preserved, no remount on close).\n//\n// Components anywhere in the tree call `useModal().open(...)` to push a node\n// and `.close()` to dismiss. Escape is captured by `Modal` itself when it's\n// open, so the App's top-level keyboard handler doesn't fight it.\n// ---------------------------------------------------------------------------\n\ninterface ModalApi {\n open: (node: ReactNode) => void\n close: () => void\n isOpen: boolean\n}\n\nconst ModalContext = createContext<ModalApi | null>(null)\n\nexport function ModalRoot({ children }: { children: ReactNode }) {\n const [active, setActive] = useState<ReactNode | null>(null)\n\n const api = useMemo<ModalApi>(() => ({\n open: node => setActive(node),\n close: () => setActive(null),\n get isOpen() { return active !== null },\n }), [active])\n\n return (\n <ModalContext.Provider value={api}>\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n {children}\n </box>\n {active && (\n // Transparent backdrop — host UI remains visible behind. The modal is\n // opaque (see <Modal/>) and sits centered; the dimming the eye perceives\n // comes from the modal's solid panel against the surrounding content,\n // not from a backdrop fill (OpenTUI cells have no alpha to blend with).\n <box\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 100,\n }}\n >\n {active}\n </box>\n )}\n </ModalContext.Provider>\n )\n}\n\nexport function useModal(): ModalApi {\n const ctx = useContext(ModalContext)\n if (!ctx)\n throw new Error('useModal must be used inside <ModalRoot>')\n return ctx\n}\n\n/**\n * Focus computed against the modal layer.\n *\n * Pass a component's preferred focus state and this returns `false` whenever a\n * modal is open — so focused inputs (textarea, selects) release their focus and\n * stop intercepting keys behind the overlay. Pair with `focusable={false}` on\n * \"passive\" focusables (scrollbox) so the renderer doesn't cycle focus into\n * them when the primary input blurs.\n */\nexport function useModalAwareFocus(preferred: boolean = true): boolean {\n const { isOpen } = useModal()\n return preferred && !isOpen\n}\n\n// ---------------------------------------------------------------------------\n// Modal — bordered content panel with built-in esc-to-close keyboard handling.\n//\n// Consumers pass their own content (forms, selects, lists). The modal owns:\n// - border + title + minimal padding\n// - sizing constraints (width caps at a comfortable column width)\n// - escape key → onClose\n// ---------------------------------------------------------------------------\n\nexport interface ModalProps {\n title?: string\n /** Called when the user presses esc. Defaults to `useModal().close()` if available. */\n onClose?: () => void\n children: ReactNode\n /** Preferred width in columns. Modal grows to this when the terminal allows. */\n maxWidth?: number\n /** Floor on the width, so content stays minimally legible. */\n minWidth?: number\n /** Columns of breathing room kept on each side of the modal. */\n horizontalMargin?: number\n}\n\n/**\n * Responsive modal — picks a width based on the live terminal size.\n *\n * - On a wide terminal, the modal grows to `maxWidth` so descriptions sit on\n * one line and don't wrap.\n * - On a narrow terminal, the modal shrinks down to `minWidth`, keeping a\n * small horizontal margin from the screen edges. Text inside wraps naturally.\n *\n * Uses `useTerminalDimensions()` so it reflows on `SIGWINCH` without remount.\n */\nexport function Modal({\n title,\n onClose,\n children,\n maxWidth = 92,\n minWidth = 44,\n horizontalMargin = 4,\n}: ModalProps) {\n const ctx = useContext(ModalContext)\n const dismiss = onClose ?? ctx?.close\n\n useKeyboard((key) => {\n if (key.name === 'escape')\n dismiss?.()\n })\n\n const { width: termWidth } = useTerminalDimensions()\n const width = Math.max(minWidth, Math.min(maxWidth, termWidth - horizontalMargin * 2))\n\n return (\n <box\n title={title ? ` ${title} ` : undefined}\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n backgroundColor: '#101010',\n paddingTop: 1,\n paddingBottom: 1,\n paddingLeft: 2,\n paddingRight: 2,\n width,\n flexDirection: 'column',\n gap: 1,\n }}\n >\n {children}\n </box>\n )\n}\n","import type { ModelInfo } from './providers'\nimport { useMemo } from 'react'\nimport { fmtTokens } from './format'\nimport { Modal } from './modal'\nimport { COLOR, SELECT_THEME } from './theme'\n\n/** Cap the visible scroll window so a 30-model list doesn't push the modal off-screen. */\nconst VISIBLE_ROW_CAP = 12\n\n/**\n * Modal that lists the available models for the current provider and lets\n * the user pick one. Options come from the active `ProviderDescriptor` —\n * either its declared `models` list or, when absent, pi-ai's built-in\n * registry looked up via `piProviderId`.\n *\n * Each row shows: `● selected · name (ctx N · reasoning · vision)`.\n */\nexport function ModelPickerModal({\n models,\n currentModelId,\n onPick,\n}: {\n models: readonly ModelInfo[]\n currentModelId: string\n onPick: (modelId: string) => void\n}) {\n // Hooks must always run in the same order — keep them above any conditional\n // return. The \"no models\" branch becomes JSX below.\n const initialIndex = useMemo(\n () => models.findIndex(m => m.id === currentModelId),\n [models, currentModelId],\n )\n\n const options = useMemo(\n () => models.map(m => ({\n name: `${m.id === currentModelId ? '● ' : ' '}${m.name ?? m.id}`,\n description: describeModel(m),\n value: m.id,\n })),\n [models, currentModelId],\n )\n\n if (models.length === 0)\n return <EmptyState />\n\n const visibleRows = Math.min(options.length, VISIBLE_ROW_CAP)\n const currentMissing = initialIndex < 0\n // `<select>` requires a non-negative index; clamp here and surface the\n // mismatch via a banner so the user knows their previously-saved model isn't\n // available in the current registry.\n const safeIndex = currentMissing ? 0 : initialIndex\n\n return (\n <Modal title=\"select model\">\n {currentMissing && (\n <text fg={COLOR.warn}>\n {`Current model \"${currentModelId}\" is not in this registry — pick one below to switch.`}\n </text>\n )}\n <select\n {...SELECT_THEME}\n focused\n options={options}\n wrapSelection\n selectedIndex={safeIndex}\n showScrollIndicator={options.length > visibleRows}\n style={{ height: visibleRows }}\n onSelect={(_idx, option) => {\n if (option)\n onPick(option.value as string)\n }}\n />\n <text fg={COLOR.mute}>\n <span fg={COLOR.warn}>↑↓</span>\n {' navigate · '}\n <span fg={COLOR.warn}>↵</span>\n {' select · '}\n <span fg={COLOR.warn}>esc</span>\n {' close'}\n </text>\n </Modal>\n )\n}\n\nfunction EmptyState() {\n return (\n <Modal title=\"select model\">\n <text fg={COLOR.dim}>No models available for this provider.</text>\n <text fg={COLOR.mute}>\n Set\n <span fg={COLOR.model}> models </span>\n on the provider descriptor (or a\n <span fg={COLOR.model}> piProviderId </span>\n that pi-ai recognizes) to populate this list.\n </text>\n </Modal>\n )\n}\n\n/** \"ctx 200k · reasoning · vision\" — compact per-model description. */\nfunction describeModel(m: ModelInfo): string {\n const parts: string[] = [`ctx ${fmtTokens(m.contextWindow)}`]\n if (m.reasoning)\n parts.push('reasoning')\n if (m.input?.includes('image'))\n parts.push('vision')\n return parts.join(' · ')\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, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\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\nfunction ensureDir(path: string): void {\n const dir = dirname(path)\n if (!existsSync(dir))\n mkdirSync(dir, { recursive: true })\n}\n\n/** Atomic write — tmp + rename so a crash never leaves a half-file. */\nexport function writeProjects(dataDir: string, file: ProjectsFile): void {\n const path = projectsFilePath(dataDir)\n ensureDir(path)\n const tmp = `${path}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify(file, null, 2))\n renameSync(tmp, path)\n}\n\n/**\n * Append `entry` to the safelist for `projectDir`, dedup-aware. Returns the\n * updated entry list (post-write) so callers can render it without re-reading.\n */\nexport function addToSafelist(\n dataDir: string,\n projectDir: string,\n entry: string,\n): readonly string[] {\n const file = readProjects(dataDir)\n const existing = file[projectDir]?.safelist ?? []\n if (existing.includes(entry))\n return existing\n const next = [...existing, entry]\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 — pure file/dir reads with no\n * side effects. Users who want to gate them 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]\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/** Extract the first whitespace-delimited token of the primary arg. */\nfunction primaryArgToken(input: Record<string, unknown>): string {\n return primaryArgValue(input).split(/\\s+/)[0] ?? ''\n}\n\n/**\n * Shell metacharacters that turn a single command into a compound: pipes,\n * sequencing, redirects, substitutions, line breaks, subshells. A `shell:git:*`\n * entry is meant to greenlight \"any git invocation\" — without this guard,\n * `git status && rm -rf /` would tokenize to `git` and pass the safelist\n * unchallenged. Reject any command that's not a single program call.\n *\n * The regex is intentionally generous: false positives (e.g. `echo \"hi & bye\"`)\n * just prompt the user again, which is the safe failure mode.\n */\nconst SHELL_COMPOUND_RE = /[;&|<>`$\\n\\r()]/\n\nfunction isCompoundShellCommand(command: string): boolean {\n return SHELL_COMPOUND_RE.test(command)\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. For `shell` this still requires\n * a single-program command (compound forms always prompt).\n * - `\"<tool>:<token>:*\"` — match when the primary arg's first token equals\n * `<token>`. For `shell`, also requires the command to be free of\n * metacharacters (`;`, `&&`, `||`, `|`, `$(`, backticks, `>`, `<`,\n * newlines, subshells) — otherwise a `shell:git:*` entry would silently\n * greenlight `git status && rm -rf /`.\n *\n * Entries that don't fit either shape are ignored (forward-compat for future\n * pattern syntax — readers shouldn't choke on entries written by a newer\n * version of the TUI).\n */\nexport function matchesSafelistEntry(\n entry: string,\n tool: string,\n input: Record<string, unknown>,\n): boolean {\n // Shell entries — bare or scoped — only match single-program commands. We\n // refuse to interpret the safelist for any command containing chaining or\n // redirection metacharacters.\n if (tool === 'shell') {\n const cmd = typeof input.command === 'string' ? input.command : ''\n if (isCompoundShellCommand(cmd))\n return false\n }\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/** True when a call matches ANY entry in the project's safelist (or is implicitly safe). */\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 return entries.some(e => matchesSafelistEntry(e, tool, input))\n}\n\n/**\n * Suggest the safelist entry to write when the user picks \"accept and\n * remember\" for a `{ tool, input }`. Heuristic:\n *\n * - `shell` → scope by first command token (`shell:git:*`).\n * - anything else → bare tool name (broad).\n *\n * Returning a string ensures the UI always has a concrete entry to display\n * as the button label.\n */\nexport function suggestSafelistEntry(\n tool: string,\n input: Record<string, unknown>,\n): string {\n if (tool === 'shell') {\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, useRef, useState } from 'react'\n\nexport type ApprovalDecision = 'accept-once' | 'accept-safelist' | 'deny'\n\nexport interface ApprovalRequest {\n id: string\n tool: string\n input: Record<string, unknown>\n resolve: (decision: ApprovalDecision) => void\n}\n\n/** Function signature consumed by `tool:gate` handlers + the child-tool wrap. */\nexport type RequestApproval = (tool: string, input: Record<string, unknown>) => 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\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 // `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) => new Promise<ApprovalDecision>((resolve) => {\n setQueue(prev => [...prev, { id: nextApprovalId(), tool, input, resolve }])\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 // 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 }\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 * 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 { OAuthCredentials, OAuthLoginCallbacks } from '@mariozechner/pi-ai/oauth'\nimport type { ProviderDescriptor } from './providers'\nimport { spawn } from 'node:child_process'\n\nexport function supportsOAuth(descriptor: ProviderDescriptor): boolean {\n return descriptor.oauthProvider !== undefined\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 /** Called when the provider needs a code entered manually (rare; only when callback server fails). */\n onCodeRequest?: () => Promise<string>\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 const callbacks: OAuthLoginCallbacks = {\n onAuth: (info) => {\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 onPrompt: async () => {\n if (!options.onCodeRequest)\n throw new Error('OAuth flow requires manual code input but no handler is wired.')\n return options.onCodeRequest()\n },\n onProgress: options.onProgress,\n signal: options.signal,\n }\n\n return descriptor.oauthProvider.login(callbacks)\n}\n\n/**\n * Best-effort cross-platform browser open. macOS uses `open`, Linux uses\n * `xdg-open`, Windows uses `start`. Failures are swallowed — the callback\n * server is already listening, and the URL is displayed in the TUI for\n * manual click.\n *\n * Uses `spawn` (not `exec`) so the URL is passed as an argv element rather\n * than interpolated into a shell command — no need to think about quoting\n * URLs that contain `&`, `?`, `\"` or other shell metacharacters.\n */\nfunction 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","import type { InputRenderable, KeyEvent, TextareaRenderable } from '@opentui/core'\nimport type { ReactNode } from 'react'\nimport type { ProviderAuth } from './auth'\nimport type { ProviderDescriptor } from './providers'\nimport type { ApprovalDecision, ApprovalRequest } from './safe-mode-context'\nimport type { SessionMeta, Settings, StreamEvent } from './types'\nimport { defaultTextareaKeyBindings } from '@opentui/core'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { detectAuth } from './auth'\nimport { Spinner, Transcript } from './components'\nimport { useConfig } from './config'\nimport { setProviderCredential } from './credentials'\nimport { ageString, shortId } from './format'\nimport { useModalAwareFocus } from './modal'\nimport { runOAuthLogin, supportsOAuth } from './oauth'\nimport { suggestSafelistEntry } from './safe-mode'\nimport { COLOR, SELECT_THEME } from './theme'\n\n/**\n * Build a key-binding set for the prompt textarea / API-key input. Strips the\n * default `return` action and reinstalls it with our preferred meaning, so the\n * binding wins regardless of modifier state. Pass `allowShiftReturnNewline`\n * to enable `shift+enter` → newline (multi-line input).\n */\nfunction makeSubmitBindings(allowShiftReturnNewline: boolean) {\n const base = defaultTextareaKeyBindings.filter(b => b.name !== 'return')\n return allowShiftReturnNewline\n ? [...base, { name: 'return', action: 'submit' as const }, { name: 'return', shift: true, action: 'newline' as const }]\n : [...base, { name: 'return', action: 'submit' as const }]\n}\n\nconst TEXTAREA_BINDINGS = makeSubmitBindings(true)\nconst API_KEY_INPUT_BINDINGS = makeSubmitBindings(false)\n\n/**\n * Look up a `{ key }` item by the value of a `<select>` option. Used by every\n * screen that mixes keyed-entry rows with sentinel \"+ new\" / \"← back\" rows —\n * sentinel handling stays explicit at the call site, this helper just trims\n * the boilerplate `.find(i => i.key === ...)` typing.\n */\nfunction findByKey<T extends { key: string }>(items: readonly T[], value: unknown): T | undefined {\n return typeof value === 'string' ? items.find(i => i.key === value) : undefined\n}\n\n// ---------------------------------------------------------------------------\n// AuthScreen — first-run provider picker.\n// ---------------------------------------------------------------------------\n\n/**\n * Sentinel value used by the picker's \"+ add / re-configure\" option. Lives\n * outside the provider key namespace (key strings are at least one char, no\n * leading `__`) so we can't collide with a real registry entry.\n */\nconst WIZARD_OPTION_VALUE = '__wizard__'\n\nexport function AuthScreen({ onPick }: { onPick: (p: ProviderAuth) => void }) {\n const config = useConfig()\n const { providers: registry } = config\n const focused = useModalAwareFocus()\n\n // `providers` is state, not a memo, so the wizard can imperatively\n // refresh it after writing credentials without us reaching for `useMemo`\n // dep tricks. The effect below seeds it on mount + whenever the registry\n // changes; `refresh` does the wizard-triggered re-detect.\n const [providers, setProviders] = useState<ProviderAuth[]>([])\n const refresh = useCallback(\n () => setProviders(detectAuth(config.paths.dir, registry)),\n [config.paths.dir, registry],\n )\n useEffect(() => { refresh() }, [refresh])\n\n // Explicit \"show the wizard\" flag — set when the user picks the\n // `+ add or re-configure` option, cleared when they save or cancel back\n // to the picker. Without it, the picker would be sticky once any provider\n // had credentials.\n const [forceWizard, setForceWizard] = useState(false)\n\n const available = useMemo(() => providers.filter(p => p.available), [providers])\n\n const onWizardDone = useCallback(() => {\n setForceWizard(false)\n refresh()\n }, [refresh])\n\n if (available.length === 0 || forceWizard) {\n // \"← back\" only makes sense when we got here from the picker (forceWizard)\n // AND there's still something to go back to. The `&&` guards against the\n // edge case where credentials disappear between renders.\n const canCancel = forceWizard && available.length > 0\n return (\n <SetupWizard\n registry={registry}\n dataDir={config.paths.dir}\n onConfigured={onWizardDone}\n onCancel={canCancel ? () => setForceWizard(false) : undefined}\n />\n )\n }\n\n const options = [\n ...available.map(p => ({\n name: p.label,\n description: p.methods.map(m => m.detail).join(' · '),\n value: p.key,\n })),\n {\n name: '+ add or re-configure a provider',\n description: 'launch the setup wizard',\n value: WIZARD_OPTION_VALUE,\n },\n ]\n\n return (\n <box\n title=\" pick a provider \"\n style={{\n border: true,\n borderColor: COLOR.border,\n padding: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n if (option.value === WIZARD_OPTION_VALUE) {\n setForceWizard(true)\n return\n }\n const provider = findByKey(available, option.value)\n if (provider)\n onPick(provider)\n }}\n style={{ flexGrow: 1 }}\n />\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SetupWizard — first-run credential setup. Three steps:\n// 1. Pick provider\n// 2. Pick auth method (apikey · OAuth where the descriptor supports it)\n// 3a. Enter API key → save to credentials.json → refresh AuthScreen\n// 3b. Open browser → OAuth callback server → save tokens → refresh\n//\n// Everything is driven by the host's {@link ProviderDescriptor}s — no\n// hardcoded provider metadata lives here.\n// ---------------------------------------------------------------------------\n\ntype WizardStep\n = | { kind: 'pick-provider' }\n | { kind: 'pick-method', descriptor: ProviderDescriptor }\n | { kind: 'enter-apikey', descriptor: ProviderDescriptor }\n | { kind: 'oauth-running', descriptor: ProviderDescriptor }\n\nfunction SetupWizard({\n registry,\n dataDir,\n onConfigured,\n onCancel,\n}: {\n registry: Readonly<Record<string, ProviderDescriptor>>\n dataDir: string\n onConfigured: () => void\n /**\n * Available only when the wizard was opened from the picker\n * (\"+ add or re-configure\"). On first launch (no providers yet) it's\n * undefined and the wizard has no cancel affordance — the user must set\n * up a provider to proceed.\n */\n onCancel?: () => void\n}) {\n const [step, setStep] = useState<WizardStep>({ kind: 'pick-provider' })\n const [error, setError] = useState<string | null>(null)\n\n const descriptors = useMemo(() => Object.values(registry), [registry])\n\n const onPickProvider = useCallback((descriptor: ProviderDescriptor) => {\n setError(null)\n setStep({ kind: 'pick-method', descriptor })\n }, [])\n\n const onPickMethod = useCallback((descriptor: ProviderDescriptor, method: 'apikey' | 'oauth') => {\n setError(null)\n if (method === 'apikey') {\n setStep({ kind: 'enter-apikey', descriptor })\n }\n else {\n setStep({ kind: 'oauth-running', descriptor })\n }\n }, [])\n\n const onApiKeySubmit = useCallback((descriptor: ProviderDescriptor, value: string) => {\n const trimmed = value.trim()\n if (!trimmed) {\n setError('API key cannot be empty.')\n return\n }\n try {\n setProviderCredential(dataDir, descriptor, { kind: 'apikey', value: trimmed })\n // Also expose via env so providers created later in this process pick it\n // up. Skip for descriptors with no env-var convention (custom providers\n // that resolve credentials some other way).\n if (descriptor.envKey)\n process.env[descriptor.envKey] = trimmed\n onConfigured()\n }\n catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n }\n }, [dataDir, onConfigured])\n\n if (descriptors.length === 0)\n return <EmptyRegistryNotice />\n\n if (step.kind === 'pick-provider') {\n return (\n <PickProviderStep\n descriptors={descriptors}\n error={error}\n onPick={onPickProvider}\n onCancel={onCancel}\n />\n )\n }\n\n if (step.kind === 'pick-method')\n return <PickMethodStep descriptor={step.descriptor} error={error} onPick={onPickMethod} />\n\n if (step.kind === 'enter-apikey')\n return <EnterApiKeyStep descriptor={step.descriptor} error={error} onSubmit={onApiKeySubmit} />\n\n return (\n <OAuthRunningStep\n descriptor={step.descriptor}\n dataDir={dataDir}\n onSuccess={onConfigured}\n onError={(msg) => {\n setError(msg)\n setStep({ kind: 'pick-method', descriptor: step.descriptor })\n }}\n />\n )\n}\n\n/**\n * Shared wrapper for every wizard step — same border + padding + flex layout\n * with a customizable title and accent color. Footnote slot at the bottom for\n * an error banner.\n */\nfunction WizardPanel({\n title,\n accent = COLOR.border,\n error,\n children,\n}: {\n title: string\n accent?: string\n error?: string | null\n children: ReactNode\n}) {\n return (\n <box\n title={title}\n style={{\n border: true,\n borderColor: accent,\n padding: 1,\n gap: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n {children}\n {error && <text fg={COLOR.error}>{error}</text>}\n </box>\n )\n}\n\n/** \"esc to exit\" footer hint shared by every wizard step that doesn't offer a \"← back\" affordance. */\nfunction WizardEscHint() {\n return <text fg={COLOR.dim}>esc to exit</text>\n}\n\nfunction EmptyRegistryNotice() {\n return (\n <WizardPanel title=\" no providers configured \" accent={COLOR.error}>\n <text fg={COLOR.error}>This TUI has no providers registered.</text>\n <text fg={COLOR.dim}>\n Pass providers via\n <span fg={COLOR.model}>{' runTui({ providers }) '}</span>\n or use the built-ins via\n <span fg={COLOR.model}>{' BUILTIN_PROVIDERS '}</span>\n .\n </text>\n </WizardPanel>\n )\n}\n\n/** Sentinel option value used for the wizard's \"← back to picker\" entry. */\nconst WIZARD_BACK_VALUE = '__back__'\n\nfunction PickProviderStep({\n descriptors,\n error,\n onPick,\n onCancel,\n}: {\n descriptors: readonly ProviderDescriptor[]\n error: string | null\n onPick: (descriptor: ProviderDescriptor) => void\n /** When set, adds a \"← back\" option that calls this to bail out without saving. */\n onCancel?: () => void\n}) {\n const focused = useModalAwareFocus()\n const options = [\n ...descriptors.map((d) => {\n const methods: string[] = supportsOAuth(d) ? ['API key', 'OAuth'] : ['API key']\n return { name: d.label, description: methods.join(' · '), value: d.key }\n }),\n ...(onCancel\n ? [{ name: '← back', description: 'return to the provider list', value: WIZARD_BACK_VALUE }]\n : []),\n ]\n\n // Different copy + title for the two entry paths: first-launch users need\n // the \"credentials live here\" hint; re-config users already know how the\n // TUI works and just want to pick a provider.\n const title = onCancel\n ? ' add or re-configure a provider '\n : ' welcome to zidane · pick a provider '\n\n return (\n <WizardPanel title={title} error={error}>\n {!onCancel && (\n <text fg={COLOR.dim}>\n No provider credentials yet. Pick a provider to configure — keys are stored in\n <span fg={COLOR.model}>{' ~/.zidane/credentials.json '}</span>\n (owner-only).\n </text>\n )}\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n if (option.value === WIZARD_BACK_VALUE) {\n onCancel?.()\n return\n }\n const descriptor = findByKey(descriptors, option.value)\n if (descriptor)\n onPick(descriptor)\n }}\n style={{ flexGrow: 1 }}\n />\n </WizardPanel>\n )\n}\n\nfunction PickMethodStep({\n descriptor,\n error,\n onPick,\n}: {\n descriptor: ProviderDescriptor\n error: string | null\n onPick: (descriptor: ProviderDescriptor, method: 'apikey' | 'oauth') => void\n}) {\n const focused = useModalAwareFocus()\n\n const options = useMemo(() => {\n interface MethodOption { name: string, description: string, value: 'apikey' | 'oauth' }\n const items: MethodOption[] = [\n { name: 'API key', description: `paste your ${descriptor.label} API key`, value: 'apikey' },\n ]\n if (supportsOAuth(descriptor)) {\n // OAuth hint comes from the descriptor — built-in `anthropicDescriptor`\n // sets it to \"Claude Pro/Max subscription\". Hosts adding OAuth on their\n // own providers set their own hint (or omit it).\n const hint = descriptor.oauthHint ? ` (${descriptor.oauthHint})` : ''\n items.push({\n name: 'OAuth',\n description: `browser-based sign-in${hint}`,\n value: 'oauth',\n })\n }\n return items\n }, [descriptor])\n\n return (\n <WizardPanel title={` configure ${descriptor.label} — pick auth method `} error={error}>\n <WizardEscHint />\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (option)\n onPick(descriptor, option.value)\n }}\n style={{ flexGrow: 1 }}\n />\n </WizardPanel>\n )\n}\n\nfunction EnterApiKeyStep({\n descriptor,\n error,\n onSubmit,\n}: {\n descriptor: ProviderDescriptor\n error: string | null\n onSubmit: (descriptor: ProviderDescriptor, value: string) => void\n}) {\n const focused = useModalAwareFocus()\n const inputRef = useRef<InputRenderable | null>(null)\n\n const submit = useCallback(() => {\n const value = inputRef.current?.value ?? ''\n onSubmit(descriptor, value)\n }, [descriptor, onSubmit])\n\n return (\n <WizardPanel title={` configure ${descriptor.label} — paste API key `} error={error}>\n <text fg={COLOR.dim}>\n Paste your\n {` ${descriptor.label} `}\n API key and press\n <span fg={COLOR.model}> enter </span>\n to save. Esc to exit.\n </text>\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n height: 3,\n }}\n >\n <input\n ref={inputRef}\n focused={focused}\n keyBindings={API_KEY_INPUT_BINDINGS}\n placeholder={descriptor.apiKeyPlaceholder ?? 'API key…'}\n onSubmit={submit}\n style={{ flexGrow: 1 }}\n />\n </box>\n </WizardPanel>\n )\n}\n\nfunction OAuthRunningStep({\n descriptor,\n dataDir,\n onSuccess,\n onError,\n}: {\n descriptor: ProviderDescriptor\n dataDir: string\n onSuccess: () => void\n onError: (msg: string) => void\n}) {\n const [url, setUrl] = useState<string | null>(null)\n const [status, setStatus] = useState('starting browser…')\n\n useEffect(() => {\n const ac = new AbortController()\n let cancelled = false\n\n void (async () => {\n try {\n const creds = await runOAuthLogin(descriptor, {\n onUrl: (loginUrl) => {\n if (cancelled)\n return\n setUrl(loginUrl)\n setStatus('waiting for browser callback…')\n },\n onProgress: (message) => {\n if (!cancelled)\n setStatus(message)\n },\n signal: ac.signal,\n })\n if (cancelled)\n return\n // `creds` carries `{ access, refresh, expires, ...extras }`. Spread\n // verbatim — the storage shape only adds our `kind` tag on top.\n setProviderCredential(dataDir, descriptor, { kind: 'oauth', ...creds })\n onSuccess()\n }\n catch (err) {\n if (cancelled)\n return\n const message = err instanceof Error ? err.message : String(err)\n onError(message)\n }\n })()\n\n return () => { cancelled = true; ac.abort() }\n }, [descriptor, dataDir, onSuccess, onError])\n\n return (\n <WizardPanel title={` configure ${descriptor.label} — OAuth `}>\n <WizardEscHint />\n <Spinner label={status} />\n {url && (\n <box style={{ flexDirection: 'column', gap: 0 }}>\n <text fg={COLOR.dim}>If the browser didn't open, visit:</text>\n <text fg={COLOR.model}>{url}</text>\n </box>\n )}\n </WizardPanel>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SessionsScreen — list of sessions with a New entry on top.\n// ---------------------------------------------------------------------------\n\nconst NEW_VALUE = '__new__'\n\nexport function SessionsScreen({\n sessions,\n currentId,\n onPick,\n onCreate,\n}: {\n sessions: SessionMeta[]\n currentId: string | null\n onPick: (id: string) => void\n onCreate: () => void\n}) {\n const focused = useModalAwareFocus()\n const options = useMemo(() => {\n const items: { name: string, description: string, value: string }[] = [\n { name: '+ new session', description: 'start fresh', value: NEW_VALUE },\n ]\n for (const s of sessions) {\n const marker = s.id === currentId ? '● ' : ' '\n const turnLabel = `${s.turnCount} turn${s.turnCount === 1 ? '' : 's'}`\n items.push({\n name: `${marker}${s.title}`,\n description: `#${shortId(s.id)} · ${turnLabel} · ${ageString(s.updatedAt)}`,\n value: s.id,\n })\n }\n return items\n }, [sessions, currentId])\n\n return (\n <box\n title=\" sessions \"\n style={{\n border: true,\n borderColor: COLOR.border,\n padding: 1,\n flexDirection: 'column',\n flexGrow: 1,\n }}\n >\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n wrapSelection\n onSelect={(_idx, option) => {\n if (!option)\n return\n if (option.value === NEW_VALUE)\n onCreate()\n else if (typeof option.value === 'string')\n onPick(option.value)\n }}\n style={{ flexGrow: 1 }}\n />\n </box>\n )\n}\n\n// ---------------------------------------------------------------------------\n// ChatScreen — transcript + auto-growing multi-line input + (running) spinner.\n// Enter inserts a newline; shift+enter submits; ctrl+↑↓ cycles prompt history.\n// ---------------------------------------------------------------------------\n\n/** Visible content lines: 1 minimum, 5 maximum (textarea scrolls past 5). */\nconst MIN_CONTENT_LINES = 1\nconst MAX_CONTENT_LINES = 5\n\nexport function ChatScreen({\n events,\n busy,\n settings,\n onSubmit,\n session,\n pending,\n onApproval,\n}: {\n events: StreamEvent[]\n busy: boolean\n settings: Settings\n onSubmit: (prompt: string) => void\n session: SessionMeta | null\n /** Head of the safe-mode approval queue, or `null` when nothing is pending. */\n pending: ApprovalRequest | null\n /** Resolve the active prompt with the user's pick. */\n onApproval: (decision: ApprovalDecision) => void\n}) {\n const title = useMemo(() => {\n if (!session)\n return ' untitled '\n const turns = `${session.turnCount} turn${session.turnCount === 1 ? '' : 's'}`\n return ` ${session.title} · #${shortId(session.id)} · ${turns} `\n }, [session])\n\n // Prior user prompts sourced from the transcript itself — kept in submit\n // order so the history-navigation feels like a regular shell.\n const userPrompts = useMemo(\n () => events.filter(e => e.kind === 'info').map(e => e.text.replace(/^❯ /, '')),\n [events],\n )\n\n return (\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n <box\n title={title}\n style={{\n border: true,\n borderColor: COLOR.border,\n flexGrow: 1,\n flexDirection: 'column',\n }}\n >\n <Transcript events={events} settings={settings} />\n </box>\n\n {/*\n Priority: pending approval wins over busy (a paused run still has\n `busy === true` while the gate awaits). The picker takes the prompt\n slot so the user can decide without losing context.\n */}\n {pending\n ? <ApprovalBlock request={pending} onPick={onApproval} />\n : busy\n ? <BusyBlock />\n : <PromptBlock userPrompts={userPrompts} onSubmit={onSubmit} />}\n </box>\n )\n}\n\n/** Max chars per scalar argument in the approval preview. */\nconst APPROVAL_ARG_MAX = 80\n\n/**\n * Render `{ path: 'x.ts', contents: 'long string' }` as\n * `path: \"x.ts\", contents: \"long string…\"` — readable, per-key, truncated\n * per value rather than dumping `JSON.stringify(input)` (which produces an\n * illegible 50KB blob for `write_file` etc.).\n */\nfunction formatApprovalArgs(input: Record<string, unknown>): string {\n const parts: string[] = []\n for (const [key, raw] of Object.entries(input)) {\n let value: string\n if (typeof raw === 'string') {\n const escaped = raw.replace(/\\n/g, '\\\\n')\n value = escaped.length > APPROVAL_ARG_MAX\n ? `\"${escaped.slice(0, APPROVAL_ARG_MAX)}…\"`\n : `\"${escaped}\"`\n }\n else {\n const json = JSON.stringify(raw)\n value = json.length > APPROVAL_ARG_MAX ? `${json.slice(0, APPROVAL_ARG_MAX)}…` : json\n }\n parts.push(`${key}: ${value}`)\n }\n return parts.join(', ')\n}\n\n// `description` is required by OpenTUI's `SelectOption` shape even when the\n// select is rendered with `showDescription={false}` — passing an empty string\n// keeps the type happy and is invisible at the render layer.\ninterface DecisionOption { name: string, description: string, value: ApprovalDecision }\n\n/**\n * Inline approval picker — replaces the chat input while a tool call is\n * pending. Three options:\n * - **accept once** — let this call execute, don't persist anything.\n * - **accept + remember** — execute + add a `projects.json` entry so the\n * same shape doesn't prompt again in this directory.\n * - **deny** — refuse the call. The model gets `Blocked: …` and adapts.\n *\n * Esc aborts the whole run via the parent keyboard handler; per-call\n * accept/deny only happens through the select below.\n *\n * Layout is fully pinned so the picker never overlaps with itself or the\n * transcript above:\n *\n * - Outer `<box>` has an explicit `height`. The slot below the transcript\n * adapts (the chat container is column-flex), so we control exactly how\n * many rows we occupy.\n * - Summary row is a `<box height: 1, overflow: hidden>` wrapping a\n * `<text wrapMode=\"none\">` — a 500-char tool-call preview can never\n * wrap to row 2 and push the select off-screen.\n * - `<select showDescription={false}>` keeps each option to exactly one\n * row. Hints live in the `name` string after a `·` separator. Without\n * this, the default `showDescription: true` makes every option take 2\n * rows, and a `height: options.length` select would overdraw into the\n * summary above (the original bug).\n */\nfunction ApprovalBlock({\n request,\n onPick,\n}: {\n request: ApprovalRequest\n onPick: (decision: ApprovalDecision) => void\n}) {\n const focused = useModalAwareFocus()\n\n const summary = useMemo(\n () => `${request.tool}(${formatApprovalArgs(request.input)})`,\n [request.tool, request.input],\n )\n\n const options = useMemo<DecisionOption[]>(() => {\n const safelistEntry = suggestSafelistEntry(request.tool, request.input)\n return [\n { name: 'accept once · allow this call only', description: '', value: 'accept-once' },\n { name: `accept + remember · add \"${safelistEntry}\" to projects.json`, description: '', value: 'accept-safelist' },\n { name: 'deny · refuse — the model will see Blocked', description: '', value: 'deny' },\n ]\n }, [request.tool, request.input])\n\n // border (2) + summary (1) + one row per option = total outer height.\n const height = 2 + 1 + options.length\n\n return (\n <box\n title=\" approve tool call · esc to abort run \"\n style={{\n border: true,\n borderColor: COLOR.warn,\n paddingLeft: 1,\n paddingRight: 1,\n paddingTop: 0,\n paddingBottom: 0,\n height,\n flexDirection: 'column',\n flexShrink: 0,\n }}\n >\n <box style={{ height: 1, overflow: 'hidden', flexShrink: 0 }}>\n <text fg={COLOR.model} wrapMode=\"none\">\n <span fg={COLOR.warn}>↳ </span>\n {summary}\n </text>\n </box>\n <select\n {...SELECT_THEME}\n focused={focused}\n options={options}\n showDescription={false}\n wrapSelection\n onSelect={(_idx, option) => {\n if (option)\n onPick(option.value)\n }}\n style={{ height: options.length, flexShrink: 0 }}\n />\n </box>\n )\n}\n\nfunction BusyBlock() {\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.warn,\n paddingLeft: 1,\n paddingRight: 1,\n height: 3,\n }}\n >\n <Spinner label=\"streaming response — esc to abort\" />\n </box>\n )\n}\n\nfunction PromptBlock({\n userPrompts,\n onSubmit,\n}: {\n userPrompts: string[]\n onSubmit: (prompt: string) => void\n}) {\n const focused = useModalAwareFocus()\n const textareaRef = useRef<TextareaRenderable | null>(null)\n /** Auto-grow: visible content rows the textarea currently occupies (clamped 1..5). */\n const [contentLines, setContentLines] = useState(MIN_CONTENT_LINES)\n /**\n * History navigation state. `null` = not navigating (textarea owns its content).\n * Once the user enters history (up at top), we snapshot the draft and cycle.\n */\n const historyRef = useRef<{ idx: number, draft: string } | null>(null)\n\n const syncLines = useCallback(() => {\n // `lineCount` is logical (newline-separated) line count — what the user\n // means by \"jumping lines\". Wrap-aware visual lines (`virtualLineCount`)\n // would also grow on long single lines, which isn't what we want.\n const lines = textareaRef.current?.lineCount ?? MIN_CONTENT_LINES\n setContentLines(Math.max(MIN_CONTENT_LINES, lines))\n }, [])\n\n const submit = useCallback(() => {\n const value = textareaRef.current?.plainText ?? ''\n if (!value.trim())\n return\n onSubmit(value)\n textareaRef.current?.clear()\n historyRef.current = null\n setContentLines(MIN_CONTENT_LINES)\n }, [onSubmit])\n\n const cycleHistory = useCallback((direction: -1 | 1) => {\n if (userPrompts.length === 0 || !textareaRef.current)\n return\n\n if (historyRef.current === null) {\n // Entering history mode: snapshot the draft so we can restore on the\n // way forward past the most-recent entry.\n historyRef.current = {\n idx: userPrompts.length,\n draft: textareaRef.current.plainText,\n }\n }\n\n const nextIdx = historyRef.current.idx + direction\n\n if (nextIdx < 0)\n return // already at oldest\n\n if (nextIdx >= userPrompts.length) {\n // Walked off the end → restore the original draft, leave history mode.\n textareaRef.current.setText(historyRef.current.draft)\n textareaRef.current.gotoBufferEnd()\n historyRef.current = null\n }\n else {\n textareaRef.current.setText(userPrompts[nextIdx])\n textareaRef.current.gotoBufferEnd()\n historyRef.current.idx = nextIdx\n }\n syncLines()\n }, [userPrompts, syncLines])\n\n /**\n * Up/Down at the buffer boundary cycles prompt history (fish/zsh pattern).\n * Mid-buffer up/down move the cursor normally — handled by the default\n * `move-up` / `move-down` actions in `TEXTAREA_BINDINGS`.\n */\n const onKeyDown = useCallback((event: KeyEvent) => {\n if (event.ctrl || event.shift || event.meta)\n return\n if (event.name !== 'up' && event.name !== 'down')\n return\n const buffer = textareaRef.current\n if (!buffer)\n return\n const cursorRow = buffer.logicalCursor.row\n if (event.name === 'up' && cursorRow === 0)\n cycleHistory(-1)\n else if (event.name === 'down' && cursorRow === buffer.lineCount - 1)\n cycleHistory(1)\n }, [cycleHistory])\n\n // Content area + 2 lines of border = total box height.\n const contentHeight = Math.min(MAX_CONTENT_LINES, contentLines)\n const boxHeight = contentHeight + 2\n\n return (\n <box\n style={{\n border: true,\n borderColor: COLOR.borderActive,\n paddingLeft: 1,\n paddingRight: 1,\n height: boxHeight,\n flexDirection: 'column',\n }}\n >\n <textarea\n ref={textareaRef}\n // Release focus while a modal is open so its `handleKeyPress` stops\n // routing keys behind the overlay (see `useModalAwareFocus`).\n focused={focused}\n keyBindings={TEXTAREA_BINDINGS}\n placeholder=\"Ask zidane… (enter = send · shift+enter = newline · ↑↓ at edges = history)\"\n // Fill the box's inner area so the textarea's own scroll engine kicks\n // in once content exceeds the visible rows (past 5 logical lines).\n style={{ flexGrow: 1, height: '100%' }}\n onSubmit={submit}\n onContentChange={syncLines}\n onKeyDown={onKeyDown}\n />\n </box>\n )\n}\n","import type { ReactNode } from 'react'\nimport type { Settings } from './types'\nimport { useKeyboard } from '@opentui/react'\nimport { createContext, useCallback, useContext, useMemo, useState } from 'react'\nimport { Modal } from './modal'\nimport { COLOR } from './theme'\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_SETTINGS: Settings = {\n showThinking: true,\n showToolCalls: true,\n showToolResults: true,\n safeMode: true,\n hideSubagentOutput: true,\n}\n\n// ---------------------------------------------------------------------------\n// SettingsProvider — single source of truth for the transcript filters.\n//\n// `onChange` is called with the latest settings whenever the user toggles a\n// row, so the host can persist them. Persistence pathing lives outside this\n// module to keep settings UI portable across hosting setups.\n// ---------------------------------------------------------------------------\n\ninterface SettingsContextValue {\n settings: Settings\n toggle: (key: keyof Settings) => 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 const [settings, setSettings] = useState<Settings>(initial)\n\n const toggle = useCallback((key: keyof Settings) => {\n setSettings((prev) => {\n const next = { ...prev, [key]: !prev[key] }\n onChange?.(next)\n return next\n })\n }, [onChange])\n\n const value = useMemo<SettingsContextValue>(() => ({ settings, toggle }), [settings, toggle])\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// SettingsModal — vertical list. Up/Down navigates, Enter/Space activates the\n// focused row (toggles a flag or invokes an action). Esc dismisses (handled\n// by the parent <Modal>).\n//\n// Two row kinds:\n// - `toggle` — a boolean stored under `settings[key]`\n// - `action` — a one-shot callback (e.g. \"Re-configure providers\")\n//\n// Hosts that mount the modal pass `actions` to wire in app-level actions\n// like re-opening the auth panel. Toggle rows are static — they always show\n// the three transcript filters.\n// ---------------------------------------------------------------------------\n\ninterface ToggleItem {\n kind: 'toggle'\n key: keyof Settings\n label: string\n description: string\n}\ninterface ActionItem {\n kind: 'action'\n id: string\n label: string\n description: string\n onPick: () => void\n}\ntype SettingsItem = ToggleItem | ActionItem\n\nconst TOGGLES: readonly ToggleItem[] = [\n { kind: 'toggle', key: 'safeMode', label: 'Safe mode', description: 'prompt before each tool call (unless safelisted)' },\n { kind: 'toggle', key: 'hideSubagentOutput', label: 'Hide subagent output', description: 'collapse subagent runs to start/done markers' },\n { kind: 'toggle', key: 'showThinking', label: 'Thinking blocks', description: 'agent reasoning shown inline' },\n { kind: 'toggle', key: 'showToolCalls', label: 'Tool calls', description: 'the ↳ name(args) lines' },\n { kind: 'toggle', key: 'showToolResults', label: 'Tool outputs', description: 'the ┃ result blocks under tool calls' },\n]\n\nexport interface SettingsActions {\n /**\n * Re-open the auth screen so the user can switch providers or run the\n * wizard for a new/existing one. Wiring this callback adds a\n * \"Re-configure providers\" row to the modal.\n */\n onReauth?: () => void\n}\n\nexport function SettingsModal({ actions }: { actions?: SettingsActions } = {}) {\n const { settings, toggle } = useSettings()\n const [cursor, setCursorRaw] = useState(0)\n\n // Build the row list dynamically so the modal only shows action rows whose\n // callbacks are wired. Toggle rows are always present.\n const items: readonly SettingsItem[] = useMemo(() => {\n const actionItems: ActionItem[] = []\n if (actions?.onReauth) {\n actionItems.push({\n kind: 'action',\n id: 'reauth',\n label: 'Authentication',\n description: 'switch provider, add another, or re-authenticate',\n onPick: actions.onReauth,\n })\n }\n return [...TOGGLES, ...actionItems]\n }, [actions])\n\n // The modal re-mounts on each open via `modal.open(<SettingsModal/>)`, but\n // if the host swaps `actions` while the modal is open (e.g. tears down the\n // re-auth wiring mid-session), the cursor might now point past the end of\n // the truncated row list. Clamp on read so we never index out of bounds.\n const safeCursor = Math.min(cursor, items.length - 1)\n const setCursor = useCallback(\n (update: (c: number) => number) =>\n setCursorRaw(prev => Math.min(Math.max(0, update(prev)), items.length - 1)),\n [items.length],\n )\n\n useKeyboard((key) => {\n if (key.name === 'up' || (key.ctrl && key.name === 'p')) {\n setCursor(c => c - 1)\n }\n else if (key.name === 'down' || (key.ctrl && key.name === 'n')) {\n setCursor(c => c + 1)\n }\n else if (key.name === 'return' || key.name === 'space') {\n const item = items[safeCursor]\n if (!item)\n return\n if (item.kind === 'toggle')\n toggle(item.key)\n else\n item.onPick()\n }\n })\n\n // Index where the action block starts — used to render a thin separator\n // between toggles and actions. Falls back to -1 when there are no actions.\n const firstActionIndex = items.findIndex(i => i.kind === 'action')\n\n return (\n <Modal title=\"settings\">\n <box style={{ flexDirection: 'column' }}>\n {items.map((item, i) => (\n <box key={item.kind === 'toggle' ? item.key : item.id} style={{ flexDirection: 'column' }}>\n {/*\n Full-width separator between the toggle block and the action\n block. `border={['top']}` draws only the top edge, which yoga\n stretches to the parent's content width — no need to measure\n the modal manually. Vertical margin gives the rule breathing\n room from the rows it separates.\n */}\n {i === firstActionIndex && i > 0 && (\n <box\n style={{\n border: ['top'],\n borderColor: COLOR.mute,\n height: 1,\n marginTop: 1,\n marginBottom: 1,\n }}\n />\n )}\n {item.kind === 'toggle'\n ? (\n <ToggleRow\n label={item.label}\n description={item.description}\n enabled={settings[item.key]}\n focused={i === safeCursor}\n />\n )\n : (\n <ActionRow\n label={item.label}\n description={item.description}\n focused={i === safeCursor}\n />\n )}\n </box>\n ))}\n </box>\n <text fg={COLOR.mute}>\n <span fg={COLOR.warn}>↑↓</span>\n {' navigate · '}\n <span fg={COLOR.warn}>↵</span>\n {firstActionIndex >= 0 ? ' toggle/select · ' : ' toggle · '}\n <span fg={COLOR.warn}>esc</span>\n {' close'}\n </text>\n </Modal>\n )\n}\n\n/**\n * Toggle row — `▶` marker · checkbox · label · description.\n *\n * Rendered as one `<text>` so OpenTUI's word-wrap handles narrow terminals\n * automatically: on wide screens everything sits on one line; on narrow ones\n * the trailing description wraps under the label without breaking the row.\n */\nfunction ToggleRow({\n label,\n description,\n enabled,\n focused,\n}: {\n label: string\n description: string\n enabled: boolean\n focused: boolean\n}) {\n return (\n <text fg={focused ? COLOR.brand : COLOR.dim}>\n <span fg={focused ? COLOR.brand : COLOR.mute}>{focused ? '▶ ' : ' '}</span>\n <span fg={enabled ? COLOR.accent : COLOR.mute}>{enabled ? '[✓] ' : '[ ] '}</span>\n <span fg={focused ? COLOR.brand : COLOR.dim}>{label}</span>\n <span fg={COLOR.mute}>{` ${description}`}</span>\n </text>\n )\n}\n\n/**\n * Action row — cursor marker · label · description · (focus-only) trailing arrow.\n *\n * The label sits in the same column as a toggle row's `[✓]` checkbox (right\n * after the 2-col cursor slot). The trailing `›` only renders when focused\n * so it reads as a \"this row runs\" affordance, not a static decoration on\n * every action.\n */\nfunction ActionRow({\n label,\n description,\n focused,\n}: {\n label: string\n description: string\n focused: boolean\n}) {\n return (\n <text fg={focused ? COLOR.brand : COLOR.dim}>\n <span fg={focused ? COLOR.brand : COLOR.mute}>{focused ? '▶ ' : ' '}</span>\n <span fg={focused ? COLOR.brand : COLOR.accent}>{label}</span>\n <span fg={COLOR.mute}>{` ${description}`}</span>\n {focused && <span fg={COLOR.brand}>{' ›'}</span>}\n </text>\n )\n}\n","import type { Dispatch, SetStateAction } from 'react'\nimport type { TurnUsage } from '../types'\nimport type { Owner, StreamEvent } from './types'\nimport { useCallback, useMemo, useRef } from 'react'\n\n/** Target one flush per ~33ms (one frame at the default renderer targetFps=30). */\nconst FLUSH_INTERVAL_MS = 33\n\nconst PARENT_OWNER: Owner = 'parent'\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\ninterface DeltaBucket {\n markdown: string\n thinking: string\n owner: Owner\n depth: number\n}\n\nfunction emptyBucket(owner: Owner, depth: number): DeltaBucket {\n return { markdown: '', thinking: '', owner, depth }\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)\n if (bucket.markdown)\n result = appendMarkdownDelta(result, bucket.markdown, bucket.owner, bucket.depth)\n return result\n}\n\nfunction appendMarkdownDelta(prev: StreamEvent[], delta: string, owner: Owner, depth: number): StreamEvent[] {\n const last = prev[prev.length - 1]\n if (last && last.kind === 'markdown' && last.streaming && ownerOf(last) === owner) {\n const next = prev.slice(0, -1)\n next.push({ ...last, text: last.text + delta })\n return next\n }\n return [\n ...prev,\n eventWithOwner({ kind: 'markdown', text: delta, streaming: true }, owner, depth),\n ]\n}\n\nfunction appendThinkingLines(prev: StreamEvent[], delta: string, owner: Owner, depth: number): StreamEvent[] {\n const lines = delta.split('\\n')\n const result = [...prev]\n const last = result[result.length - 1]\n\n if (last && last.kind === 'thinking' && ownerOf(last) === owner) {\n result[result.length - 1] = { ...last, text: last.text + lines[0] }\n }\n else if (lines[0] || lines.length > 1) {\n result.push(eventWithOwner({ kind: 'thinking', text: lines[0] }, owner, depth))\n }\n\n for (let i = 1; i < lines.length; i++)\n result.push(eventWithOwner({ kind: 'thinking', text: lines[i] }, owner, depth))\n\n return result\n}\n\nfunction ownerOf(evt: StreamEvent): Owner {\n return evt.childId ?? PARENT_OWNER\n}\n\nfunction eventWithOwner(evt: StreamEvent, owner: Owner, depth: number): StreamEvent {\n if (owner === PARENT_OWNER)\n return evt\n return { ...evt, childId: owner, depth }\n}\n\n/** Flip any trailing streaming markdown blocks (any owner) to finalized. */\nexport function finalizeStreamingMarkdown(events: StreamEvent[]): StreamEvent[] {\n let changed = false\n const next = events.map((e) => {\n if (e.kind === 'markdown' && e.streaming) {\n changed = true\n return { ...e, streaming: false }\n }\n return e\n })\n return changed ? next : events\n}\n\n/** Flip the trailing streaming markdown block for one specific owner. */\nexport function finalizeStreamingMarkdownForOwner(events: StreamEvent[], owner: Owner): StreamEvent[] {\n for (let i = events.length - 1; i >= 0; i--) {\n const e = events[i]\n if (e.kind !== 'markdown')\n continue\n if (!e.streaming)\n continue\n if (ownerOf(e) !== owner)\n continue\n const next = events.slice()\n next[i] = { ...e, streaming: false }\n return next\n }\n return events\n}\n\n// ---------------------------------------------------------------------------\n// Context-window math.\n// ---------------------------------------------------------------------------\n\n/**\n * Effective context size for a single turn.\n *\n * `usage.input` is misleading on its own when prompt caching is active: providers\n * (Anthropic, OpenRouter→Anthropic, Gemini) report `input` as the *new uncached*\n * tokens only — the cached prefix shows up in `cacheRead`, and newly-cached\n * tokens in `cacheCreation`. The model still saw all three buckets, so the real\n * context-window utilization is their sum.\n *\n * Non-caching providers leave `cacheRead`/`cacheCreation` undefined, so this\n * collapses to plain `input` for them.\n */\nexport function turnContextSize(usage: TurnUsage | undefined): number {\n if (!usage)\n return 0\n return (usage.input ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheCreation ?? 0)\n}\n\n// ---------------------------------------------------------------------------\n// useStreamBuffer — throttled stream-event accumulator with per-owner lanes.\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 flush at most once per ~33ms.\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\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\nexport function useStreamBuffer(setEvents: Dispatch<SetStateAction<StreamEvent[]>>): StreamBuffer {\n const bucketsRef = useRef<Map<Owner, DeltaBucket>>(new Map())\n const flushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const drainPendingInto = useCallback((updater?: (events: StreamEvent[]) => StreamEvent[]) => {\n if (flushTimerRef.current) {\n clearTimeout(flushTimerRef.current)\n flushTimerRef.current = null\n }\n const buckets = Array.from(bucketsRef.current.values())\n bucketsRef.current.clear()\n const hasDeltas = buckets.some(b => b.markdown.length > 0 || b.thinking.length > 0)\n if (!hasDeltas && !updater)\n return\n setEvents((prev) => {\n let merged = prev\n for (const bucket of buckets)\n merged = applyBucket(merged, bucket)\n return updater ? updater(merged) : merged\n })\n }, [setEvents])\n\n const flush = useCallback(() => drainPendingInto(), [drainPendingInto])\n\n const flushAndUpdate = useCallback(\n (update: (events: StreamEvent[]) => StreamEvent[]) => drainPendingInto(update),\n [drainPendingInto],\n )\n\n const appendImmediate = useCallback(\n (evt: StreamEvent) => drainPendingInto(events => [...events, evt]),\n [drainPendingInto],\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 if (!flushTimerRef.current)\n flushTimerRef.current = setTimeout(flush, FLUSH_INTERVAL_MS)\n }, [flush])\n\n const reset = useCallback(() => {\n if (flushTimerRef.current) {\n clearTimeout(flushTimerRef.current)\n flushTimerRef.current = null\n }\n bucketsRef.current.clear()\n }, [])\n\n // CRITICAL: stabilize the returned object. All five 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 }),\n [queueStreamDelta, appendImmediate, flushAndUpdate, flush, reset],\n )\n}\n","import type { Agent } from '../agent'\nimport type { Session } from '../session'\nimport type { ProviderAuth, ProviderKey } from './auth'\nimport type { ContextUsage, Hint } from './components'\nimport type { ResolvedConfig } from './config'\nimport type { Picked, Screen, SessionMeta, Settings, StreamEvent } from './types'\nimport { useKeyboard, useRenderer } from '@opentui/react'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createAgent } from '../agent'\nimport { createSession, loadSession } from '../session'\nimport { formatTokenUsage } from '../stats'\nimport { Footer } from './components'\nimport { ConfigProvider, useConfig } from './config'\nimport { ModalRoot, useModal } from './modal'\nimport { ModelPickerModal } from './model-picker'\nimport { getContextWindow } from './providers'\nimport { addToSafelist, getSafelist, isOnSafelist, suggestSafelistEntry } from './safe-mode'\nimport { SafeModeProvider, useSafeModeActions, useSafeModeQueue } from './safe-mode-context'\nimport { AuthScreen, ChatScreen, SessionsScreen } from './screens'\nimport { DEFAULT_SETTINGS, SettingsModal, SettingsProvider, useSettings } from './settings'\nimport {\n eventsFromTurns,\n lastContextSizeFromTurns,\n listSessionMeta,\n stripSpawnTokensLine,\n titleFromTurns,\n toolCallPreview,\n toolResultText,\n} from './store'\nimport {\n finalizeStreamingMarkdown,\n finalizeStreamingMarkdownForOwner,\n turnContextSize,\n useStreamBuffer,\n} from './streaming'\n\n/**\n * Surface failures that are normally silenced (teardown / save) when the\n * `ZIDANE_DEBUG` env var is set. Logging via `console.error` would otherwise\n * trigger OpenTUI's error console overlay and clutter the UI for end users.\n */\nconst debugLog: (label: string, err: unknown) => void\n = process.env.ZIDANE_DEBUG\n ? (label, err) => console.error(`[zidane/tui] ${label}:`, err)\n : () => {}\n\n/**\n * Top-level TUI component. Accepts a fully-resolved `ResolvedConfig` and wires\n * everything (settings, modal layer, screens, footer) underneath it.\n *\n * Hosts can either drive this via `runTui()` for the standard bootstrap or\n * mount `<App config={resolveConfig(...)} />` themselves inside a renderer\n * they already own.\n */\nexport function App({ config }: { config: ResolvedConfig }) {\n const initialSettings = useMemo(\n () => ({ ...DEFAULT_SETTINGS, ...config.initialSettings }),\n [config.initialSettings],\n )\n\n const onSettingsChange = useCallback(\n (settings: Settings) => config.stateStore.save({ ...config.stateStore.load(), settings }),\n [config.stateStore],\n )\n\n return (\n <ConfigProvider config={config}>\n <SettingsProvider initial={initialSettings} onChange={onSettingsChange}>\n <SafeModeProvider>\n <ModalRoot>\n <AppShell />\n </ModalRoot>\n </SafeModeProvider>\n </SettingsProvider>\n </ConfigProvider>\n )\n}\n\nfunction AppShell() {\n const renderer = useRenderer()\n const modal = useModal()\n const config = useConfig()\n const { settings } = useSettings()\n // `useSafeModeQueue` re-renders on every push/pop; `useSafeModeActions`\n // hands back a stable object, so anything memoizing over the actions\n // (gate handlers, abort callback) keeps a single identity across queue\n // churn. See safe-mode-context.tsx.\n const queue = useSafeModeQueue()\n const { requestApproval, resolveHead, denyAll } = useSafeModeActions()\n\n // Destructure stable identities up-front so callbacks/effects can depend on\n // them directly (avoids the \"whole config in deps\" footgun — see S1 review).\n const {\n providers: providerRegistry,\n preset,\n store,\n stateStore,\n modelsFor,\n resumeProvider,\n initialPicked,\n initialState,\n } = config\n const lastResumedSessionId = initialState.lastSessionId\n const dataDir = config.paths.dir\n\n // -------------------------------------------------------------------------\n // Safe-mode plumbing.\n //\n // Hook handlers run inside the agent loop — they need stable references to\n // the latest \"is safe-mode on?\" flag and to the cwd (the project key in\n // `projects.json`). Refs decouple registration from React re-renders so we\n // don't re-register on every settings flip.\n // -------------------------------------------------------------------------\n\n const safeModeEnabledRef = useRef(settings.safeMode)\n useEffect(() => { safeModeEnabledRef.current = settings.safeMode }, [settings.safeMode])\n\n // `process.cwd()` is the project key in `projects.json`. Captured once\n // per AppShell mount with `useState` lazy init — `useMemo([])` would\n // technically allow React to recompute under memory pressure, `useState`\n // is a hard guarantee.\n const [projectDir] = useState(() => process.cwd())\n\n // In-memory cache for the project's safelist so `gateDecision` doesn't\n // re-read `projects.json` from disk on every tool call (matters when a\n // parallel batch fires dozens of gate hooks in the same microtask). The\n // ref is seeded lazily and explicitly refreshed when we persist an entry\n // — the TUI is the only writer, so external invalidation isn't needed.\n const safelistRef = useRef<readonly string[] | null>(null)\n const readSafelist = useCallback((): readonly string[] => {\n if (safelistRef.current === null)\n safelistRef.current = getSafelist(dataDir, projectDir)\n return safelistRef.current\n }, [dataDir, projectDir])\n // Drop the cache on a project switch (only happens across mounts today,\n // but cheap insurance for future code that swaps `projectDir`).\n useEffect(() => { safelistRef.current = null }, [dataDir, projectDir])\n\n /**\n * Single source of truth for \"should this call execute?\". Returns true to\n * let the call through, false to refuse it. Handles three short-circuits:\n *\n * - Safe-mode globally off → always allow.\n * - Call covered by the project safelist or the implicit read-only set\n * → always allow without prompting.\n * - Otherwise → prompt the user and act on their decision (including\n * persisting a new safelist entry on \"accept + safelist\").\n *\n * Wired into the parent agent via `tool:gate` / `mcp:tool:gate`, and to\n * every subagent (transitively, for free) via `child:tool:gate` /\n * `child:mcp:tool:gate` — see the bubble in `src/tools/spawn.ts`.\n */\n const gateDecision = useCallback(\n async (tool: string, input: Record<string, unknown>): Promise<boolean> => {\n if (!safeModeEnabledRef.current)\n return true\n if (isOnSafelist(readSafelist(), tool, input))\n return true\n const decision = await requestApproval(tool, input)\n if (decision === 'deny')\n return false\n if (decision === 'accept-safelist') {\n const entry = suggestSafelistEntry(tool, input)\n addToSafelist(dataDir, projectDir, entry)\n safelistRef.current = null // force re-read on next gate\n }\n return true\n },\n [dataDir, projectDir, requestApproval, readSafelist],\n )\n\n // Initial screen + picked seed from the resolved config so a returning user\n // lands straight on chat (or sessions) without an auth-screen flash.\n const [screen, setScreen] = useState<Screen>(() => {\n if (!resumeProvider)\n return 'auth'\n return lastResumedSessionId ? 'chat' : 'sessions'\n })\n const [picked, setPicked] = useState<Picked | null>(() => initialPicked)\n const [sessions, setSessions] = useState<SessionMeta[]>([])\n const [currentSession, setCurrentSession] = useState<SessionMeta | null>(null)\n const [events, setEvents] = useState<StreamEvent[]>([])\n const [busy, setBusy] = useState(false)\n /** Token count from the most recent assistant turn (caching-aware). */\n const [lastInputTokens, setLastInputTokens] = useState(0)\n\n const agentRef = useRef<Agent | null>(null)\n const sessionRef = useRef<Session | null>(null)\n\n const stream = useStreamBuffer(setEvents)\n\n const makePicked = useCallback((provider: ProviderAuth, modelId?: string): Picked | null => {\n // If the host narrowed the provider registry we may have a detected auth\n // with no descriptor to back it. Return null and let the caller bail; the\n // AuthScreen already filters these out, so this is a belt-and-braces guard.\n const descriptor = providerRegistry[provider.key]\n if (!descriptor)\n return null\n const remembered = initialState.lastModelByProvider?.[provider.key]\n // Prefer remembered → descriptor default → factory probe. Factories may\n // throw on missing credentials at construction time; by the time we get\n // here we've already detected auth, so the probe is safe, but the\n // descriptor default lets us avoid constructing in the common case.\n const model = modelId ?? remembered ?? descriptor.defaultModel ?? descriptor.factory().meta.defaultModel\n return { provider, model }\n }, [providerRegistry, initialState])\n\n // -------------------------------------------------------------------------\n // Agent lifecycle: build a fresh Agent bound to the active Session and wire\n // streaming hooks. Re-created on every session switch so context never bleeds.\n // -------------------------------------------------------------------------\n\n const buildAgent = useCallback((session: Session, key: ProviderKey): Agent => {\n const descriptor = providerRegistry[key]\n if (!descriptor)\n throw new Error(`No provider registered for key \"${key}\"`)\n const agent = createAgent({ ...preset, provider: descriptor.factory(), session })\n\n // Safe-mode gates -----------------------------------------------------\n // Four hooks: native vs MCP, parent vs subagent. The `child:*:gate`\n // events bubble from every subagent's hook bus via `bubbleHooks` (see\n // `src/tools/spawn.ts`) with the **same** gate ctx, so writes to\n // `block` / `reason` here propagate straight back to the child's loop.\n // A denied call never reaches `tool:before`, so it doesn't pollute the\n // transcript — the model receives `Blocked: …` and adapts.\n const applyGate = async (\n name: string,\n input: Record<string, unknown>,\n ctx: { block: boolean, reason: string },\n ): Promise<void> => {\n if (ctx.block) // already refused by a higher-priority gate (skills/budgets/dedup)\n return\n if (!(await gateDecision(name, input))) {\n ctx.block = true\n ctx.reason = 'User denied this tool call'\n }\n }\n agent.hooks.hook('tool:gate', ctx => applyGate(ctx.name, ctx.input, ctx))\n agent.hooks.hook('child:tool:gate', ctx => applyGate(ctx.name, ctx.input, ctx))\n agent.hooks.hook('mcp:tool:gate', ctx => applyGate(ctx.displayName, ctx.input, ctx))\n agent.hooks.hook('child:mcp:tool:gate', ctx => applyGate(ctx.displayName, ctx.input, ctx))\n\n // Parent streams ------------------------------------------------------\n agent.hooks.hook('stream:thinking', ({ delta }) => stream.queueStreamDelta('thinking', delta))\n agent.hooks.hook('stream:text', ({ delta }) => stream.queueStreamDelta('markdown', delta))\n agent.hooks.hook('tool:before', ({ name, input }) => {\n stream.appendImmediate({ kind: 'tool', text: toolCallPreview(name, input), tool: name })\n })\n agent.hooks.hook('tool:after', ({ name, result }) => {\n // Spawn tool-results carry a `Tokens: …` summary that's already shown\n // by the spawn-end marker right above the tool-result — strip it from\n // the displayed string to avoid the duplicate / format-drift block.\n const raw = toolResultText(result)\n const text = name === 'spawn' ? stripSpawnTokensLine(raw) : raw\n stream.appendImmediate({ kind: 'tool-result', text, tool: name })\n })\n agent.hooks.hook('mcp:tool:after', ({ displayName, result }) => {\n stream.appendImmediate({ kind: 'tool-result', text: toolResultText(result), tool: displayName })\n })\n agent.hooks.hook('turn:after', ({ usage }) => {\n if (usage)\n setLastInputTokens(turnContextSize(usage))\n stream.flushAndUpdate(finalizeStreamingMarkdown)\n })\n\n // Subagent streams ----------------------------------------------------\n agent.hooks.hook('spawn:before', ({ id, task, depth }) => {\n const taskPreview = task.length > 80 ? `${task.slice(0, 80)}…` : task\n stream.appendImmediate({\n kind: 'spawn-start',\n text: taskPreview,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('spawn:complete', ({ id, depth, status, stats }) => {\n const tag = status === 'aborted' ? 'aborted' : status === 'error' ? 'error' : 'done'\n stream.appendImmediate({\n kind: 'spawn-end',\n text: `${tag} ${formatTokenUsage(stats)}`,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('spawn:error', ({ id, depth, error }) => {\n stream.appendImmediate({\n kind: 'error',\n text: `[${id}] ${error.message}`,\n childId: id,\n depth: depth ?? 1,\n })\n })\n agent.hooks.hook('child:stream:thinking', ({ delta, childId, depth }) => {\n stream.queueStreamDelta('thinking', delta, { childId, depth })\n })\n agent.hooks.hook('child:stream:text', ({ delta, childId, depth }) => {\n stream.queueStreamDelta('markdown', delta, { childId, depth })\n })\n agent.hooks.hook('child:tool:before', ({ name, input, childId, depth }) => {\n stream.appendImmediate({\n kind: 'tool',\n text: toolCallPreview(name, input),\n tool: name,\n childId,\n depth,\n })\n })\n agent.hooks.hook('child:tool:after', ({ name, result, childId, depth }) => {\n stream.appendImmediate({\n kind: 'tool-result',\n text: toolResultText(result),\n tool: name,\n childId,\n depth,\n })\n })\n agent.hooks.hook('child:stream:end', ({ childId }) => {\n // Finalize the child's trailing markdown block so the next child event\n // (tool call, spawn-end) doesn't keep appending into it.\n stream.flushAndUpdate(prev => finalizeStreamingMarkdownForOwner(prev, childId))\n })\n\n return agent\n }, [providerRegistry, preset, stream, gateDecision])\n\n // -------------------------------------------------------------------------\n // Session activation — load (or create) a Session, rebuild the agent, replay\n // turns into the transcript, and persist the resume hint.\n // -------------------------------------------------------------------------\n\n const refreshSessions = useCallback(async () => {\n const list = await listSessionMeta(store)\n setSessions(list)\n return list\n }, [store])\n\n const teardown = useCallback(async () => {\n stream.reset()\n await agentRef.current?.destroy().catch(err => debugLog('agent.destroy failed', err))\n agentRef.current = null\n sessionRef.current = null\n }, [stream])\n\n const activateSession = useCallback(async (id: string | null, key: ProviderKey) => {\n await teardown()\n\n const loaded = id ? await loadSession(store, id) : null\n const session = loaded ?? await createSession({ store, ...(id ? { id } : {}) })\n\n sessionRef.current = session\n agentRef.current = buildAgent(session, key)\n\n setEvents(eventsFromTurns(session.turns, session.runs))\n setLastInputTokens(lastContextSizeFromTurns(session.turns))\n setCurrentSession({\n id: session.id,\n title: titleFromTurns(session.turns) ?? 'untitled',\n turnCount: session.turns.length,\n updatedAt: Date.now(),\n })\n setScreen('chat')\n stateStore.save({\n ...stateStore.load(),\n lastProvider: key,\n lastSessionId: session.id,\n })\n }, [teardown, buildAgent, store, stateStore])\n\n // -------------------------------------------------------------------------\n // Resume on launch — when a previous provider was remembered, jump straight\n // into the last session (or the sessions list if it's been deleted).\n // -------------------------------------------------------------------------\n\n // Narrow the deps to exactly what the effect reads. Depending on `config`\n // as a whole would re-fire if any future refactor made the resolved config\n // non-stable — and re-firing here re-runs `activateSession`, destroying and\n // rebuilding the agent in a loop (this was the original leak).\n useEffect(() => {\n if (!resumeProvider)\n return\n let cancelled = false\n void (async () => {\n if (lastResumedSessionId) {\n const data = await store.load(lastResumedSessionId)\n if (cancelled)\n return\n if (data) {\n await activateSession(lastResumedSessionId, resumeProvider.key)\n return\n }\n }\n const list = await refreshSessions()\n if (cancelled)\n return\n if (list.length === 0)\n await activateSession(null, resumeProvider.key)\n else\n setScreen('sessions')\n })()\n return () => { cancelled = true }\n }, [activateSession, refreshSessions, resumeProvider, lastResumedSessionId, store])\n\n // -------------------------------------------------------------------------\n // Screen actions.\n // -------------------------------------------------------------------------\n\n const onPickProvider = useCallback(async (p: ProviderAuth) => {\n const next = makePicked(p)\n if (!next)\n return // provider isn't in the registry — guarded by AuthScreen, defensive here\n setPicked(next)\n stateStore.save({ ...stateStore.load(), lastProvider: p.key })\n const list = await refreshSessions()\n if (list.length === 0)\n await activateSession(null, p.key)\n else\n setScreen('sessions')\n }, [refreshSessions, activateSession, makePicked, stateStore])\n\n const onCreateSession = useCallback(async () => {\n if (picked)\n await activateSession(null, picked.provider.key)\n }, [picked, activateSession])\n\n const onSwitchSession = useCallback(async (id: string) => {\n if (picked)\n await activateSession(id, picked.provider.key)\n }, [picked, activateSession])\n\n const onOpenSessions = useCallback(async () => {\n await refreshSessions()\n setScreen('sessions')\n }, [refreshSessions])\n\n const onAbort = useCallback(() => {\n // Flush any pending approvals so the run can unwind cleanly; otherwise\n // `agent.abort()` only flips the run's signal and our gate handlers\n // would still hang in `await requestApproval(...)`.\n denyAll()\n agentRef.current?.abort()\n }, [denyAll])\n\n const onPickModel = useCallback((modelId: string) => {\n setPicked((prev) => {\n if (!prev)\n return prev\n // Remember per-provider so the next launch resumes the right model.\n const prior = stateStore.load()\n stateStore.save({\n ...prior,\n lastModelByProvider: { ...prior.lastModelByProvider, [prev.provider.key]: modelId },\n })\n return { ...prev, model: modelId }\n })\n modal.close()\n }, [modal, stateStore])\n\n const onSubmitPrompt = useCallback(async (prompt: string) => {\n const agent = agentRef.current\n const session = sessionRef.current\n if (!agent || !session || !picked || !prompt.trim())\n return\n\n if (events.length > 0)\n stream.appendImmediate({ kind: 'separator', text: '' })\n stream.appendImmediate({ kind: 'info', text: `❯ ${prompt}` })\n setBusy(true)\n\n try {\n await agent.run({ model: picked.model, prompt })\n await session.save().catch(err => debugLog('session.save failed', err))\n setCurrentSession(prev => prev\n ? {\n ...prev,\n title: titleFromTurns(session.turns) ?? prev.title,\n turnCount: session.turns.length,\n updatedAt: Date.now(),\n }\n : prev)\n }\n catch (err) {\n stream.appendImmediate({ kind: 'error', text: err instanceof Error ? err.message : String(err) })\n }\n finally {\n stream.flushAndUpdate(finalizeStreamingMarkdown)\n setBusy(false)\n }\n }, [picked, events.length, stream])\n\n // -------------------------------------------------------------------------\n // Top-level keyboard. Each screen handles its own primary action; this only\n // routes esc + the modal shortcuts.\n //\n // The callback is recreated on every render, but OpenTUI's `useKeyboard`\n // wraps it with `useEffectEvent` internally — registration only happens once\n // and the handler always sees the latest closure. No leak.\n // -------------------------------------------------------------------------\n\n // Settings → \"Re-configure providers\" jumps back to the auth screen. We\n // capture this as a callback so the modal closes cleanly before the screen\n // transition (avoiding a brief flash of the modal over the new screen).\n const onReauth = useCallback(() => {\n modal.close()\n setScreen('auth')\n }, [modal])\n\n // Head of the safe-mode approval queue. `null` means \"nothing pending\"\n // and the chat screen shows the prompt input as usual. Defined here so the\n // keyboard handler and the footer hints both see it.\n const pendingApproval = queue[0] ?? null\n\n useKeyboard((key) => {\n if (modal.isOpen)\n return\n if (key.ctrl && key.name === ',' && screen !== 'auth') {\n modal.open(<SettingsModal actions={{ onReauth }} />)\n return\n }\n if (key.ctrl && key.name === 'm' && screen === 'chat' && picked && !busy) {\n modal.open(\n <ModelPickerModal\n models={modelsFor(picked.provider.key)}\n currentModelId={picked.model}\n onPick={onPickModel}\n />,\n )\n return\n }\n if (key.name !== 'escape')\n return\n // Esc always aborts the in-flight run (whether or not a prompt is\n // pending) — `onAbort` denies every queued approval and flips the\n // agent's signal. Per-call accept/deny lives in the approval picker\n // itself; this keeps \"esc = stop everything\" as a single rule.\n if (busy || pendingApproval)\n return onAbort()\n if (screen === 'chat')\n return onOpenSessions()\n if (screen === 'sessions') {\n if (currentSession)\n setScreen('chat')\n else\n renderer.destroy()\n return\n }\n // auth screen — if the user got here from settings (picked already\n // exists), bounce them back to wherever they were. Only exit on a true\n // first-launch (no provider picked yet).\n if (picked) {\n setScreen(currentSession ? 'chat' : 'sessions')\n return\n }\n renderer.destroy()\n })\n\n // -------------------------------------------------------------------------\n // Derived footer state.\n // -------------------------------------------------------------------------\n\n const hints: Hint[] = useMemo(\n () => buildHints(screen, busy, !!pendingApproval, currentSession),\n [screen, busy, pendingApproval, currentSession],\n )\n\n const contextUsage: ContextUsage | null = useMemo(() => {\n if (screen !== 'chat' || !picked)\n return null\n const descriptor = providerRegistry[picked.provider.key]\n if (!descriptor)\n return null\n // `getContextWindow` already checks `descriptor.models` first and falls\n // back to pi-ai's registry — one call covers both cases.\n const max = getContextWindow(descriptor, picked.model)\n return max ? { used: lastInputTokens, max } : null\n }, [screen, picked, lastInputTokens, providerRegistry])\n\n // Drop the agent + clear timers when the app unmounts.\n useEffect(() => () => { void teardown() }, [teardown])\n\n return (\n <box style={{ flexDirection: 'column', flexGrow: 1 }}>\n <box style={{ flexDirection: 'column', flexGrow: 1, paddingLeft: 1, paddingRight: 1 }}>\n {screen === 'auth' && <AuthScreen onPick={onPickProvider} />}\n {screen === 'sessions' && (\n <SessionsScreen\n sessions={sessions}\n currentId={currentSession?.id ?? null}\n onPick={onSwitchSession}\n onCreate={onCreateSession}\n />\n )}\n {screen === 'chat' && (\n <ChatScreen\n events={events}\n busy={busy}\n settings={settings}\n onSubmit={onSubmitPrompt}\n session={currentSession}\n pending={pendingApproval}\n onApproval={resolveHead}\n />\n )}\n </box>\n <Footer hints={hints} picked={picked} context={contextUsage} />\n </box>\n )\n}\n\nfunction buildHints(\n screen: Screen,\n busy: boolean,\n pending: boolean,\n currentSession: SessionMeta | null,\n): Hint[] {\n if (pending)\n return [{ key: '↑↓', label: 'navigate' }, { key: '↵', label: 'select' }, { key: 'esc', label: 'abort run' }]\n if (busy)\n return [{ key: 'esc', label: 'abort' }]\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: 'ctrl+,', label: 'settings' },\n { key: 'esc', label: currentSession ? 'back' : 'exit' },\n ]\n }\n return [\n { key: '↵', label: 'send' },\n { key: 'ctrl+m', label: 'model' },\n { key: 'ctrl+,', label: 'settings' },\n { key: 'esc', label: 'sessions' },\n ]\n}\n","import type { TuiOptions } from './config'\nimport { createCliRenderer } from '@opentui/core'\nimport { createRoot } from '@opentui/react'\nimport { heal, init as initMd4x } from 'md4x/wasm'\nimport { App } from './app'\nimport { resolveConfig } from './config'\n\n// ---------------------------------------------------------------------------\n// Public API\n//\n// `runTui(options)` is the one-shot launcher. For consumers who want to embed\n// the App inside a renderer they already control, every building block below\n// is also exported individually.\n// ---------------------------------------------------------------------------\n\n/**\n * Tracks whether `runTui` has been invoked in this process. `createCliRenderer`\n * installs signal handlers + hijacks stdin; calling it a second time produces\n * undefined behavior. We bail loudly instead.\n */\nlet runTuiInvoked = false\n\n/**\n * Boot a full chat TUI with sensible defaults. **Does not return** under\n * normal use — it terminates the host process via `process.exit(0)` once\n * the user dismisses the renderer (Ctrl+C / Esc on the auth screen / a\n * non-zero exit code is mapped via the `catch` path below).\n *\n * **One-shot:** this function may only be invoked once per process. The\n * underlying OpenTUI renderer wires up global terminal state on init.\n *\n * **Why it exits the process:** after the renderer's `destroy()` restores\n * the terminal, React's reconciler and OpenTUI's internal listeners can\n * keep the Node/Bun event loop open indefinitely — the script appears to\n * hang in `bun run`, and under `bun --watch run` the watcher waits forever\n * for the child to exit. Forcing a clean exit here is the contract that\n * makes `runTui` a true one-shot launcher.\n *\n * Hosts that need post-renderer cleanup should mount `<App config={...} />`\n * against their own `createCliRenderer()` instead of calling `runTui()`.\n *\n * Env-var overrides (handy when launching from CI or restricted shells):\n * - `ZIDANE_STORAGE_DIR` — sets `storageDir`\n * - `ZIDANE_PREFIX` — sets `prefix`\n *\n * Hosts building on top of `zidane/tui` typically want their own env vars\n * (e.g. `MYAPP_STORAGE_DIR`); read them in your launch script and forward\n * to `runTui({ storageDir, prefix })`.\n *\n * ```ts\n * import { BUILTIN_PROVIDERS, runTui } from 'zidane/tui'\n * import { createRemoteStore } from 'zidane/session' // for the `store` option\n *\n * await runTui() // ~/.zidane/sessions.db + state.json\n * await runTui({ prefix: '.myapp' }) // ~/.myapp/...\n * await runTui({ storageDir: '/data', prefix: 'myapp' })\n * await runTui({ providers: { ...BUILTIN_PROVIDERS, mine: myDescriptor } })\n * await runTui({ store: createRemoteStore({ url: '…' }) })\n * ```\n */\nexport async function runTui(options: TuiOptions = {}): Promise<never> {\n if (runTuiInvoked) {\n throw new Error(\n 'runTui() can only be invoked once per process. '\n + 'Compose `<App config={resolveConfig(...)} />` against your own renderer '\n + 'if you need to run multiple TUIs in the same lifetime.',\n )\n }\n runTuiInvoked = true\n\n await initMd4x()\n // Self-test the instance: in some `bun build --compile` setups the inlined\n // `md4x.wasm` default export comes through as `undefined`, in which case\n // `init()` silently records a broken instance and every subsequent\n // `heal()` throws \"WASM not initialized\" mid-render. Probe here so the\n // failure surfaces as a clean startup error instead of an OpenTUI render\n // overlay. `MarkdownBlock` still falls back to raw text either way.\n try {\n heal('')\n }\n catch (err) {\n const cause = err instanceof Error ? err.message : String(err)\n process.stderr.write(`[zidane/tui] md4x WASM probe failed: ${cause}\\n`)\n }\n\n // `resolveConfig` wires `ZIDANE_CREDENTIALS_PATH` and applies stored API\n // keys into `process.env` internally — both happen before any provider\n // factory is constructed, so the wizard can render even on fresh installs\n // where the harness providers would otherwise throw \"No API key found\".\n const config = resolveConfig(options)\n\n // Promise + explicit resolver: avoid the `let done!: ...` non-null trick.\n let done: () => void = () => {}\n const exited = new Promise<void>((resolve) => { done = resolve })\n\n const renderer = await createCliRenderer({\n exitOnCtrlC: true,\n onDestroy: () => done(),\n })\n\n createRoot(renderer).render(<App config={config} />)\n\n await exited\n\n // Force a clean exit. See JSDoc above for rationale — without this the\n // process hangs after the renderer's `destroy()` returns, which manifests\n // as a stuck `bun --watch` parent and a terminal that needs a second Ctrl+C\n // to actually surrender control.\n process.exit(0)\n}\n\n// ---------------------------------------------------------------------------\n// Composition exports — for callers who want to build their own TUI shell.\n// ---------------------------------------------------------------------------\n\nexport { App } from './app'\nexport type { AuthMethod, ProviderAuth, ProviderKey } from './auth'\nexport { detectAuth } from './auth'\nexport {\n type ContextUsage,\n Footer,\n type Hint,\n marginTopFor,\n onInputSubmit,\n Spinner,\n Transcript,\n} from './components'\nexport {\n ConfigProvider,\n type ModelInfo,\n type ProviderRegistry,\n resolveConfig,\n type ResolvedConfig,\n type TuiOptions,\n useConfig,\n} from './config'\nexport type {\n ApiKeyCredential,\n CredentialsFile,\n OAuthCredential,\n ProviderCredential,\n} from './credentials'\nexport {\n applyApiKeyEnv,\n credentialsPath,\n readCredentials,\n readProviderCredential,\n removeProviderCredential,\n setProviderCredential,\n writeCredentials,\n} from './credentials'\nexport { ageString, fmtTokens, shortId } from './format'\nexport { Modal, type ModalProps, ModalRoot, useModal, useModalAwareFocus } from './modal'\nexport { ModelPickerModal } from './model-picker'\nexport type { OAuthFlowOptions } from './oauth'\nexport { runOAuthLogin, supportsOAuth } from './oauth'\nexport {\n anthropicDescriptor,\n BUILTIN_PROVIDERS,\n cerebrasDescriptor,\n credKeyOf,\n getContextWindow,\n modelsForDescriptor,\n openaiDescriptor,\n openrouterDescriptor,\n piIdOf,\n type ProviderDescriptor,\n} from './providers'\nexport {\n addToSafelist,\n getSafelist,\n IMPLICITLY_SAFE_TOOLS,\n isOnSafelist,\n matchesSafelistEntry,\n type ProjectEntry,\n type ProjectsFile,\n projectsFilePath,\n readProjects,\n suggestSafelistEntry,\n writeProjects,\n} from './safe-mode'\nexport {\n type ApprovalDecision,\n type ApprovalRequest,\n type RequestApproval,\n type SafeModeActions,\n SafeModeProvider,\n useSafeModeActions,\n useSafeModeQueue,\n} from './safe-mode-context'\nexport { AuthScreen, ChatScreen, SessionsScreen } from './screens'\nexport {\n DEFAULT_SETTINGS,\n SettingsModal,\n SettingsProvider,\n useSettings,\n} from './settings'\nexport {\n createStateStore,\n createTuiStore,\n eventsFromTurns,\n lastContextSizeFromTurns,\n listSessionMeta,\n loadState,\n saveState,\n type StateStoreApi,\n titleFromTurns,\n toolCallPreview,\n toolResultText,\n type TuiState,\n} from './store'\nexport {\n finalizeStreamingMarkdown,\n finalizeStreamingMarkdownForOwner,\n type StreamBuffer,\n type StreamSource,\n turnContextSize,\n useStreamBuffer,\n} from './streaming'\nexport { COLOR, MD_STYLE, SELECT_THEME } from './theme'\nexport type {\n Owner,\n Picked,\n Screen,\n SessionMeta,\n Settings,\n StreamEvent,\n} from './types'\n"],"mappings":"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"}