arcane-os 0.75.0 → 0.76.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.76.0
4
+
5
+ - Add public `textFormat:'plain'` to the `SpeechPlayback` constructor and
6
+ `prepare()` options. Preserve every prepared part's exact input through
7
+ audio-file, native and custom speech clients, including queued narration;
8
+ omitted selection retains the existing formatting cleanup.
9
+ - Distinguish a current native utterance's non-error stopped completion from
10
+ initial preparation with `reason:'native-playback-stopped'` on the existing
11
+ `ready` event. Natural final completion remains `ended`; an error does not
12
+ establish native resource release.
13
+ - Preserve per-part voices, ordered playback, cancellation, replay and silent
14
+ preparation. No model, dependency, native host or activation changes are
15
+ included. Focused source fixtures are authored separately from the selected
16
+ package verification; no live speech result is claimed.
17
+
3
18
  ## 0.75.0
4
19
 
5
20
  - Advertise whole-image `img2img` alongside `txt2img` for the built-in
package/README.md CHANGED
@@ -20,7 +20,7 @@ selected SDK projection or an explicit live SDK source mount. Both profiles
20
20
  share the theme, packaging, event, cancellation,
21
21
  and browser run contracts while retaining their selected application layout.
22
22
 
23
- This checkout defines the `0.75.0` SDK contract. Development applications declare
23
+ This checkout defines the `0.76.0` SDK contract. Development applications declare
24
24
  `arcane-os` as `latest` and retain their lockfile. Exact SDK pins begin when Roshi
25
25
  designates a production build. Follow [Consumer updates](docs/consumer-updates.md)
26
26
  to refresh and compare the installed SDK and lockfile with the published channel;
@@ -476,9 +476,10 @@ ordinary repeated punctuation remain literal. Complete synthesis calls may
476
476
  select `textFormat:'plain'` to preserve exact input: supply it in `prepareTTS`
477
477
  options or the payload of `fetchTTS`, provider-runtime TTS requests, or direct
478
478
  Kokoro requests. The SDK consumes this control before engine/host dispatch;
479
- omission keeps the default cleanup. Streaming narration and `SpeechPlayback`
480
- retain their existing formatting behavior. This option does not change the
481
- selected engine's input capacity or establish complete upstream synthesis. Original
479
+ omission keeps the default cleanup. `SpeechPlayback` accepts the same option in
480
+ its constructor or `prepare()` call and forwards each part's exact input.
481
+ Streaming narration retains its existing formatting behavior. This option does
482
+ not change the selected engine's input capacity or establish complete upstream synthesis. Original
482
483
  messages and caller payloads remain unchanged for display, storage, and model
483
484
  input. The `arcane-os/speech-text` entrypoint exports the same
484
485
  `MarkdownSpeech` streaming class and `stripSpeechFormatting()` one-pass helper
@@ -1008,8 +1008,11 @@ waits for its next character or the final flush.
1008
1008
  state. `fetchTTS()`, provider-runtime TTS requests, direct Kokoro provider
1009
1009
  requests, and `SpeechPlayback` apply the same default cleanup to complete input.
1010
1010
  Complete-input APIs can explicitly select `textFormat:'plain'` as described
1011
- below. The streaming `streamTTS()` options still ignore `textFormat`;
1012
- streaming narration and `SpeechPlayback` keep their existing cleanup.
1011
+ below. `SpeechPlayback` accepts `textFormat:'plain'` in its constructor or
1012
+ `prepare()` options and forwards each prepared part's exact input through every
1013
+ supported speech client. Omitted options reuse its current selection; explicit
1014
+ `null` restores default cleanup. The streaming `streamTTS()` options still ignore
1015
+ `textFormat` and streaming narration keeps its existing cleanup.
1013
1016
  SDK-internal delegation carries `{speechInputPrepared:true}` outside the speech
1014
1017
  payload after its owner selects text treatment, preventing a second
1015
1018
  non-idempotent cleanup pass or cleanup of explicitly plain input.
@@ -1019,8 +1022,9 @@ playback timing are not changed by the filter.
1019
1022
 
1020
1023
  Direct native `provider.prepare()` and `provider.play()` preserve their complete
1021
1024
  `input` as supplied. The native route of `ai.prepareTTSPlayback()` also preserves
1022
- that input; shared streaming and `SpeechPlayback` perform their existing cleanup
1023
- before calling it. Choose the high-level narration path when that speech-only
1025
+ that input; shared streaming and default `SpeechPlayback` perform cleanup before
1026
+ calling it. Plain `SpeechPlayback` preserves the complete input instead.
1027
+ Choose the high-level narration path when that speech-only
1024
1028
  formatting cleanup is desired.
1025
1029
 
1026
1030
  The shared helper is public for code that needs the speech-only
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "sdkVersion": "0.74.0",
3
+ "sdkVersion": "0.75.0",
4
4
  "environment": {
5
5
  "runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
6
6
  "minimumVersion": "22.23.2 for Node entrypoints",
@@ -1262,11 +1262,11 @@
1262
1262
  "default",
1263
1263
  "splitSpeechText"
1264
1264
  ],
1265
- "summary": "Preserves exact nonblank stored segment text; removes repeated formatting marks from only each outbound synthesis copy; eagerly submits complete parts to a capacity-advertising fetchTTS provider while keeping playback indexed; and retains serialized one-segment lookahead for other clients.",
1265
+ "summary": "Preserves exact nonblank stored segment text; removes repeated formatting marks from the outbound synthesis copy by default or forwards exact input with constructor/prepare textFormat:'plain'; eagerly submits complete parts to a capacity-advertising fetchTTS provider while keeping playback indexed; and retains serialized one-segment lookahead for other clients.",
1266
1266
  "availability": "Browser + compatible AI/native bridge",
1267
1267
  "protocol": "AI.prepareTTSPlayback or AI.fetchTTS plus providerRuntime TTS execution capacity, native provider.prepare, or compatible serialized Arcane.speech.synthesize; globalThis.arcaneEvents, Blob URLs/audio element, and native playback controls",
1268
- "normalization": "Exact nonblank split/part input strings are preserved in mutable records. Each synthesis request clones that part, removes repeated formatting marks from only the outbound input, and carries SDK-internal preparation metadata to prevent a second cleanup pass. A capable provider receives complete segments immediately and owns bounded FIFO admission; URLs and playback stay in exact input order. Native/custom clients remain serialized. Canonical state dispatch precedes the optional synchronous onState callback. Cancellation, Replay recovery, Blob results, and lifecycle state are normalized; provider failures remain external.",
1269
- "surface": "prepareTTSPlayback or direct native provider.prepare may return silent native-speech descriptors. autoplay:false stays silent; actual completion advances ordered parts and failed cancellation retains controls until released. Replay preserves completed segments and retries only missing failed generation. SpeechPlayback class/default, mutable `SPEECH_VOICE_OPTIONS` records, mutable `SPEECH_VOICE_ALIASES` membership, `SPEECH_PLAYBACK_STATE_EVENT`, optional constructor `onState(detail)`, `splitSpeechText()`, canonical playback lifecycle events, cancellation, and destroy APIs."
1268
+ "normalization": "Exact nonblank split/part input strings are preserved in mutable records. Each synthesis request copies that input, preserves it exactly for constructor/prepare textFormat:'plain' or applies default formatting cleanup, and carries SDK-internal preparation metadata to prevent a second cleanup pass. A capable provider receives complete segments immediately and owns bounded FIFO admission; URLs and playback stay in exact input order. Native/custom clients remain serialized. Canonical state dispatch precedes the optional synchronous onState callback. Cancellation, Replay recovery, Blob results, and lifecycle state are normalized; provider failures remain external.",
1269
+ "surface": "prepareTTSPlayback or direct native provider.prepare may return silent native-speech descriptors. autoplay:false stays silent; actual completion advances ordered parts and failed cancellation retains controls until released. Current native finished:false without error reports ready with reason native-playback-stopped; natural final completion reports ended. Replay preserves completed segments and retries only missing failed generation. SpeechPlayback class/default, mutable `SPEECH_VOICE_OPTIONS` records, mutable `SPEECH_VOICE_ALIASES` membership, `SPEECH_PLAYBACK_STATE_EVENT`, optional constructor `onState(detail)`, `splitSpeechText()`, canonical playback lifecycle events, cancellation, and destroy APIs."
1270
1270
  },
1271
1271
  {
1272
1272
  "file": "runtime/arcane/modules/StaticDocumentCatalog.js",
@@ -132,7 +132,7 @@ own asynchronous work, cancellation, and backpressure.
132
132
  | [`ScamRiskPolicy.js`](#scamriskpolicyjs) | esm | Combines deterministic scam signals with optional Arcane blocked-domain evidence and safety guidance. | Cross-host | Complete mutable results; blocked-domain policy requires `secure:true`. |
133
133
  | [`ScopedOPFSCache.js`](#scopedopfscachejs) | esm | Provides a narrow exact-key JSON cache inside one app-owned OPFS namespace. | Browser / native WebView | Filename-safe keys, complete JSON values, and malformed-cache cleanup normalized; storage errors mixed. |
134
134
  | [`ScreenCapture.js`](#screencapturejs) | esm | Captures a display surface as image, video, or GIF with explicit lifecycle events. | Browser / native WebView | State/events normalized; permission and codec errors mixed. |
135
- | [`SpeechPlayback.js`](#speechplaybackjs) | esm | Admits complete speech segments to a capacity-advertising provider immediately, retains serialized native/custom lookahead, and plays every result in exact indexed order. | Browser + native bridge | Stored part text stays exact; the outbound speech-input copy receives automatic formatting-mark cleanup; provider/media failures remain mixed. |
135
+ | [`SpeechPlayback.js`](#speechplaybackjs) | esm | Admits complete speech segments to a capacity-advertising provider immediately, retains serialized native/custom lookahead, and plays every result in exact indexed order. | Browser + native bridge | Stored part text stays exact; outbound input receives default formatting cleanup or explicit plain-text preservation; provider/media failures remain mixed. |
136
136
  | [`StaticDocumentCatalog.js`](#staticdocumentcatalogjs) | esm | Loads a positive static document inventory with cache, search, and complete context. | Browser / native WebView / server with fetch | Mutable complete catalog/content normalization; malformed data and transport failures remain visible. |
137
137
  | [`SystemAppearance.js`](#systemappearancejs) | esm | Reads or applies native appearance, returning an explicit unsupported result when the bridge or optional namespace is absent. | Browser/native hybrid | Exact absent optional namespace normalized with its complete error; partial-method and genuine native errors preserved. |
138
138
  | [`SystemPlatformPresentation.js`](#systemplatformpresentationjs) | classic-script | Maps kernel names to presentation labels/classes without granting platform authority. | Browser / native WebView classic script | Fully normalized presentation only. |
@@ -3973,8 +3973,8 @@ console.log(Object.keys(module));
3973
3973
 
3974
3974
  Preserves exact nonblank text, admits complete speech segments according to the
3975
3975
  selected client's advertised capacity, and plays indexed HTML audio in exact
3976
- input order. Stored parts remain exact; only each outbound synthesis payload
3977
- copy receives automatic formatting-mark cleanup.
3976
+ input order. Stored parts remain exact; each outbound synthesis payload copy
3977
+ receives default formatting-mark cleanup or explicit plain-text preservation.
3978
3978
 
3979
3979
  ### Public surface
3980
3980
 
@@ -3992,6 +3992,7 @@ new SpeechPlayback({
3992
3992
  model=null,
3993
3993
  voice=null,
3994
3994
  responseFormat=null,
3995
+ textFormat=null,
3995
3996
  speed=1,
3996
3997
  onState=()=>{},
3997
3998
  createObjectURL,
@@ -4006,7 +4007,7 @@ native descriptor, a native provider's silent `prepare(payload,{signal})`,
4006
4007
  `fetchTTS(payload, signal)`, or `synthesize(payload, {signal})`.
4007
4008
  The playback-oriented method is preferred. `SpeechPlayback` also supplies a third
4008
4009
  SDK-internal preparation object; existing two-argument clients may ignore it.
4009
- `prepare({key,parts,model,voice,responseFormat,
4010
+ `prepare({key,parts,model,voice,responseFormat,textFormat,
4010
4011
  speed,autoplay=true})` uses only caller-supplied model, voice, and response-format
4011
4012
  values; those three omitted values remain omitted so the selected AI/model
4012
4013
  catalog may provide its documented defaults. Speed defaults to `1`, is normalized
@@ -4020,13 +4021,25 @@ There is no hard-coded model, response format, voice, or cloud/browser fallback.
4020
4021
  the caller's exact string in one mutable array without trimming, splitting, or
4021
4022
  freezing it. `prepare()` likewise preserves each nonblank part's exact `input`
4022
4023
  string while normalizing its other playback fields into a new mutable record.
4023
- At synthesis time, `requestSpeech()` copies that record, removes repeated same
4024
- formatting marks from only the outbound `input`, and delegates with the
4024
+ At synthesis time, default `requestSpeech()` behavior copies that record, removes
4025
+ repeated same formatting marks from only the outbound `input`, and delegates with the
4025
4026
  SDK-internal `{speechInputPrepared:true}` argument so downstream SDK boundaries
4026
4027
  do not apply the non-idempotent filter again. Original part objects, stored
4027
4028
  parts, displayed text, and all non-input payload fields remain unchanged. The
4028
4029
  class applies no part-count, character-count, pause, or input upper cap.
4029
4030
 
4031
+ Set `textFormat:'plain'` in the constructor or `prepare()` options to forward
4032
+ every part's exact `input` without formatting cleanup. The selection is captured
4033
+ with the prepared parts, including queued and lookahead requests. A `prepare()`
4034
+ override becomes the instance's selection for later omitted options, like its
4035
+ voice and speed options; explicit `null` or another non-`undefined` value other
4036
+ than `'plain'` selects the existing cleanup. Plain requests carry `textFormat:'plain'` to every
4037
+ supported speech client along with the existing internal preparation marker;
4038
+ default requests keep their existing payload shape. Original part objects stay
4039
+ unchanged, and per-part voices, ordering, cancellation, and silent
4040
+ `autoplay:false` preparation retain their existing behavior. This controls SDK
4041
+ text treatment, not the selected speech engine's pronunciation or input capacity.
4042
+
4030
4043
  ### Admission and playback order
4031
4044
 
4032
4045
  When `speech` exposes both `fetchTTS(payload, signal)` and
@@ -4053,6 +4066,11 @@ Replay use the native control; only a successful current native `finished`
4053
4066
  advances the next part. Native errors remain observable, and a failed native
4054
4067
  control stays owned until its optional `released` promise confirms cleanup.
4055
4068
  Stale completion after stop/restart/disposal cannot advance replacement content.
4069
+ When current native `finished` resolves `false` without an error, the existing
4070
+ `ready` event carries `reason:'native-playback-stopped'` and its operation ID;
4071
+ initial prepared `ready` has no such reason. Only natural completion of the last
4072
+ part emits `ended`. Errors never establish native resource release; failed
4073
+ controls remain retained through their existing `released` boundary.
4056
4074
  Selecting a native part hides and clears the HTML audio source while preserving
4057
4075
  the same shared playback state events and complete original parts.
4058
4076
 
@@ -4087,7 +4105,7 @@ Exact lifecycle reasons are `playback-replaced`, `playback-stopped`,
4087
4105
  `playback-destroyed`, `speech-playback-cancelled`,
4088
4106
  `speech-synthesis-superseded`, `speech-synthesis-cancelled`,
4089
4107
  `speech-synthesizer-unavailable`, `synthesized-audio-contract-mismatch`,
4090
- `audio-playback-rejected`, `audio-autoplay-rejected`,
4108
+ `audio-playback-rejected`, `audio-autoplay-rejected`, `native-playback-stopped`,
4091
4109
  `speech-playback-request-contract-mismatch`, and
4092
4110
  `speech-synthesis-rejected`, as applicable to the emitted state.
4093
4111
 
@@ -8357,7 +8357,8 @@ mediaType}` and copies the supplied view at acceptance. Its explicit finite
8357
8357
  `strength` is from `0` through `1`, takes precedence over model/parameter
8358
8358
  defaults and is never clamped. It decodes in the existing worker and uses the
8359
8359
  same selected context, queue, progress, cancellation and complete result
8360
- contract. SD1.4 advertises `txt2img` and `img2img`. See the
8360
+ contract. SD1.4 and [SDXL Base 1.0](local-image-sdxl.md#edit-a-whole-image)
8361
+ advertise `txt2img` and whole-image `img2img`. See the
8361
8362
  [editing contract](local-image-editing.md#core-and-native-request) for supported
8362
8363
  PNG input, native preprocessing, alpha and strength-zero semantics; the
8363
8364
  original file remains unchanged.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.75.0",
3
+ "version": "0.76.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",
@@ -42,7 +42,7 @@ function splitSpeechText(value=''){
42
42
  return text.trim()?[text]:[];
43
43
  }
44
44
 
45
- function normalizeParts(parts,defaultVoice=null,defaultSpeed=1){
45
+ function normalizeParts(parts,defaultVoice=null,defaultSpeed=1,textFormat=null){
46
46
  if(!is.array(parts)||!parts.length){
47
47
  throw new TypeError('Narration text cannot be blank. The full visual content remains available.');
48
48
  }
@@ -63,6 +63,7 @@ function normalizeParts(parts,defaultVoice=null,defaultSpeed=1){
63
63
  if(!is.finite(speed)||speed<=0)throw new RangeError('Every speech segment requires a positive speech speed.');
64
64
  return {
65
65
  input,
66
+ ...(textFormat==='plain'?{textFormat:'plain'}:{}),
66
67
  pauseAfterMs,
67
68
  role:String(candidate?.role||'narration'),
68
69
  speed,
@@ -425,6 +426,7 @@ class SpeechPlayback{
425
426
  model=null,
426
427
  voice=null,
427
428
  responseFormat=null,
429
+ textFormat=null,
428
430
  speed=1,
429
431
  onState=function noop(){},
430
432
  createObjectURL,
@@ -485,6 +487,7 @@ class SpeechPlayback{
485
487
  this.responseFormat=responseFormat===null||responseFormat===undefined
486
488
  ?null
487
489
  :String(responseFormat);
490
+ this.textFormat=textFormat==='plain'?'plain':null;
488
491
  this.speed=normalizedSpeed;
489
492
  this.key='';
490
493
  this.state='idle';
@@ -622,10 +625,13 @@ class SpeechPlayback{
622
625
  }
623
626
 
624
627
  async requestSpeech(part,signal){
625
- const input=stripSpeechFormatting(part.input);
628
+ const input=part.textFormat==='plain'
629
+ ?part.input
630
+ :stripSpeechFormatting(part.input);
626
631
  const preparation={speechInputPrepared:true};
627
632
  const payload={
628
633
  input,
634
+ ...(part.textFormat==='plain'?{textFormat:'plain'}:{}),
629
635
  speed:part.speed,
630
636
  ...(this.model?{model:this.model}:{}),
631
637
  ...(part.voice?{voice:part.voice}:{}),
@@ -799,6 +805,7 @@ class SpeechPlayback{
799
805
  model=this.model,
800
806
  voice=this.voice,
801
807
  responseFormat=this.responseFormat,
808
+ textFormat=this.textFormat,
802
809
  speed=this.speed,
803
810
  autoplay=true
804
811
  }={}){
@@ -843,10 +850,12 @@ class SpeechPlayback{
843
850
  this.model=selectedModel;
844
851
  this.voice=selectedVoice;
845
852
  this.responseFormat=selectedResponseFormat;
853
+ this.textFormat=textFormat==='plain'?'plain':null;
846
854
  normalized=normalizeParts(
847
855
  is.function(parts)?parts():parts,
848
856
  selectedVoice,
849
- numericSpeed
857
+ numericSpeed,
858
+ this.textFormat
850
859
  );
851
860
  this.parts=normalized;
852
861
  }catch(error){
@@ -1036,7 +1045,12 @@ class SpeechPlayback{
1036
1045
  playback.abortControllers.delete(active.controller);
1037
1046
  if(completed===true)return await playback.advance();
1038
1047
  if(active.control.error)playback.fail(active.control.error);
1039
- else playback.emit('ready',playback.message('stopped'));
1048
+ else playback.emit(
1049
+ 'ready',
1050
+ playback.message('stopped'),
1051
+ playback.key,
1052
+ {reason:'native-playback-stopped'}
1053
+ );
1040
1054
  return false;
1041
1055
  }catch(error){
1042
1056
  console.error('[Arcane speech playback] Native speech failed.',error);