zidane 4.1.4 → 4.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +3 -3
  2. package/dist/{agent-BoV5Twdl.d.ts → agent-BAoqUvwA.d.ts} +27 -1
  3. package/dist/{agent-BoV5Twdl.d.ts.map → agent-BAoqUvwA.d.ts.map} +1 -1
  4. package/dist/{index-28otmfLX.d.ts → index-B8-yNSsk.d.ts} +2 -2
  5. package/dist/index-B8-yNSsk.d.ts.map +1 -0
  6. package/dist/{index-DPsd0qwm.d.ts → index-CqpNqjDy.d.ts} +2 -2
  7. package/dist/{index-DPsd0qwm.d.ts.map → index-CqpNqjDy.d.ts.map} +1 -1
  8. package/dist/index.d.ts +3 -3
  9. package/dist/index.js +4 -4
  10. package/dist/mcp.d.ts +1 -1
  11. package/dist/{presets-Cs7_CsMk.js → presets-BzkJDW1K.js} +3 -3
  12. package/dist/presets-BzkJDW1K.js.map +1 -0
  13. package/dist/presets.d.ts +1 -1
  14. package/dist/presets.js +1 -1
  15. package/dist/{providers-CX-R-Oy-.js → providers-CCDvIXGJ.js} +26 -5
  16. package/dist/providers-CCDvIXGJ.js.map +1 -0
  17. package/dist/providers.d.ts +1 -1
  18. package/dist/providers.js +1 -1
  19. package/dist/session/sqlite.d.ts +1 -1
  20. package/dist/session.d.ts +1 -1
  21. package/dist/skills.d.ts +2 -2
  22. package/dist/{stats-DoKUtF5T.js → stats-BT9l57RS.js} +34 -2
  23. package/dist/stats-BT9l57RS.js.map +1 -0
  24. package/dist/{tools-DpeWKzP1.js → tools-C8kDot0H.js} +73 -23
  25. package/dist/tools-C8kDot0H.js.map +1 -0
  26. package/dist/tools.d.ts +2 -2
  27. package/dist/tools.js +1 -1
  28. package/dist/tui.d.ts +423 -80
  29. package/dist/tui.d.ts.map +1 -1
  30. package/dist/tui.js +1604 -250
  31. package/dist/tui.js.map +1 -1
  32. package/dist/types.d.ts +2 -2
  33. package/dist/types.js +1 -1
  34. package/package.json +1 -1
  35. package/dist/index-28otmfLX.d.ts.map +0 -1
  36. package/dist/presets-Cs7_CsMk.js.map +0 -1
  37. package/dist/providers-CX-R-Oy-.js.map +0 -1
  38. package/dist/stats-DoKUtF5T.js.map +0 -1
  39. package/dist/tools-DpeWKzP1.js.map +0 -1
package/dist/tools.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as ToolDef, g as ToolContext, v as ToolMap } from "./agent-BoV5Twdl.js";
2
- import { A as edit, C as readFile, D as createInteractionTool, E as InteractionToolOptions, O as grep, S as shell, T as listFiles, _ as createSkillsUseTool, b as SkillsReadToolOptions, c as validateToolArgs, d as createToolSearchTool, f as ChildAgent, g as SkillsUseToolOptions, h as createSpawnTool, k as glob, l as LazyToolEntry, m as SpawnToolState, o as writeFile, p as SpawnToolOptions, s as ValidationResult, u as ToolSearchToolOptions, v as SkillsRunScriptToolOptions, w as multiEdit, x as createSkillsReadTool, y as createSkillsRunScriptTool } from "./index-28otmfLX.js";
1
+ import { _ as ToolDef, g as ToolContext, v as ToolMap } from "./agent-BAoqUvwA.js";
2
+ import { A as edit, C as readFile, D as createInteractionTool, E as InteractionToolOptions, O as grep, S as shell, T as listFiles, _ as createSkillsUseTool, b as SkillsReadToolOptions, c as validateToolArgs, d as createToolSearchTool, f as ChildAgent, g as SkillsUseToolOptions, h as createSpawnTool, k as glob, l as LazyToolEntry, m as SpawnToolState, o as writeFile, p as SpawnToolOptions, s as ValidationResult, u as ToolSearchToolOptions, v as SkillsRunScriptToolOptions, w as multiEdit, x as createSkillsReadTool, y as createSkillsRunScriptTool } from "./index-B8-yNSsk.js";
3
3
  export { ChildAgent, InteractionToolOptions, LazyToolEntry, SkillsReadToolOptions, SkillsRunScriptToolOptions, SkillsUseToolOptions, SpawnToolOptions, SpawnToolState, ToolContext, ToolDef, ToolMap, ToolSearchToolOptions, ValidationResult, createInteractionTool, createSkillsReadTool, createSkillsRunScriptTool, createSkillsUseTool, createSpawnTool, createToolSearchTool, edit, glob, grep, listFiles, multiEdit, readFile, shell, validateToolArgs, writeFile };
package/dist/tools.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as multiEdit, c as grep, f as createToolSearchTool, g as validateToolArgs, h as createSkillsReadTool, i as readFile, l as glob, m as createSkillsRunScriptTool, n as createSpawnTool, o as listFiles, p as createSkillsUseTool, r as shell, s as createInteractionTool, t as writeFile, u as edit } from "./tools-DpeWKzP1.js";
1
+ import { a as multiEdit, c as grep, f as createToolSearchTool, g as validateToolArgs, h as createSkillsReadTool, i as readFile, l as glob, m as createSkillsRunScriptTool, n as createSpawnTool, o as listFiles, p as createSkillsUseTool, r as shell, s as createInteractionTool, t as writeFile, u as edit } from "./tools-C8kDot0H.js";
2
2
  export { createInteractionTool, createSkillsReadTool, createSkillsRunScriptTool, createSkillsUseTool, createSpawnTool, createToolSearchTool, edit, glob, grep, listFiles, multiEdit, readFile, shell, validateToolArgs, writeFile };
package/dist/tui.d.ts CHANGED
@@ -1,13 +1,143 @@
1
- import { Et as SessionTurn, H as Provider, It as TurnUsage, Mt as ToolResultContent, O as SessionStore } from "./agent-BoV5Twdl.js";
2
- import { P as Preset } from "./index-28otmfLX.js";
1
+ import { D as SessionRun, Et as SessionTurn, H as Provider, It as TurnUsage, Mt as ToolResultContent, O as SessionStore } from "./agent-BAoqUvwA.js";
2
+ import { P as Preset } from "./index-B8-yNSsk.js";
3
+ import { OAuthCredentials, OAuthProviderInterface } from "@mariozechner/pi-ai/oauth";
3
4
  import { SyntaxStyle } from "@opentui/core";
4
5
  import * as _$react from "react";
5
6
  import { Dispatch, ReactNode, SetStateAction } from "react";
6
7
 
8
+ //#region src/tui/providers.d.ts
9
+ /**
10
+ * Structural model metadata — compatible with pi-ai's `Model` interface but
11
+ * not coupled to it, so hosts can pass either pi-ai-shaped objects or a
12
+ * custom registry of their own.
13
+ *
14
+ * Deliberately a structural duplicate of pi-ai's `Model` rather than a
15
+ * re-export: keeps the public API stable when pi-ai bumps shape, and lets
16
+ * hosts implement their own registry without depending on pi-ai's types.
17
+ *
18
+ * Lives here (rather than `./config`) because `ProviderDescriptor.models`
19
+ * needs it — pushing it to `config.tsx` created an import cycle.
20
+ */
21
+ interface ModelInfo {
22
+ id: string;
23
+ name?: string;
24
+ contextWindow: number;
25
+ maxTokens?: number;
26
+ reasoning?: boolean;
27
+ input?: readonly ('text' | 'image')[];
28
+ cost?: {
29
+ input: number;
30
+ output: number;
31
+ cacheRead?: number;
32
+ cacheWrite?: number;
33
+ };
34
+ provider?: string;
35
+ }
36
+ interface ProviderDescriptor {
37
+ /**
38
+ * Unique identifier. Used as the registry key, persisted in `state.json`
39
+ * as the resumed provider, and (by default) as the credential-file key.
40
+ */
41
+ key: string;
42
+ /** Display name shown in the TUI's provider picker and labels. */
43
+ label: string;
44
+ /**
45
+ * Factory that builds a fresh `Provider` instance. Called on every session
46
+ * activation. Some factories (e.g. zidane's built-in `anthropic`) eagerly
47
+ * resolve credentials at construction time and throw when none are
48
+ * configured — set {@link defaultModel} on the descriptor to avoid the TUI
49
+ * calling the factory before the user has picked + authed a provider.
50
+ */
51
+ factory: () => Provider;
52
+ /**
53
+ * Default model id to seed the picker with. When omitted, the TUI falls
54
+ * back to `descriptor.factory().meta.defaultModel`, which constructs the
55
+ * provider — fine for lazy-credential factories, but breaks for factories
56
+ * that throw on missing credentials before the wizard runs. Setting this
57
+ * eagerly lets the TUI render the auth wizard without ever instantiating
58
+ * the provider.
59
+ */
60
+ defaultModel?: string;
61
+ /**
62
+ * Env var checked when detecting whether the user has an API key
63
+ * configured for this provider. Optional — set to `undefined` when the
64
+ * provider only supports OAuth (or only credentials via a custom path).
65
+ */
66
+ envKey?: string;
67
+ /**
68
+ * Key under which credentials live in `credentials.json`. Defaults to
69
+ * `key`. The only built-in that overrides this is OpenAI Codex
70
+ * (`openai` → `openai-codex`) to stay compatible with the harness
71
+ * provider's existing lookup.
72
+ */
73
+ credentialFileKey?: string;
74
+ /** Placeholder shown in the wizard's API-key input. */
75
+ apiKeyPlaceholder?: string;
76
+ /**
77
+ * pi-ai (or compat) OAuth provider. When present, the wizard offers an
78
+ * OAuth option in addition to API key.
79
+ */
80
+ oauthProvider?: OAuthProviderInterface;
81
+ /**
82
+ * Optional copy appended to the wizard's "OAuth" method description.
83
+ * Use to communicate why a user might pick OAuth (e.g. subscription tier,
84
+ * org-wide SSO). Shown after `browser-based sign-in`. Skip the leading
85
+ * space — the wizard adds it. Example: `'Claude Pro/Max subscription'`.
86
+ */
87
+ oauthHint?: string;
88
+ /**
89
+ * pi-ai provider id used to look up models in pi-ai's built-in registry.
90
+ * Defaults to `key`. Only Codex differs (`openai` → `openai-codex`).
91
+ */
92
+ piProviderId?: string;
93
+ /**
94
+ * Override the model list returned for this provider's picker. When set,
95
+ * skips pi-ai's registry entirely. Useful for hosts maintaining their
96
+ * own model catalogue or for custom providers pi-ai doesn't know about.
97
+ */
98
+ models?: readonly ModelInfo[];
99
+ }
100
+ /** Convenience accessor — returns `credentialFileKey ?? key`. */
101
+ declare function credKeyOf(desc: ProviderDescriptor): string;
102
+ /** Convenience accessor — returns `piProviderId ?? key`. */
103
+ declare function piIdOf(desc: ProviderDescriptor): string;
104
+ declare const anthropicDescriptor: ProviderDescriptor;
105
+ declare const openaiDescriptor: ProviderDescriptor;
106
+ declare const openrouterDescriptor: ProviderDescriptor;
107
+ declare const cerebrasDescriptor: ProviderDescriptor;
108
+ /**
109
+ * Default provider registry. Passed verbatim when `runTui` is invoked without
110
+ * an explicit `providers` option. Hosts that want to override per-provider
111
+ * metadata can spread this and replace specific entries:
112
+ *
113
+ * ```ts
114
+ * runTui({ providers: { ...BUILTIN_PROVIDERS, anthropic: myOwnAnthropicDescriptor } })
115
+ * ```
116
+ */
117
+ declare const BUILTIN_PROVIDERS: Readonly<Record<string, ProviderDescriptor>>;
118
+ /**
119
+ * Resolve the model list for a given provider. Honors `descriptor.models`
120
+ * when set; otherwise queries pi-ai via `descriptor.piProviderId`. Returns
121
+ * `[]` for descriptors with no known mapping (custom providers without a
122
+ * model list) — callers should hide the model picker in that case.
123
+ */
124
+ declare function modelsForDescriptor(descriptor: ProviderDescriptor): readonly ModelInfo[];
125
+ /**
126
+ * Look up the model's max context window via the descriptor's model source.
127
+ * Returns `null` when the model isn't known (custom slugs, providers without
128
+ * a registry); callers should hide the context indicator in that case.
129
+ */
130
+ declare function getContextWindow(descriptor: ProviderDescriptor, modelId: string): number | null;
131
+ //#endregion
7
132
  //#region src/tui/auth.d.ts
8
- type ProviderKey = 'anthropic' | 'openai' | 'openrouter' | 'cerebras';
133
+ /**
134
+ * Provider identifier as known to the TUI. Built-in keys are `anthropic`,
135
+ * `openai`, `openrouter`, `cerebras`, but hosts can register additional
136
+ * keys via {@link ProviderDescriptor} — so the type is open.
137
+ */
138
+ type ProviderKey = string;
9
139
  interface AuthMethod {
10
- source: 'env' | 'oauth';
140
+ source: 'env' | 'oauth' | 'apikey';
11
141
  /** Human-readable detail (env var name, expiry timestamp, …). */
12
142
  detail: string;
13
143
  }
@@ -18,17 +148,20 @@ interface ProviderAuth {
18
148
  available: boolean;
19
149
  methods: AuthMethod[];
20
150
  }
21
- declare function envKeyFor(key: ProviderKey): string;
22
151
  /**
23
- * Detect available auth across the providers the harness ships with.
152
+ * Detect available auth for every registered provider.
153
+ *
154
+ * Resolution order per provider (a method appears in `methods` for each
155
+ * layer that has a credential — the agent itself resolves them in the same
156
+ * order via its provider factories):
24
157
  *
25
- * Mirrors the resolution order used by the providers at runtime:
26
- * - explicit env var (highest)
27
- * - OAuth credentials in `.credentials.json` (anthropic + openai-codex only)
158
+ * 1. `kind: 'apikey'` from `credentials.json` (injected into env at TUI launch)
159
+ * 2. explicit env var (descriptor's `envKey`)
160
+ * 3. `kind: 'oauth'` from `credentials.json` (or legacy `cwd/.credentials.json`)
28
161
  *
29
162
  * Pure read — never refreshes or rewrites the credentials file.
30
163
  */
31
- declare function detectAuth(env?: Record<string, string | undefined>): ProviderAuth[];
164
+ declare function detectAuth(dataDir: string, registry: Readonly<Record<string, ProviderDescriptor>>, env?: Record<string, string | undefined>): ProviderAuth[];
32
165
  //#endregion
33
166
  //#region src/tui/types.d.ts
34
167
  type Screen = 'auth' | 'sessions' | 'chat';
@@ -47,6 +180,13 @@ interface StreamEvent {
47
180
  childId?: string;
48
181
  /** Nesting depth — 0 = parent, ≥ 1 = subagent (matches zidane's spawn depth). */
49
182
  depth?: number;
183
+ /**
184
+ * Canonical tool name for `tool` / `tool-result` events. Lets the renderer
185
+ * filter by tool — e.g. hide the parent's `spawn` tool-result when
186
+ * `hideSubagentOutput` is on, since the spawn-end marker already shows the
187
+ * same stats. Absent for non-tool events.
188
+ */
189
+ tool?: string;
50
190
  }
51
191
  interface Picked {
52
192
  provider: ProviderAuth;
@@ -58,11 +198,28 @@ interface SessionMeta {
58
198
  turnCount: number;
59
199
  updatedAt: number;
60
200
  }
61
- /** Persisted, user-toggleable transcript filters. */
201
+ /** Persisted, user-toggleable transcript filters and modes. */
62
202
  interface Settings {
63
203
  showThinking: boolean;
64
204
  showToolCalls: boolean;
65
205
  showToolResults: boolean;
206
+ /**
207
+ * Safe mode — when enabled, every tool call requires explicit user
208
+ * approval unless it's covered by the project safelist (`projects.json`)
209
+ * or the implicit read-only allow-list. Default: on.
210
+ */
211
+ safeMode: boolean;
212
+ /**
213
+ * Hide a subagent's internal events (its streamed markdown, thinking,
214
+ * tool calls, and tool results) from the transcript. The `🌱` start and
215
+ * `✓` end markers stay visible so the user still sees that a subagent
216
+ * is working and when it finished. Default: on — subagent runs are
217
+ * usually noisy and the parent's final response already summarizes them.
218
+ *
219
+ * When off, those events are visible and wrapped in a dim bordered box
220
+ * for clear visual hierarchy.
221
+ */
222
+ hideSubagentOutput: boolean;
66
223
  }
67
224
  //#endregion
68
225
  //#region src/tui/store.d.ts
@@ -91,13 +248,24 @@ declare function listSessionMeta(store: SessionStore): Promise<SessionMeta[]>;
91
248
  /** Derive a short title from the first user message — returns null when empty. */
92
249
  declare function titleFromTurns(turns: SessionTurn[]): string | null;
93
250
  /**
94
- * Replay persisted turns as a viewable transcript. Mirrors the event shape produced
95
- * live by the agent hooks so loaded and streaming history render identically.
251
+ * Replay persisted turns as a viewable transcript. Mirrors the event shape
252
+ * produced live by the agent hooks so loaded and streaming history render
253
+ * identically — including subagent ancestry when `runs` is supplied.
96
254
  *
97
- * Skips `tool_result` blocks (they're not user-visible by default), and inserts a
98
- * `separator` event between turn groups so the eye can parse turn boundaries.
255
+ * Subagent reconstruction:
256
+ * - Every turn carries a `runId`. We look that up in `runs` to get the
257
+ * run's `depth` and tag the resulting events with `{ depth, childId }`
258
+ * — the same shape the live `child:*` bubble hooks produce.
259
+ * - We synthesize `spawn-start` / `spawn-end` markers at each child-run
260
+ * boundary so the transcript reads the same as a live run did
261
+ * (`🌱 [run-id] task` … child events … `🌳 [run-id] done · tokens`).
262
+ * - For child runs (`depth > 0`), the user-role "task" text is suppressed
263
+ * because `spawn-start` already shows it.
264
+ *
265
+ * Without `runs` (legacy callers / tests), the function falls back to the
266
+ * old behavior: depth-0 events with no subagent grouping.
99
267
  */
100
- declare function eventsFromTurns(turns: SessionTurn[]): StreamEvent[];
268
+ declare function eventsFromTurns(turns: SessionTurn[], runs?: readonly SessionRun[]): StreamEvent[];
101
269
  /** Shared formatter for the `↳ name(args)` line shown on tool calls. */
102
270
  declare function toolCallPreview(name: string, input: Record<string, unknown>): string;
103
271
  /** Render tool output as plain text, whether it's a string or structured content. */
@@ -107,35 +275,16 @@ declare function lastContextSizeFromTurns(turns: SessionTurn[]): number;
107
275
  //#endregion
108
276
  //#region src/tui/config.d.ts
109
277
  /**
110
- * Structural model metadata compatible with pi-ai's `Model` interface but
111
- * not coupled to it, so callers can pass either pi-ai-shaped objects or a
112
- * custom registry of their own.
113
- *
114
- * Deliberately a structural duplicate of `pi-ai`'s `Model` rather than a
115
- * re-export: keeps the public API stable when pi-ai bumps shape, and lets
116
- * hosts implement their own registry without depending on pi-ai's types.
278
+ * Provider registrya map keyed by `descriptor.key`. Passed verbatim to the
279
+ * TUI; there is no implicit merge with built-ins. Hosts that want the four
280
+ * default providers spread {@link BUILTIN_PROVIDERS}; hosts that only want
281
+ * their own pass exactly their own.
117
282
  */
118
- interface ModelInfo {
119
- id: string;
120
- name?: string;
121
- contextWindow: number;
122
- maxTokens?: number;
123
- reasoning?: boolean;
124
- input?: readonly ('text' | 'image')[];
125
- cost?: {
126
- input: number;
127
- output: number;
128
- cacheRead?: number;
129
- cacheWrite?: number;
130
- };
131
- provider?: string;
132
- }
133
- type ProviderRegistry = Partial<Record<ProviderKey, () => Provider>>;
134
- type ModelRegistry = Partial<Record<ProviderKey, readonly ModelInfo[]>>;
283
+ type ProviderRegistry = Readonly<Record<string, ProviderDescriptor>>;
135
284
  /**
136
285
  * Options accepted by `runTui()` and `resolveConfig()`. Every field is optional
137
- * — sensible defaults boot a working chat against the four built-in providers
138
- * with sessions stored under `~/.zidane/`.
286
+ * — defaults boot a working chat against the built-in providers with sessions
287
+ * stored under `~/.zidane/`.
139
288
  */
140
289
  interface TuiOptions {
141
290
  /**
@@ -150,9 +299,15 @@ interface TuiOptions {
150
299
  */
151
300
  storageDir?: string;
152
301
  /**
153
- * Provider factory map. Override entries (or supply a whole new map) to
154
- * register custom providers. Default: anthropic/openai/openrouter/cerebras
155
- * from the built-in zidane factories.
302
+ * Provider registry. Each value is a {@link ProviderDescriptor} carrying
303
+ * label + factory + credential metadata. **No automatic merge** — hosts
304
+ * pass exactly what they want.
305
+ *
306
+ * - Omitted → {@link BUILTIN_PROVIDERS} (anthropic + openai + openrouter + cerebras).
307
+ * - `providers: BUILTIN_PROVIDERS` → same as omitted, but explicit.
308
+ * - `providers: { anthropic: anthropicDescriptor }` → only Anthropic.
309
+ * - `providers: { ...BUILTIN_PROVIDERS, mine: customDescriptor }` → built-ins + custom.
310
+ * - `providers: { mine: customDescriptor }` → custom-only, **no built-ins**.
156
311
  */
157
312
  providers?: ProviderRegistry;
158
313
  /**
@@ -164,12 +319,6 @@ interface TuiOptions {
164
319
  * `<storageDir>/<prefix>/sessions.db`.
165
320
  */
166
321
  store?: SessionStore;
167
- /**
168
- * Per-provider model registry for the in-app model picker. Each list should
169
- * be in pi-ai's `Model`-compatible shape (see `ModelInfo`). If omitted, the
170
- * picker falls back to pi-ai's built-in registry via `getModels()`.
171
- */
172
- models?: ModelRegistry;
173
322
  }
174
323
  interface ResolvedConfig {
175
324
  prefix: string;
@@ -256,15 +405,83 @@ declare function Transcript({
256
405
  /**
257
406
  * Resolve the top margin for an event given the one rendered just before it.
258
407
  *
259
- * The only context-aware rule today: a tool/tool-result event that follows
260
- * another tool/tool-result event collapses its margin to zero, so a chain of
261
- * tool calls reads as a tight list whether the user has hidden tool outputs
262
- * or not, and whether the agent emits back-to-back calls or call→result pairs.
408
+ * Context-aware rules:
409
+ *
410
+ * - A `tool` / `tool-result` event right after another `tool` / `tool-result`
411
+ * collapses to a tight list call→result pairs and back-to-back calls
412
+ * read as one logical block.
413
+ * - A parent-level event (`depth === 0`) right after a subagent event
414
+ * (`depth > 0`) collapses too. The subagent's `🌳` end marker (and, in
415
+ * show mode, the subagent box's bottom border) already provides the
416
+ * separation; adding the event's default `marginTop` on top would
417
+ * produce the visible "line jump" between a subagent's outcome and the
418
+ * parent's follow-up. Either form of marker is enough — we don't want
419
+ * both.
263
420
  *
264
421
  * Exported so the spacing matrix can be unit-tested without rendering.
265
422
  */
266
423
  declare function marginTopFor(event: StreamEvent, previous: StreamEvent | undefined): number;
267
424
  //#endregion
425
+ //#region src/tui/credentials.d.ts
426
+ interface ApiKeyCredential {
427
+ kind: 'apikey';
428
+ value: string;
429
+ }
430
+ interface OAuthCredential {
431
+ kind: 'oauth';
432
+ access: string;
433
+ refresh?: string;
434
+ expires?: number;
435
+ /** Provider-specific extras (e.g. OpenAI Codex `accountId`). */
436
+ [extra: string]: unknown;
437
+ }
438
+ type ProviderCredential = ApiKeyCredential | OAuthCredential;
439
+ /** Top-level shape of `credentials.json` — keys are credential-file keys. */
440
+ type CredentialsFile = Record<string, ProviderCredential>;
441
+ /**
442
+ * Resolve the credentials file path given the resolved TUI data directory
443
+ * (typically `~/.zidane`, i.e. `config.paths.dir`).
444
+ *
445
+ * Matches the convention used elsewhere in the TUI (sessions.db, state.json)
446
+ * so a single `ZIDANE_STORAGE_DIR` override moves the entire data root.
447
+ */
448
+ declare function credentialsPath(dataDir: string): string;
449
+ /**
450
+ * Read credentials from disk.
451
+ *
452
+ * Returns `{}` when the file is missing or corrupt (last-ditch tolerance —
453
+ * a hand-edit gone wrong shouldn't lock the user out of re-authing). On first
454
+ * call with no file present, attempts a migration from `cwd/.credentials.json`
455
+ * (the legacy location used by `bun run auth`).
456
+ */
457
+ declare function readCredentials(dataDir: string): CredentialsFile;
458
+ /** Read a single provider's credential (translating via the descriptor). */
459
+ declare function readProviderCredential(dataDir: string, descriptor: ProviderDescriptor): ProviderCredential | undefined;
460
+ /**
461
+ * Write credentials atomically (write-then-rename) with mode 0o600.
462
+ *
463
+ * Atomic on the same filesystem — readers either see the previous file or the
464
+ * new one, never a half-written intermediate. Creates the parent dir if needed
465
+ * (first launch on a fresh machine: `~/.zidane/` may not exist yet).
466
+ */
467
+ declare function writeCredentials(dataDir: string, creds: CredentialsFile): void;
468
+ declare function setProviderCredential(dataDir: string, descriptor: ProviderDescriptor, cred: ProviderCredential): void;
469
+ declare function removeProviderCredential(dataDir: string, descriptor: ProviderDescriptor): void;
470
+ /**
471
+ * Inject API-key credentials into `process.env` so the harness providers pick
472
+ * them up via their existing env-var resolution. Called once at TUI launch
473
+ * after the credentials file has been resolved. OAuth credentials are NOT
474
+ * injected — those reach providers via `ZIDANE_CREDENTIALS_PATH` + the file
475
+ * reader in `src/providers/oauth.ts`.
476
+ *
477
+ * Does not overwrite env vars that are already set — explicit user-provided
478
+ * env values win over stored API keys.
479
+ *
480
+ * Descriptors without an `envKey` (OAuth-only providers, custom providers
481
+ * that bypass env-var resolution) are skipped silently.
482
+ */
483
+ declare function applyApiKeyEnv(dataDir: string, registry: Readonly<Record<string, ProviderDescriptor>>): void;
484
+ //#endregion
268
485
  //#region src/tui/format.d.ts
269
486
  /** Compact token formatter — 12_415 → "12.4k", 1_234_567 → "1.23M". */
270
487
  declare function fmtTokens(n: number): string;
@@ -328,9 +545,10 @@ declare function Modal({
328
545
  //#endregion
329
546
  //#region src/tui/model-picker.d.ts
330
547
  /**
331
- * Modal that lists the available models for the current provider and lets the
332
- * user pick one. Options come from `runTui({ models })` if supplied, otherwise
333
- * from pi-ai's built-in registry.
548
+ * Modal that lists the available models for the current provider and lets
549
+ * the user pick one. Options come from the active `ProviderDescriptor`
550
+ * either its declared `models` list or, when absent, pi-ai's built-in
551
+ * registry looked up via `piProviderId`.
334
552
  *
335
553
  * Each row shows: `● selected · name (ctx N · reasoning · vision)`.
336
554
  */
@@ -344,30 +562,140 @@ declare function ModelPickerModal({
344
562
  onPick: (modelId: string) => void;
345
563
  }): _$react.ReactNode;
346
564
  //#endregion
347
- //#region src/tui/providers.d.ts
565
+ //#region src/tui/oauth.d.ts
566
+ declare function supportsOAuth(descriptor: ProviderDescriptor): boolean;
567
+ interface OAuthFlowOptions {
568
+ /** Called when the provider emits its login URL — typically right after the callback server starts. */
569
+ onUrl: (url: string, instructions?: string) => void;
570
+ /** Called when the provider needs a code entered manually (rare; only when callback server fails). */
571
+ onCodeRequest?: () => Promise<string>;
572
+ /** Called with each progress message from the OAuth flow (token exchange, etc.). */
573
+ onProgress?: (message: string) => void;
574
+ /** Abort the in-flight login (e.g. user pressed esc). */
575
+ signal?: AbortSignal;
576
+ }
577
+ /**
578
+ * Run the OAuth login flow for a provider.
579
+ *
580
+ * Returns the OAuth credentials on success; caller persists them via
581
+ * `setProviderCredential(dataDir, descriptor, { kind: 'oauth', ...credentials })`.
582
+ * Throws when the descriptor has no `oauthProvider` configured.
583
+ */
584
+ declare function runOAuthLogin(descriptor: ProviderDescriptor, options: OAuthFlowOptions): Promise<OAuthCredentials>;
585
+ //#endregion
586
+ //#region src/tui/safe-mode.d.ts
587
+ /**
588
+ * Safe-mode storage + matching for the TUI.
589
+ *
590
+ * Lives at `<dataDir>/projects.json` (default `~/.zidane/projects.json`). Each
591
+ * top-level key is an absolute project directory; the value carries that
592
+ * project's persisted tool-call `safelist`.
593
+ *
594
+ * ```json
595
+ * {
596
+ * "/Users/me/proj-a": { "safelist": ["read_file", "shell:git:*"] }
597
+ * }
598
+ * ```
599
+ *
600
+ * Two granularities for safelist entries:
601
+ * - **bare tool name** — `"read_file"` matches every `read_file` call.
602
+ * - **tool + first-arg token + wildcard** — `"shell:git:*"` matches `shell`
603
+ * calls whose primary string argument starts with the token `git`
604
+ * (followed by whitespace or end-of-string). Modelled on Claude Code's
605
+ * `Bash(git:*)` syntax.
606
+ *
607
+ * A short list of read-only tools is **implicitly safe** without being
608
+ * persisted — see {@link IMPLICITLY_SAFE_TOOLS}.
609
+ */
610
+ interface ProjectEntry {
611
+ safelist?: string[];
612
+ }
613
+ type ProjectsFile = Record<string, ProjectEntry>;
614
+ /** Resolve `projects.json`'s on-disk path given the TUI data directory. */
615
+ declare function projectsFilePath(dataDir: string): string;
616
+ declare function readProjects(dataDir: string): ProjectsFile;
617
+ /** Atomic write — tmp + rename so a crash never leaves a half-file. */
618
+ declare function writeProjects(dataDir: string, file: ProjectsFile): void;
348
619
  /**
349
- * Construct a fresh provider instance for a given key.
620
+ * Append `entry` to the safelist for `projectDir`, dedup-aware. Returns the
621
+ * updated entry list (post-write) so callers can render it without re-reading.
622
+ */
623
+ declare function addToSafelist(dataDir: string, projectDir: string, entry: string): readonly string[];
624
+ /** Read the safelist for one project. Returns `[]` for unknown projects. */
625
+ declare function getSafelist(dataDir: string, projectDir: string): readonly string[];
626
+ /**
627
+ * Tools that always pass without prompting — pure file/dir reads with no
628
+ * side effects. Users who want to gate them must disable safe-mode entirely
629
+ * (or fork this list in their own embedding).
630
+ */
631
+ declare const IMPLICITLY_SAFE_TOOLS: readonly string[];
632
+ /**
633
+ * Test whether a `{ tool, input }` pair is covered by one safelist entry.
634
+ *
635
+ * Supported entry shapes:
636
+ * - `"<tool>"` — broad match on tool name. For `shell` this still requires
637
+ * a single-program command (compound forms always prompt).
638
+ * - `"<tool>:<token>:*"` — match when the primary arg's first token equals
639
+ * `<token>`. For `shell`, also requires the command to be free of
640
+ * metacharacters (`;`, `&&`, `||`, `|`, `$(`, backticks, `>`, `<`,
641
+ * newlines, subshells) — otherwise a `shell:git:*` entry would silently
642
+ * greenlight `git status && rm -rf /`.
643
+ *
644
+ * Entries that don't fit either shape are ignored (forward-compat for future
645
+ * pattern syntax — readers shouldn't choke on entries written by a newer
646
+ * version of the TUI).
647
+ */
648
+ declare function matchesSafelistEntry(entry: string, tool: string, input: Record<string, unknown>): boolean;
649
+ /** True when a call matches ANY entry in the project's safelist (or is implicitly safe). */
650
+ declare function isOnSafelist(entries: readonly string[], tool: string, input: Record<string, unknown>): boolean;
651
+ /**
652
+ * Suggest the safelist entry to write when the user picks "accept and
653
+ * remember" for a `{ tool, input }`. Heuristic:
654
+ *
655
+ * - `shell` → scope by first command token (`shell:git:*`).
656
+ * - anything else → bare tool name (broad).
350
657
  *
351
- * Providers are cheap to build credentials are resolved lazily at first
352
- * stream call so we instantiate on demand rather than caching a singleton.
353
- * This also avoids leaking state across session/provider switches.
658
+ * Returning a string ensures the UI always has a concrete entry to display
659
+ * as the button label.
354
660
  */
355
- declare const FACTORIES: Record<ProviderKey, () => Provider>;
356
- /** zidane provider key → pi-ai provider id (some don't match 1:1). */
357
- declare const PI_PROVIDER_ID: Record<ProviderKey, string>;
661
+ declare function suggestSafelistEntry(tool: string, input: Record<string, unknown>): string;
662
+ //#endregion
663
+ //#region src/tui/safe-mode-context.d.ts
664
+ type ApprovalDecision = 'accept-once' | 'accept-safelist' | 'deny';
665
+ interface ApprovalRequest {
666
+ id: string;
667
+ tool: string;
668
+ input: Record<string, unknown>;
669
+ resolve: (decision: ApprovalDecision) => void;
670
+ }
671
+ /** Function signature consumed by `tool:gate` handlers + the child-tool wrap. */
672
+ type RequestApproval = (tool: string, input: Record<string, unknown>) => Promise<ApprovalDecision>;
673
+ interface SafeModeActions {
674
+ /** Request a decision; resolves once the user picks. */
675
+ requestApproval: RequestApproval;
676
+ /** Resolve the head and shift the queue forward. */
677
+ resolveHead: (decision: ApprovalDecision) => void;
678
+ /** Resolve all pending with `deny`. Used on abort / hard exit. */
679
+ denyAll: () => void;
680
+ }
358
681
  /**
359
- * Look up the model's max context window via pi-ai's model registry.
360
- * Returns `null` when the model isn't known (e.g. a custom openrouter slug);
361
- * callers should hide the context indicator in that case.
682
+ * Owns the queue + actions. Splits the value across two contexts so a queue
683
+ * change doesn't invalidate every callback memo that closes over the actions.
362
684
  */
363
- declare function getContextWindow(key: ProviderKey, modelId: string): number | null;
685
+ declare function SafeModeProvider({
686
+ children
687
+ }: {
688
+ children: ReactNode;
689
+ }): ReactNode;
690
+ declare function useSafeModeQueue(): readonly ApprovalRequest[];
691
+ declare function useSafeModeActions(): SafeModeActions;
364
692
  //#endregion
365
693
  //#region src/tui/screens.d.ts
366
694
  declare function AuthScreen({
367
695
  onPick
368
696
  }: {
369
697
  onPick: (p: ProviderAuth) => void;
370
- }): _$react.ReactNode;
698
+ }): ReactNode;
371
699
  declare function SessionsScreen({
372
700
  sessions,
373
701
  currentId,
@@ -378,20 +706,24 @@ declare function SessionsScreen({
378
706
  currentId: string | null;
379
707
  onPick: (id: string) => void;
380
708
  onCreate: () => void;
381
- }): _$react.ReactNode;
709
+ }): ReactNode;
382
710
  declare function ChatScreen({
383
711
  events,
384
712
  busy,
385
713
  settings,
386
714
  onSubmit,
387
- session
715
+ session,
716
+ pending,
717
+ onApproval
388
718
  }: {
389
719
  events: StreamEvent[];
390
720
  busy: boolean;
391
721
  settings: Settings;
392
722
  onSubmit: (prompt: string) => void;
393
- session: SessionMeta | null;
394
- }): _$react.ReactNode;
723
+ session: SessionMeta | null; /** Head of the safe-mode approval queue, or `null` when nothing is pending. */
724
+ pending: ApprovalRequest | null; /** Resolve the active prompt with the user's pick. */
725
+ onApproval: (decision: ApprovalDecision) => void;
726
+ }): ReactNode;
395
727
  //#endregion
396
728
  //#region src/tui/settings.d.ts
397
729
  declare const DEFAULT_SETTINGS: Settings;
@@ -409,7 +741,19 @@ declare function SettingsProvider({
409
741
  children: ReactNode;
410
742
  }): ReactNode;
411
743
  declare function useSettings(): SettingsContextValue;
412
- declare function SettingsModal(): ReactNode;
744
+ interface SettingsActions {
745
+ /**
746
+ * Re-open the auth screen so the user can switch providers or run the
747
+ * wizard for a new/existing one. Wiring this callback adds a
748
+ * "Re-configure providers" row to the modal.
749
+ */
750
+ onReauth?: () => void;
751
+ }
752
+ declare function SettingsModal({
753
+ actions
754
+ }?: {
755
+ actions?: SettingsActions;
756
+ }): ReactNode;
413
757
  //#endregion
414
758
  //#region src/tui/streaming.d.ts
415
759
  /** Flip any trailing streaming markdown blocks (any owner) to finalized. */
@@ -516,18 +860,17 @@ declare const MD_STYLE: SyntaxStyle;
516
860
  * to `runTui({ storageDir, prefix })`.
517
861
  *
518
862
  * ```ts
519
- * import { runTui } from 'zidane/tui'
863
+ * import { BUILTIN_PROVIDERS, runTui } from 'zidane/tui'
520
864
  * import { createRemoteStore } from 'zidane/session' // for the `store` option
521
865
  *
522
866
  * await runTui() // ~/.zidane/sessions.db + state.json
523
867
  * await runTui({ prefix: '.myapp' }) // ~/.myapp/...
524
868
  * await runTui({ storageDir: '/data', prefix: 'myapp' })
525
- * await runTui({ providers: { custom: () => myProvider() } })
869
+ * await runTui({ providers: { ...BUILTIN_PROVIDERS, mine: myDescriptor } })
526
870
  * await runTui({ store: createRemoteStore({ url: '…' }) })
527
- * await runTui({ models: { anthropic: [{ id: 'claude-foo', contextWindow: 200_000 }] } })
528
871
  * ```
529
872
  */
530
873
  declare function runTui(options?: TuiOptions): Promise<never>;
531
874
  //#endregion
532
- export { App, type AuthMethod, AuthScreen, COLOR, ChatScreen, ConfigProvider, type ContextUsage, DEFAULT_SETTINGS, FACTORIES, Footer, type Hint, MD_STYLE, Modal, type ModalProps, ModalRoot, type ModelInfo, ModelPickerModal, type ModelRegistry, type Owner, PI_PROVIDER_ID, type Picked, type ProviderAuth, type ProviderKey, type ProviderRegistry, type ResolvedConfig, SELECT_THEME, type Screen, type SessionMeta, SessionsScreen, type Settings, SettingsModal, SettingsProvider, Spinner, type StateStoreApi, type StreamBuffer, type StreamEvent, type StreamSource, Transcript, type TuiOptions, type TuiState, ageString, createStateStore, createTuiStore, detectAuth, envKeyFor, eventsFromTurns, finalizeStreamingMarkdown, finalizeStreamingMarkdownForOwner, fmtTokens, getContextWindow, lastContextSizeFromTurns, listSessionMeta, loadState, marginTopFor, onInputSubmit, resolveConfig, runTui, saveState, shortId, titleFromTurns, toolCallPreview, toolResultText, turnContextSize, useConfig, useModal, useModalAwareFocus, useSettings, useStreamBuffer };
875
+ export { type ApiKeyCredential, App, type ApprovalDecision, type ApprovalRequest, type AuthMethod, AuthScreen, BUILTIN_PROVIDERS, COLOR, ChatScreen, ConfigProvider, type ContextUsage, type CredentialsFile, DEFAULT_SETTINGS, Footer, type Hint, IMPLICITLY_SAFE_TOOLS, MD_STYLE, Modal, type ModalProps, ModalRoot, type ModelInfo, ModelPickerModal, type OAuthCredential, type OAuthFlowOptions, type Owner, type Picked, type ProjectEntry, type ProjectsFile, type ProviderAuth, type ProviderCredential, type ProviderDescriptor, type ProviderKey, type ProviderRegistry, type RequestApproval, type ResolvedConfig, SELECT_THEME, type SafeModeActions, SafeModeProvider, type Screen, type SessionMeta, SessionsScreen, type Settings, SettingsModal, SettingsProvider, Spinner, type StateStoreApi, type StreamBuffer, type StreamEvent, type StreamSource, Transcript, type TuiOptions, type TuiState, addToSafelist, ageString, anthropicDescriptor, applyApiKeyEnv, cerebrasDescriptor, createStateStore, createTuiStore, credKeyOf, credentialsPath, detectAuth, eventsFromTurns, finalizeStreamingMarkdown, finalizeStreamingMarkdownForOwner, fmtTokens, getContextWindow, getSafelist, isOnSafelist, lastContextSizeFromTurns, listSessionMeta, loadState, marginTopFor, matchesSafelistEntry, modelsForDescriptor, onInputSubmit, openaiDescriptor, openrouterDescriptor, piIdOf, projectsFilePath, readCredentials, readProjects, readProviderCredential, removeProviderCredential, resolveConfig, runOAuthLogin, runTui, saveState, setProviderCredential, shortId, suggestSafelistEntry, supportsOAuth, titleFromTurns, toolCallPreview, toolResultText, turnContextSize, useConfig, useModal, useModalAwareFocus, useSafeModeActions, useSafeModeQueue, useSettings, useStreamBuffer, writeCredentials, writeProjects };
533
876
  //# sourceMappingURL=tui.d.ts.map