# zidane/chat

> The renderer-agnostic chat engine that backs the TUI and GUI hosts.

Import: `import { … } from 'zidane/chat'`. Source: [src/chat/index.ts](https://github.com/Tahul/zidane/blob/main/src/chat/index.ts).

Use `zidane/chat` to build your own chat app on `createChatHost`, the engine the terminal and desktop apps share ([one engine](https://zidane.si/docs/apps.md#one-engine)). It owns runs, the queue, approvals, todos, profile switching and session operations behind one API. `cacheImpact` and `cacheReport` tell a picker what a switch would re-read and how much of each request hit the prompt cache, and `cacheHintText` turns an estimate into the picker hint (`re-reads ~452k tokens`). The daemon and zidane-server wrap the engine, so `zidane/chat` never imports `zidane/ipc`. In a browser bundle, `zidane/chat` resolves to its browser build through the `browser` export condition: every type, plus the values that need no Node built-in (React contexts, settings, themes, formatters). The host engine, providers, credentials, discovery and stores are only in the Node build. `zidane/chat/pure` is the subset without React.

```ts
import { resolveConfig } from 'zidane/chat'
import { createMemoryStore } from 'zidane/session'

const config = resolveConfig({ prefix: '.myapp', store: createMemoryStore() })
console.log(Object.keys(config.providers), config.initialAgentId)
```

The Node code of the setup wizard is here too. `detectOnboardingEnvironment` reads what a computer already has: provider keys, the setups of other agents, the GitHub login and local model servers. `previewClaudeImport` lists what `importFromClaudeCode` copies, before it runs. The model of the wizard, which both apps run on, is in [`zidane/chat/pure`](https://zidane.si/docs/reference/chat-pure.md).

## Functions

### `accountCredKey`

Function.

File key holding one of a provider's non-active OAuth accounts.

Same sibling-key reasoning as [`alternateCredKey`](https://zidane.si/docs/reference/chat.md#alternateCredKey): the canonical key is
the active one, so a provider with three OAuth grants stores the active one
canonically and the other two under `#account:N`. `#alt` stays dedicated to
the other-kind (API key) alternate so files written by builds that predate
multi-account keep meaning the same thing.

```ts
function accountCredKey(fileKey: string, n: number): string
```

**Parameters**

- `fileKey` (`string`): The provider's canonical credential file key.
- `n` (`number`): Slot number, starting at 1.

**Returns** `string`

Source: [src/chat/cred-keys.ts:68](https://github.com/Tahul/zidane/blob/main/src/chat/cred-keys.ts#L68)

### `acquireUpdateLock`

Function.

Takes the install lock, replacing a stale one. Never throws: a cache dir
that cannot be written yields `acquired: true` with a no-op release, since
a lock that cannot be created protects nothing and must not block the
install.

```ts
function acquireUpdateLock(
  cacheDir: string,
  by: string,
  options?: UpdateLockOptions,
): UpdateLockResult
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the lock. Created when missing.
- `by` (`string`): Who runs the install, shown to anyone who finds the lock held.
- `options?` (`UpdateLockOptions`, default `{}`): Injected clock, liveness probe, host name and pid.

**Returns** `UpdateLockResult`

Source: [src/chat/auto-update-lock.ts:120](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-lock.ts#L120)

### `addToSafelist`

Function.

Append one or more entries to the safelist for `projectDir`, dedup-aware.
Accepts a single string or a list (a compound shell command needs one
entry per command head — see [`suggestSafelistEntries`](https://zidane.si/docs/reference/chat.md#suggestSafelistEntries)). Writes once
and returns the updated entry list (post-write) so callers can render it
without re-reading. No write happens when every entry already exists.

```ts
function addToSafelist(
  dataDir: string,
  projectDir: string,
  entries: string | readonly string[],
): readonly string[]
```

**Parameters**

- `dataDir` (`string`): Directory that holds `projects.json`.
- `projectDir` (`string`): Project key the entries are stored under.
- `entries` (`string | readonly string[]`): One entry or a list. Entries already present are skipped.

**Returns** `readonly string[]`

Source: [src/chat/safe-mode.ts:110](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L110)

### `addToSafelistForCwd`

Function.

[`addToSafelist`](https://zidane.si/docs/reference/chat.md#addToSafelist) under the project key for `cwd` — the write every host's gate uses.

```ts
function addToSafelistForCwd(
  dataDir: string,
  cwd: string,
  entries: string | readonly string[],
): readonly string[]
```

**Parameters**

- `dataDir` (`string`): Directory that holds `projects.json`.
- `cwd` (`string`): Working directory. Entries are stored under its project key.
- `entries` (`string | readonly string[]`): One entry or a list. Entries already present are skipped.

**Returns** `readonly string[]`

Source: [src/chat/safe-mode.ts:174](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L174)

### `alternateCredKey`

Function.

File key holding a provider's second credential, when it has one of each kind.

The canonical key always holds the credential the user prefers; this sibling
holds the other. That one invariant is what makes preference selection free:
every existing reader (`applyApiKeyEnv`, the harness provider factories)
already resolves the canonical key, so "switch priority" is just moving two
objects between two keys — no precedence flag to interpret, no resolution
order to keep in sync across layers.

A sibling key rather than a nested field because the canonical entry gets
REPLACED wholesale on token refresh; anything nested inside it would be
dropped by the next rotation. And older zidane versions look credentials up
by `credKeyOf`, so a suffixed key is simply invisible to them — they read the
preferred credential and behave exactly as before, which is why this needs no
migration or schema version.

```ts
function alternateCredKey(fileKey: string): string
```

**Parameters**

- `fileKey` (`string`): The provider's canonical credential file key.

**Returns** `string`

Source: [src/chat/cred-keys.ts:52](https://github.com/Tahul/zidane/blob/main/src/chat/cred-keys.ts#L52)

### `applyApiKeyEnv`

Function.

Reconcile `process.env` with the stored credentials so the harness providers
pick up the wizard's output via their existing env-var resolution. Each
host (TUI, daemon, desktop app) calls it at launch after the credentials file
has been resolved, and again after a credential change.

**Precedence: stored credential > ambient env.** When the user has run the
auth wizard, that's an explicit, recent action — it MUST win over whatever
ambient value is already in `process.env`. Otherwise an upgrade reliably
breaks for anyone whose cwd happens to contain a stale `.env`: Bun
auto-loads `.env` from cwd into `process.env` before our code runs, older
versions of `bun run auth` upserted tokens into `cwd/.env`, and even a
one-off `export ANTHROPIC_API_KEY=…` from months ago in the user's shell rc
can outlive its usefulness. None of those should override a key the user
just typed into the wizard.

Three cases per registered provider with an `envKey`:

1. Stored `kind: 'apikey'` → overwrite env with the stored value. Wizard
   wins. A debug log fires when this clobbers a different ambient value.
2. Stored `kind: 'oauth'`  → DELETE env, so `resolveOAuthApiKey`
   falls through to the file-read + refresh path. A stale OAuth token
   in env would otherwise short-circuit refresh and get rejected.
3. No stored entry → leave env alone. Env-only setups (no wizard run yet,
   hosts driving zidane purely from secrets management) keep working.

To opt out and force ambient env to win again, the user signs the provider
out via the wizard (which deletes the stored entry → case 3 above).

Descriptors without an `envKey` (OAuth-only providers, custom providers
that bypass env-var resolution) are skipped silently.

```ts
function applyApiKeyEnv(
  dataDir: string,
  registry: Readonly<Record<string, ProviderDescriptor>>,
  opts?: { log?: (message: string) => void },
): void
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `registry` (`Readonly<Record<string, ProviderDescriptor>>`): Provider descriptors keyed by id.
- `opts?` (`{ log?: (message: string) => void }`, default `{}`): Options.

Source: [src/chat/credentials.ts:803](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L803)

### `applyEnvironmentSettingsChange`

Function.

Validate `change` against the kind's schema and turn it into a settings
patch + secret writes. Throws with a user-facing message on an unknown
kind or field, a value that doesn't fit its type, or disabling `local`.

```ts
function applyEnvironmentSettingsChange(
  registry: EnvironmentRegistry,
  settings: object | undefined,
  change: EnvironmentSettingsChange,
): EnvironmentSettingsWrite
```

**Parameters**

- `registry` (`EnvironmentRegistry`): Registry of known environment kinds.
- `settings` (`object | undefined`): Current settings object, or `undefined`.
- `change` (`EnvironmentSettingsChange`): The edit to validate and apply.

**Returns** `EnvironmentSettingsWrite`

Source: [src/chat/environment-settings.ts:335](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L335)

### `applyMarkdownSubagents`

Function.

Fold markdown/builtin agents into the preset's spawn tool, filling gaps
only: an agent the profile or an extension already registered keeps its
definition. Runs after `composeExtensionPreset`, so precedence is
builtin < file < extension without threading files through composition.

```ts
function applyMarkdownSubagents(preset: Preset, subagents: SubagentRegistry): Preset
```

**Parameters**

- `preset` (`Preset`): Preset whose `spawn` tool receives the agents. Returned unchanged when it has no spawn tool or `subagents` is empty.
- `subagents` (`SubagentRegistry`): Agent registry to merge, typically from [`loadMarkdownAgents`](https://zidane.si/docs/reference/chat.md#loadMarkdownAgents).

**Returns** `Preset`

Source: [src/markdown-defs/index.ts:156](https://github.com/Tahul/zidane/blob/main/src/markdown-defs/index.ts#L156)

### `assembleDiff`

Function.

Tracked patch + untracked files as new-file diffs, within `WORKSPACE_MAX_DIFF_BYTES`.
Stops at a file boundary once the budget is spent, so the result is always
a valid patch (only an oversized FIRST file is cut, at a hunk boundary).
Untracked files are read `concurrency` at a time in listing order and
reading stops as soon as the budget or the file cap is reached.
Shared with sandbox workspaces.

```ts
function assembleDiff(
  tracked: string,
  untracked: readonly string[],
  concurrency: number,
  read: (rel: string) => Promise<string | null>,
): Promise<string>
```

**Parameters**

- `tracked` (`string`): `git diff HEAD`'s patch.
- `untracked` (`readonly string[]`): The untracked paths.
- `concurrency` (`number`): Reads in flight at once.
- `read` (`(rel: string) => Promise<string | null>`): An untracked file's text; `null` to leave it out.

**Returns** `Promise<string>`

Source: [src/chat/workspace/files.ts:397](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L397)

### `assertEnvironmentReady`

Function.

Throws [`EnvironmentNotReadyError`](https://zidane.si/docs/reference/chat.md#EnvironmentNotReadyError) unless the environment can run.

```ts
function assertEnvironmentReady(environment: SessionEnvironment): void
```

**Parameters**

- `environment` (`SessionEnvironment`): Environment to check.

Source: [src/chat/environment.ts:139](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L139)

### `autoInstallUpdate`

Function.

Installs the release a background check found, unless something says not
to (see [`isAutoInstallCandidate`](https://zidane.si/docs/reference/chat.md#isAutoInstallCandidate)) or another install holds the lock.
Never throws. Records a failure so the same version is not retried, and
clears the record on success.

```ts
function autoInstallUpdate(
  options: AutoInstallUpdateOptions,
): Promise<AutoInstallOutcome>
```

**Parameters**

- `options` (`AutoInstallUpdateOptions`): Check result, cache directory, targets and test seams.

**Returns** `Promise<AutoInstallOutcome>`

**Example**

```ts
const outcome = await autoInstallUpdate({ status, cacheDir, targets, by: 'terminal app' })
if (outcome.kind === 'failed')
  log.warn('automatic update failed', { error: outcome.error })
```

Source: [src/chat/auto-update-auto-install.ts:229](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L229)

### `autoInstallViaWorker`

Function.

An `autoInstallUpdate`-shaped function that runs the install in the
detached worker, for `useAutoInstallUpdate`'s `install` seam. A lost
worker reads as a failed install.

```ts
function autoInstallViaWorker(
  command: readonly string[],
  extra?: { env?: ProcessEnv; onLine?: (line: string) => void },
): (options: AutoInstallUpdateOptions) => Promise<AutoInstallOutcome>
```

**Parameters**

- `command` (`readonly string[]`): Argv that runs the worker.
- `extra?` (`{ env?: ProcessEnv; onLine?: (line: string) => void }`, default `{}`): Worker environment and log callback.

**Returns** `(options: AutoInstallUpdateOptions) => Promise<AutoInstallOutcome>`

Source: [src/chat/auto-update-worker.ts:345](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L345)

### `autoTitleAction`

Function.

What auto-titling should do for this session now:

- `generate`: no title yet and the first exchange happened.
- `check`: an auto title, with enough new prompts since the last check.
- `skip`: a user title, too few turns, or checked recently.

```ts
function autoTitleAction(
  turns: readonly SessionTurn[],
  metadata: Readonly<Record<string, unknown>> | undefined,
): AutoTitleAction
```

**Parameters**

- `turns` (`readonly SessionTurn[]`): The session's turns.
- `metadata` (`Readonly<Record<string, unknown>> | undefined`): Session metadata that holds the title, its source and the last check count.

**Returns** `AutoTitleAction`

Source: [src/chat/auto-title.ts:180](https://github.com/Tahul/zidane/blob/main/src/chat/auto-title.ts#L180)

### `availableAgents`

Function.

The profiles available under `settings`: those without a
`AgentProfile.requiresSetting`, or whose setting is on. Returns the
same registry object when nothing is filtered out.

```ts
function availableAgents(
  registry: AgentRegistry,
  settings: Partial<Settings>,
): AgentRegistry
```

**Parameters**

- `registry` (`AgentRegistry`): The full agent registry.
- `settings` (`Partial<Settings>`): Current settings, checked against each profile's `requiresSetting`.

**Returns** `AgentRegistry`

Source: [src/chat/agents.ts:95](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L95)

### `availableEnvironmentActions`

Function.

The actions `environment`'s kind offers for it right now (a ready, owned-or-shared environment).

```ts
function availableEnvironmentActions(
  registry: EnvironmentRegistry,
  environment: SessionEnvironment,
  config: EnvironmentConfig,
): EnvironmentAction[]
```

**Parameters**

- `registry` (`EnvironmentRegistry`): Registry that holds the session's provider.
- `environment` (`SessionEnvironment`): The session's environment.
- `config` (`EnvironmentConfig`): The kind's resolved options.

**Returns** `EnvironmentAction[]`

Source: [src/chat/environment.ts:481](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L481)

### `backfillAccountEmails`

Function.

Look up the email of the stored Claude OAuth accounts that have none, and
store it on the account (or that there is none to find). Skips accounts
already looked up, accounts whose lookup failed less than
`RETRY_AFTER_MS` ago, expired tokens (a refresh comes first) and
tokens that are not Claude OAuth tokens. Resolves when done; never rejects.

```ts
function backfillAccountEmails(
  dataDir: string,
  registry: Readonly<Record<string, ProviderDescriptor>>,
): Promise<void>
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`.
- `registry` (`Readonly<Record<string, ProviderDescriptor>>`): Provider descriptors; only those storing Claude accounts are looked at.

**Returns** `Promise<void>`

Source: [src/chat/account-email.ts:76](https://github.com/Tahul/zidane/blob/main/src/chat/account-email.ts#L76)

### `bootProfileEnabled`

Function.

Returns `true` when `ZIDANE_BOOT_PROFILE` is set. Useful for guarding
heavier instrumentation (e.g. wrapping a costly call in a span) that
you don't want paying its own cost in the default path.

```ts
function bootProfileEnabled(): boolean
```

**Returns** `boolean`

Source: [src/chat/boot-profiler.ts:64](https://github.com/Tahul/zidane/blob/main/src/chat/boot-profiler.ts#L64)

### `bootTick`

Function.

Record a checkpoint. No-op unless `ZIDANE_BOOT_PROFILE` is truthy in
the environment.

The leading delta is the time since the PREVIOUS tick (so a long
delta highlights the immediately-preceding work); the trailing total
is the time since the profiler started. Both round to one decimal of
a millisecond.

```ts
function bootTick(label: string): void
```

**Parameters**

- `label` (`string`): Name of the boot step that just finished.

Source: [src/chat/boot-profiler.ts:45](https://github.com/Tahul/zidane/blob/main/src/chat/boot-profiler.ts#L45)

### `buildBuildSystem`

Function.

Compose the Build-mode system prompt.

Layout (top to bottom):

identity → doctrine → guidance → user instructions
─── SYSTEM_PROMPT_BOUNDARY ───
env (cwd / project root / date / platform)

Everything ABOVE the boundary is byte-stable for the lifetime of the run
and rides the prompt cache across turns + sessions. The env block sits
BELOW so a moved cwd or rolled-over date doesn't invalidate the doctrine.

The fragments are joined with blank lines so the model sees clean section
breaks. Don't reorder lightly — every reorder costs one cache miss across
every active session.

```ts
function buildBuildSystem(opts?: BuildSystemOptions): string
```

**Parameters**

- `opts?` (`BuildSystemOptions`, default `{}`): Environment, override and interaction settings for the prompt.

**Returns** `string`

Source: [src/chat/agent-prompt.ts:415](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L415)

### `buildEditOutcomesAnnotation`

Function.

Render an `EditOutcome[]` as the wire-format annotation block. Returns
the body to APPEND to a tool result; callers join with a leading
`\n\n` separator. Idempotent on missing reasons — bare `applied` lines
stay terse.

```ts
function buildEditOutcomesAnnotation(outcomes: readonly EditOutcome[]): string
```

**Parameters**

- `outcomes` (`readonly EditOutcome[]`): Per-hunk outcomes, in hunk order.

**Returns** `string`

Source: [src/chat/edit-approval.ts:147](https://github.com/Tahul/zidane/blob/main/src/chat/edit-approval.ts#L147)

### `buildHints`

Function.

Build the footer's shortcut hints for the current screen. On the chat
screen the model id rides next to its `ctrl+m` shortcut and the agent
label rides next to `shift+tab`, each in its accent color — the bar
doubles as the status display without needing separate badges. When
the active model exposes reasoning, the `ctrl+m` hint grows a
secondary `/l` chord with the current effort label, surfacing the
effort picker as a discoverable, in-place affordance.

```ts
function buildHints(options: BuildHintsOptions): Hint[]
```

**Parameters**

- `options` (`BuildHintsOptions`): Screen, run state, labels, colors, and keybindings that decide which hints appear.

**Returns** `Hint[]`

Source: [src/chat/footer-hints.ts:158](https://github.com/Tahul/zidane/blob/main/src/chat/footer-hints.ts#L158)

### `buildMcpServers`

Function.

Map a user-toggled enable list onto the `mcpServers` array the agent
receives. Conventions match `buildSkillsConfig`:

- `enabled === undefined` → every discovered server enabled (default).
- `enabled === []`        → no servers (the agent runs MCP-less).
- `enabled === [names]`   → allowlist; servers not in the list are
  dropped.

`disabledTools` is the per-server tool deny-list from
`Settings.disabledMcpTools`. For each server that survives the
server-level enable filter, the per-tool names are merged into
`McpServerConfig.disabledTools` via **union** with anything the JSON
file already pinned — a tool listed in either source is dropped.
Union (not override) keeps JSON-side policy stable: a server file
that pins `disabledTools: ['dangerous_tool']` can't be re-enabled
via the UI by accident.

`enabledTools` (JSON-side allowlist) takes precedence over the
UI-side deny-list when both are set on the same server — the
allowlist's "only these" semantics would be meaningless if a UI
deny-list could subtract from it again. We don't emit `disabledTools`
when `enabledTools` is present.

Returns a fresh array — callers can mutate without affecting the
underlying discovery list. Per-server config objects are also fresh
copies (shallow), so the merged `disabledTools` doesn't leak back
onto the discovery catalog.

```ts
function buildMcpServers(
  opts: { disabledTools?: Record<string, readonly string[]>; discovered: readonly DiscoveredMcp[]; enabled?: readonly string[] },
): McpServerConfig[]
```

**Parameters**

- `opts` (`{ disabledTools?: Record<string, readonly string[]>; discovered: readonly DiscoveredMcp[]; enabled?: readonly string[] }`): Discovered servers, the enabled-name allowlist and per-server UI tool denylists to fold in.

**Returns** `McpServerConfig[]`

Source: [src/chat/mcps-discovery.ts:473](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-discovery.ts#L473)

### `buildPlanSystem`

Function.

Compose the Plan-mode system prompt. Subset of Build: identity, plan-mode
doctrine (instead of `DOING_TASKS_DOCTRINE`), read-only token discipline,
communication, and plan-mode interaction guidance. It drops the
conventions, actions-with-care, git and subagent fragments (no shell, no
spawn in plan mode). Same boundary layout as [`buildBuildSystem`](https://zidane.si/docs/reference/chat.md#buildBuildSystem).

```ts
function buildPlanSystem(opts?: BuildSystemOptions): string
```

**Parameters**

- `opts?` (`BuildSystemOptions`, default `{}`): Environment, override and interaction settings for the prompt.

**Returns** `string`

Source: [src/chat/agent-prompt.ts:454](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L454)

### `buildPluginContributions`

Function.

Map the user's enabled-plugin list onto the contributions a host merges
into the agent.

Conventions match `enabledSkills` / `enabledMcps`:

- `undefined` → every discovered plugin is enabled (default)
- `[]`        → all off
- `[names]`   → allowlist

A plugin that failed to load contributes nothing regardless of the
allowlist — `ok: false` means components were never discovered.

```ts
function buildPluginContributions(
  opts: { discovered: readonly DiscoveredPlugin[]; enabled?: readonly string[] },
): { mcpServers: McpServerConfig[]; skills: SkillConfig[] }
```

**Parameters**

- `opts` (`{ discovered: readonly DiscoveredPlugin[]; enabled?: readonly string[] }`): Discovered plugins and the enabled-name allowlist. See the conventions above.

**Returns** `{ mcpServers: McpServerConfig[]; skills: SkillConfig[] }`

Source: [src/chat/plugins-discovery.ts:224](https://github.com/Tahul/zidane/blob/main/src/chat/plugins-discovery.ts#L224)

### `buildRestartHint`

Function.

`↻ v7.0.31` — a newer build is on disk than the one running (this
process is stale; see `auto-update-restart.ts`). Takes precedence over
the `↑` chip: once the release is installed, "available" is old news and
"restart" is the one action left. Same color contract as `buildUpdateHint`.

```ts
function buildRestartHint(
  state: { installed: string | null; restartRequired: boolean } | null,
  options: BuildUpdateHintOptions,
): Hint | null
```

**Parameters**

- `state` (`{ installed: string | null; restartRequired: boolean } | null`): Installed-version state, or `null` when unknown.
- `options` (`BuildUpdateHintOptions`): Color and label settings for the hint.

**Returns** `Hint | null`

Source: [src/chat/auto-update-hook.ts:258](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-hook.ts#L258)

### `buildResumedToolResultsTurn`

Function.

Build a protocol-valid user `SessionTurn` that closes a batch of
resumed pending interactions.

- `requests` MUST be the same list returned by
  [`pendingInteractionsFromTurns`](https://zidane.si/docs/reference/chat.md#pendingInteractionsFromTurns) for the session, in order.
- `responses` MUST contain an entry for every request id; throws
  otherwise (caller bug — flush before all answers collected).

Pure: reads no store. Tested in isolation so the batching invariant doesn't
regress without the resumed-flow integration noticing.

```ts
function buildResumedToolResultsTurn(
  requests: readonly InteractionRequest[],
  responses: ReadonlyMap<string, InteractionResponse>,
  options: { createdAt?: number; runId?: string; turnId: string },
): SessionTurn
```

**Parameters**

- `requests` (`readonly InteractionRequest[]`): Pending requests, in the order [`pendingInteractionsFromTurns`](https://zidane.si/docs/reference/chat.md#pendingInteractionsFromTurns) returned them.
- `responses` (`ReadonlyMap<string, InteractionResponse>`): Responses keyed by request id. Must cover every request.
- `options` (`{ createdAt?: number; runId?: string; turnId: string }`): Turn id for the new turn, plus an optional run id and creation time.

**Returns** `SessionTurn`: A user turn with one `tool_result` per request.

Source: [src/chat/interactions-protocol.ts:545](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L545)

### `buildRunModel`

Function.

Assemble the `agent.run` `model` option from a chain.

- Single-entry chain → the bare model id string (what a single-model run
  passes).
- Multi-entry chain → pinned `{ provider, model }` targets, where
  `provider` is the runtime `Provider.name` resolved via
  `providerNameFor` (chain entries carry chat-level `ProviderKey`s,
  which need not equal the instance names the agent loop matches on).

Throws only on an empty chain — a programmer error, since chains
always carry a primary. Stale or unresolvable entries degrade
defensively instead (never throw, never produce an entry the agent
would reject):

- Entries whose key has no resolvable name (provider not constructed
  into the current agent) are dropped.
- If the PRIMARY itself doesn't resolve, or fewer than two entries
  survive, fall back to the bare primary model string.

`contextWindowFor` (optional) stamps each target with its model's
context window from the host catalog, so the loop re-bases the
auto-compact trigger when a fallback advances onto it (the host's
explicit window is sized for the PRIMARY otherwise).

```ts
function buildRunModel(
  chain: readonly ModelChainEntry[],
  providerNameFor: (key: string) => string | undefined,
  contextWindowFor?: (entry: ModelChainEntry) => number | undefined,
): string | { contextWindow?: number; model: string; provider: string }[]
```

**Parameters**

- `chain` (`readonly ModelChainEntry[]`): Ordered picks. Entry 0 is the primary.
- `providerNameFor` (`(key: string) => string | undefined`): Maps a provider key to the registered provider name, or `undefined` when none is registered.
- `contextWindowFor?` (`(entry: ModelChainEntry) => number | undefined`): Optional lookup of an entry's context window. Non-positive or missing values are omitted.

**Returns** `string | { contextWindow?: number; model: string; provider: string }[]`

Source: [src/chat/model-chain.ts:242](https://github.com/Tahul/zidane/blob/main/src/chat/model-chain.ts#L242)

### `buildSkillsConfig`

Function.

Map a user-toggled enable list onto the format `createAgent` expects.

Conventions:

- `enabled === undefined`  → all discovered skills enabled (default).
- `enabled === []`         → fully off; the agent will not scan or
  inject the skills tools.
- `enabled === [names]`    → allowlist; skills not in the list are
  left out of the catalog.

`scan` is the resolved scan-path list (passed through verbatim). Pass
`defaultSkillScanPaths()` for the standard project + user paths, or
supply a host-specific list.

```ts
function buildSkillsConfig(
  opts: { enabled?: readonly string[]; scan: SourcedScanPath[] },
): SkillsConfig
```

**Parameters**

- `opts` (`{ enabled?: readonly string[]; scan: SourcedScanPath[] }`): The resolved scan paths and the enabled-name allowlist. See the conventions above.

**Returns** `SkillsConfig`

Source: [src/chat/skills-discovery.ts:137](https://github.com/Tahul/zidane/blob/main/src/chat/skills-discovery.ts#L137)

### `buildSpawnEndEvent`

Function.

`spawn-end` marker. The facts ride on `spawn.end` (status, elapsed
wall-clock time, USD cost summed across the child's whole turn tree, token
totals); renderers read them through `describeSpawnEnd`. Most providers
don't populate `cost`, so it is usually absent: no "$0.00" lies. `timeout`
maps to `done`, matching the TUI.

`text` keeps its stored shape for clients that predate `spawn.end`; see
`spawnEndText`.

```ts
function buildSpawnEndEvent(
  ctx: { depth?: number; id: string; parentChildId?: string; stats: AgentStats; status?: 'error' | 'completed' | 'aborted' | 'timeout' },
  started: SpawnStartRecord | undefined,
  now?: number,
): StreamEvent
```

**Parameters**

- `ctx` (`{ depth?: number; id: string; parentChildId?: string; stats: AgentStats; status?: 'error' | 'completed' | 'aborted' | 'timeout' }`): The finished child: id, depth, status, stats and parent child id.
- `started` (`SpawnStartRecord | undefined`): Record saved when the child started. Without it the event has no elapsed time or task.
- `now?` (`number`): Epoch milliseconds for the end of the run.

**Returns** `StreamEvent`

Source: [src/chat/spawn-markers.ts:99](https://github.com/Tahul/zidane/blob/main/src/chat/spawn-markers.ts#L99)

### `buildSpawnStartEvent`

Function.

`spawn-start` marker for a child that just launched. `previewLine`
collapses inline newlines/tabs so the marker stays on one visual row.
Resumed children carry the label of the run they continue in
`spawn.resumes`, as the reload path does.

```ts
function buildSpawnStartEvent(
  ctx: { callId?: string; depth?: number; id: string; model?: string; parentChildId?: string; resumesChildId?: string; task: string },
  startedAt: number,
): StreamEvent
```

**Parameters**

- `ctx` (`{ callId?: string; depth?: number; id: string; model?: string; parentChildId?: string; resumesChildId?: string; task: string }`): The new child: id, task and optional call id, depth, model and resume or parent child ids.
- `startedAt` (`number`): Epoch milliseconds when the child started.

**Returns** `StreamEvent`

Source: [src/chat/spawn-markers.ts:50](https://github.com/Tahul/zidane/blob/main/src/chat/spawn-markers.ts#L50)

### `buildToolToggle`

Function.

Pure factory shared by both hook flavors. Captures `persistedDenyAll`

- `setDisabledMcpTools` at the call site so toggle handlers stay
  referentially stable across renders that don't touch the underlying
  settings entry — important for `useKeyboard` to avoid re-binding on
  every catalog change.

Exported for direct use by non-React consumers (CLI, GUI shells)
that want the same write semantics without the hook plumbing.

```ts
function buildToolToggle(
  args: { catalogNames: readonly string[]; persistedDenyAll: Record<string, readonly string[]> | undefined; serverName: string; setDisabledMcpTools: (next: Record<string, readonly string[]> | undefined) => void },
): McpToolToggleSet
```

**Parameters**

- `args` (`{ catalogNames: readonly string[]; persistedDenyAll: Record<string, readonly string[]> | undefined; serverName: string; setDisabledMcpTools: (next: Record<string, readonly string[]> | undefined) => void }`): Server name, the server's catalog tool names, the persisted deny-list for all servers and the setter that writes it.

**Returns** `McpToolToggleSet`

Source: [src/chat/mcp-tool-toggle.ts:139](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tool-toggle.ts#L139)

### `buildUpdateHint`

Function.

Builds the `↑ vX.Y.Z` hint for an available update.
Returns `null` when there is no newer version.

```ts
function buildUpdateHint(
  status: UpdateStatus | null,
  options: BuildUpdateHintOptions,
): Hint | null
```

**Parameters**

- `status` (`UpdateStatus | null`): Result of an update check, or `null` before the first check.
- `options` (`BuildUpdateHintOptions`): Color and label settings for the hint.

**Returns** `Hint | null`

Source: [src/chat/auto-update-hook.ts:235](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-hook.ts#L235)

### `buildVisibleMcpRows`

Function.

Flatten a server catalog plus the per-server expanded set into the
visible-row list a renderer cycles through.

- `catalog`         — filtered / ordered server list (post-search,
  post-allow-list).
- `expanded`        — names of servers whose tool list is open.
  Servers not in this set render as a single
  server-header row.
- `toolsByServer`   — per-server full tool catalog (from
  `chat/mcp-tools-cache.ts`). Missing key →
  `'tool-empty'` placeholder under the
  expanded server.

```ts
function buildVisibleMcpRows(
  catalog: readonly DiscoveredMcp[],
  expanded: ReadonlySet<string>,
  toolsByServer: Record<string, readonly McpToolSchema[]>,
): VisibleMcpRow[]
```

**Parameters**

- `catalog` (`readonly DiscoveredMcp[]`): Discovered servers in display order.
- `expanded` (`ReadonlySet<string>`): Names of servers whose tool rows are shown.
- `toolsByServer` (`Record<string, readonly McpToolSchema[]>`): Cached tool lists keyed by server name. A missing or empty list yields a `tool-empty` row.

**Returns** `VisibleMcpRow[]`

Source: [src/chat/mcp-rows.ts:61](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-rows.ts#L61)

### `builtinEnvironmentProviders`

Function.

Creates the built-in environment providers: local, worktree, Docker, E2B and Daytona.

```ts
function builtinEnvironmentProviders(
  options: BuiltinEnvironmentOptions,
): EnvironmentProvider[]
```

**Parameters**

- `options` (`BuiltinEnvironmentOptions`): Options shared by the providers that store data on disk.

**Returns** `EnvironmentProvider[]`

**Example**

```ts
import { builtinEnvironmentProviders, createEnvironmentRegistry } from 'zidane/chat'

const environments = createEnvironmentRegistry(
  builtinEnvironmentProviders({ dataDir: '/home/me/.zidane' }),
)
console.log(environments.get('worktree')?.label)
```

Source: [src/chat/environments/index.ts:32](https://github.com/Tahul/zidane/blob/main/src/chat/environments/index.ts#L32)

### `builtInPromptOf`

Function.

The built-in prompt a profile runs, `undefined` for a profile with its own.
Decided by the preset's prompt, never the profile's id: a preset that uses
Build's or Plan's built-in prompt (`BUILD_AGENT.preset.system`) gets it
composed with the session's environment, AGENTS.md and SYSTEM.md; a host
profile that happens to be called `build` keeps its own prompt.

```ts
function builtInPromptOf(profile: AgentProfile): BuiltInPrompt | undefined
```

**Parameters**

- `profile` (`AgentProfile`): The profile.

**Returns** `BuiltInPrompt | undefined`

Source: [src/chat/agents.ts:246](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L246)

### `cacheBreakNoticeText`

Function.

Transcript-row text for a persisted cache-break notice. Multi-line, one
fact per line, so both hosts can show a short headline and fold or list
the rest:

cache break ↑116.9k tokens \~$0.48
messagePrefix: message 16 of 41→43 rewritten
likely compaction or history trimming

The footer chip stays terse; the row carries the culprit so a break is
diagnosable from the transcript. Only the headline on a TTL / eviction
break (nothing was done wrong).

```ts
function cacheBreakNoticeText(info: CacheBreakInfo & { costUsd?: number }): string
```

**Parameters**

- `info` (`CacheBreakInfo & { costUsd?: number }`): Cache break report, with an optional estimated cost in USD.

**Returns** `string`

Source: [src/chat/footer-chips.ts:91](https://github.com/Tahul/zidane/blob/main/src/chat/footer-chips.ts#L91)

### `cachedRepoKeyOf`

Function.

[`repoKeyOf`](https://zidane.si/docs/reference/chat.md#repoKeyOf), reused for a few minutes per folder. Session lists ask
it for every row, and most rows share a few folders.

```ts
function cachedRepoKeyOf(dir: string, now?: number): string | null
```

**Parameters**

- `dir` (`string`): A folder inside the checkout.
- `now?` (`number`): The current time, epoch ms.

**Returns** `string | null`

Source: [src/chat/project-root.ts:227](https://github.com/Tahul/zidane/blob/main/src/chat/project-root.ts#L227)

### `changedCatalogItems`

Function.

Every item of a catalog that extensions changed, for a Settings list.

```ts
function changedCatalogItems(
  runtime: ExtensionRuntimeOutputs | undefined,
  kind: CatalogKind,
): readonly CatalogItemProvenance[]
```

**Parameters**

- `runtime` (`ExtensionRuntimeOutputs | undefined`): The extension runtime outputs (`ExtensionRegistry.runtime`).
- `kind` (`CatalogKind`): The catalog.

**Returns** `readonly CatalogItemProvenance[]`

Source: [src/chat/agents-catalog.ts:279](https://github.com/Tahul/zidane/blob/main/src/chat/agents-catalog.ts#L279)

### `chatAutoCompactBehavior`

Function.

Build the per-run `behavior.autoCompact` a chat host passes to `agent.run`.

Loop-native auto-compaction is the only mechanism; the TUI + GUI just
translate the user's enable/threshold settings plus the picked model's raw
context window (the SDK core has no model catalog) into the config the loop
consumes. Returns `false` when disabled. `rawContextWindow: null` (unknown
model) still enables it but leaves the trigger inert — the loop skips with
`unknown-window` rather than guessing.

```ts
function chatAutoCompactBehavior(
  opts: { enabled: boolean; rawContextWindow: number | null; summarizer?: { model: string; provider: Provider }; threshold: number },
): AutoCompact
```

**Parameters**

- `opts` (`{ enabled: boolean; rawContextWindow: number | null; summarizer?: { model: string; provider: Provider }; threshold: number }`): Whether auto-compaction is on, the trigger threshold, the main model's raw context window and an optional summarizer override.

**Returns** `AutoCompact`

Source: [src/chat/auto-compact.ts:33](https://github.com/Tahul/zidane/blob/main/src/chat/auto-compact.ts#L33)

### `chatReferenceTokensForPrompt`

Function.

Extracts the `#<chat>` reference tokens from a prompt.

```ts
function chatReferenceTokensForPrompt(prompt: string): string[]
```

**Parameters**

- `prompt` (`string`): User prompt text.

**Returns** `string[]`

Source: [src/tools/read-chat-turns.ts:107](https://github.com/Tahul/zidane/blob/main/src/tools/read-chat-turns.ts#L107)

### `chatWindowBehavior`

Function.

The per-run window knobs a chat host spreads into `agent.run`'s `behavior`:
the auto-compaction config from [`chatAutoCompactBehavior`](https://zidane.si/docs/reference/chat.md#chatAutoCompactBehavior) PLUS the
picked model's raw window as `behavior.contextWindow`, so window-relative
wire budgets (`compactThresholdWindowFraction`) see the window even when the
user has auto-compaction switched off. Both hosts go through this so they
cannot drift: the TUI and the GUI/daemon engine hand the loop the same
shape for the same settings.

```ts
function chatWindowBehavior(
  opts: { enabled: boolean; rawContextWindow: number | null; summarizer?: { model: string; provider: Provider }; threshold: number },
): { autoCompact: AutoCompact; contextWindow?: number }
```

**Parameters**

- `opts` (`{ enabled: boolean; rawContextWindow: number | null; summarizer?: { model: string; provider: Provider }; threshold: number }`): Same options as [`chatAutoCompactBehavior`](https://zidane.si/docs/reference/chat.md#chatAutoCompactBehavior).

**Returns** `{ autoCompact: AutoCompact; contextWindow?: number }`

Source: [src/chat/auto-compact.ts:76](https://github.com/Tahul/zidane/blob/main/src/chat/auto-compact.ts#L76)

### `checkForUpdate`

Function.

Resolve the latest available version of `packageName`, honoring an
on-disk TTL cache so the warm path costs zero network. Never throws —
registry failures degrade to `{ source: 'error', latest: null }`.

Designed for boot-path usage: short timeout, swallows errors, opt-outs
for CI / restricted shells. Consumers driving the explicit `upgrade`
subcommand should set `force: true` to ignore env opt-outs.

```ts
function checkForUpdate(options: CheckForUpdateOptions): Promise<UpdateStatus>
```

**Parameters**

- `options` (`CheckForUpdateOptions`): Package, current version and cache, network and opt-out settings for the check.

**Returns** `Promise<UpdateStatus>`

Source: [src/chat/auto-update.ts:367](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L367)

### `checkPlanInvariants`

Function.

Every rule a bag must satisfy after any sequence of events. Returns the
broken rules (empty when sound). The gate's tests run random event
sequences through [`applyPlanEvent`](https://zidane.si/docs/reference/chat-pure.md#applyPlanEvent) and assert this stays empty.

```ts
function checkPlanInvariants(bag: PlansBag): string[]
```

**Parameters**

- `bag` (`PlansBag`): The bag to check.

**Returns** `string[]`

Source: [src/chat/plans.ts:987](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L987)

### `checkTarballAvailable`

Function.

Whether `<packageName>@<version>` can actually be DOWNLOADED right now, not
merely resolved. Right after a publish the registry serves the new
manifest minutes before the tarball reaches the CDN; a package manager
that hits that window on an `optionalDependencies` platform binary
silently skips it and leaves a bin with nothing behind it. `upgrade` asks
this before spawning the PM so it can wait instead of breaking the install.

- `'ok'`       tarball responds 2xx
- `'missing'`  manifest or tarball 404 (published, not propagated)
- `'unknown'`  network / registry error; the caller decides

```ts
function checkTarballAvailable(
  opts: TarballAvailabilityOptions,
): Promise<'ok' | 'unknown' | 'missing'>
```

**Parameters**

- `opts` (`TarballAvailabilityOptions`): Package, version and registry settings for the availability probe.

**Returns** `Promise<'ok' | 'unknown' | 'missing'>`

Source: [src/chat/auto-update.ts:710](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L710)

### `claudeImportHostNote`

Function.

What to tell the user when the window's sessions live on a remote host:
the import went to this computer, so the host's list won't show it.
`null` for a host on this machine (it shares the store).

```ts
function claudeImportHostNote(
  activeHost: ClaudeImportActiveHost | null | undefined,
): string | null
```

**Parameters**

- `activeHost` (`ClaudeImportActiveHost | null | undefined`): The host the window uses, or `null` or `undefined` when unknown.

**Returns** `string | null`

Source: [src/chat/claude-import.ts:865](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L865)

### `cleanTitle`

Function.

Normalize a model-generated title into the shape we want to persist:

- Collapse internal whitespace + take the first line only (some
  models emit "Title: foo\n\nReason: …" despite instructions).
- Strip surrounding quotes, a leading `Title:` / `title -` preamble,
  and a `user:` / `assistant:` role prefix echoed from the transcript
  — repeatedly, in any nesting order.
- Reject placeholders and answer-shaped replies (see
  `ANSWER_SHAPED_OPENER`) by throwing.
- Strip trailing punctuation (`.`, `!`, `?`) — titles read cleaner
  without it.
- Truncate to `TITLE_MAX_CHARS` with a trailing `…` when over.

Exported as `cleanTitle` so tests can pin the normalization rules
without going through a mock provider.

```ts
function cleanTitle(raw: string): string
```

**Parameters**

- `raw` (`string`): Raw model output.

**Returns** `string`: The cleaned title.

Source: [src/chat/generate-title.ts:337](https://github.com/Tahul/zidane/blob/main/src/chat/generate-title.ts#L337)

### `clearExtensionSourceCache`

Function.

Forgets every cached listing.

```ts
function clearExtensionSourceCache(): void
```

Source: [src/chat/extension-browse.ts:76](https://github.com/Tahul/zidane/blob/main/src/chat/extension-browse.ts#L76)

### `clearFailedUpdate`

Function.

Forgets the failed install, so the next check may try again. Called by every
manual install.

```ts
function clearFailedUpdate(cacheDir: string): void
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the record.

Source: [src/chat/auto-update-auto-install.ts:122](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L122)

### `clearMcpToolsCache`

Function.

Drop entries for one or more servers, or wipe the cache when no
server is named. Idempotent — entries that aren't present are
silently skipped. Returns the resulting cache so callers can refresh
any in-memory mirror without re-reading from disk.

Meant for a "rescan server" affordance in the settings UI
(clear → trigger a real bootstrap → cache repopulates on the
`mcp:tools:list` hook).

```ts
function clearMcpToolsCache(
  opts: { dataDir: string; serverNames?: readonly string[] },
): McpToolsCache
```

**Parameters**

- `opts` (`{ dataDir: string; serverNames?: readonly string[] }`): The data directory and optional server names to drop. Omit `serverNames` to clear every entry.

**Returns** `McpToolsCache`

Source: [src/chat/mcp-tools-cache.ts:143](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tools-cache.ts#L143)

### `clearProviderUsageCache`

Function.

Drop every cached snapshot. Entries are keyed by credential KEY, not by the
token itself, so signing out and back in (or re-authing to another account)
would otherwise keep serving the previous account's numbers for the rest of
the TTL. The TUI calls this when it re-reads credentials.

```ts
function clearProviderUsageCache(): void
```

Source: [src/chat/provider-usage.ts:146](https://github.com/Tahul/zidane/blob/main/src/chat/provider-usage.ts#L146)

### `clipHintsToWidth`

Function.

Return the longest prefix of `hints` whose rendered width fits within
`budget`. Used to degrade hint rows gracefully at narrow terminal
widths instead of letting an absolutely-positioned hint row wrap
mid-segment (which paints the overflow over surrounding borders and
looks like garbled glyphs in a TUI).

Prefix-only (no reordering, no last-hint priority) so the survivors
keep their authored order — the user's muscle memory for "leftmost
hint = primary action" stays intact as the terminal shrinks.

```ts
function clipHintsToWidth(hints: readonly Hint[], budget: number): readonly Hint[]
```

**Parameters**

- `hints` (`readonly Hint[]`): Hints in display order.
- `budget` (`number`): Available width in characters.

**Returns** `readonly Hint[]`

Source: [src/chat/hints.ts:165](https://github.com/Tahul/zidane/blob/main/src/chat/hints.ts#L165)

### `cloneRepository`

Function.

Clones `input` (a git or forge URL, or `owner/repo`) into a new folder of
`parent`, calling `onProgress` as git reports. A folder that already holds
the same repository is opened, not cloned (`existed`).

```ts
function cloneRepository(
  input: string,
  parent: string,
  onProgress: (progress: RepoProgress) => void,
): CloneRun
```

**Parameters**

- `input` (`string`): A git or forge URL, or `owner/repo`.
- `parent` (`string`): The folder the clone lands in.
- `onProgress` (`(progress: RepoProgress) => void`): Called with git's progress.

**Returns** `CloneRun`

Source: [src/chat/git/clone.ts:200](https://github.com/Tahul/zidane/blob/main/src/chat/git/clone.ts#L200)

### `commandSteps`

Function.

Normalize a command to its ordered step list (`steps` wins over `run`).

```ts
function commandSteps(command: Command): readonly CommandStep[]
```

**Parameters**

- `command` (`Command`): The command to normalize.

**Returns** `readonly CommandStep[]`

Source: [src/chat/commands.ts:433](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L433)

### `commitPlanEvent`

Function.

Applies `event` to a live session's plans and persists the key before
returning. A refused or no-op event writes nothing. Without a store (a
memory-only session) the change stays in memory.

```ts
function commitPlanEvent(
  session: PlanSession,
  event: PlanEvent,
): Promise<ApplyPlanResult>
```

**Parameters**

- `session` (`PlanSession`): The live session.
- `event` (`PlanEvent`): The change.

**Returns** `Promise<ApplyPlanResult>`

Source: [src/chat/plans-session.ts:66](https://github.com/Tahul/zidane/blob/main/src/chat/plans-session.ts#L66)

### `commitPlanEventToStore`

Function.

Applies `event` to stored session data and writes only the plans key. For
the host when no agent is live; call it under the session's run lease so
no run writes the key at the same time.

```ts
function commitPlanEventToStore(
  store: SessionStore,
  data: SessionData,
  event: PlanEvent,
): Promise<ApplyPlanResult>
```

**Parameters**

- `store` (`SessionStore`): The session store.
- `data` (`SessionData`): The loaded session data; its metadata is updated in place.
- `event` (`PlanEvent`): The change.

**Returns** `Promise<ApplyPlanResult>`

Source: [src/chat/plans-session.ts:85](https://github.com/Tahul/zidane/blob/main/src/chat/plans-session.ts#L85)

### `compactSummaryEvent`

Function.

Map a `compact-summary` content block to its `compact-summary` StreamEvent —
the single source of truth for the compaction boundary card. Used by
[`eventsFromTurns`](https://zidane.si/docs/reference/chat.md#eventsFromTurns) (reload / TUI live repaint) AND by chat hosts that
surface the boundary live without a full reload (the GUI broadcasts this
from the loop's `compact:end` hook), so the live card and the reloaded card
are byte-identical regardless of who created the marker (manual compaction or
loop-native auto-compaction — both share this block shape).

```ts
function compactSummaryEvent(
  block: { compactedAt: number; model: string; replacesTurnIds: readonly string[]; summary: string; type: 'compact-summary'; usage: TurnUsage },
  turnId?: string,
): StreamEvent
```

**Parameters**

- `block` (`{ compactedAt: number; model: string; replacesTurnIds: readonly string[]; summary: string; type: 'compact-summary'; usage: TurnUsage }`): The `compact-summary` content block.
- `turnId?` (`string`): Id of the turn holding the block, attached to the event when set.

**Returns** `StreamEvent`

Source: [src/chat/store.ts:500](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L500)

### `compareSemver`

Function.

Returns `-1 / 0 / 1`. Unparseable inputs sort as `0` (assume equal).

```ts
function compareSemver(a: string, b: string): -1 | 0 | 1
```

**Parameters**

- `a` (`string`): First version string.
- `b` (`string`): Second version string.

**Returns** `-1 | 0 | 1`

Source: [src/chat/auto-update.ts:217](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L217)

### `compositionByKey`

Function.

The composition a key names in `registry`, `undefined` when none of its
profiles is registered any more.

```ts
function compositionByKey(
  registry: AgentRegistry,
  key: string,
): AgentComposition | undefined
```

**Parameters**

- `registry` (`AgentRegistry`): The profiles available to the session.
- `key` (`string`): A composition key (`family:<name>` or `profile:<id>`).

**Returns** `AgentComposition | undefined`

Source: [src/chat/agents.ts:138](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L138)

### `compositionFits`

Function.

Whether `profile` runs on `composition` without a new agent: it is the
profile's own composition, or the profile's tools are a strict subset of
the composition's (same names and specs). Codemode scripts reach the same
tools, so a composition's tools count whether they are on the wire or not.

```ts
function compositionFits(
  registry: AgentRegistry,
  composition: AgentComposition,
  profile: AgentProfile,
): boolean
```

**Parameters**

- `registry` (`AgentRegistry`): The profiles available to the session.
- `composition` (`AgentComposition`): The composition the session's agent is built with.
- `profile` (`AgentProfile`): The profile the session switches to.

**Returns** `boolean`

Source: [src/chat/agents.ts:197](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L197)

### `compositionTools`

Function.

The profile tools a composition's agent can call, by name: its base's,
then each other member's that the base doesn't have.

```ts
function compositionTools(composition: AgentComposition): ReadonlyMap<string, ToolDef>
```

**Parameters**

- `composition` (`AgentComposition`): The composition.

**Returns** `ReadonlyMap<string, ToolDef>`

Source: [src/chat/agents.ts:172](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L172)

### `computeTurnAnchors`

Function.

Per-item anchor ids for auto-scroll. Walks `items` in render order
and, for each event, returns either:

- `'turn-anchor-<turnId>'` — the first event of this turn (the
  scroll target).
- `undefined` — later event of an already-tagged turn (or a
  synthetic event with no `turnId`).

`ids[i]` is a tuple per item: length 1 for plain events, length N
for subagent runs (one entry per inner event). `idByTurn` is the
inverse lookup used by the scroll effect. `lastTurnId` is the
most-recently-rendered turn — the scroll effect special-cases it to
snap to bottom rather than scroll the anchor into view (which would
stop short of the actual tail).

Exported so the anchor-tagging matrix can be unit-tested without
rendering anything.

```ts
function computeTurnAnchors(
  items: readonly TranscriptItem[],
): { idByTurn: ReadonlyMap<string, string>; ids: readonly readonly (string | undefined)[][]; lastTurnId: string | undefined }
```

**Parameters**

- `items` (`readonly TranscriptItem[]`): Transcript items in render order.

**Returns** `{ idByTurn: ReadonlyMap<string, string>; ids: readonly readonly (string | undefined)[][]; lastTurnId: string | undefined }`

Source: [src/chat/transcript-anchors.ts:63](https://github.com/Tahul/zidane/blob/main/src/chat/transcript-anchors.ts#L63)

### `ConfigProvider`

Function.

Provides a [`ResolvedConfig`](https://zidane.si/docs/reference/chat.md#ResolvedConfig) to the components below it.

```ts
function ConfigProvider(props: { children: ReactNode; config: ResolvedConfig }): Element
```

**Parameters**

- `props` (`{ children: ReactNode; config: ResolvedConfig }`): Props with the resolved `config` and the `children` to render.

**Returns** `Element`

Source: [src/chat/config-context.tsx:20](https://github.com/Tahul/zidane/blob/main/src/chat/config-context.tsx#L20)

### `configuredProviderFields`

Function.

The values of a provider's non-secret custom fields set now: the stored
credential's, else the environment's. `undefined` when it takes none.

```ts
function configuredProviderFields(
  dataDir: string,
  descriptor: ProviderDescriptor,
): Record<string, string> | undefined
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`.
- `descriptor` (`ProviderDescriptor`): The provider.

**Returns** `Record<string, string> | undefined`

Source: [src/chat/credentials.ts:1037](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L1037)

### `configureProviderCredential`

Function.

Stores a provider's API key and custom fields (a local server's URL, a
cloud's region), merged into what is stored: a field left out keeps its
value, an empty one is removed (a secret one is kept, it is never shown to
be edited), a required one may not be emptied. No `value` keeps the
stored key.

```ts
function configureProviderCredential(
  dataDir: string,
  descriptor: ProviderDescriptor,
  input: { customFields?: Readonly<Record<string, string>>; value?: string },
): void
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider.
- `input` (`{ customFields?: Readonly<Record<string, string>>; value?: string }`): The key and the fields.

Source: [src/chat/credentials.ts:1065](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L1065)

### `confineToWorkspace`

Function.

`rel` inside `cwd` (both resolved, the final symlink followed): its real path, or `null` when it leaves the folder.

```ts
function confineToWorkspace(cwd: string, rel: string): Promise<string | null>
```

**Parameters**

- `cwd` (`string`): The folder.
- `rel` (`string`): A path relative to it.

**Returns** `Promise<string | null>`

Source: [src/chat/workspace/files.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L35)

### `connectorGithubToken`

Function.

A GitHub token zidane already knows (`GH_TOKEN`, `GITHUB_TOKEN` or the
`gh` CLI), for the GitHub connector's "Use my gh login". `null` when there
is none.

```ts
function connectorGithubToken(): Promise<{ source: string; token: string } | null>
```

**Returns** `Promise<{ source: string; token: string } | null>`

Source: [src/chat/host/onboarding/detect.ts:192](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/detect.ts#L192)

### `containedEntryPath`

Function.

`path` (relative to `cwd`) as an absolute path inside it: refused (throws)
when it leaves the folder lexically or its parent folder real-paths out.

```ts
function containedEntryPath(cwd: string, path: string): Promise<string>
```

**Parameters**

- `cwd` (`string`): The folder (absolute).
- `path` (`string`): A path relative to it.

**Returns** `Promise<string>`

Source: [src/chat/workspace/local-entries.ts:44](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/local-entries.ts#L44)

### `countClaudeSessionFiles`

Function.

Detection-preview counts — scoped and all-projects figures from ONE
directory walk (the naive two-listing approach reads every project dir
twice). The scoped classification (with its per-file cwd peek for
candidate dirs) runs at most once per file via the lazy `scoped()`
thunk.

```ts
function countClaudeSessionFiles(
  opts?: ClaudeImportOptions,
): { all: number; project: number }
```

**Parameters**

- `opts?` (`ClaudeImportOptions`, default `{}`): Discovery options. Only `cwd`, `home` and the project scope are used.

**Returns** `{ all: number; project: number }`

Source: [src/chat/claude-import.ts:128](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L128)

### `createApprovalHub`

Function.

Creates an approval hub that broadcasts requests and persists them until they are resolved.

```ts
function createApprovalHub(
  options: { broadcast: (signal: HostSignal) => void; persistence?: PendingApprovalsStore; userDataDir: () => string },
): ApprovalHub
```

**Parameters**

- `options` (`{ broadcast: (signal: HostSignal) => void; persistence?: PendingApprovalsStore; userDataDir: () => string }`): Signal broadcast (`broadcast`), the data directory holding the safelist (`userDataDir`), and an optional durable store (`persistence`, default in memory).

**Returns** `ApprovalHub`

Source: [src/chat/host/approvals.ts:97](https://github.com/Tahul/zidane/blob/main/src/chat/host/approvals.ts#L97)

### `createArtifactKindsHost`

Function.

Creates the kind registry of one registry build.

```ts
function createArtifactKindsHost(): ArtifactKindsHost
```

**Returns** `ArtifactKindsHost`

**Example**

```ts
const kinds = createArtifactKindsHost()
const runtime = { ports: { host: { artifacts: { current: bridge.current, registerKind: kinds.registerKind } } } }
const registry = await discoverAndBuildExtensionRegistry({ runtime })
const release = kinds.adopt(registry)
```

Source: [src/chat/host/extension-artifact-kinds.ts:36](https://github.com/Tahul/zidane/blob/main/src/chat/host/extension-artifact-kinds.ts#L36)

### `createArtifactsCompletionProvider`

Function.

Creates a completion provider for `#` artifact references.
The provider only suggests. It inserts the artifact's locator as plain text, so it parses no references back.

```ts
function createArtifactsCompletionProvider(
  opts: { ensureCatalog?: () => Promise<readonly ArtifactReferenceEntry[]>; getCatalog: () => readonly ArtifactReferenceEntry[]; limit?: number },
): CompletionProvider<ArtifactReferenceData>
```

**Parameters**

- `opts` (`{ ensureCatalog?: () => Promise<readonly ArtifactReferenceEntry[]>; getCatalog: () => readonly ArtifactReferenceEntry[]; limit?: number }`): Catalog getter, an optional async catalog loader and a result limit (default 30).

**Returns** `CompletionProvider<ArtifactReferenceData>`

Source: [src/chat/completion-artifacts.ts:132](https://github.com/Tahul/zidane/blob/main/src/chat/completion-artifacts.ts#L132)

### `createAutomaticInteractionResponse`

Function.

Build the deterministic response used when the host skips interaction UI.

```ts
function createAutomaticInteractionResponse(
  request: InteractionRequest,
): InteractionResponse
```

**Parameters**

- `request` (`InteractionRequest`): Request to answer. A plan is approved, a `confirm` question gets `true`, a `select` question gets its first choice, and free text gets a placeholder.

**Returns** `InteractionResponse`: A response marked `automatic: true`.

Source: [src/chat/interactions-protocol.ts:402](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L402)

### `createCapabilityProbe`

Function.

Builds the `host.capabilities` probe: call it for the cached result (a minute old at most), or with `refresh` to probe again.

```ts
function createCapabilityProbe(
  input: CapabilityProbeInput,
): (refresh?: boolean) => Promise<HostCapabilities>
```

**Parameters**

- `input` (`CapabilityProbeInput`): Where the environment kinds, workspace roots and allow flags come from, plus optional `env`, clock and version-timeout overrides.

**Returns** `(refresh?: boolean) => Promise<HostCapabilities>`

Source: [src/chat/capabilities-probe.ts:73](https://github.com/Tahul/zidane/blob/main/src/chat/capabilities-probe.ts#L73)

### `createCatalogPorts`

Function.

Builds the base port of every extension catalog from zidane's sources. The
extension runtime passes each to its catalog factory, then links models to providers
with [`linkCatalogs`](https://zidane.si/docs/reference/chat.md#linkCatalogs).

```ts
function createCatalogPorts(options?: CatalogPortsOptions): CatalogPorts
```

**Parameters**

- `options?` (`CatalogPortsOptions`, default `{}`): The sources; see `CatalogPortsOptions`.

**Returns** `CatalogPorts`

Source: [src/chat/host/agent-build/catalog-ports.ts:173](https://github.com/Tahul/zidane/blob/main/src/chat/host/agent-build/catalog-ports.ts#L173)

### `createChatHost`

Function.

The composition root: creates the shared host state (`context.ts`) and the
feature modules, wires them to each other, and assembles the `ChatHost`.
Features never import this module. Where a feature needs one built after
it, it gets one of the hoisted forwarders at the bottom of this function
(only ever called at runtime, after construction).

```ts
function createChatHost(env: ChatHostEnvironment): ChatHost
```

**Parameters**

- `env` (`ChatHostEnvironment`): What the host runs on: how to reach clients, the session store, settings and runtime configuration.

**Returns** `ChatHost`: The assembled [`ChatHost`](https://zidane.si/docs/reference/chat.md#ChatHost).

**Example**

```ts
import { createMemoryStore } from 'zidane'
import { createChatHost, DEFAULT_SETTINGS, resolveConfig } from 'zidane/chat'

const store = createMemoryStore()
const config = resolveConfig({ store })
const host = createChatHost({
  broadcast: signal => console.log(signal.kind),
  store,
  readSettings: () => DEFAULT_SETTINGS,
  runtimeConfig: async () => config,
  userDataDir: () => '/tmp/zidane-data',
})

console.log(host.isSessionRunning('s1'))
```

Source: [src/chat/host/engine.ts:99](https://github.com/Tahul/zidane/blob/main/src/chat/host/engine.ts#L99)

### `createChatTurnsReadTool`

Function.

Creates the `read_chat_turns` tool, which reads turns of a previous chat the user referenced with `#<chat>`. Chats that were not referenced are refused.

```ts
function createChatTurnsReadTool(options: ChatTurnsReadToolOptions): ToolDef
```

**Parameters**

- `options` (`ChatTurnsReadToolOptions`): Session store and the allowed-token callback.

**Returns** `ToolDef`

Source: [src/tools/read-chat-turns.ts:31](https://github.com/Tahul/zidane/blob/main/src/tools/read-chat-turns.ts#L31)

### `createDiscoverySlot`

Function.

Creates a slot that loads a list lazily and keeps it fresh.
`ensure()` starts the first walk once and schedules throttled background refreshes. `refresh()` forces a walk. Every successful walk result goes to `onLoad`.

```ts
function createDiscoverySlot<T>(options: DiscoverySlotOptions<T>): DiscoverySlot<T>
```

**Type parameters**

- `T`

**Parameters**

- `options` (`DiscoverySlotOptions<T>`): The walk function, commit hook and optional throttle and error sink.

**Returns** `DiscoverySlot<T>`

Source: [src/chat/discovery-slot.ts:76](https://github.com/Tahul/zidane/blob/main/src/chat/discovery-slot.ts#L76)

### `createEnvironmentRegistry`

Function.

A registry of `providers`. Hosts pass the chat layer's built-ins
(`builtinEnvironmentProviders`, which include `local`) plus any extras; a
registry without `local` can still create local sessions (an absent
environment IS local) but can't resolve their execution.

```ts
function createEnvironmentRegistry(
  providers?: readonly EnvironmentProvider[],
): EnvironmentRegistry
```

**Parameters**

- `providers?` (`readonly EnvironmentProvider[]`, default `[]`): Providers to register. For a duplicate kind the first one wins.

**Returns** `EnvironmentRegistry`

Source: [src/chat/environment.ts:437](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L437)

### `createExtensionMcpHost`

Function.

Creates the MCP host behind `ctx.mcp`. Connections are pooled per `(cwd, server config)` and closed after `idleMs` without a call.

```ts
function createExtensionMcpHost(
  options: CreateExtensionMcpHostOptions,
): ExtensionMcpHost
```

**Parameters**

- `options` (`CreateExtensionMcpHostOptions`): Storage prefix, settings reader, and optional credential store, timeouts, and connect override.

**Returns** `ExtensionMcpHost`

Source: [src/chat/extension-mcp-host.ts:68](https://github.com/Tahul/zidane/blob/main/src/chat/extension-mcp-host.ts#L68)

### `createFileEnvironmentSecrets`

Function.

Creates [`EnvironmentSecrets`](https://zidane.si/docs/reference/chat.md#EnvironmentSecrets) backed by `environment-credentials.json` in `dataDir`.
Reads tolerate a missing or corrupt file. A write to a corrupt file moves it aside first. Setting a value to `null` removes it.

```ts
function createFileEnvironmentSecrets(dataDir: string): EnvironmentSecrets
```

**Parameters**

- `dataDir` (`string`): Data directory that holds the credentials file.

**Returns** `EnvironmentSecrets`

Source: [src/chat/environment-credentials.ts:77](https://github.com/Tahul/zidane/blob/main/src/chat/environment-credentials.ts#L77)

### `createFileMcpCredentialStore`

Function.

Build a file-backed `McpCredentialStore`. All per-server reads / writes
funnel through the single `mcp-credentials.json` file — this keeps
concurrent flows for different servers from clobbering each other since
each save reads-modifies-writes the full file atomically.

Two flows for the same server name CAN race (last write wins). MCP OAuth
flows are user-driven and serial in practice (one login modal at a time),
so this is acceptable; a per-server file split + cross-process lock would
be required to handle a multi-process scenario.

```ts
function createFileMcpCredentialStore(dataDir: string): McpCredentialStore
```

**Parameters**

- `dataDir` (`string`): Directory that holds `mcp-credentials.json`.

**Returns** `McpCredentialStore`

Source: [src/chat/mcp-credentials.ts:86](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-credentials.ts#L86)

### `createGitWatches`

Function.

Repository watchers keyed by folder, shared by counted owners.

```ts
function createGitWatches<Owner>(
  onChange: (cwd: string, change: GitChange) => void,
  start?: StartGitWatch,
): GitWatches<Owner>
```

**Type parameters**

- `Owner`

**Parameters**

- `onChange` (`(cwd: string, change: GitChange) => void`): Called once a watched folder's repository settles from a change.
- `start?` (`StartGitWatch`, default `watchGitRepository`): Starts one folder's watcher; default [`watchGitRepository`](https://zidane.si/docs/reference/chat.md#watchGitRepository), on this thread.

**Returns** `GitWatches<Owner>`

Source: [src/chat/git/watch.ts:135](https://github.com/Tahul/zidane/blob/main/src/chat/git/watch.ts#L135)

### `createHostRegistry`

Function.

Creates a host registry backed by `hosts.json` and the credentials store in `options.dataDir`.

```ts
function createHostRegistry(
  options: { dataDir: string; now?: () => number; probe: HostProbe },
): HostRegistry
```

**Parameters**

- `options` (`{ dataDir: string; now?: () => number; probe: HostProbe }`): Where to store hosts (`dataDir`), how to reach a host (`probe`), and an optional clock (`now`, default `Date.now`).

**Returns** `HostRegistry`

**Example**

```ts
import { createHostRegistry } from 'zidane/chat'

const hosts = createHostRegistry({
  dataDir: '/home/me/.zidane',
  probe: async () => ({ ok: false, error: 'unreachable', message: 'No transport wired.' }),
})
console.log(hosts.list())
```

Source: [src/chat/hosts/registry.ts:164](https://github.com/Tahul/zidane/blob/main/src/chat/hosts/registry.ts#L164)

### `createInteractionHub`

Function.

Creates an interaction hub backed by a session store.

```ts
function createInteractionHub(
  options: { broadcast: (signal: HostSignal) => void; onPlanRejected?: (sessionId: string, reason: string) => void; readSettings: () => Settings; secrets?: InteractionSecrets; store: SessionStore },
): InteractionHub
```

**Parameters**

- `options` (`{ broadcast: (signal: HostSignal) => void; onPlanRejected?: (sessionId: string, reason: string) => void; readSettings: () => Settings; secrets?: InteractionSecrets; store: SessionStore }`): Signal broadcast (`broadcast`), the session store (`store`), a live settings reader (`readSettings`), and an optional `onPlanRejected` callback.

**Returns** `InteractionHub`

Source: [src/chat/host/interactions.ts:393](https://github.com/Tahul/zidane/blob/main/src/chat/host/interactions.ts#L393)

### `createInteractionTools`

Function.

Create the `present_plan` and `ask_user` tools as a record, ready to
spread into `createAgent({ tools })` or composed into a `Preset`.

```ts
function createInteractionTools(
  opts: CreateInteractionToolsOptions,
): Record<string, ToolDef>
```

**Parameters**

- `opts` (`CreateInteractionToolsOptions`): Resolver for the user's response and an optional skip check.

**Returns** `Record<string, ToolDef>`

**Example**

```ts
import { createInteractionTools } from 'zidane/chat'

const tools = createInteractionTools({
  requestInteraction: async () => ({ kind: 'plan', decision: 'approve' }),
})
```

Source: [src/chat/interactions-protocol.ts:970](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L970)

### `createMainSession`

Function.

Create the orchestration main session; returns its id.

Directory-less on purpose. The main is GLOBAL — one per store, orchestrating
sessions in many directories — so binding it to whichever cwd happened to be
active when the toggle flipped put it in that project's group and hid it
from every other project's list. With no cwd it sits in the "Chats" bucket
and its own tools run from HOME, which is the only directory that is always
the user's and never a project's.

```ts
function createMainSession(store: SessionStore): Promise<string>
```

**Parameters**

- `store` (`SessionStore`): Store to persist the main session in.

**Returns** `Promise<string>`

Source: [src/chat/session-factory.ts:176](https://github.com/Tahul/zidane/blob/main/src/chat/session-factory.ts#L176)

### `createMcpStatusTracker`

Function.

A host's MCP server states.

```ts
function createMcpStatusTracker(
  options: { dataDir: () => string; onChange?: () => void; onCredentialsChanged?: (name: string) => void; store: () => McpCredentialStore },
): { missing(name: string): void; signingIn(name: string, login: Promise<unknown>, signal?: AbortSignal): Promise<{ ok: true } | { error: string; ok: false } | null>; signOut(name: string): void; statuses(configs: readonly McpServerConfig[]): Map<string, McpServerStatus>; test(config: McpServerConfig): Promise<McpTestOutcome> }
```

**Parameters**

- `options` (`{ dataDir: () => string; onChange?: () => void; onCredentialsChanged?: (name: string) => void; store: () => McpCredentialStore }`): The data dir the tools cache lives in, the credential store sign-ins write, and who hears of a change.

**Returns** `{ missing(name: string): void; signingIn(name: string, login: Promise<unknown>, signal?: AbortSignal): Promise<{ ok: true } | { error: string; ok: false } | null>; signOut(name: string): void; statuses(configs: readonly McpServerConfig[]): Map<string, McpServerStatus>; test(config: McpServerConfig): Promise<McpTestOutcome> }`

Source: [src/chat/mcp-status.ts:55](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-status.ts#L55)

### `createMemoryApprovalsStore`

Function.

In-memory fallback when the host doesn't provide durable persistence.

```ts
function createMemoryApprovalsStore(): PendingApprovalsStore
```

**Returns** `PendingApprovalsStore`

Source: [src/chat/host/approvals.ts:82](https://github.com/Tahul/zidane/blob/main/src/chat/host/approvals.ts#L82)

### `createMemoryEnvironmentSecrets`

Function.

In-memory secrets — tests and hosts without a data dir.

```ts
function createMemoryEnvironmentSecrets(
  initial?: Record<string, Record<string, string>>,
): EnvironmentSecrets
```

**Parameters**

- `initial?` (`Record<string, Record<string, string>>`, default `{}`): Secrets to start with, keyed by environment kind and then field key. Copied, not shared.

**Returns** `EnvironmentSecrets`

Source: [src/chat/environment-credentials.ts:101](https://github.com/Tahul/zidane/blob/main/src/chat/environment-credentials.ts#L101)

### `createPathAutocompleteProvider`

Function.

Built-in provider: complete filesystem paths under `cwd` (or absolute /
`~` paths). Given the token `src/chat/comp`, lists `src/chat/` and
returns every matching continuation of `comp` as a candidate list —
directories first, alphabetical, `/`-terminated for directories so
typing can continue. The host ghosts the first candidate and rotates
through the rest on up/down.

Bare tokens (no `/` yet) are matched against the cwd listing too, so
a directory ghost appears as soon as the first characters of `src`
are typed — but only from the second character on, so 1-letter prose
words don't ghost-spam.

Dotfiles are only offered when the typed fragment itself starts with
`.` — the usual completion convention.

```ts
function createPathAutocompleteProvider(
  opts?: { cwd?: () => string; io?: PathProviderIo },
): InlineAutocompleteProvider
```

**Parameters**

- `opts?` (`{ cwd?: () => string; io?: PathProviderIo }`, default `{}`): Optional working-directory getter and directory-listing override.

**Returns** `InlineAutocompleteProvider`

Source: [src/chat/inline-autocomplete.ts:187](https://github.com/Tahul/zidane/blob/main/src/chat/inline-autocomplete.ts#L187)

### `createPlanLifecycleHooks`

Function.

Hooks that close plans when runs end, and remind a run once about open steps.

```ts
function createPlanLifecycleHooks(options?: PlanLifecycleHooksOptions): AgentHookMap
```

**Parameters**

- `options?` (`PlanLifecycleHooksOptions`, default `{}`): Whether to send the reminder.

**Returns** `AgentHookMap`

Source: [src/chat/plan-hooks.ts:31](https://github.com/Tahul/zidane/blob/main/src/chat/plan-hooks.ts#L31)

### `createPlanTools`

Function.

The `{ todowrite, todoread }` tools plus the plan lifecycle hooks (the
open-steps reminder, closing a run's plans when it ends).

```ts
createAgent({ ...composePresets(basic, createPlanTools()), provider })
```

```ts
function createPlanTools(options?: CreatePlanToolsOptions): Preset
```

**Parameters**

- `options?` (`CreatePlanToolsOptions`, default `{}`): Limits, loop hardening and reminder settings.

**Returns** `Preset`

Source: [src/chat/todos.ts:147](https://github.com/Tahul/zidane/blob/main/src/chat/todos.ts#L147)

### `createPromptSubmissions`

Function.

The submit entry points over a host.

```ts
function createPromptSubmissions(
  host: PromptSubmitHost,
  transform?: SendTransform,
): PromptSubmissions
```

**Parameters**

- `host` (`PromptSubmitHost`): The host methods each entry point ends in.
- `transform?` (`SendTransform`): The prompt transforms (`ChatHost.transformPrompt`). Absent: prompts go as submitted.

**Returns** `PromptSubmissions`: One function per entry point.

Source: [src/chat/host/prompt-transforms.ts:143](https://github.com/Tahul/zidane/blob/main/src/chat/host/prompt-transforms.ts#L143)

### `createProviderBuilder`

Function.

The host's `ProviderBuilder`: it applies stored credentials to the
environment before each build, so a provider always sees the current ones.

```ts
function createProviderBuilder(
  env: Pick<ChatHostEnvironment, 'applyAuthEnv' | 'userDataDir'>,
): ProviderBuilder
```

**Parameters**

- `env` (`Pick<ChatHostEnvironment, 'applyAuthEnv' | 'userDataDir'>`): The host's `applyAuthEnv` and `userDataDir`.

**Returns** `ProviderBuilder`

Source: [src/chat/host/models/index.ts:83](https://github.com/Tahul/zidane/blob/main/src/chat/host/models/index.ts#L83)

### `createSessionExecutionCache`

Function.

Creates an empty [`SessionExecutionCache`](https://zidane.si/docs/reference/chat.md#SessionExecutionCache).
Handles are spawned lazily on first use and destroyed on release, key replacement, or host shutdown.

```ts
function createSessionExecutionCache(): SessionExecutionCache
```

**Returns** `SessionExecutionCache`: A cache keyed by session id.

Source: [src/chat/execution-cache.ts:108](https://github.com/Tahul/zidane/blob/main/src/chat/execution-cache.ts#L108)

### `createSessionInStore`

Function.

Create and persist a session; returns its id.

```ts
function createSessionInStore(
  store: SessionStore,
  params: CreateSessionInStoreParams,
): Promise<string>
```

**Parameters**

- `store` (`SessionStore`): Store to persist the session in.
- `params` (`CreateSessionInStoreParams`): Creation fields for the session.

**Returns** `Promise<string>`

Source: [src/chat/session-factory.ts:158](https://github.com/Tahul/zidane/blob/main/src/chat/session-factory.ts#L158)

### `createStateStore`

Function.

Creates a [`StateStoreApi`](https://zidane.si/docs/reference/chat.md#StateStoreApi) backed by the JSON file at `path`.

```ts
function createStateStore(path: string): StateStoreApi
```

**Parameters**

- `path` (`string`): Path of the state file.

**Returns** `StateStoreApi`

Source: [src/chat/store.ts:140](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L140)

### `createWorktreeEnvironmentProvider`

Function.

Creates the provider for worktree environments: each session gets its own branch in a linked git worktree.

```ts
function createWorktreeEnvironmentProvider(
  options: { dataDir: string },
): EnvironmentProvider
```

**Parameters**

- `options` (`{ dataDir: string }`): Options. `options.dataDir` is the host's user folder (the config folder), home of the worktrees by default.

**Returns** `EnvironmentProvider`

Source: [src/chat/environments/worktree.ts:383](https://github.com/Tahul/zidane/blob/main/src/chat/environments/worktree.ts#L383)

### `credentialSlotOf`

Function.

The slot key currently holding the credential whose stable `id` is `id`, or
`undefined` when none of `fileKey`'s slots does (signed out, removed).

Slot keys are positional — promoting an account swaps entries between them —
so anything that must keep naming ONE account (a session pinned to it)
stores the id and asks here on every use, never the key.

```ts
function credentialSlotOf(
  entries: Readonly<Record<string, unknown>>,
  fileKey: string,
  id: string,
): string | undefined
```

**Parameters**

- `entries` (`Readonly<Record<string, unknown>>`): Credential entries keyed by file key.
- `fileKey` (`string`): The provider's canonical credential file key.
- `id` (`string`): Id of the account to find.

**Returns** `string | undefined`

Source: [src/chat/cred-keys.ts:126](https://github.com/Tahul/zidane/blob/main/src/chat/cred-keys.ts#L126)

### `credentialsPath`

Function.

Resolve the credentials file path given the resolved TUI data directory
(typically `~/.zidane`, i.e. `config.paths.dir`).

Matches the convention used elsewhere in the TUI (sessions.db, state.json)
so a single `ZIDANE_STORAGE_DIR` override moves the entire data root.

```ts
function credentialsPath(dataDir: string): string
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.

**Returns** `string`

Source: [src/chat/credentials.ts:149](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L149)

### `credKeyOf`

Function.

Convenience accessor — returns `credentialFileKey ?? key`.

```ts
function credKeyOf(desc: ProviderDescriptor): string
```

**Parameters**

- `desc` (`ProviderDescriptor`): The provider descriptor.

**Returns** `string`

Source: [src/chat/providers.ts:361](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L361)

### `decideEvent`

Function.

The `decide` event for a plan request's answer, or `null` for a response
that is not a plan decision.

```ts
function decideEvent(
  request: PlanRequest,
  response: InteractionResponse,
  runId: string | undefined,
  at?: number,
): PlanEvent | null
```

**Parameters**

- `request` (`PlanRequest`): The request.
- `response` (`InteractionResponse`): The answer.
- `runId` (`string | undefined`): The run that applies an approved plan.
- `at?` (`number`): Clock, epoch ms.

**Returns** `PlanEvent | null`

Source: [src/chat/plans-session.ts:128](https://github.com/Tahul/zidane/blob/main/src/chat/plans-session.ts#L128)

### `dedupMcpServersByName`

Function.

Dedup a merged MCP-server list by `name`. First entry wins.

Applied at agent build across every source that can contribute a server —
`mcps.json`, Agent Plugins, launch options, extensions — so the earliest
source in that order takes precedence.

Drops are REPORTED rather than silent, via `onDropped`. A plugin shipping a
`github` server loses to a `github` in `mcps.json`, and nothing downstream
says so: the plugin's settings row still counts the server it shipped,
because the row renders what the PACKAGE declares and only this merge knows
what survived. That mirrors how the skills side reports the same class of
event — `resolveSkills` emits `name-collision-shadowed` on the winner rather
than annotating the loser.

The reporter is injected rather than calling a logger directly so this stays
a pure function of its input, testable without a logging shim. It lives here
beside [`buildMcpServers`](https://zidane.si/docs/reference/chat.md#buildMcpServers) rather than in the TUI because merge
precedence is a config concern, not a rendering one.

```ts
function dedupMcpServersByName<T extends { name: string }>(
  servers: readonly T[],
  onDropped?: (names: readonly string[]) => void,
): T[]
```

**Type parameters**

- `T` (`extends { name: string }`)

**Parameters**

- `servers` (`readonly T[]`): Servers to filter. The first entry per `name` wins.
- `onDropped?` (`(names: readonly string[]) => void`): Called once with the dropped duplicate names when any were removed.

**Returns** `T[]`

Source: [src/chat/mcps-discovery.ts:522](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-discovery.ts#L522)

### `defaultEnvironmentKind`

Function.

The default kind for new sessions: the configured one when registered and enabled, else `local`.

```ts
function defaultEnvironmentKind(
  registry: EnvironmentRegistry,
  settings: object | undefined,
): string
```

**Parameters**

- `registry` (`EnvironmentRegistry`): Registry of known environment kinds.
- `settings` (`object | undefined`): Settings object, or `undefined`.

**Returns** `string`

Source: [src/chat/environment-settings.ts:114](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L114)

### `defaultMcpsConfigPaths`

Function.

Search order for MCP config files — see [`projectUserPaths`](https://zidane.si/docs/reference/chat.md#projectUserPaths).

Both `mcps.json` (zidane's canonical name) and `mcp.json` (Cursor /
common ecosystem alias) are picked up from every search root. The
canonical name wins within the same directory; across files the
usual first-found-by-name dedup applies, so `mcp.json` effectively
merges its non-overlapping entries on top of `mcps.json`.

Order per scope (project beats user):

1. `{project}/.agents/mcps.json`
2. `{project}/.agents/mcp.json`
3. `{project}/.{prefix}/mcps.json`
4. `{project}/.{prefix}/mcp.json`
5. `~/.agents/mcps.json`
6. `~/.agents/mcp.json`
7. `~/.{prefix}/mcps.json`
8. `~/.{prefix}/mcp.json`

Non-existent files are skipped without error.

```ts
function defaultMcpsConfigPaths(
  opts?: { cwd?: string; home?: string; prefix?: string },
): { path: string; source: 'user' | 'project' }[]
```

**Parameters**

- `opts?` (`{ cwd?: string; home?: string; prefix?: string }`, default `{}`): Overrides for the working directory, home directory and dot-directory prefix.

**Returns** `{ path: string; source: 'user' | 'project' }[]`

Source: [src/chat/mcps-discovery.ts:80](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-discovery.ts#L80)

### `defaultModelOf`

Function.

The model a session with no pick runs on: the descriptor's default, else
[`DEFAULT_MODEL_PICK`](https://zidane.si/docs/reference/chat-pure.md#DEFAULT_MODEL_PICK).

```ts
function defaultModelOf(desc: ProviderDescriptor): string
```

**Parameters**

- `desc` (`ProviderDescriptor`): The provider descriptor.

**Returns** `string`

Source: [src/chat/providers.ts:380](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L380)

### `defaultSkillScanPaths`

Function.

Resolve the default skill scan paths for a project. First-found wins on
collision; earlier entries take precedence.

Search order — see [`projectUserPaths`](https://zidane.si/docs/reference/chat.md#projectUserPaths). Non-existent paths are
returned so downstream code can choose whether to create them;
`discoverSkills` itself skips missing dirs without error.

```ts
function defaultSkillScanPaths(
  opts?: { cwd?: string; foreignAgents?: boolean; home?: string; prefix?: string; ruler?: boolean },
): SourcedScanPath[]
```

**Parameters**

- `opts?` (`{ cwd?: string; foreignAgents?: boolean; home?: string; prefix?: string; ruler?: boolean }`, default `{}`): Overrides for the working directory, home directory and dot-directory prefix, plus flags for foreign-agent and Ruler skill directories.

**Returns** `SourcedScanPath[]`

Source: [src/chat/skills-discovery.ts:31](https://github.com/Tahul/zidane/blob/main/src/chat/skills-discovery.ts#L31)

### `deleteSessionData`

Function.

Delete a session's persisted data: the store row, then every on-disk
artifact it owns — the session-first tree (`<userDir>/sessions/<id>/`:
blobs, task logs, MCP warnings) plus the two pre-session-first locations
migration leaves behind. Blob cleanup is best-effort and never throws; it
runs AFTER the row delete so a cleanup failure can't leave a live row
pointing at removed blobs.

Does NOT stop a live agent — hosts must retire it first (see
`ChatHost.deleteSession`), or its next save resurrects the row.

```ts
function deleteSessionData(
  store: SessionStore,
  sessionId: string,
  opts: { userDir: string },
): Promise<void>
```

**Parameters**

- `store` (`SessionStore`): Store holding the session row.
- `sessionId` (`string`): Id of the session to delete.
- `opts` (`{ userDir: string }`): `userDir` is the root of the per-session artifact tree.

**Returns** `Promise<void>`

Source: [src/chat/session-factory.ts:328](https://github.com/Tahul/zidane/blob/main/src/chat/session-factory.ts#L328)

### `deriveSessionTitle`

Function.

Display title for a session — preferred sources, in order:

1. `metadata.title` if it's a non-empty string (typically set by
   the session-details modal's "generate title" action), on one line
   ([`oneLineTitle`](https://zidane.si/docs/reference/chat-pure.md#oneLineTitle)): whatever wrote it, a title never wraps.
2. [`titleFromTurns`](https://zidane.si/docs/reference/chat.md#titleFromTurns) — the first user message, one-line + clipped.
3. `DEFAULT_SESSION_TITLE` (`'New session'`).

Total / pure / defensive on non-string metadata values.

```ts
function deriveSessionTitle(
  turns: SessionTurn[],
  metadata?: Record<string, unknown>,
): string
```

**Parameters**

- `turns` (`SessionTurn[]`): The session's turns, used when no title is stored.
- `metadata?` (`Record<string, unknown>`): Session metadata that may hold a stored `title`.

**Returns** `string`

Source: [src/chat/store.ts:478](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L478)

### `describeEnvironments`

Function.

Every registered kind, described for `cwd` — what `environments.list` returns.

```ts
function describeEnvironments(
  registry: EnvironmentRegistry,
  context: ResolveConfigContext & { cwd: string; machineOnly?: boolean },
): Promise<EnvironmentDescriptor[]>
```

**Parameters**

- `registry` (`EnvironmentRegistry`): Registry of known environment kinds.
- `context` (`ResolveConfigContext & { cwd: string; machineOnly?: boolean }`): Settings, secret store, optional env-var source, and the folder to check availability for (`machineOnly`: what the machine needs, not the folder).

**Returns** `Promise<EnvironmentDescriptor[]>`

Source: [src/chat/environment-settings.ts:247](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L247)

### `describeForkFailure`

Function.

The error a user sees when a fork is refused, the same on every surface.

```ts
function describeForkFailure(
  reason: 'source-not-found' | 'turn-not-found' | 'environment-not-ready',
  sessionId: string,
  turnId: string,
): string
```

**Parameters**

- `reason` (`'source-not-found' | 'turn-not-found' | 'environment-not-ready'`): The refusal reason from [`forkSession`](https://zidane.si/docs/reference/chat.md#forkSession).
- `sessionId` (`string`): Id of the session that was to be forked.
- `turnId` (`string`): Id of the requested last turn.

**Returns** `string`

Source: [src/chat/session-factory.ts:229](https://github.com/Tahul/zidane/blob/main/src/chat/session-factory.ts#L229)

### `describeTitleStatus`

Function.

Session details' title facts, one badge each: where the title came from and
what the last auto-title attempt did, e.g. `auto` `checked 5m ago, still
fits` or `auto` `check failed 2m ago (unavailable: needs a platform key)`.

```ts
function describeTitleStatus(
  metadata: Readonly<Record<string, unknown>> | undefined,
  now?: number,
): string[]
```

**Parameters**

- `metadata` (`Readonly<Record<string, unknown>> | undefined`): Session metadata that holds the title, its source and the last check record.
- `now?` (`number`): Current time in ms since epoch, used for relative times.

**Returns** `string[]`

Source: [src/chat/auto-title.ts:94](https://github.com/Tahul/zidane/blob/main/src/chat/auto-title.ts#L94)

### `describeUpdateLockHolder`

Function.

One line for people: `the desktop app (pid 4242, started 3 min ago)`.

```ts
function describeUpdateLockHolder(holder: UpdateLockHolder, now?: number): string
```

**Parameters**

- `holder` (`UpdateLockHolder`): The recorded holder.
- `now?` (`number`): Current time in ms. Defaults to `Date.now()`.

**Returns** `string`

Source: [src/chat/auto-update-lock.ts:166](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-lock.ts#L166)

### `detectAuth`

Function.

Detect available auth for every registered provider.

A method appears in `methods` for every layer that has a credential:

- `kind: 'apikey'` from `credentials.json` (injected into env at TUI launch)
- explicit env var (descriptor's `envKey`)
- `kind: 'oauth'` from `credentials.json` (migrated on first read from a
  legacy `.credentials.json`, see `readCredentials`), one per stored account

The preferred credential lives in the canonical file key and is flagged
`AuthMethod.active`; the others sit in sibling slots and are reported
too, keyed by `AuthMethod.key`, so the UI can offer to promote or
remove them. Order within `methods` is presentational — active first — not a
resolution order, because the active flag answers that directly.

Never refreshes tokens or rewrites existing credentials. The one write is
the first-read migration of a legacy `.credentials.json` when
`credentials.json` does not exist yet (see `readCredentials`).

```ts
function detectAuth(
  dataDir: string,
  registry: Readonly<Record<string, ProviderDescriptor>>,
  env?: Record<string, string | undefined>,
): ProviderAuth[]
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`.
- `registry` (`Readonly<Record<string, ProviderDescriptor>>`): Provider descriptors keyed by id.
- `env?` (`Record<string, string | undefined>`, default `process.env`): Environment used to look for ambient credentials. Defaults to `process.env`.

**Returns** `ProviderAuth[]`

Source: [src/chat/auth.ts:149](https://github.com/Tahul/zidane/blob/main/src/chat/auth.ts#L149)

### `detectLibc`

Function.

Mirrors `tui/bin/zidane.mjs#detectLibc`. Reports `'glibc'` off Linux and `'musl'` when `ldd --version` fails or names musl.

```ts
function detectLibc(): 'glibc' | 'musl'
```

**Returns** `'glibc' | 'musl'`

Source: [src/chat/auto-update.ts:1260](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L1260)

### `detectOnboardingEnvironment`

Function.

Everything the wizard shows it found: providers and their credentials,
other agents, the GitHub login, local model servers and the install
signals. The GitHub lookup and the server probe run in parallel.

```ts
function detectOnboardingEnvironment(
  opts: DetectOnboardingOptions,
): Promise<OnboardingDetection>
```

**Parameters**

- `opts` (`DetectOnboardingOptions`): The host's registry and paths, the state file and what the app knows.

**Returns** `Promise<OnboardingDetection>`

**Example**

```ts
import { BUILTIN_PROVIDERS, detectOnboardingEnvironment } from 'zidane/chat'

const found = await detectOnboardingEnvironment({
  dataDir: '/home/me/.zidane',
  registry: BUILTIN_PROVIDERS,
  state: {},
  sessions: false,
  surfaceFirstRun: true,
})
console.log(found.providers.filter(p => p.ready).map(p => p.key), found.agents)
```

Source: [src/chat/host/onboarding/detect.ts:172](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/detect.ts#L172)

### `detectOtherAgents`

Function.

The coding agents whose setup exists on this computer, for the user or for
the current project. They are Claude Code (`~/.claude`, `.claude/`), Codex
(`.codex/`) and Pi (`.pi/`).

```ts
function detectOtherAgents(opts?: DetectOtherAgentsOptions): OnboardingAgentId[]
```

**Parameters**

- `opts?` (`DetectOtherAgentsOptions`, default `{}`): Project and home directories.

**Returns** `OnboardingAgentId[]`

Source: [src/chat/host/onboarding/detect.ts:49](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/detect.ts#L49)

### `detectPackageManager`

Function.

Chooses the package manager command that installs the package globally.
Checks the binary path, then the npm user agent, then environment hints such as `VOLTA_HOME`, and falls back to `npm`.

```ts
function detectPackageManager(opts?: DetectPackageManagerOptions): PackageManagerCommand
```

**Parameters**

- `opts?` (`DetectPackageManagerOptions`, default `{}`): Optional overrides for the binary path, environment, platform, package name and channel.

**Returns** `PackageManagerCommand`

Source: [src/chat/auto-update.ts:572](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L572)

### `discoverAgentsMd`

Function.

Walk the candidate paths and load every `AGENTS.md` that exists.

Project paths anchor at the git root when one exists above `cwd`,
else at `cwd` itself — same anchor logic as `projectUserPaths` so the
three discovery surfaces (skills, mcps, AGENTS.md) stay in lock-step.

All matching files are loaded (no shadowing within a scope). Empty
or whitespace-only files are skipped. Render order matches the
search order — broader scopes first, narrower scopes last so the
project-specific file lands closest to the conversation.

Tolerant on read failures: an unreadable file is skipped silently
(logged under `ZIDANE_DEBUG`). The loader never throws — bootstrap
paths can't afford to crash on a permission glitch.

Defensive dedup: when `<git-root> === ~` (the user launched zidane
directly from `$HOME` and `$HOME` happens to be a git repo) the
project + user candidate sets would otherwise overlap. We dedup by
absolute path so the same file never lands in the block twice.

```ts
function discoverAgentsMd(opts?: DiscoverAgentsMdOptions): AgentsMdResult
```

**Parameters**

- `opts?` (`DiscoverAgentsMdOptions`, default `{}`): Discovery options: working directory, home, storage prefix, scope and Ruler import.

**Returns** `AgentsMdResult`

Source: [src/chat/agents-md.ts:128](https://github.com/Tahul/zidane/blob/main/src/chat/agents-md.ts#L128)

### `discoverProjectMcps`

Function.

Walk the default config paths, parse every file that exists, and return
`{ servers, errors }` in source-priority order. Project entries appear
before user entries; first occurrence of a `name` wins (later
duplicates dropped).

Parse failures are captured into `errors` rather than thrown — the picker
surfaces them while still showing whatever DID parse. A `ZIDANE_DEBUG`
log line is also emitted for ops visibility.

```ts
function discoverProjectMcps(
  opts?: { cwd?: string; foreignAgents?: boolean; home?: string; prefix?: string; ruler?: boolean },
): DiscoveryResult
```

**Parameters**

- `opts?` (`{ cwd?: string; foreignAgents?: boolean; home?: string; prefix?: string; ruler?: boolean }`, default `{}`): Roots to search and flags that enable foreign-agent and Ruler config files.

**Returns** `DiscoveryResult`

Source: [src/chat/mcps-discovery.ts:307](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-discovery.ts#L307)

### `discoverProjectPlugins`

Function.

Load every plugin reachable from the default search roots.

Each immediate child directory of a root is one plugin — the same
depth-1 rule the spec applies inside `skills/`. A child that isn't a
directory, or that holds no `plugin.json`, is silently skipped: dropping a
README next to your plugins is not an error.

Dedup is by plugin NAME, not directory name, because the manifest owns
identity — two directories called `tools/` shipping different plugins are
two plugins, and one plugin vendored at two scopes is one plugin.

```ts
function discoverProjectPlugins(
  opts?: { cwd?: string; extraRoots?: readonly string[]; home?: string; prefix?: string; signal?: AbortSignal },
): Promise<{ errors: DiscoveryError[]; plugins: DiscoveredPlugin[] }>
```

**Parameters**

- `opts?` (`{ cwd?: string; extraRoots?: readonly string[]; home?: string; prefix?: string; signal?: AbortSignal }`, default `{}`): Roots to scan, an optional extra scan root list and an abort signal.

**Returns** `Promise<{ errors: DiscoveryError[]; plugins: DiscoveredPlugin[] }>`

Source: [src/chat/plugins-discovery.ts:112](https://github.com/Tahul/zidane/blob/main/src/chat/plugins-discovery.ts#L112)

### `discoverProjectSkills`

Function.

Discover every skill reachable from the default scan paths. Returns
parsed `SkillConfig`s with `name`, `description`, frontmatter, and
lenient-load diagnostics. Pure I/O — does not activate or write.

`signal` is forwarded to `discoverSkills` so the TUI's directory-watch
effect can cancel a long scan when the user switches `cwd` rapidly.

Errors during parse are surfaced as `diagnostics` on the returned
skill, not thrown — keeps the picker usable even when a single
SKILL.md is malformed.

```ts
function discoverProjectSkills(
  opts?: { cwd?: string; foreignAgents?: boolean; home?: string; prefix?: string; ruler?: boolean; signal?: AbortSignal },
): Promise<SkillConfig[]>
```

**Parameters**

- `opts?` (`{ cwd?: string; foreignAgents?: boolean; home?: string; prefix?: string; ruler?: boolean; signal?: AbortSignal }`, default `{}`): Roots to scan, flags for foreign-agent and Ruler directories, and an abort signal.

**Returns** `Promise<SkillConfig[]>`

Source: [src/chat/skills-discovery.ts:107](https://github.com/Tahul/zidane/blob/main/src/chat/skills-discovery.ts#L107)

### `discoverSystemMd`

Function.

Find the active `SYSTEM.md` override, if any.

Project paths anchor at the git root when one exists above `cwd`, else at
`cwd` — the same anchor as skills / mcps / AGENTS.md discovery, so all four
surfaces agree on what "this project" means.

Tolerant: an unreadable or empty file is treated as absent (logged under
`ZIDANE_DEBUG`) and the next candidate gets its turn. Never throws — this
runs on the session-activation path.

Candidates are filtered by `DiscoverSystemMdOptions.scope`, mirroring
how `discoverAgentsMd` builds its own list.

```ts
function discoverSystemMd(opts?: DiscoverSystemMdOptions): SystemMdResult
```

**Parameters**

- `opts?` (`DiscoverSystemMdOptions`, default `{}`): Roots to search and which scopes to consult.

**Returns** `SystemMdResult`

Source: [src/chat/system-md.ts:149](https://github.com/Tahul/zidane/blob/main/src/chat/system-md.ts#L149)

### `DiscoveryProvider`

Function.

Provides a [`DiscoveryContextValue`](https://zidane.si/docs/reference/chat.md#DiscoveryContextValue) to the components below it.

```ts
function DiscoveryProvider(
  props: { children: ReactNode; value: DiscoveryContextValue },
): Element
```

**Parameters**

- `props` (`{ children: ReactNode; value: DiscoveryContextValue }`): Props with the discovery `value` and the `children` to render.

**Returns** `Element`

Source: [src/chat/discovery-context.tsx:92](https://github.com/Tahul/zidane/blob/main/src/chat/discovery-context.tsx#L92)

### `emptyPlansBag`

Function.

An empty bag.

```ts
function emptyPlansBag(): PlansBag
```

**Returns** `PlansBag`

Source: [src/chat/plans.ts:260](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L260)

### `enabledModelOptions`

Function.

Normalize a model-options blob to an enabled-only `{ id: true }` map.

Single source of truth shared by every surface that reads or persists model
options — the TUI run path, the GUI engine reader, and the GUI IPC handlers.
Tolerant of arbitrary input (persisted JSON metadata may be anything): a
non-object → `{}`, and only entries strictly equal to `true` survive. This
keeps the persisted shape minimal (no `{ fast: false }` noise) and means
callers never forward a disabled option to `agent.run`.

```ts
function enabledModelOptions(raw: unknown): Record<string, boolean>
```

**Parameters**

- `raw` (`unknown`): Persisted or untrusted options blob.

**Returns** `Record<string, boolean>`

Source: [src/chat/providers.ts:1261](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L1261)

### `ensureCredentialIds`

Function.

Give every stored credential its stable id, once. Files from before ids
(and entries written by tools that replace them wholesale) are otherwise
only id-less until their next write; run at config resolve so pickers can
pin any listed account. No-op (no write) when every entry has one, or no
file exists.

```ts
function ensureCredentialIds(
  dataDir: string,
  providers: Readonly<Record<string, ProviderDescriptor>>,
): void
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `providers` (`Readonly<Record<string, ProviderDescriptor>>`): Registered provider descriptors. Only their credential slots are given ids.

Source: [src/chat/credentials.ts:443](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L443)

### `ensureKeybindingsFile`

Function.

Create the user-level `keybindings.json` if it doesn't exist,
prefilled with every default. Returns the absolute file path either
way so the caller can open it in `$EDITOR`. Idempotent: an existing
user file is NEVER overwritten — the user's customizations are
sacred.

```ts
function ensureKeybindingsFile(userDir: string): string
```

**Parameters**

- `userDir` (`string`): User-level config directory that holds `keybindings.json`.

**Returns** `string`

Source: [src/chat/keybindings.ts:775](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L775)

### `environmentActionSummaries`

Function.

[`availableEnvironmentActions`](https://zidane.si/docs/reference/chat.md#availableEnvironmentActions), wire-safe.

```ts
function environmentActionSummaries(
  registry: EnvironmentRegistry,
  environment: SessionEnvironment,
  config: EnvironmentConfig,
): EnvironmentActionSummary[]
```

**Parameters**

- `registry` (`EnvironmentRegistry`): Registry that holds the session's provider.
- `environment` (`SessionEnvironment`): The session's environment.
- `config` (`EnvironmentConfig`): The kind's resolved options.

**Returns** `EnvironmentActionSummary[]`

Source: [src/chat/environment.ts:499](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L499)

### `environmentCredentialsPath`

Function.

Returns the path of the environment credentials file inside the data directory.

```ts
function environmentCredentialsPath(dataDir: string): string
```

**Parameters**

- `dataDir` (`string`): Data directory that holds `environment-credentials.json`.

**Returns** `string`

Source: [src/chat/environment-credentials.ts:28](https://github.com/Tahul/zidane/blob/main/src/chat/environment-credentials.ts#L28)

### `envSection`

Function.

Render the env anchor block. Plain text — XML tags only because models
lean on them as structural cues, not because we parse them anywhere.

Kept narrow on purpose: cwd / projectRoot / date / platform are the
knobs the model can't infer from the conversation. Anything else
(git branch, project name) belongs in CLAUDE.md / project-level
prompts where the user opts in.

Cache-stability: outputs are byte-stable per `(cwd, projectRoot,
date, platform)` tuple. Repeated session activations against the
same workspace produce the same prefix → provider's prompt cache
hits across turns + across sessions in the same host process.

```ts
function envSection(opts: EnvSectionOptions): string
```

**Parameters**

- `opts` (`EnvSectionOptions`): Working directory, optional project root, date and platform to render.

**Returns** `string`

Source: [src/chat/agent-prompt.ts:90](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L90)

### `eventsFromTurns`

Function.

Replays persisted turns as a viewable transcript. Equivalent to `replayTurns(turns, runs).events`.
Mirrors the event shape produced live by the agent hooks so loaded and
streaming history render identically, including subagent ancestry when
`runs` is supplied:

- Every turn carries a `runId`, looked up in `runs` to get the run's
  `depth`. The resulting events carry `{ depth, childId }`, the same
  shape the live `child:*` bubble hooks produce.
- `spawn-start` / `spawn-end` markers land at each child-run boundary,
  so the transcript reads the same as a live run did.
- For child runs (`depth > 0`), the user-role "task" text is suppressed
  because `spawn-start` already shows it.

```ts
function eventsFromTurns(
  turns: SessionTurn[],
  runs?: readonly SessionRun[],
): StreamEvent[]
```

**Parameters**

- `turns` (`SessionTurn[]`): Persisted turns in order.
- `runs?` (`readonly SessionRun[]`, default `[]`): Runs the turns belong to. Without them every event has depth 0.

**Returns** `StreamEvent[]`

Source: [src/chat/store.ts:1076](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1076)

### `executeCommand`

Function.

Run a command's steps in order, threading `ctx.data`. Stops at the first
step that throws (error isolated + returned, not rethrown) or when the
signal aborts. Pure orchestration — all I/O lives in the host-supplied
`ctx` capabilities.

```ts
function executeCommand(
  command: Command,
  ctx: CommandStepContext,
): Promise<CommandExecution>
```

**Parameters**

- `command` (`Command`): The command whose steps run.
- `ctx` (`CommandStepContext`): Host-supplied step context. Its `data` bag is shared across steps and `signal` aborts the run.

**Returns** `Promise<CommandExecution>`

Source: [src/chat/commands.ts:626](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L626)

### `exportFilename`

Function.

Compose the on-disk filename for `(sessionId, format)`. Rejects
empty ids and path-traversal-ish values so the caller can't escape
`.{prefix}/sessions/`.

```ts
function exportFilename(sessionId: string, format: SessionExportFormat): string
```

**Parameters**

- `sessionId` (`string`): The session.
- `format` (`SessionExportFormat`): The format.

**Returns** `string`

Source: [src/chat/session-export.ts:422](https://github.com/Tahul/zidane/blob/main/src/chat/session-export.ts#L422)

### `extractEditPayloadForGate`

Function.

Gate-time `extractEditPayload` wrapper — same as `tool:before` but
without the prior-content second pass for `write_file`. The gate
only needs the payload shape (hunk count, paths) to drive the
approval surface; the diff resolver works against the freshly-read
`priorContent` passed to `previewEditPayload` directly.

```ts
function extractEditPayloadForGate(
  name: string,
  input: Record<string, unknown>,
): EditPayload | undefined
```

**Parameters**

- `name` (`string`): Tool name.
- `input` (`Record<string, unknown>`): Tool input.

**Returns** `EditPayload | undefined`

Source: [src/chat/host/helpers.ts:183](https://github.com/Tahul/zidane/blob/main/src/chat/host/helpers.ts#L183)

### `fetchAllProviderUsage`

Function.

Usage for every registered provider that has both a usage surface and a
usable credential, in registry order, every account included.

```ts
function fetchAllProviderUsage(
  dataDir: string,
  registry: Readonly<Record<string, ProviderDescriptor>>,
  opts?: FetchProviderUsageOptions,
): Promise<readonly ProviderUsageSnapshot[]>
```

**Parameters**

- `dataDir` (`string`): Directory that holds the credentials files.
- `registry` (`Readonly<Record<string, ProviderDescriptor>>`): Provider descriptors keyed by provider key.
- `opts?` (`FetchProviderUsageOptions`, default `{}`): Fetch options, such as forcing a refresh.

**Returns** `Promise<readonly ProviderUsageSnapshot[]>`

Source: [src/chat/provider-usage.ts:476](https://github.com/Tahul/zidane/blob/main/src/chat/provider-usage.ts#L476)

### `fetchGithubUsage`

Function.

GitHub's limits per host, in the shape the Usage panel renders. Never rejects.

```ts
function fetchGithubUsage(
  opts?: FetchGithubUsageOptions,
): Promise<ProviderUsageSnapshot[]>
```

**Parameters**

- `opts?` (`FetchGithubUsageOptions`, default `{}`): Probe toggle, client, clock, and probe deadline.

**Returns** `Promise<ProviderUsageSnapshot[]>`

Source: [src/chat/github-usage.ts:88](https://github.com/Tahul/zidane/blob/main/src/chat/github-usage.ts#L88)

### `fetchOAuthRedirect`

Function.

Treat `pasted` as a callback-URL paste. Validates it's a loopback URL,
fires a GET, returns the status.

Pass `authUrl` (the authorization URL currently on screen) to accept
partial pastes — a bare code, a query string, a path — via
[`normalizeRedirectPaste`](https://zidane.si/docs/reference/chat.md#normalizeRedirectPaste). Without it only a full absolute URL is
accepted.

Throws when:

- `pasted` doesn't parse as a URL (and can't be completed from `authUrl`).
- The URL host isn't loopback (rejects SSRF).
- The fetch errors out (network, timeout).

Returns success/failure for non-2xx responses; the caller decides whether
a 4xx is fatal (state mismatch is a 400 the user can retry from).

```ts
function fetchOAuthRedirect(
  pasted: string,
  options?: { authUrl?: string; signal?: AbortSignal; timeoutMs?: number },
): Promise<FetchOAuthRedirectResult>
```

**Parameters**

- `pasted` (`string`): What the user pasted: a callback URL, or a partial one when `options.authUrl` is set.
- `options?` (`{ authUrl?: string; signal?: AbortSignal; timeoutMs?: number }`, default `{}`): Abort signal, request timeout in milliseconds (`5000` by default) and the authorization URL used to complete partial pastes.

**Returns** `Promise<FetchOAuthRedirectResult>`

Source: [src/chat/oauth-redirect.ts:172](https://github.com/Tahul/zidane/blob/main/src/chat/oauth-redirect.ts#L172)

### `fetchProviderUsage`

Function.

Usage for one provider, probed with the credential requests use — or, when
that one is not the kind the probe needs (an API key in use, OAuth accounts
parked beside it), the first stored one that is. What the footer shows.

`null` means there is nothing to show: the provider has no usage surface,
no credential of the kind its probe needs (file or, for API keys, env), or
the environment opted out (see [`usageChecksOptedOut`](https://zidane.si/docs/reference/chat.md#usageChecksOptedOut)).

Never rejects. Transport and parse failures land in the snapshot.

```ts
function fetchProviderUsage(
  dataDir: string,
  descriptor: ProviderDescriptor,
  opts?: FetchProviderUsageOptions,
): Promise<ProviderUsageSnapshot | null>
```

**Parameters**

- `dataDir` (`string`): Directory that holds the credentials files.
- `descriptor` (`ProviderDescriptor`): The provider to probe.
- `opts?` (`FetchProviderUsageOptions`, default `{}`): Fetch options, such as forcing a refresh.

**Returns** `Promise<ProviderUsageSnapshot | null>`

Source: [src/chat/provider-usage.ts:206](https://github.com/Tahul/zidane/blob/main/src/chat/provider-usage.ts#L206)

### `fetchProviderUsageAccounts`

Function.

Usage for EVERY credential of a provider the probe can use — one snapshot
per stored OAuth account, the active one first. What the panel renders.
Empty under the same conditions [`fetchProviderUsage`](https://zidane.si/docs/reference/chat.md#fetchProviderUsage) returns `null`.

```ts
function fetchProviderUsageAccounts(
  dataDir: string,
  descriptor: ProviderDescriptor,
  opts?: FetchProviderUsageOptions,
): Promise<readonly ProviderUsageSnapshot[]>
```

**Parameters**

- `dataDir` (`string`): Directory that holds the credentials files.
- `descriptor` (`ProviderDescriptor`): The provider to probe.
- `opts?` (`FetchProviderUsageOptions`, default `{}`): Fetch options, such as forcing a refresh.

**Returns** `Promise<readonly ProviderUsageSnapshot[]>`

Source: [src/chat/provider-usage.ts:229](https://github.com/Tahul/zidane/blob/main/src/chat/provider-usage.ts#L229)

### `fetchRepoInfo`

Function.

The info of the repository `input` names (what the import dialog would clone); `null` when there is none.

```ts
function fetchRepoInfo(
  input: string,
  options?: FetchRepoInfoOptions,
): Promise<RepoInfo | null>
```

**Parameters**

- `input` (`string`): What was pasted: a git or forge URL, or `owner/repo`.
- `options?` (`FetchRepoInfoOptions`, default `{}`): A token, and the fetch to use.

**Returns** `Promise<RepoInfo | null>`

Source: [src/chat/git/repo-info.ts:61](https://github.com/Tahul/zidane/blob/main/src/chat/git/repo-info.ts#L61)

### `fileEntriesFromPaths`

Function.

File entries (plus their ancestor dirs) from relative paths — what
[`listProjectFiles`](https://zidane.si/docs/reference/chat.md#listProjectFiles) returns, for lists produced elsewhere (a remote
sandbox's `git ls-files`).

```ts
function fileEntriesFromPaths(
  paths: readonly string[],
  source?: 'git' | 'fs',
  maxFiles?: number,
): FileEntry[]
```

**Parameters**

- `paths` (`readonly string[]`): Relative file paths.
- `source?` (`'git' | 'fs'`, default `'git'`): Where the paths came from. Defaults to `'git'`.
- `maxFiles?` (`number`, default `DEFAULT_MAX_FILES`): Maximum number of entries to return. Defaults to the same cap as [`listProjectFiles`](https://zidane.si/docs/reference/chat.md#listProjectFiles).

**Returns** `FileEntry[]`

Source: [src/chat/files-discovery.ts:261](https://github.com/Tahul/zidane/blob/main/src/chat/files-discovery.ts#L261)

### `filterChainFallbacks`

Function.

Filter candidate fallback entries against a primary pick, preserving
picked order: entries whose provider `isAvailable` rejects, entries
equal to the primary, and exact duplicates drop. Shared by the
picker's apply path (live-auth availability) and persisted-chain
restore (registry availability).

One account per provider: a chain runs each provider as ONE instance
bound to one account, so an entry naming another account of a provider
already in the chain (the primary's, or an earlier fallback's) drops — it
would otherwise run on the wrong account.

```ts
function filterChainFallbacks(
  entries: readonly ModelChainEntry[],
  primary: ModelChainEntry,
  isAvailable: (key: string) => boolean,
): ModelChainEntry[]
```

**Parameters**

- `entries` (`readonly ModelChainEntry[]`): Candidate fallback picks in order.
- `primary` (`ModelChainEntry`): The chain's primary pick. Entries equal to it are dropped.
- `isAvailable` (`(key: string) => boolean`): Whether a provider key is usable. Entries on unusable providers are dropped.

**Returns** `ModelChainEntry[]`

Source: [src/chat/model-chain.ts:331](https://github.com/Tahul/zidane/blob/main/src/chat/model-chain.ts#L331)

### `findGitRoot`

Function.

Walk parents of `cwd` looking for a `.git` entry. Returns the
absolute path of the directory containing `.git` — the CHECKOUT root
(a linked worktree's own dir, not its main checkout) — or `null` when
no git repo is found above `cwd`.

Stops at the filesystem root (`/`) — no infinite loop on `dirname`'s
idempotent root case (`dirname('/')` returns `/`).

```ts
function findGitRoot(cwd?: string): string | null
```

**Parameters**

- `cwd?` (`string`): Directory to start the upward walk from.

**Returns** `string | null`

Source: [src/chat/project-root.ts:48](https://github.com/Tahul/zidane/blob/main/src/chat/project-root.ts#L48)

### `findInstalledCompanion`

Function.

Locate a globally installed companion. `null` when its bin is not on PATH
or no `package.json` with that name can be found behind it.

```ts
function findInstalledCompanion(
  spec: CompanionSpec,
  opts?: FindCompanionOptions,
): InstalledCompanion | null
```

**Parameters**

- `spec` (`CompanionSpec`): The package and its bin name.
- `opts?` (`FindCompanionOptions`, default `{}`): Optional overrides for the environment, home directory and platform.

**Returns** `InstalledCompanion | null`

Source: [src/chat/auto-update-companion.ts:55](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-companion.ts#L55)

### `findProjectRoot`

Function.

The project `cwd` belongs to: the main checkout for a linked worktree,
the checkout root otherwise, `null` outside git. See the module header
for when to use this vs [`findGitRoot`](https://zidane.si/docs/reference/chat.md#findGitRoot).

```ts
function findProjectRoot(cwd?: string): string | null
```

**Parameters**

- `cwd?` (`string`): Directory to resolve.

**Returns** `string | null`

Source: [src/chat/project-root.ts:107](https://github.com/Tahul/zidane/blob/main/src/chat/project-root.ts#L107)

### `folderlessCwd`

Function.

Process cwd used for sessions with no folder binding.

```ts
function folderlessCwd(): string
```

**Returns** `string`

Source: [src/chat/session-cwd.ts:18](https://github.com/Tahul/zidane/blob/main/src/chat/session-cwd.ts#L18)

### `folderLocation`

Function.

The checkout and the project of `cwd`: `@` lists the checkout's files, `#` the project's chats.

```ts
function folderLocation(cwd: string): FolderLocation
```

**Parameters**

- `cwd` (`string`): Directory to resolve.

**Returns** `FolderLocation`

Source: [src/chat/project-root.ts:136](https://github.com/Tahul/zidane/blob/main/src/chat/project-root.ts#L136)

### `followRunningUpdate`

Function.

Attaches to an install that is already running (a surface that starts while
another one's worker installs). `null` when no live install holds the lock.
Otherwise resolves once the lock is released: the worker's result, or
`null` when the holder was not a worker (`zidane upgrade --in-place`) or
left no result.

```ts
function followRunningUpdate(
  options: FollowRunningUpdateOptions,
): Promise<UpdateWorkerResult | null> | null
```

**Parameters**

- `options` (`FollowRunningUpdateOptions`): Cache directory, log callback and poll interval.

**Returns** `Promise<UpdateWorkerResult | null> | null`

Source: [src/chat/auto-update-worker.ts:378](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L378)

### `forgetGitCaches`

Function.

The repository at `at` moved (`git:refs`): its default branch is read
again. A remote changed (`git:config`): its pull requests are another
repository's, so those go too. PR answers otherwise stay on a refs change:
the window already asks `fresh` then, and their backoff must outlive a
burst of commits.

```ts
function forgetGitCaches(at: string, topic: 'git:refs' | 'git:config'): void
```

**Parameters**

- `at` (`string`): The folder that moved.
- `topic` (`'git:refs' | 'git:config'`): What moved.

Source: [src/chat/git/info.ts:672](https://github.com/Tahul/zidane/blob/main/src/chat/git/info.ts#L672)

### `forkSession`

Function.

Create and persist a new session whose history is the parent's turns up to
and including `turnId`. The parent is untouched.

Carries: the protocol-clean turn prefix, the run records those turns
reference (so run stats survive and the next run can't re-mint `run_1`
over an existing reference), persisted-output blobs (copied and re-pointed),
`projectRoot` (a fork belongs to its parent's project, wherever it was
forked from), `agentId`, `FORK_INHERITED_METADATA_KEYS`, and the
parent's environment — marked not-owned: the fork runs in the same
worktree / sandbox but deleting it never tears that down. Refused while
the parent's environment isn't `ready`: nothing would provision the fork's
copy, and provisioning it would mint an environment nobody owns.

```ts
function forkSession(
  store: SessionStore,
  params: ForkSessionParams,
): Promise<ForkSessionResult>
```

**Parameters**

- `store` (`SessionStore`): Store to load the parent from and persist the fork in.
- `params` (`ForkSessionParams`): Parent, last turn to keep, artifact root and optional title.

**Returns** `Promise<ForkSessionResult>`

Source: [src/chat/session-factory.ts:257](https://github.com/Tahul/zidane/blob/main/src/chat/session-factory.ts#L257)

### `formatBindingForDisplay`

Function.

Render a binding spec (`"ctrl+return"`, `"backspace"`, `"delete"`) as a
compact display string the user recognizes at a glance. Substitutes
arrow / enter / backspace glyphs for their verbose names so the hint
row stays narrow; modifier names + plain keys pass through unchanged.

Empty / missing specs return an empty string — callers decide whether
to render a placeholder (e.g. `'—'` for an unbound action in the
keybindings panel) or hide the surface entirely (e.g. hint rows that
fall back to a different chord when the binding is empty).

```ts
function formatBindingForDisplay(spec: string | null | undefined): string
```

**Parameters**

- `spec` (`string | null | undefined`): Binding spec, possibly listing several chords separated by `|`.

**Returns** `string`

Source: [src/chat/keybindings.ts:598](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L598)

### `formatPathForCwd`

Function.

Convert a project-root-relative path (as emitted by `listProjectFiles`)
into the form the agent's tools — and the user — will actually find
given the current `cwd`.

Inputs:

- `projectRelativePath` — forward-slashed, no leading slash. Returned
  verbatim when it's empty or already absolute (defensive — the
  absolute-input branch is the de-facto "outside the project"
  escape; the catalog itself never emits absolute paths).
- `projectRoot` — absolute path of the discovery anchor.
- `cwd` — absolute path of `process.cwd()` at the moment of
  insertion.

Output is always either an absolute path (only when the input arrived
absolute) OR a CWD-relative path. The relative form may include
`../` segments when the target sits above `cwd` within the project,
which keeps cross-tree references short and scannable
(e.g. `../EDIT_THIS.md` instead of `/Users/.../zidane/EDIT_THIS.md`).

```ts
function formatPathForCwd(
  projectRelativePath: string,
  projectRoot: string,
  cwd: string,
): string
```

**Parameters**

- `projectRelativePath` (`string`): Forward-slashed path relative to `projectRoot`.
- `projectRoot` (`string`): Absolute path of the discovery anchor.
- `cwd` (`string`): Absolute working directory the result is relative to.

**Returns** `string`

Source: [src/chat/path-display.ts:55](https://github.com/Tahul/zidane/blob/main/src/chat/path-display.ts#L55)

### `formatPlanForModel`

Function.

The plan as the model reads it: a header, then one line per step. Goes
into tool results and transcript notes, which never change once written,
so it costs the prompt cache nothing. Never includes times.

```ts
function formatPlanForModel(plan: Plan): string
```

**Parameters**

- `plan` (`Plan`): The plan.

**Returns** `string`

Source: [src/chat/plans.ts:1180](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L1180)

### `formatStoppedNotice`

Function.

The one line the agent gets on the next prompt about a plan that stopped:
how to continue it, and that leaving it is fine.

```ts
function formatStoppedNotice(plan: Plan): string
```

**Parameters**

- `plan` (`Plan`): A stopped plan.

**Returns** `string`

Source: [src/chat/plans.ts:1197](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L1197)

### `generateStructuredData`

Function.

Drive one schema-pinned provider call and return the conforming payload.
Throws when the provider returns no structured payload (and rethrows
aborts verbatim). The caller owns presentation — nothing is rendered or
persisted here.

Pass a zod v4 schema and the return type is inferred from it; pass a
plain JSON Schema object and name the type via the generic.

```ts
function generateStructuredData<S extends StandardSchemaLike<unknown>>(
  options: GenerateStructuredDataOptions & { schema: S },
): Promise<InferSchemaOutput<S>>
```

Drive one schema-pinned provider call and return the conforming payload.
Throws when the provider returns no structured payload (and rethrows
aborts verbatim). The caller owns presentation — nothing is rendered or
persisted here.

Pass a zod v4 schema and the return type is inferred from it; pass a
plain JSON Schema object and name the type via the generic.

**Type parameters**

- `S` (`extends StandardSchemaLike<unknown>`)

**Parameters**

- `options` (`GenerateStructuredDataOptions & { schema: S }`): Provider, model, prompt, schema, and optional system prompt, context turns, and abort signal.

**Returns** `Promise<InferSchemaOutput<S>>`

```ts
function generateStructuredData<T = unknown>(
  options: GenerateStructuredDataOptions,
): Promise<T>
```

Drive one schema-pinned provider call and return the conforming payload.
Throws when the provider returns no structured payload (and rethrows
aborts verbatim). The caller owns presentation — nothing is rendered or
persisted here.

Pass a zod v4 schema and the return type is inferred from it; pass a
plain JSON Schema object and name the type via the generic.

**Type parameters**

- `T` (default `unknown`)

**Parameters**

- `options` (`GenerateStructuredDataOptions`): Provider, model, prompt, schema, and optional system prompt, context turns, and abort signal.

**Returns** `Promise<T>`

Source: [src/chat/generate-data.ts:70](https://github.com/Tahul/zidane/blob/main/src/chat/generate-data.ts#L70)

### `getContextWindow`

Function.

Look up the model's max context window via the descriptor's model source.
Falls back to `ProviderDescriptor.contextWindowEnvVar` (parsed via
`resolveContextWindow`, which supports a per-model map) when the
registry has nothing — this is how local LLMs, whose runtimes don't
advertise a window, get one. Returns `null` when neither source resolves a
value (custom slugs, providers without a registry or a configured window);
callers should hide the context indicator and skip auto-compaction then.

```ts
function getContextWindow(
  descriptor: ProviderDescriptor,
  modelId: string,
): number | null
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider descriptor.
- `modelId` (`string`): Model id, resolved through `modelAliases`.

**Returns** `number | null`

Source: [src/chat/providers.ts:1203](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L1203)

### `getMcpAuthStatus`

Function.

Convenience getter — treats `undefined` as `idle`.

```ts
function getMcpAuthStatus(state: McpAuthStateMap, name: string): McpAuthStatus
```

**Parameters**

- `state` (`McpAuthStateMap`): The auth state map.
- `name` (`string`): Server name to look up.

**Returns** `McpAuthStatus`

Source: [src/chat/mcp-auth-state.ts:206](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-auth-state.ts#L206)

### `getModelInfo`

Function.

Resolve a single model's metadata via the descriptor's model source.
Mirrors [`modelsForDescriptor`](https://zidane.si/docs/reference/chat.md#modelsForDescriptor) routing: descriptor's own list wins,
pi-ai's registry is the fallback. Returns `null` when the model isn't
known.

```ts
function getModelInfo(descriptor: ProviderDescriptor, modelId: string): ModelInfo | null
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider descriptor.
- `modelId` (`string`): Model id, resolved through `modelAliases`.

**Returns** `ModelInfo | null`

Source: [src/chat/providers.ts:1022](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L1022)

### `getSafelist`

Function.

Read the safelist for one project. Returns `[]` for unknown projects.

```ts
function getSafelist(dataDir: string, projectDir: string): readonly string[]
```

**Parameters**

- `dataDir` (`string`): Directory that holds `projects.json`.
- `projectDir` (`string`): Project key to read.

**Returns** `readonly string[]`

Source: [src/chat/safe-mode.ts:136](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L136)

### `getSafelistForCwd`

Function.

The safelist in effect for a working directory — what every host's gate
must read. The project's entries ([`safelistProjectKey`](https://zidane.si/docs/reference/chat.md#safelistProjectKey)), plus
entries saved under older keys for the same place, still honored but never
written again:

- the CHECKOUT root — the TUI keyed a linked worktree by its own dir
  before worktrees resolved to their main checkout;
- the exact `cwd` — the daemon/GUI keyed by raw cwd before keys were
  normalized to the project.

```ts
function getSafelistForCwd(dataDir: string, cwd: string): readonly string[]
```

**Parameters**

- `dataDir` (`string`): Directory that holds `projects.json`.
- `cwd` (`string`): Working directory. Entries under its project key, git root and own path are merged.

**Returns** `readonly string[]`

Source: [src/chat/safe-mode.ts:154](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L154)

### `getSessionHeader`

Function.

One session's header, without reading its turns when the store keeps a
header index (`getMeta`). A full load when it doesn't, or when the title
still has to be derived from the first message.

```ts
function getSessionHeader(
  store: SessionStore,
  id: string,
): Promise<SessionHeader | null>
```

**Parameters**

- `store` (`SessionStore`): Session store to read from.
- `id` (`string`): Session id.

**Returns** `Promise<SessionHeader | null>`

Source: [src/chat/store.ts:423](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L423)

### `gitBranches`

Function.

The local branches, most recently committed first, each against the default branch (the recent ones within a budget).

```ts
function gitBranches(cwd: string): Promise<GitBranchRow[]>
```

**Parameters**

- `cwd` (`string`): The checkout.

**Returns** `Promise<GitBranchRow[]>`

Source: [src/chat/git/info.ts:464](https://github.com/Tahul/zidane/blob/main/src/chat/git/info.ts#L464)

### `gitCommit`

Function.

One commit's subject, author, date and size. Only a sha-shaped argument reaches git (never an option or a range).

```ts
function gitCommit(cwd: string, sha: string): Promise<GitCommitInfo | null>
```

**Parameters**

- `cwd` (`string`): The repository.
- `sha` (`string`): A full or short sha.

**Returns** `Promise<GitCommitInfo | null>`

Source: [src/chat/git/info.ts:511](https://github.com/Tahul/zidane/blob/main/src/chat/git/info.ts#L511)

### `gitCreateBranch`

Function.

`git switch -c <name>` from where the checkout stands, uncommitted changes
carried. Refused while `running`, for a name git rejects, or one that exists.

```ts
function gitCreateBranch(
  cwd: string,
  name: string,
  running?: boolean,
): Promise<GitSwitchResult>
```

**Parameters**

- `cwd` (`string`): The checkout.
- `name` (`string`): The new branch.
- `running?` (`boolean`, default `false`): An agent works in the checkout: refused.

**Returns** `Promise<GitSwitchResult>`

Source: [src/chat/git/info.ts:857](https://github.com/Tahul/zidane/blob/main/src/chat/git/info.ts#L857)

### `gitDiff`

Function.

Unified diff of the working tree vs HEAD (staged + unstaged), plus untracked
files as new-file diffs.
`context` is the unified-context line count; `Infinity` yields whole
files so a viewer can show every line with the changes marked.

```ts
function gitDiff(cwd: string, path?: string, context?: number): Promise<string>
```

**Parameters**

- `cwd` (`string`): The folder.
- `path?` (`string`): One path only.
- `context?` (`number`, default `3`): Context lines; `Infinity`: whole files.

**Returns** `Promise<string>`

Source: [src/chat/workspace/files.ts:351](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L351)

### `githubRepoOf`

Function.

The owner and repository of a GitHub clone URL (https, scp-like or ssh); `null` for another host.

```ts
function githubRepoOf(cloneUrl: string): { owner: string; repo: string } | null
```

**Parameters**

- `cloneUrl` (`string`): The URL `git clone` takes.

**Returns** `{ owner: string; repo: string } | null`

Source: [src/chat/git/repo-info.ts:30](https://github.com/Tahul/zidane/blob/main/src/chat/git/repo-info.ts#L30)

### `gitInfo`

Function.

Where the checkout at `cwd` stands (branch, upstream, ahead / behind, uncommitted changes), the composer's git bar; `null` outside a checkout.

```ts
function gitInfo(cwd: string): Promise<GitInfo | null>
```

**Parameters**

- `cwd` (`string`): The folder.

**Returns** `Promise<GitInfo | null>`

Source: [src/chat/git/info.ts:198](https://github.com/Tahul/zidane/blob/main/src/chat/git/info.ts#L198)

### `gitOriginUrl`

Function.

The `origin` URL of the checkout holding `dir`, as written in its config; `null` outside git, without an
`origin`, or when git is missing. Clone on send hands it to a server to clone.

```ts
function gitOriginUrl(dir: string): Promise<string | null>
```

**Parameters**

- `dir` (`string`): A folder in the checkout.

**Returns** `Promise<string | null>`

Source: [src/chat/git/clone.ts:54](https://github.com/Tahul/zidane/blob/main/src/chat/git/clone.ts#L54)

### `gitPr`

Function.

The branch's open PR. None for the default branch (`gh` is not asked:
pull requests come from other branches). Cached per branch: a minute while
one is open; five minutes when there is none; half an hour once it is merged
or closed (history doesn't move); a push or a branch switch asks again with
`fresh`. After failures the wait doubles up to ten minutes. While GitHub
rate-limits `gh`, the cached answer stands.

```ts
function gitPr(cwd: string, branch: string, fresh?: boolean): Promise<GitPr | null>
```

**Parameters**

- `cwd` (`string`): The checkout.
- `branch` (`string`): The branch.
- `fresh?` (`boolean`, default `false`): Past the cache.

**Returns** `Promise<GitPr | null>`

Source: [src/chat/git/info.ts:351](https://github.com/Tahul/zidane/blob/main/src/chat/git/info.ts#L351)

### `gitPrList`

Function.

Open pull requests for the switcher; null when `gh` is missing or signed
out; `{ rateLimited }` when GitHub refused and nothing is cached.

- no query: three lists fetched apart, so a busy repository never pushes
  yours or the ones waiting on you out of view: `review-requested:@me`
  (your teams included), `--author @me`, and the most recent others.
- a query: GitHub's own search over every open PR (`--search`), so a
  repository with thousands of them is searchable from here.

```ts
function gitPrList(
  cwd: string,
  query?: string,
  fresh?: boolean,
): Promise<GitPrListResult>
```

**Parameters**

- `cwd` (`string`): The checkout.
- `query?` (`string`): Searches every open pull request.
- `fresh?` (`boolean`, default `false`): Past the cache.

**Returns** `Promise<GitPrListResult>`

Source: [src/chat/git/info.ts:719](https://github.com/Tahul/zidane/blob/main/src/chat/git/info.ts#L719)

### `gitShowHead`

Function.

Contents of `path` at `HEAD` (for split views of tracked changes).

```ts
function gitShowHead(cwd: string, path: string): Promise<string | null>
```

**Parameters**

- `cwd` (`string`): The folder.
- `path` (`string`): The file, relative to it.

**Returns** `Promise<string | null>`

Source: [src/chat/workspace/files.ts:476](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L476)

### `gitStatus`

Function.

Each changed path's status against HEAD, untracked files included; empty outside a repository.

```ts
function gitStatus(cwd: string): Promise<GitStatusEntry[]>
```

**Parameters**

- `cwd` (`string`): The folder.

**Returns** `Promise<GitStatusEntry[]>`

Source: [src/chat/workspace/files.ts:277](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L277)

### `gitStatusOf`

Function.

File entries from `run` (`git status --porcelain=v1 -z --untracked-files=<mode>`; `null`: it failed). Every untracked file
first; when that fails (a folder nobody ignored, like a fresh
`node_modules`, overflows the output or the time) again with such
folders as one record, which `parseGitStatus` drops: the tracked changes
survive. Shared with sandbox workspaces.

```ts
function gitStatusOf(
  run: (untracked: 'all' | 'normal') => Promise<string | null>,
): Promise<GitStatusEntry[]>
```

**Parameters**

- `run` (`(untracked: 'all' | 'normal') => Promise<string | null>`): Runs a git command in the repository; `null` when it failed.

**Returns** `Promise<GitStatusEntry[]>`

Source: [src/chat/workspace/files.ts:294](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L294)

### `gitSwitch`

Function.

`git switch <branch>`. Refused while `running` (the session's agent is at
work: its files must not change under it); otherwise git decides, and its
refusal comes back summarized.

```ts
function gitSwitch(
  cwd: string,
  target: GitSwitchTarget,
  running?: boolean,
): Promise<GitSwitchResult>
```

**Parameters**

- `cwd` (`string`): The checkout.
- `target` (`GitSwitchTarget`): A branch name, or `{ pr }`.
- `running?` (`boolean`, default `false`): An agent works in the checkout: refused.

**Returns** `Promise<GitSwitchResult>`

Source: [src/chat/git/info.ts:822](https://github.com/Tahul/zidane/blob/main/src/chat/git/info.ts#L822)

### `gitSync`

Function.

One sync per folder at a time: a second click joins the first.

```ts
function gitSync(cwd: string, running?: boolean): Promise<GitSyncResult>
```

**Parameters**

- `cwd` (`string`): The checkout.
- `running?` (`boolean`, default `false`): An agent works in the checkout: refused.

**Returns** `Promise<GitSyncResult>`

Source: [src/chat/git/sync.ts:74](https://github.com/Tahul/zidane/blob/main/src/chat/git/sync.ts#L74)

### `groupArtifacts`

Function.

Group by kind (built-ins first in a fixed order, then extension kinds
alphabetically); rows inside a group keep the store's newest-first order.

```ts
function groupArtifacts(
  artifacts: readonly SessionArtifact[],
  registry?: KindRegistry,
): ArtifactViewGroup[]
```

**Parameters**

- `artifacts` (`readonly SessionArtifact[]`): Session artifacts to group.
- `registry?` (`KindRegistry`): Extension registry that supplies kind labels and row renderers.

**Returns** `ArtifactViewGroup[]`

Source: [src/chat/artifacts-view.ts:44](https://github.com/Tahul/zidane/blob/main/src/chat/artifacts-view.ts#L44)

### `groupBindings`

Function.

Walk [`KEYBINDING_DEFS`](https://zidane.si/docs/reference/chat.md#KEYBINDING_DEFS) in order and bucket rows into contiguous
sections by `group`. Shared between the standalone `KeybindingsModal`
and the `Keybindings` tab inside `SettingsModal` so both surfaces
render the same catalog with the same ordering rules.

Pass `extensionRows` (resolved extension keybindings paired with
their definitions) to append an "Extensions" section at the end so
extension-contributed shortcuts are discoverable in the same UI.

```ts
function groupBindings(
  bindings: KeyBindings,
  extensionRows?: readonly { description: string; id: string; label: string; spec: string }[],
): readonly KeyBindingSection[]
```

**Parameters**

- `bindings` (`KeyBindings`): Resolved built-in bindings, keyed by action.
- `extensionRows?` (`readonly { description: string; id: string; label: string; spec: string }[]`, default `[]`): Extension-contributed keybindings to list in a trailing section.

**Returns** `readonly KeyBindingSection[]`

Source: [src/chat/keybindings.ts:702](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L702)

### `hasClaudeCodeSetup`

Function.

Whether `~/.claude` exists. The setup wizard uses it to detect Claude Code.

```ts
function hasClaudeCodeSetup(opts?: ClaudeImportOptions): boolean
```

**Parameters**

- `opts?` (`ClaudeImportOptions`, default `{}`): Discovery options. Only `home` is used.

**Returns** `boolean`

Source: [src/chat/claude-import.ts:70](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L70)

### `hasUntimedOpenCalls`

Function.

Whether a call, a subagent's included, has neither a result nor a start: a
call that may still be running, untimed. A page without one needs no
running work.

```ts
function hasUntimedOpenCalls(events: readonly CallEvent[]): boolean
```

**Parameters**

- `events` (`readonly CallEvent[]`): The page's events.

**Returns** `boolean`

Source: [src/chat/running-calls.ts:30](https://github.com/Tahul/zidane/blob/main/src/chat/running-calls.ts#L30)

### `hasUpdateAppliedMarker`

Function.

Quick existence check so hosts can decide whether to read the marker at all.

```ts
function hasUpdateAppliedMarker(cacheDir: string): boolean
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the marker file.

**Returns** `boolean`

Source: [src/chat/auto-update-restart.ts:282](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L282)

### `headerFromData`

Function.

[`SessionHeader`](https://zidane.si/docs/reference/chat.md#SessionHeader) of a fully materialized session.

```ts
function headerFromData(data: SessionData): SessionHeader
```

**Parameters**

- `data` (`SessionData`): The loaded session.

**Returns** `SessionHeader`

Source: [src/chat/store.ts:405](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L405)

### `hintsLength`

Function.

Plain-text width estimate for a list of [`Hint`](https://zidane.si/docs/reference/chat.md#Hint)s rendered as a
hint row (keys, labels, `HINT_GAP` between hints). Exported so
prompt-box overlays can run the same responsive math as the bottom-bar
footer when deciding whether trigger hints fit. Pure / total.

```ts
function hintsLength(hints: readonly Hint[]): number
```

**Parameters**

- `hints` (`readonly Hint[]`): Hints to measure.

**Returns** `number`

Source: [src/chat/hints.ts:137](https://github.com/Tahul/zidane/blob/main/src/chat/hints.ts#L137)

### `hostCredentialKey`

Function.

Where a host's token lives in the credentials store.

```ts
function hostCredentialKey(id: string): string
```

**Parameters**

- `id` (`string`): Host id.

**Returns** `string`

Source: [src/chat/hosts/registry.ts:40](https://github.com/Tahul/zidane/blob/main/src/chat/hosts/registry.ts#L40)

### `hostImportItems`

Function.

Absolute paths of this machine as import items (a drop headed for a folder
elsewhere: a sandbox, a server): files read when their turn comes, folders
walked one entry at a time. Symlinks and special files are left out.
The walk stops once the import limits are passed: what it returns then is
still over them, so `importEntries` refuses it without a whole tree read.

```ts
function hostImportItems(
  paths: readonly string[],
  limits?: { maxBytes?: number; maxEntries?: number },
): Promise<EntryImportItem[]>
```

**Parameters**

- `paths` (`readonly string[]`): The dropped paths.
- `limits?` (`{ maxBytes?: number; maxEntries?: number }`, default `{}`): When to stop walking.

**Returns** `Promise<EntryImportItem[]>`

Source: [src/chat/workspace/local-entries.ts:111](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/local-entries.ts#L111)

### `hostsFilePath`

Function.

Returns the path of the file that stores the saved hosts.

```ts
function hostsFilePath(dataDir: string): string
```

**Parameters**

- `dataDir` (`string`): Application data directory.

**Returns** `string`

Source: [src/chat/hosts/registry.ts:31](https://github.com/Tahul/zidane/blob/main/src/chat/hosts/registry.ts#L31)

### `imageGenerationTools`

Function.

The `generate_image` tool for a chat session, keyed by name: empty when
`Settings.imageGeneration` is off or no connected provider can generate
images. `Settings.imageModel` picks the default model; a stale pick falls
back to the first available provider's default.

```ts
function imageGenerationTools(
  input: { buildProvider: (key: string) => Provider; providers: Readonly<Record<string, ProviderDescriptor>>; settings: Pick<Settings, 'imageGeneration' | 'imageModel'> },
): Record<string, ToolDef>
```

**Parameters**

- `input` (`{ buildProvider: (key: string) => Provider; providers: Readonly<Record<string, ProviderDescriptor>>; settings: Pick<Settings, 'imageGeneration' | 'imageModel'> }`): Settings, provider descriptors and a provider builder.

**Returns** `Record<string, ToolDef>`

Source: [src/chat/image-models.ts:150](https://github.com/Tahul/zidane/blob/main/src/chat/image-models.ts#L150)

### `imageModelSources`

Function.

The providers a session can generate images with: every provider with
image models whose instance builds and reports itself available, keyed by
provider key (the `<provider>` of `generate_image`'s `model` argument).

```ts
function imageModelSources(
  providers: Readonly<Record<string, ProviderDescriptor>>,
  buildProvider: (key: string) => Provider,
): ImageModelSource[]
```

**Parameters**

- `providers` (`Readonly<Record<string, ProviderDescriptor>>`): Provider descriptors keyed by id.
- `buildProvider` (`(key: string) => Provider`): Builds a provider for a provider key. May throw.

**Returns** `ImageModelSource[]`

Source: [src/chat/image-models.ts:123](https://github.com/Tahul/zidane/blob/main/src/chat/image-models.ts#L123)

### `importFromClaudeCode`

Function.

Import Claude Code data from THIS machine into THIS machine: `store` must
be the local session store (never a remote host's), MCP servers, skills,
commands and subagents go to this machine's `.zidane` files. Pass `activeHost` (the
window's host) to get the remote-host note in `notes`.

```ts
function importFromClaudeCode(
  store: SessionStore,
  opts?: ClaudeImportOptions & { activeHost?: ClaudeImportActiveHost | null; agents?: boolean; commands?: boolean; mcps?: boolean; sessions?: boolean; skills?: boolean },
): Promise<ClaudeImportSummary>
```

**Parameters**

- `store` (`SessionStore`): The local session store that receives imported sessions.
- `opts?` (`ClaudeImportOptions & { activeHost?: ClaudeImportActiveHost | null; agents?: boolean; commands?: boolean; mcps?: boolean; sessions?: boolean; skills?: boolean }`, default `{}`): Discovery options plus per-category switches (`sessions`, `mcps`, `skills`, `commands`, `agents`), each on unless set to `false`, and the optional `activeHost`.

**Returns** `Promise<ClaudeImportSummary>`

**Example**

```ts
import { importFromClaudeCode } from 'zidane/chat'
import type { SessionStore } from 'zidane'

declare const store: SessionStore

const summary = await importFromClaudeCode(store, { mcps: false })
console.log(summary.sessions?.imported, summary.notes)
```

Source: [src/chat/claude-import.ts:890](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L890)

### `indexOfServerRow`

Function.

Index in `rows` of the server header for the given parent name. `-1`
when not present. Used by collapse handlers to snap the cursor up
to the parent BEFORE shrinking the row list — otherwise the cursor
would land on whichever sibling row now occupies the same flat
index.

```ts
function indexOfServerRow(rows: readonly VisibleMcpRow[], parentName: string): number
```

**Parameters**

- `rows` (`readonly VisibleMcpRow[]`): The visible rows.
- `parentName` (`string`): Server name to look for.

**Returns** `number`

Source: [src/chat/mcp-rows.ts:103](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-rows.ts#L103)

### `InteractionsProvider`

Function.

React provider that holds the FIFO queue of pending interactions. Pending entries are cancelled when it unmounts.

```ts
function InteractionsProvider(props: { children: ReactNode }): Element
```

**Parameters**

- `props` (`{ children: ReactNode }`): Component props.

**Returns** `Element`

Source: [src/chat/interactions.tsx:33](https://github.com/Tahul/zidane/blob/main/src/chat/interactions.tsx#L33)

### `invalidateDiagnostics`

Function.

Forget cached runs for cwd (after our own writes / tool results). A run in
progress still answers the requests it has, but no later one.

```ts
function invalidateDiagnostics(cwd: string): void
```

**Parameters**

- `cwd` (`string`): The folder.

Source: [src/chat/workspace/diagnostics/runner.ts:370](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/diagnostics/runner.ts#L370)

### `isAccountCredKey`

Function.

True when `key` is one of `fileKey`'s `#account:N` slots.

```ts
function isAccountCredKey(fileKey: string, key: string): boolean
```

**Parameters**

- `fileKey` (`string`): The provider's canonical credential file key.
- `key` (`string`): The key to test.

**Returns** `boolean`

Source: [src/chat/cred-keys.ts:78](https://github.com/Tahul/zidane/blob/main/src/chat/cred-keys.ts#L78)

### `isAutoInstallCandidate`

Function.

Whether a check result is one an automatic install may act on: a newer
release on the `latest` dist-tag read from the registry just now (not a
stale cache), not a prerelease, not opted out by env, and not a version
that failed before (see [`AUTO_INSTALL_RETRY_BACKOFF_MS`](https://zidane.si/docs/reference/chat.md#AUTO_INSTALL_RETRY_BACKOFF_MS)).

```ts
function isAutoInstallCandidate(
  status: UpdateStatus | null,
  options: AutoInstallCandidateOptions,
): true | AutoInstallSkipReason
```

**Parameters**

- `status` (`UpdateStatus | null`): Result of the background check.
- `options` (`AutoInstallCandidateOptions`): Cache directory and environment.

**Returns** `true | AutoInstallSkipReason`: `true`, or why not.

Source: [src/chat/auto-update-auto-install.ts:182](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L182)

### `isEnvironmentEnabled`

Function.

Whether `kind` may be picked. `local` always can: it is the fallback. An
`offByDefault` provider needs turning on first — pass it to honour that.

```ts
function isEnvironmentEnabled(
  settings: object | undefined,
  kind: string,
  provider?: Pick<EnvironmentProvider, 'offByDefault'>,
): boolean
```

**Parameters**

- `settings` (`object | undefined`): Settings object, or `undefined`.
- `kind` (`string`): Environment kind to check.
- `provider?` (`Pick<EnvironmentProvider, 'offByDefault'>`): The kind's provider. When it is `offByDefault`, the kind must also be turned on in settings.

**Returns** `boolean`

Source: [src/chat/environment-settings.ts:99](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L99)

### `isInteractionTool`

Function.

True when `name` is one of the interaction tool canonical names.

```ts
function isInteractionTool(name: string): boolean
```

**Parameters**

- `name` (`string`): Tool name to test.

**Returns** `boolean`

Source: [src/chat/interactions-protocol.ts:64](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L64)

### `isMcpAuthRefusal`

Function.

A refusal for want of credentials: HTTP 401 / 403, however the SDK words it.

```ts
function isMcpAuthRefusal(error: unknown): boolean
```

**Parameters**

- `error` (`unknown`): The error thrown by a connection attempt.

**Returns** `boolean`

Source: [src/chat/mcp-test-outcome.ts:38](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-test-outcome.ts#L38)

### `isModelInteraction`

Function.

Whether a queued request came from a model tool call. Automatic mode
answers on the model's behalf, so it must leave host-originated prompts
alone: their consumer is local code awaiting a real decision, with no
model on the other end to act on an instruction sentence.

```ts
function isModelInteraction(request: InteractionRequest): boolean
```

**Parameters**

- `request` (`InteractionRequest`): Queued request to classify.

**Returns** `boolean`

Source: [src/chat/interactions-protocol.ts:356](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L356)

### `isOnSafelist`

Function.

True when a call matches ANY entry in the project's safelist (or is
implicitly safe).

For `shell` commands, the full command string is parsed into individual
commands (handling `&&`, `||`, `;`, `|`, `$(…)`, backticks, subshells).
Every command head must be covered by at least one safelist entry for
the call to pass. If parsing fails, returns `false` (prompt the user).

```ts
function isOnSafelist(
  entries: readonly string[],
  tool: string,
  input: Record<string, unknown>,
): boolean
```

**Parameters**

- `entries` (`readonly string[]`): The project's safelist.
- `tool` (`string`): Tool name.
- `input` (`Record<string, unknown>`): The tool call's input.

**Returns** `boolean`

Source: [src/chat/safe-mode.ts:372](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L372)

### `isReservedCredKey`

Function.

True when a key claims a reserved slot namespace.

Built-in keys are plain identifiers (`anthropic`, `openai-codex`), but
`ProviderDescriptor.key` / `credentialFileKey` are open strings — so the
reservation is checked, not assumed.

```ts
function isReservedCredKey(fileKey: string): boolean
```

**Parameters**

- `fileKey` (`string`): The credential file key to check.

**Returns** `boolean`

Source: [src/chat/cred-keys.ts:164](https://github.com/Tahul/zidane/blob/main/src/chat/cred-keys.ts#L164)

### `isSecretCustomField`

Function.

Whether a provider's custom field holds a secret (never sent back, kept when left blank).

```ts
function isSecretCustomField(key: string): boolean
```

**Parameters**

- `key` (`string`): The field's key.

**Returns** `boolean`

Source: [src/chat/credentials.ts:992](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L992)

### `isStaleRunMarker`

Function.

A stored `running` status older than `STALE_RUN_MARKER_MS` whose
owner no longer holds it, or held by a dead owner: a run whose process
died before settling it. Lists show it idle unless a host says the session
runs.

```ts
function isStaleRunMarker(
  status: string | undefined,
  updatedAt: number,
  now?: number,
  owner?: RunOwner,
): boolean
```

**Parameters**

- `status` (`string | undefined`): Stored session status.
- `updatedAt` (`number`): Time of the last update in epoch ms.
- `now?` (`number`): Current time in epoch ms. Default: `Date.now()`.
- `owner?` (`RunOwner`): Stored run owner. Absent: the time rule alone.

**Returns** `boolean`

Source: [src/chat/host/runs/guard.ts:30](https://github.com/Tahul/zidane/blob/main/src/chat/host/runs/guard.ts#L30)

### `isTitleStale`

Function.

Ask the decision model whether `title` is stale. A failed decision comes
back as `failure` (no answer is not a "keep": the caller retries later).

```ts
function isTitleStale(
  decide: AgentDecide,
  title: string,
  turns: readonly SessionTurn[],
): Promise<TitleStaleness>
```

**Parameters**

- `decide` (`AgentDecide`): Decision function that asks the decision model.
- `title` (`string`): The current title.
- `turns` (`readonly SessionTurn[]`): The session's turns.

**Returns** `Promise<TitleStaleness>`

Source: [src/chat/auto-title.ts:208](https://github.com/Tahul/zidane/blob/main/src/chat/auto-title.ts#L208)

### `isTodoTool`

Function.

True when `name` is one of the todo tool canonical names.

```ts
function isTodoTool(name: string): boolean
```

**Parameters**

- `name` (`string`): Tool name to test.

**Returns** `boolean`

Source: [src/chat/todos.ts:52](https://github.com/Tahul/zidane/blob/main/src/chat/todos.ts#L52)

### `isTransientFailure`

Function.

Whether every failure in the run may pass on its own: the registry could not
be read, the release is not downloadable yet, or the package manager timed
out. A package-manager error or a wrong version on disk is not.

```ts
function isTransientFailure(report: UpgradeReport): boolean
```

**Parameters**

- `report` (`UpgradeReport`): Result of [`upgradeInstalledPackages`](https://zidane.si/docs/reference/chat.md#upgradeInstalledPackages).

**Returns** `boolean`

Source: [src/chat/auto-update-auto-install.ts:286](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L286)

### `isUpdateLockStale`

Function.

Whether a lock no longer protects a running install: its process is gone
on this host, a live one is older than `UPDATE_LOCK_LIVE_STALE_MS`,
or a holder that can't be probed is older than [`UPDATE_LOCK_STALE_MS`](https://zidane.si/docs/reference/chat.md#UPDATE_LOCK_STALE_MS).

```ts
function isUpdateLockStale(
  holder: UpdateLockHolder,
  options?: UpdateLockOptions,
): boolean
```

**Parameters**

- `holder` (`UpdateLockHolder`): The recorded holder.
- `options?` (`UpdateLockOptions`, default `{}`): Injected clock, liveness probe and host name.

**Returns** `boolean`

Source: [src/chat/auto-update-lock.ts:102](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-lock.ts#L102)

### `isWindowOnlySetting`

Function.

Tells whether a settings key stays with the window instead of the host.

```ts
function isWindowOnlySetting(key: string): boolean
```

**Parameters**

- `key` (`string`): Settings key to check.

**Returns** `boolean`: `true` for window-only keys (UI settings and [`WINDOW_LOCAL_SETTINGS`](https://zidane.si/docs/reference/chat.md#WINDOW_LOCAL_SETTINGS)).

Source: [src/chat/host-scoped-settings.ts:67](https://github.com/Tahul/zidane/blob/main/src/chat/host-scoped-settings.ts#L67)

### `keybindingsPath`

Function.

Path of the user-level keybindings file.

```ts
function keybindingsPath(userDir: string): string
```

**Parameters**

- `userDir` (`string`): User-level config directory.

**Returns** `string`

Source: [src/chat/keybindings.ts:657](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L657)

### `lastContextSizeFromTurns`

Function.

Effective context size of the most recent assistant turn — drives the footer
indicator. Walk from the end of `turns` and return the cache-aware input-token total
of the most recent **parent** (depth-0) assistant turn. Subagent turns
are skipped because their context window is the child's, not the parent's
— surfacing a subagent's `usage.input` as the footer's "context used"
after a session ends mid-spawn would be misleading. The next prompt
feeds the parent, so the parent's last view is what matters.

Zero-context placeholder turns are also skipped. `executeTurn`
synthesizes an assistant turn with `usage: { input: 0, output: 0 }`
when the provider stream throws / aborts before any usage is reported
(see `src/loop-turn-stream.ts`'s catch path) — that turn is purely a shape
contract for resume, not a real context-window measurement. Without
the skip, an abort mid-stream shadows the prior real turn and the
footer reads `ctx 0` on the next session load even though earlier
turns accumulated meaningful context (and `run.cost`).

`runs` is optional. Without it depth is unknown, so every assistant turn
qualifies.

```ts
function lastContextSizeFromTurns(
  turns: SessionTurn[],
  runs?: readonly SessionRun[],
): number
```

**Parameters**

- `turns` (`SessionTurn[]`): Persisted turns in order.
- `runs?` (`readonly SessionRun[]`, default `[]`): Runs the turns belong to, used to skip subagent turns.

**Returns** `number`

Source: [src/chat/store.ts:1585](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1585)

### `linkCatalogs`

Function.

Keeps the models catalog in step with the providers catalog: the models
base is the providers' models after their transforms, so every provider
change reloads it. Returns the unsubscribe.

```ts
function linkCatalogs(
  catalogs: { models: Pick<ModelsCatalog, 'reload'>; providers: Pick<ProvidersCatalog, 'subscribe'> },
): () => void
```

**Parameters**

- `catalogs` (`{ models: Pick<ModelsCatalog, 'reload'>; providers: Pick<ProvidersCatalog, 'subscribe'> }`): The shared providers and models catalogs.

**Returns** `() => void`

Source: [src/chat/host/agent-build/catalog-ports.ts:251](https://github.com/Tahul/zidane/blob/main/src/chat/host/agent-build/catalog-ports.ts#L251)

### `listDecisionModels`

Function.

Decision models of the signed-in providers, in provider order. Providers
whose credentials can't send decisions are listed with `available: false`
and a `reason`, so pickers can say why.

```ts
function listDecisionModels(
  input: { authed: readonly { available: boolean; key: string; label: string }[]; buildProvider?: (key: string) => Provider; providers: Readonly<Record<string, ProviderDescriptor>> },
): DecisionModelEntry[]
```

**Parameters**

- `input` (`{ authed: readonly { available: boolean; key: string; label: string }[]; buildProvider?: (key: string) => Provider; providers: Readonly<Record<string, ProviderDescriptor>> }`): Provider descriptors, the signed-in provider list and an optional provider builder.

**Returns** `DecisionModelEntry[]`

Source: [src/chat/decision-models.ts:61](https://github.com/Tahul/zidane/blob/main/src/chat/decision-models.ts#L61)

### `listDiagnosticsTools`

Function.

The diagnostics tools and whether each runs for `cwd`.

```ts
function listDiagnosticsTools(
  cwd: string,
  tools?: readonly DiagnosticsTool[],
): Promise<DiagnosticsToolInfo[]>
```

**Parameters**

- `cwd` (`string`): The folder.
- `tools?` (`readonly DiagnosticsTool[]`, default `BUILTIN_TOOLS`): The tools to look for.

**Returns** `Promise<DiagnosticsToolInfo[]>`

Source: [src/chat/workspace/diagnostics/runner.ts:135](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/diagnostics/runner.ts#L135)

### `listExtensionSource`

Function.

Lists the extensions of a source, with what installs each one. Never
throws: a source that cannot be read gives an empty list and `error`,
except the default source (`Tahul/zidane`), which then lists the native
catalog shipped in the app (`builtin: true`) with the error.

```ts
function listExtensionSource(
  spec: string,
  opts?: ListExtensionSourceOptions,
): Promise<ExtensionSourceListing>
```

**Parameters**

- `spec` (`string`): A source: `owner/repo[#ref][:dir]`, a GitHub URL, `npm:`-less package name (`@scope/pkg[@version]`), an npmjs.com package link, or a folder.
- `opts?` (`ListExtensionSourceOptions`, default `{}`): A GitHub token, the running version, a signal, and test overrides.

**Returns** `Promise<ExtensionSourceListing>`

**Example**

```ts
import { defaultExtensionSource, listExtensionSource } from 'zidane/chat'

const listing = await listExtensionSource(defaultExtensionSource('9.0.4'))
for (const ext of listing.extensions)
  console.log(ext.displayName, ext.install)
```

Source: [src/chat/extension-browse.ts:472](https://github.com/Tahul/zidane/blob/main/src/chat/extension-browse.ts#L472)

### `listImageModels`

Function.

Image models of the signed-in providers, in provider order. Providers whose
credentials cannot generate images are listed with `available: false` and a
`reason`, so pickers can say why.

```ts
function listImageModels(
  input: { authed: readonly { available: boolean; key: string; label: string }[]; buildProvider?: (key: string) => Provider; providers: Readonly<Record<string, ProviderDescriptor>> },
): ImageModelEntry[]
```

**Parameters**

- `input` (`{ authed: readonly { available: boolean; key: string; label: string }[]; buildProvider?: (key: string) => Provider; providers: Readonly<Record<string, ProviderDescriptor>> }`): Provider descriptors, the signed-in provider list and an optional provider builder.

**Returns** `ImageModelEntry[]`

Source: [src/chat/image-models.ts:74](https://github.com/Tahul/zidane/blob/main/src/chat/image-models.ts#L74)

### `listProjectFiles`

Function.

Discover every non-ignored file under `cwd`, plus the directories that
contain them (derived from the file paths, `kind: 'dir'`). Tries
`git ls-files` first; on failure (no git, not a repo, abort) walks the
fs with a hand-rolled skip list.

Errors are not thrown — the function always returns an array (possibly
empty). Callers wanting failure diagnostics can opt into them via
`ZIDANE_DEBUG`.

```ts
function listProjectFiles(opts?: ListProjectFilesOptions): Promise<FileEntry[]>
```

**Parameters**

- `opts?` (`ListProjectFilesOptions`, default `{}`): Working directory, file cap, and abort signal. All optional.

**Returns** `Promise<FileEntry[]>`

Source: [src/chat/files-discovery.ts:107](https://github.com/Tahul/zidane/blob/main/src/chat/files-discovery.ts#L107)

### `listSafelists`

Function.

Every project with at least one safelist entry.

```ts
function listSafelists(dataDir: string): SafelistProject[]
```

**Parameters**

- `dataDir` (`string`): Directory that holds `projects.json`.

**Returns** `SafelistProject[]`

Source: [src/chat/safe-mode.ts:191](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L191)

### `listSessionMeta`

Function.

Load every session and project it to the compact `SessionMeta` shape used by
the picker. Sorted by recency via the underlying store's `list()` contract
(sqlite store returns by `updated_at DESC`). `/side` sessions are left out,
like every session list (`isSideSession`).

Robust to per-row failures: `Promise.allSettled` so a single corrupt or
unreadable row (malformed JSON, partial write, transient I/O error)
doesn't take down the entire picker. Failed rows are silently skipped;
a stderr line is emitted under `ZIDANE_DEBUG` for diagnosis.

```ts
function listSessionMeta(
  store: SessionStore,
  filter?: { projectRoot?: string | null },
): Promise<SessionMeta[]>
```

**Parameters**

- `store` (`SessionStore`): Session store to read from.
- `filter?` (`{ projectRoot?: string | null }`): Optional filter. `projectRoot` restricts the list to one project.

**Returns** `Promise<SessionMeta[]>`

Source: [src/chat/store.ts:209](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L209)

### `listWorkspace`

Function.

Whole tree as relative paths (dirs implied, explicit trailing `/` for
empty dirs), bounded. `.gitignore` is honoured through one
`git ls-files` call (instant, unlike `git status --ignored`, which walks
`node_modules` and can take tens of seconds on a real repo); pass
`showIgnored` to include ignored entries, flagged so the tree can dim
them. `.git` itself is never listed.

An ignored folder is one node (`dist/`, `ignored`), never walked: list it
with `dir` when the user opens it, which walks that folder the same way
(its ignored subfolders one node each again), so a `node_modules`-sized
folder costs one `readdir` per level opened. Past the entry cap the
listing is cut and says so (`truncated`).

```ts
function listWorkspace(
  cwd: string,
  options?: { dir?: string; showIgnored?: boolean },
): Promise<WorkspaceListing>
```

**Parameters**

- `cwd` (`string`): The folder.
- `options?` (`{ dir?: string; showIgnored?: boolean }`, default `{}`): Options.

**Returns** `Promise<WorkspaceListing>`

Source: [src/chat/workspace/files.ts:71](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L71)

### `liveAgentProfiles`

Function.

A resolved config's agent profiles as the extension agents catalog has
them now. `config.agents` is overlaid once, when the config is resolved;
an extension registers profiles later too (one per bot it creates), and a
host that caches its config would never offer or build them. Read profiles
through this wherever a config outlives a catalog change.

```ts
function liveAgentProfiles(
  config: { agents: AgentRegistry; extensions?: { runtime?: ExtensionRuntimeOutputs } },
): AgentRegistry
```

**Parameters**

- `config` (`{ agents: AgentRegistry; extensions?: { runtime?: ExtensionRuntimeOutputs } }`): The resolved config: its profiles and its extensions.

**Returns** `AgentRegistry`

Source: [src/chat/agents-catalog.ts:155](https://github.com/Tahul/zidane/blob/main/src/chat/agents-catalog.ts#L155)

### `loadMarkdownAgents`

Function.

Builtin Explore/Plan plus markdown agents. User-scope files override
builtins (project files are refused with a diagnostic); among files, first-wins by name in root order. Never throws.

```ts
function loadMarkdownAgents(
  opts: LoadMarkdownOptions & { modelContext: ModelAliasContext },
): { diagnostics: MarkdownDefDiagnostic[]; subagents: SubagentRegistry }
```

**Parameters**

- `opts` (`LoadMarkdownOptions & { modelContext: ModelAliasContext }`): Search roots and switches, plus the model context used to resolve `model:` aliases.

**Returns** `{ diagnostics: MarkdownDefDiagnostic[]; subagents: SubagentRegistry }`

Source: [src/markdown-defs/index.ts:101](https://github.com/Tahul/zidane/blob/main/src/markdown-defs/index.ts#L101)

### `loadMarkdownCommands`

Function.

Markdown slash commands, first-wins by name in root order. Never throws.

```ts
function loadMarkdownCommands(
  opts: LoadMarkdownCommandsOptions,
): { commands: Command[]; diagnostics: MarkdownDefDiagnostic[] }
```

**Parameters**

- `opts` (`LoadMarkdownCommandsOptions`): Search roots, project and foreign-config switches, and an optional skill-name check.

**Returns** `{ commands: Command[]; diagnostics: MarkdownDefDiagnostic[] }`

Source: [src/markdown-defs/index.ts:64](https://github.com/Tahul/zidane/blob/main/src/markdown-defs/index.ts#L64)

### `loadMcpToolsCache`

Function.

Best-effort cache read. Returns `{}` when the file is missing, when
JSON.parse rejects, or when the top-level shape isn't a plain object —
a corrupt cache must never crash the host on startup.

Per-entry shape validation is light (presence of an array `tools`):
deeper validation would lock the cache to a single zidane version,
which is the opposite of what a long-lived cache wants. Entries that
survive the light check feed back through `McpToolSchema`'s `unknown`
`inputSchema` so downstream readers stay tolerant of upstream churn.

```ts
function loadMcpToolsCache(opts: { dataDir: string }): McpToolsCache
```

**Parameters**

- `opts` (`{ dataDir: string }`): The data directory holding the cache file.

**Returns** `McpToolsCache`

Source: [src/chat/mcp-tools-cache.ts:95](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tools-cache.ts#L95)

### `loadState`

Function.

Reads the TUI state file. Returns `{}` when it is missing or unreadable.

```ts
function loadState(path: string): TuiState
```

**Parameters**

- `path` (`string`): Path of the state file.

**Returns** `TuiState`

Source: [src/chat/store.ts:153](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L153)

### `localDayString`

Function.

Local `YYYY-MM-DD` of a date, as SQLite's `date(…, 'localtime')` buckets it.

```ts
function localDayString(d: Date): string
```

**Parameters**

- `d` (`Date`): The date.

**Returns** `string`

Source: [src/chat/usage-summary.ts:126](https://github.com/Tahul/zidane/blob/main/src/chat/usage-summary.ts#L126)

### `mainCheckoutRoot`

Function.

The main checkout a LINKED WORKTREE belongs to, or `null` when
`checkoutRoot` is not one. Does not walk: `checkoutRoot` must be a
checkout root (what [`findGitRoot`](https://zidane.si/docs/reference/chat.md#findGitRoot) returns).

A linked worktree's `.git` is a file (`gitdir: <main>/.git/worktrees/<name>`)
and that gitdir holds a `commondir` file pointing back at the shared
`<main>/.git`. Everything else resolves to `null` and stays its own
project:

- a regular checkout (`.git` is a directory);
- a submodule (its gitdir has no `commondir` — a submodule IS a
  separate repo);
- a worktree of a bare repo or of a `--separate-git-dir` repo (the
  common dir isn't a `.git` inside a working tree, so there is no main
  checkout to belong to);
- anything unreadable or malformed.

```ts
function mainCheckoutRoot(checkoutRoot: string): string | null
```

**Parameters**

- `checkoutRoot` (`string`): Root of a checkout, as returned by [`findGitRoot`](https://zidane.si/docs/reference/chat.md#findGitRoot).

**Returns** `string | null`

Source: [src/chat/project-root.ts:84](https://github.com/Tahul/zidane/blob/main/src/chat/project-root.ts#L84)

### `makeRequestInteraction`

Function.

Build the `requestInteraction` callback for `createInteractionTools` from
the React-side [`InteractionsActions`](https://zidane.si/docs/reference/chat.md#InteractionsActions). Each call adds a fresh entry
to the queue whose `resolve` / `cancel` route back to one Promise that
the tool's `execute` awaits.

```ts
function makeRequestInteraction(
  actions: InteractionsActions,
): (request: InteractionRequest, options?: RequestInteractionOptions) => Promise<InteractionResponse>
```

**Parameters**

- `actions` (`InteractionsActions`): Queue actions from [`useInteractionsActions`](https://zidane.si/docs/reference/chat.md#useInteractionsActions).

**Returns** `(request: InteractionRequest, options?: RequestInteractionOptions) => Promise<InteractionResponse>`

Source: [src/chat/interactions-protocol.ts:1324](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L1324)

### `makeToolContext`

Function.

A folder's files: listing, guarded reads and writes, git status and diffs, search, diagnostics, path resolution.

```ts
function makeToolContext(
  cwd: string,
  timeoutMs: number,
  options?: ToolContextOptions,
): DiagnosticsToolContext
```

**Parameters**

- `cwd` (`string`): The folder.
- `timeoutMs` (`number`): Per command.
- `options?` (`ToolContextOptions`, default `{}`): The abort signal, the state folder.

**Returns** `DiagnosticsToolContext`

Source: [src/chat/workspace/diagnostics/context.ts:52](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/diagnostics/context.ts#L52)

### `marginTopFor`

Function.

Resolve the top margin (in rows) for an event given the one rendered
just before it. Context-aware rules:

- A `tool` / `tool-result` event right after another
  `tool` / `tool-result` collapses to a tight list — call→result
  pairs and back-to-back calls read as one logical block.
- Consecutive `task-notification` banners stack tight — a burst of
  background-task completions reads as one log column rather than
  scattered banners.

NB: parent-level events (`depth === 0`) following a subagent block
(`depth > 0`) get their normal default margin. The subagent's
`🌳` end-marker does not provide enough visual separation to
collapse the transition to 0: a 2-cell emoji on the same row as the
close marker doesn't create a vertical break, and in
hide-subagent-output mode the SubagentBlock box isn't rendered
either, so the parent's follow-up markdown would be glued directly
against the close line. Subagents should READ like
tool calls: open with breathing room, close tight inside, and open
a clean gap before the parent's next thought — that's the contract
the tool-call → markdown transition uses, mirrored here.

Renderer-agnostic — TUI uses it as Yoga `marginTop`; a CSS host can
use the same number as the row's top margin in `em` / `rem`.

```ts
function marginTopFor(event: StreamEvent, previous: StreamEvent | undefined): number
```

**Parameters**

- `event` (`StreamEvent`): The event being rendered.
- `previous` (`StreamEvent | undefined`): The event rendered just above it, if any.

**Returns** `number`

Source: [src/chat/store.ts:1521](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1521)

### `maskToOutcomeKinds`

Function.

Convert a per-hunk approval mask into an `EditOutcome[]`. `true` →
`applied`; `false` → `denied` with the supplied reason.

Length is `Math.max(mask.length, fallbackLength)` so callers passing a
shorter mask still get a fully-populated array — missing entries
default to applied, matching the "no decision => keep" convention.

```ts
function maskToOutcomeKinds(
  mask: readonly boolean[],
  fallbackLength: number,
  deniedReason?: string,
): EditOutcome[]
```

**Parameters**

- `mask` (`readonly boolean[]`): Per-hunk flags where `true` keeps the hunk.
- `fallbackLength` (`number`): Number of hunks, used when the mask is shorter.
- `deniedReason?` (`string`, default `'denied by user'`): Reason recorded on denied hunks.

**Returns** `EditOutcome[]`

Source: [src/chat/edit-approval.ts:27](https://github.com/Tahul/zidane/blob/main/src/chat/edit-approval.ts#L27)

### `matchesBinding`

Function.

Test a KeyEvent against a parsed-or-string binding. Accepts either
form so callers can pre-parse (hot path) or pass the raw spec for
one-off checks.

Modifier semantics: ALL declared modifiers must be pressed AND no
extra modifiers may be pressed. So `'o'` does NOT match `ctrl+o`,
and `'ctrl+o'` does NOT match `ctrl+shift+o`. This avoids surprises
where a binding that should be specific accidentally fires for a
superset of key combinations.

```ts
function matchesBinding(
  event: { alt?: boolean; ctrl?: boolean; meta?: boolean; name?: string; option?: boolean; shift?: boolean },
  spec: string | ParsedBinding,
): boolean
```

**Parameters**

- `event` (`{ alt?: boolean; ctrl?: boolean; meta?: boolean; name?: string; option?: boolean; shift?: boolean }`): Key event to test.
- `spec` (`string | ParsedBinding`): Binding spec string, or a chord already parsed with [`parseBindingSpec`](https://zidane.si/docs/reference/chat-pure.md#parseBindingSpec).

**Returns** `boolean`

Source: [src/chat/keybindings.ts:543](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L543)

### `matchesSafelistEntry`

Function.

Test whether a `{ tool, input }` pair is covered by one safelist entry.

Supported entry shapes:

- `"<tool>"` — broad match on tool name.
- `"<tool>:<token>:*"` — match when the primary arg's first token
  equals `<token>`.
- `"browser_open:<host>"` / `"browser_navigate:<host>"` — match a
  load of that host (port included).

This function matches a **single command** against a **single entry**.
For shell commands that chain multiple programs (`&&`, `||`, `;`,
`$(…)`, etc.), use [`isOnSafelist`](https://zidane.si/docs/reference/chat.md#isOnSafelist) which parses the full command
and checks every head token independently.

Entries that don't fit either shape are ignored (forward-compat for
future pattern syntax — readers shouldn't choke on entries written
by a newer version of the TUI).

```ts
function matchesSafelistEntry(
  entry: string,
  tool: string,
  input: Record<string, unknown>,
): boolean
```

**Parameters**

- `entry` (`string`): One safelist entry: a tool name, or `tool:scope` for a scoped one.
- `tool` (`string`): Tool name.
- `input` (`Record<string, unknown>`): The tool call's input.

**Returns** `boolean`

Source: [src/chat/safe-mode.ts:339](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L339)

### `McpAuthProvider`

Function.

Mount once near the top of the React tree (above `ModalRoot`). Children
use `useMcpAuthState()` to read and `useMcpAuthDispatch()` to fire events.

```ts
function McpAuthProvider(props: { children: ReactNode }): Element
```

**Parameters**

- `props` (`{ children: ReactNode }`): React props. `children` renders inside both contexts.

**Returns** `Element`

Source: [src/chat/mcp-auth-context.tsx:44](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-auth-context.tsx#L44)

### `mcpCredentialsPath`

Function.

Returns the path of the MCP credentials file inside `dataDir`.

```ts
function mcpCredentialsPath(dataDir: string): string
```

**Parameters**

- `dataDir` (`string`): Directory that holds the file.

**Returns** `string`

Source: [src/chat/mcp-credentials.ts:41](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-credentials.ts#L41)

### `mcpFailureEvent`

Function.

The status a failed connection leaves, from `classifyMcpTest`'s answer
(never called with `ok`: a connection that worked leaves no failure).
A server that asked for OAuth keeps "sign-in failed" for an unexplained
failure (its tokens are the likely cause, and sign-in stays offered).

```ts
function mcpFailureEvent(
  name: string,
  outcome: { kind: 'error' | 'ok' | 'needs-auth' | 'token-rejected'; message?: string },
  oauthTagged: boolean,
): McpAuthEvent
```

**Parameters**

- `name` (`string`): Server name.
- `outcome` (`{ kind: 'error' | 'ok' | 'needs-auth' | 'token-rejected'; message?: string }`): The classified test result. Pass any kind except `ok`.
- `oauthTagged` (`boolean`): Whether the server's config asks for OAuth.

**Returns** `McpAuthEvent`

Source: [src/chat/mcp-auth-state.ts:186](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-auth-state.ts#L186)

### `mcpFailureLine`

Function.

The first line of a failure, never empty.

```ts
function mcpFailureLine(error: unknown): string
```

**Parameters**

- `error` (`unknown`): The error thrown by a connection attempt.

**Returns** `string`

Source: [src/chat/mcp-test-outcome.ts:51](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-test-outcome.ts#L51)

### `mcpsWritePath`

Function.

Resolve the CANONICAL write target for a scope — always the
`.{prefix}/mcps.json` flavor, never `.agents` (we don't own that
convention) and never `mcp.json` (Cursor's spelling is read-compat
only).

```ts
function mcpsWritePath(
  scope: McpConfigScope,
  opts?: { cwd?: string; home?: string; prefix?: string },
): string
```

**Parameters**

- `scope` (`McpConfigScope`): Whether to resolve the project or the user file.
- `opts?` (`{ cwd?: string; home?: string; prefix?: string }`): Overrides for the working directory, home directory and dot-directory prefix (`.zidane` by default).

**Returns** `string`

Source: [src/chat/mcps-write.ts:49](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-write.ts#L49)

### `mcpToolsCachePath`

Function.

Resolve the on-disk cache path under a host's data directory. Exposed
for tests + advanced hosts that want to inspect / delete the file
without re-implementing the path convention.

```ts
function mcpToolsCachePath(dataDir: string): string
```

**Parameters**

- `dataDir` (`string`): Directory that holds the cache file.

**Returns** `string`

Source: [src/chat/mcp-tools-cache.ts:78](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tools-cache.ts#L78)

### `mergeApprovalAndBodyOutcomes`

Function.

Merge body-side outcomes (keyed against the approved subset the tool
actually ran on, in subset-position order) into approval-side outcomes
(1:1 with the model's ORIGINAL `edits` list, with `denied` entries for
every hunk the user dropped).

Algorithm: walk the approval array; every `applied` placeholder
corresponds to one approved hunk that the body ran. Consume body's
outcomes in order against those placeholders. Non-`applied` approval
entries (`denied`, `skipped`) stay untouched — they describe gate-
level decisions the body never saw.

Pure. Returns a fresh array; never mutates either input.

Edge cases:

- `body` is empty / shorter than the approved count → remaining
  approval `applied` placeholders stay as `applied` (the body ran
  happily; absence of a body entry means nothing failed).
- `body` longer than approved count → trailing body entries are
  ignored. Shouldn't happen in practice (body sees the rebound
  subset), but the guard keeps the merge total-pure.

```ts
function mergeApprovalAndBodyOutcomes(
  approval: readonly EditOutcome[],
  body: readonly EditOutcome[] | null,
): EditOutcome[]
```

**Parameters**

- `approval` (`readonly EditOutcome[]`): Outcomes decided at the approval gate.
- `body` (`readonly EditOutcome[] | null`): Outcomes parsed from the tool result, or `null` when the result had none.

**Returns** `EditOutcome[]`

Source: [src/chat/edit-approval.ts:294](https://github.com/Tahul/zidane/blob/main/src/chat/edit-approval.ts#L294)

### `mergeKeybindings`

Function.

Merge a partial map of user overrides into the defaults. Unknown
action keys are dropped (a future TUI version may have retired the
action); invalid spec strings (parse fails) fall back to the default
for that action.

```ts
function mergeKeybindings(
  overrides: Readonly<Record<string, unknown>> | null | undefined,
): KeyBindings
```

**Parameters**

- `overrides` (`Readonly<Record<string, unknown>> | null | undefined`): Parsed user overrides keyed by action name.

**Returns** `KeyBindings`

Source: [src/chat/keybindings.ts:622](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L622)

### `migrateStoredSettings`

Function.

Bring a stored `settings` slice (`state.json`) up to date: maps the
legacy `allowInteraction` and `showToolCalls` booleans (never over the
modern key), the interim `runReceipt` mode, the legacy `safeMode` and
`queueMessages` booleans, drops
retired keys and, once per defaults revision, drops values equal to a
since-changed default. The result carries the
[`SETTINGS_DEFAULTS_VERSION_KEY`](https://zidane.si/docs/reference/chat.md#SETTINGS_DEFAULTS_VERSION_KEY) marker, so writers that persist it
never migrate the same slice twice (a value the user picks later, even an
old default, then sticks).

```ts
function migrateStoredSettings(
  stored: Readonly<Record<string, unknown>>,
): Record<string, unknown>
```

**Parameters**

- `stored` (`Readonly<Record<string, unknown>>`): The `settings` slice as read from disk.

**Returns** `Record<string, unknown>`: The migrated slice, marker included. `stored` is not mutated.

Source: [src/chat/settings-defaults.ts:341](https://github.com/Tahul/zidane/blob/main/src/chat/settings-defaults.ts#L341)

### `migrateTodosBag`

Function.

Converts the old `session.metadata.todos` bag. Never produces an
`applying` plan: an open list becomes `stopped (migrated)`, which a run
that resumes it reopens by writing the same step ids. A finished list
becomes `done` (or `dropped` when everything was cancelled). Sub-agent
slots (`byRun`) are dropped: their runs ended long ago.

```ts
function migrateTodosBag(raw: unknown, now: number): PlansBag
```

**Parameters**

- `raw` (`unknown`): The old bag (`session.metadata.todos`), untrusted.
- `now` (`number`): Clock for the converted plans, epoch ms.

**Returns** `PlansBag`

Source: [src/chat/plans.ts:417](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L417)

### `modelChainMetadata`

Function.

Assemble the `metadata.modelChain` value for a model pick — the full
canonical chain when at least one valid fallback survives filtering,
`undefined` for a single-model pick (metadata merges JSON-serialize,
so `undefined` REMOVES a previously persisted chain).

```ts
function modelChainMetadata(
  primary: ModelFallbackTarget,
  fallbacks: readonly ModelFallbackTarget[] | undefined,
): ModelChainEntry[] | undefined
```

Assemble the `metadata.modelChain` value for a model pick — the full
canonical chain when at least one valid fallback survives filtering,
`undefined` for a single-model pick (metadata merges JSON-serialize,
so `undefined` REMOVES a previously persisted chain).

**Parameters**

- `primary` (`ModelFallbackTarget`): The session's primary model target.
- `fallbacks` (`readonly ModelFallbackTarget[] | undefined`): Fallback targets in order. Duplicates and extra accounts of the primary's provider are dropped.

**Returns** `ModelChainEntry[] | undefined`

```ts
function modelChainMetadata(
  providerKey: string,
  modelId: string,
  fallbacks: readonly ModelFallbackTarget[] | undefined,
): ModelChainEntry[] | undefined
```

v8.0.4 form of [`modelChainMetadata`](https://zidane.si/docs/reference/chat.md#modelChainMetadata).

**Parameters**

- `providerKey` (`string`): The primary's provider key.
- `modelId` (`string`): The primary's model id.
- `fallbacks` (`readonly ModelFallbackTarget[] | undefined`): Fallback targets in order.

**Returns** `ModelChainEntry[] | undefined`

Source: [src/chat/model-chain-metadata.ts:58](https://github.com/Tahul/zidane/blob/main/src/chat/model-chain-metadata.ts#L58)

### `modelLabel`

Function.

The name the model picker shows for a model (`Opus 4.5`): the provider's
catalog name when `registry` knows the model, else the id made readable.
Without a provider key, or with one `registry` doesn't hold, the first
provider that knows the id names it. Memoized per registry: surfaces call
it on every render.

```ts
function modelLabel(
  registry: Readonly<Record<string, ProviderDescriptor>>,
  modelId: string,
  providerKey?: string | null,
): string
```

**Parameters**

- `registry` (`Readonly<Record<string, ProviderDescriptor>>`): Descriptors keyed by provider key.
- `modelId` (`string`): Model id to label.
- `providerKey?` (`string | null`): Optional provider key to look the model up under first.

**Returns** `string`

Source: [src/chat/providers.ts:1057](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L1057)

### `modelOptionsFor`

Function.

Custom [`ModelOption`](https://zidane.si/docs/reference/chat.md#ModelOption)s the given model supports (e.g. fast mode), or `[]`
when none. Drives the TUI/GUI options picker visibility — surfaces only when
non-empty.

```ts
function modelOptionsFor(
  descriptor: ProviderDescriptor,
  modelId: string,
): readonly ModelOption[]
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider descriptor.
- `modelId` (`string`): Model id, resolved through `modelAliases`.

**Returns** `readonly ModelOption[]`

Source: [src/chat/providers.ts:1245](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L1245)

### `modelsForDescriptor`

Function.

Resolve the model list for a given provider. Honors `descriptor.models`
when set; otherwise queries pi-ai via `descriptor.piProviderId`. Returns
`[]` for descriptors with no known mapping (custom providers without a
model list) — callers should hide the model picker in that case.

```ts
function modelsForDescriptor(descriptor: ProviderDescriptor): readonly ModelInfo[]
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider descriptor.

**Returns** `readonly ModelInfo[]`

Source: [src/chat/providers.ts:921](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L921)

### `modelSupportsReasoning`

Function.

Whether the given model exposes a reasoning / extended-thinking knob.
Drives the TUI's effort picker visibility — the footer's effort segment
(`effortLabel`) and its shortcut only surface when this is `true`.
Returns `false` for unknown models (no registry entry → no advertised
capability).

```ts
function modelSupportsReasoning(
  descriptor: ProviderDescriptor,
  modelId: string,
): boolean
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider descriptor.
- `modelId` (`string`): Model id, resolved through `modelAliases`.

**Returns** `boolean`

Source: [src/chat/providers.ts:1233](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L1233)

### `newSession`

Function.

Build a new, UNSAVED session with every creation-time field stamped:
`projectRoot` (git root of `cwd`), `cwd`, `agentId`, `title`, the model and
its fallback chain, the authoring markers, and the environment.

Unsaved on purpose: the TUI's local runtime creates the session when a chat
opens but only persists it on the first run, so opening and abandoning
`/new` never leaves an empty row behind. Everyone else wants
[`createSessionInStore`](https://zidane.si/docs/reference/chat.md#createSessionInStore).

`store` may be `undefined` for an in-memory-only session (ACP without a
store): same stamping, no persistence.

```ts
function newSession(
  store: SessionStore | undefined,
  params: CreateSessionInStoreParams,
): Promise<Session>
```

**Parameters**

- `store` (`SessionStore | undefined`): Store the session saves to later, or `undefined` for an in-memory session.
- `params` (`CreateSessionInStoreParams`): Creation fields for the session.

**Returns** `Promise<Session>`

Source: [src/chat/session-factory.ts:104](https://github.com/Tahul/zidane/blob/main/src/chat/session-factory.ts#L104)

### `normalizeBindingValue`

Function.

A `keybindings.json` value as one spec: a string, or an array of chords
(`["ctrl+w", "cmd+w"]`) joined as alternatives. Chords that don't parse
are dropped; `null` when none is left.

```ts
function normalizeBindingValue(raw: unknown): string | null
```

**Parameters**

- `raw` (`unknown`): Raw value from `keybindings.json`: a spec string or an array of them.

**Returns** `string | null`

Source: [src/chat/keybindings.ts:644](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L644)

### `normalizeLaunchOverrides`

Function.

The overrides that shape an agent, or `null` when none do (no `system`,
no MCP server, no non-`process` context, no cwd). The one rule for "are launch
overrides set", shared by the host, the daemon dispatcher, and the TUI.

```ts
function normalizeLaunchOverrides(
  overrides: SessionLaunchOverrides | null | undefined,
): SessionLaunchOverrides | null
```

**Parameters**

- `overrides` (`SessionLaunchOverrides | null | undefined`): Overrides to reduce. `null` and `undefined` mean none.

**Returns** `SessionLaunchOverrides | null`

Source: [src/chat/host/launch-overrides.ts:45](https://github.com/Tahul/zidane/blob/main/src/chat/host/launch-overrides.ts#L45)

### `normalizeRedirectPaste`

Function.

Widen what counts as an acceptable paste. The happy path is the full
redirect URL out of the browser's address bar, but users on SSH
routinely end up holding less than that: some authorization servers
render the code on the page instead of redirecting, and copying from a
"can't reach this site" error page loses everything but the query.

Accepted forms, given the authorization URL for context:

- `http://127.0.0.1:5123/callback?code=x&state=y` — passthrough (any
  absolute URL is returned untouched; the loopback guard in
  [`fetchOAuthRedirect`](https://zidane.si/docs/reference/chat.md#fetchOAuthRedirect) still has final say, so a pasted
  `https://evil.test` is rejected there, not silently rewritten).
- `/callback?code=x&state=y` — path + query onto the callback origin.
- `?code=x&state=y` / `code=x&state=y` — query onto the callback URL.
- `x` — a bare authorization code; `state` is taken from the
  authorization URL when it carried one (the loopback server forwards
  `state` verbatim and the SDK owns verification, so a missing one is
  not fatal). Anything with whitespace inside is prose (an error page
  fragment), not a code — refused with a clearer error than a server
  400 would give.

Throws when the input can't be turned into a URL — no `authUrl` context
for a partial paste, or an unusable authorization URL.

```ts
function normalizeRedirectPaste(pasted: string, authUrl?: string): string
```

**Parameters**

- `pasted` (`string`): What the user pasted: a full URL, a path, a query string or a bare code.
- `authUrl?` (`string`): The authorization URL on screen. Required to complete anything shorter than a full URL.

**Returns** `string`

Source: [src/chat/oauth-redirect.ts:84](https://github.com/Tahul/zidane/blob/main/src/chat/oauth-redirect.ts#L84)

### `normalizeRemoteUrl`

Function.

The repository a remote URL names, as `host/owner/repo` in lower case:
the same key for the HTTPS, SSH and `scp` forms of one remote. `null`
for a local remote (a path or `file://`), which names no shared repository.

```ts
function normalizeRemoteUrl(url: string): string | null
```

**Parameters**

- `url` (`string`): A remote URL, as in `.git/config`.

**Returns** `string | null`

Source: [src/chat/project-root.ts:147](https://github.com/Tahul/zidane/blob/main/src/chat/project-root.ts#L147)

### `oauthUsesManualCodePaste`

Function.

True when the provider's OAuth flow needs the user to paste a code back into the TUI (no loopback callback).

```ts
function oauthUsesManualCodePaste(descriptor: ProviderDescriptor): boolean
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider descriptor.

**Returns** `boolean`

Source: [src/chat/oauth.ts:57](https://github.com/Tahul/zidane/blob/main/src/chat/oauth.ts#L57)

### `onboardingInstallSignals`

Function.

The signs of an install that predates the wizard.

```ts
function onboardingInstallSignals(
  input: OnboardingInstallSignalsInput,
): OnboardingInstallSignals
```

**Parameters**

- `input` (`OnboardingInstallSignalsInput`): The credentials directory, the state file and what the app knows.

**Returns** `OnboardingInstallSignals`

Source: [src/chat/host/onboarding/detect.ts:118](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/detect.ts#L118)

### `onboardingProviders`

Function.

The providers Connect lists, featured ones first, then the provider
extensions that are not installed.

```ts
function onboardingProviders(
  registry: Readonly<Record<string, ProviderDescriptor>>,
  auth: readonly ProviderAuth[],
): OnboardingProvider[]
```

**Parameters**

- `registry` (`Readonly<Record<string, ProviderDescriptor>>`): The host's provider descriptors.
- `auth` (`readonly ProviderAuth[]`): What `detectAuth` found for them.

**Returns** `OnboardingProvider[]`

Source: [src/chat/host/onboarding/providers.ts:34](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/providers.ts#L34)

### `overlayHostSettings`

Function.

`local` with the host's values for every key the host owns.

```ts
function overlayHostSettings<S extends Settings>(
  local: S,
  host: Partial<Settings> & { safeMode?: boolean },
): S
```

**Type parameters**

- `S` (`extends Settings`)

**Parameters**

- `local` (`S`): The window's own settings.
- `host` (`Partial<Settings> & { safeMode?: boolean }`): Settings read from the host (`settings.get`, `settings.set`). Window-only keys in it are ignored.

**Returns** `S`

Source: [src/chat/host-scoped-settings.ts:109](https://github.com/Tahul/zidane/blob/main/src/chat/host-scoped-settings.ts#L109)

### `parentPlanScope`

Function.

The scope a sub-agent run's plans nest under: its parent run's scope.
`undefined` for a top-level run.

```ts
function parentPlanScope(
  runs: readonly Pick<SessionRun, 'id' | 'parentRunId'>[],
  runId: string,
): string | undefined
```

**Parameters**

- `runs` (`readonly Pick<SessionRun, 'id' | 'parentRunId'>[]`): The session's runs.
- `runId` (`string`): The run.

**Returns** `string | undefined`

Source: [src/chat/plans-session.ts:43](https://github.com/Tahul/zidane/blob/main/src/chat/plans-session.ts#L43)

### `parentServerName`

Function.

Server name a row "belongs to" for catalog / toggle / auth lookups.
For a server row that's its own name; for tool / tool-empty rows
it's the parent server's name.

```ts
function parentServerName(row: VisibleMcpRow): string
```

**Parameters**

- `row` (`VisibleMcpRow`): Any visible row.

**Returns** `string`

Source: [src/chat/mcp-rows.ts:89](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-rows.ts#L89)

### `parseDecisionModel`

Function.

Split a stored `"providerKey/modelId"` pick (first slash; model ids may contain slashes).

```ts
function parseDecisionModel(
  raw: string | undefined,
): { modelId: string; providerKey: string } | undefined
```

**Parameters**

- `raw` (`string | undefined`): Stored setting such as `providerKey/modelId`, or `undefined` when unset.

**Returns** `{ modelId: string; providerKey: string } | undefined`

Source: [src/chat/decision-models.ts:93](https://github.com/Tahul/zidane/blob/main/src/chat/decision-models.ts#L93)

### `parseEditorCommand`

Function.

Split an editor command line into `[executable, ...args]`.

Whitespace-separated, with single and double quotes honored so an editor
living under a path with spaces still works
(`"/Applications/My App/bin/ed" -w`). Quotes group and are stripped;
backslash escaping is deliberately NOT implemented, because on Windows
backslash is the path separator and treating `C:\tools\ed` as escapes would
corrupt far more command lines than it would rescue.

Returns `[]` for an empty or whitespace-only spec, so callers can fall back
to the platform opener rather than spawning `''`.

```ts
function parseEditorCommand(spec: string): string[]
```

**Parameters**

- `spec` (`string`): Raw editor command line, such as the value of `$EDITOR`.

**Returns** `string[]`

**Example**

```ts
import { parseEditorCommand } from 'zidane/chat'

const [cmd, ...args] = parseEditorCommand('code --wait')
// cmd === 'code', args === ['--wait']
```

Source: [src/chat/editor-command.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/editor-command.ts#L35)

### `parseEditOutcomesFromResult`

Function.

Parse an `<edit-outcomes>…</edit-outcomes>` annotation block out of a
tool result body. Returns the outcomes keyed by 1-based hunk index, or
`null` when the block is missing / malformed.

Anchored on the explicit tag pair so the parser doesn't false-positive
on natural prose that happens to contain `#1 applied`.

```ts
function parseEditOutcomesFromResult(
  result: string | readonly ToolResultContent[],
): EditOutcome[] | null
```

**Parameters**

- `result` (`string | readonly ToolResultContent[]`): Tool result text or content blocks.

**Returns** `EditOutcome[] | null`

Source: [src/chat/edit-approval.ts:170](https://github.com/Tahul/zidane/blob/main/src/chat/edit-approval.ts#L170)

### `parseMcpsFile`

Function.

Parse one `mcps.json` file. Canonical zidane shape is a flat array; the
other shapes exist for copy-paste from neighboring tools:

- `McpServerConfig[]` — flat array (recommended).
- `{ name: {...} }` — name-keyed map.
- `{ "mcpServers": { name: {...} } }` — Claude Desktop / Claude Code /
  Cursor wrapper. Unwrapped before normalization (otherwise
  `normalizeMcpServers` would treat the whole wrapper as a single server).

Per-entry `enabled: false` (Cursor runtime state) drops the entry — same
intent as the source tool. Use the TUI toggle list for "show but disabled".

Tolerant to common copy-paste artifacts: when strict `JSON.parse` fails,
retries after normalizing invisible whitespace that's valid Unicode but
not valid JSON whitespace (BOM, NBSP, zero-width spaces, line/paragraph
separators) — only OUTSIDE string literals, so user data isn't mutated.
If the sanitized version still fails, throws the original error with a
targeted hint when NBSPs are present.

Validation flows through `normalizeMcpServers` so the result matches
what `createAgent` accepts. Throws on truly malformed JSON; the caller
decides whether to surface or swallow.

```ts
function parseMcpsFile(text: string): McpServerConfig[]
```

**Parameters**

- `text` (`string`): Raw file contents, either a bare server map or array, or an object with an `mcpServers` key.

**Returns** `McpServerConfig[]`

Source: [src/chat/mcps-discovery.ts:126](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-discovery.ts#L126)

### `parseSemver`

Function.

Parses a semantic version string into its parts.
Accepts an optional leading `v`, a prerelease and build metadata. Returns `null` when the input is not a valid version.

```ts
function parseSemver(
  input: string,
): { major: number; minor: number; patch: number; prerelease: string[] } | null
```

**Parameters**

- `input` (`string`): Version string such as `1.2.3` or `v1.2.3-rc.1`.

**Returns** `{ major: number; minor: number; patch: number; prerelease: string[] } | null`

Source: [src/chat/auto-update.ts:199](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L199)

### `parseSessionEventXml`

Function.

Inverse of [`renderSessionEventXml`](https://zidane.si/docs/reference/chat.md#renderSessionEventXml). Untrusted input — it comes off disk
and outlives this module's versions — so it validates each field and returns
`null` for anything malformed, in which case the turn replays as the plain
user text it structurally is.

```ts
function parseSessionEventXml(text: string): SessionEvent | null
```

**Parameters**

- `text` (`string`): User-turn text that may hold a `<session-event>` block.

**Returns** `SessionEvent | null`

Source: [src/chat/orchestration.ts:494](https://github.com/Tahul/zidane/blob/main/src/chat/orchestration.ts#L494)

### `patchMcpCredential`

Function.

Convenience patches for individual sub-fields. The provider class uses
these so a `saveTokens` call doesn't have to round-trip `clientInformation`

- `discoveryState` it never touched.

```ts
function patchMcpCredential(
  store: McpCredentialStore,
  name: string,
  patch: Partial<McpCredentialEntry>,
): void
```

**Parameters**

- `store` (`McpCredentialStore`): Credential store to update.
- `name` (`string`): Server name the entry is saved under.
- `patch` (`Partial<McpCredentialEntry>`): Fields to merge over the existing entry. A missing entry starts empty.

Source: [src/chat/mcp-credentials.ts:115](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-credentials.ts#L115)

### `pendingInteractionsFromTurns`

Function.

Scan `turns` and return every interaction tool call without a matching
`tool_result`, ordered by turn appearance. Works equally for live mid-run
inspection and reload-from-disk.

Malformed payloads (input that doesn't match the tool's schema) are
skipped silently — the call still lives in the persisted history, but
the renderer wouldn't know how to display it. The next `agent.run()` on
the session will see the orphan and synthesize an error tool_result via
the loop's normal recovery path.

```ts
function pendingInteractionsFromTurns(
  turns: readonly SessionTurn[],
): InteractionRequest[]
```

**Parameters**

- `turns` (`readonly SessionTurn[]`): Session turns, oldest first.

**Returns** `InteractionRequest[]`

Source: [src/chat/interactions-protocol.ts:610](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L610)

### `performInPlaceSelfUpdate`

Function.

Replace the running binary with the latest tarball-shipped one. Works on
macOS + Linux (POSIX unlink-while-running); refuses on Windows. Returns
a result object instead of throwing — callers usually want to print
`result.reason` and fall back to a PM-driven update.

```ts
function performInPlaceSelfUpdate(
  options: PerformInPlaceUpdateOptions,
): Promise<InPlaceUpdateResult>
```

**Parameters**

- `options` (`PerformInPlaceUpdateOptions`): Platform package, binary path, registry and swap restrictions.

**Returns** `Promise<InPlaceUpdateResult>`

Source: [src/chat/auto-update.ts:944](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L944)

### `performSelfUpdate`

Function.

Re-install the package globally via the host's package manager. Resolves
with the spawned command's exit code. It does not reject: a spawn failure
comes back as `spawnError`, an abort of the caller's `signal` kills the
child (`exitCode: null`), a PM still running after `timeoutMs` is killed
(`timedOut`), and PM failures surface as a non-zero `exitCode`.
The caller decides whether to retry or fall back to in-place.

```ts
function performSelfUpdate(options: PerformSelfUpdateOptions): Promise<SelfUpdateResult>
```

**Parameters**

- `options` (`PerformSelfUpdateOptions`): Package, channel, package manager and stream settings for the install.

**Returns** `Promise<SelfUpdateResult>`

Source: [src/chat/auto-update.ts:806](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L806)

### `piIdOf`

Function.

Convenience accessor — returns `piProviderId ?? key`.

```ts
function piIdOf(desc: ProviderDescriptor): string
```

**Parameters**

- `desc` (`ProviderDescriptor`): The provider descriptor.

**Returns** `string`

Source: [src/chat/providers.ts:370](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L370)

### `planRunNotes`

Function.

Notes for a run that starts on a session with these plans and turns.

```ts
function planRunNotes(bag: PlansBag, turns: readonly SessionTurn[]): PlanRunNotes
```

**Parameters**

- `bag` (`PlansBag`): The session's plans.
- `turns` (`readonly SessionTurn[]`): The session's turns (what the model was already told).

**Returns** `PlanRunNotes`

Source: [src/chat/plan-notes.ts:47](https://github.com/Tahul/zidane/blob/main/src/chat/plan-notes.ts#L47)

### `planScopeForRun`

Function.

The plan scope of a run: its own id for a sub-agent run (it has a
`parentRunId`), else the session scope. An unknown run is top-level.

```ts
function planScopeForRun(
  runs: readonly Pick<SessionRun, 'id' | 'parentRunId'>[],
  runId: string | null | undefined,
): string
```

**Parameters**

- `runs` (`readonly Pick<SessionRun, 'id' | 'parentRunId'>[]`): The session's runs.
- `runId` (`string | null | undefined`): The run.

**Returns** `string`

Source: [src/chat/plans-session.ts:30](https://github.com/Tahul/zidane/blob/main/src/chat/plans-session.ts#L30)

### `planWorktreeProjectRootRemaps`

Function.

For persisted project roots that predate worktree-aware identity: the
`{ from, to }` pairs where a stored root is a linked worktree's checkout
root and should be its main checkout's. Only exact linked-worktree roots
remap — a plain directory that later ended up inside some repo keeps its
own identity (no parent walk here). Stores apply the pairs as a bulk
column update at open; idempotent, so a row reverted by a stale
full-document save converges on the next open.

```ts
function planWorktreeProjectRootRemaps(
  roots: Iterable<string>,
): { from: string; to: string }[]
```

**Parameters**

- `roots` (`Iterable<string>`): Persisted project roots to check.

**Returns** `{ from: string; to: string }[]`

Source: [src/chat/project-root.ts:250](https://github.com/Tahul/zidane/blob/main/src/chat/project-root.ts#L250)

### `previewClaudeImport`

Function.

What [`importFromClaudeCode`](https://zidane.si/docs/reference/chat.md#importFromClaudeCode) would bring over and skip, per piece,
without writing anything: the setup wizard's Bring your setup step shows
it before the user picks. Names already in zidane are skipped, as the
import skips them; a server listed in several Claude files counts once.

```ts
function previewClaudeImport(opts?: ClaudeImportOptions): OnboardingImportPreview
```

**Parameters**

- `opts?` (`ClaudeImportOptions`, default `{}`): Discovery options: `cwd`, `home` and `prefix`.

**Returns** `OnboardingImportPreview`

**Example**

```ts
import { previewClaudeImport } from 'zidane/chat'

const preview = previewClaudeImport()
console.log(preview.sessions.all, preview.mcps.copy, preview.skills.skip)
```

Source: [src/chat/claude-import.ts:943](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L943)

### `probeInstalledVersion`

Function.

Compute the state once — the pure heart of the watcher, exported so a host
without a lifecycle (a one-shot IPC handler) can answer "is a restart
pending?" without holding a watcher.

```ts
function probeInstalledVersion(
  spec: CompanionSpec,
  currentVersion: string,
  probe?: ((spec: CompanionSpec, opts?: FindCompanionOptions) => InstalledCompanion | null) | undefined,
  now?: () => number,
): InstalledVersionState
```

**Parameters**

- `spec` (`CompanionSpec`): The running package and its binary name.
- `currentVersion` (`string`): Version the process was built with. An empty string yields no installed version.
- `probe?` (`((spec: CompanionSpec, opts?: FindCompanionOptions) => InstalledCompanion | null) | undefined`, default `findInstalledCompanion`): Function that finds the installed package on disk. Defaults to `findInstalledCompanion`.
- `now?` (`() => number`, default `Date.now`): Clock used for `checkedAt`. Defaults to `Date.now`.

**Returns** `InstalledVersionState`

Source: [src/chat/auto-update-restart.ts:157](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L157)

### `probeLocalModelServers`

Function.

The local model servers answering on their default ports (Ollama,
LM Studio, vLLM), with the base URL the Local LLM provider takes.

```ts
function probeLocalModelServers(
  opts?: ProbeLocalModelServersOptions,
): Promise<OnboardingLocalServer[]>
```

**Parameters**

- `opts?` (`ProbeLocalModelServersOptions`, default `{}`): Fetch implementation and timeout.

**Returns** `Promise<OnboardingLocalServer[]>`

Source: [src/chat/host/onboarding/detect.ts:84](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/detect.ts#L84)

### `profileComposition`

Function.

A profile's own composition: its family (every registered member, in
registry order) or the profile alone.

```ts
function profileComposition(
  registry: AgentRegistry,
  profile: AgentProfile,
): AgentComposition
```

**Parameters**

- `registry` (`AgentRegistry`): The profiles available to the session.
- `profile` (`AgentProfile`): The profile to compose.

**Returns** `AgentComposition`

Source: [src/chat/agents.ts:122](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L122)

### `profileTools`

Function.

A profile's own tools by name. Tools the host gives every profile
(interaction, skills, MCP, …) are not part of it.

```ts
function profileTools(profile: AgentProfile): ReadonlyMap<string, ToolDef>
```

**Parameters**

- `profile` (`AgentProfile`): The profile.

**Returns** `ReadonlyMap<string, ToolDef>`

Source: [src/chat/agents.ts:157](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L157)

### `projectRootOf`

Function.

[`findProjectRoot`](https://zidane.si/docs/reference/chat.md#findProjectRoot) with a fallback: outside git, a
directory is its own project. The value to stamp as
`SessionData.projectRoot` and to key per-project state on.

```ts
function projectRootOf(cwd: string): string
```

**Parameters**

- `cwd` (`string`): Directory to resolve.

**Returns** `string`

Source: [src/chat/project-root.ts:119](https://github.com/Tahul/zidane/blob/main/src/chat/project-root.ts#L119)

### `projectsFilePath`

Function.

Resolve `projects.json`'s on-disk path given the TUI data directory.

```ts
function projectsFilePath(dataDir: string): string
```

**Parameters**

- `dataDir` (`string`): Directory that holds `projects.json`.

**Returns** `string`

Source: [src/chat/safe-mode.ts:65](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L65)

### `projectUserPaths`

Function.

Resolve the four search paths for a given subdirectory (`skills`,
`mcps.json`, …). Returns absolute paths regardless of whether they
exist — the caller chooses to `existsSync`-filter or feed straight
into a tolerant discovery routine.

```ts
function projectUserPaths(
  opts: { cwd?: string; home?: string; prefix?: string; subPath: string },
): ProjectUserPath[]
```

**Parameters**

- `opts` (`{ cwd?: string; home?: string; prefix?: string; subPath: string }`): The sub-path to append, plus overrides for the working directory, home directory and dot-directory prefix.

**Returns** `ProjectUserPath[]`

Source: [src/chat/project-user-paths.ts:42](https://github.com/Tahul/zidane/blob/main/src/chat/project-user-paths.ts#L42)

### `proposeEvent`

Function.

The `propose` event of a `present_plan` request. Its id is the request id,
so a request proposed twice (the tool, then a rehydrate) is one plan.

```ts
function proposeEvent(request: PlanRequest, scope: string, at?: number): PlanEvent
```

**Parameters**

- `request` (`PlanRequest`): The request.
- `scope` (`string`): The plan scope of the request's run.
- `at?` (`number`): Clock, epoch ms.

**Returns** `PlanEvent`

Source: [src/chat/plans-session.ts:105](https://github.com/Tahul/zidane/blob/main/src/chat/plans-session.ts#L105)

### `providerFactoryOptions`

Function.

Factory options that build `descriptor` on its stored account `account`
(a credential id), or `undefined` for the provider's active account: no
account asked for, or a provider whose factory cannot take one
(`descriptor.accounts`). An OAuth account rides as `account` and is resolved
to its slot on every token read; an API-key account as its key. An account
no longer stored still rides as `account`, so the call fails with the
pinned-account error instead of quietly running on the active account.

```ts
function providerFactoryOptions(
  dataDir: string,
  descriptor: ProviderDescriptor,
  account: string | undefined,
): ProviderFactoryOptions | undefined
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider descriptor that selects the credential file key.
- `account` (`string | undefined`): Id of the stored account to pin, or `undefined` for the active account.

**Returns** `ProviderFactoryOptions | undefined`

Source: [src/chat/credentials.ts:971](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L971)

### `providerFieldViews`

Function.

A provider's custom fields as a settings form asks for them; `undefined` when it takes none.

```ts
function providerFieldViews(
  descriptor: ProviderDescriptor,
): ProviderFieldView[] | undefined
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider.

**Returns** `ProviderFieldView[] | undefined`

Source: [src/chat/credentials.ts:1017](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L1017)

### `providerKeySetChanged`

Function.

Did the SET of provider instances change between two chains? The agent is
built with one provider instance per distinct chain provider (bound to its
account), so a set change requires a session re-activation (rebuild); a
pure model / order change within the same providers and accounts does not.

```ts
function providerKeySetChanged(
  prev: readonly ModelChainEntry[],
  next: readonly ModelChainEntry[],
): boolean
```

**Parameters**

- `prev` (`readonly ModelChainEntry[]`): The chain before the change.
- `next` (`readonly ModelChainEntry[]`): The chain after the change.

**Returns** `boolean`

Source: [src/chat/model-chain.ts:198](https://github.com/Tahul/zidane/blob/main/src/chat/model-chain.ts#L198)

### `providerUsageSupported`

Function.

Whether this provider can report subscription usage at all.

```ts
function providerUsageSupported(descriptor: ProviderDescriptor): boolean
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider to check.

**Returns** `boolean`

Source: [src/chat/provider-usage.ts:111](https://github.com/Tahul/zidane/blob/main/src/chat/provider-usage.ts#L111)

### `provisionSessionEnvironment`

Function.

Runs one provisioning attempt for a session's environment and persists each state change.
Sets the state to `provisioning`, calls the provider's `prepare`, then persists `ready` or `failed`. Resolves instead of throwing when the provider fails.

```ts
function provisionSessionEnvironment(
  input: ProvisionSessionEnvironmentInput,
): Promise<ProvisionSessionEnvironmentResult>
```

**Parameters**

- `input` (`ProvisionSessionEnvironmentInput`): Session id, loaders, registry, config lookup, persistence callback, and optional abort signal.

**Returns** `Promise<ProvisionSessionEnvironmentResult>`: Whether the attempt tore down what it prepared, and the environment as it ended.

Source: [src/chat/environment-provisioning.ts:50](https://github.com/Tahul/zidane/blob/main/src/chat/environment-provisioning.ts#L50)

### `quoteForShell`

Function.

Re-quote a single argv token for a shell that will re-parse the joined command
line — i.e. `spawn(..., { shell: true })`, which Node uses on Windows so that
`.cmd` / `.bat` wrappers (how `code`, `cursor` and friends ship there) resolve.

Node does NOT re-quote when `shell` is set: it joins `[cmd, ...args]` with
spaces and hands the string over. Since [`parseEditorCommand`](https://zidane.si/docs/reference/chat.md#parseEditorCommand) has already
STRIPPED the user's quotes, handing tokens back unquoted would split
`C:\Program Files\ed.exe` at the space and try to run `C:\Program`. This puts
the quotes back.

Only tokens that need it are quoted, so a plain `code` stays `code`. A literal
double quote is doubled, which is how `cmd.exe` escapes one inside a quoted
run.

The test covers `cmd.exe` METACHARACTERS, not just whitespace: `&`, `|`, `^`,
`<`, `>` and friends carry none of the obvious "needs quoting" signals, so a
repo cloned into `C:\work\a&b` would otherwise hand `C:\work\a&b\AGENTS.md`
over bare, and cmd would split it at the `&` and run the tail as a second
command.

`%` and `!` are in the class so such a token is quoted rather than passed
bare — but be clear about the limit: quoting does NOT stop cmd.exe expanding
`%VAR%`, nor `!VAR!` where delayed expansion is on. Double quotes suppress
splitting and other syntax, not expansion. That is a property of the shell
which no amount of quoting can close; the worst case is a wrong path, not an
injected command.

```ts
function quoteForShell(token: string): string
```

**Parameters**

- `token` (`string`): One argv token, already stripped of its original quotes.

**Returns** `string`

Source: [src/chat/editor-command.ts:103](https://github.com/Tahul/zidane/blob/main/src/chat/editor-command.ts#L103)

### `readCredentialEntry`

Function.

Read one entry by its file key, whatever owns it. For stores that keep a
secret beside the provider slots under their own namespace (the host
registry: `hosts:<id>`).

```ts
function readCredentialEntry(
  dataDir: string,
  key: string,
): ProviderCredential | undefined
```

**Parameters**

- `dataDir` (`string`): The user data folder holding the credentials file.
- `key` (`string`): The entry's key in that file.

**Returns** `ProviderCredential | undefined`

Source: [src/chat/credentials.ts:472](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L472)

### `readCredentials`

Function.

Read credentials from disk.

Returns `{}` when the file is missing or corrupt (last-ditch tolerance —
a hand-edit gone wrong shouldn't lock the user out of re-authing). On first
call with no file present, attempts a migration from a legacy
`.credentials.json` in the current directory (the location older `bun run auth`
versions used), when that directory is the home dir or the data dir.

```ts
function readCredentials(dataDir: string): CredentialsFile
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.

**Returns** `CredentialsFile`

Source: [src/chat/credentials.ts:164](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L164)

### `readEnvironmentSettings`

Function.

Tolerant read of `Settings.environments`.

```ts
function readEnvironmentSettings(
  settings: object | undefined,
): Required<EnvironmentSettings>
```

**Parameters**

- `settings` (`object | undefined`): Settings object, or `undefined`.

**Returns** `Required<EnvironmentSettings>`

Source: [src/chat/environment-settings.ts:65](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L65)

### `readFailedUpdate`

Function.

Reads the failed-install record. `null` when there is none or it does not
parse. Falls back to the record this process could not write to disk.

```ts
function readFailedUpdate(cacheDir: string): FailedUpdateRecord | null
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the record.

**Returns** `FailedUpdateRecord | null`

Source: [src/chat/auto-update-auto-install.ts:66](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L66)

### `readFileSafe`

Function.

Reads a UTF-8 file, resolving to an empty string when it cannot be read.

```ts
function readFileSafe(path: string): Promise<string>
```

**Parameters**

- `path` (`string`): File to read.

**Returns** `Promise<string>`

Source: [src/chat/host/helpers.ts:152](https://github.com/Tahul/zidane/blob/main/src/chat/host/helpers.ts#L152)

### `readKeybindings`

Function.

Load + merge the user's keybindings file (when present). Always
returns a fully-resolved [`KeyBindings`](https://zidane.si/docs/reference/chat.md#KeyBindings) — defaults fill in for
any missing / invalid entries so consumers can treat the result as
total.

Failure modes (all tolerated, all fall back to defaults):

- File missing
- File not valid JSON / JSONC (after comment stripping)
- Top-level is not an object
- Individual entries have invalid spec strings

```ts
function readKeybindings(userDir: string): KeyBindings
```

**Parameters**

- `userDir` (`string`): User-level config directory that holds `keybindings.json`.

**Returns** `KeyBindings`

Source: [src/chat/keybindings.ts:762](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L762)

### `readKeybindingsRaw`

Function.

Read the raw on-disk JSON object from `keybindings.json` after the
JSONC stripper. Used by extension-keybinding resolution so the
user's overrides for extension-owned ids flow through the same file
the built-in actions use.

Returns an empty object on missing / malformed / non-object files
— same fault-tolerance contract as [`readKeybindings`](https://zidane.si/docs/reference/chat.md#readKeybindings).

```ts
function readKeybindingsRaw(userDir: string): Readonly<Record<string, unknown>>
```

**Parameters**

- `userDir` (`string`): User-level config directory that holds `keybindings.json`.

**Returns** `Readonly<Record<string, unknown>>`

Source: [src/chat/keybindings.ts:794](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L794)

### `readPlansBag`

Function.

Reads the plans bag from session metadata. The metadata is untrusted (an
older build, a hand edit): malformed plans and steps drop out rather than
throw. A session that has no `plans` yet but has the old `todos` key is
converted in memory ([`migrateTodosBag`](https://zidane.si/docs/reference/chat.md#migrateTodosBag)); nothing is written here, the
converted bag is stored with the next real change.

```ts
function readPlansBag(
  metadata: Readonly<Record<string, unknown>>,
  now?: number,
): PlansBag
```

**Parameters**

- `metadata` (`Readonly<Record<string, unknown>>`): The session's metadata.
- `now?` (`number`): Clock for converted plans, epoch ms.

**Returns** `PlansBag`

Source: [src/chat/plans.ts:371](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L371)

### `readProjects`

Function.

Reads `projects.json`. Returns `{}` when the file is missing or unreadable.

```ts
function readProjects(dataDir: string): ProjectsFile
```

**Parameters**

- `dataDir` (`string`): Directory that holds the file.

**Returns** `ProjectsFile`

Source: [src/chat/safe-mode.ts:74](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L74)

### `readProviderCredential`

Function.

Read a single provider's credential (translating via the descriptor).

```ts
function readProviderCredential(
  dataDir: string,
  descriptor: ProviderDescriptor,
): ProviderCredential | undefined
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider descriptor that selects the credential file key.

**Returns** `ProviderCredential | undefined`

Source: [src/chat/credentials.ts:241](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L241)

### `readProviderCredentialPair`

Function.

Both stored credentials for a provider: `active` is the one every reader
resolves, `alternate` is the stored-but-not-preferred one (if any).

Predates multi-account and only sees the `#alt` slot; use
[`readProviderCredentials`](https://zidane.si/docs/reference/chat.md#readProviderCredentials) to list every stored account.

```ts
function readProviderCredentialPair(
  dataDir: string,
  descriptor: ProviderDescriptor,
): { active?: ProviderCredential; alternate?: ProviderCredential }
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider descriptor that selects the credential file key.

**Returns** `{ active?: ProviderCredential; alternate?: ProviderCredential }`

Source: [src/chat/credentials.ts:255](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L255)

### `readProviderCredentials`

Function.

Every stored credential of a provider, active first.

Tolerant of any kind in any slot: `#alt` is meant for the API key and
`#account:N` for OAuth accounts, but a file written by an older build (which
parked the OAuth alternate in `#alt`) must still list everything it holds.

```ts
function readProviderCredentials(
  dataDir: string,
  descriptor: ProviderDescriptor,
): StoredCredential[]
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider descriptor that selects the credential file key.

**Returns** `StoredCredential[]`

Source: [src/chat/credentials.ts:281](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L281)

### `readSessionEnvironment`

Function.

The session's environment, from its metadata. Tolerant: absent or
malformed reads as [`LOCAL_ENVIRONMENT`](https://zidane.si/docs/reference/chat.md#LOCAL_ENVIRONMENT) — what every session was
before environments existed.

```ts
function readSessionEnvironment(
  metadata: Record<string, unknown> | undefined,
): SessionEnvironment
```

**Parameters**

- `metadata` (`Record<string, unknown> | undefined`): Session metadata, or `undefined`.

**Returns** `SessionEnvironment`

Source: [src/chat/environment.ts:89](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L89)

### `readSessionPlans`

Function.

The session's plans (the old `todos` key converted in memory when no plans exist yet).

```ts
function readSessionPlans(
  session: { metadata: Readonly<Record<string, unknown>> },
): PlansBag
```

**Parameters**

- `session` (`{ metadata: Readonly<Record<string, unknown>> }`): Anything with metadata.

**Returns** `PlansBag`

Source: [src/chat/plans-session.ts:54](https://github.com/Tahul/zidane/blob/main/src/chat/plans-session.ts#L54)

### `readUpdateAppliedMarker`

Function.

Reads the update-applied marker.
Returns `null` when the file is missing, unreadable or malformed. Package entries with a non-string name or version are dropped.

```ts
function readUpdateAppliedMarker(cacheDir: string): UpdateAppliedMarker | null
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the marker file.

**Returns** `UpdateAppliedMarker | null`

Source: [src/chat/auto-update-restart.ts:76](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L76)

### `readUpdateLock`

Function.

Reads the lock file. Returns `null` when there is none. A file that does
not parse (another process is still writing it, or garbage) yields a holder
with `pid: 0`, dated by the file's mtime, so only age makes it stale.

```ts
function readUpdateLock(cacheDir: string): UpdateLockHolder | null
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the lock.

**Returns** `UpdateLockHolder | null`

Source: [src/chat/auto-update-lock.ts:90](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-lock.ts#L90)

### `readUpdateWorkerResult`

Function.

Reads the last worker result. `null` when there is none, it does not parse,
or it has another protocol version.

```ts
function readUpdateWorkerResult(cacheDir: string): UpdateWorkerResult | null
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the result.

**Returns** `UpdateWorkerResult | null`

Source: [src/chat/auto-update-worker.ts:164](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L164)

### `readWorkspaceFile`

Function.

A file of the folder for a viewer; `null` when it is gone or outside the folder.

```ts
function readWorkspaceFile(cwd: string, rel: string): Promise<WorkspaceFile | null>
```

**Parameters**

- `cwd` (`string`): The folder.
- `rel` (`string`): The file, relative to it.

**Returns** `Promise<WorkspaceFile | null>`

Source: [src/chat/workspace/files.ts:156](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L156)

### `redeemProviderReset`

Function.

Redeem one banked reset on a provider's credential (Codex only): empties
its rate-limit windows now. Spends a reset, so call it only on an explicit
user action. The credential's cached snapshot is dropped, so the next fetch
shows the new windows and count.

Never rejects: failures come back as `code: 'failed'` with a short `error`.

```ts
function redeemProviderReset(
  dataDir: string,
  descriptor: ProviderDescriptor,
  credentialKey: string,
  creditId?: string,
): Promise<ProviderResetOutcome>
```

**Parameters**

- `dataDir` (`string`): Directory that holds the credentials files.
- `descriptor` (`ProviderDescriptor`): The provider whose reset to redeem.
- `credentialKey` (`string`): The snapshot's `credentialKey`: which account spends it.
- `creditId?` (`string`): Which reset to spend (`ProviderResetCredit.id`). Absent: the provider picks.

**Returns** `Promise<ProviderResetOutcome>`

Source: [src/chat/provider-usage.ts:424](https://github.com/Tahul/zidane/blob/main/src/chat/provider-usage.ts#L424)

### `redirectUriFromAuthUrl`

Function.

The `redirect_uri` the authorization server was told to come back to,
read out of the authorization URL itself. That's our loopback callback
(`http://127.0.0.1:<port>/callback`) — the UI shows it so an SSH user
knows which address to fish out of the browser's URL bar, and
[`normalizeRedirectPaste`](https://zidane.si/docs/reference/chat.md#normalizeRedirectPaste) uses it to rebuild a full callback URL
from a partial paste.

Returns `null` when the URL doesn't parse or carries no usable
`redirect_uri` — callers degrade to "paste the whole URL".

```ts
function redirectUriFromAuthUrl(authUrl: string): URL | null
```

**Parameters**

- `authUrl` (`string`): The authorization URL to read.

**Returns** `URL | null`

Source: [src/chat/oauth-redirect.ts:37](https://github.com/Tahul/zidane/blob/main/src/chat/oauth-redirect.ts#L37)

### `reduceMcpAuth`

Function.

Apply one event to the state map. Pure, immutable — returns a new map
with only the affected key reassigned.

State transitions are simple "last write wins"; we don't gate (e.g.) a
`login:start` from overriding `authorizing` because the loop layer
never doubles those up — interactive login is serialized in the TUI.

```ts
function reduceMcpAuth(state: McpAuthStateMap, event: McpAuthEvent): McpAuthStateMap
```

**Parameters**

- `state` (`McpAuthStateMap`): The current state map.
- `event` (`McpAuthEvent`): The event to apply.

**Returns** `McpAuthStateMap`

Source: [src/chat/mcp-auth-state.ts:122](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-auth-state.ts#L122)

### `refreshMcpToolsCatalog`

Function.

Force-refresh the on-disk tools cache by opening a transient MCP
connection to every server, capturing the `mcp:tools:list` hook on
the way through, and closing the connection immediately. The
underlying `connectMcpServers` runs every server's bootstrap in
parallel, so the wall-clock cost collapses to the slowest server.

Two design choices worth noting:

- **Shared hook bus.** The caller passes the SAME `Hookable`
  instance that has the cache subscriber wired (typically
  `agent.hooks` after `subscribeMcpToolsCache(agent.hooks, …)`
  ran). That way `mcp:tools:list` fires on the bus the
  subscriber is listening to and the cache lands on disk
  without a separate write path.
- **Transient, NOT shared with the live agent.** The connection
  opened here is closed immediately. The agent's own MCP
  connection is untouched, so a refresh never disrupts an
  in-flight tool call.

The function intentionally swallows per-server failures (they're
already surfaced via `mcp:bootstrap:end ok: false` for any host
that wired the listener). The Promise resolves once every server's
bootstrap settles, success or otherwise.

```ts
function refreshMcpToolsCatalog(
  opts: { buildAuthProvider?: (config: McpServerConfig) => OAuthClientProvider | undefined; hooks: Hookable<AgentHooks>; servers: readonly McpServerConfig[] },
): Promise<void>
```

**Parameters**

- `opts` (`{ buildAuthProvider?: (config: McpServerConfig) => OAuthClientProvider | undefined; hooks: Hookable<AgentHooks>; servers: readonly McpServerConfig[] }`): Servers to connect to, the hooks that receive `mcp:tools:list` and an optional OAuth provider factory.

**Returns** `Promise<void>`

Source: [src/chat/mcp-tools-cache.ts:233](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tools-cache.ts#L233)

### `removeFromSafelist`

Function.

Removes one safelist entry of a project, dropping the project's record once
it holds nothing else (its last entry gone, and no other setting such as
`durability`). Synchronous, so two removals in one process never drop each
other's change.

```ts
function removeFromSafelist(dataDir: string, project: string, entry: string): boolean
```

**Parameters**

- `dataDir` (`string`): Directory that holds `projects.json`.
- `project` (`string`): Project key the entry is stored under.
- `entry` (`string`): The entry.

**Returns** `boolean`: Whether it was there.

Source: [src/chat/safe-mode.ts:207](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L207)

### `removeMcpServer`

Function.

Remove the entry named `name` from the mcps config at `filePath`,
preserving the file's shape and everything else in it — the removal
counterpart of [`upsertMcpServer`](https://zidane.si/docs/reference/chat.md#upsertMcpServer). Throws when the file is
missing, malformed, an unrecognized shape, or has no entry of that
name (the caller shows the message on the settings op line; silently
"succeeding" on a no-op would lie about what happened).

```ts
function removeMcpServer(filePath: string, name: string): RemoveMcpResult
```

**Parameters**

- `filePath` (`string`): Path of the `mcps.json` file.
- `name` (`string`): Name of the entry to remove.

**Returns** `RemoveMcpResult`

Source: [src/chat/mcps-write.ts:142](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-write.ts#L142)

### `removeProviderCredential`

Function.

Sign a provider out — drops EVERY stored credential.

"Signed out" has to mean the provider resolves nothing afterwards, which is
what this guarantees; promoting a survivor instead would silently leave the
provider authenticated through another source. Callers that want that behavior use
[`removeStoredCredential`](https://zidane.si/docs/reference/chat.md#removeStoredCredential) on the one slot they mean.

```ts
function removeProviderCredential(dataDir: string, descriptor: ProviderDescriptor): void
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider descriptor that selects the credential file key.

Source: [src/chat/credentials.ts:757](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L757)

### `removeStoredCredential`

Function.

Drop ONE stored credential. Removing the active one promotes a survivor —
another credential of the same kind when there is one (a second OAuth
account), else whatever is left — so the provider does not go dark while it
still holds a working credential. Returns false when `key` was not stored.

```ts
function removeStoredCredential(
  dataDir: string,
  descriptor: ProviderDescriptor,
  key: string,
): boolean
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider descriptor that selects the credential file key.
- `key` (`string`): File key of the credential to remove.

**Returns** `boolean`

Source: [src/chat/credentials.ts:677](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L677)

### `renderAgentsMdBlock`

Function.

Render one or more `AgentsMdFile` into a system-prompt block.

Top header tells the model what's coming + that it's user-authored
(so it's treated as guidance, not as a system constraint that can't
be questioned). Each file becomes a sub-section with its absolute
path so the model can cite it back when asking follow-up questions.

Exported separately so callers building custom profiles can inject
their own discovery list (e.g. an SDK consumer pulling AGENTS.md
from a remote source) without re-implementing the rendering.

```ts
function renderAgentsMdBlock(files: readonly AgentsMdFile[]): string
```

**Parameters**

- `files` (`readonly AgentsMdFile[]`): Files to render, in the order they should appear.

**Returns** `string`

Source: [src/chat/agents-md.ts:232](https://github.com/Tahul/zidane/blob/main/src/chat/agents-md.ts#L232)

### `renderSession`

Function.

Render a session into a string in the requested format.

`markdown` produces a human-readable transcript: a YAML-free header
block with the title and a one-line `id, turns, runs` summary,
a `## Metadata` list (id, created, updated, status, turns, runs, tokens,
cost), then a `## Conversation` block where each turn renders as `### N. role` over an
italic `ISO-date, run run_X, tokens …` line, with text, thinking, tool
calls, and tool results formatted as appropriate fenced blocks. Useful for sharing a session
with a teammate or pasting into an issue tracker.

`json` returns a 2-space-indented, deterministic dump of the full
`SessionData` blob. Useful for re-importing into the store or for
post-hoc analysis tooling.

```ts
function renderSession(session: SessionData, format: SessionExportFormat): string
```

**Parameters**

- `session` (`SessionData`): The session to render.
- `format` (`SessionExportFormat`): `json` for the pretty-printed session data, `markdown` for a transcript.

**Returns** `string`

Source: [src/chat/session-export.ts:123](https://github.com/Tahul/zidane/blob/main/src/chat/session-export.ts#L123)

### `renderSessionEventXml`

Function.

Wire format between the harness and the model — and, because it is persisted
inside the user turn's text block, between a live session and its replay.

Follows `<task-notification>` exactly: child-tag XML, one block per event,
escaped with `escapeXml`. The model already knows this shape as "the harness
telling me something", which is the point — a main that mistook a worker
report for a human instruction would act on it as one. `eventsFromTurns`
detects the block on replay and re-synthesizes the structured row instead of
painting the raw XML as a user prompt.

```ts
function renderSessionEventXml(event: SessionEvent): string
```

**Parameters**

- `event` (`SessionEvent`): The event to serialize.

**Returns** `string`

Source: [src/chat/orchestration.ts:425](https://github.com/Tahul/zidane/blob/main/src/chat/orchestration.ts#L425)

### `renderSystemMdSeed`

Function.

Seed contents for a freshly created `SYSTEM.md` override, built from the
built-in doctrine.

Seeding with the real doctrine (rather than an empty file) means creating the
override is a behavioral no-op until the user edits it. An empty seed would
silently delete every line of doctrine the moment the file appeared, making
the agent measurably worse with no visible cause.

`currentSystem` should be DOCTRINE ONLY — callers pass
`splitSystemPrompt(doctrineOnlySystem()).static`, which excludes both the
generated `<env>` block (rebuilt per turn) and the `AGENTS.md` block. Baking
the latter in would duplicate it, since it is re-appended below the override
on every compose.

`scope` is the active `userInstructionsScope`; it decides whether the seed's
precedence note is true — see `projectScopeOverridesLoad`.

```ts
function renderSystemMdSeed(currentSystem: string, scope?: AgentsMdScope): string
```

**Parameters**

- `currentSystem` (`string`): The built-in doctrine to seed the file with.
- `scope?` (`AgentsMdScope`): The active `userInstructionsScope`. Decides whether the precedence note mentions a project file.

**Returns** `string`

Source: [src/chat/system-md.ts:230](https://github.com/Tahul/zidane/blob/main/src/chat/system-md.ts#L230)

### `replayTurns`

Function.

The whole replay, and where each turn's events start in it
(`turnStarts[turns.length]` = the end). A page of turns `[from, to)` is
`events.slice(turnStarts[from], turnStarts[to])`: consecutive pages
concatenate to exactly [`eventsFromTurns`](https://zidane.si/docs/reference/chat.md#eventsFromTurns) (exchange separators, run
receipts, subagent markers, tool names and command state as the whole
replay has them), because what a run or the turn before decides lands in
the page of the turn whose replay emitted it.

```ts
function replayTurns(
  turns: SessionTurn[],
  runs: readonly SessionRun[],
): { events: StreamEvent[]; turnStarts: number[] }
```

**Parameters**

- `turns` (`SessionTurn[]`): Persisted turns in order.
- `runs` (`readonly SessionRun[]`): Runs the turns belong to. Pass `[]` for none.

**Returns** `{ events: StreamEvent[]; turnStarts: number[] }`

Source: [src/chat/store.ts:1095](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1095)

### `repoKeyOf`

Function.

The repository `dir` is a checkout of ([`normalizeRemoteUrl`](https://zidane.si/docs/reference/chat.md#normalizeRemoteUrl) of its
`origin`, else of its first remote), read from the git config without
running git. Two copies of one repository on two machines have the same
key. `null` outside git, or for a repository without a shared remote.

```ts
function repoKeyOf(dir: string): string | null
```

**Parameters**

- `dir` (`string`): A folder inside the checkout.

**Returns** `string | null`

Source: [src/chat/project-root.ts:179](https://github.com/Tahul/zidane/blob/main/src/chat/project-root.ts#L179)

### `resolveAgentId`

Function.

Resolve an agent id against a registry with sensible fallbacks: the
requested id wins when present, then `defaultId`, then the first key.
Returns `null` when the registry is empty (host misconfiguration —
the caller should surface this rather than silently failing).

```ts
function resolveAgentId(
  registry: AgentRegistry,
  requestedId: string | undefined,
  defaultId: string | undefined,
): string | null
```

**Parameters**

- `registry` (`AgentRegistry`): The agent registry to search.
- `requestedId` (`string | undefined`): Preferred profile id, such as the persisted last agent.
- `defaultId` (`string | undefined`): Fallback profile id used when the requested one is missing.

**Returns** `string | null`

Source: [src/chat/agents.ts:649](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L649)

### `resolveApprovalForPayload`

Function.

Resolves an approval decision into per-hunk outcomes and a gate verdict.
A deny blocks every hunk. A partial decision applies the hunks its mask keeps and blocks when none are kept. Any accept decision applies every hunk.

```ts
function resolveApprovalForPayload(
  decision: ApprovalDecision,
  payload: EditPayload,
): ResolvedApproval
```

**Parameters**

- `decision` (`ApprovalDecision`): The user's approval decision.
- `payload` (`EditPayload`): The edit payload being approved.

**Returns** `ResolvedApproval`

Source: [src/chat/edit-approval.ts:73](https://github.com/Tahul/zidane/blob/main/src/chat/edit-approval.ts#L73)

### `resolveChipColor`

Function.

Look up the chip color pair for a provider id, falling back to
`chips.default` when the theme has no kind-specific entry. Mirrors
`resolveChipStyleId` so both rendering surfaces (submitted echo +
live textarea) share one canonical lookup rule.

```ts
function resolveChipColor(chips: ChipColorMap, providerId: string): ChipColor
```

**Parameters**

- `chips` (`ChipColorMap`): The theme's chip color map.
- `providerId` (`string`): Completion provider id to look up.

**Returns** `ChipColor`

Source: [src/chat/theme.ts:118](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L118)

### `resolveConfig`

Function.

Resolve user options into a fully-bound runtime config.

NOT pure: besides disk reads, this mutates `process.env` so the harness
providers pick up the chat engine's credential store — it sets
`ZIDANE_CREDENTIALS_PATH` and reconciles stored API keys / OAuth entries
into env via `applyApiKeyEnv` (which can overwrite or delete ambient keys
like `ANTHROPIC_API_KEY`; see its JSDoc for the precedence rationale).

```ts
function resolveConfig(options: ChatOptions): ResolvedConfig
```

**Parameters**

- `options` (`ChatOptions`): Host options. `store` is required; a missing `store` throws.

**Returns** `ResolvedConfig`

Source: [src/chat/config.ts:518](https://github.com/Tahul/zidane/blob/main/src/chat/config.ts#L518)

### `resolveCwdPath`

Function.

Resolve a workspace-relative path against the session's `cwd`. Absolute paths pass through.

```ts
function resolveCwdPath(cwd: string, p: string): string
```

**Parameters**

- `cwd` (`string`): Session working directory.
- `p` (`string`): Absolute or workspace-relative path.

**Returns** `string`

Source: [src/chat/host/helpers.ts:167](https://github.com/Tahul/zidane/blob/main/src/chat/host/helpers.ts#L167)

### `resolveDecisionsOption`

Function.

`AgentOptions.decisions` from settings:

- off ([`decisionsEnabled`](https://zidane.si/docs/reference/chat-pure.md#decisionsEnabled)) → `false` (every decision returns `unavailable`).
- the picked model when its provider builds and its credentials allow it.
- otherwise the first available decision-capable provider (silent fallback,
  like the compaction model), preferring a provider with a native decisions
  endpoint over one that only approximates them.
- otherwise the first decision-capable provider that builds, so failures
  explain the missing credential; `undefined` when there is none.

```ts
function resolveDecisionsOption(
  settings: Pick<Settings, 'decisions' | 'approvalMode' | 'decisionModel'>,
  providers: Readonly<Record<string, ProviderDescriptor>>,
  buildProvider: (key: string) => Provider,
): false | AgentDecisionsOptions | undefined
```

**Parameters**

- `settings` (`Pick<Settings, 'decisions' | 'approvalMode' | 'decisionModel'>`): Settings that hold the `decisions` switch and the picked `decisionModel`.
- `providers` (`Readonly<Record<string, ProviderDescriptor>>`): Provider descriptors keyed by id.
- `buildProvider` (`(key: string) => Provider`): Builds a provider for a provider key. May throw.

**Returns** `false | AgentDecisionsOptions | undefined`

Source: [src/chat/decision-models.ts:113](https://github.com/Tahul/zidane/blob/main/src/chat/decision-models.ts#L113)

### `resolveExecution`

Function.

THE way a host gets a session's execution context: its environment's
provider decides. The key is namespaced by kind so two providers can't
collide on the same configuration string.

```ts
function resolveExecution(
  registry: EnvironmentRegistry,
  input: EnvironmentExecutionInput,
): EnvironmentExecution
```

**Parameters**

- `registry` (`EnvironmentRegistry`): Registry that holds the session's provider.
- `input` (`EnvironmentExecutionInput`): Session id, directory, environment, config, and optional OS sandbox policy.

**Returns** `EnvironmentExecution`

Source: [src/chat/environment.ts:469](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L469)

### `resolveExtensionKeybindings`

Function.

Resolve the live spec string for each extension-contributed
keybinding. The user's `keybindings.json` is consulted for an
override on the binding's `id`; absent / invalid entries fall back
to the binding's `default`.

A chord that is reserved or a bare printable character
(see `isReservedExtensionBinding`, `isBareCharExtensionBinding`)
is refused: a user override falls back to the default, and such a default
resolves to unbound.

Pass the raw object (from [`readKeybindingsRaw`](https://zidane.si/docs/reference/chat.md#readKeybindingsRaw)) — passing
`KeyBindings` here wouldn't help because the typed map only carries
built-in `KeyAction` entries.

Returns one entry per input binding, in source order. Empty `spec`
(binding declared `default: ''` and the user didn't bind it) is a
legitimate "unbound" state — the dispatcher never matches an empty
spec, so the action stays accessible via re-binding through
`keybindings.json`.

```ts
function resolveExtensionKeybindings(
  bindings: readonly { default: string; id: string }[],
  raw: Readonly<Record<string, unknown>>,
): ResolvedExtensionKeybinding[]
```

**Parameters**

- `bindings` (`readonly { default: string; id: string }[]`): Extension keybindings with their ids and default specs.
- `raw` (`Readonly<Record<string, unknown>>`): Raw object from [`readKeybindingsRaw`](https://zidane.si/docs/reference/chat.md#readKeybindingsRaw).

**Returns** `ResolvedExtensionKeybinding[]`

Source: [src/chat/keybindings.ts:834](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L834)

### `resolveInlineSuggestion`

Function.

Query `providers` in order; the first provider yielding a non-empty
candidate list wins (a bare string normalizes to a 1-element list).
Provider errors are swallowed (a broken extension provider must not
break typing), and each provider races its deadline —
`PROVIDER_DEADLINE_MS` by default, a provider's own `deadlineMs`
(capped at `MAX_PROVIDER_DEADLINE_MS`) when declared — so one
hung provider can't permanently suppress the ghosts of providers
behind it. Returns null when aborted or nothing matched.

```ts
function resolveInlineSuggestion(
  providers: readonly InlineAutocompleteProvider[],
  ctx: InlineAutocompleteContext,
  deadlineMs?: number,
  maxDeadlineMs?: number,
): Promise<{ providerId: string; suffixes: readonly string[] } | null>
```

**Parameters**

- `providers` (`readonly InlineAutocompleteProvider[]`): Providers in query order.
- `ctx` (`InlineAutocompleteContext`): Prompt buffer, cursor, working directory, and abort signal.
- `deadlineMs?` (`number`, default `PROVIDER_DEADLINE_MS`): Per-provider deadline in milliseconds when the provider declares none. Defaults to 150.
- `maxDeadlineMs?` (`number`, default `MAX_PROVIDER_DEADLINE_MS`): Cap applied to a provider's own `deadlineMs`. Defaults to `MAX_PROVIDER_DEADLINE_MS`.

**Returns** `Promise<{ providerId: string; suffixes: readonly string[] } | null>`

Source: [src/chat/inline-autocomplete.ts:88](https://github.com/Tahul/zidane/blob/main/src/chat/inline-autocomplete.ts#L88)

### `resolvePaths`

Function.

Resolve each text against `cwd`; answers line up with `texts`. A cwd that
does not exist resolves everything as missing.

```ts
function resolvePaths(cwd: string, texts: readonly unknown[]): Promise<ResolvedPath[]>
```

**Parameters**

- `cwd` (`string`): The folder names resolve under.
- `texts` (`readonly unknown[]`): The names, as written.

**Returns** `Promise<ResolvedPath[]>`

Source: [src/chat/workspace/path-resolve.ts:232](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/path-resolve.ts#L232)

### `resolvePlatformPackage`

Function.

Resolves the platform-specific tarball package name for the current machine.
Supports macOS on arm64 and x64, and Linux on arm64 and x64 with glibc. Returns `null` for other targets.

```ts
function resolvePlatformPackage(opts?: PlatformTarballOptions): string | null
```

**Parameters**

- `opts?` (`PlatformTarballOptions`, default `{}`): Optional overrides for platform, architecture, libc and name prefix.

**Returns** `string | null`

Source: [src/chat/auto-update.ts:1235](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L1235)

### `resolveProviderConfig`

Function.

A kind's effective options: bound settings, stored values, secrets, env-var
fallbacks, then schema defaults — in that order.

```ts
function resolveProviderConfig(
  provider: EnvironmentProvider,
  context: ResolveConfigContext,
): EnvironmentConfig
```

**Parameters**

- `provider` (`EnvironmentProvider`): Provider whose `configSchema` defines the options.
- `context` (`ResolveConfigContext`): Settings, secret store, and optional env-var source.

**Returns** `EnvironmentConfig`

Source: [src/chat/environment-settings.ts:153](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L153)

### `resolveSessionCwd`

Function.

Effective process cwd for a persisted session row: the GUI-recorded
`metadata.cwd` when present, else the canonical `projectRoot`
(TUI-created sessions), else the directory-less HOME anchor. Empty
strings count as unset — forks of directory-less sessions persist
`cwd: ''`.

```ts
function resolveSessionCwd(
  row: { metadata?: Record<string, unknown>; projectRoot?: string },
): string
```

**Parameters**

- `row` (`{ metadata?: Record<string, unknown>; projectRoot?: string }`): Persisted session row: its metadata and project root.

**Returns** `string`

Source: [src/chat/session-cwd.ts:31](https://github.com/Tahul/zidane/blob/main/src/chat/session-cwd.ts#L31)

### `resolveSessionExportTarget`

Function.

Resolve the export target for a session id + format. Pure: no fs
write happens here, the caller decides whether to `existsSync` the
directory or hand the path to `writeSessionExport`.

Anchor selection walks upward from `cwd` looking for a `.git` entry;
the first hit anchors to that repo's root (`project`). When the walk
exits without finding `.git`, the destination anchors to `home`: the
user's storage data dir.

```ts
function resolveSessionExportTarget(
  opts: { format: SessionExportFormat; sessionId: string } & ResolveOptions,
): SessionExportTarget
```

**Parameters**

- `opts` (`{ format: SessionExportFormat; sessionId: string } & ResolveOptions`): Session id, format and optional overrides for the working directory, home directory, prefix and data directory.

**Returns** `SessionExportTarget`

Source: [src/chat/session-export.ts:88](https://github.com/Tahul/zidane/blob/main/src/chat/session-export.ts#L88)

### `resolveSessionModel`

Function.

A session's provider, pinned account, model and fallback chain. With
`config`, a session without a model pick on an account (pinned, else the
provider's active one) that has its own model list runs on the provider's
default when the list has it, else on the list's first model.

```ts
function resolveSessionModel(
  metadata: Record<string, unknown> | undefined,
  providers: Readonly<Record<string, ProviderDescriptor>>,
  config?: Pick<ResolvedConfig, 'accountModelsFor'>,
): { account: string | undefined; model: string; modelFallbacks: ModelChainEntry[]; modelOverride: string | undefined; providerDescriptor: ProviderDescriptor; providerKey: string }
```

**Parameters**

- `metadata` (`Record<string, unknown> | undefined`): The session's metadata.
- `providers` (`Readonly<Record<string, ProviderDescriptor>>`): The provider registry.
- `config?` (`Pick<ResolvedConfig, 'accountModelsFor'>`): The resolved config's per-account model lists.

**Returns** `{ account: string | undefined; model: string; modelFallbacks: ModelChainEntry[]; modelOverride: string | undefined; providerDescriptor: ProviderDescriptor; providerKey: string }`

Source: [src/chat/host/models/index.ts:124](https://github.com/Tahul/zidane/blob/main/src/chat/host/models/index.ts#L124)

### `resolveStorageDirs`

Function.

Resolve the four storage slots according to the precedence above.

Pure aside from one filesystem probe (`readdirSync(home/<prefix>)`,
see `holdsLegacyStorage`). No directories are created here; the
relevant write paths (state store, credentials writer, persist
dir) handle their own lazy `mkdirSync` already.

```ts
function resolveStorageDirs(opts?: ResolveStorageDirsOptions): StorageDirs
```

**Parameters**

- `opts?` (`ResolveStorageDirsOptions`, default `{}`): Prefix, explicit storage root and test overrides for home, platform and environment.

**Returns** `StorageDirs`

Source: [src/chat/xdg.ts:105](https://github.com/Tahul/zidane/blob/main/src/chat/xdg.ts#L105)

### `resolveStoragePaths`

Function.

Compute the on-disk layout for a single TUI session. Public so hosts
(CLI tools, tests, scripted bootstraps) can ask "where will sessions
land?" without going through `resolveConfig`'s full machinery.

Storage-slot resolution (config / data / state / cache) is delegated
to [`resolveStorageDirs`](https://zidane.si/docs/reference/chat.md#resolveStorageDirs). In legacy modes the four slots collapse
to a single `<storageDir>/<prefix>` directory; in XDG mode they fan
out into `$XDG_CONFIG_HOME/zidane`, `$XDG_DATA_HOME/zidane`,
`$XDG_STATE_HOME/zidane`, `$XDG_CACHE_HOME/zidane`. See the module
doc on `./xdg.ts` for the full precedence chain.

Decision logic for project mode (least → most specific):

1. Default: OFF — sessions live in the user-scoped `dataDir`
   and get scoped per-project via the `SessionData.projectRoot` tag.
2. `<configDir>/config.json` `projectDb` flag (when present).
3. `ZIDANE_PROJECT_DB` env var (`0` / `false` / `off` / `no` → off,
   `1` / `true` / `on` / `yes` → on, any other value ignored with a warning).
4. `options.projectDb` (host call site).

Project mode activates only when the resolution above lands on
`true` AND a git repo is found above `cwd`. Outside git or with the
flag off, sessions + state stay at the user-scoped slots.

Auto-creates the project `.{prefix}/` directory when mode resolves
to ON (silent — mirrors `ensureStateDir`), with a `.gitignore` of `*` when
it creates the directory. User slots are created
lazily by the state-store + credentials writers; we don't pre-touch
them here so a read-only invocation stays read-only.

```ts
function resolveStoragePaths(
  opts: { cwd: string; prefix: string; projectDb?: boolean; storageDir?: string },
): ResolvedPaths
```

**Parameters**

- `opts` (`{ cwd: string; prefix: string; projectDb?: boolean; storageDir?: string }`): Storage prefix, working directory and optional explicit storage root and project-mode override.

**Returns** `ResolvedPaths`

Source: [src/chat/config.ts:729](https://github.com/Tahul/zidane/blob/main/src/chat/config.ts#L729)

### `resolveTheme`

Function.

Resolve a theme id to its full `Theme`, falling back to default on unknown ids.

```ts
function resolveTheme(
  id: string | undefined,
  extraThemes?: Readonly<Record<string, Theme>>,
): Theme
```

**Parameters**

- `id` (`string | undefined`): Theme id, usually from `Settings.theme`.
- `extraThemes?` (`Readonly<Record<string, Theme>>`): Additional themes keyed by id. Built-ins win on an id collision.

**Returns** `Theme`

Source: [src/chat/theme.ts:431](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L431)

### `restoreChainFallbacks`

Function.

Restore the fallback tail of a persisted chain for a resolved primary
pair. Returns entries 1+ only when the persisted chain's entry 0
matches `primary` — a divergent chain belongs to a different selection
(e.g. a `--model` launch override) and silently attaching it would
surprise. Entries whose provider isn't registered, entries equal to
the primary, and exact duplicates drop. Accepts the raw persisted
value (`state.json` is user-editable) — shape validation included.

```ts
function restoreChainFallbacks(
  saved: unknown,
  primary: ModelChainEntry,
  isRegistered: (key: string) => boolean,
): ModelChainEntry[]
```

**Parameters**

- `saved` (`unknown`): Persisted chain of unknown shape.
- `primary` (`ModelChainEntry`): The current primary pick. Fallbacks are restored only when the saved chain starts with it.
- `isRegistered` (`(key: string) => boolean`): Whether a provider key is registered. Entries on other providers are dropped.

**Returns** `ModelChainEntry[]`

Source: [src/chat/model-chain.ts:303](https://github.com/Tahul/zidane/blob/main/src/chat/model-chain.ts#L303)

### `restoreModelOptions`

Function.

Resolve a model's remembered options for a (re)pick: keep only options the
model still declares AND that are enabled, dropping any that no longer apply
(e.g. switching away from a model that supported `fast`). Returns `undefined`
when nothing applies so callers can omit the field entirely.

Shared by the TUI pick path and the launch-time resume path so "which options
survive a model switch / relaunch" is decided in exactly one place.

```ts
function restoreModelOptions(
  descriptor: ProviderDescriptor,
  modelId: string,
  remembered: Record<string, Record<string, boolean>> | undefined,
): Record<string, boolean> | undefined
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider descriptor.
- `modelId` (`string`): The model being picked.
- `remembered` (`Record<string, Record<string, boolean>> | undefined`): Remembered enabled options keyed by model id.

**Returns** `Record<string, boolean> | undefined`

Source: [src/chat/providers.ts:1285](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L1285)

### `rewriteMultiEditHeader`

Function.

Rewrite a `multi_edit` body header so the totals reflect the model's
ORIGINAL edit list (the merged outcomes count) instead of the subset
the body actually saw after gate rebinding. Without this, a partially
approved call surfaces a misleading `applied 2 of 2 edits` (subset
counts) on the wire even when the original was `applied 2 of 3`.

Three body-side shapes are handled (matching `multi_edit`'s emit):

1. `Edited <path>: applied N edits (R replacements).`
2. `Edited <path>: applied N of M edits (R replacements).`
3. `multi_edit error: no edits applied to <path> (M attempted).`

The replacements count is preserved verbatim — it's a body-side stat
the chat layer can't recompute. When the first line doesn't look like
any of the three shapes (e.g. an unrelated error preamble bubbled up),
the text is returned unchanged.

```ts
function rewriteMultiEditHeader(
  text: string,
  merged: readonly EditOutcome[],
  path: string,
): string
```

**Parameters**

- `text` (`string`): Tool result text whose first line is rewritten.
- `merged` (`readonly EditOutcome[]`): Final per-hunk outcomes.
- `path` (`string`): File path shown in the header.

**Returns** `string`

Source: [src/chat/edit-approval.ts:335](https://github.com/Tahul/zidane/blob/main/src/chat/edit-approval.ts#L335)

### `runCompaction`

Function.

Compact the session through `agent.compact()` (the SDK's cached
compaction path) and emit a compact-summary stream event. Brackets the
in-flight window with `compacting:start` / `compacting:end` signals.
Resolves what it did, or `null` when nothing was compacted (nothing to
summarize, cancelled, failed: reported as an event).

```ts
function runCompaction(
  entry: CompactionEntry,
  broadcast: (signal: HostSignal) => void,
): Promise<CompactionReport | null>
```

**Parameters**

- `entry` (`CompactionEntry`): Live state of the session to compact.
- `broadcast` (`(signal: HostSignal) => void`): Pushes the `compacting:start` and `compacting:end` signals to clients.

**Returns** `Promise<CompactionReport | null>`

Source: [src/chat/host/compaction/run.ts:58](https://github.com/Tahul/zidane/blob/main/src/chat/host/compaction/run.ts#L58)

### `runDiagnostics`

Function.

Runs the project's own tools on `paths`, cached until the files move.

```ts
function runDiagnostics(
  cwd: string,
  rawPaths: readonly string[],
  opts?: RunOptions,
): Promise<DiagnosticsResult>
```

**Parameters**

- `cwd` (`string`): The folder.
- `rawPaths` (`readonly string[]`): The files, relative to it.
- `opts?` (`RunOptions`, default `{}`): Tools switched off, the per-command timeout, the state folder, the abort signal, the tools.

**Returns** `Promise<DiagnosticsResult>`

Source: [src/chat/workspace/diagnostics/runner.ts:317](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/diagnostics/runner.ts#L317)

### `runEndNotice`

Function.

Structured stop notice for an abnormal `finishReason`, or `undefined` for a
normal end.

Normal completions (`stop` / `tool-calls`) and user aborts return
`undefined` — and so does a SUCCESSFUL auto-resume, because recovery ends
the run on `stop`. That is what keeps the notice quiet exactly when the
ceiling was handled rather than hit. `error` runs already surface their own
error event, so a second notice would double-report.

```ts
function runEndNotice(reason: TurnFinishReason | undefined): RunEndNotice | undefined
```

**Parameters**

- `reason` (`TurnFinishReason | undefined`): Finish reason of the run's last turn.

**Returns** `RunEndNotice | undefined`

Source: [src/chat/run-end-notice.ts:48](https://github.com/Tahul/zidane/blob/main/src/chat/run-end-notice.ts#L48)

### `runInProgress`

Function.

The session's top-level run in progress: the latest `running` run without
a parent (subagent runs share the session).

```ts
function runInProgress(runs: readonly SessionRun[]): SessionRun | undefined
```

**Parameters**

- `runs` (`readonly SessionRun[]`): The session's runs.

**Returns** `SessionRun | undefined`

Source: [src/chat/store.ts:1640](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1640)

### `runIntegrationSignIn`

Function.

Run the sign-in of a provider that signs in with an integration
(`ProviderDescriptor.integration`): the integration's connect flow, on the
same callbacks as [`runOAuthLogin`](https://zidane.si/docs/reference/chat.md#runOAuthLogin). The registry that holds the
integration stores the connection (another account adds one); the caller
persists nothing. Rejects when no extension in this process registers it.

```ts
function runIntegrationSignIn(
  descriptor: ProviderDescriptor,
  options: OAuthFlowOptions,
): Promise<Connection>
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): Provider with an `integration`.
- `options` (`OAuthFlowOptions`): Handlers for the authorization URL, prompts, device codes and progress.

**Returns** `Promise<Connection>`

Source: [src/chat/oauth.ts:133](https://github.com/Tahul/zidane/blob/main/src/chat/oauth.ts#L133)

### `runOAuthLogin`

Function.

Run the OAuth login flow for a provider.

Returns the OAuth credentials on success; caller persists them via
`setProviderCredential(dataDir, descriptor, { kind: 'oauth', ...credentials })`.
Throws when the descriptor has no `oauthProvider` configured.

```ts
function runOAuthLogin(
  descriptor: ProviderDescriptor,
  options: OAuthFlowOptions,
): Promise<OAuthCredentials>
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): Provider with an `oauthProvider`. Throws when it has none.
- `options` (`OAuthFlowOptions`): Handlers for the authorization URL, prompts, device codes and progress.

**Returns** `Promise<OAuthCredentials>`

Source: [src/chat/oauth.ts:150](https://github.com/Tahul/zidane/blob/main/src/chat/oauth.ts#L150)

### `runProgress`

Function.

The top-level run in progress as its agent saved it: when it began and the
output its settled turns produced. A window that missed `run:start`
(opened or reattached mid-run) starts its run readout from these, so the
time and the count don't restart at zero. `undefined` when no top-level
run is in progress.

```ts
function runProgress(data: Pick<SessionData, 'runs' | 'turns'>): RunProgress | undefined
```

**Parameters**

- `data` (`Pick<SessionData, 'runs' | 'turns'>`): The session's runs and turns.

**Returns** `RunProgress | undefined`

Source: [src/chat/store.ts:1653](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1653)

### `runSessionEnvironmentAction`

Function.

Run `actionId` for a session on `execution` — the session's own (hosts pass
the cached one, so the action runs exactly where the agent does). Throws
when the environment isn't ready or doesn't offer the action.

```ts
function runSessionEnvironmentAction(
  input: { actionId: string; config: EnvironmentConfig; cwd: string; environment: SessionEnvironment; execution: { context: ExecutionContext; handle: () => Promise<ExecutionHandle> }; registry: EnvironmentRegistry; sessionId: string },
): Promise<EnvironmentActionResult>
```

**Parameters**

- `input` (`{ actionId: string; config: EnvironmentConfig; cwd: string; environment: SessionEnvironment; execution: { context: ExecutionContext; handle: () => Promise<ExecutionHandle> }; registry: EnvironmentRegistry; sessionId: string }`): Registry, session id, action id, environment, config, host directory, and the session's execution context with its handle resolver.

**Returns** `Promise<EnvironmentActionResult>`

Source: [src/chat/environment.ts:511](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L511)

### `runUpdateCommand`

Function.

Run the `upgrade` subcommand. Exit code contract:

- `0`  — update succeeded, `--check` reported no update, or `--check`
  reported an update (informational only).
- `1`  — update failed (PM exit non-zero, in-place download failed,
  registry check failed without `--force`, release not
  downloadable yet, or the installed version did not change).
- `2`  — argv parse error (unknown flag).

Output is a short ledger, one line per package, led by a status mark
(`src/cli-style.ts`):
· zidane-tui  7.0.7 → 7.0.8
$ volta install zidane-tui\@latest
…
✓ zidane-tui  7.0.8 installed
Diagnostics go to stderr and say what to do next.

```ts
function runUpdateCommand(
  options: RunUpdateCommandOptions,
): Promise<RunUpdateCommandResult>
```

**Parameters**

- `options` (`RunUpdateCommandOptions`): Package, version, argv and I/O settings for the command.

**Returns** `Promise<RunUpdateCommandResult>`

Source: [src/chat/auto-update-cli.ts:152](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-cli.ts#L152)

### `runUpdateWorkerMain`

Function.

Entry of the worker process. Returns the exit code: `0` once a result is
written, `3` when another install holds the lock, `2` for a bad request.

```ts
function runUpdateWorkerMain(
  argv: readonly string[],
  deps?: Pick<RunUpdateWorkerDeps, 'upgradeCommand'>,
): Promise<number>
```

**Parameters**

- `argv` (`readonly string[]`): Arguments after the worker's own command (`--request <value>`).
- `deps?` (`Pick<RunUpdateWorkerDeps, 'upgradeCommand'>`, default `{}`): Overrides for tests: the `upgradeCommand` to run.

**Returns** `Promise<number>`

Source: [src/chat/auto-update-worker.ts:259](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L259)

### `safelistProjectKey`

Function.

The `projects.json` key for a working directory: its project root (the
main checkout for a linked worktree; the dir itself outside git) — the
same "project" the TUI scopes its safelist to, so a subdirectory session,
every worktree of the repo, and every host share one safelist.

```ts
function safelistProjectKey(cwd: string): string
```

**Parameters**

- `cwd` (`string`): Working directory to resolve.

**Returns** `string`

Source: [src/chat/safe-mode.ts:41](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L41)

### `SafeModeProvider`

Function.

Owns the queue + actions. Splits the value across two contexts so a queue
change doesn't invalidate every callback memo that closes over the actions.

```ts
function SafeModeProvider(props: { children: ReactNode }): Element
```

**Parameters**

- `props` (`{ children: ReactNode }`): React props. `children` renders inside both contexts.

**Returns** `Element`

Source: [src/chat/safe-mode-context.tsx:119](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode-context.tsx#L119)

### `sanitizeModelChain`

Function.

Validate a persisted chain read back from `state.json`. The state file
is user-editable JSON, so the value can be arbitrarily malformed —
return only the well-shaped `{ provider, model }` entries (both
non-empty strings), or `[]` for anything else. Never throws.

```ts
function sanitizeModelChain(value: unknown): ModelChainEntry[]
```

**Parameters**

- `value` (`unknown`): Untrusted input, usually parsed settings.

**Returns** `ModelChainEntry[]`

Source: [src/chat/model-chain.ts:275](https://github.com/Tahul/zidane/blob/main/src/chat/model-chain.ts#L275)

### `sanitizeTodoItems`

Function.

Validates untrusted todo items: rows without a string id and content and a
known status drop out, then the list is capped. The same validator guards
the tool input and user edits.

```ts
function sanitizeTodoItems(raw: readonly unknown[], cap?: number): TodoItem[]
```

**Parameters**

- `raw` (`readonly unknown[]`): Untrusted list of items.
- `cap?` (`number`, default `DEFAULT_MAX_TODO_ITEMS`): Maximum number of items kept.

**Returns** `TodoItem[]`

Source: [src/chat/todos.ts:294](https://github.com/Tahul/zidane/blob/main/src/chat/todos.ts#L294)

### `saveMcpToolsCache`

Function.

Atomic full-cache write. Used by [`subscribeMcpToolsCache`](https://zidane.si/docs/reference/chat.md#subscribeMcpToolsCache) after
each upsert. Failures bubble up as the underlying `writeFileAtomic`
error — the caller logs under `ZIDANE_DEBUG` and swallows.

```ts
function saveMcpToolsCache(opts: { cache: McpToolsCache; dataDir: string }): void
```

**Parameters**

- `opts` (`{ cache: McpToolsCache; dataDir: string }`): The data directory and the full cache to write.

Source: [src/chat/mcp-tools-cache.ts:123](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tools-cache.ts#L123)

### `saveState`

Function.

Writes the TUI state file atomically, creating its directory when needed.

```ts
function saveState(path: string, state: TuiState): void
```

**Parameters**

- `path` (`string`): Path of the state file.
- `state` (`TuiState`): The state to persist.

Source: [src/chat/store.ts:181](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L181)

### `scopeCwd`

Function.

Map a renderer-supplied scope cwd to the directory the main process
should actually operate in. The renderer signals "no folder" with an
empty string (`SidebarSession.cwd` is `''` for directory-less
sessions); catalogs (@-files, skills, commands, extensions, MCPs)
then root at HOME, matching what the TUI offers when run from `~`.

```ts
function scopeCwd(cwd: string): string
```

**Parameters**

- `cwd` (`string`): Working directory the renderer sent. `''` means no folder.

**Returns** `string`

Source: [src/chat/session-cwd.ts:49](https://github.com/Tahul/zidane/blob/main/src/chat/session-cwd.ts#L49)

### `scoreArtifacts`

Function.

Filters and ranks artifacts for a query.
An empty query returns every artifact. Otherwise it matches the kind aliases, kind label and a fuzzy text match.

```ts
function scoreArtifacts(
  catalog: readonly ArtifactReferenceEntry[],
  query: string,
  limit?: number,
): CompletionItem<ArtifactReferenceData>[]
```

**Parameters**

- `catalog` (`readonly ArtifactReferenceEntry[]`): Artifacts to search.
- `query` (`string`): Text typed after the trigger.
- `limit?` (`number`, default `DEFAULT_RESULT_LIMIT`): Maximum number of items returned.

**Returns** `CompletionItem<ArtifactReferenceData>[]`

Source: [src/chat/completion-artifacts.ts:80](https://github.com/Tahul/zidane/blob/main/src/chat/completion-artifacts.ts#L80)

### `searchWorkspace`

Function.

Lines holding `query` (a fixed string) under `cwd`: ripgrep, else `git grep`, else a capped walk.

```ts
function searchWorkspace(
  cwd: string,
  query: string,
  opts: ContentSearchOptions,
): Promise<ContentSearchResult>
```

**Parameters**

- `cwd` (`string`): The folder.
- `query` (`string`): The text.
- `opts` (`ContentSearchOptions`): Case, and the search box it answers (a later search with the same id cancels this one).

**Returns** `Promise<ContentSearchResult>`

Source: [src/chat/workspace/search.ts:90](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/search.ts#L90)

### `selectionAfterTurnRemoval`

Function.

Keep turn-selection mode alive after deleting its active row. Prefer the
row that slides into the deleted index; at the tail, select the new last
row. Returns null only when no selectable rows survive.

```ts
function selectionAfterTurnRemoval(
  before: readonly string[],
  removedId: string,
  after: readonly string[],
): string | null
```

**Parameters**

- `before` (`readonly string[]`): Selectable turn ids before the removal.
- `removedId` (`string`): Id of the removed turn.
- `after` (`readonly string[]`): Selectable turn ids after the removal.

**Returns** `string | null`

Source: [src/chat/turn-selection.ts:28](https://github.com/Tahul/zidane/blob/main/src/chat/turn-selection.ts#L28)

### `serializeInteractionResponse`

Function.

Format an [`InteractionResponse`](https://zidane.si/docs/reference/chat.md#InteractionResponse) as the tool_result content. JSON
keeps the envelope parseable and explicit. Empty fields are stripped so
the model doesn't waste tokens reading absent values.

```ts
function serializeInteractionResponse(response: InteractionResponse): string
```

**Parameters**

- `response` (`InteractionResponse`): The user's or automatic mode's response.

**Returns** `string`: A JSON string for the `tool_result` content.

Source: [src/chat/interactions-protocol.ts:449](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L449)

### `serializePreview`

Function.

Strip readonly markers so the `PreviewResult` survives structured-clone
and JSON transports. Field-for-field copy — the resolver output is
already JSON-safe primitives.

```ts
function serializePreview(preview: PreviewResult): SerializedPreview
```

**Parameters**

- `preview` (`PreviewResult`): Preview from `previewEditPayload`.

**Returns** `SerializedPreview`

Source: [src/chat/host/helpers.ts:198](https://github.com/Tahul/zidane/blob/main/src/chat/host/helpers.ts#L198)

### `serveWorkspaceMedia`

Function.

The response for one media address: its bytes, a range of them (206), or
404 (not media, not found, outside the folder), 413 (over `maxBytes`), 416
(a range past the end).

```ts
function serveWorkspaceMedia(
  request: WorkspaceMediaRequest,
  range: string | null,
  options?: WorkspaceMediaOptions,
): Promise<Response>
```

**Parameters**

- `request` (`WorkspaceMediaRequest`): The folder, the path and the side.
- `range` (`string | null`): The request's `Range` header.
- `options?` (`WorkspaceMediaOptions`, default `{}`): Options.

**Returns** `Promise<Response>`

Source: [src/chat/workspace/media.ts:154](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/media.ts#L154)

### `sessionComposition`

Function.

The composition a session on `profileId` builds with: the one it ran on
(`currentKey`) while the profile fits it, else the profile's own.
`undefined` when `profileId` is not registered.

```ts
function sessionComposition(
  registry: AgentRegistry,
  profileId: string,
  currentKey: string | undefined,
): AgentComposition | undefined
```

**Parameters**

- `registry` (`AgentRegistry`): The profiles available to the session.
- `profileId` (`string`): The session's profile.
- `currentKey` (`string | undefined`): The key of the composition the session ran on, if any.

**Returns** `AgentComposition | undefined`

Source: [src/chat/agents.ts:221](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L221)

### `sessionEnvironmentSummary`

Function.

The environment summary for list rows — omitted for local sessions (the common case).

```ts
function sessionEnvironmentSummary(
  metadata: Record<string, unknown> | undefined,
): SessionEnvironmentSummary | undefined
```

**Parameters**

- `metadata` (`Record<string, unknown> | undefined`): Session metadata that may hold the environment record.

**Returns** `SessionEnvironmentSummary | undefined`

Source: [src/chat/store.ts:321](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L321)

### `sessionModelFallbacks`

Function.

Validated fallback tail for list rows/pickers — head-matched against
the session's persisted primary, unregistered providers kept (the
caller filters against its own registry when it has one).

```ts
function sessionModelFallbacks(
  metadata: Record<string, unknown> | undefined,
): ModelFallbackTarget[] | undefined
```

**Parameters**

- `metadata` (`Record<string, unknown> | undefined`): Session metadata holding `providerKey`, `modelId` and `modelChain`.

**Returns** `ModelFallbackTarget[] | undefined`

Source: [src/chat/model-chain-metadata.ts:95](https://github.com/Tahul/zidane/blob/main/src/chat/model-chain-metadata.ts#L95)

### `setCredentialEmail`

Function.

Record what the profile lookup found for the stored OAuth account holding
`access`: its email, or (`undefined`) that there is none to find, so it is
not looked up again. Returns false when no slot holds that token any more
(refreshed, re-signed in or removed meanwhile).

```ts
function setCredentialEmail(
  dataDir: string,
  descriptor: ProviderDescriptor,
  access: string,
  email: string | undefined,
): boolean
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider descriptor that selects the credential file key.
- `access` (`string`): Access token the lookup used: names the account.
- `email` (`string | undefined`): The account's email, or `undefined` when the lookup found none.

**Returns** `boolean`

Source: [src/chat/credentials.ts:732](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L732)

### `setCredentialLabel`

Function.

Name a stored OAuth account. An empty label drops the field, which reads
back as [`DEFAULT_OAUTH_ACCOUNT_LABEL`](https://zidane.si/docs/reference/chat-pure.md#DEFAULT_OAUTH_ACCOUNT_LABEL). Returns false when `key` holds
no OAuth credential.

```ts
function setCredentialLabel(
  dataDir: string,
  descriptor: ProviderDescriptor,
  key: string,
  label: string,
): boolean
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider descriptor that selects the credential file key.
- `key` (`string`): File key of the OAuth credential to name.
- `label` (`string`): New account name. An empty or whitespace-only value clears it.

**Returns** `boolean`

Source: [src/chat/credentials.ts:706](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L706)

### `setPreferredCredential`

Function.

Make one of the provider's stored credentials the preferred one.

Swaps the target into the canonical slot and parks the previous active one
(see `parkDemoted`). Returns which of the three outcomes happened so a
caller cannot report a swap that provably did not occur — the write is
silent either way, and a `void` return would look like success to a caller
that trusted it instead of refetching.

```ts
function setPreferredCredential(
  dataDir: string,
  descriptor: ProviderDescriptor,
  target: PreferredCredentialTarget,
): PreferCredentialResult
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider descriptor that selects the credential file key.
- `target` (`PreferredCredentialTarget`): A credential kind or a stored credential's file key.

**Returns** `PreferCredentialResult`

Source: [src/chat/credentials.ts:634](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L634)

### `setProviderCredential`

Function.

Store a credential as the provider's preferred one.

The previous active credential is demoted, not destroyed: otherwise
authenticating with OAuth would silently delete a working API key (and vice
versa), and a second OAuth login would overwrite the first account. An API key moves to
[`alternateCredKey`](https://zidane.si/docs/reference/chat.md#alternateCredKey); an OAuth grant moves to the next free account slot.
Replacing an API key with an API key is a plain overwrite — there is only
ever one — and so is an OAuth login that names its predecessor via
`replaceKey`.

```ts
function setProviderCredential(
  dataDir: string,
  descriptor: ProviderDescriptor,
  cred: ProviderCredential,
  opts?: SetProviderCredentialOptions,
): void
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `descriptor` (`ProviderDescriptor`): The provider descriptor that selects the credential file key.
- `cred` (`ProviderCredential`): The credential to store as the preferred one.
- `opts?` (`SetProviderCredentialOptions`, default `{}`): Options. `replaceKey` names the stored credential this one replaces.

Source: [src/chat/credentials.ts:570](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L570)

### `settingsFromStored`

Function.

The explicitly-set settings of a stored slice, migrated, without the
storage marker: what readers layer over [`DEFAULT_SETTINGS`](https://zidane.si/docs/reference/chat-pure.md#DEFAULT_SETTINGS).

```ts
function settingsFromStored(
  stored: Readonly<Record<string, unknown>>,
): Partial<Settings>
```

**Parameters**

- `stored` (`Readonly<Record<string, unknown>>`): The `settings` slice as read from disk.

**Returns** `Partial<Settings>`: The user's settings.

Source: [src/chat/settings-defaults.ts:390](https://github.com/Tahul/zidane/blob/main/src/chat/settings-defaults.ts#L390)

### `SettingsProvider`

Function.

Holds the settings state, applies [`enforceSettingsInvariants`](https://zidane.si/docs/reference/chat-pure.md#enforceSettingsInvariants) on every write and reports each change to `onChange`.

```ts
function SettingsProvider(
  props: { children: ReactNode; initial: Settings; onChange?: (settings: Settings) => void },
): Element
```

**Parameters**

- `props` (`{ children: ReactNode; initial: Settings; onChange?: (settings: Settings) => void }`): `initial` settings, an optional `onChange` callback and the `children` to render.

**Returns** `Element`

Source: [src/chat/settings-context.tsx:49](https://github.com/Tahul/zidane/blob/main/src/chat/settings-context.tsx#L49)

### `settingsToWire`

Function.

Settings as sent to a host or to a window. Peers older than `approvalMode`
know tool approvals only as the legacy `safeMode` boolean, so it rides
along; a current peer drops it (`settingsFromWire`).

```ts
function settingsToWire<T extends object>(values: T): T & { safeMode?: boolean }
```

**Type parameters**

- `T` (`extends object`)

**Parameters**

- `values` (`T`): Settings, whole or partial; `safeMode` is added only when they carry `approvalMode`.

**Returns** `T & { safeMode?: boolean }`

Source: [src/chat/host-scoped-settings.ts:81](https://github.com/Tahul/zidane/blob/main/src/chat/host-scoped-settings.ts#L81)

### `shortChord`

Function.

Shorten a binding spec for display as a "chord continuation" — the
`/l` after `ctrl+m model` in the chat footer. We only strip the
`ctrl+` prefix so user-customized chords (`alt+n`, `meta+shift+n`)
still render in full. Falls back to the verbatim spec when no
`ctrl+` prefix is found, which keeps the visual contract honest:
the rendered key always matches the bound trigger.

```ts
function shortChord(spec: string): string
```

**Parameters**

- `spec` (`string`): Binding spec, such as `ctrl+n`.

**Returns** `string`

Source: [src/chat/footer-hints.ts:337](https://github.com/Tahul/zidane/blob/main/src/chat/footer-hints.ts#L337)

### `shouldAutoGenerateTitle`

Function.

Decide whether a session is ready for one-shot automatic titling.

Returns `true` iff the conversation has had its first exchange — at
least two turns, i.e. the opening user message plus the assistant's
reply (this is exactly the "N turns" count the sessions list shows) —
AND no explicit title has been set yet. An existing `metadata.title`
(a previous auto-generation, or a manual title from the session-details
"generate title" action) is never clobbered.

Pure + total. The runtime layers its own "is one already in flight?"
and "is a provider picked?" guards on top of this; the predicate only
encodes the content-level rule so it can be pinned in tests without a
mock provider — same rationale as the exported [`cleanTitle`](https://zidane.si/docs/reference/chat.md#cleanTitle).

```ts
function shouldAutoGenerateTitle(
  turns: readonly SessionTurn[],
  metadata?: Record<string, unknown>,
): boolean
```

**Parameters**

- `turns` (`readonly SessionTurn[]`): Conversation turns so far.
- `metadata?` (`Record<string, unknown>`): Session metadata. A non-blank `title` string there blocks auto-titling.

**Returns** `boolean`

Source: [src/chat/generate-title.ts:220](https://github.com/Tahul/zidane/blob/main/src/chat/generate-title.ts#L220)

### `signsInWithIntegration`

Function.

Whether the provider's sign-in is an integration's connect flow. Then
[`runIntegrationSignIn`](https://zidane.si/docs/reference/chat.md#runIntegrationSignIn) runs it and stores the connection itself:
nothing is persisted under the provider's credential key.

```ts
function signsInWithIntegration(descriptor: ProviderDescriptor): boolean
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider descriptor.

**Returns** `boolean`

Source: [src/chat/oauth.ts:48](https://github.com/Tahul/zidane/blob/main/src/chat/oauth.ts#L48)

### `singleAgentRegistry`

Function.

Wrap a single `Preset` into a single-profile registry. Used by
`resolveConfig` when the host passed `preset` without `agents`. The
registry has one entry, so the picker has only one option.

```ts
function singleAgentRegistry(preset: Preset): AgentRegistry
```

**Parameters**

- `preset` (`Preset`): The preset to wrap. Its `name` becomes the profile label when set.

**Returns** `AgentRegistry`

Source: [src/chat/agents.ts:669](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L669)

### `sparseSettingsUpdate`

Function.

The slice a settings writer persists: the stored keys (migrated) plus the
keys it sets, never the defaults, so a later default change reaches every
key the user did not set.

```ts
function sparseSettingsUpdate(
  stored: Readonly<Record<string, unknown>> | undefined,
  patch: Readonly<Record<string, unknown>>,
  reset?: readonly string[],
): Record<string, unknown>
```

**Parameters**

- `stored` (`Readonly<Record<string, unknown>> | undefined`): The `settings` slice as read from disk (`undefined` when absent).
- `patch` (`Readonly<Record<string, unknown>>`): The keys to set: the user's change and any key an invariant forced.
- `reset?` (`readonly string[]`, default `[]`): Keys to remove from the slice so they follow their default again (the setup wizard returning a key to its default). A key in `patch` wins.

**Returns** `Record<string, unknown>`: The slice to write under `settings`.

Source: [src/chat/settings-defaults.ts:405](https://github.com/Tahul/zidane/blob/main/src/chat/settings-defaults.ts#L405)

### `splitHostSettingsPatch`

Function.

Split a settings patch into the window's part and the host's part.

```ts
function splitHostSettingsPatch<S extends Partial<Settings>>(
  patch: S,
): { host: Partial<S>; window: Partial<S> }
```

**Type parameters**

- `S` (`extends Partial<Settings>`)

**Parameters**

- `patch` (`S`): Settings patch to partition by owner.

**Returns** `{ host: Partial<S>; window: Partial<S> }`

Source: [src/chat/host-scoped-settings.ts:129](https://github.com/Tahul/zidane/blob/main/src/chat/host-scoped-settings.ts#L129)

### `startUpdateWorker`

Function.

Starts the worker detached and follows it until it reports. The worker
keeps going when this process exits. Never rejects.

A held lock answers `skipped / locked` without starting anything. A worker
that cannot start, or stops without a result, answers `lost`; for an
automatic install that also records the version as a transient failure (in
memory when the cache dir can't be written), so it is retried on the
backoff, not on every check.

```ts
function startUpdateWorker(
  options: StartUpdateWorkerOptions,
): Promise<UpdateWorkerOutcome>
```

**Parameters**

- `options` (`StartUpdateWorkerOptions`): Worker command, environment, request and log callback.

**Returns** `Promise<UpdateWorkerOutcome>`

Source: [src/chat/auto-update-worker.ts:295](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L295)

### `stepTranscriptCap`

Function.

[`capTranscriptEvents`](https://zidane.si/docs/reference/chat-pure.md#capTranscriptEvents), at most `step` events (extended to the next
turn boundary) at a time. Applied again and again it trims until the
transcript is back within `max`, then returns `events` itself; it stops
there rather than at the cap's hysteresis floor, so the end keeps at least
what one full cap keeps. Each cut keeps the cap's seams (no split turn,
never starting on a turn-less row), so a reveal anchors on it like on a
full cap.

```ts
function stepTranscriptCap(
  events: StreamEvent[],
  max?: number,
  step?: number,
  weigh?: TranscriptWeight,
): StreamEvent[]
```

**Parameters**

- `events` (`StreamEvent[]`): The transcript, possibly already headed by a hidden-rows marker.
- `max?` (`number`, default `MAX_TRANSCRIPT_EVENTS`): Cap to trim down to.
- `step?` (`number`, default `TRANSCRIPT_TRIM_STEP_EVENTS`): Most events (or weight, with `weigh`) to drop in this call.
- `weigh?` (`TranscriptWeight`): How many rows an event counts for; each counts 1 when omitted.

**Returns** `StreamEvent[]`

Source: [src/chat/streaming-pure.ts:705](https://github.com/Tahul/zidane/blob/main/src/chat/streaming-pure.ts#L705)

### `storedCredentialById`

Function.

One stored credential of a provider by its stable id (see
`CredentialIdentity`), wherever it sits now; `undefined` when it is
no longer stored.

```ts
function storedCredentialById(
  all: CredentialsFile,
  fileKey: string,
  id: string,
): StoredCredential | undefined
```

**Parameters**

- `all` (`CredentialsFile`): The parsed credentials file.
- `fileKey` (`string`): The provider's canonical credential file key.
- `id` (`string`): Stable credential id.

**Returns** `StoredCredential | undefined`

Source: [src/chat/credentials.ts:294](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L294)

### `storedCredentialsOf`

Function.

Pure form of [`readProviderCredentials`](https://zidane.si/docs/reference/chat.md#readProviderCredentials) over an already-read file.

```ts
function storedCredentialsOf(all: CredentialsFile, fileKey: string): StoredCredential[]
```

**Parameters**

- `all` (`CredentialsFile`): The parsed credentials file.
- `fileKey` (`string`): The provider's canonical credential file key.

**Returns** `StoredCredential[]`

Source: [src/chat/credentials.ts:305](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L305)

### `storeHasDurability`

Function.

Whether a host on `store` can apply the setting: only a SQLite store has a
durable runtime (`sqliteRuntimeForStore`). The settings rows show
Crash recovery as Unavailable when it is `false`.

```ts
function storeHasDurability(store: SessionStore): boolean
```

**Parameters**

- `store` (`SessionStore`): The host's session store.

**Returns** `boolean`

Source: [src/chat/durability.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/durability.ts#L35)

### `stripEditOutcomesAnnotation`

Function.

Strip the first `<edit-outcomes>…</edit-outcomes>` block out of a tool
result body, returning the surrounding text. Used by the
`tool:transform` hook to peel a body-emitted annotation before
re-appending the merged (approval ∪ body) version — otherwise the
result would carry two annotation blocks and
[`parseEditOutcomesFromResult`](https://zidane.si/docs/reference/chat.md#parseEditOutcomesFromResult) would only see the first.

Anchored on the same `\n<edit-outcomes>\n` / start-of-string newline
shape the parser uses, so prose that incidentally mentions
`<edit-outcomes>` (e.g. a model summarizing its own format) isn't
mistakenly stripped. Trims a single leading `\n\n` separator when
present so successive strips don't leave dangling blank lines.
Idempotent on inputs that don't contain a properly-anchored block.

```ts
function stripEditOutcomesAnnotation(text: string): string
```

**Parameters**

- `text` (`string`): Tool result text that may carry an annotation block.

**Returns** `string`

Source: [src/chat/edit-approval.ts:242](https://github.com/Tahul/zidane/blob/main/src/chat/edit-approval.ts#L242)

### `stripJsonComments`

Function.

Strip `//` line comments and `/* … */` block comments from a JSONC
source string. Stays out of string literals so a `"foo // bar"` value
keeps its slashes. Exported for unit-tests; the only runtime caller
is [`readKeybindingsRaw`](https://zidane.si/docs/reference/chat.md#readKeybindingsRaw).

Trade-off: this is a tiny state-machine, not a full JSONC parser. It
doesn't understand `\\` outside strings (irrelevant for JSON) and
doesn't track line numbers in error messages. Good enough for a
config file — corruption in production gracefully falls back to the
defaults via the try/catch in `readKeybindingsRaw`.

```ts
function stripJsonComments(input: string): string
```

**Parameters**

- `input` (`string`): JSONC source text.

**Returns** `string`

Source: [src/chat/keybindings.ts:952](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L952)

### `stripSpawnTokensLine`

Function.

Strip the `Tokens: …` line from a spawn tool-result. The spawn-end marker
displayed right above already shows the same stats; keeping the line in the
rendered tool-result body just produces a visible duplicate (and, on
reloaded older sessions, an *inconsistent* duplicate — the persisted line
can use the `13 in / 4075 out` shape while the freshly synthesized
spawn-end uses the cache-aware `in 92615 (cache 92602) / 4075 out` shape).

Display-only: the persisted tool_result content is untouched, so the LLM
still sees the full string in its context window. Anchored to start-of-line
and matches both `Tokens: 13 in / 4075 out` (older) and `Tokens: in 13 …`
(`formatTokenUsage`) shapes.

```ts
function stripSpawnTokensLine(text: string): string
```

**Parameters**

- `text` (`string`): A spawn tool-result body.

**Returns** `string`

Source: [src/chat/store.ts:1393](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1393)

### `subscribeMcpToolsCache`

Function.

Subscribe to `mcp:tools:list` and persist every successful bootstrap
into the on-disk cache. Returns an unsubscribe function — call it
during agent teardown so a long-lived host (Settings modal that
outlives one agent) doesn't accumulate stale subscribers.

Each write loads the current cache, upserts the named entry, then
atomic-writes the full file. The read-modify-write is safe under
Node single-threaded event loop semantics — concurrent bootstraps in
the same process serialize through the hook bus.

Optional `onChange` callback fires synchronously after every
successful disk write with the entry that was just upserted. Lets a
React host trigger a state refresh without polling the disk.

Write failures are swallowed (logged under `ZIDANE_DEBUG`): the cache
is an accelerator, not a correctness boundary. If the disk is full
the agent run still works — the settings UI just shows stale data
until the next successful write.

```ts
function subscribeMcpToolsCache(
  hooks: Hookable<AgentHooks>,
  opts: { dataDir: string; onChange?: (entry: { name: string } & CachedMcpToolList) => void },
): () => void
```

**Parameters**

- `hooks` (`Hookable<AgentHooks>`): Agent hooks to listen on for `mcp:tools:list`.
- `opts` (`{ dataDir: string; onChange?: (entry: { name: string } & CachedMcpToolList) => void }`): The data directory to write to and an optional callback run after each write.

**Returns** `() => void`

Source: [src/chat/mcp-tools-cache.ts:179](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tools-cache.ts#L179)

### `suggestSafelistEntries`

Function.

Suggest the safelist entries to persist when the user picks "accept and
remember" (or "accept for session") for a `{ tool, input }`. Heuristic:

- `shell` → one `shell:<head>:*` per DISTINCT command head, parsing the
  command the SAME way [`isOnSafelist`](https://zidane.si/docs/reference/chat.md#isOnSafelist) matches it (`&&`, `||`, `;`,
  `|`, `$(…)`, subshells, backticks). So `cd dir && cat file` yields
  `["shell:cd:*", "shell:cat:*"]` — persisting the whole set is what
  lets the next identical call skip the gate. A single first-token
  entry would only cover `cd`, so `isOnSafelist`'s `every-head` check
  would fail on `cat` and re-prompt forever.
- `shell` with an unparseable / empty command → first-token heuristic,
  else the bare tool name.
- `browser_open` / `browser_navigate` to a non-loopback address →
  `<tool>:<host>`: approving one site does not approve the web.
- anything else → bare tool name (broad).

Always returns a non-empty list so the UI has a concrete set to render
and persist.

```ts
function suggestSafelistEntries(tool: string, input: Record<string, unknown>): string[]
```

**Parameters**

- `tool` (`string`): Tool name.
- `input` (`Record<string, unknown>`): The tool call's input.

**Returns** `string[]`

Source: [src/chat/safe-mode.ts:444](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L444)

### `summarizeArtifactCounts`

Function.

One count per kind, a badge each: `['3 pull requests', '2 files']`; empty
for none.

```ts
function summarizeArtifactCounts(
  artifacts: readonly SessionArtifact[],
  registry?: KindRegistry,
): string[]
```

**Parameters**

- `artifacts` (`readonly SessionArtifact[]`): Session artifacts to count.
- `registry?` (`KindRegistry`): Extension registry that supplies kind labels.

**Returns** `string[]`

Source: [src/chat/artifacts-view.ts:68](https://github.com/Tahul/zidane/blob/main/src/chat/artifacts-view.ts#L68)

### `summarizeUsage`

Function.

The summary of `range`, cut from a database's day buckets.

```ts
function summarizeUsage(
  allBuckets: UsageBuckets,
  range: UsageRange,
  options?: { now?: Date; projectRoot?: string; userName?: string | null },
): UsageSummary
```

**Parameters**

- `allBuckets` (`UsageBuckets`): The per-day aggregates (`runUsageAggregates`), every project's.
- `range` (`UsageRange`): The window.
- `options?` (`{ now?: Date; projectRoot?: string; userName?: string | null }`, default `{}`): Options.

**Returns** `UsageSummary`

Source: [src/chat/usage-summary.ts:221](https://github.com/Tahul/zidane/blob/main/src/chat/usage-summary.ts#L221)

### `sumRunCosts`

Function.

Sum provider-reported USD cost across every run in a session. Used to
seed the live cost indicator on session activation. Returns 0 when no
run carries a `cost` (most providers — only OpenRouter reports today).

```ts
function sumRunCosts(runs: readonly SessionRun[]): number
```

**Parameters**

- `runs` (`readonly SessionRun[]`): The session's runs.

**Returns** `number`

Source: [src/chat/store.ts:1617](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1617)

### `supersededTools`

Function.

Tool ids to skip because a runnable language server covers them.

```ts
function supersededTools(
  readyServerIds: readonly string[],
  tools?: readonly DiagnosticsTool[],
): string[]
```

**Parameters**

- `readyServerIds` (`readonly string[]`): The language servers ready for the folder.
- `tools?` (`readonly DiagnosticsTool[]`, default `BUILTIN_TOOLS`): The tools to check.

**Returns** `string[]`

Source: [src/chat/workspace/diagnostics/tools.ts:416](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/diagnostics/tools.ts#L416)

### `supportsOAuth`

Function.

Whether the provider has a browser sign-in: its own OAuth flow, or the
integration it signs in with (`ProviderDescriptor.integration`).

```ts
function supportsOAuth(descriptor: ProviderDescriptor): boolean
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider descriptor.

**Returns** `boolean`

Source: [src/chat/oauth.ts:37](https://github.com/Tahul/zidane/blob/main/src/chat/oauth.ts#L37)

### `testDecisionModel`

Function.

Send one tiny decision to check a decision model works with this host's
credentials. `decisionModel` (`"providerKey/modelId"`) is tested exactly,
with no fallback, so a broken pick is reported as broken; empty tests the
model agents would get. Never throws.

```ts
function testDecisionModel(
  input: { buildProvider: (key: string) => Provider; decisionModel?: string; providers: Readonly<Record<string, ProviderDescriptor>>; timeoutMs?: number },
): Promise<DecisionTestResult>
```

**Parameters**

- `input` (`{ buildProvider: (key: string) => Provider; decisionModel?: string; providers: Readonly<Record<string, ProviderDescriptor>>; timeoutMs?: number }`): Provider descriptors, a provider builder, an optional `decisionModel` to test exactly and an optional `timeoutMs` (default 15 000).

**Returns** `Promise<DecisionTestResult>`

Source: [src/chat/decision-models.ts:189](https://github.com/Tahul/zidane/blob/main/src/chat/decision-models.ts#L189)

### `testModel`

Function.

Send one tiny prompt to check a model answers with this host's
credentials. `model` (`"providerKey[@account]/modelId"`) is tested
exactly, on its account, with no fallback, so a broken pick is reported as
broken. Never throws.

```ts
function testModel(
  input: { buildProvider: (providerKey: string, account?: string) => Provider; model: string; providers: Readonly<Record<string, ProviderDescriptor>>; timeoutMs?: number },
): Promise<ModelTestResult>
```

**Parameters**

- `input` (`{ buildProvider: (providerKey: string, account?: string) => Provider; model: string; providers: Readonly<Record<string, ProviderDescriptor>>; timeoutMs?: number }`): What to test, and with what.

**Returns** `Promise<ModelTestResult>`

Source: [src/chat/model-test.ts:41](https://github.com/Tahul/zidane/blob/main/src/chat/model-test.ts#L41)

### `ThemeProvider`

Function.

Provides `theme` to the subtree.

```ts
function ThemeProvider(props: { children: ReactNode; theme: Theme }): Element
```

**Parameters**

- `props` (`{ children: ReactNode; theme: Theme }`): The `theme` to provide and the `children` to render.

**Returns** `Element`

Source: [src/chat/theme-context.tsx:25](https://github.com/Tahul/zidane/blob/main/src/chat/theme-context.tsx#L25)

### `titleFromTurns`

Function.

Derive a short title from the first user message — returns null when empty.

```ts
function titleFromTurns(turns: SessionTurn[]): string | null
```

**Parameters**

- `turns` (`SessionTurn[]`): Persisted turns in order.

**Returns** `string | null`

Source: [src/chat/store.ts:444](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L444)

### `tokenEndingAtCursor`

Function.

Extract the whitespace-delimited token that ends at `cursor`. Returns
null when the cursor sits right after whitespace (nothing to complete).

```ts
function tokenEndingAtCursor(text: string, cursor: number): string | null
```

**Parameters**

- `text` (`string`): Prompt buffer.
- `cursor` (`number`): Cursor offset into `text`.

**Returns** `string | null`

Source: [src/chat/inline-autocomplete.ts:138](https://github.com/Tahul/zidane/blob/main/src/chat/inline-autocomplete.ts#L138)

### `toolCallPreview`

Function.

Formats a tool call as `name(json args)`, or just the name when the input is empty.

```ts
function toolCallPreview(name: string, input: Record<string, unknown>): string
```

**Parameters**

- `name` (`string`): Tool name.
- `input` (`Record<string, unknown>`): The tool call's input.

**Returns** `string`

Source: [src/chat/store.ts:1304](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1304)

### `toolResultText`

Function.

Render tool output as plain text, whether it's a string or structured content.

```ts
function toolResultText(output: string | ToolResultContent[]): string
```

**Parameters**

- `output` (`string | ToolResultContent[]`): A tool result, as a string or structured content.

**Returns** `string`

Source: [src/chat/store.ts:1374](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1374)

### `truncateTrailing`

Function.

Truncate `text` to at most `max` characters, replacing the trailing
overflow with `…`. Edge cases:

- `max <= 0` → empty string (no room to render at all).
- `max === 1` → just the ellipsis glyph.
- `text.length <= max` → unchanged.

Trailing-style truncation matches the natural read order of titles:
the prefix carries enough signal to identify the surface.

```ts
function truncateTrailing(text: string, max: number): string
```

**Parameters**

- `text` (`string`): Text to shorten.
- `max` (`number`): Maximum length in characters, ellipsis included.

**Returns** `string`

Source: [src/chat/hints.ts:70](https://github.com/Tahul/zidane/blob/main/src/chat/hints.ts#L70)

### `tryOpenBrowser`

Function.

Opens a URL in the default browser, without waiting for it.
Uses `open` on macOS, `start` on Windows and `xdg-open` elsewhere. Failures to launch are ignored.

```ts
function tryOpenBrowser(url: string): void
```

**Parameters**

- `url` (`string`): The URL to open.

Source: [src/chat/browser.ts:27](https://github.com/Tahul/zidane/blob/main/src/chat/browser.ts#L27)

### `unwrapCallTool`

Function.

Unwrap a `call_tool` envelope. `null` when `name` is not the dispatcher or
the envelope is malformed (missing / non-string `name`, non-object
`arguments`) — callers then treat the call as a plain `call_tool` call and
let the dispatcher's own schema validation produce the error the model
needs to fix its input.

`arguments` may be omitted for zero-argument tools: it defaults to `{}`.

```ts
function unwrapCallTool(name: string, input: unknown): UnwrappedToolCall | null
```

**Parameters**

- `name` (`string`): Name of the tool call.
- `input` (`unknown`): Input of the tool call.

**Returns** `UnwrappedToolCall | null`

Source: [src/tools/call-tool.ts:47](https://github.com/Tahul/zidane/blob/main/src/tools/call-tool.ts#L47)

### `updateAppliedMarkerPath`

Function.

Returns the path of the update-applied marker file.

```ts
function updateAppliedMarkerPath(cacheDir: string): string
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the marker file.

**Returns** `string`

Source: [src/chat/auto-update-restart.ts:49](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L49)

### `updateChecksOptedOut`

Function.

Whether an env var (`ZIDANE_NO_UPDATE`, `NO_UPDATE_NOTIFIER`, `CI`) turns
background update work off. Background checks and automatic installs both
honor it; `zidane upgrade` does not.

```ts
function updateChecksOptedOut(env?: ProcessEnv): boolean
```

**Parameters**

- `env?` (`ProcessEnv`, default `process.env`): Environment to read. Defaults to `process.env`.

**Returns** `boolean`

Source: [src/chat/auto-update.ts:177](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L177)

### `updateLockPath`

Function.

Returns the path of the install lock file.

```ts
function updateLockPath(cacheDir: string): string
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the lock.

**Returns** `string`

Source: [src/chat/auto-update-lock.ts:63](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-lock.ts#L63)

### `updateToolEventOutcomes`

Function.

Update the trailing `'tool'` event matching `callId` so its
`edit.outcomes` reflects the canonical merged outcomes the chat
layer's `tool:transform` hook just computed. Without this, the live
diff badges stay frozen at whatever the gate set (approval-side
outcomes) and never reflect body-side failures from `multi_edit`'s
best-effort run — only a session reload re-attaches the merged
outcomes via [`eventsFromTurns`](https://zidane.si/docs/reference/chat.md#eventsFromTurns).

Walks back-to-front because the matching event is overwhelmingly the
most recent (we're updating right after the body returned). Returns
the same reference when no match is found (or the event already
carries the target outcomes), so React skips the re-render.

```ts
function updateToolEventOutcomes(
  events: readonly StreamEvent[],
  callId: string,
  outcomes: readonly EditOutcome[],
): StreamEvent[]
```

**Parameters**

- `events` (`readonly StreamEvent[]`): The current events.
- `callId` (`string`): Call id of the tool event to update.
- `outcomes` (`readonly EditOutcome[]`): The merged edit outcomes.

**Returns** `StreamEvent[]`

Source: [src/chat/store.ts:1351](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1351)

### `upgradeError`

Function.

Why an upgrade run did not land, one package per line; `null` when it did.
A run that found no package on PATH failed too: nothing was installed.

```ts
function upgradeError(report: UpgradeReport): string | null
```

**Parameters**

- `report` (`UpgradeReport`): Result of [`upgradeInstalledPackages`](https://zidane.si/docs/reference/chat.md#upgradeInstalledPackages).

**Returns** `string | null`

Source: [src/chat/auto-update-auto-install.ts:297](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L297)

### `upgradeInstalledPackages`

Function.

Upgrades each installed target package through its own package manager.
Skips targets whose bin is not on PATH, checks the registry for each one, reports `unavailable` instead of installing when the tarball is not downloadable yet, runs the install, and verifies the new version on disk. Writes the applied marker when something was installed and `cacheDir` is set.

```ts
function upgradeInstalledPackages(
  options: UpgradeInstalledPackagesOptions,
): Promise<UpgradeReport>
```

**Parameters**

- `options` (`UpgradeInstalledPackagesOptions`): Target packages, registry settings and progress callback.

**Returns** `Promise<UpgradeReport>`

**Example**

```ts
import { upgradeInstalledPackages } from 'zidane/chat'

const report = await upgradeInstalledPackages({
  targets: [{ packageName: 'zidane-tui', binName: 'zidane' }],
  onProgress: event => console.log(event.kind),
})
if (!report.ok)
  console.error(report.results.filter(r => r.status === 'failed'))
```

Source: [src/chat/auto-update-service.ts:129](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-service.ts#L129)

### `upsertMcpServer`

Function.

Upsert `entry` (matched by `name`) into the mcps config at `filePath`.
Preserves the existing file's shape and every field the writer doesn't
own; creates a canonical flat-array file when none exists. Throws on
malformed JSON — silently rewriting a broken file the user is mid-edit
on would destroy their work.

```ts
function upsertMcpServer(filePath: string, entry: McpServerConfig): UpsertMcpResult
```

**Parameters**

- `filePath` (`string`): Path of the `mcps.json` file.
- `entry` (`McpServerConfig`): The server config to add or replace.

**Returns** `UpsertMcpResult`

Source: [src/chat/mcps-write.ts:81](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-write.ts#L81)

### `usageByModel`

Function.

Aggregate per-model token + spend totals across every run in a session.
Parent and subagent runs alike: each turn carries its own `modelId`, so
cost-tiered delegation (cheap spawn workers) shows up as separate rows
priced at their own rates. Sorted by cost (desc), then total tokens, so
the biggest spender reads first.

```ts
function usageByModel(runs: readonly SessionRun[]): ModelUsageSummary[]
```

**Parameters**

- `runs` (`readonly SessionRun[]`): The session's runs.

**Returns** `ModelUsageSummary[]`

Source: [src/chat/store.ts:1697](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1697)

### `usageChecksOptedOut`

Function.

Whether the environment forbids usage probes outright.

```ts
function usageChecksOptedOut(env?: ProcessEnv): boolean
```

**Parameters**

- `env?` (`ProcessEnv`, default `process.env`): Environment to read. Opt-out variables are `ZIDANE_NO_USAGE_CHECK` and `CI`.

**Returns** `boolean`

Source: [src/chat/provider-usage.ts:172](https://github.com/Tahul/zidane/blob/main/src/chat/provider-usage.ts#L172)

### `usageSourceFor`

Function.

The probe for a descriptor, or `null` when it has no usage surface.

```ts
function usageSourceFor(descriptor: ProviderDescriptor): UsageSource | null
```

**Parameters**

- `descriptor` (`ProviderDescriptor`): The provider to look up.

**Returns** `UsageSource | null`

Source: [src/chat/provider-usage.ts:97](https://github.com/Tahul/zidane/blob/main/src/chat/provider-usage.ts#L97)

### `useAutoInstallUpdate`

Function.

Installs the release [`useUpdateCheck`](https://zidane.si/docs/reference/chat.md#useUpdateCheck) found, in the background, when
the user turned `autoInstallUpdates` on. One attempt per check result; the
failure record decides whether a version that failed is tried again. The
check itself runs after first paint, so this never competes with it.
Never restarts anything: the applied marker wakes `useRestartRequired`.
Returns the last outcome, `null` before the first attempt.

```ts
function useAutoInstallUpdate(
  options: UseAutoInstallUpdateOptions,
): AutoInstallOutcome | null
```

**Parameters**

- `options` (`UseAutoInstallUpdateOptions`): Check result, switches, cache directory and targets.

**Returns** `AutoInstallOutcome | null`

Source: [src/chat/auto-update-hook.ts:167](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-hook.ts#L167)

### `useColors`

Function.

Color palette only — equivalent to `useTheme().colors`.

```ts
function useColors(): ThemeColors
```

**Returns** `ThemeColors`

Source: [src/chat/theme-context.tsx:35](https://github.com/Tahul/zidane/blob/main/src/chat/theme-context.tsx#L35)

### `useCompletion`

Function.

Drive a prompt-completion popover from React state.

Inputs are pull-style — the caller owns `text` + `cursor` (typically
mirroring an `OpenTUI TextareaRenderable` or a `<textarea>` element) and
passes them in each render. Providers are matched by their `trigger`
character; every provider that shares the trigger at the cursor is bound
and their suggestions merge into one list.

Asynchronous suggest calls are aborted when the query changes or the
popover closes, so providers that hit a backend don't leak.

```ts
function useCompletion<TItem = unknown>(
  input: { cursor: number; text: string },
  providers: readonly CompletionProvider<TItem>[],
  options?: { maxQueryLength?: number },
): CompletionState<TItem>
```

**Type parameters**

- `TItem` (default `unknown`)

**Parameters**

- `input` (`{ cursor: number; text: string }`): Current buffer text and cursor offset.
- `providers` (`readonly CompletionProvider<TItem>[]`): Providers that offer suggestions.
- `options?` (`{ maxQueryLength?: number }`, default `{}`): Options. `maxQueryLength` bounds the trigger query scan.

**Returns** `CompletionState<TItem>`

Source: [src/chat/completion.ts:87](https://github.com/Tahul/zidane/blob/main/src/chat/completion.ts#L87)

### `useConfig`

Function.

Reads the [`ResolvedConfig`](https://zidane.si/docs/reference/chat.md#ResolvedConfig) from the nearest [`ConfigProvider`](https://zidane.si/docs/reference/chat.md#ConfigProvider). Throws when used outside one.

```ts
function useConfig(): ResolvedConfig
```

**Returns** `ResolvedConfig`

Source: [src/chat/config-context.tsx:25](https://github.com/Tahul/zidane/blob/main/src/chat/config-context.tsx#L25)

### `useDiscovery`

Function.

Read live discovery state + actions. Throws if used outside a
`<DiscoveryProvider>` — discovery is a load-bearing dependency for
settings + completion popovers; bailing loud here surfaces wiring
mistakes at mount instead of producing empty catalogs at first
keystroke.

```ts
function useDiscovery(): DiscoveryContextValue
```

**Returns** `DiscoveryContextValue`

Source: [src/chat/discovery-context.tsx:113](https://github.com/Tahul/zidane/blob/main/src/chat/discovery-context.tsx#L113)

### `useDiscoveryOptional`

Function.

Non-throwing variant — returns `null` when no provider is mounted.
Used by composable modals (`<SettingsModal>`, …) that accept catalog
props as a fallback for embedders who don't wire the full TUI
shell. The in-app flow always mounts `<DiscoveryProvider>` so the
modal sees live state; standalone embeds get the prop snapshot.

```ts
function useDiscoveryOptional(): DiscoveryContextValue | null
```

**Returns** `DiscoveryContextValue | null`

Source: [src/chat/discovery-context.tsx:127](https://github.com/Tahul/zidane/blob/main/src/chat/discovery-context.tsx#L127)

### `useEnabledToggleSet`

Function.

Bind an "enabled allowlist" setting to a discovered catalog.

`keyOf` extracts the persisted identity from a catalog entry (skill name,
MCP server name, …). Pass a stable, collision-free key — the persisted
allowlist is keyed against it forever.

```ts
function useEnabledToggleSet<T>(
  opts: { catalog: readonly T[]; gcKeys?: readonly string[]; keyOf: (entry: T) => string; settingKey: EnabledAllowlistKey },
): EnabledToggleSet
```

**Type parameters**

- `T`

**Parameters**

- `opts` (`{ catalog: readonly T[]; gcKeys?: readonly string[]; keyOf: (entry: T) => string; settingKey: EnabledAllowlistKey }`): Catalog, key extractor, setting key, and optional extra keys to keep when pruning the allowlist.

**Returns** `EnabledToggleSet`

Source: [src/chat/enabled-toggle-set.ts:49](https://github.com/Tahul/zidane/blob/main/src/chat/enabled-toggle-set.ts#L49)

### `useInteractionsActions`

Function.

Read the stable actions object.

```ts
function useInteractionsActions(): InteractionsActions
```

**Returns** `InteractionsActions`

Source: [src/chat/interactions.tsx:110](https://github.com/Tahul/zidane/blob/main/src/chat/interactions.tsx#L110)

### `useInteractionsQueue`

Function.

Read the live queue (re-renders on every push/pop).

```ts
function useInteractionsQueue(): readonly PendingInteractionEntry[]
```

**Returns** `readonly PendingInteractionEntry[]`

Source: [src/chat/interactions.tsx:105](https://github.com/Tahul/zidane/blob/main/src/chat/interactions.tsx#L105)

### `useMcpAuthDispatch`

Function.

Returns the function that applies an [`McpAuthEvent`](https://zidane.si/docs/reference/chat.md#McpAuthEvent) to the auth state. Throws when used outside [`McpAuthProvider`](https://zidane.si/docs/reference/chat.md#McpAuthProvider).

```ts
function useMcpAuthDispatch(): (event: McpAuthEvent) => void
```

**Returns** `(event: McpAuthEvent) => void`

Source: [src/chat/mcp-auth-context.tsx:64](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-auth-context.tsx#L64)

### `useMcpAuthState`

Function.

Returns the current auth state map, keyed by server name.

```ts
function useMcpAuthState(): McpAuthStateMap
```

**Returns** `McpAuthStateMap`

Source: [src/chat/mcp-auth-context.tsx:59](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-auth-context.tsx#L59)

### `useMcpToolToggleMap`

Function.

Bind every server in `catalogByServer` to its own per-tool toggle in
a single hook call. Returns a `serverName → McpToolToggleSet` map.

Use this when a host needs handlers for multiple servers at once
(the TUI's MCP settings panel keyboard dispatcher is the canonical
example) — calling [`useMcpToolToggleSet`](https://zidane.si/docs/reference/chat.md#useMcpToolToggleSet) in a `for` / `.map`
over a dynamic catalog would change the hook-call order whenever a
server is added or removed, violating React's rules-of-hooks. This
hook calls `useMemo` exactly once regardless of server count.

Servers absent from `catalogByServer` get no entry in the result.
The persisted deny-list still keeps their entries on disk — they
just stay invisible to this consumer until the catalog re-includes
them.

```ts
function useMcpToolToggleMap(
  opts: { catalogByServer: Record<string, readonly McpToolSchema[]> },
): Record<string, McpToolToggleSet>
```

**Parameters**

- `opts` (`{ catalogByServer: Record<string, readonly McpToolSchema[]> }`): Tool catalogs keyed by server name.

**Returns** `Record<string, McpToolToggleSet>`

Source: [src/chat/mcp-tool-toggle.ts:103](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tool-toggle.ts#L103)

### `useMcpToolToggleSet`

Function.

Bind a single MCP server's per-tool toggle to
`Settings.disabledMcpTools[serverName]`.

`catalog` is the FULL list of tools the server advertises (i.e. the
cached `listTools()` payload from `chat/mcp-tools-cache.ts`). The
hook needs it both to render `enabledSet` (every catalog tool not in
the persisted deny-list) and to garbage-collect stale deny-list
entries on every write.

```ts
function useMcpToolToggleSet(
  opts: { catalog: readonly McpToolSchema[]; serverName: string },
): McpToolToggleSet
```

**Parameters**

- `opts` (`{ catalog: readonly McpToolSchema[]; serverName: string }`): The server name and its full tool catalog.

**Returns** `McpToolToggleSet`

Source: [src/chat/mcp-tool-toggle.ts:61](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tool-toggle.ts#L61)

### `useRestartRequired`

Function.

`null` until the first probe (or when disabled / no spec), then the live
state. Flipping `enabled` off tears the watcher down and clears the state
so no stale "restart" chip lingers.

```ts
function useRestartRequired(
  options: UseRestartRequiredOptions,
): InstalledVersionState | null
```

**Parameters**

- `options` (`UseRestartRequiredOptions`): Package spec, running version, cache directory, poll interval and the master `enabled` switch.

**Returns** `InstalledVersionState | null`

Source: [src/chat/auto-update-hook.ts:303](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-hook.ts#L303)

### `userPromptCount`

Function.

User turns carrying text — the prompts the user actually typed.

```ts
function userPromptCount(turns: readonly SessionTurn[]): number
```

**Parameters**

- `turns` (`readonly SessionTurn[]`): The session's turns.

**Returns** `number`

Source: [src/chat/auto-title.ts:122](https://github.com/Tahul/zidane/blob/main/src/chat/auto-title.ts#L122)

### `userScopeOverridesLoad`

Function.

Whether a USER-scope `SYSTEM.md` would be loaded under `scope`.

Mirrors the candidate filter in [`discoverSystemMd`](https://zidane.si/docs/reference/chat.md#discoverSystemMd) so a host can ask
"would writing `~/.{prefix}/SYSTEM.md` accomplish anything?" without
re-deriving the rule and drifting from it. `'project'` excludes the user
candidate and `'none'` skips discovery altogether, so under either setting
creating that file is a silent no-op.

```ts
function userScopeOverridesLoad(scope: AgentsMdScope | undefined): boolean
```

**Parameters**

- `scope` (`AgentsMdScope | undefined`): The active `userInstructionsScope`. Treated as `both` when undefined.

**Returns** `boolean`

Source: [src/chat/system-md.ts:114](https://github.com/Tahul/zidane/blob/main/src/chat/system-md.ts#L114)

### `userSystemMdPath`

Function.

Absolute path of the USER-scope `SYSTEM.md` — the file the TUI creates when
the user asks for an override and none exists yet.

Exported so the host doesn't hardcode the layout and can't drift from what
discovery actually reads.

```ts
function userSystemMdPath(opts?: { home?: string; prefix?: string }): string
```

**Parameters**

- `opts?` (`{ home?: string; prefix?: string }`, default `{}`): Overrides for the home directory and dot-directory prefix.

**Returns** `string`

Source: [src/chat/system-md.ts:97](https://github.com/Tahul/zidane/blob/main/src/chat/system-md.ts#L97)

### `useSafeModeActions`

Function.

Returns the approval actions. Throws when used outside [`SafeModeProvider`](https://zidane.si/docs/reference/chat.md#SafeModeProvider).

```ts
function useSafeModeActions(): SafeModeActions
```

**Returns** `SafeModeActions`

Source: [src/chat/safe-mode-context.tsx:202](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode-context.tsx#L202)

### `useSafeModeQueue`

Function.

Returns the pending approval requests, oldest first.

```ts
function useSafeModeQueue(): readonly ApprovalRequest[]
```

**Returns** `readonly ApprovalRequest[]`

Source: [src/chat/safe-mode-context.tsx:197](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode-context.tsx#L197)

### `useSelectStyle`

Function.

Select-row styling — equivalent to `useTheme().select`: the `▶` marker and accent text carry the cursor.

```ts
function useSelectStyle(): ThemeSelect
```

**Returns** `ThemeSelect`

Source: [src/chat/theme-context.tsx:40](https://github.com/Tahul/zidane/blob/main/src/chat/theme-context.tsx#L40)

### `useSettings`

Function.

Returns the settings context value. Throws when used outside [`SettingsProvider`](https://zidane.si/docs/reference/chat.md#SettingsProvider).

```ts
function useSettings(): SettingsContextValue
```

**Returns** `SettingsContextValue`

Source: [src/chat/settings-context.tsx:113](https://github.com/Tahul/zidane/blob/main/src/chat/settings-context.tsx#L113)

### `useStreamBuffer`

Function.

Buffers streaming deltas and commits them to the transcript on a ticker, in smooth or batched mode.

```ts
function useStreamBuffer(
  setEvents: Dispatch<SetStateAction<StreamEvent[]>>,
  options?: StreamBufferOptions,
): StreamBuffer
```

**Parameters**

- `setEvents` (`Dispatch<SetStateAction<StreamEvent[]>>`): State setter for the transcript events.
- `options?` (`StreamBufferOptions`): Cadence and transcript cap settings.

**Returns** `StreamBuffer`

Source: [src/chat/streaming.ts:286](https://github.com/Tahul/zidane/blob/main/src/chat/streaming.ts#L286)

### `useSurfaces`

Function.

Panel / surface backgrounds — equivalent to `useTheme().surfaces`.

```ts
function useSurfaces(): ThemeSurfaces
```

**Returns** `ThemeSurfaces`

Source: [src/chat/theme-context.tsx:45](https://github.com/Tahul/zidane/blob/main/src/chat/theme-context.tsx#L45)

### `useSyntaxStyles`

Function.

Raw syntax style table — `useTheme().syntax`. Renderer converts to its native style type.

```ts
function useSyntaxStyles(): SyntaxStyles
```

**Returns** `SyntaxStyles`

Source: [src/chat/theme-context.tsx:50](https://github.com/Tahul/zidane/blob/main/src/chat/theme-context.tsx#L50)

### `useTheme`

Function.

Returns the current theme. Falls back to [`DEFAULT_THEME`](https://zidane.si/docs/reference/chat.md#DEFAULT_THEME) outside a [`ThemeProvider`](https://zidane.si/docs/reference/chat.md#ThemeProvider).

```ts
function useTheme(): Theme
```

**Returns** `Theme`

Source: [src/chat/theme-context.tsx:30](https://github.com/Tahul/zidane/blob/main/src/chat/theme-context.tsx#L30)

### `useUpdateCheck`

Function.

Returns the current update status. The check fires:

- Once per mount (every TUI launch).
- Every `recheckMs` after that, when set.
- Every time `enabled` flips from `false` → `true` (user re-enabling
  the setting expects an immediate refresh).

The check is async and deferred by `delayMs` so it never competes with
first paint. Result is `null` until the first check resolves; flipping
`enabled` to `false` resets to `null` so any chip rendered against
`hasUpdate` disappears immediately.

The hook owns its own AbortController so an unmount cancels any in-flight
registry request cleanly.

```ts
function useUpdateCheck(options: UseUpdateCheckOptions): UpdateStatus | null
```

**Parameters**

- `options` (`UseUpdateCheckOptions`): Package, current version, enable flag and cache settings for the check.

**Returns** `UpdateStatus | null`

Source: [src/chat/auto-update-hook.ts:88](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-hook.ts#L88)

### `verifyEnvironment`

Function.

"Test connection" for `kind`; a throwing or missing `verify` reads as a failure with the reason.

```ts
function verifyEnvironment(
  registry: EnvironmentRegistry,
  kind: string,
  config: EnvironmentConfig,
): Promise<EnvironmentVerifyResult>
```

**Parameters**

- `registry` (`EnvironmentRegistry`): Registry of known environment kinds.
- `kind` (`string`): Environment kind to test.
- `config` (`EnvironmentConfig`): The kind's resolved options.

**Returns** `Promise<EnvironmentVerifyResult>`

Source: [src/chat/environment.ts:545](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L545)

### `viewPlans`

Function.

What a session's readers see: every kept plan, with an applying plan whose
run is not live marked as possibly interrupted. Never writes.

```ts
function viewPlans(bag: PlansBag, isRunLive: (runId: string) => boolean): PlanView[]
```

**Parameters**

- `bag` (`PlansBag`): The bag.
- `isRunLive` (`(runId: string) => boolean`): Whether a run id is live (a run in this host, or a fresh lease elsewhere).

**Returns** `PlanView[]`

Source: [src/chat/plans.ts:1106](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L1106)

### `watchInstalledVersion`

Function.

Starts watching the disk for a newer install of the running package.
The watcher probes once on creation, then on every poll and on changes to the applied marker. It calls `onChange` only when the installed version, the restart flag or `installing` changes. It stays idle when `currentVersion` is empty.
With `cacheDir`, an install holding the lock there (`auto-update-lock.ts`) holds the probe: the state says `installing`, never `restartRequired`, and is checked again every `installingPollMs` until the lock is released.

```ts
function watchInstalledVersion(
  options: WatchInstalledVersionOptions,
): InstalledVersionWatcher
```

**Parameters**

- `options` (`WatchInstalledVersionOptions`): Package spec, running version, callbacks and timing settings.

**Returns** `InstalledVersionWatcher`

Source: [src/chat/auto-update-restart.ts:179](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L179)

### `withRunningCallStarts`

Function.

`events` with each running call's start from the host's running work, a
subagent's call matched by its `childId`, and its `resuming` mark (a
call of a batch a durable run resumed after a process death). `events`
itself when nothing is stamped.

```ts
function withRunningCallStarts<E extends CallEvent>(
  events: E[],
  work: readonly Pick<RunningWorkItem, 'kind' | 'id' | 'childId' | 'startedAt' | 'resuming'>[],
): E[]
```

**Type parameters**

- `E` (`extends CallEvent`)

**Parameters**

- `events` (`E[]`): The page's events.
- `work` (`readonly Pick<RunningWorkItem, 'kind' | 'id' | 'childId' | 'startedAt' | 'resuming'>[]`): The session's running work (`chat.listWork`).

**Returns** `E[]`

Source: [src/chat/running-calls.ts:44](https://github.com/Tahul/zidane/blob/main/src/chat/running-calls.ts#L44)

### `workspaceEntryOps`

Function.

The entry actions of `cwd`, a folder of this machine.

```ts
function workspaceEntryOps(
  cwd: string,
  options?: { trash?: (absolute: string) => Promise<void> },
): LocalEntryOps
```

**Parameters**

- `cwd` (`string`): The folder (absolute).
- `options?` (`{ trash?: (absolute: string) => Promise<void> }`, default `{}`): Options.

**Returns** `LocalEntryOps`

Source: [src/chat/workspace/local-entries.ts:69](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/local-entries.ts#L69)

### `workspacePath`

Function.

`path` in a session workspace rooted at `root` (POSIX resolve): relative
paths against `root`, absolute paths as given. The same resolution the
execution contexts apply against their handle's cwd, so a workspace call
means what the agent's own tools mean; containment is the context's own
(sandboxes confine to their workspace root, the local process context
doesn't). Clients that must stay inside the workspace (the GUI's file
panels) guard their paths themselves.

```ts
function workspacePath(root: string, path: string): string
```

**Parameters**

- `root` (`string`): Workspace root, as an absolute POSIX path.
- `path` (`string`): Relative path, or an absolute path to normalize.

**Returns** `string`

Source: [src/chat/environment.ts:569](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L569)

### `writeCredentialEntry`

Function.

Write (or, `undefined`, delete) one entry by its file key, under the same
lock, mode and atomic write as every credential write. Refuses provider
slot keys (`#`): those go through the provider writers above.

```ts
function writeCredentialEntry(
  dataDir: string,
  key: string,
  cred: ProviderCredential | undefined,
): void
```

**Parameters**

- `dataDir` (`string`): The user data folder holding the credentials file.
- `key` (`string`): The entry's key in that file.
- `cred` (`ProviderCredential | undefined`): The entry to store; `undefined` deletes it.

Source: [src/chat/credentials.ts:485](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L485)

### `writeCredentials`

Function.

Write credentials atomically (write-then-rename) with mode 0o600.

Atomic on the same filesystem — readers either see the previous file or the
new one, never a half-written intermediate. Creates the parent dir if needed
(first launch on a fresh machine: `~/.zidane/` may not exist yet).

```ts
function writeCredentials(dataDir: string, creds: CredentialsFile): void
```

**Parameters**

- `dataDir` (`string`): Directory holding `credentials.json`, usually `config.paths.dir`.
- `creds` (`CredentialsFile`): The full credentials file contents to write.

Source: [src/chat/credentials.ts:322](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L322)

### `writeFailedUpdate`

Function.

Records a failed automatic install. When the cache dir can't be written,
the record is kept in memory for this process, so a broken cache never
turns into a package-manager run on every check.

```ts
function writeFailedUpdate(cacheDir: string, record: FailedUpdateRecord): void
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the record. Created when missing.
- `record` (`FailedUpdateRecord`): What failed.

Source: [src/chat/auto-update-auto-install.ts:93](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L93)

### `writeProjects`

Function.

Writes `projects.json` atomically, creating the directory when needed.

```ts
function writeProjects(dataDir: string, file: ProjectsFile): void
```

**Parameters**

- `dataDir` (`string`): Directory that holds the file.
- `file` (`ProjectsFile`): The full projects map to write.

Source: [src/chat/safe-mode.ts:95](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L95)

### `writeSessionExport`

Function.

Render `session` and write the resulting bytes to disk. Returns the
resolved target so the caller can show the user where the file
landed. The parent directory is created on demand (`recursive: true`)
— first-time exports don't need any pre-flight setup.

```ts
function writeSessionExport(
  opts: { format: SessionExportFormat; session: SessionData } & ResolveOptions,
): Promise<SessionExportTarget>
```

**Parameters**

- `opts` (`{ format: SessionExportFormat; session: SessionData } & ResolveOptions`): The session, the format and optional overrides for the working directory, home directory, prefix and data directory.

**Returns** `Promise<SessionExportTarget>`

Source: [src/chat/session-export.ts:137](https://github.com/Tahul/zidane/blob/main/src/chat/session-export.ts#L137)

### `writeUpdateAppliedMarker`

Function.

Record that an install just landed. Best-effort: a failure to write the
marker must never fail the upgrade that succeeded — the poll catches up.

```ts
function writeUpdateAppliedMarker(
  cacheDir: string,
  marker: UpdateAppliedMarker,
): boolean
```

**Parameters**

- `cacheDir` (`string`): Directory that holds the marker file. Created when missing.
- `marker` (`UpdateAppliedMarker`): The record to write.

**Returns** `boolean`

Source: [src/chat/auto-update-restart.ts:60](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L60)

### `writeWorkspaceFile`

Function.

Atomic, guarded write for the panel editor: temp file + rename in the same
directory, only when the on-disk mtime still equals `expectedMtimeMs`.
A small race window remains between the stat and the rename; acceptable
for a human-driven save.

```ts
function writeWorkspaceFile(
  cwd: string,
  rel: string,
  contents: string,
  expectedMtimeMs: number,
): Promise<WorkspaceWriteResult>
```

**Parameters**

- `cwd` (`string`): The folder.
- `rel` (`string`): The file, relative to it.
- `contents` (`string`): The new text.
- `expectedMtimeMs` (`number`): The modification time it was read at.

**Returns** `Promise<WorkspaceWriteResult>`

Source: [src/chat/workspace/files.ts:196](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L196)

## Classes

### `EditOutcomeLedger`

Class.

Holds the per-edit approval outcomes of in-flight tool calls, keyed by call id.

```ts
class EditOutcomeLedger
```

**Constructor**

```ts
new EditOutcomeLedger()
```

**Returns** `EditOutcomeLedger`

**Methods**

#### `clear()`

```ts
clear(): void
```

Clear every entry — called on `agent:done` and session disposal.

#### `discard()`

```ts
discard(callId: string): void
```

Drop entry without reading — used by `tool:error` (no transform on throw path).

**Parameters**

- `callId` (`string`): Tool call id.

#### `drain()`

```ts
drain(callId: string): PendingAnnotation | null
```

Read + remove the entry for `callId`. `null` when no stash exists.

**Parameters**

- `callId` (`string`): Tool call id.

**Returns** `PendingAnnotation | null`

#### `peek()`

```ts
peek(callId: string): PendingAnnotation | null
```

Non-removing peek — used by `tool:before` to paint events without consuming the stash.

**Parameters**

- `callId` (`string`): Tool call id.

**Returns** `PendingAnnotation | null`

#### `stash()`

```ts
stash(callId: string, payload: PendingAnnotation): void
```

Stores the annotation for a tool call, replacing an earlier one.

**Parameters**

- `callId` (`string`): Tool call id.
- `payload` (`PendingAnnotation`): Annotation to hold.

Source: [src/chat/host/edit-outcome-ledger.ts:32](https://github.com/Tahul/zidane/blob/main/src/chat/host/edit-outcome-ledger.ts#L32)

### `EnvironmentNotReadyError`

Class.

A session whose environment can't run yet (provisioning) or can't run at all (failed).

```ts
class EnvironmentNotReadyError extends Error
```

**Constructor**

```ts
new EnvironmentNotReadyError(environment: SessionEnvironment)
```

Creates the error, with a message that depends on whether provisioning failed or is still running.

**Parameters**

- `environment` (`SessionEnvironment`): The environment that is not ready.

**Returns** `EnvironmentNotReadyError`

**Properties**

- `state` (`EnvironmentState`, readonly): State the environment was in when the error was raised.

Source: [src/chat/environment.ts:117](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L117)

## Constants

### `ACCOUNT_CRED_KEY_INFIX`

Constant.

Reserved infix marking one of a provider's ADDITIONAL OAuth accounts:
`foo#account:1`, `foo#account:2`, …

```ts
const ACCOUNT_CRED_KEY_INFIX = '#account:'
```

Source: [src/chat/cred-keys.ts:31](https://github.com/Tahul/zidane/blob/main/src/chat/cred-keys.ts#L31)

### `ACTIONS_WITH_CARE_DOCTRINE`

Constant.

Reversibility / blast-radius doctrine. Mirrors claude-code's `# Executing
actions with care` (255-267). The TUI's safe-mode already gates risky
tool calls behind explicit approval, but this prompt teaches the model
to anticipate the gate rather than be surprised by it — and to ask
before reaching for destructive shortcuts.

```ts
const ACTIONS_WITH_CARE_DOCTRINE = ...
```

Source: [src/chat/agent-prompt.ts:242](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L242)

### `ALTERNATE_CRED_KEY_SUFFIX`

Constant.

Reserved suffix marking a provider's SECOND credential slot.

Not usable as (or as the tail of) a descriptor's credential key: `foo#alt`
holds `foo`'s alternate credential, so a provider claiming that name would
share storage with it.

```ts
const ALTERNATE_CRED_KEY_SUFFIX = '#alt'
```

Source: [src/chat/cred-keys.ts:25](https://github.com/Tahul/zidane/blob/main/src/chat/cred-keys.ts#L25)

### `anthropicDescriptor`

Constant.

Descriptor for Anthropic, with OAuth and API-key accounts.

```ts
const anthropicDescriptor: ProviderDescriptor
```

Source: [src/chat/providers.ts:405](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L405)

### `ARTIFACTS_COMPLETION_PROVIDER_ID`

Constant.

Id of the artifacts completion provider.

```ts
const ARTIFACTS_COMPLETION_PROVIDER_ID = 'artifacts'
```

Source: [src/chat/completion-artifacts.ts:22](https://github.com/Tahul/zidane/blob/main/src/chat/completion-artifacts.ts#L22)

### `ASK_USER_TOOL`

Constant.

Canonical name of the tool that asks the user one or more questions.

```ts
const ASK_USER_TOOL = 'ask_user'
```

Source: [src/chat/interactions-protocol.ts:57](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L57)

### `AUTO_INSTALL_RETRY_BACKOFF_MS`

Constant.

Waits before retrying a version whose install failed in a way that may pass
on its own: after the 1st, 2nd and 3rd such failure. A 4th is final.

```ts
const AUTO_INSTALL_RETRY_BACKOFF_MS: readonly number[]
```

Source: [src/chat/auto-update-auto-install.ts:37](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L37)

### `AUTOMATIC_INTERACTION_INSTRUCTION`

Constant.

Instruction sent to the model when automatic mode answers an interaction on the user's behalf.

```ts
const AUTOMATIC_INTERACTION_INSTRUCTION: 'Automatic mode is enabled. Make the best choice from the repository and current task, then continue without asking for this input again.'
```

Source: [src/chat/interactions-protocol.ts:336](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L336)

### `BUILD_AGENT`

Constant.

Build agent — the default profile. Full read/write/shell access plus
subagent spawning. Its tools are those of the `basic` preset plus `glob`,
`grep`, `web_search` and `fetch_url`.

The system prompt is the cwd-less variant of [`buildBuildSystem`](https://zidane.si/docs/reference/chat.md#buildBuildSystem);
the TUI overrides it per-session with an env-anchored composition so
the prompt carries the current project root + date. SDK consumers
calling `createAgent({ ...BUILD_AGENT.preset })` directly get the
cwd-less prompt and can wrap their own env section.

```ts
const BUILD_AGENT: AgentProfile
```

Source: [src/chat/agents.ts:520](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L520)

### `BUILD_CODEMODE_AGENT`

Constant.

Codemode — Build's tools, reached through scripts. `codemode` in
`'only'` mode: every native tool a script can call leaves the wire and is
declared inside the `codemode` description, so the model works in scripts
by default. `spawn` is callable from scripts, so it leaves the wire too.
Tools scripts cannot call stay declared. Shown only while the Codemode
setting is on.

```ts
const BUILD_CODEMODE_AGENT: AgentProfile
```

Source: [src/chat/agents.ts:558](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L558)

### `BUILTIN_AGENTS`

Constant.

Default registry of the built-in profiles. Insertion order = picker order.
Hosts that want only one profile can pass `{ agents: { build: BUILD_AGENT } }`,
or a fully custom registry of their own profiles.

```ts
const BUILTIN_AGENTS: AgentRegistry
```

Source: [src/chat/agents.ts:630](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L630)

### `BUILTIN_PROVIDERS`

Constant.

Default provider registry. Passed verbatim when `runTui` is invoked without
an explicit `providers` option. Hosts that want to override per-provider
metadata can spread this and replace specific entries:

```ts
runTui({ providers: { ...BUILTIN_PROVIDERS, anthropic: myOwnAnthropicDescriptor } })
```

`cursor` is intentionally NOT registered here: OAuth works, but inference
isn't wired (`cursor.stream()` throws — Cursor speaks a protobuf/HTTP-2
agent protocol, not OpenAI-compat). Shipping it in the picker only lets users
select a model that errors every turn. The descriptor stays exported so a
host can opt in (`{ ...BUILTIN_PROVIDERS, cursor: cursorDescriptor }`) once
the transport lands — re-add it here at that point.

```ts
const BUILTIN_PROVIDERS: Readonly<Record<string, ProviderDescriptor>>
```

Source: [src/chat/providers.ts:893](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L893)

### `BUILTIN_SUBAGENTS`

Constant.

Claude-trained models emit `subagent_type: 'Explore'` / `'Plan'`. Shipping
both gives those calls a target. No `model`: the `subagentModels` setting
or the parent model applies. A file agent with the same name overrides.

```ts
const BUILTIN_SUBAGENTS: Readonly<SubagentRegistry>
```

Source: [src/markdown-defs/builtin-agents.ts:8](https://github.com/Tahul/zidane/blob/main/src/markdown-defs/builtin-agents.ts#L8)

### `BUILTIN_THEMES`

Constant.

Built-in theme registry, keyed by `theme.id`. The TUI looks up the active
theme here using `Settings.theme`; unknown ids fall back to
`DEFAULT_THEME`. Extensions add themes with `ctx.registerTheme`. The
resolver receives them as `extraThemes`, and built-ins win on an id collision.

Insertion order is the picker cycle order — keep `default` first so a
fresh install (no `theme` in `state.json`) sees the default theme
before the others.

```ts
const BUILTIN_THEMES: Readonly<Record<string, Theme>>
```

Source: [src/chat/theme.ts:407](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L407)

### `CALL_TOOL_NAME`

Constant.

Name of the `call_tool` dispatcher tool.

```ts
const CALL_TOOL_NAME = 'call_tool'
```

Source: [src/tools/call-tool.ts:26](https://github.com/Tahul/zidane/blob/main/src/tools/call-tool.ts#L26)

### `CATPPUCCIN_FRAPPE`

Constant.

Catppuccin Frappe, the lightest of the dark Catppuccin flavors.

```ts
const CATPPUCCIN_FRAPPE: Theme
```

Source: [src/chat/themes/catppuccin.ts:311](https://github.com/Tahul/zidane/blob/main/src/chat/themes/catppuccin.ts#L311)

### `CATPPUCCIN_LATTE`

Constant.

Catppuccin Latte, the light Catppuccin flavor. It assumes a light terminal background.

```ts
const CATPPUCCIN_LATTE: Theme
```

Source: [src/chat/themes/catppuccin.ts:316](https://github.com/Tahul/zidane/blob/main/src/chat/themes/catppuccin.ts#L316)

### `CATPPUCCIN_MACCHIATO`

Constant.

Catppuccin Macchiato, a dark flavor between Frappe and Mocha.

```ts
const CATPPUCCIN_MACCHIATO: Theme
```

Source: [src/chat/themes/catppuccin.ts:309](https://github.com/Tahul/zidane/blob/main/src/chat/themes/catppuccin.ts#L309)

### `CATPPUCCIN_MOCHA`

Constant.

Catppuccin Mocha, the darkest Catppuccin flavor.

```ts
const CATPPUCCIN_MOCHA: Theme
```

Source: [src/chat/themes/catppuccin.ts:307](https://github.com/Tahul/zidane/blob/main/src/chat/themes/catppuccin.ts#L307)

### `cerebrasDescriptor`

Constant.

Descriptor for Cerebras, defaulting to `gpt-oss-120b`.

```ts
const cerebrasDescriptor: ProviderDescriptor
```

Source: [src/chat/providers.ts:496](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L496)

### `CLAUDE_IMPORT_NOTES`

Constant.

What an import from Claude Code deliberately leaves alone, spelled out for the UI.

```ts
const CLAUDE_IMPORT_NOTES: readonly string[]
```

Source: [src/chat/claude-import.ts:823](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L823)

### `COMMUNICATION_DOCTRINE`

Constant.

User-facing communication style. Lifted from claude-code's
`# Communicating with the user` + `# Tone and style` sections (\~404-442).
The "no colon before tool calls" rule matters because the TUI renders
the model's text and the tool call as separate blocks — a trailing
colon reads as a broken sentence.

```ts
const COMMUNICATION_DOCTRINE = ...
```

Source: [src/chat/agent-prompt.ts:230](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L230)

### `CRED_KEY_SLOT_SEPARATOR`

Constant.

Separator between a provider's canonical key and a slot qualifier. Every
non-canonical slot (`#alt`, `#account:N`) starts with it, so the character is
reserved in descriptor credential keys outright.

```ts
const CRED_KEY_SLOT_SEPARATOR = '#'
```

Source: [src/chat/cred-keys.ts:16](https://github.com/Tahul/zidane/blob/main/src/chat/cred-keys.ts#L16)

### `DEFAULT_AGENT_ID`

Constant.

Id of the profile activated on first launch when nothing is persisted.

```ts
const DEFAULT_AGENT_ID = 'build'
```

Source: [src/chat/agents.ts:637](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L637)

### `DEFAULT_BUDGET_EXCLUDE_TOOLS`

Constant.

Tools whose output is exempt from the per-turn `toolOutputBudget` accounting,
for SDK consumers that turn the budget on. The built-in profiles leave it
off.

These tools exist to LOAD context into the conversation — their text output
IS the value of the call. Counting it against the budget steers the model
away from the very tool it needs to make progress (e.g. a 78 KiB
`tool_search` response for a 20-tool Notion MCP server would trigger the
"summarize before calling more tools" nudge on the very turn that
unlocked those tools, making them effectively unusable).

Note this list is intentionally narrower than [`DEFAULT_PERSIST_EXCLUDE_TOOLS`](https://zidane.si/docs/reference/chat.md#DEFAULT_PERSIST_EXCLUDE_TOOLS):
`read_file` is NOT exempt — a 200 KiB file load is exactly the case the
budget should steer against. Same for `spawn` (a sprawling subagent
report should still nudge a consolidation pass).

Exported so SDK consumers building custom profiles can extend the list
for host-defined context-loading tools.

```ts
const DEFAULT_BUDGET_EXCLUDE_TOOLS: readonly string[]
```

Source: [src/chat/agents.ts:326](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L326)

### `DEFAULT_KEYBINDINGS`

Constant.

Defaults, derived from [`KEYBINDING_DEFS`](https://zidane.si/docs/reference/chat.md#KEYBINDING_DEFS) so the two never drift.

```ts
const DEFAULT_KEYBINDINGS: KeyBindings
```

Source: [src/chat/keybindings.ts:519](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L519)

### `DEFAULT_MAX_TODO_ITEMS`

Constant.

Ceiling on one list; every write boundary caps identically.

```ts
const DEFAULT_MAX_TODO_ITEMS = MAX_PLAN_STEPS
```

Source: [src/chat/todos.ts:72](https://github.com/Tahul/zidane/blob/main/src/chat/todos.ts#L72)

### `DEFAULT_PERSIST_EXCLUDE_TOOLS`

Constant.

Tools the chat layer never persists to disk, regardless of output size.

Rationale per entry:

- `read_file` — our reader is already paginated (`offset` + `limit`).
  Persisting the slice the model asked for and pointing it at the
  persisted path is circular; if the model wants more, it pages.
- `tool_search` — short metadata envelope. Never large.
- `skills_use` / `skills_read` — the `<skill_content>` body is
  intentionally part of the prompt; persisting it defeats the purpose.
- `read_chat_turns` — previous-chat excerpts are explicitly loaded
  conversation context; persisting them defeats the purpose.
- `present_plan` / `ask_user` — short JSON envelopes (decision +
  answers). Never large.
- `spawn` — the subagent's final report IS the value of the call;
  stubbing it out would defeat the orchestration pattern.
- `codemode` — a script prints what the model asked for, as a read
  does, and caps its own output (`max_output_tokens`: start and end
  inline, the full text in a file). Persisting it would force a re-read.

Exported so SDK consumers building custom profiles can extend the list
(e.g. host-defined `screenshot` tool whose base64 payload should stay
inline). Set `behavior.persistExcludeTools` to your own list to fully
override.

```ts
const DEFAULT_PERSIST_EXCLUDE_TOOLS: readonly string[]
```

Source: [src/chat/agents.ts:290](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L290)

### `DEFAULT_THEME`

Constant.

The default dark theme.

```ts
const DEFAULT_THEME: Theme
```

Source: [src/chat/theme.ts:277](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L277)

### `DOING_TASKS_DOCTRINE`

Constant.

Software-engineering doctrine. Lifted in spirit from claude-code's
`# Doing tasks` (`constants/prompts.ts:199-253`). Key levers:

- Stick to scope — prevents speculative refactors that double output tokens.
- No narration comments — prevents inflating diffs with `// Import the module` lines.
- Verify before claiming done — prevents false-positive reports that force a follow-up turn.

```ts
const DOING_TASKS_DOCTRINE = ...
```

Source: [src/chat/agent-prompt.ts:163](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L163)

### `EMPTY_HINTS`

Constant.

Stable empty list so callers can compare by reference. Exported so
any consumer that needs to feed "no primary hints" into the same
`clipHintsToWidth` / `renderHintSpans` pipeline (e.g. the TUI's
minimal UI mode, where the prompt overlay shows only `@` / `/`
triggers) reuses the same frozen array instead of allocating a new
one each render.

```ts
const EMPTY_HINTS: readonly Hint[]
```

Source: [src/chat/hints.ts:149](https://github.com/Tahul/zidane/blob/main/src/chat/hints.ts#L149)

### `ENTRY_EMPTY_DIRS_LIMIT`

Constant.

Most empty folders a listing adds.

```ts
const ENTRY_EMPTY_DIRS_LIMIT = 2000
```

Source: [src/chat/workspace/entries.ts:69](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/entries.ts#L69)

### `ENVIRONMENT_METADATA_KEY`

Constant.

Session metadata key that stores the [`SessionEnvironment`](https://zidane.si/docs/reference/chat.md#SessionEnvironment).

```ts
const ENVIRONMENT_METADATA_KEY = 'environment'
```

Source: [src/chat/environment.ts:73](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L73)

### `FEATURED_PROVIDERS`

Constant.

Providers shown first in Connect. The rest sit under "More".

```ts
const FEATURED_PROVIDERS: readonly string[]
```

Source: [src/chat/host/onboarding/providers.ts:15](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/providers.ts#L15)

### `GRUVBOX_DARK`

Constant.

Gruvbox with a dark background.

```ts
const GRUVBOX_DARK: Theme
```

Source: [src/chat/themes/gruvbox.ts:166](https://github.com/Tahul/zidane/blob/main/src/chat/themes/gruvbox.ts#L166)

### `GRUVBOX_LIGHT`

Constant.

Gruvbox with a light background.

```ts
const GRUVBOX_LIGHT: Theme
```

Source: [src/chat/themes/gruvbox.ts:168](https://github.com/Tahul/zidane/blob/main/src/chat/themes/gruvbox.ts#L168)

### `HOST_ENABLED_LISTS`

Constant.

The per-server lists a host leaving unset means its defaults for: everything enabled, each server's own `shared`.

```ts
const HOST_ENABLED_LISTS: readonly ['enabledSkills', 'enabledMcps', 'enabledExtensions', 'enabledPlugins', 'disabledMcpTools', 'mcpSharing']
```

Source: [src/chat/host-scoped-settings.ts:72](https://github.com/Tahul/zidane/blob/main/src/chat/host-scoped-settings.ts#L72)

### `HOST_INTERACTION_ID_PREFIX`

Constant.

Id prefix for interactions the HOST synthesizes — slash commands and
extension `ctx.interaction.*` share the prompt slot with the tools but
never pass through [`createInteractionTools`](https://zidane.si/docs/reference/chat.md#createInteractionTools). Model-originated
requests carry the provider's `tool_call.id` instead. Naming only:
origin policy reads `BaseRequest.origin`, never the id.

```ts
const HOST_INTERACTION_ID_PREFIX = 'command_'
```

Source: [src/chat/interactions-protocol.ts:346](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L346)

### `IDENTITY_PREFIX`

Constant.

Lead line — short, version-stable. The first sentence matches the key of
Claude Code's identity prefix (models anchor faster on familiar shapes).
The rest sets the register: token-earning sentences with byte-exact
code ("small mouth, full brain"), and a hard rule that speed never
widens scope (the leeroy-jenkins fix). Keep changes rare — each one
costs a cache miss across active sessions.

```ts
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.'
```

Source: [src/chat/agent-prompt.ts:42](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L42)

### `IMPLICITLY_SAFE_TOOLS`

Constant.

Tools that always pass without prompting. Four categories, plus the
dev-browser, terminal-read and side-session tools noted inline in the list:

- **Pure reads** (`read_file`, `list_files`, `glob`, `grep`) — no
  side effects on disk or the model's own state.
- **Interaction tools** (`ask_user`, `present_plan`) — the call
  itself surfaces a TUI picker the user has to answer, so prompting
  for approval first would mean "approve this prompt before you see
  the prompt": redundant + breaks the flow. The picker IS the gate.
- **Todos** (`todowrite`, `todoread`) — write to a slot on
  `session.metadata.todos` and nothing else. No shell, no disk, no
  network. The model uses these for checkpointing every few steps;
  a per-call approval prompt would drown the conversation in
  interruptions that buy no safety since the tool's surface is
  read/write on a metadata bag we already trust the agent with.
- **Skill activation** (`skills_use`) — mutates a small per-agent
  Map of active skills and returns the activated skill's bundled
  instructions (or releases an entry on `mode: "deactivate"`).
  Pure state on the agent — no shell, no disk writes, no network.
  Gating it would also defeat the "model recovers from
  `AgentToolNotAllowedError` by calling
  `skills_use({ mode: "deactivate", name })`" path: a stuck model
  would have to ask the user permission to *unstick* itself on
  every retry. `skills_read` and `skills_run_script` are
  intentionally NOT on this list — the former touches disk and
  the latter executes arbitrary scripts, so both keep going
  through the regular gate.

Users who want to gate any of these must disable safe-mode entirely
(or fork this list in their own embedding).

```ts
const IMPLICITLY_SAFE_TOOLS: readonly string[]
```

Source: [src/chat/safe-mode.ts:260](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L260)

### `INTERACTION_GUIDANCE`

Constant.

How to use `present_plan` / `ask_user`. Kept short — the tool descriptions
carry the schema details; this prompt covers the cross-cutting *when*.
Exported so a custom profile can pull it in independently.

```ts
const INTERACTION_GUIDANCE = ...
```

Source: [src/chat/agent-prompt.ts:277](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L277)

### `INTERACTION_GUIDANCE_NO_PROMPTS`

Constant.

Variant of [`INTERACTION_GUIDANCE`](https://zidane.si/docs/reference/chat.md#INTERACTION_GUIDANCE) for the no-interactive-prompts
configuration (`Settings.interactionMode = 'disabled'`). The two tools are
stripped from the agent's tool set; this section tells the model to
operate autonomously and surface assumptions / open questions in its
final prose message rather than via tool calls.

```ts
const INTERACTION_GUIDANCE_NO_PROMPTS = ...
```

Source: [src/chat/agent-prompt.ts:301](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L301)

### `KEYBINDING_DEF_BY_ACTION`

Constant.

Index by action for O(1) lookup of label / description / default.

```ts
const KEYBINDING_DEF_BY_ACTION: Readonly<Record<KeyAction, KeyBindingDef>>
```

Source: [src/chat/keybindings.ts:512](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L512)

### `KEYBINDING_DEFS`

Constant.

The canonical action catalog. Order is the source of truth for the
generated keybindings file and the settings UI.

```ts
const KEYBINDING_DEFS: readonly KeyBindingDef[]
```

Source: [src/chat/keybindings.ts:162](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L162)

### `KEYBINDING_KEY_COL_WIDTH`

Constant.

Width (in cells) of the fixed key column used by every binding-row
renderer. Derived once from [`KEYBINDING_DEFS`](https://zidane.si/docs/reference/chat.md#KEYBINDING_DEFS) so a new action
with a wider default spec (`ctrl+shift+x`, …) automatically grows
the column instead of truncating the label.

```ts
const KEYBINDING_KEY_COL_WIDTH: number
```

Source: [src/chat/keybindings.ts:738](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L738)

### `LOCAL_ENVIRONMENT`

Constant.

The `local` environment: the current checkout, always ready. Sessions without environment metadata read as this.

```ts
const LOCAL_ENVIRONMENT: SessionEnvironment
```

Source: [src/chat/environment.ts:78](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L78)

### `localDescriptor`

Constant.

Local OpenAI-compatible LLM (Ollama, vLLM, LM Studio, Lemonade, llama.cpp).

No fixed base URL or model catalogue — both come from the user via
`customFields`. No `envKey`, so the wizard skips the API-key prompt and
treats the customFields-only credential as the auth signal (see
`applyApiKeyEnv` + `detectAuth`).

Vision / cache / reasoning are off by default in the factory — flip them
by passing a custom descriptor that calls `local({ capabilities })`.

`models` is a getter, not a static list: there's no fixed catalogue to ship,
but once the user has configured a default model (mirrored into
`LOCAL_LLM_DEFAULT_MODEL` by `applyApiKeyEnv`), we surface it as a
single-entry list so the model picker + footer aren't empty. Resolved at
access time because the env var is populated at TUI launch, after this
module loads. Empty list when unconfigured — the picker hides, the user
types a slug at the prompt as before.

```ts
const localDescriptor: ProviderDescriptor
```

Source: [src/chat/providers.ts:794](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L794)

### `localEnvironmentProvider`

Constant.

The provider for the local environment: the current checkout, with commands on this machine.

```ts
const localEnvironmentProvider: EnvironmentProvider
```

Source: [src/chat/environments/local.ts:12](https://github.com/Tahul/zidane/blob/main/src/chat/environments/local.ts#L12)

### `MAX_CLOSED_PLANS_PER_SCOPE`

Constant.

Closed plans kept per scope; older ones are pruned (the transcript keeps them).

```ts
const MAX_CLOSED_PLANS_PER_SCOPE = 10
```

Source: [src/chat/plans.ts:44](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L44)

### `MAX_PLAN_HISTORY`

Constant.

Earlier proposals kept on a revised plan.

```ts
const MAX_PLAN_HISTORY = 3
```

Source: [src/chat/plans.ts:46](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L46)

### `MAX_PLAN_STEPS`

Constant.

Steps per plan; matches the `todowrite` cap.

```ts
const MAX_PLAN_STEPS = 100
```

Source: [src/chat/plans.ts:48](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L48)

### `openaiDescriptor`

Constant.

Descriptor for OpenAI through the Codex credentials, with accounts.

```ts
const openaiDescriptor: ProviderDescriptor
```

Source: [src/chat/providers.ts:437](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L437)

### `openrouterDescriptor`

Constant.

Descriptor for OpenRouter, with a metadata alias for retired Claude slugs.

```ts
const openrouterDescriptor: ProviderDescriptor
```

Source: [src/chat/providers.ts:476](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L476)

### `PLAN_AGENT`

Constant.

Plan agent — read-only exploration mode. Locked down to file reading and
codebase search; no shell, no edits, no spawning. The system prompt
frames the conversation as planning rather than execution so the model
outputs proposals instead of attempting mutations.

In Build's family: a session switching between them keeps Build's agent,
a gate enforces Plan's tools and its rules arrive as a note.

```ts
const PLAN_AGENT: AgentProfile
```

Source: [src/chat/agents.ts:610](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L610)

### `PLAN_EDITED_NOTE_KIND`

Constant.

Note kind of user edits delivered at run start.

```ts
const PLAN_EDITED_NOTE_KIND = 'plan:edited'
```

Source: [src/chat/plan-notes.ts:19](https://github.com/Tahul/zidane/blob/main/src/chat/plan-notes.ts#L19)

### `PLAN_MODE_DOCTRINE`

Constant.

Plan-mode framing with explicit phases (explore → ask → propose), drawing
on claude-code's iterative-plan mode (`utils/messages.ts:3323-3383`).
Paired with the read-only tool set in [`PLAN_AGENT`](https://zidane.si/docs/reference/chat.md#PLAN_AGENT).

```ts
const PLAN_MODE_DOCTRINE = ...
```

Source: [src/chat/agent-prompt.ts:263](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L263)

### `PLAN_MODE_DOCTRINE_NO_PROMPTS`

Constant.

Variant of [`PLAN_MODE_DOCTRINE`](https://zidane.si/docs/reference/chat.md#PLAN_MODE_DOCTRINE) for the no-interactive-prompts
configuration. It has no "Ask" step, and "Propose" tells the model to
write the plan directly in its final message instead of calling
`present_plan`.

```ts
const PLAN_MODE_DOCTRINE_NO_PROMPTS = ...
```

Source: [src/chat/agent-prompt.ts:310](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L310)

### `PLAN_STOPPED_NOTE_KIND`

Constant.

Note kind of a stopped plan's notice.

```ts
const PLAN_STOPPED_NOTE_KIND = 'plan:stopped'
```

Source: [src/chat/plan-notes.ts:17](https://github.com/Tahul/zidane/blob/main/src/chat/plan-notes.ts#L17)

### `PLANS_METADATA_KEY`

Constant.

`session.metadata[PLANS_METADATA_KEY]: PlansBag`.

```ts
const PLANS_METADATA_KEY = 'plans'
```

Source: [src/chat/plans.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L35)

### `PRESENT_PLAN_TOOL`

Constant.

Canonical name of the tool that presents a plan for approval.

```ts
const PRESENT_PLAN_TOOL = 'present_plan'
```

Source: [src/chat/interactions-protocol.ts:55](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L55)

### `SELF_UPDATE_TIMEOUT_MS`

Constant.

Default for `PerformSelfUpdateOptions.timeoutMs`.

```ts
const SELF_UPDATE_TIMEOUT_MS: number
```

Source: [src/chat/auto-update.ts:792](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L792)

### `SETTINGS_CATEGORIES`

Constant.

Ordered list of categories. Display order in any consumer (TUI tabs,
GUI sidebar, settings JSON dump) should match this array — keeps
mental model consistent across surfaces.

```ts
const SETTINGS_CATEGORIES: readonly SettingsCategoryDescriptor[]
```

Source: [src/chat/settings-catalog.ts:89](https://github.com/Tahul/zidane/blob/main/src/chat/settings-catalog.ts#L89)

### `SETTINGS_DEFAULTS_VERSION`

Constant.

Current defaults revision. Bump it with a new `PREVIOUS_DEFAULTS` entry when a default changes.

```ts
const SETTINGS_DEFAULTS_VERSION = 1
```

Source: [src/chat/settings-defaults.ts:311](https://github.com/Tahul/zidane/blob/main/src/chat/settings-defaults.ts#L311)

### `SETTINGS_DEFAULTS_VERSION_KEY`

Constant.

Key of the stored `settings` slice naming the defaults revision it was migrated to.

```ts
const SETTINGS_DEFAULTS_VERSION_KEY = 'settingsDefaultsVersion'
```

Source: [src/chat/settings-defaults.ts:308](https://github.com/Tahul/zidane/blob/main/src/chat/settings-defaults.ts#L308)

### `SETTINGS_INPUTS`

Constant.

Every free-text setting the settings UI lists, in display order.

```ts
const SETTINGS_INPUTS: readonly SettingsInput[]
```

Source: [src/chat/settings-catalog.ts:705](https://github.com/Tahul/zidane/blob/main/src/chat/settings-catalog.ts#L705)

### `SETTINGS_SECTIONS`

Constant.

Section headers for the settings lists: each tab shows its rows grouped
under these titles, in this order. A key missing from the table lands in
`Other` at the end, so a new setting still shows up somewhere.

```ts
const SETTINGS_SECTIONS: readonly { keys: readonly string[]; title: string }[]
```

Source: [src/chat/settings-catalog.ts:133](https://github.com/Tahul/zidane/blob/main/src/chat/settings-catalog.ts#L133)

### `SUBAGENT_GUIDANCE`

Constant.

How to brief a subagent. Spawning a child agent is the highest-leverage
token move in zidane — a 5k-token research dive becomes a 200-token
summary in the parent's context. But shallow prompts produce shallow
work, so the doctrine is mostly about quality of the briefing.

Lifted from claude-code's `## Writing the prompt` tool-prompt
(`tools/AgentTool/prompt.ts:99-113`).

```ts
const SUBAGENT_GUIDANCE = ...
```

Source: [src/chat/agent-prompt.ts:254](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L254)

### `TITLE_CHECK_KEY`

Constant.

The last auto-title attempt's outcome ([`TitleCheckRecord`](https://zidane.si/docs/reference/chat.md#TitleCheckRecord)), shown in session details.

```ts
const TITLE_CHECK_KEY = 'titleCheck'
```

Source: [src/chat/auto-title.ts:31](https://github.com/Tahul/zidane/blob/main/src/chat/auto-title.ts#L31)

### `TITLE_CHECKED_KEY`

Constant.

User-prompt count at the last staleness check (or generation).

```ts
const TITLE_CHECKED_KEY = 'titleCheckedPrompts'
```

Source: [src/chat/auto-title.ts:23](https://github.com/Tahul/zidane/blob/main/src/chat/auto-title.ts#L23)

### `TITLE_SOURCE_KEY`

Constant.

Metadata key that records whether the title was set by auto-titling or by the user.

```ts
const TITLE_SOURCE_KEY = 'titleSource'
```

Source: [src/chat/auto-title.ts:21](https://github.com/Tahul/zidane/blob/main/src/chat/auto-title.ts#L21)

### `TODOREAD_TOOL`

Constant.

Canonical name of the tool that reads the todo list.

```ts
const TODOREAD_TOOL = 'todoread'
```

Source: [src/chat/todos.ts:45](https://github.com/Tahul/zidane/blob/main/src/chat/todos.ts#L45)

### `TODOWRITE_TOOL`

Constant.

Canonical name of the tool that replaces the todo list.

```ts
const TODOWRITE_TOOL = 'todowrite'
```

Source: [src/chat/todos.ts:43](https://github.com/Tahul/zidane/blob/main/src/chat/todos.ts#L43)

### `TOKEN_DISCIPLINE_DOCTRINE`

Constant.

zidane-specific token-economy doctrine. No claude-code analog — this is
the prose half of the `behavior.{compactStrategy, elideStaleReads,
toolDisclosure, readFileDefaults}` defaults. The behavior flags do the
heavy lifting at the wire level; this prompt nudges the model away from
the access patterns that defeat them.

```ts
const TOKEN_DISCIPLINE_DOCTRINE = ...
```

Source: [src/chat/agent-prompt.ts:201](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L201)

### `TRANSCRIPT_TRIM_STEP_EVENTS`

Constant.

Most events one [`stepTranscriptCap`](https://zidane.si/docs/reference/chat.md#stepTranscriptCap) drops. A cap that has thousands
of rows to cut (the history a reveal brought in, once the viewport is back
at the bottom) unmounts them over several commits of this size instead of
freezing one frame on all of them.

```ts
const TRANSCRIPT_TRIM_STEP_EVENTS = 300
```

Source: [src/chat/streaming-pure.ts:689](https://github.com/Tahul/zidane/blob/main/src/chat/streaming-pure.ts#L689)

### `UPDATE_APPLIED_MARKER`

Constant.

File name of the update-applied marker inside the cache directory.

```ts
const UPDATE_APPLIED_MARKER = 'update-applied.json'
```

Source: [src/chat/auto-update-restart.ts:34](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L34)

### `UPDATE_FAILED_FILE`

Constant.

File name of the failed-install record inside the cache directory.

```ts
const UPDATE_FAILED_FILE = 'update-failed.json'
```

Source: [src/chat/auto-update-auto-install.ts:31](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L31)

### `UPDATE_LOCK_FILE`

Constant.

File name of the install lock inside the cache directory.

```ts
const UPDATE_LOCK_FILE = 'update.lock'
```

Source: [src/chat/auto-update-lock.ts:18](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-lock.ts#L18)

### `UPDATE_LOCK_STALE_MS`

Constant.

A lock older than this is stale when its holder can't be probed (another host, or no pid).

```ts
const UPDATE_LOCK_STALE_MS: number
```

Source: [src/chat/auto-update-lock.ts:21](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-lock.ts#L21)

### `UPDATE_LOG_FILE`

Constant.

File name of the worker's log inside the cache directory.

```ts
const UPDATE_LOG_FILE = 'update.log'
```

Source: [src/chat/auto-update-worker.ts:44](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L44)

### `UPDATE_RESULT_FILE`

Constant.

File name of the worker's result inside the cache directory.

```ts
const UPDATE_RESULT_FILE = 'update-result.json'
```

Source: [src/chat/auto-update-worker.ts:46](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L46)

### `USAGE_CACHE_TTL_MS`

Constant.

How long a snapshot stays fresh. Usage windows move in minutes, not seconds.

```ts
const USAGE_CACHE_TTL_MS = 60_000
```

Source: [src/chat/provider-usage.ts:64](https://github.com/Tahul/zidane/blob/main/src/chat/provider-usage.ts#L64)

### `VAPORWAVE_THEME`

Constant.

Vaporwave, a dark theme with neon colors.

```ts
const VAPORWAVE_THEME: Theme
```

Source: [src/chat/themes/vaporwave.ts:40](https://github.com/Tahul/zidane/blob/main/src/chat/themes/vaporwave.ts#L40)

### `watchGitRepository`

Constant.

Watches `cwd`'s repository; resolves the stop, or `null` when `cwd` is not in one.

On macOS a recursive watch rides the thread's FSEvents stream, which every
start and stop rebuilds, and a stop waits for that rebuild: seconds when
the system's file events are busy. A process that must not wait (the
desktop app's main thread) runs it on a thread of its own.

```ts
const watchGitRepository: StartGitWatch
```

Source: [src/chat/git/watch.ts:61](https://github.com/Tahul/zidane/blob/main/src/chat/git/watch.ts#L61)

### `WINDOW_LOCAL_SETTINGS`

Constant.

Settings that belong to the window even while a host owns the settings
file (daemon or remote mode): which host the next launch uses.

```ts
const WINDOW_LOCAL_SETTINGS: readonly ['useDaemon', 'remoteHosts']
```

Source: [src/chat/host-scoped-settings.ts:21](https://github.com/Tahul/zidane/blob/main/src/chat/host-scoped-settings.ts#L21)

### `WORKSPACE_MAX_ENTRIES`

Constant.

Most entries a listing returns.

```ts
const WORKSPACE_MAX_ENTRIES = 5000
```

Source: [src/chat/workspace/files.ts:21](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L21)

### `WORKSPACE_MAX_FILE_BYTES`

Constant.

Largest file read whole; past it a read is cut.

```ts
const WORKSPACE_MAX_FILE_BYTES: number
```

Source: [src/chat/workspace/files.ts:19](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/files.ts#L19)

### `WORKTREE_ENVIRONMENT_KIND`

Constant.

Environment kind of worktree sessions.

```ts
const WORKTREE_ENVIRONMENT_KIND = 'worktree'
```

Source: [src/chat/environments/worktree.ts:27](https://github.com/Tahul/zidane/blob/main/src/chat/environments/worktree.ts#L27)

## Interfaces

### `ActivePlanSummary`

Interface.

The one-line summary the composer pill and the TUI footer show.

```ts
interface ActivePlanSummary
```

**Properties**

- `planId` (`string`): Plan id.
- `progress` (`PlanProgress`): Step counts.
- `state` (`PlanState`): Its state: applying or stopped.
- `title` (`string`): Title (empty for adhoc).

Source: [src/chat/plans.ts:1128](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L1128)

### `AgentComposition`

Interface.

What a session's agent is built with: tools and prompt. `key` names it
(`family:<name>` or `profile:<id>`) and keys the session's frozen prompt.

```ts
interface AgentComposition
```

**Properties**

- `base` (`AgentProfile`): The profile whose prompt, behavior and hooks the agent is built from.
- `key` (`string`): `family:<name>` for a family, `profile:<id>` for a profile without one.
- `members` (`readonly AgentProfile[]`): Every profile in the composition, in registry order, `base` first.

Source: [src/chat/agents.ts:106](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L106)

### `AgentProfile`

Interface.

A selectable agent profile: a labelled preset the user can switch between.

```ts
interface AgentProfile
```

**Properties**

- `accent?` (`AgentAccent`): Theme token used by the picker / footer badge. Defaults to `'accent'`.
- `description` (`string`): One-line description shown next to the label in the picker.
- `family?` (`string`): Profiles with the same family share one agent: its tools are every
  member's tools, its prompt is the first member's (in registry order).
  Switching between members keeps the agent and the prompt cache; a gate
  refuses the tools the active member doesn't have, and a note tells the
  model about the switch. Without a family the profile composes alone.
- `id` (`string`): Stable identifier. Each session stores the id of the profile it runs on.
- `label` (`string`): Human-readable label rendered in the picker and footer badge.
- `preset` (`Preset`): Preset applied to `createAgent()` when this profile is active. Profiles
  are self-contained: the host's chat-level `preset` (if any) is NOT
  merged underneath. Hosts that want a shared base across profiles
  should compose it explicitly in each profile's preset.
- `requiresSetting?` (`keyof Settings`): A boolean setting that must be on for this profile to exist. While it is
  off the profile is left out of every picker, and a session saved on it
  builds as the default profile instead (see [`availableAgents`](https://zidane.si/docs/reference/chat.md#availableAgents)).
- `switchNote?` (`string | ((ctx: ProfileSwitchNoteContext) => string)`): What the model is told when the session switches to this profile without
  rebuilding its agent. Default: the profile's label, description and
  system prompt, then the tools that are off. On the profile a composition
  is built from, it is what the model is told when the session comes back
  to it (default: its instructions and tools apply again).

Source: [src/chat/agents.ts:36](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L36)

### `AgentsMdFile`

Interface.

A single loaded AGENTS.md file.

```ts
interface AgentsMdFile
```

**Properties**

- `contents` (`string`): The file text.
- `path` (`string`): Absolute path of the file.
- `source` (`'user' | 'project'`): Whether the file came from the project or the user's home.

Source: [src/chat/agents-md.ts:46](https://github.com/Tahul/zidane/blob/main/src/chat/agents-md.ts#L46)

### `AgentsMdResult`

Interface.

Result of [`discoverAgentsMd`](https://zidane.si/docs/reference/chat.md#discoverAgentsMd). `files` is empty when none were found.

```ts
interface AgentsMdResult
```

**Properties**

- `block` (`string | null`): Pre-rendered prompt block ready to append to the system prompt, or
  `null` when no files were loaded. Header + per-file sections with
  absolute paths.
- `files` (`readonly AgentsMdFile[]`): All loaded files in render order (user first, project last).

Source: [src/chat/agents-md.ts:56](https://github.com/Tahul/zidane/blob/main/src/chat/agents-md.ts#L56)

### `ApiKeyCredential`

Interface.

A stored API key credential.

```ts
interface ApiKeyCredential extends CredentialIdentity
```

**Properties**

- `customFields?` (`Record<string, string>`): Free-form fields collected by the wizard from the descriptor's
  `customFields` definitions. Keys match the descriptor field's `key`.
  Mirrored into `process.env[field.envVar]` by `applyApiKeyEnv` so the
  provider's factory can read them via the existing env-var convention.
- `id?` (`string`, from `CredentialIdentity`)
- `kind` (`'apikey'`): Discriminant that marks an API key credential.
- `value` (`string`): Bearer API key. May be empty when the descriptor has no `envKey` and
  authenticates entirely via `customFields` (e.g. the `local`
  provider, where the auth signal is the configured base URL).

Source: [src/chat/credentials.ts:80](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L80)

### `ApplyPlanResult`

Interface.

Result of [`applyPlanEvent`](https://zidane.si/docs/reference/chat-pure.md#applyPlanEvent).

```ts
interface ApplyPlanResult
```

**Properties**

- `bag` (`PlansBag`): The bag after the event (the input bag, unchanged, when `changed` is false).
- `changed` (`boolean`): Something changed and should be persisted.
- `error?` (`string`): The event was refused; `bag` is the input.
- `plan?` (`Plan`): The plan the event acted on, after it.

Source: [src/chat/plans.ts:244](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L244)

### `ApprovalHub`

Interface.

Safe-mode gate that holds tool calls until a client approves or denies them.

```ts
interface ApprovalHub
```

**Properties**

- `covers` (`(cwd: string, tool: string, input: Record<string, unknown>) => boolean`): Whether the project safelist of `cwd` covers the call (it is checked before the decision model).
- `denyAll` (`(sessionId?: string) => void`): Denies every pending request, or only those of `sessionId` when given.
- `listPending` (`() => WireApprovalRequest[]`): Returns the requests still waiting for a decision.
- `rehydrate` (`() => void`): Register ghost resolvers for approvals persisted by a prior process lifetime.
- `request` (`(input: ApprovalRequestInput) => Promise<ApprovalDecision>`): Asks for approval. Resolves at once when the project safelist covers the call, otherwise when a client decides.
- `resolve` (`(id: string, decision: ApprovalDecision, answeredBy?: InteractionAnswerer) => boolean`): Applies a decision to the pending request `id`: `false` when it was already decided (first decision wins).
  An `accept-safelist` decision also saves the suggested entries to the project safelist. `answeredBy`: an
  orchestrator, an extension or a client decided, not the person at a window.

Source: [src/chat/host/approvals.ts:62](https://github.com/Tahul/zidane/blob/main/src/chat/host/approvals.ts#L62)

### `ApprovalRequest`

Interface.

A tool call waiting for the user's decision.

```ts
interface ApprovalRequest
```

**Properties**

- `concern?` (`ModelApproval`): Model decision approvals: the decision model's verdict when it handed this call to you (`modelConcern`).
- `correlationId?` (`string`): Cross-window correlation id (the daemon's approval request id) for
  entries surfaced by the daemon runtime. Lets `dropByCorrelation`
  retract this entry when ANOTHER window resolves the same shared
  approval, without re-sending a decision. Absent for local-runtime
  approvals.
- `id` (`string`): Unique id of the request.
- `input` (`Record<string, unknown>`): The tool call's input.
- `originator?` (`ApprovalOriginator`): Caller attribution. Absent ≡ `{ kind: 'parent' }`.
- `resolve` (`(decision: ApprovalDecision) => void`): Settles the request with the user's decision.
- `tool` (`string`): Name of the tool awaiting approval.

Source: [src/chat/safe-mode-context.tsx:55](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode-context.tsx#L55)

### `ApprovalRequestInput`

Interface.

What the agent loop hands the hub to request approval for a tool call.

```ts
interface ApprovalRequestInput
```

**Properties**

- `alwaysAsk?` (`boolean`): Skip the persisted safelist and offer nothing to remember: the prompt must show every time.
- `concern?` (`ModelApproval`): The decision model's verdict when it escalated the call (Model decision approvals).
- `cwd` (`string`): Working directory the safelist is looked up and extended for.
- `edit?` (`EditPayload`): The edit under approval, for edit tools.
- `input` (`Record<string, unknown>`): Input of the tool call.
- `onTimeout?` (`() => void`): Called right before a timed-out request is denied, so the caller can say why.
- `originator?` (`ApprovalOriginator`): Who raised the call. Absent: the parent agent.
- `preview?` (`SerializedPreview`): Diff preview of the edit.
- `priorContent?` (`string | null`): File content before the edit. `null` when the file did not exist.
- `sessionId` (`string`): Session that raised the call.
- `skipProjectSafelist?` (`boolean`): Skip the persisted project safelist (a session with its own rules); a decision may still save to it.
- `timeoutMs?` (`number`): Deny the request when no one answered it within this many ms (the session's `approvalTimeoutMs`).
- `tool` (`string`): Name of the tool being called.

Source: [src/chat/host/approvals.ts:32](https://github.com/Tahul/zidane/blob/main/src/chat/host/approvals.ts#L32)

### `ArtifactKindsHost`

Interface.

One registry build's artifact kinds.

```ts
interface ArtifactKindsHost
```

**Properties**

- `adopt` (`(registry: ExtensionRegistry) => () => void`): Binds the built registry. Returns the release. Detection and kinds already follow the live view.
- `entries` (`() => readonly ArtifactKindEntry[]`): The kinds registered now, in registration order.
- `registerKind` (`(entry: ArtifactKindEntry) => Disposable`): The `ArtifactsPort.registerKind` of the build's runtime. The Disposable withdraws the kind.

Source: [src/chat/host/extension-artifact-kinds.ts:18](https://github.com/Tahul/zidane/blob/main/src/chat/host/extension-artifact-kinds.ts#L18)

### `ArtifactReferenceData`

Interface.

Provider payload for an artifact reference.

```ts
interface ArtifactReferenceData
```

**Properties**

- `entry` (`ArtifactReferenceEntry`): The referenced artifact.
- `locator` (`string`): What gets inserted: `row.href` when present, else the raw `uri`.

Source: [src/chat/completion-artifacts.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/completion-artifacts.ts#L35)

### `ArtifactReferenceEntry`

Interface.

An artifact that can be referenced from a prompt.

```ts
interface ArtifactReferenceEntry
```

**Properties**

- `artifact` (`SessionArtifact`): The session artifact.
- `kindLabel` (`string`): Human kind label for the description (`Pull request`).
- `row` (`ExtensionArtifactRow`): Pre-rendered row (`renderArtifactRow`) — kind registry stays host-side.

Source: [src/chat/completion-artifacts.ts:25](https://github.com/Tahul/zidane/blob/main/src/chat/completion-artifacts.ts#L25)

### `ArtifactViewGroup`

Interface.

Artifacts of one kind, ready to display.

```ts
interface ArtifactViewGroup
```

**Properties**

- `kind` (`string`): Artifact kind id.
- `label` (`string`): Human kind label (`Pull request`), raw kind when unregistered.
- `rows` (`ArtifactViewRow[]`): Rows of this kind, in the store's order.

Source: [src/chat/artifacts-view.ts:23](https://github.com/Tahul/zidane/blob/main/src/chat/artifacts-view.ts#L23)

### `ArtifactViewRow`

Interface.

One artifact paired with its rendered row.

```ts
interface ArtifactViewRow
```

**Properties**

- `artifact` (`SessionArtifact`): The session artifact.
- `row` (`ExtensionArtifactRow`): Rendered row for the artifact.

Source: [src/chat/artifacts-view.ts:15](https://github.com/Tahul/zidane/blob/main/src/chat/artifacts-view.ts#L15)

### `AutoUpdateConfig`

Interface.

Host-supplied auto-update settings. The check itself is gated on
`Settings.checkForUpdates`; passing a config wires the React plumbing.

Hosts that want to publish their own consumer-facing package can pass
different values without forking anything — the registry / channel /
platform-package-prefix are all knobs.

```ts
interface AutoUpdateConfig
```

**Properties**

- `autoInstallTargets?` (`readonly { binName?: string; packageName: string }[]`): Packages a background install keeps in step when
  `Settings.autoInstallUpdates` is on, e.g. the TUI and the desktop app.
  Omit to never install in the background (dev runs from a checkout).
- `binName?` (`string`): Executable name on PATH for `packageName`, used to probe the version
  installed on disk (the `↻ vX` "restart to update" chip). Defaults to
  `packageName`; the TUI passes `'zidane'` for `zidane-tui`.
- `cacheTtlMs?` (`number`): Override the read-through cache lifetime in ms for the in-TUI hook.
  Defaults to `0` — every TUI launch fetches fresh, so the chip
  never lies. Pass a positive value (e.g. `3_600_000` for 1 h) to
  throttle the registry-poll rate at the cost of staleness.

  The on-disk cache is always WRITTEN, regardless of this value, so
  `zidane upgrade` and other tooling read paths benefit immediately.
- `channel?` (`string`): dist-tag. Defaults to `'latest'`.
- `currentVersion` (`string`): Inlined at build time — usually `pkg.version`.
- `packageName` (`string`): Top-level npm package the user installs (`zidane-tui`, `my-app`, …).
- `platformPackagePrefix?` (`string`): Platform-package prefix used by the in-place self-update flow when
  resolving the per-arch tarball name. Defaults to `'<packageName>-'`
  (i.e. `zidane-tui-darwin-arm64` when `packageName === 'zidane-tui'`).
  Override only when your platform packages don't follow the `<name>-<key>`
  convention.
- `registry?` (`string`): Registry base URL. Defaults to `'https://registry.npmjs.org'`.
- `updateWorkerCommand?` (`readonly string[]`): Argv that runs the update worker (`zidane/chat`'s `runUpdateWorkerMain`),
  e.g. `[process.execPath, '__update-worker']`. When set, background installs
  run in that detached worker and survive the TUI quitting. Omit to install
  in this process.

Source: [src/chat/config.ts:243](https://github.com/Tahul/zidane/blob/main/src/chat/config.ts#L243)

### `BuildHintsOptions`

Interface.

Options bag for [`buildHints`](https://zidane.si/docs/reference/chat.md#buildHints). Flat shape so call sites read as a
checklist of "what state do I have to surface" — easy to thread
through React `useMemo` deps and to construct fresh in tests.

```ts
interface BuildHintsOptions
```

**Properties**

- `activeSkillCount` (`number`): Number of currently-active skills. Drives the footer's
  `✦ N skill(s)` chip on the chat screen — purely informational,
  a passive surface for "tool restrictions are in effect". Hidden
  when `0` so the row stays uncluttered for the common case.
- `agentColor` (`string`): Foreground color for the agent label.
- `agentLabel` (`string`): Label of the active agent profile.
- `busy` (`boolean`): True while the agent is running a turn.
- `cacheBreakHint?` (`Hint | null`): Pre-built prompt-cache toast, non-null only for a few seconds after a
  `'cache:break'` hook fired (the host owns the visibility timer and the
  severity split): warn-colored `⚡ cache break in <culprit>: +Nk tok, ~$0.48`
  for attributable breaks, dim `⚡ cache expired: +Nk tok, ~$0.48` for
  byte-stable TTL expiry — routine across human think-time, so it must not
  cry wolf. Sits beside `updateHint` at the droppable end of the row: a
  transient cost signal, not an action.
- `currentSession` (`SessionMeta | null`): Active session, or `null` when none is open.
- `effortColor` (`string`): Foreground color for the effort label.
- `effortKeyColor?` (`string`): Foreground for the `/l` chord that introduces the effort label. Unset, it
  takes the color of the model key it extends, so the pair reads as one binding.
- `effortLabel` (`string | null`): Current reasoning effort label, or `null` when the model has no reasoning knob.
- `hasMultipleAgents` (`boolean`): True when more than one agent profile is available, so the agent switch hint is useful.
- `inFlightToolCount` (`number`): Number of currently-dispatching tool calls. Drives the
  `<cancelToolCall> cancel` chip in the `busy` hint row and the
  `cancel task` chip on the idle chat row. Without
  it, the binding stays inert and there's no point advertising the
  shortcut. Falls back to "no chip" when no tools are in flight
  (model is mid-stream but hasn't dispatched yet).
- `keybindings` (`KeyBindings`): Resolved keybindings — every hint key string flows through this so
  a user-level rebind (e.g. `"openSettings": "ctrl+p"`) shows up live
  in the bottom bar without us hard-coding the default `'ctrl+o'`.
- `modelColor` (`string`): Foreground color for the model label.
- `modelLabel` (`string | null`): Label of the active model, or `null` when none is selected.
- `modelOptionsLabel?` (`string | null`): Comma-joined labels of the enabled model options (e.g. `'fast'`), or `null`
  when none are on. Appended after the effort label in the model chord — when
  the model has no reasoning knob, it rides the chord on its own.
- `pending` (`boolean`): True while a safe-mode approval prompt is pending.
- `pendingInteractionLive` (`boolean`): True when a LIVE pending interaction is on screen (agent is mid-run,
  awaiting the tool's Promise). Same nav hints as the safe-mode
  approval picker — `abortRun` aborts the run.
- `pendingInteractionResumed` (`boolean`): True when the chat screen is showing a resumed (not live) pending
  interaction. The agent isn't running; esc leaves the form without
  persisting a response, so the hint reads "leave for later" — the
  session re-enqueues the same interaction on next activation.
- `screen` (`Screen`): Screen the footer is shown on.
- `sessionsScopeAll?` (`boolean`): Sessions-screen list scope — `true` when the list spans all
  projects. Drives the `tab` hint's label, which advertises the scope
  the toggle switches TO. Optional so non-sessions callers can omit it.
- `sessionsSpaceSwitch?` (`boolean`): Sessions screen: there is another space to switch to. Adds the `cycleSpaceInList` hint (`shift+tab space`; `←→ space` while unbound).
- `skillsChipColor` (`string`): Foreground for the skills chip (typically `COLOR.brand`).
- `todoProgress` (`string | null`): Progress label for the todo chip (`"2/5"` from `todoProgressLabel`), or
  `null` to hide it.

  As a `Hint` it inherits the row's click-to-keypress
  contract for free (`dispatchHintKey`), so clicking it opens the list the
  same way `ctrl+t` does — and it sits below the prompt like every other
  control.
- `uiMode` (`UiMode`): Chat-screen chrome density — `'minimal'` collapses the idle chat
  hint row to just `agent / model / settings`. Busy and pending
  states keep their full hint sets because those
  affordances are load-bearing (abort, navigate, approve).
- `updateHint` (`Hint | null`): Pre-built `↑ vX.Y.Z` chip from `buildUpdateHint(useUpdateCheck())`,
  or `null` when no update is available. Always last in the chat-screen
  row so it's the first to drop when the terminal narrows.

Source: [src/chat/footer-hints.ts:33](https://github.com/Tahul/zidane/blob/main/src/chat/footer-hints.ts#L33)

### `BuildSystemOptions`

Interface.

Options for [`buildBuildSystem`](https://zidane.si/docs/reference/chat.md#buildBuildSystem) and [`buildPlanSystem`](https://zidane.si/docs/reference/chat.md#buildPlanSystem).

```ts
interface BuildSystemOptions
```

**Properties**

- `allowInteraction?` (`boolean`): Whether the agent has access to interactive tools (`ask_user`,
  `present_plan`). Default `true`. When `false`, both interactive
  sections are swapped for their no-prompts variants:

  - [`INTERACTION_GUIDANCE`](https://zidane.si/docs/reference/chat.md#INTERACTION_GUIDANCE) → [`INTERACTION_GUIDANCE_NO_PROMPTS`](https://zidane.si/docs/reference/chat.md#INTERACTION_GUIDANCE_NO_PROMPTS)
  - [`PLAN_MODE_DOCTRINE`](https://zidane.si/docs/reference/chat.md#PLAN_MODE_DOCTRINE) → [`PLAN_MODE_DOCTRINE_NO_PROMPTS`](https://zidane.si/docs/reference/chat.md#PLAN_MODE_DOCTRINE_NO_PROMPTS)

  Must agree with the host's actual tool wiring — telling the model a
  tool exists that the runtime refuses to invoke produces a tool-not-
  found loop on first ambiguity. Built-in TUI honors this via
  `Settings.interactionMode !== 'disabled'`.
- `cwd?` (`string`): Actual working directory. Omitted ⇒ no env section is rendered.
- `date?` (`string`): Override date for tests.
- `platform?` (`string`): Override platform for tests.
- `projectRoot?` (`string`): Project root (typically the git root), when different from `cwd`.
  When the user spawned the agent from a subdirectory of a larger
  project, this enables the env block's two-line disclosure + path-
  resolution note (see [`envSection`](https://zidane.si/docs/reference/chat.md#envSection)). Same-as-`cwd` or
  omitted ⇒ the env block stays compact.
- `systemOverride?` (`string | null`): Contents of a `SYSTEM.md` that REPLACES the built-in doctrine — identity,
  doctrine fragments, and interaction guidance all give way to this text.
  Omitted / empty ⇒ the built-in prompt is used unchanged.

  Deliberately narrower than "replaces the whole prompt". Two things still
  compose around it:

  - `BuildSystemOptions.userInstructions` is appended BELOW it, so
    `AGENTS.md` stays an additive layer on top of whatever doctrine is in
    force — overriding the prompt shouldn't silently disable it.
  - The `<env>` block still lands below the cache boundary. It carries cwd
    - project root, which the file tools' path resolution depends on;
      dropping it would break `read_file` in ways that look like model error.

  `allowInteraction` is NOT consulted when an override is active: the
  built-in prompt swaps in no-prompts variants of its own fragments, but the
  override's text is the user's to write, so we don't rewrite it. Wire the
  tool availability you describe.

  Built by `discoverSystemMd` in `./system-md`.
- `userInstructions?` (`string | null`): Pre-rendered user-instructions block from one or more `AGENTS.md`
  files. Appended verbatim AFTER the built-in doctrine + guidance so
  user-authored preferences sit closest to the conversation. Omitted
  or empty ⇒ nothing is appended.

  Built by [`discoverAgentsMd`](https://zidane.si/docs/reference/chat.md#discoverAgentsMd) in `./agents-md`; the TUI calls
  the discovery at session activation and threads the result here.

Source: [src/chat/agent-prompt.ts:323](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L323)

### `BuildUpdateHintOptions`

Interface.

Display options for the update and restart hints.

```ts
interface BuildUpdateHintOptions
```

**Properties**

- `color` (`string`): Foreground color for the `↑` glyph + version label.
- `glyph?` (`string`): Override the leading glyph. Defaults to `'↑'`.
- `showVersion?` (`boolean`): When true (default), the chip renders the literal version (`↑ v5.6.0`).
  When false, just the glyph + `update` label — useful when the row is
  tight and the version would push higher-priority chips off the right.

Source: [src/chat/auto-update-hook.ts:215](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-hook.ts#L215)

### `BuiltinEnvironmentOptions`

Interface.

Options for [`builtinEnvironmentProviders`](https://zidane.si/docs/reference/chat.md#builtinEnvironmentProviders).

```ts
interface BuiltinEnvironmentOptions
```

**Properties**

- `dataDir` (`string`): The host's user folder, which is the config folder (`~/.zidane`, or `~/.config/zidane` under XDG). Worktrees go here by default.

Source: [src/chat/environments/index.ts:13](https://github.com/Tahul/zidane/blob/main/src/chat/environments/index.ts#L13)

### `CachedMcpToolList`

Interface.

One server's cached entry.

- `tools` — verbatim copy of the raw `tools/list` payload (pre any
  `disabledTools` / `toolFilter` filtering). The settings UI ranges
  over this to render the full server surface; the host applies its
  own deny-list on top via `Settings.disabledMcpTools` (see
  `buildMcpServers` in `mcps-discovery.ts`).
- `updatedAt` — ms-epoch of the bootstrap that populated this entry.
  Useful for "last seen N hours ago" affordances and for cache
  eviction policies a host might layer on later.
- `transport` — informational only; lets the UI distinguish a stdio
  server's tool list from an http server with the same name in
  "stale catalog" warnings.

```ts
interface CachedMcpToolList
```

**Properties**

- `tools` (`McpToolSchema[]`): Tools the server advertised on its last listing.
- `transport` (`'sse' | 'stdio' | 'streamable-http'`): Transport the server was reached over.
- `updatedAt` (`number`): Epoch milliseconds when the list was captured.

Source: [src/chat/mcp-tools-cache.ts:56](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tools-cache.ts#L56)

### `ChatHost`

Interface.

The transport-agnostic chat engine. It owns sessions, runs, approvals and queues, and talks to clients through the `broadcast` of its [`ChatHostEnvironment`](https://zidane.si/docs/reference/chat.md#ChatHostEnvironment). Create one with [`createChatHost`](https://zidane.si/docs/reference/chat.md#createChatHost).

```ts
interface ChatHost
```

**Properties**

- `agentSwitchRebuilds` (`(sessionId: string, agentId: string) => boolean`): Whether switching the session to `agentId` needs `disposeAgentForRebuild`.
  False when the live agent's composition fits the profile: it is the
  profile's own (Build ↔ Plan share one), or the profile's tools are a
  strict subset of its tools (Plan on a Codemode agent). True with no live agent.
- `appendNote` (`(sessionId: string, blocks: readonly CommandOutputBlock[]) => Promise<boolean>`): `ctx.appendNote` outside a run (an extension's timer, a panel action): a
  display-only `command` turn, broadcast live and persisted under the same
  id. A built agent's session takes it (so its later saves keep it);
  otherwise it is appended in the store. False for an unknown session or
  an empty note.
- `appendTurns` (`(sessionId: string, turns: SessionTurn[], options?: { whenIdle?: boolean }) => Promise<void>`): Append turns written outside a run (the `!` shell's output). Through the
  live session when one exists, so its next save keeps them; to the store
  otherwise. Announced with `sessions:changed`. `whenIdle` refuses while a
  run is live or an interrupted run waits to be resumed or discarded
  (a channel's context messages: they must not land inside a run).
- `applyAgentSettings` (`(before: HostSettings, next: HostSettings) => void`): Settings changed: every agent built from a value that changed is rebuilt
  (at run end when busy, no run aborted; `ActiveRun.builtFrom` knows which
  values each build read), and turning terminals off closes the user's.
  Every settings write path calls it with the before/after pair.
- `approvals` (`ApprovalHub`): Safe-mode approval hub, exposed so transports can route decisions.
- `browser` (`BrowserHub`): Dev browser hub: the GUI registers as the host of the pages the `browser_*` tools drive.
- `cacheImpact` (`(sessionId: string, change: CacheImpactChange) => Promise<CacheImpact | null>`): What `change` would cost the session's prompt cache, as this host would
  apply it: an effort note or a profile that fits the agent keeps the cache,
  a model switch or a rebuild re-reads the last prompt. `null` without a
  live agent (never builds one) or for an empty change.
- `cacheReport` (`(sessionId: string, limit?: number) => Promise<CacheReport | null>`): The session's last requests (`limit`, default 20) and what the cache did
  for each, from the live copy when an agent holds the session, else the
  stored one. `null` for an unknown session.
- `cancelPause` (`() => void`): Undo a pause without restarting.
- `cancelRunningWork` (`(sessionId: string, id: string, reason?: string) => Promise<boolean>`): Cancels the running tool call or task `id`, or all of them with `'all'`. Resolves to whether anything was cancelled.
- `commitResume` (`() => Promise<void>`): The update committed: drop the resume records kept for a rollback.
- `compactSessionNow` (`(sessionId: string) => Promise<CompactionReport | null>`): Compact the session now (any session: its agent is built if none is
  live). Resolves what was done, `null` when nothing was (cancelled or
  failed, both also announced as transcript events).
- `completeCommandArgs` (`(cwd: string, name: string, argsPrefix: string, kind?: string) => Promise<CommandArgSuggestionSummary[]>`): Argument suggestions for `/name <prefix>` — best-effort, empty on failure.
- `controlPlan` (`(sessionId: string, planId: string, action: 'stop' | 'drop') => Promise<PlansSnapshot>`): Stop or drop a plan. Stopping or dropping the plan a live run is
  applying also interrupts that run. Rejects when refused.
- `createSession` (`(params: CreateSessionRequest) => Promise<string>`): Create a session in the shared store and return its id. The shared
  lifecycle factory (`session-factory.ts`) bound to this host's store —
  used by the daemon, the embedded GUI host, and orchestration.

  `environment` picks where the session's work happens (default: local).
  A kind that needs provisioning returns immediately with the session in
  `provisioning`; it becomes `ready` / `failed` in the background, each
  transition announced as `sessions:changed`. Throws for an unknown or
  unavailable kind.

  A `spaceId` naming an extension space (`<extension>/<id>`) stamps it
  like a user space's, and its `newSession` defaults fill the agent, the
  model and the effort the request leaves out (`./extension-spaces`).
- `createSystemOverride` (`(sessionId: string) => Promise<{ created: boolean; path: string }>`): Seed a user-scope `SYSTEM.md` from the doctrine currently in effect for
  the session, and return its path for the caller to open in an editor.
  Refuses (throws) when an override could never apply — a host-supplied
  profile, or a `userInstructionsScope` that excludes user files — rather
  than writing a file that would be silently ignored. No-op when the file
  already exists (the path is still returned).
- `deleteSession` (`(sessionId: string) => Promise<{ environment: EnvironmentTeardownResult }>`): THE session delete: retire the live agent + pending HITL state, end its
  execution (background tasks included), tear down the environment it
  owns, then remove the row and every on-disk artifact. Every surface's
  delete must end here (the daemon RPC, the embedded GUI host). Resolves
  with what happened to the environment (a provider may keep it, e.g. a
  dirty worktree).
- `deleteTurn` (`(sessionId: string, turnId: string) => Promise<void>`): History edits. Both refuse while the session runs (in this host, or by
  its fresh run marker in another process): the run's saves would erase
  the edit, and its context would keep the old turn anyway, so the user
  stops the run first. The write is atomic when the store offers
  `mutateHistory`; the cached agent is retired so the next run reads the
  edited history, and `sessions:changed` tells every window.
- `disposeAgentForRebuild` (`(sessionId: string) => void`): Retire the live agent so the next run rebuilds on freshly-patched
  metadata (model / agent swap). Deferred to run end while a run is in
  flight, so a swap never aborts a run mid-stream for other windows.
- `disposeSession` (`(sessionId: string) => void`): Tear down one session's live agent + pending HITL state.
- `drain` (`(options?: { suspendGraceMs?: number }) => Promise<void>`): Permanently refuse dispatch, stop work, and await setup, runs, and
  settlement. Runs are suspended (`Agent.suspend`), so the next start
  resumes them: running tool calls get `suspendGraceMs` (default
  `DRAIN_SUSPEND_GRACE_MS`) to finish, then are cut off.
- `editPlan` (`(sessionId: string, planId: string, op: PlanEditOp) => Promise<PlansSnapshot>`): A user edit to a plan's steps (rename, add, cancel). Through the live
  agent's session, else the store (refused while another process runs the
  session). A live run applying the plan is told at once; otherwise the
  next run start is. Rejects with the gate's reason when refused.
- `editTurnText` (`(sessionId: string, turnId: string, text: string) => Promise<void>`): Replace a turn's text, keeping its non-text blocks (see `replaceTurnTextBlocks`).
- `environments` (`EnvironmentRegistry`): Environment kinds this host can create and run.
- `environmentSecrets` (`EnvironmentSecrets`): Where this host keeps environment secrets (settings writes go through it).
- `extensionSpace` (`(spaceId: string, cwd: string, kind?: string) => Promise<Space | undefined>`): The extension space `spaceId` (`<extension>/<id>`) as the extensions of
  folder `cwd` contribute it, as a read-only `Space` with its
  `newSession` defaults; `undefined` when none does (`./extension-spaces`).
- `facets` (`SessionFacetsHost`): Extension facets (`ctx.sessions.facets`): computed per session on its
  triggers, stored in its header metadata, announced as `sessions:facets`
  (`./session-facets`). The host observes its own signals; a transport
  feeds it the `sessions:changed` its handlers broadcast (`observe`).
- `folderCatalog` (`(cwd: string, kind?: string, prefix?: string) => Promise<{ discoveryCwd: string; project?: MachineProject }>`): What to scan for a folder's catalogs: this host's folder, or for a
  machine's, a folder of this host's for the user scope plus the project
  files read on the machine.
- `followUp` (`(sessionId: string, message: string | RunMessage) => Promise<boolean>`): As `steer`, but the model reads the message when the run would end.
- `forkSession` (`(params: Omit<ForkSessionParams, 'userDir'>) => Promise<ForkSessionResult>`): Fork a session at a turn into a new persisted session (see
  `forkSession` in `session-factory.ts`); blobs are rehomed under this
  host's user dir.
- `generateSessionTitle` (`(sessionId: string, hint?: TextModelHint, guard?: SessionTitleGuard) => Promise<string>`): Title the session with THIS host's providers and credentials (never the
  window's: a server session's transcript stays with the server's
  provider), write it as a rename (no recency bump) and return it. `hint`:
  the window's provider/model, used when this host has that provider;
  otherwise the session's own. Refuses the Main session. `guard`
  (auto-titling): rejects with `SessionRenamedError`, writing nothing,
  when the title was renamed since the caller's snapshot.
- `getContextBreakdown` (`(sessionId: string) => Promise<ContextBreakdown | null>`): Returns how the context window of `sessionId` is used. Builds the agent when needed, and resolves to `null` when that fails.
- `getContextSegments` (`(sessionId: string) => Promise<ContextSegmentsReport | null>`): Every chunk of text in the session's system prompt, with provenance — the
  Instructions tab's data. Sibling of `getContextBreakdown` and built from
  the same `ActiveRun` inputs so the two views describe the same assembly.
  `null` when no agent can be built for the session.
- `getLiveSession` (`(sessionId: string) => Session | null`): The live in-memory `Session` for a session with an active agent, or
  `null`. For IN-PROCESS embedders only — the GUI's `!` shell runner
  appends turns through the live `Session` so `Session.save()` /
  compaction's `replaceTurns` never erase a raw store append the
  in-memory snapshot didn't see. Not wire-exposable.
- `getLiveTurns` (`(sessionId: string) => readonly SessionTurn[] | null`): The live in-memory turns for a session with an active agent — fresh
  even mid-run (the store lags at turn boundaries). `null` when no
  agent is live. Read-only by contract.
- `getPlansSnapshot` (`(sessionId: string) => Promise<PlansSnapshot | null>`): The session's plans: from its live agent, else from the store (with an
  applying plan whose run no process holds marked possibly interrupted).
  `null` for an unknown session.
- `hasActiveRuns` (`() => boolean`): `hasActiveWork` without terminals: what a restart would abort (`zidane server wait-idle`).
- `hasActiveRunsIn` (`(cwd: string, keyOf?: (cwd: string) => string) => boolean`): `hasActiveRuns` for the sessions reading `cwd`'s extensions (their
  `discoveryCwd`). A dispatch whose agent is not built yet, and host-wide
  lifecycle work, count for every folder. `keyOf` maps `cwd` and each
  session's folder before they are compared: the caller's cache key (the
  desktop's `scopeCwd`). Default: compared as given.
- `hasActiveWork` (`() => boolean`): True when ANY session has a live run, in-flight compaction, tracked work, or a running local terminal.
- `interactions` (`InteractionHub`): HITL hubs — exposed so transports can route respond/list/rehydrate calls.
- `interrupt` (`(sessionId: string, reason?: string) => void`): Aborts the live run of `sessionId`, if any. `reason`: why, for a tool the abort cuts short to report (default: "The operation was aborted.").
- `isSessionRunning` (`(sessionId: string) => boolean`): Whether `sessionId` has a run in flight.
- `keptSessions` (`() => string[]`): The sessions the finished pause kept for the next start.
- `listBlockRuns` (`(sessionId: string) => Promise<Record<string, BlockRun>>`): The session's saved block runs, by key.
- `listCommands` (`(cwd: string, kind?: string) => Promise<CommandSummary[]>`): Wire-safe `/` command catalog for a client's completion popover. `kind`: the session's environment (a machine's folder).
- `listEnvironmentActions` (`(sessionId: string) => Promise<EnvironmentActionSummary[]>`): The per-session commands the session's environment offers right now (push, open PR, …).
- `listEnvironments` (`(cwd: string, kind?: string, options?: { machineOnly?: boolean }) => Promise<EnvironmentDescriptor[]>`): Every registered kind described for a new session in `cwd` — enabled,
  default, availability (with the reason), branch suggestion, options
  (secrets only as "configured"). What pickers and settings render.
- `listLiveSessions` (`() => { running: boolean; sessionId: string }[]`): Sessions with a live in-memory agent, with their run state (presence).
- `listRunningWork` (`(sessionId: string, options?: { children?: boolean }) => RunningWorkItem[]`): Returns the cancellable tool calls and background tasks of `sessionId`.
  Empty when it has no live agent. `children` adds the subagents' running
  calls (`childId` set): not cancellable here, read for their starts.
- `machines` (`MachineRegistry`): Machines lending this host their execution (`docs/MACHINES.md`).
- `mcpServers` (`HostMcpServers`): The MCP server processes its sessions share (`src/mcp/server-pool.ts`): list, restart, retire.
- `moveSessionEnvironment` (`(sessionId: string, request: EnvironmentRequest, cwd?: string, options?: { removeOldWorktree?: boolean }) => Promise<{ oldWorktree?: EnvironmentTeardownResult }>`): Move an idle session to another environment, of any kind (`local`,
  `worktree`, `local@<machine>`, …) and in `cwd` (a folder on the target,
  required for a machine's kinds; else the session's folder). Stops its
  terminals and background tasks first, stamps the environment like
  `createSession` would, records the move for the model's note
  (`chat/session-move.ts`), and keeps the history. Resolves once the row
  shows the new environment. Throws when the session is running, the kind
  is disabled or unavailable, its machine is offline, or the folder is
  outside the target's roots. Nothing is copied, and the old folder is left
  as it was. A run that arrives during the move is refused, a queued one
  waits for the new place: the move holds the session's dispatch slot.

  `removeOldWorktree`: when the session leaves a worktree kind (`worktree`,
  `worktree@<machine>`), remove that worktree after the switch, through its
  kind's teardown (clean worktrees only, the branch stays). A move that keeps
  the worktree as its folder goes to its main checkout instead. The removal
  never undoes the move: `oldWorktree` says whether it went, and why not.
- `orchestration` (`Orchestration`): Main-session orchestration — see `./orchestration/`.
- `patchLiveSessionMeta` (`(sessionId: string, patch: Record<string, unknown>, opts?: { bumpUpdatedAt?: boolean }) => Promise<boolean>`): Apply a metadata-only patch through the live in-memory `Session` when
  one is hosting the session (safe during a run). Returns `false` when no
  live session exists — the caller should fall back to a store-level
  write. See the implementation for the data-loss rationale.
- `patchSessionMeta` (`(sessionId: string, patch: Record<string, unknown>, opts?: { bumpUpdatedAt?: boolean }) => Promise<void>`): Metadata patch that is ALWAYS safe: live-session path when an agent
  hosts the session (`patchLiveSessionMeta`), store-level
  `load → mutate → save` otherwise. Every surface (daemon RPC, embedded
  GUI host) must write agent-affecting metadata through this — a direct
  store write under a live session is reverted by that session's next
  full-document save. `undefined` values DELETE the key.
- `pauseForRestart` (`(options?: { force?: boolean; pollMs?: number }) => Promise<{ paused: string[]; queued: string[] }>`): Pause every run at its next turn boundary for a restart, keeping what is in flight (`./restart`).
- `pushQueued` (`(sessionId: string, message: string, activatedSkills?: string[], fields?: Pick<SendOptions, 'metadata' | 'origin' | 'literal' | 'clientMessageId'>) => Promise<{ delivered: 'command' | 'send' | 'steer' | 'queued' }>`): Steer when a run is active, start a fresh run otherwise. `fields`: see `SendOptions.origin` / `SendOptions.literal` (a steer carries neither).
- `queueAdd` (`(sessionId: string, text: string, activatedSkills?: string[], attachments?: PromptAttachment[], asCommand?: string, fields?: Pick<SendOptions, 'metadata' | 'origin' | 'literal' | 'clientMessageId'>, delivery?: QueueDelivery) => Promise<{ delivered: 'steer' | 'sent' | 'queued' }>`): Shared type-ahead queue — host-owned so every window renders and
  manipulates the same list (`queue:changed` fan-out). `queueAdd`
  queues while a run is in flight — or while this host is still
  provisioning the session's environment — and sends immediately
  otherwise; the host drains FIFO at run end and when provisioning settles.
- `queueAttachments` (`(sessionId: string, id: string) => PromptAttachment[] | null`): A queued entry's attachments WITH their bytes, which `queueList` and
  `queue:changed` leave out, for a window that previews or edits them.
  `null` when the id is gone.
- `queueList` (`(sessionId: string) => QueuedItem[]`): Returns the queued entries of `sessionId`, in send order.
- `queuePushNow` (`(sessionId: string, id: string) => Promise<{ delivered: 'send' | 'steer' | 'queued' | 'missing' }>`): Sends queued entry `id` now instead of waiting for the run to end. `delivered` says what happened to it, or `missing` when the id is gone.
- `queueRemove` (`(sessionId: string, id: string) => boolean`): Removes queued entry `id`. Returns `false` when it is gone.
- `queueUpdate` (`(sessionId: string, id: string, text: string, attachments?: PromptAttachment[]) => boolean`): Edit a queued entry in place (keeps its position). `attachments`, when
  given, replaces the entry's list (`[]` removes them all). `false` when
  the id is gone.
- `rebuildAllAgents` (`(options?: { rediscover?: boolean }) => Promise<void>`): `disposeAgentForRebuild` for every session with a live agent (a runtime
  reload, an extension toggled or installed, the browser or a terminal
  viewer coming or going; other settings rebuild only the agents whose
  build read them: `applyAgentSettings`). Resolves once every one of them has
  actually been retired: immediately for idle sessions, at run end for
  busy ones, so no run is aborted. `rediscover` also drops the discovery
  caches (plugin catalogs), so the rebuilt agents re-walk the disk.
- `removeBlockRun` (`(sessionId: string, key: string) => Promise<void>`): Forgets a block's saved run.
- `resolveFolder` (`FolderResolver`): Whose folder `cwd` is (`docs/MACHINES.md` § Folders): a machine kind's
  (`kind`, or the machine whose roots hold it when this host refuses it),
  checked against that machine's roots; else this host's, checked by its
  confinement. Throws when neither takes it.
- `resumeAfterRestart` (`(sessionIds?: readonly string[]) => Promise<string[]>`): On start: continue what the previous process paused.
- `resumePending` (`(options?: { sessionId?: string }) => Promise<void>`): Resume the checkpoint runs a dead process left (durable runs), of
  `sessionId` or of every session, as each session's `durability`
  setting says: at once (`on`), or after asking (`resume-ask`, a host
  prompt). Resolves once each is resumed or asked about. A host serving
  every window calls it at start; every host when a window opens a session.
- `retryEnvironment` (`(sessionId: string, opts?: { background?: boolean }) => Promise<void>`): Provision the session's environment again — after a failure, or when a
  host restart left it `provisioning` with nothing running. No-op while
  this host is already provisioning it or when it is ready. With
  `background`, resolves once the row shows `provisioning` instead of when
  the attempt ends — so a caller polling the row never reads the old failure.
- `rewindSession` (`(sessionId: string, turnId: string, options: { inclusive: boolean }) => Promise<void>`): Cut the history at a turn, in place (see `truncateTurnsAt`): `inclusive`
  keeps the turn, otherwise the history ends just before it. Runs no
  remaining turn references are dropped. Same guard and broadcast as the
  other history edits.
- `runBlock` (`(sessionId: string, request: BlockRunRequest, options?: RunBlockOptions) => Promise<BlockRun>`): Runs a shell block of a reply (`Run here`) in the session's folder, with no input, and keeps it as the block's
  last run in the session's metadata; nothing enters the conversation. See `block-runs.ts`.
- `runEnvironmentAction` (`(sessionId: string, actionId: string) => Promise<EnvironmentActionResult>`): Run one of `listEnvironmentActions` — in the session's environment,
  resuming it when suspended. Throws with a user-facing message on failure.
- `schedules` (`Schedules`): Prompts and extension handlers that run at a set time — see `./schedules/` and docs/SCHEDULES.md.
- `secrets` (`HostSecrets`): The secrets of a session's project (`Settings.secrets`): list names, add, revoke. Values never come back.
- `sendPrompt` (`(opts: SendOptions) => Promise<{ runId: string }>`): Starts a run for the prompt in `opts.sessionId`. Resolves with the run id once the run is started.
- `sessionSystemMd` (`(sessionId: string) => { file: SystemMdFile | null; shadowed: readonly SystemMdFile[] } | null`): The `SYSTEM.md` override composing this session's prompt, for windows
  attaching to an ALREADY-WARM agent (the `system-md` signal only fires at
  agent build). `null` file = none applied; `null` result = no live agent,
  in which case the next build will broadcast.
- `setLaunchOverrides` (`(sessionId: string, overrides: SessionLaunchOverrides | null) => void`): Launch-time overrides for one session's agent (`zidane run --system / --mcp` on a host-backed window): what the local TUI applies to every
  agent it builds, applied here to this session's. In memory only, never
  persisted; they survive agent rebuilds and are dropped on `null`, on
  session delete, or (daemon) when the client that set them disconnects.
  A change retires the live agent like a model swap (deferred to run end),
  except a cwd-only change, which moves the live agent in place.
- `setLiveSessionAgentId` (`(sessionId: string, agentId: string) => Promise<boolean>`): Live-session-aware agent re-label: patches the LIVE document when the
  host holds one (saving the live doc is safe — it carries the run's
  appended turns; a stale-copy save would clobber them and a later live
  save would silently revert the label) and persists it. Returns false
  when no live session exists — callers fall back to a store patch,
  which is safe because idle sessions have no concurrent writer.
- `setSessionArchived` (`(sessionId: string, archived: boolean) => Promise<void>`): Archive or restore a session (`metadata.archived`). Archiving then lets
  its environment's `archive` remove what it owns when nothing would be
  lost (a clean worktree), in the background; restoring provisions it
  again. Every surface's archive must end here.
- `setSessionFolder` (`(sessionId: string, cwd: string) => Promise<void>`): Bind a session to a folder (`cwd`), or back to a plain chat (`''`):
  `metadata.cwd` plus the derived project root. Refused while running,
  like history edits; the Main session can't be bound.
- `setSessionModel` (`(sessionId: string, pick: SessionModelPick) => Promise<void>`): THE model-swap contract, shared by every surface: persists
  `providerKey` / `modelId`, replaces `modelChain` (cleared when
  `fallbacks` is empty/omitted), resets `effort` and `modelOptions` to
  the supplied values (cleared when omitted — a knob valid on the old
  model must not leak onto the new one), then retires the live agent so
  the next run rebuilds on the new model (run-end deferred mid-run).
- `shutdown` (`() => void`): Drain every session — call before process exit or config invalidation.
- `steer` (`(sessionId: string, message: string | RunMessage) => Promise<boolean>`): Hand a message to the live run: the model reads it after the current
  step. It may carry attachments, skills and the queued fields (`RunMessage`).
  `false` when no run took it (it was dropped, not queued).
- `terminals` (`TerminalManager`): The sessions' terminals (run in their environments; shown by the GUI, read by the agent).
- `transformPrompt` (`SendTransform`): Runs the extensions' `prompt` interceptors of the session's folder on a
  submitted prompt (`./prompt-transforms`). The submit entry points a
  client calls run it once per submission (`createPromptSubmissions`);
  the host's own send and queue methods never do.
- `verifyEnvironment` (`(kind: string) => Promise<EnvironmentVerifyResult>`): "Test connection" for `kind` with its current options.
- `verifyFolder` (`(cwd: string, kind?: string) => Promise<ResolvedFolder>`): `resolveFolder`, then a machine folder checked on the machine (real path, exists).
- `workspace` (`WorkspaceConfinement`): Where sessions may work: a server's workspace roots (`confined`), or
  unconfined on a local host. The host checks it itself at every entry
  point taking a folder or a path; embedders read it for listings.
- `workspaceCopy` (`(sessionId: string, from: string, toDir: string) => Promise<EntryResult>`): Copies `from` into folder `toDir` (`''`: the root) under a free name. See `workspaceCreate`.
- `workspaceCreate` (`(sessionId: string, path: string, kind: EntryKind, options?: EntryCreateOptions) => Promise<EntryResult>`): Entry actions in the session's workspace, wherever its environment runs
  (`createEntryOps`, `chat/workspace/entries.ts`): paths relative to the
  workspace root, never `..` or absolute, nothing overwritten. A refusal
  resolves (`ok: false`); it does not throw.
- `workspaceEmptyDirs` (`(sessionId: string) => Promise<string[]>`): The workspace's empty folders, relative to its root (what a file listing leaves out), capped.
- `workspaceExec` (`(sessionId: string, command: string, options?: WorkspaceExecOptions) => Promise<ExecResult>`): The session's workspace: shell + file operations in the session's own
  execution context (the one its agent's tools use), for any session.
  The root is `environment.execCwd` when the files live inside the
  environment (a sandbox's clone), else the session's cwd on this machine.
  What the GUI's Files panel, mentions, `!` and diffs use for sandboxed
  sessions, and what window-side extension code reaches as
  `ctx.workspace` in the TUI's daemon mode.

  Paths (and exec `cwd`) resolve against the root exactly as the execution
  context resolves them ([`workspacePath`](https://zidane.si/docs/reference/chat.md#workspacePath)); containment is the
  context's own (sandboxes confine, the local process context doesn't).
  Exec `timeoutSeconds` defaults to 30, like the execution contexts.
- `workspaceExecStream` (`(sessionId: string, command: string, options?: Omit<WorkspaceExecOptions, 'timeoutSeconds'>) => Promise<StreamHandle | null>`): The same command as a live process (`ExecutionContext.execStream`, a
  machine's included): its output as it comes, `kill()` ends its process
  group. `null` when the session's context can't stream (callers fall back
  to `workspaceExec`). Same `cwd` rules; no timeout: the caller owns it.
- `workspaceFileSize` (`(sessionId: string, path: string) => Promise<number | null>`): Byte size of a file, or `null` when missing or unknowable.
- `workspaceListFiles` (`(sessionId: string, path: string, options?: WorkspaceListFilesOptions) => Promise<ListFilesEntry[]>`): `ListFilesOptions` semantics (`glob: true` treats `path` as a pattern under the root).
- `workspaceMove` (`(sessionId: string, from: string, to: string) => Promise<EntryResult>`): Moves (renames) `from` to `to` in the session's workspace. See `workspaceCreate`.
- `workspaceReadFile` (`(sessionId: string, path: string) => Promise<string>`): Reads a file from the session's workspace, wherever its environment runs. `path` is resolved against the workspace root.
- `workspaceRemove` (`(sessionId: string, path: string) => Promise<EntryResult>`): Removes `path` (a folder whole) for good. See `workspaceCreate`.
- `workspaceWriteFile` (`(sessionId: string, path: string, content: string) => Promise<void>`): Writes a file in the session's workspace, wherever its environment runs. `path` is resolved against the workspace root.

Source: [src/chat/host/host-api.ts:94](https://github.com/Tahul/zidane/blob/main/src/chat/host/host-api.ts#L94)

### `ChatHostEnvironment`

Interface.

What an embedder supplies to [`createChatHost`](https://zidane.si/docs/reference/chat.md#createChatHost): how to reach clients, the session store, settings and runtime configuration.

```ts
interface ChatHostEnvironment
```

**Properties**

- `applyAuthEnv?` (`() => void`): Re-apply provider auth env right before building a provider.
- `approvalsPersistence?` (`PendingApprovalsStore`): Durable pending-approval persistence. Defaults to in-memory only.
- `broadcast` (`(signal: HostSignal) => void`): Push a signal to every connected client. Must never throw.
- `cacheDir?` (`() => string`): Root of the per-session artifact tree (`<dir>/sessions/<id>/`): persisted
  tool outputs, background-task logs, MCP warning logs. Forks re-home and
  deletes clean there. Defaults to `userDataDir`.
- `debugStream?` (`boolean`): Opt-in verbose stream logging to the host console.
- `environmentIdleSweepMs?` (`number`): How often idle environments are checked against their kind's `idleSuspendMinutes`. Default 60 s (tests shorten it).
- `environments?` (`EnvironmentRegistry`): Session environment providers (`chat/environment.ts`). Defaults to the
  built-ins (`local`). Hosts add kinds here (worktree, docker, sandboxes).
- `environmentSecrets?` (`() => EnvironmentSecrets`): Environment secret store. Defaults to `<userDataDir>/environment-credentials.json`.
- `hostId?` (`string`): The id this host reports as `HelloResult.hostId`: stamped on `ctx.sessions.list` rows and facet snapshots (`hostId`). Absent: left out.
- `hostRuntime?` (`ExtensionHostRuntimeBridge`): The extension host-runtime bridge the host passed to its registry
  builds (`discoverAndBuildExtensionRegistry({ hostRuntime })`). The
  engine scopes a per-session impl onto it for the duration of each `/`
  command, so `ctx.workspace` / `ctx.runtime` / `ctx.sessions` resolve to
  the invoking session instead of reading as detached. Absent → those
  surfaces stay detached (extensions get the documented guard error).
- `instrumentAgent?` (`(agent: Agent, context: { sessionId: string }) => void`): Called with every agent the host builds (rebuilds included), before its
  first run. The embedder's own instrumentation, such as lifecycle logging
  into the TUI's Logs tab for a host in the TUI's process.
- `linkHostId?` (`string`): The host id session links name (`zidane://session/<id>?host=<hostId>`): set on a server, absent on This computer, whose links carry no host.
- `logger?` (`Logger`): The host's own log, for what belongs to no session: a shared MCP
  server's output while several sessions (or none) call it. Dropped when
  unset.
- `machineHost?` (`{ principalLive?: (principal: string) => boolean }`): Set by a zidane-server: it takes machines whatever its `remoteHosts`
  setting (a server is remote by nature). Unset (local hosts): machines
  exist only while remote hosts are on; off, `machines.json` is never read
  and no machine kind resolves. `principalLive` answers whether a machine
  token is still valid: a machine id is bound to the token that first
  registered it until that token is revoked (`docs/MACHINES.md` § Security model).
- `mcpCredentialStore?` (`() => McpCredentialStore`): OAuth credential store for MCP servers, when the host supports MCP auth.
- `multiProcess?` (`boolean`): Force the cross-process run guard + marker heartbeat ON regardless of
  the `useDaemon` setting. The daemon sets this: `zidane attach` /
  `zidane daemon` are explicit opt-ins that run sessions with the
  setting off, making the daemon a multi-writer surface by
  construction. Embedders with a single writer (the GUI's in-process
  host) leave it unset and inherit the setting-gated behavior.
- `peekRuntimeConfig?` (`(cwd: string) => Promise<ResolvedConfig> | undefined`): The config `runtimeConfig(cwd)` already built or is building, without
  building one. Facet work (`./session-facets`) computes only with a
  loaded runtime. Absent: facet work calls `runtimeConfig`, which suits an
  embedder whose `runtimeConfig` never builds per folder (one config).
- `readSettings` (`() => HostSettings`): Current user settings — read fresh on every call.
- `runtimeConfig` (`(cwd: string) => Promise<ResolvedConfig>`): cwd-aware resolved runtime config (providers, agents, extensions).
  Hosts own caching/invalidation — the engine calls this per agent build.
- `stateDir?` (`() => string`): Where the host keeps runtime records that must outlive a crash: the pids
  of the MCP servers it shares, ended on its next start when it died
  without closing them. Defaults to `userDataDir`.
- `store` (`SessionStore`): Canonical session store shared with every other surface.
- `terminals?` (`boolean`): Whether this host has a surface that can show terminals (the GUI).
  `false` (a host in the TUI's own process) turns the terminal feature off
  for its agents: no terminal tools, background tasks on plain pipes.
  Default `true`; the `terminal*` settings still apply on top.
- `userDataDir` (`() => string`): The host's user folder, which is the config folder (`~/.zidane`, or `~/.config/zidane` under XDG). It holds the safelist and the tool-result blobs.
- `workspaceRoots?` (`readonly string[]`): Confine the host to these folders (a zidane-server's `workspaceRoots`;
  they must exist). The host checks them at every entry point that takes
  a folder or a path (`ChatHost.workspace`, `../workspace-confinement`):
  session creation (the wire, orchestration workers), folder changes,
  launch-override `cwd`s (dropped), command / environment scans, and the
  workspace file and exec methods, which also never reach the data dir
  (`userDataDir`). A session without a folder works in the first root.
  Unset (local hosts): unconfined.

Source: [src/chat/host/environment.ts:41](https://github.com/Tahul/zidane/blob/main/src/chat/host/environment.ts#L41)

### `ChatOptions`

Interface.

Options accepted by `resolveConfig()` (and, transitively, by `runTui()` and
any future GUI launcher). Every field is optional — defaults boot a working
chat against the built-in providers with sessions stored under `~/.zidane/`.

```ts
interface ChatOptions
```

**Properties**

- `agents?` (`Readonly<Record<string, AgentProfile>>`): Agent profile registry — keyed by `profile.id`. Each profile bundles a
  preset with display metadata (label, description, accent). The TUI
  offers a picker and the `cycleAgent` keybinding (shift+tab by default)
  when more than one profile is registered.

  - Omitted (and `preset` omitted) → [`BUILTIN_AGENTS`](https://zidane.si/docs/reference/chat.md#BUILTIN_AGENTS) (Build, Codemode and Plan).
  - Omitted but `preset` provided → single-entry registry around the preset.
  - Provided → host owns the registry; pass exactly what you want. Profiles
    contributed by extensions are added under it (the host wins on id collision).

  Hosts mixing built-ins with their own typically spread:
  `agents: { ...BUILTIN_AGENTS, debug: myDebugProfile }`.
- `autoUpdate?` (`AutoUpdateConfig | null`): Auto-update wiring. When set, the App quietly checks the npm registry
  for a newer release of `packageName` (matching the host's package),
  writes the result to the cache under `paths.cacheDir` (see
  `AutoUpdateConfig.cacheTtlMs` for how long reads trust it), and
  surfaces a passive `↑ vX.Y.Z` chip in the footer when one is available.

  Consumers wrapping `runTui` for their own CLI inline their own
  `package.json` version at build time and pass it here. The bundled
  `zidane-tui` binary wires this automatically in `tui/src/cli.ts`.

  Defaults to `null` — no auto-update plumbing, no network calls,
  no chip.
- `cwd?` (`string`): Override for the project root. Defaults to `process.cwd()`. Hosts
  embedding the TUI from a non-CLI context (tests, daemons) can pin
  the cwd here so the git-root walk produces a stable result. Has no
  effect when `projectDb` resolves to `false`.
- `defaultAgent?` (`string`): Id of the profile every fresh session (and the initial launch) starts
  in. Resolved against `agents`; unknown ids fall through to
  `DEFAULT_AGENT_ID`, then the registry's first key. Sessions remember
  the profile they were created with; this is never overridden by a
  previously picked profile.
- `extensionRegistry?` (`ExtensionRegistry`): Pre-built [`ExtensionRegistry`](https://zidane.si/docs/reference/extensions.md#ExtensionRegistry). The `resolveConfig` surface is
  synchronous; extension `setup()` can be async, so the host must
  build the registry ahead of time and pass it here verbatim.

  Typical wiring (matches `runTui`):

  ```ts
  import {
    buildExtensionRegistry,
    discoverExtensions,
    inlineLoadedExtension,
    resolveConfig,
  } from 'zidane'

  const registry = await buildExtensionRegistry([
    // Project-scoped extensions run arbitrary local code — they stay
    // off unless the host establishes trust explicitly (or the user
    // sets ZIDANE_TRUST_PROJECT_EXTENSIONS=1).
    ...await discoverExtensions({ cwd, prefix, trustProjectExtensions: hostTrustsProject }),
    ...inlineDefinitions.map(inlineLoadedExtension),
  ])
  const config = resolveConfig({ ...opts, extensionRegistry: registry })
  ```

  Omitted → empty registry (no contributions).
- `extensionScanPaths?` (`readonly string[]`): Extra extension scan roots the host used (or wants runtime re-discovery
  to use). Each path is a root containing extension dirs/packages:
  `<root>/<name>/index.ts`. `runTui` wires this from
  `ZIDANE_EXTENSION_DIRS` when omitted.
- `launch?` (`StartLocalOptions`): Optional one-shot launch request used by CLI shells such as `zidane run`.
  The TUI opens/creates the requested session and submits the prompt after
  the first provider/session activation, while keeping the normal renderer
  and persistence behavior.
- `pluginScanPaths?` (`readonly string[]`): Extra Agent Plugin scan roots. Each path is a root containing plugin
  directories: `<root>/<name>/plugin.json`. `runTui` wires this from
  `ZIDANE_PLUGIN_DIRS` when omitted.

  Appended after the four default roots, so a project or user plugin still
  wins a name collision against a dev root.
- `prefix?` (`string`): Directory name used under `storageDir` for sessions + state.
  Default: `'.zidane'` → `~/.zidane/sessions.db` + `~/.zidane/state.json`.
  Pass e.g. `'.myapp'` for `~/.myapp/...`, or `'myapp'` for a visible dir.
- `preset?` (`Preset`): Legacy single-agent preset (tools, system prompt, behavior). When set
  **without** `ChatOptions.agents`, the resolver wraps this preset
  into a one-entry [`AgentRegistry`](https://zidane.si/docs/reference/chat.md#AgentRegistry) keyed `default` so existing
  callers keep working and the picker is effectively hidden.

  Prefer `agents` for any multi-profile setup. Passing both throws.
- `projectDb?` (`boolean`): Project-scoped session storage. Off by default; when it resolves
  to `true` and the launch happens inside a git repo, zidane
  auto-creates `<git-root>/.{prefix}/` and stores `sessions.db` +
  `state.json` there — so sessions, mcps.json, and skills all live
  alongside the project they belong to. Credentials and the
  cross-project safelist stay at `~/.{prefix}/` regardless.

  Precedence (most → least specific): `options.projectDb` >
  `ZIDANE_PROJECT_DB` env var (`0`/`false`/`off`/`no` → off,
  `1`/`true`/`on`/`yes` → on) > `~/.{prefix}/config.json`
  `projectDb` > `false`. Note the host option beats the env var —
  setting `ZIDANE_PROJECT_DB` has no effect when the host passes an
  explicit `projectDb`.

  When it resolves to `false`, OR when not inside a git repo,
  everything lives at `~/.{prefix}/` — the classic single-store
  layout.
- `providers?` (`Readonly<Record<string, ProviderDescriptor>>`): Provider registry. Each value is a [`ProviderDescriptor`](https://zidane.si/docs/reference/chat.md#ProviderDescriptor) carrying
  label + factory + credential metadata. **No automatic merge** — hosts
  pass exactly what they want.

  - Omitted → [`BUILTIN_PROVIDERS`](https://zidane.si/docs/reference/chat.md#BUILTIN_PROVIDERS) (anthropic, openai, openrouter, cerebras and the other built-ins).
  - `providers: BUILTIN_PROVIDERS` → same as omitted, but explicit.
  - `providers: { anthropic: anthropicDescriptor }` → only Anthropic.
  - `providers: { ...BUILTIN_PROVIDERS, mine: customDescriptor }` → built-ins + custom.
  - `providers: { mine: customDescriptor }` → custom-only, **no built-ins**.
- `storageDir?` (`string`): Storage root. Defaults to the user's home directory. Combined with
  `prefix` to form the data directory.
- `store` (`SessionStore | ((paths: ResolvedPaths) => SessionStore)`): Session store — either an instance or a factory called with the
  resolved storage paths. **Required** at the chat layer so this module
  stays platform-agnostic (no `bun:sqlite` baked in). Terminal hosts use
  `createTuiStore` from `zidane/session/sqlite`; a future GUI host can
  supply a different adapter (IndexedDB, remote API, in-memory) without
  touching this layer.

  ```ts
  import { createTuiStore } from 'zidane/session/sqlite'

  // Either pass a factory…
  resolveConfig({ store: paths => createTuiStore(paths.db) })

  // …or an instance built ahead of time.
  const store = createTuiStore('/path/to/sessions.db')
  resolveConfig({ store })
  ```
- `trustProjectExtensions?` (`boolean`): Whether project-scoped extensions are trusted to run their (arbitrary,
  local) code. Off by default unless the embedding host has established
  project trust; falls back to `ZIDANE_TRUST_PROJECT_EXTENSIONS=1`.

  Surfaced on `ResolvedConfig.trustProjectExtensions` so a host
  that re-discovers extensions at runtime (e.g. the TUI on a working-
  directory change) reuses the same trust decision as boot.

Source: [src/chat/config.ts:54](https://github.com/Tahul/zidane/blob/main/src/chat/config.ts#L54)

### `ChatTurnsReadToolOptions`

Interface.

Options for [`createChatTurnsReadTool`](https://zidane.si/docs/reference/chat.md#createChatTurnsReadTool).

```ts
interface ChatTurnsReadToolOptions
```

**Properties**

- `getAllowedTokens` (`() => readonly string[]`): Returns the `#<chat>` tokens the user referenced in the current prompt. The tool reads only chats that match one of them.
- `store` (`SessionStore`): Session store to read chats from.

Source: [src/tools/read-chat-turns.ts:17](https://github.com/Tahul/zidane/blob/main/src/tools/read-chat-turns.ts#L17)

### `CheckForUpdateOptions`

Interface.

Options for [`checkForUpdate`](https://zidane.si/docs/reference/chat.md#checkForUpdate).

```ts
interface CheckForUpdateOptions
```

**Properties**

- `cacheDir?` (`string`): Directory used for the on-disk TTL cache. Skipped when omitted.
- `cacheTtlMs?` (`number`): Cache lifetime. Defaults to 24 h. Zero or negative never counts the cache
  as fresh, so every check hits the registry. The cache is still written
  and still serves as the fallback when the registry fails.
- `channel?` (`string`): dist-tag to read. Defaults to `'latest'`. Set to `'next'` (or any
  custom tag) for prerelease channels — the comparison still goes through
  `compareSemver`, so `5.6.0-beta.2 > 5.5.0` evaluates correctly.
- `currentVersion` (`string`): Current installed version. Inlined at build time from the consumer's `package.json`.
- `fetcher?` (`typeof fetch`): Inject for tests. Defaults to `globalThis.fetch`.
- `force?` (`boolean`): When `true`, ignore env-var opt-outs (`CI`, `NO_UPDATE_NOTIFIER`,
  `ZIDANE_NO_UPDATE`) AND bypass the on-disk TTL cache (always hit
  the registry). Used by the explicit `zidane upgrade` path where the
  user typed the command — they expect fresh data, not a 24-hour-old
  cached value. The cache is still WRITTEN with the fresh result so
  subsequent background checks benefit; cache reads only feed the
  etag into the next request so the registry can short-circuit with
  304 when nothing changed.
- `now?` (`() => number`): Inject for tests. Defaults to `Date.now`.
- `packageName` (`string`): Full npm package name to query. e.g. `'zidane-tui'`.
- `registry?` (`string`): Registry base URL. Defaults to `https://registry.npmjs.org`. Strip the
  trailing slash; the helper appends `/${packageName}/${channel}`.
- `signal?` (`AbortSignal`): Abort signal — wins over the internal timeout.
- `timeoutMs?` (`number`): Network timeout. Defaults to 3 000 ms.

Source: [src/chat/auto-update.ts:44](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L44)

### `ChipColor`

Interface.

Background + foreground pair for a completion-reference chip pill.

```ts
interface ChipColor
```

**Properties**

- `bg` (`string`): Background paint — the pill itself. High-saturation by convention.
- `fg` (`string`): Foreground paint — the chip text. Pre-paired with `ChipColor.bg` for legibility.

Source: [src/chat/theme.ts:87](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L87)

### `ClaudeCopyResult`

Interface.

Result of copying one category of Claude Code definitions into zidane.

```ts
interface ClaudeCopyResult
```

**Properties**

- `copied` (`string[]`): Names that were written.
- `duplicates?` (`string[]`): Names that appeared in MORE THAN ONE Claude source (e.g. `.mcp.json`
  AND `~/.claude.json`) — the first occurrence (highest-precedence
  source, matching Claude's own project → local → user order) was
  processed, later ones dropped. Surfaced so a differently-configured
  duplicate doesn't vanish from the accounting. MCP import only.
- `failed` (`{ error: string; name: string }[]`): Names that could not be written, with the reason.
- `skipped` (`string[]`): Names already present natively — never overwritten.

Source: [src/chat/claude-import.ts:556](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L556)

### `ClaudeImportActiveHost`

Interface.

The host a window is using, as far as the import note cares.

```ts
interface ClaudeImportActiveHost
```

**Properties**

- `kind` (`'server' | 'daemon' | 'in-process'`): Where the host runs: in this process, a local daemon or a remote server.
- `name` (`string`): Display name of the host.

Source: [src/chat/claude-import.ts:851](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L851)

### `ClaudeImportOptions`

Interface.

Options for locating and importing Claude Code data.

```ts
interface ClaudeImportOptions
```

**Properties**

- `allProjects?` (`boolean`): Import sessions from every Claude project dir, not just the current project's.
- `cwd?` (`string`): Discovery cwd. Default: `process.cwd()`.
- `home?` (`string`): User home directory. Default: `os.homedir()`.
- `prefix?` (`string`): Storage prefix for write targets. Default: `'.zidane'`.

Source: [src/chat/claude-import.ts:46](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L46)

### `ClaudeImportSummary`

Interface.

What [`importFromClaudeCode`](https://zidane.si/docs/reference/chat.md#importFromClaudeCode) imported. A category is absent when it was switched off.

```ts
interface ClaudeImportSummary
```

**Properties**

- `agents?` (`ClaudeCopyResult`): Subagent import result.
- `commands?` (`ClaudeCopyResult`): Command import result.
- `mcps?` (`ClaudeCopyResult`): MCP server import result.
- `notes` (`string[]`): What is deliberately NOT imported, spelled out for the UI.
- `sessions?` (`ClaudeSessionImportResult`): Session import result.
- `skills?` (`ClaudeCopyResult`): Skill import result.
- `target` (`'this-computer'`): Where the import landed: always this computer (its session store and
  its `.zidane` files). Claude Code's data is read from this machine, so
  the import never targets a remote host.

Source: [src/chat/claude-import.ts:829](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L829)

### `ClaudeSessionImportResult`

Interface.

Result of importing Claude Code session transcripts.

```ts
interface ClaudeSessionImportResult
```

**Properties**

- `empty` (`number`): Nothing conversational in the file (hook noise, queue bookkeeping only).
- `failed` (`{ error: string; path: string }[]`): Files that failed to parse/convert, with the reason.
- `imported` (`number`): Sessions written to the store.
- `skipped` (`number`): Already in the store (same Claude session UUID) — idempotent re-run.

Source: [src/chat/claude-import.ts:507](https://github.com/Tahul/zidane/blob/main/src/chat/claude-import.ts#L507)

### `CloneRun`

Interface.

A clone in flight.

```ts
interface CloneRun
```

**Properties**

- `cancel` (`() => void`): Stops it: the half-cloned folder is removed and `result` says `cancelled`.
- `result` (`Promise<RepoCloneResult>`): What it answered.

Source: [src/chat/git/clone.ts:184](https://github.com/Tahul/zidane/blob/main/src/chat/git/clone.ts#L184)

### `CommandAgentResult`

Interface.

Result of driving the agent from within a step via `ctx.runAgent`.

```ts
interface CommandAgentResult
```

**Properties**

- `text` (`string`): The assistant's final text for this sub-run.

Source: [src/chat/commands.ts:172](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L172)

### `CommandArgSuggestionSummary`

Interface.

Wire-safe projection of canonical `CommandArgSuggestion`.

```ts
interface CommandArgSuggestionSummary
```

**Properties**

- `description?` (`string`): Secondary text shown next to the label.
- `label?` (`string`): Display label. Defaults to `value`.
- `value` (`string`): Text inserted after `/<name> ` on accept.

Source: [src/chat/host/signals.ts:216](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L216)

### `CommandAttachment`

Interface.

A prompt attachment forwarded into a command invocation — captured when
the user submits `/cmd …` with pasted images/files attached. `data` is
base64 for EVERY media type (including text/plain): one encoding keeps
the contract lossless for binary and host-agnostic, at the cost of a
trivial decode for text consumers.

```ts
interface CommandAttachment
```

**Properties**

- `data` (`string`, readonly): Base64-encoded content.
- `mediaType` (`string`, readonly): MIME type (e.g. `image/png`, `text/plain`).
- `name` (`string`, readonly): Display name from the capture surface (e.g. `clipboard.png`).

Source: [src/chat/commands.ts:184](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L184)

### `CommandExecution`

Interface.

Outcome of a command run. `error` is set when a step threw (pipeline stops).

```ts
interface CommandExecution
```

**Properties**

- `aborted?` (`boolean`): True when the run stopped because `ctx.signal` aborted.
- `error?` (`unknown`): The value a step threw. Unset when the run succeeded or was aborted.
- `ok` (`boolean`): True when every step ran without throwing or aborting.
- `ranSteps` (`number`): Number of steps that ran (including the one that threw).

Source: [src/chat/commands.ts:547](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L547)

### `CommandInteraction`

Interface.

User-interaction surface for commands — the same prompt-slot primitives
the `ask_user` / `present_plan` TOOLS render, but WITHOUT the tools: a
command enqueues directly into the host's interaction queue. No tool
calls, no turns, no model involvement — the answer routes straight back
to the awaiting step. Every method rejects on cancel/esc, which the
executor classifies as a quiet abort.

```ts
interface CommandInteraction
```

**Properties**

- `askUser` (`(form: QuestionPayload) => Promise<Readonly<Record<string, AnswerValue>>>`): Show a form (text / textarea / select / confirm questions — identical
  UI to the ask_user tool) and resolve with the answers keyed by
  `Question.id` (`string` for text/select, `boolean` for confirm,
  `string[]` of choice ids for `multi: true` selects).
- `confirm` (`(prompt: string, opts?: { affirmLabel?: string; denyLabel?: string; intro?: string }) => Promise<boolean>`): One-tap yes/no — sugar over a single `confirm`-type question:

  if (await ctx.interaction.confirm('Push to origin/main?')) …
- `presentPlan` (`(plan: PlanProposal) => Promise<PlanResponse>`): Present a plan for the full approval ceremony (approve / reject /
  revise + optional comment — identical UI to present_plan). For
  lightweight gating prefer `confirm`.

Source: [src/chat/commands.ts:70](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L70)

### `CommandOutputBlock`

Interface.

A block of local output a step renders into the chat.

Plain blocks (`text` only) render as full markdown, both live and on
reload — use them for the output the user should read in full (e.g. a
final commit message). Blocks with `detail` render as a collapsed
`command-detail` row instead (dim `▸ label` + line-capped body with
"… N more lines") — use them for bulky supporting output (diffs, logs,
listings) that shouldn't drown the transcript.

```ts
interface CommandOutputBlock
```

**Properties**

- `custom?` (`{ data?: JsonValue; renderer: string }`): Render through an extension-registered custom renderer
  (`ctx.tui.registerRenderer`). `renderer` must be the NAMESPACED id
  (`<extension>/<id>`) — `ctx.appendNote` auto-prefixes bare ids for
  its own extension; command `emit` requires the full form. `data`
  must be JSON-safe (it persists in the session). Takes precedence
  over `detail` when both are set.
- `detail?` (`{ label?: string; syntax?: 'diff' }`): Render as a collapsed detail row (dim `▸ label` + line-capped body)
  instead of full markdown. `syntax: 'diff'` additionally colors
  `+N` / `−N` count tokens (added green / deleted red) in both label
  and body — for diff-stat-style output.
- `text` (`string`): The block body — full markdown, the detail body, or (for `custom`
  blocks) the MANDATORY fallback markdown rendered wherever the
  renderer isn't available: extension uninstalled or disabled,
  headless host, GUI, renderer threw. The fallback is what persists
  as the block's output, so pre-custom hosts render it natively.

Source: [src/chat/commands.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L35)

### `CommandSpawnResult`

Interface.

Outcome of one [`CommandSpawnTask`](https://zidane.si/docs/reference/chat.md#CommandSpawnTask).

```ts
interface CommandSpawnResult
```

**Properties**

- `label` (`string`): The task's `label` (or the child id when none was given).
- `message` (`string`): Full formatted result / diagnostic (always set; useful for non-completed statuses).
- `model?` (`string`): Model the task asked for, when pinned.
- `status` (`'error' | 'completed' | 'aborted' | 'timeout' | 'rejected'`): Terminal status. `'rejected'` = refused before running (unknown model, caps).
- `text` (`string`): The child's final assistant text. Empty unless `status === 'completed'`.

Source: [src/chat/commands.ts:158](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L158)

### `CommandSpawnTask`

Interface.

One task for `CommandStepContext.spawnAgents` — an isolated child agent run.

```ts
interface CommandSpawnTask
```

**Properties**

- `label?` (`string`): Short display label carried back on the result (defaults to the child id).
- `model?` (`string`): Model id from the host catalog (`ctx.runtime.models()` on the extension
  context). Omit to inherit the session's active model. Unknown ids reject
  the task (`status: 'rejected'`) without running anything.
- `prompt` (`string`): Task prompt for the child agent. Brief it like a colleague — it has no conversation context.
- `system?` (`string`): System prompt override for the child. Inherits the agent's system prompt when omitted.

Source: [src/chat/commands.ts:142](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L142)

### `CommandStep`

Interface.

One step of a command pipeline.

```ts
interface CommandStep
```

**Properties**

- `run` (`CommandStepFn`): Handler invoked with the shared step context.

Source: [src/chat/commands.ts:314](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L314)

### `CommandStepContext`

Interface.

Capabilities + inputs handed to every step.

```ts
interface CommandStepContext
```

**Properties**

- `appendContext?` (`(text: string, opts?: { readonly display?: string | false }) => Promise<void>`, readonly): Persist a compact MODEL-VISIBLE context note — a real user-role turn,
  sent on the wire unchanged from the next run onward, without
  triggering a run now. This is how a command's outcome becomes part of
  the conversation: the next user prompt sees what the workflow
  found/did without replaying subagent transcripts.

  `opts.display` swaps the transcript echo for a short one-line marker —
  live AND on reload (the marker persists on the turn; replay renders it
  with the full note as a collapsed detail), so note bodies the command
  also emitted never display twice. `display: false` shows nothing: the
  note is for the model only, for a command whose own closing message
  already says what happened. Keep notes compact regardless — they
  occupy context for the rest of the conversation. ABSENT on session-less
  hosts.
- `args` (`string`, readonly): Raw text after `/<name>` (trimmed of the leading space).
- `argv` (`readonly string[]`, readonly): `args` split on whitespace, empties removed.
- `attachments?` (`readonly CommandAttachment[]`, readonly): Attachments submitted with the invocation (images/files pasted into the
  prompt), when the host captures them. ABSENT — not empty — on hosts
  without an attachment surface; guard with `ctx.attachments?.length`.
- `cwd` (`string`, readonly): Working directory the command runs against.
- `data` (`Record<string, unknown>`, readonly): Mutable bag threaded across steps; a step's returned `data` is merged in.
- `emit` (`(block: CommandOutputBlock) => void`, readonly): Render + persist a local output block.
- `generateData` (`{ <S extends StandardSchemaLike<unknown>>(opts: Omit<CommandGenerateDataOptions, 'schema'> & { schema: S }): Promise<InferSchemaOutput<S>>; <T = unknown>(opts: CommandGenerateDataOptions): Promise<T> }`, readonly): Generate schema-conforming JSON with ONE silent model call — no session
  turns, no transcript output, nothing added to the conversation (the
  workflow presents results itself via `emit`). Reach for this when a
  step needs a VALUE (a commit message, a classification, extracted
  fields); reach for `runAgent` when it needs an interactive sub-run
  with tools and streaming.
- `interaction?` (`CommandInteraction`, readonly): User interaction (confirm / forms / plan approval), when the host has
  an interactive surface (the TUI always does). ABSENT on UI-less hosts
  — guard (`if (!ctx.interaction) …`) and degrade gracefully.
- `invokedAs?` (`string`, readonly): The command token the user typed (lowercased) — the alias when one
  was used, else the canonical name. Optional: hosts that dispatch
  programmatically may not have one. See `CommandInvocation.invokedAs`.
- `logger` (`Logger`, readonly): Logger with a `command=<name>` binding, routed to the host's log funnel.
- `runAgent` (`(prompt: string, opts?: { readonly display?: string }) => Promise<CommandAgentResult>`, readonly): Drive one agent run to completion and return its result. The prompt is
  injected as a real user turn (model-visible, unchanged on the wire) and
  the response streams into the transcript. Serialized with the host's run
  slot, so a workflow can interleave deterministic steps with LLM
  interaction. Any output `emit`ted before this call is flushed first,
  preserving transcript order.

  `opts.display` replaces the full-prompt echo in the transcript with a
  short line (the full prompt still renders beneath it as a collapsed
  detail row, and still reaches the model verbatim) — pass it when the
  prompt embeds bulky material like a diff.
- `session?` (`CommandSessionView`, readonly): The surrounding session, when the host runs commands inside one (the
  TUI always does). ABSENT in session-less contexts — a command that
  reads it must guard (`if (!ctx.session) …`) or degrade gracefully.
- `signal` (`AbortSignal`, readonly): Aborts when the user presses esc during execution.
- `spawnAgents?` (`(tasks: readonly CommandSpawnTask[], opts?: { readonly maxConcurrent?: number }) => Promise<readonly CommandSpawnResult[]>`, readonly): Run one or more ISOLATED child agents — each with its own context
  window and the full tool set, optionally pinned to a different model
  — and resolve when the whole batch settles. Tasks run in parallel, as
  many at once as `opts.maxConcurrent` and the session's spawn cap allow
  (the apps' Max parallel subagents setting, default 3). Children stream and
  persist into the transcript exactly like `spawn`-tool subagents, so
  the user sees their activity live and on reload; their context stays
  isolated from the conversation.

  Complements `runAgent`: `runAgent` drives THE conversation
  (model-visible turns, active model, serialized); `spawnAgents` fans
  work out to throwaway workers. Results come back to the command —
  persist what matters via `emit` (user-facing) and
  `appendContext` (model-facing).

  ABSENT on hosts without an agent (guard like `session`).

Source: [src/chat/commands.ts:194](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L194)

### `CommandStepResult`

Interface.

Optional per-step return — `data` is merged into the shared bag for later steps.

```ts
interface CommandStepResult
```

**Properties**

- `data?` (`Record<string, unknown>`): Values merged into the shared bag the next steps read.

Source: [src/chat/commands.ts:303](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L303)

### `CommandSummary`

Interface.

Wire-safe summary of an extension-contributed `/` command — the
projection of canonical `Command` (src/chat/commands.ts) minus its
function-valued fields (steps, completeArgs).

```ts
interface CommandSummary
```

**Properties**

- `aliases?` (`string[]`): Alternative names that run the same command.
- `description` (`string`): One-line description shown in completion lists.
- `group?` (`string`): Grouping label — defaults to the contributing extension's name.
- `hasArgCompletion?` (`boolean`): `true` when the canonical command declares `completeArgs` — lets a
  client wire an arg-completion shim only for commands that actually
  complete, instead of firing blind RPCs on every `/name <prefix>`.
- `name` (`string`): Command name, without the leading `/`.

Source: [src/chat/host/signals.ts:196](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L196)

### `CompactionEntry`

Interface.

The live session state a compaction needs.

```ts
interface CompactionEntry
```

**Properties**

- `agent` (`Agent`): The session's agent.
- `compactionController?` (`AbortController`): Set while a compaction runs. Aborting it cancels the summary call.
- `model` (`string`): Model the session runs on, also used for the summary.
- `sessionId` (`string`): Session to compact.

Source: [src/chat/host/compaction/run.ts:37](https://github.com/Tahul/zidane/blob/main/src/chat/host/compaction/run.ts#L37)

### `CompactionReport`

Interface.

What a compaction did, for the caller's confirmation (the TUI's "compacted N turns" banner).

```ts
interface CompactionReport
```

**Properties**

- `droppedCount` (`number`): Turns the prompt-too-long retry had to drop without summarizing (context lost).
- `effectiveTokens` (`number`): Estimated context size on the next turn: the summary plus the restored content.
- `inputTokens` (`number`): Input tokens the summary call used.
- `model` (`string`): Model that wrote the summary.
- `outputTokens` (`number`): Output tokens of the summary.
- `replacedCount` (`number`): Turns absorbed into the summary.
- `restoredFiles` (`number`): Recently-read files re-injected after the summary.
- `restoredSkills` (`number`): Active skills re-injected after the summary.

Source: [src/chat/host/compaction/run.ts:17](https://github.com/Tahul/zidane/blob/main/src/chat/host/compaction/run.ts#L17)

### `CompanionSpec`

Interface.

Identifies a companion package and its executable.

```ts
interface CompanionSpec
```

**Properties**

- `binName?` (`string`): Executable name on PATH. Defaults to `packageName`.
- `packageName` (`string`): npm package name, e.g. `'zidane-gui'`.

Source: [src/chat/auto-update-companion.ts:19](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-companion.ts#L19)

### `CompletionState`

Interface.

State surface returned by `useCompletion()`.

```ts
interface CompletionState<TItem = unknown>
```

**Type parameters**

- `TItem` (default `unknown`)

**Properties**

- `active` (`ActiveTrigger<TItem> | null`): Active trigger, or null when the cursor doesn't sit in a completable span.
- `commit` (`() => { cursor: number; text: string } | null`): Commit the highlighted item. Returns the new text + cursor pair, or
  `null` when there's nothing to commit (no active trigger, empty list).
- `dismiss` (`() => void`): Force-close the popover until the user types again.
- `items` (`readonly CompletionItem<TItem>[]`): Items for the active query. Empty when no trigger is active.
- `loading` (`boolean`): True while the provider's `suggest` promise is pending.
- `references` (`readonly CompletionReference<TItem>[]`): All references in the current buffer, regardless of provider.
- `selectedIndex` (`number`): Index of the highlighted item inside `items`.
- `selectNext` (`() => void`): Move the highlight. Wraps at the ends. No-op when `items` is empty.
- `selectPrev` (`() => void`): Moves the highlight to the previous item. Wraps at the ends. No-op when `items` is empty.

Source: [src/chat/completion.ts:47](https://github.com/Tahul/zidane/blob/main/src/chat/completion.ts#L47)

### `ConfirmQuestion`

Interface.

A yes/no question.

```ts
interface ConfirmQuestion extends BaseQuestion
```

**Properties**

- `affirmLabel?` (`string`): Label for the affirmative option. Default: `'yes'`.
- `denyLabel?` (`string`): Label for the negative option. Default: `'no'`.
- `description?` (`string`, from `BaseQuestion`): Optional helper text under the prompt.
- `id` (`string`, from `BaseQuestion`): Stable id — keys the response back to the model.
- `prompt` (`string`, from `BaseQuestion`): Question text shown above the input (Markdown allowed).
- `required?` (`boolean`, from `BaseQuestion`): Whether the answer is required (non-empty). Default: `true` for
  `select` / `confirm` (no sensible empty), `false` for free-text
  (skipping = empty string). Free-text questions still accept an
  answer; the flag only gates submission.
- `type` (`'confirm'`): Question type discriminator.

Source: [src/chat/interactions-protocol.ts:170](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L170)

### `ContentMatch`

Interface.

One matching line.

```ts
interface ContentMatch
```

**Properties**

- `line` (`number`): 1-based.
- `path` (`string`): Workspace-relative path.
- `ranges` (`{ end: number; start: number }[]`): The matches in it, as UTF-16 offsets.
- `text` (`string`): The line (a window of it when minified).

Source: [src/chat/workspace/search.ts:38](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/search.ts#L38)

### `ContentSearchOptions`

Interface.

Options of [`searchWorkspace`](https://zidane.si/docs/reference/chat.md#searchWorkspace).

```ts
interface ContentSearchOptions extends TextMatchOptions
```

**Properties**

- `caseSensitive` (`boolean`, from `TextMatchOptions`): Match case.
- `searchId` (`string`): A later search with the same id cancels this one (one per search box).

Source: [src/chat/workspace/search.ts:62](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/search.ts#L62)

### `ContentSearchResult`

Interface.

A search in files.

```ts
interface ContentSearchResult
```

**Properties**

- `capped` (`boolean`): Stopped at the cap: there are more.
- `engine` (`'none' | 'git' | 'rg' | 'walk'`): What ran: ripgrep, `git grep`, a walk of the files, or nothing (empty query, superseded).
- `matches` (`ContentMatch[]`): The matching lines.
- `unsupported?` (`'sandbox'`): File contents can't be searched here (a sandboxed session); names still can.

Source: [src/chat/workspace/search.ts:50](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/search.ts#L50)

### `ContextSegmentsReport`

Interface.

Instructions-tab payload. Mirrors the TUI's `getContextSegments` contract so
both settings surfaces render identical rows and identical explanations for
why a `SYSTEM.md` override can or cannot apply.

```ts
interface ContextSegmentsReport
```

**Properties**

- `canOverrideSystem` (`boolean`): False for host-supplied profiles, which carry their own system prompt.
- `live` (`boolean`): Always true here: the host only answers for a live (or buildable) agent.
- `overrideBlockedBy?` (`'scope' | 'profile'`): `'profile'` = picked profile supplies its own prompt; `'scope'` = `userInstructionsScope` excludes user files.
- `segments` (`readonly ContextSegment[]`): Instruction segments composing the session's system prompt.
- `shadowedSystemPath?` (`string`): A `SYSTEM.md` on disk that lost precedence — surfaced so edits to it don't look ignored for no reason.

Source: [src/chat/host/host-api.ts:49](https://github.com/Tahul/zidane/blob/main/src/chat/host/host-api.ts#L49)

### `CreateExtensionMcpHostOptions`

Interface.

Options for [`createExtensionMcpHost`](https://zidane.si/docs/reference/chat.md#createExtensionMcpHost).

```ts
interface CreateExtensionMcpHostOptions
```

**Properties**

- `connect?` (`(configs: McpServerConfig[], opts: { buildAuthProvider?: (config: McpServerConfig) => McpOAuthProvider | undefined }) => Promise<McpConnection>`): Test seam: replaces `connectMcpServers`.
- `credentialStore?` (`() => McpCredentialStore`): Stored MCP OAuth credentials; omitted → OAuth servers can't connect.
- `idleMs?` (`number`): Close a connection after this long without a call. Default 60 s.
- `prefix` (`string`): Storage prefix (`.zidane`) the discovery reads `mcps.json` under.
- `readSettings` (`() => Partial<Settings> | undefined`): Live settings: the allowlist (`enabledMcps`) and import toggles.
- `serversTtlMs?` (`number`): How long a cwd's server list is reused. Default 10 s.

Source: [src/chat/extension-mcp-host.ts:29](https://github.com/Tahul/zidane/blob/main/src/chat/extension-mcp-host.ts#L29)

### `CreateInteractionToolsOptions`

Interface.

Options for [`createInteractionTools`](https://zidane.si/docs/reference/chat.md#createInteractionTools).

```ts
interface CreateInteractionToolsOptions
```

**Properties**

- `requestInteraction` (`(request: InteractionRequest, options?: RequestInteractionOptions) => Promise<InteractionResponse>`): Called when the agent invokes one of the interaction tools. Resolves
  with the user's response (forwarded to the model as the tool_result);
  reject to signal denial / abort (the model receives
  `Tool error: <message>` as the result).
- `secrets?` (`InteractionSecretsOptions`): Turns on the `secret` question type (`Settings.secrets`). Absent: the
  type is not in the schema, and a call that uses it fails.
- `shouldSkipInteractions?` (`() => boolean`): Evaluated at invocation time so hosts can switch modes without rebuilding the agent.

Source: [src/chat/interactions-protocol.ts:930](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L930)

### `CreatePlanToolsOptions`

Interface.

Options for [`createPlanTools`](https://zidane.si/docs/reference/chat.md#createPlanTools).

```ts
interface CreatePlanToolsOptions
```

**Properties**

- `identicalBlockReason?` (`(count: number) => string`): Refusal text for the `maxIdenticalWrites` block, given the identical-call count.
- `maxIdenticalWrites?` (`number`): Hard stop after this many byte-identical `todowrite` payloads in a row
  (a run-scoped `behavior.dedupTools` entry). `0` turns it off. Default: `5`.
- `maxItems?` (`number`): Maximum item count per `todowrite`; excess items are dropped and reported. Default: `100`.
- `maxWritesPerRun?` (`number`): Opt-in hard cap on `todowrite` calls per run (`behavior.toolBudgets`). Default: `0` (none).
- `onMaxWrites?` (`'steer' | 'block'`, default `'steer'`): Reaction when `maxWritesPerRun` is exceeded.
- `readDescription?` (`string`): Replaces the `todoread` description.
- `remindOpenSteps?` (`boolean`): Send the agent one reminder when a run is about to finish with open
  steps in the plan it is applying (never after an abort or an error,
  never in a sub-agent). Headless and eval hosts may turn it off.
  Default: `true`.
- `writeDescription?` (`string`): Replaces the `todowrite` description. Leave unset for the built-in text.

Source: [src/chat/todos.ts:75](https://github.com/Tahul/zidane/blob/main/src/chat/todos.ts#L75)

### `CreateSessionInStoreParams`

Interface.

Fields stamped on a new session at creation.

```ts
interface CreateSessionInStoreParams
```

**Properties**

- `account?` (`string`): The stored account of `providerKey` to run on (credential id); omitted = its active account.
- `agentId?` (`string`): Profile the session starts in. Defaults to `DEFAULT_AGENT_ID` (`build`).
- `cwd` (`string`): Tool-execution directory; `projectRoot` is derived from it. `''` creates a
  DIRECTORY-LESS session (the GUI's "Chats" bucket): no project, and
  `resolveSessionCwd` anchors tool execution at the user's HOME at run time.
- `environment?` (`SessionEnvironment`): Where the session's work happens (see `environment.ts`) — resolved by the
  host (`initialEnvironment`), which also provisions it. Omitted or local +
  ready → nothing is stamped: an absent environment IS the local one.
- `extensionEdit?` (`boolean`): Mark as an extension-authoring session (auto-activates the docs skill).
- `fallbacks?` (`readonly ModelFallbackTarget[]`): Fallback targets after the primary, stored as the session's model chain.
- `id?` (`string`): Explicit id. Omitted → the store's generator, else a random UUID.
- `metadata?` (`Record<string, unknown>`): Extra host-specific metadata (e.g. a pinned-skills seed). Applied FIRST,
  so every canonical key stamped here wins on collision.
- `modelId?` (`string`): Model the session starts on. Applied together with `providerKey`.
- `providerKey?` (`string`): Provider the session starts on. Applied together with `modelId`.
- `sideOf?` (`string`): Make it a `/side` session of this parent (`metadata.sideOf`, role
  `'side'`): hidden from lists, deleted with the parent.
- `skillEdit?` (`{ name: string }`): Mark as a skill-authoring session for the named skill (dir = `cwd`).
- `spaceId?` (`string`): The space the session belongs to (see `./spaces`). Omitted or Default →
  nothing is stamped: an absent space id IS the Default space.
- `title?` (`string`): Initial session title.

Source: [src/chat/session-factory.ts:40](https://github.com/Tahul/zidane/blob/main/src/chat/session-factory.ts#L40)

### `CustomField`

Interface.

Free-form configuration field that a provider needs the user to supply
alongside (or instead of) an API key — e.g. a self-hosted base URL, an
Azure deployment name, a default model id.

The wizard prompts for each field in order. Submitted values are persisted
into the apikey credential (as `customFields[key]`) and mirrored into
`process.env[envVar]` so the provider's factory can read them via the same
env-var convention as `envKey`.

Descriptors without an `envKey` use `customFields` as the sole credential
mechanism — the wizard skips its API-key step in that case.

```ts
interface CustomField
```

**Properties**

- `envVar` (`string`): Env var name to mirror this field into at TUI launch + on submit.
- `hint?` (`string`): Optional helper copy rendered above the input.
- `key` (`string`): Storage key inside the credential's `customFields` record.
- `label` (`string`): Human-readable label shown in the wizard step's title.
- `placeholder?` (`string`): Optional placeholder shown in the wizard input.
- `required?` (`boolean`): When true, an empty submission is rejected by the wizard. Default: false.

Source: [src/chat/providers.ts:131](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L131)

### `DecisionModelEntry`

Interface.

A model that can serve agent decisions, with its availability.

```ts
interface DecisionModelEntry
```

**Properties**

- `available` (`boolean`): Credentials allow sending decisions to this model.
- `modelId` (`string`): Model id within the provider.
- `providerKey` (`string`): Key of the provider that serves the model.
- `providerLabel` (`string`): Display name of the provider.
- `reason?` (`string`): Why it isn't available.

Source: [src/chat/decision-models.ts:22](https://github.com/Tahul/zidane/blob/main/src/chat/decision-models.ts#L22)

### `DecisionTestResult`

Interface.

Outcome of [`testDecisionModel`](https://zidane.si/docs/reference/chat.md#testDecisionModel).

```ts
interface DecisionTestResult
```

**Properties**

- `durationMs` (`number`): Time the probe took, in ms.
- `message?` (`string`): Failure detail, when the probe failed.
- `model?` (`string`): Model the probe used.
- `ok` (`boolean`): True when the probe decision succeeded.
- `provider?` (`string`): Provider name and model the probe actually used.
- `reason?` (`string`): `DecisionFailureReason` on failure.

Source: [src/chat/decision-models.ts:166](https://github.com/Tahul/zidane/blob/main/src/chat/decision-models.ts#L166)

### `DetectOnboardingOptions`

Interface.

What [`detectOnboardingEnvironment`](https://zidane.si/docs/reference/chat.md#detectOnboardingEnvironment) reads.

```ts
interface DetectOnboardingOptions extends OnboardingInstallSignalsInput, DetectOtherAgentsOptions
```

**Properties**

- `cwd?` (`string`, from `DetectOtherAgentsOptions`): Project directory. Default: `process.cwd()`.
- `dataDir` (`string`, from `OnboardingInstallSignalsInput`): Directory of `credentials.json`.
- `env?` (`Record<string, string | undefined>`): Environment to look for provider keys in. Default: `process.env`.
- `fetch?` (`typeof fetch`): Fetch implementation for the local server probe. Default: the global `fetch`.
- `githubIdentity?` (`() => Promise<GithubIdentity>`): The GitHub identity lookup. Default: the process's GitHub client.
- `home?` (`string`, from `DetectOtherAgentsOptions`): Home directory. Default: `os.homedir()`.
- `registry` (`Readonly<Record<string, ProviderDescriptor>>`): The host's provider descriptors.
- `sessions` (`boolean`, from `OnboardingInstallSignalsInput`): The session store holds at least one session.
- `state` (`TuiState`, from `OnboardingInstallSignalsInput`): The state file's content.
- `surfaceFirstRun` (`boolean`, from `OnboardingInstallSignalsInput`): This app has never run on this machine.

Source: [src/chat/host/onboarding/detect.ts:129](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/detect.ts#L129)

### `DetectOtherAgentsOptions`

Interface.

Where to look for other agents' configuration.

```ts
interface DetectOtherAgentsOptions
```

**Properties**

- `cwd?` (`string`): Project directory. Default: `process.cwd()`.
- `home?` (`string`): Home directory. Default: `os.homedir()`.

Source: [src/chat/host/onboarding/detect.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/detect.ts#L35)

### `DetectPackageManagerOptions`

Interface.

Options for [`detectPackageManager`](https://zidane.si/docs/reference/chat.md#detectPackageManager). Every field overrides a value read from the running process, which keeps detection testable.

```ts
interface DetectPackageManagerOptions
```

**Properties**

- `binaryPath?` (`string`): Override the resolved binary path (defaults to `process.execPath`).
- `channel?` (`string`): dist-tag. Defaults to `'latest'`.
- `env?` (`Readonly<Record<string, string | undefined>>`): Override env. Used in tests.
- `home?` (`string`): Override `homedir()`. Used in tests.
- `packageName?` (`string`): Package to install. Defaults to `'zidane-tui'`.
- `platform?` (`Platform`): Override `process.platform`. Used in tests.
- `userAgent?` (`string`): Override the user-agent string (defaults to `process.env.npm_config_user_agent`).

Source: [src/chat/auto-update.ts:137](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L137)

### `Diagnostic`

Interface.

One finding from a project tool, positions 1-based like editors show them.

```ts
interface Diagnostic
```

**Properties**

- `code?` (`string`): Rule / error code (`TS2322`, `no-unused-vars`).
- `column` (`number`): 1-based column.
- `endColumn?` (`number`): Last column.
- `endLine?` (`number`): Last line.
- `line` (`number`): 1-based line.
- `message` (`string`): What it says.
- `path` (`string`): Relative to the session cwd.
- `severity` (`DiagnosticSeverity`): How bad.
- `source` (`string`): Tool id (`tsc`, `eslint`, ...).

Source: [src/chat/workspace/diagnostics/types.ts:10](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/diagnostics/types.ts#L10)

### `DiagnosticsResult`

Interface.

A diagnostics run.

```ts
interface DiagnosticsResult
```

**Properties**

- `diagnostics` (`Diagnostic[]`): The findings.
- `failed` (`{ id: string; reason: string }[]`): Tools that failed or timed out, with a short reason.
- `ran` (`string[]`): Tools that ran (ids), for the "checked by" line.

Source: [src/chat/workspace/diagnostics/types.ts:44](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/diagnostics/types.ts#L44)

### `DiagnosticsToolContext`

Interface.

What a tool's detection and run work with.

```ts
interface DiagnosticsToolContext
```

**Properties**

- `cwd` (`string`): The folder checked.
- `exec` (`(bin: string, args: readonly string[], options?: { atLeastMs?: number }) => Promise<ExecResult>`): Run a command in cwd with the run's timeout; resolves with stdout/stderr even on non-zero exit
  (linters exit 1 on findings). Rejects when it did not finish: timed out, stopped, output too large,
  or could not start. `atLeastMs` raises the timeout for a run that builds a cache later runs rely on.
- `hasFile` (`(...names: string[]) => Promise<boolean>`): True when any of these files exists under cwd.
- `resolveBin` (`(name: string) => Promise<string | null>`): Resolve a binary: `node_modules/.bin/<name>` first, then PATH. Null when absent.
- `stateDir?` (`string`): Where the tool may keep state between runs in this folder (tsc's incremental build info). Absent: every run starts cold.

Source: [src/chat/workspace/diagnostics/types.ts:54](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/diagnostics/types.ts#L54)

### `DiagnosticsToolInfo`

Interface.

A diagnostics tool, as found for a folder.

```ts
interface DiagnosticsToolInfo
```

**Properties**

- `detail?` (`string`): Why it is missing, or where it was found.
- `id` (`string`): Its id (`tsc`, `eslint`, …).
- `label` (`string`): Its name.
- `status` (`'missing' | 'absent' | 'ready'`): `ready`: configured and runnable; `missing`: configured but no binary (with the reason); `absent`: not used by this project.

Source: [src/chat/workspace/diagnostics/types.ts:32](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/diagnostics/types.ts#L32)

### `DiscoverAgentsMdOptions`

Interface.

Options for [`discoverAgentsMd`](https://zidane.si/docs/reference/chat.md#discoverAgentsMd).

```ts
interface DiscoverAgentsMdOptions
```

**Properties**

- `cwd?` (`string`): Discovery cwd. Default: `process.cwd()`.
- `home?` (`string`): User home directory. Default: `os.homedir()`.
- `prefix?` (`string`): Storage prefix. Leading dot tolerated. Default: `'.zidane'`.
- `ruler?` (`boolean`): Also load Ruler instruction markdown (`.ruler/*.md`, project and
  XDG-global) — see `ruler-configs.ts`. Mirrors the
  `importRulerConfigs` setting. When on, a repo-root `AGENTS.md`
  that `ruler apply` GENERATED (it aggregates those same sources) is
  skipped so instructions never load twice.
- `scope?` (`AgentsMdScope`): Which scopes to load. Default: `'both'`.

Source: [src/chat/agents-md.ts:86](https://github.com/Tahul/zidane/blob/main/src/chat/agents-md.ts#L86)

### `DiscoveredMcp`

Interface.

One entry in the discovered list. `path` is the source file (one of
the `mcp(s).json` variants under `.agents/` or `.{prefix}/`) so a
Settings picker can show "Defined in \~/.zidane/mcp.json" tooltips
and the user can tell which file declared what.

```ts
interface DiscoveredMcp
```

**Properties**

- `config` (`McpServerConfig`): Normalized server config.
- `path` (`string`): Absolute path of the file that declared the server.
- `source` (`'user' | 'project'`): Whether the file came from the project or the user directory.

Source: [src/chat/mcps-discovery.ts:48](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-discovery.ts#L48)

### `DiscoveredPlugin`

Interface.

One plugin found on disk, loaded (successfully or not).

```ts
interface DiscoveredPlugin
```

**Properties**

- `name` (`string`): Identity for dedup + the enabled allowlist. The manifest's `name` when
  the plugin loaded; the directory basename when it didn't, so a rejected
  plugin still has something stable to key a row and a toggle on.
- `path` (`string`): Plugin root directory, as discovered.
- `plugin` (`LoadedPlugin`): Load result. Check `ok` before trusting `manifest`.
- `source` (`'user' | 'project'`): Which search root it came from.

Source: [src/chat/plugins-discovery.ts:36](https://github.com/Tahul/zidane/blob/main/src/chat/plugins-discovery.ts#L36)

### `DiscoverSystemMdOptions`

Interface.

Options for [`discoverSystemMd`](https://zidane.si/docs/reference/chat.md#discoverSystemMd).

```ts
interface DiscoverSystemMdOptions
```

**Properties**

- `cwd?` (`string`): Discovery cwd. Default: `process.cwd()`.
- `home?` (`string`): User home directory. Default: `os.homedir()`.
- `prefix?` (`string`): Storage prefix. Leading dot tolerated. Default: `'.zidane'`.
- `scope?` (`AgentsMdScope`): Which scopes may contribute an override — the same
  `AgentsMdScope` the host applies to `AGENTS.md`, so one setting
  governs both surfaces:
  - `'none'`    — no override, ever. Short-circuits before any FS probe.
  - `'user'`   — only `~/.{prefix}/SYSTEM.md`. The repo's file is IGNORED,
    which is the whole point of the setting: it is how a user
    says "do not trust this checkout".
  - `'project'` — only `<root>/SYSTEM.md`.
  - `'both'`   — union, project winning (default).

Source: [src/chat/system-md.ts:67](https://github.com/Tahul/zidane/blob/main/src/chat/system-md.ts#L67)

### `DiscoveryContextValue`

Interface.

Skills, plugins and MCP servers found on disk, shared with the UI.

```ts
interface DiscoveryContextValue
```

**Properties**

- `ensureFiles` (`() => Promise<readonly FileEntry[]>`): Idempotent first-load trigger for files (used by `@` popover).
- `ensureSkills` (`() => Promise<readonly SkillConfig[]>`): Idempotent first-load trigger for skills (used by `/` popover + modal mount).
- `filesCatalog` (`readonly FileEntry[]`): Current file walk results — empty until lazy-load lands.
- `mcpsCatalog` (`readonly DiscoveredMcp[]`): Parsed `mcps.json` entries.
- `mcpsErrors` (`readonly DiscoveryError[]`): Non-fatal parse errors from `mcps.json` lookups.
- `mcpToolsByServer` (`Record<string, readonly McpToolSchema[]>`): Per-server full tool catalog from the on-disk cache (see
  `chat/mcp-tools-cache.ts`). Updated as MCP servers bootstrap and
  fire the `mcp:tools:list` hook. Missing key → no cache yet; the
  settings UI surfaces an empty-state row under the expanded server.
- `pluginsCatalog` (`readonly DiscoveredPlugin[]`): Agent Plugins loaded from `.agents/plugins` / `.{prefix}/plugins`
  (project + user). Includes rejected plugins — they render as read-only
  diagnostics rather than vanishing.
- `refreshFiles` (`() => Promise<void>`): Force a fresh file walk. No UI affordance calls it yet.
- `refreshMcps` (`() => Promise<boolean>`): Re-parse `mcps.json`. Sync-shaped under the hood, async for symmetry.
  Resolves `true` when the catalog actually changed (a publish that
  schedules downstream agent rebuilds), `false` on a fingerprint no-op
  or scan failure — callers that arm rebuild-coupled state (e.g. the
  add-MCP auto-login) disarm on `false`.
- `refreshPlugins` (`() => Promise<void>`): Force a fresh plugin rescan. Resolves once state is committed.
- `refreshSkills` (`() => Promise<void>`): Force a fresh skills rescan. Resolves once state is committed.
- `skillsCatalog` (`readonly SkillConfig[]`): Parsed `SkillConfig[]` from project + user scan paths.

Source: [src/chat/discovery-context.tsx:41](https://github.com/Tahul/zidane/blob/main/src/chat/discovery-context.tsx#L41)

### `DiscoveryError`

Interface.

Per-file failure surfaced from [`discoverProjectMcps`](https://zidane.si/docs/reference/chat.md#discoverProjectMcps). The TUI shows
these in the MCP settings empty / preamble state so a malformed
`mcps.json` doesn't look identical to a missing one.

```ts
interface DiscoveryError
```

**Properties**

- `informational?` (`boolean`): A deliberate, documented behavior being reported (e.g. the ruler stdio
  security carve-out) rather than a failure. Hosts should style these as
  info, not error — crying wolf over a working default trains users to
  ignore the channel that also carries real parse failures.
- `message` (`string`): Human-readable description of the failure.
- `path` (`string`): Absolute path of the file that failed.
- `source` (`'user' | 'project'`): Whether the file came from the project or the user directory.

Source: [src/chat/mcps-discovery.ts:265](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-discovery.ts#L265)

### `DiscoveryResult`

Interface.

Result of [`discoverProjectMcps`](https://zidane.si/docs/reference/chat.md#discoverProjectMcps). `servers` is the per-source-priority
merged list (project beats user, first-found wins on name collision).
`errors` is one entry per file that existed but failed to parse, plus
informational entries for servers a security gate skipped (ruler stdio
without the foreign-agent opt-in) — empty on the happy path.

```ts
interface DiscoveryResult
```

**Properties**

- `errors` (`DiscoveryError[]`): Files that failed to parse and informational notices.
- `servers` (`DiscoveredMcp[]`): Servers found, deduplicated by name with project entries ahead of user entries.

Source: [src/chat/mcps-discovery.ts:288](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-discovery.ts#L288)

### `DiscoverySlot`

Interface.

Handle returned by [`createDiscoverySlot`](https://zidane.si/docs/reference/chat.md#createDiscoverySlot).

```ts
interface DiscoverySlot<T>
```

**Type parameters**

- `T`

**Properties**

- `abort` (`() => void`): Cancel in-flight work + detach. Idempotent.
- `ensure` (`() => Promise<readonly T[]>`): Kick the first load (idempotent). Returns the first-load promise
  once it exists. Schedules a background refresh if past throttle.
- `isRefreshing` (`() => boolean`): True if a refresh is currently running.
- `refresh` (`() => Promise<void>`): Force-refresh now. De-duped across concurrent callers.

Source: [src/chat/discovery-slot.ts:56](https://github.com/Tahul/zidane/blob/main/src/chat/discovery-slot.ts#L56)

### `DiscoverySlotOptions`

Interface.

Options for [`createDiscoverySlot`](https://zidane.si/docs/reference/chat.md#createDiscoverySlot).

```ts
interface DiscoverySlotOptions<T>
```

**Type parameters**

- `T`

**Properties**

- `onError?` (`(err: unknown, phase: 'refresh' | 'first-load') => void`): Optional error sink. Defaults to no-op; useful for `debugLog`.
- `onLoad` (`Commit<T>`): Commit hook — receives every successful walk's result (first + refreshes).
- `throttleMs?` (`number`): Min interval between background refreshes triggered by `ensure()`.
  Default: 3_000ms. `refresh()` ignores this — it's the user's
  explicit-intent path.
- `walk` (`Walk<T>`): Discover function — runs the actual FS / network walk.

Source: [src/chat/discovery-slot.ts:40](https://github.com/Tahul/zidane/blob/main/src/chat/discovery-slot.ts#L40)

### `EditHunk`

Interface.

One old-to-new replacement of an edit.

```ts
interface EditHunk
```

**Properties**

- `newString` (`string`): Replacement text.
- `oldString` (`string`): Text being replaced.
- `replaceAll?` (`boolean`): Mirrored from the tool's `replace_all` flag — surfaced as `× N` in the hunk header.

Source: [src/chat/types.ts:501](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L501)

### `EditOutcome`

Interface.

Result of one edit hunk.

```ts
interface EditOutcome
```

**Properties**

- `kind` (`EditOutcomeKind`): Lifecycle state of the hunk.
- `reason?` (`string`): Short human-readable explanation surfaced in the transcript badge.

Source: [src/chat/types.ts:525](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L525)

### `EditPayload`

Interface.

Structured edit data attached to a `tool` event for `edit` /
`multi_edit` / `write_file`. Lets the renderer compute a unified diff
without re-parsing the JSON input string.

- `edit` produces one hunk with `replaceAll` defaulting to false.
- `multi_edit` produces one hunk per `edits[]` step (in order).
- `write_file` produces a single hunk where `oldString` is the
  pre-write file content (empty for a fresh create) and `newString`
  is the new content; `replaceAll` is meaningless here and absent.

Live capture happens in the host's `tool:before` hook (where the
pre-write snapshot can be read off disk before the tool runs).
Historical replay from persisted turns can only reconstruct the
edit/multi_edit shape — write_file historical events have no
pre-write content and render with `oldString: ''` (= every line is
an addition), matching git's "new file" diff convention.

```ts
interface EditPayload
```

**Properties**

- `hunks` (`readonly EditHunk[]`): Ordered list of `(old, new)` pairs. Each rendered as one hunk.
- `outcomes?` (`readonly EditOutcome[]`): Per-hunk outcome of the approval / application pipeline, 1:1 with
  `hunks`. Absent on a fresh tool call before the gate decides —
  the renderer treats absence as "all applied".

  Surfaces in the transcript so a partially-approved `multi_edit` shows
  the denied hunks alongside the applied ones, dimmed / badged. Live
  approval populates this from the modal's per-edit decisions; replay
  reconstructs it from the tool_result body (which the tool serializes
  in a stable line shape — see `multi_edit`).
- `path` (`string`): Workspace-relative file path. Used as the diff header.
- `priorContent?` (`string`): Pre-write file content snapshot, captured by the host's `tool:before`
  hook for all three edit tools. Lets the diff renderer build a
  contextual unified diff with real file line numbers (matching what
  the user's editor shows) instead of synthetic `@@ -1,N +1,M @@`
  headers anchored at line 1 within each hunk's snippet.

  Absent on historical replay (the field isn't persisted — payloads
  are reconstructed from `input` alone in `eventsFromTurns`). The
  renderer falls back to `buildUnifiedDiff` in that case.

  NOT serialized into session turns; lives only on the live in-memory
  `StreamEvent` to avoid bloating session files with snapshots of
  every edited file.
- `tool` (`'edit' | 'write_file' | 'multi_edit'`): Tool name that produced this payload — drives the header glyph + result-suppression matrix.

Source: [src/chat/types.ts:463](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L463)

### `EnabledToggleSet`

Interface.

Reactive handle over a persisted enabled-names allowlist, returned by [`useEnabledToggleSet`](https://zidane.si/docs/reference/chat.md#useEnabledToggleSet).

```ts
interface EnabledToggleSet
```

**Properties**

- `enabledSet` (`ReadonlySet<string>`): Live set of enabled names — `Set` for O(1) `has`.
- `toggle` (`(name: string) => void`): Toggle one name on/off; persists immediately via `setSetting`.

Source: [src/chat/enabled-toggle-set.ts:33](https://github.com/Tahul/zidane/blob/main/src/chat/enabled-toggle-set.ts#L33)

### `EnvironmentAction`

Interface.

A per-session command a kind offers (session menus): push the branch, open a PR, …

```ts
interface EnvironmentAction
```

**Properties**

- `available?` (`(environment: SessionEnvironment, config: EnvironmentConfig) => boolean`): Offered for this environment right now. Default: whenever it is ready.
- `description?` (`string`): One-line detail shown with the label.
- `id` (`string`): Action id, unique within the kind.
- `label` (`string`): Menu label.
- `run` (`(input: EnvironmentActionInput) => Promise<EnvironmentActionResult>`): Performs the action in the session's environment.

Source: [src/chat/environment.ts:298](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L298)

### `EnvironmentActionInput`

Interface.

Input to an [`EnvironmentAction`](https://zidane.si/docs/reference/chat.md#EnvironmentAction)'s `run`.

```ts
interface EnvironmentActionInput
```

**Properties**

- `config` (`EnvironmentConfig`): The kind's resolved options.
- `cwd` (`string`): The session's host directory (`metadata.cwd`).
- `environment` (`SessionEnvironment`): The session's environment, as persisted.
- `exec` (`(command: string, options?: { cwd?: string; env?: Record<string, string>; timeoutSeconds?: number }) => Promise<ExecResult>`): Run a command in the session's environment — its execution handle, spawned (or resumed) on demand.
- `sessionId` (`string`): Id of the session the action runs for.

Source: [src/chat/environment.ts:266](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L266)

### `EnvironmentActionResult`

Interface.

Outcome of running an [`EnvironmentAction`](https://zidane.si/docs/reference/chat.md#EnvironmentAction).

```ts
interface EnvironmentActionResult
```

**Properties**

- `message` (`string`): One line for the user ("Pushed zidane/ab12 to origin.").
- `url?` (`string`): Something to open — a pull request.

Source: [src/chat/environment.ts:280](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L280)

### `EnvironmentActionSummary`

Interface.

Wire-safe [`EnvironmentAction`](https://zidane.si/docs/reference/chat.md#EnvironmentAction) — what session menus list.

```ts
interface EnvironmentActionSummary
```

**Properties**

- `description?` (`string`): One-line detail shown with the label.
- `id` (`string`): Action id, passed back to run it.
- `label` (`string`): Menu label.

Source: [src/chat/environment.ts:288](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L288)

### `EnvironmentAvailabilityInput`

Interface.

Input to a provider's `available` and `note` checks.

```ts
interface EnvironmentAvailabilityInput
```

**Properties**

- `config` (`EnvironmentConfig`): The kind's resolved options.
- `cwd` (`string`): The folder a session would be created in (`''` = directory-less).
- `machineOnly?` (`boolean`): Check only what the machine needs (Docker answering, git installed), not
  `cwd`: a host's capabilities report, where no folder is in question.

Source: [src/chat/environment.ts:312](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L312)

### `EnvironmentCheckout`

Interface.

A worktree on an existing branch: the local branch when there is one, else `origin`'s (untracked), else, with
`pullRequest`, that pull request's head fetched into the branch. When none of them exists, the branch is cut
from `base` as usual (a ticket's branch nobody started yet).

```ts
interface EnvironmentCheckout
```

**Properties**

- `pullRequest?` (`number`): A pull request of `origin`, by number: its head is the branch's last resort (a fork's pull request).

Source: [src/chat/environment.ts:599](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L599)

### `EnvironmentConfigField`

Interface.

One option a provider declares. Settings UIs render these generically
(no per-kind UI code); `environment-settings.ts` stores, validates and
resolves them.

```ts
interface EnvironmentConfigField
```

**Properties**

- `choices?` (`readonly { description?: string; label: string; value: string }[]`): `type: 'choice'` options; `description` is the one-line detail pickers show under the label.
- `default?` (`EnvironmentConfigValue`): Value used when nothing is stored or supplied by `env`.
- `description?` (`string`): Help text shown with the option.
- `env?` (`string`): Environment variable that stands in when unset (e.g. `E2B_API_KEY`).
- `key` (`string`): Option key, unique within the kind.
- `label` (`string`): Label shown next to the option.
- `placeholder?` (`string`): Placeholder shown in an empty input.
- `setting?` (`string`): The value lives in this top-level `Settings` key instead of the kind's
  own config — for options that predate environments (Local's OS
  sandbox), so there is one source of truth.
- `type` (`'string' | 'number' | 'boolean' | 'choice' | 'secret' | 'path'`): `secret` values never live in `state.json`: they go to the 0600
  environment credential store and reach clients only as "configured".
- `when?` (`{ equals: EnvironmentConfigValue; key: string }`): Only meaningful while another field has this value (hidden otherwise).

Source: [src/chat/environment.ts:159](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L159)

### `EnvironmentDescriptor`

Interface.

Wire-safe description of one environment kind, as returned by [`describeEnvironments`](https://zidane.si/docs/reference/chat.md#describeEnvironments).

```ts
interface EnvironmentDescriptor
```

**Properties**

- `available` (`boolean`): Can be offered for the requested folder right now.
- `canVerify` (`boolean`): The kind has a "Test connection" (`provider.verify`).
- `description?` (`string`): Short description of the kind.
- `enabled` (`boolean`): Whether the kind may be picked, per [`isEnvironmentEnabled`](https://zidane.si/docs/reference/chat.md#isEnvironmentEnabled).
- `fields` (`EnvironmentFieldDescriptor[]`): The kind's options, in schema order.
- `isDefault` (`boolean`): Whether new sessions start in this kind.
- `kind` (`string`): Environment kind id, such as `local`.
- `label` (`string`): Name shown in pickers.
- `machine?` (`{ id: string; name: string; status: 'online' | 'offline' }`): A machine's kind (`local@<name>`): which machine, and whether it is online now.
- `note?` (`string`): What creating one here will do (`provider.note`), when available.
- `reason?` (`string`): Why the kind is unavailable. Present only when `available` is `false`.
- `suggestedBranch?` (`string`): Branch name the provider suggests, when `takesBranch` is `true`.
- `takesBranch` (`boolean`): Whether creating a session in this kind takes a branch name.

Source: [src/chat/environment-settings.ts:205](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L205)

### `EnvironmentExecution`

Interface.

The execution context a provider builds for a ready environment, with its cache key.

```ts
interface EnvironmentExecution
```

**Properties**

- `create` (`() => ExecutionContext`): Builds the execution context. Called when the cached one is missing or its `key` changed.
- `key` (`string`): Everything the context is configured with. The session's cached context
  is reused while this is unchanged; a new key replaces it.
- `promptCwd?` (`string`): The directory the model is told it works in, when it isn't the host
  `cwd` (inside a sandbox). Hosts use it for the prompt's environment
  section; tools resolve against the handle's cwd regardless.

Source: [src/chat/environment.ts:229](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L229)

### `EnvironmentExecutionInput`

Interface.

Input to a provider's `execution` factory.

```ts
interface EnvironmentExecutionInput
```

**Properties**

- `config` (`EnvironmentConfig`): The kind's resolved options.
- `cwd` (`string`): The session's resolved host directory.
- `environment` (`SessionEnvironment`): The session's environment, as persisted.
- `isolation?` (`IsolationPolicy`): OS sandbox policy from settings — meaningful for host-run kinds only.
- `sessionId` (`string`): Id of the session the context is for.

Source: [src/chat/environment.ts:215](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L215)

### `EnvironmentFieldDescriptor`

Interface.

Wire-safe description of one environment option, for pickers and settings sections.

```ts
interface EnvironmentFieldDescriptor
```

**Properties**

- `choices?` (`readonly { description?: string; label: string; value: string }[]`): Allowed values, for `choice` fields.
- `configured?` (`boolean`): Secrets: a value is stored, or its env var is set.
- `default?` (`EnvironmentConfigValue`): Schema default. Omitted for secrets.
- `description?` (`string`): Help text for the field.
- `fromEnv?` (`string`): Secrets / fallbacks: the env var currently supplying the value.
- `key` (`string`): Field key within the environment kind.
- `label` (`string`): Label shown next to the field.
- `placeholder?` (`string`): Placeholder shown in an empty input.
- `type` (`'string' | 'number' | 'boolean' | 'choice' | 'secret' | 'path'`): Value type of the field.
- `value?` (`EnvironmentConfigValue`): Current value — omitted for secrets.
- `visible` (`boolean`): False while its `when` condition doesn't hold — renderers hide it.

Source: [src/chat/environment-settings.ts:177](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L177)

### `EnvironmentPrepareInput`

Interface.

Input to a provider's `prepare` step.

```ts
interface EnvironmentPrepareInput
```

**Properties**

- `config` (`EnvironmentConfig`): The kind's resolved options.
- `cwd` (`string`): The session's host directory (`metadata.cwd`).
- `environment` (`SessionEnvironment`): The environment as stamped at creation (`state: 'provisioning'`).
- `sessionId` (`string`): Id of the session being provisioned.
- `signal?` (`AbortSignal`): Aborted when provisioning should stop, such as when the session is deleted.

Source: [src/chat/environment.ts:190](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L190)

### `EnvironmentPrepareResult`

Interface.

What a provider's `prepare` step produced.

```ts
interface EnvironmentPrepareResult
```

**Properties**

- `cwd?` (`string`): New host directory for the session — a worktree provider returns the
  worktree it created. Config, discovery and a host-run context follow it.
- `environment?` (`Partial<Omit<SessionEnvironment, 'error' | 'kind' | 'state'>>`): Fields to merge into the environment (`ref`, `execCwd`, resolved `branch`…).

Source: [src/chat/environment.ts:204](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L204)

### `EnvironmentProvider`

Interface.

One kind of session environment: how it is offered, prepared, executed in, acted on, and torn down. Register providers with [`createEnvironmentRegistry`](https://zidane.si/docs/reference/chat.md#createEnvironmentRegistry).

```ts
interface EnvironmentProvider
```

**Properties**

- `actions?` (`readonly EnvironmentAction[]`): Per-session commands, shown in the session menus.
- `archive?` (`(environment: SessionEnvironment, context: { config: EnvironmentConfig; cwd: string; release: () => Promise<void> }) => Promise<EnvironmentArchiveResult>`): Its owning session was archived: remove the environment when nothing
  would be lost (a worktree with no unsaved work). Call `release` right
  before removing: it ends the session's processes, and throws when the
  session was restored meanwhile. `prepare` recreates it on restore, so it
  must leave enough behind (a kept branch). Absent → kept while archived.
- `available?` (`(input: EnvironmentAvailabilityInput) => EnvironmentAvailability | Promise<EnvironmentAvailability>`): Whether it can be offered right now for that folder (credentials,
  binaries, git, a reachable daemon). Default: available.
- `checkFolder?` (`(cwd: string) => string`): The kind's folders are not on this host (a machine's,
  `docs/MACHINES.md`): the host checks a new session's folder with this
  (returning it, normalized, or throwing) instead of its own workspace
  confinement. Absent → the host's confinement applies.
- `configSchema?` (`readonly EnvironmentConfigField[]`): Options the kind takes — rendered by the Environments settings section.
- `description?` (`string`): One line for pickers and settings.
- `execution` (`(input: EnvironmentExecutionInput) => EnvironmentExecution`): The execution context for a ready environment.
- `idleSuspendMinutes?` (`(config: EnvironmentConfig) => number | undefined`): Minutes of inactivity after which the host lets go of an idle session's
  execution (a container is removed) and calls `suspend` when
  present (a sandbox is paused). Absent or `<= 0` → only on host exit /
  eviction.
- `kind` (`string`): Kind id stored in `SessionEnvironment.kind`, such as `docker`.
- `label` (`string`): Human label for pickers.
- `machine?` (`() => { id: string; lastSeen?: number; name: string; status: 'online' | 'offline' } | undefined`): The machine behind a machine kind, `undefined` once it is forgotten (the kind is then not listed).
- `note?` (`(input: EnvironmentAvailabilityInput) => Promise<string | undefined>`): One line pickers show under the kind when it's available for `cwd` —
  what creating one will do ("Clones acme/app, branching off main").
- `offByDefault?` (`boolean`): Hidden from pickers until turned on in Settings → Environments — kinds
  that need setting up first (a Docker install, a sandbox account).
- `prepare?` (`(input: EnvironmentPrepareInput) => Promise<EnvironmentPrepareResult>`): Make the environment exist. Absent → ready at creation. Must be
  idempotent: a retry (after a failure or a host restart mid-provisioning)
  runs it again over whatever a previous attempt left behind.
- `suggestBranch?` (`(config: EnvironmentConfig) => string`): Suggested branch for a new session — pickers pre-fill it and the host
  uses it when the creator leaves the branch empty.
- `suspend?` (`(environment: SessionEnvironment, context: { config: EnvironmentConfig }) => Promise<void>`): The host let go of the session's execution while the session lives on
  (idle eviction, host exit): pause what costs money while idle (a
  remote sandbox). The next `execution` resumes it. Absent → nothing to do.
- `takesBranch?` (`boolean | ((config: EnvironmentConfig) => boolean)`): Whether the creator names a branch (worktree, a fresh-worktree
  container, remote sandboxes). Pickers show a branch field when true.
- `teardown?` (`(environment: SessionEnvironment, context: { config: EnvironmentConfig; cwd: string }) => Promise<EnvironmentTeardownResult>`): Remove the environment when its owning session is deleted. Decides what
  "safe" means (a dirty worktree is kept, a sandbox is killed). Absent →
  nothing to remove.
- `verify?` (`(input: { config: EnvironmentConfig }) => Promise<EnvironmentVerifyResult>`): "Test connection" for the settings UIs: one cheap real call with the
  current options (ping the daemon, list sandboxes). Absent → no button.

Source: [src/chat/environment.ts:327](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L327)

### `EnvironmentRegistry`

Interface.

Lookup and registration of [`EnvironmentProvider`](https://zidane.si/docs/reference/chat.md#EnvironmentProvider)s by kind.

```ts
interface EnvironmentRegistry
```

**Properties**

- `get` (`(kind: string) => EnvironmentProvider | undefined`): Returns the provider for a kind, or `undefined`.
- `list` (`() => readonly EnvironmentProvider[]`): Lists every registered provider in registration order.
- `register` (`(provider: EnvironmentProvider) => void`): Add a provider. A kind already registered keeps the first registration.

Source: [src/chat/environment.ts:420](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L420)

### `EnvironmentRequest`

Interface.

What a creator asks for — the host turns it into a [`SessionEnvironment`](https://zidane.si/docs/reference/chat.md#SessionEnvironment).

```ts
interface EnvironmentRequest
```

**Properties**

- `base?` (`string`): Ref the branch is cut from.
- `branch?` (`string`): Branch to work on, for kinds that take one.
- `checkout?` (`EnvironmentCheckout`): Work on `branch` as it is rather than cutting it (worktree kinds).
- `kind` (`string`): Environment kind to create.

Source: [src/chat/environment.ts:583](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L583)

### `EnvironmentSecrets`

Interface.

Where `secret` fields live — see `environment-credentials.ts`.

```ts
interface EnvironmentSecrets
```

**Properties**

- `get` (`(kind: string, key: string) => string | undefined`): Returns the stored secret for a kind and field key, or `undefined`.
- `set` (`(kind: string, key: string, value: string | null) => void`): `null` clears.

Source: [src/chat/environment-settings.ts:43](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L43)

### `EnvironmentSettings`

Interface.

`Settings.environments`. Every part optional: absent = defaults.

```ts
interface EnvironmentSettings
```

**Properties**

- `config?` (`Record<string, Record<string, EnvironmentConfigValue>>`): Per-kind options (non-secret, non-bound fields only).
- `defaultKind?` (`string`): Kind new sessions start in. Default / invalid / disabled → `local`.
- `disabled?` (`string[]`): Kinds hidden from pickers and refused at creation. `local` can't be disabled.
- `enabled?` (`string[]`): Kinds turned on that are off by default (`provider.offByDefault`).

Source: [src/chat/environment-settings.ts:31](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L31)

### `EnvironmentSettingsChange`

Interface.

One edit from a settings UI.

```ts
interface EnvironmentSettingsChange
```

**Properties**

- `enabled?` (`boolean`): Turn the kind on (`true`) or off (`false`). Omit to leave it unchanged.
- `kind` (`string`): Environment kind the edit targets.
- `makeDefault?` (`boolean`): Make this kind the default for new sessions (enables it).
- `values?` (`Record<string, EnvironmentConfigValue | null>`): Field values; `null` resets to the default (or clears a secret).

Source: [src/chat/environment-settings.ts:305](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L305)

### `EnvironmentSettingsWrite`

Interface.

Result of [`applyEnvironmentSettingsChange`](https://zidane.si/docs/reference/chat.md#applyEnvironmentSettingsChange): the settings and secret writes to perform.

```ts
interface EnvironmentSettingsWrite
```

**Properties**

- `secrets` (`{ key: string; value: string | null }[]`): Secret writes for the environment credential store.
- `settingsPatch` (`Record<string, unknown>`): Patch for `settings.set` — `environments` plus any bound keys.

Source: [src/chat/environment-settings.ts:319](https://github.com/Tahul/zidane/blob/main/src/chat/environment-settings.ts#L319)

### `EnvironmentTeardownResult`

Interface.

Outcome of tearing down a session's environment.

```ts
interface EnvironmentTeardownResult
```

**Properties**

- `reason?` (`string`): Why it was kept — surfaced to the user (e.g. "has uncommitted changes").
- `removed` (`boolean`): The environment is gone (or there was nothing to remove).

Source: [src/chat/environment.ts:246](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L246)

### `EnvSectionOptions`

Interface.

Options for [`envSection`](https://zidane.si/docs/reference/chat.md#envSection).

```ts
interface EnvSectionOptions
```

**Properties**

- `cwd` (`string`): The agent's actual working directory — what `process.cwd()` reports
  and what its tool calls resolve relative paths against. Distinct
  from `EnvSectionOptions.projectRoot`, which carries the
  project anchor when the user spawned from a subdirectory.
- `date?` (`string`): ISO date (YYYY-MM-DD). Defaults to today. Pinned for tests.
- `platform?` (`string`): Platform string. Defaults to `process.platform`. Pinned for tests.
- `projectRoot?` (`string`): Project root (typically the git root). When set AND different from
  `cwd`, the rendered env block discloses both AND adds a path-
  convention note — file paths coming from the user's `@`-completion
  are project-root-relative, NOT cwd-relative, so the model needs to
  know there's a wider anchor to resolve them against. When omitted
  or equal to `cwd`, the block stays compact (no project-root line,
  no note), so users running from a project's root see no extra text.

Source: [src/chat/agent-prompt.ts:50](https://github.com/Tahul/zidane/blob/main/src/chat/agent-prompt.ts#L50)

### `FailedUpdateRecord`

Interface.

The automatic install that failed last.

```ts
interface FailedUpdateRecord
```

**Properties**

- `at` (`number`): ms since epoch.
- `attempts?` (`number`): Failed attempts at this version. Absent counts as 1.
- `error` (`string`): Why it failed, one package per line.
- `transient?` (`boolean`): The failure may pass on its own (registry or tarball not reachable yet, a
  timeout): the version is retried after [`AUTO_INSTALL_RETRY_BACKOFF_MS`](https://zidane.si/docs/reference/chat.md#AUTO_INSTALL_RETRY_BACKOFF_MS).
  Absent means final for this version.
- `version` (`string`): Release the install tried to reach.

Source: [src/chat/auto-update-auto-install.ts:40](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L40)

### `FetchGithubUsageOptions`

Interface.

Options for [`fetchGithubUsage`](https://zidane.si/docs/reference/chat.md#fetchGithubUsage).

```ts
interface FetchGithubUsageOptions
```

**Properties**

- `client?` (`GithubClient`): GitHub client to read state and identity from. Defaults to the shared `githubClient()`.
- `now?` (`() => number`): Clock in epoch milliseconds. Defaults to `Date.now`.
- `probe?` (`boolean`): Ask GitHub (`/rate_limit`); otherwise report what zidane observed. Ignored when the env opts out.
- `probeDeadlineMs?` (`number`): How long a probe may take before the observed state is returned instead. Default 4 s.

Source: [src/chat/github-usage.ts:31](https://github.com/Tahul/zidane/blob/main/src/chat/github-usage.ts#L31)

### `FetchOAuthRedirectResult`

Interface.

Outcome of calling the loopback callback server.

```ts
interface FetchOAuthRedirectResult
```

**Properties**

- `message?` (`string`): Best-effort message extracted from the response body (HTML title or trimmed text).
- `status` (`number`): Status code from the callback server. 2xx = success.

Source: [src/chat/oauth-redirect.ts:145](https://github.com/Tahul/zidane/blob/main/src/chat/oauth-redirect.ts#L145)

### `FetchProviderUsageOptions`

Interface.

Options for the provider usage fetchers.

```ts
interface FetchProviderUsageOptions
```

**Properties**

- `refresh?` (`boolean`): Ignore (and replace) a still-fresh cached snapshot.

Source: [src/chat/provider-usage.ts:177](https://github.com/Tahul/zidane/blob/main/src/chat/provider-usage.ts#L177)

### `FetchRepoInfoOptions`

Interface.

Options of [`fetchRepoInfo`](https://zidane.si/docs/reference/chat.md#fetchRepoInfo).

```ts
interface FetchRepoInfoOptions
```

**Properties**

- `fetch?` (`typeof fetch`): Test seam. Default: the global `fetch`.
- `token?` (`string`): A GitHub token: private repositories, and 5,000 calls an hour instead of 60.

Source: [src/chat/git/repo-info.ts:48](https://github.com/Tahul/zidane/blob/main/src/chat/git/repo-info.ts#L48)

### `FileEntry`

Interface.

One row in the project file catalog. `path` is forward-slashed + relative to `cwd`.

```ts
interface FileEntry
```

**Properties**

- `kind` (`'file' | 'dir'`): Entry kind. Directories are derived from the discovered file paths
  (every ancestor of a listed file), so only directories that contain
  at least one non-ignored file appear — mirroring `git ls-files`
  semantics, which never lists empty or fully-ignored dirs.
- `name` (`string`): Basename — used for prefix ranking in the completion provider.
- `path` (`string`): Forward-slashed relative path from the discovery cwd. Stable across OSes. No trailing slash for dirs.
- `source` (`'git' | 'fs'`): Source — `git` when listed via `git ls-files`, `fs` from the walk fallback.

Source: [src/chat/files-discovery.ts:23](https://github.com/Tahul/zidane/blob/main/src/chat/files-discovery.ts#L23)

### `FolderLocation`

Interface.

Where a folder sits: its checkout, and the project that checkout belongs to.

```ts
interface FolderLocation
```

**Properties**

- `checkoutRoot` (`string`): The git checkout holding the folder (a linked worktree's own), the folder itself outside git.
- `projectRoot` (`string`): The project root: the main checkout for a linked worktree ([`projectRootOf`](https://zidane.si/docs/reference/chat.md#projectRootOf)).

Source: [src/chat/project-root.ts:124](https://github.com/Tahul/zidane/blob/main/src/chat/project-root.ts#L124)

### `FollowRunningUpdateOptions`

Interface.

Options for [`followRunningUpdate`](https://zidane.si/docs/reference/chat.md#followRunningUpdate).

```ts
interface FollowRunningUpdateOptions
```

**Properties**

- `cacheDir` (`string`): Directory that holds the lock, log and result.
- `onLine?` (`(line: string) => void`): Each line logged from now on, starting with the run's lines so far.
- `pollMs?` (`number`): Defaults to 250 ms.

Source: [src/chat/auto-update-worker.ts:360](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L360)

### `ForkSessionParams`

Interface.

Inputs to [`forkSession`](https://zidane.si/docs/reference/chat.md#forkSession).

```ts
interface ForkSessionParams
```

**Properties**

- `source` (`string | ForkSource`): Parent session — an id to load from `store`, or the parent itself (a
  host holding the live `Session` passes it so unsaved turns fork too).
- `title?` (`string`): Title override. Default: the parent's explicit title, if any.
- `turnId` (`string`): Last turn the fork keeps (inclusive); the prefix is made protocol-clean.
- `userDir` (`string`): Root of the per-session artifact tree (`<userDir>/sessions/<id>/`) that
  the parent's persisted-output stubs point into. The fork gets its own
  copies so deleting the parent can't strand the fork's content.

Source: [src/chat/session-factory.ts:199](https://github.com/Tahul/zidane/blob/main/src/chat/session-factory.ts#L199)

### `GenerateStructuredDataOptions`

Interface.

Options for [`generateStructuredData`](https://zidane.si/docs/reference/chat.md#generateStructuredData).

```ts
interface GenerateStructuredDataOptions
```

**Properties**

- `maxContextTurns?` (`number`): Cap on turns rendered into the context block. Default 12 (most recent).
- `model` (`string`): Model id used for the call.
- `prompt` (`string`): Instruction describing the value to produce.
- `provider` (`Provider`): Provider whose `stream()` makes the call.
- `schema` (`SchemaInput`): Shape of the result — a plain JSON Schema object, or a zod v4 schema
  (converted automatically; the return type is then inferred from it).
  Becomes the forced tool's inputSchema.
- `signal?` (`AbortSignal`): Cancellation signal forwarded to the provider's stream.
- `system?` (`string`): Optional system prompt. Defaults to a terse data-extraction persona.
- `turns?` (`readonly SessionTurn[]`): Conversation turns to fold in as compacted context (same clipping as
  session auto-titling). Omit or pass empty for a context-free call.

Source: [src/chat/generate-data.ts:33](https://github.com/Tahul/zidane/blob/main/src/chat/generate-data.ts#L33)

### `GitBranchRow`

Interface.

A local branch in the switcher: when it last moved, and how it stands against the default branch.

```ts
interface GitBranchRow
```

**Properties**

- `isDefault` (`boolean`): The repository's default branch.
- `name` (`string`): The branch name.
- `stats?` (`{ added: number; ahead: number; behind: number; removed: number }`): Against the default branch; absent for branches past the stats budget.
- `updatedAt` (`number`): Last commit time (ms).

Source: [src/chat/git/types.ts:64](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L64)

### `GitCommitInfo`

Interface.

One commit, for a sha chip's hover card.

```ts
interface GitCommitInfo
```

**Properties**

- `added` (`number`): Lines added.
- `author` (`string`): The author's name.
- `authoredAt` (`number`): Author date, ms.
- `files` (`number`): Files changed.
- `removed` (`number`): Lines removed.
- `sha` (`string`): The full sha.
- `subject` (`string`): The message's first line.

Source: [src/chat/git/types.ts:7](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L7)

### `GitInfo`

Interface.

The composer's git bar: where the checkout stands.

```ts
interface GitInfo
```

**Properties**

- `ahead` (`number`): Commits on HEAD not on the base (as of the last fetch). The base is
  `upstream` when there is one (to push), else the default branch.
- `behind` (`number`): Commits on the base not on HEAD (to pull).
- `branch` (`string`): Current branch; the short commit when `detached`.
- `changes` (`{ added: number; files: number; removed: number }`): Uncommitted changes vs HEAD, untracked files included.
- `defaultBranch` (`string | null`): `origin/main`-style ref; null when none resolves.
- `detached` (`boolean`): HEAD is not on a branch.
- `publishRemote` (`string | null`): A remote the branch can be published to, when it has no upstream yet.
- `upstream` (`string | null`): The branch's remote copy (`origin/feat-x`); null when it has none.

Source: [src/chat/git/types.ts:25](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L25)

### `GitPr`

Interface.

The branch's open (or draft) pull request, from `gh`.

```ts
interface GitPr
```

**Properties**

- `draft` (`boolean`): A draft.
- `number` (`number`): Its number.
- `title` (`string`): Its title (may be empty).
- `url` (`string`): Its page.

Source: [src/chat/git/types.ts:52](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L52)

### `GitPrItem`

Interface.

An open pull request in the switcher (`gh pr list`).

```ts
interface GitPrItem
```

**Properties**

- `additions` (`number`): Lines added.
- `author` (`string`): Its author's login.
- `branch` (`string`): Its head branch.
- `checks` (`'pending' | 'pass' | 'fail' | null`): The checks' verdict, `null` when there are none.
- `deletions` (`number`): Lines removed.
- `draft` (`boolean`): A draft.
- `files` (`number`): Files changed.
- `fork` (`boolean`): Opened from a fork (checked out through `gh pr checkout`).
- `group` (`'other' | 'mine' | 'review'`): Yours, waiting on your review, or anyone else's.
- `local` (`boolean`): Its branch exists locally (a plain switch; otherwise a checkout).
- `number` (`number`): Its number.
- `review` (`'required' | 'approved' | 'changes' | null`): The review verdict, `null` when there is none.
- `title` (`string`): Its title.
- `updatedAt` (`number`): Last update (ms).
- `url` (`string`): Its page.

Source: [src/chat/git/types.ts:76](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L76)

### `GitStatusEntry`

Interface.

One path's git status.

```ts
interface GitStatusEntry
```

**Properties**

- `path` (`string`): Relative to the folder.
- `status` (`'renamed' | 'added' | 'deleted' | 'ignored' | 'modified' | 'untracked'`): What changed.

Source: [src/chat/workspace/types.ts:58](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/types.ts#L58)

### `GitWatches`

Interface.

Shared repository watchers, from [`createGitWatches`](https://zidane.si/docs/reference/chat.md#createGitWatches).

```ts
interface GitWatches<Owner>
```

**Type parameters**

- `Owner`

**Properties**

- `owners` (`(cwd: string) => Owner[]`): Who watches `cwd` now.
- `release` (`(owner: Owner) => void`): Releases every share `owner` holds (a connection dropped).
- `unwatch` (`(owner: Owner, cwd: string) => void`): Releases one of `owner`'s shares of `cwd`; the watcher stops with the last share.
- `watch` (`(cwd: string, owner: Owner) => Promise<boolean>`): Starts (or shares) the watcher of `cwd` for `owner`; each call takes one
  `GitWatches.unwatch`. `false`: nothing is watched (not a
  repository, or released meanwhile).

Source: [src/chat/git/watch.ts:106](https://github.com/Tahul/zidane/blob/main/src/chat/git/watch.ts#L106)

### `Hint`

Interface.

Single hint entry — `<key> <label>`, optionally followed by an inline
secondary chord (`<key><extra.key> <label> <extra.label>`) for compound
shortcuts like `ctrl+m/l model effort`.

Colour fields are advisory hex strings (resolved against the active
theme by the renderer); leaving them undefined lets the renderer pick
its conventional fallback (e.g. `warn` for keys, `dim` for labels; the
secondary chord takes its key's color).

```ts
interface Hint
```

**Properties**

- `extra?` (`{ key: string; keyColor?: string; keySpec?: string; label: string; labelColor?: string }`): Optional secondary chord + value rendered inline after the primary
  pair: `<key><extra.key> <label> <extra.label>`. Lets one hint advertise
  a compound shortcut (e.g. `ctrl+m/l model effort`) without forcing
  callers into a parallel struct or a custom renderer.
- `key` (`string`): Key or chord shown for the hint, such as `ctrl+l`.
- `keyColor?` (`string`): Optional override for the key color. Renderer default: warn.
- `label` (`string`): Short action text shown after the key.
- `labelColor?` (`string`): Optional override for the label color. Renderer default: dim.

Source: [src/chat/hints.ts:24](https://github.com/Tahul/zidane/blob/main/src/chat/hints.ts#L24)

### `HostRegistry`

Interface.

Saved remote hosts and their tokens, persisted under the data directory.

```ts
interface HostRegistry
```

**Properties**

- `add` (`(input: { name?: string; token: string; url: string }) => Promise<HostEntry>`): Verify the host (it must answer as a zidane-server with this token),
  then save it and its token. Adding a host already saved (same server
  id) updates its URL and token in place, keeping its name unless one is
  given. Throws [`HostRegistryError`](https://zidane.si/docs/reference/chat-pure.md#HostRegistryError).
- `get` (`(id: string) => HostEntry | undefined`): Returns the saved host with this id, or `undefined`.
- `list` (`() => HostEntry[]`): Returns every saved host.
- `remove` (`(id: string) => boolean`): Forget the entry and its token; `false` when it wasn't saved.
- `rename` (`(id: string, name: string) => HostEntry`): Renames a saved host. Throws [`HostRegistryError`](https://zidane.si/docs/reference/chat-pure.md#HostRegistryError) for an empty name, or when no saved host has this id.
- `setUrl` (`(id: string, url: string) => Promise<HostEntry>`): Move a saved host to a new URL, with its saved token: saved only once
  the same server (same id) answers there. Name and token are kept.
  `lastConnectedAt` and the server's name and version are refreshed.
  Throws [`HostRegistryError`](https://zidane.si/docs/reference/chat-pure.md#HostRegistryError) (`identity` when another server answers).
- `test` (`(id: string) => Promise<HostProbeResult>`): Connect with the saved token and say who answered; refreshes `lastConnectedAt` on success.
- `token` (`(id: string) => string | undefined`): The saved token, for transports. Never display it.

Source: [src/chat/hosts/registry.ts:45](https://github.com/Tahul/zidane/blob/main/src/chat/hosts/registry.ts#L45)

### `ImageModelEntry`

Interface.

An image model for the pickers, with its availability.

```ts
interface ImageModelEntry
```

**Properties**

- `available` (`boolean`): Credentials allow generating with this model.
- `isDefault?` (`boolean`): The model `generate_image` uses while `Settings.imageModel` is empty or stale.
- `modelId` (`string`): Model id within the provider.
- `modelName` (`string`): Display name of the model.
- `providerKey` (`string`): Key of the provider that serves the model.
- `providerLabel` (`string`): Display name of the provider.
- `reason?` (`string`): Why it is not available.
- `references` (`boolean`): The model takes reference images.
- `transparency` (`boolean`): The model can return a transparent background.

Source: [src/chat/image-models.ts:20](https://github.com/Tahul/zidane/blob/main/src/chat/image-models.ts#L20)

### `InlineAutocompleteContext`

Interface.

Input handed to an [`InlineAutocompleteProvider`](https://zidane.si/docs/reference/chat.md#InlineAutocompleteProvider) on each query.

```ts
interface InlineAutocompleteContext
```

**Properties**

- `cursor` (`number`): Cursor offset into `text`. Suggestions complete the token ENDING here.
- `cwd` (`string`): Workspace directory relative paths resolve against.
- `signal` (`AbortSignal`): Aborted when the buffer changes before the provider resolves.
- `text` (`string`): Full prompt buffer.

Source: [src/chat/inline-autocomplete.ts:27](https://github.com/Tahul/zidane/blob/main/src/chat/inline-autocomplete.ts#L27)

### `InlineAutocompleteProvider`

Interface.

Source of inline ghost-text suggestions. Providers are queried in order by [`resolveInlineSuggestion`](https://zidane.si/docs/reference/chat.md#resolveInlineSuggestion).

```ts
interface InlineAutocompleteProvider
```

**Properties**

- `deadlineMs?` (`number`): Per-provider deadline override (ms), capped at
  `MAX_PROVIDER_DEADLINE_MS`. The default
  (`PROVIDER_DEADLINE_MS`) suits instant sources like directory
  listings; a provider backed by a slow source (e.g. a local LLM) can
  extend its own budget — the wait is async (typing is never blocked,
  an edit aborts the query) and only delays providers ordered AFTER
  this one.
- `id` (`string`): Stable id — used for first-wins dedupe across extensions.
- `suggest` (`(ctx: InlineAutocompleteContext) => string | readonly string[] | Promise<string | readonly string[] | null> | null`): Return the ghost suffix (or an ordered list of candidate suffixes —
  the host shows the first and lets the user rotate with up/down) to
  append at the cursor, or null/''/\[] for no suggestion. Must respect
  its deadline budget — it runs per keystroke (debounced by the
  host).

Source: [src/chat/inline-autocomplete.ts:41](https://github.com/Tahul/zidane/blob/main/src/chat/inline-autocomplete.ts#L41)

### `InPlaceUpdateResult`

Interface.

Outcome of [`performInPlaceSelfUpdate`](https://zidane.si/docs/reference/chat.md#performInPlaceSelfUpdate).

```ts
interface InPlaceUpdateResult
```

**Properties**

- `binaryPath?` (`string`): Resolved binary path after the swap.
- `installedVersion?` (`string`): Version that was installed (from the tarball's package.json).
- `reason?` (`string`): Why we bailed. Always present on `refused` / `failed`.
- `status` (`'refused' | 'failed' | 'success'`): `'success' | 'refused' | 'failed'`.

Source: [src/chat/auto-update.ts:925](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L925)

### `InstalledCompanion`

Interface.

A companion package found on disk.

```ts
interface InstalledCompanion
```

**Properties**

- `binaryPath` (`string`): The bin as found on PATH (NOT resolved). `detectPackageManager` wants
  this: `~/.volta/bin/<name>` identifies Volta even though its realpath is
  the generic `volta-shim`.
- `packageDir` (`string`): Root of the installed package.
- `packageName` (`string`): npm package name.
- `version` (`string`): Version from the installed `package.json`.

Source: [src/chat/auto-update-companion.ts:27](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-companion.ts#L27)

### `InstalledVersionState`

Interface.

Snapshot of the running version against the version installed on disk.

```ts
interface InstalledVersionState
```

**Properties**

- `checkedAt` (`number`): When the disk was last probed (ms since epoch).
- `installed` (`string | null`): Version found on disk for the package; `null` when the probe found nothing (dev checkout, PATH without the bin).
- `installing?` (`boolean`): An install holds the lock right now (`cacheDir` watchers only). The disk
  may be half-written, so no restart is offered until it is released.
- `restartRequired` (`boolean`): `installed` is strictly newer than `running` — the process is stale.
- `running` (`string`): Version the running process was built with.

Source: [src/chat/auto-update-restart.ts:99](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L99)

### `InstalledVersionWatcher`

Interface.

Handle returned by [`watchInstalledVersion`](https://zidane.si/docs/reference/chat.md#watchInstalledVersion).

```ts
interface InstalledVersionWatcher
```

**Properties**

- `check` (`() => InstalledVersionState`): Re-probe now. Returns the fresh state (also delivered to `onChange` when it differs).
- `close` (`() => void`): Stops the watcher. Later checks return the last state and `onChange` no longer fires.
- `current` (`() => InstalledVersionState`): Last delivered state.

Source: [src/chat/auto-update-restart.ts:138](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L138)

### `InteractionHub`

Interface.

Holds interactive requests from the model (questions, plans) until a client answers them.

```ts
interface InteractionHub
```

**Properties**

- `answerThrough` (`(awakeables: InteractionAwakeables) => void`): Answer prompts through a durable runtime's awakeables from now on (see the module docs). Set once the host's runs exist.
- `cancel` (`(requestId: string, reason?: string) => { ok: boolean }`): Rejects pending request `requestId` with `reason`. `ok` is `false` when no such request is pending.
- `clearSession` (`(sessionId: string) => void`): Drop every pending interaction for a session (session delete / dispose).
- `list` (`(sessionId?: string) => PendingInteraction[]`): Returns the pending requests, only those of `sessionId` when given.
- `rehydrate` (`(sessionId: string) => Promise<PendingInteraction[]>`): Restores the requests persisted for `sessionId` by an earlier process and returns the pending ones.
- `requestInteractionFor` (`(sessionId: string) => (request: InteractionRequest, options?: RequestInteractionOptions) => Promise<InteractionResponse>`): Build the `requestInteraction` callback bound to one session.
- `resolveAutomatically` (`(sessionId?: string) => number`): Answer every pending MODEL interaction with the canonical automatic
  response — the runtime half of `interactionMode: 'skip'`. Flipping the
  setting mid-run must not leave modals that were raised under `ask` hanging
  for an answer the new mode says nobody will give. Host-synthesized prompts
  are left alone (local code is awaiting a real decision on those). Returns
  the number resolved.
- `respond` (`(requestId: string, response: InteractionResponse) => { ok: boolean }`): Answers pending request `requestId`. `ok` is `false` when no such request is pending.

Source: [src/chat/host/interactions.ts:162](https://github.com/Tahul/zidane/blob/main/src/chat/host/interactions.ts#L162)

### `InteractionsActions`

Interface.

Stable queue actions exposed by [`InteractionsProvider`](https://zidane.si/docs/reference/chat.md#InteractionsProvider). Read them with [`useInteractionsActions`](https://zidane.si/docs/reference/chat.md#useInteractionsActions).

```ts
interface InteractionsActions
```

**Properties**

- `cancelAll` (`(reason?: string) => void`): Cancel every queued request. Used by abort / session teardown.
- `cancelHead` (`(reason?: string) => void`): Cancel the head request. Meant for per-call cancel
  affordances (a "skip this" button, a GUI's close-modal-X) — the
  built-in TUI currently cancels via the run-level `cancelAll` path
  only. Kept on the public API so consumer renderers can wire it
  without a follow-up surface change.
- `dropById` (`(requestId: string) => void`): Remove the queued entry whose `request.id` matches, WITHOUT settling
  it locally — used by the daemon runtime when ANOTHER window resolved
  the same shared interaction. No-op when absent.
- `enqueue` (`(entry: PendingInteractionEntry) => void`): Add a request to the FIFO queue.
- `resolveHead` (`(response: InteractionResponse) => void`): Resolve the head request with `response`.

Source: [src/chat/interactions-protocol.ts:1288](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L1288)

### `KeyBindingDef`

Interface.

Static metadata for one action — used by the loader, hint rows, settings UI.

```ts
interface KeyBindingDef
```

**Properties**

- `action` (`KeyAction`): Stable identifier — JSON key + TS union member.
- `default` (`string`): Default trigger spec (e.g. `'ctrl+o'`).
- `description` (`string`): Longer description for the settings UI and the annotated JSON file.
- `group` (`KeyBindingGroup`): Group label rendered as a section header in the keybindings panel.
  Items are listed in [`KEYBINDING_DEFS`](https://zidane.si/docs/reference/chat.md#KEYBINDING_DEFS) order; the panel just
  inserts a header whenever this field changes between adjacent
  entries — so groups stay contiguous and the order here is the
  order on screen.
- `label` (`string`): Short label used in shortcut hint rows.

Source: [src/chat/keybindings.ts:139](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L139)

### `KeyBindingSection`

Interface.

One contiguous section of the catalog — every adjacent action of a single
`group` rendered together with the section header inserted once.
Non-adjacent runs of the same group are NOT merged: catalog order
wins so the on-screen story matches the on-disk file.

`rows` may carry built-in `KeyBindingDef`s (the common case) or
extension-owned ones (when an extension contributes a keybinding).
Both share label / description / spec — see `KeyBindingRow`.

```ts
interface KeyBindingSection
```

**Properties**

- `group` (`string`): Group name shared by every row in the section.
- `rows` (`readonly KeyBindingRow[]`): Rows in the section, in catalog order.

Source: [src/chat/keybindings.ts:671](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L671)

### `ListExtensionSourceOptions`

Interface.

Options of [`listExtensionSource`](https://zidane.si/docs/reference/chat.md#listExtensionSource).

```ts
interface ListExtensionSourceOptions
```

**Properties**

- `cwd?` (`string`, default `process.cwd()`): Where a relative folder resolves.
- `fresh?` (`boolean`): Ignore the cached listing and list again.
- `io?` (`Pick<InstallIo, 'fetch'>`): Overrides for `fetch`. Used by tests.
- `signal?` (`AbortSignal`): Stops the listing.
- `token?` (`string`): A GitHub token: 5,000 API calls an hour instead of 60, and private repositories.
- `version?` (`string`, default `ZIDANE_VERSION`): The running zidane version, for the default source's builtin rows.

Source: [src/chat/extension-browse.ts:41](https://github.com/Tahul/zidane/blob/main/src/chat/extension-browse.ts#L41)

### `ListProjectFilesOptions`

Interface.

Options for `listProjectFiles`.

```ts
interface ListProjectFilesOptions
```

**Properties**

- `cwd?` (`string`): Discovery root. Default: `process.cwd()`.
- `maxFiles?` (`number`): Cap on discovered files. Default: 10,000. Bigger lists fall back to
  a truncated set. Derived directory entries are appended on top with
  their own equal cap, so the returned array can exceed `maxFiles`
  (bounded by 2×).
- `signal?` (`AbortSignal`): Aborts the scan early. Useful when re-running on rapid project switches.

Source: [src/chat/files-discovery.ts:81](https://github.com/Tahul/zidane/blob/main/src/chat/files-discovery.ts#L81)

### `LocalEntryOps`

Interface.

The entry actions of a local folder, plus copying in paths of this machine.

```ts
interface LocalEntryOps extends EntryOps
```

**Properties**

- `copy` (`(from: string, toDir: string) => Promise<EntryResult>`, from `EntryOps`): Copies `from` into folder `toDir` under its name, or the next free one.
- `create` (`(path: string, kind: EntryKind, options?: EntryCreateOptions) => Promise<EntryResult>`, from `EntryOps`): Creates a file or a folder at `path`, making the folders on the way.
- `importPaths` (`(dir: string, paths: readonly string[]) => Promise<EntryImportResult>`): Copies absolute paths of this machine (a drop from the file manager) into folder `dir`, each under a free name.
- `move` (`(from: string, to: string) => Promise<EntryResult>`, from `EntryOps`): Moves (or renames) `from` to `to`, making the folders on the way.
- `remove` (`(path: string) => Promise<EntryResult>`, from `EntryOps`): Removes `path` (a folder whole). Where it goes (the trash, or nowhere) is the port's.

Source: [src/chat/workspace/local-entries.ts:57](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/local-entries.ts#L57)

### `MarkdownDefDiagnostic`

Interface.

A non-fatal problem with one markdown definition file. The file may still load.

```ts
interface MarkdownDefDiagnostic
```

**Properties**

- `kind` (`'command' | 'agent'`): Whether the file is a command or an agent definition.
- `message` (`string`): Human-readable description of the problem.
- `path` (`string`): Absolute path of the file the problem belongs to.

Source: [src/markdown-defs/types.ts:5](https://github.com/Tahul/zidane/blob/main/src/markdown-defs/types.ts#L5)

### `McpServerStatus`

Interface.

One server's state beside its config.

```ts
interface McpServerStatus
```

**Properties**

- `authState` (`McpAuthState`): Whether it needs, has or is getting a sign-in.
- `lastError?` (`string`): The last sign-in's failure.
- `test?` (`McpTestOutcome`): The last Test, since this host started.
- `toolError?` (`string`): Why the last listing failed.
- `tools?` (`{ description?: string; name: string }[]`): Its tools when they were last listed (any host on this data dir).
- `toolsUpdatedAt?` (`number`): Unix ms of that listing.

Source: [src/chat/mcp-status.ts:28](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-status.ts#L28)

### `McpTestOutcome`

Interface.

The last Test of a server: how it answered, and when.

```ts
interface McpTestOutcome extends McpTestResult
```

**Properties**

- `at` (`number`): Unix ms.
- `kind` (`McpTestOutcomeKind`, from `McpTestResult`): How the server answered.
- `message?` (`string`, from `McpTestResult`): User-facing explanation for non-`ok` kinds.
- `tools?` (`number`, from `McpTestResult`): Number of tools listed. Set for `ok`.

Source: [src/chat/mcp-status.ts:22](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-status.ts#L22)

### `McpToolToggleSet`

Interface.

Per-tool enable state and toggle handler for one MCP server.

```ts
interface McpToolToggleSet
```

**Properties**

- `enabledSet` (`ReadonlySet<string>`): Live set of ENABLED tool names — `Set` for O(1) `has`.
- `toggle` (`(toolName: string) => void`): Flip one tool on/off; persists immediately via `setSetting`.

Source: [src/chat/mcp-tool-toggle.ts:42](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tool-toggle.ts#L42)

### `ModelAliasContext`

Interface.

Context for resolving Claude `model:` aliases in agent files.

```ts
interface ModelAliasContext
```

**Properties**

- `catalogIds?` (`ReadonlySet<string>`): Model ids the spawn catalog lists, when one was built.
- `parentProvider?` (`string`): Provider name of the parent session, e.g. `'anthropic'`.

Source: [src/markdown-defs/model-aliases.ts:15](https://github.com/Tahul/zidane/blob/main/src/markdown-defs/model-aliases.ts#L15)

### `ModelApproval`

Interface.

A decision model's verdict on one tool call: what it chose and how sure it
was (`p`, absent when the provider reports no probability). On a `tool`
event: the call was auto-approved (`approve`) or auto-denied (`deny_*`).
On an approval request: the model's concern when it handed the call to you.

```ts
interface ModelApproval
```

**Properties**

- `by` (`'model'`): Always `model`: the decision model, not the user, answered.
- `choice` (`ModelApprovalChoice`): What it decided for the call.
- `model` (`string`): Model that answered.
- `p?` (`number`): Probability of `choice`, in `[0, 1]`.

Source: [src/chat/types.ts:710](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L710)

### `ModelFallbackTarget`

Interface.

The model a session runs on, wire-named: provider, model and (optionally)
the stored account of the provider (a credential's stable id; absent = the
provider's active account).

```ts
interface ModelFallbackTarget
```

**Properties**

- `account?` (`string`): Pinned account id for the provider.
- `modelId` (`string`): Model id.
- `providerKey` (`string`): Provider key.

Source: [src/chat/model-chain-metadata.ts:19](https://github.com/Tahul/zidane/blob/main/src/chat/model-chain-metadata.ts#L19)

### `ModelInfo`

Interface.

Structural model metadata — compatible with pi-ai's `Model` interface but
not coupled to it, so hosts can pass either pi-ai-shaped objects or a
custom registry of their own.

Deliberately a structural duplicate of pi-ai's `Model` rather than a
re-export: keeps the public API stable when pi-ai bumps shape, and lets
hosts implement their own registry without depending on pi-ai's types.

Lives here (rather than `./config`) because `ProviderDescriptor.models`
needs it — defining it in `./config` would create an import cycle.

```ts
interface ModelInfo
```

**Properties**

- `contextWindow` (`number`): Maximum context window in tokens.
- `cost?` (`{ cacheRead?: number; cacheWrite?: number; input: number; output: number }`): Rates in USD per million tokens.
- `id` (`string`): Model id as the provider's API names it.
- `input?` (`readonly ('text' | 'image')[]`): Input modalities the model accepts.
- `maxTokens?` (`number`): Maximum output tokens.
- `name?` (`string`): Display name.
- `options?` (`readonly ModelOption[]`): Model-specific toggleable knobs surfaced in the UI (e.g. Anthropic's
  "fast mode"). Each option is an independent boolean the user flips on the
  active model; enabled options are collected into `StreamOptions.modelOptions`
  keyed by `ModelOption.id` and applied to the wire by the provider.
  Omitted / empty → the model has no custom options and the picker is hidden.
- `provider?` (`string`): Catalog provider id the model belongs to.
- `reasoning?` (`boolean`): Whether the model exposes a reasoning knob.
- `tier?` (`ModelTier`): Size class within the provider's lineup; unset = derived from the id (`modelTier`).

Source: [src/chat/providers.ts:69](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L69)

### `ModelOption`

Interface.

A single toggleable model option. Declarative metadata only — the provider
owns the actual request-body translation (keyed on `id`). Kept generic
(not "fast mode"-specific) so new per-model knobs land without a type change.

```ts
interface ModelOption
```

**Properties**

- `costMultiplier?` (`{ cacheRead?: number; cacheWrite?: number; input?: number; output?: number }`): Optional cost multipliers applied to the model's base rates while the
  option is enabled. `{ input: 2, output: 2 }` doubles both. Used by the
  cost estimator so the footer reflects premium-priced modes (fast mode).
- `description?` (`string`): One-line description of the trade-off (shown in the picker row).
- `id` (`string`): Stable identifier; the key under which the toggle persists + reaches the provider.
- `label` (`string`): Short label shown in the picker (e.g. "Fast mode").

Source: [src/chat/providers.ts:103](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L103)

### `ModelTestResult`

Interface.

Outcome of [`testModel`](https://zidane.si/docs/reference/chat.md#testModel).

```ts
interface ModelTestResult
```

**Properties**

- `durationMs` (`number`): Time the probe took, in ms.
- `message?` (`string`): Failure detail.
- `model?` (`string`): Model the probe used.
- `ok` (`boolean`): True when the model answered.
- `provider?` (`string`): Provider the probe ran on.
- `reason?` (`string`): Why it failed: `unavailable` (no such provider, no credentials), `timeout` or `error`.

Source: [src/chat/model-test.ts:11](https://github.com/Tahul/zidane/blob/main/src/chat/model-test.ts#L11)

### `ModelUsageSummary`

Interface.

Per-model token/spend totals across a session — see [`usageByModel`](https://zidane.si/docs/reference/chat.md#usageByModel).

```ts
interface ModelUsageSummary
```

**Properties**

- `cacheCreation` (`number`): Tokens written to the prompt cache.
- `cacheRead` (`number`): Tokens read from the prompt cache.
- `cost` (`number`): Summed USD cost across turns that carried one (provider-reported or estimated).
- `costComplete` (`boolean`): `false` when at least one turn had no cost figure (no published rate
  for the model) — `cost` is then a floor, not a total.
- `input` (`number`): Fresh (uncached) input tokens.
- `modelId` (`string`): Provider-echoed model id, or `"unknown"` for turns that never recorded one.
- `output` (`number`): Output tokens.
- `turns` (`number`): Number of turns on this model.

Source: [src/chat/store.ts:1666](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1666)

### `OAuthCredential`

Interface.

A stored OAuth credential.

```ts
interface OAuthCredential extends CredentialIdentity
```

**Properties**

- `access` (`string`): OAuth access token.
- `email?` (`string`): Email of the signed-in account (Claude: from the token exchange, else
  looked up once from the OAuth profile, see `backfillAccountEmails`).
- `emailChecked?` (`boolean`): The profile lookup for `email` answered with none (or refused the token): not tried again.
- `expires?` (`number`): Expiry time of the access token, as recorded by the OAuth flow.
- `id?` (`string`, from `CredentialIdentity`)
- `kind` (`'oauth'`): Discriminant that marks an OAuth credential.
- `label?` (`string`): User-given account name ("work", "personal"). Optional — absent reads as
  `DEFAULT_OAUTH_ACCOUNT_LABEL` (see `oauthAccountLabel`). Survives token
  refresh because the refresh path spreads the stored entry into the rotated
  one (see `resolveOAuthApiKey`).
- `refresh?` (`string`): Refresh token used to renew the access token.

Source: [src/chat/credentials.ts:98](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L98)

### `OAuthFlowOptions`

Interface.

Callbacks and switches for [`runOAuthLogin`](https://zidane.si/docs/reference/chat.md#runOAuthLogin).

```ts
interface OAuthFlowOptions
```

**Properties**

- `callbackServer?` (`false`): `false`: the browser is on another machine (a client of a remote host).
  zidane's providers bind no loopback listener here and read the redirect
  from `onManualCodeInput`.
- `onDeviceCode?` (`(info: OAuthDeviceCodeInfo) => void`): Called when the provider initiates a device-code flow (e.g. GitHub Copilot).
  Surface `userCode` + `verificationUri` so the user can complete the flow in
  the browser. Required by pi-ai's `OAuthLoginCallbacks` since 0.75.5 — built-in
  Anthropic / OpenAI Codex providers don't fire it today, but third-party
  descriptors can.
- `onManualCodeInput?` (`() => Promise<string>`): The redirect URL (or a pasted code), racing the loopback listener (pi-ai's `onManualCodeInput`).
- `onProgress?` (`(message: string) => void`): Called with each progress message from the OAuth flow (token exchange, etc.).
- `onPrompt?` (`(prompt: OAuthPrompt) => Promise<string>`): Called when the provider needs a free-form input from the user (paste-the-code flows,
  step-up confirmations, etc.). Mirrors pi-ai's `OAuthLoginCallbacks.onPrompt` verbatim
  so callers don't lose `placeholder` / `allowEmpty` hints. Required for any flow that
  prompts, such as the code paste of a provider with `usesCallbackServer: false`:
  the login throws when the provider prompts and this is unset.
- `onSelect?` (`(prompt: OAuthSelectPrompt) => Promise<string | undefined>`): Called when the provider needs the user to pick between multiple flow options
  (e.g. browser vs device-code, a Google Cloud project). Return the chosen
  option `id`, or `undefined` to cancel. Without it a lone option is taken
  and a real choice fails the sign-in: guessing would pick for the user.
- `onUrl` (`(url: string, instructions?: string) => void`): Called when the provider emits its login URL — typically right after the callback server starts.
- `openBrowser?` (`boolean`): `false`: never launch a browser on this machine. Default true.
- `signal?` (`AbortSignal`): Abort the in-flight login (e.g. user pressed esc).

Source: [src/chat/oauth.ts:81](https://github.com/Tahul/zidane/blob/main/src/chat/oauth.ts#L81)

### `OnboardingInstallSignalsInput`

Interface.

What [`onboardingInstallSignals`](https://zidane.si/docs/reference/chat.md#onboardingInstallSignals) reads.

```ts
interface OnboardingInstallSignalsInput
```

**Properties**

- `dataDir` (`string`): Directory of `credentials.json`.
- `sessions` (`boolean`): The session store holds at least one session.
- `state` (`TuiState`): The state file's content.
- `surfaceFirstRun` (`boolean`): This app has never run on this machine.

Source: [src/chat/host/onboarding/detect.ts:102](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/detect.ts#L102)

### `PackageManagerCommand`

Interface.

A package manager install command.

```ts
interface PackageManagerCommand
```

**Properties**

- `argv` (`string[]`): Argv that, when execve'd, installs `packageName@channel` globally.
- `id` (`PackageManagerId`): Stable identifier. `npm` is the fallback when no other signal fires.
- `note?` (`string`): Free-form note for the user — explains *why* this command was chosen
  (e.g. "Volta-managed install detected via \~/.volta/bin").

Source: [src/chat/auto-update.ts:121](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L121)

### `PendingAnnotation`

Interface.

An annotation waiting to be appended to an edit tool's output.

```ts
interface PendingAnnotation
```

**Properties**

- `originalInput` (`Record<string, unknown>`): Tool input before it was reduced to the approved hunks.
- `originalPayload` (`EditPayload`): Edit payload before it was reduced to the approved hunks.
- `outcomes` (`readonly EditOutcome[]`): Per-hunk decisions from the approval.

Source: [src/chat/host/edit-outcome-ledger.ts:22](https://github.com/Tahul/zidane/blob/main/src/chat/host/edit-outcome-ledger.ts#L22)

### `PendingApprovalsStore`

Interface.

Durable persistence for pending safe-mode approvals so a request raised
before a crash still surfaces after restart. Implementations: sqlite repo
(GUI), JSON file (daemon), in-memory no-op (tests).

```ts
interface PendingApprovalsStore
```

**Properties**

- `clear` (`() => void`): Removes every request.
- `delete` (`(id: string) => void`): Removes the request with this id.
- `insert` (`(request: WireApprovalRequest) => void`): Saves a pending request.
- `list` (`() => WireApprovalRequest[]`): Returns every saved request.

Source: [src/chat/host/environment.ts:29](https://github.com/Tahul/zidane/blob/main/src/chat/host/environment.ts#L29)

### `PendingInteraction`

Interface.

Pending interaction row as returned by `interactions.list`.

```ts
interface PendingInteraction
```

**Properties**

- `live` (`boolean`): `true` when a live agent loop is waiting on the answer. `false` for one restored after a restart.
- `request` (`InteractionRequest`): The interaction request awaiting an answer.
- `sessionId` (`string`): Session that raised the interaction.

Source: [src/chat/host/signals.ts:275](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L275)

### `PendingInteractionEntry`

Interface.

A queued interaction request with the callbacks that settle it.

```ts
interface PendingInteractionEntry
```

**Properties**

- `cancel` (`(reason?: string) => void`): The user / system cancelled — distinct from `resolve` so renderers can
  differentiate "user picked something" from "user gave up / aborted".
  Live entries reject the tool's Promise; resumed entries decide what
  to persist as the cancelled tool_result.
- `request` (`InteractionRequest`): The request waiting for the user.
- `resolve` (`(response: InteractionResponse) => void`): The user picked something — forward to the live tool Promise or the resumed tool_result writer.

Source: [src/chat/interactions-protocol.ts:1271](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L1271)

### `PerformInPlaceUpdateOptions`

Interface.

Options for [`performInPlaceSelfUpdate`](https://zidane.si/docs/reference/chat.md#performInPlaceSelfUpdate).

```ts
interface PerformInPlaceUpdateOptions
```

**Properties**

- `binaryPath?` (`string`): Resolved path of the running binary. Defaults to `process.execPath`.
- `channel?` (`string`): dist-tag to install. Defaults to `'latest'`.
- `dryRun?` (`boolean`): Don't replace — just download, verify and extract.
- `fetcher?` (`typeof fetch`): Fetch implementation. Defaults to `globalThis.fetch`.
- `packageName` (`string`): Platform-specific tarball package, e.g. `'zidane-tui-darwin-arm64'`.
- `refuseUnderPrefixes?` (`readonly string[]`): When set, refuse the swap if the resolved binary lives under one of these prefixes.
- `registry?` (`string`): Registry base URL. Defaults to `https://registry.npmjs.org`.
- `signal?` (`AbortSignal`): Abort signal for the download.
- `tarballBinaryPath?` (`string`): Inside the tarball — usually `bin/<name>`. Defaults to `bin/<basename(binaryPath)>`.
- `timeoutMs?` (`number`): Network timeout for the whole update, metadata and download. Defaults to 30 000 ms — tarballs are MB-scale.

Source: [src/chat/auto-update.ts:901](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L901)

### `PerformSelfUpdateOptions`

Interface.

Options for [`performSelfUpdate`](https://zidane.si/docs/reference/chat.md#performSelfUpdate).

```ts
interface PerformSelfUpdateOptions
```

**Properties**

- `channel?` (`string`): dist-tag to install. Defaults to `'latest'`.
- `detect?` (`(opts?: DetectPackageManagerOptions) => PackageManagerCommand`): Inject for tests.
- `dryRun?` (`boolean`): Don't actually spawn — return the command we WOULD run.
- `env?` (`Readonly<Record<string, string>>`): Override env (e.g. set `npm_config_loglevel=error`).
- `packageManager?` (`'auto' | PackageManagerId`): Force a specific PM instead of auto-detect.
- `packageName` (`string`): Full npm package name to install.
- `signal?` (`AbortSignal`): Abort the install. Killing a package manager mid-install can break the
  global install: hosts that must survive their own exit run the install
  in the update worker (`./auto-update-worker`) instead.
- `spawn?` (`{ (command: string, options?: SpawnOptionsWithoutStdio): ChildProcessWithoutNullStreams; (command: string, options: SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioPipe>): ChildProcessByStdio<Writable, Readable, Readable>; (command: string, options: SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioNull>): ChildProcessByStdio<Writable, Readable, null>; (command: string, options: SpawnOptionsWithStdioTuple<StdioPipe, StdioNull, StdioPipe>): ChildProcessByStdio<Writable, null, Readable>; (command: string, options: SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioPipe>): ChildProcessByStdio<null, Readable, Readable>; (command: string, options: SpawnOptionsWithStdioTuple<StdioPipe, StdioNull, StdioNull>): ChildProcessByStdio<Writable, null, null>; (command: string, options: SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioNull>): ChildProcessByStdio<null, Readable, null>; (command: string, options: SpawnOptionsWithStdioTuple<StdioNull, StdioNull, StdioPipe>): ChildProcessByStdio<null, null, Readable>; (command: string, options: SpawnOptionsWithStdioTuple<StdioNull, StdioNull, StdioNull>): ChildProcessByStdio<null, null, null>; (command: string, options: SpawnOptions): ChildProcess; (command: string, args?: readonly string[], options?: SpawnOptionsWithoutStdio): ChildProcessWithoutNullStreams; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioPipe>): ChildProcessByStdio<Writable, Readable, Readable>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioNull>): ChildProcessByStdio<Writable, Readable, null>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioPipe, StdioNull, StdioPipe>): ChildProcessByStdio<Writable, null, Readable>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioPipe>): ChildProcessByStdio<null, Readable, Readable>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioPipe, StdioNull, StdioNull>): ChildProcessByStdio<Writable, null, null>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioNull>): ChildProcessByStdio<null, Readable, null>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioNull, StdioNull, StdioPipe>): ChildProcessByStdio<null, null, Readable>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioNull, StdioNull, StdioNull>): ChildProcessByStdio<null, null, null>; (command: string, args: readonly string[], options: SpawnOptions): ChildProcess }`): Inject for tests.
- `stderr?` (`WritableStream`): Stream that receives the package manager's stderr instead of inheriting stdio.
- `stdout?` (`WritableStream`): When set, write `pm stdout/stderr` to these streams instead of stdio inherit.
- `timeoutMs?` (`number`): Kill the package manager after this long. Defaults to 10 minutes: without
  a TTY a PM that stops on a prompt (auth, a pnpm global-dir setup) would
  otherwise hang forever. `0` disables the limit.

Source: [src/chat/auto-update.ts:746](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L746)

### `Picked`

Interface.

A model pick made in the picker.

```ts
interface Picked
```

**Properties**

- `account?` (`string`): The stored account of `provider` to run on (a credential's stable id,
  `AuthMethod.id`); absent = the provider's active account.
- `effort?` (`'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'adaptive'`): Reasoning effort the user selected for `model`. Only meaningful when
  the active model exposes reasoning (`ModelInfo.reasoning === true`);
  `undefined` otherwise. Forwarded to `agent.run` as `thinking` so
  providers route it to their per-model reasoning control.
- `fallbacks?` (`ModelChainEntry[]`): Ordered run-level fallback models AFTER the primary `provider`+`model`
  pair — tried in order when a stream fails terminally on the previous
  target (see `AgentRunOptions.model`). Picked via the model picker's
  numbered multi-select; absent/empty means single-model behavior.
  Effort and model options always follow the PRIMARY model only.
- `model` (`string`): Primary model id.
- `modelOptions?` (`Record<string, boolean>`): Enabled model-specific options for `model`, keyed by option id (e.g.
  `{ fast: true }`). Only meaningful for models that declare
  `ModelInfo.options`; reset whenever the model changes so a toggle never
  leaks onto a model that doesn't support it. Forwarded to `agent.run` as
  `modelOptions`.
- `provider` (`ProviderAuth`): Provider the model runs on.

Source: [src/chat/types.ts:533](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L533)

### `Plan`

Interface.

A plan.

```ts
interface Plan
```

**Properties**

- `awaiting?` (`'user' | 'revision'`): While `proposed`: waiting on the user's decision, or on the agent's revision.
- `body?` (`string`): Proposal body (Markdown).
- `closedAt?` (`number`): Entered a closed state (or `stopped`), epoch ms.
- `createdAt` (`number`): Created, epoch ms.
- `history?` (`PlanRevision[]`): Earlier proposals, newest last, at most [`MAX_PLAN_HISTORY`](https://zidane.si/docs/reference/chat.md#MAX_PLAN_HISTORY).
- `id` (`string`): Plan id.
- `kind` (`'proposal' | 'adhoc'`): Proposed through `present_plan`, or started by a plain `todowrite`.
- `parentPlanId?` (`string`): A sub-agent plan: the parent scope's plan it works for.
- `parentStepId?` (`string`): A sub-agent plan: the parent step that was in progress when it started.
- `pendingNotes?` (`string[]`): User edits the agent has not been told about yet (delivered at the next turn boundary).
- `pendingRevisionComment?` (`string`): While awaiting a revision: the user's note, moved into history by the revision.
- `requestId?` (`string`): The `present_plan` request (tool call id) of the current proposal.
- `revision` (`number`): Revision number, 1 for the first proposal.
- `runId?` (`string`): The run that last applied this plan. A run end only closes the plans it applied.
- `scope` (`string`): [`SESSION_SCOPE`](https://zidane.si/docs/reference/chat-pure.md#SESSION_SCOPE), or a sub-agent run id.
- `state` (`PlanState`): Lifecycle state.
- `steps` (`PlanStep[]`): The steps, in order.
- `stoppedAt?` (`string`): While `stopped`: the step it stopped at.
- `stopReason?` (`PlanStopReason`): While `stopped`: why.
- `title` (`string`): Title. Empty for an adhoc plan.
- `updatedAt` (`number`): Last change, epoch ms.

Source: [src/chat/plans.ts:112](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L112)

### `PlanLifecycleHooksOptions`

Interface.

Options for [`createPlanLifecycleHooks`](https://zidane.si/docs/reference/chat.md#createPlanLifecycleHooks).

```ts
interface PlanLifecycleHooksOptions
```

**Properties**

- `remindOpenSteps?` (`boolean`, default `true`): Send the open-steps reminder.

Source: [src/chat/plan-hooks.ts:21](https://github.com/Tahul/zidane/blob/main/src/chat/plan-hooks.ts#L21)

### `PlanProgress`

Interface.

Step counts of a plan.

```ts
interface PlanProgress
```

**Properties**

- `activeIndex` (`number`): Index of the active step (the first in progress, else the first pending), or -1.
- `cancelled` (`number`): Cancelled steps.
- `completed` (`number`): Completed steps.
- `inProgress` (`number`): Steps in progress.
- `pending` (`number`): Pending steps.
- `total` (`number`): All steps.

Source: [src/chat/plans.ts:1043](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L1043)

### `PlanProposal`

Interface.

Parsed input of a `present_plan` call.

```ts
interface PlanProposal
```

**Properties**

- `plan` (`string`): Full plan body. Markdown.
- `steps?` (`readonly PlanProposalStep[]`): Optional structured step list — surfaced as a checklist preview.
- `title` (`string`): Short headline summary — used as the modal title.

Source: [src/chat/interactions-protocol.ts:83](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L83)

### `PlanProposalStep`

Interface.

One step of a [`PlanProposal`](https://zidane.si/docs/reference/chat.md#PlanProposal).

```ts
interface PlanProposalStep
```

**Properties**

- `description?` (`string`): Longer step detail.
- `id` (`string`): Step id.
- `title` (`string`): Short step title.

Source: [src/chat/interactions-protocol.ts:73](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L73)

### `PlanRequest`

Interface.

A pending request to approve a plan.

```ts
interface PlanRequest extends BaseRequest
```

**Properties**

- `createdAt` (`number`, from `BaseRequest`): Unix ms when the request was raised.
- `id` (`string`, from `BaseRequest`): Stable id — the `tool_call.id` from the model.
- `kind` (`'plan'`): Request kind discriminator.
- `origin` (`InteractionOrigin`, from `BaseRequest`): Whether the model or the host raised this request.
- `payload` (`PlanProposal`): The plan to approve.
- `runId?` (`string`, from `BaseRequest`): Run that emitted the call, when known.
- `tool` (`'present_plan'`): Tool canonical name.
- `turnId?` (`string`, from `BaseRequest`): Turn id the call lives in.

Source: [src/chat/interactions-protocol.ts:249](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L249)

### `PlanResponse`

Interface.

The user's answer to a [`PlanRequest`](https://zidane.si/docs/reference/chat.md#PlanRequest).

```ts
interface PlanResponse
```

**Properties**

- `answeredBy?` (`InteractionAnswerer`): Answered by an orchestrator, an extension or a client rather than at a window.
- `automatic?` (`boolean`): Resolved by automatic mode rather than by a person. Serialized so the
  model can tell a real decision from a default it is free to override.
- `comment?` (`string`): Optional free-text feedback — shown to the model verbatim.
- `decision` (`PlanDecision`): The user's decision.
- `kind` (`'plan'`): Response kind discriminator.

Source: [src/chat/interactions-protocol.ts:291](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L291)

### `PlanRevision`

Interface.

An earlier proposal of a revised plan.

```ts
interface PlanRevision
```

**Properties**

- `body` (`string`): Its body, cut to a fixed length.
- `comment?` (`string`): The user's revision note.
- `revision` (`number`): Revision number of that proposal (1-based).
- `steps` (`readonly { id: string; title: string }[]`): Its steps (titles only).
- `title` (`string`): Its title.

Source: [src/chat/plans.ts:98](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L98)

### `PlanRunNotes`

Interface.

The notes a run starting now needs, and the edits they deliver.

```ts
interface PlanRunNotes
```

**Properties**

- `delivered` (`{ count: number; planId: string }[]`): Pending edits these notes deliver: mark them sent once the run started.
- `notes` (`(SystemNote & { text: string })[]`): Notes for `systemNotes`.

Source: [src/chat/plan-notes.ts:34](https://github.com/Tahul/zidane/blob/main/src/chat/plan-notes.ts#L34)

### `PlansBag`

Interface.

On-disk shape at `session.metadata.plans`.

```ts
interface PlansBag
```

**Properties**

- `plans` (`Plan[]`): Every kept plan, oldest first.
- `seen` (`string[]`): Recently applied event ids (idempotency), oldest first.
- `v` (`number`): `PLANS_BAG_VERSION`.

Source: [src/chat/plans.ts:158](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L158)

### `PlansSnapshot`

Interface.

What a window shows of a session's plans.

```ts
interface PlansSnapshot
```

**Properties**

- `active` (`ActivePlanSummary | null`): The plan worth a glance (composer pill, TUI footer), or `null`.
- `plans` (`PlanView[]`): Every kept plan, oldest first; an applying plan with no live run carries `possiblyInterrupted`.

Source: [src/chat/host/plans/index.ts:29](https://github.com/Tahul/zidane/blob/main/src/chat/host/plans/index.ts#L29)

### `PlanStep`

Interface.

One step of a plan; the todo item the agent tracks.

```ts
interface PlanStep
```

**Properties**

- `added?` (`true`): Added after approval (by the agent's `todowrite` or the user), not part of the approved proposal.
- `cancelledBy?` (`StepCancelledBy`): Set when `status` is `cancelled`: the agent left it out of a write, or the user cancelled it.
- `description?` (`string`): Longer detail, from the proposal.
- `id` (`string`): Stable id: the proposal's step id, or the id the agent gave in `todowrite`.
- `note?` (`string`): An approved step: the agent's latest wording of it in `todowrite`, when it
  differs from the title. The approved title never changes under the agent.
- `status` (`TodoStatus`): Progress.
- `title` (`string`): One-line title.
- `updatedAt` (`number`): Last status or title change, epoch ms (UI hints only, never sent to the model).

Source: [src/chat/plans.ts:75](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L75)

### `PlanView`

Interface.

The plan a session shows as "in progress", with a display-only liveness flag.

```ts
interface PlanView extends Plan
```

**Properties**

- `awaiting?` (`'user' | 'revision'`, from `Plan`): While `proposed`: waiting on the user's decision, or on the agent's revision.
- `body?` (`string`, from `Plan`): Proposal body (Markdown).
- `closedAt?` (`number`, from `Plan`): Entered a closed state (or `stopped`), epoch ms.
- `createdAt` (`number`, from `Plan`): Created, epoch ms.
- `history?` (`PlanRevision[]`, from `Plan`): Earlier proposals, newest last, at most [`MAX_PLAN_HISTORY`](https://zidane.si/docs/reference/chat.md#MAX_PLAN_HISTORY).
- `id` (`string`, from `Plan`): Plan id.
- `kind` (`'proposal' | 'adhoc'`, from `Plan`): Proposed through `present_plan`, or started by a plain `todowrite`.
- `parentPlanId?` (`string`, from `Plan`): A sub-agent plan: the parent scope's plan it works for.
- `parentStepId?` (`string`, from `Plan`): A sub-agent plan: the parent step that was in progress when it started.
- `pendingNotes?` (`string[]`, from `Plan`): User edits the agent has not been told about yet (delivered at the next turn boundary).
- `pendingRevisionComment?` (`string`, from `Plan`): While awaiting a revision: the user's note, moved into history by the revision.
- `possiblyInterrupted?` (`true`): An applying plan whose run is not live and whose lease has lapsed: it
  may have crashed. Display only; the next lease claim settles it.
- `requestId?` (`string`, from `Plan`): The `present_plan` request (tool call id) of the current proposal.
- `revision` (`number`, from `Plan`): Revision number, 1 for the first proposal.
- `runId?` (`string`, from `Plan`): The run that last applied this plan. A run end only closes the plans it applied.
- `scope` (`string`, from `Plan`): [`SESSION_SCOPE`](https://zidane.si/docs/reference/chat-pure.md#SESSION_SCOPE), or a sub-agent run id.
- `state` (`PlanState`, from `Plan`): Lifecycle state.
- `steps` (`PlanStep[]`, from `Plan`): The steps, in order.
- `stoppedAt?` (`string`, from `Plan`): While `stopped`: the step it stopped at.
- `stopReason?` (`PlanStopReason`, from `Plan`): While `stopped`: why.
- `title` (`string`, from `Plan`): Title. Empty for an adhoc plan.
- `updatedAt` (`number`, from `Plan`): Last change, epoch ms.

Source: [src/chat/plans.ts:1091](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L1091)

### `PlatformTarballOptions`

Interface.

Options for [`resolvePlatformPackage`](https://zidane.si/docs/reference/chat.md#resolvePlatformPackage).

```ts
interface PlatformTarballOptions
```

**Properties**

- `arch?` (`string`): Override `process.arch`.
- `libc?` (`'glibc' | 'musl'`): Override the libc check (returned from `detectLibc`).
- `platform?` (`Platform`): Override `process.platform`.
- `prefix?` (`string`): Prefix joined to the platform key. Defaults to `'zidane-tui-'`.

Source: [src/chat/auto-update.ts:1218](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L1218)

### `ProbeLocalModelServersOptions`

Interface.

How to probe for local model servers.

```ts
interface ProbeLocalModelServersOptions
```

**Properties**

- `fetch?` (`typeof fetch`): Fetch implementation. Default: the global `fetch`.
- `timeoutMs?` (`number`): How long each server gets to answer, in milliseconds. Default 300.

Source: [src/chat/host/onboarding/detect.ts:71](https://github.com/Tahul/zidane/blob/main/src/chat/host/onboarding/detect.ts#L71)

### `ProfileSwitchNoteContext`

Interface.

What a `AgentProfile.switchNote` function is given.

```ts
interface ProfileSwitchNoteContext
```

**Properties**

- `allowInteraction` (`boolean`): Interactive prompts (`present_plan`, `ask_user`) are on.
- `tools` (`readonly string[]`): Names of the profile's own tools. Tools every profile gets (interaction, skills, MCP, …) are not listed.

Source: [src/chat/agents.ts:77](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L77)

### `ProjectEntry`

Interface.

Per-project settings stored in `projects.json`.

```ts
interface ProjectEntry
```

**Properties**

- `durability?` (`DurabilityMode`): Overrides the global `Settings.durability` for this project (`./durability`).
- `safelist?` (`string[]`): Tool entries the user approved for this project.

Source: [src/chat/safe-mode.ts:50](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L50)

### `ProjectUserPath`

Interface.

A discovered path tagged with whether it lives in the project or the user dir.

```ts
interface ProjectUserPath
```

**Properties**

- `path` (`string`): Absolute candidate path.
- `source` (`'user' | 'project'`): Whether the path lives under the project root or the home directory.

Source: [src/chat/project-user-paths.ts:27](https://github.com/Tahul/zidane/blob/main/src/chat/project-user-paths.ts#L27)

### `PromptAttachment`

Interface.

Attachment carried alongside the prompt text (base64, no data-URL prefix).

```ts
interface PromptAttachment
```

**Properties**

- `data` (`string`): Base64 content.
- `mediaType` (`string`): MIME type of the attachment.
- `name?` (`string`): File name, when known.
- `original?` (`{ data: string; mediaType: string; name?: string }`): The file as the user picked it, when the composer converted it before
  sending (an image re-encoded to fit the provider's cap): the host saves
  these bytes for the agent's tools, the model gets `data`. Absent when
  `data` is the file itself.

Source: [src/chat/host/signals.ts:104](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L104)

### `PromptSubmissions`

Interface.

A host's submit entry points, each running the prompt transforms once
before the host method it stands for. `consumed` says an extension took
the prompt instead: `handled` (nothing ran or queued), or `steer` /
`followUp` (delivered to the live run).

```ts
interface PromptSubmissions
```

**Properties**

- `pushQueued` (`(...args: [sessionId: string, message: string, activatedSkills?: string[], fields?: Pick<SendOptions, 'metadata' | 'origin' | 'literal' | 'clientMessageId'>]) => Promise<{ delivered: 'command' | 'send' | 'steer' | 'queued' } & { consumed?: PromptConsumed }>`): `chat.pushQueued`. Consumed: `delivered` is `send` (handled) or `steer`.
- `queueAdd` (`(...args: [sessionId: string, text: string, activatedSkills?: string[], attachments?: PromptAttachment[], asCommand?: string, fields?: Pick<SendOptions, 'metadata' | 'origin' | 'literal' | 'clientMessageId'>, delivery?: QueueDelivery]) => Promise<{ delivered: 'steer' | 'sent' | 'queued' } & { consumed?: PromptConsumed }>`): `queue.add`. `delivery` (see `ChatHost.queueAdd`): an interceptor may
  ask to steer or follow up instead. Consumed: `delivered` is `sent`
  (handled), or `steer` when a run took it on the transforms' request.
- `send` (`(opts: SendOptions) => Promise<{ consumed?: PromptConsumed; runId: string }>`): `chat.send`: `runId` is `''` when consumed.

Source: [src/chat/host/prompt-transforms.ts:123](https://github.com/Tahul/zidane/blob/main/src/chat/host/prompt-transforms.ts#L123)

### `ProviderDescriptor`

Interface.

Describes one provider: how to build it, how it authenticates and which models it offers.

```ts
interface ProviderDescriptor
```

**Properties**

- `accounts?` (`boolean`): The factory honors [`ProviderFactoryOptions`](https://zidane.si/docs/reference/chat.md#ProviderFactoryOptions): an instance can run on
  any of the provider's stored accounts. Pickers only offer an account
  choice for these providers, and the host only pins one for them — a
  factory that ignored the options would bill the active account instead.
- `apiKeyPlaceholder?` (`string`): Placeholder shown in the wizard's API-key input.
- `contextWindowEnvVar?` (`string`): Env var holding a user-supplied context window (in tokens) for providers
  that have no model registry to advertise one — chiefly the `local`
  provider, whose runtime (Ollama, vLLM, …) doesn't expose its loaded
  model's window over the OpenAI-compat API. When set, [`getContextWindow`](https://zidane.si/docs/reference/chat.md#getContextWindow)
  falls back to resolving this env var (via `resolveContextWindow`)
  after the registry lookup comes up empty. The value is either a single
  window (`"32768"`, applied to every model) or a per-model map
  (`"llama3.1:8b=32768, qwen=128k, 64k"`, with an optional bare fallback).
  Pair it with a [`CustomField`](https://zidane.si/docs/reference/chat.md#CustomField) that mirrors the user's value into the
  same `envVar` so the footer's context indicator and auto-compaction work
  for local models.
- `credentialFileKey?` (`string`): Key under which credentials live in `credentials.json`. Defaults to
  `key`. Built-ins that override it: OpenAI (`openai-codex`, the key the
  harness provider looks up) and xAI (`xai-oauth`).
- `customFields?` (`readonly CustomField[]`): Additional free-form fields the wizard should collect from the user
  (e.g. base URL for a self-hosted endpoint, default model slug for a
  provider with no fixed catalogue). Each field is persisted in the
  apikey credential's `customFields` map and mirrored into the matching
  `envVar` at TUI launch.

  When `envKey` is also set, the wizard collects custom fields **before**
  the API key. When `envKey` is omitted, the wizard skips the API-key
  step entirely — `customFields` becomes the sole credential mechanism
  (used by the `local` provider, which authenticates via baseURL only).
- `decisionModels?` (`readonly string[]`): Decision models the provider's native Decisions endpoint serves
  (`Provider.decisions`), listed by the settings' decision-model picker.
  Omit for providers without one.
- `defaultModel?` (`string`): Default model id to seed the picker with. When omitted, the TUI falls
  back to `descriptor.factory().meta.defaultModel`, which constructs the
  provider — fine for lazy-credential factories, but breaks for factories
  that throw on missing credentials before the wizard runs. Setting this
  eagerly lets the TUI render the auth wizard without ever instantiating
  the provider.
- `description?` (`string`): One line on what the provider serves and how it signs in, shown under
  its name in provider pickers (the setup wizard, Settings › Accounts).
  Example: `'Claude models. Your Claude plan, or an API key.'`.
- `envKey?` (`string`): Env var checked when detecting whether the user has an API key
  configured for this provider. Optional — set to `undefined` when the
  provider only supports OAuth (or only credentials via a custom path).
- `extraModels?` (`readonly ModelInfo[]`): Extra models merged AHEAD of pi-ai's registry list (de-duped by id, pi-ai
  wins on collision). Unlike `models` this augments rather than
  replaces — used to surface freshly-shipped models pi-ai hasn't bundled
  yet without losing the rest of the registry. Ignored when `models`
  is set.
- `factory` (`(options?: ProviderFactoryOptions) => Provider`): Factory that builds a fresh `Provider` instance. Called on every session
  activation. Some factories (e.g. zidane's built-in `anthropic`) eagerly
  resolve credentials at construction time and throw when none are
  configured — set `defaultModel` on the descriptor to avoid the TUI
  calling the factory before the user has picked + authed a provider.
- `imageModels?` (`readonly ImageModelInfo[]`): Image models the provider's image endpoint serves (`Provider.images`),
  listed by the model picker and the `generate_image` tool. Omit for
  providers without one.
- `integration?` (`string`): Id of the integration (`ctx.integrations.register`) this provider signs
  in with. Pickers show the provider signed in while the integration has a
  connection (one account per connection, the default active); "Sign in"
  starts the integration's connect flow (an `oauth` or `custom` method)
  instead of `oauthProvider`; usage probes authenticate with its
  connections. The factory resolves its token through
  `ctx.integrations.connection(id, { account })` and `resolve`, where
  `account` is `ProviderFactoryOptions.account` for a session pinned
  to one (set `accounts` to offer the choice).
- `key` (`string`): Unique identifier. Used as the registry key, persisted in `state.json`
  as the resumed provider, and (by default) as the credential-file key.
- `label` (`string`): Display name shown in the TUI's provider picker and labels.
- `modelAliases?` (`Readonly<Record<string, string>>`): Back-compat aliases for persisted model ids whose upstream slug changed.
  Used for metadata lookups only; aliases are not surfaced as picker rows.
- `models?` (`readonly ModelInfo[]`): Override the model list returned for this provider's picker. When set,
  skips pi-ai's registry entirely. Useful for hosts maintaining their
  own model catalogue or for custom providers pi-ai doesn't know about.
- `oauthHint?` (`string`): Optional copy appended to the wizard's "OAuth" method description.
  Use to communicate why a user might pick OAuth (e.g. subscription tier,
  org-wide SSO). Shown after `browser-based sign-in`. Skip the leading
  space — the wizard adds it. Example: `'Claude Pro/Max subscription'`.
- `oauthProvider?` (`OAuthProviderInterface`): pi-ai (or compat) OAuth provider. When present, the wizard offers an
  OAuth option in addition to API key.
- `optionsFor?` (`(modelId: string) => readonly ModelOption[] | undefined`): Resolve the custom [`ModelOption`](https://zidane.si/docs/reference/chat.md#ModelOption)s a given model id supports. Used to
  attach options (e.g. fast mode) to models that come from pi-ai's registry —
  which `extraModels` can't reach because pi-ai wins the id collision.
  Returns `undefined` / `[]` for models with no options. Models that already
  carry `options` (e.g. via `extraModels`) keep theirs.
- `piProviderId?` (`string`): pi-ai provider id used to look up models in pi-ai's built-in registry.
  Defaults to `key`. Only Codex differs (`openai` → `openai-codex`).
- `signInLabel?` (`string`): The account a browser sign-in uses, as a button names it: `Sign in with <signInLabel>` (`'Claude'`, `'ChatGPT'`). Defaults to `label`.
- `usage?` (`ProviderUsageProbe`): Subscription-usage probe, for a provider without a built-in one (an
  extension's). Runs through the built-ins' pipeline: the user's
  subscription-usage setting and `ZIDANE_NO_USAGE_CHECK`, one probe per
  stored credential of `credentialKind` (every account), expired OAuth
  tokens renewed in their own slot first, a one-minute cache, and the last
  good snapshot kept through a transient failure. Ignored when the
  credential key has a built-in probe.

Source: [src/chat/providers.ts:192](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L192)

### `ProviderFactoryOptions`

Interface.

What a provider instance is built FOR beyond its defaults: one stored
account of the provider instead of the active one. Built by the host from a
session's pinned account (`credentials.providerFactoryOptions`): an OAuth
account rides as `account` (resolved to its slot on every token read), an
API-key account as its `apiKey`.

```ts
interface ProviderFactoryOptions
```

**Properties**

- `account?` (`string`): Stable id of the OAuth account to run on (`ProviderCredential.id`).
- `apiKey?` (`string`): The API key to run on, for an API-key account.

Source: [src/chat/providers.ts:153](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L153)

### `ProviderFieldView`

Interface.

A provider's custom field as a settings form asks for it.

```ts
interface ProviderFieldView
```

**Properties**

- `hint?` (`string`): One line under it.
- `key` (`string`): Its key in the credential's `customFields`.
- `label` (`string`): Its caption.
- `placeholder?` (`string`): Its placeholder.
- `required?` (`boolean`): Saving needs it.
- `secret?` (`boolean`): Typed masked; never shown back, kept when left blank.

Source: [src/chat/credentials.ts:997](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L997)

### `ProviderUsageProbe`

Interface.

A provider's own subscription-usage probe (see `ProviderDescriptor.usage`).

```ts
interface ProviderUsageProbe
```

**Properties**

- `credentialKind?` (`'oauth' | 'apikey'`, default `'oauth'`): Kind of stored credential the probe authenticates with.
- `fetch` (`(credential: ProviderCredential, signal: AbortSignal) => Promise<ProviderUsageReport>`): Report usage for one stored credential: an OAuth grant as stored (access
  token unexpired, extra fields included) or an API key. A throw, or no
  answer within 15s, reads as `unavailable` with the error's message.

Source: [src/chat/providers.ts:177](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L177)

### `ProviderUsageReport`

Interface.

What a [`ProviderUsageProbe`](https://zidane.si/docs/reference/chat.md#ProviderUsageProbe) reports; the host stamps identity and time.

```ts
interface ProviderUsageReport
```

**Properties**

- `credits?` (`ProviderUsageCredits | null`): Credit balance, for pay-as-you-go plans.
- `error?` (`string | null`): Short failure reason when `status` isn't `ok`.
- `note?` (`string`): A short hint shown under the windows.
- `plan?` (`string | null`): Plan name, when the provider reports one.
- `status?` (`'ok' | 'unauthorized' | 'unavailable'`): Outcome. Default `'ok'`.
- `windows?` (`readonly ProviderUsageWindow[]`): Rate-limit or quota windows. `usedPercent` is clamped to 0..100.

Source: [src/chat/providers.ts:161](https://github.com/Tahul/zidane/blob/main/src/chat/providers.ts#L161)

### `ProvisionSessionEnvironmentInput`

Interface.

Input for [`provisionSessionEnvironment`](https://zidane.si/docs/reference/chat.md#provisionSessionEnvironment).

```ts
interface ProvisionSessionEnvironmentInput
```

**Properties**

- `config` (`(kind: string) => EnvironmentConfig`): Returns the provider configuration for an environment kind.
- `load` (`() => Promise<SessionData | null>`): The session as persisted now.
- `persist` (`(patch: Record<string, unknown>) => Promise<boolean>`): Persist a metadata patch and announce it; `false` when the session no longer exists, throws on other write errors.
- `registry` (`EnvironmentRegistry`): Registry that supplies the provider for the session's environment kind.
- `sessionId` (`string`): Id of the session whose environment is provisioned.
- `signal?` (`AbortSignal`): Aborts the attempt. Whatever `prepare` produced is then torn down.

Source: [src/chat/environment-provisioning.ts:20](https://github.com/Tahul/zidane/blob/main/src/chat/environment-provisioning.ts#L20)

### `ProvisionSessionEnvironmentResult`

Interface.

Outcome of [`provisionSessionEnvironment`](https://zidane.si/docs/reference/chat.md#provisionSessionEnvironment).

```ts
interface ProvisionSessionEnvironmentResult
```

**Properties**

- `environment?` (`SessionEnvironment`): The environment as it ended — absent when there was nothing to provision.
- `tornDown` (`boolean`): The attempt tore down what it prepared (aborted, or the session vanished).

Source: [src/chat/environment-provisioning.ts:36](https://github.com/Tahul/zidane/blob/main/src/chat/environment-provisioning.ts#L36)

### `QuestionChoice`

Interface.

One option of a `select` question.

```ts
interface QuestionChoice
```

**Properties**

- `description?` (`string`): One-line detail shown under the label.
- `id` (`string`): Choice id, returned as the answer when picked.
- `label` (`string`): Text shown for the choice.
- `selected?` (`boolean`): Open a `multi` select with this choice already ticked. Ignored by
  single selects (which position the cursor from the carried answer
  instead) and overridden by a carried answer on re-entry, so resuming a
  question keeps what the user had rather than snapping back to the
  defaults.

  Exists because the alternative for an opt-out list is N keystrokes to
  accept N items: a caller offering "which of these 20 findings do you
  want to send" wants them pre-ticked and the user deselecting a few.

Source: [src/chat/interactions-protocol.ts:93](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L93)

### `QuestionPayload`

Interface.

Parsed input of an `ask_user` call.

```ts
interface QuestionPayload
```

**Properties**

- `intro?` (`string`): Optional intro / context shown above every question (Markdown).
- `questions` (`readonly Question[]`): One or more questions for the user to answer in one form.

Source: [src/chat/interactions-protocol.ts:209](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L209)

### `QuestionRequest`

Interface.

A pending request to answer questions.

```ts
interface QuestionRequest extends BaseRequest
```

**Properties**

- `createdAt` (`number`, from `BaseRequest`): Unix ms when the request was raised.
- `id` (`string`, from `BaseRequest`): Stable id — the `tool_call.id` from the model.
- `kind` (`'question'`): Request kind discriminator.
- `origin` (`InteractionOrigin`, from `BaseRequest`): Whether the model or the host raised this request.
- `payload` (`QuestionPayload`): The questions to answer.
- `runId?` (`string`, from `BaseRequest`): Run that emitted the call, when known.
- `tool` (`'ask_user'`): Tool canonical name.
- `turnId?` (`string`, from `BaseRequest`): Turn id the call lives in.

Source: [src/chat/interactions-protocol.ts:258](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L258)

### `QuestionResponse`

Interface.

The user's answers to a [`QuestionRequest`](https://zidane.si/docs/reference/chat.md#QuestionRequest).

```ts
interface QuestionResponse
```

**Properties**

- `answeredBy?` (`InteractionAnswerer`): See `PlanResponse.answeredBy`.
- `answers` (`Readonly<Record<string, AnswerValue>>`): Map of `Question.id → AnswerValue` for every question the user
  answered. Missing keys = question skipped (only possible when
  `required: false`).
- `automatic?` (`boolean`): See `PlanResponse.automatic`.
- `instructions?` (`string`): Free text the user attached to the whole form — every ask_user surface
  offers it unconditionally, so the user can always refine or override
  the questions instead of being boxed into the model's choices. Parallel
  to `PlanResponse.comment`; absent when left empty.
- `kind` (`'question'`): Response kind discriminator.

Source: [src/chat/interactions-protocol.ts:308](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L308)

### `QueuedAttachment`

Interface.

An attachment on a queued prompt, without its bytes.

```ts
interface QueuedAttachment
```

**Properties**

- `mediaType` (`string`): MIME type of the attachment.
- `name?` (`string`): File name, when known.

Source: [src/chat/host/signals.ts:250](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L250)

### `QueuedItem`

Interface.

One entry in the shared type-ahead queue.

```ts
interface QueuedItem
```

**Properties**

- `attachments?` (`QueuedAttachment[]`): What rides with the text, WITHOUT the bytes — enough for a queue row to
  show "+2 attachments" chips the way the echoed prompt will. The payload
  itself stays host-side until the entry is sent.
- `clientMessageId?` (`string`): The sender's id for the message (`SendOptions.clientMessageId`), when it gave one.
- `id` (`string`): Queue entry id.
- `text` (`string`): Queued prompt text.

Source: [src/chat/host/signals.ts:234](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L234)

### `RemoveMcpResult`

Interface.

Result of [`removeMcpServer`](https://zidane.si/docs/reference/chat.md#removeMcpServer).

```ts
interface RemoveMcpResult
```

**Properties**

- `path` (`string`): The file rewritten.

Source: [src/chat/mcps-write.ts:126](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-write.ts#L126)

### `RepoInfo`

Interface.

What the import dialog shows about a GitHub repository before cloning it (`fetchRepoInfo`).

```ts
interface RepoInfo
```

**Properties**

- `archived?` (`boolean`): Read-only on GitHub.
- `avatarUrl` (`string`): The owner's avatar.
- `defaultBranch` (`string`): The branch a clone checks out.
- `description?` (`string`): Its description.
- `imageUrl` (`string`): The preview image GitHub draws for links to it.
- `language?` (`string`): Its main language.
- `owner` (`string`): The owner, as GitHub spells it.
- `private?` (`boolean`): Only people with access see it.
- `repo` (`string`): The repository, as GitHub spells it.
- `stars` (`number`): Its stars.
- `url` (`string`): Its page.

Source: [src/chat/git/types.ts:131](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L131)

### `RepoProgress`

Interface.

A clone's progress, from git's own `--progress` lines.

```ts
interface RepoProgress
```

**Properties**

- `percent` (`number`): 0 to 100.
- `phase` (`string`): `Receiving objects`, `Resolving deltas`, …

Source: [src/chat/git/types.ts:123](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L123)

### `ResolvedApproval`

Interface.

Result of [`resolveApprovalForPayload`](https://zidane.si/docs/reference/chat.md#resolveApprovalForPayload): the resolved per-hunk
outcomes + the gate-level verdict.

The TUI's `applyGate` consumes the result: stashes `outcomes` in the pending-annotation map
(keyed by callId) so `tool:transform` can append the
`<edit-outcomes>` block to the tool result, rebinds `ctx.input.edits`
to the approved subset for `partial`, and emits the `syntheticEvent`
for fully-denied or fully-blocked calls.

```ts
interface ResolvedApproval
```

**Properties**

- `outcomes` (`EditOutcome[]`): Final state of every hunk after the decision (1:1 with payload.hunks).
- `shouldBlock` (`boolean`): True when no hunk will be applied — gate should `block` the call.
- `syntheticEvent` (`EditPayload | null`): Synthetic `EditPayload` to render in the transcript. Identical to the
  incoming payload but with `outcomes` set so the renderer can badge
  each hunk. Only meaningful when at least one hunk was denied —
  an all-applied decision returns `null` here and the normal
  `tool:before` event suffices.

Source: [src/chat/edit-approval.ts:51](https://github.com/Tahul/zidane/blob/main/src/chat/edit-approval.ts#L51)

### `ResolvedConfig`

Interface.

Fully resolved chat configuration produced by [`resolveConfig`](https://zidane.si/docs/reference/chat.md#resolveConfig).

```ts
interface ResolvedConfig
```

**Properties**

- `accountModelsFor?` (`(key: string, account?: string) => readonly ModelInfo[] | undefined`): The account's own model list (see `modelsFor`), or `undefined` when
  it has none and the provider's list applies. Without `account`: the
  provider's active account. Optional: hand-built configs omit it.
- `agents` (`AgentRegistry`): Resolved profile registry. Always non-empty; the resolver throws when
  the host passes an empty `agents` map (same contract as `providers`).
- `autoUpdate` (`AutoUpdateConfig | null`): Resolved auto-update knobs (forwarded verbatim from `ChatOptions.autoUpdate`)
  — `null` when the host didn't wire one. The TUI feeds this into
  `useUpdateCheck` so the footer chip + the `zidane upgrade` subcommand
  share a single source of truth.
- `extensionConfigCategories` (`Readonly<Record<string, ExtensionConfigCategory>>`): Merged extension-contributed config categories, keyed by category id.
  Surfaced (alongside `extensionConfigItems`) so the TUI can read
  the schema in-process — same way it reads `extensions.themes` — and
  the GUI's `extensions:configSchema` IPC can serialize it.
- `extensionConfigItems` (`Readonly<Record<string, Readonly<Record<string, StoredExtensionConfigItem>>>>`): Merged extension-contributed config items, namespaced
  `extName → itemKey → item`. Each item carries its resolved
  `category`; values are read default-first via the core resolver.
- `extensionKeybindings` (`readonly ResolvedExtensionKeybinding[]`): Resolved extension-contributed keybindings. Each entry pairs the
  extension's binding definition with the user's effective spec
  (override from `keybindings.json` when present, default
  otherwise). The TUI dispatcher iterates these alongside the
  built-in action catalog; the GUI host can do the same.

  Empty when no extension contributed a keybinding.
- `extensionPreset` (`Preset`): Single [`Preset`](https://zidane.si/docs/reference/presets.md#Preset) folded together from every extension's
  agent-side contributions. Hosts spread it into `createAgent()`
  alongside the active profile's preset:

  ```ts
  const profile = composePresets(extensionPreset, agentProfile.preset)
  const agent = createAgent({ ...profile, provider })
  ```

  Extension hooks are bucketed per event so every handler fires;
  tools and mcpServers compose with profile contributions via the
  usual `composePresets` rules. Extension instructions are not in it:
  the agent build appends them from the runtime per session.
- `extensions` (`ExtensionRegistry`): Merged extension contributions — the registry passed via
  `ChatOptions.extensionRegistry`, returned verbatim, or
  [`EMPTY_EXTENSION_REGISTRY`](https://zidane.si/docs/reference/extensions.md#EMPTY_EXTENSION_REGISTRY) when the host didn't pass one.
  `resolveConfig` is synchronous, so it never runs `setup()` itself;
  the host pre-builds the registry with `buildExtensionRegistry`.

  A live view of the extension runtime: renderer-side wiring reads
  `tuiContributions`, `panels` and `palette`, and re-reads on
  `extensions.subscribe`. The
  `extensionPreset` field below carries the agent-side surface
  (tools / hooks / mcpServers / skills) ready to spread.
- `extensionScanPaths` (`readonly string[]`): Extra extension scan roots used at boot. Runtime re-discovery (TUI cwd
  changes, future hosts) feeds these back into the shared discovery helper so
  local/dev package roots stay active after the initial launch.
- `extensionSubagents` (`SubagentRegistry`): Spawnable subagents (adds) contributed by extensions, keyed by name.
  Surfaced separately from `extensionPreset` because subagents don't ride the
  preset path — the host merges them onto the spawn tool's base
  registry via `spawn.withSubagents(...)` at agent build time.
- `extensionSystemFragments` (`readonly string[]`): Extra system-prompt fragments the host appends after the profile's
  `system`, in order. `resolveConfig` sets none; the agent build adds
  the extensions' instructions (`ctx.instructions`) after these, per
  session.
- `initialAgentId` (`string`): Id of the profile to activate on mount. Guaranteed to key into `agents`.
- `initialPicked` (`Picked | null`): Initial provider and model selection, or `null` when no provider is available.
- `initialSettings` (`Partial<Settings>`): Settings from the stored state, after migrating legacy keys.
- `initialState` (`TuiState`): State loaded from the state store at startup.
- `keybindings` (`KeyBindings`): Effective keybindings — defaults merged with the user-level
  `<userDir>/keybindings.json` (when present). Always fully-resolved
  so consumers can treat the record as total. See `./keybindings.ts`
  for the action catalog + spec format.
- `launch` (`StartLocalOptions | null`): One-shot launch request forwarded from `ChatOptions.launch`.
- `modelsFor` (`(key: string, account?: string) => readonly ModelInfo[]`): The models a provider offers, for pickers and agent builds. With
  `account` (a credential id or integration account, what a session pins):
  that account's list when an extension set one
  (`providers.transform` `models.set(id, list, { account })`), else the
  provider's. Without: the list of the provider's active account. Read per
  call: catalog changes show on the next lookup.
- `paths` (`ResolvedPaths`): Resolved storage paths for the user, project, cache and state files.
- `pluginScanPaths` (`readonly string[]`): Extra Agent Plugin scan roots used at boot. Threaded into the discovery
  shell so a dev root stays active across project/cwd changes, same as
  `ResolvedConfig.extensionScanPaths`.
- `prefix` (`string`): Storage prefix such as `.zidane`.
- `providers` (`ProviderRegistry`): Provider registry with the host's providers.
- `rawKeybindings` (`Readonly<Record<string, unknown>>`): Raw user `keybindings.json` object (post-JSONC-strip). Surfaced so a
  host re-resolving `extensionKeybindings` at runtime (after an
  extension registry rebuild) can reuse the same override source
  without a second disk read.
- `resumeProvider` (`ProviderAuth | null`): Provider to resume on launch, or `null` when none is available.
- `stateStore` (`StateStoreApi`): Store for persisted UI state.
- `storageDir` (`string`): Storage root. Falls back to the home directory when none was pinned.
- `store` (`SessionStore`): Session store used for chats.
- `trustProjectExtensions` (`boolean`): Resolved project-extension trust decision (option + env fallback).
  Hosts that re-discover extensions at runtime feed this back into
  `discoverAndBuildExtensionRegistry` so the rebuild trusts the
  same scopes boot did.

Source: [src/chat/config.ts:353](https://github.com/Tahul/zidane/blob/main/src/chat/config.ts#L353)

### `ResolvedExtensionKeybinding`

Interface.

Resolved spec string for one extension-contributed keybinding. Same
shape as a built-in [`KeyBindings`](https://zidane.si/docs/reference/chat.md#KeyBindings) entry but the `id` is
extension-owned (no `KeyAction` union member). The TUI dispatcher
iterates these alongside the built-ins; user overrides flow through
the same `keybindings.json` file by matching on `id`.

```ts
interface ResolvedExtensionKeybinding
```

**Properties**

- `id` (`string`): Extension-owned id (matches `ExtensionKeybinding.id`).
- `spec` (`string`): Active spec string — user override when present, default otherwise. May be empty (= unbound).

Source: [src/chat/keybindings.ts:115](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L115)

### `ResolvedPaths`

Interface.

Resolved on-disk layout produced by [`resolveConfig`](https://zidane.si/docs/reference/chat.md#resolveConfig).

```ts
interface ResolvedPaths
```

**Properties**

- `cacheDir` (`string`): XDG cache slot — `$XDG_CACHE_HOME/zidane` on Linux, single-dir
  elsewhere. Holds regenerable data: persisted tool results,
  background-task logs, auto-update registry cache.
- `configDir` (`string`): XDG config slot — `$XDG_CONFIG_HOME/zidane` on a fresh Linux install,
  `<storageDir>/<prefix>` (e.g. `~/.zidane/`) in legacy mode. Holds
  user-editable surface (`credentials.json`, `mcps.json`, …).
- `dataDir` (`string`): XDG data slot — `$XDG_DATA_HOME/zidane` on Linux, single-dir
  elsewhere. Holds long-lived chat history (`sessions.db`).
- `db` (`string`): Resolved `sessions.db` path. Lives under `projectDir` when active, else `dataDir`.
- `dir` (`string`): Effective data root — equals `userDir` (configDir) when project
  mode is off, `projectDir` when it's on. Prefer the explicit slots
  (`configDir` / `dataDir` / `cacheDir` / `stateDir`) or
  `projectDir` / `db` / `state`.
- `projectDir` (`string | null`): Project-scoped data root. Set to `<git-root>/<prefix>` when project
  mode resolved to ON and a git root was found. `null` otherwise.
- `state` (`string`): Resolved `state.json` path. Lives under `projectDir` when active, else `stateDir`.
- `stateDir` (`string`): XDG state slot — `$XDG_STATE_HOME/zidane` on Linux, single-dir
  elsewhere. Holds per-machine UI bookkeeping (`state.json`).
- `storageMode` (`StorageMode`): Which resolution branch produced the layout. `'xdg'` when split
  across the four XDG dirs; one of `'legacy-explicit'`,
  `'legacy-existing'`, or `'default'` when the slots collapse to a
  single directory. Surfaced for diagnostics + tests.
- `userDir` (`string`): User-scoped config root — credentials, mcp-credentials, keybindings,
  `config.json`, `mcps.json`, skills. Alias for `configDir`. In XDG mode this
  lands at `$XDG_CONFIG_HOME/zidane`; in legacy mode the four slots
  (config / data / state / cache) collapse to a single
  `<storageDir>/<prefix>` directory.

Source: [src/chat/config.ts:292](https://github.com/Tahul/zidane/blob/main/src/chat/config.ts#L292)

### `ResolveStorageDirsOptions`

Interface.

Options for [`resolveStorageDirs`](https://zidane.si/docs/reference/chat.md#resolveStorageDirs).

```ts
interface ResolveStorageDirsOptions
```

**Properties**

- `env?` (`Readonly<Record<string, string | undefined>>`): Override `process.env` for tests.
- `home?` (`string`): Override `os.homedir()` for tests.
- `platform?` (`Platform`): Override `process.platform` for tests.
- `prefix?` (`string`): Storage prefix. Leading dot tolerated. Default: `'.zidane'`.
- `storageDir?` (`string`): Explicit storage root (`ChatOptions.storageDir`). When set, forces
  legacy-explicit mode regardless of the platform / XDG signals.

Source: [src/chat/xdg.ts:79](https://github.com/Tahul/zidane/blob/main/src/chat/xdg.ts#L79)

### `RunningWorkItem`

Interface.

Parent-level cancellable tool call or background task.

```ts
interface RunningWorkItem
```

**Properties**

- `childId?` (`string`): Id of the subagent that started the item, when it was not the parent.
- `id` (`string`): Tool call id or task id.
- `kind` (`'tool' | 'task'`): Whether the item is a tool call or a background task.
- `label` (`string`): Short description shown to the user.
- `resuming?` (`true`): A call of a batch a durable run resumed after a process death, that the
  dead process did not finish (its `tool` event says `resuming`). A
  window attaching mid-run reads it from here. Optional: older hosts never
  send it.
- `startedAt` (`number`): Start time in epoch ms.

Source: [src/chat/host/signals.ts:47](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L47)

### `RunProgress`

Interface.

What a run readout shows of the run in progress: see [`runProgress`](https://zidane.si/docs/reference/chat.md#runProgress).

```ts
interface RunProgress
```

**Properties**

- `outputTokens` (`number`): Output tokens of the turns it has settled, its subagents' left out (the live count adds parent `turn:end`s).
- `startedAt` (`number`): When the run began (epoch ms, its run record).

Source: [src/chat/store.ts:1627](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L1627)

### `RunUpdateCommandOptions`

Interface.

Options for [`runUpdateCommand`](https://zidane.si/docs/reference/chat.md#runUpdateCommand).

```ts
interface RunUpdateCommandOptions
```

**Properties**

- `argv` (`readonly string[]`): Args passed after `upgrade` (typically `process.argv.slice(3)`).
- `cacheDir?` (`string`): Where to persist the TTL cache. Usually `paths.userDir`.
- `checkTarballAvailable?` (`(opts: TarballAvailabilityOptions) => Promise<'ok' | 'unknown' | 'missing'>`): Inject for tests.
- `companions?` (`readonly CompanionSpec[]`): Sibling packages to keep in step when installed (e.g. `zidane-gui`).
  Absent companions are silently skipped; `--tui-only` skips them all.
- `currentVersion` (`string`): Inlined at build time.
- `defaultChannel?` (`string`): Default channel when `--channel` isn't passed. Defaults to `'latest'`.
- `findCompanion?` (`(spec: CompanionSpec, opts?: FindCompanionOptions) => InstalledCompanion | null`): Inject for tests.
- `helpNote?` (`string`): Appended to `--help`: how this host runs installs (e.g. in the background).
- `lock?` (`UpdateLockOptions`): Inject for tests: clock, liveness probe and pid of the install lock.
- `lockHeld?` (`boolean`): The caller already holds the install lock (the update worker): don't take it again.
- `packageName` (`string`): Top-level package the user installs (e.g. `'zidane-tui'`).
- `performSelfUpdate?` (`(options: PerformSelfUpdateOptions) => Promise<SelfUpdateResult>`): Inject for tests.
- `platformPackagePrefix?` (`string`): Override the platform-package-name prefix used by `--in-place`.
  Defaults to `'<packageName>-'`, matching the `zidane-tui-<key>` layout.
- `registry?` (`string`): Override registry. Defaults to npmjs.
- `stderr?` (`WritableStream`): stderr. Defaults to `process.stderr`.
- `stdout?` (`WritableStream`): stdout. Defaults to `process.stdout`.
- `verifyInstalled?` (`(packageName: string) => string | null | undefined`): Reports the version now installed for `packageName` (e.g. by running the
  bin with `--version`), `null` when nothing usable is there, `undefined`
  to skip verification. The host wires this; the library cannot know how
  its own bin is invoked.
- `writeMarker?` (`(cacheDir: string, marker: UpdateAppliedMarker) => boolean`): Inject for tests.

Source: [src/chat/auto-update-cli.ts:53](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-cli.ts#L53)

### `RunUpdateCommandResult`

Interface.

Result of [`runUpdateCommand`](https://zidane.si/docs/reference/chat.md#runUpdateCommand).

```ts
interface RunUpdateCommandResult
```

**Properties**

- `exitCode` (`number`): Process exit code: `0` on success, `1` on failure, `2` for invalid arguments.

Source: [src/chat/auto-update-cli.ts:104](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-cli.ts#L104)

### `SafelistProject`

Interface.

The "Always allow" entries of one project, as [`listSafelists`](https://zidane.si/docs/reference/chat.md#listSafelists) lists them.

```ts
interface SafelistProject
```

**Properties**

- `entries` (`string[]`): The entries.
- `project` (`string`): The project key the entries are stored under.

Source: [src/chat/safe-mode.ts:179](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L179)

### `SafeModeActions`

Interface.

Functions that drive the approval queue.

```ts
interface SafeModeActions
```

**Properties**

- `denyAll` (`() => void`): Resolve all pending with `deny`. Used on abort / hard exit.
- `dropByCorrelation` (`(correlationId: string) => void`): Remove the entry whose `correlationId` matches, WITHOUT resolving it
  (the daemon already did, in another window). No-op when absent.
- `requestApproval` (`RequestApproval`): Request a decision; resolves once the user picks.
- `resolveHead` (`(decision: ApprovalDecision) => void`): Resolve the head and shift the queue forward.

Source: [src/chat/safe-mode-context.tsx:87](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode-context.tsx#L87)

### `SelectQuestion`

Interface.

A question answered by picking from fixed choices.

```ts
interface SelectQuestion extends BaseQuestion
```

**Properties**

- `choices` (`readonly QuestionChoice[]`): Options to pick from.
- `description?` (`string`, from `BaseQuestion`): Optional helper text under the prompt.
- `id` (`string`, from `BaseQuestion`): Stable id — keys the response back to the model.
- `multi?` (`boolean`): Allow picking MORE than one choice. The answer becomes a
  `readonly string[]` of choice ids (space toggles, ↵ commits in the
  TUI). `required` (default `true`) demands at least one selection.
- `prompt` (`string`, from `BaseQuestion`): Question text shown above the input (Markdown allowed).
- `required?` (`boolean`, from `BaseQuestion`): Whether the answer is required (non-empty). Default: `true` for
  `select` / `confirm` (no sensible empty), `false` for free-text
  (skipping = empty string). Free-text questions still accept an
  answer; the flag only gates submission.
- `type` (`'select'`): Question type discriminator.

Source: [src/chat/interactions-protocol.ts:156](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L156)

### `SelfUpdateResult`

Interface.

Outcome of [`performSelfUpdate`](https://zidane.si/docs/reference/chat.md#performSelfUpdate).

```ts
interface SelfUpdateResult
```

**Properties**

- `command` (`PackageManagerCommand`): The package manager command that was, or would be, run.
- `exitCode` (`number | null`): PM exit code. `null` when `dryRun` or the process was killed.
- `spawnError?` (`string`): Set when the spawn itself failed (`ENOENT`, `EACCES`).
- `timedOut?` (`boolean`): The PM ran past `timeoutMs` and was killed.

Source: [src/chat/auto-update.ts:780](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L780)

### `SendOptions`

Interface.

`chat.send` options.

```ts
interface SendOptions
```

**Properties**

- `activatedSkills?` (`string[]`): Skill names picked via `/` completion — activated before the run.
- `asCommand?` (`string`): The client resolved this text as the `/` command of this name in ITS
  registry. The host re-resolves against the SESSION's registry (its cwd,
  its extensions) and refuses when that misses, instead of sending
  `/fix comments` to the model as prose — the two registries can disagree
  (extension installed after the runtime cache was built, different cwd),
  and a silent prose send is indistinguishable from a broken command.
- `attachments?` (`PromptAttachment[]`): Attachments sent with the prompt.
- `clientMessageId?` (`string`): The client's own id for this message: the host reports what becomes of it
  (`delivery:changed`), and sets it as `origin.messageId` when the message
  has an origin without one.
- `keepDraft?` (`boolean`): Sent from outside the composer (Continue, Retry, a palette prompt): the
  shared draft was not consumed, so the daemon leaves it alone instead of
  clearing it in every other window.
- `literal?` (`boolean`): Never resolve the text as a `/` command: it reaches the model as sent.
- `metadata?` (`Record<string, JsonValue>`): Extension data riding with the prompt (a `prompt` interceptor's
  `metadata`): later interceptors and the run's `run` interceptors see it
  (`AgentRunOptions.metadata`). Not saved with the user turn.
- `origin?` (`TextOrigin`): Who or what the prompt comes from, when not the session's own user: the
  prompt's text block in the persisted user turn carries it. The host never
  rewrites the text because of it.
- `prompt` (`string`): Prompt text.
- `sessionId` (`string`): Session to send the prompt to.
- `wake?` (`boolean`): Host-internal wake run: delivers pending background-task notifications
  to the model with an empty prompt. Skips the `user-prompt` broadcast
  (there is no user message) — mirrors the local TUI's wake semantics.

Source: [src/chat/host/signals.ts:121](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L121)

### `SerializedPreview`

Interface.

Serializable mirror of `PreviewResult` — `readonly` arrays widened so the
payload round-trips without TypeScript friction on the client side.

```ts
interface SerializedPreview
```

**Properties**

- `diffText` (`string`): Unified diff of the proposed edit.
- `perHunkDiff` (`readonly string[]`): The diff split into one string per hunk.
- `resolution` (`readonly HunkResolution[]`): How the edit tool would resolve each hunk: whether its `old_string` is found without ambiguity.
- `resolvedPayload` (`EditPayload`): Edit payload with the resolution applied.

Source: [src/chat/host/signals.ts:71](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L71)

### `SessionEnvironment`

Interface.

`metadata.environment` — absent on sessions that predate it (read as local).

```ts
interface SessionEnvironment
```

**Properties**

- `archived?` (`boolean`): The provider's `archive` removed it when the session was archived
  (`state: 'failed'` meanwhile): restoring the session provisions it again.
- `base?` (`string`): What `branch` was cut from.
- `branch?` (`string`): Branch the session works on (worktree and remote kinds).
- `checkout?` (`EnvironmentCheckout`): Work on `branch` as it is rather than cutting it (worktree kinds): [`EnvironmentCheckout`](https://zidane.si/docs/reference/chat.md#EnvironmentCheckout).
- `error?` (`string`): Why provisioning failed (`state: 'failed'`).
- `execCwd?` (`string`): Where commands run inside the environment, when it isn't `metadata.cwd`.
- `kind` (`string`): Provider kind: `'local'`, `'worktree'`, `'docker'`, `'e2b'`, …
- `owned?` (`boolean`): `false` when the session shares another session's environment (a fork):
  it runs there but never tears it down. Absent = owned.
- `ref?` (`JsonValue`): Provider-owned state: a worktree path, a sandbox id, a container id.
- `state` (`EnvironmentState`): Where the environment is in its lifecycle. Only `ready` environments can run.
- `suspended?` (`boolean`): The host paused it while idle (`provider.suspend` — a stopped container,
  a paused sandbox). Still `ready`: the next run resumes it.

Source: [src/chat/environment.ts:38](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L38)

### `SessionEnvironmentSummary`

Interface.

The list-row slice of a session's environment (`SessionMeta.environment`).

```ts
interface SessionEnvironmentSummary
```

**Properties**

- `branch?` (`string`): Git branch the environment works on.
- `error?` (`string`): Failure message when provisioning failed.
- `kind` (`string`): Environment kind, for example `local` or `worktree@laptop`.
- `state` (`EnvironmentState`): Provisioning state of the environment.
- `suspended?` (`boolean`): Paused by the host while idle; resumes on the next run.
- `workspaceRoot?` (`string`): Set when the session's files live inside its environment (a sandbox's
  clone), not on this machine — clients route file views through the host
  (`workspace*` methods) instead of reading `cwd`.

Source: [src/chat/types.ts:569](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L569)

### `SessionExecution`

Interface.

A session's execution context and the lazy handle resolver bound to it, as returned by [`SessionExecutionCache`](https://zidane.si/docs/reference/chat.md#SessionExecutionCache).

```ts
interface SessionExecution
```

**Properties**

- `context` (`ExecutionContext`): The session's execution context — pass as `AgentOptions.execution`.
- `handle` (`() => Promise<ExecutionHandle>`): Host-owned handle, spawned on first use — pass as `AgentOptions.handle`.

Source: [src/chat/execution-cache.ts:42](https://github.com/Tahul/zidane/blob/main/src/chat/execution-cache.ts#L42)

### `SessionExecutionCache`

Interface.

Registry of host-owned execution contexts and handles, one per session. Created by [`createSessionExecutionCache`](https://zidane.si/docs/reference/chat.md#createSessionExecutionCache).

```ts
interface SessionExecutionCache
```

**Properties**

- `acquire` (`(sessionId: string, key: string, create: () => ExecutionContext) => SessionExecution`): The session's execution, created with `create()` on first use and
  reused while `key` matches. A different `key` replaces it (the previous
  context is released in the background).
- `adopt` (`(sessionId: string, hooks: AgentHookBus) => Promise<number>`): Hand the session's still-running background tasks to a newly built
  agent: rebind their exit/stall callbacks to `hooks` and fire
  `background:adopt` per task. Returns how many were adopted.
- `hasRunningTasks` (`(sessionId: string) => Promise<boolean>`): True while any background task on the session's handle is running.
- `peek` (`(sessionId: string) => SessionExecution | undefined`): The session's current execution, whatever its key — for side work
  (environment actions) that must run where the agent runs without
  re-deriving the key (a mismatch would replace the live context).
- `release` (`(sessionId: string) => Promise<void>`): Destroy the session's handle (killing its tasks) and forget it. Resolves
  once the destroy has finished — including one already started by an
  earlier release or a key replacement — so a caller can rely on the
  session's processes being gone (e.g. before removing its worktree).
- `releaseAll` (`() => Promise<void>`): Release every session — host shutdown.
- `releaseIfIdle` (`(sessionId: string, stillIdle?: () => boolean) => Promise<boolean>`): `release` unless tasks are still running; resolves whether it
  released. `stillIdle` is re-checked after the (async) task listing,
  right before destroying — hosts pass "no agent was rebuilt for this
  session meanwhile", so an eviction can't pull the handle out from
  under a build that just acquired it.
- `sessions` (`() => string[]`): Sessions currently holding an execution.

Source: [src/chat/execution-cache.ts:52](https://github.com/Tahul/zidane/blob/main/src/chat/execution-cache.ts#L52)

### `SessionExportTarget`

Interface.

Resolved file destination for a session export.

```ts
interface SessionExportTarget
```

**Properties**

- `anchor` (`SessionExportAnchor`): Where the destination was anchored.
- `dir` (`string`): Absolute directory holding the file. May not exist yet — `writeSessionExport` creates it on demand.
- `filepath` (`string`): Absolute path of the file (`dir/{id}.{ext}`).

Source: [src/chat/session-export.ts:42](https://github.com/Tahul/zidane/blob/main/src/chat/session-export.ts#L42)

### `SessionHeader`

Interface.

One session's header as `sessions.getHeader` serves it: the list row plus
the raw metadata (model, effort, environment) and profile, without the
turns and runs `sessions.get` carries.

```ts
interface SessionHeader extends SessionMeta
```

**Properties**

- `agentId?` (`string`): Agent profile the session runs.
- `artifactCount?` (`number`, from `SessionMeta`): Artifacts recorded for the session (PRs, branches, commits, files, …).
  Optional: only stores implementing artifacts populate it; absence means
  unknown, not zero. Drives the sessions-list badge without a `load()`.
- `contextTokens?` (`number`): Last parent turn's context size (the footer gauge); only when asked for (`sessions.getHeader` `usage`).
- `costUsd?` (`number`): Summed run costs, USD (the cost pill); only when asked for.
- `createdAt?` (`number`): Epoch milliseconds when the session was created.
- `cwd?` (`string`, from `SessionMeta`): The session's tool-execution directory (`metadata.cwd`) when stamped —
  may be a subdirectory of `projectRoot`. Hosts switching to a session
  must honor this over `projectRoot` (see `resolveSessionCwd`).
- `environment?` (`SessionEnvironmentSummary`, from `SessionMeta`): Where the session's work happens, for list badges and status: kind,
  provisioning state, branch, and the failure reason. Absent = local.
- `facets?` (`Record<string, JsonValue>`, from `SessionMeta`): Facet values extensions computed for the session (`ctx.sessions.facets`),
  keyed `<extensionId>.<key>`, as stored with it. Absent when it has none.
- `favorite?` (`boolean`, from `SessionMeta`): `true` when the user favorited the session (`metadata.favorite`): listed first.
- `hostKey?` (`string`, from `SessionMeta`): The host that listed the row, in a window showing several hosts' lists
  (the TUI's host directory key). Two hosts may hold the same `id`: the
  pair is the session.
- `id` (`string`, from `SessionMeta`): Session id.
- `isMain?` (`boolean`, from `SessionMeta`): `true` for the orchestration main session (`metadata.role === 'main'`).
  Derived HERE, in the projection, because list rows deliberately do not
  carry raw metadata — a client must be able to pin the main without
  loading every session, and without the metadata plumbing that would
  otherwise leak the whole bag onto the wire.
- `metadata` (`Record<string, unknown>`): Raw session metadata.
- `projectRoot?` (`string`, from `SessionMeta`): Project this session was created under (see `SessionData.projectRoot`).
  Surfaced on the meta row so the sessions screen can show a
  per-project label when `Settings.showAllProjects` is on. Absent
  for legacy pre-tagging sessions.
- `runCount` (`number`, from `SessionMeta`): Top-level runs recorded against the session (one per `agent.run()` call).
- `runOutputTokens?` (`number`): Output tokens of that run's settled turns (the run readout's count); with `runStartedAt`.
- `runStartedAt?` (`number`): When the top-level run in progress began (epoch ms); only when asked for, while the host runs the session.
- `scheduleId?` (`string`, from `SessionMeta`): The schedule that created the session (`metadata.scheduleId`): a reminder's "new session" fire. Lists show a clock mark and the Origin filter reads it.
- `spaceId?` (`string`, from `SessionMeta`): The space the session belongs to (`metadata.spaceId`), when one other
  than Default is stamped. Absent = Default (see `./spaces`).
- `status` (`'error' | 'running' | 'completed' | 'idle'`): Run status of the session.
- `title` (`string`, from `SessionMeta`): Display title.
- `turnCount` (`number`, from `SessionMeta`): Total turns (user + assistant + system).
- `updatedAt` (`number`, from `SessionMeta`): Epoch milliseconds of the last update.
- `userMessageCount` (`number`, from `SessionMeta`): Count of `role: 'user'` turns — the volume of human messages.

Source: [src/chat/types.ts:661](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L661)

### `SessionLaunchOverrides`

Interface.

`ChatHost.setLaunchOverrides`: what the window overrides on a session's agent.

```ts
interface SessionLaunchOverrides
```

**Properties**

- `cwd?` (`string`): Working directory picked mid-session (the TUI's cwd picker), instead of
  the session's folder: the live agent's tools move at once, its prompt on
  the next turn, and later builds use it. Not persisted.
- `execution?` (`LaunchExecution`): Execution context to run the session in.
- `mcp?` (`readonly McpServerConfig[]`): Extra MCP servers, after the discovered and plugin ones (which win a name collision).
- `system?` (`string`): Replaces the base system prompt (extension fragments still append).

Source: [src/chat/host/launch-overrides.ts:23](https://github.com/Tahul/zidane/blob/main/src/chat/host/launch-overrides.ts#L23)

### `SessionMeta`

Interface.

One row of the sessions list.

```ts
interface SessionMeta
```

**Properties**

- `artifactCount?` (`number`): Artifacts recorded for the session (PRs, branches, commits, files, …).
  Optional: only stores implementing artifacts populate it; absence means
  unknown, not zero. Drives the sessions-list badge without a `load()`.
- `cwd?` (`string`): The session's tool-execution directory (`metadata.cwd`) when stamped —
  may be a subdirectory of `projectRoot`. Hosts switching to a session
  must honor this over `projectRoot` (see `resolveSessionCwd`).
- `environment?` (`SessionEnvironmentSummary`): Where the session's work happens, for list badges and status: kind,
  provisioning state, branch, and the failure reason. Absent = local.
- `facets?` (`Record<string, JsonValue>`): Facet values extensions computed for the session (`ctx.sessions.facets`),
  keyed `<extensionId>.<key>`, as stored with it. Absent when it has none.
- `favorite?` (`boolean`): `true` when the user favorited the session (`metadata.favorite`): listed first.
- `hostKey?` (`string`): The host that listed the row, in a window showing several hosts' lists
  (the TUI's host directory key). Two hosts may hold the same `id`: the
  pair is the session.
- `id` (`string`): Session id.
- `isMain?` (`boolean`): `true` for the orchestration main session (`metadata.role === 'main'`).
  Derived HERE, in the projection, because list rows deliberately do not
  carry raw metadata — a client must be able to pin the main without
  loading every session, and without the metadata plumbing that would
  otherwise leak the whole bag onto the wire.
- `projectRoot?` (`string`): Project this session was created under (see `SessionData.projectRoot`).
  Surfaced on the meta row so the sessions screen can show a
  per-project label when `Settings.showAllProjects` is on. Absent
  for legacy pre-tagging sessions.
- `runCount` (`number`): Top-level runs recorded against the session (one per `agent.run()` call).
- `scheduleId?` (`string`): The schedule that created the session (`metadata.scheduleId`): a reminder's "new session" fire. Lists show a clock mark and the Origin filter reads it.
- `spaceId?` (`string`): The space the session belongs to (`metadata.spaceId`), when one other
  than Default is stamped. Absent = Default (see `./spaces`).
- `title` (`string`): Display title.
- `turnCount` (`number`): Total turns (user + assistant + system).
- `updatedAt` (`number`): Epoch milliseconds of the last update.
- `userMessageCount` (`number`): Count of `role: 'user'` turns — the volume of human messages.

Source: [src/chat/types.ts:589](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L589)

### `SessionPresence`

Interface.

Live per-session presence — which sessions have attached windows and
what state they're in. Drives the session list's "active" scope and
per-row status icons. A session appears here when it has ≥1 attached
window, a run in flight, or pending HITL state.

```ts
interface SessionPresence
```

**Properties**

- `attention` (`boolean`): Pending approval or interaction — awaiting user input somewhere.
- `running` (`boolean`): A run is executing right now.
- `sessionId` (`string`): Session the presence is for.
- `windows` (`number`): Number of windows currently attached (watching) this session.

Source: [src/chat/host/signals.ts:263](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L263)

### `SetProviderCredentialOptions`

Interface.

Options for [`setProviderCredential`](https://zidane.si/docs/reference/chat.md#setProviderCredential).

```ts
interface SetProviderCredentialOptions
```

**Properties**

- `replaceKey?` (`string`): File key of the stored credential this one REPLACES (a re-auth of a known
  account). The slot keeps its position — replacing the active one keeps it
  active. When the key is no longer stored (removed from another window
  mid-login), the credential is added as a new active account instead.

Source: [src/chat/credentials.ts:544](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L544)

### `Settings`

Interface.

The user's persisted settings.

```ts
interface Settings
```

**Properties**

- `activeIcon` (`string`): Icon that REPLACES `sessionIcon` in the terminal title while a run is
  in flight (the agent is streaming/working), so a backgrounded tab
  reads "zidane is busy" at a glance. Loses to `attentionIcon` when the
  run is simultaneously blocked on user input — attention is the more
  actionable state. Free-text (typically a single emoji). Empty falls
  back to ⚡. Only used in `'icon-session'` mode, like `sessionIcon`.
  Default: `''`.
- `approvalMode` (`ApprovalMode`): Who approves tool calls that aren't covered by the project safelist
  (`projects.json`) or the implicit read-only allow-list: `'ask'` the
  user, `'decide'` the decision model (approves or denies only when
  confident, otherwise asks), `'off'` nobody (full access). A legacy
  `safeMode` boolean (`false` → `'off'`) is mapped on read
  (`migrateStoredSettings`) and at the sync boundary. Default: `'ask'`.
- `artifacts` (`boolean`): Session artifacts (see docs/ARTIFACTS.md): record what a session
  produces — pull requests, branches, commits, deliberately recorded
  files — and surface them (rail, sessions badge, `#pr`
  completion). Off = nothing is detected or recorded, the
  `record_artifact` tool is not offered, `ctx.artifacts.record` no-ops,
  and every artifact surface is hidden. Existing rows are kept, not
  deleted. Default: on.
- `attachmentsEnabled` (`boolean`): Master switch for the prompt textarea's paste-as-attachment
  pipeline. When `true` (default), the host intercepts paste events
  and folds:

  - single-line pastes that resolve to a real file on disk
    (drag-and-drop or `file://` URLs) into a binary attachment
    tagged with the matching MIME type, and
  - multiline pastes into a `paste.txt` document attachment so
    the prompt textarea doesn't balloon vertically.

  Flip to `false` to opt out entirely — every paste lands inline in
  the textarea verbatim, regardless of size, shape, or whether the
  content matches a file path on disk. Useful for users who paste
  short snippets often and find the auto-attach behavior breaks
  their composing flow.

  Doesn't affect the rest of the multimodal pipeline — embedders
  that build `PromptPart[]` directly (image messages from a GUI,
  dropped files via a custom handler) still flow through unchanged.
  Default: on.
- `attentionIcon` (`string`): Icon that REPLACES `sessionIcon` in the terminal title while the
  agent is blocked on user input — a tool approval or an
  `ask_user` / `present_plan` form awaiting a reply. Lets a
  backgrounded tab flag "zidane needs you" at a glance. Free-text
  (typically a single emoji). Empty falls back to ⚠️. Only used in
  `'icon-session'` mode, like `sessionIcon`.
  Default: `''`.
- `autoCompact` (`boolean`): Compact the conversation automatically (the loop's own
  `behavior.autoCompact`) when the latest turn's input-token usage crosses
  `Settings.autoCompactThreshold` of the model's effective context
  window (raw window minus a fixed output reserve — see
  `OUTPUT_RESERVE_TOKENS` in `compact/policy.ts`).

  The loop fires it at a turn boundary. The host mirrors the `compact:*`
  hooks to clients: an `info` event announces the compaction, and the
  boundary card appears when the summary lands.

  Default: on.
- `autoCompactThreshold` (`number`): Threshold for `Settings.autoCompact`, expressed as a fraction of
  the effective context window. `0.8` means "fire when the latest turn
  used 80% of the effective window". Values outside `(0, 1)` are
  treated as misconfiguration and skip the trigger.

  The settings modal cycles through `0.6 / 0.7 / 0.8 / 0.9` — coarse
  enough that users don't get lost, fine enough to tune for verbose
  workflows (lower) versus context-heavy ones (higher).

  Default: `0.8`.
- `autoInstallUpdates` (`boolean`): Install a new stable release in the background when a check finds one.
  Upgrades every installed zidane package through its own package
  manager, the same way `zidane upgrade` does. Never restarts anything:
  the restart chip offers that once the install lands. Needs
  `Settings.checkForUpdates`; the same env opt-outs apply.

  Default: off.
- `autoTitle` (`boolean`): After each run, title an untitled session and, every few prompts, ask
  the decision model whether an auto-generated title still fits; when it
  doesn't, generate a new one. Titles the user typed are never replaced
  (`metadata.titleSource`). Needs `decisions` for re-titling.
  Default: off.
- `browser` (`boolean`): The GUI's dev browser (the Browser panel tab). Off = no tab, no pages,
  and the agent is not offered the `browser_*` tools. Default: on.
- `browserAgent` (`boolean`): Whether the agent may drive the dev browser (`browser_*` tools). Only
  meaningful while `browser` is on; off leaves the tab for the user alone.
  Default: on.
- `browserAutoShow` (`boolean`): Open the Browser tab when the agent starts using the browser, so the
  user can watch. Closing it keeps it closed until the run ends. Default: on.
- `checkForUpdates` (`boolean`): Quietly check the npm registry for a newer release of the host
  package (`zidane-tui` for the shipped binary; any consumer can
  pass their own via `<App auto-update>` / `useUpdateCheck`).

  Hot path is cache-driven: one HTTP roundtrip per 24 h, persisted
  under `userDir/update-check-<pkg>@<channel>.json`. Boot is never
  blocked — the check fires after first paint with a 3-second
  timeout and degrades silently on network failure. CI runners
  (env `CI`), the npm-ecosystem `NO_UPDATE_NOTIFIER`, and the
  project-specific `ZIDANE_NO_UPDATE` env vars all force-disable
  regardless of this setting.

  Surfaces in the footer as a passive `↑ vX.Y.Z` chip (renderable
  by `useUpdateCheck` and `buildUpdateHint`). The explicit
  `zidane upgrade` subcommand bypasses both this toggle and the
  env opt-outs (it's an explicit user gesture).

  Default: on.
- `closeTerminalOnCleanExit` (`boolean`): Close a terminal you opened once its shell exits with code 0 (`exit`,
  ctrl+d). A failure stays open so its output can be read; agent task
  terminals are left alone. Default: on.
- `codemode` (`boolean`): Give the model the `codemode` tool: it writes JavaScript that calls the
  other tools from a sandbox, so batched, chained or bulk work takes one
  call and only the script's output enters the context.

  Maps to `behavior.codemode: true` (`'on'` mode: every other tool stays
  declared) at agent build time, or to the profile's own mode (the Codemode
  profile, `build-codemode`, asks for `'only'`). Also gates profiles with
  `requiresSetting: 'codemode'`. Toggling it rebuilds live agents.
  Default: on.
- `codemodeShowCalls` (`boolean`): Show the calls a codemode script makes (`tools.*`) as rows under its
  Codemode row. Off by default: a script can make dozens of calls. Calls
  that run a subagent show either way, since their worker attaches there.
- `codemodeStreamProgress` (`boolean`): Show each codemode script's progress while it runs: the line each call
  came from (running, done, failed), a count of its calls, and the last
  lines it printed or its shell calls wrote. Sets
  `behavior.codemode.streamProgress`; toggling it rebuilds live agents.
  The model's result does not change. Off by default.
- `compactBeforeCacheExpiry` (`boolean`): Experimental. When a long session goes idle, compact it about a minute
  before its prompt cache is estimated to expire, so the next message
  reads a short summary instead of re-reading the whole conversation at
  full price. Only sessions whose last request was long (at least 100k
  tokens, or half the auto-compaction trigger when that is higher) are
  compacted; any activity in the session cancels the pending compaction.

  Default: off.
- `compactionModel` (`string`): Model the compaction summary call runs on, as `"providerKey/modelId"`
  (e.g. `"anthropic/claude-haiku-4-5"`). Empty string = summarize on
  the main model (default). Picked via the cross-provider model picker
  from settings; when the provider is no longer authed or the model id
  is unknown, hosts fall back to the main model.
- `compactToolCatalog` (`boolean`): Experimental. List searchable (MCP) tools by name only in the system
  prompt; the model calls `tool_search` to read a tool's description.
  Shrinks the starting prompt when many MCP tools are connected.

  Maps to `behavior.toolSearch.catalog: 'names'` at agent build time for
  every profile. Codemode keeps its own catalog choice. Takes effect on the
  next session activation.

  Default: off.
- `confirmGitSync` (`boolean`): Ask before the desktop git bar's Sync pulls and pushes (or publishes a
  branch). Its confirmation's "Don't ask again" turns this off. Default: on.
- `ctrlCBehavior` (`'quit' | 'quit-confirm' | 'stop-then-quit'`): What happens when the user presses Ctrl+C.

  - `'quit'`           — exit immediately (default).
  - `'quit-confirm'`   — show a confirmation modal; Enter / y confirms,
    Escape / n cancels. A second Ctrl+C while the
    modal is open also confirms.
  - `'stop-then-quit'` — first press aborts the active run or clears the
    input; second consecutive press (within 1.5 s)
    exits. If there's nothing to stop and the input
    is empty, the first press exits directly.
- `decisionModel` (`string`): Decision model as `"providerKey/modelId"` (e.g. `"openrouter/typesafe/jev-1.13"`).
  Empty string = the first available decision model. When the provider is
  no longer authed or its credentials can't send decisions, hosts fall back
  to the first available one.
- `decisions` (`boolean`): Let hooks and extensions make decision requests (`ctx.decide` /
  `agent.decide`): fast typed probability / choice / score answers from a
  provider's Decisions endpoint. Off maps to `AgentOptions.decisions: false`
  (every decision returns `unavailable`). Toggling it rebuilds live agents.
  Default: off.
- `defaultAgent` (`string`): Agent new sessions start on (an `AgentProfile.id`: `build`, `plan`, an
  extension's). Empty = Build. A space's own default wins
  (`Space.agentId`); an id not available where the session runs falls
  back to Build (`resolveAgentId`).
- `defaultEffort` (`'' | 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'adaptive'`): Effort new sessions start with, whatever their model. Empty = the last
  used with the session's model. A space's own wins (`Space.effort`); a
  pick in the composer wins over both.
- `defaultModel` (`string`): Model new sessions start on, as `"providerKey[@account]/modelId"`
  (`model-setting.ts`). Empty = the last used model. A space's own default
  wins (`Space.providerKey` / `modelId`); a pick in the composer wins over both.
- `defaultModelOptions` (`Record<string, boolean>`): Model options (`fast`…) new sessions start with, on or off, where their
  model offers them. Empty = the last used with the session's model. A
  space's own win (`Space.modelOptions`).
- `disabledMcpTools?` (`Record<string, readonly string[]>`): Per-server **deny-list** of tool names to hide from the model. Maps
  1:1 onto `McpServerConfig.disabledTools` at agent-construction
  time. Polarity is intentionally opposite to `enabledMcps`:

  - missing entry (or `undefined`)  → every advertised tool enabled
  - `[]`                            → every advertised tool enabled
  - `['big_tool']`                  → drop `big_tool`; keep the rest

  The deny-list semantics mean a server that ships a new tool in a
  later release auto-enables for users who haven't explicitly opted
  it out — opposite preference from server-level toggles (where a
  new server should NOT silently appear). The TUI's MCP settings
  panel writes here when the user unchecks a tool checkbox.

  Composes with `McpServerConfig.disabledTools` on the JSON-side via
  union — a tool listed in either is dropped. JSON-side wins on
  conflict (you can't re-enable a JSON-pinned tool from the UI).
- `durability` (`DurabilityMode`): Checkpoint runs so they survive a crash (see [`DurabilityMode`](https://zidane.si/docs/reference/chat.md#DurabilityMode)).
  Read when a run starts, so a change applies to new runs. A project can
  override it (`durability` in its `projects.json` entry). Sessions on a
  store without durability (anything but SQLite) behave as `off`.
  Default: `resume-ask`.
- `editDiffDisplay` (`EditDiffDisplay`): Density of the edit-diff rendering. See [`EditDiffDisplay`](https://zidane.si/docs/reference/chat.md#EditDiffDisplay).
  Only consulted when `showEditDiffs` is on. Default: `'full'`.
- `editor` (`Record<string, unknown>`): The desktop Files panel's editor preferences (editing, saving,
  diagnostics, language servers) as the editor kit records them
  (`zidane-ui/files` `EditorState`, validated by its `parseEditor`).
  Per computer, shared by every window and build. Default: `{}` (the
  kit's defaults).
- `enabledExtensions?` (`readonly string[]`): Allowlist of extension names — every entry discovered at
  `.zidane/extensions/<name>/index.{ts,tsx,js,mjs}` (or under
  `.agents/extensions/`, project + user). Same polarity + semantics
  as `enabledSkills` / `enabledMcps`:

  - `undefined` → every discovered extension runs (fresh install
    default; new extensions auto-opt-in).
  - `[]`        → no extensions run.
  - `[names]`   → allowlist; only those run.

  Extension `setup()` runs once at boot; toggling does NOT re-run it.
  Instead the TUI live-filters the captured contributions: completion
  providers, keybindings, footer hints, and agent profiles drop out
  immediately, while tools / hooks / mcpServers / skills are filtered
  at the next agent build (session activation / profile switch).
  Providers and themes fold into `ResolvedConfig` at boot and stay
  available until the next launch. Re-ENABLING a disk extension whose
  module was never imported (it was disabled at boot) also requires a
  restart.

  NB: this is an allowlist on the live catalog; an extension that's
  been removed from disk between launches drops out of the persisted
  list on the next toggle (consistent with skills / MCPs).
- `enabledMcps?` (`readonly string[]`): Allowlist of MCP server names (from `.{prefix}/mcps.json`). Same
  semantics as `enabledSkills`: `undefined` enables every discovered
  server; `[]` disables all.
- `enabledPlugins?` (`readonly string[]`): Allowlist of Agent Plugin names — everything discovered under
  `.agents/plugins/<name>/` or `.{prefix}/plugins/<name>/` (project +
  user), plus plugins an extension loaded via `ctx.loadAgentPlugin`. Same
  polarity + semantics as `enabledSkills` / `enabledMcps`:

  - `undefined` → every discovered plugin loads (fresh-install default).
  - `[]`        → no plugins load.
  - `[names]`   → allowlist; only those load.

  The key is the plugin's MANIFEST name (its identity per the Agent
  Plugins spec), falling back to the directory basename for a plugin whose
  manifest was rejected — so a broken plugin still has a stable row.

  A plugin's skills and MCP servers are gated by this list. Its skills are
  additionally force-included into `enabledSkills` when the user has an
  allowlist there, since enabling the plugin IS the consent.

  The gate is plugin-level only: a plugin's skills do not appear in the
  Skills tab (that list is a disk scan of the skill scan paths, which plugin
  skills deliberately bypass), so there is no per-skill toggle for them.
  Turning off one skill of a plugin means disabling the plugin.
- `enabledSkills?` (`readonly string[]`): Allowlist of skill names to expose to the agent + slash-command picker.
  `undefined` means "every discovered skill" (default). An empty array
  fully disables the skills subsystem (no scan, no tool injection).
- `environments?` (`EnvironmentSettings`): Session environments — which kinds are offered at creation, the
  default, and each kind's options (secrets live in the environment
  credential store, not here). Absent = defaults: every registered kind
  enabled, `local` the default. Read through `environment-settings.ts`.
- `extensionConfig?` (`Record<string, Record<string, unknown>>`): Values for extension-contributed config items, namespaced
  `extName → itemKey → value`. Written by the Settings UIs when the
  user edits a knob an extension declared via `ctx.registerConfigItem`;
  read back default-first through the core resolver
  (`resolveExtensionConfigValue`) so a missing / malformed value never
  surfaces — it degrades to the item's `default`.

  `undefined` (default) means no extension config has been touched.
  Rides the same persistence as every other setting (TUI `state.json`
  blob + round-trip; GUI `SettingsState` + nested `writeSettingsPatch`
  compare), so it needs no bespoke storage. Unknown extension / item
  keys are harmless — reads ignore anything without a registered schema.
- `externalEditor` (`boolean`): Open the current prompt draft in the user's `$EDITOR` / `$VISUAL`
  (git-style: write to a temp file, edit, reload on save) via the
  `openExternalEditor` keybinding. Purely a prompt-composing affordance
  — the agent sees the same final text either way. Off disables the
  keybinding entirely (the key falls through to its normal behavior).
  Default: on.
- `fileLogging` (`boolean`): Persist this session's logs to `${stateDir}/logs/${datetime}.txt`
  (`~/.zidane/logs/…` in the default layout). The in-TUI Logs panel
  streams logs regardless; this only controls durable, `tail`-able
  on-disk capture. Off by default — no directory or file is created
  until it's enabled. Toggling takes effect live (the file opens on
  the next log line).
- `freezeSessionPrompt` (`boolean`): Freeze each session's system prompt at its first run and announce later
  changes (date, working directory, AGENTS.md, skills, searchable tools, MCP
  instructions) as notes appended to the conversation, so agent rebuilds and
  resumes never rewrite the prompt prefix: provider prompt caches keep
  hitting and Anthropic's replayed thinking stays valid. The browser and
  terminal tools a session started with stay with it. `/refresh`
  re-reads the prompt once, on purpose.

  Applies to every profile's prompt without a launch `--system`
  replacement. Each composition a session runs on keeps its own frozen prompt.

  Default: on.
- `groupToolCalls` (`boolean`): Collapse ALL tool-call rows within one exchange (the span between two
  user prompts) into a single summary group row — like adjacency-based
  tool batching, but exchange-wide. Separators and compaction summaries
  also end a group. Subagent-owned tool rows stay inside their subagent
  cluster. Expanding the group row shows the individual rows exactly as
  without the setting. Default: on.
- `hideSubagentOutput` (`boolean`): Hide a subagent's internal events (its streamed markdown, thinking,
  tool calls, and tool results) from the transcript. The `🌱` start and
  `✓` end markers stay visible so the user still sees that a subagent
  is working and when it finished. Default: on — subagent runs are
  usually noisy and the parent's final response already summarizes them.

  When off, those events are visible and wrapped in a dim bordered box
  for clear visual hierarchy.
- `imageGeneration` (`boolean`): Give the agent the `generate_image` tool when a connected provider can
  generate images (OpenRouter, OpenAI with a platform key, Google Gemini,
  xAI). Each call bills the provider; in safe mode it asks first unless
  `generate_image` is safelisted, with safe mode off it does not ask.
  Takes effect on the next agent build. Default: on.
- `imageModel` (`string`): Default model of `generate_image`, as `"providerKey/modelId"`
  (e.g. `"openrouter/google/gemini-3-pro-image"`). Empty string = the first
  available provider's default image model. A stale pick falls back the
  same way. Set from the model picker's Image tab or Settings.
- `importForeignConfigs` (`boolean`): Discover MCP servers and skills from OTHER coding agents' configs —
  Claude Code (`.mcp.json`, `~/.claude.json`, `.claude/skills`), Codex
  (`~/.codex/config.toml`, `.codex/skills`), and Pi (`.pi/…`,
  `~/.pi/agent/…`) — at both project and user level. Read-only: the
  files stay owned by the other tools; zidane's own locations always
  win name collisions. Off by default — foreign stdio servers execute
  local commands, so importing them is an explicit opt-in.
- `importRulerConfigs` (`boolean`): Discover MCP servers, skills, and instruction markdown from
  [Ruler](https://github.com/intellectronica/ruler) directories —
  project `.ruler/` and the XDG-global `~/.config/ruler/` — see
  `ruler-configs.ts`. Read-only, same collision rules as
  `importForeignConfigs` (zidane's own locations win). ON by default:
  `.ruler/` is an intentional, committed cross-agent source of truth
  (unlike the incidental per-tool state files behind
  `importForeignConfigs`), so a ruler-managed repo works out of the box.

  SECURITY CARVE-OUT: ruler *stdio* servers (`command`/`args`) still
  require `importForeignConfigs` — a default-on flag must not execute
  repo-committed commands on the first bootstrap of a freshly cloned
  repo. Remote (url) servers, skills, and instructions import freely.
- `inlineAutocomplete` (`boolean`): Inline ghost-text autocomplete in the prompt: as you type, providers
  (built-in path completion + extension-registered ones) suggest a dim
  suffix at the cursor; Tab accepts it. Purely a prompt-composing
  affordance — the agent sees the same final text either way.
  Default: off.
- `interactionMode` (`InteractionMode`): Policy for `ask_user` / `present_plan`: prompt normally, resolve them
  automatically from repository/task context, or omit both tools. Switching
  between Ask and Skip applies live; Disabled affects the next activation.
- `loadProjectExtensions` (`boolean`): Run extensions discovered under `<project>/.zidane/extensions`
  (project-scoped). Off by default — project extensions execute
  arbitrary local code. The
  `ZIDANE_TRUST_PROJECT_EXTENSIONS=1` env flag is honored as a
  fallback.
- `machineWaitMinutes?` (`number`): How long a run waits for an offline machine (`docs/MACHINES.md` §
  Offline) before it stops with a notice. Unset → 15.
- `maxCostUsd` (`number`): Hard USD cost ceiling per run; the budget breaker stops the run when it is
  crossed. `0` = unlimited. Maps to `behavior.maxCostUsd`. Default: `0`.
- `maxOutputTokens` (`number`): Hard cap on output tokens per turn. `0` = model-driven (no cap — Anthropic
  uses the model's max output, OpenAI-style providers let the model run to a
  natural stop). Maps to `behavior.maxTokens`. Default: `0`.
- `maxParallelSubagents` (`number`): Most subagents a session runs at once, nested ones included: its spawn
  tool's cap (`createSpawnTool({ maxConcurrent })`). Past it the main
  agent's spawns wait for a slot and a subagent's are refused. `0` =
  unlimited. Above the loop's default tool concurrency
  (`behavior.maxConcurrentTools`, 10) the session runs that many tool
  calls at once too. Takes effect on the next run. Default: `3`.
- `maxTotalTokens` (`number`): Hard total-token ceiling per run; the budget breaker stops the run when it
  is crossed. `0` = unlimited. Maps to `behavior.maxTotalTokens`. Default: `0`.
- `maxTurns` (`number`): Hard cap on agent turns per run. `0` = unlimited. Maps to
  `behavior.maxTurns`. Default: `0`.
- `maxWallMinutes` (`number`): Hard wall-clock ceiling per run, in minutes; the budget breaker stops the
  run when it is crossed. `0` = unlimited. Maps to `behavior.maxWallMs`
  (×60000). Default: `0`.
- `mcpSharing?` (`Record<string, boolean>`): Per-server override of `McpServerConfig.shared`, by server name: the
  user's choice of one process for every session (`true`) or one per
  session (`false`). Wins over what `mcps.json`, a plugin or an
  extension set; a server missing here keeps its config's value (default
  shared). Applied at agent build to every server source.
- `messageDelivery` (`MessageDelivery`): What happens to a message that you send while a run works.
  `'queue'`: it waits in the queue and is sent when the run ends.
  `'steer'`: it goes into the run (`agent.steer()`), and the model reads
  it after the current step. A `/` command always waits in the queue, and
  so does a message that the run refuses. A client can ask for the other
  delivery for one message (Alt+Enter). Default: `'queue'`.
- `mouseInteractions` (`boolean`): Mouse interactions — clickable hints, row clicks, checkbox zones,
  hover highlights, and wheel scrolling inside the TUI. When off, the
  renderer stops requesting terminal mouse reporting entirely
  (`renderer.useMouse = false`), so EVERY clickable surface is disabled
  at the source — no per-surface opt-outs to drift. As a side effect
  the terminal's NATIVE mouse behavior returns: text selection, copy,
  and scrollback belong to the terminal emulator again, which is
  exactly what mouse-averse / screen-reader / tmux-copy-mode users
  want from this switch.

  Applied live via the renderer setter — takes effect immediately, no
  restart needed. Default: on.
- `notifications` (`boolean`): Desktop notifications while no Zidane window is focused: a run finished
  or failed, an approval or a question is waiting. The dock badge counts
  the waiting approvals and questions. Default: off.
- `notificationSound` (`boolean`): Play the system's default sound with each notification. Default: on.
- `openIn` (`OpenInTarget`): Where a clicked file or diff opens: the user's `$VISUAL` / `$EDITOR`
  (`editor`) or the host's own viewer (`panel`: the GUI's Files / Diffs
  side-panel tabs; the TUI falls back to the editor). Default: `panel`.
- `orchestration` (`boolean`): Orchestration: a pinned "main" session that can create, steer, and read
  other sessions ("workers") and receives their outcomes as events in its own
  history. Off by default — nothing is created, pinned, or bound until on.
- `persistToolResults` (`boolean`): Persist large `tool_result` outputs to disk and substitute a
  `<persisted-output>` stub (preview + filesystem path) inline. Built-in
  profiles default to 8 KiB threshold via `behavior.persistThreshold`;
  this setting is the user-facing kill-switch. When off, the TUI
  overrides `behavior.persistThreshold = 0` at agent build time so the
  loop's persistence branch never fires.

  The setting takes effect on the next session activation — toggling
  mid-stream doesn't tear down an in-flight tool call. Persisted blobs
  already on disk stay readable via `read_file`; only future tool
  results are affected by the flip.

  Default: on.
- `preserveScroll` (`boolean`): The desktop app reopens a conversation where its view was left: the row
  that was under the top of the view, at the same height, even if the
  conversation grew meanwhile. One left at the bottom reopens at its latest
  message, as every conversation does with this off. Remembered by the
  window until it reloads. Default: on.
- `projectHome` (`boolean`): Experimental, desktop app. The new-chat screen of a folder becomes its
  project home: the project's header, the composer, then blocks that
  extensions add (`registerPanel` with `surfaces: ['home']`; usage, recent
  sessions). Clicking a project's name in the sidebar opens it, and the
  group's collapse moves to a chevron at the end of its buttons. Off: the
  welcome screen and sidebar as before. Default: off.
- `promptSuggestions` (`boolean`): After a successful run, ask the text model (`textModel`) for the
  user's likely next prompt and offer it in the empty composer
  (`suggestion:ready`). One small call per run. Default: off.
- `remoteHosts` (`boolean`): Remote hosts (zidane-server): the opt-in for the host-picking UI. Shown
  in Settings as "Remote hosts"; launching with `--host` is its own
  explicit opt-in. `ZIDANE_NO_REMOTE=1` wins (`remoteHostsGate`).
  Default: off.
- `renderDiagrams` (`boolean`): The desktop and web apps draw a reply's \`\`\`mermaid fences as diagrams
  (Pele, themed with the app's colors and fonts), each with a toggle to
  its source. Off shows them as code. The terminal always shows the
  source. Default: on.
- `resumeLastSession` (`boolean`): Auto-resume the previously-active session on launch. Default: on.
  When off, the TUI always lands on the sessions list (or a fresh
  session if the list is empty), regardless of what `state.json`
  remembers as the last session id.

  The setting is read once on launch — toggling it mid-session
  affects the NEXT start, not the current one. `state.lastSessionId`
  is still tracked underneath so flipping the setting back on
  restores the resume behavior without losing the pointer.
- `resumeOnMaxTokens` (`boolean`): Auto-resume on output-cap truncation. When on, a turn that hits the
  model's output ceiling (`max_tokens`) without finishing injects a
  "Please continue." and keeps going, instead of the run stopping
  mid-output. Maps to `behavior.resumeOnMaxTokens`. Default: off.
- `retryMaxAttempts` (`number`): Provider retry budget — max attempts (including the first) for a
  failed `provider.stream()` call: rate-limit/overload errors the
  provider classifies retryable, plus first-response watchdog timeouts
  (a silent stall re-issues instead of killing the run or child).
  `1` disables retry, `0` = library default (3). Maps to
  `behavior.retry.maxAttempts`. Default: `0`.
- `runEstimates` (`boolean`): Estimate how long each run and subagent has left: a decision when the
  run has gone a few seconds, again when its todo list moves and once if
  it goes over. Saved on the run (`SessionRun.estimate`); windows count
  down from it. Needs `decisions`. Default: off.
- `sandbox` (`boolean`): Run the model's `shell` tool commands under the OS sandbox: macOS
  Seatbelt (`sandbox-exec`) or Linux bubblewrap (`bwrap`). Writes are
  limited to the project directory, the temp dirs, git's common dir for
  linked worktrees, and `Settings.sandboxAllowWrite`; credential
  directories (`~/.ssh`, `~/.aws`, zidane's `credentials.json`, …) are
  unreadable. `write_file` / `edit` honor the same writable set in-process.

  Scope is the same as Claude Code / Codex: kernel enforcement covers
  SUBPROCESSES only. Not covered: `!` shell commands typed by the user,
  MCP servers, `fetch_url` / `web_search`, `read_file` (reads stay
  unrestricted so persisted tool outputs and skills remain reachable).
  Docker / remote-sandbox execution contexts ignore it (already isolated).

  Fails closed: if the backend is missing on this host, every shell
  command returns an error naming the reason instead of running
  unsandboxed. Takes effect on the next session activation.

  Default: off.
- `sandboxAllowWrite` (`string`): Extra writable paths for the sandbox, comma- or newline-separated,
  `~` expanded, relative entries resolved against the project cwd
  (e.g. `~/.cache, ~/.bun/install/cache`). Empty = none. Only
  meaningful with `Settings.sandbox`.
- `sandboxNetwork` (`SandboxNetwork`): Network policy for sandboxed commands. `'allow'` (default) keeps `bun
  install`, `git fetch`, `gh` working; `'deny'` removes all socket
  access. Without network isolation a compromised command can still
  exfiltrate whatever it can read. Only meaningful with `Settings.sandbox`.
- `schedules` (`boolean`): Schedules: prompts and extension handlers that run at a set time (a
  reminder fires once). Off by default: no timer runs and nothing is read
  until on. See docs/SCHEDULES.md.
- `schedulesAgent` (`boolean`): The agent's `schedule` tool, while `schedules` is on. On by default, so
  turning on Schedules gives the agent the tool until you turn this off.
- `secrets` (`boolean`): Let the agent ask for secrets (API keys, tokens) with a `secret`
  question in `ask_user`. The value goes to a private file per project,
  never into the chat; commands read it at `$ZIDANE_SECRETS/NAME`, and
  tool output shows it as `[secret:NAME]`. A change rebuilds live agents.
  See `docs/INTERACTIONS.md` § Secrets. Default: off.
- `sessionIcon` (`string`): Icon shown before the session title when `terminalTitle` is
  `'icon-session'`. Free-text (typically a single emoji). Empty falls
  back to ⚽. Ignored by every other `terminalTitle` mode.
  Default: `''`.
- `showAllProjects` (`boolean`): Show sessions from every project (and pre-tagging legacy ones)
  in the sessions list, instead of filtering to the current
  project. Off by default — each project shows only its own
  conversations. Flip on to inspect / jump across projects without
  leaving the TUI. Per-row project labels appear when on.
- `showCacheNotices` (`boolean`): Prompt-cache notices (`⚡ cache expired: +N tok`, `cache break in …`)
  as transcript rows. Off hides them from the history; the footer chip
  still reports the current run's break. Default: off.
- `showCompactionSummary` (`boolean`): Render the LLM-generated compaction summary text under the
  `⎯⎯ N turns compacted` boundary row. Off keeps the
  metadata-only card (the summary still drives the provider context
  either way — this is purely a transcript-visibility toggle, useful
  for auditing what the model actually carries forward). Default: off.
- `showEditDiffs` (`boolean`): Render `edit` / `multi_edit` / `write_file` tool calls as a clean
  unified diff (red `-` / green `+` lines) instead of the raw
  `↳ name({"path":"…","old_string":"…"})` JSON dump. When on, the
  paired successful `tool-result` ("Edited path: …") is suppressed
  too since the diff already conveys success; errors still show.

  Off falls back to the unstructured `↳ name(args)` line + the
  regular `tool-result` block. Default: on.
- `showFullShellOutput` (`boolean`): Render EVERY line of `!` shell-mode output in the transcript instead
  of the 10-line tail window. Display-only: the persisted / wire-visible
  result keeps the shell tool's 32 KiB tail-priority trim regardless, so
  flipping this never changes what the model reads. Default: off.
- `showRowIcons` (`boolean`): Desktop app: the icon that leads each transcript row (you, the agent,
  how a step went). Off drops the icon column and the rows take its room;
  a step's line then says itself whether it runs or failed. The Focused
  conversation preset turns it off. Default: on.
- `showRunReceipt` (`boolean`): Per-run receipt (`run-summary` row: model, duration, turns, cost,
  tokens, tk/s) at the end of every run. Display only — the receipt is
  never persisted, so flipping this is instant on replay too. Default: on.
- `showThinking` (`boolean`): Show the model's thinking blocks in the transcript.
- `showThrobber` (`boolean`): Render the inline gradient throbber (cycling hex/symbol glyphs +
  optional "Thinking…" label) at the tail of the transcript while a
  run is streaming. The throbber is purely decorative — `busy` state
  is also surfaced by the prompt's status row and the streaming
  markdown's incremental updates, so users who find the animated
  glyphs noisy / distracting can flip this off without losing the
  "assistant is working" signal.

  Default: off — opt-in to keep the transcript calm by default.
- `showTodoIndicator` (`boolean`): Show the subtle one-line "in progress todo" indicator above the
  prompt input. Hidden entirely when there's no session, no active
  run, or no `in_progress` item in the active run's `todowrite` list
  — so flipping this off is a clean "I don't want even the bar".

  The dedicated modal (`ctrl+t` by default — see [`KeyAction`](https://zidane.si/docs/reference/chat.md#KeyAction))
  is unaffected: opening it is an explicit user gesture.

  Default: on.
- `showToolAttachments` (`boolean`): Under compact density's "Working" row, a strip of every image the
  exchange's tool calls returned (screenshots, read images, generated
  images), opening onto a lightbox that steps through them. Inert in full
  density, where each tool row already shows its images. Desktop only.
  Default: on.
- `showToolResults` (`boolean`): Show tool result rows in the transcript.
- `showUsageIndicator` (`boolean`): Show the subscription-usage gauge (`▰ 5h 43%`) in the footer, left of
  the cost. Hidden entirely when the active provider reports no usage, so
  flipping this off is for the case where the provider DOES report it and
  you would rather not see it.

  The panel (`ctrl+p` by default) is unaffected: opening it is an explicit
  user gesture, and it loads its own data.

  Distinct from `Settings.subscriptionUsage`, which is about network
  access rather than display: that one decides whether providers are asked
  at all. With the indicator hidden there is nothing left for the footer's
  automatic probe to paint, so it stops too, and the panel's on-open fetch
  becomes the only caller.

  Default: on.
- `sidebarRunDetails` (`boolean`): The desktop sidebar's run details: what each session's run is doing
  (thinking, writing, the tool call) and for how long, and its commits and
  pull requests under its title. Off = a running session just says
  "Working". Default: on.
- `smoothStreaming` (`boolean`): Drip-feed the assistant's text content character-by-character at a
  smooth cadence (typewriter effect) instead of committing each
  provider chunk in batches as it arrives. The display rate is capped
  so fast LLMs don't blast a wall of text into the transcript; an
  adaptive burst kicks in when the buffer falls behind, and updates queued
  at a turn boundary (tool calls, finalize) apply once the buffered text
  has typed out, so the stream order the user reads is preserved.

  Off drains everything on each \~16 ms tick (every provider delta lands
  in the transcript on the next tick, unsmoothed).

  Takes effect on the next provider delta — flipping mid-stream is
  safe; the buffer reads the setting on every tick.

  Default: on.
- `spaceList` (`Space[]`): The spaces, in switcher order. Always holds the Default space once
  normalized (`normalizeSpaces`); ignored while `spaces` is off.
- `spaces` (`boolean`): Spaces: group sessions under named spaces (Personal, Work…), each with
  its own default model. Off by default; off ignores every stored space
  id and lists sessions as if spaces did not exist. See `./spaces`.
- `stableToolList` (`boolean`): Keep the model's tool list byte-stable under lazy MCP disclosure. When
  on, tools surfaced through `tool_search` are invoked through a fixed
  `call_tool { name, arguments }` native tool instead of being appended
  to the request's `tools` array, so tool discovery never invalidates
  the prompt cache (providers hash `tools → system → messages`; any tools
  change re-bills the entire conversation). Arguments are still validated
  against the surfaced tool's own schema, and the transcript shows the
  inner tool exactly as a direct call.

  Maps to `behavior.toolSearch.mode` at agent build time: on →
  `'dispatch'`, off → `'unlock'` (surfaced tools are appended to the
  tool list; on Anthropic that also drops replayed thinking at each
  discovery wave). Takes effect on the next session activation.

  Default: on.
- `subagentModels` (`boolean`): Allow subagents to run on DIFFERENT models than the parent. When on,
  the spawn tool is built with the model catalog of the agent's
  providers (primary + fallback chain): its schema gains a `model`
  field and a `model_search` tool is registered, so the main model can
  cost-tier its delegation (mechanical work on cheaper models). When
  off, neither surface exists and children always inherit the parent
  run's model. Takes effect on the next
  agent build (session activation / provider change). Default: on.
- `subscriptionUsage` (`boolean`): Read subscription usage (rate-limit windows, credit balances) from the
  signed-in providers' own usage endpoints, for the footer indicator and the
  usage panel.

  Fires on provider switch and after a run completes, never on a timer, and
  only for providers that have a stored credential. The endpoints are not
  the ones inference goes to (`chatgpt.com/backend-api`, `grok.com`), and
  all but OpenRouter's are undocumented, so this is a separate consent from
  having signed in.

  Off still leaves the panel reachable: `ctrl+r` inside it fetches once, as
  an explicit gesture. The `ZIDANE_NO_USAGE_CHECK` and `CI` env vars
  force-disable everything including that refresh, regardless of this
  setting.

  Default: on.
- `targetFps` (`number`): Renderer target + max fps (the same value drives both — uncapping
  `maxFps` past the target just burns CPU on a terminal that's
  already painting at the target rate). The Settings modal cycles
  through `30 / 60 / 120`:

  - `30`  — easy on CPU + battery; matches OpenTUI's historical
    default. Streaming still feels fine; modal animations and
    cursor blink visibly stutter on fast input.
  - `60`  — recommended default. Matches the refresh rate of
    virtually every non-ProMotion display; sweet spot between
    smoothness and load.
  - `120` — only worth it on a 120Hz / ProMotion display + a
    modern terminal (Ghostty, Kitty, Alacritty, iTerm2 ≥ 3.5).
    Costs roughly 2× the CPU of 60 for marginal gains on most
    setups — sustained 120fps over SSH / nested tmux can add
    input latency from stdout backpressure.

  Applied live via `renderer.targetFps` / `renderer.maxFps` — flipping
  the setting takes effect on the next frame, no restart needed.

  Default: `60`.
- `terminal` (`boolean`): Terminals (the GUI's drawer and Terminal panel tab; run by the daemon in
  the session's environment). Off = no terminal UI, the host refuses to
  open terminals and closes the user's, and the agent gets no terminal
  tools. Default: on.
- `terminalAgent` (`boolean`): Whether the agent may use the session's terminals (`terminal_list`,
  `terminal_read`, `terminal_write`). Only meaningful while `terminal` is
  on. Default: on.
- `terminalAgentTasks` (`boolean`): Whether the agent's background `shell` tasks run in terminals the user
  can watch (agent tabs). Off = pipes. Only meaningful while
  `terminal` is on. Default: on.
- `terminalTitle` (`'session' | 'off' | 'app' | 'icon-session'`): What the TUI sets the terminal window / tab title to. Applied via
  OpenTUI's OSC title writer (`renderer.setTerminalTitle`) and
  re-applied whenever the value or the active session's title changes.

  - `'off'`          — never touch the title; whatever the shell set
    survives (default).
  - `'app'`          — pin it to `zidane`.
  - `'session'`      — track the current session's title, falling back
    to `zidane` on screens without an active session
    (sessions list, auth).
  - `'icon-session'` — like `'session'` but prefixed with `sessionIcon`
    as `{icon} · {title}` (e.g. `⚽ · Fix the bug`).

  The title shown before zidane took over is pushed onto the terminal's
  title stack and restored on exit (and when switched back to `'off'`).
  Terminals without a title stack (e.g. VS Code's integrated terminal)
  get an OSC clear instead, so our title never lingers after close.
  Default: `'off'`.
- `textModel` (`string`): Model for side-channel text (session titles, next-prompt suggestions),
  as `"providerKey[@account]/modelId"`. Empty string = automatic: the
  smallest current model of the session's provider (`resolveTextModel`).
  A picked provider that isn't signed in falls back to automatic.
- `theme` (`string`): Active theme id — looked up in `BUILTIN_THEMES` to resolve the palette,
  select styling, syntax highlight tokens, etc. Default: `'default'`.
  Unknown ids fall back to `DEFAULT_THEME` rather than throwing.
- `thinkingAutoExpand` (`boolean`): Desktop app: keep a thinking block expanded while it streams instead of
  showing it collapsed. Default: off.
- `thinkingBinding` (`'error' | 'off' | 'drop_block'`): Anthropic only. What the API does with a replayed `thinking` block whose
  prefix (system, tools, earlier messages) changed since it was produced:
  `'drop_block'` drops it server-side and the request succeeds (default),
  `'error'` fails the run with the API's 400 — no retry, history untouched
  (debugging), `'off'` sends no preference and the account default applies.
  Published to `ZIDANE_THINKING_BINDING_SETTING` by every runtime that
  starts runs (in-process TUI on change, chat host per send) and read by
  the Anthropic provider per request; inert on every other provider.
  Precedence: `anthropic({ thinkingBinding })` > `ZIDANE_THINKING_BINDING`
  in the launching shell > this setting > `'drop_block'`.
  Default: `'drop_block'`.
- `toolCallDisplay` (`ToolCallDisplay`): Rendering mode for `tool` events. See [`ToolCallDisplay`](https://zidane.si/docs/reference/chat.md#ToolCallDisplay).
  A stored legacy `showToolCalls` boolean maps to this on read
  (`migrateStoredSettings`).
- `transcriptDensity` (`TranscriptDensity`): Exchange layout. See [`TranscriptDensity`](https://zidane.si/docs/reference/chat.md#TranscriptDensity). Default: `'compact'`.
- `uiMode` (`UiMode`): Chat-screen chrome density. See `UiMode` for the contract —
  `'full'` keeps every advertised shortcut visible, `'minimal'`
  collapses the chat session UI to agent / model / keybindings + the
  `@` / `/` triggers, with the rest one keystroke away via `ctrl+y`.
  Default: `'full'`.
- `useDaemon` (`boolean`): Execute sessions in the shared daemon instead of in-process, so runs
  survive window close and every window on the same session (TUI,
  `zidane attach`) converges on the same live stream. Launching
  `zidane` ALWAYS opens the full TUI regardless — this flag only
  decides where the agent loop runs (experimental; `/` commands,
  `!` shell, and attachments still require the in-process runtime).
  Default: off.
- `userInstructionsScope` (`'user' | 'project' | 'none' | 'both'`): Which scope(s) of personal instructions to inject into the system
  prompt at session activation. Discovered files:

  - `'none'`    — skip discovery entirely. No `AGENTS.md` / `CLAUDE.md`
    files are loaded. Useful for reproducible reviews,
    demos, debugging the base system prompt, or running
    zidane against a repo whose committed `AGENTS.md`
    you don't trust yet.
  - `'user'`    — `~/.agents/AGENTS.md`, `~/.{prefix}/AGENTS.md`,
    `~/.claude/CLAUDE.md`. Cross-project preferences
    (coding style, "always run lint before commit", …).
  - `'project'` — `<root>/AGENTS.md`, `<root>/.agents/AGENTS.md`,
    `<root>/.{prefix}/AGENTS.md`, `<root>/CLAUDE.md`.
    Repo-local context ("use pnpm, not npm").
  - `'both'`    — union; user files render first (broader), project
    second (closer to the conversation, narrower
    context wins in the model's eyes).

  Within each enabled scope, EVERY matching file is loaded — no
  shadowing. Empty / whitespace-only files are skipped. The same scope
  governs `SYSTEM.md` override discovery.

  Takes effect on the next session activation. Default: `'both'`.
- `webSearch` (`boolean`): Give the agent the `web_search` tool (Anthropic's native search where the
  provider offers it). Off removes it from every session, its subagents and
  `/side` sessions; a change rebuilds live agents. Default: on.

Source: [src/chat/types.ts:781](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L781)

### `SettingsCategoryDescriptor`

Interface.

Display data for one [`SettingsCategory`](https://zidane.si/docs/reference/chat.md#SettingsCategory).

```ts
interface SettingsCategoryDescriptor
```

**Properties**

- `description` (`string`): One-line gloss the GUI can show as a section subtitle. The TUI omits this.
- `id` (`SettingsCategory`): The category this entry describes.
- `label` (`string`): Display label — used by the TUI tab strip + any GUI tab/section header.

Source: [src/chat/settings-catalog.ts:75](https://github.com/Tahul/zidane/blob/main/src/chat/settings-catalog.ts#L75)

### `SettingsInput`

Interface.

Free-text settings edited via a single-line input. The `SettingsModal`
renders these as a row showing the current value (or `placeholder` when
empty); `enter` opens an inline editor. Same shape a GUI can consume as
a text field.

```ts
interface SettingsInput
```

**Properties**

- `category` (`SettingsCategory`): Tab the row belongs to.
- `description` (`string`): One-line explanation of the setting.
- `key` (`StringSettingKey`): Setting the row writes.
- `label` (`string`): Row label.
- `placeholder` (`string`): Shown when the value is empty — also the conventional "default" hint.

Source: [src/chat/settings-catalog.ts:691](https://github.com/Tahul/zidane/blob/main/src/chat/settings-catalog.ts#L691)

### `SpawnEndInfo`

Interface.

How a worker ended, carried on its `spawn-end` marker (`StreamEvent.spawn.end`).

```ts
interface SpawnEndInfo
```

**Properties**

- `costUsd?` (`number`): USD cost over the worker's whole subtree. Absent when no provider reported one.
- `durationMs?` (`number`): Wall-clock time from start to end. Absent when the start was not seen.
- `status` (`'error' | 'aborted' | 'done'`): `done` also covers a timeout, as the TUI shows it.
- `tokensIn` (`number`): Input tokens over the subtree, cache reads and writes included.
- `tokensOut` (`number`): Output tokens over the subtree.

Source: [src/chat/types.ts:21](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L21)

### `SpawnStartRecord`

Interface.

Wall-clock start + task preview per in-flight child, keyed by spawn id.
Captured at `spawn:before` and read back at `spawn:complete` — drives
the elapsed time on the spawn-end marker and copies the stable task
preview onto the terminal marker.

```ts
interface SpawnStartRecord
```

**Properties**

- `model?` (`string`): Model the child runs on.
- `startedAt` (`number`): Epoch milliseconds when the child started.
- `task` (`string`): The task prompt given to the child.

Source: [src/chat/spawn-markers.ts:24](https://github.com/Tahul/zidane/blob/main/src/chat/spawn-markers.ts#L24)

### `StartUpdateWorkerOptions`

Interface.

Options for [`startUpdateWorker`](https://zidane.si/docs/reference/chat.md#startUpdateWorker).

```ts
interface StartUpdateWorkerOptions
```

**Properties**

- `command` (`readonly string[]`): Argv that runs the worker; `--request <value>` is appended. E.g. `['/usr/local/bin/zidane', '__update-worker']`.
- `env?` (`ProcessEnv`): Worker environment (PATH decides which package managers it finds). Defaults to `process.env`.
- `onLine?` (`(line: string) => void`): Each line the worker logs for this run.
- `pollMs?` (`number`): Log and result poll interval. Defaults to 250 ms.
- `request` (`Omit<UpdateWorkerRequest, 'v' | 'id'>`): What to install.
- `spawn?` (`{ (command: string, options?: SpawnOptionsWithoutStdio): ChildProcessWithoutNullStreams; (command: string, options: SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioPipe>): ChildProcessByStdio<Writable, Readable, Readable>; (command: string, options: SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioNull>): ChildProcessByStdio<Writable, Readable, null>; (command: string, options: SpawnOptionsWithStdioTuple<StdioPipe, StdioNull, StdioPipe>): ChildProcessByStdio<Writable, null, Readable>; (command: string, options: SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioPipe>): ChildProcessByStdio<null, Readable, Readable>; (command: string, options: SpawnOptionsWithStdioTuple<StdioPipe, StdioNull, StdioNull>): ChildProcessByStdio<Writable, null, null>; (command: string, options: SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioNull>): ChildProcessByStdio<null, Readable, null>; (command: string, options: SpawnOptionsWithStdioTuple<StdioNull, StdioNull, StdioPipe>): ChildProcessByStdio<null, null, Readable>; (command: string, options: SpawnOptionsWithStdioTuple<StdioNull, StdioNull, StdioNull>): ChildProcessByStdio<null, null, null>; (command: string, options: SpawnOptions): ChildProcess; (command: string, args?: readonly string[], options?: SpawnOptionsWithoutStdio): ChildProcessWithoutNullStreams; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioPipe>): ChildProcessByStdio<Writable, Readable, Readable>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioPipe, StdioPipe, StdioNull>): ChildProcessByStdio<Writable, Readable, null>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioPipe, StdioNull, StdioPipe>): ChildProcessByStdio<Writable, null, Readable>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioPipe>): ChildProcessByStdio<null, Readable, Readable>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioPipe, StdioNull, StdioNull>): ChildProcessByStdio<Writable, null, null>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioNull>): ChildProcessByStdio<null, Readable, null>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioNull, StdioNull, StdioPipe>): ChildProcessByStdio<null, null, Readable>; (command: string, args: readonly string[], options: SpawnOptionsWithStdioTuple<StdioNull, StdioNull, StdioNull>): ChildProcessByStdio<null, null, null>; (command: string, args: readonly string[], options: SpawnOptions): ChildProcess }`): Injected for tests.

Source: [src/chat/auto-update-worker.ts:268](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L268)

### `StateStoreApi`

Interface.

Reads and writes the persisted [`TuiState`](https://zidane.si/docs/reference/chat.md#TuiState).

```ts
interface StateStoreApi
```

**Properties**

- `load` (`() => TuiState`): Reads the saved state.
- `path?` (`string`): The file, when the state lives in one: hosts watch it for changes other processes make.
- `save` (`(state: TuiState) => void`): Writes the state.

Source: [src/chat/store.ts:126](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L126)

### `StorageDirs`

Interface.

Per-slot directories. In legacy / default modes the four fields all
point at the same path; in XDG mode they fan out into their
spec-conventional locations.

`mode` carries the resolution branch so a host that wants to print
a startup banner ("zidane is using XDG paths") can detect the
layout without re-running the logic.

```ts
interface StorageDirs
```

**Properties**

- `cacheDir` (`string`): Regenerable data such as persisted tool results.
- `configDir` (`string`): Credentials, `mcps.json`, keybindings and other user-editable files.
- `dataDir` (`string`): Long-lived data such as the sessions database.
- `mode` (`StorageMode`): Resolution branch that produced the layout.
- `stateDir` (`string`): Per-machine UI state such as `state.json`.

Source: [src/chat/xdg.ts:65](https://github.com/Tahul/zidane/blob/main/src/chat/xdg.ts#L65)

### `StoredCredential`

Interface.

One stored credential of a provider, addressed by the file key it lives at.

```ts
interface StoredCredential
```

**Properties**

- `active` (`boolean`): True for the canonical slot, i.e. the credential readers resolve.
- `credential` (`ProviderCredential`): The stored credential.
- `key` (`string`): File key — the selection identity for promote / remove / relabel.

Source: [src/chat/credentials.ts:128](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L128)

### `StreamBuffer`

Interface.

Handle returned by [`useStreamBuffer`](https://zidane.si/docs/reference/chat.md#useStreamBuffer).

```ts
interface StreamBuffer
```

**Properties**

- `appendImmediate` (`(evt: StreamEvent) => void`): Drain pending deltas immediately, then append a non-streaming event.
- `flush` (`() => void`): Drain pending deltas without further transformation.
- `flushAndUpdate` (`(update: (events: StreamEvent[]) => StreamEvent[]) => void`): Drain pending deltas immediately, then transform the event list.
- `queueStreamDelta` (`(kind: 'thinking' | 'markdown', delta: string, source?: StreamSource) => void`): Queue a streaming delta for the next flush tick.
- `reset` (`() => void`): Cancel any pending flush and drop buffered deltas (on session teardown).
- `whenDrained` (`(timeoutMs?: number) => Promise<void>`): Resolves once the pacer is idle — no bucket holds queued content and
  no post-drain updater is pending. Resolves immediately when already
  idle (the batched / `smoothStreaming: false` path never queues, so
  this is zero-cost there). A safety timeout (default
  `WHEN_DRAINED_TIMEOUT_MS`) resolves anyway if the ticker
  wedges — measured as a full window with NO drain progress, so a
  long response that keeps typing re-arms it and never snaps — and
  callers gating a transcript convergence on the drain can never
  dead-lock. `reset()` also releases every waiter — the backlog it
  drops would otherwise never drain.

Source: [src/chat/streaming.ts:104](https://github.com/Tahul/zidane/blob/main/src/chat/streaming.ts#L104)

### `StreamEvent`

Interface.

One row of the transcript, live or replayed from persisted turns.

```ts
interface StreamEvent
```

**Properties**

- `approval?` (`ModelApproval`): `tool` events: the decision model approved or denied this call
  (Model decision approvals). Absent when nobody or the user decided.
  Replay reads it from the tool result's metadata.
- `attachments?` (`readonly { mediaType: string; name: string; size: number }[]`): Attachment metadata for `user-prompt` events — name, media-type, and
  byte size of each file the user attached (image drag, multiline paste,
  etc.). The full content travels in the `PromptPart[]` sent to the
  model; only lightweight metadata is echoed to the transcript.
- `callId?` (`string`): Matches `tool_use.id` on the assistant turn's `tool_call` block. Lets
  out-of-process consumers (GUI, SDK, logger, replayer) pair `tool` ↔
  `tool-result` events by id instead of falling back to fragile positional
  heuristics within a tool-name bucket — the latter breaks visibly the
  moment two parallel calls share a tool name (common for `read_file`,
  `edit`, `multi_edit`).

  Always set when `kind` is `tool` / `tool-result` / `error` for a real
  tool call; absent for non-tool events (`markdown`, `thinking`,
  `user-prompt`, `separator`, `info`, `spawn-start`, `spawn-end`,
  `compact-summary`; `spawn-queued` carries its waiting call's) and for `error` events that don't have a callId
  in scope (provider / session-level failures).
- `childId?` (`string`): Subagent that produced this event. Absent / 'parent' = top-level agent.
- `compact?` (`{ cacheCreationTokens: number; cacheReadTokens: number; compactedAt: number; costUsd?: number; inputTokens: number; model: string; outputTokens: number; providerKey?: string; replacedCount: number }`): Compaction metadata — only set on `compact-summary` events. Mirrors
  the underlying `SessionContentBlock` shape but flattened to numbers
  so the renderer doesn't need to import the agent's TurnUsage type.
- `custom?` (`{ data?: JsonValue; renderer: string }`): Custom-renderer binding — only set on `custom` events. `renderer` is
  the namespaced id (`<extension>/<id>`) resolved against the LIVE
  filtered registry at render time; `data` is the JSON-safe payload the
  renderer receives. Resolution failures fall back to `text`.
- `depth?` (`number`): Nesting depth — 0 = parent, ≥ 1 = subagent (matches zidane's spawn depth).
- `detail?` (`RunErrorDetail`): `error` events ending a run: what the line leaves out, for its tooltip.
- `durationMs?` (`number`): `tool-result` events: how long the call ran, in ms. Set live by the host,
  and on reload from the saved `tool_result` block. Absent on calls saved
  before it was recorded and on replay-mode durable runs (Restate: their
  clock is journaled).
- `edit?` (`EditPayload`): Structured payload for `edit` / `multi_edit` / `write_file` tool calls,
  extracted from the model's `input` and (for `write_file`) paired with
  a pre-write snapshot read at hook time. Drives the `EditDiffBlock`
  renderer when `Settings.showEditDiffs` is on; absent means
  "render as a plain `↳ name(args)` line" (the unstructured fallback).
- `input?` (`Record<string, unknown>`): Raw tool input arguments — only set on `tool` events. Carried so the
  renderer can run per-tool formatters (e.g. `↳ Read src/foo.ts L10-25`)
  without parsing JSON back out of `text`. Plain object, same shape the
  model emitted; renderers must treat unexpected fields as optional.
- `isError?` (`true`): `tool-result` only: the call failed (the wire error bit of its result).
  Absent on success. Lets a renderer mark the call failed from its one
  result row, instead of a separate `error` row for the same failure.
- `kind` (`'thinking' | 'error' | 'tool' | 'tool-result' | 'custom' | 'info' | 'compact-summary' | 'markdown' | 'user-prompt' | 'separator' | 'spawn-start' | 'spawn-end' | 'spawn-queued' | 'tool-progress' | 'task-notification' | 'session-event' | 'run-summary' | 'command-detail' | 'command-note'`): What the row is. Decides which other fields apply.
- `label?` (`string`): `command-detail` events: the dim `▸`-prefixed header line above the body.
- `model?` (`string`): Model id a subagent runs on — set on `spawn-start` events when known
  (live via the `spawn:before` hook's `model`, reloaded sessions via the
  child run's recorded usage). Renderers show it next to the marker so
  cost-tiered delegation is visible at a glance.
- `notice?` (`{ label: string; severity: 'error' | 'info' | 'warn' }`): Harness notice metadata, set on `info` / `error` events that report
  something the HARNESS did rather than something the model said: a model
  fallback, a repeat-guard trip, history repair, a run error.

  Renderers that understand it draw an aligned one-line row (glyph gutter,
  fixed-width label, dim message). It is optional: events without it render
  as free text, and the GUI timeline switches on `kind`, not on this field.

  `text` stays the full human-readable message: the TUI collapses it to its
  first line for display, and the untruncated text is what gets persisted.

  Notices survive a reload. They persist as display-only `role: 'command'`
  turns carrying `metadata.command.notice`, which `eventsFromTurns` replays
  back into this shape, so a reopened conversation shows the same rows in
  the same places.
- `origin?` (`TextOrigin`): `user-prompt` events: who or what sent the prompt when it is not the
  session's own user in a window (a channel participant, orchestration).
  Live from the host's echo, on reload from the prompt's text block.
  Steered messages carry none.
- `parentToolCallId?` (`string`): `tool` / `tool-result` of a call another tool made through
  `ToolContext.callTool` (a codemode script's `tools.*`): the calling tool's
  `callId`. Renderers group these under the calling row (shown with
  `Settings.codemodeShowCalls`); built by `nested-call-events.ts` live and on reload.
- `refs?` (`readonly { end: number; providerId: string; start: number }[]`): Highlighted spans within `text`, in source order. Used by the renderer
  to colorize completion references in submitted prompts (`user-prompt`
  events). Provider-agnostic — the renderer only reads `start`, `end`,
  `providerId`. Spans are half-open `[start, end)`; overlapping spans
  are caller-resolved before reaching here. Field name matches
  `CompletionReference.providerId` and `PromptSegment.providerId` so
  the same identifier flows end-to-end.
- `resuming?` (`true`): `tool` events: the call belongs to a batch a durable run resumed after a
  process death (`tool-batch:resumed`) and did not finish before it: it
  runs again or is reported interrupted. Renderers show it as resuming
  until its result lands. Optional: a client that ignores it shows the call
  as running. The batch's finished calls come back as plain `tool` and
  `tool-result` events, their results rebuilt from the step records.
- `runsNestedAgent?` (`true`): With `parentToolCallId`: the nested call runs a subagent (`spawn`), so its
  row shows whatever `Settings.codemodeShowCalls` says (its worker attaches to it).
- `runSummary?` (`RunReceipt`): Structured payload for `kind: 'run-summary'`.
- `scriptProgress?` (`ScriptProgress`): On `tool-progress`: the script's progress.
- `sessionEvent?` (`SessionEvent`): Structured payload for `kind: 'session-event'` — the worker it concerns,
  its status, and the summary. Carried as data rather than encoded in `text`
  so both clients can render the row and route the click without parsing.
- `spawn?` (`{ callId?: string; end?: SpawnEndInfo; parentChildId?: string; prompt?: string; resumes?: string; startedAt: number; task?: string; turns?: number }`): Structured subagent lifecycle metadata. Keeps the stable left-hand
  identity/task lane separate from the live/terminal status tail:
  `startedAt` drives elapsed time while open; `turns` replaces the
  spinner/activity tail after completion.
- `startedAt?` (`number`): `tool` events: when the call started, epoch ms on the host's clock (the
  start its running-work item carries). Set live, and on a reloaded call the
  host still runs. Clients count a running call's time from it, not from
  when they happened to receive the event. Absent from older hosts.
- `streaming?` (`boolean`): For `markdown` events: true while the block is still receiving deltas.
  Renderers keep their markdown parser in streaming mode while this is
  true (OpenTUI's `<markdown streaming>` leaves the trailing block
  unstable to absorb tokens as they arrive). Flipped to false on
  `turn:after`.
- `syntax?` (`'diff'`): `command-detail` events: body/label syntax hint. `'diff'` colorizes
  `+N` / `−N` count tokens (added green / deleted red) via
  `segmentDiffStatLine`. Mirrors `CommandOutputBlock.detail.syntax` and
  the persisted `metadata.command.syntax` — one vocabulary at every layer.
- `synthetic?` (`boolean`): Marks a `user-prompt` event whose text is a display-only marker the
  user never typed — a `/`-command's injected-prompt echo or a context
  note's `display` line. Rendered like a prompt, but excluded from
  up-arrow prompt history (`promptHistoryFromEvents`) so synthetic
  markers (`/commit — abc123`, `/review — fixes applied`) don't
  resurface as recallable input.
- `task?` (`{ command: string; durationMs: number; exitCode: number; outputPath: string; status: 'exited' | 'killed'; taskId: string }`): Background task metadata — only set on `task-notification` events.
  Mirrors the `<task-notification>` XML's fields. Renderer reads this
  (not `text`) for the structured banner so swapping languages /
  formats stays a renderer-level concern.
- `text` (`string`): Main text of the row. Its meaning depends on `kind`.
- `tool?` (`string`): Canonical tool name for `tool` / `tool-result` events. Lets the renderer
  filter by tool — e.g. hide the parent's `spawn` tool-result when
  `hideSubagentOutput` is on, since the spawn-end marker already shows the
  same stats. Absent for non-tool events.
- `turnId?` (`string`): `SessionTurn.id` the event was emitted by — set both on historical
  events synthesized in `eventsFromTurns` and on live events tagged by
  the agent hooks (via `StreamHookContext.turnId` / `ToolHookContext.turnId`).
  Drives the TUI's "select turn" mode: each turn is addressable as a
  highlightable group of events.

  Absent on synthetic events that don't map to a turn (`spawn-start`,
  `spawn-end`, `separator`, etc.) and on events emitted before the
  first `turn:before` fires on a freshly-mounted session.
- `waitingFor?` (`{ active: number; max: number }`): On `spawn-queued`: the slots taken and the cap when the call started waiting.

Source: [src/chat/types.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L35)

### `StreamProgressInfo`

Interface.

Payload of the `stream:progress` signal — see the variant's doc.

```ts
interface StreamProgressInfo
```

**Properties**

- `elapsedMs` (`number`): Time since the turn started in ms.
- `estCostUsd?` (`number`): Running cost estimate for this turn in USD. Absent for unpriced models.
- `estimated` (`boolean`): `true` when `outputTokens` is an estimate rather than provider-counted.
- `outputTokens` (`number`): Output tokens so far.
- `turnId` (`string`): Turn that is streaming.

Source: [src/chat/host/signals.ts:285](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L285)

### `StreamSource`

Interface.

Which agent and turn a streaming delta came from.

```ts
interface StreamSource
```

**Properties**

- `childId?` (`string`): Pass `undefined` / omit for parent-agent events.
- `depth?` (`number`): Nesting depth — 0 for parent, ≥ 1 for subagents.
- `turnId?` (`string`): `SessionTurn.id` this delta belongs to. Tagged onto new events so the
  TUI's select-turn mode can group every event from one turn. Stored on
  the active bucket and refreshed on every delta so a turn boundary
  (`turn:after` flushes the bucket) doesn't carry stale ids forward.

Source: [src/chat/streaming.ts:89](https://github.com/Tahul/zidane/blob/main/src/chat/streaming.ts#L89)

### `SyntaxTokenStyle`

Interface.

One entry in a `SyntaxStyles` map — what OpenTUI's `SyntaxStyle.fromStyles`
accepts, minus the `RGBA` conversion. Renderer-agnostic JSON.

```ts
interface SyntaxTokenStyle
```

**Properties**

- `bg?` (`string`): Background color.
- `bold?` (`boolean`): Whether the text is bold.
- `dim?` (`boolean`): Whether the text is dimmed.
- `fg?` (`string`): Foreground color.
- `italic?` (`boolean`): Whether the text is italic.
- `underline?` (`boolean`): Whether the text is underlined.

Source: [src/chat/theme.ts:179](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L179)

### `SystemMdFile`

Interface.

A discovered `SYSTEM.md`.

```ts
interface SystemMdFile
```

**Properties**

- `contents` (`string`): File text.
- `path` (`string`): Absolute path of the file.
- `source` (`'user' | 'project'`): Whether the file is the project's or the user's.

Source: [src/chat/system-md.ts:38](https://github.com/Tahul/zidane/blob/main/src/chat/system-md.ts#L38)

### `SystemMdResult`

Interface.

Result of [`discoverSystemMd`](https://zidane.si/docs/reference/chat.md#discoverSystemMd).

```ts
interface SystemMdResult
```

**Properties**

- `file` (`SystemMdFile | null`): The winning override, or `null` when no `SYSTEM.md` exists (the built-in
  doctrine then applies unchanged).
- `shadowed` (`readonly SystemMdFile[]`): Files that exist but lost to `file` on precedence. Non-empty only when the
  user has both a project and a user `SYSTEM.md`. Surfaced so the UI can mark
  them as inactive instead of leaving the user wondering why edits to one of
  them changed nothing.

Source: [src/chat/system-md.ts:48](https://github.com/Tahul/zidane/blob/main/src/chat/system-md.ts#L48)

### `TarballAvailabilityOptions`

Interface.

Options for [`checkTarballAvailable`](https://zidane.si/docs/reference/chat.md#checkTarballAvailable).

```ts
interface TarballAvailabilityOptions
```

**Properties**

- `fetcher?` (`typeof fetch`): Inject for tests.
- `packageName` (`string`): Full npm package name to probe.
- `registry?` (`string`): Registry base URL. Defaults to `https://registry.npmjs.org`.
- `timeoutMs?` (`number`): Timeout for the whole probe. Defaults to 8 000 ms.
- `version` (`string`): Exact version whose tarball is probed.

Source: [src/chat/auto-update.ts:683](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L683)

### `TextMatchOptions`

Interface.

How text matches.

```ts
interface TextMatchOptions
```

**Properties**

- `caseSensitive` (`boolean`): Match case.

Source: [src/chat/workspace/search.ts:19](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/search.ts#L19)

### `TextQuestion`

Interface.

A free-text question.

```ts
interface TextQuestion extends BaseQuestion
```

**Properties**

- `default?` (`string`): Pre-fill the input with this value (still editable).
- `description?` (`string`, from `BaseQuestion`): Optional helper text under the prompt.
- `id` (`string`, from `BaseQuestion`): Stable id — keys the response back to the model.
- `placeholder?` (`string`): Placeholder shown when the input is empty.
- `prompt` (`string`, from `BaseQuestion`): Question text shown above the input (Markdown allowed).
- `required?` (`boolean`, from `BaseQuestion`): Whether the answer is required (non-empty). Default: `true` for
  `select` / `confirm` (no sensible empty), `false` for free-text
  (skipping = empty string). Free-text questions still accept an
  answer; the flag only gates submission.
- `type` (`'text' | 'textarea'`): `text` for a single line, `textarea` for several.

Source: [src/chat/interactions-protocol.ts:146](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L146)

### `Theme`

Interface.

Full theme bundle.

```ts
interface Theme
```

**Properties**

- `colors` (`ThemeColors`): Semantic text and border colors.
- `id` (`string`): Stable identifier, used as the key in `BUILTIN_THEMES` and `Settings.theme`.
- `label` (`string`): Human-readable label shown in the settings picker.
- `select` (`ThemeSelect`): Styling of select lists.
- `surfaces` (`ThemeSurfaces`): Panel and surface backgrounds.
- `syntax` (`SyntaxStyles`): Token styles for markdown and fenced code.

Source: [src/chat/theme.ts:209](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L209)

### `ThemeColors`

Interface.

Role-named color palette. Reused throughout the UI for text, borders, accents.

```ts
interface ThemeColors
```

**Properties**

- `accent` (`string`): Success / progress accent — used sparingly.
- `border` (`string`): Resting border color.
- `borderActive` (`string`): Border color on focused / active elements.
- `brand` (`string`): Primary accent — selected text, brand emphasis (e.g. `▶` markers).
- `dim` (`string`): Secondary text — captions, helper text, dim transcript lines.
- `error` (`string`): Errors — `✗` prefix on error events, validation messages.
- `model` (`string`): Model badge accent (provider/model name in the footer, links).
- `money?` (`string`): Optional tone for the footer's session-cost indicator. Falls back
  to `warn` when unset. Carved out as its own role because some
  themes (e.g. Crush) intentionally bind `warn` to a non-yellow tone
  and a separate "money" color reads more naturally on the footer.
- `mute` (`string`): Tertiary text — separators, descriptions, the `┃` tool-result bar.
- `throbber?` (`{ from: string; to: string }`): Optional gradient endpoints for the in-chat working throbber
  (`<CrushThrobber>`). Falls back to `[brand, accent]` when unset.
  Themes can pin a richer pair here — e.g. Crush's `Charple` →
  `Dolly` purple-to-pink — without having to repurpose `accent`.
- `warn` (`string`): Warnings — busy spinner, approval picker border, esc hints.

Source: [src/chat/theme.ts:33](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L33)

### `ThemeSelect`

Interface.

Select-component styling. Plain prop shape; both OpenTUI's `<select>` and any GUI equivalent can consume this.

```ts
interface ThemeSelect
```

**Properties**

- `backgroundColor` (`string`): Background of an unselected row.
- `descriptionColor` (`string`): Description color of an unselected row.
- `focusedBackgroundColor` (`string`): Background of the focused row.
- `selectedBackgroundColor` (`string`): Background of the selected row.
- `selectedDescriptionColor` (`string`): Description color of the selected row.
- `selectedTextColor` (`string`): Text color of the selected row.
- `textColor` (`string`): Text color of an unselected row.

Source: [src/chat/theme.ts:69](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L69)

### `ThemeSurfaces`

Interface.

Panel / surface backgrounds.

```ts
interface ThemeSurfaces
```

**Properties**

- `background` (`string`): Solid paint for the app's root surface. Covers the whole TUI viewport
  so transparent / translucent terminals don't show desktop windows
  underneath. Conventionally one tier deeper than `modal` so
  modal panels still read as elevated above the body.
- `chips` (`ChipColorMap`): Completion-chip color pairs keyed by provider id. Built-in themes
  ship distinct `skills` + `files` tones so the two reference kinds
  read differently in both the submitted echo and the live textarea.
- `diff` (`DiffSurfaces`): Edit-diff color block. Drives the native `<diff>` renderable used
  by `Settings.showEditDiffs`. Pre-mixed against each theme's
  primary surface so terminals without true alpha-blend get a
  legible "subtle red / subtle green" effect rather than a flat
  saturated band.

  - `*Bg`         row-level paint (gutter + content)
  - `*ContentBg`  content-only paint (overrides `*Bg` on the text
    column). Set when a deeper hue should punch
    through behind the syntax-highlighted code; omit
    to let `*Bg` carry the full row.
  - `*Fg`         color for the `+` / `-` glyph in the sign gutter.
- `modal` (`string`): Background of an overlaid modal panel (settings, model picker, …).
- `selection` (`string`): Background paint for the selected turn's events in select-turn mode.
  Subtle, low-saturation lift from the terminal default so the whole
  span of the selected turn reads as one continuous highlighted block
  without overpowering the foreground text. Inverse-tinted on the
  light flavor (Latte) so the same visual role works there.

Source: [src/chat/theme.ts:123](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L123)

### `TitleCheckRecord`

Interface.

What the last auto-title attempt did. Failures are recorded too: auto-
titling never surfaces an error mid-conversation, so this is where a
missing key or a timeout shows up.

```ts
interface TitleCheckRecord
```

**Properties**

- `at` (`number`): When the attempt ran, in ms since epoch.
- `message?` (`string`): Failure detail, when the attempt failed.
- `outcome` (`'generated' | 'kept' | 'renamed' | 'decision-failed' | 'generation-failed'`): What the attempt did or how it failed.
- `reason?` (`string`): Decision failure reason (`DecisionFailureReason`), for `decision-failed`.

Source: [src/chat/auto-title.ts:41](https://github.com/Tahul/zidane/blob/main/src/chat/auto-title.ts#L41)

### `TodoItem`

Interface.

One item of a `todowrite` call.

```ts
interface TodoItem
```

**Properties**

- `content` (`string`): One-line task summary.
- `id` (`string`): Stable id: the model picks it, or reuses a plan step's id.
- `status` (`TodoStatus`): Current progress.

Source: [src/chat/todos.ts:60](https://github.com/Tahul/zidane/blob/main/src/chat/todos.ts#L60)

### `TuiState`

Interface.

Settings the terminal UI remembers between launches.

```ts
interface TuiState
```

**Properties**

- `lastAccountByProvider?` (`Partial<Record<string, string>>`): Per-provider last-used account (a credential's stable id), next to
  `lastModelByProvider`; absent for a provider = its active account.
- `lastAgent?` (`string`, deprecated): Agent profile id saved by earlier builds.

  **Deprecated.** No longer read: the agent profile is per-session and fresh
  sessions always start in the default. Kept so older state files parse.
- `lastEffortByModel?` (`Record<string, 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'adaptive'>`): Per-model last-picked reasoning effort. Keyed by model id (not provider)
  so switching the same model across providers preserves the user's
  intent. Models without reasoning support are absent from the map.
- `lastModelByProvider?` (`Partial<Record<string, string>>`): Per-provider last-used model id.
- `lastModelChain?` (`ModelChainEntry[]`): Last-applied model fallback chain (entry 0 = primary, mirroring
  `lastProvider` + `lastModelByProvider`; entries 1+ = fallbacks).
  Additive — absent for single-model users and older state files.
  Restored on launch only when entry 0 still matches the resolved
  primary; entries are re-validated against the live registry.
- `lastModelOptionsByModel?` (`Record<string, Record<string, boolean>>`): Per-model last-picked custom options (e.g. `{ fast: true }`). Keyed by
  model id like `lastEffortByModel`, so a toggle follows the model
  across providers. Models with no options are absent from the map.
- `lastProvider?` (`string`): Provider key used last.
- `lastSessionHostKey?` (`string`): The host key of the saved server `lastSessionId` lives on (for a server,
  its host id: `src/chat/hosts/session-key`), absent for a session on this
  computer. Resume reopens it there, and only with remote hosts on. A
  `--host` session writes neither field (see `lastSessionId`).
  Read from the older `lastSessionHostId` too ([`loadState`](https://zidane.si/docs/reference/chat.md#loadState)).
- `lastSessionId?` (`string`): Id of the session open last. Resume reopens it.
- `lastSpaceId?` (`string`): The space the TUI's sessions list last showed (Spaces on; see `./spaces`). Per surface: the GUI keeps its own.
- `onboarding?` (`OnboardingState`): The setup wizard's records: a run in progress, the shared steps, and each
  app's own steps (`docs/ONBOARDING.md`). Both apps read and write it.
- `settings?` (`Partial<Settings>`): User-toggled transcript filters. Persisted so they outlive a launch.
- `sidebarSpaceId?` (`string`): The space a host's clients showed last (`sidebar.setSpace`). The TUI keeps its own (`lastSpaceId`).
- `sidebarViews?` (`Record<string, unknown>`): The sidebar views a host's clients saved (`sidebar.setView`), by space id; each client checks what it reads.

Source: [src/chat/store.ts:63](https://github.com/Tahul/zidane/blob/main/src/chat/store.ts#L63)

### `UnwrappedToolCall`

Interface.

The inner call a `call_tool` envelope carries.

```ts
interface UnwrappedToolCall
```

**Properties**

- `input` (`Record<string, unknown>`): Arguments for the inner tool.
- `name` (`string`): Inner tool name exactly as the model wrote it (a wire name).

Source: [src/tools/call-tool.ts:29](https://github.com/Tahul/zidane/blob/main/src/tools/call-tool.ts#L29)

### `UpdateAppliedMarker`

Interface.

Record written after an install lands, so running processes can notice it before the next poll.

```ts
interface UpdateAppliedMarker
```

**Properties**

- `at` (`number`): ms since epoch.
- `packages` (`{ packageName: string; version: string }[]`): Every package the run installed, with the version now on disk.

Source: [src/chat/auto-update-restart.ts:37](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L37)

### `UpdateLockHolder`

Interface.

What the lock file records about the install that holds it.

```ts
interface UpdateLockHolder
```

**Properties**

- `by` (`string`): Who runs the install, e.g. `'zidane upgrade'`, `'desktop app'`, `'terminal app'`.
- `hostname` (`string`): Host the process runs on. A pid only means something on its own host.
- `pid` (`number`): Process id of the install.
- `startedAt` (`number`): ms since epoch.

Source: [src/chat/auto-update-lock.ts:30](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-lock.ts#L30)

### `UpdateStatus`

Interface.

Result of [`checkForUpdate`](https://zidane.si/docs/reference/chat.md#checkForUpdate).

```ts
interface UpdateStatus
```

**Properties**

- `channel` (`string`): dist-tag used for the lookup — handy when surfacing "v5.6.0 (next)".
- `checkedAt` (`number | null`): When the registry was last queried (ms since epoch). `null` when never.
- `current` (`string`): Always present — echoes `currentVersion`.
- `error?` (`string`): Why the registry could not be read: set when `source === 'error'` or `stale`.
- `hasUpdate` (`boolean`): `true` when `latest` is strictly greater than `current` by semver.
- `latest` (`string | null`): `null` when the lookup failed / was skipped / cache was empty.
- `source` (`'error' | 'skipped' | 'cached' | 'fresh'`): - `'fresh'`   — fetched from the registry this call.
  - `'cached'`  — served from the on-disk cache. A background check whose
    registry fetch failed falls back to an expired entry
    and sets `stale` and `error`.
  - `'skipped'` — opt-out env var, or no `fetch` and no cache.
  - `'error'`   — registry returned non-2xx or threw; details on `error`.
- `stale?` (`boolean`): `true` when the registry fetch failed and `latest` comes from an
  expired cache entry. Never set on a `force` check: that one reports
  `'error'` instead, since the caller asked for the truth.

Source: [src/chat/auto-update.ts:90](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L90)

### `UpdateWorkerRequest`

Interface.

What a worker is asked to do.

```ts
interface UpdateWorkerRequest
```

**Properties**

- `auto?` (`UpdateStatus`): An automatic install of the release this check found: eligibility and
  the failure record apply (`autoInstallUpdate`). Absent: a manual install,
  which clears the failure record first.
- `by` (`string`): Who asked, shown to anyone who finds the lock held: `'desktop app'`.
- `cacheDir` (`string`): Where the lock, log, result, failure record and marker live.
- `channel?` (`string`): dist-tag. Defaults to `'latest'`. Manual only.
- `force?` (`boolean`): Install even when the registry check failed or reports up to date. Manual only.
- `id` (`string`): Names this run in the log and the result.
- `registry?` (`string`): Registry base URL.
- `targets` (`CompanionSpec[]`): Packages to keep in step, in order.
- `timeoutMs?` (`number`): Per package-manager run. Defaults to 10 minutes.
- `upgradeArgv?` (`string[]`): Run the host's `zidane upgrade` with these arguments instead (its own
  checks, ledger and verification), under the worker's lock. Its stdout
  lines go to the log as they are, its stderr lines prefixed with `! `.
- `v` (`number`): `UPDATE_WORKER_PROTOCOL`.

Source: [src/chat/auto-update-worker.ts:56](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L56)

### `UpdateWorkerResult`

Interface.

`<cacheDir>/update-result.json`.

```ts
interface UpdateWorkerResult
```

**Properties**

- `by` (`string`): Who asked.
- `finishedAt` (`number`): ms since epoch.
- `id` (`string`): The request's id.
- `outcome` (`UpdateWorkerOutcome`): How it ended.
- `pid` (`number`): The worker's pid: matches the lock holder an attached surface saw.
- `startedAt` (`number`): ms since epoch.
- `v` (`number`): `UPDATE_WORKER_PROTOCOL`.

Source: [src/chat/auto-update-worker.ts:108](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L108)

### `UpgradeInstalledPackagesOptions`

Interface.

Options for [`upgradeInstalledPackages`](https://zidane.si/docs/reference/chat.md#upgradeInstalledPackages).

```ts
interface UpgradeInstalledPackagesOptions
```

**Properties**

- `cacheDir?` (`string`): Where the registry cache and the applied marker live.
- `channel?` (`string`): dist-tag to install. Defaults to `'latest'`.
- `checkForUpdate?` (`(options: CheckForUpdateOptions) => Promise<UpdateStatus>`): Injected for tests. Defaults to [`checkForUpdate`](https://zidane.si/docs/reference/chat.md#checkForUpdate).
- `checkTarballAvailable?` (`(opts: TarballAvailabilityOptions) => Promise<'ok' | 'unknown' | 'missing'>`): Injected for tests. Defaults to [`checkTarballAvailable`](https://zidane.si/docs/reference/chat.md#checkTarballAvailable).
- `detectPackageManager?` (`(opts?: DetectPackageManagerOptions) => PackageManagerCommand`): Injected for tests. Defaults to [`detectPackageManager`](https://zidane.si/docs/reference/chat.md#detectPackageManager).
- `findInstalled?` (`(spec: CompanionSpec, opts?: FindCompanionOptions) => InstalledCompanion | null`): Injected for tests.
- `force?` (`boolean`): Install even when the registry check failed or reports up-to-date.
- `now?` (`() => number`): Injected for tests. Defaults to `Date.now`.
- `onProgress?` (`(event: UpgradeProgressEvent) => void`): Called with check, install, log and done events as the upgrade runs.
- `performSelfUpdate?` (`(options: PerformSelfUpdateOptions) => Promise<SelfUpdateResult>`): Injected for tests. Defaults to [`performSelfUpdate`](https://zidane.si/docs/reference/chat.md#performSelfUpdate).
- `registry?` (`string`): Registry base URL. Defaults to `https://registry.npmjs.org`.
- `signal?` (`AbortSignal`): Abort signal. Targets not yet started are skipped once it aborts.
- `targets` (`readonly CompanionSpec[]`): Packages to keep in step, e.g. `[{ packageName: 'zidane-gui' }, { packageName: 'zidane-tui', binName: 'zidane' }]`.
- `timeoutMs?` (`number`): Per package-manager run. Defaults to [`SELF_UPDATE_TIMEOUT_MS`](https://zidane.si/docs/reference/chat.md#SELF_UPDATE_TIMEOUT_MS).
- `writeMarker?` (`(cacheDir: string, marker: UpdateAppliedMarker) => boolean`): Injected for tests. Defaults to [`writeUpdateAppliedMarker`](https://zidane.si/docs/reference/chat.md#writeUpdateAppliedMarker).

Source: [src/chat/auto-update-service.ts:79](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-service.ts#L79)

### `UpgradePackageResult`

Interface.

Outcome of upgrading one package.

```ts
interface UpgradePackageResult
```

**Properties**

- `command?` (`string[]`): The PM command that ran (or would run).
- `error?` (`string`): Why the upgrade did not complete. Set for `failed` and `unavailable`.
- `from` (`string | null`): Version on disk before the run; `null` when not installed.
- `packageName` (`string`): npm package name.
- `reason?` (`'spawn' | 'registry' | 'timeout' | 'exit' | 'verify'`): What failed, for `failed`: the registry check, the package manager could
  not start, ran past its timeout or exited non-zero, or the version on disk
  is not the one asked for.
- `status` (`'failed' | 'updated' | 'unavailable' | 'not-installed' | 'up-to-date'`): - `'updated'`       — installed and verified on disk.
  - `'up-to-date'`    — nothing to do.
  - `'not-installed'` — bin not on PATH; skipped silently.
  - `'unavailable'`   — published but the tarball is not downloadable yet.
  - `'failed'`        — registry error, PM failure, or the install did not land; see `error`.
- `to` (`string | null`): Version the registry advertises; `null` when the check failed.

Source: [src/chat/auto-update-service.ts:34](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-service.ts#L34)

### `UpgradeReport`

Interface.

Outcome of [`upgradeInstalledPackages`](https://zidane.si/docs/reference/chat.md#upgradeInstalledPackages).

```ts
interface UpgradeReport
```

**Properties**

- `installedAny` (`boolean`): At least one package was actually installed — a restart is now pending.
- `ok` (`boolean`): No `failed` / `unavailable` entries.
- `results` (`UpgradePackageResult[]`): One result per target package, in order.

Source: [src/chat/auto-update-service.ts:62](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-service.ts#L62)

### `UpsertMcpResult`

Interface.

Result of [`upsertMcpServer`](https://zidane.si/docs/reference/chat.md#upsertMcpServer).

```ts
interface UpsertMcpResult
```

**Properties**

- `action` (`'updated' | 'added'`): `updated` when an entry of the same name was replaced, else `added`.
- `path` (`string`): The file written.

Source: [src/chat/mcps-write.ts:64](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-write.ts#L64)

### `UsageSummary`

Interface.

Usage and spend over a range. The heatmap always spans the last \~26 weeks, whatever the range.

```ts
interface UsageSummary
```

**Properties**

- `activeDays` (`number`): Days with a human message.
- `currentStreak` (`number`): Consecutive active days up to today (or yesterday).
- `favoriteModel` (`string | null`): The model with the most turns; `null` without data.
- `generatedAt` (`number`): When it was computed (ms).
- `heatmap` (`UsageHeatmapDay[]`): The activity heatmap's days.
- `longestStreak` (`number`): The longest run of active days.
- `messages` (`number`): Parent (human) user turns: spawned prompts excluded.
- `modelDaily` (`UsageModelDayPoint[]`): The Models chart's points (the last 120 days at most).
- `models` (`UsageModelRow[]`): Per model, most tokens first.
- `peakHour` (`number | null`): Local hour (0-23) with the most messages; `null` without data.
- `providers` (`UsageProviderRow[]`): Spend per provider, largest first.
- `range` (`UsageRange`): The range it covers.
- `sessions` (`number`): Sessions created in range.
- `tokensIn` (`number`): Tokens sent, cache included.
- `tokensOut` (`number`): Tokens produced.
- `totalCost` (`number`): USD, from assistant-turn usage costs.
- `totalTokens` (`number`): Tokens in and out.
- `userName` (`string | null`): First name for the greeting; `null` when unknown.

Source: [src/chat/usage-summary.ts:76](https://github.com/Tahul/zidane/blob/main/src/chat/usage-summary.ts#L76)

### `UseRestartRequiredOptions`

Interface.

Options for [`useRestartRequired`](https://zidane.si/docs/reference/chat.md#useRestartRequired).

```ts
interface UseRestartRequiredOptions
```

**Properties**

- `cacheDir?` (`string`): Directory that holds the applied marker. Omit to rely on polling alone.
- `currentVersion` (`string`): Version the process was built with.
- `enabled` (`boolean`): Master switch — mirrors `Settings.checkForUpdates`.
- `pollMs?` (`number`): Poll interval in milliseconds. Defaults to 5 minutes. `0` disables polling.
- `spec` (`CompanionSpec | null`): The running package. `null` disables the hook.

Source: [src/chat/auto-update-hook.ts:283](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-hook.ts#L283)

### `UseUpdateCheckOptions`

Interface.

Options for [`useUpdateCheck`](https://zidane.si/docs/reference/chat.md#useUpdateCheck).

```ts
interface UseUpdateCheckOptions
```

**Properties**

- `cacheDir?` (`string`): Directory under which the on-disk cache lives. Usually `paths.userDir`.
- `cacheTtlMs?` (`number`): Cache lifetime for the read-through. **Defaults to `0`** (always
  fetch fresh) — the TUI chip is the user's source of truth for "what
  version of zidane is on npm right now"; we'd rather pay one HTTPS
  call per session than tell them yesterday's news for up to 24 hours.

  The on-disk cache is still WRITTEN with each fresh result (so
  `zidane upgrade` and other tooling reads benefit) and still READ to
  feed the registry's ETag (so the cold path can short-circuit on 304
  — meaningful bandwidth win when nothing changed). Pass a positive
  value (e.g. `3_600_000` for 1 h) to enable read-through caching for
  a GUI shell that mounts the hook in many places.
- `channel?` (`string`): dist-tag to read. Defaults to `'latest'`.
- `currentVersion` (`string`): Inlined at build time. Skips the check if empty/falsy.
- `delayMs?` (`number`): Wall-clock delay before the first check fires. Defaults to 1 500 ms so
  we never compete with the boot path's first paint. The check itself
  is async, so this only affects WHEN the chip first appears — never
  blocks anything else.
- `enabled` (`boolean`): Master enable flag — usually `Settings.checkForUpdates`. When `false`
  the hook short-circuits without scheduling any work AND clears the
  last-known status so a stale chip doesn't linger after the user
  disabled the feature.
- `fetcher?` (`typeof fetch`): Injected for tests.
- `now?` (`() => number`): Injected for tests.
- `packageName` (`string`): npm package name. e.g. `'zidane-tui'`.
- `recheckMs?` (`number`): Check again every this many ms while mounted, so a long session still
  learns about a release. Defaults to `0`: once per mount.
- `registry?` (`string`): Registry base URL. Defaults to `https://registry.npmjs.org`.

Source: [src/chat/auto-update-hook.ts:21](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-hook.ts#L21)

### `WatchInstalledVersionOptions`

Interface.

Options for [`watchInstalledVersion`](https://zidane.si/docs/reference/chat.md#watchInstalledVersion).

```ts
interface WatchInstalledVersionOptions
```

**Properties**

- `cacheDir?` (`string`): Where the applied marker lives. Omit to rely on the poll alone.
- `currentVersion` (`string`): Inlined at build time. An empty string disables the watcher (dev runs).
- `debounceMs?` (`number`): Debounce for marker events (installs touch the file more than once). Defaults to 500 ms.
- `installingPollMs?` (`number`): While an install holds the lock, re-check this often. Defaults to 2 s.
- `now?` (`() => number`): Injected for tests.
- `onChange` (`(state: InstalledVersionState) => void`): Called with the new state when the installed version or restart flag changes.
- `pollMs?` (`number`): Poll interval. Defaults to 5 min; `0` disables the poll.
- `probe?` (`(spec: CompanionSpec, opts?: FindCompanionOptions) => InstalledCompanion | null`): Injected for tests.
- `spec` (`CompanionSpec`): The running package, e.g. `{ packageName: 'zidane-tui', binName: 'zidane' }`.

Source: [src/chat/auto-update-restart.ts:116](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-restart.ts#L116)

### `WireApprovalRequest`

Interface.

Wire-safe approval request. Canonical `ApprovalRequest` minus its live
`resolve` callback (kept host-side, keyed by `id`), plus the context a
remote approval surface needs: owning session, safelist cwd, precomputed
safelist suggestions, and the edit preview byproducts.

```ts
interface WireApprovalRequest extends Omit<ApprovalRequest, 'resolve'>
```

**Properties**

- `concern?` (`ModelApproval`, from `Omit`): Model decision approvals: the decision model's verdict when it handed this call to you (`modelConcern`).
- `correlationId?` (`string`, from `Omit`): Cross-window correlation id (the daemon's approval request id) for
  entries surfaced by the daemon runtime. Lets `dropByCorrelation`
  retract this entry when ANOTHER window resolves the same shared
  approval, without re-sending a decision. Absent for local-runtime
  approvals.
- `cwd` (`string`): Working directory that safelist entries are scoped to.
- `edit?` (`EditPayload`): The edit under approval, for edit tools.
- `id` (`string`, from `Omit`): Unique id of the request.
- `input` (`Record<string, unknown>`, from `Omit`): The tool call's input.
- `originator?` (`ApprovalOriginator`, from `Omit`): Caller attribution. Absent ≡ `{ kind: 'parent' }`.
- `preview?` (`SerializedPreview`): Diff preview of the edit.
- `priorContent?` (`string | null`): File content before the edit. `null` when the file did not exist.
- `sessionId` (`string`): Session that raised the request.
- `suggestedSafelistEntries` (`string[]`): Safelist entries the user can add when approving.
- `tool` (`string`, from `Omit`): Name of the tool awaiting approval.

Source: [src/chat/host/signals.ts:88](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L88)

### `WorkspaceEntry`

Interface.

One entry of a folder listing.

```ts
interface WorkspaceEntry
```

**Properties**

- `ignored?` (`boolean`): Gitignored (only present when listed with `showIgnored`).
- `kind` (`'file' | 'dir'`): A file, or an empty folder.
- `path` (`string`): Relative to the session cwd; directories end with `/` (only listed when empty).

Source: [src/chat/workspace/types.ts:17](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/types.ts#L17)

### `WorkspaceFile`

Interface.

A file as read for a viewer.

```ts
interface WorkspaceFile
```

**Properties**

- `binary` (`boolean`): Not text.
- `contents` (`string`): Its text; empty for a binary.
- `media?` (`MediaType`): A binary a page plays (an image, a video, audio): never read into `contents`.
- `mtimeMs` (`number`): Modification time at read; a guarded write refuses when it moved.
- `path` (`string`): Relative to the folder.
- `size` (`number`): Its size, bytes.
- `truncated` (`boolean`): Past the size cap: `contents` is cut.

Source: [src/chat/workspace/types.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/types.ts#L35)

### `WorkspaceListing`

Interface.

A folder's listing (`listWorkspace`).

```ts
interface WorkspaceListing
```

**Properties**

- `entries` (`WorkspaceEntry[]`): The entries, in tree order.
- `truncated?` (`{ atLeast?: boolean; shown: number; total: number }`): Set when the listing was cut at its cap: how many it holds and how many there are (`atLeast`: more than that).

Source: [src/chat/workspace/types.ts:27](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/types.ts#L27)

### `WorkspaceMediaOptions`

Interface.

Options of [`serveWorkspaceMedia`](https://zidane.si/docs/reference/chat.md#serveWorkspaceMedia).

```ts
interface WorkspaceMediaOptions
```

**Properties**

- `maxBytes?` (`number`): Most bytes one response carries: a longer range is cut short, a whole body over it refused (413). Default: no cap.

Source: [src/chat/workspace/media.ts:37](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/media.ts#L37)

### `WorkspaceMediaRequest`

Interface.

What to serve.

```ts
interface WorkspaceMediaRequest
```

**Properties**

- `cwd` (`string`): The folder the path is in.
- `path` (`string`): Relative to `cwd`, or (with `side`) to the repository holding it.
- `side?` (`'old' | 'new'`): One side of the path's git change: the last commit's or the working tree's. Absent: the file now.

Source: [src/chat/workspace/media.ts:27](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/media.ts#L27)

### `WorktreeRef`

Interface.

Provider-owned state on `environment.ref`.

```ts
interface WorktreeRef
```

**Properties**

- `path` (`string`): The worktree's directory.
- `repoRoot` (`string`): The main checkout it belongs to.

Source: [src/chat/environments/worktree.ts:30](https://github.com/Tahul/zidane/blob/main/src/chat/environments/worktree.ts#L30)

## Types

### `AgentRegistry`

Type.

Agent profiles keyed by profile id.

```ts
type AgentRegistry = Readonly<Record<string, AgentProfile>>
```

Source: [src/chat/agents.ts:85](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L85)

### `AnswerValue`

Type.

Per-question answer value — `string` for text/textarea/select, `boolean`
for confirm, `string[]` for multi-select. A `secret` question answers with
a [`SecretAnswer`](https://zidane.si/docs/reference/chat-pure.md#SecretAnswer) from the client, which the host replaces with a
[`SecretReference`](https://zidane.si/docs/reference/chat-pure.md#SecretReference) before anything records it.

```ts
type AnswerValue = string | boolean | readonly string[] | SecretAnswer | SecretReference
```

Source: [src/chat/interactions-protocol.ts:222](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L222)

### `ApprovalDecision`

Type.

Outcome of an approval prompt. Four bulk decisions match the original
single-edit modal contract; `partial` is the multi-edit branch — the
user accepted a subset of an `edit` / `multi_edit` / `write_file` call.

`partial.mask` is 1:1 with the call's hunks (in input order): `true`
means apply, `false` means deny. The gate handler walks the mask to
build per-hunk outcomes; the TUI rebinds `ctx.input.edits` to the
approved subset and the renderer reconstructs the full per-hunk view
from the `<edit-outcomes>` annotation appended to the tool result.

```ts
type ApprovalDecision =
  | 'accept-once'
  | 'accept-session'
  | 'accept-safelist'
  | 'deny'
  | { kind: 'partial'; mask: readonly boolean[] }
```

Source: [src/chat/safe-mode-context.tsx:37](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode-context.tsx#L37)

### `ApprovalMode`

Type.

Who approves tool calls: `ask` the user, `decide` the decision model (unsure calls still ask), `off` nobody (full access).

```ts
type ApprovalMode = 'ask' | 'decide' | 'off'
```

Source: [src/chat/types.ts:699](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L699)

### `ApprovalOriginator`

Type.

Caller attribution for approvals raised by subagents.

```ts
type ApprovalOriginator = { kind: 'parent' } | { kind: 'child'; label: string }
```

Source: [src/chat/host/signals.ts:42](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L42)

### `ArtifactScope`

Type.

Whether artifacts are listed for the current session or the whole project.

```ts
type ArtifactScope = 'session' | 'project'
```

Source: [src/chat/artifacts-view.ts:12](https://github.com/Tahul/zidane/blob/main/src/chat/artifacts-view.ts#L12)

### `AutoInstallOutcome`

Type.

Outcome of [`autoInstallUpdate`](https://zidane.si/docs/reference/chat.md#autoInstallUpdate).

```ts
type AutoInstallOutcome =
  | { kind: 'installed'; report: UpgradeReport; version: string }
  | { holder?: UpdateLockHolder; kind: 'skipped'; reason: AutoInstallSkipReason }
  | { error: string; kind: 'failed'; version: string }
```

Source: [src/chat/auto-update-auto-install.ts:157](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L157)

### `AutoInstallSkipReason`

Type.

Why [`autoInstallUpdate`](https://zidane.si/docs/reference/chat.md#autoInstallUpdate) did nothing.

```ts
type AutoInstallSkipReason = 'not-eligible' | 'failed-before' | 'retry-later' | 'locked'
```

Source: [src/chat/auto-update-auto-install.ts:146](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-auto-install.ts#L146)

### `AutoTitleAction`

Type.

What auto-titling should do next: leave the title, generate one, or check whether it is stale.

```ts
type AutoTitleAction = 'skip' | 'generate' | 'check'
```

Source: [src/chat/auto-title.ts:169](https://github.com/Tahul/zidane/blob/main/src/chat/auto-title.ts#L169)

### `BuiltInPrompt`

Type.

The built-in prompts the chat host composes (with env, AGENTS.md and SYSTEM.md).

```ts
type BuiltInPrompt = 'build' | 'plan'
```

Source: [src/chat/agents.ts:230](https://github.com/Tahul/zidane/blob/main/src/chat/agents.ts#L230)

### `ChipColorMap`

Type.

Map of provider id → chip color pair. `default` is required so callers
always have a fallback; every other key is optional so direct indexing
(`chips['unknown']`) surfaces as `ChipColor | undefined` and nudges
consumers toward [`resolveChipColor`](https://zidane.si/docs/reference/chat.md#resolveChipColor), which encodes the fallback.

```ts
type ChipColorMap = { default: ChipColor } & Partial<Record<string, ChipColor>>
```

Source: [src/chat/theme.ts:107](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L107)

### `CommandStepFn`

Type.

Handler for a single [`CommandStep`](https://zidane.si/docs/reference/chat.md#CommandStep). May return a [`CommandStepResult`](https://zidane.si/docs/reference/chat.md#CommandStepResult) to merge data into the shared bag, synchronously or as a promise.

```ts
type CommandStepFn = (ctx: CommandStepContext) => void | CommandStepResult | Promise<void | CommandStepResult>
```

Source: [src/chat/commands.ts:309](https://github.com/Tahul/zidane/blob/main/src/chat/commands.ts#L309)

### `Commit`

Type.

Notification: a fresh walk has resolved. Host is expected to commit to its own state store.

```ts
type Commit<T> = (items: readonly T[]) => void
```

**Type parameters**

- `T`

Source: [src/chat/discovery-slot.ts:37](https://github.com/Tahul/zidane/blob/main/src/chat/discovery-slot.ts#L37)

### `CreateSessionRequest`

Type.

`ChatHost.createSession` input: the factory params, with the environment as a request the host resolves.

```ts
type CreateSessionRequest = Omit<CreateSessionInStoreParams, 'environment'> & { environment?: EnvironmentRequest }
```

Source: [src/chat/host/host-api.ts:88](https://github.com/Tahul/zidane/blob/main/src/chat/host/host-api.ts#L88)

### `CredentialsFile`

Type.

Top-level shape of `credentials.json` — keys are credential-file keys.

```ts
type CredentialsFile = Record<string, ProviderCredential>
```

Source: [src/chat/credentials.ts:138](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L138)

### `DiagnosticSeverity`

Type.

How bad a finding is.

```ts
type DiagnosticSeverity = 'error' | 'warning' | 'info' | 'hint'
```

Source: [src/chat/workspace/diagnostics/types.ts:7](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/diagnostics/types.ts#L7)

### `DurabilityMode`

Type.

Whether runs survive a crash: `on` keeps a checkpoint of each run and
resumes an interrupted one by itself, `resume-ask` keeps the checkpoint
and asks before resuming, `off` keeps none (an interrupted run's tool
calls are reported interrupted on the next run).

```ts
type DurabilityMode = 'on' | 'resume-ask' | 'off'
```

Source: [src/durable/setting.ts:9](https://github.com/Tahul/zidane/blob/main/src/durable/setting.ts#L9)

### `EditDiffDisplay`

Type.

How `edit` / `multi_edit` / `write_file` diffs render in the transcript.
Gated by `Settings.showEditDiffs`; when that's off, the raw `↳ name(args)`
line is used regardless of this setting.

- `'full'`    — the entire unified diff body, with
  line numbers in the gutter. Uses real file line numbers when the
  pre-write snapshot is available (live calls), synthetic when not
  (historical replay).
- `'compact'` — header line + one short summary line per hunk
  (`L42`, `+2 −1`, `old → new`). The full diff body is dropped from
  the transcript; users open the approval modal or scroll the file
  to see the changes in detail.

```ts
type EditDiffDisplay = 'compact' | 'full'
```

Source: [src/chat/types.ts:737](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L737)

### `EditOutcomeKind`

Type.

Per-hunk lifecycle state surfaced in the diff/apply viewer. Mirrors the
states a `multi_edit` step can be in across the approve → apply pipeline:

- `applied`  — the gate let it through and the tool wrote it.
- `denied`   — the user (or an auto-deny rule) refused this specific edit.
- `skipped`  — the user explicitly stepped past this edit in the modal,
  or an earlier edit failed and the batch unwound.
- `failed`   — the gate let it through but the tool body rejected the
  edit (`old_string` not found, ambiguous, etc.).
- `pending`  — placeholder used while the modal is open. Never persisted.

```ts
type EditOutcomeKind = 'applied' | 'denied' | 'skipped' | 'failed' | 'pending'
```

Source: [src/chat/types.ts:522](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L522)

### `EnabledAllowlistKey`

Type.

Settings keys whose value type is `readonly string[] | undefined`.

```ts
type EnabledAllowlistKey =
  | 'enabledSkills'
  | 'enabledMcps'
  | 'enabledExtensions'
  | 'enabledPlugins'
```

Source: [src/chat/enabled-toggle-set.ts:28](https://github.com/Tahul/zidane/blob/main/src/chat/enabled-toggle-set.ts#L28)

### `EnvironmentAvailability`

Type.

Whether a kind can be offered now: `ok`, or not with a `reason` to show.

```ts
type EnvironmentAvailability = { ok: true } | { ok: false; reason: string }
```

Source: [src/chat/environment.ts:260](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L260)

### `EnvironmentConfig`

Type.

A kind's resolved options — see `environment-settings.ts` (`resolveProviderConfig`).

```ts
type EnvironmentConfig = Readonly<Record<string, EnvironmentConfigValue | undefined>>
```

Source: [src/chat/environment.ts:152](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L152)

### `EnvironmentConfigValue`

Type.

A provider option's value, as stored in settings (secrets resolve to strings).

```ts
type EnvironmentConfigValue = string | number | boolean
```

Source: [src/chat/environment.ts:149](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L149)

### `EnvironmentState`

Type.

Lifecycle state of a session's environment: being set up, usable, or failed to set up.

```ts
type EnvironmentState = 'provisioning' | 'ready' | 'failed'
```

Source: [src/chat/environment.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L35)

### `EnvironmentVerifyResult`

Type.

"Test connection" outcome — `detail` is what it connected to (account, region, version).

```ts
type EnvironmentVerifyResult =
  | { detail?: string; ok: true }
  | { ok: false; reason: string }
```

Source: [src/chat/environment.ts:263](https://github.com/Tahul/zidane/blob/main/src/chat/environment.ts#L263)

### `ForkSessionResult`

Type.

Outcome of [`forkSession`](https://zidane.si/docs/reference/chat.md#forkSession): the new session id, or the reason the fork was refused.

```ts
type ForkSessionResult =
  | { id: string; ok: true }
  | { ok: false; reason: 'source-not-found' | 'turn-not-found' | 'environment-not-ready' }
```

Source: [src/chat/session-factory.ts:218](https://github.com/Tahul/zidane/blob/main/src/chat/session-factory.ts#L218)

### `ForkSource`

Type.

What a fork reads from its parent — satisfied by `SessionData` and a live `Session`.

```ts
type ForkSource = Pick<SessionData, 'id' | 'turns' | 'runs' | 'metadata' | 'agentId' | 'projectRoot'>
```

Source: [src/chat/session-factory.ts:196](https://github.com/Tahul/zidane/blob/main/src/chat/session-factory.ts#L196)

### `GitChange`

Type.

What moved: the refs (HEAD, branches, remote-tracking refs) or the config (remotes, upstreams).

```ts
type GitChange = 'refs' | 'config'
```

Source: [src/chat/git/watch.ts:27](https://github.com/Tahul/zidane/blob/main/src/chat/git/watch.ts#L27)

### `GitPrListResult`

Type.

The switcher's PR list: the PRs, `null` when `gh` is missing or signed out,
or `rateLimited` when GitHub refused and nothing is cached (`retryAt`: when
it resumes, epoch ms).

```ts
type GitPrListResult = GitPrItem[] | null | { rateLimited: { retryAt: number } }
```

Source: [src/chat/git/types.ts:114](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L114)

### `GitSwitchResult`

Type.

A switch or a new branch: git's refusal summed up; `worktree`: another worktree holds the branch, at this path.

```ts
type GitSwitchResult = { ok: true } | { error: string; ok: false; worktree?: string }
```

Source: [src/chat/git/types.ts:120](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L120)

### `GitSwitchTarget`

Type.

What to switch to: a local branch by name, or a pull request by number.

```ts
type GitSwitchTarget = string | { pr: number }
```

Source: [src/chat/git/types.ts:117](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L117)

### `GitSyncResult`

Type.

What a sync (pull, merging, then push) or a publish answered; `conflicts`: the files a refused pull left conflicted.

```ts
type GitSyncResult =
  | { ok: true; published: boolean; pulled: number; pushed: number }
  | { conflicts?: string[]; error: string; ok: false }
```

Source: [src/chat/git/types.ts:48](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L48)

### `HostProbe`

Type.

Connects to `url` with `token`, says who answered (or why it failed), disconnects. Never throws.

```ts
type HostProbe = (target: { token: string; url: string }) => Promise<HostProbeResult>
```

Source: [src/chat/hosts/types.ts:61](https://github.com/Tahul/zidane/blob/main/src/chat/hosts/types.ts#L61)

### `HostSettings`

Type.

Host settings — canonical `Settings` plus the extension allowlist.

```ts
type HostSettings = Settings & { enabledExtensions?: readonly string[]; safeMode?: boolean }
```

Source: [src/chat/host/signals.ts:227](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L227)

### `HostSignal`

Type.

The single push channel. Chat stream signals mirror the agent loop hooks;
interaction/approval signals carry the HITL queues so every window renders
the same pending state; `sessions:changed` keeps session lists in sync
across windows.

```ts
type HostSignal =
  | { kind: 'run:start'; runId: string; sessionId: string; startedAt?: number }
  | { background?: true; detail?: RunErrorDetail; error?: string; kind: 'run:end'; ok: boolean; runId: string; sessionId: string; takenOver?: true }
  | { childId?: string; depth?: number; kind: 'turn:end'; runOutputTokens?: number; sessionId: string; turnId: string; usage?: TurnUsage }
  | { echo?: boolean; event: StreamEvent; kind: 'event'; sessionId: string }
  | { kind: 'compacting:start'; sessionId: string }
  | { estOutputTokens: number; kind: 'compacting:progress'; sessionId: string }
  | ({ kind: 'stream:progress'; sessionId: string } & StreamProgressInfo)
  | { kind: 'prompt-turns'; refs: readonly PromptTurnRef[]; sessionId: string }
  | { effectiveTokens?: number; kind: 'compacting:end'; ok: boolean; sessionId: string }
  | { discardTurnId?: string; from: { model: string; provider: string }; kind: 'model:fallback'; sessionId: string; to: { model: string; provider: string } }
  | { items: RunningWorkItem[]; kind: 'work:snapshot'; sessionId: string }
  | { kind: 'plans:snapshot'; sessionId: string; snapshot: PlansSnapshot }
  | { artifact: SessionArtifact; kind: 'artifact:recorded'; sessionId: string }
  | { artifact?: SessionArtifact; artifactId: string; kind: 'artifact:changed'; op: 'delete' | 'upsert'; sessionId: string }
  | { kind: 'interaction:request'; live: boolean; request: InteractionRequest; sessionId: string }
  | { answeredBy?: InteractionAnswerer; kind: 'interaction:resolved'; requestId: string; sessionId: string }
  | { kind: 'secrets:changed'; projectKey: string }
  | { hostId: string; kind: 'browser:request'; request: BrowserRequest; sessionId: string }
  | { hostId: string; kind: 'browser:cancel'; requestId: string; sessionId: string }
  | { kind: 'terminal:changed'; sessionId: string; terminals: TerminalInfo[] }
  | { data: string; kind: 'terminal:output'; offset: number; sessionId: string; terminalId: string }
  | { data: string; kind: 'terminal:reset'; offset: number; sessionId: string; terminalId: string }
  | { draftId?: string; kind: 'terminal:project-changed'; root: string; terminals: TerminalInfo[] }
  | { chunk: string; execId: string; kind: 'workspace:exec:data'; stream: 'stdout' | 'stderr' }
  | { cancelled?: true; code: number | null; execId: string; kind: 'workspace:exec:exit'; signal: string | null; truncated?: true }
  | { chunk: string; kind: 'block-run:data'; runId: string; stream: 'stdout' | 'stderr' }
  | { error?: string; kind: 'block-run:end'; run?: BlockRun; runId: string }
  | { kind: 'git:clone:progress'; percent: number; phase: string }
  | { cwd: string; kind: 'git:refs' }
  | { cwd: string; kind: 'panel:changed'; panelId: string }
  | { cwd: string; kind: 'palette:changed' }
  | { kind: 'logs:record'; record: LogRecord }
  | { kind: 'perf:sample'; sample: PerfSample }
  | { catalog: 'skills' | 'mcps' | 'extensions' | 'plugins' | 'models'; kind: 'catalog:changed' }
  | { kind: 'sync:changed' }
  | { kind: 'tokens:changed' }
  | { kind: 'approval:request'; request: WireApprovalRequest }
  | { answeredBy?: InteractionAnswerer; decision: ApprovalDecision; kind: 'approval:resolved'; requestId: string; sessionId: string }
  | { kind: 'sessions:changed'; op: 'create' | 'update' | 'delete'; sessionId: string }
  | { kind: 'block-runs:changed'; sessionId: string }
  | { facets: Record<string, JsonValue>; kind: 'sessions:facets'; sessionId: string }
  | { kind: 'settings:changed'; settings: HostSettings }
  | { kind: 'draft:changed'; sessionId: string; shell?: boolean; text: string }
  | { attachments: PromptAttachment[]; kind: 'draft:attachments'; sessionId: string }
  | { active: boolean; kind: 'search:changed'; query: string; sessionId: string }
  | { basis: string; candidates?: string[]; caret: number; kind: 'suggest:changed'; seq: number; sessionId: string; suggestion: string | null }
  | { kind: 'suggestion:ready'; runId: string; sessionId: string; text: string }
  | { estimate: RunEstimateView; kind: 'run:estimate'; sessionId: string }
  | { active: string[]; kind: 'skills:snapshot'; sessionId: string }
  | { kind: 'mcp:auth'; name: string; reason: string; sessionId: string }
  | { kind: 'mcp:settled'; sessionId: string }
  | { kind: 'mcp:status'; name: string; sessionId: string; status: 'connected' }
  | { code?: number; error: string; kind: 'mcp:status'; name: string; sessionId: string; status: 'failed'; transport: string }
  | { attempt: number; discardTurnId?: string; kind: 'stream:retry'; max: number; sessionId: string; source: string }
  | { kind: 'stream:retry:cleared'; sessionId: string; source: string }
  | { file: SystemMdFile | null; kind: 'system-md'; sessionId: string; shadowed: readonly SystemMdFile[] }
  | { info: CacheBreakInfo; kind: 'cache:break'; sessionId: string }
  | { diagnostic: ProviderDiagnostic; kind: 'provider:diagnostic'; sessionId: string }
  | { kind: 'presence:changed'; sessions: SessionPresence[] }
  | { integrations: IntegrationInfo[]; kind: 'integrations:changed' }
  | { kind: 'schedules:changed'; scheduleId?: string }
  | ({ kind: 'machine:status' } & MachineStatusChange)
  | { kind: 'machine:links'; links: MachineLinksStatus }
  | { kind: 'machine:notification'; method: string; params: JsonValue }
  | { items: QueuedItem[]; kind: 'queue:changed'; sessionId: string }
  | { clientMessageId: string; kind: 'delivery:changed'; runId?: string; sessionId: string; state: DeliveryState }
  | ExtensionsSignal
```

Source: [src/chat/host/signals.ts:304](https://github.com/Tahul/zidane/blob/main/src/chat/host/signals.ts#L304)

### `InteractionMode`

Type.

How questions to the user are handled: `ask` prompts normally, `skip` answers from the repository without asking, `disabled` removes the interaction tools.

```ts
type InteractionMode = 'ask' | 'skip' | 'disabled'
```

Source: [src/chat/types.ts:696](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L696)

### `InteractionOrigin`

Type.

Who raised a request. `model` is a tool call awaiting a `tool_result`;
`host` is a slash command or extension prompt whose consumer is local
code. Automatic mode may only answer on the model's behalf, so this is
carried as data rather than inferred from the id — a missing value is a
compile error at the construction site instead of a silent mis-answer.

```ts
type InteractionOrigin = 'model' | 'host'
```

Source: [src/chat/interactions-protocol.ts:231](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L231)

### `InteractionRequest`

Type.

A request for user input raised by the model or the host.

```ts
type InteractionRequest = PlanRequest | QuestionRequest
```

Source: [src/chat/interactions-protocol.ts:267](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L267)

### `InteractionResponse`

Type.

The user's answer to an [`InteractionRequest`](https://zidane.si/docs/reference/chat.md#InteractionRequest).

```ts
type InteractionResponse = PlanResponse | QuestionResponse
```

Source: [src/chat/interactions-protocol.ts:331](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L331)

### `KeyAction`

Type.

Every action the keybindings system can drive. Adding a new action
here forces every consumer (TUI keyboard handler, hint rows, settings
UI) to handle it, and ships a default that the user can override.

```ts
type KeyAction =
  | 'openSettings'
  | 'openSessionDetails'
  | 'newSession'
  | 'openModelPicker'
  | 'openEffortPicker'
  | 'openTodos'
  | 'openArtifacts'
  | 'panelNext'
  | 'panelPrev'
  | 'nextSpace'
  | 'prevSpace'
  | 'openContextPanel'
  | 'openMainSession'
  | 'openUsagePanel'
  | 'openKeybindings'
  | 'openPalette'
  | 'cycleAgent'
  | 'enterSelectTurnMode'
  | 'cancelToolCall'
  | 'abortRun'
  | 'resumeRun'
  | 'changeCwd'
  | 'attachClipboardImage'
  | 'openExternalEditor'
  | 'cycleSpaceInList'
  | 'enterQueueSelection'
  | 'pushQueuedMessage'
  | 'dropQueuedMessage'
  | 'turnFork'
  | 'turnDelete'
  | 'turnCopy'
  | 'turnEdit'
  | 'sessionDelete'
  | 'sessionCopyId'
  | 'sessionGenerateTitle'
  | 'sessionExportMarkdown'
  | 'sessionExportJson'
  | 'sessionCompact'
  | 'sessionMoveSpace'
  | 'sessionFavorite'
  | 'sessionMoveEnvironment'
```

Source: [src/chat/keybindings.ts:47](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L47)

### `KeyBindings`

Type.

Resolved keybindings — every action has a current spec string.

```ts
type KeyBindings = Record<KeyAction, string>
```

Source: [src/chat/keybindings.ts:106](https://github.com/Tahul/zidane/blob/main/src/chat/keybindings.ts#L106)

### `McpAuthEvent`

Type.

Events the reducer accepts. Most names mirror the agent hooks
(`mcp:connect`, `mcp:auth:*`). `login:start` / `login:cancel` / `logout` /
`reset` are UI-only verbs: `login:start` lets the renderer
optimistically flip a row to `authorizing` before the SDK has emitted
`mcp:auth:url` — useful when the loopback callback is slow to bind.

```ts
type McpAuthEvent =
  | { name: string; reason: McpNeedsAuthReason; type: 'auth-required' }
  | { name: string; type: 'auth-url'; url: string }
  | { name: string; type: 'auth-success' }
  | { error: string; name: string; type: 'auth-error' }
  | { error: string; name: string; type: 'bootstrap-error' }
  | { error: string; name: string; type: 'key-refused' }
  | { name: string; type: 'connected' }
  | { name: string; type: 'login:start' }
  | { name: string; type: 'login:cancel' }
  | { name: string; type: 'logout' }
  | { name: string; type: 'reset' }
```

Source: [src/chat/mcp-auth-state.ts:98](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-auth-state.ts#L98)

### `McpAuthState`

Type.

A server's sign-in: none needed, wanted, under way, done, or the last one failed.

```ts
type McpAuthState = 'idle' | 'needs-auth' | 'authorizing' | 'authed' | 'error'
```

Source: [src/chat/mcp-status.ts:19](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-status.ts#L19)

### `McpAuthStateMap`

Type.

Map of server name → status. `idle` is the implicit default for any
server NOT present in the map, so the renderer treats `undefined` as
`{ kind: 'idle' }` and the map can stay sparse.

```ts
type McpAuthStateMap = Readonly<Record<string, McpAuthStatus>>
```

Source: [src/chat/mcp-auth-state.ts:89](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-auth-state.ts#L89)

### `McpConfigScope`

Type.

Which config root to write to: the git root of the project, or the user's home.

```ts
type McpConfigScope = 'project' | 'user'
```

Source: [src/chat/mcps-write.ts:27](https://github.com/Tahul/zidane/blob/main/src/chat/mcps-write.ts#L27)

### `McpNeedsAuthReason`

Type.

Why a server needs sign-in: no stored tokens, a server answer that promoted it to OAuth, or a refused request.

```ts
type McpNeedsAuthReason = 'no-tokens' | 'auto-promoted' | 'refused'
```

Source: [src/chat/mcp-auth-state.ts:69](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-auth-state.ts#L69)

### `McpStatusTracker`

Type.

The MCP servers' live state for one data dir (see [`createMcpStatusTracker`](https://zidane.si/docs/reference/chat.md#createMcpStatusTracker)).

```ts
type McpStatusTracker = ReturnType<typeof createMcpStatusTracker>
```

Source: [src/chat/mcp-status.ts:44](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-status.ts#L44)

### `McpToolsCache`

Type.

`serverName → catalog`. Map shape on disk so adding / removing
servers is an O(1) JSON edit instead of an array rewrite.

```ts
type McpToolsCache = Record<string, CachedMcpToolList>
```

Source: [src/chat/mcp-tools-cache.ts:69](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-tools-cache.ts#L69)

### `MessageDelivery`

Type.

What happens to a message sent while a run works: it goes into the run (`steer`), or waits for the run to end (`queue`).

```ts
type MessageDelivery = 'steer' | 'queue'
```

Source: [src/chat/types.ts:701](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L701)

### `ModelApprovalChoice`

Type.

The decision model's answer on a tool call under Model decision approvals.

```ts
type ModelApprovalChoice =
  | 'approve'
  | 'ask'
  | 'deny_destructive'
  | 'deny_out_of_scope'
  | 'deny_secrets_or_network'
```

Source: [src/chat/types.ts:703](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L703)

### `OpenInTarget`

Type.

Where a clicked file or diff opens: the external `editor` or the host's own `panel`. See `Settings.openIn`.

```ts
type OpenInTarget = 'editor' | 'panel'
```

Source: [src/chat/types.ts:778](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L778)

### `Owner`

Type.

Identifies the agent that produced a streaming event.

```ts
type Owner = 'parent' | string
```

Source: [src/chat/types.ts:18](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L18)

### `PackageManagerId`

Type.

Identifier of a supported package manager.

```ts
type PackageManagerId = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'volta'
```

Source: [src/chat/auto-update.ts:134](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update.ts#L134)

### `PlanDecision`

Type.

How the user answered a plan: approve it, reject it, or ask for a revision.

```ts
type PlanDecision = 'approve' | 'reject' | 'revise'
```

Source: [src/chat/interactions-protocol.ts:288](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L288)

### `PlanEditOp`

Type.

A user edit to a plan's steps.

```ts
type PlanEditOp =
  | { kind: 'rename'; stepId: string; title: string }
  | { kind: 'add'; stepId?: string; title: string }
  | { kind: 'cancel'; stepId: string }
```

Source: [src/chat/plans.ts:182](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L182)

### `PlanEvent`

Type.

A change to the plans of one session.

```ts
type PlanEvent =
  | { at: number; body: string; id: string; requestId: string; runId?: string; scope: string; steps: readonly ProposalStepInput[]; title: string; type: 'propose' }
  | { at: number; comment?: string; decision: 'approve' | 'reject' | 'revise'; id: string; requestId: string; runId?: string; type: 'decide' }
  | { at: number; id: string; items: readonly WriteItemInput[]; parentScope?: string; runId: string; scope: string; type: 'write' }
  | { at: number; id: string; op: PlanEditOp; planId: string; type: 'edit' }
  | { action: 'stop' | 'drop' | 'resume'; at: number; id: string; planId: string; runId?: string; type: 'control' }
  | { at: number; id: string; outcome: 'completed' | 'aborted' | 'error'; runId: string; type: 'runEnd' }
  | { at: number; id: string; runIds?: readonly string[]; type: 'interrupt' }
  | { at: number; count: number; id: string; planId: string; type: 'notesSent' }
```

Source: [src/chat/plans.ts:188](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L188)

### `PlanSession`

Type.

A session as the plan helpers need it; `saveMetaKeys` and `store` are used when present.

```ts
type PlanSession = Pick<StoredSession, 'metadata' | 'runs' | 'setMeta'> & Partial<Pick<StoredSession, 'saveMetaKeys' | 'store'>>
```

Source: [src/chat/plans-session.ts:21](https://github.com/Tahul/zidane/blob/main/src/chat/plans-session.ts#L21)

### `PlanState`

Type.

Lifecycle state of a [`Plan`](https://zidane.si/docs/reference/chat.md#Plan).

```ts
type PlanState =
  | 'proposed'
  | 'applying'
  | 'done'
  | 'dropped'
  | 'stopped'
  | 'superseded'
  | 'rejected'
```

Source: [src/chat/plans.ts:63](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L63)

### `PlanStopReason`

Type.

Why a plan stopped.

```ts
type PlanStopReason =
  | 'run_ended'
  | 'aborted'
  | 'error'
  | 'interrupted'
  | 'user'
  | 'migrated'
```

Source: [src/chat/plans.ts:69](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L69)

### `PreferCredentialResult`

Type.

Outcome of [`setPreferredCredential`](https://zidane.si/docs/reference/chat.md#setPreferredCredential).

Three states rather than a boolean because the two non-promoting cases need
opposite handling: `already-active` is a harmless idempotent call (the UI
repaints and moves on), while `not-stored` means the caller asked for a
credential that does not exist and should say so.

```ts
type PreferCredentialResult = 'promoted' | 'already-active' | 'not-stored'
```

Source: [src/chat/credentials.ts:611](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L611)

### `PreferredCredentialTarget`

Type.

What [`setPreferredCredential`](https://zidane.si/docs/reference/chat.md#setPreferredCredential) promotes: a stored credential by file
key (from [`readProviderCredentials`](https://zidane.si/docs/reference/chat.md#readProviderCredentials) / `AuthMethod.key`), or the first
stored credential of a kind — the right form when the caller only knows
"switch to the API key".

```ts
type PreferredCredentialTarget = ProviderCredential['kind'] | { key: string }
```

Source: [src/chat/credentials.ts:619](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L619)

### `ProjectsFile`

Type.

Contents of `projects.json`: project entries keyed by project root path.

```ts
type ProjectsFile = Record<string, ProjectEntry>
```

Source: [src/chat/safe-mode.ts:58](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode.ts#L58)

### `PromptConsumed`

Type.

What an extension did with a submission instead of the asked path: `handled` it, or delivered it to the live run.

```ts
type PromptConsumed = 'handled' | 'steer' | 'followUp'
```

Source: [src/chat/host/prompt-transforms.ts:107](https://github.com/Tahul/zidane/blob/main/src/chat/host/prompt-transforms.ts#L107)

### `PromptSubmitHost`

Type.

The host methods a submission ends in.

```ts
type PromptSubmitHost = Pick<ChatHost, 'sendPrompt' | 'pushQueued' | 'queueAdd' | 'steer' | 'followUp'>
```

Source: [src/chat/host/prompt-transforms.ts:110](https://github.com/Tahul/zidane/blob/main/src/chat/host/prompt-transforms.ts#L110)

### `ProviderCredential`

Type.

Either an API key or an OAuth credential stored for a provider.

```ts
type ProviderCredential = ApiKeyCredential | OAuthCredential
```

Source: [src/chat/credentials.ts:125](https://github.com/Tahul/zidane/blob/main/src/chat/credentials.ts#L125)

### `ProviderKey`

Type.

Provider identifier as known to the TUI. Built-in keys include `anthropic`,
`openai`, `openrouter` and `cerebras`, and hosts can register additional
keys via [`ProviderDescriptor`](https://zidane.si/docs/reference/chat.md#ProviderDescriptor) — so the type is open.

```ts
type ProviderKey = string
```

Source: [src/chat/auth.ts:15](https://github.com/Tahul/zidane/blob/main/src/chat/auth.ts#L15)

### `ProviderRegistry`

Type.

Provider registry — a map keyed by `descriptor.key`. Passed verbatim to the
chat engine; there is no implicit merge with built-ins. Hosts that want the
default providers spread [`BUILTIN_PROVIDERS`](https://zidane.si/docs/reference/chat.md#BUILTIN_PROVIDERS); hosts that only want
their own pass exactly their own.

```ts
type ProviderRegistry = Readonly<Record<string, ProviderDescriptor>>
```

Source: [src/chat/config.ts:47](https://github.com/Tahul/zidane/blob/main/src/chat/config.ts#L47)

### `Question`

Type.

One question in an `ask_user` form, discriminated by `type`.

```ts
type Question = TextQuestion | SelectQuestion | ConfirmQuestion | SecretQuestion
```

Source: [src/chat/interactions-protocol.ts:196](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L196)

### `QuestionType`

Type.

Kind of an `ask_user` question, the discriminator of [`Question`](https://zidane.si/docs/reference/chat.md#Question):

- `text`     — single-line free-text answer (rendered via `<input>`).
- `textarea` — multi-line free-text answer (rendered via `<textarea>`).
- `select`   — single pick from a fixed set of `choices`.
- `confirm`  — yes/no boolean (rendered as a 2-option select).
- `secret`   — a value the host saves to the secret store; the model
  reads only a reference (`SecretQuestion`).

The model batches related clarifications into one tool call instead of
pinging the user N times with N round-trips.

```ts
type QuestionType = 'text' | 'textarea' | 'select' | 'confirm' | 'secret'
```

Source: [src/chat/interactions-protocol.ts:127](https://github.com/Tahul/zidane/blob/main/src/chat/interactions-protocol.ts#L127)

### `RepoCloneResult`

Type.

What a clone answered; `existed`: the folder already held that repository (same origin), so it was opened, not cloned.

```ts
type RepoCloneResult =
  | { existed: boolean; ok: true; path: string }
  | { cancelled?: boolean; error: string; ok: false }
```

Source: [src/chat/git/types.ts:157](https://github.com/Tahul/zidane/blob/main/src/chat/git/types.ts#L157)

### `RequestApproval`

Type.

Function signature consumed by `tool:gate` handlers + the child-tool wrap.

```ts
type RequestApproval = (tool: string, input: Record<string, unknown>, originator?: ApprovalOriginator, correlationId?: string) => Promise<ApprovalDecision>
```

Source: [src/chat/safe-mode-context.tsx:79](https://github.com/Tahul/zidane/blob/main/src/chat/safe-mode-context.tsx#L79)

### `ResolvedPath`

Type.

What a name in prose points at: a file or folder on disk, or else a git
branch of the repo (`main`, `origin/main`: `name` is the branch, `remote`
the remote of a remote-tracking one). `candidates`: when a bare name matched
several files or folders, all of them, best first (`path` is the first), so
a chip can ask which one.

```ts
type ResolvedPath =
  | { candidates?: { kind: 'file' | 'dir'; path: string }[]; kind: 'file' | 'dir'; path: string }
  | { kind: 'branch'; name: string; remote?: string }
  | { kind: 'missing' }
```

Source: [src/chat/workspace/types.ts:11](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/types.ts#L11)

### `Screen`

Type.

Top-level screen the terminal UI shows. `onboarding` is the setup wizard.

```ts
type Screen = 'auth' | 'onboarding' | 'sessions' | 'chat'
```

Source: [src/chat/types.ts:15](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L15)

### `SendTransform`

Type.

The transform step of a host's submit paths (see `createSendTransforms`).

```ts
type SendTransform = (opts: SendTransformInput) => Promise<SendTransformOutcome>
```

Source: [src/chat/host/prompt-transforms.ts:40](https://github.com/Tahul/zidane/blob/main/src/chat/host/prompt-transforms.ts#L40)

### `SessionExportAnchor`

Type.

Anchor for the resolved destination directory:

- `project` — the nearest enclosing git repository (`<repoRoot>/.{prefix}/sessions`)
- `home`    — fallback when no repo was found (`<dataDir>/sessions`; `~/.{prefix}/sessions`
  outside an XDG layout)

```ts
type SessionExportAnchor = 'project' | 'home'
```

Source: [src/chat/session-export.ts:39](https://github.com/Tahul/zidane/blob/main/src/chat/session-export.ts#L39)

### `SessionExportFormat`

Type.

File format supported by the exporter.

```ts
type SessionExportFormat = 'markdown' | 'json'
```

Source: [src/chat/session-export.ts:31](https://github.com/Tahul/zidane/blob/main/src/chat/session-export.ts#L31)

### `SettingsCategory`

Type.

Setting categories — drives the tab grouping in the TUI's
`SettingsModal` and, more importantly, gives any embedder (GUI,
web settings panel, …) a stable taxonomy to bucket settings under
without re-hardcoding the split.

- `'agent'` — agent behaviour, capabilities, persistence,
  data handling, auth. "What zidane DOES."
- `'ui'`    — display, rendering, theming, animations,
  chrome density, keybindings. "How zidane LOOKS
  and feels."

Borderline calls (`showAllProjects`, `hideSubagentOutput`) are documented
inline at their rows.

```ts
type SettingsCategory = 'agent' | 'ui'
```

Source: [src/chat/settings-catalog.ts:72](https://github.com/Tahul/zidane/blob/main/src/chat/settings-catalog.ts#L72)

### `StartGitWatch`

Type.

Starts watching a folder's repository: resolves its stop, or `null` when the folder is not in one.

```ts
type StartGitWatch = (cwd: string, onChange: (change: GitChange) => void) => Promise<(() => void) | null>
```

Source: [src/chat/git/watch.ts:51](https://github.com/Tahul/zidane/blob/main/src/chat/git/watch.ts#L51)

### `StepCancelledBy`

Type.

Who cancelled a step.

```ts
type StepCancelledBy = 'agent' | 'user'
```

Source: [src/chat/plans.ts:72](https://github.com/Tahul/zidane/blob/main/src/chat/plans.ts#L72)

### `StorageMode`

Type.

Which resolution branch produced the layout — surfaced for debugging + tests.

```ts
type StorageMode = 'legacy-explicit' | 'legacy-existing' | 'xdg' | 'default'
```

Source: [src/chat/xdg.ts:54](https://github.com/Tahul/zidane/blob/main/src/chat/xdg.ts#L54)

### `StorageSlot`

Type.

The four slot kinds we resolve. Names match the XDG spec.

```ts
type StorageSlot = 'config' | 'data' | 'state' | 'cache'
```

Source: [src/chat/xdg.ts:51](https://github.com/Tahul/zidane/blob/main/src/chat/xdg.ts#L51)

### `StringSettingKey`

Type.

Keys of `Settings` whose value type is exactly `string`. Bounds the
input table's `key` the same way [`BooleanSettingKey`](https://zidane.si/docs/reference/chat-pure.md#BooleanSettingKey) bounds the
toggle table — a free-text row can only point at a string-valued
setting.

```ts
type StringSettingKey = { [K in keyof Settings]-?: Settings[K] extends string ? K : never }[keyof Settings]
```

Source: [src/chat/settings-catalog.ts:681](https://github.com/Tahul/zidane/blob/main/src/chat/settings-catalog.ts#L681)

### `SyntaxStyles`

Type.

Map of Tree-sitter / markdown capture group → token style.

Two flavours of keys live in the same table:

- `markup.*` — the markdown structure captures (heading, bold, italic,
  list, quote, raw inline + block, link). OpenTUI's markdown parser
  emits these for the markdown text itself.
- bare token names (`keyword`, `string`, `function`, …) — emitted by
  the embedded language Tree-sitter grammars when a code fence
  declares a language. OpenTUI passes the same `SyntaxStyle` down to
  the fenced-code renderable, so this map drives both surfaces.

```ts
type SyntaxStyles = Record<string, SyntaxTokenStyle>
```

Source: [src/chat/theme.ts:206](https://github.com/Tahul/zidane/blob/main/src/chat/theme.ts#L206)

### `TitleSource`

Type.

Who set a session's title: auto-titling or the user.

```ts
type TitleSource = 'auto' | 'user'
```

Source: [src/chat/auto-title.ts:34](https://github.com/Tahul/zidane/blob/main/src/chat/auto-title.ts#L34)

### `TodoStatus`

Type.

Progress of one todo (one plan step).

```ts
type TodoStatus = 'pending' | 'in_progress' | 'completed' | 'cancelled'
```

Source: [src/chat/todos.ts:57](https://github.com/Tahul/zidane/blob/main/src/chat/todos.ts#L57)

### `ToolCallDisplay`

Type.

How `tool` events render in the transcript. Drives `Settings.toolCallDisplay`.

- `'hidden'` — `tool` events filtered out entirely (the matching
  `tool-result` block still shows unless its own toggle suppresses
  it). Use when the transcript should read like a chat log.
- `'formatted'` (default) — each tool renders a clean per-tool
  summary line (e.g. `↳ Read src/foo.ts`, `↳ Shell git status`).
  Native tools have curated formatters; unknown / MCP tools fall
  back to a compact `↳ <name>` line.
- `'full'` — debug view: `↳ <name>` header plus the entire raw
  `input` rendered as syntax-highlighted JSON. Useful for
  inspecting what the model actually sent.

```ts
type ToolCallDisplay = 'hidden' | 'formatted' | 'full'
```

Source: [src/chat/types.ts:694](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L694)

### `TranscriptDensity`

Type.

How the transcript lays an exchange out.

- `full`: every row where it happened (thinking, tool calls, batches,
  subagent blocks, text).
- `compact`: between two user prompts only the prompt and the
  assistant's text stay visible; thinking, tool calls and worker
  output fold behind one "Worked …" row that expands in place.

```ts
type TranscriptDensity = 'full' | 'compact'
```

Source: [src/chat/types.ts:747](https://github.com/Tahul/zidane/blob/main/src/chat/types.ts#L747)

### `TranscriptItem`

Type.

One entry in the partitioned transcript-row list. Single events
render as a normal row; a `child-run` groups ONE subagent's events
from a consecutive run of subagent output into one bordered box
(concurrent siblings get a `child-run` each, so their rows never
braid together inside a shared frame); `tool-group`s
collect ALL parent-owned tool/tool-result events of one exchange
into a single collapsible summary row (`Settings.groupToolCalls`);
an `exchange-fold` goes further (`Settings.transcriptDensity:
'compact'`) and takes every thinking, tool and worker event of the
exchange, leaving only the prompt and the assistant's prose in place.
`key` is stable across re-partitions of the same stream (derived
from the first grouped event) so expand state survives appends.

Currently produced by the TUI's `partitionTranscript`; a GUI shell
grouping subagent runs the same way can produce the same shape.

```ts
type TranscriptItem =
  | { event: StreamEvent; kind: 'event'; previous?: StreamEvent }
  | { events: StreamEvent[]; kind: 'child-run'; previous?: StreamEvent }
  | { events: StreamEvent[]; key: string; kind: 'tool-group'; previous?: StreamEvent }
  | { events: StreamEvent[]; key: string; kind: 'exchange-fold'; previous?: StreamEvent }
  | { events: StreamEvent[]; key: string; kind: 'message-group'; previous?: StreamEvent }
```

Source: [src/chat/transcript-anchors.ts:35](https://github.com/Tahul/zidane/blob/main/src/chat/transcript-anchors.ts#L35)

### `UpdateWorkerOutcome`

Type.

How a run ended, as its starter sees it.

```ts
type UpdateWorkerOutcome =
  | AutoInstallOutcome
  | { error: string | null; kind: 'ran'; report: UpgradeReport }
  | { exitCode: number; kind: 'exited' }
  | { error: string; kind: 'lost' }
```

Source: [src/chat/auto-update-worker.ts:90](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L90)

### `UpdateWorkerUpgradeCommand`

Type.

Runs `zidane upgrade <argv>` in the worker; the host wires it (it knows its
package, version and companions). Must not take the install lock: the
worker holds it.

```ts
type UpdateWorkerUpgradeCommand = (argv: readonly string[], stdout: NodeJS.WritableStream, stderr: NodeJS.WritableStream) => Promise<number>
```

Source: [src/chat/auto-update-worker.ts:105](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-worker.ts#L105)

### `UpgradeProgressEvent`

Type.

Progress event emitted while packages are upgraded: a version check, an install start, a line of package manager output, or a finished package.

```ts
type UpgradeProgressEvent =
  | { kind: 'check'; packageName: string }
  | { command: string[]; from: string; kind: 'install'; packageName: string; to: string }
  | { kind: 'log'; line: string; packageName: string; stream: 'stdout' | 'stderr' }
  | { kind: 'done'; result: UpgradePackageResult }
```

Source: [src/chat/auto-update-service.ts:72](https://github.com/Tahul/zidane/blob/main/src/chat/auto-update-service.ts#L72)

### `UsageRange`

Type.

The summary's window.

```ts
type UsageRange = 'all' | '30d' | '7d'
```

Source: [src/chat/usage-summary.ts:22](https://github.com/Tahul/zidane/blob/main/src/chat/usage-summary.ts#L22)

### `VisibleMcpRow`

Type.

One row in the flattened MCP settings list. Three kinds:

- `'server'`     — server header with the chevron + checkbox +
  transport detail. Always present.
- `'tool'`       — checkbox + description for a tool advertised
  by the parent server. Present only when the
  server is expanded AND its tool cache is
  populated.
- `'tool-empty'` — placeholder shown when an expanded server
  has no cached tool list yet (first launch
  before any session has bootstrapped the
  server). Lets the renderer surface a
  one-line "send a prompt to populate" hint
  WITHOUT changing the server's expand
  affordance.

```ts
type VisibleMcpRow =
  | { entry: DiscoveredMcp; kind: 'server' }
  | { kind: 'tool'; serverName: string; tool: McpToolSchema }
  | { kind: 'tool-empty'; serverName: string }
```

Source: [src/chat/mcp-rows.ts:38](https://github.com/Tahul/zidane/blob/main/src/chat/mcp-rows.ts#L38)

### `Walk`

Type.

Walk function — takes an abort signal, returns the discovered list.

```ts
type Walk<T> = (signal: AbortSignal) => Promise<readonly T[]>
```

**Type parameters**

- `T`

Source: [src/chat/discovery-slot.ts:34](https://github.com/Tahul/zidane/blob/main/src/chat/discovery-slot.ts#L34)

### `WorkspaceWriteResult`

Type.

What a guarded write did: `changed`: the file on disk is newer than expected; `denied`: outside cwd or unwritable.

```ts
type WorkspaceWriteResult =
  | { mtimeMs: number; ok: true }
  | { ok: false; reason: 'changed' | 'missing' | 'denied' }
```

Source: [src/chat/workspace/types.ts:53](https://github.com/Tahul/zidane/blob/main/src/chat/workspace/types.ts#L53)

## Re-exports

These names are documented on the entry point that owns them.

- [zidane/chat/pure](https://zidane.si/docs/reference/chat-pure.md): [`accentColor`](https://zidane.si/docs/reference/chat-pure.md#accentColor), [`activePlanSummary`](https://zidane.si/docs/reference/chat-pure.md#activePlanSummary), [`activeSnapshotFor`](https://zidane.si/docs/reference/chat-pure.md#activeSnapshotFor), [`activeSpaceOf`](https://zidane.si/docs/reference/chat-pure.md#activeSpaceOf), [`ActiveTrigger`](https://zidane.si/docs/reference/chat-pure.md#ActiveTrigger), [`AgentAccent`](https://zidane.si/docs/reference/chat-pure.md#AgentAccent), [`ageString`](https://zidane.si/docs/reference/chat-pure.md#ageString), [`API_KEY_ACCOUNT_LABEL`](https://zidane.si/docs/reference/chat-pure.md#API_KEY_ACCOUNT_LABEL), [`applyEditPayload`](https://zidane.si/docs/reference/chat-pure.md#applyEditPayload), [`applyingPlan`](https://zidane.si/docs/reference/chat-pure.md#applyingPlan), [`applyInsert`](https://zidane.si/docs/reference/chat-pure.md#applyInsert), [`applyPlanEvent`](https://zidane.si/docs/reference/chat-pure.md#applyPlanEvent), [`applySettingsPatch`](https://zidane.si/docs/reference/chat-pure.md#applySettingsPatch), [`APPROVAL_MODE_OPTIONS`](https://zidane.si/docs/reference/chat-pure.md#APPROVAL_MODE_OPTIONS), [`APPROVAL_MODE_TRADEOFFS`](https://zidane.si/docs/reference/chat-pure.md#APPROVAL_MODE_TRADEOFFS), [`AuthMethod`](https://zidane.si/docs/reference/chat-pure.md#AuthMethod), [`autoTextModelId`](https://zidane.si/docs/reference/chat-pure.md#autoTextModelId), [`bindingAlternatives`](https://zidane.si/docs/reference/chat-pure.md#bindingAlternatives), [`blendHsl`](https://zidane.si/docs/reference/chat-pure.md#blendHsl), [`BooleanSettingKey`](https://zidane.si/docs/reference/chat-pure.md#BooleanSettingKey), [`buildContextBreakdown`](https://zidane.si/docs/reference/chat-pure.md#buildContextBreakdown), [`buildContextSegments`](https://zidane.si/docs/reference/chat-pure.md#buildContextSegments), [`buildContextualDiff`](https://zidane.si/docs/reference/chat-pure.md#buildContextualDiff), [`buildLinearRamp`](https://zidane.si/docs/reference/chat-pure.md#buildLinearRamp), [`buildModelCatalog`](https://zidane.si/docs/reference/chat-pure.md#buildModelCatalog), [`buildOnboardingResult`](https://zidane.si/docs/reference/chat-pure.md#buildOnboardingResult), [`buildSessionSearchIndex`](https://zidane.si/docs/reference/chat-pure.md#buildSessionSearchIndex), [`buildSessionSearchIndexAsync`](https://zidane.si/docs/reference/chat-pure.md#buildSessionSearchIndexAsync), [`buildUnifiedDiff`](https://zidane.si/docs/reference/chat-pure.md#buildUnifiedDiff), [`CACHE_HINT_MIN_TOKENS`](https://zidane.si/docs/reference/chat-pure.md#CACHE_HINT_MIN_TOKENS), [`cacheBreakChip`](https://zidane.si/docs/reference/chat-pure.md#cacheBreakChip), [`CacheChipContent`](https://zidane.si/docs/reference/chat-pure.md#CacheChipContent), [`CacheChipTone`](https://zidane.si/docs/reference/chat-pure.md#CacheChipTone), [`cacheHintText`](https://zidane.si/docs/reference/chat-pure.md#cacheHintText), [`calibrationFactor`](https://zidane.si/docs/reference/chat-pure.md#calibrationFactor), [`canSkipOnboardingStep`](https://zidane.si/docs/reference/chat-pure.md#canSkipOnboardingStep), [`capTranscriptEvents`](https://zidane.si/docs/reference/chat-pure.md#capTranscriptEvents), [`CatalogEntry`](https://zidane.si/docs/reference/chat-pure.md#CatalogEntry), [`chainAccountConflict`](https://zidane.si/docs/reference/chat-pure.md#chainAccountConflict), [`chainEntryKey`](https://zidane.si/docs/reference/chat-pure.md#chainEntryKey), [`chainPositionOf`](https://zidane.si/docs/reference/chat-pure.md#chainPositionOf), [`chainsEqual`](https://zidane.si/docs/reference/chat-pure.md#chainsEqual), [`ChatReferenceData`](https://zidane.si/docs/reference/chat-pure.md#ChatReferenceData), [`ChatReferenceEntry`](https://zidane.si/docs/reference/chat-pure.md#ChatReferenceEntry), [`ChatReferenceScope`](https://zidane.si/docs/reference/chat-pure.md#ChatReferenceScope), [`chatReferenceTokensFromText`](https://zidane.si/docs/reference/chat-pure.md#chatReferenceTokensFromText), [`CHATS_TRIGGER`](https://zidane.si/docs/reference/chat-pure.md#CHATS_TRIGGER), [`chunkMarkdownForRender`](https://zidane.si/docs/reference/chat-pure.md#chunkMarkdownForRender), [`classifyMcpTest`](https://zidane.si/docs/reference/chat-pure.md#classifyMcpTest), [`CLOSED_PLAN_STATES`](https://zidane.si/docs/reference/chat-pure.md#CLOSED_PLAN_STATES), [`collectReferences`](https://zidane.si/docs/reference/chat-pure.md#collectReferences), [`Command`](https://zidane.si/docs/reference/chat-pure.md#Command), [`CommandArgSuggestion`](https://zidane.si/docs/reference/chat-pure.md#CommandArgSuggestion), [`CommandInvocation`](https://zidane.si/docs/reference/chat-pure.md#CommandInvocation), [`COMMANDS_TRIGGER`](https://zidane.si/docs/reference/chat-pure.md#COMMANDS_TRIGGER), [`compactingLabel`](https://zidane.si/docs/reference/chat-pure.md#compactingLabel), [`compactPath`](https://zidane.si/docs/reference/chat-pure.md#compactPath), [`compareModelVersion`](https://zidane.si/docs/reference/chat-pure.md#compareModelVersion), [`compileQuery`](https://zidane.si/docs/reference/chat-pure.md#compileQuery), [`CompletionContext`](https://zidane.si/docs/reference/chat-pure.md#CompletionContext), [`CompletionItem`](https://zidane.si/docs/reference/chat-pure.md#CompletionItem), [`CompletionProvider`](https://zidane.si/docs/reference/chat-pure.md#CompletionProvider), [`CompletionReference`](https://zidane.si/docs/reference/chat-pure.md#CompletionReference), [`computeInlineDiff`](https://zidane.si/docs/reference/chat-pure.md#computeInlineDiff), [`computeLineDiff`](https://zidane.si/docs/reference/chat-pure.md#computeLineDiff), [`CONNECTOR_CATEGORIES`](https://zidane.si/docs/reference/chat-pure.md#CONNECTOR_CATEGORIES), [`ConnectorAuth`](https://zidane.si/docs/reference/chat-pure.md#ConnectorAuth), [`connectorAuthText`](https://zidane.si/docs/reference/chat-pure.md#connectorAuthText), [`ConnectorAuthWords`](https://zidane.si/docs/reference/chat-pure.md#ConnectorAuthWords), [`connectorById`](https://zidane.si/docs/reference/chat-pure.md#connectorById), [`ConnectorCategory`](https://zidane.si/docs/reference/chat-pure.md#ConnectorCategory), [`ConnectorCategoryMeta`](https://zidane.si/docs/reference/chat-pure.md#ConnectorCategoryMeta), [`ConnectorEntry`](https://zidane.si/docs/reference/chat-pure.md#ConnectorEntry), [`ConnectorInput`](https://zidane.si/docs/reference/chat-pure.md#ConnectorInput), [`connectorInputError`](https://zidane.si/docs/reference/chat-pure.md#connectorInputError), [`connectorInstalled`](https://zidane.si/docs/reference/chat-pure.md#connectorInstalled), [`CONNECTORS`](https://zidane.si/docs/reference/chat-pure.md#CONNECTORS), [`connectorServerConfig`](https://zidane.si/docs/reference/chat-pure.md#connectorServerConfig), [`connectorsFromImport`](https://zidane.si/docs/reference/chat-pure.md#connectorsFromImport), [`ConnectorTokenAuth`](https://zidane.si/docs/reference/chat-pure.md#ConnectorTokenAuth), [`ConnectorTransport`](https://zidane.si/docs/reference/chat-pure.md#ConnectorTransport), [`ContextBreakdown`](https://zidane.si/docs/reference/chat-pure.md#ContextBreakdown), [`ContextBreakdownContributionContext`](https://zidane.si/docs/reference/chat-pure.md#ContextBreakdownContributionContext), [`ContextBreakdownContributor`](https://zidane.si/docs/reference/chat-pure.md#ContextBreakdownContributor), [`ContextBreakdownOptions`](https://zidane.si/docs/reference/chat-pure.md#ContextBreakdownOptions), [`ContextCategory`](https://zidane.si/docs/reference/chat-pure.md#ContextCategory), [`ContextCategoryId`](https://zidane.si/docs/reference/chat-pure.md#ContextCategoryId), [`ContextExactCounts`](https://zidane.si/docs/reference/chat-pure.md#ContextExactCounts), [`ContextExtensionRow`](https://zidane.si/docs/reference/chat-pure.md#ContextExtensionRow), [`ContextExtensionSection`](https://zidane.si/docs/reference/chat-pure.md#ContextExtensionSection), [`ContextItem`](https://zidane.si/docs/reference/chat-pure.md#ContextItem), [`ContextMcpGroup`](https://zidane.si/docs/reference/chat-pure.md#ContextMcpGroup), [`ContextSegment`](https://zidane.si/docs/reference/chat-pure.md#ContextSegment), [`ContextSegmentFile`](https://zidane.si/docs/reference/chat-pure.md#ContextSegmentFile), [`ContextSegmentOrigin`](https://zidane.si/docs/reference/chat-pure.md#ContextSegmentOrigin), [`ContextSegmentsInput`](https://zidane.si/docs/reference/chat-pure.md#ContextSegmentsInput), [`ContextSnapshot`](https://zidane.si/docs/reference/chat-pure.md#ContextSnapshot), [`ContextUsageSplit`](https://zidane.si/docs/reference/chat-pure.md#ContextUsageSplit), [`copyNames`](https://zidane.si/docs/reference/chat-pure.md#copyNames), [`countNeighbors`](https://zidane.si/docs/reference/chat-pure.md#countNeighbors), [`createChatsCompletionProvider`](https://zidane.si/docs/reference/chat-pure.md#createChatsCompletionProvider), [`createCommandsCompletionProvider`](https://zidane.si/docs/reference/chat-pure.md#createCommandsCompletionProvider), [`createEntryOps`](https://zidane.si/docs/reference/chat-pure.md#createEntryOps), [`createFilesCompletionProvider`](https://zidane.si/docs/reference/chat-pure.md#createFilesCompletionProvider), [`createOnboardingFlow`](https://zidane.si/docs/reference/chat-pure.md#createOnboardingFlow), [`createOnboardingRun`](https://zidane.si/docs/reference/chat-pure.md#createOnboardingRun), [`createSkillsCompletionProvider`](https://zidane.si/docs/reference/chat-pure.md#createSkillsCompletionProvider), [`currentModelKeys`](https://zidane.si/docs/reference/chat-pure.md#currentModelKeys), [`currentModels`](https://zidane.si/docs/reference/chat-pure.md#currentModels), [`currentOnboardingStep`](https://zidane.si/docs/reference/chat-pure.md#currentOnboardingStep), [`CustomModelTarget`](https://zidane.si/docs/reference/chat-pure.md#CustomModelTarget), [`customModelTargets`](https://zidane.si/docs/reference/chat-pure.md#customModelTargets), [`DAEMON_HEALTH_GLYPH`](https://zidane.si/docs/reference/chat-pure.md#DAEMON_HEALTH_GLYPH), [`DAEMON_HEALTH_STYLE`](https://zidane.si/docs/reference/chat-pure.md#DAEMON_HEALTH_STYLE), [`daemonGate`](https://zidane.si/docs/reference/chat-pure.md#daemonGate), [`DaemonHealth`](https://zidane.si/docs/reference/chat-pure.md#DaemonHealth), [`DaemonHealthStyle`](https://zidane.si/docs/reference/chat-pure.md#DaemonHealthStyle), [`DaemonHealthTone`](https://zidane.si/docs/reference/chat-pure.md#DaemonHealthTone), [`decisionsEnabled`](https://zidane.si/docs/reference/chat-pure.md#decisionsEnabled), [`DEFAULT_EXTENSION_SOURCE`](https://zidane.si/docs/reference/chat-pure.md#DEFAULT_EXTENSION_SOURCE), [`DEFAULT_EXTENSION_SOURCE_UNREACHABLE`](https://zidane.si/docs/reference/chat-pure.md#DEFAULT_EXTENSION_SOURCE_UNREACHABLE), [`DEFAULT_OAUTH_ACCOUNT_LABEL`](https://zidane.si/docs/reference/chat-pure.md#DEFAULT_OAUTH_ACCOUNT_LABEL), [`DEFAULT_SETTINGS`](https://zidane.si/docs/reference/chat-pure.md#DEFAULT_SETTINGS), [`DEFAULT_SPACE_ID`](https://zidane.si/docs/reference/chat-pure.md#DEFAULT_SPACE_ID), [`DEFAULT_SPACE_NAME`](https://zidane.si/docs/reference/chat-pure.md#DEFAULT_SPACE_NAME), [`defaultExtensionSource`](https://zidane.si/docs/reference/chat-pure.md#defaultExtensionSource), [`defaultImportSelection`](https://zidane.si/docs/reference/chat-pure.md#defaultImportSelection), [`defaultSessionAgent`](https://zidane.si/docs/reference/chat-pure.md#defaultSessionAgent), [`defaultSessionKnobs`](https://zidane.si/docs/reference/chat-pure.md#defaultSessionKnobs), [`defaultSessionModel`](https://zidane.si/docs/reference/chat-pure.md#defaultSessionModel), [`defineFeatureGate`](https://zidane.si/docs/reference/chat-pure.md#defineFeatureGate), [`deleteTurnSafely`](https://zidane.si/docs/reference/chat-pure.md#deleteTurnSafely), [`DiffLine`](https://zidane.si/docs/reference/chat-pure.md#DiffLine), [`DiffOp`](https://zidane.si/docs/reference/chat-pure.md#DiffOp), [`displayNameFor`](https://zidane.si/docs/reference/chat-pure.md#displayNameFor), [`DURABILITY_OPTIONS`](https://zidane.si/docs/reference/chat-pure.md#DURABILITY_OPTIONS), [`editableTurnText`](https://zidane.si/docs/reference/chat-pure.md#editableTurnText), [`EditHunkSummary`](https://zidane.si/docs/reference/chat-pure.md#EditHunkSummary), [`EditSummary`](https://zidane.si/docs/reference/chat-pure.md#EditSummary), [`effectiveOnboardingMode`](https://zidane.si/docs/reference/chat-pure.md#effectiveOnboardingMode), [`enabledMcpsWith`](https://zidane.si/docs/reference/chat-pure.md#enabledMcpsWith), [`enabledMcpsWithout`](https://zidane.si/docs/reference/chat-pure.md#enabledMcpsWithout), [`enforceSettingsInvariants`](https://zidane.si/docs/reference/chat-pure.md#enforceSettingsInvariants), [`engagedKillSwitches`](https://zidane.si/docs/reference/chat-pure.md#engagedKillSwitches), [`ENTRY_IMPORT_MAX_BYTES`](https://zidane.si/docs/reference/chat-pure.md#ENTRY_IMPORT_MAX_BYTES), [`ENTRY_IMPORT_MAX_ENTRIES`](https://zidane.si/docs/reference/chat-pure.md#ENTRY_IMPORT_MAX_ENTRIES), [`ENTRY_IMPORT_MAX_FILE_BYTES`](https://zidane.si/docs/reference/chat-pure.md#ENTRY_IMPORT_MAX_FILE_BYTES), [`EntryCreateOptions`](https://zidane.si/docs/reference/chat-pure.md#EntryCreateOptions), [`EntryImportItem`](https://zidane.si/docs/reference/chat-pure.md#EntryImportItem), [`EntryImportLimits`](https://zidane.si/docs/reference/chat-pure.md#EntryImportLimits), [`EntryImportResult`](https://zidane.si/docs/reference/chat-pure.md#EntryImportResult), [`entryName`](https://zidane.si/docs/reference/chat-pure.md#entryName), [`EntryOps`](https://zidane.si/docs/reference/chat-pure.md#EntryOps), [`entryParent`](https://zidane.si/docs/reference/chat-pure.md#entryParent), [`entryPath`](https://zidane.si/docs/reference/chat-pure.md#entryPath), [`EntryRefusal`](https://zidane.si/docs/reference/chat-pure.md#EntryRefusal), [`EntryResult`](https://zidane.si/docs/reference/chat-pure.md#EntryResult), [`escapeTemplate`](https://zidane.si/docs/reference/chat-pure.md#escapeTemplate), [`estimateReadout`](https://zidane.si/docs/reference/chat-pure.md#estimateReadout), [`EstimateReadout`](https://zidane.si/docs/reference/chat-pure.md#EstimateReadout), [`EXCLUDED_NATIVE_EXTENSIONS`](https://zidane.si/docs/reference/chat-pure.md#EXCLUDED_NATIVE_EXTENSIONS), [`extensionMark`](https://zidane.si/docs/reference/chat-pure.md#extensionMark), [`ExtensionMark`](https://zidane.si/docs/reference/chat-pure.md#ExtensionMark), [`ExtensionMarkInput`](https://zidane.si/docs/reference/chat-pure.md#ExtensionMarkInput), [`ExtensionSourceEntry`](https://zidane.si/docs/reference/chat-pure.md#ExtensionSourceEntry), [`ExtensionSourceErrorCode`](https://zidane.si/docs/reference/chat-pure.md#ExtensionSourceErrorCode), [`ExtensionSourceInfo`](https://zidane.si/docs/reference/chat-pure.md#ExtensionSourceInfo), [`ExtensionSourceKind`](https://zidane.si/docs/reference/chat-pure.md#ExtensionSourceKind), [`ExtensionSourceListing`](https://zidane.si/docs/reference/chat-pure.md#ExtensionSourceListing), [`extensionSpace`](https://zidane.si/docs/reference/chat-pure.md#extensionSpace), [`ExtensionSpaceSource`](https://zidane.si/docs/reference/chat-pure.md#ExtensionSpaceSource), [`extractEditPayload`](https://zidane.si/docs/reference/chat-pure.md#extractEditPayload), [`FacetPath`](https://zidane.si/docs/reference/chat-pure.md#FacetPath), [`FEATURE_GATES`](https://zidane.si/docs/reference/chat-pure.md#FEATURE_GATES), [`FeatureGate`](https://zidane.si/docs/reference/chat-pure.md#FeatureGate), [`FeatureGateOptions`](https://zidane.si/docs/reference/chat-pure.md#FeatureGateOptions), [`FILES_TRIGGER`](https://zidane.si/docs/reference/chat-pure.md#FILES_TRIGGER), [`filetypeFromPath`](https://zidane.si/docs/reference/chat-pure.md#filetypeFromPath), [`filterModelCatalog`](https://zidane.si/docs/reference/chat-pure.md#filterModelCatalog), [`filterSessionSearchCatalog`](https://zidane.si/docs/reference/chat-pure.md#filterSessionSearchCatalog), [`finalizeStreamingMarkdown`](https://zidane.si/docs/reference/chat-pure.md#finalizeStreamingMarkdown), [`finalizeStreamingMarkdownForOwner`](https://zidane.si/docs/reference/chat-pure.md#finalizeStreamingMarkdownForOwner), [`findActiveTrigger`](https://zidane.si/docs/reference/chat-pure.md#findActiveTrigger), [`findSpace`](https://zidane.si/docs/reference/chat-pure.md#findSpace), [`fitRunReceiptCells`](https://zidane.si/docs/reference/chat-pure.md#fitRunReceiptCells), [`fmtCost`](https://zidane.si/docs/reference/chat-pure.md#fmtCost), [`fmtTokens`](https://zidane.si/docs/reference/chat-pure.md#fmtTokens), [`formatCostUsd`](https://zidane.si/docs/reference/chat-pure.md#formatCostUsd), [`formatDuration`](https://zidane.si/docs/reference/chat-pure.md#formatDuration), [`formatEstimate`](https://zidane.si/docs/reference/chat-pure.md#formatEstimate), [`formatModelSetting`](https://zidane.si/docs/reference/chat-pure.md#formatModelSetting), [`formatRunDuration`](https://zidane.si/docs/reference/chat-pure.md#formatRunDuration), [`formatRunReceiptCells`](https://zidane.si/docs/reference/chat-pure.md#formatRunReceiptCells), [`formatRunReceiptLine`](https://zidane.si/docs/reference/chat-pure.md#formatRunReceiptLine), [`formatTaskStatus`](https://zidane.si/docs/reference/chat-pure.md#formatTaskStatus), [`formatTaskSummary`](https://zidane.si/docs/reference/chat-pure.md#formatTaskSummary), [`formatTokenCount`](https://zidane.si/docs/reference/chat-pure.md#formatTokenCount), [`formatTokensPerSecond`](https://zidane.si/docs/reference/chat-pure.md#formatTokensPerSecond), [`formatToolCall`](https://zidane.si/docs/reference/chat-pure.md#formatToolCall), [`formatUsagePercent`](https://zidane.si/docs/reference/chat-pure.md#formatUsagePercent), [`freeEntryName`](https://zidane.si/docs/reference/chat-pure.md#freeEntryName), [`generateSessionTitle`](https://zidane.si/docs/reference/chat-pure.md#generateSessionTitle), [`GenerateSessionTitleOptions`](https://zidane.si/docs/reference/chat-pure.md#GenerateSessionTitleOptions), [`hasRunReceipt`](https://zidane.si/docs/reference/chat-pure.md#hasRunReceipt), [`HighlightRange`](https://zidane.si/docs/reference/chat-pure.md#HighlightRange), [`HighlightSegment`](https://zidane.si/docs/reference/chat-pure.md#HighlightSegment), [`highlightSegments`](https://zidane.si/docs/reference/chat-pure.md#highlightSegments), [`HostEntry`](https://zidane.si/docs/reference/chat-pure.md#HostEntry), [`HostErrorCode`](https://zidane.si/docs/reference/chat-pure.md#HostErrorCode), [`HostIdentity`](https://zidane.si/docs/reference/chat-pure.md#HostIdentity), [`HostProbeResult`](https://zidane.si/docs/reference/chat-pure.md#HostProbeResult), [`HostRegistryError`](https://zidane.si/docs/reference/chat-pure.md#HostRegistryError), [`HostTestResult`](https://zidane.si/docs/reference/chat-pure.md#HostTestResult), [`HunkResolution`](https://zidane.si/docs/reference/chat-pure.md#HunkResolution), [`importEntries`](https://zidane.si/docs/reference/chat-pure.md#importEntries), [`importsSomething`](https://zidane.si/docs/reference/chat-pure.md#importsSomething), [`IndexedSearchTurn`](https://zidane.si/docs/reference/chat-pure.md#IndexedSearchTurn), [`indexOfEntry`](https://zidane.si/docs/reference/chat-pure.md#indexOfEntry), [`inferPromptRefs`](https://zidane.si/docs/reference/chat-pure.md#inferPromptRefs), [`InlineDiff`](https://zidane.si/docs/reference/chat-pure.md#InlineDiff), [`InlineGhost`](https://zidane.si/docs/reference/chat-pure.md#InlineGhost), [`InlineSegment`](https://zidane.si/docs/reference/chat-pure.md#InlineSegment), [`InstalledServerRef`](https://zidane.si/docs/reference/chat-pure.md#InstalledServerRef), [`isEditErrorResult`](https://zidane.si/docs/reference/chat-pure.md#isEditErrorResult), [`isExistingInstall`](https://zidane.si/docs/reference/chat-pure.md#isExistingInstall), [`isExtensionSpaceId`](https://zidane.si/docs/reference/chat-pure.md#isExtensionSpaceId), [`isKillSwitchEngaged`](https://zidane.si/docs/reference/chat-pure.md#isKillSwitchEngaged), [`isMainSession`](https://zidane.si/docs/reference/chat-pure.md#isMainSession), [`isTurnHighlighted`](https://zidane.si/docs/reference/chat-pure.md#isTurnHighlighted), [`isVisible`](https://zidane.si/docs/reference/chat-pure.md#isVisible), [`joinEntry`](https://zidane.si/docs/reference/chat-pure.md#joinEntry), [`joinFewNames`](https://zidane.si/docs/reference/chat-pure.md#joinFewNames), [`joinNames`](https://zidane.si/docs/reference/chat-pure.md#joinNames), [`LineupEntry`](https://zidane.si/docs/reference/chat-pure.md#LineupEntry), [`LiveRunStatus`](https://zidane.si/docs/reference/chat-pure.md#LiveRunStatus), [`LOCAL_SERVER_BRANDS`](https://zidane.si/docs/reference/chat-pure.md#LOCAL_SERVER_BRANDS), [`LOCAL_SERVER_NAMES`](https://zidane.si/docs/reference/chat-pure.md#LOCAL_SERVER_NAMES), [`MARKDOWN_RENDER_CHUNK_TARGET_BYTES`](https://zidane.si/docs/reference/chat-pure.md#MARKDOWN_RENDER_CHUNK_TARGET_BYTES), [`MarkdownSegment`](https://zidane.si/docs/reference/chat-pure.md#MarkdownSegment), [`MAX_TRANSCRIPT_EVENTS`](https://zidane.si/docs/reference/chat-pure.md#MAX_TRANSCRIPT_EVENTS), [`MCP_KEY_REFUSED_HINT`](https://zidane.si/docs/reference/chat-pure.md#MCP_KEY_REFUSED_HINT), [`MCP_STATUS_LABEL`](https://zidane.si/docs/reference/chat-pure.md#MCP_STATUS_LABEL), [`McpAuthStatus`](https://zidane.si/docs/reference/chat-pure.md#McpAuthStatus), [`McpTestOutcomeKind`](https://zidane.si/docs/reference/chat-pure.md#McpTestOutcomeKind), [`McpTestResult`](https://zidane.si/docs/reference/chat-pure.md#McpTestResult), [`mergeOnboardingState`](https://zidane.si/docs/reference/chat-pure.md#mergeOnboardingState), [`mergeReferences`](https://zidane.si/docs/reference/chat-pure.md#mergeReferences), [`mergeSuggestions`](https://zidane.si/docs/reference/chat-pure.md#mergeSuggestions), [`MESSAGE_DELIVERY_OPTIONS`](https://zidane.si/docs/reference/chat-pure.md#MESSAGE_DELIVERY_OPTIONS), [`MESSAGE_DELIVERY_TRADEOFFS`](https://zidane.si/docs/reference/chat-pure.md#MESSAGE_DELIVERY_TRADEOFFS), [`migratedOnboardingRecords`](https://zidane.si/docs/reference/chat-pure.md#migratedOnboardingRecords), [`ModelChainEntry`](https://zidane.si/docs/reference/chat-pure.md#ModelChainEntry), [`modelConcern`](https://zidane.si/docs/reference/chat-pure.md#modelConcern), [`ModelIdParts`](https://zidane.si/docs/reference/chat-pure.md#ModelIdParts), [`ModelNamer`](https://zidane.si/docs/reference/chat-pure.md#ModelNamer), [`modelPickKey`](https://zidane.si/docs/reference/chat-pure.md#modelPickKey), [`ModelSettingPick`](https://zidane.si/docs/reference/chat-pure.md#ModelSettingPick), [`mostConstrainedWindow`](https://zidane.si/docs/reference/chat-pure.md#mostConstrainedWindow), [`nameFromModelId`](https://zidane.si/docs/reference/chat-pure.md#nameFromModelId), [`NATIVE_EXTENSION_GROUPS`](https://zidane.si/docs/reference/chat-pure.md#NATIVE_EXTENSION_GROUPS), [`NATIVE_EXTENSIONS`](https://zidane.si/docs/reference/chat-pure.md#NATIVE_EXTENSIONS), [`NATIVE_EXTENSIONS_REPO`](https://zidane.si/docs/reference/chat-pure.md#NATIVE_EXTENSIONS_REPO), [`nativeExtensionByName`](https://zidane.si/docs/reference/chat-pure.md#nativeExtensionByName), [`NativeExtensionCommandNeed`](https://zidane.si/docs/reference/chat-pure.md#NativeExtensionCommandNeed), [`nativeExtensionEntry`](https://zidane.si/docs/reference/chat-pure.md#nativeExtensionEntry), [`NativeExtensionEntry`](https://zidane.si/docs/reference/chat-pure.md#NativeExtensionEntry), [`NativeExtensionGroup`](https://zidane.si/docs/reference/chat-pure.md#NativeExtensionGroup), [`NativeExtensionGroupMeta`](https://zidane.si/docs/reference/chat-pure.md#NativeExtensionGroupMeta), [`nativeExtensionListing`](https://zidane.si/docs/reference/chat-pure.md#nativeExtensionListing), [`NativeExtensionNeed`](https://zidane.si/docs/reference/chat-pure.md#NativeExtensionNeed), [`nativeExtensionNeedText`](https://zidane.si/docs/reference/chat-pure.md#nativeExtensionNeedText), [`NativeExtensionNeedWords`](https://zidane.si/docs/reference/chat-pure.md#NativeExtensionNeedWords), [`nativeExtensionSource`](https://zidane.si/docs/reference/chat-pure.md#nativeExtensionSource), [`needsOnboarding`](https://zidane.si/docs/reference/chat-pure.md#needsOnboarding), [`newSessionModel`](https://zidane.si/docs/reference/chat-pure.md#newSessionModel), [`newSpaceId`](https://zidane.si/docs/reference/chat-pure.md#newSpaceId), [`normalizeMarkdownCodeFences`](https://zidane.si/docs/reference/chat-pure.md#normalizeMarkdownCodeFences), [`normalizeSpaces`](https://zidane.si/docs/reference/chat-pure.md#normalizeSpaces), [`oauthAccountLabel`](https://zidane.si/docs/reference/chat-pure.md#oauthAccountLabel), [`ONBOARDING_AGENT_BRANDS`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_AGENT_BRANDS), [`ONBOARDING_AGENT_NAMES`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_AGENT_NAMES), [`ONBOARDING_FEATURE_GROUPS`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_FEATURE_GROUPS), [`ONBOARDING_FEATURES`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_FEATURES), [`ONBOARDING_PREVIEW_ATTACHMENTS`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_PREVIEW_ATTACHMENTS), [`ONBOARDING_PREVIEW_EVENTS`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_PREVIEW_EVENTS), [`ONBOARDING_SETTING_KEYS`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_SETTING_KEYS), [`ONBOARDING_STEP_SETTINGS`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_STEP_SETTINGS), [`ONBOARDING_STEPS`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_STEPS), [`ONBOARDING_VERSION`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_VERSION), [`ONBOARDING_WORDS`](https://zidane.si/docs/reference/chat-pure.md#ONBOARDING_WORDS), [`OnboardingAction`](https://zidane.si/docs/reference/chat-pure.md#OnboardingAction), [`OnboardingAgentId`](https://zidane.si/docs/reference/chat-pure.md#OnboardingAgentId), [`OnboardingAppearance`](https://zidane.si/docs/reference/chat-pure.md#OnboardingAppearance), [`OnboardingCredentialSource`](https://zidane.si/docs/reference/chat-pure.md#OnboardingCredentialSource), [`OnboardingCustomField`](https://zidane.si/docs/reference/chat-pure.md#OnboardingCustomField), [`OnboardingDetection`](https://zidane.si/docs/reference/chat-pure.md#OnboardingDetection), [`OnboardingDraft`](https://zidane.si/docs/reference/chat-pure.md#OnboardingDraft), [`OnboardingFeature`](https://zidane.si/docs/reference/chat-pure.md#OnboardingFeature), [`OnboardingFeatureBadge`](https://zidane.si/docs/reference/chat-pure.md#OnboardingFeatureBadge), [`onboardingFeatureCopy`](https://zidane.si/docs/reference/chat-pure.md#onboardingFeatureCopy), [`OnboardingFeatureCopy`](https://zidane.si/docs/reference/chat-pure.md#OnboardingFeatureCopy), [`OnboardingFeatureGroup`](https://zidane.si/docs/reference/chat-pure.md#OnboardingFeatureGroup), [`OnboardingFeatureGroupMeta`](https://zidane.si/docs/reference/chat-pure.md#OnboardingFeatureGroupMeta), [`onboardingFeatures`](https://zidane.si/docs/reference/chat-pure.md#onboardingFeatures), [`OnboardingFlowState`](https://zidane.si/docs/reference/chat-pure.md#OnboardingFlowState), [`OnboardingGate`](https://zidane.si/docs/reference/chat-pure.md#OnboardingGate), [`OnboardingGateInput`](https://zidane.si/docs/reference/chat-pure.md#OnboardingGateInput), [`OnboardingGithubIdentity`](https://zidane.si/docs/reference/chat-pure.md#OnboardingGithubIdentity), [`OnboardingImportCategory`](https://zidane.si/docs/reference/chat-pure.md#OnboardingImportCategory), [`onboardingImportParts`](https://zidane.si/docs/reference/chat-pure.md#onboardingImportParts), [`OnboardingImportPreview`](https://zidane.si/docs/reference/chat-pure.md#OnboardingImportPreview), [`OnboardingImportPreviewState`](https://zidane.si/docs/reference/chat-pure.md#OnboardingImportPreviewState), [`OnboardingImportSelection`](https://zidane.si/docs/reference/chat-pure.md#OnboardingImportSelection), [`OnboardingImportSessionCounts`](https://zidane.si/docs/reference/chat-pure.md#OnboardingImportSessionCounts), [`OnboardingImportWords`](https://zidane.si/docs/reference/chat-pure.md#OnboardingImportWords), [`OnboardingInstallSignals`](https://zidane.si/docs/reference/chat-pure.md#OnboardingInstallSignals), [`OnboardingLocalServer`](https://zidane.si/docs/reference/chat-pure.md#OnboardingLocalServer), [`OnboardingMode`](https://zidane.si/docs/reference/chat-pure.md#OnboardingMode), [`OnboardingPhase`](https://zidane.si/docs/reference/chat-pure.md#OnboardingPhase), [`OnboardingPlanContext`](https://zidane.si/docs/reference/chat-pure.md#OnboardingPlanContext), [`onboardingProgress`](https://zidane.si/docs/reference/chat-pure.md#onboardingProgress), [`OnboardingProgress`](https://zidane.si/docs/reference/chat-pure.md#OnboardingProgress), [`OnboardingProvider`](https://zidane.si/docs/reference/chat-pure.md#OnboardingProvider), [`OnboardingProviderPick`](https://zidane.si/docs/reference/chat-pure.md#OnboardingProviderPick), [`OnboardingReadable`](https://zidane.si/docs/reference/chat-pure.md#OnboardingReadable), [`onboardingRecord`](https://zidane.si/docs/reference/chat-pure.md#onboardingRecord), [`OnboardingRecord`](https://zidane.si/docs/reference/chat-pure.md#OnboardingRecord), [`onboardingRecords`](https://zidane.si/docs/reference/chat-pure.md#onboardingRecords), [`onboardingReducer`](https://zidane.si/docs/reference/chat-pure.md#onboardingReducer), [`OnboardingResult`](https://zidane.si/docs/reference/chat-pure.md#OnboardingResult), [`OnboardingRun`](https://zidane.si/docs/reference/chat-pure.md#OnboardingRun), [`OnboardingRunHost`](https://zidane.si/docs/reference/chat-pure.md#OnboardingRunHost), [`OnboardingRunOptions`](https://zidane.si/docs/reference/chat-pure.md#OnboardingRunOptions), [`OnboardingRunSetup`](https://zidane.si/docs/reference/chat-pure.md#OnboardingRunSetup), [`OnboardingRunStart`](https://zidane.si/docs/reference/chat-pure.md#OnboardingRunStart), [`onboardingSettingsChange`](https://zidane.si/docs/reference/chat-pure.md#onboardingSettingsChange), [`OnboardingSettingsChange`](https://zidane.si/docs/reference/chat-pure.md#OnboardingSettingsChange), [`OnboardingSignInState`](https://zidane.si/docs/reference/chat-pure.md#OnboardingSignInState), [`OnboardingStart`](https://zidane.si/docs/reference/chat-pure.md#OnboardingStart), [`OnboardingState`](https://zidane.si/docs/reference/chat-pure.md#OnboardingState), [`onboardingStep`](https://zidane.si/docs/reference/chat-pure.md#onboardingStep), [`OnboardingStepId`](https://zidane.si/docs/reference/chat-pure.md#OnboardingStepId), [`OnboardingStepMeta`](https://zidane.si/docs/reference/chat-pure.md#OnboardingStepMeta), [`OnboardingStepScope`](https://zidane.si/docs/reference/chat-pure.md#OnboardingStepScope), [`OnboardingSurface`](https://zidane.si/docs/reference/chat-pure.md#OnboardingSurface), [`onboardingSwitchedFeatures`](https://zidane.si/docs/reference/chat-pure.md#onboardingSwitchedFeatures), [`OnboardingSwitchedFeatures`](https://zidane.si/docs/reference/chat-pure.md#OnboardingSwitchedFeatures), [`OnboardingTask`](https://zidane.si/docs/reference/chat-pure.md#OnboardingTask), [`OnboardingTaskKind`](https://zidane.si/docs/reference/chat-pure.md#OnboardingTaskKind), [`OnboardingTaskStatus`](https://zidane.si/docs/reference/chat-pure.md#OnboardingTaskStatus), [`OnboardingTradeoff`](https://zidane.si/docs/reference/chat-pure.md#OnboardingTradeoff), [`OnboardingWords`](https://zidane.si/docs/reference/chat-pure.md#OnboardingWords), [`oneLineTitle`](https://zidane.si/docs/reference/chat-pure.md#oneLineTitle), [`ORCHESTRATED_BY_KEY`](https://zidane.si/docs/reference/chat-pure.md#ORCHESTRATED_BY_KEY), [`orchestratedBy`](https://zidane.si/docs/reference/chat-pure.md#orchestratedBy), [`otherDelivery`](https://zidane.si/docs/reference/chat-pure.md#otherDelivery), [`parseBindingSpec`](https://zidane.si/docs/reference/chat-pure.md#parseBindingSpec), [`parseBindingSpecs`](https://zidane.si/docs/reference/chat-pure.md#parseBindingSpecs), [`parseCommandInvocation`](https://zidane.si/docs/reference/chat-pure.md#parseCommandInvocation), [`ParsedBinding`](https://zidane.si/docs/reference/chat-pure.md#ParsedBinding), [`parseModelId`](https://zidane.si/docs/reference/chat-pure.md#parseModelId), [`parseModelSetting`](https://zidane.si/docs/reference/chat-pure.md#parseModelSetting), [`parseRemoteHostUrl`](https://zidane.si/docs/reference/chat-pure.md#parseRemoteHostUrl), [`patchInlineGhost`](https://zidane.si/docs/reference/chat-pure.md#patchInlineGhost), [`planOnboarding`](https://zidane.si/docs/reference/chat-pure.md#planOnboarding), [`planProgress`](https://zidane.si/docs/reference/chat-pure.md#planProgress), [`planProgressLabel`](https://zidane.si/docs/reference/chat-pure.md#planProgressLabel), [`previewEditPayload`](https://zidane.si/docs/reference/chat-pure.md#previewEditPayload), [`previewLine`](https://zidane.si/docs/reference/chat-pure.md#previewLine), [`PreviewResult`](https://zidane.si/docs/reference/chat-pure.md#PreviewResult), [`PromptSegment`](https://zidane.si/docs/reference/chat-pure.md#PromptSegment), [`PromptSegmentRef`](https://zidane.si/docs/reference/chat-pure.md#PromptSegmentRef), [`PROVIDER_BRANDS`](https://zidane.si/docs/reference/chat-pure.md#PROVIDER_BRANDS), [`ProviderAccount`](https://zidane.si/docs/reference/chat-pure.md#ProviderAccount), [`ProviderAuth`](https://zidane.si/docs/reference/chat-pure.md#ProviderAuth), [`providerBrand`](https://zidane.si/docs/reference/chat-pure.md#providerBrand), [`ProviderResetCredit`](https://zidane.si/docs/reference/chat-pure.md#ProviderResetCredit), [`ProviderResetOutcome`](https://zidane.si/docs/reference/chat-pure.md#ProviderResetOutcome), [`ProviderUsageCredits`](https://zidane.si/docs/reference/chat-pure.md#ProviderUsageCredits), [`ProviderUsageResets`](https://zidane.si/docs/reference/chat-pure.md#ProviderUsageResets), [`ProviderUsageSnapshot`](https://zidane.si/docs/reference/chat-pure.md#ProviderUsageSnapshot), [`ProviderUsageStatus`](https://zidane.si/docs/reference/chat-pure.md#ProviderUsageStatus), [`ProviderUsageWindow`](https://zidane.si/docs/reference/chat-pure.md#ProviderUsageWindow), [`QueryBound`](https://zidane.si/docs/reference/chat-pure.md#QueryBound), [`querySessions`](https://zidane.si/docs/reference/chat-pure.md#querySessions), [`QueryValue`](https://zidane.si/docs/reference/chat-pure.md#QueryValue), [`readSessionAccount`](https://zidane.si/docs/reference/chat-pure.md#readSessionAccount), [`readSessionSpaceId`](https://zidane.si/docs/reference/chat-pure.md#readSessionSpaceId), [`ReconcilableEvent`](https://zidane.si/docs/reference/chat-pure.md#ReconcilableEvent), [`reconcileEvents`](https://zidane.si/docs/reference/chat-pure.md#reconcileEvents), [`RemoteHostError`](https://zidane.si/docs/reference/chat-pure.md#RemoteHostError), [`remoteHostsGate`](https://zidane.si/docs/reference/chat-pure.md#remoteHostsGate), [`replaceTurnTextBlocks`](https://zidane.si/docs/reference/chat-pure.md#replaceTurnTextBlocks), [`requirementMet`](https://zidane.si/docs/reference/chat-pure.md#requirementMet), [`resolveSessionSpace`](https://zidane.si/docs/reference/chat-pure.md#resolveSessionSpace), [`retryLabel`](https://zidane.si/docs/reference/chat-pure.md#retryLabel), [`RUN_RECEIPT_GLYPH`](https://zidane.si/docs/reference/chat-pure.md#RUN_RECEIPT_GLYPH), [`RUN_STATUS_TIME_MS`](https://zidane.si/docs/reference/chat-pure.md#RUN_STATUS_TIME_MS), [`RUN_WAITING_DELAY_MS`](https://zidane.si/docs/reference/chat-pure.md#RUN_WAITING_DELAY_MS), [`RUN_WAITING_LABEL`](https://zidane.si/docs/reference/chat-pure.md#RUN_WAITING_LABEL), [`runEstimateView`](https://zidane.si/docs/reference/chat-pure.md#runEstimateView), [`RunEstimateView`](https://zidane.si/docs/reference/chat-pure.md#RunEstimateView), [`runFoldStatus`](https://zidane.si/docs/reference/chat-pure.md#runFoldStatus), [`RunFoldStatus`](https://zidane.si/docs/reference/chat-pure.md#RunFoldStatus), [`runningEstimateViews`](https://zidane.si/docs/reference/chat-pure.md#runningEstimateViews), [`RunReceipt`](https://zidane.si/docs/reference/chat-pure.md#RunReceipt), [`RunReceiptCell`](https://zidane.si/docs/reference/chat-pure.md#RunReceiptCell), [`RunReceiptCellKind`](https://zidane.si/docs/reference/chat-pure.md#RunReceiptCellKind), [`runReceiptFromRun`](https://zidane.si/docs/reference/chat-pure.md#runReceiptFromRun), [`RunReceiptSegment`](https://zidane.si/docs/reference/chat-pure.md#RunReceiptSegment), [`RunReceiptSegmentKind`](https://zidane.si/docs/reference/chat-pure.md#RunReceiptSegmentKind), [`RunRetry`](https://zidane.si/docs/reference/chat-pure.md#RunRetry), [`runRetryingLabel`](https://zidane.si/docs/reference/chat-pure.md#runRetryingLabel), [`runStatusKey`](https://zidane.si/docs/reference/chat-pure.md#runStatusKey), [`runTailActivity`](https://zidane.si/docs/reference/chat-pure.md#runTailActivity), [`RunTailActivity`](https://zidane.si/docs/reference/chat-pure.md#RunTailActivity), [`searchableTurnText`](https://zidane.si/docs/reference/chat-pure.md#searchableTurnText), [`SearchExcerpt`](https://zidane.si/docs/reference/chat-pure.md#SearchExcerpt), [`searchHighlightRanges`](https://zidane.si/docs/reference/chat-pure.md#searchHighlightRanges), [`searchSessionHistory`](https://zidane.si/docs/reference/chat-pure.md#searchSessionHistory), [`searchSessionHistoryIndex`](https://zidane.si/docs/reference/chat-pure.md#searchSessionHistoryIndex), [`searchSessionHistoryIndexAsync`](https://zidane.si/docs/reference/chat-pure.md#searchSessionHistoryIndexAsync), [`searchSessionIndex`](https://zidane.si/docs/reference/chat-pure.md#searchSessionIndex), [`searchSessionIndexAsync`](https://zidane.si/docs/reference/chat-pure.md#searchSessionIndexAsync), [`SearchSessionResult`](https://zidane.si/docs/reference/chat-pure.md#SearchSessionResult), [`searchSessionTurns`](https://zidane.si/docs/reference/chat-pure.md#searchSessionTurns), [`SearchTurnResult`](https://zidane.si/docs/reference/chat-pure.md#SearchTurnResult), [`searchTurns`](https://zidane.si/docs/reference/chat-pure.md#searchTurns), [`selectableTurnIds`](https://zidane.si/docs/reference/chat-pure.md#selectableTurnIds), [`SESSION_ACCOUNT_METADATA_KEY`](https://zidane.si/docs/reference/chat-pure.md#SESSION_ACCOUNT_METADATA_KEY), [`SESSION_EVENT_STATUS_STYLE`](https://zidane.si/docs/reference/chat-pure.md#SESSION_EVENT_STATUS_STYLE), [`SESSION_ROLE_KEY`](https://zidane.si/docs/reference/chat-pure.md#SESSION_ROLE_KEY), [`SESSION_SCOPE`](https://zidane.si/docs/reference/chat-pure.md#SESSION_SCOPE), [`SessionCondition`](https://zidane.si/docs/reference/chat-pure.md#SessionCondition), [`sessionDirLabel`](https://zidane.si/docs/reference/chat-pure.md#sessionDirLabel), [`SessionEvent`](https://zidane.si/docs/reference/chat-pure.md#SessionEvent), [`sessionEventExcerpt`](https://zidane.si/docs/reference/chat-pure.md#sessionEventExcerpt), [`sessionEventLabel`](https://zidane.si/docs/reference/chat-pure.md#sessionEventLabel), [`sessionEventMeta`](https://zidane.si/docs/reference/chat-pure.md#sessionEventMeta), [`SessionEventStatus`](https://zidane.si/docs/reference/chat-pure.md#SessionEventStatus), [`SessionEventStatusStyle`](https://zidane.si/docs/reference/chat-pure.md#SessionEventStatusStyle), [`SessionEventTone`](https://zidane.si/docs/reference/chat-pure.md#SessionEventTone), [`SessionFieldName`](https://zidane.si/docs/reference/chat-pure.md#SessionFieldName), [`SessionFieldPath`](https://zidane.si/docs/reference/chat-pure.md#SessionFieldPath), [`SessionGroupBy`](https://zidane.si/docs/reference/chat-pure.md#SessionGroupBy), [`SessionGroupOrder`](https://zidane.si/docs/reference/chat-pure.md#SessionGroupOrder), [`SessionHistoryMatch`](https://zidane.si/docs/reference/chat-pure.md#SessionHistoryMatch), [`SessionKnobs`](https://zidane.si/docs/reference/chat-pure.md#SessionKnobs), [`SessionQuery`](https://zidane.si/docs/reference/chat-pure.md#SessionQuery), [`SessionQueryAll`](https://zidane.si/docs/reference/chat-pure.md#SessionQueryAll), [`SessionQueryAny`](https://zidane.si/docs/reference/chat-pure.md#SessionQueryAny), [`SessionQueryEnvironment`](https://zidane.si/docs/reference/chat-pure.md#SessionQueryEnvironment), [`SessionQueryError`](https://zidane.si/docs/reference/chat-pure.md#SessionQueryError), [`SessionQueryIssue`](https://zidane.si/docs/reference/chat-pure.md#SessionQueryIssue), [`SessionQueryNot`](https://zidane.si/docs/reference/chat-pure.md#SessionQueryNot), [`SessionSearchExcerpt`](https://zidane.si/docs/reference/chat-pure.md#SessionSearchExcerpt), [`SessionSearchIndex`](https://zidane.si/docs/reference/chat-pure.md#SessionSearchIndex), [`SessionSort`](https://zidane.si/docs/reference/chat-pure.md#SessionSort), [`sessionSpaceFilter`](https://zidane.si/docs/reference/chat-pure.md#sessionSpaceFilter), [`SETTING_TRADEOFFS`](https://zidane.si/docs/reference/chat-pure.md#SETTING_TRADEOFFS), [`settingKeywords`](https://zidane.si/docs/reference/chat-pure.md#settingKeywords), [`SETTINGS_CHOICES`](https://zidane.si/docs/reference/chat-pure.md#SETTINGS_CHOICES), [`SETTINGS_TOGGLES`](https://zidane.si/docs/reference/chat-pure.md#SETTINGS_TOGGLES), [`SettingsChoice`](https://zidane.si/docs/reference/chat-pure.md#SettingsChoice), [`SettingsToggle`](https://zidane.si/docs/reference/chat-pure.md#SettingsToggle), [`SETUP_PALETTE_ITEM`](https://zidane.si/docs/reference/chat-pure.md#SETUP_PALETTE_ITEM), [`SetupPaletteItem`](https://zidane.si/docs/reference/chat-pure.md#SetupPaletteItem), [`shortId`](https://zidane.si/docs/reference/chat-pure.md#shortId), [`shortSessionId`](https://zidane.si/docs/reference/chat-pure.md#shortSessionId), [`showsAccount`](https://zidane.si/docs/reference/chat-pure.md#showsAccount), [`sideSessionParent`](https://zidane.si/docs/reference/chat-pure.md#sideSessionParent), [`SKILLS_TRIGGER`](https://zidane.si/docs/reference/chat-pure.md#SKILLS_TRIGGER), [`Space`](https://zidane.si/docs/reference/chat-pure.md#Space), [`SPACE_METADATA_KEY`](https://zidane.si/docs/reference/chat-pure.md#SPACE_METADATA_KEY), [`SpaceIcon`](https://zidane.si/docs/reference/chat-pure.md#SpaceIcon), [`spaceIdForNewSession`](https://zidane.si/docs/reference/chat-pure.md#spaceIdForNewSession), [`spaceMetadata`](https://zidane.si/docs/reference/chat-pure.md#spaceMetadata), [`spaceModelDefault`](https://zidane.si/docs/reference/chat-pure.md#spaceModelDefault), [`SpaceModelDefault`](https://zidane.si/docs/reference/chat-pure.md#SpaceModelDefault), [`spaceOfSwitchItem`](https://zidane.si/docs/reference/chat-pure.md#spaceOfSwitchItem), [`spacesEnabled`](https://zidane.si/docs/reference/chat-pure.md#spacesEnabled), [`SpacesSettings`](https://zidane.si/docs/reference/chat-pure.md#SpacesSettings), [`spaceSwitchItems`](https://zidane.si/docs/reference/chat-pure.md#spaceSwitchItems), [`spawnLane`](https://zidane.si/docs/reference/chat-pure.md#spawnLane), [`spawnLaneResolver`](https://zidane.si/docs/reference/chat-pure.md#spawnLaneResolver), [`spawnParents`](https://zidane.si/docs/reference/chat-pure.md#spawnParents), [`SpawnTreeRow`](https://zidane.si/docs/reference/chat-pure.md#SpawnTreeRow), [`splitLines`](https://zidane.si/docs/reference/chat-pure.md#splitLines), [`splitMarkdownCodeBlocks`](https://zidane.si/docs/reference/chat-pure.md#splitMarkdownCodeBlocks), [`splitPromptSegments`](https://zidane.si/docs/reference/chat-pure.md#splitPromptSegments), [`summarizeEditPayload`](https://zidane.si/docs/reference/chat-pure.md#summarizeEditPayload), [`summarizeOutcomes`](https://zidane.si/docs/reference/chat-pure.md#summarizeOutcomes), [`TextModelCandidate`](https://zidane.si/docs/reference/chat-pure.md#TextModelCandidate), [`TextModelOptions`](https://zidane.si/docs/reference/chat-pure.md#TextModelOptions), [`TextModelPick`](https://zidane.si/docs/reference/chat-pure.md#TextModelPick), [`TextModelSource`](https://zidane.si/docs/reference/chat-pure.md#TextModelSource), [`titleFromText`](https://zidane.si/docs/reference/chat-pure.md#titleFromText), [`TODO_STATUS_GLYPHS`](https://zidane.si/docs/reference/chat-pure.md#TODO_STATUS_GLYPHS), [`toggleApplies`](https://zidane.si/docs/reference/chat-pure.md#toggleApplies), [`toggleChainEntry`](https://zidane.si/docs/reference/chat-pure.md#toggleChainEntry), [`toggleHeldOn`](https://zidane.si/docs/reference/chat-pure.md#toggleHeldOn), [`tokenize`](https://zidane.si/docs/reference/chat-pure.md#tokenize), [`TOOL_DISPLAY`](https://zidane.si/docs/reference/chat-pure.md#TOOL_DISPLAY), [`ToolDisplayMeta`](https://zidane.si/docs/reference/chat-pure.md#ToolDisplayMeta), [`ToolFormatLine`](https://zidane.si/docs/reference/chat-pure.md#ToolFormatLine), [`toolInputCode`](https://zidane.si/docs/reference/chat-pure.md#toolInputCode), [`TRANSCRIPT_PRESET_KEYS`](https://zidane.si/docs/reference/chat-pure.md#TRANSCRIPT_PRESET_KEYS), [`TRANSCRIPT_PRESET_PALETTE_ID`](https://zidane.si/docs/reference/chat-pure.md#TRANSCRIPT_PRESET_PALETTE_ID), [`TRANSCRIPT_PRESETS`](https://zidane.si/docs/reference/chat-pure.md#TRANSCRIPT_PRESETS), [`TRANSCRIPT_TRIM_TO_EVENTS`](https://zidane.si/docs/reference/chat-pure.md#TRANSCRIPT_TRIM_TO_EVENTS), [`transcriptPreset`](https://zidane.si/docs/reference/chat-pure.md#transcriptPreset), [`TranscriptPreset`](https://zidane.si/docs/reference/chat-pure.md#TranscriptPreset), [`TranscriptPresetId`](https://zidane.si/docs/reference/chat-pure.md#TranscriptPresetId), [`TranscriptPresetKey`](https://zidane.si/docs/reference/chat-pure.md#TranscriptPresetKey), [`transcriptPresetNotice`](https://zidane.si/docs/reference/chat-pure.md#transcriptPresetNotice), [`transcriptPresetOf`](https://zidane.si/docs/reference/chat-pure.md#transcriptPresetOf), [`transcriptPresetOfPaletteItem`](https://zidane.si/docs/reference/chat-pure.md#transcriptPresetOfPaletteItem), [`transcriptPresetPaletteItem`](https://zidane.si/docs/reference/chat-pure.md#transcriptPresetPaletteItem), [`transcriptPresetPalettePicker`](https://zidane.si/docs/reference/chat-pure.md#transcriptPresetPalettePicker), [`TranscriptPresetSettings`](https://zidane.si/docs/reference/chat-pure.md#TranscriptPresetSettings), [`truncateTitle`](https://zidane.si/docs/reference/chat-pure.md#truncateTitle), [`truncateTurnsAt`](https://zidane.si/docs/reference/chat-pure.md#truncateTurnsAt), [`turnAsText`](https://zidane.si/docs/reference/chat-pure.md#turnAsText), [`turnContextSize`](https://zidane.si/docs/reference/chat-pure.md#turnContextSize), [`turnSelectionOwnership`](https://zidane.si/docs/reference/chat-pure.md#turnSelectionOwnership), [`TUTORIAL_TOPICS`](https://zidane.si/docs/reference/chat-pure.md#TUTORIAL_TOPICS), [`tutorialOnServer`](https://zidane.si/docs/reference/chat-pure.md#tutorialOnServer), [`TUTORIALS`](https://zidane.si/docs/reference/chat-pure.md#TUTORIALS), [`TutorialStep`](https://zidane.si/docs/reference/chat-pure.md#TutorialStep), [`TutorialTopic`](https://zidane.si/docs/reference/chat-pure.md#TutorialTopic), [`TutorialWords`](https://zidane.si/docs/reference/chat-pure.md#TutorialWords), [`uniqueChatIdsFromReferences`](https://zidane.si/docs/reference/chat-pure.md#uniqueChatIdsFromReferences), [`uniqueFilesFromReferences`](https://zidane.si/docs/reference/chat-pure.md#uniqueFilesFromReferences), [`uniqueSkillNamesFromReferences`](https://zidane.si/docs/reference/chat-pure.md#uniqueSkillNamesFromReferences), [`WORKER_REPORT_FRAGMENT`](https://zidane.si/docs/reference/chat-pure.md#WORKER_REPORT_FRAGMENT)
- [zidane/extensions](https://zidane.si/docs/reference/extensions.md): [`AccessScope`](https://zidane.si/docs/reference/extensions.md#AccessScope), [`ActionSetting`](https://zidane.si/docs/reference/extensions.md#ActionSetting), [`ActionSettingContext`](https://zidane.si/docs/reference/extensions.md#ActionSettingContext), [`ActionSettingResult`](https://zidane.si/docs/reference/extensions.md#ActionSettingResult), [`ActionSettingStatus`](https://zidane.si/docs/reference/extensions.md#ActionSettingStatus), [`ActionSettingStatusContext`](https://zidane.si/docs/reference/extensions.md#ActionSettingStatusContext), [`ActionSettingValue`](https://zidane.si/docs/reference/extensions.md#ActionSettingValue), [`AddFn`](https://zidane.si/docs/reference/extensions.md#AddFn), [`AgentEditor`](https://zidane.si/docs/reference/extensions.md#AgentEditor), [`AgentsDomain`](https://zidane.si/docs/reference/extensions.md#AgentsDomain), [`AppInfo`](https://zidane.si/docs/reference/extensions.md#AppInfo), [`ArtifactsDomain`](https://zidane.si/docs/reference/extensions.md#ArtifactsDomain), [`BadgeAction`](https://zidane.si/docs/reference/extensions.md#BadgeAction), [`BadgeActionEffect`](https://zidane.si/docs/reference/extensions.md#BadgeActionEffect), [`BadgeActionsMatch`](https://zidane.si/docs/reference/extensions.md#BadgeActionsMatch), [`BadgeActionSpec`](https://zidane.si/docs/reference/extensions.md#BadgeActionSpec), [`BadgeActionWire`](https://zidane.si/docs/reference/extensions.md#BadgeActionWire), [`BadgeTarget`](https://zidane.si/docs/reference/extensions.md#BadgeTarget), [`BadgeTargetKind`](https://zidane.si/docs/reference/extensions.md#BadgeTargetKind), [`Bindable`](https://zidane.si/docs/reference/extensions.md#Bindable), [`BooleanSetting`](https://zidane.si/docs/reference/extensions.md#BooleanSetting), [`buildExtensionPreset`](https://zidane.si/docs/reference/extensions.md#buildExtensionPreset), [`buildExtensionRegistry`](https://zidane.si/docs/reference/extensions.md#buildExtensionRegistry), [`BuildExtensionRegistryOptions`](https://zidane.si/docs/reference/extensions.md#BuildExtensionRegistryOptions), [`CatalogDomain`](https://zidane.si/docs/reference/extensions.md#CatalogDomain), [`CatalogEditor`](https://zidane.si/docs/reference/extensions.md#CatalogEditor), [`Cleanup`](https://zidane.si/docs/reference/extensions.md#Cleanup), [`CommandListing`](https://zidane.si/docs/reference/extensions.md#CommandListing), [`CommandsDomain`](https://zidane.si/docs/reference/extensions.md#CommandsDomain), [`CommandSource`](https://zidane.si/docs/reference/extensions.md#CommandSource), [`CommandSpec`](https://zidane.si/docs/reference/extensions.md#CommandSpec), [`ComponentRef`](https://zidane.si/docs/reference/extensions.md#ComponentRef), [`ComponentWire`](https://zidane.si/docs/reference/extensions.md#ComponentWire), [`composeExtensionPreset`](https://zidane.si/docs/reference/extensions.md#composeExtensionPreset), [`ComposerDomain`](https://zidane.si/docs/reference/extensions.md#ComposerDomain), [`Connection`](https://zidane.si/docs/reference/extensions.md#Connection), [`ContextSection`](https://zidane.si/docs/reference/extensions.md#ContextSection), [`ContextSectionItem`](https://zidane.si/docs/reference/extensions.md#ContextSectionItem), [`ContextSectionWire`](https://zidane.si/docs/reference/extensions.md#ContextSectionWire), [`Contract`](https://zidane.si/docs/reference/extensions.md#Contract), [`ContractsDomain`](https://zidane.si/docs/reference/extensions.md#ContractsDomain), [`ContractToken`](https://zidane.si/docs/reference/extensions.md#ContractToken), [`Credential`](https://zidane.si/docs/reference/extensions.md#Credential), [`defaultExtensionScanPaths`](https://zidane.si/docs/reference/extensions.md#defaultExtensionScanPaths), [`defineExtension`](https://zidane.si/docs/reference/extensions.md#defineExtension), [`DesktopDomain`](https://zidane.si/docs/reference/extensions.md#DesktopDomain), [`DialogOptions`](https://zidane.si/docs/reference/extensions.md#DialogOptions), [`DialogRequest`](https://zidane.si/docs/reference/extensions.md#DialogRequest), [`DialogTimeoutError`](https://zidane.si/docs/reference/extensions.md#DialogTimeoutError), [`discoverExtensions`](https://zidane.si/docs/reference/extensions.md#discoverExtensions), [`Disposable`](https://zidane.si/docs/reference/extensions.md#Disposable), [`DisposedError`](https://zidane.si/docs/reference/extensions.md#DisposedError), [`disposeExtensionRegistry`](https://zidane.si/docs/reference/extensions.md#disposeExtensionRegistry), [`EMPTY_EXTENSION_REGISTRY`](https://zidane.si/docs/reference/extensions.md#EMPTY_EXTENSION_REGISTRY), [`EnumSetting`](https://zidane.si/docs/reference/extensions.md#EnumSetting), [`EXTENSION_API_VERSION`](https://zidane.si/docs/reference/extensions.md#EXTENSION_API_VERSION), [`ExtensionApiVersion`](https://zidane.si/docs/reference/extensions.md#ExtensionApiVersion), [`ExtensionApprovalDecision`](https://zidane.si/docs/reference/extensions.md#ExtensionApprovalDecision), [`ExtensionArtifactDetection`](https://zidane.si/docs/reference/extensions.md#ExtensionArtifactDetection), [`ExtensionArtifactKindRegistration`](https://zidane.si/docs/reference/extensions.md#ExtensionArtifactKindRegistration), [`ExtensionArtifactQuery`](https://zidane.si/docs/reference/extensions.md#ExtensionArtifactQuery), [`ExtensionArtifactRow`](https://zidane.si/docs/reference/extensions.md#ExtensionArtifactRow), [`ExtensionArtifactRowFact`](https://zidane.si/docs/reference/extensions.md#ExtensionArtifactRowFact), [`ExtensionBadgeActions`](https://zidane.si/docs/reference/extensions.md#ExtensionBadgeActions), [`ExtensionBadgeColor`](https://zidane.si/docs/reference/extensions.md#ExtensionBadgeColor), [`ExtensionBadgeIcon`](https://zidane.si/docs/reference/extensions.md#ExtensionBadgeIcon), [`ExtensionBadgeTone`](https://zidane.si/docs/reference/extensions.md#ExtensionBadgeTone), [`ExtensionBehaviorTransform`](https://zidane.si/docs/reference/extensions.md#ExtensionBehaviorTransform), [`ExtensionContext`](https://zidane.si/docs/reference/extensions.md#ExtensionContext), [`ExtensionContextPanelSection`](https://zidane.si/docs/reference/extensions.md#ExtensionContextPanelSection), [`ExtensionContributions`](https://zidane.si/docs/reference/extensions.md#ExtensionContributions), [`ExtensionCreateSessionParams`](https://zidane.si/docs/reference/extensions.md#ExtensionCreateSessionParams), [`ExtensionDefinition`](https://zidane.si/docs/reference/extensions.md#ExtensionDefinition), [`ExtensionFooterHint`](https://zidane.si/docs/reference/extensions.md#ExtensionFooterHint), [`ExtensionHostKind`](https://zidane.si/docs/reference/extensions.md#ExtensionHostKind), [`ExtensionInfo`](https://zidane.si/docs/reference/extensions.md#ExtensionInfo), [`ExtensionInstallOrigin`](https://zidane.si/docs/reference/extensions.md#ExtensionInstallOrigin), [`ExtensionKeybinding`](https://zidane.si/docs/reference/extensions.md#ExtensionKeybinding), [`ExtensionKeybindingDispatchContext`](https://zidane.si/docs/reference/extensions.md#ExtensionKeybindingDispatchContext), [`ExtensionManifest`](https://zidane.si/docs/reference/extensions.md#ExtensionManifest), [`ExtensionMcpConfigTransform`](https://zidane.si/docs/reference/extensions.md#ExtensionMcpConfigTransform), [`ExtensionOrderedContribution`](https://zidane.si/docs/reference/extensions.md#ExtensionOrderedContribution), [`ExtensionPanel`](https://zidane.si/docs/reference/extensions.md#ExtensionPanel), [`ExtensionPaths`](https://zidane.si/docs/reference/extensions.md#ExtensionPaths), [`ExtensionReferenceCard`](https://zidane.si/docs/reference/extensions.md#ExtensionReferenceCard), [`ExtensionReferenceCardFact`](https://zidane.si/docs/reference/extensions.md#ExtensionReferenceCardFact), [`ExtensionReferencePattern`](https://zidane.si/docs/reference/extensions.md#ExtensionReferencePattern), [`ExtensionReferenceProvider`](https://zidane.si/docs/reference/extensions.md#ExtensionReferenceProvider), [`ExtensionReferenceSurface`](https://zidane.si/docs/reference/extensions.md#ExtensionReferenceSurface), [`ExtensionReferenceView`](https://zidane.si/docs/reference/extensions.md#ExtensionReferenceView), [`ExtensionRegistry`](https://zidane.si/docs/reference/extensions.md#ExtensionRegistry), [`ExtensionRuntimeEnv`](https://zidane.si/docs/reference/extensions.md#ExtensionRuntimeEnv), [`ExtensionRunTransform`](https://zidane.si/docs/reference/extensions.md#ExtensionRunTransform), [`ExtensionSkillPolicy`](https://zidane.si/docs/reference/extensions.md#ExtensionSkillPolicy), [`ExtensionSource`](https://zidane.si/docs/reference/extensions.md#ExtensionSource), [`ExtensionToolCatalogTransform`](https://zidane.si/docs/reference/extensions.md#ExtensionToolCatalogTransform), [`ExtensionToolResultMetadataRegistration`](https://zidane.si/docs/reference/extensions.md#ExtensionToolResultMetadataRegistration), [`filterExtensionRegistry`](https://zidane.si/docs/reference/extensions.md#filterExtensionRegistry), [`GeneratedImage`](https://zidane.si/docs/reference/extensions.md#GeneratedImage), [`GenerateDomain`](https://zidane.si/docs/reference/extensions.md#GenerateDomain), [`GenerateImageOptions`](https://zidane.si/docs/reference/extensions.md#GenerateImageOptions), [`Handle`](https://zidane.si/docs/reference/extensions.md#Handle), [`HookEvents`](https://zidane.si/docs/reference/extensions.md#HookEvents), [`HooksDomain`](https://zidane.si/docs/reference/extensions.md#HooksDomain), [`HttpRequestInput`](https://zidane.si/docs/reference/extensions.md#HttpRequestInput), [`HttpResponseInput`](https://zidane.si/docs/reference/extensions.md#HttpResponseInput), [`Infer`](https://zidane.si/docs/reference/extensions.md#Infer), [`inlineLoadedExtension`](https://zidane.si/docs/reference/extensions.md#inlineLoadedExtension), [`InstructionsDomain`](https://zidane.si/docs/reference/extensions.md#InstructionsDomain), [`IntegrationCustomMethod`](https://zidane.si/docs/reference/extensions.md#IntegrationCustomMethod), [`IntegrationCustomResult`](https://zidane.si/docs/reference/extensions.md#IntegrationCustomResult), [`IntegrationFlowIO`](https://zidane.si/docs/reference/extensions.md#IntegrationFlowIO), [`IntegrationHandle`](https://zidane.si/docs/reference/extensions.md#IntegrationHandle), [`IntegrationMethod`](https://zidane.si/docs/reference/extensions.md#IntegrationMethod), [`integrationMethodFromOAuthProvider`](https://zidane.si/docs/reference/extensions.md#integrationMethodFromOAuthProvider), [`IntegrationsDomain`](https://zidane.si/docs/reference/extensions.md#IntegrationsDomain), [`IntegrationSpec`](https://zidane.si/docs/reference/extensions.md#IntegrationSpec), [`IntegrationSpecPatch`](https://zidane.si/docs/reference/extensions.md#IntegrationSpecPatch), [`InterceptOptions`](https://zidane.si/docs/reference/extensions.md#InterceptOptions), [`InterceptorHandler`](https://zidane.si/docs/reference/extensions.md#InterceptorHandler), [`Interceptors`](https://zidane.si/docs/reference/extensions.md#Interceptors), [`ItemOrigin`](https://zidane.si/docs/reference/extensions.md#ItemOrigin), [`Json`](https://zidane.si/docs/reference/extensions.md#Json), [`JsonSchema`](https://zidane.si/docs/reference/extensions.md#JsonSchema), [`KeymapDomain`](https://zidane.si/docs/reference/extensions.md#KeymapDomain), [`KeymapEntry`](https://zidane.si/docs/reference/extensions.md#KeymapEntry), [`KeyScope`](https://zidane.si/docs/reference/extensions.md#KeyScope), [`KeyValue`](https://zidane.si/docs/reference/extensions.md#KeyValue), [`LinkHandler`](https://zidane.si/docs/reference/extensions.md#LinkHandler), [`LinkHandlerItem`](https://zidane.si/docs/reference/extensions.md#LinkHandlerItem), [`LinkSource`](https://zidane.si/docs/reference/extensions.md#LinkSource), [`Live`](https://zidane.si/docs/reference/extensions.md#Live), [`LiveAccessor`](https://zidane.si/docs/reference/extensions.md#LiveAccessor), [`LoadedExtension`](https://zidane.si/docs/reference/extensions.md#LoadedExtension), [`LoadedExtensionSource`](https://zidane.si/docs/reference/extensions.md#LoadedExtensionSource), [`McpDomain`](https://zidane.si/docs/reference/extensions.md#McpDomain), [`McpToolDescriptor`](https://zidane.si/docs/reference/extensions.md#McpToolDescriptor), [`Menu`](https://zidane.si/docs/reference/extensions.md#Menu), [`MenuItem`](https://zidane.si/docs/reference/extensions.md#MenuItem), [`MenuWire`](https://zidane.si/docs/reference/extensions.md#MenuWire), [`ModelEditor`](https://zidane.si/docs/reference/extensions.md#ModelEditor), [`ModelsDomain`](https://zidane.si/docs/reference/extensions.md#ModelsDomain), [`ModelSetting`](https://zidane.si/docs/reference/extensions.md#ModelSetting), [`NamedTool`](https://zidane.si/docs/reference/extensions.md#NamedTool), [`NoUiError`](https://zidane.si/docs/reference/extensions.md#NoUiError), [`NumberSetting`](https://zidane.si/docs/reference/extensions.md#NumberSetting), [`OnOptions`](https://zidane.si/docs/reference/extensions.md#OnOptions), [`PaletteProviderSpec`](https://zidane.si/docs/reference/extensions.md#PaletteProviderSpec), [`Panel`](https://zidane.si/docs/reference/extensions.md#Panel), [`PanelAction`](https://zidane.si/docs/reference/extensions.md#PanelAction), [`PanelBarsItem`](https://zidane.si/docs/reference/extensions.md#PanelBarsItem), [`PanelBarsPoint`](https://zidane.si/docs/reference/extensions.md#PanelBarsPoint), [`PanelBarsSeries`](https://zidane.si/docs/reference/extensions.md#PanelBarsSeries), [`PanelContent`](https://zidane.si/docs/reference/extensions.md#PanelContent), [`PanelContentItem`](https://zidane.si/docs/reference/extensions.md#PanelContentItem), [`PanelControl`](https://zidane.si/docs/reference/extensions.md#PanelControl), [`PanelControlOption`](https://zidane.si/docs/reference/extensions.md#PanelControlOption), [`PanelCustomItem`](https://zidane.si/docs/reference/extensions.md#PanelCustomItem), [`PanelFocus`](https://zidane.si/docs/reference/extensions.md#PanelFocus), [`PanelHeatmapDay`](https://zidane.si/docs/reference/extensions.md#PanelHeatmapDay), [`PanelHeatmapItem`](https://zidane.si/docs/reference/extensions.md#PanelHeatmapItem), [`PanelHomeLayout`](https://zidane.si/docs/reference/extensions.md#PanelHomeLayout), [`PanelIcon`](https://zidane.si/docs/reference/extensions.md#PanelIcon), [`PanelItem`](https://zidane.si/docs/reference/extensions.md#PanelItem), [`PanelKeyValueItem`](https://zidane.si/docs/reference/extensions.md#PanelKeyValueItem), [`PanelKeyValueRow`](https://zidane.si/docs/reference/extensions.md#PanelKeyValueRow), [`PanelLoadContext`](https://zidane.si/docs/reference/extensions.md#PanelLoadContext), [`PanelLoadScope`](https://zidane.si/docs/reference/extensions.md#PanelLoadScope), [`PanelMarkdownItem`](https://zidane.si/docs/reference/extensions.md#PanelMarkdownItem), [`PanelMore`](https://zidane.si/docs/reference/extensions.md#PanelMore), [`PanelRowAction`](https://zidane.si/docs/reference/extensions.md#PanelRowAction), [`PanelRowItem`](https://zidane.si/docs/reference/extensions.md#PanelRowItem), [`PanelSearch`](https://zidane.si/docs/reference/extensions.md#PanelSearch), [`PanelSection`](https://zidane.si/docs/reference/extensions.md#PanelSection), [`PanelStartSession`](https://zidane.si/docs/reference/extensions.md#PanelStartSession), [`PanelStat`](https://zidane.si/docs/reference/extensions.md#PanelStat), [`PanelStatsItem`](https://zidane.si/docs/reference/extensions.md#PanelStatsItem), [`PanelSurface`](https://zidane.si/docs/reference/extensions.md#PanelSurface), [`PanelView`](https://zidane.si/docs/reference/extensions.md#PanelView), [`panelViewValue`](https://zidane.si/docs/reference/extensions.md#panelViewValue), [`panelViewValues`](https://zidane.si/docs/reference/extensions.md#panelViewValues), [`PanelWire`](https://zidane.si/docs/reference/extensions.md#PanelWire), [`partitionExtensionCatalog`](https://zidane.si/docs/reference/extensions.md#partitionExtensionCatalog), [`PluginsDomain`](https://zidane.si/docs/reference/extensions.md#PluginsDomain), [`PromptInput`](https://zidane.si/docs/reference/extensions.md#PromptInput), [`ProviderEditor`](https://zidane.si/docs/reference/extensions.md#ProviderEditor), [`ProvidersDomain`](https://zidane.si/docs/reference/extensions.md#ProvidersDomain), [`Readable`](https://zidane.si/docs/reference/extensions.md#Readable), [`Reference`](https://zidane.si/docs/reference/extensions.md#Reference), [`Registry`](https://zidane.si/docs/reference/extensions.md#Registry), [`RegistryToken`](https://zidane.si/docs/reference/extensions.md#RegistryToken), [`Renderer`](https://zidane.si/docs/reference/extensions.md#Renderer), [`RendererItem`](https://zidane.si/docs/reference/extensions.md#RendererItem), [`RendererTarget`](https://zidane.si/docs/reference/extensions.md#RendererTarget), [`RendererWire`](https://zidane.si/docs/reference/extensions.md#RendererWire), [`Rpc`](https://zidane.si/docs/reference/extensions.md#Rpc), [`RpcCallContext`](https://zidane.si/docs/reference/extensions.md#RpcCallContext), [`RpcCallError`](https://zidane.si/docs/reference/extensions.md#RpcCallError), [`RpcClient`](https://zidane.si/docs/reference/extensions.md#RpcClient), [`RpcContract`](https://zidane.si/docs/reference/extensions.md#RpcContract), [`RpcDomain`](https://zidane.si/docs/reference/extensions.md#RpcDomain), [`RpcError`](https://zidane.si/docs/reference/extensions.md#RpcError), [`RpcEvent`](https://zidane.si/docs/reference/extensions.md#RpcEvent), [`RpcEvents`](https://zidane.si/docs/reference/extensions.md#RpcEvents), [`RpcEventSpec`](https://zidane.si/docs/reference/extensions.md#RpcEventSpec), [`RpcImplementation`](https://zidane.si/docs/reference/extensions.md#RpcImplementation), [`RpcMethodSpec`](https://zidane.si/docs/reference/extensions.md#RpcMethodSpec), [`RpcRegistration`](https://zidane.si/docs/reference/extensions.md#RpcRegistration), [`RunSettledEvent`](https://zidane.si/docs/reference/extensions.md#RunSettledEvent), [`scanExtensionDirectories`](https://zidane.si/docs/reference/extensions.md#scanExtensionDirectories), [`ScheduleFireEvent`](https://zidane.si/docs/reference/extensions.md#ScheduleFireEvent), [`SchedulesDomain`](https://zidane.si/docs/reference/extensions.md#SchedulesDomain), [`Schema`](https://zidane.si/docs/reference/extensions.md#Schema), [`ScreenItem`](https://zidane.si/docs/reference/extensions.md#ScreenItem), [`ScreenWire`](https://zidane.si/docs/reference/extensions.md#ScreenWire), [`SelectOption`](https://zidane.si/docs/reference/extensions.md#SelectOption), [`SessionFacetsDomain`](https://zidane.si/docs/reference/extensions.md#SessionFacetsDomain), [`SessionFacetSpec`](https://zidane.si/docs/reference/extensions.md#SessionFacetSpec), [`SessionFacetTrigger`](https://zidane.si/docs/reference/extensions.md#SessionFacetTrigger), [`SessionFields`](https://zidane.si/docs/reference/extensions.md#SessionFields), [`SessionInfo`](https://zidane.si/docs/reference/extensions.md#SessionInfo), [`SessionListPage`](https://zidane.si/docs/reference/extensions.md#SessionListPage), [`SessionListQuery`](https://zidane.si/docs/reference/extensions.md#SessionListQuery), [`SessionListRow`](https://zidane.si/docs/reference/extensions.md#SessionListRow), [`SessionListStatus`](https://zidane.si/docs/reference/extensions.md#SessionListStatus), [`SessionsDomain`](https://zidane.si/docs/reference/extensions.md#SessionsDomain), [`SessionSnapshot`](https://zidane.si/docs/reference/extensions.md#SessionSnapshot), [`Setting`](https://zidane.si/docs/reference/extensions.md#Setting), [`SettingApply`](https://zidane.si/docs/reference/extensions.md#SettingApply), [`SettingBase`](https://zidane.si/docs/reference/extensions.md#SettingBase), [`SettingScope`](https://zidane.si/docs/reference/extensions.md#SettingScope), [`SettingsDomain`](https://zidane.si/docs/reference/extensions.md#SettingsDomain), [`SettingsPage`](https://zidane.si/docs/reference/extensions.md#SettingsPage), [`SettingsPageItem`](https://zidane.si/docs/reference/extensions.md#SettingsPageItem), [`SettingsPageSpec`](https://zidane.si/docs/reference/extensions.md#SettingsPageSpec), [`SettingsPageWire`](https://zidane.si/docs/reference/extensions.md#SettingsPageWire), [`SettingsSchema`](https://zidane.si/docs/reference/extensions.md#SettingsSchema), [`SettingValue`](https://zidane.si/docs/reference/extensions.md#SettingValue), [`SidebarBadge`](https://zidane.si/docs/reference/extensions.md#SidebarBadge), [`SidebarChip`](https://zidane.si/docs/reference/extensions.md#SidebarChip), [`SidebarDecoration`](https://zidane.si/docs/reference/extensions.md#SidebarDecoration), [`SidebarDecorationItem`](https://zidane.si/docs/reference/extensions.md#SidebarDecorationItem), [`SidebarDecorationWire`](https://zidane.si/docs/reference/extensions.md#SidebarDecorationWire), [`SidebarFilter`](https://zidane.si/docs/reference/extensions.md#SidebarFilter), [`SidebarFilterItem`](https://zidane.si/docs/reference/extensions.md#SidebarFilterItem), [`SidebarFilterOption`](https://zidane.si/docs/reference/extensions.md#SidebarFilterOption), [`SidebarFilterWire`](https://zidane.si/docs/reference/extensions.md#SidebarFilterWire), [`SidebarGroup`](https://zidane.si/docs/reference/extensions.md#SidebarGroup), [`SidebarGroupItem`](https://zidane.si/docs/reference/extensions.md#SidebarGroupItem), [`SidebarGroupWire`](https://zidane.si/docs/reference/extensions.md#SidebarGroupWire), [`SidebarRow`](https://zidane.si/docs/reference/extensions.md#SidebarRow), [`SidebarRowAction`](https://zidane.si/docs/reference/extensions.md#SidebarRowAction), [`SidebarRowActionWire`](https://zidane.si/docs/reference/extensions.md#SidebarRowActionWire), [`SidebarRowItem`](https://zidane.si/docs/reference/extensions.md#SidebarRowItem), [`SidebarRowOpen`](https://zidane.si/docs/reference/extensions.md#SidebarRowOpen), [`SidebarRowStatus`](https://zidane.si/docs/reference/extensions.md#SidebarRowStatus), [`SidebarRowWire`](https://zidane.si/docs/reference/extensions.md#SidebarRowWire), [`SidebarSort`](https://zidane.si/docs/reference/extensions.md#SidebarSort), [`SidebarSortItem`](https://zidane.si/docs/reference/extensions.md#SidebarSortItem), [`SidebarSortWire`](https://zidane.si/docs/reference/extensions.md#SidebarSortWire), [`SidebarText`](https://zidane.si/docs/reference/extensions.md#SidebarText), [`SkillsDomain`](https://zidane.si/docs/reference/extensions.md#SkillsDomain), [`Slot`](https://zidane.si/docs/reference/extensions.md#Slot), [`SlotItem`](https://zidane.si/docs/reference/extensions.md#SlotItem), [`SlotName`](https://zidane.si/docs/reference/extensions.md#SlotName), [`SlotWire`](https://zidane.si/docs/reference/extensions.md#SlotWire), [`SpaceItem`](https://zidane.si/docs/reference/extensions.md#SpaceItem), [`SpaceNewSession`](https://zidane.si/docs/reference/extensions.md#SpaceNewSession), [`SpaceWire`](https://zidane.si/docs/reference/extensions.md#SpaceWire), [`StorageDomain`](https://zidane.si/docs/reference/extensions.md#StorageDomain), [`StoragePathScope`](https://zidane.si/docs/reference/extensions.md#StoragePathScope), [`StorageScope`](https://zidane.si/docs/reference/extensions.md#StorageScope), [`Store`](https://zidane.si/docs/reference/extensions.md#Store), [`StoreOptions`](https://zidane.si/docs/reference/extensions.md#StoreOptions), [`StringSetting`](https://zidane.si/docs/reference/extensions.md#StringSetting), [`SyncInterceptorName`](https://zidane.si/docs/reference/extensions.md#SyncInterceptorName), [`ThemeToken`](https://zidane.si/docs/reference/extensions.md#ThemeToken), [`ToolCall`](https://zidane.si/docs/reference/extensions.md#ToolCall), [`ToolCallDecision`](https://zidane.si/docs/reference/extensions.md#ToolCallDecision), [`ToolCallResult`](https://zidane.si/docs/reference/extensions.md#ToolCallResult), [`ToolEditor`](https://zidane.si/docs/reference/extensions.md#ToolEditor), [`ToolMetadataNamespace`](https://zidane.si/docs/reference/extensions.md#ToolMetadataNamespace), [`ToolResultDecision`](https://zidane.si/docs/reference/extensions.md#ToolResultDecision), [`ToolsDomain`](https://zidane.si/docs/reference/extensions.md#ToolsDomain), [`Transform`](https://zidane.si/docs/reference/extensions.md#Transform), [`TuiContributions`](https://zidane.si/docs/reference/extensions.md#TuiContributions), [`UiDomain`](https://zidane.si/docs/reference/extensions.md#UiDomain), [`UiEntry`](https://zidane.si/docs/reference/extensions.md#UiEntry), [`UiToken`](https://zidane.si/docs/reference/extensions.md#UiToken), [`ValueKeys`](https://zidane.si/docs/reference/extensions.md#ValueKeys), [`Webview`](https://zidane.si/docs/reference/extensions.md#Webview), [`WebviewItem`](https://zidane.si/docs/reference/extensions.md#WebviewItem), [`WebviewWire`](https://zidane.si/docs/reference/extensions.md#WebviewWire), [`WireSettings`](https://zidane.si/docs/reference/extensions.md#WireSettings), [`WireSettingsExtension`](https://zidane.si/docs/reference/extensions.md#WireSettingsExtension), [`WorkspaceDomain`](https://zidane.si/docs/reference/extensions.md#WorkspaceDomain)
- [zidane](https://zidane.si/docs/reference/index.md): [`AUTO_COMPACT_MIN_GROWTH_FRACTION`](https://zidane.si/docs/reference/index.md#AUTO_COMPACT_MIN_GROWTH_FRACTION), [`AutoCompactDecision`](https://zidane.si/docs/reference/index.md#AutoCompactDecision), [`AutoCompactInput`](https://zidane.si/docs/reference/index.md#AutoCompactInput), [`effectiveContextWindow`](https://zidane.si/docs/reference/index.md#effectiveContextWindow), [`OUTPUT_RESERVE_TOKENS`](https://zidane.si/docs/reference/index.md#OUTPUT_RESERVE_TOKENS), [`Schedule`](https://zidane.si/docs/reference/index.md#Schedule), [`ScheduleAction`](https://zidane.si/docs/reference/index.md#ScheduleAction), [`ScheduleCatchUp`](https://zidane.si/docs/reference/index.md#ScheduleCatchUp), [`ScheduleCreator`](https://zidane.si/docs/reference/index.md#ScheduleCreator), [`ScheduleFire`](https://zidane.si/docs/reference/index.md#ScheduleFire), [`ScheduleFireOutcome`](https://zidane.si/docs/reference/index.md#ScheduleFireOutcome), [`ScheduleInput`](https://zidane.si/docs/reference/index.md#ScheduleInput), [`ScheduleListFilter`](https://zidane.si/docs/reference/index.md#ScheduleListFilter), [`ScheduleNewSession`](https://zidane.si/docs/reference/index.md#ScheduleNewSession), [`SchedulePatch`](https://zidane.si/docs/reference/index.md#SchedulePatch), [`ScheduleTrigger`](https://zidane.si/docs/reference/index.md#ScheduleTrigger), [`shouldAutoCompact`](https://zidane.si/docs/reference/index.md#shouldAutoCompact)
- [zidane/contexts](https://zidane.si/docs/reference/contexts.md): [`EntryKind`](https://zidane.si/docs/reference/contexts.md#EntryKind), [`EntryPort`](https://zidane.si/docs/reference/contexts.md#EntryPort)
- .bun: `OAuthPrompt`
- [zidane/session](https://zidane.si/docs/reference/session.md): [`SessionArtifact`](https://zidane.si/docs/reference/session.md#SessionArtifact), [`SessionArtifactInput`](https://zidane.si/docs/reference/session.md#SessionArtifactInput)
