arcane-os 0.5.11 → 0.5.12

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.5.12
6
+
7
+ - Add optional `voice`, `speed`, `pauseAfterMs`, and `waitForPlayback` fields to
8
+ `AI.streamTTS(text, end, options)`. Complete passages can enter the existing
9
+ segmented generation queue immediately, retain their authored pauses on the
10
+ audio clock, and await their own playback completion. Existing calls still
11
+ return after preparation; stop and terminal failure settle playback waits
12
+ as `false`, while autoplay permission waiting remains pending.
13
+ - Restore `AI.fetch(...)` and `AI.streamMessage(...)` after their unintended
14
+ removal during source cleanup. Their existing signatures, callbacks, return
15
+ behavior, and private inference plumbing remain available alongside
16
+ `fetchRequest({...})` and `streamRequest({...})`; callers do not need to
17
+ migrate. Restore chat-memory callers and document both public forms as
18
+ sharing the current provider implementations.
19
+ - Update maintained SDK references and their generated pages, omit obsolete
20
+ provider context-token guidance, and describe current native defaults and
21
+ Winlogon bindings without treating them
22
+ as retired SDK APIs. Native Core implementations and upstream dependencies
23
+ are not changed by this SDK cleanup.
24
+
3
25
  ## 0.5.11
4
26
 
5
27
  - Default browser Kokoro TTS to four concurrent synthesis slots in both the
package/README.md CHANGED
@@ -19,7 +19,7 @@ version-locked SDK runtime, while an integrated Arcane checkout uses its live
19
19
  `arcane/` runtime. Both profiles preserve the same app URLs, theme, packaging,
20
20
  event, cancellation, and browser run contracts.
21
21
 
22
- This checkout defines the `0.5.11` SDK contract. Applications pin one exact npm
22
+ This checkout defines the `0.5.12` SDK contract. Applications pin one exact npm
23
23
  version and lockfile; registry state is deliberately not baked into application
24
24
  artifacts.
25
25
 
@@ -35,7 +35,7 @@ Create one browser application, install its pinned SDK, and start its source
35
35
  server:
36
36
 
37
37
  ```bash
38
- npx arcane-os@0.5.11 new hello-speech --path ./hello-speech --target browser
38
+ npx arcane-os@0.5.12 new hello-speech --path ./hello-speech --target browser
39
39
  cd hello-speech
40
40
  npm install
41
41
  npm run dev
@@ -297,7 +297,7 @@ uses the same controller for automatic memory extraction.
297
297
  Create a new repository-shaped Arcane application with the exact stable SDK:
298
298
 
299
299
  ```bash
300
- npx arcane-os@0.5.11 new my-app --path ./my-app --target portable --git
300
+ npx arcane-os@0.5.12 new my-app --path ./my-app --target portable --git
301
301
  cd my-app
302
302
  npm install
303
303
  npm run dev
@@ -307,7 +307,7 @@ To enroll an existing repository, install the exact SDK and initialize only
307
307
  missing Arcane files:
308
308
 
309
309
  ```bash
310
- npm install --save-dev --save-exact arcane-os@0.5.11
310
+ npm install --save-dev --save-exact arcane-os@0.5.12
311
311
  npm exec -- arcane init my-app --target portable
312
312
  ```
313
313
 
@@ -323,7 +323,7 @@ npm exec -- arcane-os targets
323
323
  No global SDK install or standalone Arcane CLI is required. The application
324
324
  repository's exact npm dependency and lockfile own the CLI and toolchain version.
325
325
 
326
- Use `npx arcane-os@0.5.11` for the initial bootstrap because it names this npm
326
+ Use `npx arcane-os@0.5.12` for the initial bootstrap because it names this npm
327
327
  package explicitly; bare `npx arcane` outside an installed project could resolve
328
328
  a different package. Both installed commands invoke the same headless toolchain.
329
329
  Project-local npm scripts use the SDK pinned by that app's `package-lock.json`,
@@ -343,7 +343,7 @@ node ./bin/arcane.mjs new local-app --path ../local-app --target portable --git
343
343
 
344
344
  # From the generated app repository
345
345
  cd ../local-app
346
- npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.5.11.tgz
346
+ npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.5.12.tgz
347
347
  npm ci
348
348
  ```
349
349
 
@@ -352,7 +352,7 @@ same location. The lockfile retains the selected package dependency while
352
352
  Arcane uses the installed package name and version. Local directory `file:` dependencies are not
353
353
  accepted because npm may install them as links; use a packed `.tgz`. A GitHub
354
354
  runner also needs that tarball at the locked path. After publication, replace
355
- the local declaration with the exact `arcane-os@0.5.11` registry package and
355
+ the local declaration with the exact `arcane-os@0.5.12` registry package and
356
356
  commit the regenerated lock.
357
357
 
358
358
  Generated repositories use `npm ci --ignore-scripts` in CI. Run dependency
@@ -491,7 +491,7 @@ package installation, or assertions.
491
491
 
492
492
  ## Current target support
493
493
 
494
- Version `0.5.11` exposes one browser target and five explicitly paired
494
+ Version `0.5.12` exposes one browser target and five explicitly paired
495
495
  native development targets: a non-runnable portable directory, a
496
496
  Windows x64 unsigned-local-test EXE bundle, Linux x64 and Linux ARM64
497
497
  unsigned-local-test DEBs, and an Android development-signed APK. The
@@ -279,7 +279,7 @@ paths are withheld from the native provider. The provider copies the complete
279
279
  selected release rather than accepting an unrelated source path. Verification
280
280
  is a separate explicit operation for a selected release artifact.
281
281
 
282
- The SDK `0.5.11` runtime requires Arcane `0.8.12` or newer. Compatibility
282
+ The SDK `0.5.12` runtime requires Arcane `0.8.12` or newer. Compatibility
283
283
  is contractual rather than exact-version pinning: the prepared Core must meet
284
284
  the highest minimum declared by the runtime, selected app, and bundled app
285
285
  dependencies; keep each app's Arcane protocol generation; and provide every
@@ -17,7 +17,7 @@ high-level page links to the relevant deep section instead of repeating it.
17
17
  Install the SDK in your application:
18
18
 
19
19
  ```sh
20
- npm install --save-exact arcane-os@0.5.11
20
+ npm install --save-exact arcane-os@0.5.12
21
21
  ```
22
22
 
23
23
  For your first AI call, follow the [TWiN Cloud quick start](ai/twin-cloud.md).
@@ -56,9 +56,9 @@ This repository contains explicitly versioned surfaces with different owners:
56
56
 
57
57
  | Surface | Source identity | Meaning |
58
58
  | --- | --- | --- |
59
- | SDK and CLI | `arcane-os` `0.5.11` | The Node.js toolchain, portable `arcane-os/event-manager`, `arcane-os/mail`, `arcane-os/preference-store`, and `arcane-os/speech-playback` entrypoints, plus the browser-only `arcane-os/ai/browser-wasm` and `arcane-os/ai/browser-speech` entrypoints in this checkout. |
60
- | Browser runtime | SDK `0.5.11`, protocol `arcane/1`, `runtime/` | The SDK-canonical runtime tree. `listRuntimeFiles()`, `readRuntimeFile()`, and `loadRuntimeRelease()` derive its current inventory directly from the selected directory. |
61
- | Browser SDK runtime | SDK `0.5.11`, `browser-runtime/` | The browser closure for events, Wllama, and Browser Speech mechanisms. `listSdkBrowserRuntimeFiles()`, `readSdkBrowserRuntimeFile()`, and `loadSdkBrowserRuntimeRelease()` derive its current inventory directly from the selected directory. |
59
+ | SDK and CLI | `arcane-os` `0.5.12` | The Node.js toolchain, portable `arcane-os/event-manager`, `arcane-os/mail`, `arcane-os/preference-store`, and `arcane-os/speech-playback` entrypoints, plus the browser-only `arcane-os/ai/browser-wasm` and `arcane-os/ai/browser-speech` entrypoints in this checkout. |
60
+ | Browser runtime | SDK `0.5.12`, protocol `arcane/1`, `runtime/` | The SDK-canonical runtime tree. `listRuntimeFiles()`, `readRuntimeFile()`, and `loadRuntimeRelease()` derive its current inventory directly from the selected directory. |
61
+ | Browser SDK runtime | SDK `0.5.12`, `browser-runtime/` | The browser closure for events, Wllama, and Browser Speech mechanisms. `listSdkBrowserRuntimeFiles()`, `readSdkBrowserRuntimeFile()`, and `loadSdkBrowserRuntimeRelease()` derive its current inventory directly from the selected directory. |
62
62
  | Core reference snapshot | Arcane OS commit `567ad110bf57a1c2d4a3daa22ae93716cc5f4d7e`, protocol `arcane/1` | The application-facing Core contract imported into `docs/reference/core/`, with SDK-local links and package-boundary notes added explicitly. |
63
63
 
64
64
  The SDK runtime source and Core reference have different owners. A browser
@@ -73,7 +73,7 @@ and the distinction between a documentation snapshot and the selected runtime.
73
73
 
74
74
  ## Installed documentation and release identity
75
75
 
76
- This reference accompanies `arcane-os@0.5.11`. The installed package includes
76
+ This reference accompanies `arcane-os@0.5.12`. The installed package includes
77
77
  the maintained `docs/` tree and `examples/wasm-ai-demo/` source alongside
78
78
  README and CHANGELOG. Open `node_modules/arcane-os/docs/reference/README.md`
79
79
  for the matching local reference. The generated website and test suites remain
@@ -13,7 +13,7 @@ import map resolves `arcane/AI` and `arcane/DBOPFS`. These browser modules are
13
13
  not Node inference APIs. To create an application:
14
14
 
15
15
  ```bash
16
- npx arcane-os@0.5.11 new hello-speech --path ./hello-speech --target browser
16
+ npx arcane-os@0.5.12 new hello-speech --path ./hello-speech --target browser
17
17
  cd hello-speech
18
18
  npm install
19
19
  npm run dev
@@ -89,8 +89,8 @@ speakButton.addEventListener('click', async function sayHello() {
89
89
  speakButton.disabled = true;
90
90
  try {
91
91
  await ai.setSpeechMuted(false); // Loads the selected TTS provider.
92
- const complete = await ai.streamTTS('Hello from Arcane. ', true);
93
- console.log('Speech preparation completed:', complete);
92
+ const prepared = await ai.streamTTS('Hello from Arcane. ', true);
93
+ console.log('Speech preparation completed:', prepared);
94
94
  } catch (error) {
95
95
  console.error(error.code, error.message);
96
96
  } finally {
@@ -101,10 +101,12 @@ speakButton.addEventListener('click', async function sayHello() {
101
101
 
102
102
  The first user action may download the selected runtime, model, and voice.
103
103
  The browser may require another audio-unlock gesture after a long load; the SDK
104
- retains prepared audio for that gesture. `streamTTS()` prepares and schedules
105
- playback; its promise is not proof that a listener heard the sound. It returns
106
- `false` for muted, stopped, or failed work, and the SDK reports full synthesis
107
- or playback failures in its console diagnostics and `ai-tts-failure` event.
104
+ retains prepared audio for that gesture. The two-argument `streamTTS()` call
105
+ resolves after preparing audio for scheduling; it does not wait for playback
106
+ to end. It returns `false` when that preparation is muted, stopped, or fails.
107
+ Later playback failures still reach the SDK's complete console diagnostics
108
+ and `ai-tts-failure` event. Use the optional playback mode below when you need
109
+ to wait for the submitted audio to end. Neither mode proves a listener heard it.
108
110
 
109
111
  To display complete high-level playback errors in this page, observe its
110
112
  existing event. The listener belongs to this example's one `ai` instance:
@@ -133,6 +135,70 @@ Ready adjacent audio buffers use contiguous AudioContext scheduling. Browser
133
135
  audio scheduling and selected WebGPU status do not prove physical GPU kernel
134
136
  overlap or audio quality. LLM and Whisper/STT capacity remains one.
135
137
 
138
+ ## Queue complete passages and wait for playback
139
+
140
+ These options are available in SDK `0.5.12`.
141
+
142
+ For a complete page or passage, call
143
+ `ai.streamTTS(text, true, {voice, speed, pauseAfterMs, waitForPlayback:true})`.
144
+ The existing AI queue owns segmentation, concurrent synthesis, ordered
145
+ playback, and cancellation. Supply the exact text; there is no need for an
146
+ application sentence queue, audio cache, or playback scheduler.
147
+
148
+ This function uses the configured `ai` above. Its application-supplied
149
+ `passages` argument is an ordered array of `{text, voice?, speed?, pauseAfterMs?}`
150
+ records. An omitted voice uses the selected model's default voice, an omitted
151
+ speed uses `ai.voiceSpeed`, and an omitted pause is zero. Each supplied voice
152
+ must be supported by the selected model; speed must be positive. A pause is
153
+ finite, nonnegative milliseconds and applies only after that passage's final
154
+ extracted segment. These options do not change the instance defaults.
155
+
156
+ ```javascript
157
+ async function speakPassages(passages) {
158
+ await ai.setSpeechMuted(false);
159
+ const pending = passages.map(
160
+ function queuePassage(passage) {
161
+ return ai.streamTTS(
162
+ passage.text,
163
+ true,
164
+ {
165
+ voice: passage.voice,
166
+ speed: passage.speed,
167
+ pauseAfterMs: passage.pauseAfterMs,
168
+ waitForPlayback: true
169
+ }
170
+ );
171
+ }
172
+ );
173
+ return Promise.all(pending);
174
+ }
175
+ ```
176
+
177
+ Call `speakPassages(...)` from your owned user action and handle errors with
178
+ the earlier `error.code` / `error.message` pattern. The `map` submits every
179
+ passage synchronously before `Promise.all` waits, so synthesis can use the
180
+ provider's available capacity. The returned array has one boolean per passage
181
+ in input order: `true` after all its extracted audio buffers naturally end,
182
+ or `false` after terminal cancellation or failure. `ai.stopAudio()` cancels
183
+ all speech owned by that AI instance and settles pending playback results
184
+ `false`.
185
+
186
+ The selected voice and speed are captured for segments extracted by that call.
187
+ That includes any text left in the same AI instance's partial-stream buffer;
188
+ finish the previous producer before starting a separate complete passage.
189
+ Options are not retained with an unfinished `end:false` remainder. A later
190
+ call supplies its own options, and `finishTTS()` uses defaults.
191
+ A call extracting no segments returns `true` without waiting for earlier jobs.
192
+ An already muted call returns `false`.
193
+
194
+ Autoplay permission waiting and recoverable audio-resume attempts leave the
195
+ playback promise pending until playback completes or is stopped. A failed
196
+ resume of a closed `AudioContext` terminates the affected jobs and settles
197
+ their playback results `false`. A trailing
198
+ pause delays the next queued audio on the existing `AudioContext` clock; it
199
+ does not delay the preceding promise after that passage's last buffer ends.
200
+ The promise is a playback result, not a listener acknowledgement.
201
+
136
202
  ## Stream chunks as they arrive
137
203
 
138
204
  Use the configured `ai` created above. Run this snippet from an owned user
@@ -9,7 +9,7 @@ same managed browser imports as the [browser speech quick start](browser-speech.
9
9
  Create an application and start its source server:
10
10
 
11
11
  ```bash
12
- npx arcane-os@0.5.11 new hello-twin --path ./hello-twin --target browser
12
+ npx arcane-os@0.5.12 new hello-twin --path ./hello-twin --target browser
13
13
  cd hello-twin
14
14
  npm install
15
15
  npm run dev
@@ -214,7 +214,7 @@ portable runtime subpaths such as `arcane-os/preference-store` and
214
214
  modules. The result reports the complete map written to the selected
215
215
  application; no fixed entry count is a release contract.
216
216
 
217
- SDK `0.5.11` preserves the physical workspace route count and ordered include
217
+ SDK `0.5.12` preserves the physical workspace route count and ordered include
218
218
  list. External and modern integrated routes require `components`, `css`,
219
219
  `dependencies`, `entities`, `img`, `modules`, and `sdk`; a physical workspace
220
220
  may omit only an optional trailing `security` include. The external license
@@ -9,7 +9,7 @@ They are not TypeScript declarations.
9
9
 
10
10
  ## Portable SDK AI and Core AI
11
11
 
12
- The SDK `0.5.11` has two related but separate normalized boundaries:
12
+ The SDK `0.5.12` has two related but separate normalized boundaries:
13
13
 
14
14
  | Boundary | Use | Host |
15
15
  |---|---|---|
@@ -132,7 +132,7 @@ No other message fields are accepted.
132
132
  | `model` | `string` | Yes | Effective configured model | Model used by provider-neutral chat. |
133
133
  | `configured` | `boolean` | Yes | - | Required provider configuration is present. |
134
134
  | `local` | `boolean` | Yes | `true` only for Ollama | Whether inference remains local. |
135
- | `responseLength` | `"low" \| "medium" \| "high"` | Yes | Legacy invalid/missing state resolves to `"medium"` | Conversational response target, not a provider token limit. |
135
+ | `responseLength` | `"low" \| "medium" \| "high"` | Yes | Invalid/missing state resolves to `"medium"` | Conversational response target, not a provider token limit. |
136
136
 
137
137
  An OpenAI profile is returned only after Arcane proves a protected credential
138
138
  exists and the configured model is available to that account.
@@ -620,7 +620,6 @@ The wrapper supplies the validated `model` field.
620
620
  | `format` | JSON value | No | Text, JSON, or schema format | Output format. |
621
621
  | `options` | object | No | Provider-native; `num_ctx` is 1,024-262,144 when supplied | Runtime options. |
622
622
  | `system`, `template` | `string` | No | Provider-native | Prompt controls. |
623
- | `context` | `array` | No | Provider-native | Legacy context tokens. |
624
623
  | `raw` | `boolean` | No | Provider-native | Raw prompt mode. |
625
624
  | `keep_alive` | `string \| number` | No | Provider-native | Residency. |
626
625
  | `think`, `logprobs`, `top_logprobs` | JSON value | No | Provider-native | Reasoning/log-probability controls. |
@@ -128,7 +128,7 @@ directly.
128
128
  | `Arcane.ollama.saveServiceSettings(settings)` | `settings`: [`OllamaServiceSettingsInput`](arcane-ai-contracts.md#ollama-service-settings-input) | [`Promise<OllamaServiceSettingsResult>`](arcane-ai-contracts.md#ollama-service-settings-result) | Saves managed service settings. |
129
129
 
130
130
  The native AI profile always returns `responseLength` as `"low"`, `"medium"`,
131
- or `"high"`; missing or invalid legacy persisted values safely resolve to
131
+ or `"high"`; missing or invalid persisted values safely resolve to
132
132
  `"medium"`. New saves reject any other value. Conversational applications may
133
133
  use this target to augment their system prompt, but specific user requests and
134
134
  required application, structured-output, tool, safety, evidence, warning, or
@@ -537,9 +537,9 @@ exclusive-mutation boundary. Native set results must match the requested name,
537
537
  protection decision, and exact ordinary value or protected five-bullet mask
538
538
  before Core returns them.
539
539
  Microsoft NT rejects a protected-record/plaintext-registry shadow. Linux binds
540
- new protected values to a random Secret Service generation in a namespace that
541
- does not overlap legacy entries, fsyncs the metadata file and directory before
542
- cleaning the prior generation, and keeps legacy lookup compatibility.
540
+ new protected values to a random Secret Service generation and fsyncs the
541
+ metadata file and directory before cleaning the prior generation. Native
542
+ storage implementation details belong to Arcane Core, not the SDK.
543
543
  Failure to verify candidate, prior-generation, or deletion cleanup rejects with
544
544
  `ENVIRONMENT_PROTECTED_CLEANUP_FAILED`. Linux post-rename durability ambiguity
545
545
  rejects with `ENVIRONMENT_METADATA_COMMIT_UNCERTAIN`. Native code retains
@@ -579,7 +579,7 @@ Native storage and preferences resolve below
579
579
  cannot provide a different identity. Browser OPFS follows
580
580
  `apps/<application-id>/...`, DBLS fallback keys use
581
581
  `arcane.apps.<application-id>:`, and native browser profiles are also app-owned.
582
- Unowned legacy global data is preserved but not guessed into an app. The complete
582
+ Unowned global data is preserved but not guessed into an app. The complete
583
583
  layout and same-origin browser limitation are maintained in the repository-only
584
584
  [Application data isolation](https://github.com/TheWizardNexus/ARCANE-OS/blob/567ad110bf57a1c2d4a3daa22ae93716cc5f4d7e/docs/application-data-isolation.md); see the public [repository-access boundary](https://github.com/TheWizardNexus/ARCANE-OS/blob/567ad110bf57a1c2d4a3daa22ae93716cc5f4d7e/apps/docs/guides/repository-access.md#private-developer-material).
585
585
 
@@ -82,7 +82,7 @@ The service is configured with the global `OLLAMA_MODELS` directory. Arcane clie
82
82
  npm run model:ensure -- --target=all --model=3b --smoke
83
83
  ```
84
84
 
85
- `--target=arcane` is the backward-compatible platform default. Product-specific targets and definitions remain documented by their owning applications. `--target=all` reuses one base pull across both currently configured targets, selects `arcane:3b` as `arcane:latest`, and preserves unrelated settings. `--smoke` adds one bounded local inference probe per selected alias. The equivalent platform-only raw Ollama sequence is `ollama pull granite4.1:3b-q4_K_M` followed by `ollama create arcane:3b -f arcane/models/Arcane-3B.Modelfile`; the npm command additionally verifies lineage and is the supported repository workflow.
85
+ `--target=arcane` selects the platform model and is the default. Product-specific targets and definitions remain documented by their owning applications. `--target=all` reuses one base pull across both currently configured targets, selects `arcane:3b` as `arcane:latest`, and preserves unrelated settings. `--smoke` adds one bounded local inference probe per selected alias. The equivalent platform-only raw Ollama sequence is `ollama pull granite4.1:3b-q4_K_M` followed by `ollama create arcane:3b -f arcane/models/Arcane-3B.Modelfile`; the npm command additionally verifies lineage and is the supported repository workflow.
86
86
 
87
87
  The managed service's aliases and model layers are machine-wide, while Arcane model preference and application profile selection remain per-user. `model:ensure` does not install or attest `ArcaneOllama`, does not update an already installed Core or UI after a source pull, and does not create application aliases outside the selected target set. Use the verified Provisioner to establish or repair the machine service and to install current application/runtime bytes. The public [Device and model support](https://github.com/TheWizardNexus/ARCANE-OS/blob/567ad110bf57a1c2d4a3daa22ae93716cc5f4d7e/apps/docs/guides/device-support.md) page tracks the exact product matrix, platform maturity, and COTS physical-validation backlog; 16B and 30B are validation targets, not managed variants.
88
88
 
@@ -516,7 +516,7 @@ recorded binding/security metadata, `canRestoreShell`,
516
516
  `shellRecoveryPrepared`, `accountMutationPhase`, and `activationRequired`.
517
517
 
518
518
  Microsoft NT records include `username`, `sid`, `enabled`, `profile`, `shell`,
519
- both policy and legacy shell values/presence flags, `shellAssigned`,
519
+ both policy and Winlogon shell values/presence flags, `shellAssigned`,
520
520
  `shellBindingVersion`, `assignmentMode`, `verification`, and `source`. Linux
521
521
  records include `username`, optional `uid`, `enabled`, `profile`, `shell`,
522
522
  `shellAssigned`, `verification`, and `source`. Nullable or recorded-only values
@@ -824,7 +824,7 @@ Provisioner type, elevation, and the exclusive Core mutation boundary. It does
824
824
  not delete the account or change its password, but it materially changes what
825
825
  starts at the user's next sign-in.
826
826
 
827
- Microsoft NT restores both prior policy and legacy shell bindings, including
827
+ Microsoft NT restores both prior policy and Winlogon shell bindings, including
828
828
  their recorded absence. Linux restores the recorded login shell after checking
829
829
  the exact uid and ensuring the prior executable still exists. The host refuses
830
830
  to overwrite a value changed outside the recorded transaction.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "sdkVersion": "0.5.11",
3
+ "sdkVersion": "0.5.12",
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",
@@ -5,7 +5,7 @@
5
5
  "repository": "https://github.com/TheWizardNexus/arcane-os-sdk.git",
6
6
  "branch": "main",
7
7
  "path": "runtime/arcane/components",
8
- "sdkVersion": "0.5.11"
8
+ "sdkVersion": "0.5.12"
9
9
  },
10
10
  "componentCount": 39,
11
11
  "loader": "/arcane/modules/HTMLImport.js",
@@ -5,7 +5,7 @@
5
5
  "repository": "https://github.com/TheWizardNexus/arcane-os-sdk.git",
6
6
  "branch": "main",
7
7
  "path": "runtime/arcane",
8
- "sdkVersion": "0.5.11",
8
+ "sdkVersion": "0.5.12",
9
9
  "protocol": "arcane/1"
10
10
  },
11
11
  "artifactCount": 84,
@@ -29,8 +29,8 @@
29
29
  "summary": "Owns provider-selectable chat and the one-time caller-authority browser STT/TTS configuration, lifecycle, synthesis, transcription, and playback boundary.",
30
30
  "availability": "Browser + native bridge + TWiN Cloud",
31
31
  "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",
32
- "normalization": "Complete mutable caller-owned browser speech authority, SDK-owned provider registration/replacement/disposal, explicit STT/TTS activation, normalized Kokoro TTS execution with default capacity 4, 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, immediate exact-segment synthesis, 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.",
33
- "surface": "Browser-speech protocol/event/error/reason constants; default `AI`; read-only `providerRuntime`, `browserSpeechConfiguration`, and `browserSpeechDescriptor`; explicit `providerRuntime.status(role,{execution:true})` snapshots; configure/dispose speech, route lifecycle, declaration-validated chat/stream requests with exact ordered calls, synthesis/transcription, and playback controls; initializes from current canonical user readiness, installs `window.ai`, and projects `ai-ready` plus active-generation `ai-tts-failure`."
32
+ "normalization": "Complete mutable caller-owned browser speech authority, SDK-owned provider registration/replacement/disposal, explicit STT/TTS activation, normalized Kokoro TTS execution with default capacity 4, 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, immediate exact-segment synthesis, per-call voice/speed capture, optional final-segment pause on the existing audio clock, optional per-call terminal playback completion, 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.",
33
+ "surface": "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 (available in SDK 0.5.12); configure/dispose speech, route lifecycle, declaration-validated chat/stream requests with exact ordered calls, 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
34
  },
35
35
  {
36
36
  "file": "runtime/arcane/modules/AIPreferenceRuntime.js",
@@ -159,7 +159,7 @@ imports such as:
159
159
  import ollama from 'arcane/Ollama';
160
160
  ```
161
161
 
162
- The physical-v1 tree lives entirely beneath `arcane/`. SDK `0.5.11` projects the
162
+ The physical-v1 tree lives entirely beneath `arcane/`. SDK `0.5.12` projects the
163
163
  complete canonical runtime and browser runtime selected by the installed SDK
164
164
  package. Runtime dependencies stay under
165
165
  `arcane/dependencies/`; the SDK event and browser-AI closure stays under
@@ -174,7 +174,7 @@ integrated physical route uses the same ordered include list in its one route.
174
174
  Omitting only that final optional entry is compatible. Removing, reordering, or
175
175
  renaming any preceding entry changes the physical contract.
176
176
 
177
- The `0.5.11` map derives its complete entries from the selected runtime graph;
177
+ The `0.5.12` map derives its complete entries from the selected runtime graph;
178
178
  application source imports do not select a fixed entry count. The operation
179
179
  result reports `imports`, `entryCount`, and `excludedModules`; reached-file
180
180
  traversal remains internal. The
@@ -239,8 +239,8 @@ exactly `dependencyName`, `packageSource`,
239
239
  `canonicalPackageRoot`, `packageName`, `packageVersion`, `runtimeRoot`,
240
240
  and `browserRuntimeRoot`. A
241
241
  workspace may use the canonical dependency name or one exact npm alias such as
242
- `npm:arcane-os@0.5.11`; the physical package manifest must still identify
243
- exactly as `arcane-os@0.5.11`. Canonical-plus-alias duplicates, multiple aliases,
242
+ `npm:arcane-os@0.5.12`; the physical package manifest must still identify
243
+ exactly as `arcane-os@0.5.12`. Canonical-plus-alias duplicates, multiple aliases,
244
244
  links/junctions, indirect package roots, or version drift are reported.
245
245
 
246
246
  For external workspaces, `arcane dev` serves the projected `arcane/` root,
@@ -351,7 +351,7 @@ does not silently delete the app-owned cache. A complete whole member supersedes
351
351
  its current resumable fragments. Cleanup failure is warned without hiding the
352
352
  usable model.
353
353
 
354
- SDK `0.5.11` requires WebGPU. Load requests full offload and waits for the runtime
354
+ SDK `0.5.12` requires WebGPU. Load requests full offload and waits for the runtime
355
355
  to report a loaded model. `navigator.gpu` presence alone is
356
356
  not readiness. There is no CPU fallback, partial-offload success mode, or
357
357
  silent switch to native/Core/cloud inference.
@@ -359,8 +359,11 @@ unload.
359
359
 
360
360
  Chat listens for the bound (or current global) AI runtime's `ai-tts-failure`
361
361
  event and forwards its complete Error and exact operation boundary to
362
- `speech.reportTTSError()`. This includes decode and playback-start failures that
363
- settle after `streamTTS()` has already resolved. Runtime mute, cancellation,
362
+ `speech.reportTTSError()`. Playback-start or playback-resume failures can occur
363
+ after Chat's two-argument `streamTTS()` preparation promise has resolved.
364
+ SDK `0.5.12` adds a `waitForPlayback:true` mode for the
365
+ terminal result of the segments submitted by one invocation; Chat continues
366
+ to feed chunks without waiting for playback. Runtime mute, cancellation,
364
367
  permission waiting, and stale generations remain non-errors.
365
368
 
366
369
  Each visible model chunk is still forwarded to `AI.streamTTS()` in arrival
@@ -141,11 +141,17 @@ default `AI`; read-only `providerRuntime`, `browserSpeechConfiguration`, and
141
141
  `configureSpeechProviders()`, `transitionAI()`, `transitionProviders()`,
142
142
  `transitionSpeechProviders()`, `startProviders()`, `setSpeechMuted()`,
143
143
  `streamRequest()`, `streamMessage()`, `fetchRequest()`, `fetch()`,
144
- read-only `ttsSegmentation`, `configureTTSSegmentation()`, `streamTTS()`,
144
+ read-only `ttsSegmentation`, `configureTTSSegmentation()`,
145
+ `streamTTS(text='',end=false,options={})`,
145
146
  `finishTTS()`, `fetchTTS()`, `fetchSTT()`, `stopAudio()`, `resumeAudio()`,
146
147
  `playAudio()`; consumes `user-entity-loaded` and `arcane-ollama-ready`,
147
148
  installs `window.ai`, and emits `ai-ready` and `ai-tts-failure`.
148
149
 
150
+ `fetch(...)` and `fetchRequest(options)` are asynchronous complete-response
151
+ entry points. `streamMessage(...)` and `streamRequest(options)` deliver
152
+ incremental responses. The positional and object forms share the existing
153
+ provider implementations; neither form is a retired compatibility API.
154
+
149
155
  Initialization uses the canonical realm user's actual readiness state. If
150
156
  `window.user?.ready` is already true, AI initializes immediately. Otherwise one
151
157
  shared registration observes `user-entity-loaded`, then rechecks readiness
@@ -248,8 +254,37 @@ the setting is the instance's `opus` default and the model rejects it, the catal
248
254
  `speech.defaultResponseFormat` is used, while any other unsupported setting is
249
255
  rejected. It propagates the caller-owned signal and returns a playable `Blob`;
250
256
  it does not independently choose a provider, cloud fallback, model, runtime, or
251
- voice policy for the application. Existing `streamTTS()` and `finishTTS()` use
252
- this same request boundary. Streaming speech retains sentence
257
+ voice policy for the application. `streamTTS(text='',end=false,options={})` and
258
+ `finishTTS()` use this same request boundary. The third-argument options below
259
+ are available in SDK `0.5.12`:
260
+
261
+ | Field | Default | Meaning |
262
+ | --- | --- | --- |
263
+ | `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. |
264
+ | `speed` | Current `ai.voiceSpeed` | A supplied positive speed is captured for those segments and forwarded to `fetchTTS()`. It does not change `ai.voiceSpeed`. |
265
+ | `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. |
266
+ | `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. |
267
+
268
+ The voice and speed use the existing `fetchTTS()` validation and error path.
269
+ No option rewrites the submitted text. Overrides belong to the segments
270
+ extracted in that invocation, including any text buffered by an earlier call.
271
+ Options are not retained with an unfinished `end:false` remainder; a later
272
+ call supplies its own options, and `finishTTS()` uses defaults. Use `end:true`
273
+ for a complete passage. A call extracting no segments resolves
274
+ `true` without waiting for earlier jobs; `finishTTS()` remains a preparation
275
+ flush, not a queue-wide playback barrier. A muted call resolves `false`.
276
+
277
+ Playback completion stays pending while the browser waits for an audio-unlock
278
+ gesture or a recoverable resume attempt. If resuming a closed `AudioContext`
279
+ fails, the affected jobs terminate and their playback results settle `false`.
280
+ `stopAudio()` cancels all speech owned
281
+ by this AI instance and settles pending playback promises `false`. A trailing
282
+ pause delays the next queued audio; the preceding promise resolves when its
283
+ last audio buffer ends, without waiting out that pause. Completion describes
284
+ the playback lifecycle, not proof that a listener heard the sound. See the
285
+ [complete-passage example](ai/browser-speech.md#queue-complete-passages-and-wait-for-playback).
286
+
287
+ Streaming speech retains sentence
253
288
  segmentation by default. `configureTTSSegmentation({punctuation,wordCadence})`
254
289
  accepts `punctuation:'sentence'|'any'|'none'` and a `wordCadence` that is either
255
290
  `null` or a positive integer. `punctuation:'any'` completes a segment at a
@@ -1661,7 +1696,7 @@ and `read`, filters source metadata before calling
1661
1696
  **Browser or compatible host with an injected DBOPFS adapter.** The adapter
1662
1697
  keeps the existing `get`, `set`, `getAllKeys`, and `delete` method names; Node
1663
1698
  can use the same class only through an explicitly imported runtime module and a
1664
- compatible storage adapter; SDK `0.5.11` publishes no Node package subpath or
1699
+ compatible storage adapter; SDK `0.5.12` publishes no Node package subpath or
1665
1700
  Node storage implementation for it. Bootstrap uses a concurrent
1666
1701
  generation, commits its manifest last, cleans partial data on failure, and
1667
1702
  rejects case-colliding IDs. Search
@@ -18,7 +18,7 @@ This table is the Node `package.json#exports` map: it defines package
18
18
  entrypoints for SDK/tooling code. It is distinct from the generated browser
19
19
  import map that resolves application-facing `arcane/*` modules and the focused
20
20
  EventManager entry. See [browser runtime delivery](protocols.md#browser-runtime-delivery)
21
- for the installed-inventory-derived physical-runtime contract in SDK `0.5.11`.
21
+ for the installed-inventory-derived physical-runtime contract in SDK `0.5.12`.
22
22
 
23
23
  | Specifier | Purpose |
24
24
  | --- | --- |
@@ -748,7 +748,7 @@ deterministic map. The package root also contains the public
748
748
  {
749
749
  schemaVersion: 1,
750
750
  kind: 'arcane-app-runtime-projection',
751
- sdkVersion: '0.5.11',
751
+ sdkVersion: '0.5.12',
752
752
  pathPrefix: 'arcane/',
753
753
  files: [{path}]
754
754
  }
@@ -2667,7 +2667,7 @@ The import-map operation also reports the stable operation-specific strings
2667
2667
  `ARCANE_IMPORT_MAP_INVALID`, `ARCANE_IMPORT_MAP_UNRESOLVED`, and
2668
2668
  `ARCANE_IMPORT_MAP_COLLISION`; package assembly can additionally report
2669
2669
  `ARCANE_IMPORT_MAP_CLEANUP_FAILED`. They are normalized `ArcaneError.code`
2670
- values, but are not properties added to this general registry in SDK `0.5.11`.
2670
+ values, but are not properties added to this general registry in SDK `0.5.12`.
2671
2671
 
2672
2672
  ### Value and import
2673
2673
 
@@ -3437,7 +3437,7 @@ workspace it additionally returns the exact installed package authority:
3437
3437
  packageSource,
3438
3438
  canonicalPackageRoot,
3439
3439
  packageName: 'arcane-os',
3440
- packageVersion: '0.5.11',
3440
+ packageVersion: '0.5.12',
3441
3441
  runtimeRoot,
3442
3442
  browserRuntimeRoot
3443
3443
  }
@@ -3445,9 +3445,9 @@ workspace it additionally returns the exact installed package authority:
3445
3445
  ```
3446
3446
 
3447
3447
  The dependency can be named `arcane-os` or be one exact npm alias for
3448
- `npm:arcane-os@0.5.11`. The selected installation must still be one direct,
3448
+ `npm:arcane-os@0.5.12`. The selected installation must still be one direct,
3449
3449
  physical, non-link package directory whose manifest identifies exactly as
3450
- `arcane-os@0.5.11`; duplicate canonical/alias declarations reject.
3450
+ `arcane-os@0.5.12`; duplicate canonical/alias declarations reject.
3451
3451
  `allowMissingManagedImportMap` is an internal packaging/development seam. An
3452
3452
  ordinary caller should leave it `false`.
3453
3453
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.5.11",
3
+ "version": "0.5.12",
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",
@@ -5673,7 +5673,8 @@ class AI {
5673
5673
 
5674
5674
  streamTTS(
5675
5675
  text='',
5676
- end=false
5676
+ end=false,
5677
+ options={}
5677
5678
  ){
5678
5679
  if(this.muted){
5679
5680
  if(end){
@@ -5682,6 +5683,12 @@ class AI {
5682
5683
  return Promise.resolve(false);
5683
5684
  }
5684
5685
 
5686
+ const {voice,speed,waitForPlayback=false}=options;
5687
+ const pauseAfterMs=Number(options.pauseAfterMs??0);
5688
+ if(!Number.isFinite(pauseAfterMs)||pauseAfterMs<0){
5689
+ throw new RangeError('Speech pauses must be a nonnegative number of milliseconds.');
5690
+ }
5691
+
5685
5692
  this.audioMessageChunks+=String(text||'');
5686
5693
  const outputs=this.#extractSpeechSegments(end);
5687
5694
 
@@ -5702,8 +5709,13 @@ class AI {
5702
5709
  const generation=this.speechGeneration;
5703
5710
  const jobs=[];
5704
5711
 
5705
- for(const output of outputs){
5706
- jobs.push(this.#queueSpeechJob(output,generation));
5712
+ for(const [index,output] of outputs.entries()){
5713
+ jobs.push(this.#queueSpeechJob(output,generation,{
5714
+ voice,
5715
+ speed,
5716
+ waitForPlayback,
5717
+ pauseAfterMs:index===outputs.length-1?pauseAfterMs:0
5718
+ }));
5707
5719
  }
5708
5720
 
5709
5721
  return Promise.all(jobs).then(
@@ -5857,7 +5869,7 @@ class AI {
5857
5869
  return this.#speechAbbreviations.has(token);
5858
5870
  }
5859
5871
 
5860
- #queueSpeechJob(text,generation){
5872
+ #queueSpeechJob(text,generation,options={}){
5861
5873
  const job={
5862
5874
  abortController:null,
5863
5875
  audioBuffer:null,
@@ -5867,13 +5879,22 @@ class AI {
5867
5879
  scheduledStart:null,
5868
5880
  sourceNode:null,
5869
5881
  state:'queued',
5870
- text
5882
+ text,
5883
+ voice:options.voice,
5884
+ speed:options.speed,
5885
+ pauseAfterMs:options.pauseAfterMs??0,
5886
+ resolvePlayback:null
5871
5887
  };
5872
5888
  const runtime=this;
5889
+ const playback=options.waitForPlayback===true
5890
+ ?new Promise(function captureSpeechPlaybackCompletion(resolve){
5891
+ job.resolvePlayback=resolve;
5892
+ })
5893
+ :null;
5873
5894
 
5874
5895
  this.speechJobs.push(job);
5875
5896
 
5876
- return Promise.resolve().then(
5897
+ const preparation=Promise.resolve().then(
5877
5898
  function synthesizeAvailableSpeech(){
5878
5899
  return runtime.#prepareSpeechJob(job);
5879
5900
  }
@@ -5886,6 +5907,7 @@ class AI {
5886
5907
  );
5887
5908
  }
5888
5909
  );
5910
+ return playback||preparation;
5889
5911
  }
5890
5912
 
5891
5913
  async #prepareSpeechJob(job){
@@ -5914,16 +5936,18 @@ class AI {
5914
5936
  async #requestSpeechAudio(job){
5915
5937
  job.abortController=new AbortController();
5916
5938
  const selection=this.#providerRuntime.selection('tts');
5917
- const voice=selection?this.#providerSpeechVoice():null;
5939
+ const voice=job.voice===undefined
5940
+ ?(selection?this.#providerSpeechVoice():null)
5941
+ :job.voice;
5918
5942
  const response=await this.fetchTTS(
5919
5943
  {
5920
5944
  model:selection?.modelId||this.modelTTS,
5921
5945
  input:job.text,
5922
- ...(voice?{voice}:{}),
5946
+ ...(job.voice!==undefined||voice?{voice}:{}),
5923
5947
  responseFormat:selection
5924
5948
  ?this.#providerSpeechResponseFormat()
5925
5949
  :this.audioFormat,
5926
- speed:this.voiceSpeed
5950
+ speed:job.speed===undefined?this.voiceSpeed:job.speed
5927
5951
  },
5928
5952
  job.abortController.signal
5929
5953
  );
@@ -6294,6 +6318,8 @@ class AI {
6294
6318
  for(const job of this.speechJobs){
6295
6319
  job.abortController?.abort();
6296
6320
  job.state='cancelled';
6321
+ job.resolvePlayback?.(false);
6322
+ job.resolvePlayback=null;
6297
6323
 
6298
6324
  if(job.sourceNode){
6299
6325
  job.sourceNode.onended=null;
@@ -6370,6 +6396,22 @@ class AI {
6370
6396
  if(attempt===this.speechResumeAttempt){
6371
6397
  this.speechResumePending=false;
6372
6398
  }
6399
+ if(context?.state==='closed'){
6400
+ this.#clearSpeechUnlock();
6401
+ const jobs=this.speechJobs.filter(function usesClosedSpeechContext(job){
6402
+ return job.audioContext===context;
6403
+ });
6404
+ if(!jobs.length){
6405
+ this.#publishTTSFailure(error,{
6406
+ boundary:'playback-resume',
6407
+ generation:this.speechGeneration
6408
+ });
6409
+ }
6410
+ for(const job of jobs){
6411
+ this.#failSpeechJob(job,error,'playback-resume');
6412
+ }
6413
+ return false;
6414
+ }
6373
6415
  this.#waitForSpeechGesture(error,context);
6374
6416
  if(error?.name!=='NotAllowedError'){
6375
6417
  this.#publishTTSFailure(error,{
@@ -6543,7 +6585,7 @@ class AI {
6543
6585
  job.state='scheduled';
6544
6586
  job.scheduledStart=scheduledStart;
6545
6587
  job.scheduledEnd=hasKnownDuration
6546
- ?scheduledStart+duration
6588
+ ?scheduledStart+duration+(job.pauseAfterMs??0)/1000
6547
6589
  :null;
6548
6590
  job.sourceNode.__arcaneStarted=true;
6549
6591
  if(!this.currentSpeechJob){
@@ -6606,6 +6648,11 @@ class AI {
6606
6648
  job.sourceNode.onended=null;
6607
6649
  }
6608
6650
 
6651
+ if(job.scheduledEnd===null&&job.pauseAfterMs>0){
6652
+ this.speechScheduleContext=job.audioContext;
6653
+ this.speechScheduleTime=
6654
+ (Number(job.audioContext?.currentTime)||0)+job.pauseAfterMs/1000;
6655
+ }
6609
6656
  job.state='complete';
6610
6657
  this.#removeSpeechJob(job);
6611
6658
 
@@ -6727,6 +6774,8 @@ class AI {
6727
6774
  }
6728
6775
 
6729
6776
  #removeSpeechJob(job){
6777
+ job.resolvePlayback?.(job.state==='complete');
6778
+ job.resolvePlayback=null;
6730
6779
  const jobIndex=this.speechJobs.indexOf(job);
6731
6780
 
6732
6781
  if(jobIndex>=0){