arcane-os 0.77.0 → 0.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,58 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "sdkVersion": "0.76.0",
3
+ "sdkVersion": "0.78.0",
4
4
  "environment": {
5
5
  "runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
6
6
  "minimumVersion": "22.23.2 for Node entrypoints",
7
7
  "moduleSystem": "ESM"
8
8
  },
9
- "memberCount": 299,
9
+ "memberCount": 301,
10
10
  "runtimeSubpathPatterns": {
11
11
  "arcane-os/modules/*": "./runtime/arcane/modules/*",
12
12
  "arcane-os/entities/*": "./runtime/arcane/entities/*"
13
13
  },
14
14
  "members": [
15
+ {
16
+ "id": "local-ai-execution-devices:createExecutionDeviceCatalog",
17
+ "name": "createExecutionDeviceCatalog",
18
+ "displayName": "createExecutionDeviceCatalog()",
19
+ "kind": "function",
20
+ "signature": "createExecutionDeviceCatalog({signal}={})",
21
+ "entrypoints": ["arcane-os/local-ai/execution-devices"],
22
+ "primaryImport": "arcane-os/local-ai/execution-devices",
23
+ "group": "Physical execution devices",
24
+ "summary": "Owns lazy physical-device discovery and saved device-ID resolution independently of models and inference.",
25
+ "availability": "Node; Windows discovery is implemented, while other platforms return an unavailable inventory without changing existing AI behavior",
26
+ "protocol": "Physical inventory {platform,state,devices,issues}; target resolution {requestedTarget,resolvedDevice,resolution,reason}",
27
+ "normalization": "Returns async devices({refresh=false,signal}={}), async resolveTarget({executionTarget=null,refresh=false,signal}={}) and dispose(). Construction performs no discovery I/O; calls share cached/coalesced discovery and refresh explicitly requests a new observation. Complete records and issues distinguish ready, partial and unavailable discovery. A saved deviceId resolves as matched, unavailable or unsupported; null returns automatic/engine-default-required without discovery, and a non-null target without a nonempty string deviceId throws TypeError. Physical presence never proves engine support or actual execution. Request cancellation rejects AbortError/ARCANE_CANCELLED and the last departing caller stops the query; retained dispose() joins owned discovery shutdown, and constructor-signal abort disposes the catalog. The direct caller owns disposal; no polling, model loading, preference writes or silent target substitution. See execution-devices.md for all record fields and reasons."
28
+ },
29
+ {
30
+ "id": "core-execution-devices:createExecutionDeviceService",
31
+ "name": "createExecutionDeviceService",
32
+ "displayName": "createExecutionDeviceService()",
33
+ "kind": "function",
34
+ "signature": "createExecutionDeviceService({catalog=createExecutionDeviceCatalog()}={})",
35
+ "entrypoints": ["arcane-os/core/execution-devices"],
36
+ "primaryImport": "arcane-os/core/execution-devices",
37
+ "group": "Physical execution devices",
38
+ "summary": "Registers physical-device discovery as an independent Core service with catalog lifetime ownership.",
39
+ "availability": "Node Core; available independently of selected or loaded model runtimes, with discovery support reported by the catalog",
40
+ "protocol": "arcane/1 localai.devices and localai.resolveTarget",
41
+ "normalization": "Returns the execution-devices service with localai.devices({refresh=false}) and localai.resolveTarget({executionTarget=null,refresh=false}); both forward the Core request signal and return the catalog's complete result. Registration performs no discovery or model work. dispose() owns disposal of the supplied or default catalog; other services may borrow that same catalog without owning it."
42
+ },
15
43
  {
16
44
  "id": "local-ai-decisions:createNativeDecisionModel",
17
45
  "name": "createNativeDecisionModel",
18
46
  "displayName": "createNativeDecisionModel()",
19
47
  "kind": "function",
20
- "signature": "createNativeDecisionModel({onnx,modelAssets,workingDirectory,paths,model='onnx-community/laya-typed-decisions-ONNX',revision='main',dtype='fp32',sessionOptions,executionPreference='gpu',signal}={})",
48
+ "signature": "createNativeDecisionModel({onnx,modelAssets,workingDirectory,paths,model='onnx-community/laya-typed-decisions-ONNX',revision='main',dtype='fp32',sessionOptions,executionPreference='gpu',executionTarget,signal}={})",
21
49
  "entrypoints": ["arcane-os/local-ai/decisions"],
22
50
  "primaryImport": "arcane-os/local-ai/decisions",
23
51
  "group": "Native typed decisions",
24
52
  "summary": "Explicit native Laya FP32 option scoring through an existing ONNX owner and a tokenizer Worker.",
25
53
  "availability": "Node on Windows/Linux/macOS with selected ONNX Runtime and model files; Android needs host adaptation",
26
54
  "protocol": "Shared browser/native decision encoding with native named tensor results",
27
- "normalization": "Returns load/evaluate/classify/current/status/subscribe/unload/dispose; preserves complete separate state/question/options/type and original rows, raw scores and every graph output. Explicit load prepares selected upstream files or uses caller-owned paths; GPU preference attempts advertised providers then CPU. Cancellation retires the shared activation; files remain retained through actual ONNX and tokenizer Worker exit. No chat history or duplicate inference engine."
55
+ "normalization": "Returns load/evaluate/classify/current/status/subscribe/unload/dispose; preserves complete separate state/question/options/type and original rows, raw scores and every graph output. Explicit load prepares selected upstream files or uses caller-owned paths. load({executionTarget,signal}) retains the latest selected target when omitted or undefined, initially the factory selection; null requests automatic resolution, while {deviceId} forwards the saved physical selection to the existing ONNX owner. With no target selection, GPU preference still attempts advertised providers then CPU and explicit session providers retain precedence. Same-target loads share activation; a changed target cancels the earlier request and joins its actual ONNX/tokenizer cleanup before replacement. pendingActivation reports the requested replacement separately; current().execution remains the complete ONNX load record, with requested/resolved/configured/observed target diagnostics in current().execution.execution. Configuration alone does not prove physical execution. Cancellation retires the shared activation; files remain retained through actual ONNX and tokenizer Worker exit. No preference writes, chat history or duplicate inference engine."
28
56
  },
29
57
  {
30
58
  "id": "core-decisions:createNativeDecisionService",
@@ -38,7 +66,7 @@
38
66
  "summary": "Renderer-independent native Laya service reusing the registered local-AI and model-assets owners.",
39
67
  "availability": "Node Core with selected native ONNX and model files",
40
68
  "protocol": "arcane/1 decisions.status/load/evaluate/unload and decisions.state",
41
- "normalization": "Registration starts no model or tokenizer. Explicit load obtains dependency-ready same-process owners and reacquires getONNXRuntime after recovery. Native load/evaluate/classify/current/subscribe/unload/dispose preserve complete results; RPC uses the existing tensor codec. Unload has accepted service lifetime; host shutdown cancels request-lifetime load/evaluation."
69
+ "normalization": "Registration starts no model or tokenizer. Explicit load obtains dependency-ready same-process owners and reacquires getONNXRuntime after recovery. Factory configuration accepts executionTarget; direct load({executionTarget,signal}) and Core decisions.load({executionTarget}) preserve omitted/undefined selection and explicit null automatic selection. Same-target loads coalesce; changed targets cancel pending work and join the prior model's disposal before replacement. pendingActivation remains separate from the full activation execution record and its nested execution diagnostics. Native load/evaluate/classify/current/subscribe/unload/dispose preserve complete results; RPC uses the existing tensor codec. Unload has accepted service lifetime; host shutdown cancels request-lifetime load/evaluation."
42
70
  },
43
71
  {
44
72
  "id": "core-decisions:default",
@@ -353,14 +381,14 @@
353
381
  "name": "createONNXRuntime",
354
382
  "displayName": "createONNXRuntime()",
355
383
  "kind": "function",
356
- "signature": "createONNXRuntime({modulePath,signal,onEvent}={})",
384
+ "signature": "createONNXRuntime({modulePath,onEvent,signal,executionDevices}={})",
357
385
  "entrypoints": ["arcane-os/local-ai/onnx"],
358
386
  "primaryImport": "arcane-os/local-ai/onnx",
359
387
  "group": "Core local AI",
360
388
  "summary": "Owns explicitly loaded ONNX graphs and complete named tensor inference in retained native workers.",
361
389
  "availability": "Node on Windows/Linux/macOS x64/arm64 with prepared onnxruntime-node; Android needs host adaptation",
362
390
  "protocol": "Per-session worker lifecycle with structured-clone tensors and replaying state",
363
- "normalization": "Returns load/run/unload/current/subscribe/close; load accepts executionPreference gpu for advertised provider creation attempts then CPU, while explicit sessionOptions.executionProviders takes precedence. Complete execution diagnostics distinguish configured providers from physical acceleration. stopping revokes new runs while exited records actual Worker exit; recoverable run errors preserve the session. Cleanup joins exit and output delivery even when termination fails."
391
+ "normalization": "Returns load/run/unload/current/subscribe/close. load({id,model,sessionOptions,executionPreference,executionTarget,signal}) preserves existing behavior when executionTarget is omitted: gpu preference tries advertised providers then CPU, while explicit sessionOptions.executionProviders takes precedence. Explicit null requests automatic selection; with gpu preference and no explicit providers it prefers the highest dedicatedMemoryMiB among mapped hardware GPUs, including integrated hardware, then uses existing provider selection when no eligible mapping exists. A saved {deviceId} selects whole-host CPU or an established DirectML adapter mapping; unavailable, unsupported or conflicting targets report LOCAL_AI_EXECUTION_TARGET_UNAVAILABLE, LOCAL_AI_EXECUTION_TARGET_UNSUPPORTED or LOCAL_AI_EXECUTION_TARGET_CONFLICT without silently replacing the selection. Every loaded session reports requestedTarget, resolvedDevice, resolution/reason and configuredTarget {source,executionProviders}; observedTarget remains null because configuration does not prove physical execution. SDK-selected GPU configurations retain CPU operator support. A supplied executionDevices catalog is borrowed; otherwise the runtime owns its lazy catalog. stopping revokes new runs while exited records actual Worker exit; recoverable run errors preserve the session. Cleanup cancels target discovery and joins preparation, exit and output delivery even when termination fails."
364
392
  },
365
393
  {
366
394
  "id": "core-onnx:createCoreONNXRuntime",
@@ -374,7 +402,7 @@
374
402
  "summary": "Accesses local system ONNX through an existing Core client with complete tensor transport.",
375
403
  "availability": "Browser with available Core ONNX service; construction installs no runtime or model",
376
404
  "protocol": "arcane/1 onnx.status/load/run/unload and localai.state",
377
- "normalization": "Returns load/run/unload/inspect/current/subscribe/close; omitted client follows late installation/replacement/retirement and reads current status, while explicit client including null stays fixed. Retirement clears readiness and cancels owned work; late old-client results are suppressed. Preserves typed and string tensors; close removes subscriptions and cancels requests without closing shared Core or implicitly unloading shared models"
405
+ "normalization": "Returns load/run/unload/inspect/current/subscribe/close; load({id,model,sessionOptions,executionPreference,executionTarget,signal,timeoutMs}) forwards provider preference and physical selection while preserving omitted-versus-null target semantics. Complete native execution diagnostics distinguish requested, resolved and configured targets from unobserved physical execution. Omitted client follows late installation/replacement/retirement and reads current status, while explicit client including null stays fixed. Retirement clears readiness and cancels owned work; late old-client results are suppressed. Preserves typed and string tensors; close removes subscriptions and cancels requests without closing shared Core or implicitly unloading shared models"
378
406
  },
379
407
  {
380
408
  "id": "root:createGitTextSnapshot",
@@ -465,14 +493,14 @@
465
493
  "name": "createLocalAIService",
466
494
  "displayName": "createLocalAIService()",
467
495
  "kind": "function",
468
- "signature": "createLocalAIService(configuration,{appRoot,runtimes=[],signal,onEvent,prepare}={})",
496
+ "signature": "createLocalAIService(configuration,{appRoot,runtimes=[],signal,onEvent,prepare,executionDevices}={})",
469
497
  "entrypoints": ["arcane-os/core/local-ai"],
470
498
  "primaryImport": "arcane-os/core/local-ai",
471
499
  "group": "Core local AI",
472
500
  "summary": "Composes selected native llama.cpp, Ollama and ONNX engines as a Core service with retained process/worker ownership and model lifecycle.",
473
501
  "availability": "Node with selected upstream runtime distributions or available configured system services",
474
502
  "protocol": "arcane/1 methods and events; llama.cpp OpenAI HTTP/SSE, Ollama native HTTP/NDJSON and ONNX tensor transport",
475
- "normalization": "Application-owned runtime and model requirements; optional prepare supplies installed records during this service startup. getONNXRuntime() returns the actual prepared ONNX owner for native composition after context.getService('local-ai'); callers reacquire after recovery and retain the acquired handle for cleanup. llama.status/models observe without starting or restarting an engine. Independent services, complete responses and exact owned-process cleanup remain unchanged."
503
+ "normalization": "Application-owned runtime and model requirements; optional prepare supplies installed records during this service startup. Optional executionDevices supplies a borrowed catalog to ONNX; its supplying owner retains catalog disposal responsibility. onnx.load forwards executionPreference and executionTarget with existing model-path resolution, cancellation and omitted-target behavior. getONNXRuntime() returns the actual prepared ONNX owner for native composition after context.getService('local-ai'); callers reacquire after recovery and retain the acquired handle for cleanup. llama.status/models observe without starting or restarting an engine. Independent services, complete responses and exact owned-process cleanup remain unchanged."
476
504
  },
477
505
  {
478
506
  "id": "core-local-ai:default",
@@ -4192,7 +4220,7 @@
4192
4220
  "summary": "Describes existing Core method input, output and semantic shapes.",
4193
4221
  "availability": "Node and browser/bundler",
4194
4222
  "protocol": "arcane/1 method descriptions",
4195
- "normalization": "Descriptive data only; the active host and its registered services remain the authority for method availability"
4223
+ "normalization": "Descriptive data only, including independent localai.devices inventory and localai.resolveTarget physical-selection records; presence does not establish engine support or observed execution. The active host and its registered services remain the authority for method availability"
4196
4224
  },
4197
4225
  {
4198
4226
  "id": "core-contracts:CoreError",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "artifactCount": 93,
12
12
  "javascriptArtifactCount": 91,
13
- "esmExportCount": 374,
13
+ "esmExportCount": 375,
14
14
  "artifacts": [
15
15
  {
16
16
  "file": "runtime/arcane/modules/AI.js",
@@ -31,7 +31,7 @@
31
31
  "availability": "Browser + native bridge + TWiN Cloud",
32
32
  "protocol": "arcane-ai-browser-speech-configuration/1, AIProviderRuntime arcane-ai-provider/2 routes, globalThis.arcaneEvents, TWiN Cloud HTTPS, Arcane.ollama, Arcane.speech, Android WebView bridge",
33
33
  "normalization": "Complete mutable caller-owned browser speech authority, SDK-owned provider registration/replacement/disposal, explicit STT/TTS activation, normalized Whisper STT and Kokoro TTS execution with default capacities 1 and 4 respectively, explicit provider-neutral execution snapshots through status(role,{execution:true}), sticky readiness, canonical window.user readiness without compatibility-event object-identity admission or polling, exact ordered structural calls with required arguments.message, atomic nonblank all-ID tool-result sequencing, complete all-choice streaming/data validation, native Ollama structural adaptation, complete-response then all validated tool callbacks then completion ordering, observed async callbacks, Blob/File transcription, default repeated-formatting-mark removal from cloned outbound TTS input, explicit textFormat:'plain' preservation for complete synthesis/preparation while streaming cleanup remains automatic and caller/visible content stays exact, immediate exact-segment synthesis, per-call voice/speed capture, optional final-segment pause on the existing audio clock, optional per-call terminal playback completion, detached preparation with complete semantic DBOPFS audio reuse and same-owner pending request sharing; each preparation owns its pending activation interest, final-interest cancellation aborts that activation while other interests preserve it, complete cache hits start no model, and cancellation after readiness leaves the ready model loaded; independent prepared playback without a model load for cached audio, ordered audio-clock scheduling, playable audio, active-generation TTS operation-failure routing, mute, and cancellation are normalized; provider/model/runtime/voice policy remains caller-owned. Both speech roles default execution.device to auto: webnn-npu when navigator.ml.createContext is exposed, then webgpu when navigator.gpu is exposed, then wasm for CPU execution. Failed backend loads clean up the pool before trying fresh Workers with the same prepared model and dtype; explicit devices never fall back. STT capacity only accepts 1; TTS accepts 1 through 4. Execution snapshots expose requestedDevice, selectedDevice, maxConcurrentRequests, and activeRequestCount for both roles; selectedDevice is null while unloaded and names the successful upstream session backend request when loaded. It does not prove every operation used the physical NPU: WebNN unsupported operations may use WASM, and compatibility depends on the browser, driver, hardware, and graph.",
34
- "surface": "Native fetchTTS(payload,signal) and fetchSTT(audio,signal) forward caller cancellation separately from their complete Core speech payloads, normalize aborts to AbortError and suppress late results; actual engine interruption remains service-owned. prepareTTSPlayback(payload,signal) silently returns a real Blob or native-speech descriptor; native controls expose finished/released/state/error/pause/resume/stop, and actual release owns the playback lane. fetchTTS and durable prepareTTS report ARCANE_AI_TTS_AUDIO_EXPORT_UNAVAILABLE for native voices. Request-local model overrides remain exact and never mutate the configured model. Browser-speech protocol/event/error/reason constants; default `AI`; read-only `providerRuntime`, `browserSpeechConfiguration`, and `browserSpeechDescriptor`; explicit `providerRuntime.status(role,{execution:true})` snapshots; `streamTTS(text,end,options={})` with optional voice, speed, pauseAfterMs, and waitForPlayback; default speech-input cleanup with SDK-internal preparation metadata and explicit complete-call textFormat:'plain' consumed before engine dispatch; `prepareTTS({parts,storage,identity,textFormat,signal,onState})` returning ordered segments, state, ready, getAudio(index), and cancel(); `prepareAudioOutput()` returning the persistent WebAudio handle {kind,state,contextState,error,subscribe,resume,dispose} without resume, unmute, model load, sources, sound, or gesture listeners; current-state replay and output-specific canonical `AI_AUDIO_OUTPUT_STATE_EVENT` (`ai-audio-output-state`), with ready only for an actually running context; explicit owner disposal observes context-bound cleanup and close; `playPreparedTTS(prepared,{signal,onState,audioOutput})` returning state, error, finished, pause(), resume(), and stop() with one playback lane per AI and observational state/error callbacks; explicit live same-AI audioOutput borrows its context until owner disposal, while omission or null preserves per-playback terminal close; configure/dispose speech, route lifecycle, declaration-validated chat/stream requests with exact ordered calls; optional streamRequest toolText:{name,field} and onToolText(text,call,displayId) for decoded selected argument text with actual call identity, distinct from ordinary prose and terminal tool execution; synthesis/transcription and playback controls; initializes from current canonical user readiness, installs `window.ai`, and projects `ai-ready` plus active-generation `ai-tts-failure`."
34
+ "surface": "Native fetchTTS(payload,signal) and fetchSTT(audio,signal) forward caller cancellation separately from their complete Core speech payloads, normalize aborts to AbortError and suppress late results; actual engine interruption remains service-owned. prepareTTSPlayback(payload,signal) silently returns a real Blob or native-speech descriptor; native controls expose finished/released/state/error/pause/resume/stop, and actual release owns the playback lane. fetchTTS and durable prepareTTS report ARCANE_AI_TTS_AUDIO_EXPORT_UNAVAILABLE for native voices. Request-local model overrides remain exact and never mutate the configured model. Browser-speech protocol/event/error/reason constants; default `AI`; read-only `providerRuntime`, `browserSpeechConfiguration`, and `browserSpeechDescriptor`; explicit `providerRuntime.status(role,{execution:true})` snapshots; `streamTTS(text,end,options={})` with optional voice, speed, pauseAfterMs, and waitForPlayback; default speech-input cleanup with SDK-internal preparation metadata and explicit complete-call textFormat:'plain' consumed before engine dispatch; `prepareTTS({parts,storage,identity,textFormat,signal,onState})` returning ordered segments, state, ready, getAudio(index), and cancel(); async `readPreparedTTS({parts,textFormat,storage,identity,signal,onState})` returning a complete ready handle or null through the same semantic matcher, with no activation, unmute, synthesis, playback, storage writes, or partial repair; manifest, audio availability, and returned-handle reads use DBOPFS createTable:false so absent tables remain absent; supplied adapters own that read-existing behavior; missing matches/audio return null, malformed metadata and storage failures reject, and omitted storage sees only completed matching same-AI in-memory audio; hits replay ready state, misses emit nothing, and the lookup signal detaches on settlement while returned-handle cancellation stays independent; `prepareAudioOutput()` returning the persistent WebAudio handle {kind,state,contextState,error,subscribe,resume,dispose} without resume, unmute, model load, sources, sound, or gesture listeners; current-state replay and output-specific canonical `AI_AUDIO_OUTPUT_STATE_EVENT` (`ai-audio-output-state`), with ready only for an actually running context; explicit owner disposal observes context-bound cleanup and close; `playPreparedTTS(prepared,{signal,onState,audioOutput})` returning state, error, finished, pause(), resume(), and stop() with one playback lane per AI and observational state/error callbacks; explicit live same-AI audioOutput borrows its context until owner disposal, while omission or null preserves per-playback terminal close; configure/dispose speech, route lifecycle, declaration-validated chat/stream requests with exact ordered calls; optional streamRequest toolText:{name,field} and onToolText(text,call,displayId) for decoded selected argument text with actual call identity, distinct from ordinary prose and terminal tool execution; synthesis/transcription and playback controls; initializes from current canonical user readiness, installs `window.ai`, and projects `ai-ready` plus active-generation `ai-tts-failure`."
35
35
  },
36
36
  {
37
37
  "file": "runtime/arcane/modules/AIModelSelectionController.js",
@@ -583,7 +583,7 @@
583
583
  "availability": "Browser / native WebView",
584
584
  "protocol": "OPFS, DBOPFSWorker, Compression Streams, per-realm globalThis.arcaneEvents authority, and native BroadcastChannel for live same-app/storage-partition committed changes",
585
585
  "normalization": "App scope and recognized file parsing are normalized; nonblank unreadable JSONL rows remain complete raw strings, empty-table removal reports removed, absent, or not-empty after a create:false directory lookup and without scanning or recursively clearing a directory, same-named files are preserved, aliases and cached handles are invalidated only when the directory is gone, and unexpected DOM/storage errors are preserved.",
586
- "surface": "default `DBOPFS`; installs `window.dbopfs`, emits `dbopfs-ready`; subscribeChanges(listener,{signal,once}) observes DBOPFS_EVENT_TYPES.change (dbopfs-change), returns idempotent unsubscribe/.dispose, and publishes metadata {applicationId,storagePath,tableName,directoryName,fileName,action,originId,sequence,changeId,remote} after successful storage commits only. Actions are write/append/delete/table-delete; batches preserve partial success, remote receipts invalidate targeted aliases before local publication and never rebroadcast, and pagehide/pageshow closes/reopens the native channel and clears memory caches without replay or fabricated changes. Local subscription/storage continue when BroadcastChannel is unavailable. Table/file/backup APIs include removeEmptyTable(tableName). createCompressedPNG({tableNames,additionalTables,signal}) returns a PNG Blob without download or record writes; omitted names select all, [] none, and supplied tables replace matching saved tables in the export. downloadCompressedPNG(name,options) reuses the same preparation and initiates one download; cancellation prevents it. restoreFromPNG(file,{selectTables}) closes the decoded image after pixel extraction, awaits the optional caller selection/projection before any writes, and rejects DBOPFS_RESTORE_WRITE_FAILED AggregateError after all selected table batches settle when records fail, preserving original errors and failures {tableName,fileName,reason}; successful writes remain. No-options restore and setMany retain their contracts."
586
+ "surface": "default `DBOPFS`; installs `window.dbopfs`, emits `dbopfs-ready`; subscribeChanges(listener,{signal,once}) observes DBOPFS_EVENT_TYPES.change (dbopfs-change), returns idempotent unsubscribe/.dispose, and publishes metadata {applicationId,storagePath,tableName,directoryName,fileName,action,originId,sequence,changeId,remote} after successful storage commits only. Actions are write/append/delete/table-delete; batches preserve partial success, remote receipts invalidate targeted aliases before local publication and never rebroadcast, and pagehide/pageshow closes/reopens the native channel and clears memory caches without replay or fabricated changes. Local subscription/storage continue when BroadcastChannel is unavailable. Table/file/backup APIs include getTableHandle(tableName='',{create=true}={}), readFile(tableName='',fileName='',{createTable=true}={}), get(tableName='',fileName='',force=false,{createTable=true}={}), and removeEmptyTable(tableName). Explicit create:false/createTable:false reads existing tables without creation or joining pending create work; default lazy creation remains unchanged. Low-level missing tables/files reject NotFoundError; get retains parsed caching/force semantics and resolves null only for NotFoundError, propagating other failures. Worker fallback reads already use existing app/table/file handles. createCompressedPNG({tableNames,additionalTables,signal}) returns a PNG Blob without download or record writes; omitted names select all, [] none, and supplied tables replace matching saved tables in the export. downloadCompressedPNG(name,options) reuses the same preparation and initiates one download; cancellation prevents it. restoreFromPNG(file,{selectTables}) closes the decoded image after pixel extraction, awaits the optional caller selection/projection before any writes, and rejects DBOPFS_RESTORE_WRITE_FAILED AggregateError after all selected table batches settle when records fail, preserving original errors and failures {tableName,fileName,reason}; successful writes remain. No-options restore and setMany retain their contracts."
587
587
  },
588
588
  {
589
589
  "file": "runtime/arcane/modules/DBOPFSDocumentLibrary.js",
@@ -1092,13 +1092,14 @@
1092
1092
  "name": "PreparedSpeech.js",
1093
1093
  "kind": "esm",
1094
1094
  "exports": [
1095
- "prepareSpeech"
1095
+ "prepareSpeech",
1096
+ "readPreparedSpeech"
1096
1097
  ],
1097
- "summary": "Owns detached ordered speech preparation, complete semantic audio reuse, and per-caller cancellation behind AI.prepareTTS.",
1098
- "availability": "Browser or native WebView with Blob, AbortController, an injected synthesis callback, and optional ready DBOPFS",
1098
+ "summary": "Owns detached ordered speech preparation, complete semantic audio reuse, cache-only lookup, and per-caller cancellation behind AI.prepareTTS and AI.readPreparedTTS.",
1099
+ "availability": "Browser or native WebView with Blob, AbortController, optional ready DBOPFS, and an injected synthesis callback for generation",
1099
1100
  "protocol": "In-process preparation handle plus caller-owned DBOPFS raw audio and version-1 semantic manifest",
1100
- "normalization": "Preserves complete original parts and JSON-compatible semantic context, shares pending matching work on the same owner/storage group, admits segments in order, stores complete Blobs and MIME metadata, retains successful audio after failure or cancellation, and separates generation readiness from playback.",
1101
- "surface": "Named prepareSpeech({owner,parts,originalParts,selection,segmentation,storage,identity,signal,onState,synthesize}) returns {segments,state,ready,getAudio(index),cancel()}; applications use AI.prepareTTS and AI.playPreparedTTS so the shared AI owner retains segmentation, provider readiness, capacity, and playback."
1101
+ "normalization": "Preserves complete original parts and JSON-compatible semantic context, shares pending matching work on the same owner/storage group, admits segments in order, stores complete Blobs and MIME metadata, retains successful audio after failure or cancellation, and separates generation readiness from playback. Cache-only lookup uses the same semantic matcher, reads every stored audio reference without writes or repair, and returns null for missing matches/audio while preserving genuine read failures.",
1102
+ "surface": "Named prepareSpeech({owner,parts,originalParts,selection,segmentation,storage,identity,signal,onState,synthesize}) returns {segments,state,ready,getAudio(index),cancel()}. Named async readPreparedSpeech({owner,parts,originalParts=parts,selection=null,segmentation=null,storage=null,identity=null,signal=null,onState=null}) returns a complete ready handle or null without synthesis, activation, unmute, playback, or storage writes. Manifest, audio availability, and returned-handle reads pass DBOPFS createTable:false, leaving absent tables absent; supplied adapters own the same read-existing contract. Omitted storage reads only completed matching same-owner in-memory preparations; pending or failed work returns null. A hit replays ready state and a miss emits nothing. The lookup signal detaches on settlement; the returned handle keeps independent explicit cancellation and MIME-preserving getAudio, including later storage errors. Applications use AI.prepareTTS, AI.readPreparedTTS, and AI.playPreparedTTS so the shared AI owner retains segmentation, provider readiness, capacity, and playback."
1102
1103
  },
1103
1104
  {
1104
1105
  "file": "runtime/arcane/modules/PrintView.js",
@@ -169,6 +169,10 @@ process between requests, and joins cleanup during disposal.
169
169
  Optional `prepare({signal,onEvent})` returns resolved runtime records before
170
170
  this service starts its selected engines. The managed development owner uses
171
171
  that callback without delaying the HTTP listener or independent services.
172
+ Optional `executionDevices` supplies the shared execution-device catalog to
173
+ the ONNX owner. Development and generated native composition share one catalog
174
+ per Core lifetime with the independent `execution-devices` service. The ONNX
175
+ owner does not dispose an injected catalog; the composing owner retains it.
172
176
  This factory accepts chat/ONNX requirements; the separate
173
177
  [`createLocalImageService()`](local-image-generation.md#core-service-and-transport)
174
178
  owns image requirements. The development and generated native composition
@@ -384,8 +388,8 @@ try {
384
388
  }
385
389
  ```
386
390
 
387
- The factory accepts `{modulePath,signal?,onEvent?}` and starts no worker until
388
- `load({id,model,sessionOptions?,executionPreference?,signal?})`. The model path is absolute for this
391
+ The factory accepts `{modulePath,signal?,onEvent?,executionDevices?}` and starts no worker until
392
+ `load({id,model,sessionOptions?,executionPreference?,executionTarget?,signal?})`. The model path is absolute for this
389
393
  direct Node API. Session creation defaults to the CPU execution provider;
390
394
  explicit native session options remain caller-owned. The returned session
391
395
  record contains `id`, `model`, `state`, `loaded`, `error`, input/output names and
@@ -400,7 +404,7 @@ A recoverable run error keeps `stopping:false` and the loaded session usable.
400
404
  rejected load or inference. Unload joins worker exit and output delivery even
401
405
  when requesting termination fails; complete cleanup errors remain observable.
402
406
 
403
- Select `executionPreference: 'gpu'` to try the installed runtime's advertised
407
+ With `executionTarget` omitted, select `executionPreference: 'gpu'` to try the installed runtime's advertised
404
408
  CUDA, TensorRT, DirectML, CoreML and WebGPU providers in that order, followed by
405
409
  CPU when no accelerator session can be created. Discovery runs once per load;
406
410
  each advertised candidate receives one actual session-creation attempt with CPU
@@ -413,18 +417,106 @@ DirectML attempts default `enableMemPattern` to `false` and `executionMode` to
413
417
  `'sequential'` as required by that provider; explicit caller values remain
414
418
  unchanged, including on the final CPU attempt.
415
419
 
416
- Automatic GPU selection adds an `execution` record to the loaded session and
417
- its ordinary state events: `preference`, complete `supportedBackends`,
420
+ Every loaded session includes an `execution` record in its ordinary state
421
+ events: `preference`, `requestedTarget`, `resolvedDevice`, `resolution`,
422
+ `reason`, `configuredTarget`, `observedTarget`, complete `supportedBackends`,
418
423
  `selectedProviders`, ordered `attempts` with their full errors, `fallback`, and
419
- `discoveryError`. Each attempt records `executionProviders`, `status`
420
- (`configured` or `failed`), and `error`. `fallback: true` means the SDK created
424
+ `discoveryError`. Each attempt records provider names in `executionProviders`,
425
+ its requested `configuredTarget`, `status` (`configured` or `failed`), and
426
+ `error`. The accepted top-level `configuredTarget` is
427
+ `{source,executionProviders}`; it retains the complete provider options,
428
+ including native numeric `deviceId` values. Its `source` is `cpu-default`,
429
+ `session-options`, `gpu-preference`, `execution-target`, or
430
+ `automatic-physical-target`. It remains `null` until session creation succeeds.
431
+ `fallback: true` means the SDK attempted
421
432
  the final CPU session after GPU preference, including when no GPU candidate was
422
- advertised. Successful creation establishes an accepted provider configuration;
433
+ advertised; `loaded` establishes whether creation succeeded. Successful creation establishes an accepted provider configuration;
423
434
  it does not establish that a GPU executed any graph nodes. If every attempt
424
435
  fails, the load error retains the complete attempt errors and `execution`
425
436
  record. Inference failures retain their existing behavior and do not replay
426
437
  inference on another provider.
427
438
 
439
+ ### Physical execution targets
440
+
441
+ `executionTarget: {deviceId}` selects a physical identifier returned by the
442
+ [execution-device catalog](execution-devices.md). The identifier is separate
443
+ from an ONNX provider's numeric device ordinal. `executionTarget: null` requests
444
+ automatic selection for this new session. Omission keeps the previous provider
445
+ selection and exact explicit `sessionOptions` behavior described above.
446
+ Generic ONNX sessions still require unloading a live ID before replacement;
447
+ passing another target cannot silently replace that running session.
448
+
449
+ The current physical routes are the catalog's whole-host CPU (`deviceId: 'cpu'`)
450
+ and GPUs with an actual `addresses.dxgiAdapterIndex` when the installed ONNX
451
+ binding advertises DirectML. DirectML receives
452
+ `{name: 'dml', deviceId: dxgiAdapterIndex}` and retains CPU participation for
453
+ unsupported graph operators. The index comes from DXGI enumeration joined to
454
+ the physical device; a display order, DXCore order or CUDA ordinal is not
455
+ substituted. No CPU socket/core affinity, physical CUDA mapping or NPU provider
456
+ support is inferred from inventory.
457
+
458
+ An explicit physical target gets one configuration attempt. Missing hardware,
459
+ unsupported provider mapping and contradictory explicit session options report
460
+ `LOCAL_AI_EXECUTION_TARGET_UNAVAILABLE`,
461
+ `LOCAL_AI_EXECUTION_TARGET_UNSUPPORTED`, or
462
+ `LOCAL_AI_EXECUTION_TARGET_CONFLICT`, respectively. An explicit provider list
463
+ must already select the mapped target; it is not overwritten. An automatic
464
+ target conflicts with a fixed provider `deviceId` in explicit session options.
465
+ Matching DirectML options retain caller-authored values and receive only the
466
+ omitted `enableMemPattern: false` and `executionMode: 'sequential'` defaults.
467
+ Native session-creation failures preserve their original errors. The SDK does
468
+ not retry a physical target on another adapter or provider.
469
+
470
+ For `executionTarget: null` with `executionPreference: 'gpu'` and no explicit
471
+ provider list, automatic selection chooses the largest known positive
472
+ `dedicatedMemoryMiB` among present hardware GPUs with a stable ID and actual
473
+ DirectML mapping, when that provider is advertised. An integrated GPU remains
474
+ eligible when it meets those same conditions. Once selected, that adapter
475
+ gets one attempt. If no such candidate is established, the existing ordered
476
+ provider attempts remain available and report their unresolved physical
477
+ identity honestly. Omitted targets keep their existing selection behavior.
478
+
479
+ ```js
480
+ await onnx.load({
481
+ id: 'cheese-radar',
482
+ model: absoluteModelPath,
483
+ executionPreference: 'gpu',
484
+ executionTarget: null
485
+ });
486
+ ```
487
+
488
+ `requestedTarget` preserves the requested physical record, or `null` for no
489
+ explicit physical choice. `resolvedDevice` is the matched catalog record or
490
+ `null`; `resolution` describes physical resolution, not engine compatibility.
491
+ During initial physical discovery it is `null`; the resolver then supplies
492
+ `matched`, `automatic`, `unavailable` or `unsupported`. `reason` explains the
493
+ selection or inability. `observedTarget` remains `null`: ONNX Runtime Node
494
+ exposes no actual physical placement observation here, including after a
495
+ successful run. Accepted GPU configuration does not establish that every node
496
+ ran on that GPU.
497
+
498
+ The runtime creates a lazy catalog only when `executionDevices` is omitted and
499
+ disposes only that owned catalog at close. Catalog preparation belongs to the
500
+ loading session, follows its cancellation and is joined during release. It
501
+ does not serialize independent sessions or delay browser rendering.
502
+
503
+ ### What Settings can know before loading
504
+
505
+ `localai.devices` and `localai.resolveTarget` belong to the independent
506
+ `execution-devices` service and do not load a model or wait for model-runtime
507
+ preparation. They establish detected hardware and physical identity only.
508
+ Settings can show those records and retain an application-owned preference
509
+ keyed by provider/model. A matched physical ID is not a confirmed selectable
510
+ target for every engine.
511
+
512
+ The current ONNX binding's compiled-provider discovery runs in the session
513
+ worker at an explicit load. There is no model-free ONNX capability query in
514
+ this interface. Before that observation, engine compatibility is unknown;
515
+ do not label every enumerated GPU or NPU supported. An advertised provider
516
+ still does not establish usable provider libraries, model compatibility or
517
+ actual execution. Consume the explicit load's configuration or complete
518
+ failure, and keep observed placement unknown when it is not supplied.
519
+
428
520
  `run({id,feeds,fetches?,runOptions?,signal?})` returns the complete output-name
429
521
  map of `{type,dims,data}` tensors. Input records use that same shape with native
430
522
  typed data or string arrays. Optional `fetches` selects output names or supplies
@@ -456,7 +548,7 @@ Other sessions retain their own lifetimes.
456
548
  | Core method | Parameters | Result |
457
549
  | --- | --- | --- |
458
550
  | `onnx.status` | `{}` | Runtime availability and current `models` session records |
459
- | `onnx.load` | `{id,model,sessionOptions?}` | Loaded session; relative model paths resolve from `appRoot` |
551
+ | `onnx.load` | `{id,model,sessionOptions?,executionPreference?,executionTarget?}` | Loaded session; relative model paths resolve from `appRoot` |
460
552
  | `onnx.run` | `{id,feeds,fetches?,runOptions?}` | Complete encoded tensor output map |
461
553
  | `onnx.unload` | `{id}` | Released session record |
462
554
 
@@ -477,7 +569,9 @@ onnx.close();
477
569
 
478
570
  The accessor exposes `load`, `run`, `unload`, `inspect`, `current`, `subscribe`
479
571
  and `close`. Request methods accept `signal` and optional `timeoutMs` (default
480
- `0`, no elapsed-time cutoff). It preserves full typed tensor values, including
572
+ `0`, no elapsed-time cutoff). Load forwards `executionPreference` and preserves
573
+ the native load's distinction between omitted and null `executionTarget`.
574
+ Tensor transport preserves full typed tensor values, including
481
575
  64-bit integers, floating-point special values and strings, through the
482
576
  transport codec. `current()` adds `busy` and `closed` to its latest Core state.
483
577
  Closing the accessor cancels its requests and subscriptions; model unloading
@@ -58,7 +58,7 @@ name to `decisions`. It exposes `load`, `evaluate`, `classify` (an alias for
58
58
  | Method | Parameters and result |
59
59
  | --- | --- |
60
60
  | `decisions.status` | Current lifecycle snapshot; does not load anything. |
61
- | `decisions.load` | Explicit activation; returns the ready lifecycle snapshot. The request signal cancels activation. |
61
+ | `decisions.load` | `{executionTarget?}`, where the target is `{deviceId:string}` or `null`; explicit activation or target replacement, returning the ready lifecycle snapshot. The request signal cancels activation. |
62
62
  | `decisions.evaluate` | `{rows, runOptions?}`; returns `{decisions, outputs}`. The request signal cancels the active model operation. |
63
63
  | `decisions.unload` | Releases the activation. Service-lifetime cleanup continues after the caller disconnects. |
64
64
 
@@ -111,12 +111,66 @@ is loaded by this API.
111
111
  This graph selection requires `dtype:'fp32'`, which is also the default; another
112
112
  dtype reports the incompatible selection rather than substituting a graph.
113
113
  `sessionOptions` and `runOptions` pass to the existing native ONNX API unchanged.
114
- `executionPreference` defaults to `gpu` here: actual advertised native GPU
115
- provider creation is attempted, followed by an honest CPU fallback. Explicit
116
- `sessionOptions.executionProviders` takes precedence. The underlying generic
117
- ONNX factory continues to default to CPU. Accepted provider configuration is
118
- not evidence that GPU nodes executed. Full provider-attempt diagnostics remain
119
- in `execution`; see [native ONNX sessions](local-ai.md#native-onnx-sessions).
114
+ With no `executionTarget` selection, `executionPreference` defaults to `gpu`
115
+ here: actual advertised native GPU provider creation is attempted, followed by
116
+ an honest CPU fallback. Explicit `sessionOptions.executionProviders` takes
117
+ precedence on that unchanged path. The underlying generic ONNX factory
118
+ continues to default to CPU. Accepted provider configuration is not evidence
119
+ that GPU nodes executed. Full provider-attempt diagnostics remain in
120
+ `execution`; see [native ONNX sessions](local-ai.md#native-onnx-sessions).
121
+
122
+ ## Select an activation's execution device
123
+
124
+ Both factories accept `executionTarget: {deviceId:string}` or `null` in their
125
+ configuration. Direct model and service calls accept
126
+ `load({executionTarget, signal})`; the Core `decisions.load` method carries the
127
+ same target in its parameters. Use an actual stable ID from the shared
128
+ [physical device catalog](execution-devices.md), or the documented whole-host
129
+ CPU ID:
130
+
131
+ ```js
132
+ await decisions.load({executionTarget: {deviceId: 'cpu'}});
133
+ // A different explicit selection drains the current activation and replaces it.
134
+ await decisions.load({executionTarget: null});
135
+ ```
136
+
137
+ Omitting `executionTarget`, or supplying `undefined`, retains the most recent
138
+ explicit selection, initially the factory's selection. When neither supplies a
139
+ target, existing constructor, session-option and provider-default behavior
140
+ remains unchanged. Explicit `null` requests automatic device resolution at the
141
+ new activation boundary. It remains distinct from omission. The selection
142
+ stays available for a later explicit load after unloading or a failed load;
143
+ the SDK creates no preference store and does not change application preferences.
144
+
145
+ The decision owner forwards the target to its existing ONNX owner. That owner
146
+ resolves the physical ID, supported provider and provider-specific address.
147
+ An unavailable or unsupported explicit target reports its actual failure; it
148
+ does not silently select another adapter or a cloud provider. GPU selection
149
+ does not require every graph node to execute on the GPU: supported CPU graph
150
+ partitioning remains an engine concern. See the native ONNX contract for exact
151
+ target routing and automatic-selection behavior.
152
+
153
+ The existing `current().execution` field remains the complete ONNX session load
154
+ record for that activation. Its `execution` member carries `requestedTarget`,
155
+ `resolvedDevice`, `resolution`, `configuredTarget` and `observedTarget`, alongside
156
+ provider-attempt diagnostics. These distinguish requested selection, physical
157
+ resolution, accepted configuration and actual execution evidence. An unknown
158
+ `observedTarget` remains `null`; successful configuration alone does not establish
159
+ which device executed graph nodes.
160
+
161
+ `pendingActivation` is separately `null` or `{executionTarget}` while a load or
162
+ replacement is pending; its `executionTarget` property is omitted at JSON
163
+ transport when the selection is `undefined`. A pending target never overwrites the previous
164
+ activation's `execution` record. The old record remains attributable to the
165
+ retiring activation until its cleanup completes, then the new activation owns
166
+ its own record. A request is not a claim that the requested device is running.
167
+
168
+ Concurrent loads for the same target share their pending activation. A
169
+ different target supersedes the earlier pending request, which rejects with
170
+ cancellation instead of returning another target's ready result. Replacement
171
+ joins this model's actual tokenizer/native cleanup before creating its
172
+ successor. Independent model owners retain their independent workers and
173
+ lifetimes; there is no all-model loading barrier or trial inference.
120
174
 
121
175
  ## Complete inputs and outputs
122
176
 
@@ -148,14 +202,15 @@ runtime; sharing the encoding implementation alone does not establish it.
148
202
  ## Lifetime and cancellation
149
203
 
150
204
  `current()` returns `{family, model, revision, dtype, state, loaded, busy,
151
- activeRequests, progress, error, execution}`. States include `unloaded`,
205
+ activeRequests, progress, error, execution, pendingActivation}`. States include
206
+ `unloaded`,
152
207
  `loading`, `ready`, `unloading`, `disposing`, `disposed` and `error`. Semantic
153
208
  progress includes model preparation, tokenization and evaluation; downloaded
154
209
  members report files, not byte progress. Complete technical errors belong in
155
210
  developer diagnostics, while the application owns its user-facing status.
156
211
 
157
- Concurrent callers share one explicit activation. Cancelling a load or
158
- evaluation cancels that activation and its outstanding operations, because the
212
+ Concurrent callers selecting the same target share one explicit activation.
213
+ Cancelling a load or evaluation cancels that activation and its outstanding operations, because the
159
214
  native session and tokenizer have one owner. Use separately owned models when
160
215
  independent cancellation is required. The construction `signal` governs the
161
216
  whole model lifetime. `unload()` permits another explicit load after successful
@@ -121,7 +121,7 @@ own asynchronous work, cancellation, and backpressure.
121
121
  | [`OpenMeteoWeatherProvider.js`](#openmeteoweatherproviderjs) | esm | Searches and loads Open-Meteo data into complete mutable Arcane weather entities. | Browser / native WebView / server with fetch + cloud | Provider data normalized to mutable entities; transport errors mixed. |
122
122
  | [`PersistentAIChatSession.js`](#persistentaichatsessionjs) | esm | Adds explicit retained-history/memory policy to complete configured chat without changing DBOPFS or ChatEntity semantics. | Browser / native WebView with DBOPFS and configured chat | Retained context commits atomically; `persist:false` turns are one-operation-only. |
123
123
  | [`PreferenceStore.js`](#preferencestorejs) | esm | Loads and updates schema-defined app preferences through native storage with a narrow browser fallback. | Browser/native hybrid | Complete values and operation context retained; exact optional namespace absence or existing Android unsupported capability selects the existing local adapter; partial-service and genuine failures remain errors; an advertised rejected batch is never retried serially. |
124
- | [`PreparedSpeech.js`](#preparedspeechjs) | esm | Owns detached ordered preparation, semantic audio reuse, and per-caller cancellation behind AI.prepareTTS. | Browser / native WebView with injected synthesis and optional DBOPFS | Complete original inputs, ordered audio metadata, durable reuse, and observable preparation results. |
124
+ | [`PreparedSpeech.js`](#preparedspeechjs) | esm | Owns detached ordered preparation, semantic audio reuse, cache-only lookup, and per-caller cancellation behind AI.prepareTTS and AI.readPreparedTTS. | Browser / native WebView with optional DBOPFS and injected synthesis for generation | Complete original inputs, ordered audio metadata, durable reuse, and observable preparation results. |
125
125
  | [`PrintView.js`](#printviewjs) | esm | Prints current rendered content and title through one document-owned print lifecycle. | Browser / supported native WebView print implementation | Complete rendered snapshot, preparation cancellation, resource lifetime, and honest request result. |
126
126
  | [`QRCode.min.js`](#qrcodeminjs) | classic-script | Vendored QRCode generator for DOM, canvas, SVG, and image output. | Browser vendor script | Vendor-native. |
127
127
  | [`Questionnaire.js`](#questionnairejs) | esm | Evaluates whether a one-time questionnaire prompt is due without performing the prompt. | Cross-host | Normalized conservative boolean. |
@@ -171,6 +171,7 @@ default `AI`; read-only `providerRuntime`, `speechActivationPending`, `browserSp
171
171
  read-only `ttsSegmentation`, `configureTTSSegmentation()`,
172
172
  `streamTTS(text='',end=false,options={})`,
173
173
  `prepareTTS({parts,textFormat,storage,identity,signal,onState})`,
174
+ `readPreparedTTS({parts,textFormat,storage,identity,signal,onState})`,
174
175
  `prepareAudioOutput()`, `playPreparedTTS(prepared,{signal,onState,audioOutput})`,
175
176
  `finishTTS()`, `prepareTTSPlayback()`, `fetchTTS()`, `fetchSTT()`, `stopAudio()`, `resumeAudio()`,
176
177
  `playAudio()`; consumes `user-entity-loaded` and `arcane-ollama-ready`,
@@ -497,6 +498,34 @@ cancelling one handle does not cancel another active matching caller.
497
498
  Preparation cancellation prevents later synthesis and releases its activation
498
499
  interest. The provider's operation remains responsible for actual cleanup.
499
500
 
501
+ `await readPreparedTTS({parts,textFormat,storage,identity,signal,onState})`
502
+ returns a complete ready preparation handle or `null`. It uses the same original
503
+ parts, selected speech configuration, voice/speed/pause values, text treatment,
504
+ segmentation, and application identity as `prepareTTS()` for semantic matching.
505
+ It never activates or unmutes a provider, synthesizes, starts playback, writes
506
+ storage, or repairs a partial record. With storage, it reads the existing
507
+ manifest and every ordered audio reference through the existing per-key storage
508
+ queue. It passes `{createTable:false}` to DBOPFS manifest and audio reads,
509
+ including later handle reads, so missing tables stay absent. Supplied database
510
+ adapters are responsible for honoring that read-existing contract.
511
+ No matching record or any missing audio returns `null`; unreadable
512
+ metadata and other storage errors reject. Without storage, only a completed,
513
+ matching, same-AI retained in-memory preparation can be returned; pending or
514
+ failed work returns `null`.
515
+
516
+ A hit returns `{segments,state,ready,getAudio(index),cancel()}` with `state`
517
+ and every segment already `ready`; `onState` synchronously replays that state.
518
+ A miss emits no state callback. The lookup signal rejects cancellation as
519
+ `AbortError` and detaches when lookup settles; aborting it after success does
520
+ not cancel the returned handle. An already-started non-abortable database read
521
+ may finish after cancellation; observed cancellation prevents subsequent reads
522
+ or a returned handle. The handle's explicit `cancel()` retains its
523
+ own lifetime. Durable `getAudio(index)` reopens the stored file with its retained
524
+ MIME type, so a later file removal remains an observable error. Native speech
525
+ without audio export rejects with `ARCANE_AI_TTS_AUDIO_EXPORT_UNAVAILABLE`, as
526
+ `prepareTTS()` does. Pass a returned handle to `playPreparedTTS()` only when
527
+ playback is wanted.
528
+
500
529
  `playPreparedTTS(prepared,{signal,onState,audioOutput})` can attach immediately. Its returned
501
530
  `{state,error,finished,pause(),resume(),stop()}` handle schedules segments in
502
531
  their original order using the existing AI audio clock. `finished` resolves `true` after
@@ -2254,6 +2283,23 @@ default `DBOPFS`; installs `window.dbopfs`, emits `dbopfs-ready`; table/file/bac
2254
2283
 
2255
2284
  Exact exports: `DBOPFS_EVENT_TYPES`, `DBOPFS_REASONS`, `default`.
2256
2285
 
2286
+ `getTableHandle(tableName='', {create=true}={})` lazily opens or creates the
2287
+ requested table by default. `{create:false}` looks up only an existing table
2288
+ without joining a pending create request; a missing table rejects with the
2289
+ platform's `NotFoundError`. The existing `memories` / `memory` alias is retained.
2290
+ `readFile(tableName='', fileName='', {createTable=true}={})` returns the complete
2291
+ raw file. `{createTable:false}` also prevents table creation, and missing tables
2292
+ or files reject with `NotFoundError`. Its synchronous-access worker fallback
2293
+ already opens existing app/table/file handles without creating them.
2294
+
2295
+ `get(tableName='', fileName='', force=false, {createTable=true}={})` retains its
2296
+ existing parsed-value cache and `force` behavior, forwarding `createTable` when
2297
+ it reads a file. On a read, `NotFoundError` resolves `null`; other errors reject.
2298
+ Use `get(tableName,fileName,true,{createTable:false})` for an existing-storage
2299
+ lookup that does not return an earlier cached value. Omitted options preserve the previous
2300
+ lazy table-creation behavior. These options change neither write operations nor
2301
+ the owning app scope's initialization.
2302
+
2257
2303
  `subscribeChanges(listener, {signal, once})` observes
2258
2304
  `DBOPFS_EVENT_TYPES.change` (`dbopfs-change`) through the existing canonical
2259
2305
  `arcane-os/event-manager` source. It returns an idempotent unsubscribe function
@@ -3649,10 +3695,11 @@ console.log(Object.keys(module));
3649
3695
 
3650
3696
  ### Overview
3651
3697
 
3652
- Shared preparation mechanism used by `AI.prepareTTS()`. It owns ordered
3653
- generation admission, same-owner request sharing, complete audio storage and
3654
- semantic reuse, and each caller's preparation lifetime. It does not construct
3655
- an audio context, play speech, or select application content.
3698
+ Shared preparation mechanism used by `AI.prepareTTS()` and `AI.readPreparedTTS()`.
3699
+ It owns ordered generation admission, same-owner request sharing, complete audio
3700
+ storage and semantic reuse, cache-only lookup, and each caller's preparation
3701
+ lifetime. It does not construct an audio context, play speech, or select
3702
+ application content.
3656
3703
 
3657
3704
  ### Public surface
3658
3705
 
@@ -3661,10 +3708,29 @@ segmentation=null,storage=null,identity=null,signal=null,onState,synthesize})`.
3661
3708
  The owning AI supplies already segmented speech parts, complete original parts,
3662
3709
  its selection snapshot, and its synthesis callback. The returned handle is
3663
3710
  `{segments,state,ready,getAudio(index),cancel()}`. Applications use
3664
- `AI.prepareTTS()` and `AI.playPreparedTTS()` so the existing AI owner retains
3665
- automatic formatting cleanup, segmentation, provider readiness/capacity, and
3711
+ `AI.prepareTTS()`, `AI.readPreparedTTS()`, and `AI.playPreparedTTS()` so the existing
3712
+ AI owner retains automatic formatting cleanup, segmentation, provider readiness/capacity, and
3666
3713
  ordered playback. See the [complete preparation contract](ai/browser-speech.md#prepare-narration-once-and-replay-stored-audio).
3667
3714
 
3715
+ Named async `readPreparedSpeech({owner,parts,originalParts=parts,selection=null,
3716
+ segmentation=null,storage=null,identity=null,signal=null,onState=null})` uses
3717
+ the same complete semantic matcher and resolves a ready preparation handle or
3718
+ `null`. It performs no synthesis, model activation, playback, storage writes,
3719
+ or repair. A durable lookup reads the existing manifest and every audio file;
3720
+ missing matching records or audio return `null`, while malformed metadata and
3721
+ other read failures reject. Manifest, audio availability, and returned-handle
3722
+ audio reads pass `{createTable:false}` to DBOPFS so a missing table is never
3723
+ created; supplied adapters own the same read-existing behavior. Without storage
3724
+ it reads only a completed matching same-owner in-memory preparation, never
3725
+ pending or failed work. A hit replays
3726
+ ready state through `onState`; a miss emits nothing. Lookup cancellation rejects
3727
+ with `AbortError` and is detached on settlement. After success the returned
3728
+ handle's explicit `cancel()` is independent of the lookup signal; its `ready`
3729
+ resolves the complete record and `getAudio(index)` preserves the existing MIME
3730
+ and later-read error behavior. Already-started non-abortable database reads may
3731
+ settle after cancellation; no further reads or handle are returned after the
3732
+ cancellation is observed.
3733
+
3668
3734
  `storage:{db,table,key}` is optional; the ready DBOPFS instance supplies
3669
3735
  `get`, `set`, `readFile`, and `writeFile`. JSON-compatible semantic inputs stay
3670
3736
  complete in the version-1 manifest. Raw audio is persisted separately and its
@@ -3672,13 +3738,14 @@ MIME type is retained in metadata. Storage mutation serializes by database,
3672
3738
  table, and key within the realm. Synthesis sharing is scoped to the same owner
3673
3739
  and matching semantic inputs/storage; playback remains outside this module.
3674
3740
 
3675
- Exact exports: `prepareSpeech`.
3741
+ Exact exports: `prepareSpeech`, `readPreparedSpeech`.
3676
3742
 
3677
3743
  ### Availability and normalization
3678
3744
 
3679
- **Browser or native WebView with Blob, AbortController, an injected synthesis
3680
- callback, and optional ready DBOPFS.** Import creates no provider or
3681
- playback. Calling the preparation function starts owned asynchronous work.
3745
+ **Browser or native WebView with Blob, AbortController, optional ready DBOPFS,
3746
+ and an injected synthesis callback for generation.** Import creates no provider or
3747
+ playback. Calling `prepareSpeech` starts owned asynchronous generation/reuse;
3748
+ `readPreparedSpeech` only reads existing complete audio.
3682
3749
  `ready` rejects complete synthesis/storage failures or `AbortError` after
3683
3750
  cancellation; successful audio remains available for reuse. Malformed part,
3684
3751
  storage, or semantic metadata inputs throw `TypeError`; an invalid segment