arcane-os 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +35 -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 +331 -25
  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 +30 -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 +19 -6
  15. package/docs/reference/inventory/runtime-modules.json +113 -9
  16. package/docs/reference/protocols.md +260 -38
  17. package/docs/reference/runtime-components.md +108 -15
  18. package/docs/reference/runtime-modules.md +422 -8
  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 +288 -52
  23. package/runtime/arcane/components/speech.html +339 -15
  24. package/runtime/arcane/modules/AI.js +1245 -136
  25. package/runtime/arcane/modules/AIProviderRuntime.js +299 -30
  26. package/runtime/arcane/modules/AIRuntimeState.js +23 -4
  27. package/runtime/arcane/modules/ConfiguredAIChatSession.js +93 -8
  28. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +448 -24
  29. package/runtime/arcane/modules/LocalAIReadinessController.js +1 -1
  30. package/schemas/arcane-lock.schema.json +6 -4
  31. package/src/dev-server.mjs +29 -13
  32. package/src/doctor.mjs +1 -3
  33. package/src/import-map.mjs +134 -83
  34. package/src/packager/core.mjs +311 -39
  35. package/src/scaffold.mjs +45 -17
  36. package/src/templates/workspace-template.mjs +23 -4
  37. package/src/toolchain.mjs +10 -2
  38. package/src/workspace.mjs +177 -24
@@ -2,9 +2,12 @@
2
2
 
3
3
  The npm package exposes a Node.js ESM control plane, the Node-and-browser
4
4
  `arcane-os/event-manager` entrypoint, and the browser-only
5
- `arcane-os/ai/browser-wasm` entrypoint. Application code otherwise uses named
6
- modules from the managed browser map, such as `arcane/ThemeBootstrap`, and
7
- calls `globalThis.Arcane` for capability-gated host behavior.
5
+ `arcane-os/ai/browser-wasm` and `arcane-os/ai/browser-speech` entrypoints.
6
+ Those package subpaths are distinct from application-facing projection modules
7
+ in the managed browser map, such as `arcane/AIProviderRuntime`,
8
+ `arcane/AIRuntimeState`, and `arcane/ThemeBootstrap`. Applications use those
9
+ mapped runtime modules and call `globalThis.Arcane` for capability-gated host
10
+ behavior; they are not additional `package.json#exports` entrypoints.
8
11
 
9
12
  This page is the canonical inventory for every JavaScript name reachable through `package.json#exports`. The same binding can appear at the root and a focused subpath; those entrypoints are listed together. The root workspace `discoverApps` and the low-level packager `discoverApps` are intentionally separate records because they are different functions.
10
13
 
@@ -14,7 +17,7 @@ This table is the Node `package.json#exports` map: it defines package
14
17
  entrypoints for SDK/tooling code. It is distinct from the generated browser
15
18
  import map that resolves application-facing `arcane/*` modules and the focused
16
19
  EventManager entry. See [browser runtime delivery](protocols.md#browser-runtime-delivery)
17
- for that 86-entry physical-runtime contract.
20
+ for that 91-entry physical-runtime contract in SDK `0.2.1`.
18
21
 
19
22
  | Specifier | Purpose |
20
23
  | --- | --- |
@@ -30,6 +33,7 @@ for that 86-entry physical-runtime contract.
30
33
  | `arcane-os/release-bundle` | Deterministic external release bundles. |
31
34
  | `arcane-os/event-manager` | Central synchronous events, bounded time-travel history, playback, and optional DOM instrumentation. |
32
35
  | `arcane-os/ai/browser-wasm` | Caller-selected browser-local Wllama inference, configurable DBOPFS model checks, streaming, cancellation, and structural tool-call results. |
36
+ | `arcane-os/ai/browser-speech` | Caller-selected browser-local Whisper STT and Kokoro TTS provider mechanisms, authenticated DBOPFS artifacts, Workers, and cancellation; no model or adapter bytes. |
33
37
 
34
38
  JSON schemas, the runtime manifest, and `package.json` are data-only export subpaths. In Node ESM, import JSON with `with {type: 'json'}`, or resolve and read it explicitly.
35
39
 
@@ -43,6 +47,12 @@ Protocol mechanics are intentionally kept in the [deep protocol guide](protocols
43
47
 
44
48
  ## Canonical member inventory
45
49
 
50
+ SDK `0.2.1` exports exactly 169 distinct JavaScript members across 13
51
+ JavaScript entrypoints. The complete export map has 23 subpaths: the other 10
52
+ are the runtime manifest, eight JSON Schemas, and package metadata. Runtime
53
+ projection modules in the managed browser map are cataloged separately in
54
+ [Runtime modules](runtime-modules.md).
55
+
46
56
  | Member | Kind | Import | Group | Availability |
47
57
  | --- | --- | --- | --- | --- |
48
58
  | `APP_BUNDLE_DESCRIPTOR_NAME` | constant | `arcane-os` | Packaging and release bundles | Node |
@@ -66,6 +76,7 @@ Protocol mechanics are intentionally kept in the [deep protocol guide](protocols
66
76
  | `ARCANE_UPSTREAM_COMMIT` | constant | `arcane-os` | Identity and protocol constants | Node |
67
77
  | `ARCANE_UPSTREAM_REPOSITORY` | constant | `arcane-os` | Identity and protocol constants | Node |
68
78
  | `ArcaneError` | class | `arcane-os` | Errors | Node |
79
+ | `adaptV1LlmProvider()` | function | `arcane-os/ai/browser-wasm` | Browser-WASM local AI | Browser; the wrapped provider retains its own WebGPU, storage, model, and lifecycle requirements |
69
80
  | `assertIntegratedNativeToolchain()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
70
81
  | `assertIntegratedPortableToolchain()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
71
82
  | `assertNativeApplicationToolchainCompatibility()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
@@ -79,6 +90,7 @@ Protocol mechanics are intentionally kept in the [deep protocol guide](protocols
79
90
  | `authenticateSharedPayloadSnapshot()` | function | `arcane-os` | Packaging and release bundles | Node |
80
91
  | `buildApplication()` | function | `arcane-os` | Headless toolchain operations | Node; selected operation may produce browser or native output |
81
92
  | `buildTarget()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
93
+ | `BROWSER_SPEECH_ARTIFACT_PROTOCOL` | constant | `arcane-os/ai/browser-speech` | Browser speech providers | Browser metadata; model/runtime use requires DBOPFS, Web Locks, Workers, and caller-supplied immutable artifacts |
82
94
  | `BROWSER_WASM_RUNTIME_AUTHORITY` | constant | `arcane-os/ai/browser-wasm` | Browser-WASM local AI | Browser metadata; no model or DBOPFS required to inspect |
83
95
  | `bumpVersion()` | function | `arcane-os/packager` | Packaging and release bundles | Node |
84
96
  | `bundleApplication()` | function | `arcane-os` | Headless toolchain operations | Node; selected operation may produce browser or native output |
@@ -88,10 +100,14 @@ Protocol mechanics are intentionally kept in the [deep protocol guide](protocols
88
100
  | `createApplication()` | function | `arcane-os` | Headless toolchain operations | Node; selected operation may produce browser or native output |
89
101
  | `createArcaneAI()` | function | `arcane-os/ai/browser-wasm` | Browser-WASM local AI | Browser; compatible LLM provider or controller required |
90
102
  | `createAppReleaseBundle()` | function | `arcane-os` | Packaging and release bundles | Node |
103
+ | `createBrowserKokoroProvider()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser with DBOPFS, Web Locks, Workers, object URLs, and caller-admitted Kokoro runtime/model artifacts |
91
104
  | `createBrowserModelSource()` | function | `arcane-os/ai/browser-wasm` | Browser-WASM local AI | Browser Fetch with a readable response body |
92
- | `createBrowserWasmLlmProvider()` | function | `arcane-os/ai/browser-wasm` | Browser-WASM local AI | Browser; WebAssembly and DBOPFS model bytes |
105
+ | `createBrowserSpeechAuthority()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser descriptor construction; use requires the selected storage and provider Web APIs |
106
+ | `createBrowserWasmLlmProvider()` | function | `arcane-os/ai/browser-wasm` | Browser-WASM local AI | Browser secure context with WebAssembly, OPFS/DBOPFS, WebGPU, and admitted full-offload evidence; no CPU fallback |
107
+ | `createBrowserWhisperProvider()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser with DBOPFS, Web Locks, Workers, object URLs, and caller-admitted Whisper runtime/model artifacts |
93
108
  | `createCanonicalUstarHeader()` | function | `arcane-os` | Packaging and release bundles | Node |
94
109
  | `createDbopfsModelStore()` | function | `arcane-os/ai/browser-wasm` | Browser-WASM local AI | Browser with a ready DBOPFS instance and OPFS |
110
+ | `createDbopfsSpeechArtifactStore()` | function | `arcane-os/ai/browser-speech` | Browser speech providers | Browser with ready DBOPFS, Web Locks, Fetch, File/Blob, and object URLs |
95
111
  | `createNativeBuildPlan()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
96
112
  | `createNativeTargetAdapter()` | function | `arcane-os` | Targets, native plans, and providers | Node; selected browser/native target or provider as documented |
97
113
  | `createReporter()` | function | `arcane-os` | Events, processes, and testing | Node |
@@ -787,7 +803,30 @@ Builds and authenticates one browser application release through the low-level p
787
803
  async packageApp(options)
788
804
  ```
789
805
 
790
- Import it from `arcane-os` or `arcane-os/packager`. The signature above states whether settlement is synchronous or promise-based. The overview and owning group define result authority, side effects, callbacks, events, cancellation, and receipt lifetime.
806
+ Import it from `arcane-os` or `arcane-os/packager`. For a non-dry-run external
807
+ package, the result includes `importMapReceipt`. That frozen receipt binds the
808
+ artifact, configured entry, every additional included `.html`/`.htm` document,
809
+ and their exact committed bytes/hashes. Each admitted document receives the
810
+ same deterministic map. The package root also contains the public
811
+ `ARCANE_RUNTIME_PROJECTION.json` inventory:
812
+
813
+ ```javascript
814
+ {
815
+ schemaVersion: 1,
816
+ kind: 'arcane-app-runtime-projection',
817
+ sdkVersion: '0.2.1',
818
+ pathPrefix: 'arcane/',
819
+ fileCount,
820
+ totalBytes,
821
+ contentSha256,
822
+ files: [{path, bytes, sha256}]
823
+ }
824
+ ```
825
+
826
+ That projection is authenticated by the private application release inventory
827
+ and the admitted runtime authorities. It is not a replacement for either
828
+ receipt. Incomplete, changed, forged, or internally inconsistent projection
829
+ data rejects with `ARCANE_RUNTIME_PROJECTION_INVALID`.
791
830
 
792
831
  ### Availability and normalization
793
832
 
@@ -798,9 +837,13 @@ Import it from `arcane-os` or `arcane-os/packager`. The signature above states w
798
837
  ```javascript
799
838
  import {packageApp} from 'arcane-os';
800
839
 
801
- async function usepackageApp(...arguments_) {
802
- return packageApp(...arguments_);
803
- }
840
+ const packaged = await packageApp({
841
+ workspaceRoot: process.cwd(),
842
+ appId: 'hello-world'
843
+ });
844
+
845
+ console.log(packaged.importMapReceipt.documentPaths);
846
+ console.log(packaged.importMapReceipt.documentCount);
804
847
  ```
805
848
 
806
849
  ## PACKAGER_VERSION
@@ -2655,7 +2698,7 @@ The import-map operation also reports the stable operation-specific strings
2655
2698
  `ARCANE_IMPORT_MAP_COLLISION`; package assembly can additionally report
2656
2699
  `ARCANE_IMPORT_MAP_CLEANUP_FAILED`. They are normalized `ArcaneError.code`
2657
2700
  values, but are not properties added to this frozen general registry in SDK
2658
- `0.1.2`.
2701
+ `0.2.1`.
2659
2702
 
2660
2703
  ### Value and import
2661
2704
 
@@ -3300,10 +3343,42 @@ Runs canonical workspace, runtime, descriptor, and selected-app validation with
3300
3343
  ### Signature and result
3301
3344
 
3302
3345
  ```text
3303
- async validateWorkspace({workspaceRoot=process.cwd(), appId, signal, onEvent}={})
3346
+ async validateWorkspace({
3347
+ workspaceRoot=process.cwd(),
3348
+ appId,
3349
+ allowMissingManagedImportMap=false,
3350
+ signal,
3351
+ onEvent
3352
+ }={})
3304
3353
  ```
3305
3354
 
3306
- Import it from `arcane-os`. The signature above states whether settlement is synchronous or promise-based. The overview and owning group define result authority, side effects, callbacks, events, cancellation, and receipt lifetime.
3355
+ Import it from `arcane-os`. It resolves to a frozen validation receipt with
3356
+ `valid`, `workspaceMode`, `workspaceRoot`, `appId`, `appRoot`, the selected
3357
+ configuration/application, lock data, and completed checks. For an external
3358
+ workspace it additionally returns the exact installed package authority:
3359
+
3360
+ ```javascript
3361
+ {
3362
+ sdkInstallation: {
3363
+ dependencyName,
3364
+ packageSource,
3365
+ canonicalPackageRoot,
3366
+ packageName: 'arcane-os',
3367
+ packageVersion: '0.2.1',
3368
+ runtimeRoot,
3369
+ browserRuntimeRoot,
3370
+ runtimeManifest,
3371
+ browserRuntimeManifest
3372
+ }
3373
+ }
3374
+ ```
3375
+
3376
+ The dependency can be named `arcane-os` or be one exact npm alias for
3377
+ `npm:arcane-os@0.2.1`. The selected installation must still be one direct,
3378
+ physical, non-link package directory whose manifest identifies exactly as
3379
+ `arcane-os@0.2.1`; duplicate canonical/alias declarations fail closed.
3380
+ `allowMissingManagedImportMap` is an internal packaging/development seam. An
3381
+ ordinary caller should leave it `false`.
3307
3382
 
3308
3383
  ### Availability and normalization
3309
3384
 
@@ -3314,9 +3389,13 @@ Import it from `arcane-os`. The signature above states whether settlement is syn
3314
3389
  ```javascript
3315
3390
  import {validateWorkspace} from 'arcane-os';
3316
3391
 
3317
- async function usevalidateWorkspace(...arguments_) {
3318
- return validateWorkspace(...arguments_);
3319
- }
3392
+ const validation = await validateWorkspace({
3393
+ workspaceRoot: process.cwd(),
3394
+ appId: 'hello-world'
3395
+ });
3396
+
3397
+ console.log(validation.sdkInstallation?.dependencyName);
3398
+ console.log(validation.sdkInstallation?.packageVersion);
3320
3399
  ```
3321
3400
 
3322
3401
 
@@ -3472,9 +3551,10 @@ const toolchain = createToolchain({
3472
3551
  }
3473
3552
  });
3474
3553
 
3475
- // Only this explicit call refreshes the managed map and HTML entry.
3554
+ // Only this explicit call refreshes the managed map and configured HTML entry.
3476
3555
  const result = await toolchain.importMap();
3477
- console.log(result.importMap.entryCount); // 86 in SDK 0.1.2
3556
+ console.log(result.importMap.entryCount); // 91 in SDK 0.2.1
3557
+ console.log(result.importMap.documentPaths, result.importMap.documentCount);
3478
3558
  ```
3479
3559
 
3480
3560
  ## describeTargets()
@@ -3542,7 +3622,10 @@ Dispatches one named headless SDK operation with normalized acceptance, events,
3542
3622
  The exact command `'import-map'` dispatches one app-scoped authenticated refresh
3543
3623
  and returns `{workspaceRoot, workspaceMode, appId, importMap}`. A normal
3544
3624
  `importMap` value binds the generated imports and the committed map/HTML file
3545
- hashes; the canonical integrated-legacy layout returns its documented skip
3625
+ hashes through `documentPaths`, `documentCount`, and the ordered `files`
3626
+ records. The direct operation supplies the configured entry only; packaging
3627
+ owns discovery of additional included browser documents. The canonical
3628
+ integrated-legacy layout returns its documented skip
3546
3629
  record instead. This route mutates the two managed application files and has no
3547
3630
  supported dry-run.
3548
3631
 
@@ -3574,13 +3657,20 @@ const result = await executeOperation('import-map', {
3574
3657
  appId:'hello-world'
3575
3658
  });
3576
3659
  console.log(result.importMap.committed, result.importMap.entryCount);
3660
+ console.log(result.importMap.documentPaths, result.importMap.documentCount);
3577
3661
  ```
3578
3662
 
3579
3663
  ## packageApplication()
3580
3664
 
3581
3665
  ### Overview
3582
3666
 
3583
- Runs the high-level package operation for one selected application.
3667
+ Runs the high-level package operation for one selected application. It
3668
+ authenticates the installed SDK/runtime authorities, injects one deterministic
3669
+ managed import map into every included `.html`/`.htm` browser document, and
3670
+ returns the low-level package result with its `importMapReceipt`. External
3671
+ packages publish `ARCANE_RUNTIME_PROJECTION.json`; private
3672
+ `ARCANE_APP_RELEASE.json` remains an internal verification authority rather
3673
+ than an application route.
3584
3674
 
3585
3675
  ### Signature and result
3586
3676
 
@@ -3599,9 +3689,12 @@ Import it from `arcane-os` or `arcane-os/toolchain`. The signature above states
3599
3689
  ```javascript
3600
3690
  import {packageApplication} from 'arcane-os';
3601
3691
 
3602
- async function usepackageApplication(...arguments_) {
3603
- return packageApplication(...arguments_);
3604
- }
3692
+ const result = await packageApplication({
3693
+ workspaceRoot: process.cwd(),
3694
+ appId: 'hello-world'
3695
+ });
3696
+
3697
+ console.log(result.release.importMapReceipt.documentCount);
3605
3698
  ```
3606
3699
 
3607
3700
  ## planApplication()
@@ -4944,7 +5037,8 @@ Deep-frozen authority for the browser-only runtime behind
4944
5037
  `arcane-ai-browser-wasm/2`, `@wllama/wllama` `3.6.0`, the embedded llama.cpp
4945
5038
  revision, the exact packaged JavaScript and WebAssembly assets, retained MIT
4946
5039
  licenses, and the disabled compatibility-runtime/remote-model-helper policy.
4947
- It contains no model weights or model catalog.
5040
+ Its execution policy requires WebGPU, proves full model offload, and declares
5041
+ `cpuFallback:false`. It contains no model weights or default model catalog.
4948
5042
 
4949
5043
  ### Value and import
4950
5044
 
@@ -4958,7 +5052,8 @@ inspecting it does not initialize Wllama, request storage, or download a model.
4958
5052
  ### Availability and normalization
4959
5053
 
4960
5054
  **Browser metadata.** This is an immutable component receipt, not a provider,
4961
- model, browser-permission grant, or proof that WebAssembly/OPFS is available.
5055
+ model, browser-permission grant, or proof that WebAssembly, OPFS, a secure
5056
+ context, or WebGPU is available.
4962
5057
 
4963
5058
  ### Example
4964
5059
 
@@ -4988,11 +5083,28 @@ createArcaneAI({ llm=null, provider=null, loadPolicy='on-demand', security }={})
4988
5083
  ```
4989
5084
 
4990
5085
  At least one `llm` or `provider` is required. The frozen facade contains
4991
- `llm`, `runtime`, `status`, `load`, `unload`, `probe`, `fetchRequest`,
4992
- `streamRequest`, and `dispose`. `status()` returns `{llm: status}`; lifecycle
4993
- methods return the flat LLM status. `fetchRequest()` returns the completion.
4994
- `streamRequest()` consumes streaming and returns text or a tool-name-to-JSON-
4995
- argument-string record. Use `ai.llm.stream()` for the async iterator.
5086
+ `llm`, `runtime`, `createChatSession`, `status`, `load`, `unload`, `probe`,
5087
+ `fetchRequest`, `streamRequest`, and `dispose`. `status()` returns
5088
+ `{llm: status}`; lifecycle methods return the flat LLM status.
5089
+ `fetchRequest()` returns the completion. `streamRequest()` consumes streaming
5090
+ and returns text or a tool-name-to-JSON-argument-string record. Use
5091
+ `ai.llm.stream()` for the async iterator.
5092
+
5093
+ When `llm` is an existing `ModelController`, it retains the security and load
5094
+ policy chosen when that controller was created. `createArcaneAI()` does not
5095
+ reapply its `loadPolicy` argument in that case, and supplying `security` with an
5096
+ existing controller throws `TypeError`. Provider input creates a new controller
5097
+ and applies the supplied `loadPolicy` and app-level `security` normally.
5098
+
5099
+ `createChatSession(options)` asynchronously imports the private managed
5100
+ `#arcane/persistent-ai-chat-session` specifier, resolves a
5101
+ `Promise<PersistentAIChatSession>`, and binds its `chat` function to this exact
5102
+ controller. Applications continue to use this public controller method rather
5103
+ than importing that private specifier. `options` must be a plain object and cannot contain
5104
+ `chat`. The resulting session preserves coherent live bounded context while
5105
+ letting each user/assistant/tool turn choose matching durable ChatEntity/DBOPFS
5106
+ persistence; `persist:false` does not write that turn to durable history or
5107
+ memory. There is no storage or provider fallback.
4996
5108
 
4997
5109
  App and load-operation security use
4998
5110
  `{security:{secure?:boolean, checks?:{byteLength?:boolean, sha256?:boolean}}}`.
@@ -5002,27 +5114,42 @@ binding scope, documented below, sits between those scopes.
5002
5114
 
5003
5115
  ### Availability and normalization
5004
5116
 
5005
- **Browser.** It normalizes lifecycle, state/progress events, lazy/manual use,
5006
- cancellation, completions, and structural tool-call visibility. It neither
5007
- selects a provider fallback nor executes an application tool.
5117
+ **Browser.** It normalizes lifecycle, `statechange` and `progress` observation,
5118
+ lazy/manual use, cancellation, completions, and structural tool-call
5119
+ visibility. It neither selects a provider fallback nor executes an application
5120
+ tool. Persistent sessions additionally require ChatEntity/DBOPFS in the
5121
+ managed browser runtime.
5008
5122
 
5009
5123
  ### Example
5010
5124
 
5011
5125
  ```javascript
5012
5126
  const ai = createArcaneAI({provider, loadPolicy:'manual'});
5013
- const stop = ai.llm.on('progress', event => renderProgress(event.detail));
5014
- await ai.load({offline:true});
5015
- stop();
5127
+ async function openLocalReviewAfterUserChoice() {
5128
+ const stop = ai.llm.on('progress', event => renderProgress(event.detail));
5129
+ try {
5130
+ await ai.load({offline:true});
5131
+ } finally {
5132
+ stop();
5133
+ }
5134
+ return ai.createChatSession({
5135
+ chatFileName:'local-review.jsonl',
5136
+ loadExisting:true
5137
+ });
5138
+ }
5016
5139
  ```
5017
5140
 
5018
5141
  ## createBrowserModelSource()
5019
5142
 
5020
5143
  ### Overview
5021
5144
 
5022
- Validates a caller-owned model descriptor and creates the cancellable HTTPS
5023
- source accepted by the browser-WASM store/provider. The canonical descriptor is
5024
- `{id, url, bytes?, sha256?}`. `id` and `url` are required. `bytes`, when
5025
- present, is the expected positive safe-integer byte length, not inline data.
5145
+ Validates a caller-owned ordered model-file descriptor and creates the
5146
+ cancellable HTTPS source accepted by the browser-WASM store/provider. The
5147
+ canonical descriptor is
5148
+ `{id,files:[{name?,url|immutableUrl,bytes?,sha256?},...]}`. The nonempty file
5149
+ array has unique normalized names and URLs. `bytes`, when present, is an
5150
+ expected positive safe-integer byte length, not inline data. The legacy
5151
+ one-file `{id,url|immutableUrl,name?,bytes?,sha256?}` shape remains accepted
5152
+ and normalizes to one ordered member.
5026
5153
 
5027
5154
  ### Signature and result
5028
5155
 
@@ -5030,17 +5157,18 @@ present, is the expected positive safe-integer byte length, not inline data.
5030
5157
  createBrowserModelSource(descriptor, { fetchImpl=null }={})
5031
5158
  ```
5032
5159
 
5033
- The URL must be absolute HTTPS with no credentials or fragment and no
5160
+ Every URL must be absolute HTTPS with no credentials or fragment and no
5034
5161
  revision-floating `main`, `master`, or `latest` path. A supplied SHA-256 value
5035
- is exactly 64 hexadecimal characters. The frozen source exposes its canonical
5036
- descriptor and `open({signal})`, which returns `{body, requestedUrl, finalUrl,
5037
- reportedBytes, cancel}`. `reportedBytes` is the nullable valid nonnegative
5038
- `Content-Length` observation; it is not an unconditional admission check. Every
5039
- direct `open()` performs the configured fetch.
5040
-
5041
- For compatibility, `immutableUrl` remains an alias for `url` and `name` can
5042
- remain a cache-filename hint; `url` and `immutableUrl` must match when both are
5043
- present. Legacy `licenseSpdx` and `sourceRevision` properties are not canonical
5162
+ is exactly 64 hexadecimal characters. The frozen source exposes `kind`, the
5163
+ canonical descriptor fields, `descriptor`, and `open(memberIndex,{signal})`.
5164
+ For a one-file source, `open({signal})` remains accepted. The result is
5165
+ `{body,requestedUrl,finalUrl,reportedBytes,cancel}`. `reportedBytes` is the
5166
+ nullable valid nonnegative `Content-Length` observation; it is not an
5167
+ unconditional admission check. Every direct `open()` performs the configured
5168
+ fetch and does not admit bytes to the cache.
5169
+
5170
+ `immutableUrl` is an alias for `url`; both must match when supplied together.
5171
+ Legacy `licenseSpdx` and `sourceRevision` properties are not canonical
5044
5172
  descriptor fields, runtime admission checks, or proof of license rights.
5045
5173
 
5046
5174
  ### Availability and normalization
@@ -5056,9 +5184,17 @@ corresponding effective check is enabled.
5056
5184
  ```javascript
5057
5185
  const source = createBrowserModelSource({
5058
5186
  id:'reviewed-model',
5059
- url:'https://models.example/revisions/4f7c/model-q4.gguf',
5060
- bytes:123456789,
5061
- sha256:'0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
5187
+ files:[{
5188
+ name:'model-q4-00001-of-00002.gguf',
5189
+ url:'https://models.example/revisions/4f7c/model-q4-00001-of-00002.gguf',
5190
+ bytes:123456789,
5191
+ sha256:'0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
5192
+ },{
5193
+ name:'model-q4-00002-of-00002.gguf',
5194
+ url:'https://models.example/revisions/4f7c/model-q4-00002-of-00002.gguf',
5195
+ bytes:98765432,
5196
+ sha256:'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789'
5197
+ }]
5062
5198
  });
5063
5199
  ```
5064
5200
 
@@ -5067,22 +5203,32 @@ const source = createBrowserModelSource({
5067
5203
  ### Overview
5068
5204
 
5069
5205
  Creates the packaged Wllama provider from genuine source and store objects
5070
- created by this module. It serializes requests and exposes provider states
5071
- `unloaded`, `loading`, `ready`, `unloading`, and `error`.
5206
+ created by this module. Structural lookalikes are rejected. It serializes
5207
+ requests and exposes provider states `unloaded`, `loading`, `ready`,
5208
+ `unloading`, and `error`.
5072
5209
 
5073
5210
  ### Signature and result
5074
5211
 
5075
5212
  ```text
5076
- createBrowserWasmLlmProvider({ source, store, loadDefaults={}, security, logger=console }={})
5213
+ createBrowserWasmLlmProvider({ source, sources, store, loadDefaults={}, security, logger=console }={})
5077
5214
  ```
5078
5215
 
5079
- The frozen provider exposes `protocol`, `id`, `model`, `capabilities`,
5080
- `status`, `load`, `unload`, `chat`, `stream`, `streamChat`, `use`, `probe`, and
5081
- `dispose`. Load settings include offline mode, `AbortSignal`, progress,
5082
- threads, context/batch/micro-batch tokens, and GPU layers. Chat supports
5083
- OpenAI-like message/generation fields, tools, tool choice, parallel tool-call
5084
- preference, and JSON/JSON-Schema structured output. `stream()` returns a frozen
5085
- async iterator with `result` and `cancel(reason)`.
5216
+ `sources` is a nonempty array of unique SDK-created sources. Optional legacy
5217
+ `source` names the default and must be one member of `sources`; when `sources`
5218
+ is omitted, `source` supplies the one-model catalog. The frozen provider
5219
+ exposes `protocol`, `id`, default `model`, `catalog`, `capabilities`, `status`,
5220
+ `load`, `unload`, `chat`, `stream`, `streamChat`, `use`, `probe`, and `dispose`.
5221
+ Direct `load()` selects a catalog model and returns `{model,status}`; the
5222
+ facade `ai.load()` returns the flat controller status. Load settings include
5223
+ offline mode, `AbortSignal`, progress, threads, and context/batch/micro-batch
5224
+ tokens. The runtime always forces `gpuLayers:99999`; callers cannot request CPU
5225
+ or partial offload.
5226
+
5227
+ Chat supports OpenAI-like message/generation fields, tools, tool choice,
5228
+ parallel tool-call preference, and JSON/JSON-Schema structured output.
5229
+ `stream()` returns a frozen async iterator with `result` and `cancel(reason)`.
5230
+ Returned tool calls are validated structural data with JSON-string arguments;
5231
+ the SDK never invokes a handler or executes a tool.
5086
5232
 
5087
5233
  Provider `security` is the provider/model-binding scope. Security fields resolve
5088
5234
  independently by precedence: load operation, provider/model binding, app SDK
@@ -5096,21 +5242,30 @@ requires unload and reload.
5096
5242
 
5097
5243
  ### Availability and normalization
5098
5244
 
5099
- **Browser with WebAssembly and a DBOPFS model.** A load succeeds only after
5100
- Wllama confirms the model is loaded. WebGPU and cross-origin isolation are
5101
- capability observations, not promised gates. Status discloses effective
5102
- `security` and per-check `integrity`; unchecked bytes are never labeled SHA-256
5103
- verified. Returned tools are validated structural data; the SDK never calls a
5104
- handler. Cancellation normalizes to `ARCANE_AI_REQUEST_ABORTED`.
5245
+ **Browser secure context with WebAssembly, OPFS/DBOPFS, WebGPU, and admitted
5246
+ full-offload/buffer/queue/fence evidence.** A load succeeds only after Wllama
5247
+ confirms the model is loaded and the full GPU execution evidence settles. There
5248
+ is no CPU fallback. Cross-origin isolation and coarse hardware fields remain
5249
+ observations rather than hard gates. Status discloses effective `security`,
5250
+ per-check `integrity`, catalog compatibility, storage evidence, and lifecycle
5251
+ state; unchecked bytes are never labeled SHA-256 verified. Adapter selection
5252
+ is instrumented as `arcane.ai.browser-wasm.webgpu.adapter.selected`.
5253
+ Cancellation normalizes to `ARCANE_AI_REQUEST_ABORTED`; load/admission failures
5254
+ surface stable `ARCANE_AI_*` codes such as `ARCANE_AI_WEBGPU_REQUIRED`,
5255
+ `ARCANE_AI_MODEL_FULL_OFFLOAD_UNPROVEN`, and
5256
+ `ARCANE_AI_MODEL_GPU_MEMORY_INSUFFICIENT`. Concurrent runtime inference can fail
5257
+ `ARCANE_AI_RUNTIME_BUSY`; inspection and status can report
5258
+ `ARCANE_AI_PROVIDER_UNAVAILABLE` or fallback `ARCANE_AI_RUNTIME_FAILED` without
5259
+ misrepresenting those observations as a successful load.
5105
5260
 
5106
5261
  ### Example
5107
5262
 
5108
5263
  ```javascript
5109
5264
  const provider = createBrowserWasmLlmProvider({
5110
- source,
5265
+ sources:[source],
5111
5266
  store,
5112
5267
  security:{secure:true},
5113
- loadDefaults:{threads:1, contextTokens:4096, gpuLayers:0}
5268
+ loadDefaults:{threads:1, contextTokens:4096}
5114
5269
  });
5115
5270
  console.log(provider.status().state); // unloaded
5116
5271
  ```
@@ -5119,26 +5274,31 @@ console.log(provider.status().state); // unloaded
5119
5274
 
5120
5275
  ### Overview
5121
5276
 
5122
- Adapts an existing DBOPFS instance into a model cache without renaming its
5123
- public methods. The adapter commits model bytes before the
5124
- `arcane.ai.browser-wasm.model.v3` completion manifest and always records the
5125
- observed downloaded or cached byte length.
5277
+ Adapts an existing DBOPFS instance into a multi-model, ordered multi-file cache
5278
+ without renaming its public methods. The adapter commits every model file
5279
+ before the `arcane.ai.browser-wasm.model.v4` completion manifest and always
5280
+ records observed downloaded or cached byte lengths. A partial file set is not
5281
+ a cache hit.
5126
5282
 
5127
5283
  ### Signature and result
5128
5284
 
5129
5285
  ```text
5130
- createDbopfsModelStore({ dbopfs, tableName='arcane_ai_browser_models' }={})
5286
+ createDbopfsModelStore({ dbopfs, tableName='arcane_ai_browser_models', estimateStorage=null }={})
5131
5287
  ```
5132
5288
 
5133
5289
  The frozen result contains `kind`, `tableName`, the original `adapter`, and
5134
5290
  `ready`, `openVerified`, `install`, `ensure`, and `remove`. `ensure()` returns
5135
- the file, manifest, `observedBytes`, integrity detail, and
5136
- `cache:'cached'|'installed'`. `openVerified()` is a compatibility helper that
5291
+ `files`, the one-file compatibility `file` or `null`, the manifest,
5292
+ `observedBytes`, integrity detail, storage evidence, and
5293
+ `cache:'cached'|'installed'`. Optional `estimateStorage()` supplies bounded
5294
+ quota evidence when the browser estimator is unavailable or the application
5295
+ owns a more precise view. `openVerified()` is a compatibility helper that
5137
5296
  forces both checks on. Ordinary install/reuse compares expected length only
5138
5297
  when effective `checks.byteLength` is true and hashes only when effective
5139
5298
  `checks.sha256` is true. `offline:true` never downloads and rejects a miss with
5140
- `ARCANE_AI_MODEL_OFFLINE_MISS`. Version-2 manifests with matching model identity
5141
- can migrate to version 3 without fabricating verification.
5299
+ `ARCANE_AI_MODEL_OFFLINE_MISS`. Version-2/3 compatibility is internal; every
5300
+ new successful completion is recorded as version 4 without fabricating an
5301
+ integrity result.
5142
5302
 
5143
5303
  ### Availability and normalization
5144
5304
 
@@ -5146,17 +5306,398 @@ can migrate to version 3 without fabricating verification.
5146
5306
  actually performed. Disabled byte-length checks do not compare descriptor
5147
5307
  `bytes`, disabled SHA-256 checks do not hash or reread solely for a digest, and
5148
5308
  overall integrity is `verified` when every enabled check succeeds or
5149
- `unchecked` when neither check is enabled. Cache
5150
- metadata is not a transferable capability or license proof. Unload/dispose keep
5151
- the cache; `store.remove(source)` explicitly deletes it.
5309
+ `unchecked` when neither check is enabled. Cache metadata is not a transferable
5310
+ capability or license proof. Provider unload/dispose keeps all cached files;
5311
+ `store.remove(source)` explicitly deletes that source's files and manifest.
5152
5312
 
5153
5313
  ### Example
5154
5314
 
5155
5315
  ```javascript
5156
5316
  const store = createDbopfsModelStore({dbopfs});
5157
- await store.ready();
5158
- const cached = await store.openVerified(source);
5159
- console.log(cached ? 'verified cache' : 'cache miss');
5317
+ async function verifyCachedModelAfterUserChoice() {
5318
+ await store.ready();
5319
+ const cached = await store.openVerified(source);
5320
+ console.log(cached ? 'verified cache' : 'cache miss');
5321
+ }
5322
+ ```
5323
+
5324
+ ## adaptV1LlmProvider()
5325
+
5326
+ ### Overview
5327
+
5328
+ Projects one compatible admitted v1 browser-WASM provider into the provider-neutral
5329
+ LLM role consumed by the managed `arcane/AIProviderRuntime` projection. The
5330
+ runtime checks the v1 protocol, required identity/methods, and local-only
5331
+ capability; it does not establish SDK provenance for an arbitrary compatible
5332
+ object. The adapter does not
5333
+ change the wrapped provider, download a model, create a fallback, or execute a
5334
+ tool.
5335
+
5336
+ ### Signature and result
5337
+
5338
+ ```text
5339
+ adaptV1LlmProvider(provider)
5340
+ ```
5341
+
5342
+ The frozen result is
5343
+ `{protocol:'arcane-ai-provider/2',role:'llm',id,localOnly:true,catalog,inspect,
5344
+ status,load,request,unload,dispose}`. `inspect()` returns an
5345
+ `arcane-ai-model-authority/1` record only for an exact catalog selection.
5346
+ `request()` accepts only the `chat` and `stream` operations, preserves
5347
+ structural tool calls, and never invokes application handlers. Re-adapting the
5348
+ same provider returns the same adapter object.
5349
+
5350
+ ### Availability and normalization
5351
+
5352
+ **Browser; the wrapped provider retains its own secure-context, WebAssembly,
5353
+ OPFS/DBOPFS, WebGPU, model, and lifecycle requirements.** The adapter normalizes
5354
+ provider/2 selection and lifecycle without adding Node, native, cloud, network,
5355
+ or CPU fallback behavior.
5356
+
5357
+ ### Example
5358
+
5359
+ ```javascript
5360
+ import {adaptV1LlmProvider} from 'arcane-os/ai/browser-wasm';
5361
+ import {getAIProviderRuntime} from 'arcane/AIProviderRuntime';
5362
+
5363
+ const runtime = getAIProviderRuntime();
5364
+ const releaseProvider = runtime.register(adaptV1LlmProvider(provider));
5365
+ // Configure an exact llm route before load/use.
5366
+ releaseProvider();
5367
+ ```
5368
+
5369
+ # Browser speech providers
5370
+
5371
+ `arcane-os/ai/browser-speech` exports exactly the five package members below.
5372
+ It ships provider, authority, artifact-store, and Worker mechanisms but no
5373
+ Whisper/Kokoro model weights, adapter runtime bytes, voices, download URLs,
5374
+ default catalog, CDN loader, native bridge, or cloud fallback. The providers
5375
+ implement `arcane-ai-provider/2`; the managed `arcane/AIProviderRuntime` and
5376
+ `arcane/AIRuntimeState` projection modules can normalize their independent
5377
+ role lifecycle and observation, but are not exports of this package subpath.
5378
+
5379
+ ## BROWSER_SPEECH_ARTIFACT_PROTOCOL
5380
+
5381
+ ### Overview
5382
+
5383
+ Stable protocol identifier for an SDK-created authenticated browser speech
5384
+ artifact store. It identifies the store contract, not a model authority,
5385
+ capability grant, or complete-cache receipt.
5386
+
5387
+ ### Value and import
5388
+
5389
+ ```text
5390
+ const BROWSER_SPEECH_ARTIFACT_PROTOCOL
5391
+ ```
5392
+
5393
+ Its exact value is `arcane-ai-browser-speech-artifacts/1`, and it is exported
5394
+ only by `arcane-os/ai/browser-speech`.
5395
+
5396
+ ### Availability and normalization
5397
+
5398
+ **Browser metadata.** Importing or reading the constant starts no Worker and
5399
+ downloads no model or runtime artifact. Store use additionally requires
5400
+ DBOPFS/OPFS, Web Locks, and the browser APIs described below.
5401
+
5402
+ ### Example
5403
+
5404
+ ```javascript
5405
+ import {
5406
+ BROWSER_SPEECH_ARTIFACT_PROTOCOL
5407
+ } from 'arcane-os/ai/browser-speech';
5408
+
5409
+ console.log(BROWSER_SPEECH_ARTIFACT_PROTOCOL);
5410
+ ```
5411
+
5412
+ ## createBrowserSpeechAuthority()
5413
+
5414
+ ### Overview
5415
+
5416
+ Validates and freezes one caller-owned Whisper STT or Kokoro TTS model/runtime
5417
+ closure. The application owns every model, runtime, revision, URL, hash, voice,
5418
+ license, and business-policy choice; the SDK supplies no artifact selection.
5419
+
5420
+ ### Signature and result
5421
+
5422
+ ```text
5423
+ createBrowserSpeechAuthority({ providerId, role, model, runtime, security }={})
5424
+ ```
5425
+
5426
+ `role` is exactly `stt` or `tts`. `model` supplies
5427
+ `{id,repository,revision,files}` and, for TTS, `defaultVoice`. `runtime`
5428
+ supplies `{adapter,version,revision,entry,files}`; the adapter is exactly
5429
+ `transformers-whisper` for STT or `kokoro-js` for TTS. File records use unique
5430
+ relative `path` and immutable HTTPS or same-origin `url` identities plus
5431
+ optional `bytes`, `sha256`, and `mediaType`. The runtime entry names one
5432
+ self-contained JavaScript module in the declared runtime closure.
5433
+
5434
+ The result is a frozen `arcane-ai-model-authority/1` record containing the
5435
+ provider/model/role identity, admitted local authority, normalized runtime and
5436
+ model file declarations, optional default voice, and normalized security policy.
5437
+ Construction binds declared identities; `store.prepare()` validates the actual
5438
+ downloaded or cached self-contained runtime graph before execution.
5439
+
5440
+ Security is the closed record
5441
+ `{secure?:boolean,checks?:{byteLength?:boolean,sha256?:boolean}}`. A provider
5442
+ resolves each field from SDK default `secure:false`, then `appSecurity`, provider
5443
+ `security`, and finally `load({security})`. Omitted checks default to the
5444
+ effective `secure` value, while explicit per-check booleans override it. Every
5445
+ enabled check requires matching `bytes` or `sha256` evidence on every runtime
5446
+ and model artifact and fails closed when evidence is absent or mismatched.
5447
+ Disabled checks are disclosed and are never reported as verification.
5448
+
5449
+ ### Availability and normalization
5450
+
5451
+ **Browser descriptor construction; actual use requires the chosen artifact
5452
+ store and provider Web APIs.** Mutable `main`, `master`, and `latest` paths,
5453
+ credentials, fragments, duplicate identities, mismatched adapters, and runtime
5454
+ graphs outside the declared closure fail closed. This function grants no
5455
+ publisher authenticity or license rights.
5456
+
5457
+ ### Example
5458
+
5459
+ ```javascript
5460
+ import {createBrowserSpeechAuthority} from 'arcane-os/ai/browser-speech';
5461
+
5462
+ const authority = createBrowserSpeechAuthority({
5463
+ providerId:'review-whisper',
5464
+ role:'stt',
5465
+ model:{
5466
+ id:'review-whisper-model',
5467
+ repository:'caller-models',
5468
+ revision:'model-r1',
5469
+ files:[{
5470
+ path:'model.onnx',
5471
+ url:'https://models.example/model-r1/model.onnx'
5472
+ }]
5473
+ },
5474
+ runtime:{
5475
+ adapter:'transformers-whisper',
5476
+ version:'1.0.0',
5477
+ revision:'runtime-r1',
5478
+ entry:'adapter.mjs',
5479
+ files:[{
5480
+ path:'adapter.mjs',
5481
+ url:'https://runtime.example/runtime-r1/adapter.mjs',
5482
+ mediaType:'text/javascript'
5483
+ }]
5484
+ }
5485
+ });
5486
+ ```
5487
+
5488
+ ## createDbopfsSpeechArtifactStore()
5489
+
5490
+ ### Overview
5491
+
5492
+ Adapts an existing DBOPFS instance into an authority-scoped speech
5493
+ runtime/model store. It serializes each exact authority with an exclusive Web
5494
+ Lock, removes partial state after failure, and commits the
5495
+ `arcane.ai.browser-speech.assets.v1` completion manifest only after every
5496
+ declared file succeeds.
5497
+
5498
+ ### Signature and result
5499
+
5500
+ ```text
5501
+ createDbopfsSpeechArtifactStore({ dbopfs, tableName='arcane_ai_browser_speech', fetchImpl=null, objectUrlFactory=null }={})
5502
+ ```
5503
+
5504
+ The frozen result is `{protocol,tableName,prepare,remove}`.
5505
+ `prepare(authority,{signal,onProgress,offline=false,security})` admits a
5506
+ compatible complete cache or downloads every declared file with omitted
5507
+ credentials, enforces only enabled byte-length/SHA-256 checks, validates the
5508
+ closed runtime graph, materializes object URLs, and returns
5509
+ `{cache,runtime,model,release}`. Call `release()` when the Worker no longer
5510
+ needs those URLs. `offline:true` never fetches and rejects a miss with
5511
+ `ARCANE_AI_ARTIFACT_OFFLINE_MISS`. `remove(authority)` deletes that exact
5512
+ authority's files and manifest.
5513
+
5514
+ ### Availability and normalization
5515
+
5516
+ **Browser with a ready DBOPFS instance, OPFS, Web Locks, Fetch or an injected
5517
+ fetch function, File/Blob, and object URLs.** An unavailable authority lock
5518
+ fails as `ARCANE_AI_STORAGE_BUSY`; download, integrity, cache, graph, and
5519
+ storage failures remain observable `ARCANE_AI_*` errors. Completion metadata
5520
+ is cache-consistency evidence, not transferable authority or publisher proof.
5521
+
5522
+ ### Example
5523
+
5524
+ ```javascript
5525
+ import {
5526
+ createDbopfsSpeechArtifactStore
5527
+ } from 'arcane-os/ai/browser-speech';
5528
+
5529
+ const speechStore = createDbopfsSpeechArtifactStore({dbopfs});
5530
+ async function inspectCachedSpeechAfterUserChoice() {
5531
+ // This strict offline call assumes this exact authority was admitted earlier.
5532
+ const prepared = await speechStore.prepare(authority, {offline:true});
5533
+ try {
5534
+ console.log(prepared.cache, prepared.runtime.entry);
5535
+ } finally {
5536
+ prepared.release();
5537
+ }
5538
+ }
5539
+ ```
5540
+
5541
+ ## createBrowserWhisperProvider()
5542
+
5543
+ ### Overview
5544
+
5545
+ Creates a local-only Whisper speech-to-text provider for the provider-neutral
5546
+ AI runtime. It owns one independent STT load/use/unload/dispose lifecycle and
5547
+ never selects a cloud, native, or LLM fallback.
5548
+
5549
+ ### Signature and result
5550
+
5551
+ ```text
5552
+ createBrowserWhisperProvider(options={})
5553
+ ```
5554
+
5555
+ The recognized options are
5556
+ `{id='arcane-browser-whisper',localOnly=true,model,runtime,appSecurity,
5557
+ security,store,offline=false}`. The frozen result is
5558
+ `{protocol:'arcane-ai-provider/2',role:'stt',id,localOnly:true,catalog,inspect,
5559
+ status,load,request,unload,dispose}`. The only request operation is
5560
+ `transcribe`; its payload is `{audio:Float32Array,sampleRate:16000}`. The
5561
+ Worker-transferred result is a structured `{text}` record; the client does not
5562
+ re-freeze the cloned record.
5563
+
5564
+ `status()` returns
5565
+ `{role,providerId,modelId,state,loaded,busy,generation,errorCode,cache}`.
5566
+ States are `unloaded`, `loading`, `ready`, `unloading`, `error`, and
5567
+ `disposed`. Compatible concurrent loads coalesce; concurrent requests fail as
5568
+ `ARCANE_AI_PROVIDER_BUSY`. Cancellation after the Worker request begins
5569
+ terminates that Worker slot, returns the provider to `unloaded`, and rejects as
5570
+ `ARCANE_AI_REQUEST_ABORTED`. Cancellation while shared Blob/File audio is still
5571
+ being decoded occurs before Worker use; it rejects with the same code while the
5572
+ loaded Worker remains intact and the provider stays `ready`.
5573
+
5574
+ The provider also accepts the shared AI speech payload
5575
+ `{audio:Blob|File,mimeType,model}`. It requires an exact model match, validates
5576
+ the MIME essence against `Blob.type`, decodes through browser audio APIs, and
5577
+ copies authoritative 16 kHz mono `Float32Array` PCM into the same native
5578
+ provider operation. Missing decode support fails
5579
+ `ARCANE_AI_AUDIO_DECODE_UNAVAILABLE`; invalid or failed decoding fails
5580
+ `ARCANE_AI_INVALID_REQUEST` or `ARCANE_AI_AUDIO_DECODE_FAILED`. Unknown fields
5581
+ and accessors are rejected before Worker use.
5582
+
5583
+ ### Availability and normalization
5584
+
5585
+ **Browser with DBOPFS, Web Locks, Workers, object URLs, and a caller-admitted
5586
+ self-contained `transformers-whisper` runtime/model closure.** The subpath is
5587
+ importable in Node, but no Node storage, Worker, audio-decoder, or speech
5588
+ execution adapter is published. No Core speech call, model/runtime download
5589
+ authority, or automatic provider fallback is added. Provider objects are not event targets; register them with
5590
+ the managed AI provider runtime when normalized `AIRuntimeState` observation is
5591
+ needed.
5592
+
5593
+ ### Example
5594
+
5595
+ ```javascript
5596
+ import {createBrowserWhisperProvider} from 'arcane-os/ai/browser-speech';
5597
+
5598
+ const whisper = createBrowserWhisperProvider({model, runtime, store:speechStore});
5599
+ async function transcribeAfterUserChoice(audioBlob) {
5600
+ await whisper.load({
5601
+ role:'stt',
5602
+ selection:{
5603
+ providerId:whisper.id,
5604
+ modelId:whisper.catalog()[0].id,
5605
+ localOnly:true
5606
+ },
5607
+ progress() {}
5608
+ });
5609
+ const transcript = await whisper.request({
5610
+ role:'stt',
5611
+ operation:'transcribe',
5612
+ payload:{
5613
+ audio:audioBlob,
5614
+ mimeType:audioBlob.type,
5615
+ model:whisper.catalog()[0].id
5616
+ }
5617
+ });
5618
+ console.log(transcript.text);
5619
+ }
5620
+ ```
5621
+
5622
+ ## createBrowserKokoroProvider()
5623
+
5624
+ ### Overview
5625
+
5626
+ Creates a local-only Kokoro text-to-speech provider for the provider-neutral AI
5627
+ runtime. It owns one independent TTS lifecycle and never selects a cloud,
5628
+ native, or LLM fallback.
5629
+
5630
+ ### Signature and result
5631
+
5632
+ ```text
5633
+ createBrowserKokoroProvider(options={})
5634
+ ```
5635
+
5636
+ The recognized options are
5637
+ `{id='arcane-browser-kokoro',localOnly=true,model,runtime,appSecurity,security,
5638
+ store,offline=false}`. The frozen result is
5639
+ `{protocol:'arcane-ai-provider/2',role:'tts',id,localOnly:true,catalog,inspect,
5640
+ status,load,request,unload,dispose}`. The only request operation is
5641
+ `synthesize`; its payload is `{text,voice?,speed=1}`. Omitted `voice` uses the
5642
+ caller-supplied `model.defaultVoice`; `speed` is greater than zero and at most
5643
+ four. The Worker-transferred result is a structured
5644
+ `{audio:Float32Array,sampleRate:24000,voice}` record; the client does not
5645
+ re-freeze the cloned record.
5646
+
5647
+ The shared AI request form is
5648
+ `{model,input,responseFormat,voice?,speed?}`. It requires the exact model,
5649
+ admits only `responseFormat:'wav'`, maps `input` to provider-native text, and
5650
+ returns frozen `{audio:Uint8Array,contentType:'audio/wav'}` containing 24 kHz
5651
+ mono 16-bit PCM. Unsupported formats fail
5652
+ `ARCANE_AI_UNSUPPORTED_RESPONSE_FORMAT`; malformed adapter audio fails
5653
+ `ARCANE_AI_INVALID_PROVIDER_RESULT`. Unknown fields and accessors fail closed.
5654
+
5655
+ Lifecycle/status, coalesced load, busy-request, unload, Worker-failure, and
5656
+ disposal behavior matches the Whisper provider. Cancellation after Worker use
5657
+ begins unloads that Worker; cancellation before Worker use rejects while the
5658
+ provider stays ready. Stable failures include
5659
+ `ARCANE_AI_INVALID_REQUEST`, `ARCANE_AI_NOT_READY`,
5660
+ `ARCANE_AI_PROVIDER_DISPOSED`, `ARCANE_AI_OPERATION_SUPERSEDED`,
5661
+ `ARCANE_AI_WORKER_CRASHED`, `ARCANE_AI_WORKER_MESSAGE_ERROR`, and
5662
+ `ARCANE_AI_ADAPTER_PROTOCOL_MISMATCH`.
5663
+
5664
+ ### Availability and normalization
5665
+
5666
+ **Browser with DBOPFS, Web Locks, Workers, object URLs, and a caller-admitted
5667
+ self-contained `kokoro-js` runtime/model/voice closure.** The subpath is
5668
+ importable in Node, but no Node storage, Worker, or speech execution adapter is
5669
+ published. No Core speech call, model/runtime/voice authority, or automatic provider fallback is
5670
+ added. Provider objects are not event targets; managed `AIProviderRuntime` and
5671
+ `AIRuntimeState` projections own normalized cross-role observation.
5672
+
5673
+ ### Example
5674
+
5675
+ ```javascript
5676
+ import {createBrowserKokoroProvider} from 'arcane-os/ai/browser-speech';
5677
+
5678
+ const kokoro = createBrowserKokoroProvider({model, runtime, store:speechStore});
5679
+ async function synthesizeAfterUserChoice() {
5680
+ await kokoro.load({
5681
+ role:'tts',
5682
+ selection:{
5683
+ providerId:kokoro.id,
5684
+ modelId:kokoro.catalog()[0].id,
5685
+ localOnly:true
5686
+ },
5687
+ progress() {}
5688
+ });
5689
+ const speech = await kokoro.request({
5690
+ role:'tts',
5691
+ operation:'synthesize',
5692
+ payload:{
5693
+ model:kokoro.catalog()[0].id,
5694
+ input:'Hello from Arcane.',
5695
+ responseFormat:'wav',
5696
+ speed:1
5697
+ }
5698
+ });
5699
+ console.log(speech.audio, speech.contentType);
5700
+ }
5160
5701
  ```
5161
5702
 
5162
5703
  ## Data export subpaths