zidane 4.0.2 → 4.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +196 -614
  2. package/dist/agent-BoV5Twdl.d.ts +2347 -0
  3. package/dist/agent-BoV5Twdl.d.ts.map +1 -0
  4. package/dist/contexts-3Arvn7yR.js +321 -0
  5. package/dist/contexts-3Arvn7yR.js.map +1 -0
  6. package/dist/contexts.d.ts +2 -25
  7. package/dist/contexts.js +2 -10
  8. package/dist/errors-D1lhd6mX.js +118 -0
  9. package/dist/errors-D1lhd6mX.js.map +1 -0
  10. package/dist/index-28otmfLX.d.ts +400 -0
  11. package/dist/index-28otmfLX.d.ts.map +1 -0
  12. package/dist/index-BfSdALzk.d.ts +113 -0
  13. package/dist/index-BfSdALzk.d.ts.map +1 -0
  14. package/dist/index-DPsd0qwm.d.ts +254 -0
  15. package/dist/index-DPsd0qwm.d.ts.map +1 -0
  16. package/dist/index.d.ts +5 -95
  17. package/dist/index.js +141 -271
  18. package/dist/index.js.map +1 -0
  19. package/dist/interpolate-CukJwP2G.js +887 -0
  20. package/dist/interpolate-CukJwP2G.js.map +1 -0
  21. package/dist/mcp-8wClKY-3.js +771 -0
  22. package/dist/mcp-8wClKY-3.js.map +1 -0
  23. package/dist/mcp.d.ts +2 -4
  24. package/dist/mcp.js +2 -13
  25. package/dist/messages-z5Pq20p7.js +1020 -0
  26. package/dist/messages-z5Pq20p7.js.map +1 -0
  27. package/dist/presets-Cs7_CsMk.js +39 -0
  28. package/dist/presets-Cs7_CsMk.js.map +1 -0
  29. package/dist/presets.d.ts +2 -43
  30. package/dist/presets.js +2 -17
  31. package/dist/providers-CX-R-Oy-.js +969 -0
  32. package/dist/providers-CX-R-Oy-.js.map +1 -0
  33. package/dist/providers.d.ts +2 -4
  34. package/dist/providers.js +3 -23
  35. package/dist/session/sqlite.d.ts +7 -12
  36. package/dist/session/sqlite.d.ts.map +1 -0
  37. package/dist/session/sqlite.js +67 -79
  38. package/dist/session/sqlite.js.map +1 -0
  39. package/dist/session-Cn68UASv.js +440 -0
  40. package/dist/session-Cn68UASv.js.map +1 -0
  41. package/dist/session.d.ts +2 -4
  42. package/dist/session.js +3 -27
  43. package/dist/skills.d.ts +3 -322
  44. package/dist/skills.js +24 -47
  45. package/dist/skills.js.map +1 -0
  46. package/dist/stats-DoKUtF5T.js +58 -0
  47. package/dist/stats-DoKUtF5T.js.map +1 -0
  48. package/dist/tools-DpeWKzP1.js +3941 -0
  49. package/dist/tools-DpeWKzP1.js.map +1 -0
  50. package/dist/tools.d.ts +3 -95
  51. package/dist/tools.js +2 -40
  52. package/dist/tui.d.ts +533 -0
  53. package/dist/tui.d.ts.map +1 -0
  54. package/dist/tui.js +2004 -0
  55. package/dist/tui.js.map +1 -0
  56. package/dist/types-Bx_F8jet.js +39 -0
  57. package/dist/types-Bx_F8jet.js.map +1 -0
  58. package/dist/types.d.ts +4 -55
  59. package/dist/types.js +4 -28
  60. package/package.json +38 -4
  61. package/dist/agent-BAHrGtqu.d.ts +0 -2425
  62. package/dist/chunk-4ILGBQ23.js +0 -803
  63. package/dist/chunk-4LPBN547.js +0 -3540
  64. package/dist/chunk-64LLNY7F.js +0 -28
  65. package/dist/chunk-6STZTA4N.js +0 -830
  66. package/dist/chunk-7GQ7P6DM.js +0 -566
  67. package/dist/chunk-IC7FT4OD.js +0 -37
  68. package/dist/chunk-JCOB6IYO.js +0 -22
  69. package/dist/chunk-JH6IAAFA.js +0 -28
  70. package/dist/chunk-LNN5UTS2.js +0 -97
  71. package/dist/chunk-PMCQOMV4.js +0 -490
  72. package/dist/chunk-UD25QF3H.js +0 -304
  73. package/dist/chunk-W57VY6DJ.js +0 -834
  74. package/dist/sandbox-D7v6Wy62.d.ts +0 -28
  75. package/dist/skills-use-DwZrNmcw.d.ts +0 -80
  76. package/dist/types-Bai5rKpa.d.ts +0 -89
  77. package/dist/validation-Pm--dQEU.d.ts +0 -185
@@ -0,0 +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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"}
@@ -0,0 +1,39 @@
1
+ import { Buffer } from "node:buffer";
2
+ //#region src/types.ts
3
+ /**
4
+ * Lossy flattener — converts `ToolResultContent[]` (or a plain string) to a single
5
+ * string. Image blocks are replaced with `[image: <media> — <n> b64 bytes]` markers.
6
+ *
7
+ * Use at UI boundaries where a string is required; providers that understand
8
+ * structured content should route the array through without flattening.
9
+ */
10
+ function toolResultToText(content) {
11
+ if (typeof content === "string") return content;
12
+ return content.map((block) => {
13
+ if (block.type === "text") return block.text;
14
+ return `[image: ${block.mediaType} — ${block.data.length} b64 bytes]`;
15
+ }).join("\n");
16
+ }
17
+ /**
18
+ * Approximate byte length of a tool output as it goes back to the model.
19
+ *
20
+ * - Plain text: UTF-8 byte length.
21
+ * - Structured content: text blocks contribute their UTF-8 byte length; image
22
+ * blocks contribute their **base64 character length**, since that is what
23
+ * the model tokenizes (the wire-encoded payload, not the decoded image).
24
+ *
25
+ * Used by the agent loop to populate `outputBytes` on `tool:after`,
26
+ * `tool:transform`, `mcp:tool:after`, and `mcp:tool:transform` hooks so
27
+ * consumers can size-budget tool output without re-counting bytes themselves.
28
+ */
29
+ function toolOutputByteLength(content) {
30
+ if (typeof content === "string") return Buffer.byteLength(content);
31
+ let total = 0;
32
+ for (const block of content) if (block.type === "text") total += Buffer.byteLength(block.text);
33
+ else total += block.data.length;
34
+ return total;
35
+ }
36
+ //#endregion
37
+ export { toolResultToText as n, toolOutputByteLength as t };
38
+
39
+ //# sourceMappingURL=types-Bx_F8jet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-Bx_F8jet.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["/**\n * Shared types for the agent system.\n */\n\nimport type { ToolDef } from './tools/types'\nimport { Buffer } from 'node:buffer'\n\n// ---------------------------------------------------------------------------\n// Thinking / Reasoning\n// ---------------------------------------------------------------------------\n\n/**\n * Thinking / extended-reasoning configuration.\n *\n * - `'off'` — no thinking.\n * - `'minimal' | 'low' | 'medium' | 'high'` — explicit token budget. Maps to\n * provider-specific reasoning controls (Anthropic `thinking.type='enabled'`\n * with a budget; OpenAI `reasoning_effort`).\n * - `'adaptive'` — let the model decide per-turn whether and how much to think.\n * Anthropic-only (`thinking.type='adaptive'`). Other providers fall back to\n * no reasoning when this value is supplied.\n */\nexport type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'adaptive'\n\n// ---------------------------------------------------------------------------\n// MCP server configuration\n// ---------------------------------------------------------------------------\n\nexport interface McpServerConfig {\n /** Display name (used for tool namespacing) */\n name: string\n /** Transport type */\n transport: 'stdio' | 'sse' | 'streamable-http'\n /** For stdio: command to run */\n command?: string\n /** For stdio: command arguments */\n args?: string[]\n /**\n * For stdio: environment variables to pass to the server process.\n *\n * Merged on top of the MCP SDK's default inherited environment — a safety\n * whitelist (`PATH`, `HOME`, `LANG`, `SHELL`, `USER` on POSIX; `APPDATA`,\n * `PATH`, ... on Win32). Setting this to `{}` no longer strips `PATH` from\n * the child process. Set {@link McpServerConfig.strictEnv} to `true` to\n * pass `env` verbatim with no inherited defaults.\n */\n env?: Record<string, string>\n /**\n * When true, {@link McpServerConfig.env} is passed verbatim to the spawned\n * process — the MCP SDK's default inherited environment (`PATH`, `HOME`, ...)\n * is NOT merged in. Most consumers should leave this off; the default merge\n * prevents `spawn ENOENT` when a stdio server declares an `env` without\n * restating `PATH`.\n */\n strictEnv?: boolean\n /** For sse/streamable-http: server URL */\n url?: string\n /** Optional headers for HTTP transports */\n headers?: Record<string, string>\n /**\n * Timeout in milliseconds for MCP server bootstrap (connect + tool discovery).\n *\n * Zidane connects MCP servers lazily on the first `run()`. Without a\n * bootstrap timeout, a slow or hung server can delay the first provider call\n * for an arbitrarily long time even when that MCP server is never used.\n *\n * Default: `10000`.\n */\n bootstrapTimeout?: number\n /** Timeout in milliseconds for MCP tool calls (default: 30000) */\n toolTimeout?: number\n /**\n * Allow-list of tool names to expose. Names match the upstream tool name\n * (NOT the namespaced `mcp_{server}_{tool}` form). Tools not in the list are\n * dropped before registration — the model never sees them in its catalog and\n * the wire cost of advertising them is avoided.\n *\n * Mutually exclusive with {@link McpServerConfig.disabledTools} — passing both\n * throws at bootstrap time.\n *\n * Composes with {@link McpServerConfig.toolFilter}: allow-list applies first,\n * then the predicate. Composes with the `mcp:tools:filter` hook: config-side\n * filters apply first, then the hook can further narrow the list.\n */\n enabledTools?: string[]\n /**\n * Deny-list of tool names. Tools matching are dropped before registration.\n * Same matching semantics as {@link McpServerConfig.enabledTools}.\n */\n disabledTools?: string[]\n /**\n * Custom predicate run on each upstream tool. Return `true` to keep, `false`\n * to drop. Receives the raw `listTools()` payload — useful for filtering by\n * description, schema shape, or other metadata that an allow/deny list can't\n * express.\n *\n * Runs after the allow/deny filter but before the `mcp:tools:filter` hook.\n */\n toolFilter?: (tool: { name: string, description?: string | null, inputSchema?: unknown }) => boolean\n /**\n * Per-server override for {@link AgentBehavior.toolDisclosure}. When set,\n * this server's tools follow this disclosure mode regardless of the\n * agent-wide default. Useful when one big MCP server (200+ tools) should\n * stay lazy while smaller servers stay eager.\n *\n * Default: inherits from `behavior.toolDisclosure`.\n */\n disclosure?: 'eager' | 'lazy'\n}\n\n// ---------------------------------------------------------------------------\n// Tool execution\n// ---------------------------------------------------------------------------\n\nexport type ToolExecutionMode = 'sequential' | 'parallel'\n\nexport interface AgentBehavior {\n /** Tool execution mode (default: 'sequential') */\n toolExecution?: ToolExecutionMode\n /**\n * Max agent loop iterations.\n *\n * Default: unlimited (Infinity). The loop runs until the model signals\n * completion (no tool calls / `end_turn`), the abort signal fires, or this\n * cap is hit. Set a finite value as a safety net for runaway loops.\n */\n maxTurns?: number\n /** Max tokens per LLM response (default: 16384) */\n maxTokens?: number\n /** Thinking token budget — overrides the level-based default when set */\n thinkingBudget?: number\n /** JSON Schema for structured output enforcement */\n schema?: Record<string, unknown>\n /**\n * Enable provider prompt caching. When on (default), the provider marks the\n * system prompt, tools, and the last stable message with cache breakpoints so\n * the shared prefix is served from cache across turns.\n *\n * - Anthropic: `cache_control: { type: 'ephemeral' }` on the last `system`\n * content part, the last tool, and the last message content part.\n * - OpenAI-compatible / OpenRouter: same shape — honored by Anthropic-backed\n * OpenRouter routes and by Gemini; ignored (no-op) by providers that cache\n * automatically (OpenAI, DeepSeek, Grok, Groq, Moonshot).\n *\n * Usage is surfaced via `TurnUsage.cacheRead` / `TurnUsage.cacheCreation`.\n *\n * Default: `true`.\n */\n cache?: boolean\n /**\n * Soft per-turn cap on total tool-output bytes. When the sum of `outputBytes`\n * across a turn's tool results exceeds this value, the loop injects a\n * synthetic user message instructing the model to summarize before calling\n * more tools, and fires the `budget:exceeded` hook.\n *\n * Measured **post-`tool:transform`** so consumer truncation counts toward the\n * budget. Off by default (undefined / `0` disables the check). A reasonable\n * starting value for OSS-model integrations is `32768`.\n */\n toolOutputBudget?: number\n /**\n * Deduplicate identical re-reads of the same file in `read_file`. When the\n * model re-reads a file with the same slice and the bytes haven't changed\n * since the last read in this session, the tool returns a short stub\n * instead of re-emitting the full content. Pairs with the read-before-edit\n * guard in `edit` / `multi_edit`.\n *\n * Requires a session (set via `createSession()`); without one, the flag is\n * a no-op since per-session state has nowhere to live.\n *\n * Default: `true`.\n */\n dedupReads?: boolean\n /**\n * Taper the thinking budget over the course of a run. Late turns are\n * usually checkpoint / cleanup work where reasoning rarely pays for\n * itself; early turns benefit most. Two forms:\n *\n * - **Struct** — geometric decay starting after `afterTurn`, multiplying by\n * `factor` each subsequent turn, clamped to `floor`. Example\n * `{ afterTurn: 5, factor: 0.5, floor: 1024 }` with a base budget of 8192:\n * turns 1-5 = 8192, turn 6 = 4096, turn 7 = 2048, turn 8+ = 1024.\n * - **Function** — `(runTurn, baseBudget) => number`. Arbitrary curves;\n * `runTurn` is 1-indexed, run-relative (resumed sessions reset).\n *\n * No-op when `thinkingBudget` is unset. Honored by every provider that\n * respects `thinkingBudget` (anthropic explicit-budget `enabled` path,\n * adaptive `maxTokensCap`, openai-compat `max_tokens` padding).\n *\n * Default: `undefined` (no decay).\n */\n thinkingDecay?: { afterTurn: number, factor: number, floor: number } | ((runTurn: number, baseBudget: number) => number)\n /**\n * Per-tool soft call budget for this run. Keyed by **canonical** tool name.\n * On the first call after the run-cumulative dispatched count for that tool\n * reaches `max`, the framework fires `onExceed`:\n *\n * - `'steer'` (default) — let the call execute, but emit a synthetic user\n * message after the turn that nudges the model away from re-calling the\n * tool. Reuses the existing post-turn steer pathway used by\n * `toolOutputBudget`. Fires `tool-budget:exceeded` with `mode: 'steer'`.\n * - `'block'` — refuse the call via `tool:gate` `block`. The model sees a\n * `Blocked: <reason>` tool result. Fires `tool-budget:exceeded` with\n * `mode: 'block'`.\n * - **Function** — `(ctx) => { mode, message }`. The consumer supplies the\n * steering / refusal text and chooses the mode dynamically.\n *\n * Counts include both real dispatches and dedup substitutes (Z19 hits).\n * Excludes calls already blocked by an earlier gate (skill allow-list,\n * consumer hook). Tool dispatched by spawned subagents has its own per-run\n * counter — child counts never charge the parent.\n *\n * For MCP tools, key by the namespaced wire name (`mcp_<server>_<tool>`).\n *\n * Atomic in parallel mode: the middleware tracks its own per-tool\n * approval counter, incremented synchronously at gate-time. A\n * 4-call parallel batch against `max: 2` will let the first 2 through\n * and refuse the rest, even though the loop's `runToolCounts` only\n * propagates between calls (not within a single batch's gate fan-out).\n *\n * Default: `undefined` (no budget enforcement).\n */\n toolBudgets?: Record<string, {\n max: number\n onExceed?: 'steer' | 'block' | ((ctx: {\n tool: string\n count: number\n max: number\n }) => { mode: 'steer' | 'block', message: string })\n }>\n /**\n * Generic per-tool argument deduplication. Keyed by the tool's **canonical**\n * name (alias-stable). Each entry is a hasher: `(input) => string | undefined`.\n *\n * **Hasher contract** — three return values, three meanings:\n *\n * | Return | Meaning |\n * |-------------------------|------------------------------------------------------------------------|\n * | a non-empty string | Cache key for this call. Equal keys (most-recent-only, this session) |\n * | | replay the prior recorded result without re-dispatching the tool. |\n * | `undefined` | **Skip dedup for this call.** The tool runs normally; nothing recorded.|\n * | `''` / non-string | Treated identically to `undefined` (defensive: no dedup, no error). |\n *\n * The `undefined` opt-out is the way to say *\"this specific call is not\n * cacheable\"* (timestamps in input, randomness baked in, debug flags). It\n * is **not** the same as `JSON.stringify(input)` — that would dedup against\n * the verbatim input. Pick one explicitly:\n *\n * ```ts\n * // Always cache by full input — every identical re-call dedups.\n * dedupTools: { todowrite: input => JSON.stringify(input) }\n *\n * // Cache by a normalized subset; non-cacheable shapes opt out.\n * dedupTools: {\n * execute_sql: (input) => {\n * const q = typeof input.query === 'string' ? input.query.trim().toLowerCase() : undefined\n * if (!q || q.includes('now()') || q.includes('random()')) return undefined\n * return q\n * },\n * }\n * ```\n *\n * On a hit, the previously-recorded result is replayed as the tool_result\n * without dispatching the tool. The substitution flows through `tool:gate`\n * `result` (Z20), so `tool:after` and `tool:transform` still fire.\n *\n * Requires a session (`createSession()`); without one, the map is a silent\n * no-op since per-session state has nowhere to live. Tools with side\n * effects or non-deterministic outputs (network, time, randomness) MUST\n * NOT be listed — there is no safety net beyond the consumer's hasher.\n *\n * For MCP tools, key by the namespaced wire name (`mcp_<server>_<tool>`).\n * Parallel mode (`toolExecution: 'parallel'`, the default) sees calls in\n * the SAME assistant turn race against each other — none can dedup against\n * a sibling that started in the same batch. Sequential mode honors order\n * within a turn.\n *\n * **Cache policy**: only the most recent `(hash, result)` per tool is\n * retained. Interleaved patterns (input A, input B, input A) miss on the\n * second A because B overwrote it. Sufficient for the common spam-the-\n * same-call loop; consumers needing a richer cache should hook\n * `tool:gate` directly.\n *\n * Default: `undefined` (no per-tool dedup).\n */\n dedupTools?: Record<string, (input: Record<string, unknown>) => string | undefined>\n /**\n * Require `read_file` before `edit` / `multi_edit` on the same path, and\n * reject edits when the file has changed on disk since the last read in\n * this session. Eliminates the silent-corruption failure mode where a\n * model \"remembers\" stale content and applies a substring edit against\n * bytes that have moved.\n *\n * Requires a session. Off by default; turn it on for stricter eval-grade\n * runs where silent edit corruption would invalidate the result.\n *\n * Default: `false`.\n */\n requireReadBeforeEdit?: boolean\n /**\n * Client-side context compaction strategy. Use this for non-Anthropic\n * providers (OSS via cerebras / openai-compat / openrouter) that don't\n * have a server-side equivalent. Anthropic users should prefer the\n * server-side `context-management-2025-06-27` beta — see\n * `AnthropicParams.contextManagement`.\n *\n * - `'off'` (default) — no client-side compaction.\n * - `'tail'` — when total tool-output bytes in the persisted history\n * exceed `compactThreshold`, replace older `tool_result` outputs with a\n * short stub, keeping the newest `compactKeepTurns` turns intact. The\n * compaction is applied to the wire-level message list only; the\n * underlying session turns are not modified.\n *\n * Default: `'off'`.\n */\n compactStrategy?: 'off' | 'tail'\n /**\n * Soft byte threshold that triggers tail compaction when\n * `compactStrategy === 'tail'`. Counts the post-`context:transform` bytes\n * of `tool_result` outputs across all messages. Default: `131_072` (128\n * KiB). Ignored when compaction is off.\n */\n compactThreshold?: number\n /**\n * Number of trailing turns to leave untouched during tail compaction. The\n * most-recent `compactKeepTurns` user/assistant messages are not eligible\n * for elision so the model keeps the freshest tool context. Default: `4`.\n */\n compactKeepTurns?: number\n /**\n * Prefix every line of `read_file` output with its 1-indexed line number\n * followed by a tab (`<N>\\t<content>`) — the compact `cat -n`-style\n * format Claude Code emits. The `edit` tool strips the prefix from\n * `old_string` / `new_string` so the model can paste back a numbered\n * chunk verbatim without breaking the match.\n *\n * Set `false` to opt out — useful for callers piping `read_file` into\n * downstream parsers that don't recognize the prefix. Per-call\n * `read_file({ lineNumbers: false })` overrides this default.\n *\n * Default: `true`.\n */\n readLineNumbers?: boolean\n /**\n * Replace older `read_file` `tool_result` blocks with a short stub when\n * a successful `edit` / `multi_edit` / `write_file` later in the same\n * run modified the same path. The replacement is applied to the\n * wire-level message list only — persisted session turns keep the\n * original content.\n *\n * Eliminates the common waste pattern where the model carries the\n * pre-edit file body forward across many turns \"in case it needs it\".\n * Pairs cleanly with `compactStrategy: 'tail'`: stale reads shrink\n * first, then the byte-threshold compaction fires if anything's left.\n *\n * Detection is conservative — only triggers when the corresponding\n * tool_result confirms success (`Edited …`, `Created …`, `Updated …`).\n * Failed edits and `No change needed` write_file calls do NOT\n * invalidate prior reads.\n *\n * Default: `false`.\n */\n elideStaleReads?: boolean\n /**\n * Tool disclosure strategy. Controls whether the model sees every tool's\n * full `inputSchema` in its tool list every turn (\"eager\") or whether MCP\n * tools are advertised as a name+description catalog in the system prompt\n * and only get full schemas after being surfaced via the `tool_search`\n * native tool (\"lazy\" / progressive disclosure).\n *\n * Native tools (those passed to `createAgent({ tools })`) and skill tools\n * are always eager — they are core to the agent and cheap. Only MCP tools\n * are eligible for lazy disclosure.\n *\n * When `'lazy'`, the agent:\n * - Appends a `<searchable_tools>` section to the system prompt listing\n * every MCP tool by `name` + `description` only (no `inputSchema`).\n * - Auto-injects a `tool_search` native tool (opt out via\n * {@link AgentBehavior.toolSearch}) the model uses to load schemas on\n * demand. Surfaced tools persist for the rest of the run.\n * - Rebuilds the wire-level tool list each turn, appending newly-unlocked\n * tools at the end so the prefix-cache breakpoint advances cleanly.\n *\n * Trade-off: every `tool_search` invocation expands the tool list and\n * invalidates the tool-list cache breakpoint for one turn. With many\n * MCP servers, the savings on cold turns (fewer schemas in context) are\n * substantial; with one tiny MCP server, the overhead may not pay back.\n *\n * Default: `'eager'`.\n */\n toolDisclosure?: 'eager' | 'lazy'\n /**\n * Fine-grained config for the `tool_search` tool auto-injected when\n * {@link AgentBehavior.toolDisclosure} is `'lazy'`. No-op in eager mode.\n *\n * - `tool: false` — opt out of the auto-injection entirely. Use when the\n * host wants to ship a custom discovery tool. Note that the catalog\n * text drops the call-to-action prose in this case so the model isn't\n * pointed at a non-existent tool.\n * - `limit` — default cap on results returned per `tool_search` call when\n * the model omits the parameter. Default: `20`.\n *\n * Note on host-defined `tool_search`: a tool the host registers under the\n * name `tool_search` (or under any alias whose canonical is `tool_search`)\n * will shadow the auto-injected one — the catalog text will point at the\n * host's wire name, but driving the unlock flow requires either using\n * `createToolSearchTool({ catalog, unlocked })` from `tools/tool-search`\n * (which internally mutates the unlock set) or fully opting out via\n * `toolSearch.tool: false` and treating discovery as a host-side concern.\n * A bare host tool that doesn't touch the unlock set will not advance the\n * lazy disclosure state and the hard gate will keep refusing lazy calls.\n *\n * Default: `undefined` (auto-inject with the default limit).\n */\n toolSearch?: {\n tool?: false\n limit?: number\n }\n}\n\n// ---------------------------------------------------------------------------\n// Prompt parts (multimodal input)\n// ---------------------------------------------------------------------------\n\n/**\n * One block of a multimodal user prompt.\n *\n * `agent.run({ prompt })` accepts either a plain string (treated as a single\n * text part) or an array of these parts for multimodal inputs.\n *\n * `document` parts are routed per provider: PDF-style mime types are sent as\n * native document blocks when the provider supports them; text documents are\n * inlined as text with an attachment header. Providers that cannot handle an\n * image or document throw early.\n */\nexport type PromptPart\n = | PromptTextPart\n | PromptImagePart\n | PromptDocumentPart\n\nexport interface PromptTextPart {\n type: 'text'\n text: string\n}\n\nexport interface PromptImagePart {\n type: 'image'\n /** IANA media type (e.g. `image/png`, `image/jpeg`) */\n mediaType: string\n /** Base64-encoded payload */\n data: string\n /** Optional display name */\n name?: string\n}\n\nexport interface PromptDocumentPart {\n type: 'document'\n /** IANA media type (e.g. `application/pdf`, `text/plain`) */\n mediaType: string\n /** Either a base64-encoded payload (`encoding: 'base64'`) or raw text (`encoding: 'text'`) */\n data: string\n encoding: 'base64' | 'text'\n /** Optional display name used in attachment headers */\n name?: string\n}\n\n// ---------------------------------------------------------------------------\n// Canonical message format (used throughout the agent system)\n// ---------------------------------------------------------------------------\n\n/**\n * A single block of structured tool-result content.\n *\n * MCP servers can return a mix of text, image, resource, and audio blocks. Tools\n * return `string` for the common text-only case or `ToolResultContent[]` when they\n * need to preserve non-text content (e.g. screenshots from a browser MCP).\n *\n * Providers that support native multi-part tool results (Anthropic, OpenAI Codex via\n * pi-ai) route image blocks into their wire format verbatim; OpenAI-compat providers\n * route them via a companion-user-message fallback when the underlying model/endpoint\n * does not accept images inside tool-role messages.\n */\nexport type ToolResultContent\n = | ToolResultTextContent\n | ToolResultImageContent\n\nexport interface ToolResultTextContent {\n type: 'text'\n text: string\n}\n\nexport interface ToolResultImageContent {\n type: 'image'\n /** IANA media type (e.g. `image/png`, `image/jpeg`) */\n mediaType: string\n /** Base64-encoded payload */\n data: string\n}\n\n/**\n * Lossy flattener — converts `ToolResultContent[]` (or a plain string) to a single\n * string. Image blocks are replaced with `[image: <media> — <n> b64 bytes]` markers.\n *\n * Use at UI boundaries where a string is required; providers that understand\n * structured content should route the array through without flattening.\n */\nexport function toolResultToText(content: string | ToolResultContent[]): string {\n if (typeof content === 'string')\n return content\n return content\n .map((block) => {\n if (block.type === 'text')\n return block.text\n return `[image: ${block.mediaType} — ${block.data.length} b64 bytes]`\n })\n .join('\\n')\n}\n\n/**\n * Approximate byte length of a tool output as it goes back to the model.\n *\n * - Plain text: UTF-8 byte length.\n * - Structured content: text blocks contribute their UTF-8 byte length; image\n * blocks contribute their **base64 character length**, since that is what\n * the model tokenizes (the wire-encoded payload, not the decoded image).\n *\n * Used by the agent loop to populate `outputBytes` on `tool:after`,\n * `tool:transform`, `mcp:tool:after`, and `mcp:tool:transform` hooks so\n * consumers can size-budget tool output without re-counting bytes themselves.\n */\nexport function toolOutputByteLength(content: string | ToolResultContent[]): number {\n if (typeof content === 'string')\n return Buffer.byteLength(content)\n let total = 0\n for (const block of content) {\n if (block.type === 'text')\n total += Buffer.byteLength(block.text)\n else\n total += block.data.length\n }\n return total\n}\n\nexport type SessionContentBlock\n = | { type: 'text', text: string }\n | { type: 'image', mediaType: string, data: string }\n | { type: 'tool_call', id: string, name: string, input: Record<string, unknown> }\n | {\n type: 'tool_result'\n callId: string\n /**\n * Tool output — either a plain string (text-only, the common case) or a structured\n * array of content blocks (text + image for multimodal tools such as screenshots).\n */\n output: string | ToolResultContent[]\n isError?: boolean\n }\n | {\n type: 'thinking'\n text: string\n signature?: string\n /**\n * Provider that minted `signature`. Signatures are provider-bound (Anthropic\n * HMAC vs. OpenAI `encrypted_content`) and are dropped on cross-provider\n * hops to avoid 400s. Unset means legacy/unknown — forwarded as-is.\n */\n signatureProducer?: 'anthropic' | 'openai'\n }\n | { type: 'redacted_thinking', data: string }\n | {\n /**\n * Opaque round-trip envelope for reasoning state minted by an OpenAI-compat\n * gateway (currently OpenRouter). The gateway expects its own\n * `reasoning_details` array echoed back verbatim on the next turn so the\n * upstream model can resume an extended-reasoning chain across tool calls.\n *\n * Stored opaquely because the items are provider-bound (Anthropic HMAC\n * signatures, OpenAI `encrypted_content`, model-specific summary formats\n * — all flowing through the gateway's normalized envelope).\n */\n type: 'provider_reasoning'\n producer: 'openrouter'\n details: unknown[]\n /**\n * Model id that produced the details. Reasoning is bound to a specific\n * upstream route — a model switch on the next turn invalidates the\n * embedded signatures, so the sender drops the block on mismatch.\n */\n model?: string\n }\n\nexport interface SessionMessage {\n role: 'user' | 'assistant'\n content: SessionContentBlock[]\n}\n\nexport interface SessionTurn {\n /** UUID — generated by the store if it provides generateTurnId, else crypto.randomUUID() */\n id: string\n /** Run that produced this turn (e.g. 'run_1') */\n runId?: string\n role: 'user' | 'assistant' | 'system'\n content: SessionContentBlock[]\n /** Token usage — only present on assistant turns */\n usage?: TurnUsage\n /** Unix timestamp (Date.now()) when the turn was created */\n createdAt: number\n}\n\n// ---------------------------------------------------------------------------\n// Agent run options\n// ---------------------------------------------------------------------------\n\n/**\n * Per-run hook registrations. Each entry can be a single handler or an array of handlers.\n * Keys are `AgentHooks` event names (loose-typed here to avoid a circular import; agent.ts\n * narrows it to the strongly-typed map).\n */\nexport type RunHookMap = Record<string, ((ctx: any) => unknown) | ((ctx: any) => unknown)[]>\n\nexport interface AgentRunOptions {\n model?: string\n /**\n * User prompt. Optional when resuming a session with existing turns.\n *\n * Accepts either a plain string (single text part) or an array of `PromptPart`s for\n * multimodal inputs (text, images, documents). See {@link PromptPart}.\n */\n prompt?: string | PromptPart[]\n system?: string\n thinking?: ThinkingLevel\n /** Abort signal — when triggered, the agent stops after the current turn */\n signal?: AbortSignal\n /** Behavior overrides for this run (overrides agent defaults) */\n behavior?: AgentBehavior\n /** Tool overrides for this run. Pass {} for no tools. Omit to use agent tools. */\n tools?: Record<string, ToolDef>\n /**\n * Per-run hook registrations. Each hook is attached before the run starts and\n * detached in a finally block so handlers never leak across runs.\n *\n * Accepts either a single handler or an array (all handlers register).\n */\n hooks?: RunHookMap\n /**\n * Parent run id. Populated automatically by the `spawn` tool when the child\n * shares the parent's session; recorded on the resulting `SessionRun` so the\n * parent↔child run tree can be reconstructed from a persisted session.\n */\n parentRunId?: string\n /**\n * Zero-based subagent depth. 0 = top-level `agent.run()`, 1 = first-level\n * child spawned by a parent agent, and so on. Used by the spawn tool to\n * enforce `maxDepth` and to stamp `child:*` forwarded hook payloads.\n */\n depth?: number\n}\n\n// ---------------------------------------------------------------------------\n// Agent stats\n// ---------------------------------------------------------------------------\n\n/**\n * Reason the provider gave for stopping the turn.\n *\n * - `'stop'` — natural turn end (`end_turn` / `stop_sequence`).\n * - `'tool-calls'` — model emitted tool_use blocks.\n * - `'length'` — `max_tokens` reached, or (Anthropic 4.6+) the response bumped\n * against the model's context window mid-stream\n * (`model_context_window_exceeded`). The partial response is preserved; the\n * loop emits this reason so consumers can prune/retry.\n * - `'content-filter'` — model refused.\n * - `'pause'` — Anthropic `pause_turn`: a server-side mid-turn pause for very\n * long thinking. The loop continues with a synthetic \"Please continue.\"\n * user message rather than terminating; consumers see the pause via this\n * finish reason on the prior assistant turn.\n * - `'error'` — provider classified the turn as failed.\n * - `'other'` — unknown / unmapped.\n */\nexport type TurnFinishReason = 'stop' | 'tool-calls' | 'length' | 'content-filter' | 'pause' | 'error' | 'other'\n\nexport interface TurnUsage {\n input: number\n output: number\n /** Tokens written to cache (Anthropic) */\n cacheCreation?: number\n /** Tokens read from cache (Anthropic) */\n cacheRead?: number\n /** Thinking/reasoning tokens used */\n thinking?: number\n /** Cost in USD as reported by the provider (OpenRouter) */\n cost?: number\n /**\n * Why the model stopped this turn. Providers normalize native stop reasons to this union.\n * Absent when the provider did not surface a reason (e.g. mock turns).\n */\n finishReason?: TurnFinishReason\n /**\n * The model ID the provider ultimately used. May differ from the requested model when the\n * provider remaps aliases. Absent for providers that do not echo a model ID.\n */\n modelId?: string\n}\n\nexport interface AgentStats {\n /**\n * Cumulative input tokens across the parent agent loop **and** every\n * recursively-spawned sub-agent. Use this for billing / token-ledger\n * consumption.\n */\n totalIn: number\n /** Cumulative output tokens. Same semantics as {@link AgentStats.totalIn}. */\n totalOut: number\n /**\n * Cumulative cache-read tokens across the parent agent loop and every\n * recursively-spawned sub-agent. Surfaced at the top level (rather than\n * only per-`TurnUsage`) because Anthropic prices cache reads at a separate\n * line-item rate from regular input — billing-correct cost computation\n * needs this number directly. Always `0` for providers that don't report\n * cache usage.\n */\n totalCacheRead: number\n /**\n * Cumulative cache-creation tokens across the parent agent loop and every\n * recursively-spawned sub-agent. Same rationale as\n * {@link AgentStats.totalCacheRead} — separate Anthropic billing rate.\n * Always `0` for providers that don't report cache usage.\n */\n totalCacheCreation: number\n /**\n * Number of parent agent-loop turns. Children's turn counts live under\n * `children[].stats.turns` and are NOT folded in here — a single \"turns\"\n * number for the whole tree would conflate two different measures\n * (parent-loop iterations vs. tree-wide tool-call rounds).\n *\n * Tree-wide turn count: `flattenTurns(stats).length`.\n */\n turns: number\n /**\n * Wall-clock duration of the top-level `agent.run()` call, in milliseconds.\n * Children run during parent tool calls so this naturally subsumes child\n * wall time — sequential children inflate it, parallel children compress\n * into the parent's window.\n */\n elapsed: number\n /**\n * Per-turn usage breakdown for the **parent loop only**. Children's per-turn\n * usages live under `children[].stats.turnUsage`. Use {@link flattenTurns}\n * to walk the full tree.\n */\n turnUsage?: TurnUsage[]\n /**\n * Cumulative cost in USD — parent loop plus every recursively-spawned\n * sub-agent. Sums per-turn `TurnUsage.cost` reported by the provider.\n * Absent when neither parent nor any descendant reported a non-zero cost.\n */\n cost?: number\n /** Stats from child agents spawned during this run, in completion order. Recursive. */\n children?: ChildRunStats[]\n /** Structured output from schema enforcement (only present when behavior.schema is set) */\n output?: Record<string, unknown>\n /**\n * Milliseconds from the start of `agent.run()` to the first observable signal from the\n * provider (first `stream:text`, `stream:thinking`, or `tool:before` event).\n *\n * Absent when the run produced no observable signals (e.g. aborted before any stream event).\n */\n timeTillFirstTokenMs?: number\n}\n\nexport interface ChildRunStats {\n id: string\n task: string\n /**\n * The child agent's full {@link AgentStats}. Cumulative for that child's\n * own subtree (child loop + its grandchildren). Do **not** sum\n * `ctx.stats.totalIn` across `spawn:complete` events to derive top-level\n * totals — `agent.run()`'s return value is the canonical cumulative root.\n */\n stats: AgentStats\n /**\n * Subagent depth when this child ran. 1 = direct child of the top-level\n * agent, 2 = grandchild, etc. Useful for telemetry that wants to group\n * runs by depth.\n */\n depth?: number\n /**\n * Terminal state of the child run. `'completed'` is the default. Exposed so\n * a parent reading `stats.children` can distinguish aborted/timed-out\n * children without re-parsing the returned string.\n */\n status?: 'completed' | 'aborted' | 'timeout' | 'error'\n /**\n * Final structured output when the child was run with `behavior.schema`.\n * Mirrors `AgentStats.output` but is surfaced here so the parent can read\n * it without peeking at the nested `stats` bag.\n */\n output?: Record<string, unknown>\n}\n\n// ---------------------------------------------------------------------------\n// Hook context types\n// ---------------------------------------------------------------------------\n\n/**\n * Base context for tool execution hooks.\n *\n * `name` is the canonical tool identity — the spec name registered on the agent (or the\n * `mcp_{server}_{tool}` name for MCP tools). Hooks should policy-match against `name`.\n *\n * `displayName` is the outward-facing name — the alias surfaced to the LLM when\n * `AgentOptions.toolAliases` maps the canonical name; otherwise equal to `name`.\n * UI/telemetry adapters should emit `displayName`.\n *\n * Canonical vs. alias matters on session resume: `session.turns` persists canonical\n * names only, so renaming an alias cannot desync history.\n */\nexport interface ToolHookContext {\n turnId: string\n callId: string\n /** Canonical tool name (spec name). Stable across alias-map changes. */\n name: string\n /** Aliased (wire) name — equal to `name` when no alias is defined. */\n displayName: string\n input: Record<string, unknown>\n}\n\n/**\n * Base context for MCP tool hooks.\n *\n * `tool` is the native tool name on the MCP server. `server` is the configured server\n * name. The canonical zidane-namespaced identity is `mcp_{server}_{tool}`.\n *\n * `displayName` equals the canonical namespaced name unless the agent has aliased\n * this MCP tool via `AgentOptions.toolAliases`; in which case `displayName` is the\n * alias that the LLM sees.\n */\nexport interface McpToolHookContext {\n turnId: string\n callId: string\n server: string\n tool: string\n /** Aliased wire name for this MCP tool, or the canonical `mcp_{server}_{tool}` name. */\n displayName: string\n input: Record<string, unknown>\n}\n\n/** Base context for session hooks */\nexport interface SessionHookContext {\n sessionId: string\n}\n\n/** Base context for spawn hooks */\nexport interface SpawnHookContext {\n id: string\n task: string\n /**\n * Subagent depth for the spawn. 1 = direct child of the top-level agent.\n * Present on spawn:before/complete/error. Absent for grandchild spawns that\n * bubble through `child:*` events (which carry their own `depth`).\n */\n depth?: number\n}\n\n/** Context for stream hooks */\nexport interface StreamHookContext {\n turnId: string\n}\n\n/** Context for OAuth refresh hooks */\nexport interface OAuthRefreshHookContext {\n provider: string\n providerId: string\n source: 'params' | 'file'\n previousCredentials: Record<string, unknown> & { access: string, refresh: string, expires: number }\n credentials: Record<string, unknown> & { access: string, refresh: string, expires: number }\n}\n\nexport type SessionEndStatus = 'completed' | 'aborted' | 'error'\n"],"mappings":";;;;;;;;;AA0fA,SAAgB,iBAAiB,SAA+C;CAC9E,IAAI,OAAO,YAAY,UACrB,OAAO;CACT,OAAO,QACJ,KAAK,UAAU;EACd,IAAI,MAAM,SAAS,QACjB,OAAO,MAAM;EACf,OAAO,WAAW,MAAM,UAAU,KAAK,MAAM,KAAK,OAAO;GACzD,CACD,KAAK,KAAK;;;;;;;;;;;;;;AAef,SAAgB,qBAAqB,SAA+C;CAClF,IAAI,OAAO,YAAY,UACrB,OAAO,OAAO,WAAW,QAAQ;CACnC,IAAI,QAAQ;CACZ,KAAK,MAAM,SAAS,SAClB,IAAI,MAAM,SAAS,QACjB,SAAS,OAAO,WAAW,MAAM,KAAK;MAEtC,SAAS,MAAM,KAAK;CAExB,OAAO"}
package/dist/types.d.ts CHANGED
@@ -1,55 +1,4 @@
1
- import { j as AgentStats, ai as TurnUsage } from './agent-BAHrGtqu.js';
2
- export { c as Agent, d as AgentAbortedError, e as AgentBehavior, f as AgentContextExceededError, A as AgentHooks, g as AgentOptions, h as AgentProviderError, i as AgentRunOptions, k as AgentToolNotAllowedError, l as AnthropicParams, C as CONTEXT_EXCEEDED_MESSAGE_PATTERNS, m as CerebrasParams, aK as ChildRunStats, n as ClassifiedError, o as ClassifiedErrorKind, p as CreateSessionOptions, M as McpConnection, r as McpServerConfig, s as McpToolHookContext, O as OAuthRefreshHookContext, w as OpenAIParams, x as OpenRouterParams, P as PromptDocumentPart, y as PromptImagePart, z as PromptPart, B as PromptTextPart, E as Provider, G as ProviderCapabilities, R as RemoteStoreOptions, H as RunHookMap, I as Session, J as SessionContentBlock, K as SessionData, L as SessionEndStatus, N as SessionHookContext, Q as SessionMessage, T as SessionRun, S as SessionStore, U as SessionTurn, X as SkillConfig, Z as SkillResource, $ as SkillsConfig, a0 as SpawnHookContext, a1 as StreamCallbacks, a2 as StreamHookContext, a3 as StreamOptions, a4 as ThinkingLevel, a5 as ToolCall, a6 as ToolContext, a7 as ToolDef, a8 as ToolExecutionMode, a9 as ToolHookContext, aa as ToolMap, ab as ToolResult, ac as ToolResultContent, ad as ToolResultImageContent, ae as ToolResultTextContent, af as ToolSpec, ag as TurnFinishReason, ah as TurnResult, ay as matchesContextExceeded, aI as toolOutputByteLength, aJ as toolResultToText } from './agent-BAHrGtqu.js';
3
- export { C as ContextCapabilities, b as ContextType, E as ExecResult, a as ExecutionContext, c as ExecutionHandle, S as SpawnConfig } from './types-Bai5rKpa.js';
4
- export { S as SandboxProvider } from './sandbox-D7v6Wy62.js';
5
- export { Preset } from './presets.js';
6
- export { C as ChildAgent, I as InteractionToolOptions, S as SpawnToolOptions, a as SpawnToolState, V as ValidationResult } from './validation-Pm--dQEU.js';
7
- import 'hookable';
8
- import '@modelcontextprotocol/sdk/client/index.js';
9
-
10
- /**
11
- * Pure derivations over `AgentStats`.
12
- *
13
- * Both helpers are tree-shakeable — import only what you need. They never
14
- * touch agent state, never do I/O, and always operate on the recursive
15
- * `AgentStats` tree returned by `agent.run()`.
16
- */
17
-
18
- /**
19
- * Per-model usage rollup produced by {@link statsByModel}.
20
- *
21
- * `turns` counts the number of `TurnUsage` entries attributed to the model
22
- * across the whole tree (parent loop + every recursively-spawned child).
23
- * Cache and cost numbers are summed from the same set of turns.
24
- */
25
- interface ModelUsage {
26
- input: number;
27
- output: number;
28
- cost: number;
29
- cacheRead: number;
30
- cacheCreation: number;
31
- turns: number;
32
- }
33
- /**
34
- * Depth-first walk over the stats tree, returning every `TurnUsage` entry
35
- * — parent loop first, then each child subtree in completion order.
36
- *
37
- * Closes the cache-token aggregation gap: `TurnUsage.cacheRead` /
38
- * `cacheCreation` live only on per-turn entries, and the top-level
39
- * `AgentStats` deliberately doesn't carry cumulative forms (one source of
40
- * truth, no risk of drift). Anything that needs a tree-wide sum walks
41
- * through this.
42
- */
43
- declare function flattenTurns(stats: AgentStats): TurnUsage[];
44
- /**
45
- * Group cumulative usage by `TurnUsage.modelId`. Each entry sums the input,
46
- * output, cache, cost, and turn-count across every turn the tree attributed
47
- * to that model — naturally handling cross-model runs (vision-fallback,
48
- * model-shifted subagents, mixed-provider workflows).
49
- *
50
- * Turns missing `modelId` (mock providers, providers that don't echo a model
51
- * id) are bucketed under the literal string `'(unknown)'`.
52
- */
53
- declare function statsByModel(stats: AgentStats): Map<string, ModelUsage>;
54
-
55
- export { AgentStats, type ModelUsage, TurnUsage, flattenTurns, statsByModel };
1
+ import { a as ContextCapabilities, c as ExecutionContext, l as ExecutionHandle, o as ContextType, s as ExecResult, t as SandboxProvider, u as SpawnConfig } from "./index-BfSdALzk.js";
2
+ import { At as ToolExecutionMode, Bt as AgentContextExceededError, C as SkillsConfig, Ct as SessionEndStatus, D as SessionRun, Dt as SpawnHookContext, E as SessionData, Et as SessionTurn, Ft as TurnFinishReason, G as StreamOptions, Gt as ClassifiedErrorKind, H as Provider, Ht as AgentToolNotAllowedError, It as TurnUsage, J as ToolSpec, K as ToolCall, Kt as matchesContextExceeded, Lt as toolOutputByteLength, Mt as ToolResultContent, Nt as ToolResultImageContent, O as SessionStore, Ot as StreamHookContext, Pt as ToolResultTextContent, Rt as toolResultToText, St as SessionContentBlock, T as Session, Tt as SessionMessage, U as ProviderCapabilities, Ut as CONTEXT_EXCEEDED_MESSAGE_PATTERNS, Vt as AgentProviderError, W as StreamCallbacks, Wt as ClassifiedError, X as OpenRouterParams, Y as TurnResult, _ as ToolDef, _t as PromptDocumentPart, bt as PromptTextPart, ct as AnthropicParams, d as McpConnection, dt as AgentRunOptions, ft as AgentStats, g as ToolContext, gt as OAuthRefreshHookContext, ht as McpToolHookContext, it as OpenAIParams, j as RemoteStoreOptions, jt as ToolHookContext, kt as ThinkingLevel, mt as McpServerConfig, n as AgentHooks, ot as CerebrasParams, pt as ChildRunStats, q as ToolResult, r as AgentOptions, t as Agent, ut as AgentBehavior, v as ToolMap, vt as PromptImagePart, w as CreateSessionOptions, wt as SessionHookContext, x as SkillResource, xt as RunHookMap, y as SkillConfig, yt as PromptPart, zt as AgentAbortedError } from "./agent-BoV5Twdl.js";
3
+ import { E as InteractionToolOptions, M as flattenTurns, N as statsByModel, P as Preset, f as ChildAgent, j as ModelUsage, m as SpawnToolState, p as SpawnToolOptions, s as ValidationResult } from "./index-28otmfLX.js";
4
+ export { type Agent, AgentAbortedError, type AgentBehavior, AgentContextExceededError, type AgentHooks, type AgentOptions, AgentProviderError, type AgentRunOptions, type AgentStats, AgentToolNotAllowedError, type AnthropicParams, CONTEXT_EXCEEDED_MESSAGE_PATTERNS, type CerebrasParams, type ChildAgent, type ChildRunStats, type ClassifiedError, type ClassifiedErrorKind, type ContextCapabilities, type ContextType, type CreateSessionOptions, type ExecResult, type ExecutionContext, type ExecutionHandle, type InteractionToolOptions, type McpConnection, type McpServerConfig, type McpToolHookContext, type ModelUsage, type OAuthRefreshHookContext, type OpenAIParams, type OpenRouterParams, type Preset, type PromptDocumentPart, type PromptImagePart, type PromptPart, type PromptTextPart, type Provider, type ProviderCapabilities, type RemoteStoreOptions, type RunHookMap, type SandboxProvider, type Session, type SessionContentBlock, type SessionData, type SessionEndStatus, type SessionHookContext, type SessionMessage, type SessionRun, type SessionStore, type SessionTurn, type SkillConfig, type SkillResource, type SkillsConfig, type SpawnConfig, type SpawnHookContext, type SpawnToolOptions, type SpawnToolState, type StreamCallbacks, type StreamHookContext, type StreamOptions, type ThinkingLevel, type ToolCall, type ToolContext, type ToolDef, type ToolExecutionMode, type ToolHookContext, type ToolMap, type ToolResult, type ToolResultContent, type ToolResultImageContent, type ToolResultTextContent, type ToolSpec, type TurnFinishReason, type TurnResult, type TurnUsage, type ValidationResult, flattenTurns, matchesContextExceeded, statsByModel, toolOutputByteLength, toolResultToText };
package/dist/types.js CHANGED
@@ -1,28 +1,4 @@
1
- import {
2
- flattenTurns,
3
- statsByModel
4
- } from "./chunk-IC7FT4OD.js";
5
- import {
6
- toolOutputByteLength,
7
- toolResultToText
8
- } from "./chunk-JH6IAAFA.js";
9
- import {
10
- AgentAbortedError,
11
- AgentContextExceededError,
12
- AgentProviderError,
13
- AgentToolNotAllowedError,
14
- CONTEXT_EXCEEDED_MESSAGE_PATTERNS,
15
- matchesContextExceeded
16
- } from "./chunk-LNN5UTS2.js";
17
- export {
18
- AgentAbortedError,
19
- AgentContextExceededError,
20
- AgentProviderError,
21
- AgentToolNotAllowedError,
22
- CONTEXT_EXCEEDED_MESSAGE_PATTERNS,
23
- flattenTurns,
24
- matchesContextExceeded,
25
- statsByModel,
26
- toolOutputByteLength,
27
- toolResultToText
28
- };
1
+ import { a as CONTEXT_EXCEEDED_MESSAGE_PATTERNS, i as AgentToolNotAllowedError, n as AgentContextExceededError, o as matchesContextExceeded, r as AgentProviderError, t as AgentAbortedError } from "./errors-D1lhd6mX.js";
2
+ import { n as toolResultToText, t as toolOutputByteLength } from "./types-Bx_F8jet.js";
3
+ import { n as statsByModel, t as flattenTurns } from "./stats-DoKUtF5T.js";
4
+ export { AgentAbortedError, AgentContextExceededError, AgentProviderError, AgentToolNotAllowedError, CONTEXT_EXCEEDED_MESSAGE_PATTERNS, flattenTurns, matchesContextExceeded, statsByModel, toolOutputByteLength, toolResultToText };