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
@@ -1,27 +1,27 @@
1
1
  # Protocol and host architecture
2
2
 
3
3
  This is the deep reference behind the compact availability notes elsewhere.
4
- Application developers normally call one documented API and do not choose a
5
- wire protocol directly.
4
+ Application developers should start with the
5
+ [availability and normalization guide](availability-and-normalization.md), call
6
+ one documented API, and treat the protocols below as implementation detail.
6
7
 
7
8
  ## Layer map
8
9
 
9
10
  ```text
10
11
  application code
11
- |-- Node SDK API ---------------- arcane-cli-events/1 + SDK receipts
12
- |-- EventManager ---------------- synchronous bus + arcane-event-stack/1
13
- |
14
- `-- renderer ESM / globalThis.Arcane
15
- |-- standalone browser ----- standard Web APIs / allowed HTTPS
16
- |-- development host ------- development HTTP bridge
17
- |-- Microsoft NT native ---- WebView2 host bridge
18
- |-- Linux native ----------- WebKitGTK host bridge
19
- `-- Android native --------- Android WebView message bridge
20
- |
21
- `-- Arcane Core/provider boundary
22
- |-- platform services
23
- |-- ArcaneOllama loopback service
24
- `-- explicitly selected cloud APIs
12
+ |-- Node SDK API ----------------- arcane-cli-events/1 + SDK receipts
13
+ |-- EventManager ----------------- synchronous bus + arcane-event-stack/1
14
+ |-- browser-local AI ------------- WebGPU/WASM/Workers/DBOPFS; no Core grant
15
+ `-- globalThis.Arcane
16
+ |-- development host -------- development HTTP bridge
17
+ |-- Microsoft NT native ----- WebView2 host bridge
18
+ |-- Linux native ------------ WebKitGTK host bridge
19
+ `-- Android native ---------- Android WebView message bridge
20
+ |
21
+ `-- Arcane Core/provider boundary
22
+ |-- platform services
23
+ |-- ArcaneOllama loopback service
24
+ `-- explicitly selected cloud APIs
25
25
  ```
26
26
 
27
27
  Each downward boundary can add authority and platform capability. None can be
@@ -103,25 +103,43 @@ imports such as:
103
103
  import ollama from 'arcane/Ollama';
104
104
  ```
105
105
 
106
- The authenticated physical-v1 tree lives entirely beneath `arcane/`. It
107
- contains 155 pinned Arcane runtime files plus 18 SDK browser-runtime files:
108
- 173 files in all. Runtime `strong-type` 1.1 stays under
109
- `arcane/dependencies/strong-type/`; the focused SDK event surface lives under
110
- `arcane/sdk/`, with `event-pubsub` 6.1 and its sibling `strong-type` 2.0 under
111
- `arcane/sdk/dependencies/`. This URL-key separation prevents the runtime and SDK
112
- dependency versions from aliasing one another. Development serves the selected
113
- app plus that authenticated tree. Packaging copies the same map, app entry, and
114
- physical bytes into `dist/<id>`; targets never resolve through the consumer
115
- workspace's root `node_modules/`.
116
-
117
- In SDK `0.1.2`, the generated map has exactly 86 entries: 73 named
118
- `arcane/*` modules, nine `arcane/entities/*` modules, and these four focused or
119
- compatibility mappings:
106
+ The authenticated physical-v1 tree lives entirely beneath `arcane/`. SDK
107
+ `0.2.1` projects it from two canonical release receipts:
108
+
109
+ | Canonical receipt | Source authority and protocol | Receipt inventory |
110
+ | --- | --- | --- |
111
+ | `runtime/ARCANE_RUNTIME_RELEASE.json` | `sdk-canonical`; `arcane/1`; builder `arcane-sdk-runtime-v1` | 160 files; 3,605,154 bytes; content SHA-256 `5b921d50b6a0cf36a13f7a7dedf96cd3a68104a322e5139136fd4197aa1ca7cb` |
112
+ | `browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json` | `arcane-os-sdk`; `arcane-sdk-browser-runtime/1`; builder `arcane-sdk-browser-runtime-v1` | 25 files; 9,279,974 bytes; content SHA-256 `a9715c4b3aef70ec4042e4738089568d6588877b29582e0f758ed83897b6814f` |
113
+
114
+ The runtime receipt is the current byte authority. Its Arcane OS
115
+ `c540014afe69f14cf5ae60493b7295f36dbcec64` / bundle `0.8.12` record is
116
+ `legacyProjection` provenance, not a second or newer runtime authority. The
117
+ browser receipt binds `event-pubsub` `6.1.0`, `strong-type` `2.0.0`, and
118
+ `@wllama/wllama` `3.6.0`, as well as the browser entry
119
+ `arcane-os/event-manager`. Runtime dependencies stay under
120
+ `arcane/dependencies/`; the SDK event and browser-AI closure stays under
121
+ `arcane/sdk/`. This URL-key separation prevents runtime and SDK dependency
122
+ versions from aliasing one another.
123
+
124
+ Those two receipt inventories contain 185 entries in total. That sum is a
125
+ release-inventory fact, not an import-map entry count and not an assertion about
126
+ one maintained example. The `0.2.1` map deterministically roots every admitted
127
+ top-level runtime ESM plus the authenticated SDK browser roots, then follows
128
+ those roots for runtime entities and dependency compatibility. Application
129
+ source imports do not select the 91 entries. Its public operation receipt is the
130
+ authority for the exact `imports`, `entryCount`, and `excludedModules`;
131
+ reached-file traversal is internal and is not exposed in that receipt. The
132
+ managed graph exposes `arcane-os/event-manager`, `arcane-os/ai/browser-wasm`,
133
+ and `arcane-os/ai/browser-speech`; dependency compatibility mappings are added
134
+ only when authenticated runtime or SDK root traversal observes them.
135
+
136
+ The focused physical targets remain stable when their bindings are reached:
120
137
 
121
138
  | Browser specifier | Physical target |
122
139
  | --- | --- |
123
140
  | `arcane-os/event-manager` | `./arcane/sdk/event-manager.mjs` |
124
141
  | `arcane-os/ai/browser-wasm` | `./arcane/sdk/ai/browser-wasm.mjs` |
142
+ | `arcane-os/ai/browser-speech` | `./arcane/sdk/ai/browser-speech.mjs` |
125
143
  | `event-pubsub` | `./arcane/sdk/dependencies/event-pubsub/index.js` |
126
144
  | `./node_modules/strong-type/index.js` | `./arcane/dependencies/strong-type/index.js` |
127
145
 
@@ -129,7 +147,50 @@ There is no `arcane-os` package-root mapping, bare `strong-type` mapping, or
129
147
  catch-all `arcane/` prefix. Host-internal `CaseEvidenceIndexer.js` is explicitly
130
148
  excluded; classic scripts, workers, stylesheets, and other non-ESM assets use
131
149
  their documented URL or host loading contract rather than invented package
132
- bindings.
150
+ bindings. Development serves the selected app plus the authenticated tree.
151
+ Packaging copies the same map, app entry, and physical bytes into `dist/<id>`;
152
+ targets never resolve through the consumer workspace's root `node_modules/`.
153
+
154
+ `generateImportMap()` is an internal toolchain operation, not a package export.
155
+ Its package path accepts the configured entry plus the deterministic included
156
+ `.html`/`.htm` document inventory. One transaction writes the artifact and the
157
+ same managed JSON into every admitted document. The receipt binds
158
+ `documentPaths`, `documentCount`, and `files`: artifact first, configured entry
159
+ second, then additional documents as `role:"document"`. The public CLI keeps
160
+ its existing two-option command and supplies only the selected entry; packaging
161
+ owns multi-page discovery.
162
+
163
+ An external package and development server expose the authenticated runtime
164
+ inventory at `/ARCANE_RUNTIME_PROJECTION.json`:
165
+
166
+ ```javascript
167
+ {
168
+ schemaVersion: 1,
169
+ kind: 'arcane-app-runtime-projection',
170
+ sdkVersion,
171
+ pathPrefix: 'arcane/',
172
+ fileCount,
173
+ totalBytes,
174
+ contentSha256,
175
+ files: [{path, bytes, sha256}]
176
+ }
177
+ ```
178
+
179
+ The projection contains public paths relative to its declared
180
+ `pathPrefix:'arcane/'` (for example, `modules/...` and `sdk/...`), byte lengths,
181
+ and SHA-256 values and is itself bound by the packaged release inventory. It does
182
+ not expose the private `/ARCANE_APP_RELEASE.json` or replace the underlying
183
+ runtime/browser receipts. Missing, changed, forged, duplicated, or internally
184
+ inconsistent projection data fails `ARCANE_RUNTIME_PROJECTION_INVALID`.
185
+
186
+ External `validateWorkspace()` results also expose a frozen `sdkInstallation`
187
+ authority with exactly `dependencyName`, `packageSource`,
188
+ `canonicalPackageRoot`, `packageName`, `packageVersion`, `runtimeRoot`,
189
+ `browserRuntimeRoot`, `runtimeManifest`, and `browserRuntimeManifest`. A
190
+ workspace may use the canonical dependency name or one exact npm alias such as
191
+ `npm:arcane-os@0.2.1`; the physical package manifest must still identify
192
+ exactly as `arcane-os@0.2.1`. Canonical-plus-alias duplicates, multiple aliases,
193
+ links/junctions, indirect package roots, or version drift fail closed.
133
194
 
134
195
  The imported module can be pure browser logic, standard-Web-API logic, or a
135
196
  client of `globalThis.Arcane`. Import-map resolution is not a new Arcane wire
@@ -164,24 +225,28 @@ heartbeat is event telemetry only and never regenerates browser state.
164
225
  </details>
165
226
 
166
227
  <details>
167
- <summary>SDK browser-runtime admission and exact receipt fields</summary>
228
+ <summary>SDK 0.2.1 browser-runtime admission and exact receipt fields</summary>
168
229
 
169
230
  `arcane.lock.json.sdkBrowserRuntime` persists the trusted manifest path,
170
231
  `manifestSha256`, `contentSha256`, `builder`, `sdkVersion`, and `source` record.
171
- For SDK `0.1.2` those identities are:
232
+ For SDK `0.2.1`, the manifest itself records:
172
233
 
173
234
  ```text
174
235
  manifest: node_modules/arcane-os/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json
175
- manifestSha256: 88395493b411fd5461fbb2bb065ae2b745f6d1672f796583fd248ec97f71f4f7
176
- contentSha256: 5e03f45a732db51cb5a2b2193cc79ecda34501d07a9b2e82e794e5fa37d55d00
236
+ fileCount: 25
237
+ totalBytes: 9279974
238
+ contentSha256: a9715c4b3aef70ec4042e4738089568d6588877b29582e0f758ed83897b6814f
177
239
  builder: arcane-sdk-browser-runtime-v1
178
- sdkVersion: 0.1.2
240
+ sdkVersion: 0.2.1
179
241
  source.protocol: arcane-sdk-browser-runtime/1
180
242
  source.browserEntry: arcane-os/event-manager
181
243
  ```
182
244
 
183
- The `source` record also binds the `arcane-os-sdk` authority/repository and the
184
- exact `event-pubsub` 6.1.0 and `strong-type` 2.0.0 package identities. Before a
245
+ The verifier computes `manifestSha256` over the exact installed manifest and
246
+ binds that value in its process-local receipt and the workspace lock; it must
247
+ not be substituted with `contentSha256`. The `source` record also binds the
248
+ `arcane-os-sdk` authority/repository and the exact `event-pubsub` 6.1.0,
249
+ `strong-type` 2.0.0, and `@wllama/wllama` 3.6.0 package identities. Before a
185
250
  workspace tree is admitted, the same-process verifier returns
186
251
  `schemaVersion`, `kind`, `canonicalLocation`, `rootIdentity`, `manifestPath`,
187
252
  `manifestSha256`, `manifestIdentity`, `builder`, `sdkVersion`, `source`,
@@ -192,6 +257,163 @@ does not recreate authority.
192
257
 
193
258
  </details>
194
259
 
260
+ ## Portable AI provider runtime
261
+
262
+ Application code should select a normalized role, not an internal protocol.
263
+ The exported
264
+ [`getAIProviderRuntime()` singleton](runtime-modules.md#aiproviderruntimejs)
265
+ comes from authenticated runtime bytes and owns independent `llm`, `stt`, and
266
+ `tts` selections. Published SDK `0.2.1` ships browser-WASM LLM and browser
267
+ speech provider/2 adapters but predates the legacy speech correction described
268
+ below. Current source also adapts selected legacy OpenAI LLM/STT/TTS,
269
+ Core-backed Ollama LLM, and admitted Core speech STT/TTS routes into provider/2;
270
+ other native, Core, or cloud routes require an externally supplied compatible
271
+ adapter. The singleton itself is not an authentication or capability token. It
272
+ normalizes inspection, model authority,
273
+ load/unload/dispose, cancellation, stream cleanup, status, and startup
274
+ barriers. Each selected provider retains its real execution requirements.
275
+ `localOnly` fails closed, and failure in one role never authorizes a Core,
276
+ cloud, or different-provider fallback.
277
+
278
+ For a browser-only LLM,
279
+ [`arcane-os/ai/browser-wasm`](ai/browser-wasm.md) exposes `createArcaneAI()`
280
+ and an adapter into the same provider-neutral lifecycle. For browser speech,
281
+ [`arcane-os/ai/browser-speech`](ai/browser-speech.md) creates independent
282
+ Whisper STT and Kokoro TTS providers that register directly with the normalized
283
+ runtime. The SDK supplies mechanism; applications retain model/runtime choice,
284
+ provenance, licenses, prompts, tools, voices, and disclosure policy.
285
+
286
+ ### Browser-WASM LLM lifecycle
287
+
288
+ The shipped browser receipt contains the authenticated Wllama JavaScript/WASM
289
+ engine and its provider/cache/controller mechanism. It contains no model
290
+ weights, default model catalog, CDN fallback, native provider, speech model, or
291
+ application profile. The caller supplies each model as a source authority with
292
+ a nonempty ordered file list, so monolithic and split GGUF models use the same
293
+ contract. HTTPS redirects are followed and the final HTTPS URL is recorded.
294
+ Exact bytes are bound only by the optional expected byte lengths and SHA-256
295
+ values whose matching fieldwise security checks are enabled.
296
+
297
+ On load, the DBOPFS store admits all ordered members and commits the completion
298
+ manifest last. A normal cache miss may fetch only the caller-supplied immutable
299
+ HTTPS sources; `offline:true` performs no model request and admits only a
300
+ compatible completed cache, otherwise it rejects with
301
+ `ARCANE_AI_MODEL_OFFLINE_MISS`. Unload releases the active Wllama session but
302
+ does not silently delete the app-owned cache.
303
+
304
+ SDK `0.2.1` requires WebGPU. Load requests full offload with exactly 99,999 GPU
305
+ layers and admits the model only after observing an adapter, full layer offload,
306
+ buffer and queue work, and a settled fence. `navigator.gpu` presence alone is
307
+ not readiness. There is no CPU fallback, partial-offload success mode, or
308
+ silent switch to native/Core/cloud inference.
309
+
310
+ ### Browser speech lifecycle
311
+
312
+ The browser-speech package contains plain-JavaScript authority, DBOPFS store,
313
+ provider, client, and Worker machinery. It supplies no Whisper or Kokoro
314
+ runtime adapter bytes, model weights, voice bytes, download URL, catalog, or
315
+ cloud fallback. The caller must provide a closed immutable runtime/model
316
+ declaration for each role. Construction validates and freezes its declared
317
+ identity; preparation validates the actual downloaded or cached closed runtime
318
+ graph. The SDK downloads only declared files when permitted, commits its
319
+ completion manifest last, and removes incomplete stored state after a
320
+ cache/install failure. `offline:true` never uses the
321
+ network and rejects a cache miss with `ARCANE_AI_ARTIFACT_OFFLINE_MISS`.
322
+
323
+ Whisper `stt` and Kokoro `tts` each own catalog, inspect, status, load, request,
324
+ unload, and dispose state. They load, cancel, unload, fail, and recover
325
+ independently from the LLM and from one another. Cancellation after Worker use
326
+ begins terminates that role's Worker slot and returns the provider to unloaded;
327
+ a later use must load it again. If shared STT `Blob` decoding is cancelled
328
+ before Worker use, the request rejects while the loaded provider remains ready.
329
+ Speech failure neither disables text chat nor retries through another local,
330
+ native, or cloud provider.
331
+
332
+ ### Persistent chat and document context
333
+
334
+ The SDK runtime owns
335
+ [`DBOPFSDocumentLibrary`](runtime-modules.md#dbopfsdocumentlibraryjs),
336
+ [`DocumentLexicalSearch`](runtime-modules.md#documentlexicalsearchjs), and
337
+ [`PersistentAIChatSession`](runtime-modules.md#persistentaichatsessionjs).
338
+ Document bootstrap is explicit and schema-driven, commits a completed
339
+ generation last, and returns bounded search results with partial read failures
340
+ disclosed. `evaluate()` can instead score a caller-owned source set without
341
+ persisting its bodies, under separate corpus/scoring/output/document budgets.
342
+ A chat session never searches the corpus unless the application
343
+ deliberately wires a document context builder into the request; generated
344
+ document context remains labeled untrusted.
345
+
346
+ Persistent chat maintains bounded live model context plus `ChatEntity`
347
+ history/memory according to the caller's persistence choice. A turn with
348
+ `persist:false` remains coherent in the live session without entering durable
349
+ history or memory. `createArcaneAI(...).createChatSession(options)` binds the
350
+ session and automatic memory work to that same selected LLM controller; it does
351
+ not select a second provider or storage fallback.
352
+
353
+ ### Cancellation and structural tools
354
+
355
+ Cancellation is part of the provider lifecycle, not just a UI decision.
356
+ `AbortSignal`, the normalized role cancel operation, and stream-handle
357
+ `cancel(reason)` propagate to the selected provider. Browser-WASM inference
358
+ requires positive llama cancellation acknowledgement when cancellation is
359
+ required. Browser speech cancellation terminates a Worker only after Worker use
360
+ has begun; cancellation during shared browser decoding leaves the loaded Worker
361
+ ready. Unload always cancels active role work before releasing that role's
362
+ execution state, and superseded late results are rejected rather than committed
363
+ or retried through another provider.
364
+
365
+ Interactive request ownership is latest-request-wins independently for each
366
+ role. A new valid request that reaches admission aborts the active role request
367
+ and waits for its provider promise settlement; stream replacement additionally
368
+ requires confirmed bounded handle cleanup. Only the newest waiting request may
369
+ start after settlement, and request-specific generations prevent superseded
370
+ callbacks from clearing or restoring newer state. The runtime revalidates
371
+ selected-provider readiness and never reloads, switches, or falls back
372
+ implicitly. Generic provider-promise settlement is not a claim that underlying
373
+ work stopped; only a provider's documented positive acknowledgement or
374
+ destructive worker teardown can prove that stronger fact.
375
+
376
+ `startAIRuntime({startTranscription:false})` is the default startup boundary for
377
+ STT. It declines to request a startup STT load; it does not unload a role already
378
+ started through another explicit lifecycle action. A selected unloaded
379
+ transcription provider remains selected and unloaded until a user lifecycle
380
+ intent or explicit `startTranscription:true` opt-in asks the provider owner to
381
+ load it. Neither state observation nor the shared speech component imports a
382
+ model or selects a fallback.
383
+
384
+ The shared speech component owns an `AbortController` for each STT request and
385
+ passes its signal through `AI.fetchSTT()`. Cancel, superseding capture, and
386
+ component teardown abort that signal and suppress late delivery. Whether the
387
+ provider's underlying computation stops remains governed by its own
388
+ cancellation contract. User TTS unmute calls `AI.setSpeechMuted(false)` before
389
+ or with its load intent so the runtime records the unmuted lifecycle preference;
390
+ mute calls `AI.setSpeechMuted(true)`, cancels active synthesis, and unloads TTS.
391
+ The selected TTS model catalog owns `defaultVoice`. AI.js uses a saved OpenAI
392
+ voice only for the OpenAI route and never forwards it to Core or browser Kokoro.
393
+
394
+ LLM tool calls are structural result data only. The SDK never executes a
395
+ handler. The application owns schema validation, authorization, side-effect
396
+ policy, dispatch, and the matching tool-result turn.
397
+
398
+ <details>
399
+ <summary>Portable AI protocol disclosure</summary>
400
+
401
+ The normalized runtime protocol is `arcane-ai-runtime/2`; registered adapters
402
+ implement `arcane-ai-provider/2` and must prove matching
403
+ `arcane-ai-model-authority/1` inspection before load. The browser-WASM component
404
+ receipt is `arcane-ai-browser-wasm/2`; its direct controller adapter uses
405
+ `arcane-ai-adapter/1`, and `adaptV1LlmProvider()` projects that surface into the
406
+ provider/2 LLM role. Browser speech stores identify themselves as
407
+ `arcane-ai-browser-speech-artifacts/1`; that identifier describes the store
408
+ contract, not a model authority, capability grant, or complete-cache receipt.
409
+
410
+ These identifiers normalize lifecycle records. They do not erase provider
411
+ availability: browser providers still require their browser capabilities,
412
+ native providers still require an admitted host and Core method, and cloud
413
+ providers still require explicit selection, network policy, and credentials.
414
+
415
+ </details>
416
+
195
417
  ## Arcane application protocol
196
418
 
197
419
  `globalThis.Arcane.protocol` is `arcane/1`. The shared API wraps transport
@@ -29,7 +29,7 @@ A component file does not register its own custom element. The `<html-import>` h
29
29
  | [`assistant-panel.html`](#assistant-panelhtml) | Reusable assistant drawer, message area, composer, pending/streaming/empty/error state, and actions. | `open()`<br>`close()`<br>`toggle()`<br>`send()`<br>`clear()`<br>`setState()`<br>`focusComposer()`<br>`scrollToEnd()` | `assistant-ready`<br>`assistant-send`<br>`assistant-clear` | DOM-normalized; caller/provider results remain external |
30
30
  | [`calculator.html`](#calculatorhtml) | Calculator keypad and result/error event surface backed by CalculatorEngine. | `calculate()` | `calculator-ready`<br>`calculation-complete`<br>`calculation-error` | Normalized Calculation/error events |
31
31
  | [`chart.html`](#charthtml) | Accessible uPlot line, area, or point chart with normalized options and rows. | `configure()`<br>`populate()`<br>`setData()`<br>`addData()`<br>`update()`<br>`destroy()` | `chart-ready`<br>`chart-remove` | Options/rows normalized; uPlot rendering is vendor-native |
32
- | [`chat.html`](#chathtml) | Shared chat, file upload, streaming, speech, language, availability, and conversation-timebox surface. | `streamMessage()`<br>`setMessageProgress()`<br>`setAIAvailability()`<br>`setInitialSpeechMuted()`<br>`setConversationComplete()`<br>`bindConversationTimebox()`<br>`submitMessage()`<br>`sendMessage()`<br>`languageChanged()` | `chat-ready`<br>`chat-send-message`<br>`chat-send-error`<br>`chat-file-uploaded`<br>`chat-language-changed`<br>`conversation-timebox-error` | UI state normalized; AI/storage/media behavior mixed |
32
+ | [`chat.html`](#chathtml) | Shared chat, visible selected-model activation request, file upload, streaming, speech, language, availability, and conversation-timebox surface. | `streamMessage()`<br>`setMessageProgress()`<br>`setAIAvailability()`<br>`setInitialSpeechMuted()`<br>`setConversationComplete()`<br>`bindConversationTimebox()`<br>`submitMessage()`<br>`sendMessage()`<br>`languageChanged()`<br>`requestAIActivation()`<br>`destroy()` | `chat-ready`<br>`chat-send-message`<br>`chat-send-error`<br>`chat-file-uploaded`<br>`chat-language-changed`<br>`chat-ai-activation-request`<br>`chat-ai-activation-error`<br>`conversation-timebox-error` | UI/runtime state and explicit user activation intent normalized; AI/storage/media behavior mixed |
33
33
  | [`conversation-view.html`](#conversation-viewhtml) | Provider-neutral conversation display, advisory actions, composer, busy state, and status. | `setConversation()`<br>`setBusy()`<br>`setStatus()`<br>`clearComposer()` | `conversation-view-ready`<br>`communication-send`<br>`communication-advisory-action` | DOM-normalized |
34
34
  | [`dashboard-config.html`](#dashboard-confightml) | Selects which normalized chart definitions are visible on a dashboard. | `configure()`<br>`setDefinitions()`<br>`setVisibility()`<br>`getChartOptions()`<br>`getEffectiveVisibility()`<br>`open()`<br>`close()` | `dashboard-config-ready`<br>`dashboard-config-opened`<br>`dashboard-config-closed`<br>`dashboard-config-change` | Fully normalized definitions and visibility |
35
35
  | [`data-maintenance.html`](#data-maintenancehtml) | Runs destructive cleanup of empty chats and memories inside the current app data scope. | `open()` | `data-maintenance-ready`<br>`data-maintenance-complete` | Normalized counts; DBOPFS failures mixed |
@@ -53,7 +53,7 @@ A component file does not register its own custom element. The `<html-import>` h
53
53
  | [`screen-capture.html`](#screen-capturehtml) | Presents image, video, or GIF display-capture workflow. | `capture()` | `screen-capture-ready`<br>`screen-capture-result` | State/result normalized; media permission/codec failures mixed |
54
54
  | [`source-code-viewer.html`](#source-code-viewerhtml) | Renders line-addressable source code with load, error, focus, and state behavior. | `configure()`<br>`load()`<br>`render()`<br>`clear()`<br>`fail()`<br>`focus()`<br>`focusLine()` | `source-code-viewer-ready`<br>`source-code-viewer-state` | Normalized source/state |
55
55
  | [`source-explanation.html`](#source-explanationhtml) | Presents an evidence finding, source selection, explanation, and save state. | `showFinding()`<br>`populate()`<br>`selectSource()`<br>`markSaved()` | `source-explanation-ready`<br>`source-explanation-save`<br>`source-explanation-source-selected` | DOM-normalized |
56
- | [`speech.html`](#speechhtml) | Coordinates speech controls, transcription completion, mute state, and microphone availability. | `configure()`<br>`setAvailability()`<br>`setMuted(); availability`<br>`muted`<br>`initialMuted`<br>`componentReady properties` | `speech-ready`<br>`speech-transcription-complete`<br>`speech-microphone-unavailable` | UI state normalized; AI/media behavior mixed |
56
+ | [`speech.html`](#speechhtml) | Coordinates explicit STT activation, speech controls, transcription completion, mute state, and microphone availability. | `configure()`<br>`setAvailability()`<br>`setMuted()`<br>`requestSTTActivation()`<br>`destroy()`<br>`availability`<br>`muted`<br>`initialMuted`<br>`componentReady` | `speech-ready`<br>`speech-transcription-complete`<br>`speech-transcription-error`<br>`speech-transcription-cancelled`<br>`speech-microphone-unavailable`<br>`speech-stt-activation-request`<br>`speech-stt-activation-error`<br>`speech-tts-lifecycle-error` | Sticky runtime speech readiness, explicit STT activation, request cancellation, and TTS mute lifecycle intent normalized; provider/model authority and media behavior remain external |
57
57
  | [`summary-strip.html`](#summary-striphtml) | Displays compact selectable KPI or summary items. | `configure()`<br>`setItems()`<br>`updateItem()`<br>`clear()` | `summary-strip-ready`<br>`summary-strip-change`<br>`summary-strip-select` | DOM-normalized |
58
58
  | [`table.html`](#tablehtml) | Builds and updates a simple header/body table. | `buildHeader()`<br>`buildTable()` | `table-ready`<br>`header-update`<br>`body-update` | DOM-normalized |
59
59
  | [`task-progress.html`](#task-progresshtml) | Runs and displays a task list with started/change/complete/error state. | `configure()`<br>`setTasks()`<br>`updateTask()`<br>`runTasks()`<br>`clear()` | `task-progress-ready`<br>`task-progress-started`<br>`task-progress-change`<br>`task-progress-complete`<br>`task-progress-error` | Task state normalized; injected task results mixed |
@@ -177,24 +177,63 @@ Shared dependencies: [`ChartLibrary.js`](runtime-modules.md#chartlibraryjs), [`C
177
177
 
178
178
  ### Overview
179
179
 
180
- Shared chat, file upload, streaming, speech, language, availability, and conversation-timebox surface.
180
+ Shared chat, visible selected-model activation request, file upload, streaming,
181
+ speech, language, availability, and conversation-timebox surface.
181
182
 
182
183
  ### Public surface
183
184
 
184
- Methods/properties: `streamMessage()`, `setMessageProgress()`, `setAIAvailability()`, `setInitialSpeechMuted()`, `setConversationComplete()`, `bindConversationTimebox()`, `submitMessage()`, `sendMessage()`, `languageChanged()`.
185
+ Methods/properties: `streamMessage()`, `setMessageProgress()`,
186
+ `setAIAvailability()`, `setInitialSpeechMuted()`,
187
+ `setConversationComplete()`, `bindConversationTimebox()`, `submitMessage()`,
188
+ `sendMessage()`, `languageChanged()`, `requestAIActivation()`, and `destroy()`.
185
189
 
186
190
  `sendMessage(text)` and `languageChanged(text)` are host-overridable async
187
191
  extension callbacks. The component installs warning-only defaults when the host
188
192
  does not supply them; applications may instead consume the corresponding
189
193
  `chat-send-message` and `chat-language-changed` events.
190
194
 
191
- Events: `chat-ready`, `chat-send-message`, `chat-send-error`, `chat-file-uploaded`, `chat-language-changed`, `conversation-timebox-error`.
192
-
193
- Shared dependencies: [`MD.js`](runtime-modules.md#mdjs), [`File.js`](runtime-entities.md#filejs), [`ConversationTimebox.js`](runtime-modules.md#conversationtimeboxjs).
195
+ `setAIAvailability()` remains an LLM compatibility input, but selected sticky
196
+ `AIRuntimeState` LLM state wins over that boolean. STT and TTS readiness always
197
+ comes from sticky runtime role state; the method never forwards compatibility
198
+ speech booleans or synthesizes ready speech roles without an admitted, loaded
199
+ provider.
200
+
201
+ When a selected LLM route is `unloaded` or in `error`, the component exposes a
202
+ keyboard-operable Start/Try again control while Send stays disabled. During
203
+ `loading`, the control becomes Cancel loading and reflects sticky progress.
204
+ The default `requestAIActivation(intent)` forwards frozen
205
+ `{role:'llm',action:'load'|'unload',reason:'user'}` to
206
+ `requestAIRuntimeIntent()`. A host may replace that callback.
207
+
208
+ Before the callback, the component dispatches the bubbles/composed/cancelable
209
+ `chat-ai-activation-request` event with frozen `{intent,state}` detail.
210
+ `preventDefault()` suppresses the callback. A callback failure dispatches the
211
+ bubbles/composed, noncancelable `chat-ai-activation-error` event with frozen
212
+ `{request,error,message}`. A recognized canceled load whose current route is
213
+ `unloaded` or `unloading` is not reported as an activation error.
214
+
215
+ `destroy()` aborts the component's AI-runtime-state subscription, destroys the
216
+ activation controller, removes its `pagehide` listener, calls the optional
217
+ speech controller's `destroy()`, sets `ready` to `false`, and returns
218
+ `undefined`. It does not initiate a provider load or unload.
219
+
220
+ Events: `chat-ready`, `chat-send-message`, `chat-send-error`,
221
+ `chat-file-uploaded`, `chat-language-changed`,
222
+ `chat-ai-activation-request`, `chat-ai-activation-error`, and
223
+ `conversation-timebox-error`.
224
+
225
+ Shared dependencies: [`MD.js`](runtime-modules.md#mdjs), [`File.js`](runtime-entities.md#filejs), [`ConversationTimebox.js`](runtime-modules.md#conversationtimeboxjs), [`AIRuntimeState.js`](runtime-modules.md#airuntimestatejs).
194
226
 
195
227
  ### Availability and normalization
196
228
 
197
- **Browser and supported native WebViews.** UI state normalized; AI/storage/media behavior mixed. HTMLImport + DOM; injected Arcane/provider modules where listed. Native methods remain subject to the bound app's capabilities. [Deep protocol details](protocols.md).
229
+ **Browser and supported native WebViews.** UI and provider-runtime state are
230
+ normalized; AI/storage/media behavior remains mixed. The component emits no
231
+ activation request on import or startup. After the user operates the visible
232
+ control and the component event is not canceled, it publishes a
233
+ capability-neutral user intent; a provider/runtime owner decides whether and
234
+ how to execute the requested load or unload. HTMLImport + DOM; injected
235
+ Arcane/provider modules where listed. Native methods remain subject to the
236
+ bound app's capabilities. [Deep protocol details](protocols.md).
198
237
 
199
238
  ### Example
200
239
 
@@ -812,19 +851,73 @@ Events: `source-explanation-ready`, `source-explanation-save`, `source-explanati
812
851
 
813
852
  ### Overview
814
853
 
815
- Coordinates speech controls, transcription completion, mute state, and microphone availability.
854
+ Coordinates explicit speech-to-text activation, speech controls, transcription
855
+ completion, mute state, and microphone availability.
816
856
 
817
857
  ### Public surface
818
858
 
819
- Methods/properties: `configure()`, `setAvailability()`, `setMuted(); availability`, `muted`, `initialMuted`, `componentReady properties`.
820
-
821
- Events: `speech-ready`, `speech-transcription-complete`, `speech-microphone-unavailable`.
822
-
823
- Shared dependencies: [`AI.js`](runtime-modules.md#aijs), [`DBLS.js`](runtime-modules.md#dblsjs).
859
+ Methods/properties: `configure()`, `setAvailability()`, `setMuted()`,
860
+ `requestSTTActivation()`, `destroy()`, `availability`, `muted`, `initialMuted`,
861
+ and `componentReady`.
862
+
863
+ Events: `speech-ready`, `speech-transcription-complete`,
864
+ `speech-transcription-error`, `speech-transcription-cancelled`,
865
+ `speech-microphone-unavailable`, `speech-stt-activation-request`,
866
+ `speech-stt-activation-error`, and `speech-tts-lifecycle-error`.
867
+
868
+ Shared dependencies: [`AI.js`](runtime-modules.md#aijs),
869
+ [`AIRuntimeState.js`](runtime-modules.md#airuntimestatejs), and
870
+ [`DBLS.js`](runtime-modules.md#dblsjs).
871
+
872
+ The Hold to talk control remains disabled unless sticky STT state is `ready`,
873
+ the role is not busy, and microphone capture is available. For an explicitly
874
+ selected STT route, a separate keyboard-operable control presents Start
875
+ transcription while `unloaded`, Cancel loading while `loading`, a disabled
876
+ Canceling state while `unloading`, and Try again with the sticky error while
877
+ `error`. Selected-unloaded, busy, and error states are shown as distinct facts;
878
+ none is treated as ready.
879
+
880
+ The default `requestSTTActivation(intent)` forwards the frozen
881
+ `{role:'stt',action:'load'|'unload',reason:'user'}` record to
882
+ `requestAIRuntimeIntent()`. Before calling it, the component emits a bubbling,
883
+ composed, cancelable `speech-stt-activation-request` event with frozen
884
+ `{intent,state}` detail. `preventDefault()` suppresses the callback and intent.
885
+ Callback failure emits `speech-stt-activation-error` with frozen
886
+ `{request,error,message}` detail. Cancel loading publishes an `unload` intent;
887
+ only subsequent sticky `unloading` or `unloaded` state confirms lifecycle
888
+ progress, and callback return never proves provider work stopped.
889
+
890
+ The component emits no activation request on import or state observation.
891
+ Provider registration and selection remain inert, and default
892
+ `startTranscription=false` does not request STT during runtime startup. The
893
+ provider/runtime owner decides whether and how to execute a user intent; the
894
+ component never selects a runtime or model, downloads artifacts, reloads after
895
+ failure, or falls back to another provider.
896
+
897
+ `setAvailability()` is compatibility-only for microphone and negative
898
+ unselected-role reports. Positive STT/TTS booleans cannot manufacture a ready
899
+ role, and no compatibility value can replace a selected sticky role. Hold to
900
+ talk stays disabled without sticky ready STT, and compatibility input cannot
901
+ enable a no-selection TTS role or bypass explicit TTS activation.
902
+
903
+ Each transcription request owns an `AbortController`; its signal is passed as
904
+ the third argument to `AI.fetchSTT()`. Cancel, a newer capture, and `destroy()`
905
+ abort that controller and suppress late results. This proves request delivery
906
+ was canceled, not that an uncooperative provider stopped underlying work.
907
+
908
+ User Unmute calls the shared `AI.setSpeechMuted(false)` lifecycle owner before
909
+ or with publishing the TTS load intent, so the runtime can legally load TTS.
910
+ Mute calls `AI.setSpeechMuted(true)`, stops playback, cancels active TTS work,
911
+ and unloads the selected TTS role. Lifecycle failures remain visible through
912
+ `speech-tts-lifecycle-error` and sticky role state.
824
913
 
825
914
  ### Availability and normalization
826
915
 
827
- **Browser and supported native WebViews.** UI state normalized; AI/media behavior mixed. HTMLImport + DOM; injected Arcane/provider modules where listed. Native methods remain subject to the bound app's capabilities. [Deep protocol details](protocols.md).
916
+ **Browser and supported native WebViews.** UI/runtime state and explicit user
917
+ STT activation intent are normalized; provider/model authority and media
918
+ behavior remain external. HTMLImport + DOM; injected Arcane/provider modules
919
+ where listed. Native methods remain subject to the bound app's capabilities.
920
+ [Deep protocol details](protocols.md).
828
921
 
829
922
  ### Example
830
923