arcane-os 0.88.1 → 0.89.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 (41) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/browser-runtime/ai/browser-decisions.mjs +31 -4
  3. package/browser-runtime/ai/core-local.mjs +105 -25
  4. package/browser-runtime/ai/decision-runtime.mjs +7 -0
  5. package/browser-runtime/ai/decision-worker.mjs +9 -1
  6. package/browser-runtime/core/client.mjs +6 -0
  7. package/browser-runtime/core/contracts.mjs +10 -0
  8. package/docs/reference/ai/browser-decisions.md +89 -7
  9. package/docs/reference/core-client.md +59 -0
  10. package/docs/reference/core-native-packaging.md +57 -10
  11. package/docs/reference/core-repositories.md +47 -3
  12. package/docs/reference/inventory/package-api.json +15 -15
  13. package/docs/reference/inventory/runtime-components.json +1 -1
  14. package/docs/reference/inventory/runtime-modules.json +4 -4
  15. package/docs/reference/local-ai.md +86 -3
  16. package/docs/reference/mcp-stdio.md +76 -1
  17. package/docs/reference/native-app-control.md +17 -5
  18. package/docs/reference/protocols.md +14 -0
  19. package/docs/reference/runtime-modules.md +9 -3
  20. package/docs/reference/sdk-api.md +135 -23
  21. package/package.json +2 -2
  22. package/runtime/arcane/modules/RiskSignalAnalyzer.js +1 -5
  23. package/schemas/arcane-app.schema.json +13 -3
  24. package/schemas/arcane-package.schema.json +10 -1
  25. package/src/app-descriptor.mjs +6 -2
  26. package/src/cli/app-control.mjs +1 -1
  27. package/src/core/hosts/windows/ArcaneHost.cs +273 -5
  28. package/src/core/hosts/windows/ArcaneLauncher.cs +9 -0
  29. package/src/core/hosts/windows/ArcaneWindowControl.cs +7 -7
  30. package/src/core/repositories.mjs +11 -1
  31. package/src/core/services/local-ai.mjs +183 -22
  32. package/src/installed-sdk-runtime.mjs +2 -0
  33. package/src/local-ai/config.mjs +16 -0
  34. package/src/local-ai/dev.mjs +2 -2
  35. package/src/local-ai/discover.mjs +3 -0
  36. package/src/local-ai/install.mjs +130 -20
  37. package/src/local-ai/llama-execution-windows.ps1 +158 -0
  38. package/src/local-ai/llama-execution.mjs +169 -0
  39. package/src/local-ai/server.mjs +38 -8
  40. package/src/native/portable-layout.mjs +13 -3
  41. package/src/workspace-runtime.mjs +97 -1
@@ -149,6 +149,37 @@ same owner with an origin-root URL, including on nested pages. It does not creat
149
149
  a second event implementation. A plain file-URL launch without this host mapping
150
150
  is not the native serving contract. No host is launched during packaging.
151
151
 
152
+ ## App-owned runtime entry
153
+
154
+ The generated `runtime/arcane-core.mjs` also accepts
155
+ `--arcane-app-entry <app-relative-module>`. This explicit mode imports the
156
+ selected file from the artifact's `app/` directory before ordinary launch-context
157
+ or service initialization. It preserves the complete original `process.argv`
158
+ and converts the native path to a file URL without treating filename characters
159
+ as URL query or fragment syntax. Missing/empty selections and actual import
160
+ failures propagate; there is no fallback to ordinary Core.
161
+
162
+ Use the existing Windows `runtime/ArcaneCore.exe` with that flag and module path
163
+ directly. On macOS, use bundled `runtime/node` with `runtime/arcane-core.mjs`
164
+ followed by the flag and module path. Portable payloads use the host's supported
165
+ Node executable with that same generated entry. The macOS artifact root is its
166
+ `.app/Contents/Resources` directory. This mode needs no new executable, SEA
167
+ loader, GUI launcher, or copied runtime.
168
+
169
+ The app includes the module and its complete source closure through
170
+ [`package.nativeResources`](#native-only-application-resources). The imported
171
+ entry starts its own top-level work, not an `import.meta.main`-guarded entry or
172
+ an automatically invoked export. It owns any explicit launch-context read,
173
+ application services, MCP tools, signals and shutdown. No ordinary Core host,
174
+ shared-host bridge, manifest services, model startup or Core pipe framing is
175
+ started by this branch. Static SDK definition imports remain available; the
176
+ ordinary parallel service imports and host lifetime stay unchanged when the
177
+ flag is absent.
178
+
179
+ For complete client command arrays, optional `--arcane-launch-config` handling,
180
+ and MCP STDIO lifetime ownership, see
181
+ [launch from a native package](mcp-stdio.md#launch-from-a-native-package).
182
+
152
183
  ## Explicit application services
153
184
 
154
185
  For the ordinary SDK build command, select native service modules in the
@@ -489,7 +520,7 @@ forced process termination.
489
520
 
490
521
  ### Initial native window size
491
522
 
492
- An application can select its initial client area and resizing behavior in
523
+ An application can select its initial client area, resizing behavior and state in
493
524
  `arcane-app.json`, independently of its page layout:
494
525
 
495
526
  ```json
@@ -499,15 +530,17 @@ An application can select its initial client area and resizing behavior in
499
530
  "icon": "img/icon.png",
500
531
  "order": 100,
501
532
  "bundledApps": [],
502
- "window": {"width": 1280, "height": 800, "resizable": true}
533
+ "window": {"width": 1280, "height": 800, "resizable": true, "state": "normal"}
503
534
  }
504
535
  }
505
536
  ```
506
537
 
507
538
  This is the `native` portion of the existing app descriptor. Each `window`
508
539
  member is optional. `width` and `height` are positive integral client dimensions
509
- in logical pixels at 96 DPI; `resizable` is a boolean. Omitting a member retains
510
- the host's existing choice, and omitting `window` preserves existing startup.
540
+ in logical pixels at 96 DPI; `resizable` is a boolean. Optional `state` is
541
+ `normal`, `maximized`, or `fullscreen`. These example dimensions are an explicit
542
+ application choice, not SDK-wide defaults. Omitting a member retains the host's
543
+ existing choice, and omitting `window` preserves existing startup.
511
544
  The native registry projection preserves the record; the browser package
512
545
  projection gains no window controls. Portable and Windows assembly copy the
513
546
  record to `arcane-native.json` as `window`, without changing page or service
@@ -517,16 +550,30 @@ The matching Windows host applies the dimensions once on initial load, using
517
550
  the display DPI and fitting the outer window within the selected monitor's
518
551
  usable work area. This fit does not impose a persistent maximum size. Explicit
519
552
  `resizable:false` uses a fixed frame and disables the maximize control; `true`
520
- permits ordinary resizing and maximizing. Later app/user resizing remains
521
- untouched. A composing launcher's `Load` handler runs after this initial sizing
522
- and can restore its own saved window state. The SDK adds no saved-bounds store.
553
+ permits ordinary resizing and maximizing. The selected initial state is applied
554
+ after this normal-window sizing, so those normal bounds remain available when
555
+ returning from maximized or fullscreen. Fullscreen uses a borderless window at
556
+ the selected monitor's full bounds, including the taskbar area, without setting
557
+ TopMost or requesting activation. Returning to normal restores the retained
558
+ normal bounds and original frame, minimize/maximize and control-box settings.
559
+ Selecting maximized from fullscreen restores them before maximizing.
560
+
561
+ Later app/user resizing remains untouched. A composing launcher's `Load` handler
562
+ runs after the initial sizing and optional state selection. Applications can
563
+ read or change their own window through
564
+ [`Arcane.window.state()` and `setState({state})`](core-client.md#current-window-state).
565
+ The application's saved Settings and their application remain application-owned;
566
+ the SDK adds no saved-state or saved-bounds store and does not delay rendering
567
+ for those settings. The window's profile, origin and Core lifetime are unchanged.
523
568
 
524
569
  Direct Windows host composition uses nullable `ArcaneHostOptions` members
525
- `InitialClientWidth`, `InitialClientHeight`, and `Resizable` with the same
526
- semantics. Older precompiled Windows hosts do not read these options; use the
570
+ `InitialClientWidth`, `InitialClientHeight`, `Resizable`, and optional string
571
+ `InitialWindowState` with the same semantics. Older precompiled Windows hosts
572
+ do not read these options; use the
527
573
  matching SDK host archive when rebuilding an application. The portable record
528
574
  is available to platform adapters; this addition implements its Windows host
529
- behavior and leaves window sizing on other platforms unchanged.
575
+ behavior and leaves window sizing/state on other platforms unchanged. The current
576
+ other-platform hosts do not implement this state selection.
530
577
 
531
578
  ### Application icon and window colors
532
579
 
@@ -47,7 +47,7 @@ const status = await repository.status({signal});
47
47
  await repository.close();
48
48
  ```
49
49
 
50
- `createRepositoryWorkspace({name,directory,dataRoot,remote,branch,longPaths,gitIdentity,onEvent,run}={})`
50
+ `createRepositoryWorkspace({name,directory,dataRoot,remote,branch,longPaths,cloneIdentity,gitIdentity,onEvent,run}={})`
51
51
  returns `{directory,open,status,configuration,pull,push,write,close,drain,dispose}`. The `directory`
52
52
  property is the absolute selected working path.
53
53
 
@@ -68,6 +68,10 @@ property is the absolute selected working path.
68
68
  writes that choice to the new repository before its first checkout. Omission
69
69
  leaves Git's configuration unchanged. The option does not rewrite an existing
70
70
  checkout, change a global setting, or alter non-Windows clone commands.
71
+ - Optional `cloneIdentity:{name?,email?}` persists explicitly selected author
72
+ fields in a newly cloned repository's local Git configuration. Omitted fields
73
+ keep Git's ordinary inheritance; existing checkouts retain their configuration.
74
+ See [clone-local author selection](#clone-local-author-selection).
71
75
  - `onEvent` and optional `run` use the existing SDK process owner. The default
72
76
  adapter is `runProcess`; Git must be available on PATH. Credentials and
73
77
  authentication remain with Git and the native host. The SDK adds no download,
@@ -304,8 +308,8 @@ Each supplied name/email becomes command-local `-c user.name=...` or
304
308
  `GIT_COMMITTER_*` fields. Those selected fields take precedence over inherited
305
309
  author/committer settings and environment values; omitted fields, dates and
306
310
  unrelated environment remain inherited. Git retains its ordinary commit
307
- identity formatting and may reject an unusable identity. The SDK changes
308
- neither `process.env` nor global/local Git files.
311
+ identity formatting and may reject an unusable identity. This command-local
312
+ selection changes neither `process.env` nor global/local Git files.
309
313
 
310
314
  `username` supplies only command-local `credential.username`. Git's existing
311
315
  credential helper and remote protocol still own actual authentication. A
@@ -322,6 +326,46 @@ fetch. Existing arguments, content, output, errors, queue and cancellation
322
326
  semantics remain unchanged. Other repository helpers and CLI cwd defaults keep
323
327
  their existing behavior.
324
328
 
329
+ ### Clone-local author selection
330
+
331
+ `createRepositoryWorkspace` also accepts `cloneIdentity:{name?,email?}` when the
332
+ application wants its explicit selection retained by a newly owned clone:
333
+
334
+ ```javascript
335
+ const repository = createRepositoryWorkspace({
336
+ name: 'moon-cheese-dispatches',
337
+ remote: applicationConnection.remote,
338
+ cloneIdentity: {name: 'Moon Dispatcher', email: 'moon@example.invalid'}
339
+ });
340
+ await repository.open({signal});
341
+ ```
342
+
343
+ Each supplied field becomes `clone --config user.name=<name>` or
344
+ `clone --config user.email=<email>`. Git writes these choices to the new local
345
+ configuration before the initial checkout, on every supported platform. This
346
+ adds no separate configuration command or post-clone initialization. It applies
347
+ whenever the owner's first preparing operation needs a clone, including `write`.
348
+
349
+ The exact strings are captured during I/O-free construction. Empty strings are
350
+ explicit values; omitted or `undefined` fields add no setting, preserving Git's
351
+ global inheritance, includes and any template configuration. Later caller edits
352
+ do not alter an accepted owner. Non-string fields, NUL and text that the native
353
+ process transport cannot represent report `TypeError` instead of changing input.
354
+ Git retains ordinary configuration precedence, author formatting and failures.
355
+
356
+ An existing checkout is never reconfigured by `cloneIdentity`, even when it is
357
+ supplied. Changing or omitting the selection on a later owner does not remove a
358
+ previously stored local setting. This option changes no global configuration,
359
+ credentials, username selection, branch or remote. A failed or cancelled clone
360
+ retains its actual diagnostics and any partial directory for the caller.
361
+
362
+ `gitIdentity` remains the separate command-local selection described above.
363
+ When both options are supplied, `cloneIdentity` selects the new clone's saved
364
+ name/email while `gitIdentity` controls the selected child commands and their
365
+ author/committer environment. Application defaults and per-repository choices
366
+ remain application-owned. Neither option identifies the authenticated GitHub
367
+ account or establishes who submitted a historical record.
368
+
325
369
  ## Write, commit and push selected text
326
370
 
327
371
  ```javascript
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "sdkVersion": "0.87.0",
3
+ "sdkVersion": "0.88.1",
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",
@@ -94,7 +94,7 @@
94
94
  "summary": "Connects to one explicit running native window for background document inspection, viewport capture, targeted DOM actions, fixed keys and Normal-window client resizing.",
95
95
  "availability": "Node local IPC on Windows, Linux and macOS; the current window adapter is Windows WebView2, with other native adapters unavailable",
96
96
  "protocol": "arcane/1 app.control.status, app.control.inspect, app.control.capture, app.control.act, app.control.key and app.control.resize over the existing framed local connection. The window host selects native.launchContext.appControl.endpoint and preserves its existing Core child, origin, profile and lifetime; assembly requires a matching published Windows host asset.",
97
- "normalization": "Returns endpoint, closed, status(options), inspect(parameters={},options), capture(parameters={},options), act(parameters,options), key(parameters,options), resize(parameters,options), and close(). Parameters pass unchanged; per-operation options preserve Core signal/timeout behavior. Status reports actual app/window/document readiness. Inspect returns complete selected DOM HTML/text and live control state with DOM-derived role/name hints, plus top-level viewport CSS dimensions/devicePixelRatio, documentHasFocus and focusPath through the active open-shadow branch; focus records are observations with null selectors, separate from the selected root's activeElement. Optional shadowPath on inspect and act is an ordered array of unique CSS host selectors through open shadow roots; selector resolves in the final root, with omitted/empty paths retaining top-level light DOM. Whole-shadow-root inspection returns complete innerHTML/textContent with null selector/state; nested shadow-host descriptors provide their full path. Frames remain descriptors and closed roots are unavailable. Capture returns mimeType image/png, complete base64 data, documentGeneration and URL for the rendered viewport. Act requires documentGeneration and one CSS selector with click, exact-string fill, exact-value select or absolute-offset scroll. Key requires documentGeneration and Tab, Enter or Space; shiftKey:true is supported only for Tab. One keyboard owner keeps each press/release pair ordered at the selected WebView's current focus. An attempted press owns a matching release even after cancellation, navigation or failure; no failed or ambiguous press is replayed. Generation checks bracket native input without atomic document binding. Resize accepts positive integral native client width/height supported by WinForms and optional documentGeneration, changes only a Normal window's ClientSize, and returns immediate previous/actual native dimensions plus the later observed CSS viewport. Native and CSS snapshots are not atomic; platform constraints may change actual dimensions. Restoration is an explicit resize using the operation's previous native dimensions; unsupported window states fail before mutation. The native document marker prevents queued DOM actions crossing replacement; cancellation before dispatch prevents execution while dispatched work is observed through completion without retry or reversal, including owned key release. Partial results remain in actual errors and complete later responses remain in host diagnostics after cancellation or disconnection. Dispatch completion does not establish application handler completion or a successful business action. close() cancels only this connection, never the application or Core. No launch, automatic reconnect, desktop activation, global input or public arbitrary evaluator. CLI inspection accepts either --selector or a complete --request JSON object; act, key and resize forward their complete --request parameter objects. See native-app-control.md for complete result fields, CLI and platform scope."
97
+ "normalization": "Returns endpoint, closed, status(options), inspect(parameters={},options), capture(parameters={},options), act(parameters,options), key(parameters,options), resize(parameters,options), and close(). Parameters pass unchanged; per-operation options preserve Core signal/timeout behavior. Status reports actual app/window/document readiness and window state Normal, Maximized, Minimized or Fullscreen. Inspect returns complete selected DOM HTML/text and live control state with DOM-derived role/name hints, plus top-level viewport CSS dimensions/devicePixelRatio, documentHasFocus and focusPath through the active open-shadow branch; focus records are observations with null selectors, separate from the selected root's activeElement. Optional shadowPath on inspect and act is an ordered array of unique CSS host selectors through open shadow roots; selector resolves in the final root, with omitted/empty paths retaining top-level light DOM. Whole-shadow-root inspection returns complete innerHTML/textContent with null selector/state; nested shadow-host descriptors provide their full path. Frames remain descriptors and closed roots are unavailable. Capture returns mimeType image/png, complete base64 data, documentGeneration and URL for the rendered viewport. Act requires documentGeneration and one CSS selector with click, exact-string fill, exact-value select or absolute-offset scroll. Key requires documentGeneration and Tab, Enter, Space or Escape; shiftKey:true is supported only for Tab. One keyboard owner keeps each press/release pair ordered at the selected WebView's current focus. An attempted press owns a matching release even after cancellation, navigation or failure; no failed or ambiguous press is replayed. Generation checks bracket native input without atomic document binding. Resize accepts positive integral native client width/height supported by WinForms and optional documentGeneration, changes only a Normal window's ClientSize, and returns immediate previous/actual native dimensions plus the later observed CSS viewport. Native and CSS snapshots are not atomic; platform constraints may change actual dimensions. Restoration is an explicit resize using the operation's previous native dimensions; Maximized, Minimized and Fullscreen states fail before size mutation. The native document marker prevents queued DOM actions crossing replacement; cancellation before dispatch prevents execution while dispatched work is observed through completion without retry or reversal, including owned key release. Partial results remain in actual errors and complete later responses remain in host diagnostics after cancellation or disconnection. Dispatch completion does not establish application handler completion or a successful business action. close() cancels only this connection, never the application or Core. No launch, automatic reconnect, desktop activation, global input or public arbitrary evaluator. CLI inspection accepts either --selector or a complete --request JSON object; act, key and resize forward their complete --request parameter objects. See native-app-control.md for complete result fields, CLI and platform scope."
98
98
  },
99
99
  {
100
100
  "id": "local-ai-execution-devices:createExecutionDeviceCatalog",
@@ -311,14 +311,14 @@
311
311
  "name": "createRepositoryWorkspace",
312
312
  "displayName": "createRepositoryWorkspace()",
313
313
  "kind": "function",
314
- "signature": "createRepositoryWorkspace({name,directory,dataRoot,remote,branch,longPaths,gitIdentity,onEvent,run=runProcess}={})",
314
+ "signature": "createRepositoryWorkspace({name,directory,dataRoot,remote,branch,longPaths,cloneIdentity,gitIdentity,onEvent,run=runProcess}={})",
315
315
  "entrypoints": ["arcane-os/core/repositories"],
316
316
  "primaryImport": "arcane-os/core/repositories",
317
317
  "group": "Native repository workspaces",
318
318
  "summary": "Owns one selected native working checkout with ArcaneData/Repos defaults and existing Git/process lifecycle.",
319
319
  "availability": "Node with Git on Windows, Linux and macOS; Android requires host process adaptation",
320
320
  "protocol": "Git working repository and SDK process adapter",
321
- "normalization": "Returns directory, open, status, configuration, pull, push, write and identical close/drain/dispose hooks. Only missing or empty destinations clone; existing roots are preserved. Optional longPaths captures a boolean: explicit Windows true/false supplies clone-local core.longpaths before the first checkout, persisting only in the new repository under ordinary Git configuration precedence. Omission preserves configuration; existing repositories and non-Windows clone commands remain unchanged. No global setting or migration. configuration observes the existing root, symbolic HEAD, origin URL/pushURL arrays and branch remote/merge/upstream URL arrays without clone/init/fetch, preserving unset, empty and repeated values. Explicit paths remain selected. Same-directory operations serialize within the process; cancellation and shutdown join accepted process work. pull/push/write accept a captured target {remote,ref} for current-branch fast-forward pull or non-force current-HEAD selected-ref push; omission retains ordinary configured behavior. write snapshots exact text and paths, stages/commits only literal owned paths and preserves unrelated staged files and Git filters. Complete outputs and causal failures retain confirmed local/committed/pushed and uncertain outcomes plus the supplied target. Optional gitIdentity captures name/email/username for child Git operations only; omitted fields remain inherited and identity selection writes no config file."
321
+ "normalization": "Returns directory, open, status, configuration, pull, push, write and identical close/drain/dispose hooks. Only missing or empty destinations clone; existing roots are preserved. Optional longPaths captures a boolean: explicit Windows true/false supplies clone-local core.longpaths before the first checkout, persisting only in the new repository under ordinary Git configuration precedence. Omission preserves configuration; existing repositories and non-Windows clone commands remain unchanged. No global setting or migration. configuration observes the existing root, symbolic HEAD, origin URL/pushURL arrays and branch remote/merge/upstream URL arrays without clone/init/fetch, preserving unset, empty and repeated values. Explicit paths remain selected. Same-directory operations serialize within the process; cancellation and shutdown join accepted process work. pull/push/write accept a captured target {remote,ref} for current-branch fast-forward pull or non-force current-HEAD selected-ref push; omission retains ordinary configured behavior. write snapshots exact text and paths, stages/commits only literal owned paths and preserves unrelated staged files and Git filters. Complete outputs and causal failures retain confirmed local/committed/pushed and uncertain outcomes plus the supplied target. Optional gitIdentity captures name/email/username for child Git operations only; omitted fields remain inherited and identity selection writes no config file. Optional cloneIdentity captures exact name/email strings and persists only supplied fields through clone --config before the first checkout on every supported platform. Empty strings are explicit; omitted or undefined fields preserve inherited/template configuration. Existing checkouts are never reconfigured. Unrepresentable process arguments fail honestly without transforming them. This option changes no global configuration or credentials and does not identify an authenticated account; command-local gitIdentity remains independent."
322
322
  },
323
323
  {
324
324
  "id": "core-repositories:readGitIdentity",
@@ -563,14 +563,14 @@
563
563
  "name": "createCoreLocalAIProvider",
564
564
  "displayName": "createCoreLocalAIProvider()",
565
565
  "kind": "function",
566
- "signature": "createCoreLocalAIProvider({client,id='llama.cpp'}={})",
566
+ "signature": "createCoreLocalAIProvider({client,id='llama.cpp',prepareModel}={})",
567
567
  "entrypoints": ["arcane-os/ai/core-local"],
568
568
  "primaryImport": "arcane-os/ai/core-local",
569
569
  "group": "Core local AI",
570
570
  "summary": "Browser llama.cpp provider through an explicitly connected Core, with application-owned model selection and complete response streaming.",
571
571
  "availability": "Browser with an available Core local AI service; construction installs no runtime or model",
572
572
  "protocol": "arcane-ai-provider/2 LLM lifecycle over arcane/1",
573
- "normalization": "Preserves complete payloads and original parsed chunks; waits for the exact requested model and cancels on observed readiness loss. Omitted client follows installation/replacement/retirement; explicit client including null stays fixed. Inspect reports load capability separately from loaded readiness; disposal releases only an actually retained selection through its captured client. Provider/2 role state reconciles at existing operation boundaries, not through an idle state subscription."
573
+ "normalization": "Preserves complete payloads and original parsed chunks; load waits for the exact requested model and inference cancels on observed readiness loss. Omitted client follows installation/replacement/retirement; explicit client including null stays fixed. Optional prepareModel({selection,signal,progress}) runs at load and returns assetProjectionId, resourcePaths, executionTarget and optional release; load({selection,signal,progress,assetProjectionId,resourcePaths,executionTarget}) lets defined direct fields override preparation, including an explicit null target. Preparation release is awaited on success, failure and cancellation, including late preparation results. Inspect reports managed preparation/load capability separately from readiness. Only the current successfully completed load accepts readiness and inference; failed same-name replacement cannot reuse prior readiness. status().execution forwards complete native execution diagnostics. Unload releases the last dispatched selection only while its owning connection remains current; disposal uses its captured client. Preparation alone creates no native release ownership. Provider/2 role state reconciles at existing operation boundaries, not through an idle state subscription."
574
574
  },
575
575
  {
576
576
  "id": "core-local-ai:createLocalAIService",
@@ -584,7 +584,7 @@
584
584
  "summary": "Composes selected native llama.cpp, Ollama and ONNX engines as a Core service with retained process/worker ownership and model lifecycle.",
585
585
  "availability": "Node with selected upstream runtime distributions or available configured system services",
586
586
  "protocol": "arcane/1 methods and events; llama.cpp OpenAI HTTP/SSE, Ollama native HTTP/NDJSON and ONNX tensor transport",
587
- "normalization": "Application-owned runtime and model requirements; optional prepare supplies installed records during this service startup. Optional executionDevices supplies a borrowed catalog to ONNX; its supplying owner retains catalog disposal responsibility. onnx.load forwards executionPreference and executionTarget with existing model-path resolution, cancellation and omitted-target behavior. getONNXRuntime() returns the actual prepared ONNX owner for native composition after context.getService('local-ai'); callers reacquire after recovery and retain the acquired handle for cleanup. llama.status/models observe without starting or restarting an engine. Independent services, complete responses and exact owned-process cleanup remain unchanged."
587
+ "normalization": "Application-owned runtime and model requirements; optional prepare supplies installed records during this service startup. Optional executionDevices supplies a borrowed catalog to ONNX and llama.cpp; its supplying owner retains catalog disposal responsibility. onnx.load forwards executionPreference and executionTarget with existing model-path resolution, cancellation and omitted-target behavior. getONNXRuntime() returns the actual prepared ONNX owner for native composition after context.getService('local-ai'); callers reacquire after recovery and retain the acquired handle for cleanup. llama.load({model,assetProjectionId,resourcePaths,executionTarget}) retains resourcePaths.model from the existing model-assets owner through actual managed process exit. Omitted target retains the same model's selection or configured default; null requests the runtime default. Explicit Windows NVIDIA targets resolve this host's deviceId to its CUDA device identity; unsupported or unavailable selections report their actual failure without substitution. execution distinguishes requested/resolved/configured targets from observedTarget:null. Existing external listeners retain their model/device ownership and reject managed reconfiguration. llama.status/models observe without starting or restarting an engine. Independent services, complete responses and exact owned-process cleanup remain unchanged."
588
588
  },
589
589
  {
590
590
  "id": "core-local-ai:default",
@@ -654,7 +654,7 @@
654
654
  "summary": "Explicit-load Laya and Julia-1 decisions with caller-selected precision, complete inputs and graph results.",
655
655
  "availability": "Browser module Workers and the selected Transformers.js backend; import/construction starts no model load",
656
656
  "protocol": "Caller-owned state/question/options rows; five-input decision graph; native Worker request/result lifecycle",
657
- "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 the complete selected runtime entry and actual loader resources through fetchResource outside cloned Worker configuration. Each activation materializes unchanged entry source with JavaScript MIME and retains its original configured URL for later loads; its object URL is revoked after Worker termination. Stored mode disables competing model caches, preserves complete HTTP responses and joins runtime-entry/resource cleanup before outstanding calls settle. Runtime-entry HTTP failures retain error.response. Custom stored entries must support an object URL and changed import.meta.url; native absolute dependencies and custom factory imports outside upstream preloads remain browser-owned. Omission retains direct native module and upstream model loading; no decision-result persistence or precision fallback"
657
+ "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. By default stored activation also materializes the unchanged runtime entry as a JavaScript object URL, retains its configured URL for later loads and revokes the object URL after Worker termination. Explicit runtime.local:true instead imports same-deployment moduleUrl and wasmPaths.mjs/wasm directly, defaulting to ai/decisions-runtime installed by browserDecisions:true materialization; only those exact executable URLs bypass the model store. It disables upstream executable WASM caching/proxy and selects one WASM thread, preserving the requested inference backend. Stored mode disables competing model caches, preserves complete HTTP responses and joins resource cleanup before outstanding calls settle. Default runtime-entry HTTP failures retain error.response; custom stored entries must support an object URL and changed import.meta.url. Omission retains existing browser behavior; no decision-result persistence or precision fallback"
658
658
  },
659
659
  {
660
660
  "id": "root:acceptWebSocket",
@@ -1734,7 +1734,7 @@
1734
1734
  "name": "materializeInstalledSdkRuntime",
1735
1735
  "displayName": "materializeInstalledSdkRuntime()",
1736
1736
  "kind": "function",
1737
- "signature": "async materializeInstalledSdkRuntime({workspaceRoot,sdkPackageSource,workspaceOperationLease,signal,onEvent}={})",
1737
+ "signature": "async materializeInstalledSdkRuntime({workspaceRoot,sdkPackageSource,browserDecisions=false,workspaceOperationLease,signal,onEvent}={})",
1738
1738
  "entrypoints": [
1739
1739
  "arcane-os"
1740
1740
  ],
@@ -1743,14 +1743,14 @@
1743
1743
  "summary": "Materializes the complete installed SDK runtime and browser runtime, then replaces semantic arcane.lock.json metadata from the actual installed dependency and package.",
1744
1744
  "availability": "Node",
1745
1745
  "protocol": "Installed SDK runtime materialization",
1746
- "normalization": "Complete installed runtime content and a mutable materialization result"
1746
+ "normalization": "Complete installed runtime content and a mutable materialization result. Explicit browserDecisions:true acquires the upstream bundled Transformers.js 4.3.0 and matched ONNX Runtime 1.31.0-dev.20260914-8d85527a0 asyncify module/WASM plus licenses into arcane/sdk/ai/decisions-runtime before replacement. The optional workspaceRuntime.browserDecisions result contains directory, versions and filenames. Default false performs no optional acquisition. Progress counts completed files; signal, HTTP and observer failures abort/join acquisition and preserve the previous projection. Select the option on each refresh that should retain the closure; model/tokenizer data remain upstream/cache-owned"
1747
1747
  },
1748
1748
  {
1749
1749
  "id": "root:materializeWorkspaceRuntimeContent",
1750
1750
  "name": "materializeWorkspaceRuntimeContent",
1751
1751
  "displayName": "materializeWorkspaceRuntimeContent()",
1752
1752
  "kind": "function",
1753
- "signature": "async materializeWorkspaceRuntimeContent({workspaceRoot,runtimeRoot=path.join(getSdkRoot(), 'runtime'),browserRuntimeRoot=getSdkBrowserRuntimeRoot(),signal,onEvent}={})",
1753
+ "signature": "async materializeWorkspaceRuntimeContent({workspaceRoot,runtimeRoot=path.join(getSdkRoot(), 'runtime'),browserRuntimeRoot=getSdkBrowserRuntimeRoot(),sdkVersion=SDK_VERSION,browserDecisions=false,signal,onEvent}={})",
1754
1754
  "entrypoints": [
1755
1755
  "arcane-os"
1756
1756
  ],
@@ -1759,7 +1759,7 @@
1759
1759
  "summary": "Replaces one workspace's complete projected runtime and browser-runtime content from selected source roots.",
1760
1760
  "availability": "Node",
1761
1761
  "protocol": "Workspace runtime materialization",
1762
- "normalization": "Complete workspace runtime content and a mutable materialization result"
1762
+ "normalization": "Complete workspace runtime content and a mutable materialization result. browserDecisions:true adds the same explicit upstream browser executable acquisition as materializeInstalledSdkRuntime; the optional browserDecisions result contains directory, transformersVersion, onnxRuntimeVersion and files. Default false leaves acquisition disabled. Complete distribution content is copied without source rewriting, with cancellation/error cleanup before staged replacement and no model/tokenizer packaging"
1763
1763
  },
1764
1764
  {
1765
1765
  "id": "root:NATIVE_BUILD_PLAN_PROTOCOL",
@@ -4360,7 +4360,7 @@
4360
4360
  "summary": "Projects a Core client into the established application-facing Arcane namespace.",
4361
4361
  "availability": "Browser and native WebView JavaScript hosts with a Core client",
4362
4362
  "protocol": "arcane/1 compatibility facade",
4363
- "normalization": "Routes namespace methods through client.invoke while retaining operation-specific parameters, correlation, streaming callbacks, timeouts and cancellation; window.setTheme(presentation,{signal}?) forwards the complete per-window backgroundColor/textColor record, with omitted fields unchanged and null reset, and returns the host's actual applied/unsupported result without changing system appearance"
4363
+ "normalization": "Routes namespace methods through client.invoke while retaining operation-specific parameters, correlation, streaming callbacks, timeouts and cancellation; window.state({signal}?) reads the requesting window and window.setState({state},{signal}?) selects normal, maximized or fullscreen. The matching Windows host returns {platform:'windows',supported:true,state}, including observed minimized state, restores normal bounds/frame controls on leaving fullscreen and prevents activation/focus assignment during visible state transitions without changing TopMost, Core, origin or profile. Saved Settings remain application-owned; cancellation does not undo an applied change and unavailable hosts retain their actual errors without fallback. window.setTheme(presentation,{signal}?) forwards the complete per-window backgroundColor/textColor record, with omitted fields unchanged and null reset, and returns the host's actual applied/unsupported result without changing system appearance"
4364
4364
  },
4365
4365
  {
4366
4366
  "id": "core-client:installCoreClient",
@@ -4500,7 +4500,7 @@
4500
4500
  "summary": "Creates the SDK-owned provider that assembles one selected app and explicit services into a portable Core payload.",
4501
4501
  "availability": "Node; produces a portable payload rather than an executable host",
4502
4502
  "protocol": "arcane-native-builder/1 portable provider",
4503
- "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. Generated coreListener opt-in composes one passive model-observation service from the existing host.runtime and actual services exposing localai.status/image.status/decisions.status before listener binding, with absent owners null and renderer attachment separate. Ordinary stdio and sharedHost/headless behavior remains unchanged; no model creation, getService or readiness barrier. Reports executable:false and never substitutes for an unavailable platform host"
4503
+ "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, optional resizable and optional state normal/maximized/fullscreen into manifest.window with omitted fields preserved; the selected platform host owns the applied behavior. Generated coreListener opt-in composes one passive model-observation service from the existing host.runtime and actual services exposing localai.status/image.status/decisions.status before listener binding, with absent owners null and renderer attachment separate. Ordinary stdio and sharedHost/headless behavior remains unchanged; no model creation, getService or readiness barrier. Generated runtime/arcane-core.mjs accepts --arcane-app-entry <app-relative-module> through the host's supported Node executable. It imports the packaged app/ module's top-level work with complete process.argv unchanged and no automatic export call; the app owns launch-context reads, services, signals and shutdown. Include the entry and complete source closure through package.nativeResources. The flag skips ordinary Core startup; omission retains it. See mcp-stdio.md#launch-from-a-native-package and core-native-packaging.md#app-owned-runtime-entry. Reports executable:false and never substitutes for an unavailable platform host"
4504
4504
  },
4505
4505
  {
4506
4506
  "id": "native-portable-provider:arcaneNativeBuilderProvider",
@@ -4542,7 +4542,7 @@
4542
4542
  "summary": "Assembles selected application services and browser content with the SDK-owned Windows x64 WebView2 and SEA host.",
4543
4543
  "availability": "Node for assembly; Windows x64 with WebView2 Runtime for execution",
4544
4544
  "protocol": "arcane-native-builder/1 windows-x64 provider",
4545
- "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"
4545
+ "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, optional resizable and optional state normal/maximized/fullscreen through manifest.window. The matching host scales dimensions to actual DPI, fits initial placement to the usable screen and applies an explicit state afterward; omitted fields preserve existing host behavior. Fullscreen fills the selected monitor without a frame and retains normal bounds/frame controls for restoration. Requesting-window window.state/window.setState observe actual minimized/maximized state and prevent activation/focus assignment during visible transitions without selecting TopMost or changing Core, origin or profile. Saved Settings remain application-owned; 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. Packaged runtime/ArcaneCore.exe accepts --arcane-app-entry <app-relative-module> directly, without a script argument or GUI launcher; it imports the packaged app/ module's top-level work, preserves complete process.argv and invokes no export. Include the entry and complete source closure through package.nativeResources. The app owns launch-context reads, services, signals and shutdown; the flag skips ordinary Core startup, while omission preserves it. See mcp-stdio.md#launch-from-a-native-package and core-native-packaging.md#app-owned-runtime-entry"
4546
4546
  },
4547
4547
  {
4548
4548
  "id": "native-windows-provider:arcaneNativeBuilderProvider",
@@ -981,7 +981,7 @@
981
981
  ],
982
982
  "availability": "Browser and supported native WebViews",
983
983
  "transport": "HTMLImport + DOM; injected Arcane/provider modules where listed",
984
- "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
+ "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; pending choices expose aria-disabled and ignore repeated activation while staying in the keyboard focus flow; completion and failure do not move focus, and an unavailable Skin choice retains native disabling"
985
985
  },
986
986
  {
987
987
  "file": "runtime/arcane/components/unified-inbox.html",
@@ -30,8 +30,8 @@
30
30
  "summary": "Owns provider-selectable chat and the one-time caller-authority browser STT/TTS configuration, lifecycle, synthesis, transcription, and playback boundary.",
31
31
  "availability": "Browser + native bridge + TWiN Cloud",
32
32
  "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",
33
- "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.",
34
- "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(); async `readPreparedTTS({parts,textFormat,storage,identity,signal,onState})` returning a complete ready handle or null through the same semantic matcher, with no activation, unmute, synthesis, playback, storage writes, or partial repair; manifest, audio availability, and returned-handle reads use DBOPFS createTable:false so absent tables remain absent; supplied adapters own that read-existing behavior; missing matches/audio return null, malformed metadata and storage failures reject, and omitted storage sees only completed matching same-AI in-memory audio; hits replay ready state, misses emit nothing, and the lookup signal detaches on settlement while returned-handle cancellation stays independent; `prepareAudioOutput()` returning the persistent WebAudio handle {kind,state,contextState,error,subscribe,resume,dispose} without resume, unmute, model load, sources, sound, or gesture listeners; current-state replay and output-specific canonical `AI_AUDIO_OUTPUT_STATE_EVENT` (`ai-audio-output-state`), with ready only for an actually running context; explicit owner disposal observes context-bound cleanup and close; `playPreparedTTS(prepared,{signal,onState,audioOutput})` returning state, error, finished, pause(), resume(), and stop() with one playback lane per AI and observational state/error callbacks; explicit live same-AI audioOutput borrows its context until owner disposal, while omission or null preserves per-playback terminal close; 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`."
33
+ "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. High-level LLM selection defaults to eager activation; startLanguageModel:false preserves the selected tuple and registered adapters without preloading through later readiness callbacks. Caller cancellation waits for accepted old-role cleanup and prevents later configuration/preload, or reaches the first owned load after selection commits. Newer accepted selections supersede older continuations; deferred or cancelled activation stays inactive until explicit load/startup, and a caller abort after successful settlement never unloads the ready model. Separate callers joining an existing runtime load retain their current cancellation ownership.",
34
+ "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(); async `readPreparedTTS({parts,textFormat,storage,identity,signal,onState})` returning a complete ready handle or null through the same semantic matcher, with no activation, unmute, synthesis, playback, storage writes, or partial repair; manifest, audio availability, and returned-handle reads use DBOPFS createTable:false so absent tables remain absent; supplied adapters own that read-existing behavior; missing matches/audio return null, malformed metadata and storage failures reject, and omitted storage sees only completed matching same-AI in-memory audio; hits replay ready state, misses emit nothing, and the lookup signal detaches on settlement while returned-handle cancellation stays independent; `prepareAudioOutput()` returning the persistent WebAudio handle {kind,state,contextState,error,subscribe,resume,dispose} without resume, unmute, model load, sources, sound, or gesture listeners; current-state replay and output-specific canonical `AI_AUDIO_OUTPUT_STATE_EVENT` (`ai-audio-output-state`), with ready only for an actually running context; explicit owner disposal observes context-bound cleanup and close; `playPreparedTTS(prepared,{signal,onState,audioOutput})` returning state, error, finished, pause(), resume(), and stop() with one playback lane per AI and observational state/error callbacks; explicit live same-AI audioOutput borrows its context until owner disposal, while omission or null preserves per-playback terminal close; 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`. `setAI(llmService,sttService,ttsService,model,modelTTS,modelSTT,options)` and `transitionAI(llmService,sttService,ttsService,model,modelTTS,modelSTT,options)` preserve six positional preferences; `configureProviders(selections,options)` and `transitionProviders(selections,options)` accept the same optional {signal:null,startLanguageModel:true} defaults. setAI retains its synchronous boolean and configureProviders its synchronous route result. Explicit `providerRuntime.load('llm',{signal})` or `startProviders({startLanguageModel:true,signal})` activates a deferred selected route."
35
35
  },
36
36
  {
37
37
  "file": "runtime/arcane/modules/AIModelSelectionController.js",
@@ -133,7 +133,7 @@
133
133
  "summary": "Sticky mutable LLM, speech-to-text, and text-to-speech lifecycle snapshots, intents, subscriptions, and startup-settlement barriers.",
134
134
  "availability": "Cross-host in-process state contract; actual role readiness remains provider specific",
135
135
  "protocol": "arcane-ai-runtime-state/1 over the per-realm globalThis.arcaneEvents authority",
136
- "normalization": "Closed monotonic sticky role snapshots, transient lifecycle intents, synchronous current-state replay, AbortSignal cleanup, and startup settlement are normalized; unloaded recovery/error may retain the exact busy operation until settlement, without readiness for new inference.",
136
+ "normalization": "Closed monotonic sticky role snapshots, transient lifecycle intents, synchronous current-state replay, AbortSignal cleanup, and startup settlement are normalized; unloaded recovery/error may retain the exact busy operation until settlement, without readiness for new inference. During unloading, an already loaded role may retain busy:true with its actual operationId while captured requests settle; this grants no readiness or new-request permission and is not a continuesAIRuntimeOperation recovery state.",
137
137
  "surface": "Protocol/event/role/state constants; snapshot, state subscribe/publish, intent request/subscribe, continuesAIRuntimeOperation(previous,current,operationId) for the real continuing runtime operation, and `startAIRuntime({startMuted,startTranscription,signal})` returning `{barrier,settled,cancel}`."
138
138
  },
139
139
  {
@@ -1201,7 +1201,7 @@
1201
1201
  "summary": "Matches configured risk signals and levels against complete text.",
1202
1202
  "availability": "Cross-host",
1203
1203
  "protocol": "In-process only",
1204
- "normalization": "Fully normalized.",
1204
+ "normalization": "Complete literal text; existing non-string conversion only.",
1205
1205
  "surface": "`DEFAULT_LEVELS`, `analyzeRiskSignals()`."
1206
1206
  },
1207
1207
  {
@@ -86,7 +86,8 @@ transport loss retain their owning cancellation behavior. A terminal engine
86
86
  failure reaches the original request as its actual error rather than a
87
87
  fabricated readiness success. See [native speech reconciliation](native-speech.md).
88
88
 
89
- `llamaCpp` accepts `url`, `model`, `modelsDirectory`, and an `args` string array.
89
+ `llamaCpp` accepts `url`, `model`, `alias`, `modelsDirectory`, `executionTarget`,
90
+ and an `args` string array.
90
91
  `model` selects a single GGUF file; `modelsDirectory` selects llama.cpp router
91
92
  mode. Paths resolve from the application root. Ollama accepts `url`,
92
93
  `modelsDirectory`, and `args`. The default endpoints are
@@ -94,6 +95,24 @@ mode. Paths resolve from the application root. Ollama accepts `url`,
94
95
  directories are `.arcane/models/llama.cpp` and `.arcane/models/ollama` inside the
95
96
  application. Applications choose and provision their models explicitly.
96
97
 
98
+ A llama runtime requirement accepts `backend: 'auto' | 'cpu' | 'cuda'`, with
99
+ `auto` preserving the ordinary platform distribution. Explicit `cpu` also
100
+ configures CPU execution, including on a Metal-capable macOS distribution.
101
+ `cuda` currently selects the official Windows x64 CUDA 13.4 engine and its
102
+ matching same-release CUDA companion archive. For example:
103
+
104
+ ```json
105
+ {"runtimes": [{"id": "llama.cpp", "backend": "cuda"}]}
106
+ ```
107
+
108
+ Development prepares this explicit distribution instead of treating an unknown
109
+ PATH executable or external listener as proof of its backend. Native packaging
110
+ bundles the selected runtime tree and companion libraries through the existing
111
+ runtime workflow. A custom CUDA `url` also needs its explicit `companionUrl`;
112
+ the SDK does not guess a companion location. Installation records distinguish
113
+ the distribution's `backend` from its `requestedBackend` execution mode.
114
+ Selecting an unavailable distribution reports its actual error.
115
+
97
116
  ## Development
98
117
 
99
118
  ```sh
@@ -226,7 +245,7 @@ background health polling.
226
245
  | --- | --- | --- |
227
246
  | `llama.status` | `{}` | Runtime state and model catalog |
228
247
  | `llama.models` | `{}` | `{models}` |
229
- | `llama.load` | `{model}` | State after the exact model reports loaded |
248
+ | `llama.load` | `{model,assetProjectionId?,resourcePaths?,executionTarget?}` | State after the exact model reports loaded |
230
249
  | `llama.unload` | `{model}` | Current state after release |
231
250
  | `llama.chat` | `{model,payload,stream?,streamId?}` | Complete OpenAI-shaped completion |
232
251
 
@@ -238,6 +257,36 @@ only after Core successfully closes its owned single-model server. Explicit
238
257
  load, inference or recovery can resume that engine; observation cannot.
239
258
  Status observation adds no background polling.
240
259
 
260
+ For a saved browser GGUF, prepare its complete file through the existing
261
+ [`model-assets` owner](model-assets.md), then call `llama.load` with its
262
+ `assetProjectionId` and `resourcePaths: {model: 'selected.gguf'}`. Core retains
263
+ that member before replacing its owned server, uses the exact `model` value as
264
+ the server alias, and releases the retained use only after actual process exit.
265
+ The preparation owner can release its use when loading settles. Replacement,
266
+ cancelled loading, unload and shutdown all preserve this lifetime. A cancelled
267
+ projected load joins process shutdown before returning. Ordinary router loads
268
+ retain their existing shared-load behavior.
269
+
270
+ `executionTarget: {deviceId}` uses the identifier from this computer's execution
271
+ device catalog. Windows CPU selection disables GPU offload; Windows NVIDIA GPU
272
+ selection requires the CUDA distribution and resolves the current adapter LUID
273
+ to its CUDA UUID. The new server sees that one physical GPU as `CUDA0`.
274
+ No machine-specific device ID is embedded in configuration examples. Each host
275
+ resolves its own saved selection; an absent or unsupported device reports an
276
+ error. Other host adapters remain subject to their declared catalog support.
277
+ `executionTarget: null` uses the configured runtime default. Omission preserves
278
+ an active selection's target. Runtime `execution` separates `requestedTarget`,
279
+ `resolvedDevice` and `configuredTarget`; `observedTarget: null` means actual
280
+ inference placement has not been observed. Configuration is not placement proof.
281
+
282
+ An external server keeps ownership of its model and device. Projected loading
283
+ or explicit target changes report `LOCAL_AI_EXTERNAL_SELECTION_UNSUPPORTED`
284
+ without stopping it or changing a working Core observation. `managed` describes
285
+ whether an installed executable can be managed at this listener; `owned` reports
286
+ an actual SDK-owned process. Recovery retains a live projection across process
287
+ replacement. If the projection was already released after exit, prepare it again;
288
+ resuming never substitutes another model or target.
289
+
241
290
  The `payload` is the complete request. SDK option names are translated only at
242
291
  the upstream protocol boundary; messages, tool definitions and documents remain
243
292
  unchanged. An explicit `payload.model` selects that request's model. Core waits
@@ -365,10 +414,44 @@ runtime still reconciles the provider at its existing operation boundaries.
365
414
  permits that load preflight even before its catalog arrives; Core's `llama.load`
366
415
  waits for startup and verifies the exact requested model. An owned, successfully
367
416
  released service permits loading only a model retained in its actual catalog.
368
- An available service still requires that catalog membership. These observations
417
+ With `prepareModel`, an installed `managed` service also admits an explicit
418
+ load before the requested model exists in its catalog. An available external
419
+ service still requires catalog membership. These observations
369
420
  do not fabricate model records or readiness, and unavailable external services,
370
421
  closed services and actual errors remain unavailable.
371
422
 
423
+ The optional `prepareModel({selection,signal,progress})` callback runs only at
424
+ an actual provider load. Return `{assetProjectionId,resourcePaths,executionTarget,release?}`;
425
+ direct `provider.load` fields override the corresponding callback
426
+ fields, including an explicit null target. The provider awaits `release()`
427
+ after Core loading settles and before reporting readiness. Preparation must
428
+ observe cancellation and settle so even a late returned projection is released.
429
+ Only a dispatched Core selection owns a native unload; cancelled preparation
430
+ alone does not unload another model.
431
+
432
+ ```js
433
+ import {prepareCoreModelAssets} from 'arcane-os/ai/core-model-assets';
434
+
435
+ // modelFile is the complete GGUF Blob/File read from the application's store.
436
+ // selectedDeviceId came from this host's execution-device catalog.
437
+ const provider = createCoreLocalAIProvider({
438
+ client,
439
+ async prepareModel({signal, progress}) {
440
+ const projection = await prepareCoreModelAssets({
441
+ client, workingDirectory: '.arcane/model-work', signal,
442
+ members: [{path: 'moon-raccoon.gguf', file: modelFile}],
443
+ onProgress: progress
444
+ });
445
+ return {
446
+ assetProjectionId: projection.id,
447
+ resourcePaths: {model: 'moon-raccoon.gguf'},
448
+ executionTarget: {deviceId: selectedDeviceId},
449
+ release: projection.release
450
+ };
451
+ }
452
+ });
453
+ ```
454
+
372
455
  `dispose()` removes installation and service subscriptions and cancels owned
373
456
  operations synchronously, then attempts its actually retained model's release
374
457
  through its captured client. A configured selection alone does not own a native