@orkestrel/agent 0.0.20 → 0.0.22

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.
@@ -1,29 +1,42 @@
1
- import { BudgetInterface } from '@orkestrel/budget';
2
- import { ControllerInterface } from '@orkestrel/workflow';
3
- import { DriverInterface } from '@orkestrel/database';
4
- import { EmitterErrorHandler } from '@orkestrel/emitter';
5
- import { EmitterHooks } from '@orkestrel/emitter';
6
- import { EmitterInterface } from '@orkestrel/emitter';
7
- import { FileInterface } from '@orkestrel/workspace';
1
+ import { ArrayShape } from '@orkestrel/contract';
2
+ import { BooleanShape } from '@orkestrel/contract';
3
+ import type { BudgetInterface } from '@orkestrel/budget';
4
+ import { ContractInterface } from '@orkestrel/contract';
5
+ import type { ContractShape } from '@orkestrel/contract';
6
+ import type { ControllerInterface } from '@orkestrel/workflow';
7
+ import type { DriverInterface } from '@orkestrel/database';
8
+ import type { EmitterErrorHandler } from '@orkestrel/emitter';
9
+ import type { EmitterHooks } from '@orkestrel/emitter';
10
+ import type { EmitterInterface } from '@orkestrel/emitter';
11
+ import type { FileInterface } from '@orkestrel/workspace';
12
+ import { JSONShape } from '@orkestrel/contract';
8
13
  import { JSONValue } from '@orkestrel/contract';
9
- import { QueueContext } from '@orkestrel/queue';
10
- import { QueueInterface } from '@orkestrel/queue';
11
- import { QueueStoreInterface } from '@orkestrel/queue';
12
- import { RunnerInterface } from '@orkestrel/workflow';
13
- import { SchedulerInterface } from '@orkestrel/workflow';
14
- import { TableInterface } from '@orkestrel/database';
15
- import { TokenUsage } from '@orkestrel/budget';
16
- import { ToolCall } from '@orkestrel/tool';
17
- import { ToolDefinition } from '@orkestrel/tool';
18
- import { ToolInterface } from '@orkestrel/tool';
19
- import { ToolManagerInterface } from '@orkestrel/tool';
20
- import { ToolResult } from '@orkestrel/tool';
21
- import { WorkspaceManagerInterface } from '@orkestrel/workspace';
14
+ import { LiteralShape } from '@orkestrel/contract';
15
+ import { NumberShape } from '@orkestrel/contract';
16
+ import { ObjectShape } from '@orkestrel/contract';
17
+ import { OptionalShape } from '@orkestrel/contract';
18
+ import type { QueueContext } from '@orkestrel/queue';
19
+ import type { QueueInterface } from '@orkestrel/queue';
20
+ import type { QueueStoreInterface } from '@orkestrel/queue';
21
+ import type { RunnerInterface } from '@orkestrel/workflow';
22
+ import type { SchedulerInterface } from '@orkestrel/workflow';
23
+ import { StringShape } from '@orkestrel/contract';
24
+ import type { TableInterface } from '@orkestrel/database';
25
+ import type { TokenUsage } from '@orkestrel/budget';
26
+ import type { ToolCall } from '@orkestrel/tool';
27
+ import type { ToolDefinition } from '@orkestrel/tool';
28
+ import type { ToolInterface } from '@orkestrel/tool';
29
+ import type { ToolManagerInterface } from '@orkestrel/tool';
30
+ import type { ToolResult } from '@orkestrel/tool';
31
+ import { UnionShape } from '@orkestrel/contract';
32
+ import type { WorkspaceManagerInterface } from '@orkestrel/workspace';
22
33
 
23
34
  /**
24
35
  * Composes a {@link ProviderInterface}, an {@link AgentContext}, and a
25
- * {@link ToolManagerInterface} into a bounded context → provider → tools → repeat
26
- * turn, exposed as both a one-shot `generate` and a live `stream`.
36
+ * {@link ToolManagerInterface} into a bounded context → provider → tools → repeat turn, exposed
37
+ * as both a one-shot `generate` and a live `stream` that share one private run — bounded by the
38
+ * run `signal`, the `timeout`, and the `budget` folded through `AbortSignal.any`, paced by
39
+ * `scheduler`, with tool iteration capped at `limit`.
27
40
  *
28
41
  * @remarks
29
42
  * - **One loop, two faces.** A single private async generator (`#run`) drives the
@@ -40,15 +53,15 @@ import { WorkspaceManagerInterface } from '@orkestrel/workspace';
40
53
  * assistant message and stop.
41
54
  * - **Bounded.** Each run arms one cancel through `createAbort({ signal: AbortSignal.any([
42
55
  * …]) })` folding the external `signal`, the `timeout` deadline, and the `budget`
43
- * signal; `abort()` fires it. Any trip stops the loop and commits a PARTIAL result
44
- * (the `result` promise RESOLVES, never rejects, on a cancel) — only a genuine
56
+ * signal; `abort()` fires it. Any trip stops the loop and commits a partial result
57
+ * (the `result` promise resolves, never rejects, on a cancel) — only a genuine
45
58
  * provider / tool error rejects.
46
59
  * - **Paced + capped.** The `scheduler` (when given) `yield`s between turns; tool
47
60
  * iteration is capped at `limit`.
48
- * - **Two observation surfaces.** The PULL {@link AgentChunk} stream carries per-token
49
- * deltas (+ usage/tool chunks); the PUSH {@link emitter} ({@link AgentEventMap}) carries
61
+ * - **Two observation surfaces.** The pull {@link AgentChunk} stream carries per-token
62
+ * deltas (+ usage/tool chunks); the push {@link emitter} ({@link AgentEventMap}) carries
50
63
  * lifecycle + usage/tool/deny moments for fire-and-forget observers. Every event is
51
- * emitted directly, AFTER the relevant state transition / settle; the emitter isolates a
64
+ * emitted directly, after the relevant state transition / settle; the emitter isolates a
52
65
  * listener throw and routes it to its `error` handler (the `error` option), so a buggy
53
66
  * observer can never escape into / reorder / corrupt the settle-once loop — observation is
54
67
  * purely a side-channel.
@@ -74,15 +87,15 @@ export declare class Agent implements AgentInterface {
74
87
 
75
88
  /**
76
89
  * Represents a streamed step of an agent turn — the union the loop yields as it runs, discriminated
77
- * by the `category` of step it carries.
90
+ * by the `category` of step it carries, and the pull surface beside the push {@link AgentEventMap}.
78
91
  *
79
92
  * @remarks
80
93
  * - `token` — a content delta the provider streamed (the `'content'`
81
94
  * {@link ProviderDelta}s a {@link ProviderInterface}'s `stream` yields), re-surfaced for
82
- * live rendering of the assistant ANSWER.
95
+ * live rendering of the assistant answer.
83
96
  * - `think` — a reasoning delta the provider streamed (the `'thinking'`
84
97
  * {@link ProviderDelta}s, the daemon's native `message.thinking` channel), surfaced so a
85
- * consumer can stream the model's reasoning LIVE into a collapsible; NEVER answer content
98
+ * consumer can stream the model's reasoning live into a collapsible; never answer content
86
99
  * (it is never fed into the accumulated `content`).
87
100
  * - `tool` — a {@link ToolCall} the loop dispatched paired with its {@link ToolResult},
88
101
  * emitted once the tool ran (so a consumer sees what was called and what came back).
@@ -107,58 +120,59 @@ export declare type AgentChunk = {
107
120
  /**
108
121
  * Assembles a provider request from the richer turn context — the optional system prompt, the
109
122
  * observable context managers (instructions / workspaces), the
110
- * {@link ConversationManagerInterface} message source (whose active conversation IS `messages`),
111
- * the {@link ToolManagerInterface} registry, and an active {@link ScopeInterface} changed through
112
- * {@link AgentContextInterface.apply}.
123
+ * {@link ConversationManagerInterface} message source whose active conversation is `messages`, the
124
+ * {@link ToolManagerInterface} registry, and an active {@link ScopeInterface} changed through
125
+ * {@link AgentContextInterface.apply}. `build()` folds the scoped managers and the active
126
+ * workspace into one system block, then the conversation, and never reads `tools`.
113
127
  *
114
128
  * @remarks
115
129
  * - **Composition.** `system` is the optional system prompt; `instructions` / `tools` /
116
130
  * `workspaces` / `conversations` are the registries passed in `options` (bring your own), or
117
- * fresh empty ones when omitted (so `workspaces` is ALWAYS present); `messages` is the ACTIVE
118
- * conversation's live tail (ALWAYS defined — see below). `scope` is the active filter —
131
+ * fresh empty ones when omitted (so `workspaces` is always present); `messages` is the active
132
+ * conversation's live tail (always defined — see the following item). `scope` is the active filter —
119
133
  * `undefined` (the default) ⇒ no filtering; change it through `apply(scope)`. The structural
120
134
  * `workspaces` / `conversations` registries are fixed at construction; switch their active
121
135
  * members through their own `switch(id)` methods.
122
- * - **The message source — the conversation registry's ACTIVE conversation.** `conversations` is a
123
- * {@link ConversationManagerInterface}; the context ENSURES it always has an active conversation
124
- * (at construction it `add`s a default when the manager has none), so the DYNAMIC `messages`
125
- * getter — `this.#conversations.active` — is ALWAYS defined. `messages` returns the active
126
- * conversation ITSELF (it owns the live tail + the message verbs directly, satisfying
136
+ * - **The message source — the conversation registry's active conversation.** `conversations` is a
137
+ * {@link ConversationManagerInterface}; the context ensures it always has an active conversation
138
+ * (at construction it `add`s a default when the manager has none), so the dynamic `messages`
139
+ * getter — `this.#conversations.active` — is always defined. `messages` returns the active
140
+ * conversation itself (it owns the live tail + the message verbs directly, satisfying
127
141
  * {@link MessageManagerInterface} structurally — the same reference, no duplication), and
128
142
  * `build()` folds that conversation's `view()` (its per-section summaries + live tail) as the
129
- * AUTHORITATIVE message inclusion — the scope does NOT filter the conversation (it owns inclusion
143
+ * authoritative message inclusion — the scope does not filter the conversation (it owns inclusion
130
144
  * through compaction; scope filters only instructions / tools / workspace files). Because `messages`
131
- * is read dynamically, an agent SWITCHES the active
132
- * conversation BETWEEN runs (`conversations.switch(id)`) to serve MANY threads (the real
145
+ * is read dynamically, an agent switches the active
146
+ * conversation between runs (`conversations.switch(id)`) to serve many threads (the real
133
147
  * multi-conversation pattern); switch between runs, not during a run, and use separate agents
134
148
  * for concurrent threads.
135
149
  * - **`build(format?)` — the scoped assembly + the format cascade.** It folds, in order,
136
- * the system prompt then the scope-filtered instructions → the ACTIVE workspace's text files
150
+ * the system prompt then the scope-filtered instructions → the active workspace's text files
137
151
  * (each as a block: the section's resolved `open` text, each item's resolved rendering, then
138
- * any resolved `close` text) into ONE leading `system` message (prepended only when at least
139
- * one part exists), then appends the ACTIVE conversation's `view()` (the conversation owns
140
- * message inclusion through compaction — the scope does NOT filter the conversation). Each
152
+ * any resolved `close` text) into one leading `system` message (prepended only when at least
153
+ * one part exists), then appends the active conversation's `view()` (the conversation owns
154
+ * message inclusion through compaction — the scope does not filter the conversation). Each
141
155
  * `open` / item / `close`
142
- * resolves INDEPENDENTLY, MOST-SPECIFIC-FIRST — `build()`'s optional `format` (a provider's
143
- * per-section default) is the PROVIDER level: `open` = manager-options-override > provider >
156
+ * resolves independently, most-specific-first — `build()`'s optional `format` (a provider's
157
+ * per-section default) is the provider level: `open` = manager-options-override > provider >
144
158
  * built-in; per item = item-override > manager-options-override > provider > built-in; `close` =
145
- * manager-options-override > provider (NO built-in ⇒ no closing line when unset) (see
146
- * {@link AgentContextInterface.build}). Passing NO `format` (and with no overrides / no per-item
159
+ * manager-options-override > provider (no built-in ⇒ no closing line when unset) (see
160
+ * {@link AgentContextInterface.build}). Passing no `format` (and with no overrides / no per-item
147
161
  * override) reproduces the built-in framing byte-for-byte (each section is its built-in header +
148
162
  * items, no closing line). The active workspace's scoped-in image files' `base64` payload is attached to
149
- * the LAST user message (a vision provider reads images off a user turn); when no user message
163
+ * the last user message (a vision provider reads images off a user turn); when no user message
150
164
  * exists the attachment is skipped. Built fresh each call (recomputed, never cached), so it
151
165
  * always reflects the current managers / messages / scope / active workspace; it never mutates a
152
166
  * manager or the stored messages.
153
- * - **The ACTIVE workspace, rendered BY CARRIER — the SOLE document/image context.**
167
+ * - **The active workspace, rendered by carrier — the sole document/image context.**
154
168
  * `workspaces.active` (when set) has its {@link import('@orkestrel/workspace').FileInterface}s scope-filtered by `scope.files`,
155
- * then split: TEXT files fold into a dedicated `## Workspace` system section (fenced reference
156
- * blocks — placed right after the instructions section), and IMAGE files' `base64` payload attaches
157
- * to the last user message. ACTIVE-ONLY — never the other registered workspaces; with no active
158
- * workspace nothing renders for workspaces. `build()` OWNS this render (a `Workspace` /
169
+ * then split: text files fold into a dedicated `## Workspace` system section (fenced reference
170
+ * blocks — placed right after the instructions section), and image files' `base64` payload attaches
171
+ * to the last user message. Active-only — never the other registered workspaces; with no active
172
+ * workspace nothing renders for workspaces. `build()` owns this render (a `Workspace` /
159
173
  * `WorkspaceManager` stays file-focused).
160
174
  * - **Tools are structural, not in the prompt.** The registry is advertised to the provider
161
- * through `tools.definitions()` (scope-filtered by the loop), NEVER serialized into the
175
+ * through `tools.definitions()` (scope-filtered by the loop), never serialized into the
162
176
  * message array — so `build()`'s output carries no tool content, scoped or not.
163
177
  * - **Event-free context; observable managers.** The context itself owns no Emitter; the
164
178
  * context managers each carry their own (the push observation surface).
@@ -194,12 +208,12 @@ export declare class AgentContext implements AgentContextInterface {
194
208
  * (`instructions` / `workspaces` / `conversations`), `messages` (the active
195
209
  * conversation's live tail, satisfying {@link MessageManagerInterface}), and the current `scope`
196
210
  * (the active {@link ScopeInterface} filter, or `undefined` for no filtering). `build()` folds the
197
- * scoped instructions into ONE leading `system` message (under the manager's `open`,
198
- * each item through its `render`) — PLUS the ACTIVE workspace's scope-filtered text files
199
- * (rendered as fenced reference blocks) — and appends the ACTIVE conversation's `view()`, attaching
200
- * the active workspace's scope-filtered image files' `base64` payload to the LAST user message. The
201
- * active workspace is the SOLE document/image context. Tools are advertised to the provider
202
- * STRUCTURALLY (through `tools.definitions()`, scope-filtered by the loop), NOT serialized into
211
+ * scoped instructions into one leading `system` message (under the manager's `open`,
212
+ * each item through its `render`) — plus the active workspace's scope-filtered text files
213
+ * (rendered as fenced reference blocks) — and appends the active conversation's `view()`, attaching
214
+ * the active workspace's scope-filtered image files' `base64` payload to the last user message. The
215
+ * active workspace is the sole document/image context. Tools are advertised to the provider
216
+ * structurally (through `tools.definitions()`, scope-filtered by the loop), not serialized into
203
217
  * the prompt, so they never appear in `build()`'s output. The context managers are observable
204
218
  * (their own `emitter`s); the context itself is event-free.
205
219
  */
@@ -207,32 +221,32 @@ export declare interface AgentContextInterface {
207
221
  readonly system: string | undefined;
208
222
  readonly instructions: InstructionManagerInterface;
209
223
  /**
210
- * Holds the {@link WorkspaceManagerInterface} whose ACTIVE workspace `build()` renders by carrier —
224
+ * Holds the {@link WorkspaceManagerInterface} whose active workspace `build()` renders by carrier —
211
225
  * its text files folded into the system block (fenced reference blocks) and its image files'
212
- * `base64` payload attached to the LAST user message. The active workspace is the SOLE
213
- * document/image context. ALWAYS present (a fresh empty manager when none was supplied).
214
- * `build()` reads its `active` (and the active workspace's `files()`) FRESH each call. With NO
226
+ * `base64` payload attached to the last user message. The active workspace is the sole
227
+ * document/image context. Always present (a fresh empty manager when none was supplied).
228
+ * `build()` reads its `active` (and the active workspace's `files()`) fresh each call. With no
215
229
  * active workspace, nothing is rendered for workspaces. Active-only — never the other registered
216
230
  * workspaces.
217
231
  */
218
232
  readonly workspaces: WorkspaceManagerInterface;
219
233
  /**
220
- * Holds the active conversation's LIVE tail — the agent's message source, ALWAYS defined (the
234
+ * Holds the active conversation's live tail — the agent's message source, always defined (the
221
235
  * {@link conversations} registry always has an active conversation; a default is added at
222
- * construction). It IS the active {@link ConversationInterface} itself (which satisfies
236
+ * construction). It is the active {@link ConversationInterface} itself (which satisfies
223
237
  * {@link MessageManagerInterface} structurally), so appends through `messages` route to the
224
238
  * active conversation's tail and `build()` folds its `view()`. Computed dynamically (it follows
225
- * `conversations.switch(id)`), the SAME reference the active conversation exposes — no
239
+ * `conversations.switch(id)`), the same reference the active conversation exposes — no
226
240
  * duplication.
227
241
  */
228
242
  readonly messages: MessageManagerInterface;
229
243
  /**
230
- * Holds the {@link ConversationManagerInterface} the message source flows from — `messages` IS its
231
- * ACTIVE conversation's live tail and `build()` folds that conversation's `view()`. ALWAYS holds
244
+ * Holds the {@link ConversationManagerInterface} the message source flows from — `messages` is its
245
+ * active conversation's live tail and `build()` folds that conversation's `view()`. Always holds
232
246
  * an active conversation (a default is added at construction when none was supplied), so
233
247
  * `messages` is always defined. Switch the active conversation through
234
- * `conversations.switch(id)` — so one agent can serve MANY conversations (set the active one per
235
- * request). Switch BETWEEN runs, not during one; for CONCURRENT threads use separate agents.
248
+ * `conversations.switch(id)` — so one agent can serve many conversations (set the active one per
249
+ * request). Switch between runs, not during one; for concurrent threads use separate agents.
236
250
  */
237
251
  readonly conversations: ConversationManagerInterface;
238
252
  /**
@@ -243,7 +257,8 @@ export declare interface AgentContextInterface {
243
257
  /** Holds the active scope applied at `build()` time + the loop's tool-advertise step (`undefined` ⇒ no filtering). */
244
258
  readonly scope: ScopeInterface | undefined;
245
259
  /**
246
- * Applies the active per-turn scope filter. Passing `undefined` removes filtering.
260
+ * Applies the given scope as the active per-turn filter; passing `undefined` explicitly
261
+ * removes filtering.
247
262
  *
248
263
  * @param scope - The scope to apply, or `undefined` to remove the active filter
249
264
  *
@@ -255,51 +270,57 @@ export declare interface AgentContextInterface {
255
270
  */
256
271
  apply(scope: ScopeInterface | undefined): void;
257
272
  /**
258
- * Builds the provider input for the next turn: a leading `system` message folding the prompt
259
- * + the scoped instructions + the ACTIVE workspace's scoped-in TEXT files (rendered as
260
- * fenced reference blocks), then the ACTIVE conversation's `view()` (with the active workspace's
261
- * scoped-in IMAGE files' `base64` payload attached to the last user message). Tools are advertised
262
- * structurally, not in the prompt. Built fresh on each call.
273
+ * Builds the provider input for the next turn: a leading `system` message folding the
274
+ * prompt, the scope-filtered instructions (each section's header and each item's rendering
275
+ * resolved through the format cascade), and the active workspace's scope-filtered
276
+ * (`scope.files`) text files as fenced reference blocks in a `## Workspace` section, then
277
+ * the active conversation's `view()`, with the active workspace's image files' `base64`
278
+ * payload attached to the last user message. Takes an optional `format` — typically
279
+ * `provider.format`, the provider level of the cascade — and omitting it with no overrides
280
+ * set renders each section on its manager's built-in framing. The `system` message is
281
+ * prepended only when some part of it exists, the workspace render covers the active
282
+ * workspace alone, tools are advertised structurally rather than in the prompt, and the
283
+ * input is built fresh on each call.
263
284
  *
264
285
  * @remarks
265
- * **The active workspace (rendered by carrier) — the SOLE document/image context.** When
286
+ * **The active workspace (rendered by carrier) — the sole document/image context.** When
266
287
  * `workspaces.active` is set, its
267
288
  * {@link import('@orkestrel/workspace').WorkspaceInterface.files} are filtered by
268
289
  * `scope.files` (a three-way allow-list; `undefined` ⇒ all active files), then split by
269
- * carrier: TEXT files ({@link import('@orkestrel/workspace').isText}) render into a dedicated
290
+ * carrier: text files ({@link import('@orkestrel/workspace').isText}) render into a dedicated
270
291
  * `## Workspace` section in the system block — each a fenced
271
292
  * `` File: <path>\n```<language>\n<text>\n``` `` block — placed immediately after the instructions
272
293
  * section; binary files whose MIME starts with `image/` have their `base64` payload
273
- * attached to the LAST user message (a vision provider reads images off a user turn).
274
- * ACTIVE-ONLY — never the other registered workspaces; with NO active workspace nothing is
294
+ * attached to the last user message (a vision provider reads images off a user turn).
295
+ * Active-only — never the other registered workspaces; with no active workspace nothing is
275
296
  * rendered for workspaces.
276
297
  *
277
298
  * **The format cascade.** Each manager section frames as `[open, ...items.map(render), close]`
278
299
  * (empty / absent slots dropped, the survivors `\n\n`-joined). The `open` (the section's
279
300
  * leading text), each item's `render`, and the `close` (the section's trailing text)
280
- * resolve INDEPENDENTLY, MOST-SPECIFIC-FIRST, from a {@link ContextSectionFormat} at each
301
+ * resolve independently, most-specific-first, from a {@link ContextSectionFormat} at each
281
302
  * level — an item override, a manager-options override, the provider `format` default,
282
303
  * and the manager's built-in. For the `instructions` section kind `K`, manager
283
304
  * `M`, and the supplied `format` `F`:
284
305
  * - **open** = `M.format?.open ?? F?.[K]?.open ?? M.open` — that is,
285
306
  * **manager-options override > provider default > built-in** (the leading text has no
286
- * per-item level). The manager ENCAPSULATES the `[options-override → built-in]` half:
307
+ * per-item level). The manager encapsulates the `[options-override → built-in]` half:
287
308
  * `M.open` already returns the options override's `open` when one is set, else
288
- * the built-in header — so `build()` only layers the provider default BETWEEN them.
309
+ * the built-in header — so `build()` only layers the provider default between them.
289
310
  * - **item** `I` = `I.override ?? M.format?.render?.(I) ?? F?.[K]?.render?.(I) ?? M.render(I)`
290
311
  * — that is, **item override > manager-options override > provider default > built-in**.
291
312
  * Again `M.render(I)` already returns the options override when set, else the
292
- * built-in, so `build()` layers the per-item `I.override` ON TOP and the provider
293
- * default BETWEEN.
313
+ * built-in, so `build()` layers the per-item `I.override` on top and the provider
314
+ * default between.
294
315
  * - **close** = `M.format?.close ?? F?.[K]?.close` — that is, **manager-options
295
- * override > provider default**, with NO built-in floor (the trailing text has no
316
+ * override > provider default**, with no built-in floor (the trailing text has no
296
317
  * per-item level): unset at both levels ⇒ `undefined` ⇒ no closing line. Paired with
297
- * `open`, it lets a level WRAP the group (`open: '<instructions>'` … `close: '</instructions>'`).
318
+ * `open`, it lets a level wrap the group (`open: '<instructions>'` … `close: '</instructions>'`).
298
319
  *
299
- * Passing NO `format` (the default) leaves the provider-default level empty, so the
300
- * output is BYTE-FOR-BYTE the managers' built-in framing — every section is its
320
+ * Passing no `format` (the default) leaves the provider-default level empty, so the
321
+ * output is byte-for-byte the managers' built-in framing — every section is its
301
322
  * built-in header + items, with no closing line (the regression contract). Scope
302
- * filtering runs BEFORE formatting (unchanged); the workspace image-data attachment to the
323
+ * filtering runs before formatting (unchanged); the workspace image-data attachment to the
303
324
  * last user message is unchanged.
304
325
  *
305
326
  * @param format - The provider's optional {@link ContextFormat} default
@@ -312,7 +333,10 @@ export declare interface AgentContextInterface {
312
333
  }
313
334
 
314
335
  /**
315
- * Configures `createAgentContext` — the richer context's configuration.
336
+ * Configures `createAgentContext` — the optional system prompt plus the pre-built managers to
337
+ * reuse: an `instructions` registry, a `workspaces` registry (the only document channel), a
338
+ * `conversations` registry (the message source), a `tools` registry (the loop's advertise and
339
+ * dispatch surface), and an initial `scope`.
316
340
  *
317
341
  * @remarks
318
342
  * `system` is the optional system prompt prepended to the turn's input. `instructions` /
@@ -322,10 +346,10 @@ export declare interface AgentContextInterface {
322
346
  * initial active filter applied at `build()` time (and at the loop's tool-advertise step); it
323
347
  * defaults to `undefined` — no filtering — and can be changed through the context's `apply`
324
348
  * method afterwards. `conversations` is the structural {@link ConversationManagerInterface} the
325
- * context's message source flows from: `messages` IS the manager's ACTIVE conversation's live tail
349
+ * context's message source flows from: `messages` is the manager's active conversation's live tail
326
350
  * and `build()` folds that conversation's `view()` (section summaries + live). When omitted, a fresh
327
351
  * {@link ConversationManagerInterface} is created and a default conversation is added (so
328
- * `messages` is ALWAYS defined). All default to a context with no system prompt, empty registries,
352
+ * `messages` is always defined). All default to a context with no system prompt, empty registries,
329
353
  * no scope, and a fresh conversation registry holding one default conversation.
330
354
  */
331
355
  export declare interface AgentContextOptions {
@@ -339,21 +363,21 @@ export declare interface AgentContextOptions {
339
363
  readonly instructions?: InstructionManagerInterface;
340
364
  /**
341
365
  * Reuses a pre-built {@link WorkspaceManagerInterface}; a fresh empty one is created when
342
- * omitted (so `context.workspaces` is ALWAYS present). `build()` renders the ACTIVE workspace's
366
+ * omitted (so `context.workspaces` is always present). `build()` renders the active workspace's
343
367
  * files by carrier — text files into the system block (fenced), image files attached to the last
344
368
  * user message. The registry is structural; change its active workspace through
345
- * `workspaces.switch(id)`. The active workspace is the SOLE document/image context.
369
+ * `workspaces.switch(id)`. The active workspace is the sole document/image context.
346
370
  */
347
371
  readonly workspaces?: WorkspaceManagerInterface;
348
372
  /** Sets the initial active scope (the build-time filter); `undefined` ⇒ no filtering. */
349
373
  readonly scope?: ScopeInterface;
350
374
  /**
351
375
  * Reuses a pre-built {@link ConversationManagerInterface} as the message source; a fresh
352
- * empty one is created when omitted. The context ENSURES an active conversation at construction
353
- * (it `add`s a default when the manager has none), so `messages` — the manager's ACTIVE
354
- * conversation's LIVE tail — is ALWAYS defined. `build()` folds the active conversation's
355
- * `view()` (the per-section summaries + the live tail) as its AUTHORITATIVE message inclusion —
356
- * the scope does NOT filter the conversation (it owns inclusion through compaction; scope filters
376
+ * empty one is created when omitted. The context ensures an active conversation at construction
377
+ * (it `add`s a default when the manager has none), so `messages` — the manager's active
378
+ * conversation's live tail — is always defined. `build()` folds the active conversation's
379
+ * `view()` (the per-section summaries + the live tail) as its authoritative message inclusion —
380
+ * the scope does not filter the conversation (it owns inclusion through compaction; scope filters
357
381
  * only instructions / tools / workspace files). The registry is structural; change its active
358
382
  * conversation through the manager's `switch(id)`.
359
383
  */
@@ -361,13 +385,16 @@ export declare interface AgentContextOptions {
361
385
  }
362
386
 
363
387
  /**
364
- * Reports a concurrent run that would corrupt SHARED per-agent accounting, or a rehydration
388
+ * Reports a concurrent run that would corrupt shared per-agent accounting, or a rehydration
365
389
  * name absent from its registry pool — thrown synchronously by an {@link AgentInterface}'s
366
- * `stream()` and by an {@link AgentRegistryInterface}'s accessors, carrying a machine-readable
367
- * `code`.
390
+ * `stream()` (and so by `generate()`, which calls it) and by an
391
+ * {@link AgentRegistryInterface}'s accessors, carrying the machine-readable `code`
392
+ * `'CONCURRENCY' | 'REGISTRY'`. Synchronous means a fire-and-forget
393
+ * `agent.generate().catch(…)` never catches it: `await` the call inside `try`/`catch`, or wrap
394
+ * the call expression itself.
368
395
  *
369
396
  * @remarks
370
- * `'CONCURRENCY'` reports a run already in flight on the same agent, PLUS a construction-level
397
+ * `'CONCURRENCY'` reports a run already in flight on the same agent, plus a construction-level
371
398
  * `window` (a shared context budget) or a construction-level `budget` with no per-run override
372
399
  * (a shared cost budget) — a second concurrent `stream()` would race its charges against the
373
400
  * same shared instance, corrupting the accounting. Use separate agents, or per-run `budget`
@@ -382,26 +409,26 @@ export declare class AgentError extends Error {
382
409
  }
383
410
 
384
411
  /**
385
- * Maps the push observation surface of an {@link AgentInterface} — the
386
- * lifecycle + usage/tool moments a fire-and-forget observer (logging, metrics,
387
- * tracing) subscribes to, ALONGSIDE the pull {@link AgentChunk} stream.
412
+ * Maps the push observation surface of an {@link AgentInterface} — the lifecycle, usage, and tool
413
+ * moments a fire-and-forget observer (logging, metrics, tracing) subscribes to, beside the pull
414
+ * {@link AgentChunk} stream.
388
415
  *
389
416
  * @remarks
390
- * Push vs. pull: the Emitter carries the loop's LIFECYCLE moments (a run begins /
417
+ * Push vs. pull: the Emitter carries the loop's lifecycle moments (a run begins /
391
418
  * each turn / a settle / a cancel) plus usage and dispatched-tool events — the things
392
419
  * the chunk stream can't express (a `deny` never reaches the stream) or that a
393
- * fire-and-forget observer wants without draining the stream. PER-TOKEN deltas stay
394
- * EXCLUSIVELY the {@link AgentChunk} stream's job (the pull surface) — there is
395
- * deliberately NO `token` event here. Subscribe through `agent.emitter.on(...)`.
420
+ * fire-and-forget observer wants without draining the stream. Per-token deltas stay
421
+ * exclusively the {@link AgentChunk} stream's job (the pull surface) — there is
422
+ * deliberately no `token` event here. Subscribe through `agent.emitter.on(...)`.
396
423
  *
397
424
  * Observation is side-effect-free on the loop: listener isolation is the emitter's
398
- * — every event is emitted directly and a listener throw is routed to the emitter's OWN
425
+ * — every event is emitted directly and a listener throw is routed to the emitter's own
399
426
  * `error` handler (the `error` option), never onto this domain map and never into the
400
427
  * settle-once / wake-park engine — so a buggy observer can never reorder, throw into, or
401
428
  * corrupt the run.
402
429
  *
403
- * A cancelled run emits `abort` (the cancel signal) AND then `finish` (the settled
404
- * PARTIAL result) — so an observer sees both that the run was cancelled and the partial
430
+ * A cancelled run emits `abort` (the cancel signal) and then `finish` (the settled
431
+ * partial result) — so an observer sees both that the run was cancelled and the partial
405
432
  * outcome it committed; a genuine error emits `error` instead of `finish`.
406
433
  *
407
434
  * Declared as a `type` alias (not `interface extends EventMap` — `EventMap` is a
@@ -418,9 +445,9 @@ export declare type AgentEventMap = {
418
445
  readonly tool: readonly [call: ToolCall, result: ToolResult];
419
446
  /** Reports a turn's {@link TokenUsage} — emitted after a usage-bearing provider call. */
420
447
  readonly usage: readonly [usage: TokenUsage];
421
- /** Reports a call the authority DENIED — the call + the optional reason (NOT in the chunk stream). */
448
+ /** Reports a call the authority denied — the call + the optional reason (not in the chunk stream). */
422
449
  readonly deny: readonly [call: ToolCall, reason: string | undefined];
423
- /** Reports the run settled successfully (a natural finish OR a cancel's partial) — the {@link AgentResult}. */
450
+ /** Reports the run settled successfully (a natural finish or a cancel's partial) — the {@link AgentResult}. */
424
451
  readonly finish: readonly [result: AgentResult];
425
452
  /** Reports the run settled with a genuine (non-cancel) error — the thrown value (always `unknown`). */
426
453
  readonly error: readonly [error: unknown];
@@ -429,20 +456,20 @@ export declare type AgentEventMap = {
429
456
  /**
430
457
  * Reports the loop exhausting its `limit` while still holding unresolved tool intent (the model
431
458
  * requested tools on the very last allowed turn) — the turn count reached. Distinct from
432
- * `abort`: exhaustion is NOT a cancel (no external signal / timeout / budget tripped), so
433
- * this fires INSTEAD of `abort`, still followed by `finish` carrying the partial result.
459
+ * `abort`: exhaustion is not a cancel (no external signal / timeout / budget tripped), so
460
+ * this fires instead of `abort`, still followed by `finish` carrying the partial result.
434
461
  */
435
462
  readonly exhaust: readonly [turns: number];
436
463
  /**
437
- * Reports AUTOMATIC compaction's summarizer THROWING — a NON-FATAL warn channel (the run continues; see
464
+ * Reports automatic compaction's summarizer throwing — a non-fatal warn channel (the run continues; see
438
465
  * {@link AgentOptions.window}). When the loop's between-turns / pre-first-turn auto-compaction
439
- * (`conversation.compact()`) rejects, the run does NOT crash: the loop skips compaction that
466
+ * (`conversation.compact()`) rejects, the run does not crash: the loop skips compaction that
440
467
  * turn and surfaces the caught error here so the failure is observable, never silently lost.
441
- * The run still SETTLES through the other events — `finish` for the lenient default, and
468
+ * The run still settles through the other events — `finish` for the lenient default, and
442
469
  * `error` when {@link AgentOptions.strict} rethrows the same caught value — so `fault` reports
443
- * the best-effort optimization that failed and never the run's own outcome. A MANUAL
444
- * `conversation.compact()` still propagates its own error; only the agent's AUTO path is
445
- * resilient. A DOMAIN event (the emitter isolates a listener throw separately, routing it to
470
+ * the best-effort optimization that failed and never the run's own outcome. A manual
471
+ * `conversation.compact()` still propagates its own error; only the agent's auto path is
472
+ * resilient. A domain event (the emitter isolates a listener throw separately, routing it to
446
473
  * its `error` handler).
447
474
  */
448
475
  readonly fault: readonly [error: unknown];
@@ -453,7 +480,7 @@ export declare type AgentEventMap = {
453
480
  * {@link ToolManagerInterface} into a bounded context → provider → tools → repeat turn.
454
481
  *
455
482
  * @remarks
456
- * - **One loop, two faces.** `generate` and `stream` share ONE private run, so they
483
+ * - **One loop, two faces.** `generate` and `stream` share one private run, so they
457
484
  * can never diverge: `generate` drains the same stream `stream` exposes, then
458
485
  * resolves its settled {@link AgentResult}.
459
486
  * - **Bounded.** Each turn arms a single cancel folded from the external `signal`, the
@@ -461,11 +488,11 @@ export declare type AgentEventMap = {
461
488
  * or `abort()` — stops the loop and settles the result `partial: true`.
462
489
  * - **Paced + capped.** The `scheduler` (when given) yields between turns; tool
463
490
  * iteration is capped at `limit` so the loop always terminates.
464
- * - **Two observation surfaces.** PULL: the {@link AgentChunk} stream (`stream().events`)
465
- * carries per-token answer deltas, per-think reasoning deltas, and usage/tool chunks for a live consumer. PUSH: the
491
+ * - **Two observation surfaces.** Pull: the {@link AgentChunk} stream (`stream().events`)
492
+ * carries per-token answer deltas, per-think reasoning deltas, and usage/tool chunks for a live consumer. Push: the
466
493
  * {@link emitter} ({@link AgentEventMap}) carries lifecycle + usage/tool/deny moments
467
494
  * for fire-and-forget observers — the emitter isolates a listener throw and routes it to
468
- * its `error` handler (the `error` option), so a buggy observer can NEVER corrupt the
495
+ * its `error` handler (the `error` option), so a buggy observer can never corrupt the
469
496
  * loop. Per-token / per-thinking deltas are the stream's job exclusively; there is no
470
497
  * `token` or `think` event.
471
498
  * - **Per-run overrides.** Both faces accept an optional {@link AgentRunOptions} bag whose
@@ -477,14 +504,14 @@ export declare interface AgentInterface {
477
504
  readonly status: AgentStatus;
478
505
  readonly context: AgentContextInterface;
479
506
  /**
480
- * Runs the turn to completion, discarding the live chunks — drains the shared
481
- * stream and resolves the settled outcome.
507
+ * Runs the turn to completion, discarding the live chunks — drains the shared stream and
508
+ * resolves the settled {@link AgentResult} (`partial: true` when cancelled).
482
509
  *
483
510
  * @remarks
484
511
  * A concurrent run on a shared accounting agent throws an
485
512
  * {@link import('./errors.js').AgentError} (`code: 'CONCURRENCY'`) — and it throws
486
- * SYNCHRONOUSLY, before any `Promise` is returned. A fire-and-forget
487
- * `agent.generate().catch(...)` therefore will NOT catch it (the throw happens on the call
513
+ * synchronously, before any `Promise` is returned. A fire-and-forget
514
+ * `agent.generate().catch(...)` therefore will not catch it (the throw happens on the call
488
515
  * itself, ahead of the `.catch` ever attaching) — `await` the call (inside a `try`/`catch`)
489
516
  * or wrap the call expression itself in `try`/`catch`.
490
517
  *
@@ -495,11 +522,12 @@ export declare interface AgentInterface {
495
522
  generate(options?: AgentRunOptions): Promise<AgentResult>;
496
523
  /**
497
524
  * Runs the turn as a live stream — iterate `events` for {@link AgentChunk}s and
498
- * `await result` for the settled outcome.
525
+ * `await result` for the settled outcome; `result` resolves partial on a cancel and rejects
526
+ * on a genuine error.
499
527
  *
500
528
  * @remarks
501
529
  * Like `generate()`, a concurrent run on a shared accounting agent throws an
502
- * {@link import('./errors.js').AgentError} (`code: 'CONCURRENCY'`) SYNCHRONOUSLY — before the
530
+ * {@link import('./errors.js').AgentError} (`code: 'CONCURRENCY'`) synchronously — before the
503
531
  * {@link AgentStreamInterface} handle is even returned, so it cannot be caught by chaining
504
532
  * off the (never-produced) handle; wrap the call itself in `try`/`catch`.
505
533
  *
@@ -520,13 +548,13 @@ export declare interface AgentInterface {
520
548
  /**
521
549
  * Reports an {@link AgentInterface} run that ended {@link AgentResult.partial} under a
522
550
  * `partial` policy of `false` (the default) — thrown by an agent-job handler (a
523
- * `createAgentQueue` / `createAgentRunner` job), carrying the partial
524
- * {@link AgentResult} so the failure stays inspectable.
551
+ * `createAgentQueue` / `createAgentRunner` job), carrying the partial {@link AgentResult} so
552
+ * the failure stays inspectable, and the machine-readable `code` `'PARTIAL'`.
525
553
  *
526
554
  * @remarks
527
555
  * A partial result means the agent was cancelled (an external `signal` abort, a queue /
528
556
  * runner abort threaded in, a `timeout` deadline, or an exhausted token `budget`) rather
529
- * than finishing naturally. For a durable JOB that is a failure by default: throwing this
557
+ * than finishing naturally. For a durable job that is a failure by default: throwing this
530
558
  * lets the Queue's retries re-run the job and a Runner's fail-fast abort its siblings.
531
559
  * Set `partial: true` (see `AgentQueueOptions` / `AgentRunnerOptions`) to treat a
532
560
  * partial as success instead, in which case this is never thrown. Narrow a caught value
@@ -543,11 +571,11 @@ export declare class AgentJobError extends Error {
543
571
  }
544
572
 
545
573
  /**
546
- * Represents a JSON-serializable agent job — the descriptor a durable queue / runner runs. Its
547
- * non-serializable pieces (the provider, tools, authority, scheduler) are referenced by
548
- * NAME and resolved to live objects through an {@link AgentRegistryInterface} at handler
549
- * time; its data fields (the seed `messages`, `system`, `limit`, `timeout`, and a token
550
- * `budget` ceiling) carry directly.
574
+ * Represents a JSON-serializable agent job — the descriptor a durable queue or runner runs. Its
575
+ * non-serializable pieces (the provider, tools, authority, scheduler) are referenced by name and
576
+ * resolved to live objects through an {@link AgentRegistryInterface} at handler time, while its
577
+ * data fields (the seed `messages`, `system`, `limit`, `timeout`, and a token `budget` ceiling)
578
+ * carry directly.
551
579
  *
552
580
  * @remarks
553
581
  * Because every field is JSON-serializable, a job survives a crash through the Queue's
@@ -581,7 +609,7 @@ export declare interface AgentJobInput {
581
609
  /**
582
610
  * Lists the sub-agent jobs this job fans out — each a nested {@link AgentJobInput} (so the whole
583
611
  * tree stays serializable). On a `createAgentRunner`, the handler `controller.spawn`s
584
- * each child through the same bounded queue BEFORE running this (parent) job, so the
612
+ * each child through the same bounded queue before running this (parent) job, so the
585
613
  * children run as sibling sub-agents and their results join the run after the declared
586
614
  * jobs (in spawn order). Ignored by `createAgentQueue` (a queue has no fan-out).
587
615
  */
@@ -589,7 +617,11 @@ export declare interface AgentJobInput {
589
617
  }
590
618
 
591
619
  /**
592
- * Configures `createAgent` — bounds and pacing for the agent loop.
620
+ * Configures `createAgent` — the loop's bounds and pacing, the reserved `on` hooks, the
621
+ * construction-time context wiring (`instructions` / `workspaces` / `scope`), the `conversations`
622
+ * registry that is the message source, the context `window` budget that opts into automatic
623
+ * compaction of the active conversation, and the `strict` switch that aborts the run on an
624
+ * automatic-compaction summarizer failure instead of the lenient default.
593
625
  *
594
626
  * @remarks
595
627
  * - `system` — an optional system prompt prepended to the turn (seeds the context).
@@ -609,16 +641,16 @@ export declare interface AgentJobInput {
609
641
  * - `signal` — an optional external `AbortSignal` whose abort cancels the turn (a
610
642
  * partial result).
611
643
  * - `conversations` — an optional {@link ConversationManagerInterface} forwarded to the agent's
612
- * context as the message source (so `context.messages` is its ACTIVE conversation's live tail);
644
+ * context as the message source (so `context.messages` is its active conversation's live tail);
613
645
  * omitted ⇒ a fresh registry holding one default conversation. Auto-compaction (`window`) folds
614
- * the ACTIVE conversation when it is summarizable.
615
- * - `window` — an optional CONTEXT {@link BudgetInterface} for AUTOMATIC conversation
616
- * compaction: when set, the loop measures the CURRENT FULL prompt against this budget each turn
646
+ * the active conversation when it is summarizable.
647
+ * - `window` — an optional context {@link BudgetInterface} for automatic conversation
648
+ * compaction: when set, the loop measures the current full prompt against this budget each turn
617
649
  * (its `consumer` is a token estimator, its `max` the context window) and, when the prompt
618
- * reaches the window AND the active conversation is summarizable, COMPACTS the active
650
+ * reaches the window and the active conversation is summarizable, compacts the active
619
651
  * conversation + continues on the rebuilt smaller view — compact-and-continue, distinct from
620
652
  * `budget`'s hard abort. Omitted ⇒ no auto-compaction.
621
- * - `strict` — when `true`, a summarizer failure during AUTOMATIC compaction ABORTS the run
653
+ * - `strict` — when `true`, a summarizer failure during automatic compaction aborts the run
622
654
  * (rethrown after the `fault` event, propagating through `#run` to a genuine `error`
623
655
  * settle) instead of skipping compaction and continuing over-window. Defaults to `false`
624
656
  * (lenient — the prior, byte-for-byte behavior).
@@ -663,33 +695,153 @@ export declare interface AgentOptions {
663
695
  */
664
696
  readonly authority?: AuthorityInterface;
665
697
  /**
666
- * Holds an optional {@link ConversationManagerInterface} that becomes the agent context's MESSAGE
667
- * SOURCE — forwarded to the {@link AgentContextInterface} the agent builds, so
668
- * `agent.context.messages` IS its ACTIVE conversation's live tail and `build()` folds that
698
+ * Holds an optional {@link ConversationManagerInterface} that becomes the agent context's message
699
+ * source — forwarded to the {@link AgentContextInterface} the agent builds, so
700
+ * `agent.context.messages` is its active conversation's live tail and `build()` folds that
669
701
  * conversation's `view()` (the per-section summaries + the live tail). Omitted ⇒ a fresh
670
- * registry holding one default conversation. With `window` set, AUTOMATIC compaction folds the
671
- * ACTIVE conversation between turns (when it is summarizable).
702
+ * registry holding one default conversation. With `window` set, automatic compaction folds the
703
+ * active conversation between turns (when it is summarizable).
672
704
  */
673
705
  readonly conversations?: ConversationManagerInterface;
674
706
  /**
675
- * Holds an optional CONTEXT {@link BudgetInterface} for AUTOMATIC compaction. Its `consumer` is a
707
+ * Holds an optional context {@link BudgetInterface} for automatic compaction. Its `consumer` is a
676
708
  * token estimator (for example the exported {@link import('./helpers.js').estimateMessages}) and
677
- * its `max` is the context window. When set, the loop measures the CURRENT FULL prompt (the
678
- * next provider request) against this budget each turn; when that prompt reaches the window AND
709
+ * its `max` is the context window. When set, the loop measures the current full prompt (the
710
+ * next provider request) against this budget each turn; when that prompt reaches the window and
679
711
  * the active conversation is summarizable, it **compacts the active conversation + continues on
680
712
  * the rebuilt smaller view** (compact-and-continue) — the same consume-to-a-ceiling primitive
681
713
  * as the cost `budget`, but compaction is the ceiling action instead of abort. Omit to disable.
682
714
  */
683
715
  readonly window?: BudgetInterface<readonly Message[]>;
684
716
  /**
685
- * If `true`, a summarizer failure during AUTOMATIC compaction ABORTS the run — the
686
- * `fault` event still fires, then the caught error is RETHROWN so the run settles
717
+ * If `true`, a summarizer failure during automatic compaction aborts the run — the
718
+ * `fault` event still fires, then the caught error is rethrown so the run settles
687
719
  * `error` instead of continuing over-window. Defaults to `false` (lenient — the run
688
720
  * continues over-window, byte-for-byte the prior behavior).
689
721
  */
690
722
  readonly strict?: boolean;
691
723
  }
692
724
 
725
+ /**
726
+ * Implements bounded HTTP streaming and result assembly behind concrete wire seams.
727
+ *
728
+ * @remarks
729
+ * Every call owns its parser, splitter, deadline, and accumulation. Success bodies
730
+ * have no size limit. A qwen3 implicit-open reclassification corrects the final
731
+ * content while content deltas already yielded cannot be recalled. A subclass fills
732
+ * `name`, `frame`, `body`, `read`, and `finish`; the constructor takes the `split`
733
+ * and `strict` switches to control reasoning separation and settled-result requirements.
734
+ *
735
+ * @example Writing a provider for a new wire
736
+ * ```ts
737
+ * import type {
738
+ * ProviderIncrement,
739
+ * ProviderOptions,
740
+ * ProviderParserInterface,
741
+ * ProviderRequest,
742
+ * } from '@orkestrel/agent'
743
+ * import { AgentProvider } from '@orkestrel/agent'
744
+ *
745
+ * class TextFrame implements ProviderParserInterface<string> {
746
+ * parse(chunk: string): readonly string[] {
747
+ * return [chunk]
748
+ * }
749
+ * clear(): void {} // Raw text retains no framing state.
750
+ * }
751
+ *
752
+ * interface TextOptions extends ProviderOptions {
753
+ * readonly url: string
754
+ * }
755
+ *
756
+ * class TextProvider extends AgentProvider<string> {
757
+ * readonly name = 'text'
758
+ * constructor(options: TextOptions) {
759
+ * super({ ...options, path: '/generate' })
760
+ * }
761
+ * frame(): ProviderParserInterface<string> {
762
+ * return new TextFrame()
763
+ * }
764
+ * body(request: ProviderRequest): object {
765
+ * return { messages: request.messages }
766
+ * }
767
+ * read(record: string): ProviderIncrement {
768
+ * return { content: record, thinking: '', tools: [] }
769
+ * }
770
+ * finish(_parser: ProviderParserInterface<string>): readonly string[] {
771
+ * return [] // Raw text retains no records at end of input.
772
+ * }
773
+ * }
774
+ * ```
775
+ */
776
+ export declare abstract class AgentProvider<TRecord = Readonly<Record<string, unknown>>> implements AgentProviderInterface<TRecord> {
777
+ #private;
778
+ constructor(input: AgentProviderInput);
779
+ /** Identifies the concrete backend. */
780
+ abstract readonly name: string;
781
+ /** Exposes the instance's minted UUID. */
782
+ get id(): string;
783
+ /** Exposes the context framing exactly as supplied. */
784
+ get format(): ContextFormat | undefined;
785
+ /** Creates fresh framing state for the call. */
786
+ abstract frame(): ProviderParserInterface<TRecord>;
787
+ /** Projects a domain request onto the concrete protocol's wire body. */
788
+ abstract body(request: ProviderRequest): object;
789
+ /** Decodes a framed record into a turn increment. */
790
+ abstract read(record: TRecord): ProviderIncrement;
791
+ /** Returns any records retained at end of input. */
792
+ abstract finish(parser: ProviderParserInterface<TRecord>): readonly TRecord[];
793
+ /**
794
+ * Generates a complete turn by draining the shared stream engine.
795
+ *
796
+ * @param messages - The conversation turns
797
+ * @param signal - The caller's cancellation bound
798
+ * @param tools - The advertised tool definitions
799
+ * @param options - The per-call generation configuration
800
+ * @returns The terminal stream result
801
+ */
802
+ generate(messages: readonly Message[], signal: AbortSignal, tools?: readonly ToolDefinition[], options?: ProviderStreamOptions): Promise<ProviderResult>;
803
+ /**
804
+ * Streams decoded deltas and returns the authoritative or assembled turn result.
805
+ *
806
+ * @param messages - The conversation turns
807
+ * @param signal - The caller's cancellation bound
808
+ * @param tools - The advertised tool definitions
809
+ * @param options - The per-call generation configuration
810
+ * @returns Content and native thinking deltas followed by the settled result
811
+ * @throws ProviderAbortError Thrown when the combined cancellation bound fires
812
+ * @throws ProviderError Thrown for an HTTP or protocol failure
813
+ */
814
+ stream(messages: readonly Message[], signal: AbortSignal, tools?: readonly ToolDefinition[], options?: ProviderStreamOptions): AsyncGenerator<ProviderDelta, ProviderResult>;
815
+ }
816
+
817
+ /**
818
+ * Configures the HTTP destination and stream assembly of a provider base.
819
+ *
820
+ * @remarks
821
+ * `path` appends to `url`. If `split` is true, separates in-content reasoning;
822
+ * if false, preserves content verbatim. Default: true.
823
+ * If `strict` is true, requires a settled result record; if false, assembles at end of input.
824
+ * Default: false.
825
+ */
826
+ export declare interface AgentProviderInput extends ProviderOptions {
827
+ readonly url: string;
828
+ readonly path?: string;
829
+ readonly split?: boolean;
830
+ readonly strict?: boolean;
831
+ }
832
+
833
+ /** Defines the wire-specific seams of the shared HTTP provider engine. */
834
+ export declare interface AgentProviderInterface<TRecord = Readonly<Record<string, unknown>>> extends ProviderInterface {
835
+ /** Creates fresh framing state for a call. */
836
+ frame(): ProviderParserInterface<TRecord>;
837
+ /** Projects a request to the concrete protocol's serializable body. */
838
+ body(request: ProviderRequest): object;
839
+ /** Decodes a framed record into its contribution to the turn. */
840
+ read(record: TRecord): ProviderIncrement;
841
+ /** Returns records retained at end of input before the parser is cleared. */
842
+ finish(parser: ProviderParserInterface<TRecord>): readonly TRecord[];
843
+ }
844
+
693
845
  /**
694
846
  * Configures `createAgentQueue` — the registry that rehydrates jobs, the partial-result
695
847
  * policy, and the substrate knobs threaded into the backing `createQueue`.
@@ -698,9 +850,9 @@ export declare interface AgentOptions {
698
850
  * - `registry` — the {@link AgentRegistryInterface} the handler rehydrates each job
699
851
  * through (required).
700
852
  * - `partial` — the partial policy. A partial {@link AgentResult} (a job committed
701
- * early from an abort / budget / timeout) is by DEFAULT a FAILURE: the handler THROWS
853
+ * early from an abort / budget / timeout) is by default a failure: the handler throws
702
854
  * an {@link import('./errors.js').AgentJobError}, so the Queue's retries (and a
703
- * Runner's fail-fast) engage. Set `true` to treat a partial as SUCCESS instead — the
855
+ * Runner's fail-fast) engage. Set `true` to treat a partial as success instead — the
704
856
  * handler resolves the partial result rather than throwing.
705
857
  * - `concurrency` / `retries` / `timeout` / `store` — passed straight to the backing
706
858
  * `QueueInterface` (see `QueueOptions`): bounded concurrency, the retry budget, the
@@ -708,7 +860,7 @@ export declare interface AgentOptions {
708
860
  */
709
861
  export declare interface AgentQueueOptions {
710
862
  readonly registry: AgentRegistryInterface;
711
- /** If `true`, a partial `AgentResult` resolves as success; if `false` (the default), it THROWS and retries engage. */
863
+ /** If `true`, a partial `AgentResult` resolves as success; if `false` (the default), it throws and retries engage. */
712
864
  readonly partial?: boolean;
713
865
  readonly concurrency?: number;
714
866
  readonly retries?: number;
@@ -717,9 +869,10 @@ export declare interface AgentQueueOptions {
717
869
  }
718
870
 
719
871
  /**
720
- * Makes a durable, JSON-serializable {@link AgentJobInput} runnable — holds the named
721
- * pools of live, non-serializable pieces (providers, tools, authorities, schedulers) and
722
- * rehydrates a seeded, signal-wired {@link Agent} from a job's names + data.
872
+ * Makes a durable, JSON-serializable {@link AgentJobInput} runnable — holds the named pools of
873
+ * live, non-serializable pieces (providers, tools, authorities, schedulers), throws on a name
874
+ * absent from its pool, and `build`s a seeded, signal-wired {@link Agent} from a job's names and
875
+ * data.
723
876
  *
724
877
  * @remarks
725
878
  * - **Why it exists.** An `AgentJobInput` is serializable so it can survive a crash in a
@@ -728,9 +881,9 @@ export declare interface AgentQueueOptions {
728
881
  * construct it once with the live pools, then a queue / runner handler calls `build`
729
882
  * on each (possibly restored) job to get a ready agent.
730
883
  * - **Accessors throw on a miss.** `provider` / `tool` / `authority` / `scheduler` resolve a
731
- * name against their pool and THROW an {@link AgentError} carrying `code: 'REGISTRY'` and
884
+ * name against their pool and throw an {@link AgentError} carrying `code: 'REGISTRY'` and
732
885
  * the message `unknown <category>: <name>` when it is absent — an unknown name in a
733
- * rehydrated job is a programmer / config error that must fail LOUDLY at build time, never
886
+ * rehydrated job is a programmer / config error that must fail loudly at build time, never
734
887
  * silently resolve to `undefined` and run an agent missing a dependency.
735
888
  * - **`build` rehydrates.** Resolve the job's `provider`; assemble a fresh
736
889
  * {@link ToolManager} from the `tools` names; rebuild the token `budget` from its
@@ -767,7 +920,7 @@ export declare class AgentRegistry implements AgentRegistryInterface {
767
920
  *
768
921
  * @remarks
769
922
  * - **Accessors throw on a miss.** `provider` / `tool` / `authority` / `scheduler` look one
770
- * up by name and THROW an {@link AgentError} carrying `code: 'REGISTRY'` and the message
923
+ * up by name and throw an {@link AgentError} carrying `code: 'REGISTRY'` and the message
771
924
  * `unknown <category>: <name>` when the name is unregistered — an unknown name in a
772
925
  * rehydrated job must fail loudly, never silently resolve to `undefined`, so a
773
926
  * misconfigured job surfaces at once rather than running with a missing dependency.
@@ -780,7 +933,8 @@ export declare class AgentRegistry implements AgentRegistryInterface {
780
933
  */
781
934
  export declare interface AgentRegistryInterface {
782
935
  /**
783
- * Resolves a registered {@link ProviderInterface} by name.
936
+ * Resolves a registered {@link ProviderInterface} by name — throws `unknown provider: <name>` when
937
+ * absent.
784
938
  *
785
939
  * @param name - The provider's registry key
786
940
  * @returns The live provider
@@ -788,7 +942,8 @@ export declare interface AgentRegistryInterface {
788
942
  */
789
943
  provider(name: string): ProviderInterface;
790
944
  /**
791
- * Resolves a registered {@link ToolInterface} by name.
945
+ * Resolves a registered {@link ToolInterface} by name — throws `unknown tool: <name>` when
946
+ * absent.
792
947
  *
793
948
  * @param name - The tool's registry key
794
949
  * @returns The live tool
@@ -796,7 +951,8 @@ export declare interface AgentRegistryInterface {
796
951
  */
797
952
  tool(name: string): ToolInterface;
798
953
  /**
799
- * Resolves a registered {@link AuthorityInterface} by name.
954
+ * Resolves a registered {@link AuthorityInterface} by name — throws `unknown authority: <name>` when
955
+ * absent.
800
956
  *
801
957
  * @param name - The authority's registry key
802
958
  * @returns The live authority
@@ -804,7 +960,8 @@ export declare interface AgentRegistryInterface {
804
960
  */
805
961
  authority(name: string): AuthorityInterface;
806
962
  /**
807
- * Resolves a registered {@link SchedulerInterface} by name.
963
+ * Resolves a registered {@link SchedulerInterface} by name — throws `unknown scheduler: <name>` when
964
+ * absent.
808
965
  *
809
966
  * @param name - The scheduler's registry key
810
967
  * @returns The live scheduler
@@ -812,8 +969,9 @@ export declare interface AgentRegistryInterface {
812
969
  */
813
970
  scheduler(name: string): SchedulerInterface;
814
971
  /**
815
- * Rehydrates a live, seeded {@link AgentInterface} from a serializable job — resolving
816
- * its names, rebuilding its budget, seeding its conversation, and wiring `signal`.
972
+ * Rehydrates a live, seeded {@link AgentInterface} from a serializable {@link AgentJobInput}
973
+ * — resolving its names, rebuilding its token budget, seeding its conversation, and wiring
974
+ * `signal`; a name absent from its pool throws.
817
975
  *
818
976
  * @param input - The serializable {@link AgentJobInput} to rehydrate
819
977
  * @param signal - An optional cancel threaded into the agent (a queue / runner abort)
@@ -824,20 +982,21 @@ export declare interface AgentRegistryInterface {
824
982
  }
825
983
 
826
984
  /**
827
- * Configures `createAgentRegistry` — the named pools of live, non-serializable pieces
828
- * a {@link AgentJobInput}'s names resolve against.
985
+ * Configures `createAgentRegistry` — the named pools of live, non-serializable pieces an {@link
986
+ * AgentJobInput}'s names resolve against, plus the optional durable `store` every built agent's
987
+ * conversation manager shares.
829
988
  *
830
989
  * @remarks
831
990
  * `providers` is required (a job always names a provider); `tools` / `authorities` /
832
991
  * `schedulers` are optional pools, each an entity-keyed record mapping a registry
833
992
  * name to its live object. A name absent from its pool throws when resolved (see
834
993
  * {@link AgentRegistryInterface}). `store` is the durable {@link ConversationStoreInterface}
835
- * every agent this registry builds carries: each built agent gets its OWN store-backed
836
- * {@link ConversationManagerInterface} over THIS shared store — a fresh conversation id per
994
+ * every agent this registry builds carries: each built agent gets its own store-backed
995
+ * {@link ConversationManagerInterface} over this shared store — a fresh conversation id per
837
996
  * build (minted by the seeded `add`), so concurrent builds never collide, and the store
838
997
  * accumulates one snapshot per built agent that later calls `save`. Persistence
839
998
  * stays caller-triggered (`open` / `save`) — `build` never hydrates, so `build` stays
840
- * SYNCHRONOUS. Omitted ⇒ every built agent gets a registry-only manager, byte-identical
999
+ * synchronous. Omitted ⇒ every built agent gets a registry-only manager, byte-identical
841
1000
  * to today.
842
1001
  */
843
1002
  export declare interface AgentRegistryOptions {
@@ -857,13 +1016,13 @@ export declare interface AgentRegistryOptions {
857
1016
  * `partial` is `true` when the turn was committed early from a cancel — an external
858
1017
  * `signal` abort, the turn's own `abort()`, a `timeout` deadline, or an exhausted
859
1018
  * `budget` — in which case `content` is whatever had accumulated when the cancel
860
- * landed. `partial` is ALSO `true` when the loop exhausted its `limit` while still
1019
+ * landed. `partial` is also `true` when the loop exhausted its `limit` while still
861
1020
  * holding unresolved tool intent (the model requested tools on the very last allowed
862
1021
  * turn) — a distinct, non-cancel cause covered by {@link RunOutcome.exhausted} (see
863
1022
  * the `exhaust` {@link AgentEventMap} event). It is `false` for a turn that ran to a
864
1023
  * natural finish (including a `limit: 0` run, which never enters the loop). `usage` is
865
1024
  * present only when at least one provider call reported usage — an aborted run's `usage`
866
- * INCLUDES the cancelled turn's tokens when the provider reports partial usage on the
1025
+ * includes the cancelled turn's tokens when the provider reports partial usage on the
867
1026
  * abort (folded in exactly like a completed turn's); a provider that cannot observe
868
1027
  * usage mid-stream (for example a daemon whose final counts never arrive before the cancel)
869
1028
  * reports none for that turn, and none is fabricated. `thinking` is present
@@ -881,8 +1040,11 @@ export declare interface AgentResult {
881
1040
  }
882
1041
 
883
1042
  /**
884
- * Projects an unknown value onto the canonical JSON representation of an
885
- * {@link AgentResult}.
1043
+ * Projects an unknown value onto a fresh, exact `JSONValue` representation of an
1044
+ * {@link AgentResult} — capturing each structural field once through a total boundary,
1045
+ * accepting conforming accessors and inherited properties, preserving finite negative and
1046
+ * fractional usage counts, dropping extras, and resolving `undefined` for a malformed field, a
1047
+ * non-finite usage number, a throwing getter, or a hostile or revoked proxy.
886
1048
  *
887
1049
  * @remarks
888
1050
  * This is a total hostile-boundary projection. Each structural field is captured once
@@ -912,7 +1074,7 @@ export declare function agentResultToJSON(value: unknown): JSONValue | undefined
912
1074
  *
913
1075
  * @remarks
914
1076
  * Identical partial policy to {@link AgentQueueOptions} (`partial` — a partial
915
- * `AgentResult` THROWS by default so the run's fail-fast engages, `true` resolves it as
1077
+ * `AgentResult` throws by default so the run's fail-fast engages, `true` resolves it as
916
1078
  * success). `concurrency` / `retries` / `timeout` pass straight to the backing
917
1079
  * `RunnerInterface` (see `RunnerOptions`). The runner enables sub-agent fan-out: a
918
1080
  * parent job's handler can `controller.spawn(childJob)` to launch a child agent job
@@ -920,7 +1082,7 @@ export declare function agentResultToJSON(value: unknown): JSONValue | undefined
920
1082
  */
921
1083
  export declare interface AgentRunnerOptions {
922
1084
  readonly registry: AgentRegistryInterface;
923
- /** If `true`, a partial `AgentResult` resolves as success; if `false` (the default), it THROWS and fail-fast engages. */
1085
+ /** If `true`, a partial `AgentResult` resolves as success; if `false` (the default), it throws and fail-fast engages. */
924
1086
  readonly partial?: boolean;
925
1087
  readonly concurrency?: number;
926
1088
  readonly retries?: number;
@@ -929,14 +1091,15 @@ export declare interface AgentRunnerOptions {
929
1091
 
930
1092
  /**
931
1093
  * Carries the per-run override bag an {@link AgentInterface}'s `generate` / `stream` accepts — each
932
- * member overrides the matching {@link AgentOptions} value for ONE run.
1094
+ * member overrides the matching {@link AgentOptions} value for one run, where `think` and `schema`
1095
+ * forward to the provider call and `signal` composes with the constructed one.
933
1096
  *
934
1097
  * @remarks
935
1098
  * Every member is optional and resolved independently, so an omitted member leaves the
936
1099
  * agent's constructed value in force and a caller that passes no options runs exactly the
937
1100
  * agent it configured. `think` / `schema` ride through to the provider as
938
1101
  * {@link ProviderStreamOptions}; `limit` / `timeout` / `budget` replace their construction
939
- * defaults for this run only; `signal` COMPOSES with the constructed `signal` (both fold into
1102
+ * defaults for this run only; `signal` composes with the constructed `signal` (both fold into
940
1103
  * the run's bound abort) rather than replacing it. Nothing here mutates the agent — the next
941
1104
  * run reads the construction defaults again.
942
1105
  */
@@ -956,23 +1119,23 @@ export declare interface AgentRunOptions {
956
1119
  */
957
1120
  readonly schema?: Readonly<Record<string, unknown>>;
958
1121
  /**
959
- * Overrides {@link AgentOptions.limit} for THIS run only — the max tool-iteration turns
1122
+ * Overrides {@link AgentOptions.limit} for this run only — the max tool-iteration turns
960
1123
  * before the loop stops. Omitted ⇒ the agent's constructed `limit` applies.
961
1124
  */
962
1125
  readonly limit?: number;
963
1126
  /**
964
- * Overrides {@link AgentOptions.timeout} for THIS run only — a wall-clock deadline (ms)
1127
+ * Overrides {@link AgentOptions.timeout} for this run only — a wall-clock deadline (ms)
965
1128
  * whose abort commits a partial result. Omitted ⇒ the agent's constructed `timeout` applies.
966
1129
  */
967
1130
  readonly timeout?: number;
968
1131
  /**
969
- * Overrides {@link AgentOptions.budget} for THIS run only — a token cost bound whose abort
1132
+ * Overrides {@link AgentOptions.budget} for this run only — a token cost bound whose abort
970
1133
  * commits a partial result; `start()`ed for this run exactly as the constructed budget is.
971
1134
  * Omitted ⇒ the agent's constructed `budget` applies.
972
1135
  */
973
1136
  readonly budget?: BudgetInterface<TokenUsage>;
974
1137
  /**
975
- * Carries an additional per-run external cancel, COMPOSED with {@link AgentOptions.signal} (both
1138
+ * Carries an additional per-run external cancel, composed with {@link AgentOptions.signal} (both
976
1139
  * fold into the run's bound abort through `AbortSignal.any` — neither is dropped). Omitted ⇒
977
1140
  * only the agent's constructed `signal` (if any) applies.
978
1141
  */
@@ -994,10 +1157,11 @@ export declare type AgentStreamInterface = StreamInterface<AgentChunk, AgentResu
994
1157
 
995
1158
  /**
996
1159
  * Assembles the settled {@link AgentResult} from a run's {@link RunOutcome} — `thinking` and
997
- * `usage` are carried only when the run surfaced them.
1160
+ * `usage` are carried only when the run surfaced them, and the loop-internal `exhausted` flag is
1161
+ * left out.
998
1162
  *
999
1163
  * @remarks
1000
- * Pure and total. An absent optional is OMITTED rather than stored as `undefined` (the
1164
+ * Pure and total. An absent optional is omitted rather than stored as `undefined` (the
1001
1165
  * present-when-given convention the message store follows), so a settled result JSON
1002
1166
  * round-trips without an explicit `undefined` field. `exhausted` is loop bookkeeping and does
1003
1167
  * not reach the public result — the `exhaust` event carries it instead.
@@ -1014,11 +1178,11 @@ export declare type AgentStreamInterface = StreamInterface<AgentChunk, AgentResu
1014
1178
  export declare function assembleResult(outcome: RunOutcome): AgentResult;
1015
1179
 
1016
1180
  /**
1017
- * Copies a message with image data merged onto its `images` — the message's own images
1018
- * first, then the attached data.
1181
+ * Copies a message with image data merged onto its `images` — the message's own images first,
1182
+ * then the attached data, carrying `calls` only when present and never mutating the original.
1019
1183
  *
1020
1184
  * @remarks
1021
- * Pure and total: the original message is NEVER mutated. `calls` is carried only when the
1185
+ * Pure and total: the original message is never mutated. `calls` is carried only when the
1022
1186
  * source message has one (kept omitted otherwise, mirroring the store's present-when-given
1023
1187
  * convention).
1024
1188
  *
@@ -1035,13 +1199,14 @@ export declare function assembleResult(outcome: RunOutcome): AgentResult;
1035
1199
  export declare function attachImages(message: Message, data: readonly string[]): Message;
1036
1200
 
1037
1201
  /**
1038
- * Attaches image data to a conversation's LAST user message — the turn a vision provider
1039
- * reads images off.
1202
+ * Attaches image data to a conversation's last user message — the turn a vision provider reads
1203
+ * images off — as a new array with that one message replaced by its carrying copy, and unchanged
1204
+ * when there is no data or no user turn.
1040
1205
  *
1041
1206
  * @remarks
1042
- * Pure and total: the conversation and its messages are NEVER mutated, and the returned array
1207
+ * Pure and total: the conversation and its messages are never mutated, and the returned array
1043
1208
  * replaces exactly the one target message with the copy {@link attachImages} builds. Empty
1044
- * data returns the conversation unchanged; a conversation with NO user message returns it
1209
+ * data returns the conversation unchanged; a conversation with no user message returns it
1045
1210
  * unchanged too (there is nowhere to attach, and the images already rode the system block).
1046
1211
  *
1047
1212
  * @param conversation - The messages to attach into (left unchanged)
@@ -1057,18 +1222,20 @@ export declare function attachImages(message: Message, data: readonly string[]):
1057
1222
  export declare function attachUserImages(conversation: readonly Message[], data: readonly string[]): readonly Message[];
1058
1223
 
1059
1224
  /**
1060
- * Gates the agent loop's tool calls — the synchronous policy consulted before each call
1061
- * runs, turning one {@link AuthorityContext} into an {@link AuthorityDecision}.
1225
+ * Gates the agent loop's tool calls — the synchronous policy consulted before each call runs,
1226
+ * turning one {@link AuthorityContext} into an {@link AuthorityDecision} by walking the ordered
1227
+ * rules first-match-wins and falling back to a configurable default, which allows an unmatched
1228
+ * call unless its `fallback` denies.
1062
1229
  *
1063
1230
  * @remarks
1064
1231
  * - **Ordered, first-match-wins.** `evaluate` walks the configured rules in order and
1065
- * returns the FIRST whose `match(context)` is true as
1066
- * `{ zone, allowed: rule.allowed ?? true, reason }` — a matched rule ALLOWS by
1067
- * default and DENIES only when its `allowed` is explicitly `false`.
1232
+ * returns the first whose `match(context)` is true as
1233
+ * `{ zone, allowed: rule.allowed ?? true, reason }` — a matched rule allows by
1234
+ * default and denies only when its `allowed` is explicitly `false`.
1068
1235
  * - **Fallback.** When no rule matches, `evaluate` returns the configured `fallback`.
1069
1236
  * It defaults to `{ zone: DEFAULT_AUTHORITY_ZONE, allowed: true }` (allow-unmatched),
1070
- * so a rules list of denials behaves as a DENYLIST. To make the gate deny-by-default
1071
- * (an ALLOWLIST — only matched rules that allow get through), pass an `allowed: false`
1237
+ * so a rules list of denials behaves as a denylist. To make the gate deny-by-default
1238
+ * (an allowlist — only matched rules that allow get through), pass an `allowed: false`
1072
1239
  * `fallback`.
1073
1240
  * - **Consulted before each tool call.** The agent loop calls `evaluate({ call })` for
1074
1241
  * every {@link import('@orkestrel/tool').ToolCall} the model emits; a denied call is fed
@@ -1133,7 +1300,8 @@ export declare interface AuthorityDecision {
1133
1300
  */
1134
1301
  export declare interface AuthorityInterface {
1135
1302
  /**
1136
- * Evaluates one tool call against the ordered rules.
1303
+ * Evaluates one tool call against the ordered rules — returns the first matching rule's
1304
+ * verdict, which allows unless `allowed: false`, or the fallback when none match.
1137
1305
  *
1138
1306
  * @param context - The call under consideration (see {@link AuthorityContext})
1139
1307
  * @returns The first matching rule's verdict, or the fallback when none match
@@ -1146,7 +1314,7 @@ export declare interface AuthorityInterface {
1146
1314
  *
1147
1315
  * @remarks
1148
1316
  * `rules` are evaluated in order, first match wins (see {@link AuthorityRule}).
1149
- * `fallback` is the {@link AuthorityDecision} returned when NO rule matches; it
1317
+ * `fallback` is the {@link AuthorityDecision} returned when no rule matches; it
1150
1318
  * defaults to `{ zone: DEFAULT_AUTHORITY_ZONE, allowed: true }` (allow-unmatched — a
1151
1319
  * rules list of denials acts as a denylist). Set `fallback` to an `allowed: false`
1152
1320
  * decision to flip the gate to deny-by-default (an allowlist — only matched rules
@@ -1161,8 +1329,8 @@ export declare interface AuthorityOptions {
1161
1329
  * Represents one ordered policy rule an {@link AuthorityInterface} evaluates.
1162
1330
  *
1163
1331
  * @remarks
1164
- * The FIRST rule whose `match` returns true decides; if none match, the authority's
1165
- * `fallback` decides. A matched rule ALLOWS by default and DENIES only when its
1332
+ * The first rule whose `match` returns true decides; if none match, the authority's
1333
+ * `fallback` decides. A matched rule allows by default and denies only when its
1166
1334
  * `allowed` is explicitly `false`. `zone` classifies the matched call; `reason` is the
1167
1335
  * optional explanation carried into the {@link AuthorityDecision} (and, on a denial,
1168
1336
  * into the denial {@link ToolResult}).
@@ -1175,13 +1343,28 @@ export declare interface AuthorityRule {
1175
1343
  }
1176
1344
 
1177
1345
  /**
1178
- * Builds the FRAMED recap message for one compacted section — the same role and stable `id`
1179
- * as {@link buildSummaryMessage}, with the content prefixed by
1180
- * {@link import('./constants.js').CONVERSATION_RECAP_PREFIX}.
1346
+ * Assembles a provider result with only populated optional fields.
1347
+ *
1348
+ * @param content - The authoritative answer
1349
+ * @param thinking - The joined reasoning
1350
+ * @param tools - The accumulated calls
1351
+ * @param usage - The reported token usage
1352
+ * @returns The assembled result
1353
+ * @example
1354
+ * ```ts
1355
+ * buildProviderResult('ok', '', [], undefined) // { content: 'ok' }
1356
+ * ```
1357
+ */
1358
+ export declare function buildProviderResult(content: string, thinking: string, tools: readonly ToolCall[], usage: TokenUsage | undefined): ProviderResult;
1359
+
1360
+ /**
1361
+ * Builds the framed recap message for one compacted section — the same role and stable `id` as
1362
+ * {@link buildSummaryMessage}, with the content prefixed by {@link
1363
+ * import('./constants.js').CONVERSATION_RECAP_PREFIX}.
1181
1364
  *
1182
1365
  * @remarks
1183
- * Pure and total. The prefix is what makes a small model read the message as a CONDENSED
1184
- * RECAP of earlier turns rather than a literal assistant turn to echo or answer from. It is a
1366
+ * Pure and total. The prefix is what makes a small model read the message as a condensed
1367
+ * recap of earlier turns rather than a literal assistant turn to echo or answer from. It is a
1185
1368
  * fixed handful of tokens, so a conversation's `view()` stays lean however many sections it
1186
1369
  * carries.
1187
1370
  *
@@ -1197,12 +1380,12 @@ export declare interface AuthorityRule {
1197
1380
  export declare function buildRecapMessage(section: Section): Message;
1198
1381
 
1199
1382
  /**
1200
- * Builds the RAW synthetic summary message for one compacted section — role `'assistant'`,
1201
- * the section's stable `id`, its `summary` VERBATIM as content.
1383
+ * Builds the raw synthetic summary message for one compacted section — role `'assistant'`, the
1384
+ * section's stable `id`, and its `summary` verbatim as content.
1202
1385
  *
1203
1386
  * @remarks
1204
1387
  * Pure and total. This is the unframed form the rollup regeneration digests (a
1205
- * summary-of-summaries over the section summaries); the recap LABEL is a `view()`
1388
+ * summary-of-summaries over the section summaries); the recap label is a `view()`
1206
1389
  * presentation concern kept out of what the summarizer re-reads — see
1207
1390
  * {@link buildRecapMessage}.
1208
1391
  *
@@ -1218,10 +1401,11 @@ export declare function buildRecapMessage(section: Section): Message;
1218
1401
  export declare function buildSummaryMessage(section: Section): Message;
1219
1402
 
1220
1403
  /**
1221
- * Buffers chunks in a minimal unbounded async channel — the eager pump WRITES them in
1222
- * (`push`) and ends it (`close` / `fail`) regardless of consumption; a consumer READS them back
1223
- * live through the `drain` async-iterator. Decoupling write from read is what lets a
1224
- * producer make progress without a consumer pulling.
1404
+ * Buffers chunks in a minimal unbounded async channel — the eager pump writes them in (`push`)
1405
+ * and ends it (`close` / `fail`) regardless of consumption, while a consumer reads them back live
1406
+ * through the `drain` async-iterator. Decoupling write from read is what lets a producer make
1407
+ * progress without a consumer pulling, and it is why an agent's `result` settles whether or not
1408
+ * its `events` are drained.
1225
1409
  *
1226
1410
  * @remarks
1227
1411
  * The standard resolver-swap: a waiting `drain` parks on `#wake` (a void resolver);
@@ -1250,24 +1434,25 @@ export declare class Channel<T> implements ChannelInterface<T> {
1250
1434
  }
1251
1435
 
1252
1436
  /**
1253
- * Buffers values in an unbounded async channel — a producer WRITES them in (`push`) and
1254
- * ends it (`close` / `fail`) regardless of consumption, while a consumer READS them back
1255
- * live through `drain`.
1437
+ * Buffers values in an unbounded async channel — a producer writes them in (`push`) and ends it
1438
+ * (`close` / `fail`) regardless of consumption, while a consumer reads them back live through
1439
+ * `drain`.
1256
1440
  *
1257
1441
  * @remarks
1258
1442
  * Decoupling the write from the read is what lets a producer make progress with nobody
1259
1443
  * pulling: an agent's eager pump writes each {@link AgentChunk} into one, so the run's
1260
1444
  * `result` settles whether or not `events` is ever drained. A waiting `drain` parks on a
1261
1445
  * resolver the next `push` / `close` / `fail` fires, so a value pushed at a parked reader is
1262
- * delivered rather than dropped. Buffered values are always yielded BEFORE the end is
1263
- * reported, so a `close` arriving alongside the last values still delivers them. The FIRST
1446
+ * delivered rather than dropped. Buffered values are always yielded before the end is
1447
+ * reported, so a `close` arriving alongside the last values still delivers them. The first
1264
1448
  * failure wins — a later `close` / `fail` cannot override a recorded error. Event-free.
1265
1449
  *
1266
1450
  * @typeParam T - The value type the channel carries
1267
1451
  */
1268
1452
  export declare interface ChannelInterface<T> {
1269
1453
  /**
1270
- * Writes one value — buffered, then handed to a parked consumer.
1454
+ * Writes one value — buffered, then handed to a parked consumer; a value pushed at an
1455
+ * already-parked reader is delivered, never dropped.
1271
1456
  *
1272
1457
  * @param value - The value to enqueue
1273
1458
  */
@@ -1275,13 +1460,14 @@ export declare interface ChannelInterface<T> {
1275
1460
  /** Ends the channel normally — a draining consumer returns once the buffer is empty. */
1276
1461
  close(): void;
1277
1462
  /**
1278
- * Ends the channel with a failure — a draining consumer throws it once the buffer is empty.
1463
+ * Ends the channel with a failure — a draining consumer throws it once the buffer is empty;
1464
+ * the first failure wins.
1279
1465
  *
1280
1466
  * @param error - The failure to surface (the first one recorded wins)
1281
1467
  */
1282
1468
  fail(error: unknown): void;
1283
1469
  /**
1284
- * Reads the values back live, in write order.
1470
+ * Reads the values back live, in write order — returning on `close` and throwing on `fail`.
1285
1471
  *
1286
1472
  * @returns A generator yielding each pushed value, returning on `close` and throwing on `fail`
1287
1473
  */
@@ -1289,11 +1475,11 @@ export declare interface ChannelInterface<T> {
1289
1475
  }
1290
1476
 
1291
1477
  /**
1292
- * Collects the `base64` payload of the IMAGE files in a workspace file list — the data an
1293
- * agent context attaches to the last user message.
1478
+ * Collects the `base64` payload of the image files in a workspace file list — the data an agent
1479
+ * context attaches to the last user message.
1294
1480
  *
1295
1481
  * @remarks
1296
- * Pure and total. `isBinary` NARROWS the tagless content to its binary arm (a total guard,
1482
+ * Pure and total. `isBinary` narrows the tagless content to its binary arm (a total guard,
1297
1483
  * never an assertion), then the MIME prefix gates it to an image, so a text file and a non-image
1298
1484
  * binary (a PDF) are both skipped. Order follows the file list.
1299
1485
  *
@@ -1309,14 +1495,15 @@ export declare interface ChannelInterface<T> {
1309
1495
  export declare function collectImageData(files: readonly FileInterface[]): readonly string[];
1310
1496
 
1311
1497
  /**
1312
- * Configures one {@link ConversationInterface.compact} call — overrides for ONE fold.
1498
+ * Configures one {@link ConversationInterface.compact} call — the retained-tail size, the
1499
+ * `sections` cap, or both, overridden for one fold.
1313
1500
  *
1314
1501
  * @remarks
1315
- * `keep` overrides the conversation's configured retained-tail size for THIS compaction only
1502
+ * `keep` overrides the conversation's configured retained-tail size for this compaction only
1316
1503
  * (the older `count - keep` live messages fold; when `count <= keep` nothing folds and
1317
1504
  * `compact()` is a no-op returning `undefined`). Omitted ⇒ the conversation's own `keep`
1318
1505
  * (its option, or `DEFAULT_CONVERSATION_KEEP`) applies. `sections` overrides the conversation's
1319
- * configured `sections` cap for THIS compaction only — after the new section is pushed, an
1506
+ * configured `sections` cap for this compaction only — after the new section is pushed, an
1320
1507
  * overflow past `sections` folds the oldest sections into one merged section. Omitted ⇒ the
1321
1508
  * conversation's own `sections` cap (or unlimited) applies.
1322
1509
  */
@@ -1328,17 +1515,17 @@ export declare interface CompactOptions {
1328
1515
  }
1329
1516
 
1330
1517
  /**
1331
- * Holds a provider's OPTIONAL context-framing default, keyed by section kind — the framing a
1332
- * model prefers (for example XML tags against Markdown headers), declared by a
1333
- * {@link ProviderInterface} that opts in.
1518
+ * Holds a provider's optional context-framing default, keyed by section kind — the framing a model
1519
+ * prefers (for example XML tags against Markdown headers), declared by a {@link ProviderInterface}
1520
+ * that opts in.
1334
1521
  *
1335
1522
  * @remarks
1336
1523
  * Each key is a {@link ContextSectionFormat} for one of the observable context sections
1337
1524
  * (the `instructions` section), so a provider can frame each section independently — and any
1338
- * it omits falls through to that manager's built-in default. It is the PROVIDER-DEFAULT
1339
- * level of the {@link import('./AgentContext.js').AgentContext} build cascade: it BEATS a
1340
- * manager's built-in default but is BEATEN by a manager-options override and by a per-item
1341
- * override (see {@link AgentContextInterface.build}). It references the ABSTRACT core
1525
+ * it omits falls through to that manager's built-in default. It is the provider-default
1526
+ * level of the {@link import('./AgentContext.js').AgentContext} build cascade: it beats a
1527
+ * manager's built-in default but is beaten by a manager-options override and by a per-item
1528
+ * override (see {@link AgentContextInterface.build}). It references the abstract core
1342
1529
  * item interface ({@link InstructionInterface}), so a provider opting in imports it from
1343
1530
  * `@orkestrel/agent` — the type is provider-agnostic, with no backend coupling. Omitting it
1344
1531
  * entirely (the default for an agnostic provider) leaves every section on its manager's
@@ -1356,17 +1543,17 @@ export declare interface ContextFormat {
1356
1543
  * line rendered once after the items.
1357
1544
  *
1358
1545
  * @remarks
1359
- * `open`, `render`, and `close` are OPTIONAL and resolved INDEPENDENTLY (so an override may set only
1546
+ * `open`, `render`, and `close` are optional and resolved independently (so an override may set only
1360
1547
  * the top, only the per-item rendering, only the bottom, or any mix). A section assembles
1361
1548
  * as `[open, ...items.map(render), close]` with empty / absent slots dropped, the survivors
1362
- * blank-line (`\n\n`) joined — so `open` + `close` together let a developer WRAP the whole
1549
+ * blank-line (`\n\n`) joined — so `open` + `close` together let a developer wrap the whole
1363
1550
  * group (for example `open: '<instructions>'` … `close: '</instructions>'`). `open` is the
1364
1551
  * section's leading text (the header, or a group's opening tag); `render` turns one section
1365
1552
  * item (an {@link InstructionInterface}) into its prompt text; `close` is the trailing text.
1366
1553
  * `open` and `render` cascade through the
1367
1554
  * built-in floor (`open` ⇒ the manager's built-in header, `render` ⇒ the manager's built-in
1368
- * rendering); `close` has NO built-in, so an unset `close` yields no closing line.
1369
- * It is the unit BOTH a provider's {@link ContextFormat} (a per-section-kind
1555
+ * rendering); `close` has no built-in, so an unset `close` yields no closing line.
1556
+ * It is the unit both a provider's {@link ContextFormat} (a per-section-kind
1370
1557
  * default) and a manager's `Options` carry — see {@link AgentContextInterface.build} for
1371
1558
  * the full precedence.
1372
1559
  *
@@ -1374,7 +1561,7 @@ export declare interface ContextFormat {
1374
1561
  */
1375
1562
  export declare interface ContextSectionFormat<T> {
1376
1563
  /**
1377
- * Holds text rendered ONCE before the section's items — the section header or a group's
1564
+ * Holds text rendered once before the section's items — the section header or a group's
1378
1565
  * opening wrapper, for example `'<instructions>'`; omitted ⇒ the next cascade level decides
1379
1566
  * (defaulting to the built-in header).
1380
1567
  */
@@ -1382,25 +1569,25 @@ export declare interface ContextSectionFormat<T> {
1382
1569
  /** Overrides one item's rendering; omitted ⇒ the next cascade level decides. */
1383
1570
  readonly render?: (item: T) => string;
1384
1571
  /**
1385
- * Holds text rendered ONCE after the section's items — a group's closing wrapper, for
1572
+ * Holds text rendered once after the section's items — a group's closing wrapper, for
1386
1573
  * example `'</instructions>'`; omitted ⇒ no closing line (there is no built-in close).
1387
1574
  */
1388
1575
  readonly close?: string;
1389
1576
  }
1390
1577
 
1391
1578
  /**
1392
- * Exposes the manager surface one context section's format cascade reads — its built-in
1393
- * `open` / `render`, plus the raw options override the cascade layers a provider
1394
- * default beneath.
1579
+ * Exposes the manager surface one context section's format cascade reads — its built-in `open` /
1580
+ * `render`, plus the raw options override the cascade layers a provider default beneath; {@link
1581
+ * InstructionManagerInterface} satisfies it structurally.
1395
1582
  *
1396
1583
  * @remarks
1397
1584
  * The narrow contract the cascade resolvers
1398
1585
  * ({@link import('./helpers.js').resolveOpen} / {@link import('./helpers.js').resolveClose} /
1399
- * {@link import('./helpers.js').resolveItem}) take, so they stay independent of WHICH manager
1586
+ * {@link import('./helpers.js').resolveItem}) take, so they stay independent of which manager
1400
1587
  * supplies the section: an {@link InstructionManagerInterface} satisfies it structurally.
1401
1588
  * `open` and `render` already encapsulate `[options-override → built-in]` (so a manager
1402
1589
  * used standalone renders correctly), and `format` exposes the raw override so `build()` can
1403
- * interleave the provider default BENEATH it.
1590
+ * interleave the provider default beneath it.
1404
1591
  *
1405
1592
  * @typeParam T - The section item this source renders
1406
1593
  */
@@ -1409,41 +1596,45 @@ export declare interface ContextSectionSourceInterface<T> {
1409
1596
  readonly open: string;
1410
1597
  /** Holds the raw manager-options override, or `undefined` when none was supplied. */
1411
1598
  readonly format: ContextSectionFormat<T> | undefined;
1412
- /** Renders one item (already resolved against the manager-options override). */
1599
+ /**
1600
+ * Renders one section item, already resolved against the manager-options override and
1601
+ * otherwise on the built-in rendering.
1602
+ */
1413
1603
  render(item: T): string;
1414
1604
  }
1415
1605
 
1416
1606
  /**
1417
- * Represents a conversation — a live uncompacted tail of messages it OWNS DIRECTLY ABOVE a
1418
- * flat message store, plus compacted, summarized {@link Section}s and a regenerated rollup
1419
- * `summary`, with on-demand `rehydrate` and substring `search`, driven by a provider-agnostic
1420
- * {@link ConversationSummaryHandler} seam (so `core` never imports a provider).
1607
+ * Represents a conversation — a live uncompacted tail of messages it owns directly above a flat
1608
+ * message store, plus compacted, summarized {@link Section}s, a regenerated rollup `summary`, and
1609
+ * a `summarizable` flag, with on-demand `rehydrate` and substring `search`, driven by a
1610
+ * provider-agnostic {@link ConversationSummaryHandler} seam so `core` never imports a provider.
1611
+ * Observable through its own `emitter`.
1421
1612
  *
1422
1613
  * @remarks
1423
- * - **Live tail + sections.** The conversation OWNS its live tail DIRECTLY — `#messages` is an
1614
+ * - **Live tail + sections.** The conversation owns its live tail directly — `#messages` is an
1424
1615
  * insertion-ordered `Map` of immutable {@link Message}s keyed by their minted id
1425
- * (the SAME store mechanics a flat manager had, folded in: `add` / `message` / `messages` /
1616
+ * (the same store mechanics a flat manager had, folded in: `add` / `message` / `messages` /
1426
1617
  * `remove` / `clear` / `count`), exactly as a `Workspace` owns its files (no separate
1427
1618
  * per-value manager). `#sections` are the compacted history (oldest → newest), each a
1428
- * summarized slice that RETAINS its originals. `#summary` is the rollup (a
1619
+ * summarized slice that retains its originals. `#summary` is the rollup (a
1429
1620
  * summary-of-summaries over all sections), regenerated on each compaction (`undefined`
1430
1621
  * until the first).
1431
- * - **`view()`.** Each section folds to ONE synthetic summary message (role `'assistant'` — a
1622
+ * - **`view()`.** Each section folds to one synthetic summary message (role `'assistant'` — a
1432
1623
  * prior-context recap — keyed by the section's stable `id`), then the live messages
1433
- * verbatim. The rollup `summary` is NOT injected (it is separately pull-able); `view()`
1434
- * carries the per-section summaries, which ARE the compaction benefit.
1624
+ * verbatim. The rollup `summary` is not injected (it is separately pull-able); `view()`
1625
+ * carries the per-section summaries, which are the compaction benefit.
1435
1626
  * - **`compact()`.** Folds the oldest `count - keep` live messages into a new section
1436
1627
  * (its `summary` from `#summarize`), removes them from the live tail by id, regenerates the
1437
- * rollup (a SECOND `#summarize` over all section summaries), and emits `summary` then
1628
+ * rollup (a second `#summarize` over all section summaries), and emits `summary` then
1438
1629
  * `compact`. Returns the section, or `undefined` when nothing folds (`count <= keep`).
1439
- * THROWS a {@link ConversationError} when no `#summarize` was supplied. Two summarizer calls
1440
- * per compaction.
1630
+ * Throws a {@link ConversationError} when no `#summarize` was supplied. A compaction calls
1631
+ * the summarizer for the section digest and again for the rollup.
1441
1632
  * - **`rehydrate(id)` / `search(query)`.** `rehydrate` returns a section's full original
1442
1633
  * messages (`[]` for an unknown id) and emits `rehydrate` — a pure read (the caller decides
1443
1634
  * whether to re-add them; `rehydrate` never reinserts). `search` is a case-insensitive
1444
- * substring scan of `content` across ALL messages (every section's originals + the live tail).
1635
+ * substring scan of `content` across all messages (every section's originals + the live tail).
1445
1636
  * - **Observable.** The owned {@link emitter} ({@link ConversationEventMap}) carries
1446
- * `compact` / `summary` / `rehydrate`, emitted directly, strictly AFTER the state change;
1637
+ * `compact` / `summary` / `rehydrate`, emitted directly, strictly after the state change;
1447
1638
  * the emitter isolates a listener throw and routes it to its `error` handler (the `error`
1448
1639
  * option), so a buggy observer can never corrupt a compaction.
1449
1640
  *
@@ -1485,12 +1676,13 @@ export declare class Conversation implements ConversationInterface {
1485
1676
 
1486
1677
  /**
1487
1678
  * Names the framing label a {@link ConversationInterface}'s `view()` prefixes onto each compacted
1488
- * section's summary so a small model reads it as a CONDENSED RECAP of earlier turns — not a
1489
- * literal assistant turn to echo or treat as the live answer.
1679
+ * section's summary so a small model reads it as a condensed recap of earlier turns — the lean
1680
+ * `'[Summary of earlier messages] '` marker, never a literal assistant turn to echo or treat as
1681
+ * the live answer.
1490
1682
  *
1491
1683
  * @remarks
1492
- * Deliberately a FIXED, lean handful of tokens (a short bracketed marker) so the framing adds a
1493
- * bounded `prefix × sections` overhead and NEVER an open-ended blow-up — the
1684
+ * Deliberately a fixed, lean handful of tokens (a short bracketed marker) so the framing adds a
1685
+ * bounded `prefix × sections` overhead and never an open-ended blow-up — the
1494
1686
  * {@link ConversationInterface} no-bloat test guard pins exactly that. Kept here (beside
1495
1687
  * {@link DEFAULT_CONVERSATION_KEEP}) as the conversation layer's one tunable framing constant, so
1496
1688
  * the wording has a single source of truth as it is optimized against real small-model behavior
@@ -1500,12 +1692,13 @@ export declare class Conversation implements ConversationInterface {
1500
1692
  export declare const CONVERSATION_RECAP_PREFIX = "[Summary of earlier messages] ";
1501
1693
 
1502
1694
  /**
1503
- * Reports a conversation with no {@link ConversationSummaryHandler} to fold its messages
1504
- * with, or with a structurally invalid `sections` cap — thrown by a
1505
- * {@link ConversationInterface}'s `compact()`, carrying a machine-readable `code`.
1695
+ * Reports a conversation with no {@link ConversationSummaryHandler} to fold its messages with,
1696
+ * or with a `sections` cap below `1` — thrown by a {@link ConversationInterface}'s `compact()`
1697
+ * or its construction, carrying the machine-readable `code`
1698
+ * `'SUMMARIZER' | 'SECTIONS'`.
1506
1699
  *
1507
1700
  * @remarks
1508
- * Compaction REQUIRES a summarizer (it digests the folded slice into a section summary and
1701
+ * Compaction requires a summarizer (it digests the folded slice into a section summary and
1509
1702
  * regenerates the rollup); a conversation created without one can still store + `view()` its
1510
1703
  * live tail, but a `compact()` is a programmer error and throws this with `'SUMMARIZER'`.
1511
1704
  * A `sections` cap (on {@link import('./types.js').ConversationOptions} /
@@ -1526,7 +1719,7 @@ export declare class ConversationError extends Error {
1526
1719
  *
1527
1720
  * @remarks
1528
1721
  * `compact` carries the newly-folded {@link Section}; `collapse` carries a section
1529
- * created by folding multiple OLDER sections together (a bounded-`sections` cap enforcement,
1722
+ * created by folding multiple older sections together (a bounded-`sections` cap enforcement,
1530
1723
  * distinct from `compact`'s fresh live-tail fold); `summary` carries the regenerated
1531
1724
  * conversation rollup (refreshed on each compaction); `rehydrate` carries the `id` of a
1532
1725
  * section whose originals were pulled back. Listener isolation is the emitter's:
@@ -1543,31 +1736,32 @@ export declare type ConversationEventMap = {
1543
1736
  /** Reports a section's original messages pulled back — the section's `id`. */
1544
1737
  readonly rehydrate: readonly [id: string];
1545
1738
  /**
1546
- * Reports the bounded-`sections` cap folding the oldest sections into ONE merged section — the
1739
+ * Reports the bounded-`sections` cap folding the oldest sections into one merged section — the
1547
1740
  * merged {@link Section} that replaced them.
1548
1741
  */
1549
1742
  readonly collapse: readonly [section: Section];
1550
1743
  };
1551
1744
 
1552
1745
  /**
1553
- * Carries the data to author a {@link ConversationInterface} through a
1554
- * {@link ConversationManagerInterface} — the optional `id`, a `summarize` override, a `keep`
1555
- * override, and the reserved `on` hooks.
1746
+ * Carries the data to author a {@link ConversationInterface} through a {@link
1747
+ * ConversationManagerInterface} — the optional `id`, a `summarize` override, a `keep` override, a
1748
+ * `sections` cap override, the reserved `on` hooks, and a {@link ConversationSnapshot} to hydrate
1749
+ * from.
1556
1750
  *
1557
1751
  * @remarks
1558
- * `id` is the conversation's identity (minted when omitted). `summarize` OVERRIDES the
1752
+ * `id` is the conversation's identity (minted when omitted). `summarize` overrides the
1559
1753
  * manager's default {@link ConversationSummaryHandler} for this conversation (omitted ⇒ the
1560
1754
  * manager's default flows in). `keep` overrides the manager's default retained-tail size.
1561
1755
  * `sections` overrides the manager's default `sections` cap. `on` is the reserved listener key
1562
1756
  * (initial {@link ConversationEventMap} listeners). `snapshot` is
1563
1757
  * the construction-time hydration seam — a {@link ConversationSnapshot} whose `id` / `summary` /
1564
- * `sections` / live tail are RESTORED into the new conversation (the live `summarize` / `keep` /
1758
+ * `sections` / live tail are restored into the new conversation (the live `summarize` / `keep` /
1565
1759
  * `on` re-supplied alongside it), the conversation analogue of
1566
1760
  * {@link import('@orkestrel/workspace').WorkspaceOptions}'s `seed`, carried onto
1567
1761
  * {@link ConversationOptions.snapshot}, that a
1568
1762
  * {@link ConversationManagerInterface.open} reads a stored snapshot back through; hydration is
1569
1763
  * silent (no events). When both `snapshot.id` and `id` are given, `snapshot.id` wins (the snapshot
1570
- * IS the conversation's identity).
1764
+ * is the conversation's identity).
1571
1765
  */
1572
1766
  export declare interface ConversationInput {
1573
1767
  readonly id?: string;
@@ -1583,52 +1777,53 @@ export declare interface ConversationInput {
1583
1777
  }
1584
1778
 
1585
1779
  /**
1586
- * Groups messages ABOVE the flat {@link MessageManagerInterface} — a live
1587
- * uncompacted tail plus compacted, summarized {@link Section}s and a conversation
1588
- * rollup `summary`, with on-demand `rehydrate` and substring `search`, driven by a
1589
- * provider-agnostic {@link ConversationSummaryHandler} seam.
1780
+ * Groups messages above the flat {@link MessageManagerInterface} — a live uncompacted tail plus
1781
+ * compacted, summarized {@link Section}s and a conversation rollup `summary`, with on-demand
1782
+ * `rehydrate`, substring `search`, a cross-conversation `reference`, and a JSON `snapshot`, driven
1783
+ * by a provider-agnostic {@link ConversationSummaryHandler} seam; `summarizable` reports whether
1784
+ * that seam was supplied, and the agent loop gates automatic compaction on it.
1590
1785
  *
1591
1786
  * @remarks
1592
- * - **Live tail + sections.** The conversation OWNS its LIVE uncompacted tail DIRECTLY — a
1787
+ * - **Live tail + sections.** The conversation owns its live uncompacted tail directly — a
1593
1788
  * caller appends turns through its own message verbs (`add` mints each `id`, `message` /
1594
1789
  * `messages` look up, `remove` / `clear` drop, `count` tallies), exactly as a `Workspace`
1595
1790
  * owns its files (no separate per-value manager). `sections` are the compacted history
1596
- * (oldest → newest), each a summarized slice that RETAINS its originals. `summary` is the
1791
+ * (oldest → newest), each a summarized slice that retains its originals. `summary` is the
1597
1792
  * conversation rollup (a summary-of-summaries over all sections), regenerated on each
1598
1793
  * compaction (`undefined` until the first compaction).
1599
1794
  * - **Message verbs (the inlined store).** `add` takes one {@link MessageInput} or a batch,
1600
- * MINTS each message's `id` (a random UUID), stores it, and returns the created
1795
+ * mints each message's `id` (a random UUID), stores it, and returns the created
1601
1796
  * message(s); a stored message is immutable. `message(id)` resolves one (`undefined` when
1602
1797
  * absent); `messages()` lists the live tail in insertion order; `remove` drops one by id or
1603
- * a batch (`true` only when EVERY supplied id was removed); `clear` empties the tail;
1798
+ * a batch (`true` only when every supplied id was removed); `clear` empties the tail;
1604
1799
  * `count` is how many live messages are stored.
1605
- * - **`view()` — the model input.** Each section folds to ONE synthetic summary message,
1800
+ * - **`view()` — the model input.** Each section folds to one synthetic summary message,
1606
1801
  * followed by the live messages verbatim: `[...sections-as-summary-messages, ...live]`. The
1607
- * rollup `summary` is NOT injected (it is a separately pull-able digest for a
1608
- * cross-conversation case); `view()` carries the per-section summaries, which ARE the
1802
+ * rollup `summary` is not injected (it is a separately pull-able digest for a
1803
+ * cross-conversation case); `view()` carries the per-section summaries, which are the
1609
1804
  * compaction benefit.
1610
1805
  * - **`compact()` — fold older live → a section.** Folds the oldest `count - keep` live
1611
1806
  * messages into a new {@link Section} (its `summary` from `summarize`), removes
1612
- * them from the live tail, REGENERATES the rollup (a second `summarize` over all section
1807
+ * them from the live tail, regenerates the rollup (a second `summarize` over all section
1613
1808
  * summaries), and emits `summary` then `compact` — returning the new section (or
1614
- * `undefined` when nothing folds). TWO summarizer calls per compaction (the section digest
1615
- * + the rollup). Throws a {@link import('./errors.js').ConversationError} when no
1616
- * `summarize` was supplied.
1617
- * - **`summarizable` — whether a `compact()` CAN fold.** `true` when a
1618
- * {@link ConversationSummaryHandler} was supplied, `false` otherwise. The agent loop's AUTOMATIC
1809
+ * `undefined` when nothing folds). A compaction calls the summarizer for the section
1810
+ * digest and again for the rollup. Throws a
1811
+ * {@link import('./errors.js').ConversationError} when no `summarize` was supplied.
1812
+ * - **`summarizable` — whether a `compact()` can fold.** `true` when a
1813
+ * {@link ConversationSummaryHandler} was supplied, `false` otherwise. The agent loop's automatic
1619
1814
  * compaction (`AgentOptions.window`) gates on it so a conversation that has no summarizer is
1620
1815
  * never auto-compacted (and the loop never throws the `compact()` `SUMMARIZER` error from the
1621
- * auto path). A MANUAL `compact()` still throws without a summarizer — only the auto path is
1816
+ * auto path). A manual `compact()` still throws without a summarizer — only the auto path is
1622
1817
  * guarded.
1623
1818
  * - **`rehydrate(id)` / `search(query)` — read the retained originals.** `rehydrate` returns
1624
1819
  * a section's full original messages (`[]` for an unknown id) and emits `rehydrate` — a
1625
- * pure READ (the caller decides whether to re-add them; `rehydrate` never reinserts).
1626
- * `search` is a case-insensitive substring scan of `content` across ALL messages (every
1820
+ * pure read (the caller decides whether to re-add them; `rehydrate` never reinserts).
1821
+ * `search` is a case-insensitive substring scan of `content` across all messages (every
1627
1822
  * section's originals + the live tail).
1628
- * - **`reference(options?)` — pull THIS conversation into ANOTHER with provenance.** A PURE
1823
+ * - **`reference(options?)` — pull this conversation into another with provenance.** A pure
1629
1824
  * string render (no model call) of a self-labeled, fenced cross-conversation block — the
1630
1825
  * rollup `summary` (when included + present) plus cherry-picked excerpts — framed so a small
1631
- * model reads it as FOREIGN material. Written into the ACTIVE conversation's context through
1826
+ * model reads it as foreign material. Written into the active conversation's context through
1632
1827
  * the active workspace (`context.workspaces.active?.write(path, block)`); the cherry-pick
1633
1828
  * comes from this conversation's own `search` / `rehydrate`, never its whole history.
1634
1829
  * - **Observable.** The owned `emitter` ({@link ConversationEventMap}) carries
@@ -1643,17 +1838,18 @@ export declare interface ConversationInterface {
1643
1838
  /** Lists the compacted history, oldest → newest. */
1644
1839
  readonly sections: readonly Section[];
1645
1840
  /**
1646
- * Reports whether a `compact()` CAN fold — `true` when a {@link ConversationSummaryHandler} was supplied.
1647
- * The agent loop's AUTOMATIC compaction (`AgentOptions.window`) gates on it (a non-summarizable
1841
+ * Reports whether a `compact()` can fold — `true` when a {@link ConversationSummaryHandler} was supplied.
1842
+ * The agent loop's automatic compaction (`AgentOptions.window`) gates on it (a non-summarizable
1648
1843
  * conversation is never auto-compacted, so the auto path never throws the `SUMMARIZER` error);
1649
- * a MANUAL `compact()` still throws without a summarizer.
1844
+ * a manual `compact()` still throws without a summarizer.
1650
1845
  */
1651
1846
  readonly summarizable: boolean;
1652
- /** Counts the LIVE (uncompacted) messages stored in the tail. */
1847
+ /** Counts the live (uncompacted) messages stored in the tail. */
1653
1848
  readonly count: number;
1654
1849
  /**
1655
- * Appends one message to the live tail (or a batch) — MINTS each message's `id`
1656
- * (a random UUID) and returns the created message(s); a stored message is immutable.
1850
+ * Appends one {@link MessageInput} to the live tail, or a batch — mints each message's `id`
1851
+ * (a random UUID) and returns the created message or messages; a stored message is
1852
+ * immutable.
1657
1853
  *
1658
1854
  * @param input - One {@link MessageInput}, or a batch
1659
1855
  * @returns The created {@link Message}(s), with their minted `id`s
@@ -1661,50 +1857,52 @@ export declare interface ConversationInterface {
1661
1857
  add(input: MessageInput): Message;
1662
1858
  add(inputs: readonly MessageInput[]): readonly Message[];
1663
1859
  /**
1664
- * Looks up one LIVE message by id.
1860
+ * Looks up one live message by id (`undefined` when absent).
1665
1861
  *
1666
1862
  * @param id - The message id to resolve
1667
1863
  * @returns The {@link Message}, or `undefined` when absent
1668
1864
  */
1669
1865
  message(id: string): Message | undefined;
1670
1866
  /**
1671
- * Lists every LIVE (uncompacted) message in the tail, in insertion order.
1867
+ * Lists every live, uncompacted message in the tail, in insertion order.
1672
1868
  *
1673
1869
  * @returns The live tail, in insertion order
1674
1870
  */
1675
1871
  messages(): readonly Message[];
1676
1872
  /**
1677
- * Removes one LIVE message by id (or a batch) from the tail.
1873
+ * Removes one live message by id, or a batch, from the tail — `true` only when every
1874
+ * supplied id was removed.
1678
1875
  *
1679
1876
  * @param id - One message id, or a batch
1680
1877
  * @returns True when every supplied id was present and removed; false otherwise
1681
1878
  */
1682
1879
  remove(id: string): boolean;
1683
1880
  remove(ids: readonly string[]): boolean;
1684
- /** Empties the live tail (the compacted `sections` are untouched). */
1881
+ /** Empties the live tail, leaving the compacted `sections` untouched. */
1685
1882
  clear(): void;
1686
1883
  /**
1687
- * Builds the model input for the next turn — each section as ONE synthetic summary message,
1688
- * then the live messages verbatim (the rollup `summary` is NOT injected).
1884
+ * Builds the model input for the next turn — each section as one synthetic recap message,
1885
+ * its summary prefixed with `CONVERSATION_RECAP_PREFIX` so a small model reads it as a
1886
+ * recap rather than a literal turn, then the live tail verbatim; the rollup `summary` is
1887
+ * not injected.
1689
1888
  *
1690
1889
  * @returns `[...sections-as-summary-messages, ...live messages]`
1691
1890
  */
1692
1891
  view(): readonly Message[];
1693
1892
  /**
1694
- * Folds the older live messages into a summarized {@link Section}, regenerates the
1695
- * rollup, and emits `summary` then `compact`.
1893
+ * Folds the oldest `count - keep` live messages into a summarized {@link Section} through
1894
+ * the {@link ConversationSummaryHandler}, removes them from the live tail, regenerates the
1895
+ * rollup, and emits `summary` then `compact` — resolving `undefined` when nothing folds
1896
+ * (`count <= keep`). Throws a {@link import('./errors.js').ConversationError} when no
1897
+ * summarizer was supplied.
1696
1898
  *
1697
1899
  * @remarks
1698
- * Folds the oldest `count - keep` live messages (`keep` from `options`, else the
1699
- * conversation's own); when `count <= keep` NOTHING folds and this is a no-op resolving
1700
- * `undefined`. Otherwise it summarizes the slice into the section, removes those messages
1701
- * from the live tail, regenerates the rollup (a second `summarize` over all sections), and
1702
- * resolves the new section. Requires a {@link ConversationSummaryHandler} — THROWS a
1703
- * {@link import('./errors.js').ConversationError} when none was supplied.
1900
+ * The effective `keep` comes from `options`, else the conversation's own. Regenerating the
1901
+ * rollup runs `summarize` again, over all sections.
1704
1902
  *
1705
1903
  * @remarks
1706
1904
  * When a `sections` cap is set and the fold pushes the section count over it, an overflow
1707
- * merge step folds the oldest sections into one — if THAT merge's `summarize` call throws,
1905
+ * merge step folds the oldest sections into one — if that merge's `summarize` call throws,
1708
1906
  * the merge is skipped (sections transiently sit at `cap + 1`, no loss) but the rollup still
1709
1907
  * regenerates over the current unmerged sections (never left stale) before the error
1710
1908
  * propagates; the next successful `compact()` self-heals the section count back to `cap`.
@@ -1714,35 +1912,38 @@ export declare interface ConversationInterface {
1714
1912
  */
1715
1913
  compact(options?: CompactOptions): Promise<Section | undefined>;
1716
1914
  /**
1717
- * Returns a section's full original messages — a pure READ that emits `rehydrate`.
1915
+ * Returns a section's full original messages — a pure read that emits `rehydrate`, empty for
1916
+ * an unknown id and never reinserting.
1718
1917
  *
1719
1918
  * @param id - The {@link Section} `id` to pull back
1720
1919
  * @returns The section's retained original messages (empty when no such section)
1721
1920
  */
1722
1921
  rehydrate(id: string): readonly Message[];
1723
1922
  /**
1724
- * Searches `content` for a case-insensitive substring across ALL messages — every section's
1725
- * retained originals plus the live tail.
1923
+ * Searches `content` for a case-insensitive substring across every message — each section's
1924
+ * retained originals, then the live tail.
1726
1925
  *
1727
1926
  * @param query - The substring to match (case-insensitive)
1728
1927
  * @returns The matching messages, sections' originals first then the live tail
1729
1928
  */
1730
1929
  search(query: string): readonly Message[];
1731
1930
  /**
1732
- * Renders THIS conversation as a self-labeled, fenced PROVENANCE block to pull INTO another
1733
- * conversation — a pure string (NO model call), framed so a small model reads it as FOREIGN
1734
- * material, not as part of the live thread.
1931
+ * Renders this conversation as a self-labeled, fenced provenance block to pull into another
1932
+ * conversation — a pure string with no model call: a leading
1933
+ * `[Reference — conversation "<label>" — NOT part of this conversation]` marker, the rollup
1934
+ * `Summary:` when `summary` is not `false` and a rollup exists, and the cherry-picked
1935
+ * excerpts (`- role: content`) when `messages` is supplied. `label` defaults to the `id`.
1735
1936
  *
1736
1937
  * @remarks
1737
1938
  * The block leads with an unmistakable provenance marker
1738
1939
  * (`[Reference — conversation "<label>" — NOT part of this conversation]`), then optionally
1739
- * the rollup `Summary:` (when `options.summary !== false` AND a rollup exists), then the
1940
+ * the rollup `Summary:` (when `options.summary !== false` and a rollup exists), then the
1740
1941
  * cherry-picked `Relevant messages:` (each `- role: content`) when `options.messages` is
1741
1942
  * supplied. The intended flow is to pull another conversation B into the active conversation
1742
1943
  * A's active workspace: decide relevance from `B.summary`, select the few right turns with
1743
1944
  * `B.search(query)` / `B.rehydrate(id)`, frame them here, then
1744
1945
  * `A.context.workspaces.active?.write(\`conversation:${B.id}.md\`, B.reference({ label, messages }))`.
1745
- * Keep the excerpts CHERRY-PICKED, never B's whole history — this content enters another
1946
+ * Keep the excerpts cherry-picked, never B's whole history — this content enters another
1746
1947
  * context a small model must read.
1747
1948
  *
1748
1949
  * @param options - The {@link ConversationReferenceOptions} (label / summary / cherry-picked messages)
@@ -1750,17 +1951,18 @@ export declare interface ConversationInterface {
1750
1951
  */
1751
1952
  reference(options?: ConversationReferenceOptions): string;
1752
1953
  /**
1753
- * Serializes this conversation to a plain, JSON-serializable {@link ConversationSnapshot} — its
1754
- * `id`, the rollup `summary`, the compacted `sections`, and the live tail (its `messages()`).
1954
+ * Serializes this conversation to a plain, JSON-serializable {@link ConversationSnapshot} —
1955
+ * its `id`, the rollup `summary`, the compacted `sections`, and the live tail; the live
1956
+ * `summarize` / `keep` are configuration re-supplied on hydrate rather than serialized.
1755
1957
  *
1756
1958
  * @remarks
1757
- * The container serializes ITSELF (`{ id, summary, sections, messages: this.messages() }`) — the
1959
+ * The container serializes itself (`{ id, summary, sections, messages: this.messages() }`) — the
1758
1960
  * {@link ConversationStoreInterface} persistence seam's payload, the exact analogue of
1759
1961
  * {@link import('@orkestrel/workspace').WorkspaceInterface}'s `snapshot`. The summarizer /
1760
- * `keep` are NOT serialized — they are live
1761
- * CONFIG re-supplied on hydrate (a `ConversationSummaryHandler` is a function, not data). The snapshot
1962
+ * `keep` are not serialized — they are live
1963
+ * config re-supplied on hydrate (a `ConversationSummaryHandler` is a function, not data). The snapshot
1762
1964
  * is the durable analogue of the `snapshot` option: a {@link ConversationManagerInterface}
1763
- * HYDRATES a conversation from it through that seam (see {@link ConversationManagerInterface.open}).
1965
+ * hydrates a conversation from it through that seam (see {@link ConversationManagerInterface.open}).
1764
1966
  * Pure — the sections + messages are already plain immutable records (so the snapshot
1765
1967
  * `structuredClone`s / JSON-round-trips losslessly), and snapshotting mutates nothing.
1766
1968
  *
@@ -1770,26 +1972,27 @@ export declare interface ConversationInterface {
1770
1972
  }
1771
1973
 
1772
1974
  /**
1773
- * Registers {@link Conversation}s keyed by `id`, in insertion order, WITH an active pointer
1774
- * — the id-keyed store over the conversation layer PLUS the `active` / `switch` seam the
1775
- * {@link import('../AgentContext.js').AgentContext} renders. Event-free (a registry, like
1776
- * {@link import('@orkestrel/workspace').WorkspaceManager}); the observability lives
1777
- * on each {@link Conversation}.
1975
+ * Registers {@link Conversation}s keyed by `id`, in insertion order, with an active pointer —
1976
+ * the id-keyed store over the conversation layer, the `active` / `switch` seam the
1977
+ * {@link import('../AgentContext.js').AgentContext} renders, and the durable `open` / `save`
1978
+ * store seam. Event-free (a registry, like
1979
+ * {@link import('@orkestrel/workspace').WorkspaceManager}); the observability lives on each
1980
+ * {@link Conversation}.
1778
1981
  *
1779
1982
  * @remarks
1780
1983
  * - **Registry.** Conversations live in an insertion-ordered `Map` keyed by `id`. `add(input?)`
1781
1984
  * mints a {@link Conversation} (its `id` from `input` or `crypto.randomUUID()`), flowing the
1782
- * manager's default `#summarize` / `#keep` in unless the `input` OVERRIDES them, and stores
1783
- * it (an already-present `id` OVERWRITES — last write wins). `count` is the map size,
1985
+ * manager's default `#summarize` / `#keep` in unless the `input` overrides them, and stores
1986
+ * it (an already-present `id` overwrites — last write wins). `count` is the map size,
1784
1987
  * `conversation(id)` looks one up, `conversations()` lists them in insertion order.
1785
1988
  * - **Active pointer.** `active` is the active conversation (the agent's message source the
1786
- * context renders), `undefined` until the FIRST `add` (which auto-activates it — a registry
1989
+ * context renders), `undefined` until the first `add` (which auto-activates it — a registry
1787
1990
  * with conversations always has one active). A subsequent `add` leaves `active` unchanged.
1788
1991
  * `switch(id)` re-points `active` to the conversation with `id` and returns it; an unknown `id`
1789
1992
  * returns `undefined` and leaves `active` unchanged (the lenient lookup style — never throws,
1790
1993
  * no new error code).
1791
- * - **Removal.** `remove` drops one by id, or a batch — `true` only when EVERY supplied id was
1792
- * removed; removing the ACTIVE conversation sets `active` to `undefined`. `clear` empties the registry
1994
+ * - **Removal.** `remove` drops one by id, or a batch — `true` only when every supplied id was
1995
+ * removed; removing the active conversation sets `active` to `undefined`. `clear` empties the registry
1793
1996
  * and sets `active` to `undefined`.
1794
1997
  * - **Event-free.** A purely registry store — no Emitter, no events (each conversation owns
1795
1998
  * its own observable `emitter`).
@@ -1820,32 +2023,32 @@ export declare class ConversationManager implements ConversationManagerInterface
1820
2023
  }
1821
2024
 
1822
2025
  /**
1823
- * Registers {@link ConversationInterface}s keyed by their `id`, in insertion order, WITH an
1824
- * active pointer — the id-keyed store over the conversation layer PLUS the `active` / `switch` seam the
1825
- * {@link AgentContextInterface} renders. Event-free (a registry, like
1826
- * {@link import('@orkestrel/workspace').WorkspaceManagerInterface}); the observability lives on each
1827
- * {@link ConversationInterface}.
2026
+ * Registers {@link ConversationInterface}s keyed by their `id`, in insertion order, with an active
2027
+ * pointer — the id-keyed store over the conversation layer, the `active` / `switch` seam the {@link
2028
+ * AgentContextInterface} renders, and the durable `open` / `save` store seam. Event-free (a
2029
+ * registry, like {@link import('@orkestrel/workspace').WorkspaceManagerInterface}); the
2030
+ * observability lives on each {@link ConversationInterface}.
1828
2031
  *
1829
2032
  * @remarks
1830
2033
  * - **Registry.** `count` is how many are stored. `add(input?)` mints a
1831
2034
  * {@link ConversationInterface} (its `id` from `input` or a random UUID), flowing the
1832
2035
  * manager's default `summarize` / `keep` in unless the `input` overrides them; `add` of an
1833
- * already-present `id` OVERWRITES it (last write wins). `conversation(id)` looks one up
2036
+ * already-present `id` overwrites it (last write wins). `conversation(id)` looks one up
1834
2037
  * (`undefined` when absent); `conversations()` lists them in insertion order.
1835
2038
  * - **Active pointer.** `active` is the active conversation (the agent's message source the
1836
- * context renders), `undefined` until the FIRST `add` (which auto-activates it — a registry
2039
+ * context renders), `undefined` until the first `add` (which auto-activates it — a registry
1837
2040
  * with conversations always has one active). A subsequent `add` leaves `active` unchanged.
1838
2041
  * `switch(id)` re-points `active` to the conversation with `id` and returns it; an unknown
1839
2042
  * `id` returns `undefined` and leaves `active` unchanged (the lenient lookup style — never
1840
2043
  * throws, no new error code).
1841
- * - **Removal.** `remove` drops one by id, or a batch (array overload FIRST) — `true`
1842
- * only when EVERY supplied id was removed; removing the ACTIVE conversation sets `active` to `undefined`. `clear`
2044
+ * - **Removal.** `remove` drops one by id, or a batch (the array overload declared first) — `true`
2045
+ * only when every supplied id was removed; removing the active conversation sets `active` to `undefined`. `clear`
1843
2046
  * empties the registry and sets `active` to `undefined`.
1844
2047
  * - **Durable open / save (the optional `store` seam).** When a {@link ConversationStoreInterface}
1845
- * is supplied (the `store` option), `open(id)` HYDRATES a conversation from the store on a registry
2048
+ * is supplied (the `store` option), `open(id)` hydrates a conversation from the store on a registry
1846
2049
  * miss (rebuilding it through the `snapshot` option, flowing the manager's
1847
- * default `summarize` / `keep` in) and `save(id)` PERSISTS a registered conversation's
1848
- * {@link ConversationInterface.snapshot}. Both are LENIENT without a store — `open` resolves only
2050
+ * default `summarize` / `keep` in) and `save(id)` persists a registered conversation's
2051
+ * {@link ConversationInterface.snapshot}. Both are lenient without a store — `open` resolves only
1849
2052
  * registered ids, `save` is a no-op (`false`) — consistent with the lenient `switch`. It mirrors
1850
2053
  * the workspace package manager's `open` / `save` seam.
1851
2054
  * - **Event-free.** A purely registry store — no Emitter, no events (each conversation owns
@@ -1855,47 +2058,66 @@ export declare interface ConversationManagerInterface {
1855
2058
  readonly count: number;
1856
2059
  /** Holds the active conversation — the agent's message source the context renders; `undefined` until the first `add`. */
1857
2060
  readonly active: ConversationInterface | undefined;
2061
+ /** Looks up one conversation by id (`undefined` when absent). */
1858
2062
  conversation(id: string): ConversationInterface | undefined;
2063
+ /** Lists every conversation, in insertion order. */
1859
2064
  conversations(): readonly ConversationInterface[];
2065
+ /**
2066
+ * Mints a conversation, taking its `id` from the input or a fresh UUID and flowing the
2067
+ * manager's default `summarize` / `keep` in unless the input overrides them — auto-activates
2068
+ * the first, and an already-present `id` overwrites, last write wins.
2069
+ */
1860
2070
  add(input?: ConversationInput): ConversationInterface;
2071
+ /**
2072
+ * Re-points `active` at the conversation with `id` and returns it; an unknown `id` returns
2073
+ * `undefined` and leaves `active` unchanged, never throwing.
2074
+ */
1861
2075
  switch(id: string): ConversationInterface | undefined;
1862
2076
  /**
1863
- * Resolves a conversation by id, ACTIVATING it — from the registry if present, else HYDRATED from
1864
- * the optional {@link ConversationStoreInterface} (`store`).
2077
+ * Resolves a conversation by id and activates it — from the registry when present, else
2078
+ * hydrated from the optional {@link ConversationStoreInterface} (`store`); `undefined` when
2079
+ * it is neither registered nor stored.
1865
2080
  *
1866
2081
  * @remarks
1867
- * - If `id` is ALREADY registered, it is ACTIVATED (`switch`ed to) and returned — no store hit.
1868
- * - Else if a `store` is set, `store.get(id)` is awaited; on a HIT the snapshot is rehydrated
2082
+ * - If `id` is already registered, it is activated (`switch`ed to) and returned — no store hit.
2083
+ * - Else if a `store` is set, `store.get(id)` is awaited; on a hit the snapshot is rehydrated
1869
2084
  * into a fresh {@link ConversationInterface} through the `snapshot` option
1870
2085
  * (`add({ snapshot, ... })`, flowing the manager's default `summarize` / `keep` in), which
1871
- * registers AND activates it, and it is returned.
1872
- * - Else (no store, or a store MISS) ⇒ `undefined` (lenient — no throw).
2086
+ * registers and activates it, and it is returned.
2087
+ * - Else (no store, or a store miss) ⇒ `undefined` (lenient — no throw).
1873
2088
  *
1874
2089
  * @param id - The conversation id to open
1875
2090
  * @returns The activated {@link ConversationInterface}, or `undefined` when neither registered nor stored
1876
2091
  */
1877
2092
  open(id: string): Promise<ConversationInterface | undefined>;
1878
2093
  /**
1879
- * Persists a REGISTERED conversation's {@link ConversationInterface.snapshot} to the optional
1880
- * {@link ConversationStoreInterface} (`store`).
2094
+ * Persists a registered conversation's {@link ConversationInterface.snapshot} to the optional
2095
+ * {@link ConversationStoreInterface} (`store`) — `true` when persisted, `false` when there is
2096
+ * no store or the id is unknown, and never throwing.
1881
2097
  *
1882
2098
  * @remarks
1883
- * Lenient: when a `store` is set AND `id` is registered, `store.set(conversation.snapshot())` is
1884
- * awaited and `true` is returned; otherwise (no store, OR an unknown id) it is a NO-OP returning
2099
+ * Lenient: when a `store` is set and `id` is registered, `store.set(conversation.snapshot())` is
2100
+ * awaited and `true` is returned; otherwise (no store, or an unknown id) it is a no-op returning
1885
2101
  * `false` — never a throw, consistent with the lenient `switch`.
1886
2102
  *
1887
2103
  * @param id - The id of the registered conversation to persist
1888
2104
  * @returns True if the snapshot was persisted; false otherwise (no store, or an unknown id)
1889
2105
  */
1890
2106
  save(id: string): Promise<boolean>;
2107
+ /**
2108
+ * Removes one conversation by id, or a batch — `true` only when every supplied id was
2109
+ * removed; clears `active` when a removed conversation was the active one.
2110
+ */
1891
2111
  remove(ids: readonly string[]): boolean;
1892
2112
  remove(id: string): boolean;
2113
+ /** Removes every conversation and clears `active`. */
1893
2114
  clear(): void;
1894
2115
  }
1895
2116
 
1896
2117
  /**
1897
- * Configures `createConversationManager` — the default {@link ConversationSummaryHandler} and
1898
- * retained-tail size the conversations it creates inherit.
2118
+ * Configures `createConversationManager` — the default `ConversationSummaryHandler`, retained-tail
2119
+ * size, and `sections` cap the conversations it creates inherit, plus the optional durable `store`
2120
+ * backing `open` / `save`.
1899
2121
  *
1900
2122
  * @remarks
1901
2123
  * `summarize` is the default summarizer flowed into every conversation the manager `add`s
@@ -1916,8 +2138,8 @@ export declare interface ConversationManagerOptions {
1916
2138
  /**
1917
2139
  * Holds the optional durable {@link ConversationStoreInterface} backing
1918
2140
  * {@link ConversationManagerInterface.open} / {@link ConversationManagerInterface.save} — a memory
1919
- * / JSON / SQLite / IndexedDB store a conversation is HYDRATED from (`open` a registry-miss) and
1920
- * PERSISTED to (`save`). Omitted ⇒ the manager is registry-only: `open` resolves only what is
2141
+ * / JSON / SQLite / IndexedDB store a conversation is hydrated from (`open` a registry-miss) and
2142
+ * persisted to (`save`). Omitted ⇒ the manager is registry-only: `open` resolves only what is
1921
2143
  * already registered, and `save` is a no-op (`false`). The exact analogue of
1922
2144
  * {@link import('@orkestrel/workspace').WorkspaceManagerOptions}'s `store`.
1923
2145
  */
@@ -1926,25 +2148,26 @@ export declare interface ConversationManagerOptions {
1926
2148
 
1927
2149
  /**
1928
2150
  * Configures `createConversation` — the optional `id`, the reserved `on` hooks, the
1929
- * provider-agnostic `summarize` seam, and the retained-tail size.
2151
+ * provider-agnostic `summarize` seam, the retained-tail size, an optional cap on the compacted
2152
+ * `sections` list, and a {@link ConversationSnapshot} to hydrate from.
1930
2153
  *
1931
2154
  * @remarks
1932
2155
  * `id` is the conversation's identity (a random UUID when omitted). `on` is the reserved
1933
2156
  * listener key (initial {@link ConversationEventMap} listeners). `summarize` is the
1934
- * {@link ConversationSummaryHandler} compaction needs — ABSENT ⇒ `compact()` throws a
2157
+ * {@link ConversationSummaryHandler} compaction needs — absent ⇒ `compact()` throws a
1935
2158
  * {@link import('./errors.js').ConversationError} (a conversation can still store + view a
1936
2159
  * live tail; it cannot fold). `keep` is how many recent live messages a `compact()`
1937
- * retains VERBATIM (folding only the older ones); it defaults to
2160
+ * retains verbatim (folding only the older ones); it defaults to
1938
2161
  * {@link import('./constants.js').DEFAULT_CONVERSATION_KEEP} (`0` — a manual `compact()`
1939
- * folds the WHOLE current live tail into one section). `sections` is an optional cap on the
2162
+ * folds the whole current live tail into one section). `sections` is an optional cap on the
1940
2163
  * compacted `sections` list — when set (`>= 1`), a `compact()` that would leave more than
1941
- * `sections` sections FOLDS the oldest overflow into ONE merged section so the list never
2164
+ * `sections` sections folds the oldest overflow into one merged section so the list never
1942
2165
  * exceeds `sections`, emitting `collapse`; omitted ⇒ unlimited (the prior behavior).
1943
- * `snapshot` is the HYDRATION seam — a {@link ConversationSnapshot} whose `id`, rollup
1944
- * `summary`, compacted `sections`, and live tail are RESTORED into the new conversation, with
2166
+ * `snapshot` is the hydration seam — a {@link ConversationSnapshot} whose `id`, rollup
2167
+ * `summary`, compacted `sections`, and live tail are restored into the new conversation, with
1945
2168
  * the live `summarize` / `keep` / `on` supplied alongside it (a summarizer is a function, not
1946
- * serialized data). Restoring is SILENT (no events — nothing was edited), and a `snapshot.id`
1947
- * WINS over `id` (the snapshot IS the conversation's identity). It is what lets
2169
+ * serialized data). Restoring is silent (no events — nothing was edited), and a `snapshot.id`
2170
+ * wins over `id` (the snapshot is the conversation's identity). It is what lets
1948
2171
  * `createConversation` hydrate, and what a {@link ConversationManagerInterface.open} reads a
1949
2172
  * stored snapshot back through.
1950
2173
  */
@@ -1953,7 +2176,7 @@ export declare interface ConversationOptions {
1953
2176
  readonly on?: EmitterHooks<ConversationEventMap>;
1954
2177
  /** Holds the emitter's listener-error handler — a listener throw routes here, not to a domain event. */
1955
2178
  readonly error?: EmitterErrorHandler;
1956
- /** Supplies the summarizer compaction needs; ABSENT ⇒ `compact()` throws a `ConversationError`. */
2179
+ /** Supplies the summarizer compaction needs; absent ⇒ `compact()` throws a `ConversationError`. */
1957
2180
  readonly summarize?: ConversationSummaryHandler;
1958
2181
  /** Keeps this many recent live messages verbatim on `compact`; defaults to `DEFAULT_CONVERSATION_KEEP` (`0`). */
1959
2182
  readonly keep?: number;
@@ -1964,22 +2187,23 @@ export declare interface ConversationOptions {
1964
2187
  }
1965
2188
 
1966
2189
  /**
1967
- * Configures {@link ConversationInterface.reference} — how to render ONE conversation as a
1968
- * self-labeled, fenced PROVENANCE block to pull INTO another conversation (by writing it to
1969
- * the active context's active workspace).
2190
+ * Configures {@link ConversationInterface.reference} — how to render one conversation as a
2191
+ * self-labeled, fenced provenance block to pull into another conversation by writing it to the
2192
+ * active context's active workspace: `label` defaults to the `id`, `summary` defaults to `true`,
2193
+ * and `messages` are cherry-picked excerpts defaulting to none.
1970
2194
  *
1971
2195
  * @remarks
1972
- * The rendered block is a cross-conversation reference a SMALL model must read as foreign
1973
- * material, NOT as part of the live thread — so every member keeps it CONCISE and unmistakably
2196
+ * The rendered block is a cross-conversation reference a small model must read as foreign
2197
+ * material, not as part of the live thread — so every member keeps it concise and unmistakably
1974
2198
  * attributed:
1975
- * - `label` — the human PROVENANCE name shown in the block's leading marker (for example `'planning'`);
2199
+ * - `label` — the human provenance name shown in the block's leading marker (for example `'planning'`);
1976
2200
  * defaults to the conversation's own `id`. It is what the model attributes the content to.
1977
2201
  * - `summary` — whether to include the conversation's rollup `summary` (its summary-of-summaries)
1978
- * in the block; defaults to `true` (the rollup is included WHEN one exists — `undefined` until
2202
+ * in the block; defaults to `true` (the rollup is included when one exists — `undefined` until
1979
2203
  * the first compaction omits the `Summary:` line). Pass `false` to exclude it.
1980
- * - `messages` — the CHERRY-PICKED excerpts to include (each rendered `role: content`), default
1981
- * NONE. The intended source is the conversation's OWN `search(query)` / `rehydrate(id)` output
1982
- * (select the few relevant turns), NOT its whole history — dumping every message defeats the
2204
+ * - `messages` — the cherry-picked excerpts to include (each rendered `role: content`), default
2205
+ * none. The intended source is the conversation's own `search(query)` / `rehydrate(id)` output
2206
+ * (select the few relevant turns), not its whole history — dumping every message defeats the
1983
2207
  * point (it re-bloats the destination context a small model then has to wade through).
1984
2208
  */
1985
2209
  export declare interface ConversationReferenceOptions {
@@ -1998,11 +2222,11 @@ export declare interface ConversationReferenceOptions {
1998
2222
  * {@link import('@orkestrel/workspace').WorkspaceSnapshot}.
1999
2223
  *
2000
2224
  * @remarks
2001
- * Pure JSON DATA (no class instances, no functions): each {@link Section} and
2002
- * {@link Message} is already a PLAIN record that `structuredClone`s / JSON-round-trips
2225
+ * Pure JSON data (no class instances, no functions): each {@link Section} and
2226
+ * {@link Message} is already a plain record that `structuredClone`s / JSON-round-trips
2003
2227
  * losslessly. The snapshot carries the rollup `summary` (a summary-of-summaries; `undefined` until
2004
- * the first compaction), the compacted `sections` (each RETAINING its folded originals), and the
2005
- * live uncompacted tail `messages` — but NOT the `summarize` / `keep`, which are live CONFIG
2228
+ * the first compaction), the compacted `sections` (each retaining its folded originals), and the
2229
+ * live uncompacted tail `messages` — but not the `summarize` / `keep`, which are live config
2006
2230
  * re-supplied on hydrate (a summarizer is a function, not serializable data). The snapshot the
2007
2231
  * container produces from itself ({@link ConversationInterface.snapshot}); the durable analogue of
2008
2232
  * the {@link ConversationOptions.snapshot} hydration seam. A {@link ConversationManagerInterface}
@@ -2014,23 +2238,24 @@ export declare interface ConversationSnapshot {
2014
2238
  readonly id: string;
2015
2239
  /** Holds the rollup (a summary-of-summaries); `undefined` until the first compaction. */
2016
2240
  readonly summary?: string;
2017
- /** Lists the compacted history, oldest → newest (each section RETAINS its folded originals). */
2241
+ /** Lists the compacted history, oldest → newest (each section retains its folded originals). */
2018
2242
  readonly sections: readonly Section[];
2019
2243
  /** Lists the live uncompacted tail, in insertion order. */
2020
2244
  readonly messages: readonly Message[];
2021
2245
  }
2022
2246
 
2023
2247
  /**
2024
- * Represents one row of the table a
2025
- * {@link import('./conversations/stores/DatabaseConversationStore.js').DatabaseConversationStore}
2026
- * persists — a conversation `id` plus its {@link ConversationSnapshot} held as ONE OPAQUE JSON
2027
- * column. The exact analogue of {@link import('@orkestrel/workspace').WorkspaceSnapshotRow}.
2248
+ * Represents one row of the table a {@link
2249
+ * import('./conversations/stores/DatabaseConversationStore.js').DatabaseConversationStore} persists
2250
+ * — a conversation `id` plus its {@link ConversationSnapshot} held as one opaque JSON column, read
2251
+ * back as `unknown` and narrowed on `get`. The exact analogue of {@link
2252
+ * import('@orkestrel/workspace').WorkspaceSnapshotRow}.
2028
2253
  *
2029
2254
  * @remarks
2030
2255
  * The Database twin of {@link ConversationStoreInterface} stores the snapshot whole (the `snapshot`
2031
2256
  * column is a `rawShape`, an opaque JSON blob — exactly as
2032
2257
  * {@link import('@orkestrel/workspace').WorkspaceSnapshotRow} stores a workspace snapshot), so the
2033
- * row type stays FLAT and the sections/messages snapshot shape never
2258
+ * row type stays flat and the sections/messages snapshot shape never
2034
2259
  * forces the contract to `Infer` it. The column therefore reads back as the broad `unknown`; the
2035
2260
  * store narrows it to a {@link ConversationSnapshot} on `get`
2036
2261
  * ({@link import('./validators.js').isConversationSnapshot}, the total boundary guard). `id`
@@ -2044,30 +2269,30 @@ export declare interface ConversationSnapshotRow {
2044
2269
  }
2045
2270
 
2046
2271
  /**
2047
- * Persists a {@link ConversationSnapshot} durably — three async primitives
2048
- * (`get` / `set` / `delete`) keyed by a conversation id, the exact analogue of
2049
- * {@link import('@orkestrel/workspace').WorkspaceStoreInterface}.
2272
+ * Persists a {@link ConversationSnapshot} durably — the async `get` / `set` / `delete` primitives,
2273
+ * keyed by a conversation id and holding no expiry, the exact analogue of {@link
2274
+ * import('@orkestrel/workspace').WorkspaceStoreInterface}.
2050
2275
  *
2051
2276
  * @remarks
2052
2277
  * The store persists the {@link ConversationSnapshot} — the self-contained, pure-JSON conversation
2053
- * state — so a JSON / SQLite / IndexedDB backend swaps in WITHOUT touching the manager or the
2278
+ * state — so a JSON / SQLite / IndexedDB backend swaps in without touching the manager or the
2054
2279
  * conversation: the in-memory default
2055
2280
  * {@link import('./conversations/stores/MemoryConversationStore.js').MemoryConversationStore} and its
2056
2281
  * driver-pluggable twin
2057
2282
  * {@link import('./conversations/stores/DatabaseConversationStore.js').DatabaseConversationStore} (the
2058
- * snapshot as one opaque JSON column) share THIS one interface. Hydration is NOT a store concern
2283
+ * snapshot as one opaque JSON column) share this one interface. Hydration is not a store concern
2059
2284
  * — a {@link ConversationManagerInterface} reads a snapshot back and rebuilds the live conversation
2060
2285
  * through the {@link ConversationOptions.snapshot} seam (re-supplying the live `summarize` / `keep`; see
2061
2286
  * {@link ConversationManagerInterface.open} / {@link ConversationManagerInterface.save}).
2062
2287
  *
2063
2288
  * Every primitive is async (a `Promise`), so a durable backend (a database round-trip) fits the
2064
- * same shape as the memory one. The snapshot carries its OWN id, so `set` takes no separate id
2289
+ * same shape as the memory one. The snapshot carries its own id, so `set` takes no separate id
2065
2290
  * param (mirroring
2066
- * {@link import('@orkestrel/workspace').WorkspaceStoreInterface}'s `set`). UNLIKE a session store
2067
- * there is NO idle-TTL
2291
+ * {@link import('@orkestrel/workspace').WorkspaceStoreInterface}'s `set`). Unlike a session store
2292
+ * there is no idle-TTL
2068
2293
  * / eviction — a persisted conversation lives until an explicit `delete`. It is concrete over
2069
2294
  * {@link ConversationSnapshot} — no generic parameter, because the
2070
- * snapshot is the ONE payload a conversation store persists.
2295
+ * snapshot is the one payload a conversation store persists.
2071
2296
  */
2072
2297
  export declare interface ConversationStoreInterface {
2073
2298
  /**
@@ -2093,10 +2318,10 @@ export declare interface ConversationStoreInterface {
2093
2318
  }
2094
2319
 
2095
2320
  /**
2096
- * Summarizes a conversation, provider-agnostically — the seam the agent RUNTIME supplies so
2097
- * core never imports a provider. Given the folded messages, it resolves their digest (the
2098
- * model-written summary), used both to summarize a compacted {@link Section} and
2099
- * to regenerate a {@link ConversationInterface}'s rollup `summary`.
2321
+ * Summarizes a conversation, provider-agnostically — the seam the agent runtime supplies so core
2322
+ * never imports a provider. Given the folded messages, it resolves their digest, the model-written
2323
+ * summary used to summarize a compacted {@link Section} and to regenerate a {@link
2324
+ * ConversationInterface}'s rollup `summary`.
2100
2325
  *
2101
2326
  * @remarks
2102
2327
  * The agent runtime builds one from its `ProviderInterface` (for example
@@ -2117,10 +2342,10 @@ export declare type ConversationSummaryHandler = (messages: readonly Message[])
2117
2342
  * `generate` and a live `stream`.
2118
2343
  *
2119
2344
  * @remarks
2120
- * One private loop drives the turn; `generate` DRAINS the same stream `stream`
2345
+ * One private loop drives the turn; `generate` drains the same stream `stream`
2121
2346
  * exposes, so they can never diverge. Each turn is bounded by one cancel folded from
2122
2347
  * `signal` + `timeout` + `budget` (through `AbortSignal.any`) — any trip (or `abort()`)
2123
- * commits a PARTIAL result (the stream's `result` RESOLVES on a cancel, rejects only
2348
+ * commits a partial result (the stream's `result` resolves on a cancel, rejects only
2124
2349
  * on a genuine provider / tool error). The `scheduler` paces between turns; tool
2125
2350
  * iteration is capped at `limit` (default `DEFAULT_AGENT_LIMIT`). Tools are advertised
2126
2351
  * structurally through `context.tools.definitions()`. Two observation surfaces: the
@@ -2155,19 +2380,20 @@ export declare type ConversationSummaryHandler = (messages: readonly Message[])
2155
2380
  export declare function createAgent(provider: ProviderInterface, options?: AgentOptions): AgentInterface;
2156
2381
 
2157
2382
  /**
2158
- * Creates a richer turn context — an {@link AgentContextInterface} assembling a provider
2159
- * request from the optional system prompt, the instruction registry, the workspace registry,
2160
- * the conversation store, the tool registry, and the active scope.
2383
+ * Creates a richer turn context — an {@link AgentContextInterface} assembling a provider request
2384
+ * from the optional system prompt, the instruction registry, the workspace registry (the only
2385
+ * document channel), the conversation registry that is its `messages` source, the tool registry,
2386
+ * and the active scope, which `build()` folds into the next turn's input.
2161
2387
  *
2162
2388
  * @remarks
2163
2389
  * `system` is the optional system prompt; `tools` / `instructions` / `workspaces` are pre-built
2164
- * managers to reuse (empty ones are created when omitted, so `context.workspaces` is ALWAYS
2390
+ * managers to reuse (empty ones are created when omitted, so `context.workspaces` is always
2165
2391
  * present); `scope` is the initial active filter (`undefined` ⇒ no filtering, changeable afterwards
2166
2392
  * through `context.apply(...)`). The `messages` store is always fresh. `build()` folds the scoped
2167
- * instructions — PLUS the ACTIVE workspace's scope-filtered text files (fenced) — into ONE leading
2393
+ * instructions — plus the active workspace's scope-filtered text files (fenced) — into one leading
2168
2394
  * `system` message and appends the scoped conversation (attaching the active workspace's
2169
2395
  * scope-filtered image files' `base64` payload to the last user message), built fresh each call; the active
2170
- * workspace is the SOLE document/image context. Tools are advertised STRUCTURALLY (through
2396
+ * workspace is the sole document/image context. Tools are advertised structurally (through
2171
2397
  * `tools.definitions()`, scope-filtered by the loop), never serialized into the prompt.
2172
2398
  *
2173
2399
  * @param options - Optional `system` / `tools` / `instructions` / `workspaces` / `scope`
@@ -2188,7 +2414,7 @@ export declare function createAgentContext(options?: AgentContextOptions): Agent
2188
2414
 
2189
2415
  /**
2190
2416
  * Creates a durable, bounded-concurrency agent-job queue — a {@link QueueInterface} over
2191
- * serializable {@link AgentJobInput}s that COMPOSES `createQueue`: each job is rehydrated
2417
+ * serializable {@link AgentJobInput}s that composes `createQueue`: each job is rehydrated
2192
2418
  * through the `registry` into a live {@link AgentInterface}, run to its {@link AgentResult},
2193
2419
  * and subjected to the partial-as-configurable-failure policy.
2194
2420
  *
@@ -2201,9 +2427,9 @@ export declare function createAgentContext(options?: AgentContextOptions): Agent
2201
2427
  * (for example `createMemoryQueueStore` / `createDatabaseQueueStore`) persists outstanding
2202
2428
  * jobs; `restore()` re-enqueues them after a restart and the `registry` rehydrates the
2203
2429
  * live pieces from the names — so a job survives a crash.
2204
- * - **Partial policy.** A partial result THROWS an
2430
+ * - **Partial policy.** A partial result throws an
2205
2431
  * {@link import('./errors.js').AgentJobError} by default, so a job cancelled by its
2206
- * attempt deadline / a queue abort RETRIES while attempts remain; `partial: true`
2432
+ * attempt deadline / a queue abort retries while attempts remain; `partial: true`
2207
2433
  * resolves the partial as success instead.
2208
2434
  * - **Cancellation threads through.** The handler passes `context.signal` into
2209
2435
  * `registry.build`, so a queue `abort()` or a per-attempt timeout cancels the in-flight
@@ -2234,7 +2460,7 @@ export declare function createAgentQueue(options: AgentQueueOptions): QueueInter
2234
2460
  *
2235
2461
  * @remarks
2236
2462
  * `providers` is required; `tools` / `authorities` / `schedulers` are optional pools.
2237
- * The accessors (`provider` / `tool` / `authority` / `scheduler`) THROW an
2463
+ * The accessors (`provider` / `tool` / `authority` / `scheduler`) throw an
2238
2464
  * {@link import('./errors.js').AgentError} carrying `code: 'REGISTRY'` and the message
2239
2465
  * `unknown <category>: <name>` on an unregistered name — a misconfigured or crash-restored
2240
2466
  * job fails loudly rather than running with a missing dependency. `build(input, signal)`
@@ -2263,21 +2489,21 @@ export declare function createAgentRegistry(options: AgentRegistryOptions): Agen
2263
2489
 
2264
2490
  /**
2265
2491
  * Creates an agent-job runner — a {@link RunnerInterface} over serializable
2266
- * {@link AgentJobInput}s that COMPOSES `createRunner` (one-shot, ordered, fail-fast), each
2267
- * unit rehydrated through the `registry` and subjected to the partial policy. The runner
2268
- * enables **sub-agent fan-out**: a parent job's handler can `controller.spawn(childJob)`.
2492
+ * {@link AgentJobInput}s that composes `createRunner` (one-shot, ordered, fail-fast), each unit
2493
+ * rehydrated through the `registry` and subjected to the partial policy. The runner also carries
2494
+ * sub-agent fan-out: a parent job's handler can `controller.spawn(childJob)`.
2269
2495
  *
2270
2496
  * @remarks
2271
2497
  * - **Composes the substrate (no new engine).** Bounded `concurrency`, `retries`, the
2272
2498
  * per-attempt `timeout`, ordered results, and fail-fast are all the backing Runner's;
2273
2499
  * the handler adds only rehydration + the partial policy.
2274
2500
  * - **Sub-agent fan-out.** Each unit's handler receives a `ControllerInterface` whose
2275
- * `spawn(childJob)` launches a CHILD agent job through the same bounded queue (the
2501
+ * `spawn(childJob)` launches a child agent job through the same bounded queue (the
2276
2502
  * child's result joins the run after the declared units, in spawn order). On a bounded
2277
- * runner, FAN OUT and return — do NOT inline-`await` a spawn from within the handler (a
2503
+ * runner, fan out and return — do not inline-`await` a spawn from within the handler (a
2278
2504
  * slot-holding handler awaiting its own spawn can deadlock; see `ControllerInterface`).
2279
2505
  * - **Partial policy + cancellation.** Same as `createAgentQueue`: a partial result
2280
- * THROWS by default (the run's fail-fast engages), `partial: true` resolves it; the
2506
+ * throws by default (the run's fail-fast engages), `partial: true` resolves it; the
2281
2507
  * handler threads `controller.signal` into `registry.build`, so a runner abort / a
2282
2508
  * per-attempt timeout cancels the agent.
2283
2509
  *
@@ -2305,11 +2531,11 @@ export declare function createAgentRunner(options: AgentRunnerOptions): RunnerIn
2305
2531
  * to the configured default when none match.
2306
2532
  *
2307
2533
  * @remarks
2308
- * `rules` are evaluated in order — the FIRST whose `match` is true decides (a matched
2309
- * rule ALLOWS unless its `allowed` is explicitly `false`). When no rule matches, the
2534
+ * `rules` are evaluated in order — the first whose `match` is true decides (a matched
2535
+ * rule allows unless its `allowed` is explicitly `false`). When no rule matches, the
2310
2536
  * `fallback` decides; it defaults to `{ zone: DEFAULT_AUTHORITY_ZONE, allowed: true }`
2311
- * (allow-unmatched — a rules list of denials acts as a DENYLIST). Pass an
2312
- * `allowed: false` `fallback` to flip the gate to deny-by-default (an ALLOWLIST). Wire
2537
+ * (allow-unmatched — a rules list of denials acts as a denylist). Pass an
2538
+ * `allowed: false` `fallback` to flip the gate to deny-by-default (an allowlist). Wire
2313
2539
  * the result into `createAgent` through `AgentOptions.authority`: a denied call is fed back
2314
2540
  * to the model as a denial `ToolResult` (not executed, no budget cost), so the model
2315
2541
  * can react. Synchronous — `evaluate` returns the verdict directly.
@@ -2339,7 +2565,7 @@ export declare function createAuthority(options?: AuthorityOptions): AuthorityIn
2339
2565
  * Write and read are decoupled, so the producer never waits for a consumer: an agent's eager
2340
2566
  * pump writes each chunk into one, which is why a run's `result` settles whether or not the
2341
2567
  * live events are drained. A value pushed at an already-parked consumer is delivered, buffered
2342
- * values are yielded before the end is reported, and the FIRST failure wins.
2568
+ * values are yielded before the end is reported, and the first failure wins.
2343
2569
  *
2344
2570
  * @typeParam T - The value type the channel carries
2345
2571
  * @returns A fresh, empty {@link ChannelInterface}
@@ -2360,16 +2586,16 @@ export declare function createChannel<T>(): ChannelInterface<T>;
2360
2586
 
2361
2587
  /**
2362
2588
  * Creates a conversation — a {@link ConversationInterface} grouping messages above a flat
2363
- * message store it OWNS DIRECTLY, with compaction into summarized sections, a regenerated
2589
+ * message store it owns directly, with compaction into summarized sections, a regenerated
2364
2590
  * rollup `summary`, on-demand `rehydrate`, and substring `search`, driven by a
2365
2591
  * provider-agnostic {@link ConversationSummaryHandler} seam.
2366
2592
  *
2367
2593
  * @remarks
2368
2594
  * Append turns through the conversation's own `add` (the live tail it owns); `view()` is the model input
2369
2595
  * (each section as a summary message, then the live tail). `compact()` folds the older live
2370
- * messages into a summarized {@link Section} and regenerates the rollup — it REQUIRES
2596
+ * messages into a summarized {@link Section} and regenerates the rollup — it requires
2371
2597
  * a `summarize` (omitted ⇒ `compact()` throws a `ConversationError`); `keep` retains a recent
2372
- * tail (default `DEFAULT_CONVERSATION_KEEP` — fold ALL). `rehydrate(id)` / `search(query)` read
2598
+ * tail (default `DEFAULT_CONVERSATION_KEEP` — fold all). `rehydrate(id)` / `search(query)` read
2373
2599
  * the retained originals. Observable (`emitter` — `compact` / `summary` / `rehydrate`), wired
2374
2600
  * through the reserved `on` option; the emitter isolates a listener throw and routes it to
2375
2601
  * its `error` handler (the `error` option), so it can never corrupt a compaction.
@@ -2377,41 +2603,61 @@ export declare function createChannel<T>(): ChannelInterface<T>;
2377
2603
  * @param options - Optional `id` / `on` hooks + the `summarize` seam + `keep` (see {@link ConversationOptions})
2378
2604
  * @returns A working {@link ConversationInterface}
2379
2605
  *
2380
- * @example
2606
+ * @example Conversations & compaction
2381
2607
  * ```ts
2382
2608
  * import type { ProviderInterface } from '@orkestrel/agent'
2383
2609
  * import { createConversation } from '@orkestrel/agent'
2384
2610
  *
2385
2611
  * declare const provider: ProviderInterface // any concrete implementation supplied by the host app
2612
+ * // The summarizer seam — built from the provider by the runtime; core stays provider-agnostic.
2613
+ * // Append the instruction as the FINAL user turn: a chat model emits nothing when the prompt
2614
+ * // ends on an assistant turn, so a leading-system instruction is unreliable.
2386
2615
  * const conversation = createConversation({
2387
- * // Append the instruction as the FINAL user turn — a chat model emits nothing when the
2388
- * // prompt ends on an assistant turn, so a leading-system instruction is unreliable.
2389
2616
  * summarize: async (messages) =>
2390
- * (await provider.generate([...messages, { id: 's', role: 'user', content: 'Summarize the conversation so far concisely.' }], AbortSignal.timeout(30_000))).content,
2617
+ * (
2618
+ * await provider.generate(
2619
+ * [
2620
+ * ...messages,
2621
+ * { id: 's', role: 'user', content: 'Summarize the conversation so far concisely.' },
2622
+ * ],
2623
+ * AbortSignal.timeout(30_000),
2624
+ * )
2625
+ * ).content,
2626
+ * keep: 2, // retain the two most recent turns verbatim on each compaction
2391
2627
  * })
2392
- * conversation.add({ role: 'user', content: 'Hello' })
2393
- * await conversation.compact() // folds the live tail into a summarized section
2628
+ * conversation.add([
2629
+ * { role: 'user', content: 'My name is Ada.' },
2630
+ * { role: 'assistant', content: 'Nice to meet you, Ada.' },
2631
+ * { role: 'user', content: 'What did I say my name was?' },
2632
+ * ])
2633
+ *
2634
+ * const section = await conversation.compact() // folds the older turns → a summarized section
2635
+ * conversation.view() // [<section summary message>, ...the retained recent tail] — the model input
2636
+ * conversation.summary // the regenerated rollup (a summary-of-summaries over all sections)
2637
+ * conversation.search('ada') // case-insensitive across sections' originals + the live tail
2638
+ * section && conversation.rehydrate(section.id) // the section's full original messages (a pure read)
2394
2639
  * ```
2395
2640
  */
2396
2641
  export declare function createConversation(options?: ConversationOptions): ConversationInterface;
2397
2642
 
2398
2643
  /**
2399
2644
  * Creates a conversation registry — a {@link ConversationManagerInterface} holding
2400
- * {@link ConversationInterface}s keyed by their `id` (in insertion order) WITH an active pointer:
2645
+ * {@link ConversationInterface}s keyed by their `id`, in insertion order, with an active pointer:
2401
2646
  * the id-keyed store over the conversation layer plus the `active` / `switch` seam the context
2402
- * renders.
2647
+ * renders. `add` auto-activates the first conversation and flows the registry's default
2648
+ * `summarize` / `keep` into every conversation it creates.
2403
2649
  *
2404
2650
  * @remarks
2405
2651
  * Starts empty; `add(input?)` mints a {@link ConversationInterface} (its `id` from the input
2406
2652
  * or a random UUID), flowing the manager's default `summarize` / `keep` in unless the input
2407
2653
  * overrides them, and stores it (an already-present `id` overwrites — last write wins) — and
2408
- * AUTO-ACTIVATES the FIRST one (a registry with conversations always has one `active`); a later
2654
+ * auto-activates the first one (a registry with conversations always has one `active`); a later
2409
2655
  * `add` leaves `active` unchanged. `switch(id)` re-points `active` (an unknown `id` returns
2410
2656
  * `undefined`, leaving `active` unchanged — lenient, never throws); `conversation(id)` /
2411
2657
  * `conversations()` look up; `remove` (one or a batch) reports `true` only when every supplied id
2412
- * was removed AND clears `active` if it was a removed one; `clear` empties it and clears `active`.
2658
+ * was removed and clears `active` if it was a removed one; `clear` empties it and clears `active`.
2413
2659
  * Event-free
2414
- * (each conversation owns its own observable `emitter`). A conversation created with NEITHER a
2660
+ * (each conversation owns its own observable `emitter`). A conversation created with neither a
2415
2661
  * manager default nor a per-`add` `summarize` cannot `compact` (it throws a `ConversationError`).
2416
2662
  *
2417
2663
  * @param options - Optional default `summarize` / `keep` (see {@link ConversationManagerOptions})
@@ -2429,23 +2675,24 @@ export declare function createConversation(options?: ConversationOptions): Conve
2429
2675
  export declare function createConversationManager(options?: ConversationManagerOptions): ConversationManagerInterface;
2430
2676
 
2431
2677
  /**
2432
- * Creates a {@link DatabaseConversationStore} over any {@link DriverInterface} — the durable,
2433
- * driver-pluggable backing for the conversation persistence seam, the opt-in twin of
2678
+ * Creates a {@link DatabaseConversationStore} over any {@link DriverInterface}, defaulting to
2679
+ * `createMemoryDriver()` — the durable, driver-pluggable backing for the conversation persistence
2680
+ * seam, holding each snapshot as one opaque JSON column and standing as the opt-in twin of
2434
2681
  * {@link createMemoryConversationStore}. The exact twin of
2435
2682
  * {@link import('@orkestrel/workspace').createDatabaseWorkspaceStore}.
2436
2683
  *
2437
2684
  * @remarks
2438
2685
  * Builds a one-table database (`conversations`, keyed by `id`) over the supplied driver, the snapshot
2439
- * held as ONE OPAQUE JSON COLUMN — the column map is `{ id; snapshot }` where `snapshot` is a
2686
+ * held as one opaque JSON column — the column map is `{ id; snapshot }` where `snapshot` is a
2440
2687
  * `rawShape` (a JSON blob), exactly as
2441
2688
  * {@link import('@orkestrel/workspace').createDatabaseWorkspaceStore} stores its snapshot. The
2442
- * snapshot is already a COMPLETE, self-contained, pure-JSON payload, so storing it whole is lossless
2443
- * AND keeps the row type FLAT (the column reads back as `unknown`, narrowed on `get` by
2444
- * {@link import('./validators.js').isConversationSnapshot}). The `driver` DEFAULTS to
2445
- * {@link createMemoryDriver}, so the store ALSO works in memory out of the box; pass a server
2689
+ * snapshot is already a complete, self-contained, pure-JSON payload, so storing it whole is lossless
2690
+ * and keeps the row type flat (the column reads back as `unknown`, narrowed on `get` by
2691
+ * {@link import('./validators.js').isConversationSnapshot}). The `driver` defaults to
2692
+ * {@link createMemoryDriver}, so the store also works in memory out of the box; pass a server
2446
2693
  * `createJSONDriver` / `createSQLiteDriver` (or a browser IndexedDB driver) for a persistent one —
2447
2694
  * the durability is the driver's job, the store engine is shared. It swaps in behind
2448
- * {@link ConversationStoreInterface} WITHOUT touching the manager or the conversation.
2695
+ * {@link ConversationStoreInterface} without touching the manager or the conversation.
2449
2696
  *
2450
2697
  * @param driver - The storage backend the snapshots persist to (defaults to {@link createMemoryDriver})
2451
2698
  * @returns A {@link ConversationStoreInterface} over the driver
@@ -2491,7 +2738,7 @@ export declare function createInstruction(input: InstructionInput): InstructionI
2491
2738
  * immutable instructions keyed by `name`, listed by descending `priority`.
2492
2739
  *
2493
2740
  * @remarks
2494
- * Starts empty; `add` (one or a batch) MINTS each `id` and OVERWRITES a same-name
2741
+ * Starts empty; `add` (one or a batch) mints each `id` and overwrites a same-name
2495
2742
  * instruction (last write wins); `instructions()` lists them sorted by descending
2496
2743
  * `priority` (stable for ties); `open` / `render` are the build contract a richer
2497
2744
  * context renders an instructions block with; `remove` (one or a batch) reports `true` only
@@ -2519,7 +2766,7 @@ export declare function createInstructionManager(options?: InstructionManagerOpt
2519
2766
  /**
2520
2767
  * Creates the in-memory conversation store — a {@link ConversationStoreInterface} backed by a
2521
2768
  * process-lifetime `Map` of {@link import('./types.js').ConversationSnapshot}s keyed by conversation
2522
- * id, the DEFAULT backing for the durable {@link ConversationManagerInterface.open} /
2769
+ * id, the default backing for the durable {@link ConversationManagerInterface.open} /
2523
2770
  * {@link ConversationManagerInterface.save} seam. The exact twin of
2524
2771
  * {@link import('@orkestrel/workspace').createMemoryWorkspaceStore}.
2525
2772
  *
@@ -2527,10 +2774,10 @@ export declare function createInstructionManager(options?: InstructionManagerOpt
2527
2774
  * A plain `Map` (the snapshot is already pure JSON, so no encoding is needed for the memory tier),
2528
2775
  * the structural twin of {@link import('@orkestrel/workspace').createMemoryWorkspaceStore}.
2529
2776
  * `get` / `set` / `delete` are async (the
2530
- * same shape a durable backend fits); UNLIKE a session store there is NO idle-TTL / eviction — a
2777
+ * same shape a durable backend fits); unlike a session store there is no idle-TTL / eviction — a
2531
2778
  * persisted conversation lives until an explicit `delete`. Its driver-pluggable twin is
2532
2779
  * {@link createDatabaseConversationStore} (the snapshot as one opaque JSON column over a `databases`
2533
- * table) — for a DURABLE store pass it a JSON / SQLite / IndexedDB driver, and it swaps in WITHOUT
2780
+ * table) — for a durable store pass it a JSON / SQLite / IndexedDB driver, and it swaps in without
2534
2781
  * touching the manager or the conversation. Hydration stays a manager concern: read a snapshot back
2535
2782
  * and rebuild the live conversation through the `snapshot` option (re-supplying the live
2536
2783
  * `summarize` / `keep`).
@@ -2550,14 +2797,85 @@ export declare function createInstructionManager(options?: InstructionManagerOpt
2550
2797
  */
2551
2798
  export declare function createMemoryConversationStore(): ConversationStoreInterface;
2552
2799
 
2800
+ /**
2801
+ * Creates an authorized relay handler that validates a bounded JSON request before streaming.
2802
+ *
2803
+ * @remarks
2804
+ * Answers with the `401` status when authorization refuses or throws, the `400`
2805
+ * status when the body is missing, unreadable, or invalid, the `413` status when
2806
+ * the body fills its byte budget or the inbound read is aborted, and the `502`
2807
+ * status when the upstream provider call cannot be constructed. Refusals carry no body.
2808
+ *
2809
+ * @param options - The upstream provider, authorization decision, and optional byte budget
2810
+ * @returns A fetch-standard handler suitable for a router
2811
+ * @example Mounting the relay on your server
2812
+ * ```ts
2813
+ * import type { ProviderInterface } from '@orkestrel/agent'
2814
+ * import { createRelay } from '@orkestrel/agent'
2815
+ * import { createDispatcher } from '@orkestrel/router'
2816
+ * import { createServer } from '@orkestrel/server'
2817
+ *
2818
+ * declare const upstream: ProviderInterface // the server-side provider holding the credential
2819
+ * declare const bearer: string
2820
+ *
2821
+ * const handler = createRelay({
2822
+ * provider: upstream,
2823
+ * authorize: (request) => request.headers.get('authorization') === `Bearer ${bearer}`,
2824
+ * })
2825
+ * const dispatcher = createDispatcher({
2826
+ * routes: [{ method: 'POST', path: '/relay', handler }],
2827
+ * })
2828
+ *
2829
+ * export function serve(request: Request): Promise<Response> {
2830
+ * return dispatcher.handle(request, undefined)
2831
+ * }
2832
+ *
2833
+ * const server = createServer({ dispatcher, state: () => undefined })
2834
+ * await server.start()
2835
+ * process.on('SIGTERM', () => server.stop()) // signal cancellation, drain, then close the listener
2836
+ * ```
2837
+ */
2838
+ export declare function createRelay(options: RelayOptions): RelayHandler;
2839
+
2840
+ /**
2841
+ * Creates a provider that carries calls through a relay endpoint.
2842
+ *
2843
+ * @remarks
2844
+ * This is the browser end alone. {@link createRelay} mounts the server end, and its example
2845
+ * is the server half this one pairs with.
2846
+ *
2847
+ * @param options - The endpoint, parser factory, and HTTP call configuration
2848
+ * @returns The concrete relay provider
2849
+ * @example Reaching the relay from the browser
2850
+ * ```ts
2851
+ * import type { ProviderInterface } from '@orkestrel/agent'
2852
+ * import { createRelayProvider } from '@orkestrel/agent'
2853
+ * import { createAbort } from '@orkestrel/abort'
2854
+ * // The browser application supplies this parser dependency.
2855
+ * import { createNDJSONParser } from '@orkestrel/ndjson'
2856
+ *
2857
+ * declare const bearer: string
2858
+ * const abort = createAbort()
2859
+ * const messages = [{ id: '1', role: 'user', content: 'Say hello.' }] as const
2860
+ *
2861
+ * const browser: ProviderInterface = createRelayProvider({
2862
+ * url: 'https://app.example/relay',
2863
+ * parser: createNDJSONParser,
2864
+ * headers: () => ({ authorization: `Bearer ${bearer}` }),
2865
+ * })
2866
+ * const result = await browser.generate(messages, abort.signal) // a ProviderResult like a local provider's
2867
+ * ```
2868
+ */
2869
+ export declare function createRelayProvider(options: RelayProviderOptions): RelayProvider;
2870
+
2553
2871
  /**
2554
2872
  * Creates a named scope — an immutable {@link ScopeInterface} from its `name` and its
2555
2873
  * per-category allow-lists, the `id` minted at construction.
2556
2874
  *
2557
2875
  * @remarks
2558
- * Each list is THREE-WAY: `undefined` ⇒ NO constraint on that category (all pass), `[]` ⇒
2559
- * NONE pass, a non-empty list ⇒ only the listed keys pass. `narrow(config)` composes a
2560
- * tighter child by set-INTERSECTION (an `undefined` side imposing no constraint). Stored
2876
+ * Each list is three-way: `undefined` ⇒ no constraint on that category (all pass), `[]` ⇒
2877
+ * none pass, a non-empty list ⇒ only the listed keys pass. `narrow(config)` composes a
2878
+ * tighter child by set-intersection (an `undefined` side imposing no constraint). Stored
2561
2879
  * immutable — never mutated after creation (`narrow` returns a new scope).
2562
2880
  *
2563
2881
  * @param input - `name` (required) and the optional `instructions` / `tools` / `files`
@@ -2601,18 +2919,19 @@ export declare function createScope(input: ScopeInput): ScopeInterface;
2601
2919
  export declare function createScopeManager(options?: ScopeManagerOptions): ScopeManagerInterface;
2602
2920
 
2603
2921
  /**
2604
- * Creates a stream-stateful `<think>` separator — a {@link ThinkSplitterInterface} that
2605
- * splits a thinking model's in-content `<think>…</think>` reasoning spans away from
2606
- * the answer, delta by delta, so a provider yields ONLY clean content and surfaces the
2607
- * accumulated reasoning as {@link import('./types.js').ProviderResult.thinking}.
2922
+ * Creates a fresh stream-stateful `<think>` separator — a {@link ThinkSplitterInterface} that
2923
+ * splits a thinking model's in-content `<think>…</think>` reasoning spans away from the answer,
2924
+ * delta by delta, so a provider yields clean content alone and surfaces the accumulated
2925
+ * reasoning as {@link import('./types.js').ProviderResult.thinking}. One splitter serves one
2926
+ * stream.
2608
2927
  *
2609
2928
  * @remarks
2610
2929
  * Feed each raw wire delta through `split(delta)` (it returns the clean content to
2611
2930
  * surface — possibly `''` mid-think) and settle the stream end with `flush()` (a held
2612
- * partial open tag that never completed returns as final content; an UNCLOSED think
2613
- * span lands on `thinking`). Tags split ACROSS deltas are held back until
2931
+ * partial open tag that never completed returns as final content; an unclosed think
2932
+ * span lands on `thinking`). Tags split across deltas are held back until
2614
2933
  * disambiguated, multiple spans accumulate in order, and a nested-looking `<think>`
2615
- * inside an open span is thinking text. One splitter serves ONE stream — create
2934
+ * inside an open span is thinking text. One splitter serves one stream — create
2616
2935
  * a fresh one per provider call.
2617
2936
  *
2618
2937
  * @returns A fresh {@link ThinkSplitterInterface} (state empty, outside any span)
@@ -2631,37 +2950,39 @@ export declare function createThinkSplitter(): ThinkSplitterInterface;
2631
2950
 
2632
2951
  /**
2633
2952
  * Backs a {@link ConversationStoreInterface} with one table of the `databases` layer — a
2634
- * conversation's durable state IS a row, so persistence reduces to keyed point-access (`get` / `set`
2635
- * / `delete`) over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`
2636
- * {@link import('./MemoryConversationStore.js').MemoryConversationStore}. The EXACT twin of
2953
+ * conversation's durable state is a row holding the snapshot as one opaque JSON column, narrowed
2954
+ * back on `get` by {@link import('../../validators.js').isConversationSnapshot}, so persistence
2955
+ * reduces to keyed point-access (`get` / `set` / `delete`) over a {@link TableInterface}. The
2956
+ * driver-pluggable twin of the plain-`Map`
2957
+ * {@link import('./MemoryConversationStore.js').MemoryConversationStore}, and the exact twin of
2637
2958
  * {@link import('@orkestrel/workspace').DatabaseWorkspaceStore}.
2638
2959
  *
2639
2960
  * @remarks
2640
2961
  * The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,
2641
2962
  * JSON, SQLite, IndexedDB) is chosen by whoever builds it (the factories), so a JSON / SQLite /
2642
- * IndexedDB backend swaps in WITHOUT touching the
2963
+ * IndexedDB backend swaps in without touching the
2643
2964
  * {@link import('../ConversationManager.js').ConversationManager} or the
2644
2965
  * {@link import('../Conversation.js').Conversation} — the same seam as
2645
2966
  * {@link import('@orkestrel/workspace').DatabaseWorkspaceStore}. The
2646
2967
  * driver defaults to memory ({@link import('../../factories.js').createDatabaseConversationStore}
2647
- * passes `createMemoryDriver()`), so it ALSO works in memory out of the box; you opt into the
2968
+ * passes `createMemoryDriver()`), so it also works in memory out of the box; you opt into the
2648
2969
  * durable plumbing by passing a JSON / SQLite / IndexedDB driver.
2649
2970
  *
2650
- * The {@link ConversationSnapshot} is stored as ONE OPAQUE JSON COLUMN — the table is a row of
2971
+ * The {@link ConversationSnapshot} is stored as one opaque JSON column — the table is a row of
2651
2972
  * `{ id; snapshot }` ({@link ConversationSnapshotRow}), the snapshot the whole JSON blob (a
2652
2973
  * `rawShape` column the factory builds) — exactly as `DatabaseWorkspaceStore` stores its snapshot.
2653
- * The snapshot is already a COMPLETE, self-contained, pure-JSON payload, so storing it whole is
2654
- * lossless AND keeps the row type flat (`snapshot` reads back as `unknown`).
2974
+ * The snapshot is already a complete, self-contained, pure-JSON payload, so storing it whole is
2975
+ * lossless and keeps the row type flat (`snapshot` reads back as `unknown`).
2655
2976
  *
2656
- * - **`set(snapshot)` upserts under the snapshot's OWN `id`** (no separate id param) — it writes
2977
+ * - **`set(snapshot)` upserts under the snapshot's own `id`** (no separate id param) — it writes
2657
2978
  * the row `{ id: snapshot.id, snapshot }`.
2658
2979
  * - **`get(id)` resolves the stored snapshot for an id**, narrowing the opaque JSON column back to
2659
2980
  * a {@link ConversationSnapshot} ({@link import('../../validators.js').isConversationSnapshot} — the
2660
2981
  * total guard for an untrusted storage read), or `undefined` if none is stored.
2661
2982
  * - **`delete(id)` drops a snapshot by id**; an absent id is a no-op (no throw).
2662
2983
  *
2663
- * UNLIKE a session store there is NO idle-TTL / eviction — a persisted conversation lives until an
2664
- * explicit `delete`. The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the
2984
+ * Unlike a session store there is no idle-TTL / eviction — a persisted conversation lives until an
2985
+ * explicit `delete`. The public surface is exactly `get` / `set` / `delete` — no extra members (the
2665
2986
  * method bijection with {@link ConversationStoreInterface}). Hydration stays a caller concern: a
2666
2987
  * {@link import('../ConversationManager.js').ConversationManager} reads a snapshot back and rebuilds
2667
2988
  * the live conversation through the `snapshot` option (its `open` / `save`).
@@ -2690,39 +3011,51 @@ export declare class DatabaseConversationStore implements ConversationStoreInter
2690
3011
  constructor(table: TableInterface<ConversationSnapshotRow>);
2691
3012
  /** Resolves the persisted snapshot for `id`, narrowing the opaque JSON column back to a `ConversationSnapshot`. */
2692
3013
  get(id: string): Promise<ConversationSnapshot | undefined>;
2693
- /** Inserts or replaces under the snapshot's OWN `id` (no separate id param) — the row is `{ id, snapshot }`. */
3014
+ /** Inserts or replaces under the snapshot's own `id` (no separate id param) — the row is `{ id, snapshot }`. */
2694
3015
  set(snapshot: ConversationSnapshot): Promise<void>;
2695
3016
  /** Drops a snapshot by id; an absent id is a no-op (no throw). */
2696
3017
  delete(id: string): Promise<void>;
2697
3018
  }
2698
3019
 
2699
3020
  /**
2700
- * Caps an {@link AgentInterface} turn's tool iterations by default — the maximum
2701
- * number of context → provider → tools cycles before the loop stops, so a model that
2702
- * keeps requesting tools can never loop forever. Overridable per agent through
2703
- * `AgentOptions.limit`.
3021
+ * Caps an {@link AgentInterface} turn's tool iterations by default — `10` context → provider →
3022
+ * tools cycles before the loop stops, so a model that keeps requesting tools can never loop
3023
+ * forever. Overridable per agent through `AgentOptions.limit`.
2704
3024
  */
2705
3025
  export declare const DEFAULT_AGENT_LIMIT = 10;
2706
3026
 
2707
3027
  /**
2708
3028
  * Names the zone an {@link AuthorityInterface}'s default fallback {@link AuthorityDecision}
2709
- * carries — the classification for a tool call that matched no rule. Paired with the
2710
- * default `allowed: true` fallback, an unmatched call is allowed under this zone, so a
2711
- * rules list of denials acts as a denylist; a caller wanting deny-by-default supplies
2712
- * an `allowed: false` `fallback` of their own (see `AuthorityOptions`).
3029
+ * carries — `'default'`, the classification for a tool call that matched no rule. Paired with
3030
+ * the default `allowed: true` fallback, an unmatched call is allowed under this zone, so a
3031
+ * rules list of denials acts as a denylist; a caller wanting deny-by-default supplies an
3032
+ * `allowed: false` `fallback` of their own (see `AuthorityOptions`).
2713
3033
  */
2714
3034
  export declare const DEFAULT_AUTHORITY_ZONE = "default";
2715
3035
 
2716
3036
  /**
2717
3037
  * Sets the default number of recent live messages a {@link ConversationInterface}'s `compact()`
2718
- * RETAINS verbatim — `0`, so a manual `compact()` folds ALL of the current live messages
2719
- * into one summarized section (no tail kept). A caller retains a recent tail by passing
2720
- * `keep` (on {@link ConversationOptions}, {@link ConversationManagerOptions}, or per-fold
2721
- * through {@link CompactOptions}), folding only the older `count - keep` messages and leaving
2722
- * the most recent `keep` live for the next turn. Overridable everywhere `keep` is accepted.
3038
+ * retains verbatim — `0`, so a manual `compact()` folds every current live message into one
3039
+ * summarized section and keeps no tail. A caller retains a recent tail by passing `keep` (on
3040
+ * {@link ConversationOptions}, {@link ConversationManagerOptions}, or per-fold through
3041
+ * {@link CompactOptions}), folding only the older `count - keep` messages and leaving the most
3042
+ * recent `keep` live for the next turn. Overridable everywhere `keep` is accepted.
2723
3043
  */
2724
3044
  export declare const DEFAULT_CONVERSATION_KEEP = 0;
2725
3045
 
3046
+ /**
3047
+ * Holds the default provider deadline in milliseconds — `120_000`, the wall-clock bound a call
3048
+ * runs under when `AgentProviderInput.timeout` is omitted, folded with the caller's signal so
3049
+ * whichever trips first cancels the call.
3050
+ */
3051
+ export declare const DEFAULT_PROVIDER_TIMEOUT = 120000;
3052
+
3053
+ /**
3054
+ * Holds the default relay request limit in bytes — `1_048_576`, the byte budget a relay applies
3055
+ * to an inbound body when `RelayOptions.limit` is omitted, refusing a body that reaches it.
3056
+ */
3057
+ export declare const DEFAULT_RELAY_LIMIT = 1048576;
3058
+
2726
3059
  /**
2727
3060
  * Synthesizes the denial {@link ToolResult} an authority-blocked call is fed back with — the
2728
3061
  * call's `id` / `name` keyed back, carrying a denial `error` instead of a value.
@@ -2745,16 +3078,19 @@ export declare const DEFAULT_CONVERSATION_KEEP = 0;
2745
3078
  export declare function denyCall(call: ToolCall, reason: string | undefined): ToolResult;
2746
3079
 
2747
3080
  /**
2748
- * Estimates the context-token footprint of a batch of messages — the default `consumer`
2749
- * estimator for an agent's context `BudgetInterface` (a budgets surface's tracking contract)
2750
- * (the {@link import('./types.js').AgentOptions} `window`).
3081
+ * Estimates the context-token footprint of a batch of messages — each message's content plus
3082
+ * {@link import('./constants.js').MESSAGE_TOKEN_OVERHEAD}, a tool-call JSON estimate, and
3083
+ * {@link import('./constants.js').IMAGE_TOKEN_ESTIMATE} for each attached image. The default
3084
+ * `consumer` estimator for an agent's context budget (the
3085
+ * {@link import('./types.js').AgentOptions} `window`), total and never throwing, and a
3086
+ * deliberate provider-agnostic approximation rather than an exact tokenizer count.
2751
3087
  *
2752
3088
  * @remarks
2753
3089
  * Sums, per message, {@link estimateTokens} over its `content` (the `ceil(length / 4)` char
2754
- * heuristic) PLUS {@link import('./constants.js').MESSAGE_TOKEN_OVERHEAD} (a fixed per-message
2755
- * role/framing overhead) PLUS, when present, {@link estimateTokens} over its JSON-stringified
2756
- * `calls` PLUS `images.length * `{@link import('./constants.js').IMAGE_TOKEN_ESTIMATE} (a coarse,
2757
- * deliberately-approximate per-image cost — a base64 length is NOT a token proxy). Deterministic
3090
+ * heuristic) plus {@link import('./constants.js').MESSAGE_TOKEN_OVERHEAD} (a fixed per-message
3091
+ * role/framing overhead) plus, when present, {@link estimateTokens} over its JSON-stringified
3092
+ * `calls` plus `images.length * `{@link import('./constants.js').IMAGE_TOKEN_ESTIMATE} (a coarse,
3093
+ * deliberately-approximate per-image cost — a base64 length is not a token proxy). Deterministic
2758
3094
  * and provider-free — the same messages always yield the same estimate, with an empty batch `0`.
2759
3095
  * It is the fully-swappable default an agent's auto-compaction context budget charges each
2760
3096
  * turn's new messages through; a caller wanting a sharper count supplies its own `consumer` to
@@ -2775,15 +3111,15 @@ export declare function denyCall(call: ToolCall, reason: string | undefined): To
2775
3111
  export declare function estimateMessages(messages: readonly Message[]): number;
2776
3112
 
2777
3113
  /**
2778
- * Estimates the context-token footprint of a string — the deterministic char-based heuristic
2779
- * {@link estimateMessages} sums over a conversation's messages (the default context-budget
2780
- * estimator).
3114
+ * Estimates the context-token footprint of a string — the deterministic `ceil(length / 4)`
3115
+ * character heuristic {@link estimateMessages} sums over a conversation's messages (the default
3116
+ * context-budget estimator).
2781
3117
  *
2782
3118
  * @remarks
2783
3119
  * Approximates `ceil(length / 4)` (≈ four characters per token — the rough average for
2784
3120
  * English text), so the same input always yields the same estimate (no model round-trip).
2785
3121
  * Empty text is `0`. This is a planning heuristic for reasoning about how much a turn's
2786
- * messages cost the next request, NOT an exact tokenizer count — it never calls a provider,
3122
+ * messages cost the next request, not an exact tokenizer count — it never calls a provider,
2787
3123
  * so the agent layer stays provider-agnostic and synchronous where it can be.
2788
3124
  *
2789
3125
  * @param text - The text to estimate (a section summary, a message's content)
@@ -2799,15 +3135,16 @@ export declare function estimateMessages(messages: readonly Message[]): number;
2799
3135
  export declare function estimateTokens(text: string): number;
2800
3136
 
2801
3137
  /**
2802
- * Filters a list of items by a {@link import('./types.js').ScopeInterface} allow-list of
2803
- * keys — the pure, total set-membership primitive the context's build step and the agent
2804
- * loop's tool-advertise step apply a scope through.
3138
+ * Filters a list of items by a {@link import('./types.js').ScopeInterface} allow-list of keys —
3139
+ * `undefined` passes everything, `[]` passes nothing, and a non-empty list passes the listed keys
3140
+ * alone, order preserved. The pure, total set-membership primitive the context's build step and
3141
+ * the agent loop's tool-advertise step apply a scope through.
2805
3142
  *
2806
3143
  * @remarks
2807
3144
  * Three-way by the allow-list's shape, so a `Scope` category cleanly expresses "all /
2808
3145
  * none / only these":
2809
- * - `undefined` ⇒ NO constraint — every item passes (returned unchanged).
2810
- * - `[]` (empty) ⇒ NONE pass (no key is in an empty set).
3146
+ * - `undefined` ⇒ no constraint — every item passes (returned unchanged).
3147
+ * - `[]` (empty) ⇒ none pass (no key is in an empty set).
2811
3148
  * - a non-empty list ⇒ only items whose `key(item)` is in the list pass.
2812
3149
  *
2813
3150
  * Order-preserving (it filters `items` in place order, never reorders) and total — never
@@ -2858,11 +3195,12 @@ export declare function handleAgentQueueJob(registry: AgentRegistryInterface, pa
2858
3195
  export declare function handleAgentRunnerJob(registry: AgentRegistryInterface, partial: boolean, controller: ControllerInterface<AgentJobInput, AgentResult>): Promise<AgentResult>;
2859
3196
 
2860
3197
  /**
2861
- * Names the coarse, deliberately-approximate per-image token cost {@link import('./helpers.js').estimateMessages}
2862
- * charges for each attached image.
3198
+ * Names the coarse, deliberately approximate per-image token cost
3199
+ * {@link import('./helpers.js').estimateMessages} charges for each attached image — `512`, because
3200
+ * a base64 payload's length is no reliable token proxy.
2863
3201
  *
2864
3202
  * @remarks
2865
- * A base64 image payload's LENGTH is NOT a reliable token proxy (a vision model's actual image
3203
+ * A base64 image payload's length is not a reliable token proxy (a vision model's actual image
2866
3204
  * token cost depends on resolution / tiling, not byte size), so this is a fixed, coarse
2867
3205
  * per-image estimate rather than a derivation from `image.length` — a planning heuristic, not an
2868
3206
  * exact count.
@@ -2870,15 +3208,15 @@ export declare function handleAgentRunnerJob(registry: AgentRegistryInterface, p
2870
3208
  export declare const IMAGE_TOKEN_ESTIMATE = 512;
2871
3209
 
2872
3210
  /**
2873
- * Represents an immutable named directive — a {@link InstructionInterface} assembled once
2874
- * from its input (`name` / `content`, an optional `priority` defaulting to `0`), the `id`
2875
- * minted at construction.
3211
+ * Represents an immutable named directive — an {@link InstructionInterface} assembled once from
3212
+ * its input (`name` / `content`, an optional `priority` defaulting to `0`), the `id` minted at
3213
+ * construction.
2876
3214
  *
2877
3215
  * @remarks
2878
3216
  * A thin immutable value object (mirroring {@link import('@orkestrel/tool').Tool}): the
2879
3217
  * constructor mints a fresh `id` (`crypto.randomUUID()`), copies the input's `name` /
2880
3218
  * `content`, resolves `priority` to the input's value or `0`, and carries the input's
2881
- * per-item `override` ONLY when supplied (assigned when present, mirroring a
3219
+ * per-item `override` only when supplied (assigned when present, mirroring a
2882
3220
  * message's `images` / `calls` present-when-given convention — kept absent otherwise).
2883
3221
  * Never mutated after construction. An
2884
3222
  * {@link import('./InstructionManager.js').InstructionManager} keys it by `name` and
@@ -2910,7 +3248,7 @@ export declare interface InstructionInput {
2910
3248
  /** Weights the ordering (higher renders first); defaults to `0` when omitted. */
2911
3249
  readonly priority?: number;
2912
3250
  /**
2913
- * Holds a fully-rendered override of THIS instruction's prompt text — the most-specific
3251
+ * Holds a fully-rendered override of this instruction's prompt text — the most-specific
2914
3252
  * level of the {@link import('./AgentContext.js').AgentContext} build cascade (beats a
2915
3253
  * manager-options / provider / built-in format for this item). Round-tripped onto the
2916
3254
  * stored {@link InstructionInterface} when given (present-when-supplied, like `images`).
@@ -2937,8 +3275,8 @@ export declare interface InstructionInterface {
2937
3275
  readonly priority: number;
2938
3276
  /**
2939
3277
  * Holds a fully-rendered per-item override of this instruction's prompt text — the
2940
- * MOST-SPECIFIC level of the {@link import('./AgentContext.js').AgentContext} build
2941
- * cascade, beating every format level for THIS item. Present only when supplied on the
3278
+ * most-specific level of the {@link import('./AgentContext.js').AgentContext} build
3279
+ * cascade, beating every format level for this item. Present only when supplied on the
2942
3280
  * {@link InstructionInput} (round-tripped through the manager, like a message's
2943
3281
  * `images`); absent ⇒ the cascade decides.
2944
3282
  */
@@ -2946,28 +3284,29 @@ export declare interface InstructionInterface {
2946
3284
  }
2947
3285
 
2948
3286
  /**
2949
- * Registers the immutable {@link Instruction}s a richer context assembles a directives
2950
- * block from — keyed by `name`, listed by descending `priority`.
3287
+ * Registers the immutable {@link Instruction}s a richer context assembles a directives block
3288
+ * from — keyed by `name` so a re-`add` overwrites, last write wins, and listed by descending
3289
+ * `priority`, carrying the `open` / `render` build contract and an observable `emitter`.
2951
3290
  *
2952
3291
  * @remarks
2953
3292
  * - **Registry.** Instructions live in an insertion-ordered `Map` keyed by `name`;
2954
- * `add` takes one {@link InstructionInput} or a batch, MINTS each instruction's
2955
- * `id`, and a re-`add` of the same name OVERWRITES it (last write wins). `count` is the
2956
- * map size, `instruction(name)` looks one up, and `instructions()` lists them SORTED by
3293
+ * `add` takes one {@link InstructionInput} or a batch, mints each instruction's
3294
+ * `id`, and a re-`add` of the same name overwrites it (last write wins). `count` is the
3295
+ * map size, `instruction(name)` looks one up, and `instructions()` lists them sorted by
2957
3296
  * descending `priority` (a stable sort, so equal priorities keep insertion order).
2958
3297
  * - **Build contract (with the manager-options override).** `open` is the section
2959
3298
  * header a context renders the instructions under; `render(instruction)` renders one
2960
- * instruction (its `content`). Each ENCAPSULATES the cascade's `[options-override →
3299
+ * instruction (its `content`). Each encapsulates the cascade's `[options-override →
2961
3300
  * built-in]` half: when `InstructionManagerOptions.format` supplies an `open` /
2962
- * `render`, `open` / `render` return IT, else the built-in — so a richer context
3301
+ * `render`, `open` / `render` return it, else the built-in — so a richer context
2963
3302
  * reads one consistent pair and layers the provider default + per-item override on top
2964
3303
  * (see {@link import('../AgentContext.js').AgentContext}). The per-item
2965
3304
  * {@link InstructionInput.override} is round-tripped onto the stored instruction.
2966
- * - **Removal.** `remove` drops one by name, or a batch — `true` only when EVERY supplied
3305
+ * - **Removal.** `remove` drops one by name, or a batch — `true` only when every supplied
2967
3306
  * name was removed; `clear` empties the registry.
2968
3307
  * - **Observable.** The owned {@link emitter} ({@link InstructionManagerEventMap})
2969
3308
  * carries `add` (the created instruction) / `remove` (the name) / `clear` for
2970
- * fire-and-forget observers. Every event is emitted directly, strictly AFTER the map
3309
+ * fire-and-forget observers. Every event is emitted directly, strictly after the map
2971
3310
  * mutation completes; the emitter isolates a listener throw and routes it to its `error`
2972
3311
  * handler (the `error` option), so a buggy observer can never corrupt a mutation.
2973
3312
  *
@@ -3020,9 +3359,9 @@ export declare type InstructionManagerEventMap = {
3020
3359
  };
3021
3360
 
3022
3361
  /**
3023
- * Registers {@link InstructionInterface}s keyed by `name` — `add` (one or a batch)
3024
- * mints each `id` and OVERWRITES a same-name instruction (last write wins);
3025
- * `instructions()` lists them SORTED by descending `priority` (stable for ties).
3362
+ * Registers {@link InstructionInterface}s keyed by `name` — `add` (one or a batch) mints each `id`
3363
+ * and overwrites a same-name instruction, last write wins, while `instructions()` lists them sorted
3364
+ * by descending `priority` and stable for ties.
3026
3365
  *
3027
3366
  * @remarks
3028
3367
  * - **Build contract.** `open` is the section header a richer context renders
@@ -3040,22 +3379,32 @@ export declare interface InstructionManagerInterface {
3040
3379
  readonly open: string;
3041
3380
  /**
3042
3381
  * Holds the manager-options format override (the {@link InstructionManagerOptions.format}
3043
- * supplied at construction), or `undefined` when none — the MANAGER-OPTIONS level of the
3382
+ * supplied at construction), or `undefined` when none — the manager-options level of the
3044
3383
  * {@link import('./AgentContext.js').AgentContext} build cascade, exposed so `build()`
3045
- * can interleave the provider default BENEATH it (`open` / `render` already
3384
+ * can interleave the provider default beneath it (`open` / `render` already
3046
3385
  * encapsulate the `[override → built-in]` half for standalone use). A `readonly` data
3047
3386
  * member, not a method.
3048
3387
  */
3049
3388
  readonly format: ContextSectionFormat<InstructionInterface> | undefined;
3389
+ /**
3390
+ * Adds one {@link InstructionInput}, or a batch — mints each `id`; a re-`add` of the same
3391
+ * name overwrites it, last write wins.
3392
+ */
3050
3393
  add(input: InstructionInput): InstructionInterface;
3051
3394
  add(inputs: readonly InstructionInput[]): readonly InstructionInterface[];
3395
+ /** Looks up one instruction by name (`undefined` when absent). */
3052
3396
  instruction(name: string): InstructionInterface | undefined;
3053
3397
  /** Lists every instruction, sorted by descending `priority` (stable for equal priorities). */
3054
3398
  instructions(): readonly InstructionInterface[];
3055
3399
  /** Renders one instruction for the prompt — its `content`. */
3056
3400
  render(instruction: InstructionInterface): string;
3401
+ /**
3402
+ * Removes one instruction by name, or a batch — `true` only when every supplied name was
3403
+ * removed.
3404
+ */
3057
3405
  remove(name: string): boolean;
3058
3406
  remove(names: readonly string[]): boolean;
3407
+ /** Removes every instruction. */
3059
3408
  clear(): void;
3060
3409
  }
3061
3410
 
@@ -3066,10 +3415,10 @@ export declare interface InstructionManagerInterface {
3066
3415
  * @remarks
3067
3416
  * `on` is the reserved listener key: initial listeners for the manager's
3068
3417
  * {@link InstructionManagerEventMap}, wired at construction. `format` is the
3069
- * MANAGER-OPTIONS level of the {@link import('./AgentContext.js').AgentContext} build
3070
- * cascade — a {@link ContextSectionFormat} the manager consults FIRST in its own
3418
+ * manager-options level of the {@link import('./AgentContext.js').AgentContext} build
3419
+ * cascade — a {@link ContextSectionFormat} the manager consults first in its own
3071
3420
  * `open` / `render` (falling back to the built-in when a member is omitted), so it
3072
- * BEATS the provider default and the built-in, while a per-item
3421
+ * beats the provider default and the built-in, while a per-item
3073
3422
  * {@link InstructionInput.override} still beats it. Omitted ⇒ the built-in framing applies.
3074
3423
  */
3075
3424
  export declare interface InstructionManagerOptions {
@@ -3081,12 +3430,12 @@ export declare interface InstructionManagerOptions {
3081
3430
  }
3082
3431
 
3083
3432
  /**
3084
- * Intersects two scope category lists under the "`undefined` is the universal set" rule — the
3085
- * primitive a scope narrows through.
3433
+ * Intersects two scope category lists under the "`undefined` is the universal set" rule — a
3434
+ * fresh copy that can only tighten, and the primitive a scope narrows through.
3086
3435
  *
3087
3436
  * @remarks
3088
- * Pure and total, and it can only TIGHTEN: `undefined` ∩ `undefined` is `undefined` (still no
3089
- * constraint); `undefined` ∩ a list is a COPY of that list (the `undefined` side imposes
3437
+ * Pure and total, and it can only tighten: `undefined` ∩ `undefined` is `undefined` (still no
3438
+ * constraint); `undefined` ∩ a list is a copy of that list (the `undefined` side imposes
3090
3439
  * nothing); a list ∩ a list keeps the child keys the parent also allows, so a parent-excluded
3091
3440
  * key can never be re-admitted. Every returned list is a fresh copy, so a later mutation of
3092
3441
  * either input cannot leak into the result.
@@ -3105,7 +3454,15 @@ export declare interface InstructionManagerOptions {
3105
3454
  export declare function intersectKeys(parent: readonly string[] | undefined, child: readonly string[] | undefined): readonly string[] | undefined;
3106
3455
 
3107
3456
  /**
3108
- * Narrows an unknown caught value to an {@link AgentError}.
3457
+ * Names the status a relay answers for a body that is missing, unreadable, or rejected by
3458
+ * `providerRequestContract` — `400`, carried with no body and reaching the browser as a
3459
+ * `ProviderError` with the `HTTP` code.
3460
+ */
3461
+ export declare const INVALID_RELAY_STATUS = 400;
3462
+
3463
+ /**
3464
+ * Narrows an unknown caught value to an {@link AgentError} through `instanceof`, so a `catch`
3465
+ * can branch on its `code`.
3109
3466
  *
3110
3467
  * @param value - The value to test (typically a `catch` binding)
3111
3468
  * @returns True if `value` is an {@link AgentError}; false otherwise
@@ -3122,7 +3479,8 @@ export declare function intersectKeys(parent: readonly string[] | undefined, chi
3122
3479
  export declare function isAgentError(value: unknown): value is AgentError;
3123
3480
 
3124
3481
  /**
3125
- * Narrows an unknown caught value to an {@link AgentJobError}.
3482
+ * Narrows an unknown caught value to an {@link AgentJobError} through `instanceof`, so a
3483
+ * `catch` can recover its `partial` result.
3126
3484
  *
3127
3485
  * @param value - The value to test (typically a `catch` binding or a rejected enqueue)
3128
3486
  * @returns True if `value` is an {@link AgentJobError}; false otherwise
@@ -3139,7 +3497,8 @@ export declare function isAgentError(value: unknown): value is AgentError;
3139
3497
  export declare function isAgentJobError(value: unknown): value is AgentJobError;
3140
3498
 
3141
3499
  /**
3142
- * Narrows an unknown caught value to a {@link ConversationError}.
3500
+ * Narrows an unknown caught value to a {@link ConversationError} through `instanceof`, so a
3501
+ * `catch` can branch on its `code`.
3143
3502
  *
3144
3503
  * @param value - The value to test (typically a `catch` binding)
3145
3504
  * @returns True if `value` is a {@link ConversationError}; false otherwise
@@ -3156,19 +3515,20 @@ export declare function isAgentJobError(value: unknown): value is AgentJobError;
3156
3515
  export declare function isConversationError(value: unknown): value is ConversationError;
3157
3516
 
3158
3517
  /**
3159
- * Narrows an `unknown` to a {@link ConversationSnapshot} — the total boundary guard for an
3160
- * UNTRUSTED snapshot read (a storage row a
3518
+ * Narrows an `unknown` to a {@link ConversationSnapshot} — a `string` `id`, an optional `string`
3519
+ * `summary`, and valid `sections` and `messages` arrays; the total boundary guard for an
3520
+ * untrusted snapshot read (a storage row a
3161
3521
  * {@link import('./conversations/stores/DatabaseConversationStore.js').DatabaseConversationStore}
3162
- * reads back from its opaque JSON column, a snapshot loaded from disk). The EXACT analogue of
3163
- * {@link import('@orkestrel/workspace').isWorkspaceSnapshot}.
3522
+ * reads back from its opaque JSON column, a snapshot loaded from disk), never throwing. The exact
3523
+ * analogue of {@link import('@orkestrel/workspace').isWorkspaceSnapshot}.
3164
3524
  *
3165
3525
  * @remarks
3166
- * A total guard (it NEVER throws — adversarial input returns `false`). It checks the snapshot's
3167
- * SHAPE: a `string` `id`, an OPTIONAL `string` `summary` (present-or-absent — the rollup is
3168
- * `undefined` until the first compaction), a `sections` array EVERY element of which is a valid
3169
- * {@link Section} ({@link isSection}), and a `messages` array EVERY element of which is a
3526
+ * A total guard (it never throws — adversarial input returns `false`). It checks the snapshot's
3527
+ * shape: a `string` `id`, an optional `string` `summary` (present-or-absent — the rollup is
3528
+ * `undefined` until the first compaction), a `sections` array every element of which is a valid
3529
+ * {@link Section} ({@link isSection}), and a `messages` array every element of which is a
3170
3530
  * valid {@link Message} ({@link isMessage}) — enough to safely impose the
3171
- * {@link ConversationSnapshot} type at a storage boundary WITHOUT a cast. The structural twin of
3531
+ * {@link ConversationSnapshot} type at a storage boundary without a cast. The structural twin of
3172
3532
  * {@link import('@orkestrel/workspace').isWorkspaceSnapshot}. A malformed blob (a non-record, a missing / non-string `id`, a
3173
3533
  * non-string `summary` when present, a non-array `sections` / `messages`, or any malformed
3174
3534
  * element) resolves `false`, so a
@@ -3189,39 +3549,27 @@ export declare function isConversationError(value: unknown): value is Conversati
3189
3549
  export declare function isConversationSnapshot(value: unknown): value is ConversationSnapshot;
3190
3550
 
3191
3551
  /**
3192
- * Checks whether an `unknown` is structurally a {@link Message} record — the per-message step of
3193
- * the {@link isConversationSnapshot} read-boundary narrow (a total guard over an untrusted storage
3194
- * read, never an assertion). The conversation analogue of
3195
- * {@link import('@orkestrel/workspace').isFile}.
3552
+ * Checks whether a value satisfies the domain conversation-message contract.
3196
3553
  *
3197
3554
  * @remarks
3198
- * A total guard (it NEVER throws — adversarial input returns `false`). It checks the message's
3199
- * SHAPE: a record with a `string` `id`, a `string` `role`, a `string` `content`, and — WHEN present
3200
- * — a `calls` array EVERY element of which is a valid
3201
- * {@link import('@orkestrel/tool').ToolCall} ({@link isToolCall} — the
3202
- * fail-closed deepening: a tampered `calls` element rejects the message, so the snapshot
3203
- * reads back as absent rather than replaying a malformed call) and an `images` that is an array
3204
- * (an absent optional passes). The `role` is left as a broad `string` here (an open
3205
- * {@link import('./types.js').MessageRole}, so any storage-read role string is accepted
3206
- * defensively rather than rejected against the current literal set). Enough to safely impose the
3207
- * {@link Message} type at
3208
- * a storage boundary WITHOUT a cast.
3209
- *
3210
- * @param value - The value to test (one element of a snapshot's `messages` / a section's `messages`)
3211
- * @returns True if `value` has the structural shape of a {@link Message}; false otherwise
3555
+ * Roles belong to MessageRole and image elements are strings. Tool arguments may
3556
+ * carry non-JSON values, as the domain type permits; the message wire contract is
3557
+ * narrower. Unreadable fields and hostile inputs return false.
3212
3558
  *
3559
+ * @param value - The unknown message candidate
3560
+ * @returns True if the domain message fields are valid; false otherwise
3213
3561
  * @example
3214
3562
  * ```ts
3215
3563
  * isMessage({ id: '1', role: 'user', content: 'hi' }) // true
3216
- * isMessage({ id: '1', role: 'assistant', content: '', calls: [] }) // true
3217
- * isMessage({ id: '1', role: 'user' }) // false (missing content)
3218
- * isMessage({ id: '1', role: 'assistant', content: '', calls: [null] }) // false (malformed call)
3564
+ * isMessage({ id: '1', role: 'other', content: '' }) // false
3565
+ * isMessage({ id: '1', role: 'user', content: '', images: [1] }) // false
3219
3566
  * ```
3220
3567
  */
3221
3568
  export declare function isMessage(value: unknown): value is Message;
3222
3569
 
3223
3570
  /**
3224
- * Narrows an unknown caught value to a {@link ProviderAbortError}.
3571
+ * Narrows an unknown caught value to a {@link ProviderAbortError} through `instanceof`, so a
3572
+ * `catch` can recover its `partial` result.
3225
3573
  *
3226
3574
  * @param value - The value to test (typically a `catch` binding)
3227
3575
  * @returns True if `value` is a {@link ProviderAbortError}; false otherwise
@@ -3238,15 +3586,28 @@ export declare function isMessage(value: unknown): value is Message;
3238
3586
  export declare function isProviderAbortError(value: unknown): value is ProviderAbortError;
3239
3587
 
3240
3588
  /**
3241
- * Checks whether an `unknown` is structurally a {@link Section} record — the per-section step of
3242
- * the {@link isConversationSnapshot} read-boundary narrow (a total guard over an untrusted storage
3243
- * read, never an assertion).
3589
+ * Narrows a caught value to the provider failure class through instanceof.
3590
+ *
3591
+ * @param value - The caught value
3592
+ * @returns True if the value is a provider failure; false otherwise
3593
+ * @example
3594
+ * ```ts
3595
+ * isProviderError(new ProviderError('HTTP', 'unavailable', { status: 503 })) // true
3596
+ * ```
3597
+ */
3598
+ export declare function isProviderError(value: unknown): value is ProviderError;
3599
+
3600
+ /**
3601
+ * Checks whether an `unknown` is structurally a {@link Section} record — a `string` `id` and
3602
+ * `summary` beside a `messages` array of valid {@link Message}s, the per-section step of the
3603
+ * {@link isConversationSnapshot} read-boundary narrow. Total, never throwing, and never an
3604
+ * assertion.
3244
3605
  *
3245
3606
  * @remarks
3246
- * A total guard (it NEVER throws — adversarial input returns `false`). It checks the section's
3247
- * SHAPE: a record with a `string` `id`, a `string` `summary`, and a `messages` array EVERY element
3607
+ * A total guard (it never throws — adversarial input returns `false`). It checks the section's
3608
+ * shape: a record with a `string` `id`, a `string` `summary`, and a `messages` array every element
3248
3609
  * of which is a valid {@link Message} record ({@link isMessage}). Enough to safely impose
3249
- * the {@link Section} type at a storage boundary WITHOUT a cast.
3610
+ * the {@link Section} type at a storage boundary without a cast.
3250
3611
  *
3251
3612
  * @param value - The value to test (one element of a snapshot's `sections` array)
3252
3613
  * @returns True if `value` has the structural shape of a {@link Section}; false otherwise
@@ -3277,33 +3638,42 @@ export declare function isSection(value: unknown): value is Section;
3277
3638
  * @example
3278
3639
  * ```ts
3279
3640
  * joinThinking(undefined, 'first') // 'first'
3641
+ * joinThinking('', 'x') // 'x'
3280
3642
  * joinThinking('first', 'second') // 'first\n\nsecond'
3281
3643
  * ```
3282
3644
  */
3283
3645
  export declare function joinThinking(running: string | undefined, next: string): string;
3284
3646
 
3647
+ /**
3648
+ * Bounds the decoded error excerpt's input in bytes — `2048`, the leading bytes of a non-OK
3649
+ * response body handed to the decoder before the read cancels the remainder, so a `ProviderError`
3650
+ * message never carries a longer excerpt.
3651
+ */
3652
+ export declare const MAX_ERROR_BODY_LENGTH = 2048;
3653
+
3285
3654
  /**
3286
3655
  * Implements the {@link ConversationStoreInterface} in memory — a process-lifetime `Map` of
3287
- * {@link ConversationSnapshot}s keyed by conversation id, the DEFAULT store
3288
- * {@link import('../../factories.js').createMemoryConversationStore} builds. The EXACT twin of
3656
+ * {@link ConversationSnapshot}s keyed by conversation id, the default store
3657
+ * {@link import('../../factories.js').createMemoryConversationStore} builds and the default
3658
+ * backing for `open` / `save`. The exact twin of
3289
3659
  * {@link import('@orkestrel/workspace').MemoryWorkspaceStore}.
3290
3660
  *
3291
3661
  * @remarks
3292
3662
  * A plain `Map<string, ConversationSnapshot>` — the snapshot is already pure,
3293
3663
  * self-contained JSON, so no encoding is needed for the memory tier. Like the
3294
3664
  * {@link import('@orkestrel/workspace').MemoryWorkspaceStore} it twins,
3295
- * there is NO idle-TTL and NO eviction: a persisted conversation lives until an explicit `delete`. A
3296
- * durable backend (JSON / SQLite / IndexedDB) swaps in through the SAME interface without touching
3665
+ * there is no idle-TTL and no eviction: a persisted conversation lives until an explicit `delete`. A
3666
+ * durable backend (JSON / SQLite / IndexedDB) swaps in through the same interface without touching
3297
3667
  * the {@link import('../ConversationManager.js').ConversationManager} or the
3298
3668
  * {@link import('../Conversation.js').Conversation} — its driver-pluggable twin is
3299
3669
  * {@link import('./DatabaseConversationStore.js').DatabaseConversationStore} (the snapshot as one
3300
3670
  * opaque JSON column).
3301
3671
  *
3302
3672
  * - **`get` resolves the persisted snapshot for an id**, or `undefined` if none is stored.
3303
- * - **`set` inserts / replaces under the snapshot's OWN `id`** (no separate id param).
3673
+ * - **`set` inserts / replaces under the snapshot's own `id`** (no separate id param).
3304
3674
  * - **`delete` drops a snapshot by id**; an absent id is a no-op (no throw).
3305
3675
  *
3306
- * The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the method
3676
+ * The public surface is exactly `get` / `set` / `delete` — no extra members (the method
3307
3677
  * bijection with {@link ConversationStoreInterface}). Hydration is a caller concern: a
3308
3678
  * {@link import('../ConversationManager.js').ConversationManager} reads a snapshot back and rebuilds
3309
3679
  * the live conversation through the `snapshot` option (its `open` / `save`).
@@ -3322,8 +3692,27 @@ export declare function joinThinking(running: string | undefined, next: string):
3322
3692
  */
3323
3693
  export declare class MemoryConversationStore implements ConversationStoreInterface {
3324
3694
  #private;
3695
+ /**
3696
+ * Resolves the persisted snapshot for `id`, or `undefined` if none is stored.
3697
+ *
3698
+ * @param id - The conversation id to resolve (a {@link ConversationSnapshot.id})
3699
+ * @returns The persisted snapshot, or `undefined` if absent
3700
+ */
3325
3701
  get(id: string): Promise<ConversationSnapshot | undefined>;
3702
+ /**
3703
+ * Inserts or replaces a snapshot under its own `snapshot.id` (no separate id param —
3704
+ * mirroring {@link import('@orkestrel/workspace').WorkspaceStoreInterface}'s `set`).
3705
+ *
3706
+ * @param snapshot - The snapshot to store (keyed by its `id`)
3707
+ * @returns A promise that resolves after the snapshot is stored
3708
+ */
3326
3709
  set(snapshot: ConversationSnapshot): Promise<void>;
3710
+ /**
3711
+ * Drops a snapshot by id; an absent id is a no-op (no throw).
3712
+ *
3713
+ * @param id - The conversation id to drop
3714
+ * @returns A promise that resolves after the snapshot is dropped
3715
+ */
3327
3716
  delete(id: string): Promise<void>;
3328
3717
  }
3329
3718
 
@@ -3352,13 +3741,30 @@ export declare interface Message {
3352
3741
  }
3353
3742
 
3354
3743
  /**
3355
- * Estimates the per-message role/framing overhead {@link import('./helpers.js').estimateMessages}
3356
- * adds on top of a message's content estimate — accounts for the fixed wire framing every
3357
- * conversation turn carries (its role tag, delimiters) that {@link import('./helpers.js').estimateTokens}'s
3358
- * content-only heuristic does not otherwise capture.
3744
+ * Estimates the per-message role and framing overhead {@link import('./helpers.js').estimateMessages}
3745
+ * adds on top of a message's content estimate — `4` tokens for the fixed wire framing every
3746
+ * conversation turn carries (its role tag, its delimiters) that
3747
+ * {@link import('./helpers.js').estimateTokens}'s content-only heuristic does not otherwise
3748
+ * capture.
3359
3749
  */
3360
3750
  export declare const MESSAGE_TOKEN_OVERHEAD = 4;
3361
3751
 
3752
+ /** Validates and projects conversation messages at a JSON wire boundary. */
3753
+ export declare const messageContract: ContractInterface<Readonly<{
3754
+ id: string;
3755
+ role: "system" | "user" | "assistant" | "tool";
3756
+ content: string;
3757
+ } & {
3758
+ calls?: readonly Readonly<{
3759
+ id: string;
3760
+ name: string;
3761
+ arguments: {
3762
+ readonly [k: string]: JSONValue;
3763
+ };
3764
+ } & {}>[];
3765
+ images?: readonly string[];
3766
+ }>>;
3767
+
3362
3768
  /**
3363
3769
  * Carries the minimal data needed to author a {@link Message} — the `id` is
3364
3770
  * assigned by the layer that stores it, so a caller supplies only role / content
@@ -3376,64 +3782,103 @@ export declare interface MessageInput {
3376
3782
  }
3377
3783
 
3378
3784
  /**
3379
- * Stores immutable {@link Message}s in insertion order; `add` mints the `id`.
3785
+ * Stores immutable {@link Message}s in insertion order and mints each `id` on `add` — the
3786
+ * message-store contract {@link AgentContextInterface.messages} is typed to, which the active
3787
+ * {@link ConversationInterface} satisfies structurally.
3380
3788
  *
3381
3789
  * @remarks
3382
3790
  * - **Store.** Messages live in insertion order; `count` is how many are stored.
3383
- * `add` takes one {@link MessageInput} or a batch and MINTS each message's
3791
+ * `add` takes one {@link MessageInput} or a batch and mints each message's
3384
3792
  * `id` (a random UUID), returning the created message(s). A stored message is
3385
3793
  * immutable — created once from its input, never mutated.
3386
3794
  * - **Lookup.** `message(id)` resolves one by id (`undefined` when absent);
3387
3795
  * `messages()` lists every message in insertion order.
3388
- * - **Removal.** `remove` drops one by id, or a batch — `true` only when EVERY supplied id
3796
+ * - **Removal.** `remove` drops one by id, or a batch — `true` only when every supplied id
3389
3797
  * was removed; `clear` empties the store.
3390
3798
  * - **Event-free.** A purely data store — no Emitter, no events.
3391
3799
  */
3392
3800
  export declare interface MessageManagerInterface {
3393
3801
  readonly count: number;
3802
+ /**
3803
+ * Stores one {@link MessageInput}, or a batch — mints each message's `id` and returns the
3804
+ * created message or messages; a stored message is immutable.
3805
+ */
3394
3806
  add(input: MessageInput): Message;
3395
3807
  add(inputs: readonly MessageInput[]): readonly Message[];
3808
+ /** Looks up one stored message by id (`undefined` when absent). */
3396
3809
  message(id: string): Message | undefined;
3810
+ /** Lists every stored message, in insertion order. */
3397
3811
  messages(): readonly Message[];
3812
+ /**
3813
+ * Removes one message by id, or a batch — `true` only when every supplied id was removed.
3814
+ */
3398
3815
  remove(id: string): boolean;
3399
3816
  remove(ids: readonly string[]): boolean;
3817
+ /** Removes every stored message. */
3400
3818
  clear(): void;
3401
3819
  }
3402
3820
 
3403
3821
  /** Names the role a {@link Message} plays in a conversation turn. */
3404
3822
  export declare type MessageRole = 'system' | 'user' | 'assistant' | 'tool';
3405
3823
 
3824
+ /**
3825
+ * Describes a conversation message's JSON wire projection.
3826
+ *
3827
+ * @remarks
3828
+ * The wire is strictly narrower than Message: non-JSON call arguments are refused,
3829
+ * and caller context is refused by the guard or dropped by the parser.
3830
+ */
3831
+ export declare const messageShape: ObjectShape< {
3832
+ id: StringShape;
3833
+ role: LiteralShape<readonly ["system", "user", "assistant", "tool"]>;
3834
+ content: StringShape;
3835
+ calls: OptionalShape<ArrayShape<ObjectShape< {
3836
+ id: StringShape;
3837
+ name: StringShape;
3838
+ arguments: ObjectShape<Record<never, never>, JSONShape>;
3839
+ }, boolean | ContractShape>>>;
3840
+ images: OptionalShape<ArrayShape<StringShape>>;
3841
+ }, boolean | ContractShape>;
3842
+
3843
+ /**
3844
+ * Names the status a relay answers for a request body at or above its byte budget — `413`,
3845
+ * carried with no body and answered for an aborted inbound read as well.
3846
+ */
3847
+ export declare const OVERSIZED_RELAY_STATUS = 413;
3848
+
3406
3849
  /**
3407
3850
  * Reports a provider stream cancelled mid-flight by its bound signal — thrown by a
3408
- * {@link ProviderInterface}'s `stream`, carrying the {@link ProviderResult} assembled
3409
- * from whatever streamed before the cancel.
3851
+ * {@link ProviderInterface}'s `stream`, carrying the {@link ProviderResult} assembled from
3852
+ * whatever streamed before the cancel and the machine-readable `code` `'ABORT'`.
3410
3853
  *
3411
3854
  * @remarks
3412
3855
  * Lets a caller recover the partial content (and any tool calls / usage seen so far)
3413
3856
  * on cancellation: `catch` the throw, narrow with {@link isProviderAbortError}, and
3414
3857
  * read `partial`. `code` is the machine-readable condition (`'ABORT'` — the only one this
3415
- * error reports), so a `catch` branches on it rather than on the message string.
3858
+ * error reports), so a `catch` branches on it rather than on the message string. `cause`
3859
+ * holds the failure the cancel superseded when a throw raced the abort — the wire decoder's
3860
+ * {@link ProviderError}, say — and is undefined when the cancel was the only failure.
3416
3861
  */
3417
3862
  export declare class ProviderAbortError extends Error {
3418
3863
  /** Names the machine-readable condition — `'ABORT'`: a stream cancelled mid-flight. */
3419
3864
  readonly code: "ABORT";
3420
3865
  readonly partial: ProviderResult;
3421
- constructor(partial: ProviderResult);
3866
+ constructor(partial: ProviderResult, options?: ErrorOptions);
3422
3867
  }
3423
3868
 
3424
3869
  /**
3425
- * Represents one streamed delta a {@link ProviderInterface}'s `stream` yields — a TAGGED unit
3426
- * discriminated by the channel it belongs to, so the agent loop can re-surface the two
3427
- * channels separately (answer content vs. live reasoning) as it pumps.
3870
+ * Represents one streamed delta a {@link ProviderInterface}'s `stream` yields — a unit tagged by
3871
+ * the channel it belongs to, so the agent loop can re-surface answer content and live reasoning
3872
+ * separately as it pumps.
3428
3873
  *
3429
3874
  * @remarks
3430
3875
  * The discriminant `channel` names the axis that varies: a
3431
- * `'content'` delta is a chunk of the assistant ANSWER (the deltas that accumulate into
3876
+ * `'content'` delta is a chunk of the assistant answer (the deltas that accumulate into
3432
3877
  * {@link ProviderResult.content}); a `'thinking'` delta is a chunk of the model's
3433
- * REASONING the provider separated from the answer (the daemon's native
3434
- * `message.thinking` wire channel), surfaced LIVE so a consumer can stream it into a
3878
+ * reasoning the provider separated from the answer (the daemon's native
3879
+ * `message.thinking` wire channel), surfaced live so a consumer can stream it into a
3435
3880
  * collapsible without waiting for the assembled result. `text` is the delta's literal
3436
- * text. Thinking NEVER re-enters the conversation — it is display/audit metadata, exactly
3881
+ * text. Thinking never re-enters the conversation — it is display/audit metadata, exactly
3437
3882
  * as {@link ProviderResult.thinking} (the authoritative final accumulation) is.
3438
3883
  */
3439
3884
  export declare type ProviderDelta = {
@@ -3445,10 +3890,51 @@ export declare type ProviderDelta = {
3445
3890
  };
3446
3891
 
3447
3892
  /**
3448
- * Defines the pluggable LLM inference boundary — the one contract every agent chunk depends
3449
- * on. A provider turns a conversation (plus optional tools) into either a single
3450
- * assembled {@link ProviderResult} (`generate`) or a stream of {@link ProviderDelta}s that
3451
- * RETURNS the assembled result (`stream`).
3893
+ * Reports a coded provider failure with its HTTP status and underlying cause when available.
3894
+ *
3895
+ * @remarks
3896
+ * The provider base throws this error for a non-OK HTTP response, a missing response
3897
+ * body, or a missing settled result when strict assembly is enabled. Concrete wire
3898
+ * decoders also use it for malformed records and relayed provider failures.
3899
+ * `status` is present only for an `HTTP` failure; other codes leave it undefined.
3900
+ */
3901
+ export declare class ProviderError extends Error {
3902
+ /** Names the machine-readable condition — `'HTTP'`: a non-OK response, including a relay refusing an oversized request with 413; `'PROTOCOL'`: a missing response body, a malformed wire record, or a strict stream with no settled result; `'PROVIDER'`: an upstream failure carried by a relay error record. */
3903
+ readonly code: ProviderErrorCode;
3904
+ /** Holds the response status for an HTTP failure, or undefined for other codes. */
3905
+ readonly status: number | undefined;
3906
+ constructor(code: ProviderErrorCode, message: string, options?: ProviderErrorOptions);
3907
+ }
3908
+
3909
+ /** Names the machine-readable provider failure conditions. */
3910
+ export declare type ProviderErrorCode =
3911
+ /** Reports a non-OK HTTP response, including a relay request rejected with status 413. */
3912
+ 'HTTP'
3913
+ /** Reports a missing body, malformed wire record, or missing required settled result. */
3914
+ | 'PROTOCOL'
3915
+ /** Reports an upstream provider failure carried by a relay error record. */
3916
+ | 'PROVIDER';
3917
+
3918
+ /** Carries a provider failure's HTTP status and underlying cause. */
3919
+ export declare interface ProviderErrorOptions {
3920
+ readonly status?: number;
3921
+ readonly cause?: unknown;
3922
+ }
3923
+
3924
+ /** Holds the decoded contribution of a wire record to a provider turn. */
3925
+ export declare interface ProviderIncrement {
3926
+ readonly content: string;
3927
+ readonly thinking: string;
3928
+ readonly tools: readonly ToolCall[];
3929
+ readonly usage?: TokenUsage;
3930
+ readonly result?: ProviderResult;
3931
+ }
3932
+
3933
+ /**
3934
+ * Defines the pluggable LLM inference boundary — the one contract every agent chunk depends on. A
3935
+ * provider turns a conversation (plus optional tools) into either a single assembled {@link
3936
+ * ProviderResult} (`generate`) or a stream of {@link ProviderDelta}s that returns the assembled
3937
+ * result (`stream`).
3452
3938
  *
3453
3939
  * @remarks
3454
3940
  * - `id` is a stable per-instance trace label; `name` identifies the backend
@@ -3464,9 +3950,9 @@ export declare interface ProviderInterface {
3464
3950
  readonly id: string;
3465
3951
  readonly name: string;
3466
3952
  /**
3467
- * Holds the model's preferred context framing, by section kind — an OPTIONAL
3953
+ * Holds the model's preferred context framing, by section kind — an optional
3468
3954
  * {@link ContextFormat} an {@link import('./AgentContext.js').AgentContext}
3469
- * applies as the PROVIDER-DEFAULT level of its build cascade (beating the managers'
3955
+ * applies as the provider-default level of its build cascade (beating the managers'
3470
3956
  * built-in framing, beaten by a manager-options or per-item override). Omitted ⇒ the
3471
3957
  * provider is framing-agnostic and the managers' built-in defaults apply unchanged.
3472
3958
  */
@@ -3482,14 +3968,14 @@ export declare interface ProviderInterface {
3482
3968
  */
3483
3969
  generate(messages: readonly Message[], signal: AbortSignal, tools?: readonly ToolDefinition[], options?: ProviderStreamOptions): Promise<ProviderResult>;
3484
3970
  /**
3485
- * Streams one turn — yields {@link ProviderDelta}s (channel-tagged `content` / `thinking`
3486
- * chunks) as they arrive and RETURNS the assembled {@link ProviderResult} (the
3487
- * concatenated content + any separated reasoning + any tool calls + any usage) when the
3488
- * stream completes.
3971
+ * Streams one turn — yields channel-tagged `content` / `thinking` {@link ProviderDelta}s as
3972
+ * they arrive and returns the assembled {@link ProviderResult} (the concatenated content,
3973
+ * any separated reasoning, any tool calls, and any usage) when the stream completes. A
3974
+ * mid-stream abort throws a `ProviderAbortError` carrying the partial result.
3489
3975
  *
3490
3976
  * @remarks
3491
- * An abort mid-stream throws a `ProviderAbortError` whose `partial` holds whatever
3492
- * streamed before the cancel, so a caller can recover the partial content.
3977
+ * The `partial` holds whatever streamed before the cancel, so a caller can recover the
3978
+ * partial content.
3493
3979
  *
3494
3980
  * @param messages - The conversation so far
3495
3981
  * @param signal - Bounds the request; an abort throws `ProviderAbortError`
@@ -3500,15 +3986,110 @@ export declare interface ProviderInterface {
3500
3986
  stream(messages: readonly Message[], signal: AbortSignal, tools?: readonly ToolDefinition[], options?: ProviderStreamOptions): AsyncGenerator<ProviderDelta, ProviderResult>;
3501
3987
  }
3502
3988
 
3989
+ /**
3990
+ * Configures a provider's deadline, transport, headers, and context framing.
3991
+ *
3992
+ * @remarks
3993
+ * `timeout` is an integer duration in milliseconds. Default: 120_000.
3994
+ * `fetch` defaults to the global transport bound to its global receiver.
3995
+ * `headers` runs for each request inside its deadline and receives the combined caller
3996
+ * and deadline signal so token requests can share that bound. It overrides the JSON
3997
+ * content type only when it returns that header. `format` is exposed to context assembly.
3998
+ */
3999
+ export declare interface ProviderOptions {
4000
+ readonly timeout?: number;
4001
+ readonly fetch?: typeof globalThis.fetch;
4002
+ readonly headers?: (signal: AbortSignal) => Readonly<Record<string, string>> | Promise<Readonly<Record<string, string>>>;
4003
+ readonly format?: ContextFormat;
4004
+ }
4005
+
4006
+ /** Defines the structural framing seam supplied by a concrete provider. */
4007
+ export declare interface ProviderParserInterface<TRecord = Readonly<Record<string, unknown>>> {
4008
+ /** Parses a decoded chunk into complete records. */
4009
+ parse(chunk: string): readonly TRecord[];
4010
+ /** Clears retained framing state. */
4011
+ clear(): void;
4012
+ }
4013
+
4014
+ /** Carries the conversation and per-call configuration sent to a provider. */
4015
+ export declare interface ProviderRequest {
4016
+ readonly messages: readonly Message[];
4017
+ readonly tools?: readonly ToolDefinition[];
4018
+ readonly options?: ProviderStreamOptions;
4019
+ }
4020
+
4021
+ /** Validates and projects provider requests at a JSON wire boundary. */
4022
+ export declare const providerRequestContract: ContractInterface<Readonly<{
4023
+ messages: readonly Readonly<{
4024
+ id: string;
4025
+ role: "system" | "user" | "assistant" | "tool";
4026
+ content: string;
4027
+ } & {
4028
+ calls?: readonly Readonly<{
4029
+ id: string;
4030
+ name: string;
4031
+ arguments: {
4032
+ readonly [k: string]: JSONValue;
4033
+ };
4034
+ } & {}>[];
4035
+ images?: readonly string[];
4036
+ }>[];
4037
+ } & {
4038
+ tools?: readonly Readonly<{
4039
+ name: string;
4040
+ } & {
4041
+ description?: string;
4042
+ parameters?: {
4043
+ readonly [k: string]: JSONValue;
4044
+ };
4045
+ }>[];
4046
+ options?: Readonly<{} & {
4047
+ think?: boolean;
4048
+ schema?: {
4049
+ readonly [k: string]: JSONValue;
4050
+ };
4051
+ }>;
4052
+ }>>;
4053
+
4054
+ /**
4055
+ * Describes a provider request's JSON wire projection.
4056
+ *
4057
+ * @remarks
4058
+ * The wire is strictly narrower than ProviderRequest: non-JSON arguments, parameters,
4059
+ * or schema members are refused, and caller context is refused or dropped.
4060
+ */
4061
+ export declare const providerRequestShape: ObjectShape< {
4062
+ messages: ArrayShape<ObjectShape< {
4063
+ id: StringShape;
4064
+ role: LiteralShape<readonly ["system", "user", "assistant", "tool"]>;
4065
+ content: StringShape;
4066
+ calls: OptionalShape<ArrayShape<ObjectShape< {
4067
+ id: StringShape;
4068
+ name: StringShape;
4069
+ arguments: ObjectShape<Record<never, never>, JSONShape>;
4070
+ }, boolean | ContractShape>>>;
4071
+ images: OptionalShape<ArrayShape<StringShape>>;
4072
+ }, boolean | ContractShape>>;
4073
+ tools: OptionalShape<ArrayShape<ObjectShape< {
4074
+ name: StringShape;
4075
+ description: OptionalShape<StringShape>;
4076
+ parameters: OptionalShape<ObjectShape<Record<never, never>, JSONShape>>;
4077
+ }, boolean | ContractShape>>>;
4078
+ options: OptionalShape<ObjectShape< {
4079
+ think: OptionalShape<BooleanShape>;
4080
+ schema: OptionalShape<ObjectShape<Record<never, never>, JSONShape>>;
4081
+ }, boolean | ContractShape>>;
4082
+ }, boolean | ContractShape>;
4083
+
3503
4084
  /**
3504
4085
  * Holds a single inference turn's structured outcome — the assembled assistant content,
3505
4086
  * any reasoning the provider separated from it, any tool calls the model requested,
3506
4087
  * and the token usage it reported.
3507
4088
  *
3508
4089
  * @remarks
3509
- * `thinking` is present only when the turn produced reasoning the provider SPLIT
3510
- * AWAY from the answer (an in-content `<think>…</think>` span a thinking model
3511
- * emitted, or a wire-side reasoning field) — `content` is always the CLEAN answer,
4090
+ * `thinking` is present only when the turn produced reasoning the provider split
4091
+ * away from the answer (an in-content `<think>…</think>` span a thinking model
4092
+ * emitted, or a wire-side reasoning field) — `content` is always the clean answer,
3512
4093
  * and the thinking never re-enters the conversation (it is display/audit metadata,
3513
4094
  * not prompt text). `tools` is present only when the model wants tool calls (an
3514
4095
  * empty array is never surfaced — its absence means "no calls"). `usage` is present
@@ -3525,19 +4106,60 @@ export declare interface ProviderResult {
3525
4106
  readonly usage?: TokenUsage;
3526
4107
  }
3527
4108
 
4109
+ /** Validates and projects provider results at a JSON wire boundary. */
4110
+ export declare const providerResultContract: ContractInterface<Readonly<{
4111
+ content: string;
4112
+ } & {
4113
+ thinking?: string;
4114
+ tools?: readonly Readonly<{
4115
+ id: string;
4116
+ name: string;
4117
+ arguments: {
4118
+ readonly [k: string]: JSONValue;
4119
+ };
4120
+ } & {}>[];
4121
+ usage?: Readonly<{
4122
+ prompt: number;
4123
+ completion: number;
4124
+ total: number;
4125
+ } & {}>;
4126
+ }>>;
4127
+
4128
+ /**
4129
+ * Describes a provider result's JSON wire projection.
4130
+ *
4131
+ * @remarks
4132
+ * The wire is strictly narrower than ProviderResult: non-JSON call arguments are
4133
+ * refused, and caller context is refused or dropped.
4134
+ */
4135
+ export declare const providerResultShape: ObjectShape< {
4136
+ content: StringShape;
4137
+ thinking: OptionalShape<StringShape>;
4138
+ tools: OptionalShape<ArrayShape<ObjectShape< {
4139
+ id: StringShape;
4140
+ name: StringShape;
4141
+ arguments: ObjectShape<Record<never, never>, JSONShape>;
4142
+ }, boolean | ContractShape>>>;
4143
+ usage: OptionalShape<ObjectShape< {
4144
+ prompt: NumberShape;
4145
+ completion: NumberShape;
4146
+ total: NumberShape;
4147
+ }, boolean | ContractShape>>;
4148
+ }, boolean | ContractShape>;
4149
+
3528
4150
  /**
3529
4151
  * Carries the per-call options threaded into a {@link ProviderInterface}'s `generate` / `stream` —
3530
- * the bag a caller passes to influence ONE inference call without reconfiguring the
3531
- * provider instance.
4152
+ * the bag a caller passes to influence one inference call without reconfiguring the provider
4153
+ * instance.
3532
4154
  *
3533
4155
  * @remarks
3534
- * `think` OVERRIDES the provider's constructed reasoning preference for THIS call: `true`
4156
+ * `think` overrides the provider's constructed reasoning preference for this call: `true`
3535
4157
  * asks the backend to separate reasoning natively (a thinking model returns it on its
3536
4158
  * `message.thinking` channel, surfaced as `'thinking'` {@link ProviderDelta}s + the final
3537
4159
  * {@link ProviderResult.thinking}); `false` suppresses it. `schema`, when given, asks the
3538
4160
  * backend to constrain its response to the given JSON-Schema shape (the same open
3539
4161
  * JSON-Schema record {@link ToolDefinition.parameters} already carries) — a structured-output
3540
- * request for THIS call only. Both omitted ⇒ the provider's own defaults apply (the
4162
+ * request for this call only. Both omitted ⇒ the provider's own defaults apply (the
3541
4163
  * constructor value / no schema constraint), so the contract stays backward-safe — a caller
3542
4164
  * that passes no options behaves exactly as before.
3543
4165
  */
@@ -3549,9 +4171,302 @@ export declare interface ProviderStreamOptions {
3549
4171
  }
3550
4172
 
3551
4173
  /**
3552
- * Renders a path-addressed text body as a fenced reference block — the framing an
3553
- * {@link import('./AgentContext.js').AgentContext}'s ACTIVE-workspace text-file render emits (the
3554
- * active workspace is the SOLE document/image context).
4174
+ * Decodes UTF-8 chunks with a final flush and releases the stream on every exit.
4175
+ *
4176
+ * @param body - The readable byte stream
4177
+ * @param signal - The optional cancellation bound; abort ends iteration without further chunks
4178
+ * @returns Decoded text chunks, including a held decoder tail
4179
+ * @example
4180
+ * ```ts
4181
+ * const body = new Response('answer').body
4182
+ * if (body !== null) for await (const chunk of readChunks(body)) render(chunk)
4183
+ * ```
4184
+ */
4185
+ export declare function readChunks(body: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncGenerator<string>;
4186
+
4187
+ /**
4188
+ * Reads a UTF-8 prefix of a byte stream and cancels its remainder.
4189
+ *
4190
+ * @remarks
4191
+ * The `limit` parameter bounds bytes passed to the decoder, including a partial final
4192
+ * character. An omitted limit reads to completion. A source may deliver a chunk larger
4193
+ * than the remaining limit; its unused bytes are discarded without decoding.
4194
+ * The read can overshoot by one source chunk. An abort leaves completion false.
4195
+ *
4196
+ * @param body - The readable byte stream
4197
+ * @param limit - The maximum byte prefix, or undefined for the complete stream
4198
+ * @param signal - The optional cancellation bound; abort returns the decoded prefix
4199
+ * @returns The decoded prefix and whether EOF occurred within the byte budget
4200
+ * @example
4201
+ * ```ts
4202
+ * import { readText } from '@orkestrel/agent'
4203
+ *
4204
+ * const body = new Response('answer').body
4205
+ * if (body !== null) await readText(body, 3) // { text: 'ans', complete: false }
4206
+ * ```
4207
+ */
4208
+ export declare function readText(body: ReadableStream<Uint8Array>, limit?: number, signal?: AbortSignal): Promise<TextRead>;
4209
+
4210
+ /**
4211
+ * Names the relay's newline-delimited JSON content type — `'application/x-ndjson; charset=utf-8'`,
4212
+ * the header a relay response carries beside `cache-control: no-store`.
4213
+ */
4214
+ export declare const RELAY_CONTENT_TYPE = "application/x-ndjson; charset=utf-8";
4215
+
4216
+ /**
4217
+ * Names the public message for an unexpected upstream relay failure — `'relay provider failed'`,
4218
+ * the fixed text every `error` frame carries, so an upstream failure's own message never reaches
4219
+ * the browser.
4220
+ */
4221
+ export declare const RELAY_PROVIDER_MESSAGE = "relay provider failed";
4222
+
4223
+ /** Carries a relay delta, settled result, remote abort, or remote failure. */
4224
+ export declare type RelayFrame = ProviderDelta | {
4225
+ readonly channel: 'result';
4226
+ readonly result: ProviderResult;
4227
+ } | {
4228
+ readonly channel: 'abort';
4229
+ readonly partial: ProviderResult;
4230
+ } | {
4231
+ readonly channel: 'error';
4232
+ readonly message: string;
4233
+ };
4234
+
4235
+ /** Validates and projects channel-discriminated relay frames at a JSON wire boundary. */
4236
+ export declare const relayFrameContract: ContractInterface<Readonly<{
4237
+ channel: "content" | "thinking";
4238
+ text: string;
4239
+ } & {}> | Readonly<{
4240
+ channel: "result";
4241
+ result: Readonly<{
4242
+ content: string;
4243
+ } & {
4244
+ thinking?: string;
4245
+ tools?: readonly Readonly<{
4246
+ id: string;
4247
+ name: string;
4248
+ arguments: {
4249
+ readonly [k: string]: JSONValue;
4250
+ };
4251
+ } & {}>[];
4252
+ usage?: Readonly<{
4253
+ prompt: number;
4254
+ completion: number;
4255
+ total: number;
4256
+ } & {}>;
4257
+ }>;
4258
+ } & {}> | Readonly<{
4259
+ channel: "abort";
4260
+ partial: Readonly<{
4261
+ content: string;
4262
+ } & {
4263
+ thinking?: string;
4264
+ tools?: readonly Readonly<{
4265
+ id: string;
4266
+ name: string;
4267
+ arguments: {
4268
+ readonly [k: string]: JSONValue;
4269
+ };
4270
+ } & {}>[];
4271
+ usage?: Readonly<{
4272
+ prompt: number;
4273
+ completion: number;
4274
+ total: number;
4275
+ } & {}>;
4276
+ }>;
4277
+ } & {}> | Readonly<{
4278
+ channel: "error";
4279
+ message: string;
4280
+ } & {}>>;
4281
+
4282
+ /**
4283
+ * Describes the channel-discriminated JSON relay wire projection.
4284
+ *
4285
+ * @remarks
4286
+ * The wire is strictly narrower than RelayFrame through its result and partial
4287
+ * fields: non-JSON arguments are refused, and caller context is refused or dropped.
4288
+ */
4289
+ export declare const relayFrameShape: UnionShape<readonly [ObjectShape< {
4290
+ channel: LiteralShape<readonly ["content", "thinking"]>;
4291
+ text: StringShape;
4292
+ }, boolean | ContractShape>, ObjectShape< {
4293
+ channel: LiteralShape<readonly ["result"]>;
4294
+ result: ObjectShape< {
4295
+ content: StringShape;
4296
+ thinking: OptionalShape<StringShape>;
4297
+ tools: OptionalShape<ArrayShape<ObjectShape< {
4298
+ id: StringShape;
4299
+ name: StringShape;
4300
+ arguments: ObjectShape<Record<never, never>, JSONShape>;
4301
+ }, boolean | ContractShape>>>;
4302
+ usage: OptionalShape<ObjectShape< {
4303
+ prompt: NumberShape;
4304
+ completion: NumberShape;
4305
+ total: NumberShape;
4306
+ }, boolean | ContractShape>>;
4307
+ }, boolean | ContractShape>;
4308
+ }, boolean | ContractShape>, ObjectShape< {
4309
+ channel: LiteralShape<readonly ["abort"]>;
4310
+ partial: ObjectShape< {
4311
+ content: StringShape;
4312
+ thinking: OptionalShape<StringShape>;
4313
+ tools: OptionalShape<ArrayShape<ObjectShape< {
4314
+ id: StringShape;
4315
+ name: StringShape;
4316
+ arguments: ObjectShape<Record<never, never>, JSONShape>;
4317
+ }, boolean | ContractShape>>>;
4318
+ usage: OptionalShape<ObjectShape< {
4319
+ prompt: NumberShape;
4320
+ completion: NumberShape;
4321
+ total: NumberShape;
4322
+ }, boolean | ContractShape>>;
4323
+ }, boolean | ContractShape>;
4324
+ }, boolean | ContractShape>, ObjectShape< {
4325
+ channel: LiteralShape<readonly ["error"]>;
4326
+ message: StringShape;
4327
+ }, boolean | ContractShape>]>;
4328
+
4329
+ /** Defines a host-independent relay request handler. */
4330
+ export declare type RelayHandler = (request: Request) => Promise<Response>;
4331
+
4332
+ /** Configures the upstream provider, mandatory authorization, and request byte limit. */
4333
+ export declare interface RelayOptions {
4334
+ readonly provider: ProviderInterface;
4335
+ /**
4336
+ * Authorizes the request before its body is read.
4337
+ *
4338
+ * @remarks
4339
+ * The hook must not consume the request body; a body-reading hook locks the stream
4340
+ * and the handler answers with the `400` status. The relay performs no origin or
4341
+ * method check. An application trusting an ambient credential such as a cookie
4342
+ * must compose origin and CSRF middleware before the handler to prevent cross-site calls.
4343
+ */
4344
+ readonly authorize: (request: Request) => boolean | Promise<boolean>;
4345
+ /**
4346
+ * Bounds the request body in bytes; the handler answers with the `413` status
4347
+ * for a body at or above the limit.
4348
+ */
4349
+ readonly limit?: number;
4350
+ }
4351
+
4352
+ /**
4353
+ * Carries provider calls over an authenticated NDJSON relay endpoint.
4354
+ *
4355
+ * @remarks
4356
+ * The `ToolCall.caller` member never crosses the hop. The wire body is an owned snapshot of
4357
+ * the projection read through property descriptors, so a serializer reachable only through a
4358
+ * `get` trap or a prototype is never consulted; an own function-valued property such as a
4359
+ * `toJSON` method is a value outside JSON and is refused before fetching, with the clone's
4360
+ * failure as the refusal's `cause`. A remote abort reconstructs a `ProviderAbortError` instance
4361
+ * without aborting the
4362
+ * local signal; the `Agent` runtime treats that instance as an error unless its own bound
4363
+ * signal is aborted.
4364
+ * Content is preserved verbatim, including literal thinking tags.
4365
+ * A refusal reaches the browser as a `ProviderError` instance with the `HTTP` code and status.
4366
+ * This is the browser end alone; {@link createRelay} mounts the server end, and
4367
+ * {@link createRelayProvider}'s example is the browser half of that pair.
4368
+ *
4369
+ * @example
4370
+ * ```ts
4371
+ * import type { ProviderResult } from '@orkestrel/agent'
4372
+ * import { RelayProvider } from '@orkestrel/agent'
4373
+ * // The browser application supplies this parser dependency.
4374
+ * import { createNDJSONParser } from '@orkestrel/ndjson'
4375
+ *
4376
+ * export function ask(bearer: string, signal: AbortSignal): Promise<ProviderResult> {
4377
+ * const browser = new RelayProvider({
4378
+ * url: 'https://relay.example/relay',
4379
+ * parser: createNDJSONParser,
4380
+ * headers: () => ({ authorization: `Bearer ${bearer}` }),
4381
+ * })
4382
+ * return browser.generate([{ id: 'ask', role: 'user', content: 'ping' }], signal)
4383
+ * }
4384
+ * ```
4385
+ */
4386
+ export declare class RelayProvider extends AgentProvider {
4387
+ #private;
4388
+ constructor(options: RelayProviderOptions);
4389
+ /** Identifies the relay backend. */
4390
+ readonly name = "relay";
4391
+ /** Creates fresh framing state for each response. */
4392
+ frame(): ProviderParserInterface;
4393
+ /**
4394
+ * Projects declared request fields and refuses values the JSON wire cannot carry.
4395
+ *
4396
+ * @remarks
4397
+ * The snapshot is taken from property descriptors, so a custom serializer a projected
4398
+ * value carries is ignored rather than consulted and cannot reach the wire.
4399
+ *
4400
+ * @param request - The domain conversation and call configuration
4401
+ * @returns The validated wire request with caller context omitted
4402
+ * @throws {ProviderError} Thrown when the snapshot cannot be taken — a hostile read or a
4403
+ * value outside JSON — carrying that failure as its `cause`, and when the snapshot the
4404
+ * projection produced is not a valid wire request
4405
+ */
4406
+ body(request: ProviderRequest): object;
4407
+ /**
4408
+ * Validates a relay frame and translates its channel into the shared stream engine.
4409
+ *
4410
+ * @param record - The framed wire record
4411
+ * @returns A delta contribution or authoritative result
4412
+ * @throws {ProviderAbortError} Thrown for a remote abort carrying its partial
4413
+ * @throws {ProviderError} Thrown for a malformed frame or remote provider failure
4414
+ */
4415
+ read(record: Readonly<Record<string, unknown>>): ProviderIncrement;
4416
+ /**
4417
+ * Recovers an unterminated final frame by completing its NDJSON line.
4418
+ *
4419
+ * @param parser - The call's retained framing state
4420
+ * @returns Records completed by the final newline
4421
+ */
4422
+ finish(parser: ProviderParserInterface): ReadonlyArray<Readonly<Record<string, unknown>>>;
4423
+ }
4424
+
4425
+ /** Configures a relay destination and its fresh structural parser factory. */
4426
+ export declare interface RelayProviderOptions extends ProviderOptions {
4427
+ readonly url: string;
4428
+ /** Creates a fresh parser for each relay response stream. */
4429
+ readonly parser: () => ProviderParserInterface;
4430
+ }
4431
+
4432
+ /**
4433
+ * Streams a provider call as validated NDJSON frames under response backpressure.
4434
+ *
4435
+ * @remarks
4436
+ * Cancellation aborts upstream before returning its iterator. Request listeners are
4437
+ * released on settlement. Unexpected provider failures carry a fixed public message.
4438
+ * An inbound abort leaves the response body neither closed nor errored — a server runtime
4439
+ * cancels that body when the client disconnects — so a consumer that aborts the inbound
4440
+ * signal itself must cancel the body rather than keep reading it.
4441
+ *
4442
+ * @example
4443
+ * ```ts
4444
+ * import type { ProviderInterface } from '@orkestrel/agent'
4445
+ * import { RelayStream } from '@orkestrel/agent'
4446
+ *
4447
+ * export function respond(provider: ProviderInterface, signal: AbortSignal): Response {
4448
+ * return new RelayStream({ provider, request: { messages: [] }, signal }).response
4449
+ * }
4450
+ * ```
4451
+ */
4452
+ export declare class RelayStream {
4453
+ #private;
4454
+ constructor(options: RelayStreamOptions);
4455
+ /** Exposes the pull-driven response for the provider call. */
4456
+ get response(): Response;
4457
+ }
4458
+
4459
+ /** Carries the upstream call and cancellation bound of a relay response stream. */
4460
+ export declare interface RelayStreamOptions {
4461
+ readonly provider: ProviderInterface;
4462
+ readonly request: ProviderRequest;
4463
+ readonly signal: AbortSignal;
4464
+ }
4465
+
4466
+ /**
4467
+ * Renders a path-addressed text body as a fenced reference block — a `File: <path>` label line
4468
+ * over a language-tagged fence, the framing an
4469
+ * {@link import('./AgentContext.js').AgentContext}'s active-workspace text-file render emits.
3555
4470
  *
3556
4471
  * @remarks
3557
4472
  * Produces `` File: <path>\n```<language>\n<content>\n``` `` — the `File:` label line, then a
@@ -3577,10 +4492,10 @@ export declare function renderFencedFile(path: string, language: string, content
3577
4492
 
3578
4493
  /**
3579
4494
  * Renders one context section — the resolved `open`, each item's rendering, and the resolved
3580
- * `close` when one exists, blank-line joined.
4495
+ * `close` when one exists, blank-line joined; `undefined` when the section has no items.
3581
4496
  *
3582
4497
  * @remarks
3583
- * Pure and total. A section with NO items renders nothing (`undefined`), so an empty or fully
4498
+ * Pure and total. A section with no items renders nothing (`undefined`), so an empty or fully
3584
4499
  * scoped-out manager stays silent — its `open` / `close` never appear without items. `close`
3585
4500
  * is the only optional slot: an unset one (there is no built-in close) drops the
3586
4501
  * trailing line.
@@ -3602,13 +4517,13 @@ export declare function renderFencedFile(path: string, language: string, content
3602
4517
  export declare function renderSection<T>(open: string, items: readonly T[], render: (item: T) => string, close: string | undefined): string | undefined;
3603
4518
 
3604
4519
  /**
3605
- * Resolves one section's CLOSE text through the format cascade — manager-options override >
3606
- * provider default.
4520
+ * Resolves one section's close text through the format cascade — manager-options override >
4521
+ * provider default; `undefined` when neither sets one, because there is no built-in close.
3607
4522
  *
3608
4523
  * @remarks
3609
- * Pure and total. There is NO built-in close, so a section with neither level set returns
4524
+ * Pure and total. There is no built-in close, so a section with neither level set returns
3610
4525
  * `undefined` and {@link renderSection} appends no closing line. Paired with
3611
- * {@link resolveOpen}, one level can WRAP the whole group.
4526
+ * {@link resolveOpen}, one level can wrap the whole group.
3612
4527
  *
3613
4528
  * @typeParam T - The section item the manager renders
3614
4529
  * @param manager - The section source (its `format` override)
@@ -3624,14 +4539,14 @@ export declare function renderSection<T>(open: string, items: readonly T[], rend
3624
4539
  export declare function resolveClose<T>(manager: ContextSectionSourceInterface<T>, provider: ContextSectionFormat<T> | undefined): string | undefined;
3625
4540
 
3626
4541
  /**
3627
- * Resolves ONE item's rendering through the format cascade — item override >
3628
- * manager-options override > provider default > built-in rendering.
4542
+ * Resolves one item's rendering through the format cascade — item override > manager-options
4543
+ * override > provider default > built-in rendering.
3629
4544
  *
3630
4545
  * @remarks
3631
4546
  * Pure and total. The item's own `override` is the most-specific level (a fully-rendered
3632
4547
  * string for that item alone). A manager's `render(item)` already encapsulates
3633
4548
  * `[options-override → built-in]`, so it is reached only when no higher level applies — and
3634
- * there it IS the built-in rendering.
4549
+ * there it is the built-in rendering.
3635
4550
  *
3636
4551
  * @typeParam T - The section item being rendered (it may carry its own `override`)
3637
4552
  * @param manager - The section source (its `format` override + its built-in `render`)
@@ -3651,13 +4566,13 @@ export declare function resolveItem<T extends {
3651
4566
  }>(manager: ContextSectionSourceInterface<T>, provider: ContextSectionFormat<T> | undefined, item: T): string;
3652
4567
 
3653
4568
  /**
3654
- * Resolves one section's OPEN text through the format cascade — manager-options override >
4569
+ * Resolves one section's open text through the format cascade — manager-options override >
3655
4570
  * provider default > built-in header.
3656
4571
  *
3657
4572
  * @remarks
3658
- * Pure and total. The leading text has NO per-item level. A manager's `open` already
4573
+ * Pure and total. The leading text has no per-item level. A manager's `open` already
3659
4574
  * encapsulates `[options-override → built-in]`, so it is reached only when neither the
3660
- * override's `open` nor the provider's `open` applies — and there it IS the built-in header.
4575
+ * override's `open` nor the provider's `open` applies — and there it is the built-in header.
3661
4576
  *
3662
4577
  * @typeParam T - The section item the manager renders
3663
4578
  * @param manager - The section source (its `format` override + its built-in `open`)
@@ -3674,15 +4589,15 @@ export declare function resolveOpen<T>(manager: ContextSectionSourceInterface<T>
3674
4589
 
3675
4590
  /**
3676
4591
  * Holds the immutable per-run outcome an {@link AgentInterface}'s loop settles on — the value its
3677
- * run RETURNS, assembled from there into the {@link AgentResult} its `stream`'s `result`
3678
- * promise resolves.
4592
+ * run returns, assembled from there into the {@link AgentResult} its `stream`'s `result` promise
4593
+ * resolves.
3679
4594
  *
3680
4595
  * @remarks
3681
4596
  * Computed inside one run (so concurrent runs never share state) and returned once, when the
3682
4597
  * loop settles: `content` is the streamed assistant text, `thinking` the reasoning the
3683
4598
  * provider calls separated from it ({@link ProviderResult.thinking}, joined across calls —
3684
4599
  * `undefined` when none surfaced), `usage` the summed {@link TokenUsage} (present only when
3685
- * a provider call reported it), `partial` is `true` when a cancel committed the run early OR
4600
+ * a provider call reported it), `partial` is `true` when a cancel committed the run early or
3686
4601
  * when the loop exhausted its `limit` with unresolved tool intent, and `exhausted` is `true`
3687
4602
  * in that second case specifically (a distinct, non-cancel cause the {@link AgentEventMap}
3688
4603
  * `exhaust` event observes). It is the settled outcome one run returns, before the agent folds
@@ -3697,7 +4612,8 @@ export declare interface RunOutcome {
3697
4612
  }
3698
4613
 
3699
4614
  /**
3700
- * Sanitizes one reported token count into a safe non-negative integer.
4615
+ * Sanitizes one reported token count into a safe non-negative integer — a non-finite or
4616
+ * non-positive value becomes `0`, and a positive fractional value floors down.
3701
4617
  *
3702
4618
  * @param value - The token count to sanitize
3703
4619
  * @returns The floored count, or `0` when the value is non-finite or non-positive
@@ -3725,23 +4641,25 @@ export declare function sanitizeToken(value: number): number;
3725
4641
  export declare function sanitizeUsage(usage: TokenUsage): TokenUsage;
3726
4642
 
3727
4643
  /**
3728
- * Represents a named, immutable filter over a richer context's items — an optional
3729
- * allow-list per category (`instructions` / `tools` / `files`), each keyed by that
3730
- * category's identity (an instruction's `name`, a tool's `name`, a workspace file's `path`).
4644
+ * Represents a named, immutable filter over a richer context's items — an optional allow-list per
4645
+ * category (`instructions` / `tools` / `files`), each keyed by that category's identity (an
4646
+ * instruction's `name`, a tool's `name`, a workspace file's `path`) and read as an allow-list:
4647
+ * `undefined` lets everything pass, `[]` lets nothing pass, and a non-empty list passes the listed
4648
+ * keys alone. `narrow` composes a tighter child by set intersection.
3731
4649
  *
3732
4650
  * @remarks
3733
- * - **A category list is three-way.** `undefined` ⇒ NO constraint on that category (all
3734
- * pass); `[]` ⇒ NONE pass; a non-empty list ⇒ only the listed keys pass. The build
4651
+ * - **A category list is three-way.** `undefined` ⇒ no constraint on that category (all
4652
+ * pass); `[]` ⇒ none pass; a non-empty list ⇒ only the listed keys pass. The build
3735
4653
  * step / loop apply this through `filterAllowList`.
3736
- * - **Immutable.** The `id` is minted at construction; every supplied list is COPIED in
4654
+ * - **Immutable.** The `id` is minted at construction; every supplied list is copied in
3737
4655
  * (so a later mutation of the caller's array can't leak in), and the lists are
3738
- * `readonly`. A `Scope` is never mutated after construction — `narrow` returns a NEW
4656
+ * `readonly`. A `Scope` is never mutated after construction — `narrow` returns a new
3739
4657
  * one rather than altering this one.
3740
- * - **`narrow` is set-INTERSECTION (immutable composition).** A child scope's visible set
3741
- * per category is the intersection of THIS scope's list and the config's list — but
4658
+ * - **`narrow` is set-intersection (immutable composition).** A child scope's visible set
4659
+ * per category is the intersection of this scope's list and the config's list — but
3742
4660
  * `undefined` means "no constraint", so it acts as the universal set: intersecting
3743
4661
  * `undefined` with a list yields the list, and `undefined` with `undefined` stays
3744
- * `undefined`. Narrowing can only TIGHTEN, never widen — a key excluded by a parent
4662
+ * `undefined`. Narrowing can only tighten, never widen — a key excluded by a parent
3745
4663
  * can never be re-admitted by a child.
3746
4664
  *
3747
4665
  * @example
@@ -3765,16 +4683,17 @@ export declare class Scope implements ScopeInterface {
3765
4683
  }
3766
4684
 
3767
4685
  /**
3768
- * Lists the per-category allow-lists a {@link ScopeInterface} carries — an optional
3769
- * `readonly string[]` for `instructions`, for `tools`, and for `files`, each keyed by that
3770
- * category's identity (an instruction's `name`, a tool's `name`, a workspace file's
3771
- * `path`).
4686
+ * Lists the per-category allow-lists a {@link ScopeInterface} carries — an optional `readonly
4687
+ * string[]` for `instructions`, for `tools`, and for `files`, each keyed by that category's
4688
+ * identity (an instruction's `name`, a tool's `name`, a workspace file's `path`) and read as an
4689
+ * allow-list: `undefined` lets everything pass, `[]` lets nothing pass, and a non-empty list passes
4690
+ * the listed keys alone.
3772
4691
  *
3773
4692
  * @remarks
3774
- * Each list is THREE-WAY (see {@link import('./helpers.js').filterAllowList}): `undefined`
3775
- * ⇒ NO constraint on that category (all pass); `[]` ⇒ NONE pass; a non-empty list ⇒ only
4693
+ * Each list is three-way (see {@link import('./helpers.js').filterAllowList}): `undefined`
4694
+ * ⇒ no constraint on that category (all pass); `[]` ⇒ none pass; a non-empty list ⇒ only
3776
4695
  * the listed keys pass. It is the shape both a {@link ScopeInput} and `Scope.narrow`
3777
- * accept (a `name`-less narrowing config). `files` filters the ACTIVE workspace's rendered
4696
+ * accept (a `name`-less narrowing config). `files` filters the active workspace's rendered
3778
4697
  * files (by `path`) in {@link AgentContextInterface.build} — both the text files folded into
3779
4698
  * the system block and the image files attached to the last user message.
3780
4699
  */
@@ -3784,7 +4703,7 @@ export declare interface ScopeFilter {
3784
4703
  /** Lists the allowed tool `name`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed). */
3785
4704
  readonly tools?: readonly string[];
3786
4705
  /**
3787
- * Lists the allowed ACTIVE-workspace file `path`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed) —
4706
+ * Lists the allowed active-workspace file `path`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed) —
3788
4707
  * the filter {@link AgentContextInterface.build} applies to the active workspace's
3789
4708
  * {@link import('@orkestrel/workspace').WorkspaceInterface.files} before rendering them (text → the system block, image →
3790
4709
  * the last user message).
@@ -3801,33 +4720,35 @@ export declare interface ScopeInput extends ScopeFilter {
3801
4720
  }
3802
4721
 
3803
4722
  /**
3804
- * Represents a named, immutable filter over a richer context's items — the per-category
3805
- * allow-lists ({@link ScopeFilter}) plus an `id` / `name`, and a `narrow` that
3806
- * composes a tighter child by set-INTERSECTION.
4723
+ * Represents a named, immutable filter over a richer context's items — the per-category allow-lists
4724
+ * ({@link ScopeFilter}) plus an `id` / `name`, and a `narrow` that composes a tighter child by set
4725
+ * intersection.
3807
4726
  *
3808
4727
  * @remarks
3809
4728
  * Each list is three-way (`undefined` ⇒ all, `[]` ⇒ none, else only-listed). `narrow`
3810
- * returns a NEW scope whose per-category visible set is the intersection of this scope's
4729
+ * returns a new scope whose per-category visible set is the intersection of this scope's
3811
4730
  * list and the config's — with `undefined` treated as the universal set (no constraint),
3812
4731
  * so `undefined ∩ list = list` and `undefined ∩ undefined = undefined`. Narrowing can
3813
- * only TIGHTEN (a parent-excluded key never returns); the scope itself is never mutated.
4732
+ * only tighten (a parent-excluded key never returns); the scope itself is never mutated.
3814
4733
  */
3815
4734
  export declare interface ScopeInterface extends ScopeFilter {
3816
4735
  readonly id: string;
3817
4736
  readonly name: string;
3818
4737
  /**
3819
- * Composes a tighter child scope — its per-category set is the intersection of this
3820
- * scope's list and `config`'s (an `undefined` side imposing no constraint).
4738
+ * Composes a tighter child scope — each category is the set intersection of this scope's
4739
+ * list and `config`'s (an `undefined` side imposing no constraint), returned as a new
4740
+ * scope that leaves this one unchanged.
3821
4741
  *
3822
4742
  * @param config - The narrowing allow-lists (a `name`-less {@link ScopeFilter})
3823
- * @returns A NEW, tighter {@link ScopeInterface} (this one is left unchanged)
4743
+ * @returns A new, tighter {@link ScopeInterface} (this one is left unchanged)
3824
4744
  */
3825
4745
  narrow(config: ScopeFilter): ScopeInterface;
3826
4746
  }
3827
4747
 
3828
4748
  /**
3829
- * Registers the named filters a richer context reuses — immutable {@link Scope}s keyed by
3830
- * their minted `id`, in insertion order.
4749
+ * Registers the named filters a richer context reuses — immutable {@link Scope}s keyed by their
4750
+ * minted `id`, in insertion order, where `create` always mints and stores rather than overwriting,
4751
+ * and an observable `emitter` reports each change.
3831
4752
  *
3832
4753
  * @remarks
3833
4754
  * - **Registry.** Scopes live in an insertion-ordered `Map` keyed by their minted `id`;
@@ -3836,11 +4757,11 @@ export declare interface ScopeInterface extends ScopeFilter {
3836
4757
  * one up, and `scopes()` lists them in insertion order. (Unlike the name-keyed
3837
4758
  * instruction registry, a scope's key is its minted `id`, so two scopes may share a
3838
4759
  * `name`; `create` therefore always adds — it never overwrites.)
3839
- * - **Removal.** `remove` drops one by id, or a batch — `true` only when EVERY supplied id
4760
+ * - **Removal.** `remove` drops one by id, or a batch — `true` only when every supplied id
3840
4761
  * was removed; `clear` empties the registry.
3841
4762
  * - **Observable.** The owned {@link emitter} ({@link ScopeManagerEventMap}) carries
3842
4763
  * `create` (the created scope) / `remove` (the id) / `clear`. Every event is emitted
3843
- * directly, strictly AFTER the map mutation completes; the emitter isolates a listener
4764
+ * directly, strictly after the map mutation completes; the emitter isolates a listener
3844
4765
  * throw and routes it to its `error` handler (the `error` option), so a buggy observer can
3845
4766
  * never corrupt a mutation.
3846
4767
  *
@@ -3900,12 +4821,19 @@ export declare type ScopeManagerEventMap = {
3900
4821
  export declare interface ScopeManagerInterface {
3901
4822
  readonly emitter: EmitterInterface<ScopeManagerEventMap>;
3902
4823
  readonly count: number;
4824
+ /**
4825
+ * Mints a scope from a {@link ScopeInput} (an `id` plus the per-category allow-lists) and
4826
+ * stores it — always adds, never overwrites.
4827
+ */
3903
4828
  create(input: ScopeInput): ScopeInterface;
4829
+ /** Looks up one scope by id (`undefined` when absent). */
3904
4830
  scope(id: string): ScopeInterface | undefined;
3905
4831
  /** Lists every scope, in insertion order. */
3906
4832
  scopes(): readonly ScopeInterface[];
4833
+ /** Removes one scope by id, or a batch — `true` only when every supplied id was removed. */
3907
4834
  remove(id: string): boolean;
3908
4835
  remove(ids: readonly string[]): boolean;
4836
+ /** Removes every scope. */
3909
4837
  clear(): void;
3910
4838
  }
3911
4839
 
@@ -3925,9 +4853,9 @@ export declare interface ScopeManagerOptions {
3925
4853
  *
3926
4854
  * @remarks
3927
4855
  * `summary` is the model-written digest of this slice (through the
3928
- * {@link ConversationSummaryHandler}); `messages` are the folded ORIGINALS, RETAINED in full so
4856
+ * {@link ConversationSummaryHandler}); `messages` are the folded originals, retained in full so
3929
4857
  * `rehydrate` can pull them back and `search` can scan them (compaction shrinks the model
3930
- * INPUT, never discards history).
4858
+ * input, never discards history).
3931
4859
  */
3932
4860
  export declare interface Section {
3933
4861
  readonly id: string;
@@ -3938,15 +4866,17 @@ export declare interface Section {
3938
4866
  }
3939
4867
 
3940
4868
  /**
3941
- * Runs one rehydrated agent and applies the partial-as-configurable-failure policy — the
3942
- * shared job-handler step BOTH `createAgentQueue` and `createAgentRunner` settle each job
3943
- * through, so the policy can never diverge between them.
4869
+ * Runs one rehydrated agent and applies the partial-as-configurable-failure policy — a partial
4870
+ * run throws an {@link import('./errors.js').AgentJobError} unless the `partial` policy allows
4871
+ * it, and a natural finish resolves. The shared job-handler step `createAgentQueue` and
4872
+ * `createAgentRunner` both settle each job through, so the policy can never diverge between
4873
+ * them.
3944
4874
  *
3945
4875
  * @remarks
3946
- * A turn that committed PARTIAL (a cancel — abort / budget / timeout) is by default a
3947
- * FAILURE, so it THROWS an {@link import('./errors.js').AgentJobError} carrying the partial
3948
- * (the Queue's retries + a Runner's fail-fast then engage); the `partial` policy RESOLVES
3949
- * it as success instead. A natural finish ALWAYS resolves with its result.
4876
+ * A turn that committed partial (a cancel — abort / budget / timeout) is by default a
4877
+ * failure, so it throws an {@link import('./errors.js').AgentJobError} carrying the partial
4878
+ * (the Queue's retries + a Runner's fail-fast then engage); the `partial` policy resolves
4879
+ * it as success instead. A natural finish always resolves with its result.
3950
4880
  *
3951
4881
  * @param agent - The rehydrated {@link AgentInterface} to run to its {@link AgentResult}
3952
4882
  * @param partial - The partial policy. If `true`, a partial result resolves as success; if
@@ -3969,7 +4899,7 @@ export declare function settleAgentJob(agent: AgentInterface, partial: boolean):
3969
4899
  * @remarks
3970
4900
  * Iterate `events` to consume the live `T` chunks as they arrive; `await result` for
3971
4901
  * the eventual `R` outcome (it resolves once `events` completes). `abort(reason)`
3972
- * cancels the in-flight operation — for an agent turn the `result` then RESOLVES
4902
+ * cancels the in-flight operation — for an agent turn the `result` then resolves
3973
4903
  * (with a partial outcome), since a cancel is not an error.
3974
4904
  *
3975
4905
  * @typeParam T - The live event type the stream yields
@@ -4008,40 +4938,51 @@ export declare interface StreamInterface<T, R> {
4008
4938
  */
4009
4939
  export declare function sumUsage(running: TokenUsage | undefined, next: TokenUsage): TokenUsage;
4010
4940
 
4941
+ /** Carries a decoded stream prefix and whether the stream ended within its byte budget. */
4942
+ export declare interface TextRead {
4943
+ readonly text: string;
4944
+ /**
4945
+ * Reports true only when a read observes the `done` flag before exhausting the
4946
+ * byte budget; an abort reports false.
4947
+ */
4948
+ readonly complete: boolean;
4949
+ }
4950
+
4011
4951
  /**
4012
- * Names the closing tag that ends a {@link THINK_OPEN} reasoning span. A span the stream never closes
4013
- * (the model was cut off mid-reasoning) is treated as thinking to its end —
4014
- * {@link import('./types.js').ThinkSplitterInterface.flush} settles it.
4952
+ * Names the closing tag that ends a {@link THINK_OPEN} reasoning span — `'</think>'`. A span the
4953
+ * stream never closes (the model was cut off mid-reasoning) is treated as thinking to its end,
4954
+ * and {@link import('./types.js').ThinkSplitterInterface.flush} settles it.
4015
4955
  */
4016
4956
  export declare const THINK_CLOSE = "</think>";
4017
4957
 
4018
4958
  /**
4019
4959
  * Names the opening tag a {@link import('./ThinkSplitter.js').ThinkSplitter} recognizes as the start of
4020
- * an in-content reasoning span — the de-facto wire convention thinking models (qwen3, DeepSeek-R1
4021
- * family) emit their chain-of-thought under when a daemon renders it inline instead of on a
4022
- * separate wire field. Paired with {@link THINK_CLOSE}.
4960
+ * an in-content reasoning span — `'<think>'`, the de-facto wire convention thinking models
4961
+ * (qwen3, DeepSeek-R1 family) emit their chain-of-thought under when a daemon renders it inline
4962
+ * instead of on a separate wire field. Paired with {@link THINK_CLOSE}.
4023
4963
  */
4024
4964
  export declare const THINK_OPEN = "<think>";
4025
4965
 
4026
4966
  /**
4027
- * Feeds raw content deltas through a tiny stream-stateful state machine that routes
4028
- * everything inside a `<think>…</think>` span to `thinking` and returns everything outside
4029
- * it as clean content, so a provider yields ONLY the answer and surfaces the reasoning as
4030
- * {@link import('./types.js').ProviderResult.thinking}.
4967
+ * Feeds raw content deltas through a tiny stream-stateful state machine that routes everything
4968
+ * inside a `<think>…</think>` span to `thinking` and returns everything outside it as clean
4969
+ * content, so a provider yields the answer alone and surfaces the reasoning as
4970
+ * {@link import('./types.js').ProviderResult.thinking}. A tag split across deltas is held until
4971
+ * disambiguated, `flush()` settles the stream end, and one splitter serves one stream.
4031
4972
  *
4032
4973
  * @remarks
4033
4974
  * - **Cross-chunk tags.** A tag may arrive split across wire deltas (`'<thi'` then
4034
- * `'nk>'`): any suffix of the pending text that is a strict PREFIX of a tag being
4035
- * scanned for is HELD BACK (neither surfaced nor routed) until the next delta — or
4975
+ * `'nk>'`): any suffix of the pending text that is a strict prefix of a tag being
4976
+ * scanned for is held back (neither surfaced nor routed) until the next delta — or
4036
4977
  * `flush()` — disambiguates it. A held tag prefix that never completes is real
4037
4978
  * content; a held close-tag prefix inside a span is thinking.
4038
- * - **The IMPLICIT leading open (the qwen3-template shape).** Some chat templates
4039
- * PRE-SEED `<think>` into the prompt scaffold, so the wire stream begins
4040
- * MID-REASONING and only a bare `</think>` appears. Before any tag event, a bare
4041
- * close therefore RECLASSIFIES everything surfaced so far (plus the pre-close
4979
+ * - **The implicit leading open (the qwen3-template shape).** Some chat templates
4980
+ * pre-seed `<think>` into the prompt scaffold, so the wire stream begins
4981
+ * mid-reasoning and only a bare `</think>` appears. Before any tag event, a bare
4982
+ * close therefore reclassifies everything surfaced so far (plus the pre-close
4042
4983
  * pending) as thinking — `content` is corrected retroactively (the already-returned
4043
4984
  * prefix cannot be recalled, so `content` is the authoritative accumulation). The
4044
- * rule is ONE-SHOT: after any tag event a bare `</think>` is plain text.
4985
+ * rule is one-shot: after any tag event a bare `</think>` is plain text.
4045
4986
  * - **Multiple spans** accumulate onto `thinking` in stream order. A nested-looking
4046
4987
  * `<think>` inside an open span is thinking text (no nesting is tracked — the
4047
4988
  * first `</think>` closes the span), matching how the models emit it.
@@ -4069,59 +5010,94 @@ export declare class ThinkSplitter implements ThinkSplitterInterface {
4069
5010
  }
4070
5011
 
4071
5012
  /**
4072
- * Splits a thinking model's in-content `<think>…</think>` reasoning spans away from the
4073
- * answer, delta by delta with per-stream state, so a provider yields ONLY clean content
4074
- * and surfaces the reasoning as {@link ProviderResult.thinking}.
5013
+ * Splits a thinking model's in-content `<think>…</think>` reasoning spans away from the answer,
5014
+ * delta by delta with per-stream state, so a provider yields clean content alone and surfaces the
5015
+ * reasoning as {@link ProviderResult.thinking}.
4075
5016
  *
4076
5017
  * @remarks
4077
- * - **Stateful across deltas.** A tag may arrive SPLIT across wire chunks (`'<thi'`
5018
+ * - **Stateful across deltas.** A tag may arrive split across wire chunks (`'<thi'`
4078
5019
  * ending one delta, `'nk>'` opening the next) — `split` holds any ambiguous tail
4079
5020
  * back until the next delta (or `flush`) disambiguates it, so a partial tag is
4080
5021
  * never leaked as content and never mis-eaten as thinking.
4081
- * - **`split(delta)`** feeds one raw content delta and returns the CLEAN content to
5022
+ * - **`split(delta)`** feeds one raw content delta and returns the clean content to
4082
5023
  * surface for it (possibly `''` — for example mid-think). Text inside a
4083
5024
  * `<think>…</think>` span accumulates on `thinking`; multiple spans accumulate in
4084
5025
  * order; a nested-looking `<think>` inside an open span is thinking text (no
4085
5026
  * nesting — the first `</think>` closes).
4086
- * - **The IMPLICIT leading open (the qwen3-template shape).** Some chat templates
4087
- * PRE-SEED `<think>` into the prompt scaffold, so the wire stream begins
4088
- * MID-REASONING and only a bare `</think>` ever appears. Before any tag event, a
4089
- * bare close therefore RECLASSIFIES everything surfaced so far (plus the pre-close
5027
+ * - **The implicit leading open (the qwen3-template shape).** Some chat templates
5028
+ * pre-seed `<think>` into the prompt scaffold, so the wire stream begins
5029
+ * mid-reasoning and only a bare `</think>` ever appears. Before any tag event, a
5030
+ * bare close therefore reclassifies everything surfaced so far (plus the pre-close
4090
5031
  * pending) as thinking — `content` is corrected retroactively, while the already
4091
5032
  * `split`-returned prefix cannot be recalled (the one shape where the per-delta
4092
- * returns over-report; `content` stays authoritative). The rule is ONE-SHOT: after
5033
+ * returns over-report; `content` stays authoritative). The rule is one-shot: after
4093
5034
  * any tag event a bare `</think>` is plain text (prose quoting the tag stays text).
4094
- * - **`flush()`** settles the stream end: an UNCLOSED `<think>` tail (the model was
5035
+ * - **`flush()`** settles the stream end: an unclosed `<think>` tail (the model was
4095
5036
  * cut off mid-reasoning) lands on `thinking`; a held partial tag that never
4096
5037
  * completed (`'<thi'` then EOF) is returned as the final clean-content delta —
4097
5038
  * it was real text after all.
4098
5039
  * - **`content` / `thinking`** are the authoritative accumulations so far (read them
4099
- * after the stream — or mid-stream for a cancel's partial); `content` is the ONE
5040
+ * after the stream — or mid-stream for a cancel's partial); `content` is the one
4100
5041
  * exact clean-content source (the per-delta returns match it except across an
4101
- * implicit-open reclassification). One splitter serves ONE stream; create a fresh
5042
+ * implicit-open reclassification). One splitter serves one stream; create a fresh
4102
5043
  * one per call ({@link import('./factories.js').createThinkSplitter}).
4103
5044
  */
4104
5045
  export declare interface ThinkSplitterInterface {
4105
- /** Holds the AUTHORITATIVE clean content accumulated so far (corrected across an implicit-open reclassification). */
5046
+ /** Holds the authoritative clean content accumulated so far (corrected across an implicit-open reclassification). */
4106
5047
  readonly content: string;
4107
5048
  /** Holds the reasoning text accumulated from every `<think>…</think>` span so far. */
4108
5049
  readonly thinking: string;
4109
- /** Feeds one raw delta; returns the clean (non-think) content to surface for it. */
5050
+ /**
5051
+ * Feeds one raw delta and returns the clean, non-think content to surface for it (possibly
5052
+ * `''`) — a tag split across deltas is held until disambiguated, never leaked as content
5053
+ * and never mis-eaten as thinking.
5054
+ */
4110
5055
  split(delta: string): string;
4111
- /** Settles the stream end — returns any held clean tail; an unclosed think span lands on `thinking`. */
5056
+ /**
5057
+ * Settles the stream end — a held partial tag that never completed returns as the final
5058
+ * content delta, and an unclosed think span's tail lands on `thinking`.
5059
+ */
4112
5060
  flush(): string;
4113
5061
  }
4114
5062
 
5063
+ /**
5064
+ * Describes a tool call's JSON wire projection.
5065
+ *
5066
+ * @remarks
5067
+ * The wire is strictly narrower than the domain: non-JSON arguments are refused.
5068
+ * ToolCall.caller is not in this shape and never crosses a wire. The guard refuses
5069
+ * that extra member; the contract parser drops it.
5070
+ */
5071
+ export declare const toolCallShape: ObjectShape< {
5072
+ id: StringShape;
5073
+ name: StringShape;
5074
+ arguments: ObjectShape<Record<never, never>, JSONShape>;
5075
+ }, boolean | ContractShape>;
5076
+
5077
+ /**
5078
+ * Names the status a relay answers when the `authorize` callback returns anything but `true` or throws —
5079
+ * `401`, carried with no body and reaching the browser as a `ProviderError` with the `HTTP` code.
5080
+ */
5081
+ export declare const UNAUTHORIZED_RELAY_STATUS = 401;
5082
+
5083
+ /**
5084
+ * Names the status a relay answers when the upstream provider call cannot be constructed — `502`,
5085
+ * carried with no body after `provider.stream` was entered and threw before returning its
5086
+ * iterator.
5087
+ */
5088
+ export declare const UPSTREAM_RELAY_STATUS = 502;
5089
+
4115
5090
  /**
4116
5091
  * Names the section header {@link import('./AgentContext.js').AgentContext}'s `build()` renders the
4117
- * ACTIVE workspace's TEXT files under — the leading line of the dedicated workspace block in the
4118
- * system message, the carrier-split counterpart to the documents / images section headers.
5092
+ * active workspace's text files under — `'## Workspace'`, the leading line of the dedicated
5093
+ * workspace block in the system message and the carrier-split counterpart to the documents and
5094
+ * images section headers.
4119
5095
  *
4120
5096
  * @remarks
4121
- * `build()` OWNS the workspace render (a `Workspace` / `WorkspaceManager` stays file-focused — no
5097
+ * `build()` owns the workspace render (a `Workspace` / `WorkspaceManager` stays file-focused — no
4122
5098
  * `open` / `format` getters), so this header lives here as the agents module's one
4123
5099
  * workspace-section framing constant rather than on a manager. Each workspace text file renders
4124
- * beneath it as a fenced `` File: <path>\n```<language>\n<text>\n``` `` block — the SAME framing
5100
+ * beneath it as a fenced `` File: <path>\n```<language>\n<text>\n``` `` block — the same framing
4125
5101
  * the documents section uses — placed immediately after the documents section in the system block.
4126
5102
  */
4127
5103
  export declare const WORKSPACE_SECTION_HEADER = "## Workspace";