mindwire 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,909 @@
1
+ /**
2
+ * Wire types for the mindwire daemon, mirrored 1:1 from the Go structs in
3
+ * `daemon/internal/agent` and `daemon/internal/session`. The daemon normalizes every
4
+ * agent's native output into these shapes, so the client renders one protocol regardless
5
+ * of which harness (Claude Code, Codex, Copilot CLI, opencode, …) actually ran.
6
+ *
7
+ * Field names and optionality match the JSON tags exactly. Values the Go side leaves as
8
+ * open-ended strings (e.g. auth-flow `status`) are typed as a union with a `(string & {})`
9
+ * escape hatch so a newer daemon adding a variant does not break older SDK builds.
10
+ */
11
+ /** One kind of unified streaming event. Every adapter normalizes native output into these. */
12
+ export type EventType = "session" | "text" | "thinking" | "tool_use" | "tool_result" | "result" | "error" | "status" | "interaction" | "compaction" | "continuation";
13
+ export interface ToolEvent {
14
+ id?: string;
15
+ name?: string;
16
+ input?: unknown;
17
+ output?: string;
18
+ isError?: boolean;
19
+ /** Deep-normalized view of the tool call; rides alongside the raw name/input/output. */
20
+ action?: ToolAction;
21
+ }
22
+ /** Canonical, agent-independent classification of a tool call. */
23
+ export type ToolKind = "file_edit" | "file_read" | "shell" | "search" | "web_search" | "web_fetch" | "mcp" | "other" | (string & {});
24
+ /**
25
+ * Normalized view of a tool call. Only the sub-object matching `kind` is populated; the rest are
26
+ * absent. Rides alongside the untouched raw input/output, so a client can render the diff / command /
27
+ * changes without knowing any one agent's private tool vocabulary (Claude's Edit/Write/Bash vs Codex's
28
+ * apply_patch/shell).
29
+ */
30
+ export interface ToolAction {
31
+ kind: ToolKind;
32
+ /** Short human label (e.g. the command or the path). */
33
+ title?: string;
34
+ /** Present for `kind: "file_edit"`. */
35
+ files?: FileChange[];
36
+ /** Present for `kind: "shell"`. */
37
+ shell?: ShellCommand;
38
+ /** Present for `kind: "search"`. */
39
+ search?: SearchQuery;
40
+ /** Present for `kind: "web_search"` or `"web_fetch"`. */
41
+ web?: WebSearch;
42
+ /** Present for `kind: "mcp"`. */
43
+ mcp?: MCPCall;
44
+ }
45
+ /** One file touched by a `file_edit` action. */
46
+ export interface FileChange {
47
+ path: string;
48
+ op?: "create" | "edit" | "delete" | (string & {});
49
+ /**
50
+ * Best-effort unified diff; absent when the agent doesn't supply enough to compute one (e.g. Codex's
51
+ * live file_change reports path+op only). Absent means "not supplied", never "no change".
52
+ */
53
+ diff?: string;
54
+ /** Best-effort; absent when the agent doesn't supply it. */
55
+ oldText?: string;
56
+ /** Best-effort; absent when the agent doesn't supply it. */
57
+ newText?: string;
58
+ }
59
+ /** A shell action. `stdout` is the combined/aggregated output as the agent gave it. */
60
+ export interface ShellCommand {
61
+ command?: string;
62
+ cwd?: string;
63
+ stdout?: string;
64
+ /**
65
+ * Best-effort; absent when the agent doesn't report it (Claude's Bash merges stderr into stdout).
66
+ * Absent means "not reported", never an empty stderr.
67
+ */
68
+ stderr?: string;
69
+ /**
70
+ * Best-effort; absent when the agent doesn't report it (Claude's Bash surfaces no exit code).
71
+ * Absent means "not reported", never exit 0.
72
+ */
73
+ exitCode?: number;
74
+ }
75
+ /** A workspace-search action (grep/glob). */
76
+ export interface SearchQuery {
77
+ query?: string;
78
+ path?: string;
79
+ glob?: string;
80
+ }
81
+ /** A web search (`query`) or a URL fetch (`url`); the parent action's `kind` disambiguates. */
82
+ export interface WebSearch {
83
+ query?: string;
84
+ url?: string;
85
+ }
86
+ /** An MCP server tool invocation. */
87
+ export interface MCPCall {
88
+ server?: string;
89
+ tool?: string;
90
+ }
91
+ /**
92
+ * Token accounting for a turn. Every field is best-effort and additive (`omitempty` on the wire) —
93
+ * an agent that doesn't report a given count leaves it absent. Populated on the terminal `result`
94
+ * event across adapters; the exact keys are unified so a client can sum them uniformly:
95
+ * • `inputTokens` / `outputTokens` — prompt vs completion tokens.
96
+ * • `cacheReadTokens` / `cacheWriteTokens` — prompt-cache hits vs cache-creation writes.
97
+ * • `reasoningTokens` — thinking/reasoning tokens billed on top of output (o-series / extended thinking).
98
+ * • `totalTokens` — the agent's own grand total when it reports one; otherwise derive it client-side.
99
+ */
100
+ export interface Usage {
101
+ inputTokens?: number;
102
+ outputTokens?: number;
103
+ cacheReadTokens?: number;
104
+ cacheWriteTokens?: number;
105
+ reasoningTokens?: number;
106
+ totalTokens?: number;
107
+ }
108
+ export interface ResultInfo {
109
+ text?: string;
110
+ isError?: boolean;
111
+ sessionId?: string;
112
+ costUsd?: number;
113
+ /** Per-turn token accounting, when the agent reports it. */
114
+ usage?: Usage;
115
+ numTurns?: number;
116
+ durationMs?: number;
117
+ /**
118
+ * The agent's own terminal-result classifier when it distinguishes one (Claude's result subtype:
119
+ * `"success"`, `"error_max_turns"`, `"error_max_budget_usd"`, …). Absent for agents whose terminal
120
+ * event is always fully settled (Codex).
121
+ */
122
+ subtype?: string;
123
+ /**
124
+ * Derived convenience: `true` when the turn stopped short on a continuable subtype (max turns / max
125
+ * budget) rather than genuinely finishing. The signal a global-resolve run auto-resumes on.
126
+ */
127
+ incomplete?: boolean;
128
+ }
129
+ /** The unified stream item. Optional fields are populated per `type`. */
130
+ export interface Event {
131
+ type: EventType;
132
+ sessionId?: string;
133
+ /** Assistant/thinking text body. */
134
+ text?: string;
135
+ /** `true` when `text` is a live token delta rather than a final block. */
136
+ delta?: boolean;
137
+ /** Cumulative token count for the block (e.g. thinking preview). */
138
+ tokens?: number;
139
+ tool?: ToolEvent;
140
+ result?: ResultInfo;
141
+ interaction?: Interaction;
142
+ /** Populated on a `compaction` event. */
143
+ compaction?: CompactionInfo;
144
+ /** Populated on a `continuation` event (resolve mode). */
145
+ continuation?: ContinuationInfo;
146
+ error?: string;
147
+ meta?: Record<string, unknown>;
148
+ /** RFC3339 timestamp. */
149
+ at?: string;
150
+ }
151
+ /**
152
+ * Describes a conversation compaction — `auto` (the agent hit its context window and summarized on
153
+ * its own) or `manual` (an on-demand compact). The agent-agnostic payload for a `compaction` stream
154
+ * event AND for a `compaction` transcript {@link Part}, so the live stream and reloaded history show
155
+ * a compaction the same way. Every field is best-effort — an agent that doesn't report token counts
156
+ * or a trigger leaves them absent.
157
+ */
158
+ export interface CompactionInfo {
159
+ /** `"auto"` | `"manual"` (absent when the agent doesn't say). */
160
+ trigger?: string;
161
+ /** Context size before compaction. */
162
+ preTokens?: number;
163
+ /** Context size after compaction. */
164
+ postTokens?: number;
165
+ /** The continuation summary the agent wrote, when present. */
166
+ summary?: string;
167
+ }
168
+ /**
169
+ * Delimits one iteration of a global-resolve run on the merged parent stream (see {@link Mindwire.resolve}).
170
+ * The daemon emits a `continuation` event before each child turn (and once at the caps boundary), so a
171
+ * client reading the parent topic can tell one sub-turn from the next and see WHY the loop advanced.
172
+ */
173
+ export interface ContinuationInfo {
174
+ /** 0-based iteration index this boundary opens. */
175
+ iteration: number;
176
+ /**
177
+ * Why this iteration is running: `"start"` (first), `"max_turns"`/`"max_budget"` (resuming a
178
+ * continuable stop), or `"probe"` (a clean settle with no completion sentinel — probing for done).
179
+ */
180
+ reason?: "start" | "continue" | "probe" | "max_turns" | "max_budget" | (string & {});
181
+ /** The child {@link Run} this iteration streams under (its own record in the tree). */
182
+ childRunId?: string;
183
+ /** Set only on the FINAL boundary: why the loop ended. */
184
+ stopReason?: "done" | "capped" | "error" | (string & {});
185
+ }
186
+ /** A user action surfaced with an interaction/notification (e.g. Approve / Reject). */
187
+ export interface Action {
188
+ id: string;
189
+ label: string;
190
+ }
191
+ export interface TodoItem {
192
+ content: string;
193
+ status: "pending" | "in_progress" | "completed" | (string & {});
194
+ }
195
+ /**
196
+ * A structured, self-describing request an agent surfaces mid-turn for the client to render
197
+ * generically — and, when `needsResponse`, for the user to answer.
198
+ */
199
+ export interface Interaction {
200
+ id?: string;
201
+ kind: "todos" | "approval" | "choice" | "select" | "input" | "plan" | (string & {});
202
+ title?: string;
203
+ detail?: string;
204
+ /** `kind: "todos"` */
205
+ items?: TodoItem[];
206
+ /** `kind: "approval" | "choice" | "select" | "plan"` */
207
+ options?: Action[];
208
+ needsResponse?: boolean;
209
+ meta?: Record<string, unknown>;
210
+ }
211
+ /**
212
+ * The user's answer to a mid-turn {@link Interaction}, sent via {@link Run.respond}. `interactionId`
213
+ * ties the answer to the interaction the turn paused on; `decision` is the approval verdict
214
+ * (allow/deny) for a permission or plan; `text` is the free-form answer (or deny reason); `options`
215
+ * carries a multi-select answer.
216
+ */
217
+ export interface RespondInput {
218
+ interactionId?: string;
219
+ decision?: string;
220
+ options?: string[];
221
+ text?: string;
222
+ }
223
+ /** Whether an agent provides a feature natively, needs the core to emulate it, or lacks it. */
224
+ export type Support = "none" | "native" | "emulated";
225
+ /** How the daemon drives the agent for a turn — the control channel. */
226
+ export type Protocol = "cli" | "http" | "persistent";
227
+ /** How an agent emits a turn's output. */
228
+ export type OutputMode = "structured_json" | "terminal";
229
+ /** Per-agent feature matrix. The core switches on some fields; the client reads the rest as UI hints. */
230
+ export interface Capabilities {
231
+ protocol: Protocol;
232
+ output: OutputMode;
233
+ history: Support;
234
+ sessions: Support;
235
+ resume: boolean;
236
+ toolEvents: boolean;
237
+ cancel: boolean;
238
+ persistent: boolean;
239
+ models: boolean;
240
+ /**
241
+ * Client hint: image attachments are delivered as true vision content (the model sees the image),
242
+ * not just a path it must open with a Read tool. Attachments themselves are ungated.
243
+ */
244
+ imageInput: boolean;
245
+ /** User-in-loop ingress — each gates its route: answer an interaction, inject a follow-up, soft-stop. */
246
+ respond: boolean;
247
+ input: boolean;
248
+ interrupt: boolean;
249
+ /** Runtime control — switch the model / permission mode of a live turn (persistent transport only). */
250
+ setModel: boolean;
251
+ setPermissionMode: boolean;
252
+ /**
253
+ * Turn-option support — whether the agent honors these per-turn inputs. Each gates a turn request:
254
+ * sending an option the selected agent can't honor returns 400 rather than silently dropping it.
255
+ * `systemPrompt`/`appendSystemPrompt` gate the prompt overrides (the typed `TurnOptions.systemPrompt`
256
+ * or the canon-addressed setting); `mcpServers` gates `TurnOptions.mcpServers`. `subagents` gates
257
+ * `TurnOptions.subagents` (Claude's `--agents`); `claudeSettings` gates `TurnOptions.claudeSettings`
258
+ * (Claude's `--settings`, where hooks live).
259
+ */
260
+ systemPrompt: boolean;
261
+ appendSystemPrompt: boolean;
262
+ mcpServers: boolean;
263
+ subagents: boolean;
264
+ claudeSettings: boolean;
265
+ /**
266
+ * Persistent prompt/memory surface. `memory` = the agent exposes its memory file (Claude's
267
+ * `CLAUDE.md`, Codex's `AGENTS.md`) via `/memory`; `promptTemplates` = it exposes saved prompt
268
+ * templates (Claude slash-commands, Codex saved prompts) via `/prompts`. Both are UI hints — the
269
+ * daemon type-asserts the underlying module per request, so a call still 400s if unsupported.
270
+ */
271
+ memory: boolean;
272
+ promptTemplates: boolean;
273
+ /**
274
+ * `subagentDefs` = the agent exposes its persistent subagent definition files
275
+ * (`.claude/agents/*.md`) via `/subagents`. Distinct from the per-turn `subagents` passthrough
276
+ * above: that honors a per-turn `--agents` payload; this reads/writes the on-disk store. Claude-only.
277
+ */
278
+ subagentDefs: boolean;
279
+ /**
280
+ * `mcpConfig` = the agent exposes the persistent MCP-server config it loads on every run via
281
+ * `/mcp` (Claude's JSON stores, Codex's `config.toml`). Distinct from the per-turn `mcpServers`
282
+ * passthrough above: that overlays servers for one turn; this reads/writes the on-disk config.
283
+ */
284
+ mcpConfig: boolean;
285
+ /**
286
+ * `customProviders` = the agent exposes the custom-LLM-provider control plane via `/providers` — it can
287
+ * point at a custom OpenAI-compatible endpoint from its native config (opencode's `opencode.json`,
288
+ * Codex's `config.toml`). Claude is `false` (it uses its gateway auth lane instead). Managed via
289
+ * {@link ProvidersApi}; registered models surface in {@link Mindwire.models} with `custom:true`.
290
+ */
291
+ customProviders: boolean;
292
+ /**
293
+ * `compactNow` = the agent supports on-demand conversation compaction via `POST /chats/{id}/compact`
294
+ * (the SDK's {@link Mindwire.compact}). A compaction folds prior context into a summary the
295
+ * agent carries forward, streaming and recording the boundary exactly like an auto-compaction.
296
+ */
297
+ compactNow: boolean;
298
+ /**
299
+ * `resolve` = the agent supports global-resolve runs (`POST /turns {mode:"resolve"}`, the SDK's
300
+ * {@link Mindwire.resolve}). Unlike the other switches this is NOT gated in the daemon — resolve is
301
+ * pure daemon logic over the existing resume path, so every agent that can resume can be resolved;
302
+ * the flag is a UI hint only.
303
+ */
304
+ resolve: boolean;
305
+ }
306
+ export type FieldType = "text" | "secret" | "select" | "multiselect" | "toggle";
307
+ /**
308
+ * Whether a field is a UNIFIED cross-agent concept (model, permission mode, system prompt — one
309
+ * every agent has some form of) or a CUSTOM agent-specific one. Unified fields carry a stable
310
+ * {@link Field.canon} the client addresses regardless of the selected agent.
311
+ */
312
+ export type FieldScope = "unified" | "custom";
313
+ export interface Option {
314
+ value: string;
315
+ label: string;
316
+ }
317
+ export interface Field {
318
+ key: string;
319
+ label: string;
320
+ type: FieldType;
321
+ /** `unified` = addressed cross-agent by `canon`; `custom` = agent-specific. */
322
+ scope?: FieldScope;
323
+ /** Stable cross-agent key; equals `key` for custom fields. */
324
+ canon?: string;
325
+ required?: boolean;
326
+ placeholder?: string;
327
+ help?: string;
328
+ /** For `select` / `multiselect`. */
329
+ options?: Option[];
330
+ default?: string;
331
+ }
332
+ export interface Section {
333
+ title: string;
334
+ fields: Field[];
335
+ }
336
+ export interface SettingsSchema {
337
+ sections: Section[];
338
+ }
339
+ /** Minimal picker entry — one row in the catalog. */
340
+ export interface CatalogEntry {
341
+ id: string;
342
+ name: string;
343
+ tagline: string;
344
+ }
345
+ /**
346
+ * Which persistent layer a memory file or prompt template lives at. `project` = a working directory
347
+ * (the daemon cwd by default, or an explicit `dir`); `user` = the agent's home config dir
348
+ * (`~/.claude`, `~/.codex`). One canonical axis regardless of which agent is selected.
349
+ */
350
+ export type MemoryScope = "project" | "user";
351
+ /**
352
+ * Per-million-token pricing for a model, in USD. Present only when the on-disk catalog knows it.
353
+ */
354
+ export interface ModelCost {
355
+ input?: number;
356
+ output?: number;
357
+ cacheRead?: number;
358
+ cacheWrite?: number;
359
+ }
360
+ /**
361
+ * One model an agent can run: its API `id` (passed as the `model` setting) plus a human `label`, and
362
+ * provider-aware metadata. Returned by {@link Mindwire.models} for agents that can enumerate their
363
+ * models. The daemon emits bare rows (`id`/`label`/`provider`); provider-aware metadata (limits, cost,
364
+ * modalities, flags) is overlaid from the live models.dev catalog (see {@link catalogModels}). Every
365
+ * field beyond `id`/`label` is optional: a model the catalog can't match still carries just those two.
366
+ */
367
+ export interface ModelInfo {
368
+ id: string;
369
+ label: string;
370
+ /** Catalog/harness provider that runs this model (e.g. `"anthropic"`, `"openai"`). */
371
+ provider?: string;
372
+ /** Context-window and max-output token limits. */
373
+ contextWindow?: number;
374
+ maxOutput?: number;
375
+ /** Accepted input / produced output modalities (`"text"`, `"image"`, `"pdf"`, …). */
376
+ inputModalities?: string[];
377
+ outputModalities?: string[];
378
+ /** Capability flags from the catalog. */
379
+ reasoning?: boolean;
380
+ toolCall?: boolean;
381
+ attachment?: boolean;
382
+ /** Per-million-token pricing when known. */
383
+ cost?: ModelCost;
384
+ /** True when the model comes from a client-registered custom provider (metadata is typically sparse). */
385
+ custom?: boolean;
386
+ }
387
+ /**
388
+ * One provider in the models.dev catalog — the reference list the SDK fetches live from
389
+ * `https://models.dev/api.json` (see {@link catalogProviders}). This is pure catalog data: which
390
+ * providers and models exist, not which are configured or authenticated. `env` is the environment
391
+ * variable(s) the provider authenticates through (e.g. `["OPENAI_API_KEY"]`) — a UI storing a key for
392
+ * this provider should store it under the first of these so the daemon injects it to the harness.
393
+ */
394
+ export interface CatalogProvider {
395
+ id: string;
396
+ name: string;
397
+ /** Env-var name(s) the provider's API key is read from. Empty for keyless/local providers (e.g. ollama). */
398
+ env: string[];
399
+ /** npm package that implements the provider (models.dev metadata), when published. */
400
+ npm?: string;
401
+ /** Provider API base URL, when models.dev lists one. */
402
+ api?: string;
403
+ /** Provider documentation URL, when models.dev lists one. */
404
+ doc?: string;
405
+ /** Every model this provider offers, as {@link ModelInfo} (with `provider` set to this id). */
406
+ models: ModelInfo[];
407
+ }
408
+ /**
409
+ * One agent memory file at a given {@link MemoryScope} (Claude's `CLAUDE.md`, Codex's `AGENTS.md`).
410
+ * `path` is always the resolved absolute location — even when the file is absent; `exists`
411
+ * distinguishes an empty file from a missing one.
412
+ */
413
+ export interface MemoryDoc {
414
+ scope: MemoryScope;
415
+ path: string;
416
+ exists: boolean;
417
+ content: string;
418
+ }
419
+ /**
420
+ * A saved prompt template (Claude slash-command `.claude/commands/<name>.md`, Codex saved prompt
421
+ * `~/.codex/prompts/<name>.md`). `name` excludes the `.md` extension. `content` is omitted when
422
+ * listing and populated on read.
423
+ */
424
+ export interface PromptTemplate {
425
+ name: string;
426
+ scope: MemoryScope;
427
+ path: string;
428
+ content?: string;
429
+ }
430
+ /**
431
+ * Best-effort parsed view of a subagent definition's frontmatter — a convenience only; the raw
432
+ * {@link Subagent.content} is canonical. Every field is optional; a definition with no usable
433
+ * frontmatter has no `meta` at all.
434
+ */
435
+ export interface SubagentMeta {
436
+ name?: string;
437
+ description?: string;
438
+ tools?: string[];
439
+ model?: string;
440
+ }
441
+ /**
442
+ * One persistent subagent definition file (Claude `.claude/agents/<name>.md`). `name` is the
443
+ * definition's identity (its frontmatter `name`, else the filename stem). `content` is the canonical
444
+ * raw body — omitted when listing, populated on read/write. `meta` is the parsed convenience view.
445
+ */
446
+ export interface Subagent {
447
+ name: string;
448
+ scope: MemoryScope;
449
+ path: string;
450
+ content?: string;
451
+ meta?: SubagentMeta;
452
+ }
453
+ /**
454
+ * One persistent MCP-server definition — the config an agent loads on every run (Claude's project
455
+ * `.mcp.json` + user `.claude.json`, Codex's `config.toml` `[mcp_servers.*]`). Two shapes: a **stdio**
456
+ * server (`command` + `args` + `env`, optional `cwd`) or an **HTTP** server (`url`, optional
457
+ * `httpHeaders`). Managed via {@link McpApi}, distinct from a turn's per-turn `mcpServers` passthrough.
458
+ *
459
+ * SECURITY: this surface never carries a secret value. HTTP bearer auth is expressed by
460
+ * `bearerTokenEnvVar` — the NAME of an environment variable the agent resolves at run time (Claude maps
461
+ * it to an `Authorization: Bearer ${VAR}` header) — never the token itself.
462
+ */
463
+ export interface MCPServer {
464
+ /** stdio transport: the executable to launch. */
465
+ command?: string;
466
+ /** stdio transport: arguments passed to `command`. */
467
+ args?: string[];
468
+ /** stdio transport: extra environment variables (names → values you control, not agent secrets). */
469
+ env?: Record<string, string>;
470
+ /** stdio transport: working directory for the launched process. */
471
+ cwd?: string;
472
+ /** HTTP transport: the server endpoint. */
473
+ url?: string;
474
+ /** HTTP transport: the NAME of the env var holding the bearer token (never the token value). */
475
+ bearerTokenEnvVar?: string;
476
+ /** HTTP transport: literal headers sent with each request. */
477
+ httpHeaders?: Record<string, string>;
478
+ }
479
+ /**
480
+ * One registered custom OpenAI-compatible LLM provider — a base URL + model ids an agent loads on every
481
+ * run from its own native config (opencode's `opencode.json` `provider.<id>` block, Codex's
482
+ * `config.toml` `[model_providers.<id>]` table). Managed via {@link ProvidersApi}; a call 400s if the
483
+ * selected agent can't materialize one (check `capabilities.customProviders` first — Claude uses its
484
+ * gateway auth lane instead). The provider's models surface in {@link Mindwire.models} with `custom:true`.
485
+ *
486
+ * SECURITY: this shape never carries the API key. The key is supplied write-only to {@link ProvidersApi.set}
487
+ * and reported only as `hasKey`. The harness config references it solely through the env-var named by
488
+ * `envVar` (default derived from the id); the value is stored in the daemon and enters a run only through
489
+ * the auth env path — never written literally into any config file.
490
+ */
491
+ export interface CustomProvider {
492
+ /** Provider id, e.g. `"my-llm"` — the path segment and the config block key. */
493
+ id: string;
494
+ /** Human-readable display name (optional). */
495
+ name?: string;
496
+ /** OpenAI-compatible base URL, e.g. `"https://llm.example/v1"`. */
497
+ baseUrl: string;
498
+ /** The model ids this provider serves (surfaced by {@link Mindwire.models} as `provider/model`). */
499
+ models: string[];
500
+ /** Env var the key is referenced/exported as; defaults to a value derived from the id (e.g. `MY_LLM_API_KEY`). For a multi-var provider this is the first of {@link envVars}. */
501
+ envVar?: string;
502
+ /**
503
+ * ALL env-var NAMES a secret is currently stored under (the multi-var connect path, e.g. AWS Bedrock's
504
+ * `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`/`AWS_REGION`). NAMES only — the values are never returned.
505
+ * Empty/absent for a single-key provider (use {@link envVar}) or one with no stored secret.
506
+ */
507
+ envVars?: string[];
508
+ /** Whether a secret is stored for this provider. The key value is never returned. */
509
+ hasKey: boolean;
510
+ }
511
+ /** One way to authenticate an agent. Field-based methods collect `fields`; interactive ones drive a begin→step→status flow. */
512
+ export interface AuthMethod {
513
+ id: string;
514
+ label: string;
515
+ /**
516
+ * Same taxonomy as {@link Field.scope}: `unified` = a cross-agent auth concept (API key, login,
517
+ * gateway token); `custom` = agent-specific (e.g. Claude's Bedrock/Vertex/Foundry cloud providers),
518
+ * declared + typed in the daemon, never open passthrough. Absent reads as `unified`.
519
+ */
520
+ scope?: FieldScope;
521
+ help?: string;
522
+ interactive?: boolean;
523
+ /** Reuses the settings `Field` shape. */
524
+ fields?: Field[];
525
+ }
526
+ /** The current step of an in-progress auth flow. */
527
+ export interface AuthState {
528
+ method: string;
529
+ status: "needs_input" | "pending" | "complete" | "error" | (string & {});
530
+ url?: string;
531
+ code?: string;
532
+ message?: string;
533
+ /** Inputs the client should collect next. */
534
+ fields?: Field[];
535
+ }
536
+ /** The resting state: is the agent authenticated, and via which method. */
537
+ export interface AuthStatus {
538
+ configured: boolean;
539
+ method?: string;
540
+ detail?: string;
541
+ }
542
+ export type Condition = "finished" | "error" | "waiting_approval" | "waiting_feedback" | "waiting_input" | (string & {});
543
+ /** The unified payload the daemon emits (also fanned out to devices via the notify channel). */
544
+ export interface Notification {
545
+ condition: Condition;
546
+ title: string;
547
+ body: string;
548
+ agent?: string;
549
+ chatId?: string;
550
+ runId?: string;
551
+ actions?: Action[];
552
+ }
553
+ export interface ConditionUX {
554
+ condition: Condition;
555
+ title: string;
556
+ actions?: Action[];
557
+ }
558
+ export interface NotificationSpec {
559
+ conditions: ConditionUX[];
560
+ }
561
+ /** A paired tool call (use + result) within an assistant turn. */
562
+ export interface ToolPart {
563
+ id?: string;
564
+ name?: string;
565
+ input?: unknown;
566
+ output?: string;
567
+ isError?: boolean;
568
+ /** Deep-normalized view of the tool call; rides alongside the raw name/input/output. */
569
+ action?: ToolAction;
570
+ }
571
+ /** One ordered piece of an assistant turn. */
572
+ export interface Part {
573
+ type: "text" | "thinking" | "tool" | "interaction" | "compaction" | (string & {});
574
+ text?: string;
575
+ durationMs?: number;
576
+ tokens?: number;
577
+ tool?: ToolPart;
578
+ interaction?: Interaction;
579
+ /** Set on a `compaction` part — a conversation boundary in the reloaded transcript. */
580
+ compaction?: CompactionInfo;
581
+ at?: string;
582
+ }
583
+ export interface Message {
584
+ id: string;
585
+ chatId: string;
586
+ /** `"system"` marks a non-conversational boundary such as a compaction marker. */
587
+ role: "user" | "assistant" | "system" | (string & {});
588
+ text: string;
589
+ createdAt: string;
590
+ /** Ordered rich transcript (text / thinking / tool / compaction). Absent for user & text-only messages. */
591
+ parts?: Part[];
592
+ }
593
+ /**
594
+ * Per-turn parameters distinct from the agent's sticky settings (persisted via `setConfig`). Passed
595
+ * to {@link Mindwire.turn}; every field is optional, so a bare `{ chatId, message }` turn is unchanged.
596
+ */
597
+ export interface TurnOptions {
598
+ /**
599
+ * Per-turn setting OVERRIDES addressed by canonical key (see {@link Field.canon}). Resolved
600
+ * canon→the selected agent's key and filtered to declared non-secret keys server-side; overrides
601
+ * win over the sticky config. e.g. `{ reasoningEffort: "high" }` works regardless of which agent runs.
602
+ */
603
+ settings?: Record<string, string>;
604
+ /** Fully REPLACES the agent's default system prompt for this turn (distinct from the sticky append). */
605
+ systemPrompt?: string;
606
+ /** Pin an explicit session id for this turn. */
607
+ sessionId?: string;
608
+ /** Resume the most recent session in the cwd. */
609
+ continueLatest?: boolean;
610
+ /** Branch a new session id from the resumed one instead of continuing it in place. */
611
+ forkOnResume?: boolean;
612
+ /**
613
+ * JSON Schema constraining the turn's structured output. The daemon writes it to a per-turn temp
614
+ * file and passes it to the agent (e.g. Claude's `--json-schema`); cleaned up when the turn ends.
615
+ */
616
+ outputSchema?: unknown;
617
+ /**
618
+ * MCP servers configuration object (same shape the agent's own `--mcp-config` file expects).
619
+ * Materialized to a per-turn temp file for this turn only; not persisted to the agent's config.
620
+ */
621
+ mcpServers?: unknown;
622
+ /**
623
+ * Per-turn subagent definitions in the agent's native shape (Claude's `--agents` inline JSON:
624
+ * `{ name: { description, prompt } }`). Gated by the `subagents` capability; not persisted.
625
+ */
626
+ subagents?: unknown;
627
+ /**
628
+ * Per-turn settings/hooks bundle in the agent's native format (Claude's `--settings`, where
629
+ * hooks/permissions/env live). Materialized to a per-turn temp file; gated by `claudeSettings`.
630
+ */
631
+ claudeSettings?: unknown;
632
+ /** Files made available to the turn, referenced from the message text. */
633
+ attachments?: Attachment[];
634
+ }
635
+ /**
636
+ * Caps for a global-resolve run (`POST /turns {mode:"resolve"}`, the SDK's {@link Mindwire.resolve}).
637
+ * Both fields are optional — the daemon applies its own defaults (≈20 iterations, a ≈2h deadline) so a
638
+ * bare resolve is bounded. A resolve that hits either cap ends with `stopReason: "capped"` rather than
639
+ * running unbounded.
640
+ */
641
+ export interface ResolveOptions {
642
+ /** Max number of auto-continued child turns before the loop stops with `stopReason: "capped"`. */
643
+ maxIterations?: number;
644
+ /** Overall wall-clock budget for the whole resolve, in seconds; exceeding it caps the loop. */
645
+ deadlineSeconds?: number;
646
+ }
647
+ /**
648
+ * A file made available to a turn. Path-reference only: set `path` for a file already on disk
649
+ * (preferred), or `data` (base64) for inline bytes the daemon writes to a temp file, then references.
650
+ * Inline image content blocks require the persistent transport and are a follow-on.
651
+ */
652
+ export interface Attachment {
653
+ /** Display/file name shown to the agent when the file is referenced. */
654
+ name?: string;
655
+ /** Absolute path to a file already on disk (preferred). */
656
+ path?: string;
657
+ mime?: string;
658
+ /** Inline bytes, base64-encoded (Go `[]byte`); written to a temp file, then referenced. */
659
+ data?: string;
660
+ }
661
+ export type RunStatus = "running" | "done" | "error" | "cancelled" | (string & {});
662
+ /**
663
+ * One durable agent turn. The daemon owns it — it keeps running regardless of client connection. A
664
+ * global-resolve run is a PARENT (`kind: "resolve"`) whose auto-continued iterations are child runs
665
+ * carrying `parentId`; an ordinary turn leaves all resolve fields absent.
666
+ */
667
+ export interface Run {
668
+ id: string;
669
+ chatId: string;
670
+ /** Agent type that executed the turn. */
671
+ agent?: string;
672
+ status: RunStatus;
673
+ error?: string;
674
+ /** Assistant message id when done. */
675
+ replyId?: string;
676
+ createdAt: string;
677
+ endedAt?: string;
678
+ /** Absent for an ordinary turn; `"resolve"` marks the parent of a global-resolve run. */
679
+ kind?: "resolve" | (string & {});
680
+ /** Set on a child iteration of a resolve run: the id of its parent resolve run. */
681
+ parentId?: string;
682
+ /** Parent-only: why the resolve loop ended. */
683
+ stopReason?: "done" | "capped" | "error" | "cancelled" | (string & {});
684
+ /** Parent-only: how many child turns the resolve loop ran. */
685
+ iterations?: number;
686
+ }
687
+ /** The daemon's view of a chat, for a sessions list. */
688
+ export interface ChatSummary {
689
+ chatId: string;
690
+ agent?: string;
691
+ title: string;
692
+ messages: number;
693
+ updatedAt: string;
694
+ lastStatus?: RunStatus;
695
+ lastRunId?: string;
696
+ }
697
+ /**
698
+ * `DELETE /chats/{id}` result: the bookkeeping was purged, plus which agents' native transcripts
699
+ * were removed vs. failed to remove (best-effort, per session the chat mapped to).
700
+ */
701
+ export interface DeleteResult {
702
+ deleted: boolean;
703
+ /** How many `(agent, session)` mappings the chat had. */
704
+ sessions: number;
705
+ /** Agent types whose native transcript was removed. */
706
+ nativePurged?: string[];
707
+ /** Agent types whose native delete errored. */
708
+ nativeFailed?: string[];
709
+ }
710
+ /** `GET /catalog` */
711
+ export interface Catalog {
712
+ version: string;
713
+ agents: CatalogEntry[];
714
+ }
715
+ /** `GET /agent` — everything the client needs to render one agent's screen. */
716
+ export interface AgentInfo {
717
+ version: string;
718
+ agentType: string;
719
+ name: string;
720
+ capabilities: Capabilities;
721
+ schema: SettingsSchema;
722
+ authMethods: AuthMethod[];
723
+ authStatus: AuthStatus;
724
+ installedVersion: string;
725
+ configured: boolean;
726
+ configPath: string;
727
+ /**
728
+ * The models.dev catalog providers whose models this agent runs. The daemon no longer stores the
729
+ * catalog (the SDK fetches it live — see {@link catalogProviders}); an agent that can't self-enumerate
730
+ * a full model list (e.g. Codex has no scriptable list) names its provider scope here, and the client
731
+ * sources the model picker from the live catalog for those providers. The sentinel `"*"` means "all
732
+ * providers" (a provider-agnostic agent like opencode). Empty when the agent self-enumerates its
733
+ * complete list ({@link Mindwire.models} already carries every model, e.g. Claude's account API).
734
+ */
735
+ modelProviders: string[];
736
+ }
737
+ /** One diagnostic result (`GET /doctor`). */
738
+ export interface Check {
739
+ name: string;
740
+ status: "ok" | "warn" | "fail" | (string & {});
741
+ detail?: string;
742
+ }
743
+ export interface DoctorReport {
744
+ ok: boolean;
745
+ checks: Check[];
746
+ }
747
+ /** Outcome of one toolchain step (`GET /setup`). */
748
+ export interface StepResult {
749
+ name: string;
750
+ status: "satisfied" | "installed" | "failed" | (string & {});
751
+ output?: string;
752
+ }
753
+ /** Live toolchain install state (`GET /setup`). */
754
+ export interface SetupStatus {
755
+ running: boolean;
756
+ ok: boolean;
757
+ started: boolean;
758
+ current?: string;
759
+ steps: StepResult[];
760
+ }
761
+ /** `GET /notify/config` — the token is never returned. */
762
+ export interface NotifyConfigStatus {
763
+ configured: boolean;
764
+ url: string;
765
+ channel: string;
766
+ }
767
+ /** `PUT /notify/config` body. */
768
+ export interface NotifyConfigInput {
769
+ url: string;
770
+ channel: string;
771
+ token?: string;
772
+ }
773
+ /** Delivery payload shape of a channel (selects only how the outgoing POST is framed). */
774
+ export type NotifyChannelType = "webhook" | "slack" | "discord" | "telegram" | (string & {});
775
+ /**
776
+ * `GET /notify/channels` — the masked read view of a channel. Secrets never cross the wire: the URL,
777
+ * token, HMAC secret, and header VALUES are omitted; only their presence (and the URL host, as a
778
+ * display hint) is reported.
779
+ */
780
+ export interface NotifyChannel {
781
+ id: string;
782
+ type: NotifyChannelType;
783
+ label?: string;
784
+ /** Host of the configured URL (e.g. `hooks.slack.com`) — a display hint, never the full URL. */
785
+ urlHost?: string;
786
+ /** Whether a URL is set. */
787
+ hasUrl: boolean;
788
+ /** Names of the custom headers set (values are never returned). */
789
+ headerKeys?: string[];
790
+ /** Whether a bearer token is stored. */
791
+ hasToken: boolean;
792
+ /** Whether an HMAC signing secret is stored (webhook type). */
793
+ hasSecret: boolean;
794
+ enabled: boolean;
795
+ }
796
+ /**
797
+ * `POST`/`PUT /notify/channels` body. Secrets are WRITE-ONLY: `url`, `token`, `secret`, and header
798
+ * values are only ever sent, never read back. On a `PUT`, omitting `url`/`token`/`secret` preserves
799
+ * the stored value (send a new value to rotate it); send `headers: {}` to clear all headers.
800
+ */
801
+ export interface NotifyChannelInput {
802
+ type?: NotifyChannelType;
803
+ label?: string;
804
+ url?: string;
805
+ headers?: Record<string, string>;
806
+ token?: string;
807
+ secret?: string;
808
+ enabled?: boolean;
809
+ }
810
+ /** Which notifications a rule applies to, by origin. */
811
+ export type NotifyRuleScope = "global" | "agent" | "session" | (string & {});
812
+ /**
813
+ * A routing rule (`GET /notify/rules`). Fires when enabled, the notification's condition is in
814
+ * `conditions` (empty = all), and the scope matches: `global` always; `agent` when `agent` equals
815
+ * the notification's agent type; `session` when `session` equals its chatId. Matching rules union
816
+ * their `channelIds` (a channel referenced twice is delivered to once).
817
+ */
818
+ export interface NotifyRule {
819
+ id: string;
820
+ scope: NotifyRuleScope;
821
+ agent?: string;
822
+ session?: string;
823
+ conditions?: Condition[];
824
+ channelIds: string[];
825
+ enabled: boolean;
826
+ }
827
+ /** `POST`/`PUT /notify/rules` body (id is server-assigned on create). */
828
+ export type NotifyRuleInput = Omit<NotifyRule, "id"> & {
829
+ id?: string;
830
+ };
831
+ /** `POST /notify/channels/{id}/test` result — a failed delivery is data (HTTP 200), not an error. */
832
+ export interface NotifyChannelTestResult {
833
+ ok: boolean;
834
+ error?: string;
835
+ }
836
+ /** `GET /healthz` — the daemon's liveness probe. */
837
+ export interface Health {
838
+ ok: boolean;
839
+ agent: string;
840
+ version: string;
841
+ }
842
+ /**
843
+ * `GET /stats` — the daemon **process's** own resource snapshot, straight from the Go runtime. It is
844
+ * deliberately cheap (a single `ReadMemStats`, no `/proc` parsing, no sampling) so it's safe to fetch
845
+ * on demand; the daemon never polls in the background. These numbers describe the daemon's footprint
846
+ * (heap in use, memory reserved from the OS, goroutines, GC cycles) plus host facts (cores, platform,
847
+ * uptime) — NOT the whole machine's RAM/CPU, which can't be read cheaply from pure stdlib cross-platform.
848
+ */
849
+ export interface Stats {
850
+ /** GOOS the daemon runs on. */
851
+ os: string;
852
+ /** GOARCH the daemon runs on. */
853
+ arch: string;
854
+ /** Go runtime version. */
855
+ goVersion: string;
856
+ /** Logical CPU cores visible to the daemon. */
857
+ numCpu: number;
858
+ /** Live goroutines — a rough concurrency gauge. */
859
+ numGoroutine: number;
860
+ /** Heap objects currently in use (runtime `HeapAlloc`), in bytes. */
861
+ memAllocBytes: number;
862
+ /** Total memory reserved from the OS (runtime `Sys`), in bytes. */
863
+ memSysBytes: number;
864
+ /** Completed GC cycles since boot. */
865
+ numGc: number;
866
+ /** Seconds since the daemon started. */
867
+ uptimeSeconds: number;
868
+ }
869
+ /**
870
+ * One running turn's live resource use at a single sampling tick, from `GET /processes/stream`. Every
871
+ * turn runs in its own process group; these figures are summed over that whole group (bash → node →
872
+ * the agent CLI). Labels + numbers only — never a secret.
873
+ */
874
+ export interface ProcessSample {
875
+ /** Agent type this turn belongs to. */
876
+ agent: string;
877
+ /** Chat/session the turn is running for. */
878
+ chatId: string;
879
+ /** Run id of the turn (the sampling key). */
880
+ runId: string;
881
+ /** Group-leader pid — the whole turn's process tree. */
882
+ pid: number;
883
+ /**
884
+ * CPU% over the last sampling window (delta of cumulative CPU-seconds ÷ wall-clock). `0` on the
885
+ * first tick a group is seen — a real percentage arrives from the second tick onward.
886
+ */
887
+ cpuPercent: number;
888
+ /** Resident set size (physical memory) summed over the process group, in bytes. */
889
+ rssBytes: number;
890
+ }
891
+ /**
892
+ * One tick of the on-demand resource stream: every currently-running turn that had a live process this
893
+ * tick. Sampling is refcounted on the daemon — it starts when the first client subscribes and stops on
894
+ * the last disconnect, so nothing runs in the background. An empty `samples` array (no running turns)
895
+ * is a valid keep-alive frame.
896
+ */
897
+ export interface ProcessFrame {
898
+ /** RFC3339 timestamp of the tick. */
899
+ at: string;
900
+ /** One entry per running turn sampled this tick. */
901
+ samples: ProcessSample[];
902
+ }
903
+ /**
904
+ * The daemon's error envelope: every non-2xx JSON body is `{ "error": <message> }`. This is the
905
+ * parsed body shape carried on the thrown `ApiError`'s `body` field (when the daemon returned JSON).
906
+ */
907
+ export interface ApiErrorBody {
908
+ error: string;
909
+ }