@happyvertical/smrt-web 0.51.32 → 0.51.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/__typechecks__/webmcp-tool-definition.d.ts +2 -0
  2. package/dist/__typechecks__/webmcp-tool-definition.d.ts.map +1 -0
  3. package/dist/ai.d.ts +330 -358
  4. package/dist/ai.d.ts.map +1 -0
  5. package/dist/capability-classification.d.ts +89 -0
  6. package/dist/capability-classification.d.ts.map +1 -0
  7. package/dist/capability.d.ts +191 -0
  8. package/dist/capability.d.ts.map +1 -0
  9. package/dist/data-query.d.ts +121 -0
  10. package/dist/data-query.d.ts.map +1 -0
  11. package/dist/data-surface-actions.d.ts +118 -0
  12. package/dist/data-surface-actions.d.ts.map +1 -0
  13. package/dist/durable-store.d.ts +88 -0
  14. package/dist/durable-store.d.ts.map +1 -0
  15. package/dist/index.d.ts +468 -2245
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/intents.d.ts +273 -300
  18. package/dist/intents.d.ts.map +1 -0
  19. package/dist/internal.d.ts +15 -0
  20. package/dist/internal.d.ts.map +1 -0
  21. package/dist/offline/durable-queue.d.ts +171 -0
  22. package/dist/offline/durable-queue.d.ts.map +1 -0
  23. package/dist/offline/engine.d.ts +359 -0
  24. package/dist/offline/engine.d.ts.map +1 -0
  25. package/dist/offline/leader.d.ts +57 -0
  26. package/dist/offline/leader.d.ts.map +1 -0
  27. package/dist/offline/types.d.ts +232 -0
  28. package/dist/offline/types.d.ts.map +1 -0
  29. package/dist/offline.d.ts +258 -0
  30. package/dist/offline.d.ts.map +1 -0
  31. package/dist/persistence/snapshot-store.d.ts +121 -0
  32. package/dist/persistence/snapshot-store.d.ts.map +1 -0
  33. package/dist/persistence.d.ts +157 -0
  34. package/dist/persistence.d.ts.map +1 -0
  35. package/dist/remote-query.d.ts +61 -0
  36. package/dist/remote-query.d.ts.map +1 -0
  37. package/dist/sse-client.d.ts +182 -0
  38. package/dist/sse-client.d.ts.map +1 -0
  39. package/dist/update-state/meta-store.d.ts +66 -0
  40. package/dist/update-state/meta-store.d.ts.map +1 -0
  41. package/dist/update-state.d.ts +115 -0
  42. package/dist/update-state.d.ts.map +1 -0
  43. package/dist/webmcp-tool-names.d.ts +106 -112
  44. package/dist/webmcp-tool-names.d.ts.map +1 -0
  45. package/dist/webmcp.d.ts +193 -653
  46. package/dist/webmcp.d.ts.map +1 -0
  47. package/package.json +4 -4
package/dist/ai.d.ts CHANGED
@@ -1,358 +1,330 @@
1
- /**
2
- * Reject a turn this module's backends cannot faithfully carry, narrowing
3
- * `messages` to {@link InferenceTextMessage} on success.
4
- *
5
- * Every backend here is a TEXT conversation: roles `user`/`assistant`/
6
- * `system`, string content, no tools. Each of those limits is a real
7
- * difference between the backends' transports, so refusing up front is the
8
- * only way a caller can tell "this backend can't carry my turn" from "this
9
- * backend answered". The route wire contract (`chat-stream.ts`
10
- * `normalizeMessages`) and the local adapter both silently DROP exactly these
11
- * shapes, which is why this check is shared rather than reimplemented per
12
- * backend — two copies would drift, and the whole point is that both backends
13
- * agree.
14
- *
15
- * `backendLabel` names the backend in the message so a caller knows which one
16
- * refused.
17
- */
18
- export declare function assertTextOnlyTurn(backendLabel: string, messages: InferenceMessage[], options?: InferenceChatOptions): asserts messages is InferenceTextMessage[];
19
-
20
- /**
21
- * Create the browser's single inference path over `options.backends`.
22
- *
23
- * Resolution happens inside every `chat`/`stream` call, so a `preference` or
24
- * `select()` change made between calls is honored by the next one. A call
25
- * whose resolution finds nothing usable throws `InferencePathError` with
26
- * `code: 'no_usable_backend'` rather than falling back to a fake answer.
27
- */
28
- export declare function createInferencePath(options: InferencePathOptions): InferencePath;
29
-
30
- /**
31
- * A backend over a `text/event-stream` inference route.
32
- *
33
- * The wire contract is `@happyvertical/smrt-chat`'s `ChatStreamEvent` framing
34
- * (token deltas, then a terminal `done` or `error`), consumed here without
35
- * depending on that package: smrt-web carries no inter-SMRT dependencies, so
36
- * the frame vocabulary is mirrored the same way the rest of this module is.
37
- *
38
- * `status` is always `'ready'` — there is nothing to load — so an `auto` path
39
- * picks this backend until a local one reports ready. It defines neither
40
- * `load` nor `unload`, so a control must not offer those actions for it.
41
- *
42
- * A turn this wire cannot carry (tool turns, multimodal content, tool
43
- * definitions) is REFUSED with `unsupported_message` rather than forwarded:
44
- * the route silently drops those shapes, so forwarding would answer a
45
- * different question than the caller asked.
46
- */
47
- export declare function createRouteInferenceBackend(options: RouteInferenceBackendOptions): InferenceBackend;
48
-
49
- export declare interface InferenceBackend {
50
- /** Stable id, unique across a path. A preference names this. */
51
- readonly id: string;
52
- /**
53
- * Where this backend runs. Purely descriptive: it exists so a UI can say
54
- * "runs on your device" and so callers can order backends sensibly. The
55
- * path's resolution never reads it.
56
- */
57
- readonly kind: InferenceBackendKind;
58
- readonly status: InferenceBackendStatus;
59
- /** Present while `status` is `loading` (and after a failure). */
60
- readonly progress?: InferenceProgress;
61
- /** Load whatever `status: 'idle'` is waiting on. No-op when already ready. */
62
- load?(onProgress?: (progress: InferenceProgress) => void): Promise<void>;
63
- /** Release loaded resources. No-op for backends with nothing to release. */
64
- unload?(): Promise<void>;
65
- chat(messages: InferenceMessage[], options?: InferenceChatOptions): Promise<InferenceResponse>;
66
- stream(messages: InferenceMessage[], options?: InferenceChatOptions): AsyncIterable<string>;
67
- /**
68
- * Optional change notification. A backend that exposes this lets the path
69
- * re-broadcast status/progress changes to UI subscribers; one that omits it
70
- * is simply non-reactive for those fields.
71
- */
72
- subscribe?(listener: () => void): () => void;
73
- }
74
-
75
- /** Where a backend executes. Reporting only — the path never branches on it. */
76
- export declare type InferenceBackendKind = 'local' | 'route';
77
-
78
- /**
79
- * Whether a backend can answer right now.
80
- *
81
- * - `unavailable` — cannot run in this environment at all (no WebGPU, model
82
- * not offered). Never auto-selected.
83
- * - `idle` — could run, nothing loaded yet. Not auto-selected; call `load()`.
84
- * - `loading` — a load is in progress. Not auto-selected.
85
- * - `ready` — can answer now. The only state auto-selection picks.
86
- * - `error` — the last load or call failed. Never auto-selected.
87
- */
88
- export declare type InferenceBackendStatus = 'unavailable' | 'idle' | 'loading' | 'ready' | 'error';
89
-
90
- /**
91
- * Per-call options (mirrors the `ChatOptions` fields a browser backend can
92
- * honor).
93
- *
94
- * `signal` is the cancellation seam a backend SHOULD respect — one that cannot
95
- * (the browser LLM adapter has no cancel path) ignores it rather than
96
- * pretending, so a caller must not read an abort as proof the work stopped.
97
- *
98
- * `tools` is refused, not ignored: every backend here is a text conversation,
99
- * and a backend that silently dropped a tool definition would let a caller
100
- * believe an answer was produced with tools it never received. Use
101
- * {@link assertTextOnlyTurn} to check a turn up front.
102
- */
103
- export declare interface InferenceChatOptions {
104
- model?: string;
105
- maxTokens?: number;
106
- temperature?: number;
107
- topP?: number;
108
- stop?: string | string[];
109
- tools?: InferenceTool[];
110
- toolChoice?: 'auto' | 'none' | {
111
- type: 'function';
112
- function: {
113
- name: string;
114
- };
115
- };
116
- /** Token sink for a streamed reply. Backends that cannot stream ignore it. */
117
- onProgress?: (chunk: string) => void;
118
- /**
119
- * Caller cancellation. Honored by the route backend and by bitgpu, which
120
- * aborts a live generation; both compose it with `timeout`. The WebLLM
121
- * adapter cannot cancel a generation already running on the GPU and does not
122
- * read it.
123
- */
124
- signal?: AbortSignal;
125
- /**
126
- * Deadline in milliseconds for one call. Honored by the route backend, which
127
- * aborts the request when it elapses, and by bitgpu, which aborts the
128
- * generation. The WebLLM adapter has no cancellation seam and ignores it.
129
- */
130
- timeout?: number;
131
- }
132
-
133
- export declare type InferenceContentPart = InferenceTextPart | InferenceImagePart;
134
-
135
- /** Image content part (mirrors `ImageContentPart`). */
136
- export declare interface InferenceImagePart {
137
- type: 'image_url';
138
- image_url: {
139
- url: string;
140
- detail?: 'auto' | 'low' | 'high';
141
- };
142
- }
143
-
144
- /** One conversation message (mirrors `AIMessage`). */
145
- export declare interface InferenceMessage {
146
- role: InferenceRole;
147
- content: string | InferenceContentPart[];
148
- name?: string;
149
- tool_calls?: InferenceToolCall[];
150
- }
151
-
152
- export declare interface InferencePath {
153
- readonly backends: readonly InferenceBackend[];
154
- /** The backend the NEXT call would use; `null` when none is usable. */
155
- readonly activeId: string | null;
156
- /** The configured preference, or `'auto'`. Settable; takes effect next call. */
157
- preference: string;
158
- /** The resolution the next call would produce, without making one. */
159
- resolve(): InferenceResolution;
160
- /** Pin to a backend id, or back to `'auto'`. */
161
- select(preference: string): void;
162
- chat(messages: InferenceMessage[], options?: InferenceChatOptions): Promise<InferenceResponse>;
163
- stream(messages: InferenceMessage[], options?: InferenceChatOptions): AsyncIterable<string>;
164
- /**
165
- * Notified when a backend reports a status/progress change or the
166
- * preference changes. Returns an unsubscribe function.
167
- */
168
- subscribe(listener: () => void): () => void;
169
- /** Detach every backend subscription and drop all listeners. */
170
- dispose(): void;
171
- }
172
-
173
- export declare class InferencePathError extends Error {
174
- readonly code: InferencePathErrorCode;
175
- constructor(message: string, code: InferencePathErrorCode);
176
- }
177
-
178
- export declare type InferencePathErrorCode = 'no_backends' | 'duplicate_backend' | 'unknown_backend' | 'no_usable_backend' | 'unsupported_message' | 'route_request_failed' | 'route_stream_error' | 'route_stream_truncated';
179
-
180
- export declare interface InferencePathOptions {
181
- /**
182
- * Candidate backends, in priority order. Order IS the auto-selection
183
- * preference: list the local model first to prefer it when it is usable.
184
- */
185
- backends: readonly InferenceBackend[];
186
- /** Backend id to prefer, or `'auto'`. Defaults to `'auto'`. */
187
- preference?: string;
188
- /**
189
- * Whether a backend may answer. Defaults to `backend.status === 'ready'`,
190
- * so an unloaded (`idle`) local backend is skipped rather than silently
191
- * triggering a multi-gigabyte download mid-conversation.
192
- */
193
- canUse?: (backend: InferenceBackend) => boolean;
194
- }
195
-
196
- /**
197
- * Model load/download progress, mirroring the browser adapters'
198
- * `DownloadProgressInfo` so a `DownloadProgress` control can render it
199
- * directly.
200
- */
201
- export declare interface InferenceProgress {
202
- state: 'idle' | 'downloading' | 'extracting' | 'complete' | 'error';
203
- bytesLoaded: number;
204
- bytesTotal: number;
205
- percent: number;
206
- currentFile?: string;
207
- estimatedTimeRemaining?: number;
208
- error?: string;
209
- }
210
-
211
- /** Why a call was routed to the backend it was routed to. */
212
- export declare interface InferenceResolution {
213
- backend: InferenceBackend;
214
- /**
215
- * - `preferred` — an explicit preference named this backend and it is usable.
216
- * - `fallback` — an explicit preference was unusable; `requested` names it.
217
- * - `auto` — no explicit preference; first usable backend in declared order.
218
- */
219
- reason: 'preferred' | 'fallback' | 'auto';
220
- /** The preference that could not be honored (`reason: 'fallback'` only). */
221
- requested?: string;
222
- /** Ids skipped because `canUse` returned false, in declared order. */
223
- skipped: readonly string[];
224
- }
225
-
226
- /** A completion result (mirrors `AIResponse`). */
227
- export declare interface InferenceResponse {
228
- content: string;
229
- usage?: InferenceUsage;
230
- model?: string;
231
- finishReason?: 'stop' | 'length' | 'tool_calls' | 'content_filter';
232
- toolCalls?: InferenceToolCall[];
233
- }
234
-
235
- /** Message roles, mirroring `AIMessage['role']`. */
236
- export declare type InferenceRole = 'system' | 'user' | 'assistant' | 'function' | 'tool';
237
-
238
- /**
239
- * A turn in the subset every backend in this module can carry: a text
240
- * conversation with no tool traffic.
241
- */
242
- export declare interface InferenceTextMessage {
243
- role: 'system' | 'user' | 'assistant';
244
- content: string;
245
- }
246
-
247
- /**
248
- * Browser inference path — ONE call-site object over interchangeable inference
249
- * backends.
250
- *
251
- * A caller (a component, a hook, a workbench) holds a single `InferencePath`
252
- * and calls `chat()`/`stream()` on it. Which backend actually answers is
253
- * resolved **per call**, never cached on the path, so changing the preference
254
- * between two calls takes effect on the next one. That is what makes "switch
255
- * between the local model and the server route mid-session" a property
256
- * assignment rather than session state to unwind.
257
- *
258
- * ## Why a mirror rather than an import
259
- *
260
- * `InferenceMessage`/`InferenceResponse`/`InferenceChatOptions` are TEXTUAL
261
- * structural mirrors of `@happyvertical/ai`'s `AIMessage`/`AIResponse`/
262
- * `ChatOptions`, and `InferenceProgress` mirrors the browser adapters'
263
- * `DownloadProgressInfo`. smrt-web deliberately has no `@happyvertical/*`
264
- * dependency (`AGENTS.md`, "No inter-smrt dependencies"), so an `AIInterface`
265
- * — the ecosystem's canonical inference boundary — is described here by shape.
266
- *
267
- * The mirror covers the text-generation surface only. A backend whose
268
- * `chat`/`stream` accept these shapes satisfies the consumers in this
269
- * repository; a consumer that needs more of `AIInterface` adapts at its own
270
- * boundary rather than widening this contract.
271
- *
272
- * ## What this module does NOT own
273
- *
274
- * Backends arrive as arguments, the same way definitions and fetchers do
275
- * everywhere else in this package. A WebLLM backend lives beside the adapter
276
- * it wraps; `createRouteInferenceBackend` here covers the streaming-route case
277
- * because that one is pure `fetch` plus an SSE frame reader.
278
- *
279
- * @module
280
- */
281
- /** Text content part (mirrors `TextContentPart`). */
282
- export declare interface InferenceTextPart {
283
- type: 'text';
284
- text: string;
285
- }
286
-
287
- /** A tool definition offered to the model (mirrors `AITool`). */
288
- export declare interface InferenceTool {
289
- type: 'function';
290
- function: {
291
- name: string;
292
- description?: string;
293
- parameters?: Record<string, unknown>;
294
- };
295
- }
296
-
297
- export declare interface InferenceToolCall {
298
- id: string;
299
- type: 'function';
300
- function: {
301
- name: string;
302
- arguments: string;
303
- };
304
- }
305
-
306
- /** Token usage (mirrors `TokenUsage`). */
307
- export declare interface InferenceUsage {
308
- promptTokens: number;
309
- completionTokens: number;
310
- totalTokens: number;
311
- }
312
-
313
- /** Cap on the reply a route backend's `chat()` will assemble. */
314
- export declare const MAX_INFERENCE_REPLY_UNITS: number;
315
-
316
- /** Cap on total bytes read for one route call. */
317
- export declare const MAX_INFERENCE_STREAM_BYTES: number;
318
-
319
- /**
320
- * Cap on one undelivered SSE line, in UTF-16 code units. Without it a peer
321
- * that streams bytes with no `\n` grows the reader's buffer until the tab's
322
- * heap is exhausted.
323
- */
324
- export declare const MAX_INFERENCE_STREAM_LINE_UNITS = 1048576;
325
-
326
- /**
327
- * Compose the caller's cancellation signal with this call's deadline, so a
328
- * request with a `timeout` cannot outlive it even when no signal was passed.
329
- *
330
- * Exported so every backend that CAN cancel enforces `timeout` the same way: a
331
- * deadline the route backend would have enforced must not be quietly ignored
332
- * by a local one.
333
- */
334
- export declare function requestSignal(chatOptions?: InferenceChatOptions): AbortSignal | undefined;
335
-
336
- export declare interface RouteInferenceBackendOptions {
337
- /** Full URL of a `text/event-stream` endpoint accepting `{ messages, session }`. */
338
- endpoint: string;
339
- /** Backend id. Defaults to `'route'`. */
340
- id?: string;
341
- /** Extra request headers, evaluated per call (auth tokens rotate). */
342
- headers?: () => Record<string, string>;
343
- /** Fetch credentials mode. Defaults to the platform default. */
344
- credentials?: RequestCredentials;
345
- /** Session metadata attached to the request body. */
346
- session?: Record<string, unknown>;
347
- /** Injectable fetch (tests, SSR-safe wrappers). */
348
- fetchImpl?: typeof fetch;
349
- /**
350
- * Model id recorded on the returned `InferenceResponse`. Reporting only —
351
- * the route resolves the model it actually uses, and a caller cannot widen
352
- * it by naming one here (the generation caps live in the server-resolved
353
- * stream context).
354
- */
355
- model?: string;
356
- }
357
-
358
- export { }
1
+ /**
2
+ * Browser inference path — ONE call-site object over interchangeable inference
3
+ * backends.
4
+ *
5
+ * A caller (a component, a hook, a workbench) holds a single `InferencePath`
6
+ * and calls `chat()`/`stream()` on it. Which backend actually answers is
7
+ * resolved **per call**, never cached on the path, so changing the preference
8
+ * between two calls takes effect on the next one. That is what makes "switch
9
+ * between the local model and the server route mid-session" a property
10
+ * assignment rather than session state to unwind.
11
+ *
12
+ * ## Why a mirror rather than an import
13
+ *
14
+ * `InferenceMessage`/`InferenceResponse`/`InferenceChatOptions` are TEXTUAL
15
+ * structural mirrors of `@happyvertical/ai`'s `AIMessage`/`AIResponse`/
16
+ * `ChatOptions`, and `InferenceProgress` mirrors the browser adapters'
17
+ * `DownloadProgressInfo`. smrt-web deliberately has no `@happyvertical/*`
18
+ * dependency (`AGENTS.md`, "No inter-smrt dependencies"), so an `AIInterface`
19
+ * — the ecosystem's canonical inference boundary — is described here by shape.
20
+ *
21
+ * The mirror covers the text-generation surface only. A backend whose
22
+ * `chat`/`stream` accept these shapes satisfies the consumers in this
23
+ * repository; a consumer that needs more of `AIInterface` adapts at its own
24
+ * boundary rather than widening this contract.
25
+ *
26
+ * ## What this module does NOT own
27
+ *
28
+ * Backends arrive as arguments, the same way definitions and fetchers do
29
+ * everywhere else in this package. A WebLLM backend lives beside the adapter
30
+ * it wraps; `createRouteInferenceBackend` here covers the streaming-route case
31
+ * because that one is pure `fetch` plus an SSE frame reader.
32
+ *
33
+ * @module
34
+ */
35
+ /** Text content part (mirrors `TextContentPart`). */
36
+ export interface InferenceTextPart {
37
+ type: 'text';
38
+ text: string;
39
+ }
40
+ /** Image content part (mirrors `ImageContentPart`). */
41
+ export interface InferenceImagePart {
42
+ type: 'image_url';
43
+ image_url: {
44
+ url: string;
45
+ detail?: 'auto' | 'low' | 'high';
46
+ };
47
+ }
48
+ export type InferenceContentPart = InferenceTextPart | InferenceImagePart;
49
+ /** Message roles, mirroring `AIMessage['role']`. */
50
+ export type InferenceRole = 'system' | 'user' | 'assistant' | 'function' | 'tool';
51
+ export interface InferenceToolCall {
52
+ id: string;
53
+ type: 'function';
54
+ function: {
55
+ name: string;
56
+ arguments: string;
57
+ };
58
+ }
59
+ /** One conversation message (mirrors `AIMessage`). */
60
+ export interface InferenceMessage {
61
+ role: InferenceRole;
62
+ content: string | InferenceContentPart[];
63
+ name?: string;
64
+ tool_calls?: InferenceToolCall[];
65
+ }
66
+ /** A tool definition offered to the model (mirrors `AITool`). */
67
+ export interface InferenceTool {
68
+ type: 'function';
69
+ function: {
70
+ name: string;
71
+ description?: string;
72
+ parameters?: Record<string, unknown>;
73
+ };
74
+ }
75
+ /** Token usage (mirrors `TokenUsage`). */
76
+ export interface InferenceUsage {
77
+ promptTokens: number;
78
+ completionTokens: number;
79
+ totalTokens: number;
80
+ }
81
+ /** A completion result (mirrors `AIResponse`). */
82
+ export interface InferenceResponse {
83
+ content: string;
84
+ usage?: InferenceUsage;
85
+ model?: string;
86
+ finishReason?: 'stop' | 'length' | 'tool_calls' | 'content_filter';
87
+ toolCalls?: InferenceToolCall[];
88
+ }
89
+ /**
90
+ * Per-call options (mirrors the `ChatOptions` fields a browser backend can
91
+ * honor).
92
+ *
93
+ * `signal` is the cancellation seam a backend SHOULD respect — one that cannot
94
+ * (the browser LLM adapter has no cancel path) ignores it rather than
95
+ * pretending, so a caller must not read an abort as proof the work stopped.
96
+ *
97
+ * `tools` is refused, not ignored: every backend here is a text conversation,
98
+ * and a backend that silently dropped a tool definition would let a caller
99
+ * believe an answer was produced with tools it never received. Use
100
+ * {@link assertTextOnlyTurn} to check a turn up front.
101
+ */
102
+ export interface InferenceChatOptions {
103
+ model?: string;
104
+ maxTokens?: number;
105
+ temperature?: number;
106
+ topP?: number;
107
+ stop?: string | string[];
108
+ tools?: InferenceTool[];
109
+ toolChoice?: 'auto' | 'none' | {
110
+ type: 'function';
111
+ function: {
112
+ name: string;
113
+ };
114
+ };
115
+ /** Token sink for a streamed reply. Backends that cannot stream ignore it. */
116
+ onProgress?: (chunk: string) => void;
117
+ /**
118
+ * Caller cancellation. Honored by the route backend and by bitgpu, which
119
+ * aborts a live generation; both compose it with `timeout`. The WebLLM
120
+ * adapter cannot cancel a generation already running on the GPU and does not
121
+ * read it.
122
+ */
123
+ signal?: AbortSignal;
124
+ /**
125
+ * Deadline in milliseconds for one call. Honored by the route backend, which
126
+ * aborts the request when it elapses, and by bitgpu, which aborts the
127
+ * generation. The WebLLM adapter has no cancellation seam and ignores it.
128
+ */
129
+ timeout?: number;
130
+ }
131
+ /**
132
+ * Model load/download progress, mirroring the browser adapters'
133
+ * `DownloadProgressInfo` so a `DownloadProgress` control can render it
134
+ * directly.
135
+ */
136
+ export interface InferenceProgress {
137
+ state: 'idle' | 'downloading' | 'extracting' | 'complete' | 'error';
138
+ bytesLoaded: number;
139
+ bytesTotal: number;
140
+ percent: number;
141
+ currentFile?: string;
142
+ estimatedTimeRemaining?: number;
143
+ error?: string;
144
+ }
145
+ /**
146
+ * Cap on one undelivered SSE line, in UTF-16 code units. Without it a peer
147
+ * that streams bytes with no `\n` grows the reader's buffer until the tab's
148
+ * heap is exhausted.
149
+ */
150
+ export declare const MAX_INFERENCE_STREAM_LINE_UNITS = 1048576;
151
+ /** Cap on total bytes read for one route call. */
152
+ export declare const MAX_INFERENCE_STREAM_BYTES: number;
153
+ /** Cap on the reply a route backend's `chat()` will assemble. */
154
+ export declare const MAX_INFERENCE_REPLY_UNITS: number;
155
+ /**
156
+ * A turn in the subset every backend in this module can carry: a text
157
+ * conversation with no tool traffic.
158
+ */
159
+ export interface InferenceTextMessage {
160
+ role: 'system' | 'user' | 'assistant';
161
+ content: string;
162
+ }
163
+ /**
164
+ * Reject a turn this module's backends cannot faithfully carry, narrowing
165
+ * `messages` to {@link InferenceTextMessage} on success.
166
+ *
167
+ * Every backend here is a TEXT conversation: roles `user`/`assistant`/
168
+ * `system`, string content, no tools. Each of those limits is a real
169
+ * difference between the backends' transports, so refusing up front is the
170
+ * only way a caller can tell "this backend can't carry my turn" from "this
171
+ * backend answered". The route wire contract (`chat-stream.ts`
172
+ * `normalizeMessages`) and the local adapter both silently DROP exactly these
173
+ * shapes, which is why this check is shared rather than reimplemented per
174
+ * backend — two copies would drift, and the whole point is that both backends
175
+ * agree.
176
+ *
177
+ * `backendLabel` names the backend in the message so a caller knows which one
178
+ * refused.
179
+ */
180
+ export declare function assertTextOnlyTurn(backendLabel: string, messages: InferenceMessage[], options?: InferenceChatOptions): asserts messages is InferenceTextMessage[];
181
+ /**
182
+ * Whether a backend can answer right now.
183
+ *
184
+ * - `unavailable` — cannot run in this environment at all (no WebGPU, model
185
+ * not offered). Never auto-selected.
186
+ * - `idle` — could run, nothing loaded yet. Not auto-selected; call `load()`.
187
+ * - `loading` — a load is in progress. Not auto-selected.
188
+ * - `ready` — can answer now. The only state auto-selection picks.
189
+ * - `error` — the last load or call failed. Never auto-selected.
190
+ */
191
+ export type InferenceBackendStatus = 'unavailable' | 'idle' | 'loading' | 'ready' | 'error';
192
+ /** Where a backend executes. Reporting only — the path never branches on it. */
193
+ export type InferenceBackendKind = 'local' | 'route';
194
+ export interface InferenceBackend {
195
+ /** Stable id, unique across a path. A preference names this. */
196
+ readonly id: string;
197
+ /**
198
+ * Where this backend runs. Purely descriptive: it exists so a UI can say
199
+ * "runs on your device" and so callers can order backends sensibly. The
200
+ * path's resolution never reads it.
201
+ */
202
+ readonly kind: InferenceBackendKind;
203
+ readonly status: InferenceBackendStatus;
204
+ /** Present while `status` is `loading` (and after a failure). */
205
+ readonly progress?: InferenceProgress;
206
+ /** Load whatever `status: 'idle'` is waiting on. No-op when already ready. */
207
+ load?(onProgress?: (progress: InferenceProgress) => void): Promise<void>;
208
+ /** Release loaded resources. No-op for backends with nothing to release. */
209
+ unload?(): Promise<void>;
210
+ chat(messages: InferenceMessage[], options?: InferenceChatOptions): Promise<InferenceResponse>;
211
+ stream(messages: InferenceMessage[], options?: InferenceChatOptions): AsyncIterable<string>;
212
+ /**
213
+ * Optional change notification. A backend that exposes this lets the path
214
+ * re-broadcast status/progress changes to UI subscribers; one that omits it
215
+ * is simply non-reactive for those fields.
216
+ */
217
+ subscribe?(listener: () => void): () => void;
218
+ }
219
+ /** Why a call was routed to the backend it was routed to. */
220
+ export interface InferenceResolution {
221
+ backend: InferenceBackend;
222
+ /**
223
+ * - `preferred` — an explicit preference named this backend and it is usable.
224
+ * - `fallback` — an explicit preference was unusable; `requested` names it.
225
+ * - `auto` — no explicit preference; first usable backend in declared order.
226
+ */
227
+ reason: 'preferred' | 'fallback' | 'auto';
228
+ /** The preference that could not be honored (`reason: 'fallback'` only). */
229
+ requested?: string;
230
+ /** Ids skipped because `canUse` returned false, in declared order. */
231
+ skipped: readonly string[];
232
+ }
233
+ export interface InferencePathOptions {
234
+ /**
235
+ * Candidate backends, in priority order. Order IS the auto-selection
236
+ * preference: list the local model first to prefer it when it is usable.
237
+ */
238
+ backends: readonly InferenceBackend[];
239
+ /** Backend id to prefer, or `'auto'`. Defaults to `'auto'`. */
240
+ preference?: string;
241
+ /**
242
+ * Whether a backend may answer. Defaults to `backend.status === 'ready'`,
243
+ * so an unloaded (`idle`) local backend is skipped rather than silently
244
+ * triggering a multi-gigabyte download mid-conversation.
245
+ */
246
+ canUse?: (backend: InferenceBackend) => boolean;
247
+ }
248
+ export interface InferencePath {
249
+ readonly backends: readonly InferenceBackend[];
250
+ /** The backend the NEXT call would use; `null` when none is usable. */
251
+ readonly activeId: string | null;
252
+ /** The configured preference, or `'auto'`. Settable; takes effect next call. */
253
+ preference: string;
254
+ /** The resolution the next call would produce, without making one. */
255
+ resolve(): InferenceResolution;
256
+ /** Pin to a backend id, or back to `'auto'`. */
257
+ select(preference: string): void;
258
+ chat(messages: InferenceMessage[], options?: InferenceChatOptions): Promise<InferenceResponse>;
259
+ stream(messages: InferenceMessage[], options?: InferenceChatOptions): AsyncIterable<string>;
260
+ /**
261
+ * Notified when a backend reports a status/progress change or the
262
+ * preference changes. Returns an unsubscribe function.
263
+ */
264
+ subscribe(listener: () => void): () => void;
265
+ /** Detach every backend subscription and drop all listeners. */
266
+ dispose(): void;
267
+ }
268
+ export type InferencePathErrorCode = 'no_backends' | 'duplicate_backend' | 'unknown_backend' | 'no_usable_backend' | 'unsupported_message' | 'route_request_failed' | 'route_stream_error' | 'route_stream_truncated';
269
+ export declare class InferencePathError extends Error {
270
+ readonly code: InferencePathErrorCode;
271
+ constructor(message: string, code: InferencePathErrorCode);
272
+ }
273
+ /**
274
+ * Create the browser's single inference path over `options.backends`.
275
+ *
276
+ * Resolution happens inside every `chat`/`stream` call, so a `preference` or
277
+ * `select()` change made between calls is honored by the next one. A call
278
+ * whose resolution finds nothing usable throws `InferencePathError` with
279
+ * `code: 'no_usable_backend'` rather than falling back to a fake answer.
280
+ */
281
+ export declare function createInferencePath(options: InferencePathOptions): InferencePath;
282
+ export interface RouteInferenceBackendOptions {
283
+ /** Full URL of a `text/event-stream` endpoint accepting `{ messages, session }`. */
284
+ endpoint: string;
285
+ /** Backend id. Defaults to `'route'`. */
286
+ id?: string;
287
+ /** Extra request headers, evaluated per call (auth tokens rotate). */
288
+ headers?: () => Record<string, string>;
289
+ /** Fetch credentials mode. Defaults to the platform default. */
290
+ credentials?: RequestCredentials;
291
+ /** Session metadata attached to the request body. */
292
+ session?: Record<string, unknown>;
293
+ /** Injectable fetch (tests, SSR-safe wrappers). */
294
+ fetchImpl?: typeof fetch;
295
+ /**
296
+ * Model id recorded on the returned `InferenceResponse`. Reporting only —
297
+ * the route resolves the model it actually uses, and a caller cannot widen
298
+ * it by naming one here (the generation caps live in the server-resolved
299
+ * stream context).
300
+ */
301
+ model?: string;
302
+ }
303
+ /**
304
+ * Compose the caller's cancellation signal with this call's deadline, so a
305
+ * request with a `timeout` cannot outlive it even when no signal was passed.
306
+ *
307
+ * Exported so every backend that CAN cancel enforces `timeout` the same way: a
308
+ * deadline the route backend would have enforced must not be quietly ignored
309
+ * by a local one.
310
+ */
311
+ export declare function requestSignal(chatOptions?: InferenceChatOptions): AbortSignal | undefined;
312
+ /**
313
+ * A backend over a `text/event-stream` inference route.
314
+ *
315
+ * The wire contract is `@happyvertical/smrt-chat`'s `ChatStreamEvent` framing
316
+ * (token deltas, then a terminal `done` or `error`), consumed here without
317
+ * depending on that package: smrt-web carries no inter-SMRT dependencies, so
318
+ * the frame vocabulary is mirrored the same way the rest of this module is.
319
+ *
320
+ * `status` is always `'ready'` — there is nothing to load — so an `auto` path
321
+ * picks this backend until a local one reports ready. It defines neither
322
+ * `load` nor `unload`, so a control must not offer those actions for it.
323
+ *
324
+ * A turn this wire cannot carry (tool turns, multimodal content, tool
325
+ * definitions) is REFUSED with `unsupported_message` rather than forwarded:
326
+ * the route silently drops those shapes, so forwarding would answer a
327
+ * different question than the caller asked.
328
+ */
329
+ export declare function createRouteInferenceBackend(options: RouteInferenceBackendOptions): InferenceBackend;
330
+ //# sourceMappingURL=ai.d.ts.map