arcane-os 0.5.16 → 0.5.17

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
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.5.17
6
+
7
+ - Restore the public `SPEECH_VOICE_OPTIONS` ordered records and
8
+ `SPEECH_VOICE_ALIASES` membership set used by existing speech controls. Both
9
+ remain ordinary mutable compatibility values, and `SpeechPlayback` does not
10
+ select a voice from them automatically.
11
+ - Restore the optional `SpeechPlayback` constructor `onState(detail)` callback.
12
+ Canonical state dispatch remains first; callback failures are reported
13
+ without replacing playback settlement. Preserve capability-gated eager
14
+ submission, the provider-owned default capacity of four, indexed playback
15
+ order, native/custom serialization, cancellation, and Replay behavior.
16
+ - Apply the shared repeated-formatting-mark cleanup automatically to every TTS
17
+ entry path, including direct fetch, provider-runtime synthesis, browser
18
+ Kokoro requests, streaming chunks, and `SpeechPlayback`. Export the shared
19
+ `MarkdownSpeech` and `stripSpeechFormatting()` owner from
20
+ `arcane-os/speech-text`; keep original caller records unchanged and use
21
+ operation-local SDK metadata to prevent a second cleanup pass. No application
22
+ option is required, and the prior `textFormat` extra no longer disables or
23
+ selects cleanup.
24
+
5
25
  ## 0.5.16
6
26
 
7
27
  - Add optional Markdown narration filtering before `AI.streamTTS()`
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.16` SDK contract. Applications pin one exact npm
22
+ This checkout defines the `0.5.17` 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.16 new hello-speech --path ./hello-speech --target browser
38
+ npx arcane-os@0.5.17 new hello-speech --path ./hello-speech --target browser
39
39
  cd hello-speech
40
40
  npm install
41
41
  npm run dev
@@ -115,7 +115,13 @@ import SpeechPlayback from 'arcane/SpeechPlayback';
115
115
 
116
116
  const audio = document.body.appendChild(document.createElement('audio'));
117
117
  audio.controls = true;
118
- const narration = new SpeechPlayback({audio, speech: ai});
118
+ const narration = new SpeechPlayback({
119
+ audio,
120
+ speech: ai,
121
+ onState(detail) {
122
+ console.log('Speech state:', detail.state);
123
+ }
124
+ });
119
125
  const speakAll = document.body.appendChild(document.createElement('button'));
120
126
  speakAll.textContent = 'Speak all segments';
121
127
  speakAll.addEventListener('click', async function speakAllSegments() {
@@ -132,6 +138,20 @@ segments enter the provider queue immediately, up to four synthesize at once,
132
138
  and the audio still plays first, second, third. Native or custom speech without
133
139
  advertised provider execution capacity stays serialized with one lookahead.
134
140
 
141
+ Existing speech controls can also import the shared compatibility catalogs.
142
+ They list the ten existing value/label choices but do not choose a voice for
143
+ the application or prove that a selected provider supports one:
144
+
145
+ ```javascript
146
+ import {
147
+ SPEECH_VOICE_ALIASES,
148
+ SPEECH_VOICE_OPTIONS
149
+ } from 'arcane/SpeechPlayback';
150
+
151
+ console.log(SPEECH_VOICE_OPTIONS[0]); // {value: 'alloy', label: 'Alloy'}
152
+ console.log(SPEECH_VOICE_ALIASES.has('alloy')); // true
153
+ ```
154
+
135
155
  Continue with [streaming chunks, device selection, cancellation, status, and cleanup](https://github.com/TheWizardNexus/arcane-os-sdk/blob/main/docs/reference/ai/browser-speech.md),
136
156
  the [tiny TWiN Cloud request and saved-preference migration](https://github.com/TheWizardNexus/arcane-os-sdk/blob/main/docs/reference/ai/twin-cloud.md),
137
157
  or the [maintained WASM voice-chat example](https://github.com/TheWizardNexus/arcane-os-sdk/tree/main/examples/wasm-ai-demo).
@@ -290,13 +310,14 @@ TTS stream without taking over synthesis or playback:
290
310
  ```js
291
311
  ai.configureTTSSegmentation({
292
312
  punctuation:'any',
293
- wordCadence:4
313
+ wordCadence:null
294
314
  });
295
315
  ```
296
316
 
297
317
  The compatibility default remains sentence punctuation with no word cadence.
298
- The configured stream preserves every character and punctuation mark, chooses
299
- the earliest complete boundary, begins synthesis as each segment becomes
318
+ Within the already prepared speech text, the configured segmentation preserves
319
+ every character and punctuation mark, chooses the earliest complete boundary,
320
+ begins synthesis as each segment becomes
300
321
  available, and plays the completed audio in exact segment order. Ready adjacent
301
322
  buffers are scheduled consecutively on the browser audio clock rather than
302
323
  waiting for an `ended` callback before the next start.
@@ -305,13 +326,15 @@ keeping apostrophes, commas, and hyphens that join Unicode letters or numbers
305
326
  inside the same segment.
306
327
  Mute, stop, provider transition, and cancellation still govern the whole queue.
307
328
 
308
- For raw Markdown narration, use
309
- `ai.streamTTS(text,false,{textFormat:'markdown'})`. The SDK removes repeated
310
- same formatting marks (`*`, `#`, `_`, backtick, `~`) before segmentation,
311
- including runs split across chunks. Single punctuation and ordinary repeated
312
- punctuation remain literal. The format lasts through `finishTTS()`; new
313
- streams default to exact plain text. Shared chat selects Markdown mode while
314
- preserving original messages for display and storage.
329
+ Every TTS call automatically removes repeated same formatting marks (`*`, `#`,
330
+ `_`, backtick, `~`) from its outbound speech-input copy before synthesis.
331
+ Streaming calls also recognize a run split across chunks. Single marks and
332
+ ordinary repeated punctuation remain literal. No application option is needed;
333
+ an existing `textFormat` extra is ignored and cannot disable cleanup. Original
334
+ messages and caller payloads remain unchanged for display, storage, and model
335
+ input. The dependency-free `arcane-os/speech-text` entrypoint exports the same
336
+ `MarkdownSpeech` streaming class and `stripSpeechFormatting()` one-pass helper
337
+ for code that needs the speech-only transformation directly.
315
338
 
316
339
  The SDK runtime also owns `DBOPFSDocumentLibrary`,
317
340
  `DocumentLexicalSearch`, and `PersistentAIChatSession`. Document bootstrap is
@@ -334,7 +357,7 @@ uses the same controller for automatic memory extraction.
334
357
  Create a new repository-shaped Arcane application with the exact stable SDK:
335
358
 
336
359
  ```bash
337
- npx arcane-os@0.5.16 new my-app --path ./my-app --target portable --git
360
+ npx arcane-os@0.5.17 new my-app --path ./my-app --target portable --git
338
361
  cd my-app
339
362
  npm install
340
363
  npm run dev
@@ -344,7 +367,7 @@ To enroll an existing repository, install the exact SDK and initialize only
344
367
  missing Arcane files:
345
368
 
346
369
  ```bash
347
- npm install --save-dev --save-exact arcane-os@0.5.16
370
+ npm install --save-dev --save-exact arcane-os@0.5.17
348
371
  npm exec -- arcane init my-app --target portable
349
372
  ```
350
373
 
@@ -360,7 +383,7 @@ npm exec -- arcane-os targets
360
383
  No global SDK install or standalone Arcane CLI is required. The application
361
384
  repository's exact npm dependency and lockfile own the CLI and toolchain version.
362
385
 
363
- Use `npx arcane-os@0.5.16` for the initial bootstrap because it names this npm
386
+ Use `npx arcane-os@0.5.17` for the initial bootstrap because it names this npm
364
387
  package explicitly; bare `npx arcane` outside an installed project could resolve
365
388
  a different package. Both installed commands invoke the same headless toolchain.
366
389
  Project-local npm scripts use the SDK pinned by that app's `package-lock.json`,
@@ -380,7 +403,7 @@ node ./bin/arcane.mjs new local-app --path ../local-app --target portable --git
380
403
 
381
404
  # From the generated app repository
382
405
  cd ../local-app
383
- npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.5.16.tgz
406
+ npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.5.17.tgz
384
407
  npm ci
385
408
  ```
386
409
 
@@ -389,7 +412,7 @@ same location. The lockfile retains the selected package dependency while
389
412
  Arcane uses the installed package name and version. Local directory `file:` dependencies are not
390
413
  accepted because npm may install them as links; use a packed `.tgz`. A GitHub
391
414
  runner also needs that tarball at the locked path. After publication, replace
392
- the local declaration with the exact `arcane-os@0.5.16` registry package and
415
+ the local declaration with the exact `arcane-os@0.5.17` registry package and
393
416
  commit the regenerated lock.
394
417
 
395
418
  Generated repositories use `npm ci --ignore-scripts` in CI. Run dependency
@@ -528,7 +551,7 @@ package installation, or assertions.
528
551
 
529
552
  ## Current target support
530
553
 
531
- Version `0.5.16` exposes one browser target and five explicitly paired
554
+ Version `0.5.17` exposes one browser target and five explicitly paired
532
555
  native development targets: a non-runnable portable directory, a
533
556
  Windows x64 unsigned-local-test EXE bundle, Linux x64 and Linux ARM64
534
557
  unsigned-local-test DEBs, and an Android development-signed APK. The
@@ -5,6 +5,7 @@ import {
5
5
  isBrowserSpeechAuthority,
6
6
  isDbopfsSpeechArtifactStore,
7
7
  } from "./browser-speech-artifacts.mjs";
8
+ import { stripSpeechFormatting } from "../speech-text.mjs";
8
9
 
9
10
  const completeValue = (value) => value;
10
11
  import {
@@ -896,7 +897,7 @@ function cloneNativeTranscriptionPayload(payload, authority) {
896
897
  });
897
898
  }
898
899
 
899
- function normalizeSynthesisPayload(payload, authority) {
900
+ function normalizeSynthesisPayload(payload, authority, speechInputPrepared) {
900
901
  const shared = Object.hasOwn(payload, "input");
901
902
  let descriptors;
902
903
  let textValue;
@@ -932,7 +933,8 @@ function normalizeSynthesisPayload(payload, authority) {
932
933
  assertPayloadModel(payload, authority, { operationSubject: "tts-synthesis" });
933
934
  textValue = descriptors.text.value;
934
935
  }
935
- const text = typeof textValue === "string" ? textValue : "";
936
+ const suppliedText = typeof textValue === "string" ? textValue : "";
937
+ const text = speechInputPrepared === true ? suppliedText : stripSpeechFormatting(suppliedText);
936
938
  const voiceValue = Object.hasOwn(descriptors, "voice")
937
939
  ? descriptors.voice.value
938
940
  : authority.defaultVoice;
@@ -983,6 +985,7 @@ async function normalizeRequestPayload(
983
985
  authority,
984
986
  signal,
985
987
  cancellationReason,
988
+ speechInputPrepared,
986
989
  ) {
987
990
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
988
991
  throw providerError(
@@ -1009,7 +1012,7 @@ async function normalizeRequestPayload(
1009
1012
  }
1010
1013
  return cloneNativeTranscriptionPayload(payload, authority);
1011
1014
  }
1012
- return normalizeSynthesisPayload(payload, authority);
1015
+ return normalizeSynthesisPayload(payload, authority, speechInputPrepared);
1013
1016
  }
1014
1017
 
1015
1018
  function encodeSharedSynthesisResult(result, authority) {
@@ -1619,7 +1622,7 @@ function createBrowserSpeechProvider({
1619
1622
  }, role);
1620
1623
  },
1621
1624
 
1622
- async request(context = {}) {
1625
+ async request(context = {}, { speechInputPrepared = false } = {}) {
1623
1626
  let requestContext;
1624
1627
  try {
1625
1628
  requestContext = providerContext(context, role, "request");
@@ -1713,6 +1716,7 @@ function createBrowserSpeechProvider({
1713
1716
  authority,
1714
1717
  linked.controller.signal,
1715
1718
  browserSpeechRequestAbortReason,
1719
+ speechInputPrepared,
1716
1720
  );
1717
1721
  throwIfAborted(
1718
1722
  linked.controller.signal,
@@ -0,0 +1,31 @@
1
+ function stripSpeechFormatting(text=''){
2
+ return text.replace(/([*#_`~])\1+/g,'');
3
+ }
4
+
5
+ // Speech-only filtering; single markers and all other text remain literal.
6
+ class MarkdownSpeech {
7
+ #pending='';
8
+
9
+ append(text='',end=false){
10
+ if(typeof text!=='string'){
11
+ throw new TypeError('Markdown speech input must be text.');
12
+ }
13
+
14
+ const source=this.#pending+text;
15
+ const trailing=end?null:/([*#_`~])\1*$/u.exec(source);
16
+ // Two copies retain the fact that a run repeats without retaining it all.
17
+ this.#pending=trailing
18
+ ?trailing[1].repeat(trailing[0].length===1?1:2)
19
+ :'';
20
+
21
+ return stripSpeechFormatting(
22
+ trailing?source.slice(0,trailing.index):source
23
+ );
24
+ }
25
+
26
+ reset(){
27
+ this.#pending='';
28
+ }
29
+ }
30
+
31
+ export {MarkdownSpeech,stripSpeechFormatting};
@@ -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.16` runtime requires Arcane `0.8.12` or newer. Compatibility
282
+ The SDK `0.5.17` 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.16
20
+ npm install --save-exact arcane-os@0.5.17
21
21
  ```
22
22
 
23
23
  For your first AI call, follow the [TWiN Cloud quick start](ai/twin-cloud.md).
@@ -58,9 +58,9 @@ This repository contains explicitly versioned surfaces with different owners:
58
58
 
59
59
  | Surface | Source identity | Meaning |
60
60
  | --- | --- | --- |
61
- | SDK and CLI | `arcane-os` `0.5.16` | The Node.js toolchain, portable `arcane-os/event-manager`, `arcane-os/logging`, `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. |
62
- | Browser runtime | SDK `0.5.16`, protocol `arcane/1`, `runtime/` | The SDK-canonical runtime tree. `listRuntimeFiles()`, `readRuntimeFile()`, and `loadRuntimeRelease()` derive its current inventory directly from the selected directory. |
63
- | Browser SDK runtime | SDK `0.5.16`, `browser-runtime/` | The browser closure for events, shared logging, Wllama, and Browser Speech mechanisms. `listSdkBrowserRuntimeFiles()`, `readSdkBrowserRuntimeFile()`, and `loadSdkBrowserRuntimeRelease()` derive its current inventory directly from the selected directory. |
61
+ | SDK and CLI | `arcane-os` `0.5.17` | The Node.js toolchain, portable `arcane-os/event-manager`, `arcane-os/logging`, `arcane-os/mail`, `arcane-os/preference-store`, `arcane-os/speech-playback`, and `arcane-os/speech-text` entrypoints, plus the browser-only `arcane-os/ai/browser-wasm` and `arcane-os/ai/browser-speech` entrypoints in this checkout. |
62
+ | Browser runtime | SDK `0.5.17`, protocol `arcane/1`, `runtime/` | The SDK-canonical runtime tree. `listRuntimeFiles()`, `readRuntimeFile()`, and `loadRuntimeRelease()` derive its current inventory directly from the selected directory. |
63
+ | Browser SDK runtime | SDK `0.5.17`, `browser-runtime/` | The browser closure for events, shared logging, speech-text cleanup, Wllama, and Browser Speech mechanisms. `listSdkBrowserRuntimeFiles()`, `readSdkBrowserRuntimeFile()`, and `loadSdkBrowserRuntimeRelease()` derive its current inventory directly from the selected directory. |
64
64
  | 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. |
65
65
 
66
66
  The SDK runtime source and Core reference have different owners. A browser
@@ -75,7 +75,7 @@ and the distinction between a documentation snapshot and the selected runtime.
75
75
 
76
76
  ## Installed documentation and release identity
77
77
 
78
- This reference accompanies `arcane-os@0.5.16`. The installed package includes
78
+ This reference accompanies `arcane-os@0.5.17`. The installed package includes
79
79
  the maintained `docs/` tree and `examples/wasm-ai-demo/` source alongside
80
80
  README and CHANGELOG. Open `node_modules/arcane-os/docs/reference/README.md`
81
81
  for the matching local reference. The generated website and test suites remain
@@ -124,11 +124,11 @@ Public reference entries follow the established Arcane documentation model:
124
124
 
125
125
  ## Public runtime inventory
126
126
 
127
- The package exposes 198 semantic JavaScript records across 17 JavaScript
127
+ The package exposes 202 semantic JavaScript records across 18 JavaScript
128
128
  entrypoints, plus eight JSON Schemas and package metadata. Ten entrypoints are
129
129
  Node.js control-plane surfaces,
130
130
  `arcane-os/event-manager`, `arcane-os/logging`, `arcane-os/mail`, `arcane-os/preference-store`, and
131
- `arcane-os/speech-playback` run in Node and browsers, and
131
+ `arcane-os/speech-playback` plus `arcane-os/speech-text` run in Node and browsers, and
132
132
  `arcane-os/ai/browser-wasm` plus `arcane-os/ai/browser-speech` are browser-only.
133
133
  The [machine-readable package
134
134
  inventory](inventory/package-api.json) and [SDK member reference](sdk-api.md)
@@ -139,7 +139,7 @@ download, install, or self-update.
139
139
 
140
140
  The synchronized browser payload exposes:
141
141
 
142
- - 82 JavaScript module artifacts under `runtime/arcane/modules/`, including
142
+ - 83 JavaScript module artifacts under `runtime/arcane/modules/`, including
143
143
  ESM modules, classic vendor globals, one worker protocol, and one Node-oriented
144
144
  mail transport;
145
145
  - 14 shared entity modules under `runtime/arcane/entities/`;
@@ -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.16 new hello-speech --path ./hello-speech --target browser
16
+ npx arcane-os@0.5.17 new hello-speech --path ./hello-speech --target browser
17
17
  cd hello-speech
18
18
  npm install
19
19
  npm run dev
@@ -288,7 +288,8 @@ That includes any text left in the same AI instance's partial-stream buffer;
288
288
  finish the previous producer before starting a separate complete passage.
289
289
  Voice, speed, pause, and playback options are not retained with an unfinished
290
290
  `end:false` remainder. A later call supplies its own options, and `finishTTS()`
291
- uses their defaults. The optional text format below lasts through that flush.
291
+ uses their defaults while flushing any pending single formatting mark through
292
+ the same automatic speech-input cleanup.
292
293
  A call extracting no segments returns `true` without waiting for earlier jobs.
293
294
  An already muted call returns `false`.
294
295
 
@@ -343,18 +344,19 @@ if (finalPrepared === false) {
343
344
  ```
344
345
 
345
346
  The segmentation default uses sentence punctuation. To submit smaller complete
346
- segments, call `ai.configureTTSSegmentation({punctuation:'any',wordCadence:4})`
347
+ segments, call `ai.configureTTSSegmentation({punctuation:'any',wordCadence:null})`
347
348
  before feeding the stream. Chunk boundaries themselves do not force a sentence
348
349
  boundary; `finishTTS()` flushes any remaining text. It is not a playback-ended
349
350
  notification. Do not mute or dispose immediately after it if playback should
350
351
  continue.
351
352
 
352
- ## Omit Markdown formatting marks from narration
353
+ ## Automatic speech-input formatting cleanup
353
354
 
354
- Select `textFormat:'markdown'` for raw model text that contains formatting:
355
+ Every TTS entrypoint removes repeated same formatting marks from the outbound
356
+ speech-input copy automatically. No application option is required:
355
357
 
356
358
  ```javascript
357
- ai.streamTTS('## Heading\n**Hello', false, {textFormat:'markdown'});
359
+ ai.streamTTS('## Heading\n**Hello');
358
360
  ai.streamTTS('**. Next sentence.');
359
361
  await ai.finishTTS();
360
362
  ```
@@ -366,12 +368,30 @@ preserved. This is a small narration filter, not a full Markdown parser.
366
368
  Ordinary prose is forwarded immediately; only a trailing formatting candidate
367
369
  waits for its next character or the final flush.
368
370
 
369
- The format stays active until `end:true`, `finishTTS()`, or cancellation. New
370
- streams default to `plain`, which preserves exact submitted text. The shared
371
- chat component selects Markdown mode automatically. Applications already
372
- speaking visible DOM text can keep plain mode. Displayed messages, saved
373
- history, model input, language, voice, synthesis capacity, and playback timing
374
- are not changed by the filter.
371
+ `end:true`, `finishTTS()`, or cancellation clears pending streaming formatting
372
+ state. `fetchTTS()`, provider-runtime TTS requests, direct Kokoro provider
373
+ requests, and `SpeechPlayback` apply the same cleanup to complete input. An
374
+ existing `textFormat` extra is ignored and cannot disable or select cleanup.
375
+ SDK-internal delegation carries `{speechInputPrepared:true}` outside the speech
376
+ payload only after one cleanup pass, preventing a second non-idempotent pass.
377
+ Applications omit that internal metadata. Displayed messages, saved history,
378
+ model input, caller payload objects, language, voice, synthesis capacity, and
379
+ playback timing are not changed by the filter.
380
+
381
+ The dependency-free helper is public for code that needs the speech-only
382
+ transformation directly:
383
+
384
+ ```javascript
385
+ import {
386
+ MarkdownSpeech,
387
+ stripSpeechFormatting
388
+ } from 'arcane-os/speech-text';
389
+
390
+ console.log(stripSpeechFormatting('**Hello**')); // Hello
391
+ const speechText = new MarkdownSpeech();
392
+ console.log(speechText.append('## Head', false)); // ' Head'
393
+ console.log(speechText.append('ing', true)); // ing
394
+ ```
375
395
 
376
396
  ## Choose a device or reduce memory use
377
397
 
@@ -476,7 +496,8 @@ in place; it does not erase the application's data.
476
496
  For a cancellable individual synthesis, unmute first. A fresh browser speech
477
497
  configuration is muted, so calling `providerRuntime.load('tts')` directly at
478
498
  that point rejects with `ARCANE_AI_TTS_MUTED`. `fetchTTS()` accepts an
479
- `AbortSignal` as its second argument and returns a WAV `Blob` without playing it:
499
+ `AbortSignal` as its second argument, cleans repeated formatting marks from the
500
+ outbound input copy, and returns a WAV `Blob` without playing it:
480
501
 
481
502
  ```javascript
482
503
  const synthesisController = new AbortController();
@@ -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.16 new hello-twin --path ./hello-twin --target browser
12
+ npx arcane-os@0.5.17 new hello-twin --path ./hello-twin --target browser
13
13
  cd hello-twin
14
14
  npm install
15
15
  npm run dev
@@ -32,12 +32,13 @@ version; WebKitGTK availability must not be generalized to macOS.
32
32
  | Select and observe independent LLM/STT/TTS roles | `/arcane/modules/AIProviderRuntime.js` and `AIRuntimeState.js` | **Cross-host** controller/state; registered providers retain their own host requirements | Required/projected provider members, route/configuration records, and status fields; per-role lifecycle, cancellation, stream cleanup, sticky state, and startup barriers are normalized. `localOnly` creates no fallback. |
33
33
  | Run a caller-selected local LLM entirely in a browser renderer | `arcane-os/ai/browser-wasm` through `createArcaneAI()` | **Browser** only; secure context, WebAssembly, OPFS/DBOPFS, WebGPU, and requested full offload are required; no CPU fallback | The public AI API module normalizes multi-model lifecycle, status, complete all-choice streaming, cancellation, exact ordered structural tool-call visibility, and session persistence. Model sources are canonical ordered file descriptors; licenses and model choice remain application policy. |
34
34
  | Run caller-selected Whisper or Kokoro in a browser renderer | `arcane-os/ai/browser-speech` registered with `AIProviderRuntime` | **Browser** only; DBOPFS, Web Locks, Workers, Fetch/object URLs, and a caller-supplied self-contained runtime/model closure are required | STT/TTS use independent provider/2 lifecycle and status. Kokoro adds bounded Worker/session concurrency and explicit `auto`, `webgpu`, or `wasm` execution. Complete model/runtime selection, offline behavior, cancellation, Worker teardown, and request/result shapes are normalized. No runtime/model content or cloud fallback is supplied. |
35
+ | Prepare ordered speech playback or reuse the speech-input formatting filter | `arcane-os/speech-playback` and `arcane-os/speech-text` | **Node** with injected media adapters, or **Browser / Native WebView** media; the text filter itself is **Cross-host** | Stored and caller-owned text stays exact. Only the outbound synthesis copy automatically loses repeated same formatting marks. A capacity-advertising provider receives complete segments immediately while retaining indexed playback; native/custom synthesis stays serialized. |
35
36
  | Preserve complete chat history and memory | `/arcane/modules/PersistentAIChatSession.js` | **Browser / native WebView** with ChatEntity/DBOPFS and a configured chat function | Existing DBOPFS names and memory semantics are preserved. Live-context commit is atomic; durable persistence is explicit and coherent across user/assistant turns and atomic all-ID tool-result batches. |
36
37
  | Search an app-owned document corpus for explicit chat context | `/arcane/modules/DBOPFSDocumentLibrary.js` | **Browser** or compatible injected DBOPFS adapter | Generation/manifest completion, complete lexical search, partial read failures, and untrusted context labels are normalized. Construction does not search; an explicitly wired context builder performs retrieval for each prepared chat send. |
37
38
  | Read host identity, capabilities, storage, preferences, appearance, or platform state | `globalThis.Arcane` | **Cross-host** where the method is implemented and admitted | Promise behavior and `Arcane.Error` are normalized. Result fields are normalized unless the method explicitly documents a platform-dependent snapshot. |
38
39
  | Use local AI without coupling app code to Ollama HTTP | `Arcane.localAI`, `Arcane.ai`, or `/arcane/modules/Ollama.js` | Primarily **Native**; Android exposes a narrower admitted inference projection | Admission, errors, and managed-operation events are normalized. Direct Ollama response envelopes remain **Provider-native**. |
39
40
  | Use TWiN Cloud from the renderer profile | `/arcane/modules/AI.js` | **Cloud** from an allowed browser/native renderer | High-level chat behavior is normalized by the module. The TWiN access key authenticates remote LLM chat; raw provider diagnostics remain provider-specific. No automatic cloud fallback is inferred from local failure. |
40
- | Use speech through one application helper | `/arcane/modules/AI.js` and `Arcane.speech` | **Browser** or **Native** | The helper keeps audio on device: Whisper owns STT and Kokoro owns TTS. It normalizes application-facing audio/text behavior while browser and native request/response plumbing differs below that boundary. |
41
+ | Use speech through one application helper | `/arcane/modules/AI.js` and `Arcane.speech` | **Browser** or **Native** | The helper keeps audio on device: Whisper owns STT and Kokoro owns TTS. It automatically cleans only the outbound speech-input copy and normalizes application-facing audio/text behavior while browser and native request/response plumbing differs below that boundary. |
41
42
  | Inspect or manage raw Ollama models | `Arcane.ollama` or `/arcane/modules/Ollama.js` | **Native** desktop Core for management; narrower Android inference only | Wrapper method names, errors, streaming correlation, and admission are Arcane-controlled. Direct Ollama success envelopes are intentionally provider-native. |
42
43
  | Use native terminal, installation, user, provisioning, or machine controls | matching `Arcane.*` namespace | **Native** and app/capability restricted | Calls and errors use the common bridge contract. Platform results can be host-specific and are marked in the method guide. |
43
44
 
@@ -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.16` preserves the physical workspace route count and ordered include
217
+ SDK `0.5.17` 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.16` has two related but separate normalized boundaries:
12
+ The SDK `0.5.17` has two related but separate normalized boundaries:
13
13
 
14
14
  | Boundary | Use | Host |
15
15
  |---|---|---|
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "sdkVersion": "0.5.16",
3
+ "sdkVersion": "0.5.17",
4
4
  "environment": {
5
5
  "runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
6
6
  "minimumVersion": "22.23.2 for Node entrypoints",
7
7
  "moduleSystem": "ESM"
8
8
  },
9
- "memberCount": 198,
9
+ "memberCount": 202,
10
10
  "members": [
11
11
  {
12
12
  "id": "root:APP_BUNDLE_DESCRIPTOR_NAME",
@@ -1395,7 +1395,7 @@
1395
1395
  "summary": "Default binding for the canonical SpeechPlayback runtime class, with capability-gated eager provider submission and exact-order playback.",
1396
1396
  "availability": "Node with injected media adapters, or browser/native WebView media",
1397
1397
  "protocol": "SpeechPlayback runtime contract",
1398
- "normalization": "Binding identity and namespace come directly from the canonical runtime module; a fetchTTS client with positive advertised TTS execution capacity receives all complete segments immediately while its provider owns bounded admission and SpeechPlayback retains indexed order; other clients remain serialized"
1398
+ "normalization": "Binding identity and namespace come directly from the canonical runtime module; stored part input remains exact while only each outbound synthesis copy receives automatic repeated-formatting-mark cleanup and SDK-internal preparation metadata; a fetchTTS client with positive advertised TTS execution capacity receives all complete segments immediately while its provider owns bounded admission and SpeechPlayback retains indexed order; other clients remain serialized; canonical state dispatch precedes the optional synchronous onState callback"
1399
1399
  },
1400
1400
  {
1401
1401
  "id": "speech-playback:SPEECH_PLAYBACK_STATE_EVENT",
@@ -1411,6 +1411,34 @@
1411
1411
  "protocol": "SpeechPlayback runtime contract",
1412
1412
  "normalization": "Exact immutable speech-playback-state event name"
1413
1413
  },
1414
+ {
1415
+ "id": "speech-playback:SPEECH_VOICE_ALIASES",
1416
+ "name": "SPEECH_VOICE_ALIASES",
1417
+ "displayName": "SPEECH_VOICE_ALIASES",
1418
+ "kind": "constant",
1419
+ "signature": "const SPEECH_VOICE_ALIASES",
1420
+ "entrypoints": ["arcane-os/speech-playback"],
1421
+ "primaryImport": "arcane-os/speech-playback",
1422
+ "group": "Portable runtime modules",
1423
+ "summary": "Mutable compatibility membership set for the ten shared speech voice identifiers.",
1424
+ "availability": "Node and browser",
1425
+ "protocol": "SpeechPlayback runtime contract",
1426
+ "normalization": "Derived once from SPEECH_VOICE_OPTIONS in published order; ordinary mutable Set containing alloy, ash, ballad, coral, echo, fable, nova, onyx, sage, and shimmer; SpeechPlayback does not select it automatically"
1427
+ },
1428
+ {
1429
+ "id": "speech-playback:SPEECH_VOICE_OPTIONS",
1430
+ "name": "SPEECH_VOICE_OPTIONS",
1431
+ "displayName": "SPEECH_VOICE_OPTIONS",
1432
+ "kind": "constant",
1433
+ "signature": "const SPEECH_VOICE_OPTIONS",
1434
+ "entrypoints": ["arcane-os/speech-playback"],
1435
+ "primaryImport": "arcane-os/speech-playback",
1436
+ "group": "Portable runtime modules",
1437
+ "summary": "Mutable ordered compatibility value/label records for existing speech controls.",
1438
+ "availability": "Node and browser",
1439
+ "protocol": "SpeechPlayback runtime contract",
1440
+ "normalization": "Ordinary mutable array and mutable records ordered as Alloy, Ash, Ballad, Coral, Echo, Fable, Nova, Onyx, Sage, and Shimmer; provider support and selection remain caller-owned"
1441
+ },
1414
1442
  {
1415
1443
  "id": "speech-playback:SpeechPlayback",
1416
1444
  "name": "SpeechPlayback",
@@ -1423,7 +1451,7 @@
1423
1451
  "summary": "Named binding for the same capability-aware, exact-order canonical class exposed as the subpath default.",
1424
1452
  "availability": "Node with injected media adapters, or browser/native WebView media",
1425
1453
  "protocol": "SpeechPlayback runtime contract",
1426
- "normalization": "Binding identity equals the default export; prepare preserves each nonblank part input exactly without trimming, splitting, or freezing it; provider-advertised capacity enables eager submission while the provider owns its bound, native and custom clients remain serialized, and media availability remains host-owned"
1454
+ "normalization": "Binding identity equals the default export; prepare preserves each nonblank part input exactly without trimming, splitting, or freezing it; requestSpeech changes only the cloned outbound input through automatic repeated-formatting-mark cleanup and carries SDK-internal preparation metadata; provider-advertised capacity enables eager submission while the provider owns its bound, native and custom clients remain serialized, canonical state dispatch precedes optional synchronous onState delivery, and media availability remains host-owned"
1427
1455
  },
1428
1456
  {
1429
1457
  "id": "speech-playback:splitSpeechText",
@@ -1439,6 +1467,34 @@
1439
1467
  "protocol": "SpeechPlayback runtime contract",
1440
1468
  "normalization": "Uses trimming only to detect blank input; nonblank text is returned unchanged in one mutable array without trimming, splitting, or freezing"
1441
1469
  },
1470
+ {
1471
+ "id": "speech-text:MarkdownSpeech",
1472
+ "name": "MarkdownSpeech",
1473
+ "displayName": "MarkdownSpeech",
1474
+ "kind": "class",
1475
+ "signature": "new MarkdownSpeech()",
1476
+ "entrypoints": ["arcane-os/speech-text"],
1477
+ "primaryImport": "arcane-os/speech-text",
1478
+ "group": "Portable runtime modules",
1479
+ "summary": "Streams speech-only repeated-formatting-mark removal across input chunk boundaries.",
1480
+ "availability": "Node and browser",
1481
+ "protocol": "Speech text normalization",
1482
+ "normalization": "append(text='',end=false) emits newly available narration, retains only a trailing candidate formatting mark across chunks, and resets after a terminal append; reset() discards that pending state; caller text remains unchanged"
1483
+ },
1484
+ {
1485
+ "id": "speech-text:stripSpeechFormatting",
1486
+ "name": "stripSpeechFormatting",
1487
+ "displayName": "stripSpeechFormatting()",
1488
+ "kind": "function",
1489
+ "signature": "stripSpeechFormatting(text='')",
1490
+ "entrypoints": ["arcane-os/speech-text"],
1491
+ "primaryImport": "arcane-os/speech-text",
1492
+ "group": "Portable runtime modules",
1493
+ "summary": "Removes repeated runs of speech formatting marks from one complete text value.",
1494
+ "availability": "Node and browser",
1495
+ "protocol": "Speech text normalization",
1496
+ "normalization": "Calls the supplied value's replace method and returns its result with runs of two or more identical *, #, _, backtick, or ~ marks removed; single marks and every other character remain literal; it does not parse Markdown or modify the supplied value; values without callable replace fail with the native TypeError"
1497
+ },
1442
1498
  {
1443
1499
  "id": "root:readRuntimeFile",
1444
1500
  "name": "readRuntimeFile",
@@ -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.16"
8
+ "sdkVersion": "0.5.17"
9
9
  },
10
10
  "componentCount": 39,
11
11
  "loader": "/arcane/modules/HTMLImport.js",
@@ -171,7 +171,7 @@
171
171
  ],
172
172
  "availability": "Browser and supported native WebViews",
173
173
  "transport": "HTMLImport + DOM; injected Arcane/provider modules where listed",
174
- "normalization": "UI/runtime state, honest model progress, complete timestamped transcript including ui_hidden stored records, user-facing structural arguments.message, nonblank all-ID tool-result restoration, collapsed complete raw inspection, ordered parallel-call replay validation, per-choice streamed/terminal complete-envelope correlation, complete nonstructural stream rendering, generic visible failure outcomes with complete console diagnostics, atomic executed/declined/cancelled/not-executed result batches with one continuation, plural pending-call reload recovery, reentrant terminal-event ownership, and BFCache-preserving page lifecycle are normalized; destroy aborts observation and returns true once/false thereafter; AI/storage/media behavior remains mixed"
174
+ "normalization": "UI/runtime state, honest model progress, complete timestamped transcript including ui_hidden stored records, user-facing structural arguments.message, nonblank all-ID tool-result restoration, collapsed complete raw inspection, ordered parallel-call replay validation, per-choice streamed/terminal complete-envelope correlation, complete nonstructural stream rendering, automatic repeated-formatting-mark removal from only the outbound speech-input copy while displayed and stored Markdown stays exact, generic visible failure outcomes with complete console diagnostics, atomic executed/declined/cancelled/not-executed result batches with one continuation, plural pending-call reload recovery, reentrant terminal-event ownership, and BFCache-preserving page lifecycle are normalized; destroy aborts observation and returns true once/false thereafter; AI/storage/media behavior remains mixed"
175
175
  },
176
176
  {
177
177
  "file": "runtime/arcane/components/conversation-view.html",