arcane-os 0.70.0 → 0.71.1

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,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.71.1
4
+
5
+ - Correct explicit disabled PWA lifecycle in standalone source development by
6
+ serving a retirement worker when the root worker file is absent. Native
7
+ installation requests activation and unregisters only its own registration,
8
+ preserving saved data, authored workers, open-page content and other modes.
9
+ Browser update timing remains native; no page reload or polling is added.
10
+
11
+ ## 0.71.0
12
+
13
+ - Add public theme-switcher CSS properties for group radius, option radius,
14
+ option font and group background. Applications can select modest rounded
15
+ rectangles and ordinary labels through the shared component while retaining
16
+ its existing literal defaults, complete labels, wrapping, native-button
17
+ targets, selected hover, keyboard focus, disabled state and theme persistence.
18
+
3
19
  ## 0.70.0
4
20
 
5
21
  - Add app-scoped DBOPFS committed-change subscriptions through the existing
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.70.0` SDK contract. Development applications declare
23
+ This checkout defines the `0.71.1` 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;
@@ -50,14 +50,21 @@ alone does not make bare module names resolve in a browser.
50
50
  | Use a shared entity | [Runtime entity modules](runtime-entities.md) and [exact export contracts](core/arcane-entities.md) |
51
51
  | Load a reusable HTML component | [Runtime component catalog](runtime-components.md) |
52
52
  | Present Light / Dark / System choices with complete labels | [Theme-switcher configuration](runtime-components.md#theme-switcherhtml) |
53
+ | Choose theme-switcher corner radii, label font and group background through host CSS | [Theme-switcher styling properties](runtime-components.md#theme-switcherhtml) |
53
54
  | Use optional Core preferences and appearance without masking real failures | [PreferenceStore](runtime-modules.md#preferencestorejs), [SystemAppearance](runtime-modules.md#systemappearancejs), and [Core lookup errors](core-runtime.md#state-and-frames) |
54
55
  | Submit complete speech parts immediately and play them in order | [`SpeechPlayback`](runtime-modules.md#speechplaybackjs) and the [basic browser example](ai/browser-speech.md#play-a-complete-array-with-speechplayback) |
56
+ | Preserve exact text in an explicit complete synthesis call | [Exact complete synthesis input](ai/browser-speech.md#exact-complete-synthesis-input) and [managed AI narration](sdk-api.md#managed-ai-narration) |
57
+ | Select initial native window dimensions and resizing behavior | [Initial native window size](core-native-packaging.md#initial-native-window-size) and [Windows provider](sdk-api.md#createwindowsnativeprovider) |
55
58
  | Call `globalThis.Arcane` | [Arcane Core API](core/arcane-api.md) |
56
59
  | Connect a browser to one Core transport | [Core browser client](core-client.md) |
57
60
  | Cancel one Core speech synthesis request without changing its payload | [Speech request lifetime](core-client.md#events-and-request-lifetime) and [Core facade](sdk-api.md#createcorefacade) |
58
61
  | Compose native Core dispatch, service lifecycle, and stdio hosting | [Native Core runtime](core-runtime.md) |
59
62
  | Reuse dependency-ready native services without a renderer | [Core service composition](core-runtime.md#native-service-composition) and [existing ONNX owner](local-ai.md#native-service-owners) |
60
63
  | Compose application Core services during source development, independently of local AI | [Development Core services](core-development.md) |
64
+ | Share one application service runtime between its native UI and MCP process | [Shared Core host](core-shared-host.md) |
65
+ | Store complete browser model resources through one DBOPFS owner | [Model resources](ai/browser-decisions.md#shared-dbopfs-model-resources) and [speech routing](ai/browser-speech.md#ordinary-module-routing) |
66
+ | Observe committed storage changes across live application documents | [DBOPFS change subscriptions](runtime-modules.md#dbopfsjs) |
67
+ | Cancel cold speech preparation while preserving other activation interests | [Prepared narration](ai/browser-speech.md#prepare-narration-once-and-replay-stored-audio) |
61
68
  | Own an explicitly selected native Codex App Server session | [Codex App Server](codex-app-server.md) |
62
69
  | Expose application-owned tools and static resources through Node STDIO | [MCP STDIO server](mcp-stdio.md) |
63
70
  | Run application-selected llama.cpp, Ollama or ONNX through Core | [Local AI through Core](local-ai.md) |
@@ -1443,7 +1443,7 @@ const store = createDbopfsSpeechArtifactStore({
1443
1443
  });
1444
1444
  ```
1445
1445
 
1446
- The store exposes `{protocol,tableName,prepare,remove}`. It serializes updates
1446
+ The store exposes `{protocol,tableName,prepare,remove,fetchResource}`. It serializes updates
1447
1447
  to one selected authority with Web Locks, downloads each caller-selected file
1448
1448
  after explicit provider activation, stores it in DBOPFS, and reopens the stored
1449
1449
  file before materialization. Missing storage, an unreadable response, a failed
@@ -655,35 +655,46 @@ when the default estimator is unavailable or an application owns a more precise
655
655
  quota view. `downloadConcurrency` must be a positive safe integer and bounds
656
656
  the selected file or Range worker pool; the default is four. The mutable result
657
657
  contains `kind`, `tableName`, `downloadConcurrency`, the original `adapter`,
658
- and `ready`, `install`, `ensure`, and `remove`. `ensure()` preserves the complete
658
+ and `ready`, `install`, `ensure`, `remove`, and `fetchResource`. `ensure()` preserves the complete
659
659
  ordered model set and reports whether it was cached or installed.
660
660
  `install(source,{signal,onProgress})` and
661
661
  `ensure(source,{signal,onProgress,offline})` publish `cache-check` and
662
- `download` records using ordered file counts plus aggregate transfer telemetry
663
- when `onProgress` is supplied. Chunk-driven changes are coalesced on a 250 ms
664
- cadence, with immediate boundary records at start, plan/total discovery,
665
- active-worker changes, and completion. Multi-file sources use up to that many concurrent member workers
662
+ `download` records using ordered file counts and per-member closed-part progress
663
+ when `onProgress` is supplied. Each member reports `completed`, `total`, and
664
+ `unit:'shards'`; streaming totals remain `null` until completion. Records retain
665
+ `activeTransfers`, `transferLimit`, and `transferMode`, with prompt part/lifecycle
666
+ updates and an existing 250 ms heartbeat. They contain no transfer rate or ETA.
667
+ Multi-file sources use up to that many concurrent member workers
666
668
  while retaining descriptor order, preserve completed members and completed
667
669
  Range parts within members across an interrupted attempt, and fetch only
668
- missing work on retry. A complete set of optional member
669
- `bytes` values makes aggregate total and remaining progress available from the
670
- start; otherwise those fields remain `null` until an honest aggregate total is
671
- known. Each missing source member first requests `bytes=0-0`. If a followed
670
+ missing work on retry. Each missing source member first requests the HTTP
671
+ Range `bytes=0-0`. If a followed
672
672
  redirect turns that probe into a full `200`, the source probes the final URL directly;
673
673
  confirmed support cancels the original body and starts parallel Range workers,
674
674
  while refusal keeps the original full response as the single-fetch fallback. A
675
675
  valid `Content-Range`, or optional descriptor `bytes` when that header is not
676
676
  exposed, supplies the total for deterministic resumable Range parts of roughly
677
- 4 MB each, up to 4,096 parts. Without an observable or declared total, the store falls back to one full fetch and
678
- uses its readable `Content-Length` when available. A later non-206,
677
+ 4 MB each, up to 4,096 parts. These values belong only to HTTP Range framing,
678
+ not product progress or content admission. Without an observable or declared
679
+ total, one full fetch streams into ordered persistent parts without requiring
680
+ a response length. Existing complete whole files remain readable without
681
+ migration. A later non-206,
679
682
  contradictory exposed Range response, or incorrectly framed Range body fails
680
683
  rather than silently assembling a partial model. A transfer failure lets peer
681
684
  transfers settle; explicit cancellation stops active transfers. Both preserve
682
685
  completed members and exact Range parts for retry, while unfinished active
683
686
  parts restart after refresh. A complete whole member supersedes its current
684
687
  Range fragments.
685
- Cleanup failure is warned without replacing a usable model. Zero-length abandoned entries are removed
686
- because Wllama cannot consume an empty model Blob or File.
688
+ Cleanup failure is warned without replacing a usable model.
689
+
690
+ `fetchResource(input,{signal,onProgress,...requestOptions})` returns the complete
691
+ stored response as `{file,status,statusText,headers,url,redirected}`. SDK model
692
+ loaders share this owner for actual configuration, tokenizer, graph,
693
+ external-data and support-file requests. Complete successful GET resources may
694
+ be reused. Cancellation joins reader/writer cleanup and preserves closed parts
695
+ for supported HTTP resume; a full response restarts that resource without
696
+ duplicating its saved prefix. Resource progress uses `unit:'shards'` with
697
+ `total:null` until completion. Independent resource requests remain concurrent.
687
698
 
688
699
  ### Availability and normalization
689
700
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "sdkVersion": "0.68.0",
3
+ "sdkVersion": "0.71.0",
4
4
  "environment": {
5
5
  "runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
6
6
  "minimumVersion": "22.23.2 for Node entrypoints",
7
7
  "moduleSystem": "ESM"
8
8
  },
9
- "memberCount": 293,
9
+ "memberCount": 297,
10
10
  "runtimeSubpathPatterns": {
11
11
  "arcane-os/modules/*": "./runtime/arcane/modules/*",
12
12
  "arcane-os/entities/*": "./runtime/arcane/entities/*"
@@ -535,14 +535,14 @@
535
535
  "name": "createBrowserDecisionModel",
536
536
  "displayName": "createBrowserDecisionModel()",
537
537
  "kind": "function",
538
- "signature": "createBrowserDecisionModel({family,model,revision,device,dtype,runtime}={})",
538
+ "signature": "createBrowserDecisionModel({family,model,revision,device,dtype,runtime,store}={})",
539
539
  "entrypoints": ["arcane-os/ai/browser-decisions"],
540
540
  "primaryImport": "arcane-os/ai/browser-decisions",
541
541
  "group": "Browser typed decisions",
542
542
  "summary": "Explicit-load Laya and Julia-1 decisions with caller-selected precision, complete inputs and graph results.",
543
543
  "availability": "Browser module Workers and the selected Transformers.js backend; import/construction starts no model load",
544
544
  "protocol": "Caller-owned state/question/options rows; five-input decision graph; native Worker request/result lifecycle",
545
- "normalization": "Full field tokenization, raw logits and unrounded softmax; dtype defaults to fp16 for Laya and fp32 for Julia, with explicit values forwarded unchanged to the upstream loader; no policy thresholds, storage or precision fallback"
545
+ "normalization": "Full field tokenization, raw logits and unrounded softmax; dtype defaults to fp16 for Laya and fp32 for Julia, with explicit values forwarded unchanged. Optional existing SDK store routes actual loader resources through fetchResource outside cloned Worker configuration; stored mode disables competing model caches, preserves complete HTTP responses and joins resource cleanup before outstanding calls settle. Omission retains upstream loading; no decision-result persistence or precision fallback"
546
546
  },
547
547
  {
548
548
  "id": "root:acceptWebSocket",
@@ -3529,10 +3529,10 @@
3529
3529
  ],
3530
3530
  "primaryImport": "arcane-os/ai/browser-wasm",
3531
3531
  "group": "Browser-WASM local AI",
3532
- "summary": "Adapts an existing DBOPFS instance into a resumable model cache with bounded parallel shard or monolithic Range-part transfer and live aggregate byte progress.",
3532
+ "summary": "Adapts an existing DBOPFS instance into complete resumable model and Fetch-resource storage with bounded transfers and semantic shard progress.",
3533
3533
  "availability": "Browser with a ready DBOPFS instance and OPFS support",
3534
3534
  "protocol": "Resumable ordered model members and deterministic per-member Range parts over existing DBOPFS method semantics",
3535
- "normalization": "Uses one bounded transfer axis, preserves ordered completed shards and deterministic current Range parts within any member across interruption, emits coalesced additive loaded/total/remaining/rate/ETA/active-worker telemetry, rolls discarded partial writes back out of progress, cleans superseded current Range parts after a complete replacement exists, and settles failed or cancelled peers while retaining completed work for retry"
3535
+ "normalization": "New full and Range responses stream into ordered persistent parts; complete logical files preserve existing formats without migration. Progress uses file and closed-shard counts with unknown totals until completion. fetchResource(input,{signal,onProgress,...requestOptions}) returns complete {file,status,statusText,headers,url,redirected}; successful complete GET resources may be reused. Cancellation joins reader/writer cleanup and retains closed parts for supported resume. Independent resources remain concurrent"
3536
3536
  },
3537
3537
  {
3538
3538
  "id": "browser-device:getBrowserDeviceClass",
@@ -3772,7 +3772,7 @@
3772
3772
  "summary": "Adapts an existing DBOPFS instance into a caller-scoped speech runtime/model artifact store and ordinary module router.",
3773
3773
  "availability": "Browser with a ready DBOPFS instance, Web Locks, Fetch or an explicit fetch implementation, File/Blob, and object URLs",
3774
3774
  "protocol": "arcane-ai-browser-speech-artifacts/1 ordinary DBOPFS storage and module routing",
3775
- "normalization": "Serializes complete caller-selected content, invalidates stale cache selections without byte identities, routes known materialized imports/fetches/Workers/cache reads, falls back to native browser behavior for unmapped operations, and keeps secure intent dormant"
3775
+ "normalization": "Exposes prepare, remove and fetchResource; streams new declared files and fetched resources into ordered DBOPFS parts, preserves complete old files and closed parts, reports file/shard progress and joins cancellation cleanup. Known graph mappings remain explicit; dedicated speech and nested module Worker fetches share the store with complete HTTP response metadata. Competing native caches are unavailable only in that Worker lifetime and restored afterward without saved-cache deletion. Native ESM imports and unmapped classic Worker internals remain platform-owned"
3776
3776
  },
3777
3777
  {
3778
3778
  "id": "mail:MAIL_OUTBOX_IDEMPOTENCY_WINDOW_MS",
@@ -4374,7 +4374,7 @@
4374
4374
  "summary": "Creates the SDK-owned provider that assembles one selected app and explicit services into a portable Core payload.",
4375
4375
  "availability": "Node; produces a portable payload rather than an executable host",
4376
4376
  "protocol": "arcane-native-builder/1 portable provider",
4377
- "normalization": "Preserves complete selected app and dependency releases; an explicit services selection overrides appDescriptor.native.services, otherwise the descriptor selection is used; reports executable:false and never substitutes for an unavailable platform host"
4377
+ "normalization": "Preserves complete selected app and dependency releases; an explicit services selection overrides appDescriptor.native.services, otherwise the descriptor selection is used; optional appDescriptor.native.window carries positive integral logical client width/height and optional resizable into manifest.window with omitted fields preserved; reports executable:false and never substitutes for an unavailable platform host"
4378
4378
  },
4379
4379
  {
4380
4380
  "id": "native-portable-provider:arcaneNativeBuilderProvider",
@@ -4416,7 +4416,7 @@
4416
4416
  "summary": "Assembles selected application services and browser content with the SDK-owned Windows x64 WebView2 and SEA host.",
4417
4417
  "availability": "Node for assembly; Windows x64 with WebView2 Runtime for execution",
4418
4418
  "protocol": "arcane-native-builder/1 windows-x64 provider",
4419
- "normalization": "Uses a selected hostDirectory or the exact installed SDK version's numeric release asset; preserves complete payloads; app displayName plus .exe (app ID fallback) and matching .exe.config use Windows filename substitutions only; manifest.host.executable owns inventory, icon, verify and run while generic cache/Core names remain unchanged; native.icon PNG/ICO derives the copied executable and host.icon without an app compiler, while other formats retain assembly with native.icon.unsupported and executableIcon:false; the matching host loads the selected window/taskbar icon and handles window.setTheme with per-field applied/unsupported results; run cancellation closes stdin and waits for host-owned graceful shutdown"
4419
+ "normalization": "Uses a selected hostDirectory or the exact installed SDK version's numeric release asset; preserves complete payloads; app displayName plus .exe (app ID fallback) and matching .exe.config use Windows filename substitutions only; manifest.host.executable owns inventory, icon, verify and run while generic cache/Core names remain unchanged; native.icon PNG/ICO derives the copied executable and host.icon without an app compiler, while other formats retain assembly with native.icon.unsupported and executableIcon:false; the matching host loads the selected window/taskbar icon and handles window.setTheme with per-field applied/unsupported results; native.window selects initial 96-DPI logical client width/height and optional resizable through manifest.window, the matching host scales to actual DPI and fits initial placement to the usable screen while omitted fields and existing restored non-normal state remain unchanged; no saved-bounds subsystem is added and initial-fit logic is not reapplied during later resizing; run cancellation closes stdin and waits for host-owned graceful shutdown"
4420
4420
  },
4421
4421
  {
4422
4422
  "id": "native-windows-provider:arcaneNativeBuilderProvider",
@@ -4557,6 +4557,62 @@
4557
4557
  "availability": "Node with readable input and writable output streams",
4558
4558
  "protocol": "arcane/1 over Content-Length framed stdio",
4559
4559
  "normalization": "Returns the existing {runtime,closed,close} transport owner; an optional abort signal invokes the same idempotent drain without killing the process, replacing service readiness or cancelling accepted service-lifetime work"
4560
+ },
4561
+ {
4562
+ "id": "core-host:startSharedCoreHost",
4563
+ "name": "startSharedCoreHost",
4564
+ "displayName": "startSharedCoreHost()",
4565
+ "kind": "function",
4566
+ "signature": "startSharedCoreHost({endpoint,application,version,configure,getReplayEvents,signal,onError}={})",
4567
+ "entrypoints": ["arcane-os/core/host"],
4568
+ "primaryImport": "arcane-os/core/host",
4569
+ "group": "Shared Core host",
4570
+ "summary": "Claims an app-selected local endpoint before composing one shared Core runtime.",
4571
+ "availability": "Node named pipes on Windows and Unix-domain sockets on Linux/macOS; Android requires its host adaptation",
4572
+ "protocol": "arcane/1 over local IPC with per-connection request correlation and current-state replay",
4573
+ "normalization": "Returns {runtime,endpoint,closed,close}; configure(runtime,{signal}) registers independently started services after the endpoint claim. Close joins composition, accepted service work, disposal and queued frames before releasing the endpoint. Complete transport/startup/shutdown errors remain observable; an occupied endpoint never invokes the factory"
4574
+ },
4575
+ {
4576
+ "id": "core-host:connectSharedCoreHost",
4577
+ "name": "connectSharedCoreHost",
4578
+ "displayName": "connectSharedCoreHost()",
4579
+ "kind": "function",
4580
+ "signature": "connectSharedCoreHost({endpoint,start,signal,onError}={})",
4581
+ "entrypoints": ["arcane-os/core/host"],
4582
+ "primaryImport": "arcane-os/core/host",
4583
+ "group": "Shared Core host",
4584
+ "summary": "Connects to one selected host and optionally starts an explicit headless entry when absent.",
4585
+ "availability": "Node local IPC and optional explicit child process with diagnostic file",
4586
+ "protocol": "arcane/1 Core client; optional Node startup IPC handshake",
4587
+ "normalization": "Returns {client,endpoint,closed,close,shutdown}. Only ENOENT/ECONNREFUSED selects start {command,args,cwd,env,logFile}; no request retry. Close cancels this connection's request work while preserving the host and accepted service work. Explicit shutdown drains all clients' shared host; cancelling a launch wait does not kill the host"
4588
+ },
4589
+ {
4590
+ "id": "core-host:runSharedCoreHost",
4591
+ "name": "runSharedCoreHost",
4592
+ "displayName": "runSharedCoreHost()",
4593
+ "kind": "function",
4594
+ "signature": "runSharedCoreHost(options)",
4595
+ "entrypoints": ["arcane-os/core/host"],
4596
+ "primaryImport": "arcane-os/core/host",
4597
+ "group": "Shared Core host",
4598
+ "summary": "Runs the explicit headless entry with one endpoint winner and startup result reporting.",
4599
+ "availability": "Node host entry; parent IPC reporting when present",
4600
+ "protocol": "Shared local Core endpoint and Node startup handshake",
4601
+ "normalization": "Accepts startSharedCoreHost options; returns the winning host handle or null after a losing launcher connects to the existing owner and confirms dispatcher response. The loser does not construct services. Reports complete startup errors and never calls process.exit"
4602
+ },
4603
+ {
4604
+ "id": "core-host:startSharedCoreBridge",
4605
+ "name": "startSharedCoreBridge",
4606
+ "displayName": "startSharedCoreBridge()",
4607
+ "kind": "function",
4608
+ "signature": "startSharedCoreBridge({endpoint,start,input=process.stdin,output=process.stdout,signal,onError}={})",
4609
+ "entrypoints": ["arcane-os/core/host"],
4610
+ "primaryImport": "arcane-os/core/host",
4611
+ "group": "Shared Core host",
4612
+ "summary": "Connects a complete framed stdio stream to the shared application's Core host.",
4613
+ "availability": "Node readable/writable streams and local IPC",
4614
+ "protocol": "arcane/1 Content-Length stdio to the selected shared Core endpoint",
4615
+ "normalization": "Returns {endpoint,closed,close}; EOF or cancellation disconnects this client without shutting down shared services. Terminal stream errors settle pending writes and reject closed. Native launch opts in through sharedHost:{endpoint,logFile}; omitted sharedHost preserves ordinary stdio ownership"
4560
4616
  }
4561
4617
  ]
4562
4618
  }
@@ -967,7 +967,7 @@
967
967
  {
968
968
  "file": "runtime/arcane/components/theme-switcher.html",
969
969
  "name": "theme-switcher.html",
970
- "purpose": "Selects and refreshes system, light, dark, or custom theme mode with per-instance choices and labels.",
970
+ "purpose": "Selects and refreshes system, light, dark, or custom theme mode with per-instance choices, labels, and CSS-configurable group/option radii, option font and group background.",
971
971
  "methods": [
972
972
  "configure()",
973
973
  "setMode()",
@@ -980,7 +980,7 @@
980
980
  ],
981
981
  "availability": "Browser and supported native WebViews",
982
982
  "transport": "HTMLImport + DOM; injected Arcane/provider modules where listed",
983
- "normalization": "Preference/native appearance behavior mixed; no component-ready contract"
983
+ "normalization": "Preference/native appearance behavior mixed; selected button background, text and outline remain visible during hover while the hover fill applies only to unselected enabled buttons; no component-ready contract"
984
984
  },
985
985
  {
986
986
  "file": "runtime/arcane/components/unified-inbox.html",
@@ -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 Whisper STT and Kokoro TTS execution with default capacities 1 and 4 respectively, explicit provider-neutral execution snapshots through status(role,{execution:true}), sticky readiness, canonical window.user readiness without compatibility-event object-identity admission or polling, exact ordered structural calls with required arguments.message, atomic nonblank all-ID tool-result sequencing, complete all-choice streaming/data validation, native Ollama structural adaptation, complete-response then all validated tool callbacks then completion ordering, observed async callbacks, Blob/File transcription, automatic repeated-formatting-mark removal from cloned outbound TTS input while caller and visible content stays exact, immediate exact-segment synthesis, per-call voice/speed capture, optional final-segment pause on the existing audio clock, optional per-call terminal playback completion, detached preparation with complete semantic DBOPFS audio reuse and same-owner pending request sharing, independent prepared playback without a model load for cached audio, ordered audio-clock scheduling, playable audio, active-generation TTS operation-failure routing, mute, and cancellation are normalized; provider/model/runtime/voice policy remains caller-owned. Both speech roles default execution.device to auto: webnn-npu when navigator.ml.createContext is exposed, then webgpu when navigator.gpu is exposed, then wasm for CPU execution. Failed backend loads clean up the pool before trying fresh Workers with the same prepared model and dtype; explicit devices never fall back. STT capacity only accepts 1; TTS accepts 1 through 4. Execution snapshots expose requestedDevice, selectedDevice, maxConcurrentRequests, and activeRequestCount for both roles; selectedDevice is null while unloaded and names the successful upstream session backend request when loaded. It does not prove every operation used the physical NPU: WebNN unsupported operations may use WASM, and compatibility depends on the browser, driver, hardware, and graph.",
33
- "surface": "Native fetchTTS(payload,signal) and fetchSTT(audio,signal) forward caller cancellation separately from their complete Core speech payloads, normalize aborts to AbortError and suppress late results; actual engine interruption remains service-owned. prepareTTSPlayback(payload,signal) silently returns a real Blob or native-speech descriptor; native controls expose finished/released/state/error/pause/resume/stop, and actual release owns the playback lane. fetchTTS and durable prepareTTS report ARCANE_AI_TTS_AUDIO_EXPORT_UNAVAILABLE for native voices. Request-local model overrides remain exact and never mutate the configured model. Browser-speech protocol/event/error/reason constants; default `AI`; read-only `providerRuntime`, `browserSpeechConfiguration`, and `browserSpeechDescriptor`; explicit `providerRuntime.status(role,{execution:true})` snapshots; `streamTTS(text,end,options={})` with optional voice, speed, pauseAfterMs, and waitForPlayback; automatic speech-input cleanup with SDK-internal preparation metadata; `prepareTTS({parts,storage,identity,signal,onState})` returning ordered segments, state, ready, getAudio(index), and cancel(); `playPreparedTTS(prepared,{signal,onState})` returning state, error, finished, pause(), resume(), and stop() with one playback lane per AI and observational state/error callbacks; configure/dispose speech, route lifecycle, declaration-validated chat/stream requests with exact ordered calls; optional streamRequest toolText:{name,field} and onToolText(text,call,displayId) for decoded selected argument text with actual call identity, distinct from ordinary prose and terminal tool execution; synthesis/transcription and playback controls; initializes from current canonical user readiness, installs `window.ai`, and projects `ai-ready` plus active-generation `ai-tts-failure`."
32
+ "normalization": "Complete mutable caller-owned browser speech authority, SDK-owned provider registration/replacement/disposal, explicit STT/TTS activation, normalized Whisper STT and Kokoro TTS execution with default capacities 1 and 4 respectively, explicit provider-neutral execution snapshots through status(role,{execution:true}), sticky readiness, canonical window.user readiness without compatibility-event object-identity admission or polling, exact ordered structural calls with required arguments.message, atomic nonblank all-ID tool-result sequencing, complete all-choice streaming/data validation, native Ollama structural adaptation, complete-response then all validated tool callbacks then completion ordering, observed async callbacks, Blob/File transcription, default repeated-formatting-mark removal from cloned outbound TTS input, explicit textFormat:'plain' preservation for complete synthesis/preparation while streaming cleanup remains automatic and caller/visible content stays exact, immediate exact-segment synthesis, per-call voice/speed capture, optional final-segment pause on the existing audio clock, optional per-call terminal playback completion, detached preparation with complete semantic DBOPFS audio reuse and same-owner pending request sharing; each preparation owns its pending activation interest, final-interest cancellation aborts that activation while other interests preserve it, complete cache hits start no model, and cancellation after readiness leaves the ready model loaded; independent prepared playback without a model load for cached audio, ordered audio-clock scheduling, playable audio, active-generation TTS operation-failure routing, mute, and cancellation are normalized; provider/model/runtime/voice policy remains caller-owned. Both speech roles default execution.device to auto: webnn-npu when navigator.ml.createContext is exposed, then webgpu when navigator.gpu is exposed, then wasm for CPU execution. Failed backend loads clean up the pool before trying fresh Workers with the same prepared model and dtype; explicit devices never fall back. STT capacity only accepts 1; TTS accepts 1 through 4. Execution snapshots expose requestedDevice, selectedDevice, maxConcurrentRequests, and activeRequestCount for both roles; selectedDevice is null while unloaded and names the successful upstream session backend request when loaded. It does not prove every operation used the physical NPU: WebNN unsupported operations may use WASM, and compatibility depends on the browser, driver, hardware, and graph.",
33
+ "surface": "Native fetchTTS(payload,signal) and fetchSTT(audio,signal) forward caller cancellation separately from their complete Core speech payloads, normalize aborts to AbortError and suppress late results; actual engine interruption remains service-owned. prepareTTSPlayback(payload,signal) silently returns a real Blob or native-speech descriptor; native controls expose finished/released/state/error/pause/resume/stop, and actual release owns the playback lane. fetchTTS and durable prepareTTS report ARCANE_AI_TTS_AUDIO_EXPORT_UNAVAILABLE for native voices. Request-local model overrides remain exact and never mutate the configured model. Browser-speech protocol/event/error/reason constants; default `AI`; read-only `providerRuntime`, `browserSpeechConfiguration`, and `browserSpeechDescriptor`; explicit `providerRuntime.status(role,{execution:true})` snapshots; `streamTTS(text,end,options={})` with optional voice, speed, pauseAfterMs, and waitForPlayback; default speech-input cleanup with SDK-internal preparation metadata and explicit complete-call textFormat:'plain' consumed before engine dispatch; `prepareTTS({parts,storage,identity,textFormat,signal,onState})` returning ordered segments, state, ready, getAudio(index), and cancel(); `playPreparedTTS(prepared,{signal,onState})` returning state, error, finished, pause(), resume(), and stop() with one playback lane per AI and observational state/error callbacks; configure/dispose speech, route lifecycle, declaration-validated chat/stream requests with exact ordered calls; optional streamRequest toolText:{name,field} and onToolText(text,call,displayId) for decoded selected argument text with actual call identity, distinct from ordinary prose and terminal tool execution; synthesis/transcription and playback controls; initializes from current canonical user readiness, installs `window.ai`, and projects `ai-ready` plus active-generation `ai-tts-failure`."
34
34
  },
35
35
  {
36
36
  "file": "runtime/arcane/modules/AIModelSelectionController.js",
@@ -90,8 +90,8 @@
90
90
  "summary": "Provider-neutral selection, lifecycle, routing, startup, request, streaming, cancellation, and independent LLM/STT/TTS state.",
91
91
  "availability": "Cross-host in-process runtime; registered providers remain browser, native, or cloud specific",
92
92
  "protocol": "arcane-ai-runtime/2, arcane-ai-provider/2, arcane-ai-model-authority/1",
93
- "normalization": "Normalizes mutable complete per-role routes, lifecycle/status, cancellation, streaming cleanup, capacity-1 FIFO LLM/STT lanes, bounded provider-declared parallel TTS requests with FIFO overflow, automatic repeated-formatting-mark removal from cloned direct TTS payloads, local-only selection, direct LLM history/declaration/terminal structural contracts, complete all-choice content/reasoning FIFO iteration, private automatic provider draining with result-first buffering, terminal-only structural calls, exact per-choice observed/terminal correlation, and a separate complete validated terminal result without creating a fallback.",
94
- "surface": "Protocol constants; singleton-only `AIProviderRuntime`; `aiProviderRuntime`; `getAIProviderRuntime()`; provider registration/identity/selection, closed three-role and STT/TTS-only validation/configuration/replacement, status/catalog/inspection, startup, per-role load/unload/dispose/cancel, dispose-all, request/stream/speech aliases, SDK-internal speech-input preparation metadata, and mute controls."
93
+ "normalization": "Normalizes mutable complete per-role routes, lifecycle/status, cancellation, streaming cleanup, capacity-1 FIFO LLM/STT lanes, bounded provider-declared parallel TTS requests with FIFO overflow, default repeated-formatting-mark removal from cloned direct TTS payloads or explicit textFormat:'plain' preservation with the control consumed before engine dispatch, local-only selection, direct LLM history/declaration/terminal structural contracts, complete all-choice content/reasoning FIFO iteration, private automatic provider draining with result-first buffering, terminal-only structural calls, exact per-choice observed/terminal correlation, and a separate complete validated terminal result without creating a fallback.",
94
+ "surface": "Protocol constants; singleton-only `AIProviderRuntime`; `aiProviderRuntime`; `getAIProviderRuntime()`; provider registration/identity/selection, closed three-role and STT/TTS-only validation/configuration/replacement, status/catalog/inspection, startup, per-role load/unload/dispose/cancel, dispose-all, request/stream/speech aliases, SDK-internal speech-input preparation metadata, and setSpeechMuted(muted,{signal}={}) controls. Each pending unmute caller owns its abortable interest; the final interest aborts the owned activation while other preparation/playback or joining direct TTS-load interests preserve it. A pre-aborted unmute starts no activation. Explicit mute retains global cancellation and joined unload even with an aborted observer signal."
95
95
  },
96
96
  {
97
97
  "file": "runtime/arcane/modules/AIResponseURLPolicy.js",
@@ -169,6 +169,44 @@ Reference updates finish before file removal begins. Cancellation or a file
169
169
  error is reported without undoing earlier completed changes; a later call can
170
170
  finish retirement. The operation does not claim an all-or-nothing transaction.
171
171
 
172
+ ### Disabled worker retirement on the source server
173
+
174
+ `startDevServer()` and `developApplication()` serve a retirement worker at
175
+ `/arcane-sw.js` when source mode selects a standalone `appsRoot:"."` application
176
+ with explicit `pwa.enabled:false` and that root worker file is absent. This
177
+ browser lifecycle is separate from the import-map filesystem operation above.
178
+ The existing server returns JavaScript with `Cache-Control: no-cache`, including
179
+ when the request carries a former enabled worker's `If-Modified-Since` date.
180
+ It writes no replacement worker file and performs no offline-inventory scan.
181
+
182
+ When the browser updates the existing registration from that URL, the
183
+ retirement worker calls `skipWaiting()` during installation and unregisters its
184
+ own registration during activation. It has no fetch handler, so subsequent
185
+ requests reaching it use the ordinary network path. It never clears caches,
186
+ DBOPFS, profiles or other saved data; it does not enumerate or claim clients,
187
+ reload pages, or start a polling loop. Other service-worker registrations remain
188
+ untouched.
189
+
190
+ Native service-worker update and activation timing remains browser-owned. An
191
+ already open page may retain the neutral retirement worker as its controller
192
+ for the rest of its lifetime, and its already rendered content stays unchanged.
193
+ Unregistration removes the registration from future navigation matching;
194
+ retirement does not promise immediate `controller === null` or automatic page
195
+ refresh. These boundaries follow the platform's
196
+ [activation](https://w3c.github.io/ServiceWorker/#activate-algorithm) and
197
+ [unregistration](https://w3c.github.io/ServiceWorker/#dom-serviceworkerregistration-unregister)
198
+ contracts.
199
+
200
+ An existing root `arcane-sw.js` retains its ordinary authored-file routing,
201
+ including its normal application selection; only an `ENOENT` filesystem result
202
+ selects the retirement response. Omitted PWA configuration, enabled PWA,
203
+ multi-app source workspaces, other URLs and packaged serving keep their existing
204
+ behavior. A separately operated static host must own its worker lifecycle;
205
+ filesystem retirement alone does not establish browser retirement on that host.
206
+ The server path uses the existing portable Node serving owner, and browser
207
+ retirement requires the browser's native service-worker support and an existing
208
+ registration eligible for that update.
209
+
172
210
  ### Authored host root and app entry
173
211
 
174
212
  An authored `index.html` at the source host root keeps `/` separate from the
@@ -112,7 +112,7 @@ snapshot remains owned until the browser's `afterprint` event.
112
112
  | [`task-progress.html`](#task-progresshtml) | Runs and displays a task list with started/change/complete/error state. | `configure()`<br>`setTasks()`<br>`updateTask()`<br>`runTasks()`<br>`clear()`<br>`destroy()` | `task-progress-ready`<br>`task-progress-started`<br>`task-progress-change`<br>`task-progress-complete`<br>`task-progress-error` | Task state normalized; injected task results mixed |
113
113
  | [`terminal-workspace.html`](#terminal-workspacehtml) | Presents multiple terminal sessions, output, active selection, theme, and terminal actions. | `configure()`<br>`addSession()`<br>`removeSession()`<br>`activateSession()`<br>`append()`<br>`clear()`<br>`setState()`<br>`setTheme()`<br>`focus()`<br>`destroy()` | `terminal-workspace-ready`<br>`terminal-submit`<br>`terminal-interrupt`<br>`terminal-clear`<br>`terminal-session-new`<br>`terminal-session-close`<br>`terminal-session-select`<br>`terminal-settings` | UI/session state normalized; native command results supplied externally |
114
114
  | [`theme-editor.html`](#theme-editorhtml) | Edits, previews, saves, and resets semantic custom theme tokens. | `configure()`<br>`getTheme()`<br>`setTheme()`<br>`setBusy()`<br>`setStatus()`<br>`destroy()` | `theme-editor-ready`<br>`theme-preview`<br>`theme-save`<br>`theme-reset` | Fully normalized Theme values |
115
- | [`theme-switcher.html`](#theme-switcherhtml) | Selects and refreshes system, light, dark, or custom theme mode with per-instance choices and labels. | `configure()`<br>`setMode()`<br>`refresh()` | No component-specific event | Preference/native appearance behavior mixed; no component-ready contract |
115
+ | [`theme-switcher.html`](#theme-switcherhtml) | Selects and refreshes system, light, dark, or custom theme mode with per-instance choices, labels, and CSS-configurable group/option radii, option font and group background. | `configure()`<br>`setMode()`<br>`refresh()` | No component-specific event | Preference/native appearance behavior mixed; no component-ready contract |
116
116
  | [`unified-inbox.html`](#unified-inboxhtml) | Displays provider-neutral communication threads with active/loading state. | `configure()`<br>`setThreads()`<br>`setActive()`<br>`setLoading()`<br>`destroy()` | `unified-inbox-ready`<br>`inbox-refresh`<br>`thread-select` | DOM-normalized |
117
117
  | [`voice-transcription.html`](#voice-transcriptionhtml) | Records manual clips or optional continuous microphone audio with pause/periodic segments, ordered transcription and saves, retained failures, and complete transcript delivery. | `configure()`<br>`requestSTTActivation()`<br>`startRecording()`<br>`stopRecording()`<br>`retryTranscription()`<br>`cancelRecording()`<br>`save()`<br>`completeTranscription()/complete()`<br>`clear()`<br>`reset()`<br>`destroy()` | `voice-transcription-ready`<br>`voice-transcription-state`<br>`voice-transcription-segment`<br>`voice-transcription-empty`<br>`voice-transcription-error`<br>`voice-transcription-change`<br>`voice-transcription-complete`<br>`speech-transcription-complete`<br>`speech-transcription-cancelled`<br>`speech-stt-activation-request`<br>`speech-stt-activation-error` | Sticky STT readiness, capture/queue lifecycle, retry, cancellation, and complete state/text are normalized; media/provider behavior remains external |
118
118
  | [`weather-widget.html`](#weather-widgethtml) | Displays normalized current and daily weather with refresh intent. | `setWeather()`<br>`clear()`<br>`destroy()` | `weather-widget-ready`<br>`weather-refresh` | Display normalized; provider supplied externally |
@@ -2073,6 +2073,24 @@ The host inherits text color. Existing `--app-bar-border` (falling back to
2073
2073
  remain supported. Applications own their palette and surrounding placement;
2074
2074
  the component adds no application-specific palette or settings section.
2075
2075
 
2076
+ Set these public CSS custom properties on the `html-import` host or an ancestor
2077
+ to style one instance through the ordinary cascade. Omitting them preserves the
2078
+ existing presentation:
2079
+
2080
+ | Property | Controls | Default |
2081
+ | --- | --- | --- |
2082
+ | `--theme-switcher-radius` | Outer group border radius | `1.6rem` |
2083
+ | `--theme-switcher-option-radius` | Each option's border radius | `1.4rem` |
2084
+ | `--theme-switcher-font` | Complete option `font` shorthand | `700 .875rem/1.25 system-ui,sans-serif` |
2085
+ | `--theme-switcher-background` | Group background, independent of option state | `color-mix(in srgb,currentColor 8%,transparent)` |
2086
+
2087
+ The font value includes its size and family, plus any weight and line height.
2088
+ Use an application-owned complete font token or value, rather than the
2089
+ CSS-wide keyword `inherit` as the custom-property value. The host still inherits
2090
+ text color. These properties do not alter the `2.75rem` minimum targets,
2091
+ full-label wrapping, selected/hover/focus/disabled states, or theme persistence.
2092
+ Applications retain responsibility for readable font and color choices.
2093
+
2076
2094
  This fragment declares no component-specific readiness or action event; use the loader's `html-import-ready` only to know that import execution completed.
2077
2095
 
2078
2096
  Shared dependencies: [`ThemeManager.js`](runtime-modules.md#thememanagerjs).
@@ -2097,6 +2115,22 @@ the two configuration attributes retains all four default choices. Both use
2097
2115
  the existing shared preference owner; presentation does not create a new store
2098
2116
  or migrate saved themes.
2099
2117
 
2118
+ An application can give that instance modest rounded corners and its own
2119
+ ordinary label font without accessing the shadow root:
2120
+
2121
+ ```css
2122
+ #theme-switcher {
2123
+ --theme-switcher-radius: .65rem;
2124
+ --theme-switcher-option-radius: .5rem;
2125
+ --theme-switcher-font: 400 1rem/1.25 var(--app-font-family, system-ui, sans-serif);
2126
+ --theme-switcher-background: transparent;
2127
+ --app-bar-border: var(--border-color);
2128
+ }
2129
+ ```
2130
+
2131
+ These are example application values, not new component defaults. Other
2132
+ instances retain the defaults unless the application cascades overrides to them.
2133
+
2100
2134
  ## unified-inbox.html
2101
2135
 
2102
2136
  ### Overview
@@ -136,12 +136,16 @@ Root applications import `AI` through `arcane-os/ai` in the managed browser map.
136
136
  `ai.prepareTTSPlayback(payload,signal)` silently prepares real Blob audio or a
137
137
  native speech descriptor for playback. Native browser voices expose no audio
138
138
  file; use this path or shared streaming/SpeechPlayback for native output.
139
- `ai.prepareTTS({parts,storage,identity,signal,onState})` prepares complete
139
+ `ai.prepareTTS({parts,storage,identity,textFormat,signal,onState})` prepares complete
140
140
  punctuation segments without playing them and optionally saves/reuses their
141
141
  audio through application-owned DBOPFS. Its immediate handle exposes ordered
142
142
  segments, preparation state, `ready`, `getAudio(index)`, and `cancel()`.
143
143
  `ai.playPreparedTTS(prepared,{signal,onState})` attaches ordered playback and
144
144
  returns `state`, `error`, `finished`, `pause()`, `resume()`, and `stop()`.
145
+ Each preparation owns its pending activation interest. Cancelling the final
146
+ interest aborts that activation; other preparation, playback or joining direct
147
+ TTS-load interests preserve it. Cancelling after readiness leaves the model
148
+ loaded, and complete cache hits start no model. Actual cleanup is provider-owned.
145
149
  Each AI has one playback lane; replacing playback preserves independent
146
150
  preparation. Fully cached replay does not load the speech
147
151
  model. See [prepared narration](ai/browser-speech.md#prepare-narration-once-and-replay-stored-audio)
@@ -149,6 +153,17 @@ for complete inputs, storage ownership, progress, errors, and cancellation,
149
153
  and [AI.js](runtime-modules.md#aijs) for the managed runtime contract.
150
154
  These are AI instance methods, not new npm export names.
151
155
 
156
+ Complete synthesis calls can select `textFormat:'plain'` to preserve supplied
157
+ speech text, including repeated formatting marks and surrounding whitespace.
158
+ This applies to `ai.fetchTTS(payload,signal)`, audio-file preparation through
159
+ `ai.prepareTTSPlayback(payload,signal)`, durable `ai.prepareTTS(...)`, provider
160
+ runtime TTS requests and direct Kokoro synthesis. The SDK consumes the control
161
+ before engine dispatch; durable preparation retains the existing punctuation
162
+ segmentation. Omitted or other values retain automatic speech cleanup.
163
+ Streaming `streamTTS`, `finishTTS` and shared `SpeechPlayback` retain their
164
+ automatic cleanup; this option does not change upstream engine capacity.
165
+ See [exact complete synthesis input](ai/browser-speech.md#exact-complete-synthesis-input).
166
+
152
167
  ## Shared operation contract
153
168
 
154
169
  High-level operations accept one options object. Common fields are `workspaceRoot`, `appId`, `target`, `signal`, and `onEvent`; only fields meaningful to that operation are consumed. Long work acknowledges first, owns its child tasks, keeps event delivery ordered and backpressured, emits progress or heartbeats, observes cancellation where safe, and rejects with `ArcaneError` on failure.
@@ -410,6 +425,10 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
410
425
  | `arcaneNativeBuilderProvider` | singleton | `arcane-os/native/portable-provider` | Portable native provider | Node; produces a portable payload rather than an executable host |
411
426
  | `arcaneNativeBuilderProvider default export` | singleton | `arcane-os/native/portable-provider` | Portable native provider | Node; produces a portable payload rather than an executable host |
412
427
  | `startCoreHost()` | function | `arcane-os/core/host` | Native Core host composition | Node with readable input and writable output streams |
428
+ | `startSharedCoreHost()` | function | `arcane-os/core/host` | Shared Core host | Node local IPC on Windows, Linux and macOS |
429
+ | `connectSharedCoreHost()` | function | `arcane-os/core/host` | Shared Core host | Node local IPC and optional explicit headless startup |
430
+ | `runSharedCoreHost()` | function | `arcane-os/core/host` | Shared Core host | Node headless entry and optional parent startup IPC |
431
+ | `startSharedCoreBridge()` | function | `arcane-os/core/host` | Shared Core host | Node readable/writable streams and local IPC |
413
432
  | `readCoreLaunchContext()` | function | `arcane-os/core/host` | Native Core host composition | Node; reads explicitly selected launch JSON |
414
433
  | `resolveArcaneDataPaths()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node on Windows, Linux and macOS; Android supplies its data root |
415
434
  | `createRepositoryWorkspace()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node with Git; Android requires host process adaptation |
@@ -4229,6 +4248,17 @@ External source mode accepts both the installed-package route layout and the
4229
4248
  existing physical runtime layout. Installed-package mode serves the configured
4230
4249
  SDK resources directly from `node_modules` at their logical browser URLs;
4231
4250
  it does not create a workspace `arcane/` directory or require `arcane.lock.json`.
4251
+
4252
+ For a standalone `appsRoot:'.'` source application with explicit
4253
+ `pwa.enabled:false` and an absent root `arcane-sw.js`, the exact worker URL
4254
+ returns a small retirement worker. Its native install requests `skipWaiting()`;
4255
+ activation unregisters only its own registration, without a fetch handler,
4256
+ storage/cache changes, client claiming or page reload. The browser owns update
4257
+ timing and an open page may retain the neutral controller until its lifetime
4258
+ ends. An existing authored worker, omitted/enabled PWA, multi-app source routes
4259
+ and packaged mode stay unchanged. See the complete
4260
+ [disabled-worker lifecycle](pwa.md#disabled-worker-retirement-on-the-source-server).
4261
+
4232
4262
  `host` defaults to `127.0.0.1` and accepts an
4233
4263
  explicit network address or hostname. Use `0.0.0.0` for all IPv4 interfaces or
4234
4264
  `::` for the platform's IPv6 wildcard listeners. `port` selects the HTTPS
@@ -4738,6 +4768,14 @@ lifecycle. Package-only apps remain unchanged. The operation generates
4738
4768
  no packaged output; enabled PWA manifests
4739
4769
  are served directly from the selected source resources.
4740
4770
 
4771
+ The source listener also supplies the
4772
+ [disabled-worker retirement lifecycle](pwa.md#disabled-worker-retirement-on-the-source-server)
4773
+ for explicit disabled standalone PWA configuration when the root worker is
4774
+ absent. This does not grant filesystem retirement authority or change the
4775
+ refresh operation's preservation of authored files. Browser activation and
4776
+ unregistration proceed through the native lifecycle without clearing saved data
4777
+ or reloading an open page.
4778
+
4741
4779
  ### Signature and result
4742
4780
 
4743
4781
  ```text
@@ -7530,6 +7568,12 @@ published SDK/runtime dependencies and the canonical Core entry/client into a
7530
7568
  fresh output directory. Service factories are imported only when a real native
7531
7569
  host later launches the generated Core entry, never during packaging.
7532
7570
 
7571
+ An optional `appDescriptor.native.window` record carries `width`, `height` and
7572
+ `resizable` into `manifest.window`. Dimensions are positive integral logical
7573
+ client units; `resizable` is an optional boolean. Omitted fields remain omitted.
7574
+ The portable payload records the selection; the selected platform host owns
7575
+ its window behavior. See [initial native window size](core-native-packaging.md#initial-native-window-size).
7576
+
7533
7577
  ### Availability and normalization
7534
7578
 
7535
7579
  **Node; produces a portable payload rather than an executable host.** Existing
@@ -7641,6 +7685,16 @@ the selected output root. There is no compiler or OS checkout requirement for
7641
7685
  application builds. The build machine needs its normal Node/npm toolchain and
7642
7686
  `tar`; execution needs Windows x64, .NET Framework 4.6.2 or later and WebView2.
7643
7687
 
7688
+ The matching Windows host consumes `appDescriptor.native.window` through
7689
+ `manifest.window`. Optional `width` and `height` select initial client dimensions
7690
+ in 96-DPI logical units; the host scales them for its actual DPI and fits the
7691
+ outer window within the current screen's usable area. Optional `resizable:false`
7692
+ selects a fixed-size border and disables maximizing. Omitted fields preserve
7693
+ the existing host defaults. Initial sizing preserves a window already restored
7694
+ to a non-normal state and does not add saved-bounds storage. The initial-fit
7695
+ logic is not reapplied during later user resizing. See [initial native window size](core-native-packaging.md#initial-native-window-size)
7696
+ for a complete descriptor example and the other-platform boundary.
7697
+
7644
7698
  The returned `arcane-native-builder/1` provider exposes `describe`, `doctor`,
7645
7699
  `prepare`, `build`, `verify` and `run`. Build returns `{app,target,manifest}`
7646
7700
  with the actual directory at `target.rootDir`. Verification reads the artifact;
@@ -8631,8 +8685,156 @@ async function bundleMoonCheeseRuntime(outputRoot){
8631
8685
  The ordinary provider invokes this owner only for an explicit native local-AI
8632
8686
  selection. See [selected local-AI runtimes](core-native-packaging.md#selected-local-ai-runtimes).
8633
8687
 
8688
+ ## startSharedCoreHost()
8689
+
8690
+ ### Overview
8691
+
8692
+ Owns one application's Core runtime at an explicitly selected local endpoint.
8693
+ It claims the endpoint before constructing services, so UI and MCP processes
8694
+ can share the same service state without constructing duplicate owners.
8695
+
8696
+ ### Signature and result
8697
+
8698
+ ```text
8699
+ startSharedCoreHost({endpoint,application,version,configure,getReplayEvents,signal,onError}={})
8700
+ ```
8701
+
8702
+ Resolves `{runtime,endpoint,closed,close}`. `configure(runtime,{signal})`
8703
+ registers services through the existing runtime; services start independently.
8704
+ Optional synchronous `getReplayEvents()` supplies current `{event,data}`
8705
+ snapshots, never historical replies. `close()` joins composition, accepted
8706
+ service work, service cleanup and queued frames before releasing the endpoint.
8707
+ Request-lifetime work is cancelled; accepted service-lifetime work remains
8708
+ service-owned. The supplied signal begins the same shutdown. `closed` rejects
8709
+ on shutdown failure; `onError` observes transport/background failures, with
8710
+ complete console diagnostics by default.
8711
+
8712
+ ### Availability and example
8713
+
8714
+ Node supports app-selected named pipes on Windows and Unix-domain sockets on
8715
+ Linux/macOS; Android needs its host adaptation. Parent directories must exist.
8716
+ An occupied endpoint returns native `EADDRINUSE` with
8717
+ `coreHostPhase:'listen'`, without calling `configure`. Native failures remain
8718
+ observable; the SDK does not delete an existing endpoint to take ownership.
8719
+
8720
+ ```js
8721
+ import {startSharedCoreHost} from 'arcane-os/core/host';
8722
+
8723
+ const host = await startSharedCoreHost(
8724
+ {
8725
+ endpoint: selectedEndpoint,
8726
+ application: {id: 'moon-cheese-ledger'},
8727
+ configure(runtime) {
8728
+ runtime.registerService(ledgerService);
8729
+ }
8730
+ }
8731
+ );
8732
+ // The application owner later calls await host.close().
8733
+ ```
8734
+
8735
+ `selectedEndpoint` and `ledgerService` belong to the application. See
8736
+ [shared Core hosting](core-shared-host.md) for correlation, replay and cleanup.
8737
+
8738
+ ## connectSharedCoreHost()
8739
+
8740
+ ### Overview and signature
8741
+
8742
+ Connects to the selected application host, optionally starting an explicit
8743
+ headless entry when that endpoint is absent.
8744
+
8745
+ ```text
8746
+ connectSharedCoreHost({endpoint,start,signal,onError}={})
8747
+ ```
8748
+
8749
+ Resolves `{client,endpoint,closed,close,shutdown}`. `client` is the existing Core
8750
+ client with current-runtime replay. Optional `start` is
8751
+ `{command,args=[],cwd,env,logFile}`; its entry calls `runSharedCoreHost()`.
8752
+ Only `ENOENT` or `ECONNREFUSED` selects startup. Arguments/environment are
8753
+ captured at acceptance, diagnostics append completely to the selected log,
8754
+ and requests are never automatically repeated. Other connection/start errors
8755
+ propagate; `closed` rejects on transport failure. Node/platform availability
8756
+ matches `startSharedCoreHost()`.
8757
+
8758
+ `close()` disconnects this client and cancels only its request-lifetime work;
8759
+ it preserves the host and accepted service work. Cancelling startup wait does
8760
+ not kill a host another client may use. No startup timeout is imposed; supply
8761
+ `signal` when needed. `shutdown(invokeOptions)` explicitly invokes
8762
+ `core.host.shutdown`, drains all shared services and returns `{state:'closed'}`
8763
+ before disconnecting. It affects every client; cancellation after acceptance
8764
+ does not reverse it. A drain failure returns the full Core error.
8765
+
8766
+ ```js
8767
+ import {connectSharedCoreHost} from 'arcane-os/core/host';
8768
+
8769
+ const connection = await connectSharedCoreHost(
8770
+ {endpoint: selectedEndpoint}
8771
+ );
8772
+ try {
8773
+ const catalog = await connection.client.invoke(
8774
+ 'catalog.read',
8775
+ {}
8776
+ );
8777
+ console.log(catalog);
8778
+ } finally {
8779
+ await connection.close();
8780
+ }
8781
+ ```
8782
+
8783
+ ## runSharedCoreHost()
8784
+
8785
+ Accepts the same options as `startSharedCoreHost(options)` and adds the headless
8786
+ startup handshake. The winning launcher returns the host handle. A losing
8787
+ launcher connects to the existing owner, confirms dispatcher response and
8788
+ returns `null` without constructing its own services. Startup success or the
8789
+ complete error reaches an existing parent Node IPC channel; the helper never
8790
+ calls `process.exit()`. Host lifetime, errors and platform requirements retain
8791
+ the contracts above.
8792
+
8793
+ ```js
8794
+ import {runSharedCoreHost} from 'arcane-os/core/host';
8795
+
8796
+ const host = await runSharedCoreHost(applicationHostOptions);
8797
+ await host?.closed;
8798
+ ```
8799
+
8800
+ The application supplies `applicationHostOptions`, including its endpoint and
8801
+ service factory. This is the entry selected by the `start` option of
8802
+ `connectSharedCoreHost()`.
8803
+
8804
+ ## startSharedCoreBridge()
8805
+
8806
+ Connects one framed stdio client to the shared host, preserving complete
8807
+ requests, responses and events through the existing `arcane/1` transport.
8808
+
8809
+ ```text
8810
+ startSharedCoreBridge({endpoint,start,input=process.stdin,output=process.stdout,signal,onError}={})
8811
+ ```
8812
+
8813
+ Resolves `{endpoint,closed,close}`. `start` and connection errors follow
8814
+ `connectSharedCoreHost()`. EOF, `close()` or cancellation disconnects this
8815
+ client only. Terminal stream errors settle pending writes and reject `closed`;
8816
+ service shutdown is separate. Node readable/writable streams and the selected
8817
+ local IPC endpoint are required. This is Core framing, not external MCP framing.
8818
+
8819
+ ```js
8820
+ import {startSharedCoreBridge} from 'arcane-os/core/host';
8821
+
8822
+ const bridge = await startSharedCoreBridge(
8823
+ {endpoint: selectedEndpoint}
8824
+ );
8825
+ await bridge.closed;
8826
+ ```
8827
+
8828
+ Generated native entries select this bridge only through explicit launch
8829
+ context `sharedHost:{endpoint,logFile}`. Omitting it preserves ordinary
8830
+ stdio-owned Core lifetime. See [native shared-host launch](core-shared-host.md#native-launch-and-platforms).
8831
+
8634
8832
  ## startCoreHost()
8635
8833
 
8834
+ For independent UI and MCP processes sharing one application runtime, use the
8835
+ [shared host functions](#startsharedcorehost) above. Ordinary `startCoreHost()`
8836
+ retains its existing stdio-owned lifetime.
8837
+
8636
8838
  ### Overview
8637
8839
 
8638
8840
  Composes explicitly supplied application services with the shared Core runtime
@@ -9311,7 +9513,7 @@ Existing chat and speech providers are unchanged.
9311
9513
  ### Signature and result
9312
9514
 
9313
9515
  ```text
9314
- createBrowserDecisionModel({family,model,revision,device,dtype,runtime}={})
9516
+ createBrowserDecisionModel({family,model,revision,device,dtype,runtime,store}={})
9315
9517
  ```
9316
9518
 
9317
9519
  ```js
@@ -9346,6 +9548,15 @@ selections report its failure without precision fallback. For Laya FP32, pass
9346
9548
  Cancellation terminates that client's Worker and rejects its in-flight work.
9347
9549
  No decisions are inserted into chat history or durable storage.
9348
9550
 
9551
+ Optional `store` is an existing SDK DBOPFS model or speech store exposing
9552
+ `fetchResource(input, options)`. It remains outside cloned Worker configuration.
9553
+ The selected loader routes its actual model/configuration/tokenizer resources
9554
+ through that owner and disables competing model caches in this explicit mode.
9555
+ Omission preserves ordinary upstream loading. Native ESM imports retain their
9556
+ platform loader. Resource progress counts closed shards; cancellation joins
9557
+ resource cleanup before outstanding `load()`/`evaluate()` calls settle.
9558
+ `unload()` and `dispose()` acknowledge their state transition synchronously.
9559
+
9349
9560
  ### Example
9350
9561
 
9351
9562
  ```js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.70.0",
3
+ "version": "0.71.1",
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",
@@ -15,8 +15,8 @@
15
15
  max-inline-size:100%;
16
16
  padding:.2rem;
17
17
  border:1px solid var(--app-bar-border,var(--border-color,rgba(255,255,255,.25)));
18
- border-radius:1.6rem;
19
- background:color-mix(in srgb,currentColor 8%,transparent);
18
+ border-radius:var(--theme-switcher-radius,1.6rem);
19
+ background:var(--theme-switcher-background,color-mix(in srgb,currentColor 8%,transparent));
20
20
  }
21
21
 
22
22
  button {
@@ -28,11 +28,11 @@
28
28
  min-block-size:2.75rem;
29
29
  padding:.5rem .75rem;
30
30
  border:0;
31
- border-radius:1.4rem;
31
+ border-radius:var(--theme-switcher-option-radius,1.4rem);
32
32
  background:transparent;
33
33
  color:inherit;
34
34
  cursor:pointer;
35
- font:700 .875rem/1.25 system-ui,sans-serif;
35
+ font:var(--theme-switcher-font,700 .875rem/1.25 system-ui,sans-serif);
36
36
  text-align:center;
37
37
  white-space:normal;
38
38
  overflow-wrap:anywhere;
@@ -17,6 +17,7 @@ import {
17
17
  readWorkspaceAssetVersion,resolveAssetReference,rewriteAssetReferences,versionAssetUrl
18
18
  } from './import-map.mjs';
19
19
  import {createPwaArtifacts,selectPwaFiles} from './pwa.mjs';
20
+ import {createPwaRetirementWorkerScript} from './pwa-worker.mjs';
20
21
 
21
22
  const is = new Is(false);
22
23
 
@@ -1301,6 +1302,30 @@ async function startOwnedDevServer({
1301
1302
  );
1302
1303
  return;
1303
1304
  }
1305
+ if (mode === 'source' && selectedRoutes.config.appsRoot === '.'
1306
+ && selectedRoutes.app?.pwa?.enabled === false && target.path === '/arcane-sw.js') {
1307
+ let rootWorkerExists = true;
1308
+ try {
1309
+ await lstat(path.join(selectedRoutes.workspaceRoot, 'arcane-sw.js'));
1310
+ } catch (error) {
1311
+ if (error?.code !== 'ENOENT') throw error;
1312
+ rootWorkerExists = false;
1313
+ }
1314
+ if (!rootWorkerExists) {
1315
+ response.setHeader('Cache-Control', 'no-cache');
1316
+ await serveGeneratedRepresentation(
1317
+ fileServer,
1318
+ request,
1319
+ response,
1320
+ {
1321
+ // A former enabled worker's date cannot validate this retirement script.
1322
+ contentType: contentTypes.js,
1323
+ body: createPwaRetirementWorkerScript
1324
+ }
1325
+ );
1326
+ return;
1327
+ }
1328
+ }
1304
1329
  const rootRequest = segments.length === 0;
1305
1330
  if (rootRequest && !selectedRoutes.rootDocument) {
1306
1331
  response.writeHead(302,{location:`${selectedRoutes.startPath}${target.search}`});
@@ -2,6 +2,25 @@ export function createPwaWorkerScript(manifest, clientUrl = 'arcane/sdk/pwa.mjs'
2
2
  return `(${installPwaWorker.toString()})(${JSON.stringify(manifest, null, 4)}, ${JSON.stringify(clientUrl)});\n`;
3
3
  }
4
4
 
5
+ export function createPwaRetirementWorkerScript() {
6
+ return `(${installPwaRetirementWorker.toString()})();\n`;
7
+ }
8
+
9
+ function installPwaRetirementWorker() {
10
+ self.addEventListener(
11
+ 'install',
12
+ function installRetiringWorker(event) {
13
+ event.waitUntil(self.skipWaiting());
14
+ }
15
+ );
16
+ self.addEventListener(
17
+ 'activate',
18
+ function activateRetiringWorker(event) {
19
+ event.waitUntil(self.registration.unregister());
20
+ }
21
+ );
22
+ }
23
+
5
24
  function installPwaWorker(manifest, clientUrl) {
6
25
  const scope = self.registration.scope;
7
26
  const scopeOrigin = new URL(scope).origin;