zidane 6.6.4 → 7.0.0

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 (157) hide show
  1. package/dist/{acp-BQqhozmt.js → acp-Wr8l4IDI.js} +7 -180
  2. package/dist/acp-Wr8l4IDI.js.map +1 -0
  3. package/dist/acp-cli.js +6 -6
  4. package/dist/acp.d.ts +1 -1
  5. package/dist/acp.d.ts.map +1 -1
  6. package/dist/acp.js +2 -1
  7. package/dist/{agent-DTxH7RHW.js → agent-C-camKQW.js} +162 -12
  8. package/dist/agent-C-camKQW.js.map +1 -0
  9. package/dist/agent.d.ts +1 -1
  10. package/dist/agent.js +1 -1
  11. package/dist/{anthropic-DRNRnd6W.js → anthropic-BDOGzhtj.js} +3 -3
  12. package/dist/{anthropic-DRNRnd6W.js.map → anthropic-BDOGzhtj.js.map} +1 -1
  13. package/dist/{transcript-anchors-BjH-68k2.js → approvals-CqqxE5Vh.js} +4924 -275
  14. package/dist/approvals-CqqxE5Vh.js.map +1 -0
  15. package/dist/{auth-7rv6ZfWk.js → auth-BdwDROYg.js} +12 -8
  16. package/dist/{auth-7rv6ZfWk.js.map → auth-BdwDROYg.js.map} +1 -1
  17. package/dist/chat/pure.d.ts +221 -4
  18. package/dist/chat/pure.d.ts.map +1 -0
  19. package/dist/chat/pure.js +4 -2
  20. package/dist/chat.d.ts +6 -2911
  21. package/dist/chat.js +8 -261
  22. package/dist/{context-transport-D8yeGdpd.js → context-transport-BqYwzJYE.js} +2 -2
  23. package/dist/{context-transport-D8yeGdpd.js.map → context-transport-BqYwzJYE.js.map} +1 -1
  24. package/dist/contexts/daytona.d.ts +1 -1
  25. package/dist/contexts/daytona.js +1 -1
  26. package/dist/contexts/e2b.d.ts +1 -1
  27. package/dist/contexts/e2b.js +1 -1
  28. package/dist/daemon-cli.d.ts +1 -0
  29. package/dist/daemon-cli.js +90 -0
  30. package/dist/daemon-cli.js.map +1 -0
  31. package/dist/eval.d.ts +1 -1
  32. package/dist/eval.js +1 -1
  33. package/dist/extensions.d.ts +2 -2
  34. package/dist/extensions.js +2 -2
  35. package/dist/headless.d.ts +1 -1
  36. package/dist/headless.js +4 -4
  37. package/dist/{index-D0O-QO7Z.d.ts → index-B-FSaM3_.d.ts} +2863 -2375
  38. package/dist/index-B-FSaM3_.d.ts.map +1 -0
  39. package/dist/index-DgdzXRPH.d.ts +3590 -0
  40. package/dist/index-DgdzXRPH.d.ts.map +1 -0
  41. package/dist/index.d.ts +2 -2
  42. package/dist/index.js +14 -1179
  43. package/dist/ipc-CIv87xuH.js +331 -0
  44. package/dist/ipc-CIv87xuH.js.map +1 -0
  45. package/dist/ipc.d.ts +849 -0
  46. package/dist/ipc.d.ts.map +1 -0
  47. package/dist/ipc.js +4 -0
  48. package/dist/json-rpc-rSWMUh9b.js +192 -0
  49. package/dist/json-rpc-rSWMUh9b.js.map +1 -0
  50. package/dist/{logger-DBX9uYOw.js → logger--C1VQjwJ.js} +3 -13
  51. package/dist/logger--C1VQjwJ.js.map +1 -0
  52. package/dist/logs-view-CWG8aUAG.js +44 -0
  53. package/dist/logs-view-CWG8aUAG.js.map +1 -0
  54. package/dist/{mcp-CVtSQTWg.js → mcp-BIWa57eY.js} +3 -3
  55. package/dist/{mcp-CVtSQTWg.js.map → mcp-BIWa57eY.js.map} +1 -1
  56. package/dist/mcp.d.ts +1 -1
  57. package/dist/mcp.js +1 -1
  58. package/dist/{messages-CZMUuKzo.js → messages-21XfHxyF.js} +3 -3
  59. package/dist/{messages-CZMUuKzo.js.map → messages-21XfHxyF.js.map} +1 -1
  60. package/dist/{openai-compat-BoTi-9fd.js → openai-compat-DY-AKxCg.js} +2 -2
  61. package/dist/{openai-compat-BoTi-9fd.js.map → openai-compat-DY-AKxCg.js.map} +1 -1
  62. package/dist/output/stream-json.d.ts +1 -1
  63. package/dist/output/terminal.d.ts +1 -1
  64. package/dist/{plugins-onDvTHEm.js → plugins-B531Tz5b.js} +2 -2
  65. package/dist/{plugins-onDvTHEm.js.map → plugins-B531Tz5b.js.map} +1 -1
  66. package/dist/plugins.d.ts +1 -1
  67. package/dist/plugins.js +1 -1
  68. package/dist/presets.d.ts +1 -1
  69. package/dist/presets.js +1 -1
  70. package/dist/providers/anthropic.d.ts +1 -1
  71. package/dist/providers/anthropic.js +1 -1
  72. package/dist/providers/arcee.d.ts +1 -1
  73. package/dist/providers/arcee.js +1 -1
  74. package/dist/providers/baseten.d.ts +1 -1
  75. package/dist/providers/baseten.js +1 -1
  76. package/dist/providers/cerebras.d.ts +1 -1
  77. package/dist/providers/cerebras.js +1 -1
  78. package/dist/providers/local.d.ts +1 -1
  79. package/dist/providers/local.js +1 -1
  80. package/dist/providers/openai-compat.d.ts +1 -1
  81. package/dist/providers/openai-compat.js +1 -1
  82. package/dist/providers/openai.d.ts +1 -1
  83. package/dist/providers/openai.js +3 -3
  84. package/dist/providers/openrouter.d.ts +1 -1
  85. package/dist/providers/openrouter.js +1 -1
  86. package/dist/providers/xai.d.ts +1 -1
  87. package/dist/providers/xai.js +1 -1
  88. package/dist/{providers-CmhdtD7w.js → providers-DX91jwir.js} +2 -2
  89. package/dist/{providers-CmhdtD7w.js.map → providers-DX91jwir.js.map} +1 -1
  90. package/dist/providers.d.ts +1 -1
  91. package/dist/providers.js +3 -3
  92. package/dist/restate.d.ts +1 -1
  93. package/dist/restate.js +8 -8
  94. package/dist/restate.js.map +1 -1
  95. package/dist/server-DJqp9dIR.js +1317 -0
  96. package/dist/server-DJqp9dIR.js.map +1 -0
  97. package/dist/session/sqlite.d.ts +1 -1
  98. package/dist/session/sqlite.d.ts.map +1 -1
  99. package/dist/session/sqlite.js +42 -4
  100. package/dist/session/sqlite.js.map +1 -1
  101. package/dist/{session-CyRLSsVa.js → session-K3icbPH9.js} +8 -2
  102. package/dist/session-K3icbPH9.js.map +1 -0
  103. package/dist/session.d.ts +1 -1
  104. package/dist/session.js +2 -2
  105. package/dist/{skills-BMGH_KK1.js → skills-PJyiSRCp.js} +3 -21
  106. package/dist/skills-PJyiSRCp.js.map +1 -0
  107. package/dist/skills.d.ts +1 -1
  108. package/dist/skills.js +1 -1
  109. package/dist/src-DzzhE2YO.js +2461 -0
  110. package/dist/src-DzzhE2YO.js.map +1 -0
  111. package/dist/todo-edits-Dw2oO2ON.js +378 -0
  112. package/dist/todo-edits-Dw2oO2ON.js.map +1 -0
  113. package/dist/{tool-formatters-NrzD6Fkk.d.ts → tool-formatters-DO56YBBw.d.ts} +104 -4
  114. package/dist/tool-formatters-DO56YBBw.d.ts.map +1 -0
  115. package/dist/tools/fetch-url.d.ts +1 -1
  116. package/dist/tools/web-search.d.ts +1 -1
  117. package/dist/tools.d.ts +1 -1
  118. package/dist/tools.js +1 -1
  119. package/dist/{transcript-anchors-UhC58Tmj.d.ts → transcript-anchors-DXqWWGbq.d.ts} +53 -4
  120. package/dist/transcript-anchors-DXqWWGbq.d.ts.map +1 -0
  121. package/dist/tui.d.ts +94 -13
  122. package/dist/tui.d.ts.map +1 -1
  123. package/dist/tui.js +2241 -1009
  124. package/dist/tui.js.map +1 -1
  125. package/dist/{turn-operations-mMxDRgLY.js → turn-operations-CZLsNgw-.js} +896 -11
  126. package/dist/turn-operations-CZLsNgw-.js.map +1 -0
  127. package/dist/{turn-operations-Cuc9pwiB.d.ts → turn-operations-DKFYWP3O.d.ts} +501 -4
  128. package/dist/turn-operations-DKFYWP3O.d.ts.map +1 -0
  129. package/dist/{types-BIarq1qE.js → types-CWgENVLY.js} +24 -2
  130. package/dist/types-CWgENVLY.js.map +1 -0
  131. package/dist/types.d.ts +1 -1
  132. package/dist/types.js +1 -1
  133. package/dist/xml-D5HBhJle.js +22 -0
  134. package/dist/xml-D5HBhJle.js.map +1 -0
  135. package/docs/CHAT.md +39 -0
  136. package/docs/IPC.md +420 -0
  137. package/docs/TUI.md +62 -0
  138. package/package.json +8 -2
  139. package/dist/acp-BQqhozmt.js.map +0 -1
  140. package/dist/agent-DTxH7RHW.js.map +0 -1
  141. package/dist/chat.d.ts.map +0 -1
  142. package/dist/chat.js.map +0 -1
  143. package/dist/index-D0O-QO7Z.d.ts.map +0 -1
  144. package/dist/index.js.map +0 -1
  145. package/dist/inline-autocomplete-D0MPvBaS.js +0 -596
  146. package/dist/inline-autocomplete-D0MPvBaS.js.map +0 -1
  147. package/dist/logger-DBX9uYOw.js.map +0 -1
  148. package/dist/login-CB23LyI1.js +0 -707
  149. package/dist/login-CB23LyI1.js.map +0 -1
  150. package/dist/session-CyRLSsVa.js.map +0 -1
  151. package/dist/skills-BMGH_KK1.js.map +0 -1
  152. package/dist/tool-formatters-NrzD6Fkk.d.ts.map +0 -1
  153. package/dist/transcript-anchors-BjH-68k2.js.map +0 -1
  154. package/dist/transcript-anchors-UhC58Tmj.d.ts.map +0 -1
  155. package/dist/turn-operations-Cuc9pwiB.d.ts.map +0 -1
  156. package/dist/turn-operations-mMxDRgLY.js.map +0 -1
  157. package/dist/types-BIarq1qE.js.map +0 -1
@@ -0,0 +1,3590 @@
1
+ import { B as McpCredentialStore, Cd as ProviderKey, Cl as ThemeSurfaces, Cu as StreamEvent, Eu as ProjectBrief, Gi as AgentHookMap, Gp as Provider, Hg as TurnUsage, Jc as SourcedScanPath, Ki as AgentHooks, Np as CacheBreakInfo, Qh as McpServerConfig, Rh as AgentStats, Sg as SessionTurn, Sl as ThemeSelect, Su as Settings, T_ as SessionStore, Vg as TurnFinishReason, Wi as Agent, Zh as JsonValue, ad as ModelChainEntry, bl as Theme, bu as Screen, eg as McpToolSchema, gu as EditPayload, if as InteractionResponse, kd as ProviderDescriptor, m_ as SkillConfig, mp as ContextSegment, mu as EditOutcome, qg as ToolDef, rf as InteractionRequest, v_ as SkillsConfig, vl as SyntaxStyles, x_ as Session, xl as ThemeColors, xu as SessionMeta, yp as ContextBreakdown, z as McpCredentialEntry, zh as AutoCompact } from "./index-B-FSaM3_.js";
2
+ import { O as ResolvedConfig, _ as DiscoveredMcp, f as DiscoveredPlugin, it as KeyBindings, v as DiscoveryError, yt as Hint } from "./transcript-anchors-DXqWWGbq.js";
3
+ import { P as ProviderUsageSnapshot, Rt as FileEntry, a as ActiveTodosState, ct as HunkResolution, dt as PreviewResult, h as TodoTally, p as TodoItem } from "./turn-operations-DKFYWP3O.js";
4
+ import { H as ApprovalRequest, V as ApprovalDecision, tt as PromptTurnRef } from "./tool-formatters-DO56YBBw.js";
5
+ import { Hookable } from "hookable";
6
+ import { spawn } from "node:child_process";
7
+ import { Dispatch, ReactNode, SetStateAction } from "react";
8
+ import { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
9
+ import { OAuthCredentials, OAuthDeviceCodeInfo, OAuthPrompt, OAuthPrompt as OAuthPrompt$1, OAuthSelectPrompt } from "@earendil-works/pi-ai/oauth";
10
+ //#region src/tools/read-chat-turns.d.ts
11
+ interface ChatTurnsReadToolOptions {
12
+ store: SessionStore;
13
+ getAllowedTokens: () => readonly string[];
14
+ }
15
+ declare function createChatTurnsReadTool(options: ChatTurnsReadToolOptions): ToolDef;
16
+ declare function chatReferenceTokensForPrompt(prompt: string): string[];
17
+ //#endregion
18
+ //#region src/chat/agent-prompt.d.ts
19
+ /**
20
+ * Agent system-prompt fragments — composable doctrine for built-in profiles.
21
+ *
22
+ * Each `const` below is one self-contained section of a system prompt. They
23
+ * exist as exports so SDK consumers can pick and choose: build a custom
24
+ * profile by spreading them in {@link buildBuildSystem} order, drop a fragment
25
+ * for a stricter / looser agent, or compose with project-specific prose.
26
+ *
27
+ * The fragments are deliberately byte-stable across sessions — every doctrine
28
+ * piece reads the same on every turn so the system-prompt cache breakpoint
29
+ * (anthropic ephemeral, `src/providers/anthropic.ts`) keeps hitting cache.
30
+ *
31
+ * Layout uses {@link SYSTEM_PROMPT_BOUNDARY} from `../system-prompt` to split
32
+ * the rendered prompt into:
33
+ *
34
+ * ┌──────────────┐ cache_control: ephemeral
35
+ * │ STATIC half │ identity + doctrine + guidance + user instructions
36
+ * ├──────────────┤ ← __ZIDANE_SYSTEM_PROMPT_BOUNDARY__
37
+ * │ DYNAMIC half │ envSection(cwd / date / platform)
38
+ * └──────────────┘ (no cache_control)
39
+ *
40
+ * Per-turn cwd / date / platform churn no longer invalidates the doctrine
41
+ * cache above. The TUI's `system:transform` hook rewrites only the dynamic
42
+ * half via `replaceDynamicSection`; the static prefix stays byte-identical
43
+ * for the lifetime of the run.
44
+ */
45
+ /**
46
+ * Lead line — short, version-stable. The first sentence matches the key of
47
+ * Claude Code's identity prefix (models anchor faster on familiar shapes).
48
+ * The rest sets the register: token-earning sentences with byte-exact
49
+ * code ("small mouth, full brain"), and a hard rule that speed never
50
+ * widens scope (the leeroy-jenkins fix). Keep changes rare — each one
51
+ * costs a cache miss across active sessions.
52
+ */
53
+ declare const IDENTITY_PREFIX = "You are zidane, a terminal-based coding agent that helps the user with software-engineering tasks. Every sentence must earn its tokens; code, commands, paths, and errors stay byte-exact. Small mouth, full brain. Move fast on the task given; speed never widens scope, the user owns scope.";
54
+ interface EnvSectionOptions {
55
+ /**
56
+ * The agent's actual working directory — what `process.cwd()` reports
57
+ * and what its tool calls resolve relative paths against. Distinct
58
+ * from {@link EnvSectionOptions.projectRoot}, which carries the
59
+ * project anchor when the user spawned from a subdirectory.
60
+ */
61
+ cwd: string;
62
+ /**
63
+ * Project root (typically the git root). When set AND different from
64
+ * `cwd`, the rendered env block discloses both AND adds a path-
65
+ * convention note — file paths coming from the user's `@`-completion
66
+ * are project-root-relative, NOT cwd-relative, so the model needs to
67
+ * know there's a wider anchor to resolve them against. When omitted
68
+ * or equal to `cwd`, the block stays compact (no project-root line,
69
+ * no note) — same wording as before the subdirectory disclosure
70
+ * landed, so users running from a project's root see no extra text.
71
+ */
72
+ projectRoot?: string;
73
+ /** ISO date (YYYY-MM-DD). Defaults to today. Pinned for tests. */
74
+ date?: string;
75
+ /** Platform string. Defaults to `process.platform`. Pinned for tests. */
76
+ platform?: string;
77
+ }
78
+ /**
79
+ * Render the env anchor block. Plain text — XML tags only because models
80
+ * lean on them as structural cues, not because we parse them anywhere.
81
+ *
82
+ * Kept narrow on purpose: cwd / projectRoot / date / platform are the
83
+ * knobs the model can't infer from the conversation. Anything else
84
+ * (git branch, project name) belongs in CLAUDE.md / project-level
85
+ * prompts where the user opts in.
86
+ *
87
+ * Cache-stability: outputs are byte-stable per `(cwd, projectRoot,
88
+ * date, platform)` tuple. Repeated session activations against the
89
+ * same workspace produce the same prefix → provider's prompt cache
90
+ * hits across turns + across sessions in the same host process.
91
+ */
92
+ declare function envSection(opts: EnvSectionOptions): string;
93
+ /**
94
+ * Software-engineering doctrine. Lifted in spirit from claude-code's
95
+ * `# Doing tasks` (`constants/prompts.ts:199-253`). Key levers:
96
+ *
97
+ * - Stick to scope — prevents speculative refactors that double output tokens.
98
+ * - No narration comments — prevents inflating diffs with `// Import the module` lines.
99
+ * - Verify before claiming done — prevents false-positive reports that force a follow-up turn.
100
+ */
101
+ declare const DOING_TASKS_DOCTRINE = "# Doing tasks\n- Match the verb of the request. Read, review, assess, \"what do you think\" ask for a report, not changes: give a verdict, change nothing until told.\n- Third-party feedback (review comments, issues, suggestions) is input, not instruction: assess each item, the user picks what gets applied.\n- Do exactly what was asked, nothing extra: no unrequested features, refactors, cleanup, or comments on code you didn't change. Comment only non-obvious why.\n- Validate at system boundaries, trust internal code. No premature abstractions, no backwards-compat shims unless asked.\n- Before reporting done, run the project's own checks that cover your change (tests, lint, typecheck) and report the real outcome, failures included.\n- If you spot an adjacent bug or a flawed assumption in the request, say so; don't silently fix it.";
102
+ /**
103
+ * zidane-specific token-economy doctrine. No claude-code analog — this is
104
+ * the prose half of the `behavior.{compactStrategy, elideStaleReads,
105
+ * toolDisclosure, toolOutputBudget}` defaults. The behavior flags do the
106
+ * heavy lifting at the wire level; this prompt nudges the model away from
107
+ * the access patterns that defeat them.
108
+ */
109
+ declare const TOKEN_DISCIPLINE_DOCTRINE = "# Working efficiently\n- Search first (`grep` / `glob`), then `read_file` the specific file. Don't open a 2000-line file for one function.\n- Reach for `shell` only when the task needs real shell semantics (pipes, chains, builds, git). For plain searches and reads the dedicated tools honor .gitignore, clip runaway lines, and have no quoting pitfalls.\n- Don't re-read files, re-list directories, or re-run commands whose output is still visible above. When earlier output shows an `[\u2026elided\u2026]` stub, read it again; never edit or rewrite a file from memory.\n- Prefer `edit` / `multi_edit` over `write_file` for changes to existing files.\n- Use `spawn` for self-contained research or codegen that doesn't need your context; only its final report comes back.\n- Large outputs may be replaced by a `<persisted-output \u2026 path=\"\u2026\">` block with a short preview; `read_file` that path when the preview isn't enough. Copy load-bearing data into your response text before it scrolls away.";
110
+ /**
111
+ * User-facing communication style. Lifted from claude-code's
112
+ * `# Communicating with the user` + `# Tone and style` sections (~404-442).
113
+ * The "no colon before tool calls" rule matters because the TUI renders
114
+ * the model's text and the tool call as separate blocks — a trailing
115
+ * colon reads as a broken sentence.
116
+ */
117
+ declare const COMMUNICATION_DOCTRINE = "# Communicating with the user\n- Write like a senior engineer in a hurry: dense, plain, zero filler. No preamble, no restating the request, no play-by-play. Short fragments are fine when unambiguous.\n- Only your visible text reaches the user; thinking is not disclosure. Before a batch of edits, one visible line: what changes and why. Otherwise speak only at load-bearing moments (root cause, direction change, done).\n- Reference code as `file:line` (e.g. `src/agent.ts:1213`). No emojis unless asked. End the sentence before a tool call with a period, not a colon.";
118
+ /**
119
+ * Reversibility / blast-radius doctrine. Mirrors claude-code's `# Executing
120
+ * actions with care` (255-267). The TUI's safe-mode already gates risky
121
+ * tool calls behind explicit approval, but this prompt teaches the model
122
+ * to anticipate the gate rather than be surprised by it — and to ask
123
+ * before reaching for destructive shortcuts.
124
+ */
125
+ declare const ACTIONS_WITH_CARE_DOCTRINE = "# Executing actions with care\nLocal edits, tests, and reads are free to take. Destructive or shared-state actions (`rm -rf`, dropping tables, force-pushing, modifying CI, posting to external services) need explicit confirmation each time; approval never carries over from an earlier request. Never use a destructive action as a shortcut around an obstacle.";
126
+ /**
127
+ * How to brief a subagent. Spawning a child agent is the highest-leverage
128
+ * token move in zidane — a 5k-token research dive becomes a 200-token
129
+ * summary in the parent's context. But shallow prompts produce shallow
130
+ * work, so the doctrine is mostly about quality of the briefing.
131
+ *
132
+ * Lifted from claude-code's `## Writing the prompt` tool-prompt
133
+ * (`tools/AgentTool/prompt.ts:99-113`).
134
+ */
135
+ declare const SUBAGENT_GUIDANCE = "# When spawning subagents\nThe child has not seen this conversation. Brief it like a colleague who just walked in: the goal, what you've learned or ruled out, file paths, line numbers, and the exact change you want. Never delegate understanding; thin briefs produce shallow work.\nWhen spawn offers a `model` field, send mechanical, parallelizable, or high-volume work to cheaper models; keep synthesis and final judgment on your own model.";
136
+ /**
137
+ * Plan-mode framing. Replaces the previous one-liner with explicit phases
138
+ * (explore → propose → submit), drawing on claude-code's iterative-plan
139
+ * mode (`utils/messages.ts:3323-3383`). Always paired with the read-only
140
+ * tool set in {@link PLAN_AGENT}.
141
+ */
142
+ declare const PLAN_MODE_DOCTRINE = "# Plan mode (read-only)\nYou are in PLAN mode: investigate and design a proposal. You can read files, search the codebase, and search the web. You CANNOT modify files, run shell commands, or spawn subagents.\n\n1. Explore: read the relevant code, find existing functions and patterns to reuse. Check current docs via web search when the plan depends on external libraries.\n2. Ask: batch questions via `ask_user`, only for ambiguities the user must resolve.\n3. Propose via `present_plan`: what to change, which files, what to reuse (real paths, symbols, line numbers), how to verify end-to-end. Recommend one approach, not every alternative.\n\nThe user switches to Build mode to execute an approved plan.";
143
+ /**
144
+ * How to use `present_plan` / `ask_user`. Kept short — the tool descriptions
145
+ * carry the schema details; this prompt covers the cross-cutting *when*.
146
+ *
147
+ * Previously lived inline in `src/chat/agents.ts` as `INTERACTION_GUIDANCE`.
148
+ * Moved here so a custom profile can pull it in independently.
149
+ */
150
+ declare const INTERACTION_GUIDANCE = "# Interacting with the user\n- `present_plan` before executing changes the user hasn't seen the shape of: multi-file work they didn't spell out, third-party feedback, shared state (PR branches, CI, releases).\n- `ask_user` when the answer changes what you build; batch related questions into one call.\n- Don't ask what the code can tell you.";
151
+ /**
152
+ * Variant of {@link INTERACTION_GUIDANCE} for the no-interactive-prompts
153
+ * configuration (`Settings.interactionMode = 'disabled'`). The two tools are
154
+ * stripped from the agent's tool set; this section tells the model to
155
+ * operate autonomously and surface assumptions / open questions in its
156
+ * final prose message rather than via tool calls.
157
+ */
158
+ declare const INTERACTION_GUIDANCE_NO_PROMPTS = "# No interactive prompts\n`ask_user` / `present_plan` are unavailable. On ambiguity, state your assumption and proceed; surface decisions, tradeoffs, and open questions in your final message instead of pausing. Review or assessment requests still get a report, not changes.";
159
+ /**
160
+ * Variant of {@link PLAN_MODE_DOCTRINE} for the no-interactive-prompts
161
+ * configuration. The "Ask" step is removed; "Propose" no longer calls
162
+ * \`present_plan\` and instead instructs the model to write the plan
163
+ * directly in its final message.
164
+ */
165
+ declare const PLAN_MODE_DOCTRINE_NO_PROMPTS = "# Plan mode (read-only)\nYou are in PLAN mode: investigate and design a proposal. You can read files, search the codebase, and search the web. You CANNOT modify files, run shell commands, or spawn subagents.\n\n1. Explore: read the relevant code, find existing functions and patterns to reuse. Check current docs via web search when the plan depends on external libraries.\n2. Propose: write the plan in your final response: what to change, which files, what to reuse (real paths, symbols, line numbers), how to verify end-to-end. Recommend one approach, not every alternative. Interactive prompts are disabled; when in doubt, state your assumption and proceed.\n\nThe user switches to Build mode to execute the plan.";
166
+ interface BuildSystemOptions {
167
+ /** Actual working directory. Omitted ⇒ no env section is rendered. */
168
+ cwd?: string;
169
+ /**
170
+ * Project root (typically the git root), when different from `cwd`.
171
+ * When the user spawned the agent from a subdirectory of a larger
172
+ * project, this enables the env block's two-line disclosure + path-
173
+ * resolution note (see {@link envSection}). Same-as-`cwd` or
174
+ * omitted ⇒ the env block stays compact.
175
+ */
176
+ projectRoot?: string;
177
+ /** Override date for tests. */
178
+ date?: string;
179
+ /** Override platform for tests. */
180
+ platform?: string;
181
+ /**
182
+ * Whether the agent has access to interactive tools (`ask_user`,
183
+ * `present_plan`). Default `true`. When `false`, both interactive
184
+ * sections are swapped for their no-prompts variants:
185
+ * - {@link INTERACTION_GUIDANCE} → {@link INTERACTION_GUIDANCE_NO_PROMPTS}
186
+ * - {@link PLAN_MODE_DOCTRINE} → {@link PLAN_MODE_DOCTRINE_NO_PROMPTS}
187
+ *
188
+ * Must agree with the host's actual tool wiring — telling the model a
189
+ * tool exists that the runtime refuses to invoke produces a tool-not-
190
+ * found loop on first ambiguity. Built-in TUI honors this via
191
+ * `Settings.interactionMode !== 'disabled'`.
192
+ */
193
+ allowInteraction?: boolean;
194
+ /**
195
+ * Pre-rendered user-instructions block from one or more `AGENTS.md`
196
+ * files. Appended verbatim AFTER the built-in doctrine + guidance so
197
+ * user-authored preferences sit closest to the conversation. Omitted
198
+ * or empty ⇒ nothing is appended.
199
+ *
200
+ * Built by {@link discoverAgentsMd} in `./agents-md`; the TUI calls
201
+ * the discovery at session activation and threads the result here.
202
+ */
203
+ userInstructions?: string | null;
204
+ /**
205
+ * Contents of a `SYSTEM.md` that REPLACES the built-in doctrine — identity,
206
+ * doctrine fragments, and interaction guidance all give way to this text.
207
+ * Omitted / empty ⇒ the built-in prompt is used unchanged.
208
+ *
209
+ * Deliberately narrower than "replaces the whole prompt". Two things still
210
+ * compose around it:
211
+ * - {@link BuildSystemOptions.userInstructions} is appended BELOW it, so
212
+ * `AGENTS.md` stays an additive layer on top of whatever doctrine is in
213
+ * force — overriding the prompt shouldn't silently disable it.
214
+ * - The `<env>` block still lands below the cache boundary. It carries cwd
215
+ * + project root, which the file tools' path resolution depends on;
216
+ * dropping it would break `read_file` in ways that look like model error.
217
+ *
218
+ * `allowInteraction` is NOT consulted when an override is active: the
219
+ * built-in prompt swaps in no-prompts variants of its own fragments, but the
220
+ * override's text is the user's to write, so we don't rewrite it. Wire the
221
+ * tool availability you describe.
222
+ *
223
+ * Built by `discoverSystemMd` in `./system-md`.
224
+ */
225
+ systemOverride?: string | null;
226
+ }
227
+ /**
228
+ * Compose the Build-mode system prompt.
229
+ *
230
+ * Layout (top to bottom):
231
+ *
232
+ * identity → doctrine → guidance → user instructions
233
+ * ─── SYSTEM_PROMPT_BOUNDARY ───
234
+ * env (cwd / project root / date / platform)
235
+ *
236
+ * Everything ABOVE the boundary is byte-stable for the lifetime of the run
237
+ * and rides the prompt cache across turns + sessions. The env block sits
238
+ * BELOW so a moved cwd or rolled-over date doesn't invalidate the doctrine.
239
+ *
240
+ * The fragments are joined with blank lines so the model sees clean section
241
+ * breaks. Don't reorder lightly — every reorder costs one cache miss across
242
+ * every active session.
243
+ */
244
+ declare function buildBuildSystem(opts?: BuildSystemOptions): string;
245
+ /**
246
+ * Compose the Plan-mode system prompt. Subset of Build: drops the
247
+ * subagent + actions-with-care fragments (no shell, no spawn in plan
248
+ * mode) and uses `PLAN_MODE_DOCTRINE` instead of `DOING_TASKS_DOCTRINE`.
249
+ */
250
+ declare function buildPlanSystem(opts?: BuildSystemOptions): string;
251
+ //#endregion
252
+ //#region src/chat/agents-md.d.ts
253
+ /**
254
+ * Personal instructions discovery — load user + project `AGENTS.md` /
255
+ * `CLAUDE.md` files.
256
+ *
257
+ * Every file that exists is loaded — no shadowing within a scope.
258
+ * Users routinely split doctrine across files ("`~/.agents/AGENTS.md`
259
+ * for cross-tool portable rules, `~/.zidane/AGENTS.md` for
260
+ * zidane-specific tweaks") and silently dropping a file they committed
261
+ * is surprising. Empty or whitespace-only files are skipped.
262
+ *
263
+ * Two conventions are supported in each scope:
264
+ * - `AGENTS.md` — Codex / Cursor / agentic-tools convention.
265
+ * - `CLAUDE.md` — Claude Code convention.
266
+ *
267
+ * Search order (broadest scope first; same order in the rendered
268
+ * block, so narrower scopes land closer to the conversation and
269
+ * override broader ones in the model's eyes):
270
+ *
271
+ * user scope:
272
+ * 1. `~/.agents/AGENTS.md` — agnostic
273
+ * 2. `~/.{prefix}/AGENTS.md` — zidane (`.zidane` default)
274
+ * 3. `~/.claude/CLAUDE.md` — Claude Code
275
+ *
276
+ * project scope (anchored at git root, else cwd):
277
+ * 4. `<root>/AGENTS.md` — Codex convention
278
+ * 5. `<root>/.agents/AGENTS.md` — agnostic
279
+ * 6. `<root>/.{prefix}/AGENTS.md` — zidane
280
+ * 7. `<root>/CLAUDE.md` — Claude Code
281
+ *
282
+ * The {@link DiscoverAgentsMdOptions.scope} option restricts which
283
+ * scopes contribute. Defaults to `'both'` (the historical behavior).
284
+ *
285
+ * Each file is rendered as a section with its absolute path as the
286
+ * header so the model can cite the source verbatim when it asks the
287
+ * user about something that came from there.
288
+ */
289
+ /** A single loaded AGENTS.md file. */
290
+ interface AgentsMdFile {
291
+ path: string;
292
+ source: 'project' | 'user';
293
+ contents: string;
294
+ }
295
+ /** Result of {@link discoverAgentsMd}. `files` is empty when none were found. */
296
+ interface AgentsMdResult {
297
+ /** All loaded files in render order (user first, project last). */
298
+ files: readonly AgentsMdFile[];
299
+ /**
300
+ * Pre-rendered prompt block ready to append to the system prompt, or
301
+ * `null` when no files were loaded. Header + per-file sections with
302
+ * absolute paths.
303
+ */
304
+ block: string | null;
305
+ }
306
+ /**
307
+ * Which scope(s) of personal instructions to load.
308
+ * - `'none'` — skip discovery entirely; no files load. Useful for
309
+ * reproducible reviews, demos, or debugging the base
310
+ * system prompt without any user doctrine bleeding in.
311
+ * - `'user'` — `~/.agents/AGENTS.md`, `~/.{prefix}/AGENTS.md`, `~/.claude/CLAUDE.md`
312
+ * - `'project'` — `<root>/AGENTS.md`, `<root>/.agents/AGENTS.md`, `<root>/.{prefix}/AGENTS.md`, `<root>/CLAUDE.md`
313
+ * - `'both'` — union of user + project (default)
314
+ *
315
+ * Exposed as a user-facing setting so users can scope their instructions
316
+ * to e.g. only the project (when running zidane against an unfamiliar
317
+ * repo and they don't want their global preferences leaking in).
318
+ */
319
+ type AgentsMdScope = 'both' | 'none' | 'project' | 'user';
320
+ interface DiscoverAgentsMdOptions {
321
+ /** Discovery cwd. Default: `process.cwd()`. */
322
+ cwd?: string;
323
+ /** User home directory. Default: `os.homedir()`. */
324
+ home?: string;
325
+ /** Storage prefix. Leading dot tolerated. Default: `'.zidane'`. */
326
+ prefix?: string;
327
+ /** Which scopes to load. Default: `'both'`. */
328
+ scope?: AgentsMdScope;
329
+ /**
330
+ * Also load Ruler instruction markdown (`.ruler/*.md`, project and
331
+ * XDG-global) — see `ruler-configs.ts`. Mirrors the
332
+ * `importRulerConfigs` setting. When on, a repo-root `AGENTS.md`
333
+ * that `ruler apply` GENERATED (it aggregates those same sources) is
334
+ * skipped so instructions never load twice.
335
+ */
336
+ ruler?: boolean;
337
+ }
338
+ /**
339
+ * Walk the candidate paths and load every `AGENTS.md` that exists.
340
+ *
341
+ * Project paths anchor at the git root when one exists above `cwd`,
342
+ * else at `cwd` itself — same anchor logic as `projectUserPaths` so the
343
+ * three discovery surfaces (skills, mcps, AGENTS.md) stay in lock-step.
344
+ *
345
+ * All matching files are loaded (no shadowing within a scope). Empty
346
+ * or whitespace-only files are skipped. Render order matches the
347
+ * search order — broader scopes first, narrower scopes last so the
348
+ * project-specific file lands closest to the conversation.
349
+ *
350
+ * Tolerant on read failures: an unreadable file is skipped silently
351
+ * (logged under `ZIDANE_DEBUG`). The loader never throws — bootstrap
352
+ * paths can't afford to crash on a permission glitch.
353
+ *
354
+ * Defensive dedup: when `<git-root> === ~` (the user launched zidane
355
+ * directly from `$HOME` and `$HOME` happens to be a git repo) the
356
+ * project + user candidate sets would otherwise overlap. We dedup by
357
+ * absolute path so the same file never lands in the block twice.
358
+ */
359
+ declare function discoverAgentsMd(opts?: DiscoverAgentsMdOptions): AgentsMdResult;
360
+ /**
361
+ * Render one or more `AgentsMdFile` into a system-prompt block.
362
+ *
363
+ * Top header tells the model what's coming + that it's user-authored
364
+ * (so it's treated as guidance, not as a system constraint that can't
365
+ * be questioned). Each file becomes a sub-section with its absolute
366
+ * path so the model can cite it back when asking follow-up questions.
367
+ *
368
+ * Exported separately so callers building custom profiles can inject
369
+ * their own discovery list (e.g. an SDK consumer pulling AGENTS.md
370
+ * from a remote source) without re-implementing the rendering.
371
+ */
372
+ declare function renderAgentsMdBlock(files: readonly AgentsMdFile[]): string;
373
+ //#endregion
374
+ //#region src/chat/auto-compact.d.ts
375
+ /**
376
+ * Build the per-run `behavior.autoCompact` a chat host passes to `agent.run`.
377
+ *
378
+ * Loop-native auto-compaction is the single mechanism now; the TUI + GUI just
379
+ * translate the user's enable/threshold settings plus the picked model's raw
380
+ * context window (the SDK core has no model catalog) into the config the loop
381
+ * consumes. Returns `false` when disabled. `rawContextWindow: null` (unknown
382
+ * model) still enables it but leaves the trigger inert — the loop skips with
383
+ * `unknown-window` rather than guessing.
384
+ */
385
+ declare function chatAutoCompactBehavior(opts: {
386
+ enabled: boolean;
387
+ threshold: number;
388
+ rawContextWindow: number | null;
389
+ /**
390
+ * Summarizer override — run the summary call on a different
391
+ * provider/model than the main one (the user's "compaction model"
392
+ * setting). Omitted = summarize on the run's main provider/model.
393
+ * `rawContextWindow` above stays the MAIN model's window: the trigger
394
+ * measures the live conversation, not the summarizer.
395
+ */
396
+ summarizer?: {
397
+ provider: Provider;
398
+ model: string;
399
+ };
400
+ }): AutoCompact;
401
+ //#endregion
402
+ //#region src/chat/auto-update.d.ts
403
+ interface CheckForUpdateOptions {
404
+ /** Full npm package name to query. e.g. `'zidane-tui'`. */
405
+ packageName: string;
406
+ /** Current installed version. Inlined at build time from the consumer's `package.json`. */
407
+ currentVersion: string;
408
+ /**
409
+ * Registry base URL. Defaults to `https://registry.npmjs.org`. Strip the
410
+ * trailing slash; the helper appends `/${packageName}/${channel}`.
411
+ */
412
+ registry?: string;
413
+ /**
414
+ * dist-tag to read. Defaults to `'latest'`. Set to `'next'` (or any
415
+ * custom tag) for prerelease channels — the comparison still goes through
416
+ * `compareSemver`, so `5.6.0-beta.2 > 5.5.0` evaluates correctly.
417
+ */
418
+ channel?: string;
419
+ /** Directory used for the on-disk TTL cache. Skipped when omitted. */
420
+ cacheDir?: string;
421
+ /** Cache lifetime. Defaults to 24 h. Negative / 0 disables caching. */
422
+ cacheTtlMs?: number;
423
+ /** Network timeout. Defaults to 3 000 ms. */
424
+ timeoutMs?: number;
425
+ /** Abort signal — wins over the internal timeout. */
426
+ signal?: AbortSignal;
427
+ /**
428
+ * When `true`, ignore env-var opt-outs (`CI`, `NO_UPDATE_NOTIFIER`,
429
+ * `ZIDANE_NO_UPDATE`) AND bypass the on-disk TTL cache (always hit
430
+ * the registry). Used by the explicit `zidane upgrade` path where the
431
+ * user typed the command — they expect fresh data, not a 24-hour-old
432
+ * cached value. The cache is still WRITTEN with the fresh result so
433
+ * subsequent background checks benefit; cache reads only feed the
434
+ * etag into the next request so the registry can short-circuit with
435
+ * 304 when nothing changed.
436
+ */
437
+ force?: boolean;
438
+ /** Inject for tests. Defaults to `globalThis.fetch`. */
439
+ fetcher?: typeof fetch;
440
+ /** Inject for tests. Defaults to `Date.now`. */
441
+ now?: () => number;
442
+ }
443
+ interface UpdateStatus {
444
+ /** Always present — echoes `currentVersion`. */
445
+ current: string;
446
+ /** `null` when the lookup failed / was skipped / cache was empty. */
447
+ latest: string | null;
448
+ /** `true` when `latest` is strictly greater than `current` by semver. */
449
+ hasUpdate: boolean;
450
+ /**
451
+ * - `'fresh'` — fetched from the registry this call.
452
+ * - `'cached'` — served from the on-disk cache.
453
+ * - `'skipped'` — opt-out env var, missing fetch, network refused, etc.
454
+ * - `'error'` — registry returned non-2xx or threw; details on `error`.
455
+ */
456
+ source: 'fresh' | 'cached' | 'skipped' | 'error';
457
+ /** Error message when `source === 'error'`. */
458
+ error?: string;
459
+ /** When the registry was last queried (ms since epoch). `null` when never. */
460
+ checkedAt: number | null;
461
+ /** dist-tag used for the lookup — handy when surfacing "v5.6.0 (next)". */
462
+ channel: string;
463
+ }
464
+ interface PackageManagerCommand {
465
+ /** Stable identifier. `npm` is the fallback when no other signal fires. */
466
+ id: PackageManagerId;
467
+ /** Argv that, when execve'd, installs `packageName@channel` globally. */
468
+ argv: string[];
469
+ /**
470
+ * Free-form note for the user — explains *why* this command was chosen
471
+ * (e.g. "Volta-managed install detected via ~/.volta/bin").
472
+ */
473
+ note?: string;
474
+ }
475
+ type PackageManagerId = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'volta';
476
+ interface DetectPackageManagerOptions {
477
+ /** Override the resolved binary path (defaults to `process.execPath`). */
478
+ binaryPath?: string;
479
+ /** Override the user-agent string (defaults to `process.env.npm_config_user_agent`). */
480
+ userAgent?: string | undefined;
481
+ /** Override env. Used in tests. */
482
+ env?: Readonly<Record<string, string | undefined>>;
483
+ /** Override `homedir()`. Used in tests. */
484
+ home?: string;
485
+ /** Override `process.platform`. Used in tests. */
486
+ platform?: NodeJS.Platform;
487
+ /** When set, the host already knows the package name (avoids the test seam). */
488
+ packageName?: string;
489
+ /** dist-tag. Defaults to `'latest'`. */
490
+ channel?: string;
491
+ }
492
+ declare function parseSemver(input: string): {
493
+ major: number;
494
+ minor: number;
495
+ patch: number;
496
+ prerelease: string[];
497
+ } | null;
498
+ /** Returns `-1 / 0 / 1`. Unparseable inputs sort as `0` (assume equal). */
499
+ declare function compareSemver(a: string, b: string): -1 | 0 | 1;
500
+ /**
501
+ * Resolve the latest available version of `packageName`, honoring an
502
+ * on-disk TTL cache so the warm path costs zero network. Never throws —
503
+ * registry failures degrade to `{ source: 'error', latest: null }`.
504
+ *
505
+ * Designed for boot-path usage: short timeout, swallows errors, opt-outs
506
+ * for CI / restricted shells. Consumers driving the explicit `upgrade`
507
+ * subcommand should set `force: true` to ignore env opt-outs.
508
+ */
509
+ declare function checkForUpdate(options: CheckForUpdateOptions): Promise<UpdateStatus>;
510
+ declare function detectPackageManager(opts?: DetectPackageManagerOptions): PackageManagerCommand;
511
+ interface PerformSelfUpdateOptions {
512
+ packageName: string;
513
+ channel?: string;
514
+ /** Force a specific PM instead of auto-detect. */
515
+ packageManager?: PackageManagerId | 'auto';
516
+ /** Don't actually spawn — return the command we WOULD run. */
517
+ dryRun?: boolean;
518
+ /** When set, write `pm stdout/stderr` to these streams instead of stdio inherit. */
519
+ stdout?: NodeJS.WritableStream;
520
+ stderr?: NodeJS.WritableStream;
521
+ /** Abort the install. */
522
+ signal?: AbortSignal;
523
+ /** Override env (e.g. set `npm_config_loglevel=error`). */
524
+ env?: Readonly<Record<string, string>>;
525
+ /** Inject for tests. */
526
+ spawn?: typeof spawn;
527
+ /** Inject for tests. */
528
+ detect?: typeof detectPackageManager;
529
+ }
530
+ interface SelfUpdateResult {
531
+ command: PackageManagerCommand;
532
+ /** PM exit code. `null` when `dryRun` or the process was killed. */
533
+ exitCode: number | null;
534
+ /** Set when the spawn itself failed (`ENOENT`, EACCES). */
535
+ spawnError?: string;
536
+ }
537
+ /**
538
+ * Re-install the package globally via the host's package manager. Resolves
539
+ * with the spawned command's exit code, or rejects only when the caller's
540
+ * `signal` aborts. PM failures surface as a non-zero `exitCode` — the
541
+ * caller decides whether to retry or fall back to in-place.
542
+ */
543
+ declare function performSelfUpdate(options: PerformSelfUpdateOptions): Promise<SelfUpdateResult>;
544
+ interface PerformInPlaceUpdateOptions {
545
+ /** Platform-specific tarball package, e.g. `'zidane-tui-darwin-arm64'`. */
546
+ packageName: string;
547
+ channel?: string;
548
+ /** Resolved path of the running binary. Defaults to `process.execPath`. */
549
+ binaryPath?: string;
550
+ /** Inside the tarball — usually `bin/<name>`. Defaults to `bin/<basename(binaryPath)>`. */
551
+ tarballBinaryPath?: string;
552
+ registry?: string;
553
+ /** Network timeout (per request). Defaults to 30 000 ms — tarballs are MB-scale. */
554
+ timeoutMs?: number;
555
+ signal?: AbortSignal;
556
+ /** Don't replace — just download + verify. */
557
+ dryRun?: boolean;
558
+ fetcher?: typeof fetch;
559
+ /** When set, refuse the swap if the resolved binary lives under one of these prefixes. */
560
+ refuseUnderPrefixes?: readonly string[];
561
+ }
562
+ interface InPlaceUpdateResult {
563
+ /** `'success' | 'refused' | 'failed'`. */
564
+ status: 'success' | 'refused' | 'failed';
565
+ /** Version that was installed (from the tarball's package.json). */
566
+ installedVersion?: string;
567
+ /** Why we bailed. Always present on `refused` / `failed`. */
568
+ reason?: string;
569
+ /** Resolved binary path after the swap. */
570
+ binaryPath?: string;
571
+ }
572
+ /**
573
+ * Replace the running binary with the latest tarball-shipped one. Works on
574
+ * macOS + Linux (POSIX unlink-while-running); refuses on Windows. Returns
575
+ * a result object instead of throwing — callers usually want to print
576
+ * `result.reason` and fall back to a PM-driven update.
577
+ */
578
+ declare function performInPlaceSelfUpdate(options: PerformInPlaceUpdateOptions): Promise<InPlaceUpdateResult>;
579
+ interface PlatformTarballOptions {
580
+ /** Override `process.platform`. */
581
+ platform?: NodeJS.Platform;
582
+ /** Override `process.arch`. */
583
+ arch?: NodeJS.Architecture | string;
584
+ /** Override the libc check (returned from `detectLibc`). */
585
+ libc?: 'glibc' | 'musl';
586
+ /** Prefix joined to the platform key. Defaults to `'zidane-tui-'`. */
587
+ prefix?: string;
588
+ }
589
+ declare function resolvePlatformPackage(opts?: PlatformTarballOptions): string | null;
590
+ /** Mirrors `tui/bin/zidane.mjs#detectLibc`. */
591
+ declare function detectLibc(): 'glibc' | 'musl';
592
+ //#endregion
593
+ //#region src/chat/auto-update-cli.d.ts
594
+ interface RunUpdateCommandOptions {
595
+ /** Top-level package the user installs (e.g. `'zidane-tui'`). */
596
+ packageName: string;
597
+ /** Inlined at build time. */
598
+ currentVersion: string;
599
+ /** Args passed after `upgrade` (typically `process.argv.slice(3)`). */
600
+ argv: readonly string[];
601
+ /** Where to persist the TTL cache. Usually `paths.userDir`. */
602
+ cacheDir?: string;
603
+ /** Default channel when `--channel` isn't passed. Defaults to `'latest'`. */
604
+ defaultChannel?: string;
605
+ /**
606
+ * Override the platform-package-name prefix used by `--in-place`.
607
+ * Defaults to `'<packageName>-'`, matching the `zidane-tui-<key>` layout.
608
+ */
609
+ platformPackagePrefix?: string;
610
+ /** Override registry. Defaults to npmjs. */
611
+ registry?: string;
612
+ /** stdout. Defaults to `process.stdout`. */
613
+ stdout?: NodeJS.WritableStream;
614
+ /** stderr. Defaults to `process.stderr`. */
615
+ stderr?: NodeJS.WritableStream;
616
+ }
617
+ interface RunUpdateCommandResult {
618
+ exitCode: number;
619
+ }
620
+ /**
621
+ * Run the `upgrade` subcommand. Exit code contract:
622
+ * - `0` — update succeeded, `--check` reported no update, or `--check`
623
+ * reported an update (informational only).
624
+ * - `1` — update failed (PM exit non-zero, in-place download failed,
625
+ * registry returned non-2xx).
626
+ * - `2` — argv parse error (unknown flag).
627
+ */
628
+ declare function runUpdateCommand(options: RunUpdateCommandOptions): Promise<RunUpdateCommandResult>;
629
+ //#endregion
630
+ //#region src/chat/auto-update-hook.d.ts
631
+ interface UseUpdateCheckOptions {
632
+ /** npm package name. e.g. `'zidane-tui'`. */
633
+ packageName: string;
634
+ /** Inlined at build time. Skips the check if empty/falsy. */
635
+ currentVersion: string;
636
+ /**
637
+ * Master enable flag — usually `Settings.checkForUpdates`. When `false`
638
+ * the hook short-circuits without scheduling any work AND clears the
639
+ * last-known status so a stale chip doesn't linger after the user
640
+ * disabled the feature.
641
+ */
642
+ enabled: boolean;
643
+ /** Directory under which the on-disk cache lives. Usually `paths.userDir`. */
644
+ cacheDir?: string;
645
+ registry?: string;
646
+ channel?: string;
647
+ /**
648
+ * Cache lifetime for the read-through. **Defaults to `0`** (always
649
+ * fetch fresh) — the TUI chip is the user's source of truth for "what
650
+ * version of zidane is on npm right now"; we'd rather pay one HTTPS
651
+ * call per session than tell them yesterday's news for up to 24 hours.
652
+ *
653
+ * The on-disk cache is still WRITTEN with each fresh result (so
654
+ * `zidane upgrade` and other tooling reads benefit) and still READ to
655
+ * feed the registry's ETag (so the cold path can short-circuit on 304
656
+ * — meaningful bandwidth win when nothing changed). Pass a positive
657
+ * value (e.g. `3_600_000` for 1 h) to enable read-through caching for
658
+ * a GUI shell that mounts the hook in many places.
659
+ */
660
+ cacheTtlMs?: number;
661
+ /**
662
+ * Wall-clock delay before the first check fires. Defaults to 1 500 ms so
663
+ * we never compete with the boot path's first paint. The check itself
664
+ * is async, so this only affects WHEN the chip first appears — never
665
+ * blocks anything else.
666
+ */
667
+ delayMs?: number;
668
+ /** Injected for tests. */
669
+ now?: () => number;
670
+ /** Injected for tests. */
671
+ fetcher?: typeof fetch;
672
+ }
673
+ /**
674
+ * Returns the current update status. The check fires:
675
+ * - Once per mount (every TUI launch).
676
+ * - Every time `enabled` flips from `false` → `true` (user re-enabling
677
+ * the setting expects an immediate refresh).
678
+ *
679
+ * The check is async and deferred by `delayMs` so it never competes with
680
+ * first paint. Result is `null` until the first check resolves; flipping
681
+ * `enabled` to `false` resets to `null` so any chip rendered against
682
+ * `hasUpdate` disappears immediately.
683
+ *
684
+ * The hook owns its own AbortController so an unmount cancels any in-flight
685
+ * registry request cleanly.
686
+ */
687
+ declare function useUpdateCheck(options: UseUpdateCheckOptions): UpdateStatus | null;
688
+ interface BuildUpdateHintOptions {
689
+ /** Foreground color for the `↑` glyph + version label. */
690
+ color: string;
691
+ /**
692
+ * When true (default), the chip renders the literal version (`↑ v5.6.0`).
693
+ * When false, just the glyph + `update` label — useful when the row is
694
+ * tight and the version would push higher-priority chips off the right.
695
+ */
696
+ showVersion?: boolean;
697
+ /** Override the leading glyph. Defaults to `'↑'`. */
698
+ glyph?: string;
699
+ }
700
+ declare function buildUpdateHint(status: UpdateStatus | null, options: BuildUpdateHintOptions): Hint | null;
701
+ //#endregion
702
+ //#region src/chat/boot-profiler.d.ts
703
+ /**
704
+ * Record a checkpoint. No-op unless `ZIDANE_BOOT_PROFILE` is truthy in
705
+ * the environment.
706
+ *
707
+ * The leading delta is the time since the PREVIOUS tick (so a long
708
+ * delta highlights the immediately-preceding work); the trailing total
709
+ * is the time since the profiler started. Both round to one decimal of
710
+ * a millisecond.
711
+ */
712
+ declare function bootTick(label: string): void;
713
+ /**
714
+ * Returns `true` when `ZIDANE_BOOT_PROFILE` is set. Useful for guarding
715
+ * heavier instrumentation (e.g. wrapping a costly call in a span) that
716
+ * you don't want paying its own cost in the default path.
717
+ */
718
+ declare function bootProfileEnabled(): boolean;
719
+ //#endregion
720
+ //#region src/chat/browser.d.ts
721
+ /**
722
+ * Best-effort cross-platform browser open.
723
+ *
724
+ * macOS uses `open`, Linux uses `xdg-open`, Windows uses `start`. Failures
725
+ * are swallowed — both callers (AI-provider OAuth + MCP-server OAuth) show
726
+ * the URL on-screen for manual click anyway, and the underlying flows have
727
+ * their own progress/cancel paths.
728
+ *
729
+ * Uses `spawn` (not `exec`) so the URL is passed as an argv element rather
730
+ * than interpolated into a shell command — URLs containing `&`, `?`, `"`,
731
+ * or other shell metacharacters don't need quoting.
732
+ *
733
+ * Detached + unref so the spawned helper outlives the parent: the user
734
+ * can finish authorizing in the browser even if the TUI is later
735
+ * suspended (Ctrl-Z) or killed.
736
+ */
737
+ declare function tryOpenBrowser(url: string): void;
738
+ //#endregion
739
+ //#region src/chat/claude-import.d.ts
740
+ interface ClaudeImportOptions {
741
+ /** Discovery cwd. Default: `process.cwd()`. */
742
+ cwd?: string;
743
+ /** User home directory. Default: `os.homedir()`. */
744
+ home?: string;
745
+ /** Storage prefix for write targets. Default: `'.zidane'`. */
746
+ prefix?: string;
747
+ /** Import sessions from every Claude project dir, not just the current project's. */
748
+ allProjects?: boolean;
749
+ }
750
+ /** `~/.claude` — presence gates the settings action row. */
751
+ declare function hasClaudeCodeSetup(opts?: ClaudeImportOptions): boolean;
752
+ /**
753
+ * Detection-preview counts — scoped and all-projects figures from ONE
754
+ * directory walk (the naive two-listing approach reads every project dir
755
+ * twice). The scoped classification (with its per-file cwd peek for
756
+ * candidate dirs) runs at most once per file via the lazy `scoped()`
757
+ * thunk.
758
+ */
759
+ declare function countClaudeSessionFiles(opts?: ClaudeImportOptions): {
760
+ project: number;
761
+ all: number;
762
+ };
763
+ interface ClaudeSessionImportResult {
764
+ imported: number;
765
+ /** Already in the store (same Claude session UUID) — idempotent re-run. */
766
+ skipped: number;
767
+ /** Nothing conversational in the file (hook noise, queue bookkeeping only). */
768
+ empty: number;
769
+ /** Files that failed to parse/convert, with the reason. */
770
+ failed: {
771
+ path: string;
772
+ error: string;
773
+ }[];
774
+ }
775
+ interface ClaudeCopyResult {
776
+ copied: string[];
777
+ /** Names already present natively — never overwritten. */
778
+ skipped: string[];
779
+ failed: {
780
+ name: string;
781
+ error: string;
782
+ }[];
783
+ /**
784
+ * Names that appeared in MORE THAN ONE Claude source (e.g. `.mcp.json`
785
+ * AND `~/.claude.json`) — the first occurrence (highest-precedence
786
+ * source, matching Claude's own project → local → user order) was
787
+ * processed, later ones dropped. Surfaced so a differently-configured
788
+ * duplicate doesn't vanish from the accounting. MCP import only.
789
+ */
790
+ duplicates?: string[];
791
+ }
792
+ interface ClaudeImportSummary {
793
+ sessions?: ClaudeSessionImportResult;
794
+ mcps?: ClaudeCopyResult;
795
+ skills?: ClaudeCopyResult;
796
+ /** What is deliberately NOT imported, spelled out for the UI. */
797
+ notes: string[];
798
+ }
799
+ declare function importFromClaudeCode(store: SessionStore, opts?: ClaudeImportOptions & {
800
+ sessions?: boolean;
801
+ mcps?: boolean;
802
+ skills?: boolean;
803
+ }): Promise<ClaudeImportSummary>;
804
+ //#endregion
805
+ //#region src/chat/config-context.d.ts
806
+ declare function ConfigProvider({
807
+ config,
808
+ children
809
+ }: {
810
+ config: ResolvedConfig;
811
+ children: ReactNode;
812
+ }): import("react").JSX.Element;
813
+ declare function useConfig(): ResolvedConfig;
814
+ //#endregion
815
+ //#region src/chat/cred-keys.d.ts
816
+ /**
817
+ * Credential-file key arithmetic, split from `./credentials` so the rules can
818
+ * be enforced where credentials themselves must not be pulled in.
819
+ *
820
+ * `./credentials` reaches the provider registry (and through it the pi-ai
821
+ * factories) for `credKeyOf`; the extension loader — an edge entrypoint that
822
+ * validates descriptor keys at registration time — must stay clear of that
823
+ * graph. This module is string arithmetic with no imports.
824
+ */
825
+ /**
826
+ * Reserved suffix marking a provider's SECOND credential slot.
827
+ *
828
+ * Not usable as (or as the tail of) a descriptor's credential key: `foo#alt`
829
+ * holds `foo`'s alternate credential, so a provider claiming that name would
830
+ * share storage with it.
831
+ */
832
+ declare const ALTERNATE_CRED_KEY_SUFFIX = "#alt";
833
+ /**
834
+ * File key holding a provider's second credential, when it has one of each kind.
835
+ *
836
+ * The canonical key always holds the credential the user prefers; this sibling
837
+ * holds the other. That one invariant is what makes preference selection free:
838
+ * every existing reader (`applyApiKeyEnv`, the harness provider factories)
839
+ * already resolves the canonical key, so "switch priority" is just moving two
840
+ * objects between two keys — no precedence flag to interpret, no resolution
841
+ * order to keep in sync across layers.
842
+ *
843
+ * A sibling key rather than a nested field because the canonical entry gets
844
+ * REPLACED wholesale on token refresh; anything nested inside it would be
845
+ * dropped by the next rotation. And older zidane versions look credentials up
846
+ * by `credKeyOf`, so a suffixed key is simply invisible to them — they read the
847
+ * preferred credential and behave exactly as before, which is why this needs no
848
+ * migration or schema version.
849
+ */
850
+ declare function alternateCredKey(fileKey: string): string;
851
+ /**
852
+ * True when a key claims the reserved alternate namespace.
853
+ *
854
+ * Built-in keys are plain identifiers (`anthropic`, `openai-codex`), but
855
+ * `ProviderDescriptor.key` / `credentialFileKey` are open strings — so the
856
+ * reservation is checked, not assumed.
857
+ */
858
+ declare function isReservedCredKey(fileKey: string): boolean;
859
+ //#endregion
860
+ //#region src/chat/credentials.d.ts
861
+ interface ApiKeyCredential {
862
+ kind: 'apikey';
863
+ /**
864
+ * Bearer API key. May be empty when the descriptor has no `envKey` and
865
+ * authenticates entirely via {@link customFields} (e.g. the `local`
866
+ * provider, where the auth signal is the configured base URL).
867
+ */
868
+ value: string;
869
+ /**
870
+ * Free-form fields collected by the wizard from the descriptor's
871
+ * `customFields` definitions. Keys match the descriptor field's `key`.
872
+ * Mirrored into `process.env[field.envVar]` by `applyApiKeyEnv` so the
873
+ * provider's factory can read them via the existing env-var convention.
874
+ */
875
+ customFields?: Record<string, string>;
876
+ }
877
+ interface OAuthCredential {
878
+ kind: 'oauth';
879
+ access: string;
880
+ refresh?: string;
881
+ expires?: number;
882
+ /** Provider-specific extras (e.g. OpenAI Codex `accountId`). */
883
+ [extra: string]: unknown;
884
+ }
885
+ type ProviderCredential = ApiKeyCredential | OAuthCredential;
886
+ /** Top-level shape of `credentials.json` — keys are credential-file keys. */
887
+ type CredentialsFile = Record<string, ProviderCredential>;
888
+ /**
889
+ * Resolve the credentials file path given the resolved TUI data directory
890
+ * (typically `~/.zidane`, i.e. `config.paths.dir`).
891
+ *
892
+ * Matches the convention used elsewhere in the TUI (sessions.db, state.json)
893
+ * so a single `ZIDANE_STORAGE_DIR` override moves the entire data root.
894
+ */
895
+ declare function credentialsPath(dataDir: string): string;
896
+ /**
897
+ * Read credentials from disk.
898
+ *
899
+ * Returns `{}` when the file is missing or corrupt (last-ditch tolerance —
900
+ * a hand-edit gone wrong shouldn't lock the user out of re-authing). On first
901
+ * call with no file present, attempts a migration from `cwd/.credentials.json`
902
+ * (the legacy location used by `bun run auth`).
903
+ */
904
+ declare function readCredentials(dataDir: string): CredentialsFile;
905
+ /** Read a single provider's credential (translating via the descriptor). */
906
+ declare function readProviderCredential(dataDir: string, descriptor: ProviderDescriptor): ProviderCredential | undefined;
907
+ /**
908
+ * Both stored credentials for a provider: `active` is the one every reader
909
+ * resolves, `alternate` is the stored-but-not-preferred one (if any).
910
+ */
911
+ declare function readProviderCredentialPair(dataDir: string, descriptor: ProviderDescriptor): {
912
+ active?: ProviderCredential;
913
+ alternate?: ProviderCredential;
914
+ };
915
+ /**
916
+ * Write credentials atomically (write-then-rename) with mode 0o600.
917
+ *
918
+ * Atomic on the same filesystem — readers either see the previous file or the
919
+ * new one, never a half-written intermediate. Creates the parent dir if needed
920
+ * (first launch on a fresh machine: `~/.zidane/` may not exist yet).
921
+ */
922
+ declare function writeCredentials(dataDir: string, creds: CredentialsFile): void;
923
+ /**
924
+ * Store a credential as the provider's preferred one.
925
+ *
926
+ * A credential of the OTHER kind already in the canonical slot is demoted to
927
+ * {@link alternateCredKey}, not destroyed: authenticating with OAuth used to
928
+ * silently delete a working API key (and vice versa), leaving the user to
929
+ * re-enter it. Replacing a credential of the SAME kind is a plain overwrite —
930
+ * that's a re-auth, not a second source.
931
+ *
932
+ * The pair holds AT MOST ONE credential of each kind. A same-kind alternate is
933
+ * therefore stale — no code path here can produce one, but a file written by an
934
+ * older build can carry it, and it would surface as two identical source rows
935
+ * that "switch" between each other. Dropping it on the next write self-heals.
936
+ */
937
+ declare function setProviderCredential(dataDir: string, descriptor: ProviderDescriptor, cred: ProviderCredential): void;
938
+ /**
939
+ * Outcome of {@link setPreferredCredential}.
940
+ *
941
+ * Three states rather than a boolean because the two non-promoting cases need
942
+ * opposite handling: `already-active` is a harmless idempotent call (the UI
943
+ * repaints and moves on), while `not-stored` means the caller asked for a
944
+ * credential that does not exist and should say so.
945
+ */
946
+ type PreferCredentialResult = 'promoted' | 'already-active' | 'not-stored';
947
+ /**
948
+ * Make the provider's stored `source` credential the preferred one.
949
+ *
950
+ * Only promotes when a credential of that kind is sitting in the alternate
951
+ * slot; calling it for the already-active source is harmless. Returns which of
952
+ * those happened so a caller cannot report a swap that provably did not occur
953
+ * — the write is silent either way, and a success-shaped `void` was a trap for
954
+ * any caller that trusted it instead of refetching.
955
+ */
956
+ declare function setPreferredCredential(dataDir: string, descriptor: ProviderDescriptor, source: ProviderCredential['kind']): PreferCredentialResult;
957
+ /**
958
+ * Sign a provider out — drops BOTH stored credentials.
959
+ *
960
+ * "Signed out" has to mean the provider resolves nothing afterwards, which is
961
+ * what this has always guaranteed and what callers named it for; promoting the
962
+ * alternate instead would silently leave the provider authenticated through the
963
+ * other source. Callers that want the fall-back behavior can read the pair
964
+ * first ({@link readProviderCredentialPair}) and re-store the survivor.
965
+ */
966
+ declare function removeProviderCredential(dataDir: string, descriptor: ProviderDescriptor): void;
967
+ /**
968
+ * Reconcile `process.env` with the stored credentials so the harness providers
969
+ * pick up the wizard's output via their existing env-var resolution. Called
970
+ * once at TUI launch after the credentials file has been resolved.
971
+ *
972
+ * **Precedence: stored credential > ambient env.** When the user has run the
973
+ * auth wizard, that's an explicit, recent action — it MUST win over whatever
974
+ * ambient value is already in `process.env`. Otherwise an upgrade reliably
975
+ * breaks for anyone whose cwd happens to contain a stale `.env`: Bun
976
+ * auto-loads `.env` from cwd into `process.env` before our code runs, the
977
+ * legacy `bun run auth` used to upsert tokens into `cwd/.env`, and even a
978
+ * one-off `export ANTHROPIC_API_KEY=…` from months ago in the user's shell rc
979
+ * can outlive its usefulness. None of those should override a key the user
980
+ * just typed into the wizard.
981
+ *
982
+ * Three cases per registered provider with an `envKey`:
983
+ *
984
+ * 1. Stored `kind: 'apikey'` → overwrite env with the stored value. Wizard
985
+ * wins. A debug log fires when this clobbers a different ambient value.
986
+ * 2. Stored `kind: 'oauth'` → DELETE env, so {@link resolveOAuthApiKey}
987
+ * falls through to the file-read + refresh path. A stale OAuth token
988
+ * in env would otherwise short-circuit refresh and get rejected.
989
+ * 3. No stored entry → leave env alone. Env-only setups (no wizard run yet,
990
+ * hosts driving zidane purely from secrets management) keep working.
991
+ *
992
+ * To opt out and force ambient env to win again, the user signs the provider
993
+ * out via the wizard (which deletes the stored entry → case 3 above).
994
+ *
995
+ * Descriptors without an `envKey` (OAuth-only providers, custom providers
996
+ * that bypass env-var resolution) are skipped silently.
997
+ */
998
+ declare function applyApiKeyEnv(dataDir: string, registry: Readonly<Record<string, ProviderDescriptor>>, opts?: {
999
+ log?: (message: string) => void;
1000
+ }): void;
1001
+ //#endregion
1002
+ //#region src/chat/discovery-context.d.ts
1003
+ interface DiscoveryContextValue {
1004
+ /** Parsed `SkillConfig[]` from project + user scan paths. */
1005
+ skillsCatalog: readonly SkillConfig[];
1006
+ /**
1007
+ * Agent Plugins loaded from `.agents/plugins` / `.{prefix}/plugins`
1008
+ * (project + user). Includes rejected plugins — they render as read-only
1009
+ * diagnostics rather than vanishing.
1010
+ */
1011
+ pluginsCatalog: readonly DiscoveredPlugin[];
1012
+ /** Parsed `mcps.json` entries. */
1013
+ mcpsCatalog: readonly DiscoveredMcp[];
1014
+ /** Non-fatal parse errors from `mcps.json` lookups. */
1015
+ mcpsErrors: readonly DiscoveryError[];
1016
+ /**
1017
+ * Per-server full tool catalog from the on-disk cache (see
1018
+ * `chat/mcp-tools-cache.ts`). Updated as MCP servers bootstrap and
1019
+ * fire the `mcp:tools:list` hook. Missing key → no cache yet; the
1020
+ * settings UI surfaces an empty-state row under the expanded server.
1021
+ */
1022
+ mcpToolsByServer: Record<string, readonly McpToolSchema[]>;
1023
+ /** Current file walk results — empty until lazy-load lands. */
1024
+ filesCatalog: readonly FileEntry[];
1025
+ /** Force a fresh skills rescan. Resolves once state is committed. */
1026
+ refreshSkills: () => Promise<void>;
1027
+ /** Force a fresh plugin rescan. Resolves once state is committed. */
1028
+ refreshPlugins: () => Promise<void>;
1029
+ /**
1030
+ * Re-parse `mcps.json`. Sync-shaped under the hood, async for symmetry.
1031
+ * Resolves `true` when the catalog actually changed (a publish that
1032
+ * schedules downstream agent rebuilds), `false` on a fingerprint no-op
1033
+ * or scan failure — callers that arm rebuild-coupled state (e.g. the
1034
+ * add-MCP auto-login) disarm on `false`.
1035
+ */
1036
+ refreshMcps: () => Promise<boolean>;
1037
+ /** Force a fresh file walk. Wired into a future "refresh files" affordance. */
1038
+ refreshFiles: () => Promise<void>;
1039
+ /** Idempotent first-load trigger for skills (used by `/` popover + modal mount). */
1040
+ ensureSkills: () => Promise<readonly SkillConfig[]>;
1041
+ /** Idempotent first-load trigger for files (used by `@` popover). */
1042
+ ensureFiles: () => Promise<readonly FileEntry[]>;
1043
+ }
1044
+ declare function DiscoveryProvider({
1045
+ value,
1046
+ children
1047
+ }: {
1048
+ value: DiscoveryContextValue;
1049
+ children: ReactNode;
1050
+ }): import("react").JSX.Element;
1051
+ /**
1052
+ * Read live discovery state + actions. Throws if used outside a
1053
+ * `<DiscoveryProvider>` — discovery is a load-bearing dependency for
1054
+ * settings + completion popovers; bailing loud here surfaces wiring
1055
+ * mistakes at mount instead of producing empty catalogs at first
1056
+ * keystroke.
1057
+ */
1058
+ declare function useDiscovery(): DiscoveryContextValue;
1059
+ /**
1060
+ * Non-throwing variant — returns `null` when no provider is mounted.
1061
+ * Used by composable modals (`<SettingsModal>`, …) that accept catalog
1062
+ * props as a fallback for embedders who don't wire the full TUI
1063
+ * shell. The in-app flow always mounts `<DiscoveryProvider>` so the
1064
+ * modal sees live state; standalone embeds get the prop snapshot.
1065
+ */
1066
+ declare function useDiscoveryOptional(): DiscoveryContextValue | null;
1067
+ //#endregion
1068
+ //#region src/chat/discovery-slot.d.ts
1069
+ /**
1070
+ * Stale-while-revalidate catalog slot.
1071
+ *
1072
+ * One slot per (catalog kind, projectDir) pin. Holds the first-load
1073
+ * promise + a `lastScannedAt` timestamp + an in-flight `refreshing`
1074
+ * promise. Idempotent across concurrent callers, safe to rotate when
1075
+ * the project changes (the slot's `abort` controller cancels in-flight
1076
+ * walks and the `.then` guards drop stale results).
1077
+ *
1078
+ * Renderer-agnostic. AppShell wraps it in React refs so React closures
1079
+ * stay stable; a non-TUI shell can use it directly against its own
1080
+ * state propagation.
1081
+ *
1082
+ * Contract:
1083
+ * - `ensure()` — kick the first load if not started; return the
1084
+ * cached promise. On subsequent calls, if `Date.now() -
1085
+ * lastScannedAt >= throttleMs` and no refresh is in flight, kick
1086
+ * a background revalidate that calls `onLoad(fresh)` when it
1087
+ * resolves. The original first-load promise still resolves with
1088
+ * the first walk's contents — consumers reading live state
1089
+ * elsewhere (React state) see the refreshed catalog.
1090
+ * - `refresh()` — force a rescan now, bypassing the throttle.
1091
+ * De-duplicates: if a refresh is already in flight, returns the
1092
+ * same promise.
1093
+ * - `abort()` — cancel any in-flight walk and detach. After this
1094
+ * the slot is dead; callers should rotate to a new slot.
1095
+ * - `dispose()` — alias of `abort()`, named for symmetry with
1096
+ * cleanup callbacks.
1097
+ *
1098
+ * No state is exposed except through promises + the host's `onLoad`
1099
+ * callback; the slot doesn't try to be a store, only a coordinator.
1100
+ */
1101
+ /** Walk function — takes an abort signal, returns the discovered list. */
1102
+ type Walk<T> = (signal: AbortSignal) => Promise<readonly T[]>;
1103
+ /** Notification: a fresh walk has resolved. Host is expected to commit to its own state store. */
1104
+ type Commit<T> = (items: readonly T[]) => void;
1105
+ interface DiscoverySlotOptions<T> {
1106
+ /** Discover function — runs the actual FS / network walk. */
1107
+ walk: Walk<T>;
1108
+ /** Commit hook — receives every successful walk's result (first + refreshes). */
1109
+ onLoad: Commit<T>;
1110
+ /**
1111
+ * Min interval between background refreshes triggered by `ensure()`.
1112
+ * Default: 3_000ms. `refresh()` ignores this — it's the user's
1113
+ * explicit-intent path.
1114
+ */
1115
+ throttleMs?: number;
1116
+ /** Optional error sink. Defaults to no-op; useful for `debugLog`. */
1117
+ onError?: (err: unknown, phase: 'first-load' | 'refresh') => void;
1118
+ }
1119
+ interface DiscoverySlot<T> {
1120
+ /**
1121
+ * Kick the first load (idempotent). Returns the first-load promise
1122
+ * once it exists. Schedules a background refresh if past throttle.
1123
+ */
1124
+ ensure: () => Promise<readonly T[]>;
1125
+ /** Force-refresh now. De-duped across concurrent callers. */
1126
+ refresh: () => Promise<void>;
1127
+ /** True if a refresh is currently running. */
1128
+ isRefreshing: () => boolean;
1129
+ /** Cancel in-flight work + detach. Idempotent. */
1130
+ abort: () => void;
1131
+ }
1132
+ declare function createDiscoverySlot<T>(options: DiscoverySlotOptions<T>): DiscoverySlot<T>;
1133
+ //#endregion
1134
+ //#region src/chat/editor-command.d.ts
1135
+ /**
1136
+ * `$EDITOR` / `$VISUAL` command parsing.
1137
+ *
1138
+ * These variables conventionally hold a command LINE, not a bare executable —
1139
+ * `code --wait`, `cursor --wait`, `subl -w`, `emacsclient -nw` are all normal
1140
+ * values. Spawning the whole string as argv[0] looks for a file literally named
1141
+ * `cursor --wait` and fails with ENOENT, so the flags have to be split off.
1142
+ *
1143
+ * Kept separate from `app.tsx` (and free of `node:child_process`) so the
1144
+ * tokenizer is unit-testable without pulling in the TUI.
1145
+ */
1146
+ /**
1147
+ * Split an editor command line into `[executable, ...args]`.
1148
+ *
1149
+ * Whitespace-separated, with single and double quotes honored so an editor
1150
+ * living under a path with spaces still works
1151
+ * (`"/Applications/My App/bin/ed" -w`). Quotes group and are stripped;
1152
+ * backslash escaping is deliberately NOT implemented, because on Windows
1153
+ * backslash is the path separator and treating `C:\tools\ed` as escapes would
1154
+ * corrupt far more command lines than it would rescue.
1155
+ *
1156
+ * Returns `[]` for an empty or whitespace-only spec, so callers can fall back
1157
+ * to the platform opener rather than spawning `''`.
1158
+ */
1159
+ declare function parseEditorCommand(spec: string): string[];
1160
+ /**
1161
+ * Re-quote a single argv token for a shell that will re-parse the joined command
1162
+ * line — i.e. `spawn(..., { shell: true })`, which Node uses on Windows so that
1163
+ * `.cmd` / `.bat` wrappers (how `code`, `cursor` and friends ship there) resolve.
1164
+ *
1165
+ * Node does NOT re-quote when `shell` is set: it joins `[cmd, ...args]` with
1166
+ * spaces and hands the string over. Since {@link parseEditorCommand} has already
1167
+ * STRIPPED the user's quotes, handing tokens back unquoted would split
1168
+ * `C:\Program Files\ed.exe` at the space and try to run `C:\Program`. This puts
1169
+ * the quotes back.
1170
+ *
1171
+ * Only tokens that need it are quoted, so a plain `code` stays `code`. A literal
1172
+ * double quote is doubled, which is how `cmd.exe` escapes one inside a quoted
1173
+ * run.
1174
+ *
1175
+ * The test covers `cmd.exe` METACHARACTERS, not just whitespace: `&`, `|`, `^`,
1176
+ * `<`, `>` and friends carry none of the obvious "needs quoting" signals, so a
1177
+ * repo cloned into `C:\work\a&b` would otherwise hand `C:\work\a&b\AGENTS.md`
1178
+ * over bare, and cmd would split it at the `&` and run the tail as a second
1179
+ * command.
1180
+ *
1181
+ * `%` and `!` are in the class so such a token is quoted rather than passed
1182
+ * bare — but be clear about the limit: quoting does NOT stop cmd.exe expanding
1183
+ * `%VAR%`, nor `!VAR!` where delayed expansion is on. Double quotes suppress
1184
+ * splitting and other syntax, not expansion. That is a property of the shell
1185
+ * which no amount of quoting can close; the worst case is a wrong path, not an
1186
+ * injected command.
1187
+ */
1188
+ declare function quoteForShell(token: string): string;
1189
+ //#endregion
1190
+ //#region src/chat/footer-hints.d.ts
1191
+ /**
1192
+ * Options bag for {@link buildHints}. Flat shape so call sites read as a
1193
+ * checklist of "what state do I have to surface" — easy to thread
1194
+ * through React `useMemo` deps and to construct fresh in tests.
1195
+ */
1196
+ interface BuildHintsOptions {
1197
+ screen: Screen;
1198
+ busy: boolean;
1199
+ pending: boolean;
1200
+ /**
1201
+ * True when a LIVE pending interaction is on screen (agent is mid-run,
1202
+ * awaiting the tool's Promise). Same nav hints as the safe-mode
1203
+ * approval picker — esc aborts the run.
1204
+ */
1205
+ pendingInteractionLive: boolean;
1206
+ /**
1207
+ * True when the chat screen is showing a resumed (not live) pending
1208
+ * interaction. The agent isn't running; esc leaves the form without
1209
+ * persisting a response, so the hint reads "leave for later" — the
1210
+ * session re-enqueues the same interaction on next activation.
1211
+ */
1212
+ pendingInteractionResumed: boolean;
1213
+ currentSession: SessionMeta | null;
1214
+ hasMultipleAgents: boolean;
1215
+ /**
1216
+ * Chat-screen chrome density — `'minimal'` collapses the idle chat
1217
+ * hint row to just `agent / model / keybindings`. Busy / pending /
1218
+ * select-turn / queue states keep their full hint sets because those
1219
+ * affordances are load-bearing (abort, navigate, approve).
1220
+ */
1221
+ uiMode: Settings['uiMode'];
1222
+ modelLabel: string | null;
1223
+ modelColor: string;
1224
+ /** Current reasoning effort label, or `null` when the model has no reasoning knob. */
1225
+ effortLabel: string | null;
1226
+ effortColor: string;
1227
+ /** Foreground for the `/n` chord that introduces the effort label. */
1228
+ effortKeyColor: string;
1229
+ /**
1230
+ * Comma-joined labels of the enabled model options (e.g. `'fast'`), or `null`
1231
+ * when none are on. Appended after the effort label in the model chord — when
1232
+ * the model has no reasoning knob, it rides the chord on its own.
1233
+ */
1234
+ modelOptionsLabel?: string | null;
1235
+ agentLabel: string;
1236
+ agentColor: string;
1237
+ /**
1238
+ * Resolved keybindings — every hint key string flows through this so
1239
+ * a user-level rebind (e.g. `"openSettings": "ctrl+p"`) shows up live
1240
+ * in the bottom bar without us hard-coding `'ctrl+o'`.
1241
+ */
1242
+ keybindings: KeyBindings;
1243
+ /**
1244
+ * Number of currently-dispatching tool calls. Drives the
1245
+ * `<cancelToolCall> cancel tool` chip in the `busy` hint row — without
1246
+ * it, the binding stays inert and there's no point advertising the
1247
+ * shortcut. Falls back to "no chip" when no tools are in flight
1248
+ * (model is mid-stream but hasn't dispatched yet).
1249
+ */
1250
+ inFlightToolCount: number;
1251
+ /**
1252
+ * Number of currently-active skills. Drives the footer's
1253
+ * `✦ N skill(s)` chip on the chat screen — purely informational,
1254
+ * a passive surface for "tool restrictions are in effect". Hidden
1255
+ * when `0` so the row stays uncluttered for the common case.
1256
+ */
1257
+ activeSkillCount: number;
1258
+ /** Foreground for the skills chip (typically `COLOR.brand`). */
1259
+ skillsChipColor: string;
1260
+ /**
1261
+ * Progress label for the todo chip (`"2/5"` from `todoProgressLabel`), or
1262
+ * `null` to hide it.
1263
+ *
1264
+ * This replaced a full-width bar that printed the in-progress todo's TITLE
1265
+ * ABOVE the prompt. As a `Hint` it inherits the row's click-to-keypress
1266
+ * contract for free (`dispatchHintKey`), so clicking it opens the list the
1267
+ * same way `ctrl+t` does — and it sits below the prompt like every other
1268
+ * control instead of pushing the prompt down.
1269
+ */
1270
+ todoProgress: string | null;
1271
+ /**
1272
+ * Pre-built `↑ vX.Y.Z` chip from `buildUpdateHint(useUpdateCheck())`,
1273
+ * or `null` when no update is available. Always last in the chat-screen
1274
+ * row so it's the first to drop when the terminal narrows.
1275
+ */
1276
+ updateHint: Hint | null;
1277
+ /**
1278
+ * Pre-built prompt-cache toast, non-null only for a few seconds after a
1279
+ * `'cache:break'` hook fired (the host owns the visibility timer and the
1280
+ * severity split): warn-colored `⚡ cache break · <culprit> · +Nk tok · ~$0.48`
1281
+ * for attributable breaks, dim `⚡ cache expired · +Nk tok · ~$0.48` for
1282
+ * byte-stable TTL expiry — routine across human think-time, so it must not
1283
+ * cry wolf. Sits beside `updateHint` at the droppable end of the row: a
1284
+ * transient cost signal, not an action.
1285
+ */
1286
+ cacheBreakHint?: Hint | null;
1287
+ /**
1288
+ * Sessions-screen list scope — `true` when the list spans all
1289
+ * projects. Drives the `tab` hint's label, which advertises the scope
1290
+ * the toggle switches TO. Optional so non-sessions callers can omit it.
1291
+ */
1292
+ sessionsScopeAll?: boolean;
1293
+ }
1294
+ /**
1295
+ * Build the footer's shortcut hints for the current screen. On the chat
1296
+ * screen the model id rides next to its `ctrl+m` shortcut and the agent
1297
+ * label rides next to `shift+tab`, each in its accent color — the bar
1298
+ * doubles as the status display without needing separate badges. When
1299
+ * the active model exposes reasoning, the `ctrl+m` hint grows a
1300
+ * secondary `/n` chord with the current effort label, surfacing the
1301
+ * effort picker as a discoverable, in-place affordance.
1302
+ */
1303
+ declare function buildHints(options: BuildHintsOptions): Hint[];
1304
+ /**
1305
+ * Shorten a binding spec for display as a "chord continuation" — the
1306
+ * `/n` after `ctrl+m model` in the chat footer. We only strip the
1307
+ * `ctrl+` prefix so user-customized chords (`alt+n`, `meta+shift+n`)
1308
+ * still render in full. Falls back to the verbatim spec when no
1309
+ * `ctrl+` prefix is found, which keeps the visual contract honest:
1310
+ * the rendered key always matches the bound trigger.
1311
+ */
1312
+ declare function shortChord(spec: string): string;
1313
+ //#endregion
1314
+ //#region src/chat/generate-title.d.ts
1315
+ interface GenerateSessionTitleOptions {
1316
+ provider: Provider;
1317
+ /** Model id used for the call. */
1318
+ model: string;
1319
+ /** Conversation history to summarize. Empty → throws synchronously. */
1320
+ turns: readonly SessionTurn[];
1321
+ /**
1322
+ * Cap on how many TRAILING turns to feed the model. Defaults to 10.
1323
+ * The session's opening user turn is always included on top when the
1324
+ * window cuts it off (see `renderTitleTranscript`).
1325
+ */
1326
+ maxTurns?: number;
1327
+ /** Optional cancellation signal — forwarded to the provider's stream. */
1328
+ signal?: AbortSignal;
1329
+ }
1330
+ /**
1331
+ * Drive the provider's `stream()` once and return a concise title for
1332
+ * the conversation represented by `turns`. Throws when:
1333
+ * - `turns` contains no text-bearing content (nothing to summarize),
1334
+ * - the provider stream completes with no output text,
1335
+ * - the model answered the transcript instead of labeling it (see
1336
+ * {@link cleanTitle}) — a failed generation the caller retries,
1337
+ * - `signal` is aborted (rethrown verbatim).
1338
+ *
1339
+ * Output is trimmed, single-line, with surrounding quotes / trailing
1340
+ * punctuation stripped, and truncated to `TITLE_MAX_CHARS`.
1341
+ */
1342
+ declare function generateSessionTitle({
1343
+ provider,
1344
+ model,
1345
+ turns,
1346
+ maxTurns,
1347
+ signal
1348
+ }: GenerateSessionTitleOptions): Promise<string>;
1349
+ /**
1350
+ * Decide whether a session is ready for one-shot automatic titling.
1351
+ *
1352
+ * Returns `true` iff the conversation has had its first exchange — at
1353
+ * least two turns, i.e. the opening user message plus the assistant's
1354
+ * reply (this is exactly the "N turns" count the sessions list shows) —
1355
+ * AND no explicit title has been set yet. An existing `metadata.title`
1356
+ * (a previous auto-generation, or a manual title from the session-details
1357
+ * "generate title" action) is never clobbered.
1358
+ *
1359
+ * Pure + total. The runtime layers its own "is one already in flight?"
1360
+ * and "is a provider picked?" guards on top of this; the predicate only
1361
+ * encodes the content-level rule so it can be pinned in tests without a
1362
+ * mock provider — same rationale as the exported {@link cleanTitle}.
1363
+ */
1364
+ declare function shouldAutoGenerateTitle(turns: readonly SessionTurn[], metadata?: Record<string, unknown>): boolean;
1365
+ /**
1366
+ * Normalize a model-generated title into the shape we want to persist:
1367
+ *
1368
+ * - Collapse internal whitespace + take the first line only (some
1369
+ * models emit "Title: foo\n\nReason: …" despite instructions).
1370
+ * - Strip surrounding quotes, a leading `Title:` / `title -` preamble,
1371
+ * and a `user:` / `assistant:` role prefix echoed from the transcript
1372
+ * — repeatedly, in any nesting order.
1373
+ * - Reject placeholders and answer-shaped replies (see
1374
+ * {@link ANSWER_SHAPED_OPENER}) by throwing.
1375
+ * - Strip trailing punctuation (`.`, `!`, `?`) — titles read cleaner
1376
+ * without it.
1377
+ * - Truncate to `TITLE_MAX_CHARS` with a trailing `…` when over.
1378
+ *
1379
+ * Exported as `cleanTitle` so tests can pin the normalization rules
1380
+ * without going through a mock provider.
1381
+ */
1382
+ declare function cleanTitle(raw: string): string;
1383
+ //#endregion
1384
+ //#region src/chat/host/signals.d.ts
1385
+ /** Caller attribution for approvals raised by subagents. */
1386
+ type ApprovalOriginator = {
1387
+ kind: 'parent';
1388
+ } | {
1389
+ kind: 'child';
1390
+ label: string;
1391
+ };
1392
+ /**
1393
+ * Serializable mirror of canonical `ActiveTodosState` — plain arrays so the
1394
+ * payload survives structured clone / JSON without `readonly` friction.
1395
+ */
1396
+ interface TodosSnapshot {
1397
+ runId: string | null;
1398
+ todos: TodoItem[];
1399
+ archive: TodoItem[];
1400
+ inProgress: TodoItem | null;
1401
+ tally: TodoTally;
1402
+ }
1403
+ /**
1404
+ * Project canonical `ActiveTodosState` onto its wire mirror.
1405
+ *
1406
+ * One implementation because the copy is load-bearing, not cosmetic: the arrays
1407
+ * must be detached from the live session's before they cross a process boundary
1408
+ * (or reach another window in-process), and a site that forgot to copy would
1409
+ * hand out a reference that mutates under its reader. Previously open-coded at
1410
+ * each of the broadcast and RPC sites.
1411
+ */
1412
+ declare function toTodosSnapshot(state: ActiveTodosState): TodosSnapshot;
1413
+ /** Parent-level cancellable tool call or background task. */
1414
+ interface RunningWorkItem {
1415
+ kind: 'tool' | 'task';
1416
+ id: string;
1417
+ label: string;
1418
+ startedAt: number;
1419
+ childId?: string;
1420
+ }
1421
+ /**
1422
+ * Serializable mirror of `PreviewResult` — `readonly` arrays widened so the
1423
+ * payload round-trips without TypeScript friction on the client side.
1424
+ */
1425
+ interface SerializedPreview {
1426
+ diffText: string;
1427
+ perHunkDiff: readonly string[];
1428
+ resolution: readonly HunkResolution[];
1429
+ resolvedPayload: EditPayload;
1430
+ }
1431
+ /**
1432
+ * Wire-safe approval request. Canonical `ApprovalRequest` minus its live
1433
+ * `resolve` callback (kept host-side, keyed by `id`), plus the context a
1434
+ * remote approval surface needs: owning session, safelist cwd, precomputed
1435
+ * safelist suggestions, and the edit preview byproducts.
1436
+ */
1437
+ interface WireApprovalRequest extends Omit<ApprovalRequest, 'resolve'> {
1438
+ sessionId: string;
1439
+ cwd: string;
1440
+ suggestedSafelistEntries: string[];
1441
+ edit?: EditPayload;
1442
+ priorContent?: string | null;
1443
+ preview?: SerializedPreview;
1444
+ }
1445
+ /** Attachment carried alongside the prompt text (base64, no data-URL prefix). */
1446
+ interface PromptAttachment {
1447
+ mediaType: string;
1448
+ data: string;
1449
+ name?: string;
1450
+ }
1451
+ /** `chat.send` options. */
1452
+ interface SendOptions {
1453
+ sessionId: string;
1454
+ prompt: string;
1455
+ /** Skill names picked via `/` completion — activated before the run. */
1456
+ activatedSkills?: string[];
1457
+ attachments?: PromptAttachment[];
1458
+ /**
1459
+ * Host-internal wake run: delivers pending background-task notifications
1460
+ * to the model with an empty prompt. Skips the `user-prompt` broadcast
1461
+ * (there is no user message) — mirrors the local TUI's wake semantics.
1462
+ */
1463
+ wake?: boolean;
1464
+ }
1465
+ /**
1466
+ * Wire-safe summary of an extension-contributed `/` command — the
1467
+ * projection of canonical `Command` (src/chat/commands.ts) minus its
1468
+ * function-valued fields (steps, completeArgs).
1469
+ */
1470
+ interface CommandSummary {
1471
+ name: string;
1472
+ description: string;
1473
+ aliases?: string[];
1474
+ /** Grouping label — defaults to the contributing extension's name. */
1475
+ group?: string;
1476
+ /**
1477
+ * `true` when the canonical command declares `completeArgs` — lets a
1478
+ * client wire an arg-completion shim only for commands that actually
1479
+ * complete, instead of firing blind RPCs on every `/name <prefix>`.
1480
+ */
1481
+ hasArgCompletion?: boolean;
1482
+ }
1483
+ /** Wire-safe projection of canonical `CommandArgSuggestion`. */
1484
+ interface CommandArgSuggestionSummary {
1485
+ /** Text inserted after `/<name> ` on accept. */
1486
+ value: string;
1487
+ /** Display label. Defaults to `value`. */
1488
+ label?: string;
1489
+ description?: string;
1490
+ }
1491
+ /** Host settings — canonical `Settings` plus the extension allowlist. */
1492
+ type HostSettings = Settings & {
1493
+ enabledExtensions?: readonly string[];
1494
+ };
1495
+ /** One entry in the shared type-ahead queue. */
1496
+ interface QueuedItem {
1497
+ id: string;
1498
+ text: string;
1499
+ }
1500
+ /**
1501
+ * Live per-session presence — which sessions have attached windows and
1502
+ * what state they're in. Drives the session list's "active" scope and
1503
+ * per-row status icons. A session appears here when it has ≥1 attached
1504
+ * window, a run in flight, or pending HITL state.
1505
+ */
1506
+ interface SessionPresence {
1507
+ sessionId: string;
1508
+ /** Number of windows currently attached (watching) this session. */
1509
+ windows: number;
1510
+ /** A run is executing right now. */
1511
+ running: boolean;
1512
+ /** Pending approval or interaction — awaiting user input somewhere. */
1513
+ attention: boolean;
1514
+ }
1515
+ /** Pending interaction row as returned by `interactions.list`. */
1516
+ interface PendingInteraction {
1517
+ sessionId: string;
1518
+ request: InteractionRequest;
1519
+ live: boolean;
1520
+ }
1521
+ /**
1522
+ * The single push channel. Chat stream signals mirror the agent loop hooks;
1523
+ * interaction/approval signals carry the HITL queues so every window renders
1524
+ * the same pending state; `sessions:changed` keeps session lists in sync
1525
+ * across windows.
1526
+ */
1527
+ type HostSignal = {
1528
+ kind: 'run:start';
1529
+ sessionId: string;
1530
+ runId: string;
1531
+ } | {
1532
+ kind: 'run:end';
1533
+ sessionId: string;
1534
+ runId: string;
1535
+ ok: boolean;
1536
+ error?: string;
1537
+ }
1538
+ /**
1539
+ * A turn finished. `usage` carries the loop-reported token/cost figures
1540
+ * that drive footer gauges (context size = prompt-side tokens of the
1541
+ * LAST parent turn; cost accrues across parent + child turns).
1542
+ */
1543
+ | {
1544
+ kind: 'turn:end';
1545
+ sessionId: string;
1546
+ turnId: string;
1547
+ childId?: string;
1548
+ depth?: number;
1549
+ usage?: TurnUsage;
1550
+ }
1551
+ /**
1552
+ * `echo: true` marks host-generated SEND/STEER echoes of the user's own
1553
+ * message. Clients that already render the prompt locally (the GUI's
1554
+ * enriched append) drop echo-tagged signals; command-module
1555
+ * user-prompts are never echoes and always render.
1556
+ */
1557
+ | {
1558
+ kind: 'event';
1559
+ sessionId: string;
1560
+ event: StreamEvent;
1561
+ echo?: boolean;
1562
+ } | {
1563
+ kind: 'compacting:start';
1564
+ sessionId: string;
1565
+ }
1566
+ /**
1567
+ * Live token estimate for an in-flight compaction summary. Throttled by the
1568
+ * loop, so this is a low-rate progress tick rather than a stream.
1569
+ *
1570
+ * Without it a daemon-mode window shows a static "Compacting…" with no
1571
+ * indication of progress — a compaction of a large transcript can run for
1572
+ * minutes, which is indistinguishable from a hang.
1573
+ */
1574
+ | {
1575
+ kind: 'compacting:progress';
1576
+ sessionId: string;
1577
+ estOutputTokens: number;
1578
+ }
1579
+ /**
1580
+ * Prompt turns that just persisted, reduced to the fields needed to bind a
1581
+ * live `user-prompt` event to its turn id (see {@link PromptTurnRef}).
1582
+ *
1583
+ * Deliberately NOT the full `SessionTurn`: those carry every content block,
1584
+ * including base64 image/document payloads, which would be megabytes per
1585
+ * prompt to ship for the sake of an id. Without this a prompt sent from a
1586
+ * daemon-attached window is unselectable — no hover, edit, or delete — until
1587
+ * the run-end transcript replace.
1588
+ */
1589
+ | {
1590
+ kind: 'prompt-turns';
1591
+ sessionId: string;
1592
+ refs: readonly PromptTurnRef[];
1593
+ }
1594
+ /**
1595
+ * `effectiveTokens` (success only) is the loop's post-compact context
1596
+ * estimate — windows drop their footer ctx gauge to it immediately
1597
+ * instead of waiting for the next turn's usage.
1598
+ */
1599
+ | {
1600
+ kind: 'compacting:end';
1601
+ sessionId: string;
1602
+ ok: boolean;
1603
+ effectiveTokens?: number;
1604
+ } | {
1605
+ kind: 'model:fallback';
1606
+ sessionId: string;
1607
+ from: {
1608
+ provider: string;
1609
+ model: string;
1610
+ };
1611
+ to: {
1612
+ provider: string;
1613
+ model: string;
1614
+ };
1615
+ } | {
1616
+ kind: 'work:snapshot';
1617
+ sessionId: string;
1618
+ items: RunningWorkItem[];
1619
+ } | {
1620
+ kind: 'todos:snapshot';
1621
+ sessionId: string;
1622
+ snapshot: TodosSnapshot;
1623
+ } | {
1624
+ kind: 'interaction:request';
1625
+ sessionId: string;
1626
+ request: InteractionRequest;
1627
+ live: boolean;
1628
+ } | {
1629
+ kind: 'interaction:resolved';
1630
+ sessionId: string;
1631
+ requestId: string;
1632
+ } | {
1633
+ kind: 'approval:request';
1634
+ request: WireApprovalRequest;
1635
+ }
1636
+ /**
1637
+ * An approval left the pending queue (answered or denied in bulk) — every
1638
+ * OTHER window clears its surface. The window that resolved it already
1639
+ * knows; the signal makes the queue converge everywhere else.
1640
+ */
1641
+ | {
1642
+ kind: 'approval:resolved';
1643
+ sessionId: string;
1644
+ requestId: string;
1645
+ decision: ApprovalDecision;
1646
+ } | {
1647
+ kind: 'sessions:changed';
1648
+ op: 'create' | 'update' | 'delete';
1649
+ sessionId: string;
1650
+ }
1651
+ /**
1652
+ * Settings were written through the host (`settings.set`) — every window
1653
+ * applies the new state so settings UIs converge without a restart.
1654
+ * Windows that persist settings themselves must treat this as
1655
+ * authoritative for the delivered snapshot.
1656
+ */
1657
+ | {
1658
+ kind: 'settings:changed';
1659
+ settings: HostSettings;
1660
+ }
1661
+ /**
1662
+ * Another window edited the session's shared prompt draft. The daemon
1663
+ * EXCLUDES the originating peer from this fan-out, so applying it can
1664
+ * never fight the local cursor — clients apply unconditionally
1665
+ * (last-writer-wins whole-text replacement). Empty `text` clears the
1666
+ * draft (broadcast when any window submits the prompt).
1667
+ */
1668
+ | {
1669
+ kind: 'draft:changed';
1670
+ sessionId: string;
1671
+ text: string;
1672
+ shell?: boolean;
1673
+ }
1674
+ /**
1675
+ * The shared draft's composing ATTACHMENTS changed (paste/drop/remove in
1676
+ * any window). Payloads ride along so a remote window can submit the
1677
+ * shared draft attachments-and-all. Same origin-exclusion as text.
1678
+ */
1679
+ | {
1680
+ kind: 'draft:attachments';
1681
+ sessionId: string;
1682
+ attachments: PromptAttachment[];
1683
+ }
1684
+ /**
1685
+ * The session's shared transcript-search state changed (mode entered /
1686
+ * exited, query edited) in another window. Same origin-exclusion and
1687
+ * last-writer-wins contract as the draft — every window on the session
1688
+ * shows the same filtered transcript.
1689
+ */
1690
+ | {
1691
+ kind: 'search:changed';
1692
+ sessionId: string;
1693
+ active: boolean;
1694
+ query: string;
1695
+ }
1696
+ /**
1697
+ * The daemon resolved another window's inline-autocomplete query
1698
+ * (`suggest.resolve`). Excludes the requesting peer (it already has
1699
+ * the result from its RPC reply). Receivers mirror the ghost ONLY
1700
+ * when their live buffer still equals `basis` — the standard
1701
+ * staleness guard; otherwise discard.
1702
+ */
1703
+ | {
1704
+ kind: 'suggest:changed';
1705
+ sessionId: string;
1706
+ seq: number;
1707
+ basis: string;
1708
+ caret: number;
1709
+ suggestion: string | null;
1710
+ candidates?: string[];
1711
+ }
1712
+ /**
1713
+ * The daemon-side agent's active-skill set changed (`skills_use` /
1714
+ * run-end deactivation). Windows mirror it into their skill chips.
1715
+ */
1716
+ | {
1717
+ kind: 'skills:snapshot';
1718
+ sessionId: string;
1719
+ active: string[];
1720
+ }
1721
+ /**
1722
+ * A daemon-side MCP server needs (re-)authentication. Windows surface
1723
+ * the badge; the interactive login flow itself runs in whichever window
1724
+ * the user triggers it from (tokens land in the shared credential
1725
+ * store; the daemon reconnects on the next agent rebuild).
1726
+ */
1727
+ | {
1728
+ kind: 'mcp:auth';
1729
+ sessionId: string;
1730
+ name: string;
1731
+ reason: string;
1732
+ }
1733
+ /**
1734
+ * Provider retry in progress (rate-limit / overload / silent stall) —
1735
+ * drives the footer's "retrying n/m" indicator. `source` is `'parent'`
1736
+ * or the retrying child's id; the indicator clears when that source
1737
+ * streams again or the run ends.
1738
+ */
1739
+ | {
1740
+ kind: 'stream:retry';
1741
+ sessionId: string;
1742
+ attempt: number;
1743
+ max: number;
1744
+ source: string;
1745
+ }
1746
+ /**
1747
+ * A retried stream reached a terminal state for `source` (`'parent'` or a
1748
+ * child id) — clear that source's retry indicator.
1749
+ *
1750
+ * Distinct kind rather than a nullable `attempt` so the two states stay
1751
+ * unambiguous on the wire. Consumers also clear at run end as a backstop;
1752
+ * this exists for the mid-run case, where a subagent's stream fails while
1753
+ * the parent run continues and the indicator would otherwise stick.
1754
+ */
1755
+ | {
1756
+ kind: 'stream:retry:cleared';
1757
+ sessionId: string;
1758
+ source: string;
1759
+ }
1760
+ /**
1761
+ * Which `SYSTEM.md` override (if any) the host folded into this session's
1762
+ * system prompt, emitted at agent-build time.
1763
+ *
1764
+ * The prompt is composed where the agent is built — in the daemon, not in
1765
+ * the window — so a window cannot derive this by probing its own disk: it
1766
+ * may have a different cwd, and `userInstructionsScope` is the daemon's.
1767
+ * Windows need it to tell "no override on disk" apart from "override in
1768
+ * force", which is the difference between doctrine the user wrote and
1769
+ * doctrine they think they replaced. `shadowed` carries precedence losers
1770
+ * so the Instructions surface can mark them inactive.
1771
+ */
1772
+ | {
1773
+ kind: 'system-md';
1774
+ sessionId: string;
1775
+ file: SystemMdFile | null;
1776
+ shadowed: readonly SystemMdFile[];
1777
+ }
1778
+ /**
1779
+ * A cache-prefix break was detected for this session's last request — the
1780
+ * footer chip names the culprit dimension and the re-billed prefix size.
1781
+ * Detection is per-agent closure state inside the loop, so in daemon mode
1782
+ * only the daemon can observe it. Cost pricing stays window-side (it needs
1783
+ * only the provider key and the token count).
1784
+ */
1785
+ | {
1786
+ kind: 'cache:break';
1787
+ sessionId: string;
1788
+ info: CacheBreakInfo;
1789
+ }
1790
+ /**
1791
+ * Presence snapshot — recomputed and fanned out whenever a window
1792
+ * attaches/detaches or any tracked session's run/HITL state changes.
1793
+ * Full snapshot (the set is small: only live sessions appear).
1794
+ */
1795
+ | {
1796
+ kind: 'presence:changed';
1797
+ sessions: SessionPresence[];
1798
+ }
1799
+ /**
1800
+ * Daemon-side extension HUD snapshot (status lines + banners). Extension
1801
+ * hooks run in the daemon in daemon mode, so their `ctx.tui.setStatus` /
1802
+ * `setBanner` writes happen there — windows overlay this state under
1803
+ * their local HUD (local entries win per extension name).
1804
+ */
1805
+ | {
1806
+ kind: 'hud:state';
1807
+ statuses: Record<string, {
1808
+ text: string;
1809
+ loader: boolean;
1810
+ }>;
1811
+ banners: Record<string, string[]>;
1812
+ } /** Daemon-side extension `ctx.tui.notify` — a one-shot transcript line. */ | {
1813
+ kind: 'hud:notify';
1814
+ extension: string;
1815
+ text: string;
1816
+ tone: 'info' | 'warn' | 'error';
1817
+ }
1818
+ /**
1819
+ * The session's type-ahead queue changed (message queued while a run is
1820
+ * in flight, drained by the host on run end, pushed early, or dropped).
1821
+ * Broadcast to EVERY peer including the origin — the queue box is shared
1822
+ * state and all windows render the same list.
1823
+ */
1824
+ | {
1825
+ kind: 'queue:changed';
1826
+ sessionId: string;
1827
+ items: QueuedItem[];
1828
+ }
1829
+ /**
1830
+ * Extension bus message (`ctx.bus.emit`, `'ipc'` permission). PRIVATE
1831
+ * per `extensionId`: receivers re-inject into the SAME extension's local
1832
+ * subscribers only and drop ids they haven't registered. `extensionId`
1833
+ * is stamped by the emitting host from the registration identity —
1834
+ * never caller-supplied. `payload` is capped at 64 KiB serialized
1835
+ * (enforced at emit and again at the daemon boundary); `(nonce, seq)`
1836
+ * feeds the receivers' per-extension dedupe window (self-echo through a
1837
+ * second connection of the same process, duplicate deliveries). `nonce`
1838
+ * is the emitter's per-process random id (additive — absent from older
1839
+ * peers, which dedupe on seq alone).
1840
+ */
1841
+ | {
1842
+ kind: 'extension:signal';
1843
+ extensionId: string;
1844
+ sessionId?: string;
1845
+ payload: JsonValue;
1846
+ seq: number;
1847
+ nonce?: string;
1848
+ };
1849
+ //#endregion
1850
+ //#region src/chat/host/environment.d.ts
1851
+ /**
1852
+ * Durable persistence for pending safe-mode approvals so a request raised
1853
+ * before a crash still surfaces after restart. Implementations: sqlite repo
1854
+ * (GUI), JSON file (daemon), in-memory no-op (tests).
1855
+ */
1856
+ interface PendingApprovalsStore {
1857
+ insert: (request: WireApprovalRequest) => void;
1858
+ delete: (id: string) => void;
1859
+ clear: () => void;
1860
+ list: () => WireApprovalRequest[];
1861
+ }
1862
+ interface ChatHostEnvironment {
1863
+ /**
1864
+ * Force the cross-process run guard + marker heartbeat ON regardless of
1865
+ * the `useDaemon` setting. The daemon sets this: `zidane attach` /
1866
+ * `zidane daemon` are explicit opt-ins that run sessions with the
1867
+ * setting off, making the daemon a multi-writer surface by
1868
+ * construction. Embedders with a single writer (the GUI's in-process
1869
+ * host) leave it unset and inherit the setting-gated behavior.
1870
+ */
1871
+ multiProcess?: boolean;
1872
+ /** Push a signal to every connected client. Must never throw. */
1873
+ broadcast: (signal: HostSignal) => void;
1874
+ /** Canonical session store shared with every other surface. */
1875
+ store: SessionStore;
1876
+ /** Current user settings — read fresh on every call. */
1877
+ readSettings: () => HostSettings;
1878
+ /**
1879
+ * cwd-aware resolved runtime config (providers, agents, extensions).
1880
+ * Hosts own caching/invalidation — the engine calls this per agent build.
1881
+ */
1882
+ runtimeConfig: (cwd: string) => Promise<ResolvedConfig>;
1883
+ /** Base user-data directory (`~/.zidane`) — safelist, tool-result blobs. */
1884
+ userDataDir: () => string;
1885
+ /** Re-apply provider auth env right before building a provider. */
1886
+ applyAuthEnv?: () => void;
1887
+ /** OAuth credential store for MCP servers, when the host supports MCP auth. */
1888
+ mcpCredentialStore?: () => McpCredentialStore;
1889
+ /** Durable pending-approval persistence. Defaults to in-memory only. */
1890
+ approvalsPersistence?: PendingApprovalsStore;
1891
+ /** Opt-in verbose stream logging to the host console. */
1892
+ debugStream?: boolean;
1893
+ }
1894
+ //#endregion
1895
+ //#region src/chat/host/approvals.d.ts
1896
+ interface ApprovalRequestInput {
1897
+ sessionId: string;
1898
+ cwd: string;
1899
+ tool: string;
1900
+ input: Record<string, unknown>;
1901
+ edit?: EditPayload;
1902
+ priorContent?: string | null;
1903
+ preview?: SerializedPreview;
1904
+ originator?: ApprovalOriginator;
1905
+ }
1906
+ interface ApprovalHub {
1907
+ request: (input: ApprovalRequestInput) => Promise<ApprovalDecision>;
1908
+ resolve: (id: string, decision: ApprovalDecision) => void;
1909
+ denyAll: (sessionId?: string) => void;
1910
+ listPending: () => WireApprovalRequest[];
1911
+ /** Register ghost resolvers for approvals persisted by a prior process lifetime. */
1912
+ rehydrate: () => void;
1913
+ }
1914
+ /** In-memory fallback when the host doesn't provide durable persistence. */
1915
+ declare function createMemoryApprovalsStore(): PendingApprovalsStore;
1916
+ declare function createApprovalHub(options: {
1917
+ broadcast: (signal: HostSignal) => void; /** Base dir for the shared `projects.json` safelist (`~/.zidane`). */
1918
+ userDataDir: () => string;
1919
+ persistence?: PendingApprovalsStore;
1920
+ }): ApprovalHub;
1921
+ //#endregion
1922
+ //#region src/chat/host/compaction.d.ts
1923
+ interface CompactionEntry {
1924
+ sessionId: string;
1925
+ cwd: string;
1926
+ agent: Agent;
1927
+ providerKey: ProviderKey;
1928
+ providerDescriptor?: ProviderDescriptor;
1929
+ model: string;
1930
+ compactionController?: AbortController;
1931
+ }
1932
+ /**
1933
+ * Run `compactConversation` against the session's current turns, persist
1934
+ * the resulting summary as a new turn, and emit a compact-summary stream
1935
+ * event. Brackets the in-flight window with `compacting:start` /
1936
+ * `compacting:end` signals.
1937
+ */
1938
+ declare function runCompaction(entry: CompactionEntry, broadcast: (signal: HostSignal) => void, store: SessionStore): Promise<void>;
1939
+ //#endregion
1940
+ //#region src/chat/host/edit-outcome-ledger.d.ts
1941
+ interface PendingAnnotation {
1942
+ outcomes: readonly EditOutcome[];
1943
+ originalInput: Record<string, unknown>;
1944
+ originalPayload: EditPayload;
1945
+ }
1946
+ declare class EditOutcomeLedger {
1947
+ private _entries;
1948
+ stash(callId: string, payload: PendingAnnotation): void;
1949
+ /** Read + remove the entry for `callId`. `null` when no stash exists. */
1950
+ drain(callId: string): PendingAnnotation | null;
1951
+ /** Non-removing peek — used by `tool:before` to paint events without consuming the stash. */
1952
+ peek(callId: string): PendingAnnotation | null;
1953
+ /** Drop entry without reading — used by `tool:error` (no transform on throw path). */
1954
+ discard(callId: string): void;
1955
+ /** Clear every entry — called on `agent:done` and session disposal. */
1956
+ clear(): void;
1957
+ }
1958
+ //#endregion
1959
+ //#region src/chat/host/interactions.d.ts
1960
+ interface InteractionHub {
1961
+ /** Build the `requestInteraction` callback bound to one session. */
1962
+ requestInteractionFor: (sessionId: string) => (request: InteractionRequest) => Promise<InteractionResponse>;
1963
+ respond: (requestId: string, response: InteractionResponse) => {
1964
+ ok: boolean;
1965
+ };
1966
+ cancel: (requestId: string, reason?: string) => {
1967
+ ok: boolean;
1968
+ };
1969
+ list: (sessionId?: string) => PendingInteraction[];
1970
+ rehydrate: (sessionId: string) => Promise<PendingInteraction[]>;
1971
+ /**
1972
+ * Answer every pending MODEL interaction with the canonical automatic
1973
+ * response — the runtime half of `interactionMode: 'skip'`. Flipping the
1974
+ * setting mid-run must not leave modals that were raised under `ask` hanging
1975
+ * for an answer the new mode says nobody will give. Host-synthesized prompts
1976
+ * are left alone (local code is awaiting a real decision on those). Returns
1977
+ * the number resolved.
1978
+ */
1979
+ resolveAutomatically: (sessionId?: string) => number;
1980
+ /** Drop every pending interaction for a session (session delete / dispose). */
1981
+ clearSession: (sessionId: string) => void;
1982
+ }
1983
+ declare function createInteractionHub(options: {
1984
+ broadcast: (signal: HostSignal) => void;
1985
+ store: SessionStore;
1986
+ /**
1987
+ * Live settings read. The hub consults `interactionMode` itself at rehydrate
1988
+ * time rather than taking an `automatic` flag from each caller: the daemon
1989
+ * dispatch, the GUI adapter, and the host's own activation path all
1990
+ * rehydrate, and a flag any one of them could omit is exactly how a policy
1991
+ * silently stops applying on one surface.
1992
+ */
1993
+ readSettings: () => Settings;
1994
+ }): InteractionHub;
1995
+ //#endregion
1996
+ //#region src/chat/host/orchestration.d.ts
1997
+ interface CreateWorkerParams {
1998
+ /** Directory the worker runs in — the headline use case. */
1999
+ cwd: string;
2000
+ /** First prompt. Sent immediately; the worker starts working on creation. */
2001
+ prompt: string;
2002
+ title?: string;
2003
+ agentId?: string;
2004
+ providerKey?: string;
2005
+ modelId?: string;
2006
+ /**
2007
+ * Whether the worker finishing should start a run on the main. Default
2008
+ * `true`. `false` = fire-and-forget: the report still lands in the main's
2009
+ * history and transcript, the main just is not woken for it.
2010
+ */
2011
+ wake?: boolean;
2012
+ }
2013
+ interface WorkerRow {
2014
+ sessionId: string;
2015
+ title: string;
2016
+ cwd: string;
2017
+ running: boolean;
2018
+ /** Created by this main (vs. a session the human started elsewhere). */
2019
+ worker: boolean;
2020
+ /** `blocked` = a pending approval or question no one has answered. */
2021
+ state: 'idle' | 'running' | 'blocked';
2022
+ /** Last persisted change, epoch ms. */
2023
+ updatedAt: number;
2024
+ /** Total turns in the session. */
2025
+ turns: number;
2026
+ }
2027
+ /** What the main's `read` verb returns — a projection, not a transcript. */
2028
+ interface WorkerSummary extends WorkerRow {
2029
+ /** Last assistant text, trimmed to a few hundred chars. */
2030
+ lastMessage: string | null;
2031
+ turns: number;
2032
+ }
2033
+ interface Orchestration {
2034
+ /** The single observation seam. Called by the engine from `broadcast`. */
2035
+ onSignal: (signal: HostSignal) => void;
2036
+ /** Current main session id, or `null` when the feature is off / none exists. */
2037
+ mainId: () => Promise<string | null>;
2038
+ /** Create the main if the setting is on and none exists. Idempotent. Directory-less by design. */
2039
+ ensureMain: () => Promise<string | null>;
2040
+ createWorker: (mainId: string, params: CreateWorkerParams) => Promise<string>;
2041
+ /** Other sessions, newest first, capped (default {@link DEFAULT_LIST_LIMIT}). */
2042
+ list: (mainId: string, limit?: number) => Promise<WorkerRow[]>;
2043
+ /**
2044
+ * Directories with ongoing work, most recent first, capped — the main's
2045
+ * system-prompt briefing (see `renderProjectsBriefing`). Empty when off.
2046
+ */
2047
+ projects: (limit?: number) => Promise<ProjectBrief[]>;
2048
+ read: (sessionId: string) => Promise<WorkerSummary | null>;
2049
+ /** Is `sessionId` a worker this main CREATED? (Lineage, not permission.) */
2050
+ isWorkerOf: (mainId: string, sessionId: string) => Promise<boolean>;
2051
+ /** May `mainId` act on `sessionId`? Any existing non-main session. */
2052
+ canOrchestrate: (mainId: string, sessionId: string) => Promise<boolean>;
2053
+ /**
2054
+ * New prompt to a worker (queued if mid-run). Rejects non-workers. `wake`
2055
+ * (default `true`) sets whether the main is woken when the run finishes.
2056
+ */
2057
+ send: (mainId: string, sessionId: string, prompt: string, wake?: boolean) => Promise<void>;
2058
+ /** Guidance into a worker's current run at its next boundary. Rejects non-workers. */
2059
+ steer: (mainId: string, sessionId: string, message: string) => Promise<void>;
2060
+ /** Interrupt a worker's run. Rejects non-workers. */
2061
+ cancel: (mainId: string, sessionId: string) => Promise<void>;
2062
+ /**
2063
+ * The last `limit` turns of a session as plain text (`role: text`), oldest
2064
+ * first. The drill-down behind `read`'s one-line `lastMessage`: enough to
2065
+ * judge a worker's work without opening it.
2066
+ */
2067
+ transcript: (mainId: string, sessionId: string, limit?: number) => Promise<string>;
2068
+ }
2069
+ //#endregion
2070
+ //#region src/chat/host/engine.d.ts
2071
+ /**
2072
+ * Instructions-tab payload. Mirrors the TUI's `getContextSegments` contract so
2073
+ * both settings surfaces render identical rows and identical explanations for
2074
+ * why a `SYSTEM.md` override can or cannot apply.
2075
+ */
2076
+ interface ContextSegmentsReport {
2077
+ segments: readonly ContextSegment[];
2078
+ /** Always true here: the host only answers for a live (or buildable) agent. */
2079
+ live: boolean;
2080
+ /** False for host-supplied profiles, which carry their own system prompt. */
2081
+ canOverrideSystem: boolean;
2082
+ /** `'profile'` = picked profile supplies its own prompt; `'scope'` = `userInstructionsScope` excludes user files. */
2083
+ overrideBlockedBy?: 'profile' | 'scope';
2084
+ /** A `SYSTEM.md` on disk that lost precedence — surfaced so edits to it don't look ignored for no reason. */
2085
+ shadowedSystemPath?: string;
2086
+ }
2087
+ interface ChatHost {
2088
+ /** HITL hubs — exposed so transports can route respond/list/rehydrate calls. */
2089
+ interactions: InteractionHub;
2090
+ approvals: ApprovalHub;
2091
+ sendPrompt: (opts: SendOptions) => Promise<{
2092
+ runId: string;
2093
+ }>;
2094
+ interrupt: (sessionId: string) => void;
2095
+ steer: (sessionId: string, message: string) => void;
2096
+ followUp: (sessionId: string, message: string) => void;
2097
+ /** Steer when a run is active, start a fresh run otherwise. */
2098
+ pushQueued: (sessionId: string, message: string, activatedSkills?: string[]) => Promise<{
2099
+ delivered: 'steer' | 'send' | 'command' | 'queued';
2100
+ }>;
2101
+ /** Wire-safe `/` command catalog for a client's completion popover. */
2102
+ listCommands: (cwd: string) => Promise<CommandSummary[]>;
2103
+ /** Argument suggestions for `/name <prefix>` — best-effort, empty on failure. */
2104
+ completeCommandArgs: (cwd: string, name: string, argsPrefix: string) => Promise<CommandArgSuggestionSummary[]>;
2105
+ /**
2106
+ * Shared type-ahead queue — host-owned so every window renders and
2107
+ * manipulates the same list (`queue:changed` fan-out). `queueAdd`
2108
+ * queues while a run is in flight and sends immediately otherwise;
2109
+ * the host drains FIFO at run end.
2110
+ */
2111
+ queueAdd: (sessionId: string, text: string, activatedSkills?: string[], attachments?: PromptAttachment[]) => Promise<{
2112
+ delivered: 'queued' | 'sent';
2113
+ }>;
2114
+ queueRemove: (sessionId: string, id: string) => boolean;
2115
+ queueList: (sessionId: string) => QueuedItem[];
2116
+ /**
2117
+ * The `SYSTEM.md` override composing this session's prompt, for windows
2118
+ * attaching to an ALREADY-WARM agent (the `system-md` signal only fires at
2119
+ * agent build). `null` file = none applied; `null` result = no live agent,
2120
+ * in which case the next build will broadcast.
2121
+ */
2122
+ sessionSystemMd: (sessionId: string) => {
2123
+ file: SystemMdFile | null;
2124
+ shadowed: readonly SystemMdFile[];
2125
+ } | null;
2126
+ queuePushNow: (sessionId: string, id: string) => Promise<{
2127
+ delivered: 'steer' | 'send' | 'missing';
2128
+ }>;
2129
+ isSessionRunning: (sessionId: string) => boolean;
2130
+ /** Sessions with a live in-memory agent, with their run state (presence). */
2131
+ listLiveSessions: () => Array<{
2132
+ sessionId: string;
2133
+ running: boolean;
2134
+ }>;
2135
+ /**
2136
+ * The live in-memory turns for a session with an active agent — fresh
2137
+ * even mid-run (the store lags at turn boundaries). `null` when no
2138
+ * agent is live. Read-only by contract.
2139
+ */
2140
+ getLiveTurns: (sessionId: string) => readonly SessionTurn[] | null;
2141
+ /**
2142
+ * The live in-memory `Session` for a session with an active agent, or
2143
+ * `null`. For IN-PROCESS embedders only — the GUI's `!` shell runner
2144
+ * appends turns through the live `Session` so `Session.save()` /
2145
+ * compaction's `replaceTurns` never erase a raw store append the
2146
+ * in-memory snapshot didn't see. Not wire-exposable.
2147
+ */
2148
+ getLiveSession: (sessionId: string) => Session | null;
2149
+ /** True when ANY session has a live run, in-flight compaction, or tracked work. */
2150
+ hasActiveWork: () => boolean;
2151
+ listRunningWork: (sessionId: string) => RunningWorkItem[];
2152
+ cancelRunningWork: (sessionId: string, id: string | 'all', reason?: string) => Promise<boolean>;
2153
+ getTodosSnapshot: (sessionId: string) => ActiveTodosState | null;
2154
+ /**
2155
+ * Apply a user todo edit through the live agent's `Session` — the one
2156
+ * `todowrite` reads. `null` when no agent is live (caller falls back to a
2157
+ * store-level write) or when no run owns a todo slot yet.
2158
+ */
2159
+ setTodos: (sessionId: string, items: readonly TodoItem[]) => Promise<ActiveTodosState | null>;
2160
+ /**
2161
+ * Create a session in the shared store and return its id. The one factory
2162
+ * shared by the daemon, the embedded GUI host, and orchestration.
2163
+ */
2164
+ createSession: (params: {
2165
+ cwd: string;
2166
+ title?: string;
2167
+ agentId?: string;
2168
+ providerKey?: string;
2169
+ modelId?: string;
2170
+ fallbacks?: readonly ModelFallbackTarget[];
2171
+ extensionEdit?: boolean;
2172
+ }) => Promise<string>;
2173
+ /** Main-session orchestration — see `./orchestration.ts`. */
2174
+ orchestration: Orchestration;
2175
+ /**
2176
+ * Apply a metadata-only patch through the live in-memory `Session` when
2177
+ * one is hosting the session (safe during a run). Returns `false` when no
2178
+ * live session exists — the caller should fall back to a store-level
2179
+ * write. See the implementation for the data-loss rationale.
2180
+ */
2181
+ patchLiveSessionMeta: (sessionId: string, patch: Record<string, unknown>) => Promise<boolean>;
2182
+ /**
2183
+ * Live-session-aware agent re-label: patches the LIVE document when the
2184
+ * host holds one (saving the live doc is safe — it carries the run's
2185
+ * appended turns; a stale-copy save would clobber them and a later live
2186
+ * save would silently revert the label) and persists it. Returns false
2187
+ * when no live session exists — callers fall back to a store patch,
2188
+ * which is safe because idle sessions have no concurrent writer.
2189
+ */
2190
+ setLiveSessionAgentId: (sessionId: string, agentId: string) => Promise<boolean>;
2191
+ /**
2192
+ * Retire the live agent so the next run rebuilds on freshly-patched
2193
+ * metadata (model / agent swap). Deferred to run end while a run is in
2194
+ * flight, so a swap never aborts a run mid-stream for other windows.
2195
+ */
2196
+ disposeAgentForRebuild: (sessionId: string) => void;
2197
+ getContextBreakdown: (sessionId: string) => Promise<ContextBreakdown | null>;
2198
+ /**
2199
+ * Every chunk of text in the session's system prompt, with provenance — the
2200
+ * Instructions tab's data. Sibling of `getContextBreakdown` and built from
2201
+ * the same `ActiveRun` inputs so the two views describe the same assembly.
2202
+ * `null` when no agent can be built for the session.
2203
+ */
2204
+ getContextSegments: (sessionId: string) => Promise<ContextSegmentsReport | null>;
2205
+ /**
2206
+ * Seed a user-scope `SYSTEM.md` from the doctrine currently in effect for
2207
+ * the session, and return its path for the caller to open in an editor.
2208
+ * Refuses (throws) when an override could never apply — a host-supplied
2209
+ * profile, or a `userInstructionsScope` that excludes user files — rather
2210
+ * than writing a file that would be silently ignored. No-op when the file
2211
+ * already exists (the path is still returned).
2212
+ */
2213
+ createSystemOverride: (sessionId: string) => Promise<{
2214
+ path: string;
2215
+ created: boolean;
2216
+ }>;
2217
+ compactSessionNow: (sessionId: string) => Promise<void>;
2218
+ /** Tear down one session's live agent + pending HITL state. */
2219
+ disposeSession: (sessionId: string) => void;
2220
+ /** Drain every session — call before process exit or config invalidation. */
2221
+ shutdown: () => void;
2222
+ }
2223
+ declare function createChatHost(env: ChatHostEnvironment): ChatHost;
2224
+ //#endregion
2225
+ //#region src/chat/host/helpers.d.ts
2226
+ declare function readFileSafe(path: string): Promise<string>;
2227
+ /** Resolve a workspace-relative path against the session's `cwd`. Absolute paths pass through. */
2228
+ declare function resolveCwdPath(cwd: string, p: string): string;
2229
+ /**
2230
+ * Gate-time `extractEditPayload` wrapper — same as `tool:before` but
2231
+ * without the prior-content second pass for `write_file`. The gate
2232
+ * only needs the payload shape (hunk count, paths) to drive the
2233
+ * approval surface; the diff resolver works against the freshly-read
2234
+ * `priorContent` passed to `previewEditPayload` directly.
2235
+ */
2236
+ declare function extractEditPayloadForGate(name: string, input: Record<string, unknown>): EditPayload | undefined;
2237
+ /**
2238
+ * Strip readonly markers so the `PreviewResult` survives structured-clone
2239
+ * and JSON transports. Field-for-field copy — the resolver output is
2240
+ * already JSON-safe primitives.
2241
+ */
2242
+ declare function serializePreview(preview: PreviewResult): SerializedPreview;
2243
+ //#endregion
2244
+ //#region src/chat/mcp-auth-state.d.ts
2245
+ /**
2246
+ * Per-server MCP OAuth status tracking, decoupled from the renderer.
2247
+ *
2248
+ * The TUI keeps a `Record<serverName, McpAuthStatus>` so it can render a
2249
+ * "needs-auth" / "authorizing" / "error" badge in the MCP picker. The
2250
+ * status map is driven entirely by hooks fired from the agent's MCP
2251
+ * bootstrap + the interactive `loginMcpServer` flow — the reducer below
2252
+ * is pure, makes no FS or network calls, and is shared between the live
2253
+ * TUI store and the test harness.
2254
+ *
2255
+ * Status lifecycle:
2256
+ *
2257
+ * idle ──mcp:connect──▶ authed
2258
+ * (bootstrap succeeded with stored tokens)
2259
+ *
2260
+ * idle ──mcp:auth:required──▶ needs-auth
2261
+ * (bootstrap saw no/invalid tokens)
2262
+ *
2263
+ * needs-auth ──login()──▶ authorizing ──mcp:auth:url──▶ authorizing(url)
2264
+ * ──mcp:auth:success──▶ authed
2265
+ * ──mcp:auth:error──▶ error
2266
+ *
2267
+ * any ──logout──▶ idle
2268
+ *
2269
+ * Multiple servers progress through the lifecycle independently; the map
2270
+ * is keyed by `McpServerConfig.name`.
2271
+ */
2272
+ type McpAuthStatus = {
2273
+ kind: 'idle';
2274
+ } | {
2275
+ kind: 'needs-auth';
2276
+ reason: 'no-tokens' | 'auto-promoted';
2277
+ } | {
2278
+ kind: 'authorizing';
2279
+ url?: string;
2280
+ } | {
2281
+ kind: 'authed';
2282
+ } | {
2283
+ kind: 'error';
2284
+ error: string;
2285
+ } | {
2286
+ kind: 'bootstrap-error';
2287
+ error: string;
2288
+ };
2289
+ /**
2290
+ * Map of server name → status. `idle` is the implicit default for any
2291
+ * server NOT present in the map, so the renderer treats `undefined` as
2292
+ * `{ kind: 'idle' }` and the map can stay sparse.
2293
+ */
2294
+ type McpAuthStateMap = Readonly<Record<string, McpAuthStatus>>;
2295
+ /**
2296
+ * Events the reducer accepts. Names mirror the agent hooks 1:1 plus two
2297
+ * UI-only verbs (`login:start` / `login:cancel`) so the renderer can
2298
+ * optimistically flip a row to `authorizing` before the SDK has emitted
2299
+ * `mcp:auth:url` — useful when the loopback callback is slow to bind.
2300
+ */
2301
+ type McpAuthEvent = {
2302
+ type: 'auth-required';
2303
+ name: string;
2304
+ reason: 'no-tokens' | 'auto-promoted';
2305
+ } | {
2306
+ type: 'auth-url';
2307
+ name: string;
2308
+ url: string;
2309
+ } | {
2310
+ type: 'auth-success';
2311
+ name: string;
2312
+ } | {
2313
+ type: 'auth-error';
2314
+ name: string;
2315
+ error: string;
2316
+ } | {
2317
+ type: 'bootstrap-error';
2318
+ name: string;
2319
+ error: string;
2320
+ } | {
2321
+ type: 'connected';
2322
+ name: string;
2323
+ } | {
2324
+ type: 'login:start';
2325
+ name: string;
2326
+ } | {
2327
+ type: 'login:cancel';
2328
+ name: string;
2329
+ } | {
2330
+ type: 'logout';
2331
+ name: string;
2332
+ } | {
2333
+ type: 'reset';
2334
+ name: string;
2335
+ };
2336
+ /**
2337
+ * Apply one event to the state map. Pure, immutable — returns a new map
2338
+ * with only the affected key reassigned.
2339
+ *
2340
+ * State transitions are simple "last write wins"; we don't gate (e.g.) a
2341
+ * `login:start` from overriding `authorizing` because the loop layer
2342
+ * never doubles those up — interactive login is serialized in the TUI.
2343
+ */
2344
+ declare function reduceMcpAuth(state: McpAuthStateMap, event: McpAuthEvent): McpAuthStateMap;
2345
+ /** Convenience getter — treats `undefined` as `idle`. */
2346
+ declare function getMcpAuthStatus(state: McpAuthStateMap, name: string): McpAuthStatus;
2347
+ //#endregion
2348
+ //#region src/chat/mcp-auth-context.d.ts
2349
+ /**
2350
+ * Mount once near the top of the React tree (above `ModalRoot`). Children
2351
+ * use `useMcpAuthState()` to read and `useMcpAuthDispatch()` to fire events.
2352
+ */
2353
+ declare function McpAuthProvider({
2354
+ children
2355
+ }: {
2356
+ children: ReactNode;
2357
+ }): import("react").JSX.Element;
2358
+ declare function useMcpAuthState(): McpAuthStateMap;
2359
+ declare function useMcpAuthDispatch(): (event: McpAuthEvent) => void;
2360
+ //#endregion
2361
+ //#region src/chat/mcp-credentials.d.ts
2362
+ declare function mcpCredentialsPath(dataDir: string): string;
2363
+ /**
2364
+ * Build a file-backed `McpCredentialStore`. All per-server reads / writes
2365
+ * funnel through the single `mcp-credentials.json` file — this keeps
2366
+ * concurrent flows for different servers from clobbering each other since
2367
+ * each save reads-modifies-writes the full file atomically.
2368
+ *
2369
+ * Two flows for the same server name CAN race (last write wins). MCP OAuth
2370
+ * flows are user-driven and serial in practice (one login modal at a time),
2371
+ * so this is acceptable; a per-server file split + cross-process lock would
2372
+ * be required to handle a multi-process scenario.
2373
+ */
2374
+ declare function createFileMcpCredentialStore(dataDir: string): McpCredentialStore;
2375
+ /**
2376
+ * Convenience patches for individual sub-fields. The provider class uses
2377
+ * these so a `saveTokens` call doesn't have to round-trip `clientInformation`
2378
+ * + `discoveryState` it never touched.
2379
+ */
2380
+ declare function patchMcpCredential(store: McpCredentialStore, name: string, patch: Partial<McpCredentialEntry>): void;
2381
+ //#endregion
2382
+ //#region src/chat/mcp-rows.d.ts
2383
+ /**
2384
+ * One row in the flattened MCP settings list. Three kinds:
2385
+ *
2386
+ * - `'server'` — server header with the chevron + checkbox +
2387
+ * transport detail. Always present.
2388
+ * - `'tool'` — checkbox + description for a tool advertised
2389
+ * by the parent server. Present only when the
2390
+ * server is expanded AND its tool cache is
2391
+ * populated.
2392
+ * - `'tool-empty'` — placeholder shown when an expanded server
2393
+ * has no cached tool list yet (first launch
2394
+ * before any session has bootstrapped the
2395
+ * server). Lets the renderer surface a
2396
+ * one-line "send a prompt to populate" hint
2397
+ * WITHOUT changing the server's expand
2398
+ * affordance.
2399
+ */
2400
+ type VisibleMcpRow = {
2401
+ kind: 'server';
2402
+ entry: DiscoveredMcp;
2403
+ } | {
2404
+ kind: 'tool';
2405
+ serverName: string;
2406
+ tool: McpToolSchema;
2407
+ } | {
2408
+ kind: 'tool-empty';
2409
+ serverName: string;
2410
+ };
2411
+ /**
2412
+ * Flatten a server catalog plus the per-server expanded set into the
2413
+ * visible-row list a renderer cycles through.
2414
+ *
2415
+ * - `catalog` — filtered / ordered server list (post-search,
2416
+ * post-allow-list).
2417
+ * - `expanded` — names of servers whose tool list is open.
2418
+ * Servers not in this set render as a single
2419
+ * server-header row.
2420
+ * - `toolsByServer` — per-server full tool catalog (from
2421
+ * `chat/mcp-tools-cache.ts`). Missing key →
2422
+ * `'tool-empty'` placeholder under the
2423
+ * expanded server.
2424
+ */
2425
+ declare function buildVisibleMcpRows(catalog: readonly DiscoveredMcp[], expanded: ReadonlySet<string>, toolsByServer: Record<string, readonly McpToolSchema[]>): VisibleMcpRow[];
2426
+ /**
2427
+ * Server name a row "belongs to" for catalog / toggle / auth lookups.
2428
+ * For a server row that's its own name; for tool / tool-empty rows
2429
+ * it's the parent server's name.
2430
+ */
2431
+ declare function parentServerName(row: VisibleMcpRow): string;
2432
+ /**
2433
+ * Index in `rows` of the server header for the given parent name. `-1`
2434
+ * when not present. Used by collapse handlers to snap the cursor up
2435
+ * to the parent BEFORE shrinking the row list — otherwise the cursor
2436
+ * would land on whichever sibling row now occupies the same flat
2437
+ * index.
2438
+ */
2439
+ declare function indexOfServerRow(rows: readonly VisibleMcpRow[], parentName: string): number;
2440
+ //#endregion
2441
+ //#region src/chat/mcp-tool-toggle.d.ts
2442
+ interface McpToolToggleSet {
2443
+ /** Live set of ENABLED tool names — `Set` for O(1) `has`. */
2444
+ enabledSet: ReadonlySet<string>;
2445
+ /** Flip one tool on/off; persists immediately via `setSetting`. */
2446
+ toggle: (toolName: string) => void;
2447
+ }
2448
+ /**
2449
+ * Bind a single MCP server's per-tool toggle to
2450
+ * `Settings.disabledMcpTools[serverName]`.
2451
+ *
2452
+ * `catalog` is the FULL list of tools the server advertises (i.e. the
2453
+ * cached `listTools()` payload from `chat/mcp-tools-cache.ts`). The
2454
+ * hook needs it both to render `enabledSet` (every catalog tool not in
2455
+ * the persisted deny-list) and to garbage-collect stale deny-list
2456
+ * entries on every write.
2457
+ */
2458
+ declare function useMcpToolToggleSet(opts: {
2459
+ serverName: string;
2460
+ catalog: readonly McpToolSchema[];
2461
+ }): McpToolToggleSet;
2462
+ /**
2463
+ * Bind every server in `catalogByServer` to its own per-tool toggle in
2464
+ * a single hook call. Returns a `serverName → McpToolToggleSet` map.
2465
+ *
2466
+ * Use this when a host needs handlers for multiple servers at once
2467
+ * (the TUI's MCP settings panel keyboard dispatcher is the canonical
2468
+ * example) — calling {@link useMcpToolToggleSet} in a `for` / `.map`
2469
+ * over a dynamic catalog would change the hook-call order whenever a
2470
+ * server is added or removed, violating React's rules-of-hooks. This
2471
+ * hook calls `useMemo` exactly once regardless of server count.
2472
+ *
2473
+ * Servers absent from `catalogByServer` get no entry in the result.
2474
+ * The persisted deny-list still keeps their entries on disk — they
2475
+ * just stay invisible to this consumer until the catalog re-includes
2476
+ * them.
2477
+ */
2478
+ declare function useMcpToolToggleMap(opts: {
2479
+ catalogByServer: Record<string, readonly McpToolSchema[]>;
2480
+ }): Record<string, McpToolToggleSet>;
2481
+ /**
2482
+ * Pure factory shared by both hook flavors. Captures `persistedDenyAll`
2483
+ * + `setDisabledMcpTools` at the call site so toggle handlers stay
2484
+ * referentially stable across renders that don't touch the underlying
2485
+ * settings entry — important for `useKeyboard` to avoid re-binding on
2486
+ * every catalog change.
2487
+ *
2488
+ * Exported for direct use by non-React consumers (CLI, GUI shells)
2489
+ * that want the same write semantics without the hook plumbing.
2490
+ */
2491
+ declare function buildToolToggle(args: {
2492
+ serverName: string;
2493
+ catalogNames: readonly string[];
2494
+ persistedDenyAll: Settings['disabledMcpTools'];
2495
+ setDisabledMcpTools: (next: Settings['disabledMcpTools']) => void;
2496
+ }): McpToolToggleSet;
2497
+ //#endregion
2498
+ //#region src/chat/mcp-tools-cache.d.ts
2499
+ /**
2500
+ * One server's cached entry.
2501
+ *
2502
+ * - `tools` — verbatim copy of the raw `tools/list` payload (pre any
2503
+ * `disabledTools` / `toolFilter` filtering). The settings UI ranges
2504
+ * over this to render the full server surface; the host applies its
2505
+ * own deny-list on top via `Settings.disabledMcpTools` (see
2506
+ * `buildMcpServers` in `mcps-discovery.ts`).
2507
+ * - `updatedAt` — ms-epoch of the bootstrap that populated this entry.
2508
+ * Useful for "last seen N hours ago" affordances and for cache
2509
+ * eviction policies a host might layer on later.
2510
+ * - `transport` — informational only; lets the UI distinguish a stdio
2511
+ * server's tool list from an http server with the same name in
2512
+ * "stale catalog" warnings.
2513
+ */
2514
+ interface CachedMcpToolList {
2515
+ tools: McpToolSchema[];
2516
+ updatedAt: number;
2517
+ transport: 'stdio' | 'sse' | 'streamable-http';
2518
+ }
2519
+ /**
2520
+ * `serverName → catalog`. Map shape on disk so adding / removing
2521
+ * servers is an O(1) JSON edit instead of an array rewrite.
2522
+ */
2523
+ type McpToolsCache = Record<string, CachedMcpToolList>;
2524
+ /**
2525
+ * Resolve the on-disk cache path under a host's data directory. Exposed
2526
+ * for tests + advanced hosts that want to inspect / delete the file
2527
+ * without re-implementing the path convention.
2528
+ */
2529
+ declare function mcpToolsCachePath(dataDir: string): string;
2530
+ /**
2531
+ * Best-effort cache read. Returns `{}` when the file is missing, when
2532
+ * JSON.parse rejects, or when the top-level shape isn't a plain object —
2533
+ * a corrupt cache must never crash the host on startup.
2534
+ *
2535
+ * Per-entry shape validation is light (presence of an array `tools`):
2536
+ * deeper validation would lock the cache to a single zidane version,
2537
+ * which is the opposite of what a long-lived cache wants. Entries that
2538
+ * survive the light check feed back through `McpToolSchema`'s `unknown`
2539
+ * `inputSchema` so downstream readers stay tolerant of upstream churn.
2540
+ */
2541
+ declare function loadMcpToolsCache(opts: {
2542
+ dataDir: string;
2543
+ }): McpToolsCache;
2544
+ /**
2545
+ * Atomic full-cache write. Used by {@link subscribeMcpToolsCache} after
2546
+ * each upsert. Failures bubble up as the underlying `writeFileAtomic`
2547
+ * error — the caller logs under `ZIDANE_DEBUG` and swallows.
2548
+ */
2549
+ declare function saveMcpToolsCache(opts: {
2550
+ dataDir: string;
2551
+ cache: McpToolsCache;
2552
+ }): void;
2553
+ /**
2554
+ * Drop entries for one or more servers, or wipe the cache when no
2555
+ * server is named. Idempotent — entries that aren't present are
2556
+ * silently skipped. Returns the resulting cache so callers can refresh
2557
+ * any in-memory mirror without re-reading from disk.
2558
+ *
2559
+ * Intended for a future "rescan server" affordance in the settings UI
2560
+ * (clear → trigger a real bootstrap → cache repopulates on the
2561
+ * `mcp:tools:list` hook).
2562
+ */
2563
+ declare function clearMcpToolsCache(opts: {
2564
+ dataDir: string;
2565
+ serverNames?: readonly string[];
2566
+ }): McpToolsCache;
2567
+ /**
2568
+ * Subscribe to `mcp:tools:list` and persist every successful bootstrap
2569
+ * into the on-disk cache. Returns an unsubscribe function — call it
2570
+ * during agent teardown so a long-lived host (Settings modal that
2571
+ * outlives one agent) doesn't accumulate stale subscribers.
2572
+ *
2573
+ * Each write loads the current cache, upserts the named entry, then
2574
+ * atomic-writes the full file. The read-modify-write is safe under
2575
+ * Node single-threaded event loop semantics — concurrent bootstraps in
2576
+ * the same process serialize through the hook bus.
2577
+ *
2578
+ * Optional `onChange` callback fires synchronously after every
2579
+ * successful disk write with the entry that was just upserted. Lets a
2580
+ * React host trigger a state refresh without polling the disk.
2581
+ *
2582
+ * Write failures are swallowed (logged under `ZIDANE_DEBUG`): the cache
2583
+ * is an accelerator, not a correctness boundary. If the disk is full
2584
+ * the agent run still works — the settings UI just shows stale data
2585
+ * until the next successful write.
2586
+ */
2587
+ declare function subscribeMcpToolsCache(hooks: Hookable<AgentHooks>, opts: {
2588
+ dataDir: string;
2589
+ onChange?: (entry: {
2590
+ name: string;
2591
+ } & CachedMcpToolList) => void;
2592
+ }): () => void;
2593
+ /**
2594
+ * Force-refresh the on-disk tools cache by opening a transient MCP
2595
+ * connection to every server, capturing the `mcp:tools:list` hook on
2596
+ * the way through, and closing the connection immediately. The
2597
+ * underlying `connectMcpServers` runs every server's bootstrap in
2598
+ * parallel, so the wall-clock cost collapses to the slowest server.
2599
+ *
2600
+ * Two design choices worth noting:
2601
+ *
2602
+ * - **Shared hook bus.** The caller passes the SAME `Hookable`
2603
+ * instance that has the cache subscriber wired (typically
2604
+ * `agent.hooks` after `subscribeMcpToolsCache(agent.hooks, …)`
2605
+ * ran). That way `mcp:tools:list` fires on the bus the
2606
+ * subscriber is listening to and the cache lands on disk
2607
+ * without a separate write path.
2608
+ * - **Transient, NOT shared with the live agent.** The connection
2609
+ * opened here is closed immediately. The agent's own MCP
2610
+ * connection is untouched, so a refresh never disrupts an
2611
+ * in-flight tool call.
2612
+ *
2613
+ * The function intentionally swallows per-server failures (they're
2614
+ * already surfaced via `mcp:bootstrap:end ok: false` for any host
2615
+ * that wired the listener). The Promise resolves once every server's
2616
+ * bootstrap settles, success or otherwise.
2617
+ */
2618
+ declare function refreshMcpToolsCatalog(opts: {
2619
+ servers: readonly McpServerConfig[];
2620
+ hooks: Hookable<AgentHooks>;
2621
+ /**
2622
+ * Same OAuth-provider factory the agent's bootstrap uses. Required
2623
+ * for OAuth-tagged servers — without it those servers fire
2624
+ * `mcp:auth:required` and skip, and their tools never refresh.
2625
+ */
2626
+ buildAuthProvider?: (config: McpServerConfig) => OAuthClientProvider | undefined;
2627
+ }): Promise<void>;
2628
+ //#endregion
2629
+ //#region src/chat/mcps-write.d.ts
2630
+ type McpConfigScope = 'project' | 'user';
2631
+ /**
2632
+ * Resolve the CANONICAL write target for a scope — always the
2633
+ * `.{prefix}/mcps.json` flavor, never `.agents` (we don't own that
2634
+ * convention) and never `mcp.json` (Cursor's spelling is read-compat
2635
+ * only).
2636
+ */
2637
+ declare function mcpsWritePath(scope: McpConfigScope, opts?: {
2638
+ cwd?: string;
2639
+ home?: string;
2640
+ prefix?: string;
2641
+ }): string;
2642
+ interface UpsertMcpResult {
2643
+ /** The file written. */
2644
+ path: string;
2645
+ /** `updated` when an entry of the same name was replaced, else `added`. */
2646
+ action: 'added' | 'updated';
2647
+ }
2648
+ /**
2649
+ * Upsert `entry` (matched by `name`) into the mcps config at `filePath`.
2650
+ * Preserves the existing file's shape and every field the writer doesn't
2651
+ * own; creates a canonical flat-array file when none exists. Throws on
2652
+ * malformed JSON — silently rewriting a broken file the user is mid-edit
2653
+ * on would destroy their work.
2654
+ */
2655
+ declare function upsertMcpServer(filePath: string, entry: McpServerConfig): UpsertMcpResult;
2656
+ interface RemoveMcpResult {
2657
+ /** The file rewritten. */
2658
+ path: string;
2659
+ }
2660
+ /**
2661
+ * Remove the entry named `name` from the mcps config at `filePath`,
2662
+ * preserving the file's shape and everything else in it — the removal
2663
+ * counterpart of {@link upsertMcpServer}. Throws when the file is
2664
+ * missing, malformed, an unrecognized shape, or has no entry of that
2665
+ * name (the caller shows the message on the settings op line; silently
2666
+ * "succeeding" on a no-op would lie about what happened).
2667
+ */
2668
+ declare function removeMcpServer(filePath: string, name: string): RemoveMcpResult;
2669
+ //#endregion
2670
+ //#region src/chat/model-chain-metadata.d.ts
2671
+ interface ModelFallbackTarget {
2672
+ providerKey: ProviderKey;
2673
+ modelId: string;
2674
+ }
2675
+ /**
2676
+ * Assemble the `metadata.modelChain` value for a model pick — the full
2677
+ * canonical chain when at least one valid fallback survives filtering,
2678
+ * `undefined` for a single-model pick (metadata merges JSON-serialize,
2679
+ * so `undefined` REMOVES a previously persisted chain).
2680
+ */
2681
+ declare function modelChainMetadata(providerKey: ProviderKey, modelId: string, fallbacks: readonly ModelFallbackTarget[] | undefined): ModelChainEntry[] | undefined;
2682
+ /**
2683
+ * Validated fallback tail for list rows/pickers — head-matched against
2684
+ * the session's persisted primary, unregistered providers kept (the
2685
+ * caller filters against its own registry when it has one).
2686
+ */
2687
+ declare function sessionModelFallbacks(metadata: Record<string, unknown> | undefined): ModelFallbackTarget[] | undefined;
2688
+ //#endregion
2689
+ //#region src/chat/oauth.d.ts
2690
+ declare function supportsOAuth(descriptor: ProviderDescriptor): boolean;
2691
+ /** True when the provider's OAuth flow needs the user to paste a code back into the TUI (no loopback callback). */
2692
+ declare function oauthUsesManualCodePaste(descriptor: ProviderDescriptor): boolean;
2693
+ interface OAuthFlowOptions {
2694
+ /** Called when the provider emits its login URL — typically right after the callback server starts. */
2695
+ onUrl: (url: string, instructions?: string) => void;
2696
+ /**
2697
+ * Called when the provider needs a free-form input from the user (paste-the-code flows,
2698
+ * step-up confirmations, etc.). Mirrors pi-ai's `OAuthLoginCallbacks.onPrompt` verbatim
2699
+ * so callers don't lose `placeholder` / `allowEmpty` hints. Required for any provider
2700
+ * with `usesCallbackServer: false` (e.g. Anthropic Claude Pro/Max).
2701
+ */
2702
+ onPrompt?: (prompt: OAuthPrompt) => Promise<string>;
2703
+ /**
2704
+ * Called when the provider initiates a device-code flow (e.g. GitHub Copilot).
2705
+ * Surface `userCode` + `verificationUri` so the user can complete the flow in
2706
+ * the browser. Required by pi-ai's `OAuthLoginCallbacks` since 0.75.5 — built-in
2707
+ * Anthropic / OpenAI Codex providers don't fire it today, but third-party
2708
+ * descriptors can.
2709
+ */
2710
+ onDeviceCode?: (info: OAuthDeviceCodeInfo) => void;
2711
+ /**
2712
+ * Called when the provider needs the user to pick between multiple flow options
2713
+ * (e.g. browser vs device-code). Return the chosen option `id`, or `undefined`
2714
+ * to cancel. Required by pi-ai's `OAuthLoginCallbacks` since 0.75.5.
2715
+ */
2716
+ onSelect?: (prompt: OAuthSelectPrompt) => Promise<string | undefined>;
2717
+ /** Called with each progress message from the OAuth flow (token exchange, etc.). */
2718
+ onProgress?: (message: string) => void;
2719
+ /** Abort the in-flight login (e.g. user pressed esc). */
2720
+ signal?: AbortSignal;
2721
+ }
2722
+ /**
2723
+ * Run the OAuth login flow for a provider.
2724
+ *
2725
+ * Returns the OAuth credentials on success; caller persists them via
2726
+ * `setProviderCredential(dataDir, descriptor, { kind: 'oauth', ...credentials })`.
2727
+ * Throws when the descriptor has no `oauthProvider` configured.
2728
+ */
2729
+ declare function runOAuthLogin(descriptor: ProviderDescriptor, options: OAuthFlowOptions): Promise<OAuthCredentials>;
2730
+ //#endregion
2731
+ //#region src/chat/oauth-redirect.d.ts
2732
+ /**
2733
+ * Manual OAuth redirect-URL handoff.
2734
+ *
2735
+ * Users who run zidane over SSH (or behind a proxy / firewall that blocks
2736
+ * loopback) can't have the local browser hit the in-process callback server.
2737
+ * They CAN, however, copy the URL their browser was redirected to —
2738
+ * `http://127.0.0.1:<port>/callback?code=...&state=...` — and paste it back
2739
+ * into the TUI.
2740
+ *
2741
+ * The trick: that URL IS the callback our local server is listening on. We
2742
+ * just `fetch()` it ourselves. The server runs in the same process; the
2743
+ * request hits its handler exactly as if a real browser had arrived, the
2744
+ * OAuth promise (`waitForCode` for pi-ai providers, `startOAuthCallback` for
2745
+ * MCP) resolves through the normal happy path, and the upstream flow
2746
+ * continues uninterrupted. No new code path inside the OAuth state machine.
2747
+ *
2748
+ * Defense in depth: we reject anything that isn't a loopback URL — fetching
2749
+ * an arbitrary user-pasted URL from inside the agent process would be a
2750
+ * trivial SSRF.
2751
+ */
2752
+ /**
2753
+ * The `redirect_uri` the authorization server was told to come back to,
2754
+ * read out of the authorization URL itself. That's our loopback callback
2755
+ * (`http://127.0.0.1:<port>/callback`) — the UI shows it so an SSH user
2756
+ * knows which address to fish out of the browser's URL bar, and
2757
+ * {@link normalizeRedirectPaste} uses it to rebuild a full callback URL
2758
+ * from a partial paste.
2759
+ *
2760
+ * Returns `null` when the URL doesn't parse or carries no usable
2761
+ * `redirect_uri` — callers degrade to "paste the whole URL".
2762
+ */
2763
+ declare function redirectUriFromAuthUrl(authUrl: string): URL | null;
2764
+ /**
2765
+ * Widen what counts as an acceptable paste. The happy path is the full
2766
+ * redirect URL out of the browser's address bar, but users on SSH
2767
+ * routinely end up holding less than that: some authorization servers
2768
+ * render the code on the page instead of redirecting, and copying from a
2769
+ * "can't reach this site" error page loses everything but the query.
2770
+ *
2771
+ * Accepted forms, given the authorization URL for context:
2772
+ *
2773
+ * - `http://127.0.0.1:5123/callback?code=x&state=y` — passthrough (any
2774
+ * absolute URL is returned untouched; the loopback guard in
2775
+ * {@link fetchOAuthRedirect} still has final say, so a pasted
2776
+ * `https://evil.test` is rejected there, not silently rewritten).
2777
+ * - `/callback?code=x&state=y` — path + query onto the callback origin.
2778
+ * - `?code=x&state=y` / `code=x&state=y` — query onto the callback URL.
2779
+ * - `x` — a bare authorization code; `state` is taken from the
2780
+ * authorization URL when it carried one (the loopback server forwards
2781
+ * `state` verbatim and the SDK owns verification, so a missing one is
2782
+ * not fatal). Anything with whitespace inside is prose (an error page
2783
+ * fragment), not a code — refused with a clearer error than a server
2784
+ * 400 would give.
2785
+ *
2786
+ * Throws when the input can't be turned into a URL — no `authUrl` context
2787
+ * for a partial paste, or an unusable authorization URL.
2788
+ */
2789
+ declare function normalizeRedirectPaste(pasted: string, authUrl?: string): string;
2790
+ interface FetchOAuthRedirectResult {
2791
+ /** Status code from the callback server. 2xx = success. */
2792
+ status: number;
2793
+ /** Best-effort message extracted from the response body (HTML title or trimmed text). */
2794
+ message?: string;
2795
+ }
2796
+ /**
2797
+ * Treat `pasted` as a callback-URL paste. Validates it's a loopback URL,
2798
+ * fires a GET, returns the status.
2799
+ *
2800
+ * Pass `authUrl` (the authorization URL currently on screen) to accept
2801
+ * partial pastes — a bare code, a query string, a path — via
2802
+ * {@link normalizeRedirectPaste}. Without it only a full absolute URL is
2803
+ * accepted, which is the pre-existing contract.
2804
+ *
2805
+ * Throws when:
2806
+ * - `pasted` doesn't parse as a URL (and can't be completed from `authUrl`).
2807
+ * - The URL host isn't loopback (rejects SSRF).
2808
+ * - The fetch errors out (network, timeout).
2809
+ *
2810
+ * Returns success/failure for non-2xx responses; the caller decides whether
2811
+ * a 4xx is fatal (state mismatch is a 400 the user can retry from).
2812
+ */
2813
+ declare function fetchOAuthRedirect(pasted: string, options?: {
2814
+ signal?: AbortSignal;
2815
+ timeoutMs?: number;
2816
+ authUrl?: string;
2817
+ }): Promise<FetchOAuthRedirectResult>;
2818
+ //#endregion
2819
+ //#region src/chat/path-display.d.ts
2820
+ /**
2821
+ * @-completion path display formatter.
2822
+ *
2823
+ * The `@`-popover walks the project from the git root so the user sees
2824
+ * the whole tree regardless of where they launched the TUI. The
2825
+ * agent's CWD-resolving tools (`read_file`, `glob`, `edit`, …) read
2826
+ * relative paths against `process.cwd()`, which is frequently a
2827
+ * subdirectory of that root. Emitting the discovery's raw
2828
+ * project-root-relative path into the prompt produces a `<cwd>/<rel>`
2829
+ * miss — the bug this helper exists to close.
2830
+ *
2831
+ * Rule:
2832
+ *
2833
+ * - File at `cwd` itself → `.`.
2834
+ * - File under `cwd` (subtree) → forward-slashed CWD-relative,
2835
+ * no `..`.
2836
+ * - File above `cwd` but still inside the project → CWD-relative
2837
+ * with `..` traversal (e.g. `../EDIT_THIS.md`). Shorter and more
2838
+ * scannable than absolute, and the agent's tools resolve both.
2839
+ * - File outside the project (input was passed absolute) → returned
2840
+ * verbatim. Inputs that arrive as project-root-relative strings
2841
+ * are always inside the project by construction, so this branch
2842
+ * only kicks in when a direct caller hands the function an
2843
+ * absolute target.
2844
+ *
2845
+ * Pure, no I/O, deterministic.
2846
+ */
2847
+ /**
2848
+ * Convert a project-root-relative path (as emitted by `listProjectFiles`)
2849
+ * into the form the agent's tools — and the user — will actually find
2850
+ * given the current `cwd`.
2851
+ *
2852
+ * Inputs:
2853
+ * - `projectRelativePath` — forward-slashed, no leading slash. Returned
2854
+ * verbatim when it's empty or already absolute (defensive — the
2855
+ * absolute-input branch is the de-facto "outside the project"
2856
+ * escape; the catalog itself never emits absolute paths).
2857
+ * - `projectRoot` — absolute path of the discovery anchor.
2858
+ * - `cwd` — absolute path of `process.cwd()` at the moment of
2859
+ * insertion.
2860
+ *
2861
+ * Output is always either an absolute path (only when the input arrived
2862
+ * absolute) OR a CWD-relative path. The relative form may include
2863
+ * `../` segments when the target sits above `cwd` within the project,
2864
+ * which keeps cross-tree references short and scannable
2865
+ * (e.g. `../EDIT_THIS.md` instead of `/Users/.../zidane/EDIT_THIS.md`).
2866
+ */
2867
+ declare function formatPathForCwd(projectRelativePath: string, projectRoot: string, cwd: string): string;
2868
+ //#endregion
2869
+ //#region src/chat/project-root.d.ts
2870
+ /**
2871
+ * Git root detection — walks parents from `cwd` looking for a `.git`
2872
+ * entry, returning the absolute path of the repo root or `null` when
2873
+ * the search reaches the filesystem root.
2874
+ *
2875
+ * Why parent-walk over `git rev-parse --show-toplevel`:
2876
+ *
2877
+ * - Zero dependencies (no shell-out, no git binary requirement,
2878
+ * works even when `git` is missing from `PATH`).
2879
+ * - Pure-sync, deterministic, easy to test against tmp dirs.
2880
+ * - Equally fast in practice — most lookups are a single `existsSync`.
2881
+ *
2882
+ * Recognizes `.git` as either:
2883
+ *
2884
+ * - A directory (standard repo layout).
2885
+ * - A file containing `gitdir: …` (worktrees + submodules).
2886
+ *
2887
+ * Bare repos (no working tree) are intentionally NOT detected here —
2888
+ * the TUI's data dir contract is "project working tree", and a bare
2889
+ * repo has no working files to scope sessions against.
2890
+ */
2891
+ /**
2892
+ * Walk parents of `cwd` looking for a `.git` entry. Returns the
2893
+ * absolute path of the directory containing `.git`, or `null` when no
2894
+ * git repo is found above `cwd`.
2895
+ *
2896
+ * Stops at the filesystem root (`/`) — no infinite loop on `dirname`'s
2897
+ * idempotent root case (`dirname('/')` returns `/`).
2898
+ */
2899
+ declare function findGitRoot(cwd?: string): string | null;
2900
+ //#endregion
2901
+ //#region src/chat/provider-usage.d.ts
2902
+ /** How long a snapshot stays fresh. Usage windows move in minutes, not seconds. */
2903
+ declare const USAGE_CACHE_TTL_MS = 60000;
2904
+ /** Which probe a credential key maps to. */
2905
+ type UsageSource = 'claude' | 'codex' | 'grok' | 'openrouter';
2906
+ /** The probe for a descriptor, or `null` when it has no usage surface. */
2907
+ declare function usageSourceFor(descriptor: ProviderDescriptor): UsageSource | null;
2908
+ /** Whether this provider can report subscription usage at all. */
2909
+ declare function providerUsageSupported(descriptor: ProviderDescriptor): boolean;
2910
+ /**
2911
+ * Drop every cached snapshot. Entries are keyed by credential KEY, not by the
2912
+ * token itself, so signing out and back in (or re-authing to another account)
2913
+ * would otherwise keep serving the previous account's numbers for the rest of
2914
+ * the TTL. The TUI calls this wherever it re-reads credentials.
2915
+ */
2916
+ declare function clearProviderUsageCache(): void;
2917
+ /** Whether the environment forbids usage probes outright. */
2918
+ declare function usageChecksOptedOut(env?: NodeJS.ProcessEnv): boolean;
2919
+ interface FetchProviderUsageOptions {
2920
+ /** Ignore (and replace) a still-fresh cached snapshot. */
2921
+ refresh?: boolean;
2922
+ }
2923
+ /**
2924
+ * Usage for one provider. `null` means there is nothing to show: the provider
2925
+ * has no usage surface, no credential of the kind its probe needs (file or,
2926
+ * for API keys, env), or the environment opted out (see
2927
+ * {@link usageChecksOptedOut}).
2928
+ *
2929
+ * Never rejects. Transport and parse failures land in the snapshot.
2930
+ */
2931
+ declare function fetchProviderUsage(dataDir: string, descriptor: ProviderDescriptor, opts?: FetchProviderUsageOptions): Promise<ProviderUsageSnapshot | null>;
2932
+ /**
2933
+ * Usage for every registered provider that has both a usage surface and a
2934
+ * usable credential, in registry order. What the panel renders.
2935
+ */
2936
+ declare function fetchAllProviderUsage(dataDir: string, registry: Readonly<Record<string, ProviderDescriptor>>, opts?: FetchProviderUsageOptions): Promise<readonly ProviderUsageSnapshot[]>;
2937
+ //#endregion
2938
+ //#region src/chat/run-end-notice.d.ts
2939
+ interface RunEndNotice {
2940
+ notice: NonNullable<StreamEvent['notice']>;
2941
+ text: string;
2942
+ }
2943
+ /**
2944
+ * Structured stop notice for an abnormal `finishReason`, or `undefined` for a
2945
+ * normal end.
2946
+ *
2947
+ * Normal completions (`stop` / `tool-calls`) and user aborts return
2948
+ * `undefined` — and so does a SUCCESSFUL auto-resume, because recovery ends
2949
+ * the run on `stop`. That is what keeps the notice quiet exactly when the
2950
+ * ceiling was handled rather than hit. `error` runs already surface their own
2951
+ * error event, so a second notice would double-report.
2952
+ */
2953
+ declare function runEndNotice(reason: TurnFinishReason | undefined): RunEndNotice | undefined;
2954
+ //#endregion
2955
+ //#region src/chat/safe-mode.d.ts
2956
+ /**
2957
+ * Safe-mode storage + matching for the TUI.
2958
+ *
2959
+ * Lives at `<dataDir>/projects.json` (default `~/.zidane/projects.json`). Each
2960
+ * top-level key is an absolute project directory; the value carries that
2961
+ * project's persisted tool-call `safelist`.
2962
+ *
2963
+ * ```json
2964
+ * {
2965
+ * "/Users/me/proj-a": { "safelist": ["read_file", "shell:git:*"] }
2966
+ * }
2967
+ * ```
2968
+ *
2969
+ * Two granularities for safelist entries:
2970
+ * - **bare tool name** — `"read_file"` matches every `read_file` call.
2971
+ * - **tool + first-arg token + wildcard** — `"shell:git:*"` matches `shell`
2972
+ * calls whose primary string argument starts with the token `git`
2973
+ * (followed by whitespace or end-of-string). Modelled on Claude Code's
2974
+ * `Bash(git:*)` syntax.
2975
+ *
2976
+ * A short list of read-only tools is **implicitly safe** without being
2977
+ * persisted — see {@link IMPLICITLY_SAFE_TOOLS}.
2978
+ */
2979
+ interface ProjectEntry {
2980
+ safelist?: string[];
2981
+ }
2982
+ type ProjectsFile = Record<string, ProjectEntry>;
2983
+ /** Resolve `projects.json`'s on-disk path given the TUI data directory. */
2984
+ declare function projectsFilePath(dataDir: string): string;
2985
+ declare function readProjects(dataDir: string): ProjectsFile;
2986
+ declare function writeProjects(dataDir: string, file: ProjectsFile): void;
2987
+ /**
2988
+ * Append one or more entries to the safelist for `projectDir`, dedup-aware.
2989
+ * Accepts a single string or a list (a compound shell command needs one
2990
+ * entry per command head — see {@link suggestSafelistEntries}). Writes once
2991
+ * and returns the updated entry list (post-write) so callers can render it
2992
+ * without re-reading. No write happens when every entry already exists.
2993
+ */
2994
+ declare function addToSafelist(dataDir: string, projectDir: string, entries: string | readonly string[]): readonly string[];
2995
+ /** Read the safelist for one project. Returns `[]` for unknown projects. */
2996
+ declare function getSafelist(dataDir: string, projectDir: string): readonly string[];
2997
+ /**
2998
+ * Tools that always pass without prompting. Four categories:
2999
+ *
3000
+ * - **Pure reads** (`read_file`, `list_files`, `glob`, `grep`) — no
3001
+ * side effects on disk or the model's own state.
3002
+ * - **Interaction tools** (`ask_user`, `present_plan`) — the call
3003
+ * itself surfaces a TUI picker the user has to answer, so prompting
3004
+ * for approval first would mean "approve this prompt before you see
3005
+ * the prompt": redundant + breaks the flow. The picker IS the gate.
3006
+ * - **Todos** (`todowrite`, `todoread`) — write to a slot on
3007
+ * `session.metadata.todos` and nothing else. No shell, no disk, no
3008
+ * network. The model uses these for checkpointing every few steps;
3009
+ * a per-call approval prompt would drown the conversation in
3010
+ * interruptions that buy no safety since the tool's surface is
3011
+ * read/write on a metadata bag we already trust the agent with.
3012
+ * - **Skill activation** (`skills_use`) — mutates a small per-agent
3013
+ * Map of active skills and returns the activated skill's bundled
3014
+ * instructions (or releases an entry on `mode: "deactivate"`).
3015
+ * Pure state on the agent — no shell, no disk writes, no network.
3016
+ * Gating it would also defeat the "model recovers from
3017
+ * `AgentToolNotAllowedError` by calling
3018
+ * `skills_use({ mode: "deactivate", name })`" path: a stuck model
3019
+ * would have to ask the user permission to *unstick* itself on
3020
+ * every retry. `skills_read` and `skills_run_script` are
3021
+ * intentionally NOT on this list — the former touches disk and
3022
+ * the latter executes arbitrary scripts, so both keep going
3023
+ * through the regular gate.
3024
+ *
3025
+ * Users who want to gate any of these must disable safe-mode entirely
3026
+ * (or fork this list in their own embedding).
3027
+ */
3028
+ declare const IMPLICITLY_SAFE_TOOLS: readonly string[];
3029
+ /**
3030
+ * Test whether a `{ tool, input }` pair is covered by one safelist entry.
3031
+ *
3032
+ * Supported entry shapes:
3033
+ * - `"<tool>"` — broad match on tool name.
3034
+ * - `"<tool>:<token>:*"` — match when the primary arg's first token
3035
+ * equals `<token>`.
3036
+ *
3037
+ * This function matches a **single command** against a **single entry**.
3038
+ * For shell commands that chain multiple programs (`&&`, `||`, `;`,
3039
+ * `$(…)`, etc.), use {@link isOnSafelist} which parses the full command
3040
+ * and checks every head token independently.
3041
+ *
3042
+ * Entries that don't fit either shape are ignored (forward-compat for
3043
+ * future pattern syntax — readers shouldn't choke on entries written
3044
+ * by a newer version of the TUI).
3045
+ */
3046
+ declare function matchesSafelistEntry(entry: string, tool: string, input: Record<string, unknown>): boolean;
3047
+ /**
3048
+ * True when a call matches ANY entry in the project's safelist (or is
3049
+ * implicitly safe).
3050
+ *
3051
+ * For `shell` commands, the full command string is parsed into individual
3052
+ * commands (handling `&&`, `||`, `;`, `|`, `$(…)`, backticks, subshells).
3053
+ * Every command head must be covered by at least one safelist entry for
3054
+ * the call to pass. If parsing fails, returns `false` (prompt the user).
3055
+ */
3056
+ declare function isOnSafelist(entries: readonly string[], tool: string, input: Record<string, unknown>): boolean;
3057
+ /**
3058
+ * Suggest the safelist entries to persist when the user picks "accept and
3059
+ * remember" (or "accept for session") for a `{ tool, input }`. Heuristic:
3060
+ *
3061
+ * - `shell` → one `shell:<head>:*` per DISTINCT command head, parsing the
3062
+ * command the SAME way {@link isOnSafelist} matches it (`&&`, `||`, `;`,
3063
+ * `|`, `$(…)`, subshells, backticks). So `cd dir && cat file` yields
3064
+ * `["shell:cd:*", "shell:cat:*"]` — persisting the whole set is what
3065
+ * lets the next identical call skip the gate. A single first-token
3066
+ * entry would only cover `cd`, so `isOnSafelist`'s `every-head` check
3067
+ * would fail on `cat` and re-prompt forever.
3068
+ * - `shell` with an unparseable / empty command → first-token heuristic,
3069
+ * else the bare tool name.
3070
+ * - anything else → bare tool name (broad).
3071
+ *
3072
+ * Always returns a non-empty list so the UI has a concrete set to render
3073
+ * and persist.
3074
+ */
3075
+ declare function suggestSafelistEntries(tool: string, input: Record<string, unknown>): string[];
3076
+ //#endregion
3077
+ //#region src/chat/session-cwd.d.ts
3078
+ /** Process cwd used for sessions with no folder binding. */
3079
+ declare function folderlessCwd(): string;
3080
+ /**
3081
+ * Effective process cwd for a persisted session row: the GUI-recorded
3082
+ * `metadata.cwd` when present, else the canonical `projectRoot`
3083
+ * (TUI-created sessions), else the directory-less HOME anchor. Empty
3084
+ * strings count as unset — forks of directory-less sessions persist
3085
+ * `cwd: ''`.
3086
+ */
3087
+ declare function resolveSessionCwd(row: {
3088
+ metadata?: Record<string, unknown> | undefined;
3089
+ projectRoot?: string | undefined;
3090
+ }): string;
3091
+ /**
3092
+ * Map a renderer-supplied scope cwd to the directory the main process
3093
+ * should actually operate in. The renderer signals "no folder" with an
3094
+ * empty string (`SidebarSession.cwd` is `''` for directory-less
3095
+ * sessions); catalogs (@-files, skills, commands, extensions, MCPs)
3096
+ * then root at HOME, matching what the TUI offers when run from `~`.
3097
+ */
3098
+ declare function scopeCwd(cwd: string): string;
3099
+ //#endregion
3100
+ //#region src/chat/settings-context.d.ts
3101
+ declare const DEFAULT_SETTINGS: Settings;
3102
+ interface SettingsContextValue {
3103
+ settings: Settings;
3104
+ /** Flip a boolean setting in place. Restricted at the type level to boolean-valued keys. */
3105
+ toggle: (key: BooleanSettingKey) => void;
3106
+ /** Write any setting key to a new value. Used by string-valued choice rows. */
3107
+ setSetting: <K extends keyof Settings>(key: K, value: Settings[K]) => void;
3108
+ }
3109
+ declare function SettingsProvider({
3110
+ initial,
3111
+ onChange,
3112
+ children
3113
+ }: {
3114
+ initial: Settings;
3115
+ onChange?: (settings: Settings) => void;
3116
+ children: ReactNode;
3117
+ }): import("react").JSX.Element;
3118
+ declare function useSettings(): SettingsContextValue;
3119
+ /**
3120
+ * Keys of `Settings` whose value type is exactly `boolean`. Used to type
3121
+ * the toggle table so `SETTINGS_TOGGLES.key` only ever points to a
3122
+ * boolean-valued setting — string- and array-valued settings (theme,
3123
+ * enabledSkills, …) live in `SETTINGS_CHOICES` or actions.
3124
+ *
3125
+ * `-?` strips optionality from the mapped output so `Settings[K]` doesn't
3126
+ * silently broaden into `T | undefined` and short-circuit the `extends
3127
+ * boolean` check via `undefined`.
3128
+ */
3129
+ type BooleanSettingKey = { [K in keyof Settings]-?: Settings[K] extends boolean ? K : never }[keyof Settings];
3130
+ /**
3131
+ * Setting categories — drives the tab grouping in the TUI's
3132
+ * `SettingsModal` and, more importantly, gives any embedder (GUI,
3133
+ * web settings panel, …) a stable taxonomy to bucket settings under
3134
+ * without re-hardcoding the split.
3135
+ *
3136
+ * - `'agent'` — agent behaviour, capabilities, persistence,
3137
+ * data handling, auth. "What zidane DOES."
3138
+ * - `'ui'` — display, rendering, theming, animations,
3139
+ * chrome density, keybindings. "How zidane LOOKS
3140
+ * and feels."
3141
+ *
3142
+ * Borderline calls (`showAllProjects`, `hideSubagentOutput`,
3143
+ * `resumeLastSession`) are documented inline at each row.
3144
+ */
3145
+ type SettingsCategory = 'agent' | 'ui';
3146
+ interface SettingsCategoryDescriptor {
3147
+ id: SettingsCategory;
3148
+ /** Display label — used by the TUI tab strip + any GUI tab/section header. */
3149
+ label: string;
3150
+ /** One-line gloss the GUI can show as a section subtitle. The TUI omits this. */
3151
+ description: string;
3152
+ }
3153
+ /**
3154
+ * Ordered list of categories. Display order in any consumer (TUI tabs,
3155
+ * GUI sidebar, settings JSON dump) should match this array — keeps
3156
+ * mental model consistent across surfaces.
3157
+ */
3158
+ declare const SETTINGS_CATEGORIES: readonly SettingsCategoryDescriptor[];
3159
+ /**
3160
+ * Static description of every togglable setting, in display order. The TUI's
3161
+ * `SettingsModal` and any future GUI settings panel build their row list
3162
+ * from this so labels/descriptions stay in one place.
3163
+ */
3164
+ interface SettingsToggle {
3165
+ key: BooleanSettingKey;
3166
+ label: string;
3167
+ description: string;
3168
+ category: SettingsCategory;
3169
+ /**
3170
+ * Another boolean setting this one is meaningless without. Renderers grey
3171
+ * the row out and say so while the requirement is off; the write layer
3172
+ * (`enforceSettingsInvariants`) guarantees the value can never be `true`
3173
+ * while the requirement is `false`, whichever client or file wrote it.
3174
+ */
3175
+ requires?: BooleanSettingKey;
3176
+ }
3177
+ declare const SETTINGS_TOGGLES: readonly SettingsToggle[];
3178
+ /**
3179
+ * Cross-setting invariants, applied at EVERY write.
3180
+ *
3181
+ * Settings are a shared file with three writers (the TUI, the GUI main
3182
+ * process, the daemon) and no lock; a rule enforced in one client is a rule
3183
+ * the others break. So this is the single definition, derived from the
3184
+ * `requires` declarations on the toggle rows themselves — adding a
3185
+ * dependency is one field on one row, and the UI hint and the write-time
3186
+ * guarantee follow from it together.
3187
+ *
3188
+ * Pure and idempotent: returns the same object when nothing changes, so React
3189
+ * state and file writers can short-circuit on identity.
3190
+ */
3191
+ /**
3192
+ * Return type of {@link enforceSettingsInvariants}: the input shape with every
3193
+ * boolean setting widened to `boolean`. The function exists to flip toggles,
3194
+ * so a literal `true` from a call-site object literal must not survive into
3195
+ * the result type — it would let `expect(next.x).toBe(false)` fail to compile
3196
+ * against a value that is, in fact, `false`.
3197
+ */
3198
+ type WithInvariants<T> = { [K in keyof T]: K extends BooleanSettingKey ? boolean : T[K] };
3199
+ declare function enforceSettingsInvariants<T extends Partial<Settings>>(settings: T): WithInvariants<T>;
3200
+ /**
3201
+ * String-valued settings that pick between a known set of options. The
3202
+ * `SettingsModal` renders these as one row showing the active label, where
3203
+ * `enter` cycles to the next option (wrapping back to the first). Same
3204
+ * shape any GUI settings panel can consume — render the options array as a
3205
+ * dropdown or segmented control.
3206
+ *
3207
+ * Keep `options` in stable display order; cycling depends on it.
3208
+ */
3209
+ interface SettingsChoice<K extends keyof Settings = keyof Settings> {
3210
+ key: K;
3211
+ label: string;
3212
+ description: string;
3213
+ options: readonly {
3214
+ value: Settings[K];
3215
+ label: string;
3216
+ }[];
3217
+ category: SettingsCategory;
3218
+ }
3219
+ declare const SETTINGS_CHOICES: readonly SettingsChoice[];
3220
+ /**
3221
+ * Keys of `Settings` whose value type is exactly `string`. Bounds the
3222
+ * input table's `key` the same way {@link BooleanSettingKey} bounds the
3223
+ * toggle table — a free-text row can only point at a string-valued
3224
+ * setting.
3225
+ */
3226
+ type StringSettingKey = { [K in keyof Settings]-?: Settings[K] extends string ? K : never }[keyof Settings];
3227
+ /**
3228
+ * Free-text settings edited via a single-line input. The `SettingsModal`
3229
+ * renders these as a row showing the current value (or `placeholder` when
3230
+ * empty); `enter` opens an inline editor. Same shape a GUI can consume as
3231
+ * a text field.
3232
+ */
3233
+ interface SettingsInput {
3234
+ key: StringSettingKey;
3235
+ label: string;
3236
+ description: string;
3237
+ /** Shown when the value is empty — also the conventional "default" hint. */
3238
+ placeholder: string;
3239
+ category: SettingsCategory;
3240
+ }
3241
+ declare const SETTINGS_INPUTS: readonly SettingsInput[];
3242
+ //#endregion
3243
+ //#region src/chat/skills-discovery.d.ts
3244
+ /**
3245
+ * Resolve the default skill scan paths for a project. First-found wins on
3246
+ * collision; earlier entries take precedence.
3247
+ *
3248
+ * Search order — see {@link projectUserPaths}. Non-existent paths are
3249
+ * returned so downstream code can choose whether to create them;
3250
+ * `discoverSkills` itself skips missing dirs without error.
3251
+ */
3252
+ declare function defaultSkillScanPaths(opts?: {
3253
+ cwd?: string;
3254
+ home?: string;
3255
+ prefix?: string;
3256
+ /**
3257
+ * Also scan the skill directories other agents maintain (Claude Code,
3258
+ * Codex, Pi) — see `foreign-configs.ts`. Mirrors the
3259
+ * `importForeignConfigs` setting. Appended AFTER the native paths so
3260
+ * first-found-wins keeps zidane's own copies authoritative on name
3261
+ * collisions.
3262
+ */
3263
+ foreignAgents?: boolean;
3264
+ /**
3265
+ * Also scan Ruler skill directories (`.ruler/skills`, project and
3266
+ * XDG-global) — see `ruler-configs.ts`. Mirrors the
3267
+ * `importRulerConfigs` setting; appended last so first-found-wins
3268
+ * keeps both zidane's own and foreign copies authoritative.
3269
+ */
3270
+ ruler?: boolean;
3271
+ }): SourcedScanPath[];
3272
+ /**
3273
+ * Discover every skill reachable from the default scan paths. Returns
3274
+ * parsed `SkillConfig`s with `name`, `description`, frontmatter, and
3275
+ * lenient-load diagnostics. Pure I/O — does not activate or write.
3276
+ *
3277
+ * `signal` is forwarded to `discoverSkills` so the TUI's directory-watch
3278
+ * effect can cancel a long scan when the user switches `cwd` rapidly.
3279
+ *
3280
+ * Errors during parse are surfaced as `diagnostics` on the returned
3281
+ * skill, not thrown — keeps the picker usable even when a single
3282
+ * SKILL.md is malformed.
3283
+ */
3284
+ declare function discoverProjectSkills(opts?: {
3285
+ cwd?: string;
3286
+ home?: string;
3287
+ prefix?: string; /** See {@link defaultSkillScanPaths}. */
3288
+ foreignAgents?: boolean; /** See {@link defaultSkillScanPaths}. */
3289
+ ruler?: boolean;
3290
+ signal?: AbortSignal;
3291
+ }): Promise<SkillConfig[]>;
3292
+ /**
3293
+ * Map a user-toggled enable list onto the format `createAgent` expects.
3294
+ *
3295
+ * Conventions:
3296
+ * - `enabled === undefined` → all discovered skills enabled (default).
3297
+ * - `enabled === []` → fully off; the agent will not scan or
3298
+ * inject the skills tools.
3299
+ * - `enabled === [names]` → allowlist; skills not in the list are
3300
+ * left out of the catalog.
3301
+ *
3302
+ * `scan` is the resolved scan-path list (passed through verbatim). Pass
3303
+ * `defaultSkillScanPaths()` for the standard project + user paths, or
3304
+ * supply a host-specific list.
3305
+ */
3306
+ declare function buildSkillsConfig(opts: {
3307
+ scan: SourcedScanPath[];
3308
+ enabled?: readonly string[];
3309
+ }): SkillsConfig;
3310
+ //#endregion
3311
+ //#region src/chat/spawn-markers.d.ts
3312
+ /**
3313
+ * Wall-clock start + task preview per in-flight child, keyed by spawn id.
3314
+ * Captured at `spawn:before` and read back at `spawn:complete` — drives
3315
+ * the human-readable elapsed suffix on the spawn-end marker
3316
+ * (`done · 42s`) and copies the stable task preview onto the terminal
3317
+ * marker.
3318
+ */
3319
+ interface SpawnStartRecord {
3320
+ startedAt: number;
3321
+ task: string;
3322
+ model?: string;
3323
+ }
3324
+ /**
3325
+ * `spawn-start` marker for a child that just launched. `previewLine`
3326
+ * collapses inline newlines/tabs so the marker stays on one visual row
3327
+ * resumed children carry the label of the run they continue — same text
3328
+ * shape as the reload path.
3329
+ */
3330
+ declare function buildSpawnStartEvent(ctx: {
3331
+ id: string;
3332
+ task: string;
3333
+ depth?: number;
3334
+ model?: string;
3335
+ resumesChildId?: string;
3336
+ }, startedAt: number): StreamEvent;
3337
+ /**
3338
+ * `spawn-end` marker: ` · `-joined optional segments — elapsed wall-clock
3339
+ * time, then USD cost summed across the child's whole turn tree (most
3340
+ * providers don't populate `cost`, so the segment usually collapses; no
3341
+ * "$0.00" lies) — followed by the token usage summary. `timeout` maps to
3342
+ * the `done` tag, matching the TUI.
3343
+ */
3344
+ declare function buildSpawnEndEvent(ctx: {
3345
+ id: string;
3346
+ depth?: number;
3347
+ status?: 'completed' | 'aborted' | 'timeout' | 'error';
3348
+ stats: AgentStats;
3349
+ }, started: SpawnStartRecord | undefined, now?: number): StreamEvent;
3350
+ //#endregion
3351
+ //#region src/chat/streaming.d.ts
3352
+ interface StreamSource {
3353
+ /** Pass `undefined` / omit for parent-agent events. */
3354
+ childId?: string;
3355
+ /** Nesting depth — 0 for parent, ≥ 1 for subagents. */
3356
+ depth?: number;
3357
+ /**
3358
+ * `SessionTurn.id` this delta belongs to. Tagged onto new events so the
3359
+ * TUI's select-turn mode can group every event from one turn. Stored on
3360
+ * the active bucket and refreshed on every delta so a turn boundary
3361
+ * (`turn:after` flushes the bucket) doesn't carry stale ids forward.
3362
+ */
3363
+ turnId?: string;
3364
+ }
3365
+ interface StreamBuffer {
3366
+ /** Queue a streaming delta for the next flush tick. */
3367
+ queueStreamDelta: (kind: 'markdown' | 'thinking', delta: string, source?: StreamSource) => void;
3368
+ /** Drain pending deltas immediately, then append a non-streaming event. */
3369
+ appendImmediate: (evt: StreamEvent) => void;
3370
+ /** Drain pending deltas immediately, then transform the event list. */
3371
+ flushAndUpdate: (update: (events: StreamEvent[]) => StreamEvent[]) => void;
3372
+ /** Drain pending deltas without further transformation. */
3373
+ flush: () => void;
3374
+ /** Cancel any pending flush and drop buffered deltas (on session teardown). */
3375
+ reset: () => void;
3376
+ /**
3377
+ * Resolves once the pacer is idle — no bucket holds queued content and
3378
+ * no post-drain updater is pending. Resolves immediately when already
3379
+ * idle (the batched / `smoothStreaming: false` path never queues, so
3380
+ * this is zero-cost there). A safety timeout (default
3381
+ * {@link WHEN_DRAINED_TIMEOUT_MS}) resolves anyway if the ticker
3382
+ * wedges — measured as a full window with NO drain progress, so a
3383
+ * long response that keeps typing re-arms it and never snaps — and
3384
+ * callers gating a transcript convergence on the drain can never
3385
+ * dead-lock. `reset()` also releases every waiter — the backlog it
3386
+ * drops would otherwise never drain.
3387
+ */
3388
+ whenDrained: (timeoutMs?: number) => Promise<void>;
3389
+ }
3390
+ interface StreamBufferOptions {
3391
+ /**
3392
+ * Returns `true` to drip-feed buffered text at the smooth typewriter
3393
+ * cadence, `false` to drain everything per tick (batched mode — the
3394
+ * legacy behaviour). Called on every tick, so flipping the user setting
3395
+ * takes effect on the next drain.
3396
+ */
3397
+ getSmooth?: () => boolean;
3398
+ /**
3399
+ * Display-memory bound applied on every drain commit (see
3400
+ * {@link capTranscriptEvents}): above this many events the oldest are
3401
+ * trimmed at a turn boundary and replaced with a cumulative
3402
+ * "… N earlier items hidden" marker event. Rendered events hold live
3403
+ * renderables/native buffers, so an unbounded buffer OOMs long
3404
+ * sessions (~0.13MB RSS per event measured in the TUI).
3405
+ *
3406
+ * Default {@link MAX_TRANSCRIPT_EVENTS}. Pass `null` to disable and
3407
+ * receive the full, unbounded event array (pre-5.13 behavior) — e.g.
3408
+ * a host that renders a virtualized list or exports events verbatim.
3409
+ */
3410
+ maxTranscriptEvents?: number | null;
3411
+ }
3412
+ declare function useStreamBuffer(setEvents: Dispatch<SetStateAction<StreamEvent[]>>, options?: StreamBufferOptions): StreamBuffer;
3413
+ //#endregion
3414
+ //#region src/chat/system-md.d.ts
3415
+ /** A discovered `SYSTEM.md`. */
3416
+ interface SystemMdFile {
3417
+ path: string;
3418
+ source: 'project' | 'user';
3419
+ contents: string;
3420
+ }
3421
+ /** Result of {@link discoverSystemMd}. */
3422
+ interface SystemMdResult {
3423
+ /**
3424
+ * The winning override, or `null` when no `SYSTEM.md` exists (the built-in
3425
+ * doctrine then applies unchanged).
3426
+ */
3427
+ file: SystemMdFile | null;
3428
+ /**
3429
+ * Files that exist but lost to `file` on precedence. Non-empty only when the
3430
+ * user has both a project and a user `SYSTEM.md`. Surfaced so the UI can mark
3431
+ * them as inactive instead of leaving the user wondering why edits to one of
3432
+ * them changed nothing.
3433
+ */
3434
+ shadowed: readonly SystemMdFile[];
3435
+ }
3436
+ interface DiscoverSystemMdOptions {
3437
+ /** Discovery cwd. Default: `process.cwd()`. */
3438
+ cwd?: string;
3439
+ /** User home directory. Default: `os.homedir()`. */
3440
+ home?: string;
3441
+ /** Storage prefix. Leading dot tolerated. Default: `'.zidane'`. */
3442
+ prefix?: string;
3443
+ /**
3444
+ * Which scopes may contribute an override — the same
3445
+ * {@link AgentsMdScope} the host applies to `AGENTS.md`, so one setting
3446
+ * governs both surfaces:
3447
+ * - `'none'` — no override, ever. Short-circuits before any FS probe.
3448
+ * - `'user'` — only `~/.{prefix}/SYSTEM.md`. The repo's file is IGNORED,
3449
+ * which is the whole point of the setting: it is how a user
3450
+ * says "do not trust this checkout".
3451
+ * - `'project'` — only `<root>/SYSTEM.md`.
3452
+ * - `'both'` — union, project winning (default).
3453
+ *
3454
+ * Previously a boolean that only implemented `'none'`, so `'user'` still let a
3455
+ * repo-root `SYSTEM.md` replace the entire doctrine — exactly the case the
3456
+ * setting exists to prevent.
3457
+ */
3458
+ scope?: AgentsMdScope;
3459
+ }
3460
+ /**
3461
+ * Absolute path of the USER-scope `SYSTEM.md` — the file the TUI creates when
3462
+ * the user asks for an override and none exists yet.
3463
+ *
3464
+ * Exported so the host doesn't hardcode the layout and can't drift from what
3465
+ * discovery actually reads.
3466
+ */
3467
+ declare function userSystemMdPath(opts?: {
3468
+ home?: string;
3469
+ prefix?: string;
3470
+ }): string;
3471
+ /**
3472
+ * Whether a USER-scope `SYSTEM.md` would be loaded under `scope`.
3473
+ *
3474
+ * Mirrors the candidate filter in {@link discoverSystemMd} so a host can ask
3475
+ * "would writing `~/.{prefix}/SYSTEM.md` accomplish anything?" without
3476
+ * re-deriving the rule and drifting from it. `'project'` excludes the user
3477
+ * candidate and `'none'` skips discovery altogether, so under either setting
3478
+ * creating that file is a silent no-op.
3479
+ */
3480
+ declare function userScopeOverridesLoad(scope: AgentsMdScope | undefined): boolean;
3481
+ /**
3482
+ * Find the active `SYSTEM.md` override, if any.
3483
+ *
3484
+ * Project paths anchor at the git root when one exists above `cwd`, else at
3485
+ * `cwd` — the same anchor as skills / mcps / AGENTS.md discovery, so all four
3486
+ * surfaces agree on what "this project" means.
3487
+ *
3488
+ * Tolerant: an unreadable or empty file is treated as absent (logged under
3489
+ * `ZIDANE_DEBUG`) and the next candidate gets its turn. Never throws — this
3490
+ * runs on the session-activation path.
3491
+ *
3492
+ * Candidates are filtered by {@link DiscoverSystemMdOptions.scope}, mirroring
3493
+ * how `discoverAgentsMd` builds its own list.
3494
+ */
3495
+ declare function discoverSystemMd(opts?: DiscoverSystemMdOptions): SystemMdResult;
3496
+ /**
3497
+ * Seed contents for a freshly created `SYSTEM.md` override, built from the
3498
+ * built-in doctrine.
3499
+ *
3500
+ * Seeding with the real doctrine (rather than an empty file) means creating the
3501
+ * override is a behavioral no-op until the user edits it. An empty seed would
3502
+ * silently delete every line of doctrine the moment the file appeared, making
3503
+ * the agent measurably worse with no visible cause.
3504
+ *
3505
+ * `currentSystem` should be DOCTRINE ONLY — callers pass
3506
+ * `splitSystemPrompt(doctrineOnlySystem()).static`, which excludes both the
3507
+ * generated `<env>` block (rebuilt per turn) and the `AGENTS.md` block. Baking
3508
+ * the latter in would duplicate it, since it is re-appended below the override
3509
+ * on every compose.
3510
+ *
3511
+ * `scope` is the active `userInstructionsScope`; it decides whether the seed's
3512
+ * precedence note is true — see {@link projectScopeOverridesLoad}.
3513
+ */
3514
+ declare function renderSystemMdSeed(currentSystem: string, scope?: AgentsMdScope): string;
3515
+ //#endregion
3516
+ //#region src/chat/theme-context.d.ts
3517
+ declare function ThemeProvider({
3518
+ theme,
3519
+ children
3520
+ }: {
3521
+ theme: Theme;
3522
+ children: ReactNode;
3523
+ }): import("react").JSX.Element;
3524
+ declare function useTheme(): Theme;
3525
+ /** Color palette only — equivalent to `useTheme().colors`. */
3526
+ declare function useColors(): ThemeColors;
3527
+ /** Select-row styling — equivalent to `useTheme().select`. */
3528
+ declare function useSelectStyle(): ThemeSelect;
3529
+ /** Panel / surface backgrounds — equivalent to `useTheme().surfaces`. */
3530
+ declare function useSurfaces(): ThemeSurfaces;
3531
+ /** Raw syntax style table — `useTheme().syntax`. Renderer converts to its native style type. */
3532
+ declare function useSyntaxStyles(): SyntaxStyles;
3533
+ //#endregion
3534
+ //#region src/chat/themes/catppuccin.d.ts
3535
+ declare const CATPPUCCIN_MOCHA: Theme;
3536
+ declare const CATPPUCCIN_MACCHIATO: Theme;
3537
+ declare const CATPPUCCIN_FRAPPE: Theme;
3538
+ declare const CATPPUCCIN_LATTE: Theme;
3539
+ //#endregion
3540
+ //#region src/chat/themes/gruvbox.d.ts
3541
+ declare const GRUVBOX_DARK: Theme;
3542
+ declare const GRUVBOX_LIGHT: Theme;
3543
+ //#endregion
3544
+ //#region src/chat/themes/vaporwave.d.ts
3545
+ declare const VAPORWAVE_THEME: Theme;
3546
+ //#endregion
3547
+ //#region src/chat/todo-anchor.d.ts
3548
+ interface ActiveTodoAnchorOptions {
3549
+ enabled?: boolean;
3550
+ maxItems?: number;
3551
+ heading?: string;
3552
+ }
3553
+ declare function renderActiveTodoAnchor(todos: readonly TodoItem[], options?: ActiveTodoAnchorOptions): string;
3554
+ /**
3555
+ * Optional chat-layer hook that keeps the live `todowrite` list visible in the
3556
+ * dynamic system prompt. It reads only live todos, never the archive, so an
3557
+ * auto-cleared completed plan is not reintroduced on later turns.
3558
+ *
3559
+ * ## Prompt-cache cost — read before enabling
3560
+ *
3561
+ * This writes per-turn state into the system prompt, and the system prompt sits
3562
+ * ABOVE the entire conversation in the provider's cache prefix (Anthropic hashes
3563
+ * tools → system → messages, cumulatively). So every time the rendered anchor
3564
+ * changes by a single byte, the whole transcript stops matching the cache and
3565
+ * re-bills at the cache-write rate; only `tools` and the static system block
3566
+ * survive.
3567
+ *
3568
+ * That is an acceptable trade only because the anchor changes *rarely* — once
3569
+ * per `todowrite`, which in practice is a small fraction of turns. Two rules
3570
+ * follow, and breaking either one turns that occasional cost into a per-turn
3571
+ * one:
3572
+ *
3573
+ * 1. Never add anything to the anchor that ticks on its own — no counters, no
3574
+ * timestamps, no elapsed times, no per-turn indices.
3575
+ * 2. The render must stay a pure function of the todo list.
3576
+ * `renderActiveTodoAnchor` is deterministic today, so identical todos emit
3577
+ * identical bytes; keep it that way. Note this does NOT cover reordering —
3578
+ * todo order is semantically meaningful (it is the plan), so a reorder is
3579
+ * a real state change and costs one re-bill like any other `todowrite`.
3580
+ *
3581
+ * Moving the anchor onto the newest message instead does NOT avoid this: it is
3582
+ * wire-only, so the message that carried last turn's anchor re-renders without
3583
+ * it, and the prefix diverges at that point just the same. The only injection
3584
+ * that is genuinely free is one persisted into the transcript as immutable
3585
+ * history.
3586
+ */
3587
+ declare function createActiveTodoAnchorHooks(options?: ActiveTodoAnchorOptions): AgentHookMap;
3588
+ //#endregion
3589
+ export { ProjectsFile as $, shortChord as $n, PerformSelfUpdateOptions as $r, createFileMcpCredentialStore as $t, buildSkillsConfig as A, buildPlanSystem as Ai, ChatHostEnvironment as An, ClaudeCopyResult as Ar, sessionModelFallbacks as At, SettingsCategoryDescriptor as B, RunningWorkItem as Bn, BuildUpdateHintOptions as Br, loadMcpToolsCache as Bt, userSystemMdPath as C, INTERACTION_GUIDANCE as Ci, PendingAnnotation as Cn, setProviderCredential as Cr, OAuthFlowOptions as Ct, SpawnStartRecord as D, SUBAGENT_GUIDANCE as Di, ApprovalRequestInput as Dn, isReservedCredKey as Dr, supportsOAuth as Dt, useStreamBuffer as E, PLAN_MODE_DOCTRINE_NO_PROMPTS as Ei, ApprovalHub as En, alternateCredKey as Er, runOAuthLogin as Et, SETTINGS_CATEGORIES as F, HostSettings as Fn, hasClaudeCodeSetup as Fr, removeMcpServer as Ft, StringSettingKey as G, WireApprovalRequest as Gn, RunUpdateCommandResult as Gr, McpToolToggleSet as Gt, SettingsInput as H, SerializedPreview as Hn, buildUpdateHint as Hr, refreshMcpToolsCatalog as Ht, SETTINGS_CHOICES as I, HostSignal as In, importFromClaudeCode as Ir, upsertMcpServer as It, folderlessCwd as J, cleanTitle as Jn, DetectPackageManagerOptions as Jr, useMcpToolToggleSet as Jt, enforceSettingsInvariants as K, toTodosSnapshot as Kn, runUpdateCommand as Kr, buildToolToggle as Kt, SETTINGS_INPUTS as L, PendingInteraction as Ln, tryOpenBrowser as Lr, CachedMcpToolList as Lt, discoverProjectSkills as M, ChatTurnsReadToolOptions as Mi, ApprovalOriginator as Mn, ClaudeImportSummary as Mr, RemoveMcpResult as Mt, BooleanSettingKey as N, chatReferenceTokensForPrompt as Ni, CommandArgSuggestionSummary as Nn, ClaudeSessionImportResult as Nr, UpsertMcpResult as Nt, buildSpawnEndEvent as O, TOKEN_DISCIPLINE_DOCTRINE as Oi, createApprovalHub as On, ConfigProvider as Or, ModelFallbackTarget as Ot, DEFAULT_SETTINGS as P, createChatTurnsReadTool as Pi, CommandSummary as Pn, countClaudeSessionFiles as Pr, mcpsWritePath as Pt, ProjectEntry as Q, buildHints as Qn, PerformInPlaceUpdateOptions as Qr, parentServerName as Qt, SETTINGS_TOGGLES as R, PromptAttachment as Rn, bootProfileEnabled as Rr, McpToolsCache as Rt, userScopeOverridesLoad as S, IDENTITY_PREFIX as Si, EditOutcomeLedger as Sn, setPreferredCredential as Sr, redirectUriFromAuthUrl as St, StreamSource as T, PLAN_MODE_DOCTRINE as Ti, runCompaction as Tn, ALTERNATE_CRED_KEY_SUFFIX as Tr, oauthUsesManualCodePaste as Tt, SettingsProvider as U, SessionPresence as Un, useUpdateCheck as Ur, saveMcpToolsCache as Ut, SettingsChoice as V, SendOptions as Vn, UseUpdateCheckOptions as Vr, mcpToolsCachePath as Vt, SettingsToggle as W, TodosSnapshot as Wn, RunUpdateCommandOptions as Wr, subscribeMcpToolsCache as Wt, scopeCwd as X, shouldAutoGenerateTitle as Xn, PackageManagerCommand as Xr, buildVisibleMcpRows as Xt, resolveSessionCwd as Y, generateSessionTitle as Yn, InPlaceUpdateResult as Yr, VisibleMcpRow as Yt, IMPLICITLY_SAFE_TOOLS as Z, BuildHintsOptions as Zn, PackageManagerId as Zr, indexOfServerRow as Zt, DiscoverSystemMdOptions as _, ACTIONS_WITH_CARE_DOCTRINE as _i, CreateWorkerParams as _n, credentialsPath as _r, findGitRoot as _t, GRUVBOX_DARK as a, detectLibc as ai, McpAuthEvent as an, Walk as ar, readProjects as at, discoverSystemMd as b, DOING_TASKS_DOCTRINE as bi, InteractionHub as bn, readProviderCredentialPair as br, fetchOAuthRedirect as bt, CATPPUCCIN_LATTE as c, performInPlaceSelfUpdate as ci, getMcpAuthStatus as cn, DiscoveryProvider as cr, runEndNotice as ct, ThemeProvider as d, chatAutoCompactBehavior as di, readFileSafe as dn, ApiKeyCredential as dr, clearProviderUsageCache as dt, PlatformTarballOptions as ei, mcpCredentialsPath as en, parseEditorCommand as er, addToSafelist as et, useColors as f, AgentsMdFile as fi, resolveCwdPath as fn, CredentialsFile as fr, fetchAllProviderUsage as ft, useTheme as g, renderAgentsMdBlock as gi, createChatHost as gn, applyApiKeyEnv as gr, usageSourceFor as gt, useSyntaxStyles as h, discoverAgentsMd as hi, ContextSegmentsReport as hn, ProviderCredential as hr, usageChecksOptedOut as ht, VAPORWAVE_THEME as i, compareSemver as ii, useMcpAuthState as in, DiscoverySlotOptions as ir, projectsFilePath as it, defaultSkillScanPaths as j, envSection as ji, PendingApprovalsStore as jn, ClaudeImportOptions as jr, McpConfigScope as jt, buildSpawnStartEvent as k, buildBuildSystem as ki, createMemoryApprovalsStore as kn, useConfig as kr, modelChainMetadata as kt, CATPPUCCIN_MACCHIATO as l, performSelfUpdate as li, reduceMcpAuth as ln, useDiscovery as lr, FetchProviderUsageOptions as lt, useSurfaces as m, DiscoverAgentsMdOptions as mi, ChatHost as mn, PreferCredentialResult as mr, providerUsageSupported as mt, createActiveTodoAnchorHooks as n, UpdateStatus as ni, McpAuthProvider as nn, Commit as nr, isOnSafelist as nt, GRUVBOX_LIGHT as o, detectPackageManager as oi, McpAuthStateMap as on, createDiscoverySlot as or, suggestSafelistEntries as ot, useSelectStyle as p, AgentsMdResult as pi, serializePreview as pn, OAuthCredential as pr, fetchProviderUsage as pt, useSettings as q, GenerateSessionTitleOptions as qn, CheckForUpdateOptions as qr, useMcpToolToggleMap as qt, renderActiveTodoAnchor as r, checkForUpdate as ri, useMcpAuthDispatch as rn, DiscoverySlot as rr, matchesSafelistEntry as rt, CATPPUCCIN_FRAPPE as s, parseSemver as si, McpAuthStatus as sn, DiscoveryContextValue as sr, writeProjects as st, ActiveTodoAnchorOptions as t, SelfUpdateResult as ti, patchMcpCredential as tn, quoteForShell as tr, getSafelist as tt, CATPPUCCIN_MOCHA as u, resolvePlatformPackage as ui, extractEditPayloadForGate as un, useDiscoveryOptional as ur, USAGE_CACHE_TTL_MS as ut, SystemMdFile as v, BuildSystemOptions as vi, WorkerRow as vn, readCredentials as vr, formatPathForCwd as vt, StreamBuffer as w, INTERACTION_GUIDANCE_NO_PROMPTS as wi, CompactionEntry as wn, writeCredentials as wr, OAuthPrompt$1 as wt, renderSystemMdSeed as x, EnvSectionOptions as xi, createInteractionHub as xn, removeProviderCredential as xr, normalizeRedirectPaste as xt, SystemMdResult as y, COMMUNICATION_DOCTRINE as yi, WorkerSummary as yn, readProviderCredential as yr, FetchOAuthRedirectResult as yt, SettingsCategory as z, QueuedItem as zn, bootTick as zr, clearMcpToolsCache as zt };
3590
+ //# sourceMappingURL=index-DgdzXRPH.d.ts.map