arcane-os 0.95.0 → 0.96.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.
@@ -115,6 +115,8 @@ own asynchronous work, cancellation, and backpressure.
115
115
  | [`MemoryRecords.js`](#memoryrecordsjs) | esm | Normalizes memory content and detects meaningful stored memory. | Cross-host | Fully normalized string/boolean results. |
116
116
  | [`MessageAdvisory.js`](#messageadvisoryjs) | esm | Normalizes message content advisories and contains per-message inspection failures. | Cross-host | Normalized advisory records; inspector failures converted to unavailable results. |
117
117
  | [`ModelDefinition.js`](#modeldefinitionjs) | esm | Parses the deterministic packaged Modelfile subset and extracts the SYSTEM prompt. | Cross-host | Complete mutable definition with coded malformed-input errors. |
118
+ | [`NativeSpeechCapture.js`](#nativespeechcapturejs) | esm | Connects microphone PCM to one retained Core recognition stream. | Browser / native WebView with streaming Core speech | Complete snapshots, retained endpoint scope and joined capture/native cleanup. |
119
+ | [`NativeSpeechPlayback.js`](#nativespeechplaybackjs) | esm | Prepares and plays ordered native PCM using shared Web Audio. | Browser / native WebView with streaming speech | Inert preparation, exact audio order and separate playback/release completion. |
118
120
  | [`Ollama.js`](#ollamajs) | esm | Provides the first-class Arcane Ollama client without direct access to localhost:11434. | Native bridge | Principal methods preserve provider-native envelopes; readiness/text/unload helpers normalize. |
119
121
  | [`OllamaModelIdentifier.js`](#ollamamodelidentifierjs) | esm | Validates and canonicalizes the syntax of Ollama model identifiers without granting model admission. | Cross-host | Fully normalized string/boolean result. |
120
122
  | [`OllamaSettings.js`](#ollamasettingsjs) | esm | Defines complete runtime/service preference schemas and deterministic Arcane brain alias names. | Cross-host | Fully normalized settings/name contract. |
@@ -155,6 +157,12 @@ own asynchronous work, cancellation, and backpressure.
155
157
 
156
158
  ### Overview
157
159
 
160
+ Explicit speech-only construction uses
161
+ `new AI(null, 'LOCAL_SPEACH', 'LOCAL_SPEACH', null, ttsModel, sttModel)`.
162
+ Both LLM arguments must be `null`; no default LLM is selected or activated.
163
+ Omitted or empty arguments retain historical defaults, and an existing
164
+ `window.ai` singleton is reused. Later `configureProviders` can select an LLM.
165
+
158
166
  Provider-selectable chat, speech-to-text, text-to-speech, tool calling,
159
167
  structured output, streaming, bounded synthesis, and ordered audio-clock
160
168
  playback.
@@ -174,6 +182,7 @@ read-only `ttsSegmentation`, `configureTTSSegmentation()`,
174
182
  `readPreparedTTS({parts,textFormat,storage,identity,signal,onState})`,
175
183
  `prepareAudioOutput()`, `playPreparedTTS(prepared,{signal,onState,audioOutput})`,
176
184
  `finishTTS()`, `prepareTTSPlayback()`, `fetchTTS()`, `fetchSTT()`, `stopAudio()`, `resumeAudio()`,
185
+ `supportsTranscriptionCapture()`, `createTranscriptionCapture(options={})`,
177
186
  `playAudio()`; consumes `user-entity-loaded` and `arcane-ollama-ready`,
178
187
  installs `window.ai`, and emits `ai-ready`, `ai-tts-failure`, and
179
188
  `AI_AUDIO_OUTPUT_STATE_EVENT` (`'ai-audio-output-state'`).
@@ -415,7 +424,7 @@ physical accelerator; WebNN may execute unsupported operations through WASM.
415
424
  Calling `status()` without options keeps
416
425
  the existing sticky lifecycle snapshot and does not inspect provider execution.
417
426
  Provider inspection failures are surfaced to the caller.
418
- `fetchTTS({model,voice,input,responseFormat,speed},signal,preparation={})` accepts the public
427
+ `fetchTTS({model,voice,input,responseFormat,languageCode,speed},signal,preparation={})` accepts the public
419
428
  provider-neutral synthesis shape, requires any explicit model to match the
420
429
  selected route, and fills an omitted voice only from the selected model
421
430
  catalog's `defaultVoice`. An omitted response format preserves the instance's
@@ -438,13 +447,15 @@ default cleanup. The third `preparation` argument is reserved for SDK-internal
438
447
  delegation, where `{speechInputPrepared:true}` prevents a second cleanup pass;
439
448
  applications omit it. `streamTTS(text='',end=false,options={})` and
440
449
  `finishTTS()` use the selected playback boundary. The third-argument options below
441
- are available in SDK `0.5.12`. In these streaming options, the `textFormat`
450
+ are available from SDK `0.5.12`, with `languageCode` added in `0.95.0`.
451
+ In these streaming options, the `textFormat`
442
452
  compatibility extra added in SDK `0.5.16` remains ignored:
443
453
 
444
454
  | Field | Default | Meaning |
445
455
  | --- | --- | --- |
446
456
  | `voice` | Current selected model's default voice | A supplied voice is captured for every segment extracted by this call and forwarded unchanged to `fetchTTS()`. It does not change the instance or provider default. |
447
457
  | `speed` | Current `ai.voiceSpeed` | A supplied positive speed is captured for those segments and forwarded to `fetchTTS()`. It does not change `ai.voiceSpeed`. |
458
+ | `languageCode` | Selected engine's language default | Per-utterance language control, separate from the unchanged text. Support belongs to the selected engine/tokenizer; Magpie uses natural speed `1`. |
448
459
  | `pauseAfterMs` | `0` | Finite, nonnegative milliseconds placed after the final extracted segment on the existing audio clock. Invalid values throw `RangeError`; no pause is inserted between this call's other segments. |
449
460
  | `waitForPlayback` | `false` | Omission retains the preparation promise. With `true`, the promise resolves after every extracted segment reaches a terminal playback state: `true` when all naturally end, or `false` after terminal cancellation or failure. |
450
461
  | `textFormat` | Ignored streaming compatibility extra | Streaming narration retains automatic cleanup. The public plain selection belongs to complete synthesis payloads or `prepareTTS` options. |
@@ -461,13 +472,24 @@ formatting state. Streaming `textFormat` is not an opt-out.
461
472
 
462
473
  Voice, speed, pause, and playback overrides belong to the segments
463
474
  extracted in that invocation, including any text buffered by an earlier call.
464
- Those overrides are not retained with an unfinished `end:false` remainder; a
465
- later call supplies its own options, and `finishTTS()` uses their defaults
466
- while flushing any pending formatting mark. Use `end:true`
467
- for a complete passage. A call extracting no segments resolves
468
- `true` without waiting for earlier jobs; `finishTTS()` remains a preparation
475
+ Voice, speed and languageCode are retained with an unfinished `end:false`
476
+ utterance through continuation and `finishTTS()`. Explicitly changed language
477
+ first queues the old remainder under its prior selection, then accepts new text.
478
+ End, mute and stop clear the retained selection. Pause and waitForPlayback
479
+ remain per-call controls.
480
+
481
+ Use `end:true` for a complete passage. A call extracting no segments resolves
482
+ `true` without waiting for earlier jobs, except that a language-change call also
483
+ joins its preceding remainder preparation. `finishTTS()` remains a preparation
469
484
  flush, not a queue-wide playback barrier. A muted call resolves `false`.
470
485
 
486
+ A native engine catalog with `speech.streaming:true` selects an inert PCM
487
+ playback descriptor through `prepareTTSPlayback`. Explicit play uses the shared
488
+ provider lane and Web Audio output; `released` joins native cleanup independently
489
+ of `finished`. Magpie also retains complete-audio `fetchTTS`; browser-native
490
+ voices without audio export retain their existing unsupported result. See
491
+ [shared native capture/playback](native-speech.md#shared-ai-capture-and-playback).
492
+
471
493
  `prepareTTSPlayback(payload,signal,preparation={})` is the silent playback-oriented
472
494
  request. Audio-file providers retain the `fetchTTS()` Blob contract; a selected
473
495
  model with `speech.playback:'native'` returns an inert `native-speech` descriptor.
@@ -1130,6 +1152,17 @@ The singleton exposes read-only `protocol`, `configured`, and `speechMuted`;
1130
1152
  `setSpeechMuted(muted,{signal}={})`. Provider payloads must be data-only; callbacks,
1131
1153
  accessors, symbols, and cycles are rejected at the provider boundary.
1132
1154
 
1155
+ `supportsTranscriptionCapture(providerId=null)` checks the selected provider's
1156
+ capture implementation and optional capability predicate. Construction of
1157
+ `createTranscriptionCapture(options)` performs no capture; it returns null or
1158
+ an owned `start/stop/cancel/destroy/done` wrapper with `forceEndpoint` when the
1159
+ provider supplies it. Start must originate in the user's capture action and
1160
+ requires an idle ready STT role; it is never queued. The role remains owned
1161
+ until `done` joins cleanup, including on cancellation or provider retirement.
1162
+ Native streaming TTS likewise uses the existing provider request lane rather
1163
+ than an application-owned synthesis queue. See
1164
+ [shared native capture/playback](native-speech.md#shared-ai-capture-and-playback).
1165
+
1133
1166
  `setSpeechMuted(false,{signal})` shares pending activation while retaining an
1134
1167
  independent interest for each caller. Aborting one signal rejects only that
1135
1168
  caller's wait. When no pending interests remain, abort reaches the owned load.
@@ -2046,11 +2079,22 @@ component adds ordered STT, persistence, retry, and presentation.
2046
2079
  ### Public surface
2047
2080
 
2048
2081
  Default export `ContinuousVoiceCapture` accepts
2049
- `{preRollMs=1500,quietMs=2000,chunkMs=30000,activityThreshold=0.02,onSegment,onError,onState}`.
2050
- `start({mediaConstraints={audio:true},signal}={})` acquires microphone input and
2082
+ `{preRollMs=1500,quietMs=2000,chunkMs=30000,activityThreshold=0.02,mode='segments',pcmChunkMs=100,onPCM,onActivity,onSegment,onError,onState}`.
2083
+ `start({mediaConstraints={audio:true},sampleRate,signal}={})` acquires microphone input and
2051
2084
  resolves `true` once capture starts, or `false` after cancellation or a
2052
2085
  reported start failure. Starting capture is an explicit caller action.
2053
2086
 
2087
+ Select `mode:'pcm'` to deliver every captured sample through ordered awaited
2088
+ `onPCM({samples:Float32Array,sampleRate,channels:1,sequence})` callbacks.
2089
+ The record reports the actual AudioContext rate. `pcmChunkMs` is transport
2090
+ scheduling, never a recording limit. `onActivity({active,audioTime})` reports
2091
+ amplitude changes without gating PCM or interpreting speech. `flush()` sends
2092
+ pending samples and awaits preceding delivery while leaving capture open.
2093
+ Stop drains accepted PCM and joins actual AudioContext closure. Cancel/destroy
2094
+ suppress queued delivery and join in-flight delivery and close; genuine delivery
2095
+ and cleanup failures reject rather than being reported as success.
2096
+
2097
+ In the default `mode:'segments'`:
2054
2098
  `onSegment({audio,sequence,reason,durationMs})` receives a complete
2055
2099
  `audio/wav` Blob containing mono Float32 audio. Sequence starts at one for
2056
2100
  each capture session; reason is `pause`, `periodic`, or `stop`. The detector
@@ -2073,7 +2117,8 @@ cancellation boundary. The optional signal cancels this capture session.
2073
2117
  flushes the active clip before reporting interruption while the worklet remains
2074
2118
  available. A failed audio context or processor reports interruption directly;
2075
2119
  audio still inside an unavailable processor cannot be recovered. Callback
2076
- promises are observed without delaying capture or stop acknowledgement.
2120
+ promises for segment/state/activity observers are observed without delaying
2121
+ capture; ordered PCM delivery and actual context closure are joined at stop.
2077
2122
  Capture state is independent
2078
2123
  of model readiness, STT processing, and application save state.
2079
2124
 
@@ -3411,6 +3456,100 @@ import * as module from '/arcane/modules/ModelDefinition.js';
3411
3456
  console.log(Object.keys(module));
3412
3457
  ```
3413
3458
 
3459
+ ## NativeSpeechCapture.js
3460
+
3461
+ ### Overview
3462
+
3463
+ Default `createNativeSpeechCapture({speech,model,...options})` connects one
3464
+ microphone lifetime to one retained Core ASR stream. Construction opens no
3465
+ microphone or model. Applications normally use `ai.createTranscriptionCapture`,
3466
+ which also owns the selected provider slot.
3467
+
3468
+ ### Public surface and lifetime
3469
+
3470
+ Options are `sampleRate=16000`, `pcmChunkMs=100`, `activityThreshold=0.02`,
3471
+ `streamOptions`, `mediaConstraints`, `onUpdate`, `onInterim`, `onSegment`,
3472
+ `onActivity`, `onState`, `onError`, `onProgress` and `onRequest`.
3473
+ The handle exposes `start({signal,mediaConstraints})` (boolean promise),
3474
+ `forceEndpoint()`, `stop()`, `requestStop()`, `cancel(reason)`,
3475
+ `destroy(reason)`, `done`, and `current()`. Capture uses the actual selected
3476
+ sample rate; `streamOptions.sampleRate` overrides the factory rate. Every PCM
3477
+ contribution is retained in order.
3478
+
3479
+ Recognition callbacks receive `(result,{requestStop})`; `onUpdate` precedes
3480
+ the appropriate final `onSegment` or provisional `onInterim`. Partials replace,
3481
+ finals preserve complete native segments and speaker scope. Call `requestStop()`
3482
+ without awaiting it, return from the callback, then join `stop()` or `done`
3483
+ externally. Never await those or `forceEndpoint()` from their own callback.
3484
+ External endpoint flushes pending audio and recognition while keeping capture
3485
+ and the stream open. Stop closes capture and drains terminally. Cancel/destroy
3486
+ join both cleanup owners without claiming a complete transcript. `current()`
3487
+ returns the latest snapshot or null. Actual failures remain in `done` and
3488
+ `onError`. No snapshot or control sends chat or decides conversational meaning.
3489
+
3490
+ ### Availability and example
3491
+
3492
+ Requires microphone, Web Audio and AudioWorklet in a browser/native WebView,
3493
+ plus an explicitly selected ready streaming Core speech engine.
3494
+
3495
+ ```javascript
3496
+ import createNativeSpeechCapture from 'arcane-os/modules/NativeSpeechCapture.js';
3497
+
3498
+ function prepareDragonInterpreter(speech, model, showSnapshot) {
3499
+ return createNativeSpeechCapture({speech, model, onUpdate: showSnapshot});
3500
+ }
3501
+ // Start from the application's capture action; its owner joins stop/done.
3502
+ ```
3503
+
3504
+ See [Core speech](native-speech.md) and [Nemotron](native-nemotron.md) for complete
3505
+ native formats, results, callback constraints, reset and platform prerequisites.
3506
+
3507
+ ## NativeSpeechPlayback.js
3508
+
3509
+ ### Overview
3510
+
3511
+ Default `prepareNativeSpeechPlayback({speech,request,getAudioContext,signal=null})`
3512
+ prepares an inert native speech descriptor. It creates no model, microphone or
3513
+ separate utterance queue. Applications normally use `ai.prepareTTSPlayback`.
3514
+
3515
+ ### Public surface and lifetime
3516
+
3517
+ The result is `{kind:'native-speech',input,voice,language,speed,play}`.
3518
+ `play({signal,onState})` returns `finished`, `released`, live `state` and
3519
+ `error`, plus boolean `pause()`, `resume()` and `stop()` controls.
3520
+ Preparation performs no synthesis/playback. Play consumes ordered signed
3521
+ 16-bit little-endian PCM at its supplied rate and channel count through the
3522
+ caller-owned AudioContext, with awaited audio-clock backpressure.
3523
+ Pause retains unplayed audio; stop cancels only this utterance.
3524
+
3525
+ `finished` resolves true after natural playback or false after stop, and
3526
+ rejects actual failure. `released` additionally joins native synthesis and
3527
+ cleanup. Both preparation and play signals can cancel. The shared output
3528
+ context remains open for its owner. `onState({state,error})` observes queued,
3529
+ waiting-for-audio, playing, paused, complete, stopped or error.
3530
+ Missing callable dependencies throw TypeError; unusable PCM or a closed context
3531
+ reports `ARCANE_AI_SPEECH_PLAYBACK_FAILED`. Native, playback and cleanup
3532
+ failures remain observable, including combined causes.
3533
+
3534
+ ### Availability and example
3535
+
3536
+ Requires browser Web Audio plus a supplied streaming speech API.
3537
+
3538
+ ```javascript
3539
+ import prepareNativeSpeechPlayback from 'arcane-os/modules/NativeSpeechPlayback.js';
3540
+
3541
+ function prepareDragonAnnouncement(speech, getAudioContext, signal) {
3542
+ return prepareNativeSpeechPlayback({
3543
+ speech, getAudioContext, signal,
3544
+ request: {input: 'The dragon has landed.', languageCode: 'en-US'}
3545
+ });
3546
+ }
3547
+ // Play from the owning action, then join the control's released promise.
3548
+ ```
3549
+
3550
+ See [Magpie](native-magpie.md) and
3551
+ [shared native playback](native-speech.md#shared-ai-capture-and-playback).
3552
+
3414
3553
  ## Ollama.js
3415
3554
 
3416
3555
  ### Overview
@@ -4826,7 +4965,7 @@ the audio rendering thread. It performs no semantic speech detection or STT.
4826
4965
  No ESM exports. The capture owner loads this static file with
4827
4966
  `audioContext.audioWorklet.addModule()` and creates the
4828
4967
  `arcane-continuous-voice-capture` processor with
4829
- `{preRollMs,quietMs,chunkMs,activityThreshold}` in `processorOptions`.
4968
+ `{preRollMs,quietMs,chunkMs,activityThreshold,mode,pcmChunkMs}` in `processorOptions`.
4830
4969
  `activityThreshold` measures root-mean-square amplitude over fixed 20 ms
4831
4970
  windows counted from the audio sample rate, independently of browser callback
4832
4971
  lengths. Brief peaks do not reset the quiet duration when the window's RMS
@@ -4843,6 +4982,13 @@ trailing audio before the quiet boundary. Analysis adds at most one 20 ms
4843
4982
  window of capture-to-segmentation delay; model processing is independently
4844
4983
  owned by the transcription queue.
4845
4984
 
4985
+ In `mode:'pcm'`, the processor transfers every sample through
4986
+ `{type:'pcm',audio:ArrayBuffer,sampleRate,sequence}` and reports
4987
+ `{type:'activity',active,audioTime}` on amplitude-state changes. It keeps no
4988
+ semantic classifier and does not omit quiet audio. `{type:'flush',id}` sends
4989
+ pending PCM then `{type:'flushed',id}` without stopping. The capture owner
4990
+ adds `channels:1` and joins ordered delivery before resolving its flush.
4991
+
4846
4992
  The owner sends `{type:'stop'}`. The processor transfers each complete
4847
4993
  `{type:'segment',audio:ArrayBuffer,sampleRate,sequence,reason,durationMs}`
4848
4994
  and finally `{type:'stopped'}`. Each complete WAV includes its own format,
@@ -81,6 +81,10 @@ runtime layouts.
81
81
  | `arcane-os/ai/core-image` | Browser image lifecycle and complete PNG Blobs through an available Core service. |
82
82
  | `arcane-os/core/image` | Native image Core service with independent runtime preparation and retained model ownership. |
83
83
  | `arcane-os/core/speech` | Independent injected STT/TTS engine loading, request cancellation and joined Core service lifetime. |
84
+ | `arcane-os/local-ai/nemotron` | Retained native streaming recognition with complete timed words and optional diarization. |
85
+ | `arcane-os/local-ai/nemotron/build` | First-party ASR helper build against a prepared NeMo runtime. |
86
+ | `arcane-os/local-ai/magpie` | Retained native Magpie/NanoCodec synthesis with complete audio or ordered PCM. |
87
+ | `arcane-os/local-ai/magpie/build` | First-party TTS helper build against a prepared NeMo runtime. |
84
88
  | `arcane-os/local-ai/whisper` | Retained native Whisper model, complete recordings and joined helper lifetime. |
85
89
  | `arcane-os/local-ai/kokoro` | Retained native Kokoro synthesis using the existing ONNX/model-resource owners and a compiled phonemization/Opus helper. |
86
90
  | `arcane-os/local-ai/whisper/build` | First-party persistent helper build against selected installed Whisper headers and libraries. |
@@ -476,6 +480,14 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
476
480
  | `KOKORO_REVISION` | constant | `arcane-os/local-ai/kokoro` | Core native speech | Selected upstream model revision |
477
481
  | `KOKORO_SAMPLE_RATE` | constant | `arcane-os/local-ai/kokoro` | Core native speech | Native PCM sample rate |
478
482
  | `KOKORO_VOICES` | constant | `arcane-os/local-ai/kokoro` | Core native speech | Complete 28-voice US/GB selection |
483
+ | `createNemotronRuntime()` | function | `arcane-os/local-ai/nemotron` | Core native speech | Node with a matching first-party helper, selected NeMo ASR runtime and models; Windows/Linux/macOS source, Android host adaptation |
484
+ | `createNemotronRuntime default export` | function | `arcane-os/local-ai/nemotron` | Core native speech | Same named function |
485
+ | `buildNemotronHelper()` | function | `arcane-os/local-ai/nemotron/build` | Core native speech | Node with CMake 3.26+, C++17 toolchain and platform-matching public NeMo ASR headers/libraries/CMake package |
486
+ | `buildNemotronHelper default export` | function | `arcane-os/local-ai/nemotron/build` | Core native speech | Same named function |
487
+ | `createMagpieRuntime()` | function | `arcane-os/local-ai/magpie` | Core native speech | Node with a matching first-party helper, selected NeMo TTS runtime, Magpie/tokenizer and NanoCodec assets; Windows/Linux/macOS source, Android host adaptation |
488
+ | `createMagpieRuntime default export` | function | `arcane-os/local-ai/magpie` | Core native speech | Same named function |
489
+ | `buildMagpieHelper()` | function | `arcane-os/local-ai/magpie/build` | Core native speech | Node with CMake 3.26+, C++17 toolchain, platform threads and matching public NeMo TTS headers/libraries/CMake package |
490
+ | `buildMagpieHelper default export` | function | `arcane-os/local-ai/magpie/build` | Core native speech | Same named function |
479
491
  | `createWhisperRuntime()` | function | `arcane-os/local-ai/whisper` | Core native speech | Node with a prepared matching helper, runtime, decoder and selected model |
480
492
  | `createWhisperRuntime default export` | function | `arcane-os/local-ai/whisper` | Core native speech | Same retained native engine factory |
481
493
  | `buildWhisperHelper()` | function | `arcane-os/local-ai/whisper/build` | Core native speech | Node with installed CMake/C++17 toolchain and matching Whisper source/runtime |
@@ -7232,6 +7244,18 @@ no engine. The native routes of `AI.fetchTTS(payload, signal)` and
7232
7244
  `AI.fetchSTT(audio, signal)` forward their signals through these calls and
7233
7245
  normalize cancellation to `AbortError` with `ARCANE_AI_REQUEST_ABORTED`.
7234
7246
 
7247
+ `facade.speech.openStream(request,options)` returns
7248
+ `{streamId,ready,completion,current,push,forceEndpoint,finish,cancel}`.
7249
+ Ordered Float32 input retains the actual declared sample rate. Endpoint preserves
7250
+ stream/speaker scope and waits preceding updates; finish drains terminally.
7251
+ Do not await endpoint/finish/completion from their own update callback. Explicit
7252
+ cancel joins its native cleanup control, unlike ordinary signal cancellation.
7253
+ `synthesizeStream(request,{signal,onAudio,onProgress,onRequest})` awaits each
7254
+ complete ordered Uint8Array PCM contribution and its consumption acknowledgement,
7255
+ returns terminal metadata, and joins native cancellation. It plays no audio.
7256
+ Both streaming operations have no fixed timeout. Engine support/readiness remains
7257
+ independent from facade availability. See [streaming native speech](core-client.md#streaming-native-speech).
7258
+
7235
7259
  `facade.window.state({signal}?)` reads the calling document's native window;
7236
7260
  `facade.window.setState({state}, {signal}?)` selects `normal`, `maximized` or
7237
7261
  `fullscreen` for that same window. The matching Windows host returns
@@ -8961,6 +8985,211 @@ US/GB voice identifiers. The engine uses `af_heart` when the caller omits
8961
8985
  `voice`; an explicit unsupported selection reports an error. Use this complete
8962
8986
  array to populate a native Kokoro voice selector.
8963
8987
 
8988
+ ## createNemotronRuntime()
8989
+
8990
+ ### Overview and syntax
8991
+
8992
+ Creates one retained native recognizer and optional diarizer without opening a
8993
+ microphone, loading a model or starting inference at construction.
8994
+
8995
+ ```javascript
8996
+ createNemotronRuntime({
8997
+ runtime, prepare, modelId = 'nemotron-3.5-asr-streaming-0.6b',
8998
+ modelPath, diarizationModelPath, gpu, onEvent
8999
+ } = {})
9000
+ ```
9001
+
9002
+ Supply a prepared `runtime` or lazy `prepare({signal,onEvent})` descriptor
9003
+ with `helperExecutable` and selected native library paths. Select `gpu`
9004
+ explicitly (native ordinal, or `-1` for CPU). Model paths may come from the
9005
+ descriptor; omit the diarization path when speaker labels are not requested.
9006
+ Preparation, models and device selection remain caller-owned.
9007
+
9008
+ ### Returned members and lifetime
9009
+
9010
+ The engine exposes `current/subscribe/load/unload/transcribe/openStream/close`.
9011
+ Current state and replaying subscriptions report actual loaded readiness,
9012
+ native version, requested device, active speaker scope, error and reset state.
9013
+ `load` initializes the selection; `unload` and `close` join owned cleanup.
9014
+
9015
+ `openStream({sampleRate=16000,languageCode,model,onUpdate,signal})` resolves an
9016
+ owned `push/forceEndpoint/finish/cancel/completion/current` handle plus
9017
+ `sampleRate` and `speakerScope`. Feed mono `Float32Array` or
9018
+ `{encoding:'f32le',data:BASE64}` at the actual declared rate. The native sample
9019
+ rate contract accepts integer 8000–96000 or 0 for model rate. No encoded-media
9020
+ decoder is supplied. Updates preserve every alternative, text, word, timestamp,
9021
+ numeric speaker and final/provisional status.
9022
+
9023
+ `forceEndpoint` drains an utterance while retaining stream and speaker scope;
9024
+ `finish` terminally drains accepted input. Partial snapshots replace; finals
9025
+ remain separate recognition segments, never inferred conversational completion.
9026
+ `transcribe({audio,sampleRate,model?,languageCode?},options)` uses this same
9027
+ stream and returns complete `{text,words,results,speakerScope}`.
9028
+
9029
+ Cancellation joins native and callback cleanup. Terminating genuinely
9030
+ noninterruptible helper work exposes `resetRequired` and requires explicit
9031
+ reload; no engine restarts silently. Callbacks must return before joining their
9032
+ own stream/engine lifecycle, otherwise `NEMOTRON_CALLBACK_WAIT` is reported.
9033
+ Unsupported input reports `NEMOTRON_AUDIO_FORMAT_UNSUPPORTED`; native, callback
9034
+ and cleanup failures remain observable.
9035
+
9036
+ ### Availability and example
9037
+
9038
+ Node on Windows, Linux and macOS with the first-party helper and matching
9039
+ upstream NeMo ASR runtime/models; Android requires host adaptation. Selected
9040
+ Windows helper compilation is recorded in the [engine guide](native-nemotron.md);
9041
+ that is separate from inference, microphone or other-platform execution.
9042
+
9043
+ ```javascript
9044
+ import {createNemotronRuntime} from 'arcane-os/local-ai/nemotron';
9045
+
9046
+ function prepareDragonInterpreter(runtime, modelPath, gpu) {
9047
+ return createNemotronRuntime({runtime, modelPath, gpu});
9048
+ }
9049
+ // The native owner explicitly loads, supplies PCM and joins close.
9050
+ ```
9051
+
9052
+ See [native Nemotron](native-nemotron.md) for complete options, snapshots,
9053
+ callback rules and cancellation/reset outcomes.
9054
+
9055
+ ## createNemotronRuntime default export
9056
+
9057
+ The default export from `arcane-os/local-ai/nemotron` is the same
9058
+ `createNemotronRuntime` function, with identical inputs, results and lifetime.
9059
+
9060
+ ## buildNemotronHelper()
9061
+
9062
+ ### Overview and syntax
9063
+
9064
+ `buildNemotronHelper({runtime,outputRoot,signal,onEvent}={})` configures, builds
9065
+ and installs only the first-party Nemotron helper. `runtime.cmakeDirectory`
9066
+ selects an installed NeMo CMake package with its matching public ASR headers and
9067
+ libraries; `outputRoot` is the caller-owned output directory.
9068
+
9069
+ ### Result, availability and example
9070
+
9071
+ Returns `{platform,architecture,root,helperExecutable,executable,diagnostics}`;
9072
+ `executable` aliases `helperExecutable`, and diagnostics retains complete
9073
+ configure/compile/install results. Requires Node, CMake 3.26+, a C++17 compiler
9074
+ and matching platform runtime. It downloads no dependency or model and performs
9075
+ no inference. Cancellation and process failures remain observable.
9076
+
9077
+ ```javascript
9078
+ import {buildNemotronHelper} from 'arcane-os/local-ai/nemotron/build';
9079
+
9080
+ async function buildSelectedRecognizer(runtime, outputRoot, signal, onEvent) {
9081
+ return buildNemotronHelper({runtime, outputRoot, signal, onEvent});
9082
+ }
9083
+ ```
9084
+
9085
+ See [preparation and selection](native-nemotron.md#preparation-and-selection).
9086
+
9087
+ ## buildNemotronHelper default export
9088
+
9089
+ The default export from `arcane-os/local-ai/nemotron/build` is the same
9090
+ `buildNemotronHelper` function.
9091
+
9092
+ ## createMagpieRuntime()
9093
+
9094
+ ### Overview and syntax
9095
+
9096
+ Creates one retained native Magpie/NanoCodec synthesis owner. Construction
9097
+ downloads, loads and plays nothing.
9098
+
9099
+ ```javascript
9100
+ createMagpieRuntime({
9101
+ runtime, prepare, paths, modelAssets, assetProjectionId, resourcePaths,
9102
+ modelId = 'magpie', voice = 'Aria', languageCode = 'en-US',
9103
+ executionTarget, signal, onEvent
9104
+ } = {})
9105
+ ```
9106
+
9107
+ Supply a prepared executable/library descriptor or `prepare({signal})`.
9108
+ Select matching Magpie, NanoCodec and tokenizer `paths`, or an existing
9109
+ model-assets projection and `resourcePaths`. Explicit `executionTarget`
9110
+ selects CPU or CUDA with the host's visible device/UUID; no provider or device
9111
+ fallback is inferred. The tokenizer's own speaker map determines voice IDs.
9112
+
9113
+ ### Returned members and lifetime
9114
+
9115
+ The engine exposes `current/subscribe/load/unload/synthesize/synthesizeStream/stop/close`.
9116
+ State includes independent loaded readiness, active/queued requests, voice and
9117
+ language defaults, model-owned voices and streaming/format capability.
9118
+ `load` retains selected models; `unload` and `close` join owned work/assets.
9119
+
9120
+ Both synthesis methods accept exact `{input,model?,voice?,languageCode?,speed?}`;
9121
+ language is separate control information, and native speed must be 1.
9122
+ `synthesize(request,{signal,requestId,onProgress})` returns complete base64
9123
+ WAV (default) or PCM audio, MIME/format and speech metadata. Select
9124
+ `responseFormat:'pcm'` explicitly when needed.
9125
+ `synthesizeStream(request,{signal,requestId,onProgress,onAudio})` requires an
9126
+ awaited ordered `onAudio({pcm,format,sampleRate,channels,requestId})` callback.
9127
+ PCM is a Node Buffer of signed 16-bit little-endian samples; terminal metadata
9128
+ has `complete:true` without retaining the complete audio again. Playback
9129
+ belongs to the caller.
9130
+
9131
+ Requests retain FIFO ownership. `stop({requestId})` stops only that queued or
9132
+ active utterance and joins native/callback cleanup; cancellation rejects
9133
+ synthesis. In-flight native interruption is observed at a PCM callback boundary.
9134
+ A callback can abort its caller-owned signal and return; self-joining lifecycle
9135
+ calls reject `MAGPIE_CALLBACK_WAIT`. Complete native and cleanup errors remain
9136
+ observable. An interrupted request never returns fabricated complete audio.
9137
+
9138
+ ### Availability and example
9139
+
9140
+ Node on Windows, Linux and macOS with a matching helper, upstream NeMo TTS
9141
+ runtime and selected model assets; Android needs host adaptation. The
9142
+ [engine guide](native-magpie.md) separates selected Windows compilation from
9143
+ unverified inference, playback and other-platform execution.
9144
+
9145
+ ```javascript
9146
+ import {createMagpieRuntime} from 'arcane-os/local-ai/magpie';
9147
+
9148
+ function prepareDragonAnnouncer(runtime, paths, executionTarget) {
9149
+ return createMagpieRuntime({runtime, paths, executionTarget});
9150
+ }
9151
+ // The native owner loads explicitly; synthesis returns audio, not playback.
9152
+ ```
9153
+
9154
+ See [native Magpie](native-magpie.md) for complete selection, language, voices,
9155
+ callback constraints and request lifetime.
9156
+
9157
+ ## createMagpieRuntime default export
9158
+
9159
+ The default export from `arcane-os/local-ai/magpie` is the same
9160
+ `createMagpieRuntime` function, with identical inputs, results and lifetime.
9161
+
9162
+ ## buildMagpieHelper()
9163
+
9164
+ ### Overview and syntax
9165
+
9166
+ `buildMagpieHelper({runtime,outputRoot,signal,onEvent}={})` configures, builds
9167
+ and installs the first-party Magpie helper against the caller's installed
9168
+ `runtime.cmakeDirectory`, public NeMo TTS headers and matching libraries.
9169
+
9170
+ ### Result, availability and example
9171
+
9172
+ Returns `{platform,architecture,root,helperExecutable,diagnostics}`, preserving
9173
+ complete configure/compile/install results. Requires Node, CMake 3.26+, C++17,
9174
+ platform threads and matching NeMo libraries. `outputRoot` belongs to the
9175
+ caller. It downloads no dependency, model or voice and performs no inference.
9176
+ Cancellation and process failures remain observable.
9177
+
9178
+ ```javascript
9179
+ import {buildMagpieHelper} from 'arcane-os/local-ai/magpie/build';
9180
+
9181
+ async function buildSelectedAnnouncer(runtime, outputRoot, signal, onEvent) {
9182
+ return buildMagpieHelper({runtime, outputRoot, signal, onEvent});
9183
+ }
9184
+ ```
9185
+
9186
+ See [helper build prerequisites](native-magpie.md#build-the-first-party-helper).
9187
+
9188
+ ## buildMagpieHelper default export
9189
+
9190
+ The default export from `arcane-os/local-ai/magpie/build` is the same
9191
+ `buildMagpieHelper` function.
9192
+
8964
9193
  ## createWhisperRuntime()
8965
9194
 
8966
9195
  ### Overview
@@ -9114,6 +9343,18 @@ requests and results unchanged. Format decoding, model processing and encoding
9114
9343
  remain with that engine. This factory supplies no speech engine or automatic
9115
9344
  browser/native provider selection.
9116
9345
 
9346
+ Optional engine `openStream({...options,signal,onUpdate})` supplies continuous
9347
+ recognition; optional `synthesizeStream(request,{signal,onProgress,onAudio,requestId})`
9348
+ supplies awaited ordered PCM output. Each role reports `supportsStreaming`
9349
+ independently from loaded readiness. The Core streaming methods are
9350
+ `speech.open`, `push`, `endpoint`, `finish`, `cancel`, `synthesizeStream`,
9351
+ `audioConsumed` and `cancelSynthesis` (each under `speech.`). Request-scoped
9352
+ `speech.opened`, `speech.transcription` and `speech.audio` events retain stream
9353
+ ownership; TTS acknowledgements apply backpressure while control traffic continues.
9354
+ Endpoint drains an utterance without closing its stream; finish closes terminally.
9355
+ ASR finals are recognition segments, not conversational decisions. Cancellation
9356
+ joins the selected engine's native work. See the [complete streaming contract](native-speech.md#continuous-transcription-and-streaming-synthesis).
9357
+
9117
9358
  ### Members, results and events
9118
9359
 
9119
9360
  The returned object has `name: 'speech'`, `current`, `subscribe`, `close`,
@@ -9170,10 +9411,12 @@ their operation with `SPEECH_ENGINE_UNAVAILABLE`; work after shutdown begins
9170
9411
  rejects with `CORE_CLOSING`. Engine errors remain observable, and engine cleanup
9171
9412
  failures reject shutdown with `AggregateError`. Asynchronous listener failures
9172
9413
  reach developer diagnostics. Engine platform support remains its own contract;
9173
- this Node composition layer adds no engine dependency. Existing AI readiness
9174
- consumers recognize `whisper.cpp` and `kokoro-onnx` IDs independently of the
9175
- service's engine-neutral interface. See the [complete engine and lifetime
9176
- contract](native-speech.md).
9414
+ this Node composition layer adds no engine dependency. The shared AI adapter
9415
+ follows the selected model and engine in per-role snapshots, including Nemotron
9416
+ and Magpie. Only its older aggregate-only fallback retains fixed
9417
+ `whisper.cpp`/`whisper-small` and `kokoro-onnx`/`kokoro` expectations.
9418
+ Unavailable streaming operations reject `SPEECH_OPERATION_UNAVAILABLE` without
9419
+ fallback. See the [complete engine and lifetime contract](native-speech.md).
9177
9420
 
9178
9421
  ## createSpeechService default export
9179
9422
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.95.0",
3
+ "version": "0.96.0",
4
4
  "description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -137,8 +137,8 @@
137
137
  "test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
138
138
  "test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
139
139
  "test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/app-selection.test.mjs test/contracts.test.mjs test/conversation-closing-report.test.mjs test/doctor.test.mjs test/mail-credentials.test.mjs test/mail-outbox.test.mjs test/mail-public-api.test.mjs test/mail-send.test.mjs test/mail-transport.test.mjs test/native-windows-icon.test.mjs test/risk-signal-analyzer.test.mjs test/targets.test.mjs test/workspace-operation-lock.test.mjs",
140
- "test:functional": "node ./bin/arcane-test.mjs test/ai-ollama-readiness.test.mjs test/core-ollama-lifecycle.test.mjs test/ai-request-model.test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.test.mjs test/browser-decisions.test.mjs test/browser-kokoro-complete-input.test.mjs test/browser-speech-providers.test.mjs test/browser-speech-artifacts.test.mjs test/browser-speech-resource-bridge.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/chat-input.test.mjs test/chat-stream-speech.test.mjs test/chat-transcript-scroll.test.mjs test/chat-transient-retention.test.mjs test/cli.test.mjs test/continuous-voice-capture.test.mjs test/codex-app-server.test.mjs test/mcp-stdio-server.test.mjs test/core-classic-source.test.mjs test/core-client.test.mjs test/core-client-installation.test.mjs test/core-ai-installation.test.mjs test/core-llama-lifecycle.test.mjs test/core-development.test.mjs test/core-host.test.mjs test/core-shared-host.test.mjs test/core-model-assets.test.mjs test/core-native-services.test.mjs test/core-packaged-web.test.mjs test/core-runtime.test.mjs test/core-stdio.test.mjs test/dbopfs-committed-events.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs-model-resources.test.mjs test/dbopfs-png.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/document-acquisition.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/execution-devices.test.mjs test/file-preview.test.mjs test/import-map.test.mjs test/local-image-editing.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/markdown-editor.test.mjs test/markdown-media.test.mjs test/markdown-source-map.test.mjs test/modal.test.mjs test/model-resource-bridge.test.mjs test/native-decisions.test.mjs test/native-onnx.test.mjs test/native-module-packaging.test.mjs test/native-module-resource-references.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/rendered-print.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/theme-manager.test.mjs test/twin-cloud-images.test.mjs test/twin-cloud.test.mjs test/websocket-client-import-map.test.mjs test/websocket.test.mjs test/update-check.test.mjs test/user-explicit-updates.test.mjs",
141
- "test:integration": "node ./bin/arcane-test.mjs test/installed-package-runtime.test.mjs test/root-app-layout.test.mjs test/integrated-shared.test.mjs test/integrated-workspace.test.mjs test/mail-browser.test.mjs test/native-macos-provider.test.mjs test/native-plan.test.mjs test/native-portable-provider.test.mjs test/native-provider-loader.test.mjs test/npm-release.test.mjs test/release-bundle.test.mjs test/release-capability-smoke.test.mjs test/shared-payload-batch.test.mjs test/tarball.test.mjs test/browser-wasm-cpu.test.mjs test/wllama-webgpu-runtime.test.mjs",
140
+ "test:functional": "node ./bin/arcane-test.mjs test/ai-ollama-readiness.test.mjs test/core-ollama-lifecycle.test.mjs test/ai-request-model.test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.test.mjs test/browser-decisions.test.mjs test/browser-kokoro-complete-input.test.mjs test/browser-speech-providers.test.mjs test/browser-speech-artifacts.test.mjs test/browser-speech-resource-bridge.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/chat-input.test.mjs test/chat-stream-speech.test.mjs test/chat-transcript-scroll.test.mjs test/chat-transient-retention.test.mjs test/cli.test.mjs test/continuous-voice-capture.test.mjs test/codex-app-server.test.mjs test/mcp-stdio-server.test.mjs test/core-classic-source.test.mjs test/core-client.test.mjs test/core-client-installation.test.mjs test/core-ai-installation.test.mjs test/core-llama-lifecycle.test.mjs test/core-development.test.mjs test/core-host.test.mjs test/core-shared-host.test.mjs test/core-model-assets.test.mjs test/core-native-services.test.mjs test/core-packaged-web.test.mjs test/core-runtime.test.mjs test/core-stdio.test.mjs test/dbopfs-committed-events.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs-model-resources.test.mjs test/dbopfs-png.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/document-acquisition.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/execution-devices.test.mjs test/file-preview.test.mjs test/import-map.test.mjs test/local-image-editing.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/markdown-editor.test.mjs test/markdown-media.test.mjs test/markdown-source-map.test.mjs test/modal.test.mjs test/model-resource-bridge.test.mjs test/nemo-models.test.mjs test/native-decisions.test.mjs test/native-onnx.test.mjs test/native-module-packaging.test.mjs test/native-module-resource-references.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/rendered-print.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/theme-manager.test.mjs test/twin-cloud-images.test.mjs test/twin-cloud.test.mjs test/websocket-client-import-map.test.mjs test/websocket.test.mjs test/update-check.test.mjs test/user-explicit-updates.test.mjs",
141
+ "test:integration": "node ./bin/arcane-test.mjs test/installed-package-runtime.test.mjs test/root-app-layout.test.mjs test/integrated-shared.test.mjs test/integrated-workspace.test.mjs test/mail-browser.test.mjs test/native-macos-provider.test.mjs test/native-plan.test.mjs test/native-portable-provider.test.mjs test/native-provider-loader.test.mjs test/native-publish-output.test.mjs test/npm-release.test.mjs test/release-bundle.test.mjs test/release-capability-smoke.test.mjs test/shared-payload-batch.test.mjs test/tarball.test.mjs test/browser-wasm-cpu.test.mjs test/wllama-webgpu-runtime.test.mjs",
142
142
  "test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.test.mjs test/html-import-registration.test.mjs test/html-import-packaged-script.test.mjs test/logging-regression.test.mjs test/markdown-speech.test.mjs test/prepared-speech.test.mjs test/ai-audio-output.test.mjs test/native-provider-generation.test.mjs test/speech-queue-regression.test.mjs test/testing.test.mjs test/test-sets.test.mjs",
143
143
  "check": "node tools/check-source.mjs && npm test",
144
144
  "check:release": "node tools/check-source.mjs --package-only && npm run test:release",
package/src/core/host.mjs CHANGED
@@ -39,6 +39,7 @@ export function resolveNativeLaunchContext({appId, context = {}} = {}) {
39
39
  const resolved = {
40
40
  ...context,
41
41
  stateRoot,
42
+ modelsDirectory: context.modelsDirectory === undefined ? path.join(root, 'Models') : context.modelsDirectory,
42
43
  workspaceRoot: context.workspaceRoot === undefined ? path.join(root, 'Workspace') : context.workspaceRoot
43
44
  };
44
45
  if (context.sharedHost !== undefined) {