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.
- package/dist/{acp-BQqhozmt.js → acp-Wr8l4IDI.js} +7 -180
- package/dist/acp-Wr8l4IDI.js.map +1 -0
- package/dist/acp-cli.js +6 -6
- package/dist/acp.d.ts +1 -1
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +2 -1
- package/dist/{agent-DTxH7RHW.js → agent-C-camKQW.js} +162 -12
- package/dist/agent-C-camKQW.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/{anthropic-DRNRnd6W.js → anthropic-BDOGzhtj.js} +3 -3
- package/dist/{anthropic-DRNRnd6W.js.map → anthropic-BDOGzhtj.js.map} +1 -1
- package/dist/{transcript-anchors-BjH-68k2.js → approvals-CqqxE5Vh.js} +4924 -275
- package/dist/approvals-CqqxE5Vh.js.map +1 -0
- package/dist/{auth-7rv6ZfWk.js → auth-BdwDROYg.js} +12 -8
- package/dist/{auth-7rv6ZfWk.js.map → auth-BdwDROYg.js.map} +1 -1
- package/dist/chat/pure.d.ts +221 -4
- package/dist/chat/pure.d.ts.map +1 -0
- package/dist/chat/pure.js +4 -2
- package/dist/chat.d.ts +6 -2911
- package/dist/chat.js +8 -261
- package/dist/{context-transport-D8yeGdpd.js → context-transport-BqYwzJYE.js} +2 -2
- package/dist/{context-transport-D8yeGdpd.js.map → context-transport-BqYwzJYE.js.map} +1 -1
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/daytona.js +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/contexts/e2b.js +1 -1
- package/dist/daemon-cli.d.ts +1 -0
- package/dist/daemon-cli.js +90 -0
- package/dist/daemon-cli.js.map +1 -0
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/extensions.d.ts +2 -2
- package/dist/extensions.js +2 -2
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +4 -4
- package/dist/{index-D0O-QO7Z.d.ts → index-B-FSaM3_.d.ts} +2863 -2375
- package/dist/index-B-FSaM3_.d.ts.map +1 -0
- package/dist/index-DgdzXRPH.d.ts +3590 -0
- package/dist/index-DgdzXRPH.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -1179
- package/dist/ipc-CIv87xuH.js +331 -0
- package/dist/ipc-CIv87xuH.js.map +1 -0
- package/dist/ipc.d.ts +849 -0
- package/dist/ipc.d.ts.map +1 -0
- package/dist/ipc.js +4 -0
- package/dist/json-rpc-rSWMUh9b.js +192 -0
- package/dist/json-rpc-rSWMUh9b.js.map +1 -0
- package/dist/{logger-DBX9uYOw.js → logger--C1VQjwJ.js} +3 -13
- package/dist/logger--C1VQjwJ.js.map +1 -0
- package/dist/logs-view-CWG8aUAG.js +44 -0
- package/dist/logs-view-CWG8aUAG.js.map +1 -0
- package/dist/{mcp-CVtSQTWg.js → mcp-BIWa57eY.js} +3 -3
- package/dist/{mcp-CVtSQTWg.js.map → mcp-BIWa57eY.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-CZMUuKzo.js → messages-21XfHxyF.js} +3 -3
- package/dist/{messages-CZMUuKzo.js.map → messages-21XfHxyF.js.map} +1 -1
- package/dist/{openai-compat-BoTi-9fd.js → openai-compat-DY-AKxCg.js} +2 -2
- package/dist/{openai-compat-BoTi-9fd.js.map → openai-compat-DY-AKxCg.js.map} +1 -1
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/{plugins-onDvTHEm.js → plugins-B531Tz5b.js} +2 -2
- package/dist/{plugins-onDvTHEm.js.map → plugins-B531Tz5b.js.map} +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +3 -3
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +1 -1
- package/dist/{providers-CmhdtD7w.js → providers-DX91jwir.js} +2 -2
- package/dist/{providers-CmhdtD7w.js.map → providers-DX91jwir.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +3 -3
- package/dist/restate.d.ts +1 -1
- package/dist/restate.js +8 -8
- package/dist/restate.js.map +1 -1
- package/dist/server-DJqp9dIR.js +1317 -0
- package/dist/server-DJqp9dIR.js.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +42 -4
- package/dist/session/sqlite.js.map +1 -1
- package/dist/{session-CyRLSsVa.js → session-K3icbPH9.js} +8 -2
- package/dist/session-K3icbPH9.js.map +1 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/{skills-BMGH_KK1.js → skills-PJyiSRCp.js} +3 -21
- package/dist/skills-PJyiSRCp.js.map +1 -0
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +1 -1
- package/dist/src-DzzhE2YO.js +2461 -0
- package/dist/src-DzzhE2YO.js.map +1 -0
- package/dist/todo-edits-Dw2oO2ON.js +378 -0
- package/dist/todo-edits-Dw2oO2ON.js.map +1 -0
- package/dist/{tool-formatters-NrzD6Fkk.d.ts → tool-formatters-DO56YBBw.d.ts} +104 -4
- package/dist/tool-formatters-DO56YBBw.d.ts.map +1 -0
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-UhC58Tmj.d.ts → transcript-anchors-DXqWWGbq.d.ts} +53 -4
- package/dist/transcript-anchors-DXqWWGbq.d.ts.map +1 -0
- package/dist/tui.d.ts +94 -13
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +2241 -1009
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-mMxDRgLY.js → turn-operations-CZLsNgw-.js} +896 -11
- package/dist/turn-operations-CZLsNgw-.js.map +1 -0
- package/dist/{turn-operations-Cuc9pwiB.d.ts → turn-operations-DKFYWP3O.d.ts} +501 -4
- package/dist/turn-operations-DKFYWP3O.d.ts.map +1 -0
- package/dist/{types-BIarq1qE.js → types-CWgENVLY.js} +24 -2
- package/dist/types-CWgENVLY.js.map +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/xml-D5HBhJle.js +22 -0
- package/dist/xml-D5HBhJle.js.map +1 -0
- package/docs/CHAT.md +39 -0
- package/docs/IPC.md +420 -0
- package/docs/TUI.md +62 -0
- package/package.json +8 -2
- package/dist/acp-BQqhozmt.js.map +0 -1
- package/dist/agent-DTxH7RHW.js.map +0 -1
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js.map +0 -1
- package/dist/index-D0O-QO7Z.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/inline-autocomplete-D0MPvBaS.js +0 -596
- package/dist/inline-autocomplete-D0MPvBaS.js.map +0 -1
- package/dist/logger-DBX9uYOw.js.map +0 -1
- package/dist/login-CB23LyI1.js +0 -707
- package/dist/login-CB23LyI1.js.map +0 -1
- package/dist/session-CyRLSsVa.js.map +0 -1
- package/dist/skills-BMGH_KK1.js.map +0 -1
- package/dist/tool-formatters-NrzD6Fkk.d.ts.map +0 -1
- package/dist/transcript-anchors-BjH-68k2.js.map +0 -1
- package/dist/transcript-anchors-UhC58Tmj.d.ts.map +0 -1
- package/dist/turn-operations-Cuc9pwiB.d.ts.map +0 -1
- package/dist/turn-operations-mMxDRgLY.js.map +0 -1
- 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
|