@orkestrel/ollama 0.0.13 → 0.0.14

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,21 +1,44 @@
1
- import { ContextFormatInterface } from '@orkestrel/agent';
2
- import { MessageInterface } from '@orkestrel/agent';
1
+ import { ContextFormat } from '@orkestrel/agent';
2
+ import { Message } from '@orkestrel/agent';
3
3
  import { ProviderDelta } from '@orkestrel/agent';
4
4
  import { ProviderInterface } from '@orkestrel/agent';
5
5
  import { ProviderResult } from '@orkestrel/agent';
6
6
  import { ProviderStreamOptions } from '@orkestrel/agent';
7
+ import { ThinkSplitterInterface } from '@orkestrel/agent';
7
8
  import { TimeoutInterface } from '@orkestrel/timeout';
9
+ import { TokenUsage } from '@orkestrel/budget';
10
+ import { ToolCall } from '@orkestrel/tool';
8
11
  import { ToolDefinition } from '@orkestrel/tool';
9
12
 
10
13
  /**
11
- * Create a local Ollama inference provider — a {@link ProviderInterface} over the
14
+ * Builds a provider result from a turn's content, reasoning, tool calls, and usage.
15
+ *
16
+ * @remarks
17
+ * Only the present optionals are set: no empty `thinking`, no empty `tools`, and no
18
+ * `usage` unless the wire reported one.
19
+ *
20
+ * @param content - The clean assistant content the splitter accumulated
21
+ * @param thinking - The joined reasoning, empty when the turn produced none
22
+ * @param tools - The tool calls collected across the turn
23
+ * @param usage - The token usage, or `undefined` when the wire reported none
24
+ * @returns The result carrying only its populated fields
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * buildResult('ok', '', [], undefined) // { content: 'ok' }
29
+ * ```
30
+ */
31
+ export declare function buildResult(content: string, thinking: string, tools: readonly ToolCall[], usage: TokenUsage | undefined): ProviderResult;
32
+
33
+ /**
34
+ * Creates a local Ollama inference provider — a {@link ProviderInterface} over the
12
35
  * daemon's `POST /api/chat`, supporting non-streaming `generate` and streaming
13
36
  * `stream`.
14
37
  *
15
38
  * @remarks
16
39
  * Only `model` is required; `url` defaults to the local daemon, `keepAlive` to `'5m'`,
17
40
  * `timeout` to `120_000`ms, and `options` is forwarded verbatim as sampling
18
- * parameters (`temperature` / `seed` / `num_predict` / …). Both calls take an
41
+ * parameters (`temperature`, `seed`, and `num_predict`). Both calls take an
19
42
  * `AbortSignal` to bound the request; a `stream` cancelled mid-flight throws a
20
43
  * `ProviderAbortError` carrying the partial result.
21
44
  *
@@ -23,12 +46,12 @@ import { ToolDefinition } from '@orkestrel/tool';
23
46
  * point `url` at your own server, inject a custom `fetch`, and have `headers` attach a
24
47
  * generated/obfuscated bearer token your server validates — so a browser runtime
25
48
  * reaches the LLM through your middleware WITHOUT this library ever handling the real API
26
- * key. Both omitted ⇒ today's behaviour (the global `fetch`, only a JSON content type).
49
+ * key. Both omitted ⇒ the global `fetch` and only a JSON content type.
27
50
  *
28
51
  * The optional `format` is the provider's context-framing default — the PROVIDER-DEFAULT
29
- * level of `AgentContext`'s format cascade (see [agents.md]; beaten by a manager-options
30
- * or per-item override, beating the managers' built-in framing), declaring how this
31
- * provider's models prefer context sections framed (e.g. XML group wrappers vs. Markdown
52
+ * level of `AgentContext`'s format cascade (beaten by a manager-options or per-item
53
+ * override, beating the managers' built-in framing), declaring how this
54
+ * provider's models prefer context sections framed (for example XML group wrappers vs. Markdown
32
55
  * headers). It is EXPOSED on the provider for the Agent's `build()` and is NOT Ollama's
33
56
  * `/api/chat` `format` wire parameter (structured output) — the two are unrelated despite
34
57
  * the shared word. Omitted ⇒ the provider is framing-agnostic (core's built-in defaults).
@@ -40,7 +63,7 @@ import { ToolDefinition } from '@orkestrel/tool';
40
63
  * @example
41
64
  * ```ts
42
65
  * import { createAbort } from '@orkestrel/abort'
43
- * import { createOllama } from '@src/server'
66
+ * import { createOllama } from '@orkestrel/ollama'
44
67
  *
45
68
  * const provider = createOllama({ model: 'qwen3.5:2b-q4_K_M' })
46
69
  * const abort = createAbort()
@@ -48,7 +71,7 @@ import { ToolDefinition } from '@orkestrel/tool';
48
71
  * ```
49
72
  *
50
73
  * @example
51
- * Route through your own server with an obfuscated token (deployment scenario S2):
74
+ * Route through your own server with an obfuscated token:
52
75
  * ```ts
53
76
  * const provider = createOllama({
54
77
  * model: 'qwen3.5:2b-q4_K_M',
@@ -77,50 +100,171 @@ import { ToolDefinition } from '@orkestrel/tool';
77
100
  export declare function createOllama(options: OllamaOptions): ProviderInterface;
78
101
 
79
102
  /**
80
- * How long the model stays resident after a call when `OllamaOptions.keepAlive` is
103
+ * Names how long the model stays resident after a call when `OllamaOptions.keepAlive` is
81
104
  * omitted — Ollama's own `keep_alive` default, expressed as a duration string.
105
+ *
106
+ * @remarks
107
+ * The name mirrors the Ollama `/api/chat` `keep_alive` field this value is sent as, so
108
+ * the constant, the `OllamaOptions.keepAlive` key, and the wire member read as one term.
82
109
  */
83
110
  export declare const DEFAULT_KEEP_ALIVE = "5m";
84
111
 
85
- /** The local Ollama daemon base URL assumed when `OllamaOptions.url` is omitted. */
112
+ /** Names the local Ollama daemon base URL assumed when `OllamaOptions.url` is omitted. */
86
113
  export declare const DEFAULT_OLLAMA_URL = "http://localhost:11434";
87
114
 
88
115
  /**
89
- * The per-call deadline in milliseconds when `OllamaOptions.timeout` is omitted —
116
+ * Names the per-call deadline in milliseconds when `OllamaOptions.timeout` is omitted —
90
117
  * generous enough that a cold model load does not trip it.
91
118
  */
92
119
  export declare const DEFAULT_PROVIDER_TIMEOUT = 120000;
93
120
 
94
121
  /**
95
- * Whether a value is an {@link OllamaHTTPError}.
122
+ * Extracts a wire `arguments` value as a record.
123
+ *
124
+ * @remarks
125
+ * Total: an object passes through, a JSON string is parsed when it yields a record, and
126
+ * a malformed string yields `{}` rather than throwing.
127
+ *
128
+ * @param value - The wire's `function.arguments` value, of unknown shape
129
+ * @returns The argument record, or `{}` when the value carries none
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * extractArguments('{"city":"Oslo"}') // { city: 'Oslo' }
134
+ * ```
135
+ */
136
+ export declare function extractArguments(value: unknown): Readonly<Record<string, unknown>>;
137
+
138
+ /**
139
+ * Extracts the assistant text of one wire record.
140
+ *
141
+ * @param record - One parsed `/api/chat` record — a non-stream body or an NDJSON line
142
+ * @returns The record's `message.content` when it is a string, else `''`
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * extractContent({ message: { content: 'ok' } }) // 'ok'
147
+ * ```
148
+ */
149
+ export declare function extractContent(record: Readonly<Record<string, unknown>>): string;
150
+
151
+ /**
152
+ * Extracts the daemon-side reasoning of one wire record.
153
+ *
154
+ * @remarks
155
+ * `message.thinking` is the `think: true` wire shape. It is read whatever the configured
156
+ * flag says, because a daemon may separate reasoning on its own.
157
+ *
158
+ * @param record - One parsed `/api/chat` record — a non-stream body or an NDJSON line
159
+ * @returns The record's `message.thinking` when it is a string, else `''`
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * extractThinking({ message: { thinking: 'weighing it' } }) // 'weighing it'
164
+ * ```
165
+ */
166
+ export declare function extractThinking(record: Readonly<Record<string, unknown>>): string;
167
+
168
+ /**
169
+ * Extracts the tool calls of one wire record's `message.tool_calls`.
170
+ *
171
+ * @remarks
172
+ * Each entry narrows to `{ id, name, arguments }`: the entry and its `function` must be
173
+ * records and `name` a string, else the entry is dropped. An id is minted when the wire
174
+ * omits one.
175
+ *
176
+ * @param record - One parsed `/api/chat` record — a non-stream body or an NDJSON line
177
+ * @returns The narrowed tool calls, empty when the record carries none
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * extractTools({ message: { tool_calls: [{ function: { name: 'weather' } }] } })
182
+ * // [{ id: '…', name: 'weather', arguments: {} }]
183
+ * ```
184
+ */
185
+ export declare function extractTools(record: Readonly<Record<string, unknown>>): readonly ToolCall[];
186
+
187
+ /**
188
+ * Extracts the token usage of one wire record.
189
+ *
190
+ * @remarks
191
+ * Both counts must be numbers, which is true of the non-stream body and the stream's
192
+ * `done: true` line. A delta line carries neither, so it yields `undefined`.
193
+ *
194
+ * @param record - One parsed `/api/chat` record — a non-stream body or an NDJSON line
195
+ * @returns The `TokenUsage` shape, or `undefined` when either count is absent
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * extractUsage({ prompt_eval_count: 3, eval_count: 4 })
200
+ * // { prompt: 3, completion: 4, total: 7 }
201
+ * ```
202
+ */
203
+ export declare function extractUsage(record: Readonly<Record<string, unknown>>): TokenUsage | undefined;
204
+
205
+ /**
206
+ * Checks whether a value is an {@link OllamaHTTPError}.
96
207
  *
97
208
  * @param value - The value to test
98
- * @returns `true` when `value` is an `OllamaHTTPError`
209
+ * @returns True if `value` is an `OllamaHTTPError`; false otherwise
99
210
  */
100
211
  export declare function isOllamaHTTPError(value: unknown): value is OllamaHTTPError;
101
212
 
102
213
  /**
103
- * The cap, in characters, on how much of a non-OK response body is
214
+ * Joins a call's two reasoning carriers into the result's `thinking`.
215
+ *
216
+ * @param splitter - The per-call splitter holding the separated in-content spans
217
+ * @param wired - The accumulated wire-side `message.thinking` text
218
+ * @returns The two carriers separated by a blank line, or whichever one is non-empty
219
+ *
220
+ * @example
221
+ * ```ts
222
+ * joinThinking(createThinkSplitter(), 'from the wire') // 'from the wire'
223
+ * ```
224
+ */
225
+ export declare function joinThinking(splitter: ThinkSplitterInterface, wired: string): string;
226
+
227
+ /**
228
+ * Maps conversation turns onto the `/api/chat` wire's minimal message shape.
229
+ *
230
+ * @remarks
231
+ * `tool_calls` is emitted only on a turn that replays them and `images` only on a
232
+ * multimodal turn, so an empty optional never reaches the wire.
233
+ *
234
+ * @param messages - The conversation turns to send
235
+ * @returns The wire `messages` array, one entry per turn, in order
236
+ *
237
+ * @example
238
+ * ```ts
239
+ * mapMessages([{ id: '1', role: 'user', content: 'Say hello.' }])
240
+ * // [{ role: 'user', content: 'Say hello.' }]
241
+ * ```
242
+ */
243
+ export declare function mapMessages(messages: readonly Message[]): WireChatRequest['messages'];
244
+
245
+ /**
246
+ * Names the cap, in characters, on how much of a non-OK response body is
104
247
  * incorporated into a thrown {@link OllamaHTTPError}'s message.
105
248
  *
106
249
  * @remarks
107
250
  * Bounds the excerpt so a defensive proxy or a misbehaving daemon handing
108
251
  * back an unbounded response body cannot inflate the thrown error's message
109
- * without limit (§14). `2048` characters is generous enough to carry a
252
+ * without limit. `2048` characters is generous enough to carry a
110
253
  * useful diagnostic snippet while staying well short of any practical size
111
254
  * concern.
112
255
  */
113
256
  export declare const MAX_ERROR_BODY_LENGTH = 2048;
114
257
 
115
258
  /**
116
- * An error thrown when the Ollama `/api/chat` HTTP transport fails.
259
+ * Represents an error thrown when the Ollama `/api/chat` HTTP transport fails.
117
260
  *
118
261
  * @remarks
119
- * Carries the response `status` (0 when no HTTP response was received at all,
120
- * e.g. a `null` body). Thrown by {@link OllamaProvider} at its two HTTP
121
- * failure sites — the non-OK status branch and the null-body branch — so a
122
- * caller can branch on `error.status` instead of parsing the message. Narrow
123
- * a caught value with {@link isOllamaHTTPError}.
262
+ * Carries the machine-readable `code` `'HTTP'` and the response `status` (0 when no
263
+ * HTTP response was received at all, for example a `null` body). Thrown by
264
+ * {@link OllamaProvider} at its HTTP failure sites — the non-OK status branch and the
265
+ * null-body branch — so a caller can branch on `error.code` and read `error.status`
266
+ * for the HTTP number instead of parsing the message. Narrow a caught value with
267
+ * {@link isOllamaHTTPError}.
124
268
  *
125
269
  * @example
126
270
  * ```ts
@@ -134,100 +278,124 @@ export declare const MAX_ERROR_BODY_LENGTH = 2048;
134
278
  * ```
135
279
  */
136
280
  export declare class OllamaHTTPError extends Error {
281
+ /**
282
+ * Names the machine-readable condition this error reports — `'HTTP'`: an `/api/chat`
283
+ * transport, status, or body failure.
284
+ */
285
+ readonly code: "HTTP";
137
286
  readonly status: number;
138
- constructor(message: string, status: number, options?: {
139
- readonly cause?: unknown;
140
- });
287
+ constructor(message: string, status: number, options?: OllamaHTTPErrorOptions);
141
288
  }
142
289
 
143
290
  /**
144
- * Options for `createOllama` — the local Ollama backend's configuration.
291
+ * Represents the options a thrown {@link OllamaHTTPError} accepts beside its message and status —
292
+ * the standard error `cause` link, named so a consumer can reference the shape.
293
+ *
294
+ * @remarks
295
+ * `cause` is the underlying value that produced the HTTP failure: the transport or
296
+ * body-read rejection the provider caught before rethrowing. It is `unknown` because a
297
+ * thrown value is unconstrained. Omitted ⇒ the error carries no cause.
298
+ */
299
+ export declare interface OllamaHTTPErrorOptions {
300
+ readonly cause?: unknown;
301
+ }
302
+
303
+ /**
304
+ * Represents the configuration `createOllama` accepts for the local Ollama backend.
145
305
  *
146
306
  * @remarks
147
307
  * Only `model` is required. `url` defaults to the local daemon, `keepAlive` controls
148
308
  * how long the model stays resident after a call, `timeout` is the per-call deadline
149
309
  * in milliseconds, and `options` is a passthrough bag of sampling parameters
150
- * (`temperature` / `seed` / `num_predict` / …) forwarded verbatim to the wire.
310
+ * (`temperature`, `seed`, and `num_predict`) forwarded verbatim to the wire.
151
311
  *
152
312
  * The optional `fetch` + `headers` form a **transport seam**: by default the provider
153
313
  * talks straight to a local daemon over `globalThis.fetch` with only a JSON content
154
314
  * type, but a browser-side runtime can inject a custom transport AND a dynamic header
155
- * (e.g. an obfuscated bearer token) so requests route through the developer's OWN
315
+ * (for example an obfuscated bearer token) so requests route through the developer's OWN
156
316
  * server, which validates that header and forwards to the real LLM. Your app never
157
317
  * holds a real API key — the real key lives only on the developer's server; the
158
318
  * `headers` hook supplies whatever short-lived/obfuscated token that server expects.
159
319
  */
160
320
  export declare interface OllamaOptions {
161
321
  readonly model: string;
162
- /** The daemon base URL; defaults to `'http://localhost:11434'`. */
322
+ /** Sets the daemon base URL; defaults to `'http://localhost:11434'`. */
163
323
  readonly url?: string;
164
- /** How long the model stays resident after a call; defaults to `'5m'`. */
324
+ /**
325
+ * Sets how long the model stays resident after a call; defaults to `'5m'`. Mirrors the
326
+ * Ollama `/api/chat` `keep_alive` field, whose value this key carries verbatim onto
327
+ * {@link WireChatRequest.keep_alive}.
328
+ */
165
329
  readonly keepAlive?: string | number;
166
- /** The per-call deadline in milliseconds; defaults to `120_000`. */
330
+ /** Sets the per-call deadline in milliseconds; defaults to `120_000`. */
167
331
  readonly timeout?: number;
168
- /** Passthrough sampling options (`temperature` / `seed` / `num_predict` / …). */
332
+ /**
333
+ * Carries passthrough sampling parameters (`temperature`, `seed`, and `num_predict`).
334
+ * Mirrors the Ollama `/api/chat` `options` field, whose value this key carries verbatim
335
+ * onto {@link WireChatRequest.options}.
336
+ */
169
337
  readonly options?: Readonly<Record<string, unknown>>;
170
338
  /**
171
- * The `/api/chat` `think` wire flag; defaults to `false`. When `true`, a thinking-capable
172
- * model (e.g. `qwen3`) separates its reasoning NATIVELY at the wire — the daemon returns it
339
+ * Sets the `/api/chat` `think` wire flag; defaults to `false`. When `true`, a thinking-capable
340
+ * model (for example `qwen3`) separates its reasoning NATIVELY at the wire — the daemon returns it
173
341
  * on the distinct `message.thinking` channel (surfaced on `ProviderResult.thinking`) rather
174
- * than inline in `message.content`. The default stays `false` so a general-purpose provider
175
- * is backward-compatible and immediate for non-thinking models; the per-call ThinkSplitter
342
+ * than inline in `message.content`. The default is `false`, so a non-thinking model needs no
343
+ * configuration and answers immediately; the per-call ThinkSplitter
176
344
  * remains the defensive fallback for daemons/models that still inline `<think>` tags either
177
345
  * way. Set it `true` for a thinking model whose reasoning you intend to DISPLAY separately.
178
346
  */
179
347
  readonly think?: boolean;
180
348
  /**
181
- * A custom `fetch` implementation for every request; defaults to
349
+ * Sets a custom `fetch` implementation for every request; defaults to
182
350
  * `globalThis.fetch`. Lets a runtime inject its own transport (a browser fetch
183
- * pointed at the developer's server, an instrumented wrapper, …) without changing
351
+ * pointed at the developer's server, an instrumented wrapper) without changing
184
352
  * the wire protocol. Omitted ⇒ the global `fetch`.
185
353
  */
186
354
  readonly fetch?: typeof globalThis.fetch;
187
355
  /**
188
- * A dynamic, possibly-async header injector called once per request; its returned
356
+ * Sets a dynamic, possibly-async header injector called once per request; its returned
189
357
  * headers are merged into the request on top of the base `Content-Type`. Use it to
190
- * attach an authorization header — e.g. an obfuscated/generated bearer token the
358
+ * attach an authorization header — for example an obfuscated/generated bearer token the
191
359
  * developer's server validates before relaying to the real LLM — so a browser
192
360
  * runtime can authenticate WITHOUT your app ever handling a real API key. Async so a
193
361
  * token can be refreshed/fetched per call. A returned `Content-Type` overrides the
194
362
  * default; other headers add to it. Omitted ⇒ only `Content-Type: application/json`.
195
363
  */
196
- readonly headers?: () => Record<string, string> | Promise<Record<string, string>>;
364
+ readonly headers?: () => Readonly<Record<string, string>> | Promise<Readonly<Record<string, string>>>;
197
365
  /**
198
- * The provider's OPTIONAL context-framing default — the PROVIDER-DEFAULT level of
366
+ * Sets the provider's OPTIONAL context-framing default — the PROVIDER-DEFAULT level of
199
367
  * `AgentContext`'s format cascade (beaten by a manager-options or per-item override,
200
368
  * beating the managers' built-in framing). Declares how this provider's models prefer
201
- * context sections framed (e.g. XML group wrappers vs. Markdown headers). Omitted ⇒
369
+ * context sections framed (for example XML group wrappers vs. Markdown headers). Omitted ⇒
202
370
  * the provider is framing-agnostic and core's built-in defaults apply unchanged. NOTE:
203
371
  * this is the prompt-CONTEXT framing consumed by `AgentContext.build()` — it is NOT
204
372
  * Ollama's `/api/chat` `format` wire parameter (structured-output / JSON schema),
205
- * which this provider does not currently send; the two are unrelated despite the
206
- * shared word.
373
+ * which this provider sends only when a call supplies a `schema`; the two are unrelated
374
+ * despite the shared word.
207
375
  */
208
- readonly format?: ContextFormatInterface;
376
+ readonly format?: ContextFormat;
209
377
  }
210
378
 
211
379
  /**
212
- * The local Ollama inference boundary — a {@link ProviderInterface} over Ollama's
380
+ * Implements the local Ollama inference boundary — a {@link ProviderInterface} over Ollama's
213
381
  * `POST /api/chat`, both non-streaming (`generate`) and streaming NDJSON (`stream`).
214
382
  *
215
383
  * @remarks
216
384
  * - **Wire protocol.** Posts `{ model, messages, stream, keep_alive, think }` plus
217
385
  * passthrough sampling `options` and mapped function `tools`. The `think` flag is
218
- * CONFIGURABLE via {@link OllamaOptions.think} (default `false`). Non-stream parses
386
+ * CONFIGURABLE through {@link OllamaOptions.think} (default `false`). Non-stream parses
219
387
  * one JSON body; stream consumes NDJSON (one JSON object per `\n`-terminated line) —
220
388
  * deltas carry `message.content`, the final `done: true` line carries the token usage.
221
- * - **Think separation (H4).** The wire `think` flag is configurable
389
+ * - **Think separation.** The wire `think` flag is configurable
222
390
  * ({@link OllamaOptions.think}, default `false`). With `think: true` a thinking model's
223
391
  * daemon separates reasoning NATIVELY — returning it on the distinct `message.thinking`
224
- * channel (read here via `#thinking`) instead of inline in `message.content`. EITHER
392
+ * channel (read here through `extractThinking`) instead of inline in `message.content`. EITHER
225
393
  * way the per-call {@link ThinkSplitterInterface} is the defensive guarantee: a daemon
226
394
  * may ignore `think: false` for a thinking model and inline `<think>` tags, so every
227
395
  * content delta routes through the splitter, only CLEAN content is yielded / assembled,
228
396
  * and the separated reasoning (plus any daemon-side `message.thinking` deltas) lands on
229
397
  * `ProviderResult.thinking`, never in the conversation.
230
- * - **Boundary narrowing (§14).** Every wire value arrives as `unknown` and is
398
+ * - **Boundary narrowing.** Every wire value arrives as `unknown` and is
231
399
  * narrowed through guards (`isRecord` / `isString` / `isNumber`) — never `as`. A
232
400
  * missing / malformed field degrades to a sensible default (empty content, no
233
401
  * usage, `{}` arguments), never a throw.
@@ -244,7 +412,8 @@ export declare interface OllamaOptions {
244
412
  * `globalThis.fetch`) and {@link OllamaOptions.headers} is a per-request, possibly
245
413
  * async header injector merged over the base `Content-Type` — so a browser runtime
246
414
  * can route through the developer's own server with an obfuscated bearer token,
247
- * without this library ever handling a real API key. Both omitted ⇒ today's behaviour.
415
+ * without this library ever handling a real API key. Both omitted ⇒ the global `fetch`
416
+ * and only a JSON content type.
248
417
  * Orthogonal to the deadline: the hook is awaited inside `#fetch`'s try, so a hook
249
418
  * rejection clears the armed timer like any other request failure.
250
419
  *
@@ -256,11 +425,18 @@ export declare interface OllamaOptions {
256
425
  */
257
426
  export declare class OllamaProvider implements ProviderInterface {
258
427
  #private;
259
- readonly id: `${string}-${string}-${string}-${string}-${string}`;
260
428
  readonly name = "ollama";
261
429
  constructor(options: OllamaOptions);
262
430
  /**
263
- * The provider's context-framing default — the PROVIDER-DEFAULT level of
431
+ * Exposes this instance's identity — a fresh `crypto.randomUUID()` minted at
432
+ * construction, satisfying the {@link ProviderInterface.id} contract member. A second
433
+ * provider built from identical options carries a distinct id.
434
+ *
435
+ * @returns The instance's minted identifier
436
+ */
437
+ get id(): string;
438
+ /**
439
+ * Exposes the provider's context-framing default — the PROVIDER-DEFAULT level of
264
440
  * {@link import('@orkestrel/agent').AgentContextInterface.build}'s format cascade (it BEATS
265
441
  * the managers' built-in framing, is BEATEN by a manager-options or per-item override).
266
442
  * Satisfies the OPTIONAL {@link ProviderInterface.format} contract member: `undefined`
@@ -274,21 +450,21 @@ export declare class OllamaProvider implements ProviderInterface {
274
450
  * structured-output `format` wire parameter — that one IS sent in `#body`, but only when
275
451
  * a per-call `ProviderStreamOptions.schema` is supplied; only the word collides.
276
452
  *
277
- * @returns The configured {@link ContextFormatInterface}, or `undefined` when none
453
+ * @returns The configured {@link ContextFormat}, or `undefined` when none
278
454
  */
279
- get format(): ContextFormatInterface | undefined;
280
- generate(messages: readonly MessageInterface[], signal: AbortSignal, tools?: readonly ToolDefinition[], options?: ProviderStreamOptions): Promise<ProviderResult>;
281
- stream(messages: readonly MessageInterface[], signal: AbortSignal, tools?: readonly ToolDefinition[], options?: ProviderStreamOptions): AsyncGenerator<ProviderDelta, ProviderResult>;
455
+ get format(): ContextFormat | undefined;
456
+ generate(messages: readonly Message[], signal: AbortSignal, tools?: readonly ToolDefinition[], options?: ProviderStreamOptions): Promise<ProviderResult>;
457
+ stream(messages: readonly Message[], signal: AbortSignal, tools?: readonly ToolDefinition[], options?: ProviderStreamOptions): AsyncGenerator<ProviderDelta, ProviderResult>;
282
458
  }
283
459
 
284
460
  /**
285
- * A live `fetch` to `/api/chat` with the deadline + combined signal that bound it —
286
- * the internal wire-shape `OllamaProvider.#fetch` hands back to a consuming call.
461
+ * Represents an open `POST /api/chat` response together with the deadline and the
462
+ * combined signal that bound the request.
287
463
  *
288
464
  * @remarks
289
465
  * The `response` is the open `POST /api/chat` `Response`; `timeout` is the armed
290
466
  * {@link TimeoutInterface} the consuming call clears once it finishes reading the body
291
- * (or that `#fetch` itself clears on a failed/aborted request); `combined` is the
467
+ * (or that the provider clears on a failed or aborted request); `combined` is the
292
468
  * `AbortSignal.any([timeout.signal, callerSignal])` the request was issued under, which
293
469
  * the streaming path checks to tell a mid-stream cancel apart from any other error.
294
470
  */
@@ -299,13 +475,33 @@ export declare interface OllamaResponse {
299
475
  }
300
476
 
301
477
  /**
302
- * The exact `POST /api/chat` request body `OllamaProvider` sends — the internal typed
478
+ * Parses a non-stream `/api/chat` response body into a wire record.
479
+ *
480
+ * @remarks
481
+ * Total by construction: an empty body, a body that is not JSON, and a body whose JSON is
482
+ * not an object all yield `undefined`, so a malformed daemon response never escapes as a
483
+ * `SyntaxError`. The call site supplies the empty-record default that reads as empty
484
+ * content and no usage.
485
+ *
486
+ * @param response - The 200-OK `/api/chat` response whose body is read as text
487
+ * @returns The parsed record, or `undefined` when the body is empty or malformed
488
+ *
489
+ * @example
490
+ * ```ts
491
+ * await parseBody(new Response('{"message":{"content":"ok"}}'))
492
+ * // { message: { content: 'ok' } }
493
+ * ```
494
+ */
495
+ export declare function parseBody(response: Response): Promise<Readonly<Record<string, unknown>> | undefined>;
496
+
497
+ /**
498
+ * Represents the exact `POST /api/chat` request body `OllamaProvider` sends — the internal typed
303
499
  * wire contract.
304
500
  *
305
501
  * @remarks
306
502
  * This is the typed wire shape asserted against the official `ollama` client's
307
503
  * `ChatRequest` by the compile-time parity test; `src/` never imports `ollama` itself.
308
- * `messages` mirrors the minimal turn shape `#plain` builds (`role` / `content`, plus
504
+ * `messages` mirrors the minimal turn shape `mapMessages` builds (`role` / `content`, plus
309
505
  * `tool_calls` only on a turn that replays them and `images` only on a multimodal
310
506
  * turn); `options` and `tools` are only present when configured.
311
507
  */
@@ -335,7 +531,7 @@ export declare interface WireChatRequest {
335
531
  };
336
532
  }>;
337
533
  /**
338
- * The `/api/chat` structured-output constraint — a JSON-Schema object forwarded
534
+ * Holds the `/api/chat` structured-output constraint — a JSON-Schema object forwarded
339
535
  * verbatim from the per-call `ProviderStreamOptions.schema`. This is NOT
340
536
  * `OllamaOptions.format` (the unrelated prompt-context framing); only present
341
537
  * when a call supplies a `schema`.