zidane 4.1.3 → 4.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.js +1 -1
- package/dist/{providers-CX-R-Oy-.js → providers-CCDvIXGJ.js} +26 -5
- package/dist/providers-CCDvIXGJ.js.map +1 -0
- package/dist/providers.js +1 -1
- package/dist/tui.d.ts +257 -68
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +826 -187
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/dist/providers-CX-R-Oy-.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":["EmptyState","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/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 } 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","/**\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 { 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 { 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 * 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 { 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 { 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}: {\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 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: '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 { 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 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 // 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 // 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 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 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 // 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, currentSession),\n [screen, busy, 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 />\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 { 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 // `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 { 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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"}
|