import { F as SessionData, I as SessionRun, L as SessionStore, On as ToolResultContent, an as McpServerConfig, b as ToolDef, wn as ThinkingLevel, xn as SessionTurn } from "./agent-BHkvYIH9.js";
import { t as Preset } from "./index-C4aT2kO_.js";
import { A as SessionMeta, G as ProviderKey, I as CompletionItem, L as CompletionProvider, M as StreamEvent, O as Picked, P as ActiveTrigger, Q as ProviderDescriptor, R as CompletionReference, W as ProviderAuth, Y as ModelInfo, j as Settings, w as EditOutcome } from "./tool-formatters-D_fX6FGl.js";
import { ReactNode } from "react";

//#region src/chat/agents.d.ts
/**
 * Theme color token used to accent the active profile in the UI (footer
 * badge, picker highlight). Resolved against `ThemeColors` via
 * `useColors()`; falls back to `accent` when omitted.
 */
type AgentAccent = 'brand' | 'accent' | 'warn' | 'model';
/**
 * Resolve a profile's `accent` token to a concrete hex color via the
 * caller's color palette. Renderer-agnostic — accepts the four-role
 * subset of `ThemeColors` that's relevant to accent surfaces, so this
 * helper composes against either `useColors()` (TUI) or a CSS-variable
 * lookup (GUI). Omitted / unknown tokens fall back to `accent`.
 */
declare function accentColor(accent: AgentAccent | undefined, COLOR: {
  brand: string;
  accent: string;
  warn: string;
  model: string;
}): string;
interface AgentProfile {
  /** Stable identifier persisted in `TuiState.lastAgent` and shown in keybindings. */
  id: string;
  /** Human-readable label rendered in the picker and footer badge. */
  label: string;
  /** One-line description shown next to the label in the picker. */
  description: string;
  /**
   * Preset applied to `createAgent()` when this profile is active. Profiles
   * are self-contained: the host's chat-level `preset` (if any) is NOT
   * merged underneath. Hosts that want a shared base across profiles
   * should compose it explicitly in each profile's preset.
   */
  preset: Preset;
  /** Theme token used by the picker / footer badge. Defaults to `'accent'`. */
  accent?: AgentAccent;
}
type AgentRegistry = Readonly<Record<string, AgentProfile>>;
/**
 * Tools the chat layer never persists to disk, regardless of output size.
 *
 * Rationale per entry:
 * - `read_file` — our reader is already paginated (`offset` + `limit`).
 *   Persisting the slice the model asked for and pointing it at the
 *   persisted path is circular; if the model wants more, it pages.
 * - `tool_search` — short metadata envelope. Never large.
 * - `skills_use` / `skills_read` — the `<skill_content>` body is
 *   intentionally part of the prompt; persisting it defeats the purpose.
 * - `present_plan` / `ask_user` — short JSON envelopes (decision +
 *   answers). Never large.
 * - `spawn` — the subagent's final report IS the value of the call;
 *   stubbing it out would defeat the orchestration pattern.
 *
 * Exported so SDK consumers building custom profiles can extend the list
 * (e.g. host-defined `screenshot` tool whose base64 payload should stay
 * inline). Set `behavior.persistExcludeTools` to your own list to fully
 * override.
 */
declare const DEFAULT_PERSIST_EXCLUDE_TOOLS: readonly string[];
/**
 * Tools whose output is exempt from the per-turn `toolOutputBudget` accounting.
 *
 * These tools exist to LOAD context into the conversation — their text output
 * IS the value of the call. Counting it against the budget steers the model
 * away from the very tool it needs to make progress (e.g. a 78 KiB
 * `tool_search` response for a 20-tool Notion MCP server would trigger the
 * "summarize before calling more tools" nudge on the very turn that
 * unlocked those tools, making them effectively unusable).
 *
 * Note this list is intentionally narrower than {@link DEFAULT_PERSIST_EXCLUDE_TOOLS}:
 * `read_file` is NOT exempt — a 200 KiB file load is exactly the case the
 * budget should steer against. Same for `spawn` (a sprawling subagent
 * report should still nudge a consolidation pass).
 *
 * Exported so SDK consumers building custom profiles can extend the list
 * for host-defined context-loading tools.
 */
declare const DEFAULT_BUDGET_EXCLUDE_TOOLS: readonly string[];
/**
 * Build agent — the default profile. Full read/write/shell access plus
 * subagent spawning. Mirrors the legacy `basic` preset so existing TUI
 * behavior is preserved when `agents` is omitted.
 *
 * The system prompt is the cwd-less variant of {@link buildBuildSystem};
 * the TUI overrides it per-session with an env-anchored composition so
 * the prompt carries the current project root + date. SDK consumers
 * calling `createAgent({ ...BUILD_AGENT.preset })` directly get the
 * cwd-less prompt and can wrap their own env section.
 */
declare const BUILD_AGENT: AgentProfile;
/**
 * Plan agent — read-only exploration mode. Locked down to file reading and
 * codebase search; no shell, no edits, no spawning. The system prompt
 * frames the conversation as planning rather than execution so the model
 * outputs proposals instead of attempting mutations.
 */
declare const PLAN_AGENT: AgentProfile;
/**
 * Default registry shipped with `zidane/tui`. Insertion order = picker order.
 * Hosts that want only one profile can pass `{ agents: { build: BUILD_AGENT } }`,
 * or a fully custom registry of their own profiles.
 */
declare const BUILTIN_AGENTS: AgentRegistry;
/** Id of the profile activated on first launch when nothing is persisted. */
declare const DEFAULT_AGENT_ID = "build";
/**
 * Resolve an agent id against a registry with sensible fallbacks: the
 * requested id wins when present, then `defaultId`, then the first key.
 * Returns `null` when the registry is empty (host misconfiguration —
 * the caller should surface this rather than silently failing).
 */
declare function resolveAgentId(registry: AgentRegistry, requestedId: string | undefined, defaultId: string | undefined): string | null;
/**
 * Wrap a legacy single `Preset` into a single-profile registry. Used by
 * `resolveConfig` when the host passed `preset` without `agents`, so older
 * call sites keep working — they get a one-entry registry whose picker is
 * a no-op (only one option).
 */
declare function singleAgentRegistry(preset: Preset): AgentRegistry;
//#endregion
//#region src/chat/hints.d.ts
/**
 * Single hint entry — `<key> <label>`, optionally followed by an inline
 * secondary chord (`<key><extra.key> <label> <extra.label>`) for compound
 * shortcuts like `ctrl+m/n model effort`.
 *
 * Colour fields are advisory hex strings (resolved against the active
 * theme by the renderer); leaving them undefined lets the renderer pick
 * its conventional fallback (e.g. `warn` for keys, `dim` for labels,
 * `mute` for secondary).
 */
interface Hint {
  key: string;
  label: string;
  /** Optional override for the key color. Renderer default: warn. */
  keyColor?: string;
  /** Optional override for the label color. Renderer default: dim. */
  labelColor?: string;
  /**
   * Optional secondary chord + value rendered inline after the primary
   * pair: `<key><extra.key> <label> <extra.label>`. Lets one hint advertise
   * a compound shortcut (e.g. `ctrl+m/n model effort`) without forcing
   * callers into a parallel struct or a custom renderer.
   */
  extra?: {
    key: string;
    label: string; /** Defaults to mute on the renderer side. */
    keyColor?: string; /** Defaults to dim on the renderer side. */
    labelColor?: string;
  };
}
/**
 * Truncate `text` to at most `max` characters, replacing the trailing
 * overflow with `…`. Edge cases:
 *   - `max <= 0` → empty string (no room to render at all).
 *   - `max === 1` → just the ellipsis glyph.
 *   - `text.length <= max` → unchanged.
 *
 * Trailing-style truncation matches the natural read order of titles:
 * the prefix carries enough signal to identify the surface.
 */
declare function truncateTrailing(text: string, max: number): string;
/**
 * Plain-text width estimate for a list of {@link Hint}s rendered as
 * `<key> <label> · <key> <label> · …`. Exported so prompt-box overlays
 * can run the same responsive math as the bottom-bar footer when
 * deciding whether trigger hints fit. Pure / total.
 */
declare function hintsLength(hints: readonly Hint[]): number;
/**
 * Stable empty list so callers can compare by reference. Exported so
 * any consumer that needs to feed "no primary hints" into the same
 * `clipHintsToWidth` / `renderHintSpans` pipeline (e.g. the TUI's
 * minimal UI mode, where the prompt overlay shows only `@` / `/`
 * triggers) reuses the same frozen array instead of allocating a new
 * one each render.
 */
declare const EMPTY_HINTS: readonly Hint[];
/**
 * Return the longest prefix of `hints` whose rendered width fits within
 * `budget`. Used to degrade hint rows gracefully at narrow terminal
 * widths instead of letting an absolutely-positioned hint row wrap
 * mid-segment (which paints the overflow over surrounding borders and
 * looks like garbled glyphs in a TUI).
 *
 * Prefix-only (no reordering, no last-hint priority) so the survivors
 * keep their authored order — the user's muscle memory for "leftmost
 * hint = primary action" stays intact as the terminal shrinks.
 */
declare function clipHintsToWidth(hints: readonly Hint[], budget: number): readonly Hint[];
//#endregion
//#region src/chat/completion.d.ts
/** State surface returned by `useCompletion()`. */
interface CompletionState<TItem = unknown> {
  /** Active trigger, or null when the cursor doesn't sit in a completable span. */
  active: ActiveTrigger<TItem> | null;
  /** Items for the active query. Empty when no trigger is active. */
  items: readonly CompletionItem<TItem>[];
  /** True while the provider's `suggest` promise is pending. */
  loading: boolean;
  /** Index of the highlighted item inside `items`. */
  selectedIndex: number;
  /** Move the highlight. Wraps at the ends. No-op when `items` is empty. */
  selectNext: () => void;
  selectPrev: () => void;
  /**
   * Commit the highlighted item. Returns the new text + cursor pair, or
   * `null` when there's nothing to commit (no active trigger, empty list).
   */
  commit: () => {
    text: string;
    cursor: number;
  } | null;
  /** Force-close the popover until the user types again. */
  dismiss: () => void;
  /** All references in the current buffer, regardless of provider. */
  references: readonly CompletionReference<TItem>[];
}
/**
 * Drive a prompt-completion popover from React state.
 *
 * Inputs are pull-style — the caller owns `text` + `cursor` (typically
 * mirroring an `OpenTUI TextareaRenderable` or a `<textarea>` element) and
 * passes them in each render. Providers are matched by their `trigger`
 * character; the engine picks at most one active provider per cursor
 * position.
 *
 * Asynchronous suggest calls are aborted when the query changes or the
 * popover closes, so providers that hit a backend don't leak.
 */
declare function useCompletion<TItem = unknown>(input: {
  text: string;
  cursor: number;
}, providers: readonly CompletionProvider<TItem>[], options?: {
  maxQueryLength?: number;
}): CompletionState<TItem>;
//#endregion
//#region src/start/shared-options.d.ts
declare const startProviderNames: readonly ["anthropic", "openai", "openrouter", "cerebras", "arcee", "baseten", "local", "openai-compat"];
type StartProviderName = typeof startProviderNames[number];
declare const startPresetNames: readonly ["basic"];
type StartPresetName = typeof startPresetNames[number];
declare const startFormatNames: readonly ["zidane", "provider"];
type StartFormatName = typeof startFormatNames[number];
interface StartLocalOptions {
  system?: string;
  prompt: string;
  model?: string;
  baseURL?: string;
  apiKeyEnv?: string;
  headersEnv?: string;
  extraHeaders?: Record<string, string>;
  vision?: boolean;
  imageInToolResult?: boolean;
  temperature?: number;
  seed?: number;
  preset: StartPresetName;
  thinking: ThinkingLevel;
  provider: StartProviderName;
  context: string;
  image?: string;
  cwd?: string;
  mcp?: McpServerConfig[];
  sessionDb?: string;
  sessionId?: string;
  json: boolean;
  streamJson: boolean;
  format: StartFormatName;
}
//#endregion
//#region src/chat/keybindings.d.ts
/**
 * Keybindings — a single source of truth for the app's global shortcuts.
 *
 * Every customizable action is declared once in {@link KEYBINDING_DEFS}
 * with its default trigger, label, and description. The TUI imports
 * `DEFAULT_KEYBINDINGS` for the baked-in defaults; on launch we load
 * `<userDir>/keybindings.json` (when present) and merge user overrides
 * on top so the user can rebind any action without recompiling.
 *
 * Scope (deliberate):
 *
 *   - We expose only the **global, action-opening** shortcuts here —
 *     "open the Settings modal", "cycle agent profile", "enter
 *     select-turn mode", etc. Modal-internal letter shortcuts (`f`
 *     fork, `d` delete, `c` copy on the turn-details modal) and
 *     universal navigation (`↑/↓/↵/⎋`) stay hardcoded — they live
 *     too close to a renderable's input contract to remap safely.
 *
 *   - Storage lives at the **user level** (`<userDir>/keybindings.json`),
 *     never inside a project's `.{prefix}/`. Shortcut muscle memory is
 *     a per-user concern, not a per-repo one.
 *
 * Format on disk: JSON with `//` and `/* … *\/` comments allowed
 * (parsed via a small JSONC stripper). Each entry maps an action id
 * to a binding spec string. Specs are parsed by {@link parseBindingSpec};
 * unknown / malformed entries fall back to the default for that action
 * (with a debug log under `ZIDANE_DEBUG`) so a syntax slip never bricks
 * the TUI.
 */
/**
 * Every action the keybindings system can drive. Adding a new action
 * here forces every consumer (TUI keyboard handler, hint rows, settings
 * UI) to handle it, and ships a default that the user can override.
 */
type KeyAction = 'openSettings' | 'openSessionDetails' | 'newSession' | 'openModelPicker' | 'openEffortPicker' | 'openTodos' | 'openContextPanel' | 'openKeybindings' | 'cycleAgent' | 'enterSelectTurnMode' | 'cancelToolCall' | 'changeCwd' | 'enterQueueSelection' | 'pushQueuedMessage' | 'dropQueuedMessage' | 'turnFork' | 'turnDelete' | 'turnCopy' | 'turnEdit' | 'sessionDelete' | 'sessionCopyId' | 'sessionGenerateTitle' | 'sessionExportMarkdown' | 'sessionExportJson' | 'sessionCompact';
/** Resolved keybindings — every action has a current spec string. */
type KeyBindings = Record<KeyAction, string>;
/**
 * Logical grouping for one action — used by the keybindings panel to
 * render section headers. Stable strings (not an enum) so future
 * groups can be added without churning every existing entry.
 */
type KeyBindingGroup = 'Global' | 'Message queue' | 'Turn details' | 'Session details';
/** Static metadata for one action — used by the loader, hint rows, settings UI. */
interface KeyBindingDef {
  /** Stable identifier — JSON key + TS union member. */
  action: KeyAction;
  /** Default trigger spec (e.g. `'ctrl+o'`). */
  default: string;
  /** Short label used in shortcut hint rows. */
  label: string;
  /** Longer description for the settings UI and the annotated JSON file. */
  description: string;
  /**
   * Group label rendered as a section header in the keybindings panel.
   * Items are listed in {@link KEYBINDING_DEFS} order; the panel just
   * inserts a header whenever this field changes between adjacent
   * entries — so groups stay contiguous and the order here is the
   * order on screen.
   */
  group: KeyBindingGroup;
}
/**
 * The canonical action catalog. Order is the source of truth for the
 * generated keybindings file and the settings UI.
 */
declare const KEYBINDING_DEFS: readonly KeyBindingDef[];
/** Index by action for O(1) lookup of label / description / default. */
declare const KEYBINDING_DEF_BY_ACTION: Readonly<Record<KeyAction, KeyBindingDef>>;
/** Defaults, derived from {@link KEYBINDING_DEFS} so the two never drift. */
declare const DEFAULT_KEYBINDINGS: KeyBindings;
/** Parsed shape of a `"ctrl+shift+o"` spec. */
interface ParsedBinding {
  ctrl: boolean;
  shift: boolean;
  alt: boolean;
  meta: boolean;
  /** The key name (e.g. `'o'`, `'tab'`, `'escape'`). Always lowercase. */
  name: string;
}
/**
 * Parse a `'ctrl+shift+o'`-style spec into structured fields. Returns
 * `null` for empty / malformed inputs; the caller falls back to the
 * default for that action.
 *
 * Tolerant by design:
 *   - Case-insensitive (`Ctrl+O` → same as `ctrl+o`).
 *   - Modifier order doesn't matter (`shift+ctrl+o` works).
 *   - Mixed `+` and `-` separators allowed (`ctrl-o`, `ctrl+o`).
 *   - Trailing / leading whitespace is stripped.
 *   - Duplicate modifiers collapse silently.
 */
declare function parseBindingSpec(spec: string | undefined | null): ParsedBinding | null;
/**
 * Test a KeyEvent against a parsed-or-string binding. Accepts either
 * form so callers can pre-parse (hot path) or pass the raw spec for
 * one-off checks.
 *
 * Modifier semantics: ALL declared modifiers must be pressed AND no
 * extra modifiers may be pressed. So `'o'` does NOT match `ctrl+o`,
 * and `'ctrl+o'` does NOT match `ctrl+shift+o`. This avoids surprises
 * where a binding that should be specific accidentally fires for a
 * superset of key combinations.
 */
declare function matchesBinding(event: {
  name?: string;
  ctrl?: boolean;
  shift?: boolean;
  meta?: boolean;
  option?: boolean;
  alt?: boolean;
}, spec: string | ParsedBinding): boolean;
/**
 * Render a binding spec (`"ctrl+return"`, `"backspace"`, `"delete"`) as a
 * compact display string the user recognizes at a glance. Substitutes
 * arrow / enter / backspace glyphs for their verbose names so the hint
 * row stays narrow; modifier names + plain keys pass through unchanged.
 *
 * Empty / missing specs return an empty string — callers decide whether
 * to render a placeholder (e.g. `'—'` for an unbound action in the
 * keybindings panel) or hide the surface entirely (e.g. hint rows that
 * fall back to a different chord when the binding is empty).
 */
declare function formatBindingForDisplay(spec: string | null | undefined): string;
/**
 * Merge a partial map of user overrides into the defaults. Unknown
 * action keys are dropped (a future TUI version may have retired the
 * action); invalid spec strings (parse fails) fall back to the default
 * for that action.
 */
declare function mergeKeybindings(overrides: Readonly<Record<string, unknown>> | null | undefined): KeyBindings;
/** Path of the user-level keybindings file. */
declare function keybindingsPath(userDir: string): string;
/**
 * One contiguous section of the catalog — every action in a single
 * `group` rendered together with the section header inserted once.
 * Adjacent sections with the same group are NOT merged: catalog order
 * wins so the on-screen story matches the on-disk file.
 */
interface KeyBindingSection {
  group: KeyBindingDef['group'];
  rows: ReadonlyArray<{
    def: KeyBindingDef;
    spec: string;
  }>;
}
/**
 * Walk {@link KEYBINDING_DEFS} in order and bucket rows into contiguous
 * sections by `group`. Shared between the standalone `KeybindingsModal`
 * and the `Keybindings` tab inside `SettingsModal` so both surfaces
 * render the same catalog with the same ordering rules.
 */
declare function groupBindings(bindings: KeyBindings): readonly KeyBindingSection[];
/**
 * Width (in cells) of the fixed key column used by every binding-row
 * renderer. Derived once from {@link KEYBINDING_DEFS} so a new action
 * with a wider default spec (`ctrl+shift+x`, …) automatically grows
 * the column instead of truncating the label.
 */
declare const KEYBINDING_KEY_COL_WIDTH: number;
/**
 * Load + merge the user's keybindings file (when present). Always
 * returns a fully-resolved {@link KeyBindings} — defaults fill in for
 * any missing / invalid entries so consumers can treat the result as
 * total.
 *
 * Failure modes (all tolerated, all fall back to defaults):
 *   - File missing
 *   - File not valid JSON / JSONC (after comment stripping)
 *   - Top-level is not an object
 *   - Individual entries have invalid spec strings
 */
declare function readKeybindings(userDir: string): KeyBindings;
/**
 * Create the user-level `keybindings.json` if it doesn't exist,
 * prefilled with every default. Returns the absolute file path either
 * way so the caller can open it in `$EDITOR`. Idempotent: an existing
 * user file is NEVER overwritten — the user's customizations are
 * sacred.
 */
declare function ensureKeybindingsFile(userDir: string): string;
/**
 * Strip `//` line comments and `/* … *\/` block comments from a JSONC
 * source string. Stays out of string literals so a `"foo // bar"` value
 * keeps its slashes. Exported for unit-tests; the only runtime caller
 * is {@link readKeybindings}.
 *
 * Trade-off: this is a tiny state-machine, not a full JSONC parser. It
 * doesn't understand `\\` outside strings (irrelevant for JSON) and
 * doesn't track line numbers in error messages. Good enough for a
 * config file — corruption in production gracefully falls back to the
 * defaults via the try/catch in `readKeybindings`.
 */
declare function stripJsonComments(input: string): string;
//#endregion
//#region src/chat/store.d.ts
interface TuiState {
  lastProvider?: ProviderKey;
  lastSessionId?: string;
  /** Per-provider last-used model id. */
  lastModelByProvider?: Partial<Record<ProviderKey, string>>;
  /**
   * Per-model last-picked reasoning effort. Keyed by model id (not provider)
   * so switching the same model across providers preserves the user's
   * intent. Models without reasoning support are absent from the map.
   */
  lastEffortByModel?: Record<string, ThinkingLevel>;
  /**
   * Per-model last-picked custom options (e.g. `{ fast: true }`). Keyed by
   * model id like {@link lastEffortByModel}, so a toggle follows the model
   * across providers. Models with no options are absent from the map.
   */
  lastModelOptionsByModel?: Record<string, Record<string, boolean>>;
  /**
   * Last-active agent profile id (see {@link AgentRegistry}). Resumed on
   * launch so a returning user lands back in the same mode (Build / Plan /
   * a host profile). Falls back to the host's `defaultAgent` then the first
   * registry key if the id is no longer registered.
   */
  lastAgent?: string;
  /** User-toggled transcript filters. Persisted so they outlive a launch. */
  settings?: Partial<Settings>;
}
interface StateStoreApi {
  load: () => TuiState;
  save: (state: TuiState) => void;
}
declare function createStateStore(path: string): StateStoreApi;
declare function loadState(path: string): TuiState;
declare function saveState(path: string, state: TuiState): void;
/**
 * Load every session and project it to the compact `SessionMeta` shape used by
 * the picker. Sorted by recency via the underlying store's `list()` contract
 * (sqlite store returns by `updated_at DESC`).
 *
 * Robust to per-row failures: `Promise.allSettled` so a single corrupt or
 * unreadable row (malformed JSON, partial write, transient I/O error)
 * doesn't take down the entire picker. Failed rows are silently skipped;
 * a stderr line is emitted under `ZIDANE_DEBUG` for diagnosis.
 */
declare function listSessionMeta(store: SessionStore, filter?: {
  /**
   * Restrict to sessions belonging to this project. Omit to ignore
   * the axis; pass `null` to ask for untagged (legacy) sessions
   * specifically. The TUI defaults to passing the current git root /
   * cwd here so each project sees only its own conversations.
   */
  projectRoot?: string | null;
}): Promise<SessionMeta[]>;
/** Derive a short title from the first user message — returns null when empty. */
declare function titleFromTurns(turns: SessionTurn[]): string | null;
/**
 * Display title for a session — preferred sources, in order:
 *
 *   1. `metadata.title` if it's a non-empty string (typically set by
 *      the session-details modal's "generate title" action).
 *   2. {@link titleFromTurns} — the first user message, one-line + clipped.
 *   3. The literal `'untitled'`.
 *
 * Total / pure / defensive on non-string metadata values.
 */
declare function deriveSessionTitle(turns: SessionTurn[], metadata?: Record<string, unknown>): string;
/**
 * Replay persisted turns as a viewable transcript. Mirrors the event shape
 * produced live by the agent hooks so loaded and streaming history render
 * identically — including subagent ancestry when `runs` is supplied.
 *
 * Subagent reconstruction:
 *   - Every turn carries a `runId`. We look that up in `runs` to get the
 *     run's `depth` and tag the resulting events with `{ depth, childId }`
 *     — the same shape the live `child:*` bubble hooks produce.
 *   - We synthesize `spawn-start` / `spawn-end` markers at each child-run
 *     boundary so the transcript reads the same as a live run did
 *     (`🌱 [run-id] task` … child events … `🌳 [run-id] done · tokens`).
 *   - For child runs (`depth > 0`), the user-role "task" text is suppressed
 *     because `spawn-start` already shows it.
 *
 * Without `runs` (legacy callers / tests), the function falls back to the
 * old behavior: depth-0 events with no subagent grouping.
 */
declare function eventsFromTurns(turns: SessionTurn[], runs?: readonly SessionRun[]): StreamEvent[];
declare function toolCallPreview(name: string, input: Record<string, unknown>): string;
/**
 * Update the trailing `'tool'` event matching `callId` so its
 * `edit.outcomes` reflects the canonical merged outcomes the chat
 * layer's `tool:transform` hook just computed. Without this, the live
 * diff badges stay frozen at whatever the gate set (approval-side
 * outcomes) and never reflect body-side failures from `multi_edit`'s
 * best-effort run — only a session reload re-attaches the merged
 * outcomes via {@link eventsFromTurns}.
 *
 * Walks back-to-front because the matching event is overwhelmingly the
 * most recent (we're updating right after the body returned). Returns
 * the same reference when no match is found (or the event already
 * carries the target outcomes), so React skips the re-render.
 */
declare function updateToolEventOutcomes(events: readonly StreamEvent[], callId: string, outcomes: readonly EditOutcome[]): StreamEvent[];
/** Render tool output as plain text, whether it's a string or structured content. */
declare function toolResultText(output: string | ToolResultContent[]): string;
/**
 * Strip the `Tokens: …` line from a spawn tool-result. The spawn-end marker
 * displayed right above already shows the same stats; keeping the line in the
 * rendered tool-result body just produces a visible duplicate (and, on
 * reloaded pre-fix sessions, an *inconsistent* duplicate — the persisted line
 * uses the old `13 in / 4075 out` shape while the freshly synthesized
 * spawn-end uses the cache-aware `in 92615 (cache 92602) / 4075 out` shape).
 *
 * Display-only: the persisted tool_result content is untouched, so the LLM
 * still sees the full string in its context window. Anchored to start-of-line
 * and matches both `Tokens: 13 in / 4075 out` (legacy) and `Tokens: in 13 …`
 * (post-`formatTokenUsage`) shapes.
 */
declare function stripSpawnTokensLine(text: string): string;
/**
 * Resolve the top margin (in rows) for an event given the one rendered
 * just before it. Context-aware rules:
 *
 *   - A `tool` / `tool-result` event right after another
 *     `tool` / `tool-result` collapses to a tight list — call→result
 *     pairs and back-to-back calls read as one logical block.
 *   - Consecutive `task-notification` banners stack tight — a burst of
 *     background-task completions reads as one log column rather than
 *     scattered banners.
 *
 * NB: parent-level events (`depth === 0`) following a subagent block
 * (`depth > 0`) get their normal default margin. An earlier revision
 * collapsed this transition to 0 on the assumption that the subagent's
 * `🌳` end-marker provided enough visual separation — it didn't. A
 * 2-cell emoji on the same row as the close marker doesn't create a
 * vertical break, and in hide-subagent-output mode the SubagentBlock
 * box isn't rendered either, so the parent's follow-up markdown was
 * glued directly against the close line. Subagents should READ like
 * tool calls: open with breathing room, close tight inside, and open
 * a clean gap before the parent's next thought — that's the contract
 * the tool-call → markdown transition uses, mirrored here.
 *
 * Renderer-agnostic — TUI uses it as Yoga `marginTop`; a CSS host can
 * use the same number as the row's top margin in `em` / `rem`.
 */
declare function marginTopFor(event: StreamEvent, previous: StreamEvent | undefined): number;
/** Effective context size of the most recent assistant turn — drives the footer indicator. */
/**
 * Walk from the end of `turns` and return the cache-aware input-token total
 * of the most recent **parent** (depth-0) assistant turn. Subagent turns
 * are skipped because their context window is the child's, not the parent's
 * — surfacing a subagent's `usage.input` as the footer's "context used"
 * after a session ends mid-spawn would be misleading. The next prompt
 * feeds the parent, so the parent's last view is what matters.
 *
 * Zero-context placeholder turns are also skipped. `executeTurn`
 * synthesizes an assistant turn with `usage: { input: 0, output: 0 }`
 * when the provider stream throws / aborts before any usage is reported
 * (see `src/loop.ts`'s catch path) — that turn is purely a shape
 * contract for resume, not a real context-window measurement. Without
 * the skip, an abort mid-stream shadows the prior real turn and the
 * footer reads `ctx 0` on the next session load even though earlier
 * turns accumulated meaningful context (and `run.cost`).
 *
 * `runs` is optional for backwards compatibility (older call sites and
 * tests that don't have ancestry info). Without it, this falls back to the
 * pre-fix behavior — depth is unknown so every assistant turn qualifies.
 */
declare function lastContextSizeFromTurns(turns: SessionTurn[], runs?: readonly SessionRun[]): number;
/**
 * Sum provider-reported USD cost across every run in a session. Used to
 * seed the live cost indicator on session activation. Returns 0 when no
 * run carries a `cost` (most providers — only OpenRouter reports today).
 */
declare function sumRunCosts(runs: readonly SessionRun[]): number;
//#endregion
//#region src/chat/xdg.d.ts
/**
 * XDG Base Directory resolution for zidane.
 *
 * The user's storage spreads across four logical slots:
 *
 *   - **config** — credentials, mcp-credentials, keybindings, config.json,
 *     mcps.json, skills (user-editable surface).
 *   - **data**   — sessions.db (long-lived chat history).
 *   - **state**  — state.json (per-machine UI bookkeeping: last provider,
 *     last model, last agent, settings).
 *   - **cache**  — persisted tool results, background-task logs,
 *     auto-update registry cache (regenerable, safe to wipe).
 *
 * Historically all four collapsed to `~/.zidane/`. This module adds
 * XDG-Base-Directory-aware defaults so a Linux user with no existing
 * `~/.zidane/` lands in the conventional `~/.config/zidane/`,
 * `~/.local/share/zidane/`, `~/.local/state/zidane/`, `~/.cache/zidane/`
 * trio. Existing setups (anyone with a `~/.zidane/` directory or
 * `ZIDANE_STORAGE_DIR` set) keep the single-dir layout unchanged.
 *
 * Resolution flow — applied in this order:
 *
 *   1. Caller passed an explicit `storageDir` (`ChatOptions.storageDir`)
 *      OR `ZIDANE_STORAGE_DIR` is set → **legacy-explicit** single-dir
 *      layout under `<storageDir>/<prefix>`. The four slots collapse to
 *      the same directory.
 *   2. `~/<prefix>/` already exists on disk → **legacy-existing**
 *      single-dir layout under that directory. Upgrades never silently
 *      shift files around.
 *   3. Any `XDG_*_HOME` env var is set OR `process.platform === 'linux'`
 *      → **XDG split**. Each slot lands in its own dir. Per-slot
 *      `ZIDANE_{CONFIG,DATA,CACHE,STATE}_DIR` overrides beat the XDG
 *      vars for surgical tweaks.
 *   4. Otherwise (macOS/Windows, no XDG signal, no existing dir) →
 *      **default** single-dir layout at `~/<prefix>/`. Day-one behavior
 *      on these platforms is unchanged.
 *
 * The `userDir` legacy alias always equals `configDir` so existing code
 * that still references `paths.userDir` keeps reading credentials, mcps,
 * etc. from the right place.
 */
/** The four slot kinds we resolve. Names match the XDG spec. */
type StorageSlot = 'config' | 'data' | 'state' | 'cache';
/** Which resolution branch produced the layout — surfaced for debugging + tests. */
type StorageMode = 'legacy-explicit' | 'legacy-existing' | 'xdg' | 'default';
/**
 * Per-slot directories. In legacy / default modes the four fields all
 * point at the same path; in XDG mode they fan out into their
 * spec-conventional locations.
 *
 * `mode` carries the resolution branch so a host that wants to print
 * a startup banner ("zidane is using XDG paths") can detect the
 * layout without re-running the logic.
 */
interface StorageDirs {
  configDir: string;
  dataDir: string;
  stateDir: string;
  cacheDir: string;
  mode: StorageMode;
}
interface ResolveStorageDirsOptions {
  /** Storage prefix. Leading dot tolerated. Default: `'.zidane'`. */
  prefix?: string;
  /**
   * Explicit storage root (`ChatOptions.storageDir`). When set, forces
   * legacy-explicit mode regardless of the platform / XDG signals.
   */
  storageDir?: string;
  /** Override `os.homedir()` for tests. */
  home?: string;
  /** Override `process.platform` for tests. */
  platform?: NodeJS.Platform;
  /** Override `process.env` for tests. */
  env?: Readonly<Record<string, string | undefined>>;
}
/**
 * Resolve the four storage slots according to the precedence above.
 *
 * Pure aside from one filesystem probe (`existsSync(home/<prefix>)`)
 * — the same probe `resolveStoragePaths` would do anyway when
 * computing `paths.userDir`. No directories are created here; the
 * relevant write paths (state store, credentials writer, persist
 * dir) handle their own lazy `mkdirSync` already.
 */
declare function resolveStorageDirs(opts?: ResolveStorageDirsOptions): StorageDirs;
//#endregion
//#region src/chat/config.d.ts
/**
 * Provider registry — a map keyed by `descriptor.key`. Passed verbatim to the
 * chat engine; there is no implicit merge with built-ins. Hosts that want the
 * four default providers spread {@link BUILTIN_PROVIDERS}; hosts that only
 * want their own pass exactly their own.
 */
type ProviderRegistry = Readonly<Record<string, ProviderDescriptor>>;
/**
 * Options accepted by `resolveConfig()` (and, transitively, by `runTui()` and
 * any future GUI launcher). Every field is optional — defaults boot a working
 * chat against the built-in providers with sessions stored under `~/.zidane/`.
 */
interface ChatOptions {
  /**
   * Directory name used under `storageDir` for sessions + state.
   * Default: `'.zidane'` → `~/.zidane/sessions.db` + `~/.zidane/state.json`.
   * Pass e.g. `'.myapp'` for `~/.myapp/...`, or `'myapp'` for a visible dir.
   */
  prefix?: string;
  /**
   * Storage root. Defaults to the user's home directory. Combined with
   * `prefix` to form the data directory.
   */
  storageDir?: string;
  /**
   * Provider registry. Each value is a {@link ProviderDescriptor} carrying
   * label + factory + credential metadata. **No automatic merge** — hosts
   * pass exactly what they want.
   *
   * - Omitted → {@link BUILTIN_PROVIDERS} (anthropic + openai + openrouter + cerebras).
   * - `providers: BUILTIN_PROVIDERS` → same as omitted, but explicit.
   * - `providers: { anthropic: anthropicDescriptor }` → only Anthropic.
   * - `providers: { ...BUILTIN_PROVIDERS, mine: customDescriptor }` → built-ins + custom.
   * - `providers: { mine: customDescriptor }` → custom-only, **no built-ins**.
   */
  providers?: ProviderRegistry;
  /**
   * Legacy single-agent preset (tools, system prompt, behavior). When set
   * **without** {@link ChatOptions.agents}, the resolver wraps this preset
   * into a one-entry {@link AgentRegistry} keyed `default` so existing
   * callers keep working and the picker is effectively hidden.
   *
   * Prefer `agents` for any multi-profile setup. Passing both throws.
   */
  preset?: Preset;
  /**
   * Agent profile registry — keyed by `profile.id`. Each profile bundles a
   * preset with display metadata (label, description, accent). The TUI
   * shows a picker (Ctrl+A) when more than one profile is registered.
   *
   * - Omitted (and `preset` omitted) → {@link BUILTIN_AGENTS} (Build + Plan).
   * - Omitted but `preset` provided → single-entry registry around the preset.
   * - Provided → host owns the registry; pass exactly what you want.
   *
   * Hosts mixing built-ins with their own typically spread:
   * `agents: { ...BUILTIN_AGENTS, debug: myDebugProfile }`.
   */
  agents?: AgentRegistry;
  /**
   * Id of the profile activated on first launch. Resolved against `agents`;
   * unknown ids fall through to the registry's first key. Persisted
   * `state.lastAgent` overrides this on subsequent launches.
   */
  defaultAgent?: string;
  /**
   * Session store — either an instance or a factory called with the
   * resolved storage paths. **Required** at the chat layer so this module
   * stays platform-agnostic (no `bun:sqlite` baked in). Terminal hosts use
   * `createTuiStore` from `zidane/session/sqlite`; a future GUI host can
   * supply a different adapter (IndexedDB, remote API, in-memory) without
   * touching this layer.
   *
   * ```ts
   * import { createTuiStore } from 'zidane/session/sqlite'
   *
   * // Either pass a factory…
   * resolveConfig({ store: paths => createTuiStore(paths.db) })
   *
   * // …or an instance built ahead of time.
   * const store = createTuiStore('/path/to/sessions.db')
   * resolveConfig({ store })
   * ```
   */
  store: SessionStore | ((paths: ResolvedPaths) => SessionStore);
  /**
   * Project-scoped session storage. Off by default; when it resolves
   * to `true` and the launch happens inside a git repo, zidane
   * auto-creates `<git-root>/.{prefix}/` and stores `sessions.db` +
   * `state.json` there — so sessions, mcps.json, and skills all live
   * alongside the project they belong to. Credentials and the
   * cross-project safelist stay at `~/.{prefix}/` regardless.
   *
   * Precedence (most → least specific): `options.projectDb` >
   * `ZIDANE_PROJECT_DB` env var (`0`/`false`/`off`/`no` → off,
   * `1`/`true`/`on`/`yes` → on) > `~/.{prefix}/config.json`
   * `projectDb` > `false`. Note the host option beats the env var —
   * setting `ZIDANE_PROJECT_DB` has no effect when the host passes an
   * explicit `projectDb`.
   *
   * When it resolves to `false`, OR when not inside a git repo,
   * everything lives at `~/.{prefix}/` — the classic single-store
   * layout.
   */
  projectDb?: boolean;
  /**
   * Override for the project root. Defaults to `process.cwd()`. Hosts
   * embedding the TUI from a non-CLI context (tests, daemons) can pin
   * the cwd here so the git-root walk produces a stable result. Has no
   * effect when `projectDb` resolves to `false`.
   */
  cwd?: string;
  /**
   * Optional one-shot launch request used by CLI shells such as `zidane run`.
   * The TUI opens/creates the requested session and submits the prompt after
   * the first provider/session activation, while keeping the normal renderer
   * and persistence behavior.
   */
  launch?: StartLocalOptions;
  /**
   * Auto-update wiring. When set, the App quietly checks the npm registry
   * for a newer release of `packageName` (matching the host's package),
   * caches the result for a day under `userDir/`, and surfaces a passive
   * `↑ vX.Y.Z` chip in the footer when one is available.
   *
   * Consumers wrapping `runTui` for their own CLI inline their own
   * `package.json` version at build time and pass it here. The bundled
   * `zidane-tui` binary wires this automatically in `tui/src/cli.ts`.
   *
   * Defaults to `null` — no auto-update plumbing, no network calls,
   * no chip.
   */
  autoUpdate?: AutoUpdateConfig | null;
}
/**
 * Host-supplied auto-update settings. The check itself is gated on
 * `Settings.checkForUpdates`; passing a config wires the React plumbing.
 *
 * Hosts that want to publish their own consumer-facing package can pass
 * different values without forking anything — the registry / channel /
 * platform-package-prefix are all knobs.
 */
interface AutoUpdateConfig {
  /** Top-level npm package the user installs (`zidane-tui`, `my-app`, …). */
  packageName: string;
  /** Inlined at build time — usually `pkg.version`. */
  currentVersion: string;
  /** dist-tag. Defaults to `'latest'`. */
  channel?: string;
  /** Registry base URL. Defaults to `'https://registry.npmjs.org'`. */
  registry?: string;
  /**
   * Override the read-through cache lifetime in ms for the in-TUI hook.
   * Defaults to `0` — every TUI launch fetches fresh, so the chip
   * never lies. Pass a positive value (e.g. `3_600_000` for 1 h) to
   * throttle the registry-poll rate at the cost of staleness.
   *
   * The on-disk cache is always WRITTEN, regardless of this value, so
   * `zidane upgrade` and other tooling read paths benefit immediately.
   */
  cacheTtlMs?: number;
  /**
   * Platform-package prefix used by the in-place self-update flow when
   * resolving the per-arch tarball name. Defaults to `'<packageName>-'`
   * (i.e. `zidane-tui-darwin-arm64` when `packageName === 'zidane-tui'`).
   * Override only when your platform packages don't follow the `<name>-<key>`
   * convention.
   */
  platformPackagePrefix?: string;
}
/** Resolved on-disk layout produced by {@link resolveConfig}. */
interface ResolvedPaths {
  /**
   * Effective data root — equals `userDir` (configDir) when project
   * mode is off, `projectDir` when it's on. Kept for callers that
   * wrote against the pre-project-db API; new code should reference
   * the explicit slots (`configDir` / `dataDir` / `cacheDir` /
   * `stateDir`) or `projectDir` / `db` / `state` instead.
   */
  dir: string;
  /**
   * User-scoped config root — credentials, mcp-credentials, keybindings,
   * `config.json`, `mcps.json`, skills. Legacy alias for `configDir`;
   * existing call sites still reference `userDir`. In XDG mode this
   * lands at `$XDG_CONFIG_HOME/zidane`; in legacy mode the four slots
   * (config / data / state / cache) collapse to a single
   * `<storageDir>/<prefix>` directory.
   */
  userDir: string;
  /**
   * Project-scoped data root. Set to `<git-root>/<prefix>` when project
   * mode resolved to ON and a git root was found. `null` otherwise.
   */
  projectDir: string | null;
  /** Resolved `sessions.db` path. Lives under `projectDir` when active, else `dataDir`. */
  db: string;
  /** Resolved `state.json` path. Lives under `projectDir` when active, else `stateDir`. */
  state: string;
  /**
   * XDG config slot — `$XDG_CONFIG_HOME/zidane` on a fresh Linux install,
   * `<storageDir>/<prefix>` (e.g. `~/.zidane/`) in legacy mode. Holds
   * user-editable surface (`credentials.json`, `mcps.json`, …).
   */
  configDir: string;
  /**
   * XDG data slot — `$XDG_DATA_HOME/zidane` on Linux, single-dir
   * elsewhere. Holds long-lived chat history (`sessions.db`).
   */
  dataDir: string;
  /**
   * XDG state slot — `$XDG_STATE_HOME/zidane` on Linux, single-dir
   * elsewhere. Holds per-machine UI bookkeeping (`state.json`).
   */
  stateDir: string;
  /**
   * XDG cache slot — `$XDG_CACHE_HOME/zidane` on Linux, single-dir
   * elsewhere. Holds regenerable data: persisted tool results,
   * background-task logs, auto-update registry cache.
   */
  cacheDir: string;
  /**
   * Which resolution branch produced the layout. `'xdg'` when split
   * across the four XDG dirs; one of `'legacy-explicit'`,
   * `'legacy-existing'`, or `'default'` when the slots collapse to a
   * single directory. Surfaced for diagnostics + tests.
   */
  storageMode: StorageMode;
}
interface ResolvedConfig {
  prefix: string;
  storageDir: string;
  paths: ResolvedPaths;
  providers: ProviderRegistry;
  /**
   * Resolved profile registry. Always non-empty; the resolver throws when
   * the host passes an empty `agents` map (same contract as `providers`).
   */
  agents: AgentRegistry;
  /** Id of the profile to activate on mount. Guaranteed to key into `agents`. */
  initialAgentId: string;
  store: SessionStore;
  stateStore: StateStoreApi;
  /** Lookup by `ProviderKey` returning the available models for the picker. */
  modelsFor: (key: ProviderKey) => readonly ModelInfo[];
  initialState: TuiState;
  initialSettings: Partial<Settings>;
  resumeProvider: ProviderAuth | null;
  initialPicked: Picked | null;
  /**
   * Effective keybindings — defaults merged with the user-level
   * `<userDir>/keybindings.json` (when present). Always fully-resolved
   * so consumers can treat the record as total. See `./keybindings.ts`
   * for the action catalog + spec format.
   */
  keybindings: KeyBindings;
  /**
   * Resolved auto-update knobs (forwarded verbatim from `ChatOptions.autoUpdate`)
   * — `null` when the host didn't wire one. The TUI feeds this into
   * `useUpdateCheck` so the footer chip + the `zidane upgrade` subcommand
   * share a single source of truth.
   */
  autoUpdate: AutoUpdateConfig | null;
  /** One-shot launch request forwarded from {@link ChatOptions.launch}. */
  launch: StartLocalOptions | null;
}
/**
 * Resolve user options into a fully-bound runtime config.
 *
 * NOT pure: besides disk reads, this mutates `process.env` so the harness
 * providers pick up the chat engine's credential store — it sets
 * `ZIDANE_CREDENTIALS_PATH` and reconciles stored API keys / OAuth entries
 * into env via `applyApiKeyEnv` (which can overwrite or delete ambient keys
 * like `ANTHROPIC_API_KEY`; see its JSDoc for the precedence rationale).
 */
declare function resolveConfig(options: ChatOptions): ResolvedConfig;
/**
 * Compute the on-disk layout for a single TUI session. Public so hosts
 * (CLI tools, tests, scripted bootstraps) can ask "where will sessions
 * land?" without going through `resolveConfig`'s full machinery.
 *
 * Storage-slot resolution (config / data / state / cache) is delegated
 * to {@link resolveStorageDirs}. In legacy modes the four slots collapse
 * to a single `<storageDir>/<prefix>` directory; in XDG mode they fan
 * out into `$XDG_CONFIG_HOME/zidane`, `$XDG_DATA_HOME/zidane`,
 * `$XDG_STATE_HOME/zidane`, `$XDG_CACHE_HOME/zidane`. See the module
 * doc on `./xdg.ts` for the full precedence chain.
 *
 * Decision logic for project mode (least → most specific):
 *
 *   1. Default: OFF — sessions live in the user-scoped `dataDir`
 *      and get scoped per-project via the `SessionData.projectRoot` tag.
 *   2. `<configDir>/config.json` `projectDb` flag (when present).
 *   3. `ZIDANE_PROJECT_DB` env var (`'0'` / `'false'` / `'off'` → off,
 *      anything else true if set).
 *   4. `options.projectDb` (host call site).
 *
 * Project mode activates only when the resolution above lands on
 * `true` AND a git repo is found above `cwd`. Outside git or with the
 * flag off, sessions + state stay at the user-scoped slots.
 *
 * Auto-creates the project `.{prefix}/` directory when mode resolves
 * to ON (silent — mirrors `ensureStateDir`). User slots are created
 * lazily by the state-store + credentials writers; we don't pre-touch
 * them here so a read-only invocation stays read-only.
 */
declare function resolveStoragePaths(opts: {
  prefix: string; /** Explicit storage root. When set, forces legacy-explicit mode. */
  storageDir?: string;
  cwd: string; /** Host override for project mode. Beats the user-config + env-var sources. */
  projectDb?: boolean;
}): ResolvedPaths;
//#endregion
//#region src/chat/mcps-discovery.d.ts
/**
 * One entry in the discovered list. `path` is the source file (one of
 * the `mcp(s).json` variants under `.agents/` or `.{prefix}/`) so a
 * Settings picker can show "Defined in ~/.zidane/mcp.json" tooltips
 * and the user can tell which file declared what.
 */
interface DiscoveredMcp {
  config: McpServerConfig;
  source: 'project' | 'user';
  path: string;
}
/**
 * Search order for MCP config files — see {@link projectUserPaths}.
 *
 * Both `mcps.json` (zidane's canonical name) and `mcp.json` (Cursor /
 * common ecosystem alias) are picked up from every search root. The
 * canonical name wins within the same directory; across files the
 * usual first-found-by-name dedup applies, so `mcp.json` effectively
 * merges its non-overlapping entries on top of `mcps.json`.
 *
 * Order per scope (project beats user):
 *   1. `{project}/.agents/mcps.json`
 *   2. `{project}/.agents/mcp.json`
 *   3. `{project}/.{prefix}/mcps.json`
 *   4. `{project}/.{prefix}/mcp.json`
 *   5. `~/.agents/mcps.json`
 *   6. `~/.agents/mcp.json`
 *   7. `~/.{prefix}/mcps.json`
 *   8. `~/.{prefix}/mcp.json`
 *
 * Non-existent files are skipped without error.
 */
declare function defaultMcpsConfigPaths(opts?: {
  cwd?: string;
  home?: string;
  prefix?: string;
}): {
  path: string;
  source: 'project' | 'user';
}[];
/**
 * Parse one `mcps.json` file. Canonical zidane shape is a flat array; the
 * other shapes exist for copy-paste from neighboring tools:
 *
 *   - `McpServerConfig[]` — flat array (recommended).
 *   - `{ name: {...} }` — name-keyed map.
 *   - `{ "mcpServers": { name: {...} } }` — Claude Desktop / Claude Code /
 *     Cursor wrapper. Unwrapped before normalization (otherwise
 *     `normalizeMcpServers` would treat the whole wrapper as a single server).
 *
 * Per-entry `enabled: false` (Cursor runtime state) drops the entry — same
 * intent as the source tool. Use the TUI toggle list for "show but disabled".
 *
 * Tolerant to common copy-paste artifacts: when strict `JSON.parse` fails,
 * retries after normalizing invisible whitespace that's valid Unicode but
 * not valid JSON whitespace (BOM, NBSP, zero-width spaces, line/paragraph
 * separators) — only OUTSIDE string literals, so user data isn't mutated.
 * If the sanitized version still fails, throws the original error with a
 * targeted hint when NBSPs are present.
 *
 * Validation flows through `normalizeMcpServers` so the result matches
 * what `createAgent` accepts. Throws on truly malformed JSON; the caller
 * decides whether to surface or swallow.
 */
declare function parseMcpsFile(text: string): McpServerConfig[];
/**
 * Per-file failure surfaced from {@link discoverProjectMcps}. The TUI shows
 * these in the MCP settings empty / preamble state so a malformed
 * `mcps.json` doesn't look identical to a missing one.
 */
interface DiscoveryError {
  path: string;
  source: 'project' | 'user';
  message: string;
}
/**
 * Result of {@link discoverProjectMcps}. `servers` is the per-source-priority
 * merged list (project beats user, first-found wins on name collision).
 * `errors` is one entry per file that existed but failed to parse — empty
 * on the happy path.
 */
interface DiscoveryResult {
  servers: DiscoveredMcp[];
  errors: DiscoveryError[];
}
/**
 * Walk the default config paths, parse every file that exists, and return
 * `{ servers, errors }` in source-priority order. Project entries appear
 * before user entries; first occurrence of a `name` wins (later
 * duplicates dropped).
 *
 * Parse failures are captured into `errors` rather than thrown — the picker
 * surfaces them while still showing whatever DID parse. A `ZIDANE_DEBUG`
 * log line is also emitted for ops visibility.
 */
declare function discoverProjectMcps(opts?: {
  cwd?: string;
  home?: string;
  prefix?: string;
}): DiscoveryResult;
/**
 * Map a user-toggled enable list onto the `mcpServers` array the agent
 * receives. Conventions match `buildSkillsConfig`:
 *
 *   - `enabled === undefined` → every discovered server enabled (default).
 *   - `enabled === []`        → no servers (the agent runs MCP-less).
 *   - `enabled === [names]`   → allowlist; servers not in the list are
 *                               dropped.
 *
 * `disabledTools` is the per-server tool deny-list from
 * `Settings.disabledMcpTools`. For each server that survives the
 * server-level enable filter, the per-tool names are merged into
 * `McpServerConfig.disabledTools` via **union** with anything the JSON
 * file already pinned — a tool listed in either source is dropped.
 * Union (not override) keeps JSON-side policy stable: a server file
 * that pins `disabledTools: ['dangerous_tool']` can't be re-enabled
 * via the UI by accident.
 *
 * `enabledTools` (JSON-side allowlist) takes precedence over the
 * UI-side deny-list when both are set on the same server — the
 * allowlist's "only these" semantics would be meaningless if a UI
 * deny-list could subtract from it again. We don't emit `disabledTools`
 * when `enabledTools` is present.
 *
 * Returns a fresh array — callers can mutate without affecting the
 * underlying discovery list. Per-server config objects are also fresh
 * copies (shallow), so the merged `disabledTools` doesn't leak back
 * onto the discovery catalog.
 */
declare function buildMcpServers(opts: {
  discovered: readonly DiscoveredMcp[];
  enabled?: readonly string[];
  disabledTools?: Record<string, readonly string[]>;
}): McpServerConfig[];
//#endregion
//#region src/chat/enabled-toggle-set.d.ts
/**
 * Renderer-agnostic state machine for an "enabled allowlist" — the shape
 * used by `settings.enabledSkills` / `settings.enabledMcps` (and any future
 * `enabledX` toggle backed by a `Settings` field).
 *
 * Semantics, kept identical across all three call sites:
 *   - `undefined`  → every catalog entry is enabled (default — user has
 *                    never opened the picker; new entries flow in).
 *   - `[]`         → the whole subsystem is off.
 *   - `[names]`    → explicit allowlist; the persisted shape stays a
 *                    concrete array once the user has toggled anything.
 *
 * The hook exposes a live `enabledSet` (Set<string>) + a `toggle(name)`
 * callback that persists the result through `useSettings().setSetting`.
 * The first toggle seeds the persisted allowlist from the current catalog
 * so newly-added entries don't silently drop on the next launch.
 *
 * Renderer-neutral: works for the TUI's `<ToggleListModal>`, a GUI
 * checkbox list, a CLI flag tool. Hook tests live alongside the chat
 * suite; the rendering layer is tested where it lives.
 */
/** Settings keys whose value type is `readonly string[] | undefined`. */
type EnabledAllowlistKey = 'enabledSkills' | 'enabledMcps';
interface EnabledToggleSet {
  /** Live set of enabled names — `Set` for O(1) `has`. */
  enabledSet: ReadonlySet<string>;
  /** Toggle one name on/off; persists immediately via `setSetting`. */
  toggle: (name: string) => void;
}
/**
 * Bind an "enabled allowlist" setting to a discovered catalog.
 *
 * `keyOf` extracts the persisted identity from a catalog entry (skill name,
 * MCP server name, …). Pass a stable, collision-free key — the persisted
 * allowlist is keyed against it forever.
 */
declare function useEnabledToggleSet<T>(opts: {
  catalog: readonly T[];
  keyOf: (entry: T) => string;
  settingKey: EnabledAllowlistKey;
}): EnabledToggleSet;
//#endregion
//#region src/chat/interactions.d.ts
declare const PRESENT_PLAN_TOOL = "present_plan";
declare const ASK_USER_TOOL = "ask_user";
/** True when `name` is one of the interaction tool canonical names. */
declare function isInteractionTool(name: string): boolean;
interface PlanStep {
  id: string;
  title: string;
  description?: string;
}
interface PlanPayload {
  /** Short headline summary — used as the modal title. */
  title: string;
  /** Full plan body. Markdown. */
  plan: string;
  /** Optional structured step list — surfaced as a checklist preview. */
  steps?: readonly PlanStep[];
}
interface QuestionChoice {
  id: string;
  label: string;
  description?: string;
}
/**
 * One question in an `ask_user` batch. Discriminated by `type`:
 *
 *   - `text`     — single-line free-text answer (rendered via `<input>`).
 *   - `textarea` — multi-line free-text answer (rendered via `<textarea>`).
 *   - `select`   — single pick from a fixed set of `choices`.
 *   - `confirm`  — yes/no boolean (rendered as a 2-option select).
 *
 * The model batches related clarifications into one tool call instead of
 * pinging the user N times with N round-trips.
 */
type QuestionType = 'text' | 'textarea' | 'select' | 'confirm';
interface BaseQuestion {
  /** Stable id — keys the response back to the model. */
  id: string;
  /** Question text shown above the input (Markdown allowed). */
  prompt: string;
  /** Optional helper text under the prompt. */
  description?: string;
  /**
   * Whether the answer is required (non-empty). Default: `true` for
   * `select` / `confirm` (no sensible empty), `false` for free-text
   * (skipping = empty string). Free-text questions still accept an
   * answer; the flag only gates submission.
   */
  required?: boolean;
}
interface TextQuestion extends BaseQuestion {
  type: 'text' | 'textarea';
  /** Placeholder shown when the input is empty. */
  placeholder?: string;
  /** Pre-fill the input with this value (still editable). */
  default?: string;
}
interface SelectQuestion extends BaseQuestion {
  type: 'select';
  choices: readonly QuestionChoice[];
}
interface ConfirmQuestion extends BaseQuestion {
  type: 'confirm';
  /** Label for the affirmative option. Default: `'yes'`. */
  affirmLabel?: string;
  /** Label for the negative option. Default: `'no'`. */
  denyLabel?: string;
}
type Question = TextQuestion | SelectQuestion | ConfirmQuestion;
interface QuestionPayload {
  /** Optional intro / context shown above every question (Markdown). */
  intro?: string;
  /** One or more questions for the user to answer in one form. */
  questions: readonly Question[];
}
/** Per-question answer value — `string` for text/textarea/select, `boolean` for confirm. */
type AnswerValue = string | boolean;
interface BaseRequest {
  /** Stable id — the `tool_call.id` from the model. */
  id: string;
  /** Tool canonical name. */
  tool: string;
  /** Run that emitted the call, when known. */
  runId?: string;
  /** Turn id the call lives in. */
  turnId?: string;
  /** Unix ms when the request was raised. */
  createdAt: number;
}
interface PlanRequest extends BaseRequest {
  kind: 'plan';
  tool: typeof PRESENT_PLAN_TOOL;
  payload: PlanPayload;
}
interface QuestionRequest extends BaseRequest {
  kind: 'question';
  tool: typeof ASK_USER_TOOL;
  payload: QuestionPayload;
}
type InteractionRequest = PlanRequest | QuestionRequest;
type PlanDecision = 'approve' | 'reject' | 'revise';
interface PlanResponse {
  kind: 'plan';
  decision: PlanDecision;
  /** Optional free-text feedback — shown to the model verbatim. */
  comment?: string;
}
interface QuestionResponse {
  kind: 'question';
  /**
   * Map of `Question.id → AnswerValue` for every question the user
   * answered. Missing keys = question skipped (only possible when
   * `required: false`).
   */
  answers: Readonly<Record<string, AnswerValue>>;
}
type InteractionResponse = PlanResponse | QuestionResponse;
/**
 * Format an {@link InteractionResponse} as the tool_result content. JSON
 * keeps the envelope parseable and explicit. Empty fields are stripped so
 * the model doesn't waste tokens reading absent values.
 */
declare function serializeInteractionResponse(response: InteractionResponse): string;
/**
 * Build a protocol-valid user `SessionTurn` that closes a batch of
 * resumed pending interactions.
 *
 * - `requests` MUST be the same list returned by
 *   {@link pendingInteractionsFromTurns} for the session, in order.
 * - `responses` MUST contain an entry for every request id; throws
 *   otherwise (caller bug — flush before all answers collected).
 *
 * Pure / total. Tested in isolation so the batching invariant doesn't
 * regress without the resumed-flow integration noticing.
 */
declare function buildResumedToolResultsTurn(requests: readonly InteractionRequest[], responses: ReadonlyMap<string, InteractionResponse>, options: {
  /** Turn id minted by the session store. */turnId: string;
  /**
   * Run id that emitted the pending tool calls — when known, attaching
   * it ties the resumed turn back to the originating `SessionRun` row
   * so consumers grouping turns by run keep clean ancestry across the
   * close/reopen boundary.
   */
  runId?: string; /** `createdAt` timestamp. Defaults to `Date.now()`. */
  createdAt?: number;
}): SessionTurn;
/**
 * Scan `turns` and return every interaction tool call without a matching
 * `tool_result`, ordered by turn appearance. Works equally for live mid-run
 * inspection and reload-from-disk.
 *
 * Malformed payloads (input that doesn't match the tool's schema) are
 * skipped silently — the call still lives in the persisted history, but
 * the renderer wouldn't know how to display it. The next `agent.run()` on
 * the session will see the orphan and synthesize an error tool_result via
 * the loop's normal recovery path.
 */
declare function pendingInteractionsFromTurns(turns: readonly SessionTurn[]): InteractionRequest[];
interface CreateInteractionToolsOptions {
  /**
   * Called when the agent invokes one of the interaction tools. Resolves
   * with the user's response (forwarded to the model as the tool_result);
   * reject to signal denial / abort (the model receives
   * `Tool error: <message>` as the result).
   */
  requestInteraction: (request: InteractionRequest) => Promise<InteractionResponse>;
}
/**
 * Create the `present_plan` and `ask_user` tools as a record, ready to
 * spread into `createAgent({ tools })` or composed into a {@link Preset}.
 */
declare function createInteractionTools(opts: CreateInteractionToolsOptions): Record<string, ToolDef>;
interface PendingInteractionEntry {
  request: InteractionRequest;
  /** The user picked something — forward to the live tool Promise or the resumed tool_result writer. */
  resolve: (response: InteractionResponse) => void;
  /**
   * The user / system cancelled — distinct from `resolve` so renderers can
   * differentiate "user picked something" from "user gave up / aborted".
   * Live entries reject the tool's Promise; resumed entries decide what
   * to persist as the cancelled tool_result.
   */
  cancel: (reason?: string) => void;
}
interface InteractionsActions {
  /** Add a request to the FIFO queue. */
  enqueue: (entry: PendingInteractionEntry) => void;
  /** Resolve the head request with `response`. */
  resolveHead: (response: InteractionResponse) => void;
  /**
   * Cancel the head request. Reserved for future per-call cancel
   * affordances (a "skip this" button, a GUI's close-modal-X) — the
   * built-in TUI currently cancels via the run-level `cancelAll` path
   * only. Kept on the public API so consumer renderers can wire it
   * without a follow-up surface change.
   */
  cancelHead: (reason?: string) => void;
  /** Cancel every queued request. Used by abort / session teardown. */
  cancelAll: (reason?: string) => void;
}
declare function InteractionsProvider({
  children
}: {
  children: ReactNode;
}): import("react/jsx-runtime").JSX.Element;
/** Read the live queue (re-renders on every push/pop). */
declare function useInteractionsQueue(): readonly PendingInteractionEntry[];
/** Read the stable actions object. */
declare function useInteractionsActions(): InteractionsActions;
/**
 * Build the `requestInteraction` callback for `createInteractionTools` from
 * the React-side {@link InteractionsActions}. Each call adds a fresh entry
 * to the queue whose `resolve` / `cancel` route back to one Promise that
 * the tool's `execute` awaits.
 */
declare function makeRequestInteraction(actions: InteractionsActions): CreateInteractionToolsOptions['requestInteraction'];
//#endregion
//#region src/chat/markdown-segments.d.ts
/**
 * One block returned by {@link splitMarkdownCodeBlocks}. Prose segments
 * carry their original markdown (including blank lines that bracketed
 * the fence) so the surrounding paragraph spacing stays faithful to the
 * original input. Code segments carry the raw body — fence markers and
 * info-string stripped — so a "copy code" action lands what the user
 * actually sees, byte-for-byte.
 */
interface MarkdownSegment {
  kind: 'prose' | 'code';
  content: string;
  /** Info-string (e.g. `ts`, `python`). Empty when the fence had none. */
  lang?: string;
  /**
   * `true` on a code segment whose closing fence hasn't arrived yet —
   * i.e. the trailing block during streaming. Consumers can render this
   * in "streaming" mode (no copy affordance, partial-highlight pipeline)
   * until the closer lands and it flips to a regular closed segment.
   */
  open?: boolean;
}
/**
 * Split markdown text into alternating prose / fenced-code segments.
 *
 * Recognizes both ` ``` ` and `~~~` fences, with arbitrary length ≥ 3,
 * matching CommonMark's "fence indicator must be at least as long to
 * close" rule. Info-strings (the bit after the opening fence) are kept
 * as the `lang` hint; any trailing whitespace is trimmed. Closing
 * fences are detected on a line of their own (whitespace tolerated).
 *
 * Unclosed fences fall back to emitting the would-be-code body as a
 * trailing prose segment so no content is dropped — finalized markdown
 * isn't expected to ship one, but the fallback keeps the renderer
 * truthful when the model produces malformed output.
 *
 * Exported for unit tests.
 */
declare function splitMarkdownCodeBlocks(text: string): MarkdownSegment[];
//#endregion
//#region src/chat/session-export.d.ts
/** File format supported by the exporter. */
type SessionExportFormat = 'markdown' | 'json';
/**
 * Anchor for the resolved destination directory:
 *   - `project` — the nearest enclosing git repository (`<repoRoot>/.{prefix}/sessions`)
 *   - `home`    — fallback when no repo was found (`~/.{prefix}/sessions`)
 */
type SessionExportAnchor = 'project' | 'home';
/** Resolved file destination for a session export. */
interface SessionExportTarget {
  /** Absolute directory holding the file. May not exist yet — `writeSessionExport` creates it on demand. */
  dir: string;
  /** Absolute path of the file (`dir/{id}.{ext}`). */
  filepath: string;
  /** Where the destination was anchored. */
  anchor: SessionExportAnchor;
}
/** Options shared by every resolve / write entry-point. */
interface ResolveOptions {
  /** Discovery cwd. Defaults to `process.cwd()`. */
  cwd?: string;
  /** User home directory. Defaults to `os.homedir()`. */
  home?: string;
  /**
   * Storage prefix. Leading dot tolerated (`zidane` and `.zidane` are
   * equivalent). Defaults to `'.zidane'`.
   */
  prefix?: string;
}
/**
 * Resolve the export target for a session id + format. Pure: no fs
 * write happens here, the caller decides whether to `existsSync` the
 * directory or hand the path to `writeSessionExport`.
 *
 * Anchor selection walks upward from `cwd` looking for a `.git` entry;
 * the first hit anchors to that repo's root (`project`). When the walk
 * exits without finding `.git`, the destination anchors to `home`.
 *
 * @throws RangeError when `sessionId` would resolve to an empty / `..`
 *   filename — defensive guard against malicious or buggy callers
 *   crossing into a sibling directory via the id.
 */
declare function resolveSessionExportTarget(opts: {
  sessionId: string;
  format: SessionExportFormat;
} & ResolveOptions): SessionExportTarget;
/**
 * Render a session into a string in the requested format.
 *
 * `markdown` produces a human-readable transcript: a YAML-free header
 * block with the title and a one-line `id · created · turns` summary,
 * a stats section (turns / runs / tokens / cost / status), then a
 * `## Conversation` block where each turn renders as `### N. role ·
 * run_X · ISO-date` with text, thinking, tool calls, and tool results
 * formatted as appropriate fenced blocks. Useful for sharing a session
 * with a teammate or pasting into an issue tracker.
 *
 * `json` returns a 2-space-indented, deterministic dump of the full
 * `SessionData` blob. Useful for re-importing into the store or for
 * post-hoc analysis tooling.
 */
declare function renderSession(session: SessionData, format: SessionExportFormat): string;
/**
 * Render `session` and write the resulting bytes to disk. Returns the
 * resolved target so the caller can show the user where the file
 * landed. The parent directory is created on demand (`recursive: true`)
 * — first-time exports don't need any pre-flight setup.
 */
declare function writeSessionExport(opts: {
  session: SessionData;
  format: SessionExportFormat;
} & ResolveOptions): Promise<SessionExportTarget>;
//#endregion
//#region src/chat/theme.d.ts
/**
 * Renderer-agnostic theme system.
 *
 * A `Theme` bundles every variable that can change visually between
 * "themes" — colors, select-row styling, code/markdown syntax highlight
 * tokens, panel backgrounds. Plain JSON: no OpenTUI dependency, no React,
 * no functions. The TUI consumes the theme by reading from `useTheme()`
 * and converting hex strings into OpenTUI's `RGBA`/`SyntaxStyle`; a future
 * GUI consumes the same theme by converting into CSS variables or Tailwind
 * tokens.
 *
 * Components should always read theme values through `useTheme()` /
 * `useColors()` so a runtime theme switch (Settings → Theme) re-paints
 * the whole tree. Importing a raw theme object directly bypasses the
 * context and pins the component to a single look.
 *
 * Built-in flavors (Catppuccin, Vaporwave) live in `./themes/`. This file
 * just defines the shape + the default theme + the registry.
 */
/** Role-named color palette. Reused throughout the UI for text, borders, accents. */
interface ThemeColors {
  /** Primary accent — selected text, brand emphasis (e.g. `▶` markers). */
  brand: string;
  /** Success / progress accent — used sparingly. */
  accent: string;
  /** Model badge accent (provider/model name in the footer, links). */
  model: string;
  /** Warnings — busy spinner, approval picker border, esc hints. */
  warn: string;
  /** Errors — `✗` prefix on error events, validation messages. */
  error: string;
  /** Secondary text — captions, helper text, dim transcript lines. */
  dim: string;
  /** Tertiary text — separators, descriptions, the `┃` tool-result bar. */
  mute: string;
  /** Resting border color. */
  border: string;
  /** Border color on focused / active elements. */
  borderActive: string;
  /**
   * Optional gradient endpoints for the in-chat working throbber
   * (`<CrushThrobber>`). Falls back to `[brand, accent]` when unset.
   * Themes can pin a richer pair here — e.g. Crush's `Charple` →
   * `Dolly` purple-to-pink — without having to repurpose `accent`.
   */
  throbber?: {
    from: string;
    to: string;
  };
  /**
   * Optional tone for the footer's session-cost indicator. Falls back
   * to `warn` when unset. Carved out as its own role because some
   * themes (e.g. Crush) intentionally bind `warn` to a non-yellow tone
   * and a separate "money" color reads more naturally on the footer.
   */
  money?: string;
}
/** Select-component styling. Plain prop shape; both OpenTUI's `<select>` and any GUI equivalent can consume this. */
interface ThemeSelect {
  backgroundColor: string;
  focusedBackgroundColor: string;
  selectedBackgroundColor: string;
  selectedTextColor: string;
  textColor: string;
  descriptionColor: string;
  selectedDescriptionColor: string;
}
/** Background + foreground pair for a completion-reference chip pill. */
interface ChipColor {
  /** Background paint — the pill itself. High-saturation by convention. */
  bg: string;
  /** Foreground paint — the chip text. Pre-paired with {@link ChipColor.bg} for legibility. */
  fg: string;
}
/**
 * Chip colors keyed by completion-provider id (`'skills'`, `'files'`, …).
 * `default` is the required fallback used by any provider id without an
 * explicit entry — keeps host-registered providers themable out of the
 * box. Use {@link resolveChipColor} to perform the kind-specific → default
 * lookup rather than indexing the map directly.
 */
/**
 * Map of provider id → chip color pair. `default` is required so callers
 * always have a fallback; every other key is optional so direct indexing
 * (`chips['unknown']`) surfaces as `ChipColor | undefined` and nudges
 * consumers toward {@link resolveChipColor}, which encodes the fallback.
 */
type ChipColorMap = {
  default: ChipColor;
} & Partial<Record<string, ChipColor>>;
/**
 * Look up the chip color pair for a provider id, falling back to
 * `chips.default` when the theme has no kind-specific entry. Mirrors
 * `resolveChipStyleId` so both rendering surfaces (submitted echo +
 * live textarea) share one canonical lookup rule.
 */
declare function resolveChipColor(chips: ChipColorMap, providerId: string): ChipColor;
/** Panel / surface backgrounds. */
interface ThemeSurfaces {
  /**
   * Solid paint for the app's root surface. Covers the whole TUI viewport
   * so transparent / translucent terminals don't show desktop windows
   * underneath. Conventionally one tier deeper than {@link modal} so
   * modal panels still read as elevated above the body.
   */
  background: string;
  /** Background of an overlaid modal panel (settings, model picker, …). */
  modal: string;
  /**
   * Completion-chip color pairs keyed by provider id. Built-in themes
   * ship distinct `skills` + `files` tones so the two reference kinds
   * read differently in both the submitted echo and the live textarea.
   */
  chips: ChipColorMap;
  /**
   * Background paint for the selected turn's events in select-turn mode.
   * Subtle, low-saturation lift from the terminal default so the whole
   * span of the selected turn reads as one continuous highlighted block
   * without overpowering the foreground text. Inverse-tinted on the
   * light flavor (Latte) so the same visual role works there.
   */
  selection: string;
  /**
   * Edit-diff color block. Drives the native `<diff>` renderable used
   * by {@link Settings.showEditDiffs}. Pre-mixed against each theme's
   * primary surface so terminals without true alpha-blend get a
   * legible "subtle red / subtle green" effect rather than a flat
   * saturated band.
   *
   *   - `*Bg`         row-level paint (gutter + content)
   *   - `*ContentBg`  content-only paint (overrides `*Bg` on the text
   *                   column). Set when a deeper hue should punch
   *                   through behind the syntax-highlighted code; omit
   *                   to let `*Bg` carry the full row.
   *   - `*Fg`         color for the `+` / `-` glyph in the sign gutter.
   */
  diff: DiffSurfaces;
}
/** Per-row paints used by the native `<diff>` renderable. */
interface DiffSurfaces {
  addBg: string;
  removeBg: string;
  contextBg?: string;
  addContentBg?: string;
  removeContentBg?: string;
  addFg: string;
  removeFg: string;
}
/**
 * One entry in a `SyntaxStyles` map — what OpenTUI's `SyntaxStyle.fromStyles`
 * accepts, minus the `RGBA` conversion. Renderer-agnostic JSON.
 */
interface SyntaxTokenStyle {
  fg?: string;
  bg?: string;
  bold?: boolean;
  italic?: boolean;
  underline?: boolean;
  dim?: boolean;
}
/**
 * Map of Tree-sitter / markdown capture group → token style.
 *
 * Two flavours of keys live in the same table:
 *   - `markup.*` — the markdown structure captures (heading, bold, italic,
 *     list, quote, raw inline + block, link). OpenTUI's markdown parser
 *     emits these for the markdown text itself.
 *   - bare token names (`keyword`, `string`, `function`, …) — emitted by
 *     the embedded language Tree-sitter grammars when a code fence
 *     declares a language. OpenTUI passes the same `SyntaxStyle` down to
 *     the fenced-code renderable, so this map drives both surfaces.
 */
type SyntaxStyles = Record<string, SyntaxTokenStyle>;
/** Full theme bundle. */
interface Theme {
  /** Stable identifier, used as the key in `BUILTIN_THEMES` and `Settings.theme`. */
  id: string;
  /** Human-readable label shown in the settings picker. */
  label: string;
  colors: ThemeColors;
  select: ThemeSelect;
  surfaces: ThemeSurfaces;
  syntax: SyntaxStyles;
}
declare const DEFAULT_THEME: Theme;
/**
 * Built-in theme registry, keyed by `theme.id`. The TUI looks up the active
 * theme here using `Settings.theme`; unknown ids fall back to
 * `DEFAULT_THEME`. Hosts can extend this by passing additional themes to a
 * future `runTui({ themes })` option (not yet wired).
 *
 * Insertion order is the picker cycle order — keep `default` first so a
 * fresh install (no `theme` in `state.json`) sees the familiar yellow theme
 * before the others.
 */
declare const BUILTIN_THEMES: Readonly<Record<string, Theme>>;
/** Resolve a theme id to its full `Theme`, falling back to default on unknown ids. */
declare function resolveTheme(id: string | undefined): Theme;
//#endregion
//#region src/chat/transcript-anchors.d.ts
/**
 * One entry in the partitioned transcript-row list. Single events
 * render as a normal row; `child-run`s group consecutive subagent
 * events into one bordered box (the TUI renders one outer container
 * for the whole run, hence the inner-events array).
 *
 * Currently produced by the TUI's `partitionTranscript`; a GUI shell
 * grouping subagent runs the same way can produce the same shape.
 */
type TranscriptItem = {
  kind: 'event';
  event: StreamEvent;
  previous?: StreamEvent;
} | {
  kind: 'child-run';
  events: StreamEvent[];
  previous?: StreamEvent;
};
/**
 * Per-item anchor ids for auto-scroll. Walks `items` in render order
 * and, for each event, returns either:
 *   - `'turn-anchor-<turnId>'` — the first event of this turn (the
 *     scroll target).
 *   - `undefined` — later event of an already-tagged turn (or a
 *     synthetic event with no `turnId`).
 *
 * `ids[i]` is a tuple per item: length 1 for plain events, length N
 * for subagent runs (one entry per inner event). `idByTurn` is the
 * inverse lookup used by the scroll effect. `lastTurnId` is the
 * most-recently-rendered turn — the scroll effect special-cases it to
 * snap to bottom rather than scroll the anchor into view (which would
 * stop short of the actual tail).
 *
 * Exported so the anchor-tagging matrix can be unit-tested without
 * rendering anything.
 */
declare function computeTurnAnchors(items: readonly TranscriptItem[]): {
  idByTurn: ReadonlyMap<string, string>;
  ids: readonly (readonly (string | undefined)[])[];
  lastTurnId: string | undefined;
};
//#endregion
export { useInteractionsQueue as $, parseBindingSpec as $t, InteractionsProvider as A, marginTopFor as At, QuestionPayload as B, KEYBINDING_DEF_BY_ACTION as Bt, ASK_USER_TOOL as C, TuiState as Ct, InteractionRequest as D, lastContextSizeFromTurns as Dt, CreateInteractionToolsOptions as E, eventsFromTurns as Et, PlanRequest as F, toolCallPreview as Ft, TextQuestion as G, KeyBindings as Gt, QuestionResponse as H, KeyAction as Ht, PlanResponse as I, toolResultText as It, isInteractionTool as J, formatBindingForDisplay as Jt, buildResumedToolResultsTurn as K, ParsedBinding as Kt, PlanStep as L, updateToolEventOutcomes as Lt, PendingInteractionEntry as M, stripSpawnTokensLine as Mt, PlanDecision as N, sumRunCosts as Nt, InteractionResponse as O, listSessionMeta as Ot, PlanPayload as P, titleFromTurns as Pt, useInteractionsActions as Q, mergeKeybindings as Qt, Question as R, DEFAULT_KEYBINDINGS as Rt, splitMarkdownCodeBlocks as S, StateStoreApi as St, ConfirmQuestion as T, deriveSessionTitle as Tt, QuestionType as U, KeyBindingDef as Ut, QuestionRequest as V, KEYBINDING_KEY_COL_WIDTH as Vt, SelectQuestion as W, KeyBindingSection as Wt, pendingInteractionsFromTurns as X, keybindingsPath as Xt, makeRequestInteraction as Y, groupBindings as Yt, serializeInteractionResponse as Z, matchesBinding as Zt, SessionExportTarget as _, PLAN_AGENT as _n, ResolveStorageDirsOptions as _t, ChipColorMap as a, Hint as an, DiscoveryResult as at, writeSessionExport as b, singleAgentRegistry as bn, StorageSlot as bt, SyntaxTokenStyle as c, truncateTrailing as cn, discoverProjectMcps as ct, ThemeSelect as d, AgentRegistry as dn, ChatOptions as dt, readKeybindings as en, EnabledAllowlistKey as et, ThemeSurfaces as f, BUILD_AGENT as fn, ProviderRegistry as ft, SessionExportFormat as g, DEFAULT_PERSIST_EXCLUDE_TOOLS as gn, resolveStoragePaths as gt, SessionExportAnchor as h, DEFAULT_BUDGET_EXCLUDE_TOOLS as hn, resolveConfig as ht, ChipColor as i, EMPTY_HINTS as in, DiscoveryError as it, PRESENT_PLAN_TOOL as j, saveState as jt, InteractionsActions as k, loadState as kt, Theme as l, AgentAccent as ln, parseMcpsFile as lt, resolveTheme as m, DEFAULT_AGENT_ID as mn, ResolvedPaths as mt, computeTurnAnchors as n, CompletionState as nn, useEnabledToggleSet as nt, DEFAULT_THEME as o, clipHintsToWidth as on, buildMcpServers as ot, resolveChipColor as p, BUILTIN_AGENTS as pn, ResolvedConfig as pt, createInteractionTools as q, ensureKeybindingsFile as qt, BUILTIN_THEMES as r, useCompletion as rn, DiscoveredMcp as rt, SyntaxStyles as s, hintsLength as sn, defaultMcpsConfigPaths as st, TranscriptItem as t, stripJsonComments as tn, EnabledToggleSet as tt, ThemeColors as u, AgentProfile as un, AutoUpdateConfig as ut, renderSession as v, accentColor as vn, StorageDirs as vt, AnswerValue as w, createStateStore as wt, MarkdownSegment as x, resolveStorageDirs as xt, resolveSessionExportTarget as y, resolveAgentId as yn, StorageMode as yt, QuestionChoice as z, KEYBINDING_DEFS as zt };
//# sourceMappingURL=transcript-anchors-DA6XawEU.d.ts.map