arcane-os 0.53.0 → 0.54.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/NOTICE +4 -1
  3. package/README.md +1 -1
  4. package/bin/arcane-test.mjs +2 -1
  5. package/browser-runtime/ai/browser-decisions.mjs +405 -0
  6. package/browser-runtime/ai/decision-runtime.mjs +281 -0
  7. package/browser-runtime/ai/decision-worker.mjs +67 -0
  8. package/browser-runtime/dependencies/event-pubsub/dependencies/strong-type/index.js +1151 -0
  9. package/browser-runtime/dependencies/event-pubsub/dependencies/strong-type/licence +21 -0
  10. package/browser-runtime/dependencies/event-pubsub/dependencies/strong-type/package.json +61 -0
  11. package/browser-runtime/dependencies/event-pubsub/index.js +2 -2
  12. package/browser-runtime/dependencies/event-pubsub/package.json +11 -4
  13. package/browser-runtime/dependencies/ws-share/WS.js +128 -0
  14. package/browser-runtime/dependencies/ws-share/licence.md +27 -0
  15. package/browser-runtime/dependencies/ws-share/package.json +64 -0
  16. package/browser-runtime/websocket-client.mjs +1 -0
  17. package/docs/event-manager.md +7 -4
  18. package/docs/reference/ai/browser-decisions.md +184 -0
  19. package/docs/reference/inventory/package-api.json +30 -2
  20. package/docs/reference/inventory/runtime-modules.json +17 -4
  21. package/docs/reference/protocols.md +76 -1
  22. package/docs/reference/runtime-modules.md +45 -2
  23. package/docs/reference/sdk-api.md +133 -9
  24. package/docs/reference/websocket-client.md +132 -0
  25. package/node_modules/event-pubsub/CHANGELOG.md +12 -1
  26. package/node_modules/event-pubsub/MIGRATION.md +8 -5
  27. package/node_modules/event-pubsub/README.md +75 -19
  28. package/node_modules/event-pubsub/SECURITY.md +3 -3
  29. package/node_modules/event-pubsub/index.js +2 -2
  30. package/node_modules/event-pubsub/package.json +11 -4
  31. package/package.json +8 -5
  32. package/runtime/arcane/modules/HTMLImport.js +81 -26
  33. package/runtime/arcane/modules/HTMLImportScript.js +3 -0
  34. package/src/import-map.mjs +416 -59
  35. package/src/index.mjs +1 -0
  36. package/src/packager/core.mjs +24 -6
  37. package/src/packager/native-modules.mjs +458 -0
  38. package/src/testing-loader.mjs +41 -20
@@ -174,6 +174,69 @@ output must remain separate; a conflicting native assembly fails before it
174
174
  packages any app. App launch paths, resource URLs, and enabled PWA
175
175
  representations retain their package-relative layout.
176
176
 
177
+ ## Native-module package output
178
+
179
+ The public `packageApp` operation accepts `moduleFormat:'native'`. Omission, or
180
+ `'import-map'`, retains ordinary browser output. Optional operation-level
181
+ `outputDirectory` overrides only this invocation's destination; `verifyApp`
182
+ accepts that same override. Configuration, source layout, app identity, storage
183
+ scope and previously generated sibling outputs remain unchanged.
184
+
185
+ The native transformation runs in the selected package stage after application
186
+ adapter work, before the final inventory and any enabled PWA artifacts. It uses
187
+ the existing selected document/runtime records and shared lexical/HTML owners.
188
+ Each selected document supplies its saved maps and authored base. Native mode
189
+ uses the static layout with map declarations before executable scripts; it does
190
+ not reproduce late-map insertion or its already-resolved-module timing rules.
191
+ Exact keys, longest matching prefixes and most-specific importer scopes resolve to package
192
+ URLs, including URL aliases used by direct installed-SDK layouts. Static
193
+ imports, re-exports, string-literal dynamic imports and string-literal
194
+ `import.meta.resolve()` become native relative references. Query strings and
195
+ fragments retain their meaning. Equivalent graphs share output modules;
196
+ documents whose maps resolve differently receive distinct context files beside
197
+ the original module, including when their graphs contain cycles.
198
+
199
+ Selected document import-map elements are consumed in the output only.
200
+ Executable inline document scripts move into complete external files while
201
+ their script elements retain position, type and effective scheduling. The
202
+ packager removes ineffective `async`/`defer` attributes only from formerly inline
203
+ classic scripts, whose external form otherwise would activate those attributes.
204
+ Module scripts and already-external document scripts retain their attributes.
205
+ Imported component scripts use one canonical classic wrapper, with an explicit
206
+ `data-arcane-packaged-script` marker consumed by `HTMLImport`. That path loads
207
+ the external script directly and retains per-instance host binding, ordered
208
+ asynchronous completion, cancellation, teardown and ready/error events. Ordinary
209
+ browser components keep their existing inline path. Application documents not
210
+ selected for document processing remain unchanged unless they are explicitly
211
+ loaded as components.
212
+
213
+ The selected payload is preserved in full, including runtime files, assets,
214
+ workers and licenses. Graph processing follows each document's executable and
215
+ resource references, including literal URLs to selected shared component HTML.
216
+ It does not assign an unused shared file to another document's map. Unreferenced
217
+ selected files retain their complete content. Referenced scripts, component HTML
218
+ and stylesheets are processed at their existing owners; resource URLs are rebased
219
+ for deployment under a directory such as `app/`. The operation adds generated scripts to the
220
+ complete release inventory. It does not bundle, prune, minify or rewrite
221
+ application prose, data or saved history. The existing stage replacement
222
+ preserves prior output if packaging fails or is cancelled.
223
+
224
+ This is a static module/script packaging capability, not a replacement browser
225
+ or a runtime import-map interpreter. Computed import expressions remain authored;
226
+ applications supply native-resolvable URLs for runtime-selected imports. It
227
+ does not convert event-handler attributes, dynamically evaluated source, or
228
+ provider-created executable blobs. An inline document script beneath an authored
229
+ remote effective base cannot address a package-local generated file; native mode
230
+ reports that incompatible transformation and preserves the original output.
231
+ Already-external scripts retain their remote-base behavior. Remote modules/providers
232
+ and WebAssembly still depend on their actual host capabilities. Resolving package references is
233
+ distinct from executing the complete application on Chromium or another host.
234
+
235
+ For an extension, select `browserPwa:false` for that artifact and place the
236
+ complete SDK-produced app beneath the app-owned extension root. The application
237
+ owns its manifest, toolbar/background launcher and permissions. Ordinary browser
238
+ PWA configuration and generated browser output remain independently available.
239
+
177
240
  ## Installed-package browser routes
178
241
 
179
242
  An external application can serve and package its installed SDK directly,
@@ -413,7 +476,7 @@ renaming any preceding entry changes the physical contract.
413
476
 
414
477
  The map derives its complete entries from the selected runtime graph;
415
478
  application source imports do not select a fixed entry count. The operation
416
- result reports `imports`, `documentCount`, and `excludedModules`; reached-file
479
+ result reports `imports`, optional dependency `scopes`, `documentCount`, and `excludedModules`; reached-file
417
480
  traversal remains internal. The
418
481
  managed graph exposes `arcane-os/event-manager`, `arcane-os/ai/browser-wasm`,
419
482
  `arcane-os/ai/browser-speech`, `arcane-os/preference-store`, and
@@ -425,6 +488,8 @@ The focused physical targets remain stable when their bindings are reached:
425
488
  | Browser specifier | Physical target |
426
489
  | --- | --- |
427
490
  | `arcane-os/event-manager` | `./arcane/sdk/event-manager.mjs` |
491
+ | `arcane-os/websocket-client` | `./arcane/sdk/websocket-client.mjs` |
492
+ | `ws-share` | `./arcane/sdk/dependencies/ws-share/WS.js` |
428
493
  | `arcane-os/ai/browser-wasm` | `./arcane/sdk/ai/browser-wasm.mjs` |
429
494
  | `arcane-os/ai/browser-speech` | `./arcane/sdk/ai/browser-speech.mjs` |
430
495
  | `arcane-os/speech-text` | `./arcane/sdk/speech-text.mjs` |
@@ -434,6 +499,16 @@ The focused physical targets remain stable when their bindings are reached:
434
499
  | `strong-type` | `./arcane/dependencies/strong-type/index.js` |
435
500
  | `./node_modules/strong-type/index.js` | `./arcane/dependencies/strong-type/index.js` |
436
501
 
502
+ When the selected runtime contains event-pubsub's own dependency projection,
503
+ the `./arcane/sdk/dependencies/event-pubsub/` scope maps `strong-type` to
504
+ `./arcane/sdk/dependencies/event-pubsub/dependencies/strong-type/index.js`.
505
+ This keeps its published 2.0.0 selection separate from the global SDK 2.0.1
506
+ binding. Scope keys and targets follow installed-package and document-base
507
+ translation together. Older inventories without that nested projection retain
508
+ their existing map shape. Internal managed test contexts accept and preserve
509
+ optional `scopes` alongside `imports`; their loader selects the most-specific
510
+ matching importer scope before the global map.
511
+
437
512
  Direct installed routes translate these targets to their actual npm destinations,
438
513
  including aliases. There is no `arcane-os` package-root mapping or catch-all
439
514
  `arcane/` prefix. Classic scripts, workers, stylesheets, and other
@@ -98,7 +98,8 @@ own asynchronous work, cancellation, and backpressure.
98
98
  | [`DocumentNavigation.js`](#documentnavigationjs) | esm | Binds document navigation, filtering, history, current-item reveal, and load initialization. | Browser / native WebView | Normalized filter/navigation state; DOM effects preserved. |
99
99
  | [`Errors.js`](#errorsjs) | esm | Normalizes global errors/rejections, assigns occurrence identifiers, persists a complete ledger, and performs complete delivery. | Browser / native WebView hybrid | Incident records normalized; storage/mail failures isolated. |
100
100
  | [`GifEncoder.js`](#gifencoderjs) | esm | Encodes indexed frames into a complete animated GIF using palette mapping and LZW. | Cross-host | Normalized complete binary output. |
101
- | [`HTMLImport.js`](#htmlimportjs) | esm | Defines the same-origin `<html-import>` loader with open shadow root, inline script execution, and readiness/error events. | Browser / native WebView | Public error detail normalized; fetch/DOM failure preserved. |
101
+ | [`HTMLImport.js`](#htmlimportjs) | esm | Defines the same-origin `<html-import>` loader with open shadow root, inline or packaged external script execution, and readiness/error events. | Browser / native WebView | Public error detail normalized; fetch/DOM failure preserved. |
102
+ | [`HTMLImportScript.js`](#htmlimportscriptjs) | esm | Renders the complete classic-script host wrapper shared by HTMLImport and native-module packaging. | Cross-host | Complete source retained with a terminating newline before the wrapper closes. |
102
103
  | [`InMemoryCommunicationProvider.js`](#inmemorycommunicationproviderjs) | esm | Implements deterministic in-memory thread/message/send behavior for demos and tests. | Cross-host | Normalized communication entities. |
103
104
  | [`IsolatedModelQuestionRunner.js`](#isolatedmodelquestionrunnerjs) | esm | Inspects one selected model and runs one isolated question while preserving the complete answer. | Native bridge or injected provider | Normalized model/result and coded errors. |
104
105
  | [`LocalAIReadiness.js`](#localaireadinessjs) | esm | Derives selected AI requirements and returns a complete readiness/recovery report across browser, desktop, and Android modes. | Browser/native hybrid | Fully normalized report and stable error codes; browsers never probe Ollama. |
@@ -2500,7 +2501,7 @@ console.log(Object.keys(module));
2500
2501
 
2501
2502
  ### Overview
2502
2503
 
2503
- Defines the same-origin `<html-import>` loader with open shadow root, inline script execution, and readiness/error events.
2504
+ Defines the same-origin `<html-import>` loader with open shadow root, inline or packaged external script execution, and readiness/error events.
2504
2505
 
2505
2506
  ### Public surface
2506
2507
 
@@ -2520,6 +2521,14 @@ Local component resources use stable URLs. The loader removes retired SDK-owned
2520
2521
  including caller-owned `v` fields, and fragments remain intact. External resource
2521
2522
  URLs retain their complete query. Ordinary HTTP conditional caching is unchanged.
2522
2523
 
2524
+ Native-module packages mark component scripts with `data-arcane-packaged-script`
2525
+ and an external `src`. The loader executes those files through a real classic
2526
+ script element instead of fetching their text for inline execution. Each host
2527
+ receives its own invocation, `this` binding, ordered initialization, readiness
2528
+ and error events, cancellation, and imported `destroy()` restoration. Independent
2529
+ component hosts remain concurrent. Ordinary unmarked components retain their
2530
+ existing inline/fetched-script path.
2531
+
2523
2532
  Exact exports: `default`.
2524
2533
 
2525
2534
  ### Availability and normalization
@@ -2534,6 +2543,40 @@ import * as module from '/arcane/modules/HTMLImport.js';
2534
2543
  console.log(Object.keys(module));
2535
2544
  ```
2536
2545
 
2546
+ ## HTMLImportScript.js
2547
+
2548
+ ### Overview
2549
+
2550
+ Renders the complete classic-script host wrapper shared by HTMLImport and
2551
+ native-module packaging. Rendering returns source text; it does not execute it
2552
+ or access the DOM.
2553
+
2554
+ ### Public surface
2555
+
2556
+ `createHTMLImportScript(source)` returns the complete source inside the loader's
2557
+ async host-bound initializer. A newline separates the authored ending from the
2558
+ wrapper closure, including when the body ends in a line comment. At execution,
2559
+ the wrapper uses the current script's host token and the HTMLImport-owned
2560
+ registry, then exposes the initializer promise to that host's loader.
2561
+
2562
+ Exact exports: `createHTMLImportScript`.
2563
+
2564
+ ### Availability and normalization
2565
+
2566
+ **Cross-host.** Complete source retained with a terminating newline before the
2567
+ wrapper closes. Transport: In-process only. [Deep protocol details](protocols.md).
2568
+
2569
+ ### Example
2570
+
2571
+ ```javascript
2572
+ import {createHTMLImportScript} from 'arcane-os/modules/HTMLImportScript.js';
2573
+
2574
+ const source=createHTMLImportScript('this.textContent="The moon burglar arrived.";');
2575
+ ```
2576
+
2577
+ Application packaging normally calls `packageApp({moduleFormat:'native', ...})`
2578
+ instead of assembling or executing component wrappers itself.
2579
+
2537
2580
  ## InMemoryCommunicationProvider.js
2538
2581
 
2539
2582
  ### Overview
@@ -2,9 +2,10 @@
2
2
 
3
3
  The npm package exposes a Node.js ESM control plane, the portable
4
4
  `arcane-os/event-manager`, `arcane-os/logging`, `arcane-os/mail`,
5
- `arcane-os/preference-store`, `arcane-os/speech-playback`,
5
+ `arcane-os/preference-store`, `arcane-os/speech-playback`, `arcane-os/websocket-client`,
6
6
  `arcane-os/speech-text`, `arcane-os/ai/tool-text-stream`, `arcane-os/ai/twin-cloud`, and `arcane-os/browser-device` entrypoints, and the browser-only
7
- `arcane-os/pwa` and `arcane-os/ai/browser-wasm` entrypoints. The
7
+ `arcane-os/pwa`, `arcane-os/ai/browser-wasm`, and
8
+ `arcane-os/ai/browser-decisions` entrypoints. The
8
9
  `arcane-os/ai/browser-speech` entrypoint includes local and native browser
9
10
  providers plus a cloud TTS adapter usable in Node with Fetch and Blob.
10
11
  The `arcane-os/modules/<filename>` and `arcane-os/entities/<filename>` paths
@@ -41,7 +42,8 @@ runtime layouts.
41
42
  | `arcane-os` | Complete high-level SDK surface. |
42
43
  | `arcane-os/toolchain` | Headless application operations. |
43
44
  | `arcane-os/events` | CLI/event reporter. |
44
- | `arcane-os/websocket` | Node HTTP/1.1 WebSocket Upgrade, complete messages and connection lifecycle. Browser clients use native `WebSocket`. |
45
+ | `arcane-os/websocket` | Node HTTP/1.1 WebSocket Upgrade, complete messages and accepted connection lifecycle. |
46
+ | `arcane-os/websocket-client` | Shared native client `WS`, one managed-connection observer and current connection snapshots through published ws-share. |
45
47
  | `arcane-os/testing` | Isolated test registration and execution. |
46
48
  | `arcane-os/targets` | Target adapters and dispatch. |
47
49
  | `arcane-os/native` | Native plan and builder protocol. |
@@ -74,6 +76,7 @@ runtime layouts.
74
76
  | `arcane-os/browser-device` | Synchronous mobile or desktop identity hints for application-owned settings. |
75
77
  | `arcane-os/pwa` | Nonblocking PWA registration, worker updates, native installation state and a dismissible installation component. |
76
78
  | `arcane-os/ai/browser-wasm` | Caller-selected browser-local Wllama inference, complete DBOPFS model storage, streaming, cancellation, and structural tool-call results. |
79
+ | `arcane-os/ai/browser-decisions` | Explicit-load Laya FP16 and Julia-1 FP32 typed decisions through the shared Transformers.js Worker owner. |
77
80
  | `arcane-os/ai/tool-text-stream` | Shared selected tool-argument text observer for provider integration. |
78
81
  | `arcane-os/ai/twin-cloud` | Stateless chat, System One state/questions and image generation with explicit key/model, complete results and operation-specific retry/cancellation. |
79
82
  | `arcane-os/ai/browser-speech` | Caller-selected Whisper/Kokoro, native browser recognition/synthesis, cloud TTS, upstream assets, role lifecycle and cancellation. |
@@ -132,6 +135,8 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
132
135
  | Member | Kind | Import | Group | Availability |
133
136
  | --- | --- | --- | --- | --- |
134
137
  | `acceptWebSocket()` | function | `arcane-os`, `arcane-os/websocket` | WebSocket protocol | Node; browser-native client interoperability |
138
+ | `WS` | class | `arcane-os`, `arcane-os/websocket-client` | Shared WebSocket clients | Node and native-WebSocket browser hosts |
139
+ | `createBrowserDecisionModel()` | function | `arcane-os/ai/browser-decisions` | Browser typed decisions | Browser module Workers and the caller-selected inference backend |
135
140
  | `fetchRequest()` | function | `arcane-os/ai/twin-cloud` | TWiN Cloud requests | Node and Browser; remote HTTPS provider |
136
141
  | `fetchSystemOneRequest()` | function | `arcane-os/ai/twin-cloud` | TWiN Cloud state-and-questions requests | Node and Browser; remote HTTPS provider |
137
142
  | `generateImages()` | function | `arcane-os/ai/twin-cloud` | TWiN Cloud image generation | Node and Browser with Fetch and Blob; remote HTTPS provider |
@@ -868,6 +873,42 @@ The package preserves the complete selected content, applying the documented
868
873
  asset-version and enabled browser-PWA transformations to resource references.
869
874
  Malformed configuration or descriptors fail while preserving the prior output.
870
875
 
876
+ `moduleFormat` defaults to `'import-map'`, preserving the ordinary browser
877
+ package. Select `'native'` to resolve saved document import maps into native
878
+ module URLs in the package stage and emit executable inline scripts as external
879
+ files. Static imports, re-exports, string-literal dynamic imports and
880
+ string-literal `import.meta.resolve()` retain exact/prefix/scoped mappings.
881
+ Imported HTML components use the shared `HTMLImport` externally executable
882
+ script path, retaining their host binding and asynchronous lifecycle. No
883
+ application-owned module loader is needed. Source HTML, saved data and the
884
+ ordinary browser output remain unchanged. See
885
+ [native-module package output](protocols.md#native-module-package-output)
886
+ for resource handling and the static transformation boundary.
887
+
888
+ An operation may override `outputDirectory` without changing the saved app
889
+ configuration. For example, package a complete application beneath an
890
+ application-owned extension manifest and launcher:
891
+
892
+ ```javascript
893
+ const release = await packageApp({
894
+ workspaceRoot,
895
+ appId: 'moon-dashboard',
896
+ outputDirectory: 'dist/extension/app',
897
+ moduleFormat: 'native',
898
+ browserPwa: false
899
+ });
900
+ ```
901
+
902
+ The override uses the same workspace-relative destination and staged replacement
903
+ rules as the saved field. It replaces only `dist/extension/app`, leaving its
904
+ extension-root siblings and an existing browser output intact. `browserPwa:false`
905
+ omits generated PWA artifacts for this invocation; it does not change the app's
906
+ saved PWA configuration. The app owns extension metadata, permissions and toolbar
907
+ launch behavior. This API builds the application directory, not an extension
908
+ manifest or an operating-system launcher. Native-mode dry runs report selected
909
+ input files; generated script/context files are included in the completed
910
+ package's full returned inventory.
911
+
871
912
  The app's optional schema-1 `outputDirectory` selects the final
872
913
  workspace-relative destination. For example, `"ai"` emits directly into
873
914
  `<workspace>/ai`; omission retains `dist/<app-id>`. The selected directory is
@@ -1151,11 +1192,14 @@ Authenticates one existing browser app release through the low-level packager AP
1151
1192
  ### Signature and result
1152
1193
 
1153
1194
  ```text
1154
- async verifyApp({workspaceRoot, appId, signal, onEvent})
1195
+ async verifyApp({workspaceRoot, appId, outputDirectory, signal, onEvent})
1155
1196
  ```
1156
1197
 
1157
1198
  Import it from `arcane-os` or `arcane-os/packager`. The signature above states whether settlement is synchronous or promise-based. The overview and owning group define result authority, side effects, callbacks, events, cancellation, and lifecycle.
1158
1199
 
1200
+ Optional `outputDirectory` selects the same workspace-relative override accepted
1201
+ by `packageApp`, without changing the app's saved destination.
1202
+
1159
1203
  ### Availability and normalization
1160
1204
 
1161
1205
  **Node.** Normalized SDK validation with complete canonical archive and release content. Deep protocol: [SDK packager and deterministic bundle contract](protocols.md).
@@ -1304,14 +1348,16 @@ The resolved result contains:
1304
1348
  runtimeRoot,
1305
1349
  documentPaths,
1306
1350
  documentCount,
1307
- documents: [{path, filePath, imports}],
1351
+ documents: [{path, filePath, imports, scopes?}],
1308
1352
  committed: true
1309
1353
  }
1310
1354
  ```
1311
1355
 
1312
1356
  `documentPaths` and each `filePath` are absolute filesystem paths. Each `path`
1313
- is relative to `documentRoot`; `imports` is the complete map rendered for that
1314
- document. The operation inventories the runtime once, builds the shared map
1357
+ is relative to `documentRoot`; `imports` contains the global bindings rendered
1358
+ for that document. Optional `scopes` contains dependency-specific bindings;
1359
+ scope URLs and their targets are rebased with the same document base as global
1360
+ imports. The operation inventories the runtime once, builds the shared map
1315
1361
  once, and reads the selected documents with up to four concurrent readers.
1316
1362
  It renders every document before writing any of them, then writes in selected
1317
1363
  order. `committed: true` means all selected writes completed; the batch is not
@@ -3755,6 +3801,22 @@ async function useselectApp(...arguments_) {
3755
3801
  }
3756
3802
  ```
3757
3803
 
3804
+ ## WS
3805
+
3806
+ Import `WS` as the default or named binding from `arcane-os/websocket-client`,
3807
+ or as a named binding from the Node root `arcane-os`. `new WS(uri,protocols?)`
3808
+ returns the exact native socket from published ws-share, sharing a connecting
3809
+ or open socket for the same exact URI string and ordered protocol list.
3810
+ `await WS.observe()` exposes one event-pubsub observer with
3811
+ `created`/`open`/`error`/`close` lifecycle records; `WS.getConnections()` returns
3812
+ current managed-connection snapshots. Each physical connection has its own ID,
3813
+ including replacements at the same address.
3814
+
3815
+ The [shared-client guide](websocket-client.md) covers complete record fields,
3816
+ subscribe-then-snapshot ordering, listener cleanup, shared close ownership,
3817
+ native per-socket behavior and the per-loaded-module/realm boundary. This
3818
+ client entry does not change the server-side `acceptWebSocket` contract.
3819
+
3758
3820
  ## acceptWebSocket()
3759
3821
 
3760
3822
  ### Overview
@@ -3766,8 +3828,9 @@ the public `.server` / `.secureServer` of `node-http-server`. There is no
3766
3828
  listener creation, default port, application envelope, message persistence,
3767
3829
  client sharing, reconnect loop or third-party WebSocket runtime.
3768
3830
 
3769
- Browser clients use native `WebSocket` directly. Node's global `WebSocket` is
3770
- also a client, not a server. This server adapter supports version 13 over an
3831
+ Clients may use native `WebSocket` directly or the separate shared
3832
+ [`WS` client](websocket-client.md). Node's global `WebSocket` is also a client,
3833
+ not a server. This server adapter supports version 13 over an
3771
3834
  HTTP/1.1 `upgrade` event, including HTTP/1.1 negotiated on an HTTPS listener.
3772
3835
  It does not implement HTTP/2 extended CONNECT or negotiate extensions such as
3773
3836
  compression. TLS setup remains with the existing server owner.
@@ -6819,6 +6882,67 @@ See the [mounting contract](pwa.md#mountpwainstallprompt) and
6819
6882
  [component reference](runtime-components.md#pwa-installhtml). The browser owns
6820
6883
  native installation eligibility and URL-bar promotion.
6821
6884
 
6885
+ ## createBrowserDecisionModel()
6886
+
6887
+ ### Overview
6888
+
6889
+ Creates an explicitly loaded browser model for Laya typed decisions in FP16 or
6890
+ Julia-1 in FP32. The shared Worker owns Transformers.js loading, full-input
6891
+ encoding, batched inference, complete results, cancellation and resource lifetime.
6892
+ Applications own model selection, questions, option meanings and decision policy.
6893
+ Existing chat and speech providers are unchanged.
6894
+
6895
+ ### Signature and result
6896
+
6897
+ ```js
6898
+ import {createBrowserDecisionModel} from 'arcane-os/ai/browser-decisions';
6899
+
6900
+ const decisions = createBrowserDecisionModel(
6901
+ {
6902
+ family: 'laya',
6903
+ model: 'onnx-community/laya-typed-decisions-ONNX',
6904
+ device: 'webgpu'
6905
+ }
6906
+ );
6907
+ ```
6908
+
6909
+ Construction starts no model download. The returned client exposes
6910
+ `load({signal})`, `evaluate(rows, {signal})`, `status()`,
6911
+ `subscribe(listener, {emitCurrent: true, signal})`, `unload()` and `dispose()`.
6912
+ Each row contains its complete `state`, `question`, string `options`, and
6913
+ optional `type` (`choice`, `score`, or `noul`). `evaluate()` returns
6914
+ `{decisions, outputs}` with all graph outputs and raw, unrounded probabilities.
6915
+ See the [complete browser decision contract](ai/browser-decisions.md) for
6916
+ precision, tokenization, output meanings, lifecycle and model limitations.
6917
+
6918
+ ### Availability and normalization
6919
+
6920
+ Browser module Workers plus the selected Transformers.js backend are required
6921
+ on explicit use. Import and construction do not activate a model. Laya fixes
6922
+ `dtype: 'fp16'`; Julia fixes `dtype: 'fp32'`; there is no precision fallback.
6923
+ Cancellation terminates that client's Worker and rejects its in-flight work.
6924
+ No decisions are inserted into chat history or durable storage.
6925
+
6926
+ ### Example
6927
+
6928
+ ```js
6929
+ runButton.addEventListener(
6930
+ 'click',
6931
+ async function askTeaRobot() {
6932
+ const result = await decisions.evaluate(
6933
+ [
6934
+ {
6935
+ state: 'The dragon is asleep. The tea robot has a very loud kettle.',
6936
+ question: 'Should the robot boil tea now?',
6937
+ options: ['Wait quietly', 'Boil the kettle']
6938
+ }
6939
+ ]
6940
+ );
6941
+ output.textContent = result.decisions[0].value;
6942
+ }
6943
+ );
6944
+ ```
6945
+
6822
6946
  ## createBrowserWasmLlmProvider()
6823
6947
 
6824
6948
  ### Overview
@@ -0,0 +1,132 @@
1
+ # Shared WebSocket clients
2
+
3
+ `arcane-os/websocket-client` exposes the published `ws-share` constructor as
4
+ both `default` and `WS`. Node also exports the same `WS` binding from
5
+ `arcane-os`. Browser modules use the focused entry through the generated SDK
6
+ import map. These entries reuse the upstream implementation rather than
7
+ creating another pool or event bus.
8
+
9
+ ```javascript
10
+ import WS from 'arcane-os/websocket-client';
11
+
12
+ const airlock=new WS('wss://moon.example/airlock',['moon-control']);
13
+ const oxygenPanel=new WS('wss://moon.example/airlock',['moon-control']);
14
+ console.log(airlock===oxygenPanel); // true while connecting or open
15
+
16
+ function showAirlockMessage(event){
17
+ console.log(event.data);
18
+ }
19
+ airlock.addEventListener('message',showAirlockMessage);
20
+
21
+ // When this panel goes away, release only its listener.
22
+ function disposePanel(){
23
+ airlock.removeEventListener('message',showAirlockMessage);
24
+ }
25
+ ```
26
+
27
+ Replace the example URL with your application's WebSocket endpoint. The exact
28
+ URI string and ordered protocol list select a shared connection. Repeated
29
+ construction reuses a connecting or open socket. A closing or closed socket
30
+ gets a new physical connection and a new ID; the predecessor's eventual close
31
+ cannot remove its replacement.
32
+
33
+ The returned object is the native `WebSocket`, with its native `send`,
34
+ `close`, `binaryType`, properties and per-socket events. Complete messages pass
35
+ through unchanged. Upstream `on`/`off` and `addListener`/`removeListener`
36
+ aliases retain native listener semantics, rather than Node EventEmitter
37
+ semantics. Importing the SDK entry starts no connection and does not replace
38
+ `globalThis.WebSocket`.
39
+
40
+ ## Observe all managed connections
41
+
42
+ `await WS.observe()` returns one lazy `event-pubsub` observer per loaded
43
+ implementation. Repeated or concurrent calls return that same observer.
44
+ Enabling observation opens no socket. Register handlers after the await, then
45
+ read `WS.getConnections()` synchronously without another await between them.
46
+
47
+ ```javascript
48
+ import {WS} from 'arcane-os/websocket-client';
49
+
50
+ const events=await WS.observe();
51
+ const liveConnections=new Map();
52
+
53
+ function trackConnection(type,connection){
54
+ if(type==='close')liveConnections.delete(connection.id);
55
+ else liveConnections.set(connection.id,connection);
56
+ console.log(type,connection.id,connection.url,connection.readyState);
57
+ }
58
+
59
+ events.on('*',trackConnection);
60
+ for(const connection of WS.getConnections()){
61
+ liveConnections.set(connection.id,connection);
62
+ }
63
+
64
+ function disposeConnectionPanel(){
65
+ events.off('*',trackConnection);
66
+ }
67
+ ```
68
+
69
+ The typed events `created`, `open`, `error` and `close` each receive one record.
70
+ The wildcard handler receives `(type,record)`. Each record contains:
71
+
72
+ | Field | Meaning |
73
+ | --- | --- |
74
+ | `id` | Numeric physical-connection identity within this loaded implementation. |
75
+ | `socket` | The exact native socket. |
76
+ | `url` | The native socket's URL. |
77
+ | `protocols` | A fresh copy of the requested, normalized protocol list. |
78
+ | `readyState` | Native state when the record is produced. |
79
+ | `event` | Original native event; `created` has `undefined`. |
80
+
81
+ `WS.getConnections()` returns fresh records with the first five fields. It
82
+ includes tracked sockets until their native close event, including a closing
83
+ predecessor after its replacement exists. Records are snapshots; the `socket`
84
+ remains live. Read a new snapshot when current state is needed.
85
+
86
+ `created` is queued in a microtask only when observation was enabled at
87
+ construction. Reusing a connection emits no second `created` event. A snapshot
88
+ can include a connection before its queued `created` notification arrives, so
89
+ reconcile by `id` rather than appending duplicates. Native close removes the
90
+ connection and its internal lifecycle listeners before global close handlers
91
+ run. Applications may still use native socket listeners alongside the global
92
+ observer; there is no second per-socket event bus.
93
+
94
+ ## Ownership and lifetime
95
+
96
+ Every caller shares the same socket, including changes to `binaryType`, a
97
+ property handler, or a call to `close()`. A component that only owns a listener
98
+ removes that exact listener. The application decides when the shared socket
99
+ should close; construction does not acquire an independent reference-counted
100
+ lifetime. Remove only your own observer handlers instead of resetting the
101
+ shared observer. Observe asynchronous handler failures at their owning caller.
102
+
103
+ All modules that should share connections use the same SDK module URL in
104
+ their realm. Separate package copies, browser realms, Workers, processes, and
105
+ upstream classic-script versus ESM loads have separate pools. The observer
106
+ reports actual native lifecycle events; it provides no closed-event history
107
+ or delivery after the realm/process ends. It adds no automatic reconnect,
108
+ heartbeat, message envelope, storage, or application routing.
109
+
110
+ ## Runtime and package delivery
111
+
112
+ The SDK's Node baseline supplies native `WebSocket`; browsers need their native
113
+ WebSocket capability. The focused entry is portable JavaScript, with the host
114
+ owning networking and native errors. Browser Workers need module resolution
115
+ provided by their host because document import maps do not apply to Workers.
116
+
117
+ The SDK selects `ws-share@3.1.0` and `event-pubsub@6.1.1`. npm resolves the
118
+ latter's `strong-type@2.0.0`, while the SDK's own direct `strong-type@2.0.1`
119
+ remains unchanged. The canonical browser projection generator copies the
120
+ installed public runtime files unchanged. Managed maps resolve the shared
121
+ constructor and event package to one URL each, and a dependency scope selects
122
+ the event package's own strong-type version. Physical, direct-installed,
123
+ packaged-document and managed test maps preserve that scope. Applications
124
+ regenerate their managed maps/runtime through the ordinary public SDK flow;
125
+ they do not copy the upstream implementation into application source.
126
+
127
+ `acceptWebSocket` remains the separate Node server-side API at
128
+ `arcane-os/websocket`. It owns accepted server connections, not this client
129
+ pool. Existing native clients and server Upgrade handling remain available.
130
+
131
+ Upstream details: [ws-share 3.1.0 release](https://github.com/RIAEvangelist/ws-share/releases/tag/3.1.0)
132
+ and [observer documentation](https://riaevangelist.github.io/ws-share/#observe).
@@ -1,8 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.1.1 — 2026-08-23
4
+
5
+ - Replaced the consumer-root-relative `../strong-type/index.js` import with the bare `strong-type` package name so Node selects event-pubsub's declared nested exact dependency under a root version conflict.
6
+ - Declared the shared `index.js` source as the explicit browser entry and made bundled plus no-build unbundled support explicit, including normal and conflict-scoped import maps.
7
+ - Added a packed-consumer regression that installs `strong-type` 1.1.0 at the consumer root, poisons that copy, instruments event-pubsub's nested 2.0.0, and proves both ESM and CommonJS execute the nested validator.
8
+ - Added packed real-Chrome consumers for the normal and conflict-scoped import maps plus a Rollup 4.62.5/node-resolve 16.0.3 bundle of the poisoned conflict layout.
9
+ - Staged the exact validator beneath event-pubsub and made the shared Chrome suite and live playgrounds resolve the bare `event-pubsub` and `strong-type` names through explicit maps.
10
+ - Added a 12-scenario Behavioral suite with Given/When/Then consumer workflows for audited delivery, nested events, lifecycle teardown, routing, synchronous replies, shared payloads, failure propagation, asynchronous side effects, live subscriber changes, and hub isolation.
11
+ - Expanded the shared host-neutral Node/Chrome registry to 131 checks and added the focused `npm run test:behavioral` command.
12
+ - Added a focused Behavioral inventory page and carried the 131-check total through CI, coverage, status, and deployment validation.
13
+
3
14
  ## 6.1.0 — 2026-08-21
4
15
 
5
- Published to npm as the current registry release from the verified `main` source. No separate 6.1.0 GitHub tag or release was created.
16
+ Published to npm and GitHub from the same verified source commit.
6
17
 
7
18
  - Added direct CommonJS `require('event-pubsub')` support while keeping one native ESM implementation for both module systems.
8
19
 
@@ -1,6 +1,6 @@
1
1
  # Migrating from event-pubsub 5.x to 6.x
2
2
 
3
- Version 6 preserves the public method names and synchronous wildcard-first model while formalizing previously inconsistent edge behavior. The 6.1.0 npm release restores the original delegated validation and package layout while adding shared-source CommonJS consumption.
3
+ Version 6 preserves the public method names and synchronous wildcard-first model while formalizing previously inconsistent edge behavior. The 6.1.0 npm release restored delegated validation and shared-source CommonJS consumption. The 6.1.1 correction makes Node resolve that validator through event-pubsub's declared dependency boundary.
4
4
 
5
5
  ## Requirements
6
6
 
@@ -23,7 +23,7 @@ Version 6 preserves the public method names and synchronous wildcard-first model
23
23
 
24
24
  ### Package layout
25
25
 
26
- The package keeps its historical ESM entry at `event-pubsub/index.js` without a restrictive export map. Applications should normally import `event-pubsub`; repository-only paths are not part of the supported API.
26
+ The package keeps its historical ESM entry at `event-pubsub/index.js` without a restrictive export map and declares that same source as its browser entry. Applications should normally import `event-pubsub`; repository-only paths are not part of the supported API.
27
27
 
28
28
  ## Behavior to review
29
29
 
@@ -53,15 +53,18 @@ Public argument checks again come from `strong-type` 2.0.0. Invalid inputs still
53
53
 
54
54
  Each `list` access creates a null-prototype object with copied handler arrays. Mutating or deleting snapshot values no longer mutates the live registry. Use `Object.hasOwn(snapshot, type)` instead of calling `snapshot.hasOwnProperty(...)`. The wildcard entry remains available through `Symbol.for('event-pubsub-all')`.
55
55
 
56
- ### Browser package imports
56
+ ### Package dependency resolution
57
57
 
58
- The 6.1.0 release restores `strong-type` 2.0.0 as the sole production dependency and preserves the deliberate `../strong-type/index.js` include shim. Node installs both packages as siblings. For an unbundled browser, serve the same sibling directories and map the public `event-pubsub` name to its `index.js`; the relative validator import then resolves without a second package-name mapping.
58
+ The 6.1.0 entry reached `strong-type` through `../strong-type/index.js`. In a consumer with an incompatible root validator and event-pubsub's exact 2.0.0 nested beneath it, that relative path could execute the root package. The 6.1.1 entry uses the bare `strong-type` name, so Node and bundlers resolve from event-pubsub outward and select its nested declared dependency first.
59
+
60
+ Browser support is explicit in both modes: bundlers resolve both package names normally, while unbundled native ESM runs the same source directly with an import map and no build step. Native browsers do not read the npm `browser` field. Put the map before the first module script, serve the app over HTTP(S), and map both `event-pubsub` and `strong-type`. When npm nests event-pubsub's validator because the root version conflicts, add an import-map scope for `./node_modules/event-pubsub/` that maps `strong-type` to `./node_modules/event-pubsub/node_modules/strong-type/index.js`. The README contains complete hoisted and conflicting-layout examples, both exercised from a packed install in real Chrome.
59
61
 
60
62
  ## Tooling cleanup
61
63
 
62
64
  - `vanilla-test` is now pinned exactly at 2.1.1.
65
+ - Rollup 4.62.5 with node-resolve 16.0.3 is exact-pinned and configured only for JavaScript as the bundled-browser conformance probe.
63
66
  - Native Node and Chrome coverage replaces c8.
64
- - The old `copyfiles` dependency is gone; a dependency-free staging script now verifies the package-shaped Node/browser layout.
67
+ - The old `copyfiles` dependency is gone; a dependency-free staging script now verifies the nested exact dependency in Node and maps that same copy for browser coverage.
65
68
  - `copyfiles`, `c8`, and the test-only `node-http-server` dependency are removed.
66
69
  - Production installs contain exactly one dependency: `strong-type` 2.0.0. They do not install `copyfiles` or the development-only `vanilla-test`.
67
70