arcane-os 0.2.1 → 0.2.3

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 (30) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +8 -8
  3. package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +5 -5
  4. package/browser-runtime/ai/browser-speech-providers.mjs +1 -0
  5. package/docs/architecture.md +2 -2
  6. package/docs/reference/README.md +79 -13
  7. package/docs/reference/ai/browser-speech.md +336 -0
  8. package/docs/reference/ai/browser-wasm.md +207 -82
  9. package/docs/reference/availability-and-normalization.md +33 -4
  10. package/docs/reference/behavioral-testing.md +4 -1
  11. package/docs/reference/cli.md +29 -10
  12. package/docs/reference/core/arcane-ai-contracts.md +43 -9
  13. package/docs/reference/inventory/package-api.json +110 -14
  14. package/docs/reference/inventory/runtime-components.json +42 -14
  15. package/docs/reference/inventory/runtime-modules.json +118 -13
  16. package/docs/reference/protocols.md +264 -38
  17. package/docs/reference/runtime-components.md +177 -21
  18. package/docs/reference/runtime-modules.md +449 -13
  19. package/docs/reference/sdk-api.md +626 -85
  20. package/package.json +1 -1
  21. package/runtime/ARCANE_RUNTIME_RELEASE.json +19 -19
  22. package/runtime/arcane/components/chat.html +17 -52
  23. package/runtime/arcane/components/speech.html +109 -29
  24. package/runtime/arcane/components/voice-transcription.html +437 -68
  25. package/runtime/arcane/modules/AI.js +556 -128
  26. package/runtime/arcane/modules/AIProviderRuntime.js +299 -30
  27. package/runtime/arcane/modules/AIRuntimeState.js +23 -4
  28. package/runtime/arcane/modules/ComponentContracts.js +272 -0
  29. package/runtime/arcane/modules/LocalAIReadinessController.js +1 -1
  30. package/schemas/arcane-lock.schema.json +2 -2
@@ -9,7 +9,7 @@ page is the focused local-browser path beneath the normalized AI decision
9
9
  guide.
10
10
 
11
11
  The wiring example assumes a scaffolded or materialized Arcane application
12
- with SDK `0.1.2`'s authenticated runtime tree and 86-entry browser import map.
12
+ with SDK `0.2.2`'s authenticated runtime tree and generated browser import map.
13
13
  `arcane/DBOPFS` is a managed browser-map specifier, not an npm package export.
14
14
  See [browser runtime delivery](../protocols.md#browser-runtime-delivery) before
15
15
  using the example in a custom host or bundler.
@@ -25,9 +25,17 @@ import {
25
25
 
26
26
  const MODEL = Object.freeze({
27
27
  id:'my-reviewed-model',
28
- url:'https://models.example/revisions/4f7c/model-q4.gguf',
29
- bytes:123456789,
30
- sha256:'0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
28
+ files:Object.freeze([{
29
+ name:'model-q4-00001-of-00002.gguf',
30
+ url:'https://models.example/revisions/4f7c/model-q4-00001-of-00002.gguf',
31
+ bytes:123456789,
32
+ sha256:'0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
33
+ },{
34
+ name:'model-q4-00002-of-00002.gguf',
35
+ url:'https://models.example/revisions/4f7c/model-q4-00002-of-00002.gguf',
36
+ bytes:98765432,
37
+ sha256:'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789'
38
+ }])
31
39
  });
32
40
 
33
41
  const dbopfs = globalThis.dbopfs || new DBOPFS({applicationId:'my-app'});
@@ -41,18 +49,23 @@ const ai = createArcaneAI({
41
49
  security:{secure:true}
42
50
  });
43
51
 
44
- // Put this behind an explicit user action: it can download MODEL.bytes bytes.
52
+ // Put this behind an explicit user action: it can download every declared file.
45
53
  async function loadReviewedModel() {
46
- return ai.load({threads:1, contextTokens:4096, gpuLayers:0});
54
+ return ai.load({threads:1, contextTokens:4096});
47
55
  }
48
56
  ```
49
57
 
50
58
  The browser-WASM runtime closure packages the authenticated
51
59
  `@wllama/wllama` `3.6.0` ESM and WebAssembly runtime plus the Wllama and
52
60
  llama.cpp MIT license texts. It
53
- packages no model weights, model catalog, CDN fallback, native provider, speech
54
- synthesis, or transcription. Callers supply the exact model authority. `bytes`
55
- is an optional expected positive byte length, not inline model data.
61
+ packages no model weights, default model catalog, CDN fallback, native
62
+ provider, speech synthesis, or transcription. The sibling
63
+ [`arcane-os/ai/browser-speech`](browser-speech.md) entrypoint supplies speech
64
+ provider mechanisms but still no runtime/model bytes. Callers supply every
65
+ model-source authority. Each file's `bytes` is an optional expected positive
66
+ byte length, not inline model data. The declared URL binds selection identity;
67
+ exact byte identity is established only by the checks the caller enables and
68
+ supplies.
56
69
 
57
70
  ## Lifecycle at a glance
58
71
 
@@ -67,6 +80,7 @@ is an optional expected positive byte length, not inline model data.
67
80
  | `ai.llm.chat(request)` / `ai.fetchRequest(request)` | Validated OpenAI-like completion. |
68
81
  | `ai.llm.stream(request)` | Frozen async-iterator handle with `result` and `cancel(reason)`. |
69
82
  | `ai.streamRequest(request)` | Consumes the stream and returns text or `{toolName: argumentJsonString}`. |
83
+ | `ai.createChatSession(options)` | Asynchronously resolves `Promise<PersistentAIChatSession>` bound to this exact controller; caller-supplied `chat` is rejected. |
70
84
  | `ai.unload()` | Cancels active work, releases the Wllama session, and returns flat unloaded status; the DBOPFS cache remains. |
71
85
  | `ai.dispose()` | Permanently disposes the controller; explicit `store.remove(source)` is required to delete cached model bytes. |
72
86
 
@@ -77,11 +91,19 @@ current frozen status. Provider states are `unloaded`, `loading`, `ready`,
77
91
 
78
92
  ## Model authority, security, and cache admission
79
93
 
80
- The canonical model descriptor is `{id, url, bytes?, sha256?}`. `id` and `url`
81
- are required. The URL must be absolute HTTPS without credentials or a fragment;
82
- revision-floating `main`, `master`, and `latest` path segments are rejected.
83
- When supplied, `bytes` is a positive safe integer and `sha256` is exactly 64
84
- hexadecimal characters.
94
+ The canonical model descriptor is `{id, files:[{name?,url,bytes?,sha256?},...]}`.
95
+ The ordered `files` array is nonempty; names and URLs are unique. Each URL must
96
+ be absolute HTTPS without credentials or a fragment; revision-floating `main`,
97
+ `master`, and `latest` path segments are rejected. When supplied, `bytes` is a
98
+ positive safe integer and `sha256` is exactly 64 hexadecimal characters. The
99
+ legacy one-file `{id,url,bytes?,sha256?}` shape remains accepted and normalizes
100
+ to one ordered member.
101
+
102
+ Fetch follows HTTPS redirects and records the requested and final HTTPS URL in
103
+ the completion evidence. A redirect that leaves HTTPS is rejected. A declared
104
+ URL, revision-looking path, or recorded final URL is not a byte digest: only an
105
+ enabled byte-length or SHA-256 check establishes that corresponding integrity
106
+ fact. When a check is disabled, status reports it as `unchecked`.
85
107
 
86
108
  App, provider/model-binding, and load-operation options use the same
87
109
  plain-JavaScript shape:
@@ -115,8 +137,8 @@ and does not hash or reread a multi-gigabyte model solely to produce a digest.
115
137
  Only enabled checks fail closed. Regardless of optional integrity checks, a
116
138
  load succeeds only after Wllama reports that the model is loaded.
117
139
 
118
- The DBOPFS adapter commits an `arcane.ai.browser-wasm.model.v3` completion
119
- manifest with the observed byte length. Status reports the effective
140
+ The DBOPFS adapter commits an `arcane.ai.browser-wasm.model.v4` completion
141
+ manifest only after every ordered file succeeds. Status reports the effective
120
142
  `security.secure`, both effective check booleans, and per-check integrity
121
143
  outcomes. Overall integrity is `verified` when every enabled check succeeded,
122
144
  `pending` while an enabled check is running, and `unchecked` when neither check
@@ -133,41 +155,53 @@ console.log(integrity.state); // 'unchecked' or 'verified'
133
155
  console.log(integrity.byteLength.observed); // actual cached/downloaded bytes
134
156
  ```
135
157
 
136
- For compatibility, older descriptors can supply `immutableUrl` as the URL alias
137
- and `name` as a cache-filename hint. If both `url` and `immutableUrl` are
138
- present, they must match. Legacy `licenseSpdx` and `sourceRevision` properties
139
- are not canonical descriptor fields or runtime admission checks; applications
140
- remain responsible for model selection, provenance, and license compliance.
141
- Version-2 cache manifests can be migrated to version 3 when their model identity
142
- matches, without inventing an integrity result.
158
+ For one-file compatibility, older descriptors can supply `immutableUrl` as the
159
+ URL alias and `name` as a cache-filename hint. If both `url` and
160
+ `immutableUrl` are present, they must match. Legacy `licenseSpdx` and
161
+ `sourceRevision` properties are not canonical descriptor fields or runtime
162
+ admission checks; applications remain responsible for model selection,
163
+ provenance, and license compliance. Version-2/3 compatibility is internal; a
164
+ new successful completion is always recorded as version 4 without inventing an
165
+ integrity result.
143
166
 
144
167
  `localOnly:true` describes inference after load. It does not mean a cache miss
145
168
  cannot download. Source downloads use CORS, omit credentials and referrer,
146
169
  disable HTTP caching, and honor `AbortSignal`.
147
170
 
171
+ On Chrome, observing a lower-power Intel WebGPU adapter may open
172
+ `chrome://flags/#force-high-performance-gpu` in a new tab and show an alert
173
+ explaining that Chrome must be completely restarted after changing the flag.
174
+ The notice is advisory; it does not change browser settings, prove a faster
175
+ adapter exists, or make a failed load succeed.
176
+
148
177
  ## Streaming, cancellation, and tools
149
178
 
150
179
  ```javascript
151
- const abort = new AbortController();
152
- const stream = ai.llm.stream({
153
- localOnly:true,
154
- signal:abort.signal,
155
- messages:[{role:'user', content:'Summarize this text.'}],
156
- maxTokens:128
157
- });
158
-
159
- const cancelButton = document.querySelector('[data-cancel-local-ai]');
160
- const cancel = () => abort.abort('user cancelled');
161
- cancelButton.addEventListener('click', cancel, {once:true});
162
- try {
163
- for await (const chunk of stream) renderChunk(chunk);
164
- const completion = await stream.result;
165
- renderCompletion(completion);
166
- } catch (error) {
167
- if (error?.code !== 'ARCANE_AI_REQUEST_ABORTED') throw error;
168
- renderCancelled();
169
- } finally {
170
- cancelButton.removeEventListener('click', cancel);
180
+ async function streamLocalSummaryAfterUserChoice(cancelButton) {
181
+ // The selected browser-WASM model must already be ready.
182
+ if (!cancelButton?.addEventListener) {
183
+ throw new TypeError('A cancel button is required.');
184
+ }
185
+ const abort = new AbortController();
186
+ const stream = ai.llm.stream({
187
+ localOnly:true,
188
+ signal:abort.signal,
189
+ messages:[{role:'user', content:'Summarize this text.'}],
190
+ maxTokens:128
191
+ });
192
+
193
+ const cancel = () => abort.abort('user cancelled');
194
+ cancelButton.addEventListener('click', cancel, {once:true});
195
+ try {
196
+ for await (const chunk of stream) renderChunk(chunk);
197
+ const completion = await stream.result;
198
+ renderCompletion(completion);
199
+ } catch (error) {
200
+ if (error?.code !== 'ARCANE_AI_REQUEST_ABORTED') throw error;
201
+ renderCancelled();
202
+ } finally {
203
+ cancelButton.removeEventListener('click', cancel);
204
+ }
171
205
  }
172
206
  ```
173
207
 
@@ -176,8 +210,11 @@ serialized; provider status exposes `busy` and `queued`. Supported request
176
210
  generation fields include temperature, top-K, top-P, min-P, repeat penalty,
177
211
  maximum tokens, seed, and stop sequences. Load settings separately include
178
212
  `contextTokens`, `batchTokens`, `microBatchTokens`, `threads`, and GPU-layer
179
- count. WebGPU is optional; cross-origin isolation and hardware fields are
180
- observations, not readiness promises.
213
+ count. The shipped runtime always sets `gpuLayers: 99999`: WebGPU and proved
214
+ full offload are mandatory, and there is no CPU fallback. Secure context,
215
+ adapter selection, full-offload, logical-buffer, queue-submission, and
216
+ settled-fence evidence are load admission; cross-origin isolation and coarse
217
+ hardware fields remain observations.
181
218
 
182
219
  Tool definitions, tool choice, parallel-tool-call preference, and JSON or JSON
183
220
  Schema structured-output requests are passed to Wllama. Returned tool calls are
@@ -197,24 +234,41 @@ as unavailable.
197
234
  | --- | --- |
198
235
  | Source and download | `ARCANE_AI_MODEL_SOURCE_INVALID`, `ARCANE_AI_MODEL_SOURCE_UNAVAILABLE`, `ARCANE_AI_MODEL_DOWNLOAD_FAILED`, `ARCANE_AI_MODEL_REDIRECT_BLOCKED`, `ARCANE_AI_MODEL_SIZE_MISMATCH`, `ARCANE_AI_MODEL_DIGEST_MISMATCH` |
199
236
  | Cache and storage | `ARCANE_AI_MODEL_CACHE_REJECTED`, `ARCANE_AI_MODEL_OFFLINE_MISS`, `ARCANE_AI_STORAGE_UNAVAILABLE`, `ARCANE_AI_STORAGE_READ_FAILED`, `ARCANE_AI_STORAGE_DELETE_FAILED` |
200
- | Lifecycle | `ARCANE_AI_UNAVAILABLE`, `ARCANE_AI_NOT_READY`, `ARCANE_AI_LOAD_FAILED`, `ARCANE_AI_UNLOAD_FAILED`, `ARCANE_AI_DISPOSE_FAILED`, `ARCANE_AI_DISPOSED`, `ARCANE_AI_OPERATION_SUPERSEDED`, `ARCANE_AI_SECURITY_RELOAD_REQUIRED` |
201
- | Requests | `ARCANE_AI_REQUEST_ABORTED`, `ARCANE_AI_REQUEST_FAILED`, `ARCANE_AI_INVALID_PROVIDER_RESULT`, `ARCANE_AI_LOCAL_ONLY_UNAVAILABLE`, `ARCANE_AI_ADAPTER_PROTOCOL_MISMATCH` |
237
+ | Lifecycle | `ARCANE_AI_UNAVAILABLE`, `ARCANE_AI_NOT_READY`, `ARCANE_AI_MODEL_NOT_READY`, `ARCANE_AI_LOAD_FAILED`, `ARCANE_AI_UNLOAD_FAILED`, `ARCANE_AI_DISPOSE_FAILED`, `ARCANE_AI_DISPOSED`, `ARCANE_AI_OPERATION_SUPERSEDED`, `ARCANE_AI_SECURITY_RELOAD_REQUIRED` |
238
+ | Requests | `ARCANE_AI_REQUEST_ABORTED`, `ARCANE_AI_REQUEST_FAILED`, `ARCANE_AI_RUNTIME_BUSY`, `ARCANE_AI_INVALID_PROVIDER_RESULT`, `ARCANE_AI_LOCAL_ONLY_UNAVAILABLE`, `ARCANE_AI_ADAPTER_PROTOCOL_MISMATCH` |
239
+ | Provider/2 adapter | `ARCANE_AI_MODEL_AUTHORITY_REQUIRED`, `ARCANE_AI_PROVIDER_ROLE_MISMATCH`, `ARCANE_AI_PROVIDER_PROGRESS_INVALID`, `ARCANE_AI_PROVIDER_STATUS_INVALID`, `ARCANE_AI_PROVIDER_OPERATION_UNAVAILABLE` |
240
+ | WebGPU and model admission | `ARCANE_AI_WEBGPU_REQUIRED`, `ARCANE_AI_WEBGPU_API_UNAVAILABLE`, `ARCANE_AI_WEBGPU_EVIDENCE_INVALID`, `ARCANE_AI_MODEL_FULL_OFFLOAD_UNPROVEN`, `ARCANE_AI_MODEL_WEBGPU_REQUIREMENT_FAILED`, `ARCANE_AI_MODEL_GPU_MEMORY_INSUFFICIENT`, `ARCANE_AI_MODEL_SHARD_TOO_LARGE`, `ARCANE_AI_MODEL_RELOAD_REQUIRED`, `ARCANE_AI_LOAD_PLAN_RELOAD_REQUIRED` |
241
+ | Worker cleanup and recovery | `ARCANE_AI_WORKER_TERMINATION_UNCONFIRMED`, `ARCANE_AI_COMPLETION_RECOVERY_UNCONFIRMED` |
202
242
  | Diagnostics | `ARCANE_AI_PROBE_FAILED` |
203
243
 
244
+ Capability and status records also carry stable reason codes. These observations
245
+ are not all thrown errors: positive and unknown states let an application
246
+ explain why load is available, blocked, or not yet measured without guessing.
247
+
248
+ | Observation | Status/reason codes |
249
+ | --- | --- |
250
+ | Browser prerequisites | `ARCANE_AI_WEBASSEMBLY_UNAVAILABLE`, `ARCANE_AI_OPFS_UNAVAILABLE`, `ARCANE_AI_SECURE_CONTEXT_REQUIRED` |
251
+ | Model/storage sizing | `ARCANE_AI_MODEL_STORAGE_REQUIREMENT_UNBOUNDED`, `ARCANE_AI_MODEL_STORAGE_REQUIREMENT_UNKNOWN`, `ARCANE_AI_STORAGE_ESTIMATE_UNAVAILABLE`, `ARCANE_AI_STORAGE_ESTIMATE_FAILED`, `ARCANE_AI_STORAGE_ESTIMATE_INVALID`, `ARCANE_AI_STORAGE_NOT_MEASURED`, `ARCANE_AI_STORAGE_CAPACITY_INSUFFICIENT` |
252
+ | Positive cache/storage state | `ARCANE_AI_MODEL_CACHE_COMPLETE`, `ARCANE_AI_STORAGE_CAPACITY_AVAILABLE` |
253
+ | WebGPU execution evidence | `ARCANE_AI_WEBGPU_EXECUTION_OBSERVED`, `ARCANE_AI_WEBGPU_EXECUTION_UNOBSERVED` |
254
+ | Provider and runtime failure state | `ARCANE_AI_PROVIDER_UNAVAILABLE`, `ARCANE_AI_RUNTIME_FAILED` |
255
+
204
256
  `capabilities()` reports browser observations such as WebAssembly, OPFS,
205
- WebGPU, secure context, cross-origin isolation, and hardware concurrency.
206
- Feature-detect them. The authoritative Chrome behavior passes without
207
- cross-origin isolation, so that flag is not a hard gate. `probe()` exercises
208
- packaged Wllama backend operations only while unloaded; it neither admits nor
209
- downloads a model.
257
+ WebGPU API presence, admitted WebGPU operation, secure context, cross-origin
258
+ isolation, and hardware concurrency. `navigator.gpu` alone is not operational
259
+ evidence. The authoritative runtime can operate without cross-origin isolation,
260
+ so that flag is not a hard gate; secure context and WebGPU/full-offload evidence
261
+ are. `probe()` exercises packaged Wllama backend operations only while unloaded;
262
+ it neither admits nor downloads a model.
210
263
 
211
264
  ## BROWSER_WASM_RUNTIME_AUTHORITY
212
265
 
213
266
  ### Overview
214
267
 
215
268
  Deep-frozen identity for the shipped browser runtime. Its protocol is
216
- `arcane-ai-browser-wasm/2`; the provider adapter uses
217
- `arcane-ai-adapter/1`. It records Wllama `3.6.0`, the embedded llama.cpp
269
+ `arcane-ai-browser-wasm/2`; the direct provider uses
270
+ `arcane-ai-adapter/1` and `adaptV1LlmProvider()` projects it into
271
+ `arcane-ai-provider/2`. It records Wllama `3.6.0`, the embedded llama.cpp
218
272
  revision, authenticated module/WASM byte lengths and SHA-256 values, licenses,
219
273
  and the disabled compatibility-runtime and remote-model-helper policy.
220
274
 
@@ -255,12 +309,18 @@ createArcaneAI({ llm=null, provider=null, loadPolicy='on-demand', security }={})
255
309
 
256
310
  At least one `llm` or `provider` is required; when both are supplied, `llm`
257
311
  takes precedence. `loadPolicy` is `on-demand` or `manual`. The frozen result
258
- contains `llm`, `runtime`, `status`, `load`,
312
+ contains `llm`, `runtime`, `createChatSession`, `status`, `load`,
259
313
  `unload`, `probe`, `fetchRequest`, `streamRequest`, and `dispose`.
260
314
  `security` is the app-level security configuration inherited by provider loads.
261
315
  The SDK default is `secure:false`; `ai.load({security})` can override inherited
262
316
  fields for that operation.
263
317
 
318
+ When `llm` is an existing `ModelController`, that controller keeps the security
319
+ and load policy with which it was created. This function does not reapply its
320
+ `loadPolicy` argument in that case, and supplying `security` alongside the
321
+ existing controller throws `TypeError`. Passing a provider instead creates a
322
+ new controller with the requested policy and security.
323
+
264
324
  ### Availability and normalization
265
325
 
266
326
  **Browser.** It normalizes provider lifecycle and request observation without
@@ -271,17 +331,30 @@ creating a fallback provider.
271
331
 
272
332
  ```javascript
273
333
  const ai = createArcaneAI({provider, loadPolicy:'manual'});
274
- const off = ai.llm.on('statechange', event => renderStatus(event.detail));
275
- await ai.load({offline:true});
276
- off();
334
+ async function loadCachedModelAfterUserChoice() {
335
+ const off = ai.llm.on('statechange', event => renderStatus(event.detail));
336
+ try {
337
+ return await ai.load({offline:true});
338
+ } finally {
339
+ off();
340
+ }
341
+ }
277
342
  ```
278
343
 
344
+ `await ai.createChatSession(options)` dynamically creates a
345
+ [`PersistentAIChatSession`](../runtime-modules.md#persistentaichatsessionjs)
346
+ whose `chat` function is permanently bound to this controller. `options` must
347
+ be a plain object and must not contain `chat`; this prevents a session from
348
+ claiming the controller's lifecycle while sending its turns through another
349
+ provider.
350
+
279
351
  ## createBrowserModelSource()
280
352
 
281
353
  ### Overview
282
354
 
283
- Validates a caller-owned canonical `{id, url, bytes?, sha256?}` descriptor and
284
- creates the one cancellable HTTPS download source accepted by this provider.
355
+ Validates a caller-owned canonical ordered multi-file descriptor and creates
356
+ the cancellable HTTPS download source accepted by this provider. A legacy
357
+ one-file descriptor normalizes to one ordered member.
285
358
 
286
359
  ### Signature and result
287
360
 
@@ -290,8 +363,10 @@ createBrowserModelSource(descriptor, { fetchImpl=null }={})
290
363
  ```
291
364
 
292
365
  The frozen source includes `kind`, the canonical descriptor fields,
293
- `descriptor`, and `open({signal})`. `open()` returns a readable response body,
294
- requested/final URLs, and `cancel()`; it does not admit bytes to the cache.
366
+ `descriptor`, and `open(memberIndex,{signal})`. `open()` requires a member
367
+ index for multi-file sources and returns a readable response body,
368
+ requested/final URLs, reported byte length, and `cancel()`; it does not admit
369
+ bytes to the cache.
295
370
 
296
371
  ### Availability and normalization
297
372
 
@@ -303,7 +378,7 @@ enabled for load.
303
378
 
304
379
  ```javascript
305
380
  const source = createBrowserModelSource(MODEL);
306
- console.log(source.id, source.bytes, source.sha256);
381
+ console.log(source.id, source.files);
307
382
  ```
308
383
 
309
384
  ## createBrowserWasmLlmProvider()
@@ -316,12 +391,16 @@ created by this module. Structural lookalikes are rejected.
316
391
  ### Signature and result
317
392
 
318
393
  ```text
319
- createBrowserWasmLlmProvider({ source, store, loadDefaults={}, security, logger=console }={})
394
+ createBrowserWasmLlmProvider({ source, sources, store, loadDefaults={}, security, logger=console }={})
320
395
  ```
321
396
 
322
- The frozen result exposes protocol and provider identity, model metadata,
323
- `capabilities`, `status`, `load`, `unload`, `chat`, `stream`, `streamChat`,
324
- `use`, `probe`, and `dispose`. Direct provider `load()` returns `{model,status}`;
397
+ `sources` is a nonempty array of unique SDK-created model sources. Optional
398
+ legacy `source` identifies the default and must be one member of `sources`; when
399
+ `sources` is omitted, `source` supplies the one-model catalog. The frozen
400
+ result exposes protocol and provider identity, default model metadata,
401
+ `catalog`, `capabilities`, `status`, `load`, `unload`, `chat`, `stream`,
402
+ `streamChat`, `use`, `probe`, and `dispose`. Direct provider `load()` selects a
403
+ catalog model and returns `{model,status}`;
325
404
  the facade `ai.load()` returns the flat controller status.
326
405
  Provider `security` supplies the provider/model-binding scope. Direct
327
406
  `provider.load({security})` and facade `ai.load({security})` supply the
@@ -329,18 +408,20 @@ operation scope.
329
408
 
330
409
  ### Availability and normalization
331
410
 
332
- **Browser with WebAssembly and DBOPFS model bytes.** Inference is local after a
333
- successful Wllama load. WebGPU is optional and defaults to zero GPU layers
334
- unless the caller selects otherwise. Status discloses effective checks and
335
- whether enabled integrity checks are pending, unchecked, or verified.
411
+ **Browser secure context with WebAssembly, OPFS/DBOPFS, WebGPU, and admitted
412
+ full-offload evidence.** Inference is local after a successful Wllama load.
413
+ The runtime forces `gpuLayers: 99999`; callers cannot select CPU or partial
414
+ offload. Status discloses effective checks, capability policy, storage/model
415
+ compatibility, and whether enabled integrity checks are pending, unchecked, or
416
+ verified.
336
417
 
337
418
  ### Example
338
419
 
339
420
  ```javascript
340
421
  const provider = createBrowserWasmLlmProvider({
341
- source,
422
+ sources:[source],
342
423
  store,
343
- loadDefaults:{threads:1, contextTokens:4096, gpuLayers:0}
424
+ loadDefaults:{threads:1, contextTokens:4096}
344
425
  });
345
426
  console.log(provider.status().state); // unloaded
346
427
  ```
@@ -356,14 +437,18 @@ completion-manifest behavior.
356
437
  ### Signature and result
357
438
 
358
439
  ```text
359
- createDbopfsModelStore({ dbopfs, tableName='arcane_ai_browser_models' }={})
440
+ createDbopfsModelStore({ dbopfs, tableName='arcane_ai_browser_models', estimateStorage=null }={})
360
441
  ```
361
442
 
362
- The frozen result contains `kind`, `tableName`, the original `adapter`, and
363
- `ready`, `openVerified`, `install`, `ensure`, and `remove`. `ensure()` returns a
364
- file, completion manifest, observed byte count, integrity detail, and cache state
365
- `cached` or `installed`. `openVerified()` remains a compatibility helper that
366
- requires both byte-length and SHA-256 verification.
443
+ The optional `estimateStorage()` function supplies bounded storage evidence
444
+ when the browser's default estimator is unavailable or an application owns a
445
+ more precise quota view. The frozen result contains `kind`, `tableName`, the
446
+ original `adapter`, and `ready`, `openVerified`, `install`, `ensure`, and
447
+ `remove`. `ensure()` returns
448
+ `{files,file,manifest,observedBytes,integrity,cache}`: `files` preserves the
449
+ ordered model set, while the one-file compatibility field `file` is that sole
450
+ member or `null`. `cache` is `cached` or `installed`. `openVerified()` remains
451
+ a compatibility helper that requires both byte-length and SHA-256 verification.
367
452
 
368
453
  ### Availability and normalization
369
454
 
@@ -376,15 +461,52 @@ model license rights.
376
461
 
377
462
  ```javascript
378
463
  const store = createDbopfsModelStore({dbopfs});
379
- await store.ready();
380
- const cached = await store.openVerified(source);
381
- console.log(cached ? 'verified cache' : 'cache miss');
464
+ async function verifyCachedModelAfterUserChoice() {
465
+ await store.ready();
466
+ const cached = await store.openVerified(source);
467
+ console.log(cached ? 'verified cache' : 'cache miss');
468
+ }
469
+ ```
470
+
471
+ ## adaptV1LlmProvider()
472
+
473
+ ### Overview
474
+
475
+ Projects one compatible admitted v1 browser-WASM provider into the same
476
+ provider/2 LLM role used by `AIProviderRuntime.js`. Admission checks the v1
477
+ protocol, required identity/methods, and local-only capability; it does not
478
+ establish SDK provenance for an arbitrary compatible object. The adapter does
479
+ not change the wrapped provider, download a model, execute a tool, or create a
480
+ fallback.
481
+
482
+ ### Signature and result
483
+
484
+ ```text
485
+ adaptV1LlmProvider(provider)
486
+ ```
487
+
488
+ The frozen result exposes `{protocol:'arcane-ai-provider/2',role:'llm',id,
489
+ localOnly:true,catalog,inspect,status,load,request,unload,dispose}`. Inspection
490
+ returns `arcane-ai-model-authority/1` only for an exact catalog selection.
491
+ `request()` admits only `chat` and `stream` and preserves structural tool data.
492
+
493
+ ### Example
494
+
495
+ ```javascript
496
+ import {adaptV1LlmProvider} from 'arcane-os/ai/browser-wasm';
497
+ import {getAIProviderRuntime} from 'arcane/AIProviderRuntime';
498
+
499
+ const runtime = getAIProviderRuntime();
500
+ const release = runtime.register(adaptV1LlmProvider(provider));
501
+ // Configure an exact llm route before load/use. Release registration at teardown.
502
+ release();
382
503
  ```
383
504
 
384
505
  ## Related reference
385
506
 
386
507
  - [Canonical `createArcaneAI()` entry](../sdk-api.md#createarcaneai) and the
387
508
  sibling [`BROWSER_WASM_RUNTIME_AUTHORITY`](../sdk-api.md#browserwasmruntimeauthority),
509
+ [`adaptV1LlmProvider()`](../sdk-api.md#adaptv1llmprovider),
388
510
  [`createBrowserModelSource()`](../sdk-api.md#createbrowsermodelsource),
389
511
  [`createBrowserWasmLlmProvider()`](../sdk-api.md#createbrowserwasmllmprovider),
390
512
  and [`createDbopfsModelStore()`](../sdk-api.md#createdbopfsmodelstore) entries
@@ -392,3 +514,6 @@ console.log(cached ? 'verified cache' : 'cache miss');
392
514
  - [Authenticated browser runtime delivery](../protocols.md#browser-runtime-delivery)
393
515
  - [Browser-WASM behavior evidence](../behavioral-testing.md#behavioral-coverage-model)
394
516
  - [DBOPFS runtime module](../runtime-modules.md#dbopfsjs)
517
+ - [Browser speech providers](browser-speech.md)
518
+ - [Provider-neutral AI runtime](../runtime-modules.md#aiproviderruntimejs)
519
+ - [Persistent chat](../runtime-modules.md#persistentaichatsessionjs)
@@ -11,7 +11,7 @@ it runs; the [protocol guide](protocols.md) contains the implementation detail.
11
11
  | **Browser** | Uses standard browser APIs and can run without a native host when its own dependencies are available. |
12
12
  | **Native** | Requires an admitted `globalThis.Arcane` host method or a native target provider. |
13
13
  | **Cloud** | Calls a remote provider over HTTPS and needs provider configuration and network policy. |
14
- | **Cross-host** | Keeps one application API while Arcane selects WebView2, WebKitGTK, Android WebView, or development HTTP transport. |
14
+ | **Cross-host** | Keeps one application contract usable across supported hosts. Execution may stay in-process, use a registered provider, or cross a documented Arcane WebView2, WebKitGTK, Android WebView, or development HTTP transport. |
15
15
  | **Provider-native** | Intentionally returns the underlying provider's bounded envelope instead of an Arcane-normalized entity. |
16
16
 
17
17
  “Available” never means “authorized.” App grants, method allowlists, host
@@ -29,7 +29,11 @@ version; WebKitGTK availability must not be generalized to macOS.
29
29
  | Scaffold, inspect, test, package, bundle, build, verify, or run an app | `arcane` CLI or `arcane-os` package functions | **Node**; native targets invoke one explicit provider | CLI events and SDK errors/results are normalized by versioned SDK contracts. Native artifact receipts remain target-specific inside a common receipt lifecycle. |
30
30
  | Publish application events or review a bounded event history | `arcane-os/event-manager` | **Node** and **Browser**; optional DOM capture needs a browser DOM or compatible host | Live listeners receive original arguments. Recorded payloads and metadata become bounded, redacted, deeply frozen `arcane-event-stack/1` snapshots. The stack format is local diagnostic data, not a host transport. |
31
31
  | Build browser UI and app-local behavior | `/arcane/modules/*.js`, shared entities, and components | **Browser**; many modules also run inside every native renderer | Pure modules own their result contracts. Modules that call `Arcane` inherit the bridge boundary described below. |
32
- | Run a caller-selected local LLM entirely in a browser renderer | `arcane-os/ai/browser-wasm` through `createArcaneAI()` | **Browser** only; WebAssembly and OPFS/DBOPFS are required, WebGPU is optional, and a cache miss uses the caller's HTTPS model URL unless `offline:true` | The facade normalizes lifecycle, status, security precedence, effective-check disclosure, streaming, cancellation, and structural tool-call visibility. The canonical model descriptor is `{id, url, bytes?, sha256?}`; license is application provenance policy, not a runtime admission check. |
32
+ | Select and observe independent LLM/STT/TTS roles | `/arcane/modules/AIProviderRuntime.js` and `AIRuntimeState.js` | **Cross-host** controller/state; registered providers retain their own host requirements | Required/projected provider members, closed route/configuration records, and validated authority/status fields; per-role lifecycle, cancellation, stream cleanup, sticky state, and startup barriers are normalized. `localOnly` fails closed and creates no fallback. |
33
+ | Run a caller-selected local LLM entirely in a browser renderer | `arcane-os/ai/browser-wasm` through `createArcaneAI()` | **Browser** only; secure context, WebAssembly, OPFS/DBOPFS, WebGPU, requested full offload, and admitted adapter/buffer/queue/fence evidence are required; no CPU fallback | The facade normalizes multi-model lifecycle, status, security precedence, effective-check disclosure, streaming, cancellation, and structural tool-call visibility. Model sources are canonical ordered file descriptors; licenses and model choice remain application policy. |
34
+ | Run caller-selected Whisper or Kokoro in a browser renderer | `arcane-os/ai/browser-speech` registered with `AIProviderRuntime` | **Browser** only; DBOPFS, Web Locks, Workers, Fetch/object URLs, and a caller-supplied self-contained runtime/model closure are required | STT/TTS use independent provider/2 lifecycle and status. Immutable artifact authority, manifest-last cache, strict offline admission, cancellation, Worker teardown, and request/result shapes are normalized. No runtime/model bytes or cloud fallback are supplied. |
35
+ | Preserve bounded chat history and memory | `/arcane/modules/PersistentAIChatSession.js` | **Browser / native WebView** with ChatEntity/DBOPFS and a configured chat function | Existing DBOPFS names and memory semantics are preserved. Live-context commit is atomic; durable persistence is explicit and coherent across user/assistant/tool turns. |
36
+ | Search an app-owned document corpus for explicit chat context | `/arcane/modules/DBOPFSDocumentLibrary.js` | **Browser** or compatible injected DBOPFS adapter | Generation/manifest completion, bounded lexical search, partial read failures, and untrusted context labels are normalized. Construction does not search; an explicitly wired context builder performs bounded retrieval for each prepared chat send. |
33
37
  | Read host identity, capabilities, storage, preferences, appearance, or platform state | `globalThis.Arcane` | **Cross-host** where the method is implemented and admitted | Promise behavior and `Arcane.Error` are normalized. Result fields are normalized unless the method explicitly documents a platform-dependent snapshot. |
34
38
  | Use local AI without coupling app code to Ollama HTTP | `Arcane.localAI`, `Arcane.ai`, or `/arcane/modules/Ollama.js` | Primarily **Native**; Android exposes a narrower admitted inference projection | Admission, errors, and managed-operation events are normalized. Direct Ollama response envelopes remain **Provider-native**. |
35
39
  | Use OpenAI from the renderer profile | `/arcane/modules/AI.js` | **Cloud** from an allowed browser/native renderer | High-level AI chat/text behavior is normalized by the module; raw provider diagnostics and some response detail remain provider-specific. No automatic cloud fallback is inferred from local failure. |
@@ -81,12 +85,19 @@ state. See [EventManager and time-travel review](event-manager.md).
81
85
  ### Browser-local provider adapter
82
86
 
83
87
  [`arcane-os/ai/browser-wasm`](ai/browser-wasm.md) exposes the same
84
- `arcane-ai-adapter/1` LLM lifecycle used by `createArcaneAI()`, while its
85
- packaged Wllama engine and caller-supplied model run inside the browser. This
88
+ provider-neutral lifecycle used by `createArcaneAI()`, while its packaged
89
+ Wllama engine and caller-supplied model run inside the browser. This
86
90
  surface does not require an Arcane Core method grant because it does not call a
87
91
  Core host. Browser Fetch, CORS, storage policy, secure-context behavior, and
88
92
  resource limits still apply.
89
93
 
94
+ The shipped `0.2.2` runtime requires WebGPU and has no CPU fallback. A successful
95
+ load requests full GPU offload (`gpuLayers: 99999`) and admits actual adapter,
96
+ full-offload, buffer, queue, and settled-fence evidence. `navigator.gpu`
97
+ presence by itself is not readiness. The provider emits the instrumented
98
+ `arcane.ai.browser-wasm.webgpu.adapter.selected` capability event only after
99
+ admitted adapter selection evidence.
100
+
90
101
  `localOnly:true` describes inference after load; it does not promise that load
91
102
  is offline. A normal cache miss downloads from the exact caller-supplied HTTPS
92
103
  URL. App, provider/model-binding, and load-operation options use
@@ -108,6 +119,24 @@ cache entry and otherwise rejects with `ARCANE_AI_MODEL_OFFLINE_MISS`. Tool
108
119
  calls are result data for application review and dispatch; the SDK never
109
120
  executes them.
110
121
 
122
+ [`arcane-os/ai/browser-speech`](ai/browser-speech.md) implements the sibling
123
+ `stt` and `tts` provider/2 roles. Each caller-authenticated Whisper or Kokoro
124
+ provider has its own load, use, cancellation, unload, dispose, cache, Worker,
125
+ status, and error state. The SDK supplies neither speech adapter runtime bytes
126
+ nor model/voice bytes; every immutable file is application-owned and admitted
127
+ through an SDK-created authority and DBOPFS artifact store.
128
+
129
+ The projected [`AIProviderRuntime`](runtime-modules.md#aiproviderruntimejs)
130
+ normalizes those browser providers and can admit an externally supplied native
131
+ or cloud provider/2 adapter. `AI.js` also supplies compatibility adapters for
132
+ an already-selected legacy OpenAI route, Ollama route, or admitted Core speech
133
+ route. SDK `0.2.2` publishes no privileged Core implementation, credential,
134
+ model, or speech-runtime authority, and those adapters never probe, select,
135
+ download, or fall back. The sticky
136
+ [`AIRuntimeState`](runtime-modules.md#airuntimestatejs) surface keeps
137
+ application UI independent of transport. A selected route remains explicit:
138
+ browser failure is not permission to invoke Core or cloud.
139
+
111
140
  ### Arcane bridge-normalized
112
141
 
113
142
  Core-backed calls return promises and reject with `Arcane.Error`. Transport
@@ -37,7 +37,10 @@ release acceptance.
37
37
  | EventManager and event stacks | Live pub/sub ordering and payload identity, nested causation, immutable/redacted snapshots, strict import, bounded overflow, seek, all playback modes/lifecycle outcomes, cancellation, and DOM start/stop/privacy behavior. | Real user journeys and browser layout belong in a browser harness; event-stack review never proves that external side effects can be replayed. |
38
38
  | CLI | Commands parse, acknowledge, select one scope, produce normalized human/JSON/NDJSON output, propagate cancellation/failure, and reject invalid cardinality. | Native build/run requires the selected real provider and host. |
39
39
  | Browser runtime modules | Every shipped ESM module parses and its export inventory matches the catalog; pure helpers run focused success/error cases. | DOM, OPFS, media, and Web Component journeys use a browser harness. |
40
- | Browser-WASM local AI | The exact exported namespace, canonical `{id, url, bytes?, sha256?}` descriptor, fieldwise app/provider/load security precedence, default-unchecked and secure-check paths, observed-byte persistence, honest status, provider/facade lifecycle, lazy/manual policy, successful Wllama-load requirement, abort normalization, and structural-only tool behavior run with bounded deterministic providers. | The publication gate installs the packed SDK into a real Chrome app, loads the authenticated Wllama 3.6.0 JS/WASM assets, configures `secure:true`, performs a cold exact-length/SHA-256 model install, real inference, in-flight cancellation, unload, and a verified offline reload with zero model requests. It is an explicit heavyweight capability gate, not an implicit model download in every local `npm run check`. |
40
+ | Provider-neutral AI runtime and chat/speech activation | Provider/2 registration, closed three-role configuration, opt-in STT startup, legacy Cloud/Core speech readiness, independent LLM/STT/TTS load/unload/status, latest-request-wins settlement, owned STT signals, TTS mute lifecycle, route-owned voice defaults, sticky-state-only readiness for both speech components, shared selected-unloaded activation request/cancellation/error behavior, fail-closed programmatic voice recording, public transcript-replacement supersession of late transcribe/save/complete settlement, `AI.fetchSTT` callback-position compatibility, stale-callback suppression, and absence of silent provider fallback are exercised against bounded providers and host callbacks. | Real model/runtime admission remains the selected provider's evidence boundary; provider-promise settlement, state, an abort signal, or an activation event does not by itself prove underlying provider work stopped or native, cloud, or browser-model availability. |
41
+ | Browser-WASM local AI | The exact exported namespace, canonical ordered `{id, files:[{name?,url,bytes?,sha256?},...]}` descriptor plus its one-file compatibility input, fieldwise app/provider/load security precedence, default-unchecked and secure-check paths, observed-byte persistence, honest capability/status reasons, provider/facade lifecycle, lazy/manual policy, successful Wllama-load requirement, abort normalization, and structural-only tool behavior run with bounded deterministic providers. | The publication gate installs the packed SDK into a real Chrome app, loads the authenticated Wllama 3.6.0 JS/WASM assets, configures `secure:true`, performs a cold exact-length/SHA-256 model install, real inference, in-flight cancellation, unload, and a verified offline reload with zero model requests. It is an explicit heavyweight capability gate, not an implicit model download in every local `npm run check`. |
42
+ | Browser speech | Caller-owned Whisper/Kokoro authority, closed runtime graph, app/provider/load security precedence, manifest-last DBOPFS cache, strict offline admission, independent Worker lifecycle, pre-Worker versus in-Worker cancellation, Blob/File STT conversion, WAV TTS conversion, and no cloud fallback are exercised with bounded synthetic artifacts and adapters. | A real runtime/model/voice download and actual transcription or synthesis require the application's admitted immutable artifacts, licenses, browser media support, and explicit user action. |
43
+ | Persistent chat and document context | Atomic in-memory/history commit, explicit per-turn persistence, single structural tool-call sequencing, bounded bootstrap/search/context, caller-source `evaluate()` budgets, cancellation, and reject versus `preserve-readable` partial coverage are exercised with app-scoped adapters. | Live Core/provider inference and durable browser storage remain separate authorities; tests never treat a fake chat function or in-memory adapter as host/storage proof. |
41
44
  | Core bridge docs | Canonical namespace/method/event/entity inventories match their one-per-member guides and required sections. | Live Core conformance belongs in Arcane OS because Core implementation is not shipped as SDK source. |
42
45
  | Arcane Ollama wrapper | Missing-host error, method forwarding, text/readiness normalization, unload request, and stream-option forwarding run against a deterministic fake `Arcane.ollama`. | Real managed-service, model download/create, GPU/resource admission, and service restart require an admitted Arcane host. |
43
46
  | Native providers | Plan/provider protocol, explicit target, receipt authentication, artifact reader, and unavailable-path honesty are tested with bounded fixtures. | Exact Windows, Linux, or Android artifact verification and launch must run on that actual platform/architecture. |