arcane-os 0.88.0 → 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 (44) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +1 -1
  3. package/browser-runtime/ai/browser-decisions.mjs +31 -4
  4. package/browser-runtime/ai/core-local.mjs +105 -25
  5. package/browser-runtime/ai/decision-runtime.mjs +7 -0
  6. package/browser-runtime/ai/decision-worker.mjs +9 -1
  7. package/browser-runtime/core/client.mjs +6 -0
  8. package/browser-runtime/core/contracts.mjs +10 -0
  9. package/docs/reference/ai/browser-decisions.md +89 -7
  10. package/docs/reference/cli.md +10 -0
  11. package/docs/reference/core-client.md +59 -0
  12. package/docs/reference/core-native-packaging.md +57 -10
  13. package/docs/reference/core-repositories.md +47 -3
  14. package/docs/reference/inventory/package-api.json +15 -15
  15. package/docs/reference/inventory/runtime-components.json +1 -1
  16. package/docs/reference/inventory/runtime-modules.json +4 -4
  17. package/docs/reference/local-ai.md +86 -3
  18. package/docs/reference/mcp-stdio.md +76 -1
  19. package/docs/reference/native-app-control.md +17 -5
  20. package/docs/reference/protocols.md +14 -0
  21. package/docs/reference/runtime-modules.md +9 -3
  22. package/docs/reference/sdk-api.md +146 -23
  23. package/package.json +2 -2
  24. package/runtime/arcane/modules/RiskSignalAnalyzer.js +1 -5
  25. package/schemas/arcane-app.schema.json +13 -3
  26. package/schemas/arcane-package.schema.json +10 -1
  27. package/src/app-descriptor.mjs +6 -2
  28. package/src/cli/app-control.mjs +1 -1
  29. package/src/core/hosts/windows/ArcaneHost.cs +273 -5
  30. package/src/core/hosts/windows/ArcaneLauncher.cs +9 -0
  31. package/src/core/hosts/windows/ArcaneWindowControl.cs +7 -7
  32. package/src/core/repositories.mjs +11 -1
  33. package/src/core/services/local-ai.mjs +183 -22
  34. package/src/installed-sdk-runtime.mjs +2 -0
  35. package/src/local-ai/config.mjs +16 -0
  36. package/src/local-ai/dev.mjs +2 -2
  37. package/src/local-ai/discover.mjs +3 -0
  38. package/src/local-ai/install.mjs +130 -20
  39. package/src/local-ai/llama-execution-windows.ps1 +158 -0
  40. package/src/local-ai/llama-execution.mjs +169 -0
  41. package/src/local-ai/server.mjs +38 -8
  42. package/src/native/portable-layout.mjs +13 -3
  43. package/src/workspace-operation-lock.mjs +23 -10
  44. package/src/workspace-runtime.mjs +97 -1
@@ -75,7 +75,8 @@ Save this in the consuming application and configure the MCP client's subprocess
75
75
  command as `node` with that application's script path. Client configuration
76
76
  file formats and process launch policy remain client-owned. Change `menu` to
77
77
  change the complete result; no model, repository, or native host is needed for
78
- this example.
78
+ this example. A native package can instead use its
79
+ [existing bundled runtime](#launch-from-a-native-package).
79
80
 
80
81
  The application explicitly converts its domain record into an MCP result.
81
82
  The SDK does not guess from `content`, `message`, or other domain field names.
@@ -255,6 +256,80 @@ The adapter uses `-32700` for malformed JSON, `-32600` for invalid requests,
255
256
  `-32603` for internal failures, and `-32002` for a missing resource. Its
256
257
  `-32000` lifecycle error requests initialization before ordinary operations.
257
258
 
259
+ ## Launch from a native package
260
+
261
+ The generated `runtime/arcane-core.mjs` accepts
262
+ `--arcane-app-entry <app-relative-module>` to import an application-owned entry
263
+ instead of starting ordinary Core. Include the entry and its complete local
264
+ source closure in the application's `package.nativeResources`, for example:
265
+
266
+ ```json
267
+ {
268
+ "package": {
269
+ "nativeResources": {
270
+ "include": ["native", "src", "package.json"]
271
+ }
272
+ }
273
+ }
274
+ ```
275
+
276
+ Save the complete example above as `native/moon-mcp.mjs`. Native packaging copies
277
+ the selected files beneath the artifact's `app/` directory. It does not discover
278
+ entry dependencies from this runtime argument or execute the entry during
279
+ packaging. Public imports such as `arcane-os/mcp/stdio` use the artifact's
280
+ existing SDK package and Node's ordinary package resolution.
281
+
282
+ Configure the MCP client's command and argument array for the selected artifact:
283
+
284
+ | Artifact | Command | Arguments |
285
+ | --- | --- | --- |
286
+ | Windows | `<artifact-root>/runtime/ArcaneCore.exe` | `["--arcane-app-entry", "native/moon-mcp.mjs"]` |
287
+ | macOS | `<artifact-root>/runtime/node` | `["<artifact-root>/runtime/arcane-core.mjs", "--arcane-app-entry", "native/moon-mcp.mjs"]` |
288
+ | Portable | The host's supported Node executable | `["<artifact-root>/runtime/arcane-core.mjs", "--arcane-app-entry", "native/moon-mcp.mjs"]` |
289
+
290
+ These are placeholders for the actual native paths. For macOS, `artifact-root`
291
+ is `<app-id>.app/Contents/Resources`. The portable provider supplies no Node
292
+ executable. Windows uses its existing SEA executable and filesystem loader;
293
+ do not pass `arcane-core.mjs` as a script argument to that executable. These
294
+ commands select the runtime directly, not the GUI launcher, and create no
295
+ window or second Core host.
296
+
297
+ The module path resolves from the artifact's `app/`, independently of the
298
+ client's working directory. Native path separators, spaces, Unicode, `#` and
299
+ `%` retain their filesystem meaning through file-URL conversion. The original
300
+ `process.argv` remains complete and unchanged, including this flag, its path,
301
+ and every app argument. The flag is read from user arguments starting at index
302
+ 2 for both normal Node and SEA. A missing or empty value throws `TypeError`;
303
+ module-loading failures remain actual errors and never start ordinary Core.
304
+
305
+ This is a dynamic import: the entry runs its top-level code and observes its
306
+ own session lifetime, as the example's `server.start()` and `await server.closed`
307
+ do. Do not put its only startup behind `if (import.meta.main)`; the imported
308
+ module is not Node's main entry. The SDK calls no default export or factory.
309
+
310
+ App-entry mode skips ordinary Core launch-context loading, manifest service
311
+ composition, state-directory creation, local-AI startup, shared-host startup,
312
+ and Core signal handlers. If the application wants the existing explicit launch
313
+ JSON or host state-root arguments, append them to the same argument array and
314
+ read them deliberately in its entry:
315
+
316
+ ```javascript
317
+ import {readCoreLaunchContext} from 'arcane-os/core/host';
318
+
319
+ const context = await readCoreLaunchContext();
320
+ ```
321
+
322
+ For example, append `"--arcane-launch-config", "<selected-launch.json>"`.
323
+ Relative launch JSON paths retain their caller-working-directory meaning. The
324
+ application chooses defaults, services and any connection to an existing Core;
325
+ the entry selector neither reads nor applies the manifest's launch defaults.
326
+
327
+ The application owns process signals, graceful MCP drain, and its underlying
328
+ service shutdown. Keep stdout exclusively for MCP JSON-RPC and diagnostics on
329
+ stderr; the ordinary Core `arcane/1` transport must not share those MCP pipes.
330
+ When the flag is absent, ordinary Core startup and service lifecycle are
331
+ unchanged. See [native entry composition](core-native-packaging.md#app-owned-runtime-entry).
332
+
258
333
  ## Evidence boundary
259
334
 
260
335
  `test/mcp-stdio-server.test.mjs` contains focused in-memory stream fixtures for
@@ -73,11 +73,11 @@ connection or other business result.
73
73
  | Member | Contract |
74
74
  | --- | --- |
75
75
  | `connectAppControl({endpoint, signal?, onError?})` | Connect once to the explicit running host. It never launches an app, creates Core, changes a profile or reconnects automatically. |
76
- | `status(options?)` | Returns app identity, platform/host, current URL, document generation, readiness, window title/state/client dimensions and available operations. Readiness describes the document, not models or application services. |
76
+ | `status(options?)` | Returns app identity, platform/host, current URL, document generation, readiness, window title/state/client dimensions and available operations. Window state is `Normal`, `Maximized`, `Minimized`, or `Fullscreen`. Readiness describes the document, not models or application services. |
77
77
  | `inspect(parameters={}, options?)` | Optional `shadowPath` selects an open shadow root. Optional CSS `selector` selects zero or more elements within the selected root; omission selects the whole root. Optional `documentGeneration` selects the expected document. Returns complete HTML/text, live control state and DOM-derived role/name hints. |
78
78
  | `capture(parameters={}, options?)` | Returns `{mimeType:'image/png', data, documentGeneration, url}`. `data` contains the complete base64 PNG. Optional `documentGeneration` selects the expected document. |
79
79
  | `act(parameters, options?)` | Requires `documentGeneration`, one unambiguous CSS `selector` within the root selected by optional `shadowPath`, and the action fields below. |
80
- | `key(parameters, options?)` | Requires `documentGeneration` and `key:'Tab'`, `'Enter'`, or `'Space'`. Optional `shiftKey:true` is supported only for Tab. Sends one ordered press/release pair to the selected WebView's current focus. |
80
+ | `key(parameters, options?)` | Requires `documentGeneration` and `key:'Tab'`, `'Enter'`, `'Space'`, or `'Escape'`. Optional `shiftKey:true` is supported only for Tab. Sends one ordered press/release pair to the selected WebView's current focus. |
81
81
  | `resize(parameters, options?)` | Requires positive integral native client `width` and `height` supported by WinForms. Optional `documentGeneration` selects the expected document. Resizes only a Normal window and returns immediate previous/actual client dimensions plus the subsequently observed CSS viewport. |
82
82
  | `close()` | Closes this caller's connection and cancels its pending requests. It never closes the app or Core. Returns the connection's completion promise. |
83
83
  | `closed` | Connection lifetime promise; actual transport failures remain observable. |
@@ -207,6 +207,8 @@ const moved = await app.key({documentGeneration: page.documentGeneration, key: '
207
207
  console.log(moved.previous.focusPath, moved.actual.focusPath);
208
208
  await app.key({documentGeneration: moved.documentGeneration, key: 'Tab', shiftKey: true});
209
209
  await app.key({documentGeneration: moved.documentGeneration, key: 'Enter'});
210
+ const current = await app.inspect();
211
+ await app.key({documentGeneration: current.documentGeneration, key: 'Escape'});
210
212
  ```
211
213
 
212
214
  Keys use the selected WebView's current focus. No selector, shadow path, frame
@@ -214,7 +216,10 @@ selection, focus assignment or desktop activation is performed. Tab and
214
216
  Shift+Tab use a fixed Tab `rawKeyDown`/`keyUp` pair, with the Shift modifier on
215
217
  both events for Shift+Tab. Enter and Space use `keyDown` with their corresponding
216
218
  text, followed by `keyUp`, preserving browser default actions and application
217
- `preventDefault` handling. Shift+Tab does not send a separate Shift key pair.
219
+ `preventDefault` handling. Escape uses `key:'Escape'`, `code:'Escape'` and native
220
+ virtual key 27 in a `rawKeyDown`/`keyUp` pair, without text fields. It follows the
221
+ selected WebView's normal Escape handling; it does not promise that a particular
222
+ dialog closes. Shift+Tab does not send a separate Shift key pair.
218
223
  The API exposes these fixed keys, not an arbitrary protocol method or script.
219
224
 
220
225
  The result contains `key`, `shiftKey`, `previous` and `actual` document views,
@@ -264,7 +269,7 @@ origin. Read `viewport` for the resulting CSS dimensions instead of assuming a
264
269
  particular DPI or zoom conversion.
265
270
 
266
271
  Immediately before the UI-thread mutation, the host checks the actual
267
- `WindowState` and records `previous:{title,state,width,height}`. It records
272
+ window state and records `previous:{title,state,width,height}`. It records
268
273
  `actual` in the same shape immediately after the native setter, then observes
269
274
  the CSS `viewport` asynchronously. The result also contains `requested`,
270
275
  `resizeAttempted`, `resizeCompleted`, `documentGeneration` and `url`.
@@ -274,13 +279,20 @@ native snapshot and the document observation; these records are not an atomic
274
279
  native/CSS snapshot. Completion does not establish that asynchronous application
275
280
  resize handlers have settled.
276
281
 
277
- Maximized or minimized windows return `ARCANE_APP_CONTROL_FAILED` before any
282
+ Maximized, minimized or fullscreen windows return `ARCANE_APP_CONTROL_FAILED` before any
278
283
  size mutation, with `reason:'unsupported-window-state'`,
279
284
  `supportedWindowState:'Normal'` and the complete current `window` in the error's
280
285
  `data` entries. The host never automatically restores, maximizes or minimizes a
281
286
  window to satisfy this request. Invalid dimensions return `INVALID_ARGUMENT`.
282
287
  The same state rule applies to explicit restoration.
283
288
 
289
+ Native fullscreen reports `Fullscreen`, including while its underlying WinForms
290
+ state is Normal. A minimized fullscreen window reports `Minimized`. The
291
+ requesting document can select its state through
292
+ [`Arcane.window.setState({state})`](core-client.md#current-window-state); app-control
293
+ does not add a separate state-setting operation. That facade uses lowercase
294
+ state values, while app-control preserves its existing capitalized values.
295
+
284
296
  Restore using the actual `previous.width` and `previous.height` from the
285
297
  operation being reversed, never a historical status snapshot or CSS viewport
286
298
  dimensions. Restoration is an explicit second request; cancellation does not
@@ -475,6 +475,20 @@ the selected runtime content. The lock document describes the selected
475
475
  installation and projected roots; it is not a content-identity or admission
476
476
  record.
477
477
 
478
+ Both runtime materializers accept explicit `browserDecisions: true` to acquire
479
+ the upstream bundled Transformers.js 4.3.0 entry, matching ONNX Runtime
480
+ 1.31.0-dev.20260914-8d85527a0 asyncify module/WASM, and license files into
481
+ `arcane/sdk/ai/decisions-runtime/` before replacement. Default `false` performs
482
+ no optional acquisition. The complete selected files remain unchanged;
483
+ `workspace.decisions.started`/`workspace.decisions.progress` count completed
484
+ files. Cancellation, HTTP failures and observer failures join acquisition
485
+ cleanup and preserve the previous projection. Retain the option on each refresh
486
+ that needs the closure. The optional `browserDecisions` result describes its
487
+ directory, versions and filenames; installed materialization nests it under
488
+ `workspaceRuntime`. Model/tokenizer data remain upstream and cache-owned.
489
+ See [extension-local decisions](ai/browser-decisions.md#extension-local-executables-manifest-v3)
490
+ for the separate `runtime.local: true` browser selection.
491
+
478
492
  `upgradeApplication()` and `arcane upgrade` run the application's ordinary
479
493
  `npm upgrade` command. They do not independently reconcile the installed SDK
480
494
  projection, semantic Arcane lock, or managed import map.
@@ -128,7 +128,7 @@ own asynchronous work, cancellation, and backpressure.
128
128
  | [`RecordLinkIndex.js`](#recordlinkindexjs) | esm | Parses record links and builds their normalized index. | Cross-host | Fully normalized. |
129
129
  | [`RecordPassageIndex.js`](#recordpassageindexjs) | esm | Indexes text lines, page markers, dates, rules, and excerpts for record review. | Cross-host | Fully normalized. |
130
130
  | [`RecordReviewStore.js`](#recordreviewstorejs) | esm | Stores normalized record-review decisions through native storage or app-scoped local fallback. | Browser/native hybrid | Complete records preserved; unreadable stored content fails observably. |
131
- | [`RiskSignalAnalyzer.js`](#risksignalanalyzerjs) | esm | Matches configured risk signals and levels against complete text. | Cross-host | Fully normalized. |
131
+ | [`RiskSignalAnalyzer.js`](#risksignalanalyzerjs) | esm | Matches configured risk signals and levels against complete text. | Cross-host | Complete literal text; existing non-string conversion only. |
132
132
  | [`ScamRiskPolicy.js`](#scamriskpolicyjs) | esm | Combines deterministic scam signals with optional Arcane blocked-domain evidence and safety guidance. | Cross-host | Complete mutable results; blocked-domain policy requires `secure:true`. |
133
133
  | [`ScopedOPFSCache.js`](#scopedopfscachejs) | esm | Provides a narrow exact-key JSON cache inside one app-owned OPFS namespace. | Browser / native WebView | Filename-safe keys, complete JSON values, and malformed-cache cleanup normalized; storage errors mixed. |
134
134
  | [`ScreenCapture.js`](#screencapturejs) | esm | Captures a display surface as image, video, or GIF with explicit lifecycle events. | Browser / native WebView | State/events normalized; permission and codec errors mixed. |
@@ -1386,7 +1386,11 @@ transcription-model load.
1386
1386
  **Cross-host state contract.** States are `unavailable`, `unloaded`, `loading`,
1387
1387
  `recovering`, `ready`, `unloading`, `error`, and `disposed`. Recovery remains
1388
1388
  `loaded: false`; `recovering` and `error` may retain `busy: true` with an actual
1389
- `operationId` until the pending request settles. Revisions increase monotonically.
1389
+ `operationId` until the pending request settles. During `unloading`, an already
1390
+ loaded role may retain `busy: true` with its actual `operationId` while captured
1391
+ requests settle. That observation grants no readiness or permission for a new
1392
+ request and is not a `continuesAIRuntimeOperation` recovery state.
1393
+ Revisions increase monotonically.
1390
1394
  The events `arcane-ai-runtime-state`, `arcane-ai-runtime-intent`, and
1391
1395
  `arcane-ai-runtime-startup-settled` normalize observation only: receiving one
1392
1396
  does not grant a native capability, prove browser support, or load a provider.
@@ -4010,9 +4014,11 @@ Matches configured risk signals and levels against complete text.
4010
4014
 
4011
4015
  Exact exports: `DEFAULT_LEVELS`, `analyzeRiskSignals`.
4012
4016
 
4017
+ `analyzeRiskSignals(input, {signals, levels})` scans the complete `String(input ?? '')` without Unicode normalization or whitespace changes. Fullwidth characters and decomposed characters remain as supplied. Each signal's `RegExp.lastIndex` resets before testing; signal order, weights and selected levels determine the mutable `{level, matches, score, textLength}` result. `textLength` is the JavaScript string length after the existing non-string conversion; `null` and `undefined` become an empty string. Ordinary `ScamRiskPolicy.assessScamRisk()` uses this same literal-text path.
4018
+
4013
4019
  ### Availability and normalization
4014
4020
 
4015
- **Cross-host.** Fully normalized. Transport: In-process only. [Deep protocol details](protocols.md).
4021
+ **Cross-host.** Complete literal text; existing non-string conversion only. Transport: In-process only. [Deep protocol details](protocols.md).
4016
4022
 
4017
4023
  ### Example
4018
4024
 
@@ -1857,7 +1857,7 @@ create `arcane.lock.json` before serving, refreshing app import maps, or packagi
1857
1857
  ### Signature and result
1858
1858
 
1859
1859
  ```text
1860
- async materializeInstalledSdkRuntime({workspaceRoot,sdkPackageSource,workspaceOperationLease,signal,onEvent}={})
1860
+ async materializeInstalledSdkRuntime({workspaceRoot,sdkPackageSource,browserDecisions=false,workspaceOperationLease,signal,onEvent}={})
1861
1861
  ```
1862
1862
 
1863
1863
  Import it through the exact dependency key declared by the workspace: normally
@@ -1868,6 +1868,27 @@ whole-tree replacement. The mutable result includes the installed-package
1868
1868
  location, materialized workspace runtime paths, and `workspaceLock` path and
1869
1869
  document. It does not install dependencies or merge application source.
1870
1870
 
1871
+ Explicit `browserDecisions: true` also acquires the bundled Transformers.js
1872
+ 4.3.0 entry and matching ONNX Runtime 1.31.0-dev.20260914-8d85527a0 asyncify
1873
+ module/WASM and license files from their upstream distributions into
1874
+ `arcane/sdk/ai/decisions-runtime/`. It returns their `directory`,
1875
+ `transformersVersion`, `onnxRuntimeVersion` and `files` under
1876
+ `workspaceRuntime.browserDecisions`. The default `false` performs no optional
1877
+ acquisition. Select this option on each refresh that should retain those files.
1878
+ The [extension-local decision guide](ai/browser-decisions.md#extension-local-executables-manifest-v3)
1879
+ describes `runtime.local: true`, complete resource ownership and MV3 usage.
1880
+
1881
+ Ordinary acquisition automatically recovers a workspace-operation lock when
1882
+ the recorded owner process is confirmed absent, without waiting solely for its
1883
+ six-hour expiry. Retry this public operation normally; `workspaceOperationLease`
1884
+ is for active nested SDK work and cannot be reconstructed from the lock JSON.
1885
+ Live or indeterminate owners remain `ARCANE_WORKSPACE_BUSY`; an indeterminate
1886
+ process-probe error is retained as the cause. Existing published lock documents
1887
+ remain readable. Recovery rechecks cancellation, the captured nonce and PID,
1888
+ and owner presence after asynchronous observation, preserving a replacement
1889
+ detected there. This cooperative read-then-remove protocol does not provide an
1890
+ atomic compare-and-delete across competing processes.
1891
+
1871
1892
  Replacement removes files absent from the selected runtime content and restores
1872
1893
  the prior tree if commit fails. `signal` and
1873
1894
  `onEvent` remain caller-owned before commit. Once the atomic replacement starts,
@@ -1899,13 +1920,24 @@ the installed-package lock document owned by `materializeInstalledSdkRuntime()`.
1899
1920
  ### Signature and result
1900
1921
 
1901
1922
  ```text
1902
- async materializeWorkspaceRuntimeContent({workspaceRoot,runtimeRoot=path.join(getSdkRoot(), 'runtime'),browserRuntimeRoot=getSdkBrowserRuntimeRoot(),signal,onEvent}={})
1923
+ async materializeWorkspaceRuntimeContent({workspaceRoot,runtimeRoot=path.join(getSdkRoot(), 'runtime'),browserRuntimeRoot=getSdkBrowserRuntimeRoot(),sdkVersion=SDK_VERSION,browserDecisions=false,signal,onEvent}={})
1903
1924
  ```
1904
1925
 
1905
1926
  The operation stages the complete projection, replaces the prior tree at the
1906
1927
  commit boundary, restores it if commit fails, and removes paths absent from the
1907
1928
  selected sources.
1908
1929
 
1930
+ `browserDecisions: true` acquires the same upstream executable closure described
1931
+ by `materializeInstalledSdkRuntime()` before replacement. Its result is at
1932
+ `result.browserDecisions` because this lower-level operation returns the
1933
+ workspace runtime directly. Full downloaded content remains unchanged; model
1934
+ and tokenizer data are not materialized. `workspace.decisions.started` and
1935
+ `workspace.decisions.progress` report completed files and the fixed total.
1936
+ Cancellation, HTTP failures and throwing/rejecting observers abort and join
1937
+ pending acquisitions before returning; the prior runtime remains in place.
1938
+ An HTTP failure retains its complete response in `error.response`, or in the
1939
+ corresponding member of `AggregateError.errors` when several operations fail.
1940
+
1909
1941
  ### Availability and normalization
1910
1942
 
1911
1943
  **Node.** Complete workspace runtime content and a mutable materialization result. Deep protocol: [Installed SDK runtime materialization](protocols.md#installed-sdk-runtime-materialization).
@@ -7111,6 +7143,21 @@ no engine. The native routes of `AI.fetchTTS(payload, signal)` and
7111
7143
  `AI.fetchSTT(audio, signal)` forward their signals through these calls and
7112
7144
  normalize cancellation to `AbortError` with `ARCANE_AI_REQUEST_ABORTED`.
7113
7145
 
7146
+ `facade.window.state({signal}?)` reads the calling document's native window;
7147
+ `facade.window.setState({state}, {signal}?)` selects `normal`, `maximized` or
7148
+ `fullscreen` for that same window. The matching Windows host returns
7149
+ `{platform:'windows', supported:true, state}`, with observed `state` also able
7150
+ to be `minimized`. Fullscreen removes the frame and fills the selected monitor;
7151
+ leaving it restores the preceding normal bounds and frame controls before any
7152
+ requested maximization. Visible state transitions prevent activation/focus
7153
+ assignment to the requesting window without changing TopMost, Core, origin or
7154
+ profile. Optional `native.window.state` selects startup state after initial
7155
+ sizing; omission preserves host behavior. Saved Settings remain application-owned.
7156
+ Cancellation does not undo an already-applied change. Other hosts retain their
7157
+ actual unavailable-method/transport errors; the facade provides no fallback.
7158
+ See [current-window state](core-client.md#current-window-state) for complete
7159
+ native errors, partial-state evidence, navigation and cleanup ownership.
7160
+
7114
7161
  `facade.window.setTheme(presentation, {signal}?)` forwards the complete
7115
7162
  presentation to the current native window, independently of OS-user appearance.
7116
7163
  Optional `backgroundColor` and `textColor` accept `{red,green,blue,alpha}` or
@@ -7766,6 +7813,17 @@ published SDK/runtime dependencies and the canonical Core entry/client into a
7766
7813
  fresh output directory. Service factories are imported only when a real native
7767
7814
  host later launches the generated Core entry, never during packaging.
7768
7815
 
7816
+ The generated `runtime/arcane-core.mjs` also accepts
7817
+ `--arcane-app-entry <app-relative-module>` through the host's supported Node
7818
+ executable; the portable provider supplies no executable. Include that module
7819
+ and its complete source closure through `package.nativeResources`. The flag
7820
+ imports it from the packaged `app/` directory, preserves complete `process.argv`,
7821
+ and runs its top-level work rather than invoking an export or making it
7822
+ `import.meta.main`. The entry owns explicit launch-context reads, services,
7823
+ signals and shutdown; ordinary Core startup is skipped only in this mode.
7824
+ See [app-owned runtime entry](core-native-packaging.md#app-owned-runtime-entry)
7825
+ and [packaged MCP commands and lifetime](mcp-stdio.md#launch-from-a-native-package).
7826
+
7769
7827
  Explicit `native.launchContext.coreListener:{endpoint}` also composes one passive
7770
7828
  model observation service after `startCoreHost()` and before listener binding,
7771
7829
  using that runtime and existing services exposing `localai.status`, `image.status`
@@ -7773,11 +7831,13 @@ and `decisions.status`. Missing owners remain `null`; renderer attachment stays
7773
7831
  application-owned. Ordinary stdio and shared/headless branches gain no model
7774
7832
  startup or readiness barrier. See [native observation composition](core-native-packaging.md#launch-time-locations).
7775
7833
 
7776
- An optional `appDescriptor.native.window` record carries `width`, `height` and
7777
- `resizable` into `manifest.window`. Dimensions are positive integral logical
7778
- client units; `resizable` is an optional boolean. Omitted fields remain omitted.
7779
- The portable payload records the selection; the selected platform host owns
7780
- its window behavior. See [initial native window size](core-native-packaging.md#initial-native-window-size).
7834
+ An optional `appDescriptor.native.window` record carries `width`, `height`,
7835
+ `resizable` and `state` into `manifest.window`. Dimensions are positive integral
7836
+ logical client units; `resizable` is an optional boolean, and `state` selects
7837
+ `normal`, `maximized` or `fullscreen`. Omitted fields remain omitted. The portable
7838
+ payload records the selection; the matching Windows host applies it, while
7839
+ current other-platform hosts retain their existing window behavior. See
7840
+ [initial native window size and state](core-native-packaging.md#initial-native-window-size).
7781
7841
 
7782
7842
  ### Availability and normalization
7783
7843
 
@@ -7894,11 +7954,25 @@ The matching Windows host consumes `appDescriptor.native.window` through
7894
7954
  `manifest.window`. Optional `width` and `height` select initial client dimensions
7895
7955
  in 96-DPI logical units; the host scales them for its actual DPI and fits the
7896
7956
  outer window within the current screen's usable area. Optional `resizable:false`
7897
- selects a fixed-size border and disables maximizing. Omitted fields preserve
7898
- the existing host defaults. Initial sizing preserves a window already restored
7899
- to a non-normal state and does not add saved-bounds storage. The initial-fit
7900
- logic is not reapplied during later user resizing. See [initial native window size](core-native-packaging.md#initial-native-window-size)
7901
- for a complete descriptor example and the other-platform boundary.
7957
+ selects a fixed-size border and disables the maximize control. Optional `state`
7958
+ selects `normal`, `maximized` or `fullscreen`; the host applies an explicit state
7959
+ after its initial sizing pass and before the composing launcher's `Load` handler.
7960
+ Initial sizing runs only for a Normal window. Omitted fields preserve existing
7961
+ host choices, including the current state when `state` is omitted. Fullscreen
7962
+ retains normal bounds and frame controls for restoration. No saved-bounds store
7963
+ is added, and initial-fit logic is not reapplied during later user resizing.
7964
+ See [initial native window size and state](core-native-packaging.md#initial-native-window-size)
7965
+ for the complete descriptor and unchanged other-platform behavior.
7966
+
7967
+ The packaged `runtime/ArcaneCore.exe` also accepts
7968
+ `--arcane-app-entry <app-relative-module>` directly, without a script argument
7969
+ or the GUI launcher. Include the entry and complete source closure through
7970
+ `package.nativeResources`. It imports the selected packaged `app/` module with
7971
+ complete `process.argv` unchanged, running top-level work without invoking an
7972
+ export or making it `import.meta.main`. The entry owns explicit launch-context
7973
+ reads, services, signals and shutdown; this mode skips ordinary Core startup,
7974
+ while omission retains it. See [app-owned runtime entry](core-native-packaging.md#app-owned-runtime-entry)
7975
+ and [packaged MCP commands and lifetime](mcp-stdio.md#launch-from-a-native-package).
7902
7976
 
7903
7977
  The returned `arcane-native-builder/1` provider exposes `describe`, `doctor`,
7904
7978
  `prepare`, `build`, `verify` and `run`. Build returns `{app,target,manifest}`
@@ -8274,7 +8348,7 @@ explicit existing path. The application owns the remote, branch and Core API.
8274
8348
  ### Signature and result
8275
8349
 
8276
8350
  ```text
8277
- createRepositoryWorkspace({name,directory,dataRoot,remote,branch,longPaths,gitIdentity,onEvent,run=runProcess}={})
8351
+ createRepositoryWorkspace({name,directory,dataRoot,remote,branch,longPaths,cloneIdentity,gitIdentity,onEvent,run=runProcess}={})
8278
8352
  ```
8279
8353
 
8280
8354
  Returns `{directory,open,status,configuration,pull,push,write,close,drain,dispose}`. Construction does
@@ -8293,6 +8367,16 @@ unchanged; no global setting or data migration is performed. Git's ordinary
8293
8367
  configuration precedence, hooks and single-clone lifecycle remain in effect.
8294
8368
  See [Windows long paths](core-repositories.md#windows-long-paths-for-a-new-checkout).
8295
8369
 
8370
+ Optional `cloneIdentity:{name?,email?}` captures exact author strings and saves
8371
+ only supplied fields in a newly created checkout through Git's `clone --config`
8372
+ before its first checkout, on every supported platform. Empty strings are
8373
+ explicit values; omitted or `undefined` fields preserve inherited and template
8374
+ configuration. Existing repositories are never reconfigured. Unrepresentable
8375
+ process arguments fail without changing the supplied text. This option changes
8376
+ no global configuration, credentials, remote or branch, and does not identify
8377
+ the authenticated account. Command-local `gitIdentity` remains independent.
8378
+ See [clone-local author selection](core-repositories.md#clone-local-author-selection).
8379
+
8296
8380
  Optional `gitIdentity:{name?,email?,username?}` captures explicit non-secret
8297
8381
  settings for child Git operations without writing global/local configuration.
8298
8382
  Selected author fields override inherited child author/committer fields;
@@ -8385,15 +8469,16 @@ Construction selects no model and installs no runtime or transport.
8385
8469
  ### Signature and result
8386
8470
 
8387
8471
  ```text
8388
- createCoreLocalAIProvider({client,id='llama.cpp'}={})
8472
+ createCoreLocalAIProvider({client,id='llama.cpp',prepareModel}={})
8389
8473
  ```
8390
8474
 
8391
8475
  Import it from `arcane-os/ai/core-local`. The provider implements
8392
8476
  `arcane-ai-provider/2` and exposes `catalog`, `status`, `inspect`, `load`,
8393
8477
  `request`, `unload` and `dispose`. Register it with the shared AI provider runtime,
8394
8478
  select the exact provider/model with `localOnly:true`, and load that selection.
8395
- Requests wait for the requested model's actual readiness, preserve complete
8396
- payloads and parsed streaming chunks, and cancel on observed readiness loss.
8479
+ `load()` waits for the selected model's actual readiness; inference requests
8480
+ require that load to have completed successfully. Requests preserve complete
8481
+ payloads and parsed streaming chunks and cancel on observed readiness loss.
8397
8482
  Missing Core reports unavailability; it does not choose a browser or cloud route.
8398
8483
  Dispose/unregister at the caller's owning lifecycle boundary.
8399
8484
 
@@ -8407,6 +8492,17 @@ client, so never-loaded and already-unloaded providers send no native unload.
8407
8492
  The provider/2 runtime reconciles status at its operation boundaries; this
8408
8493
  does not add an idle role-state subscription.
8409
8494
 
8495
+ Optional `prepareModel({selection,signal,progress})` runs at actual load and
8496
+ returns `{assetProjectionId,resourcePaths,executionTarget,release?}`. Direct
8497
+ `load` fields with those names override preparation when defined; an explicit
8498
+ `executionTarget:null` requests the runtime default. Preparation release is
8499
+ awaited after native load settles, including failure, cancellation and late
8500
+ preparation results. The current successfully completed load owns readiness
8501
+ and inference; a failed replacement cannot reuse an older model's ready state.
8502
+ Native cleanup owns the last dispatched selection, while preparation alone
8503
+ creates no native unload obligation. `status().execution` forwards native
8504
+ execution diagnostics, including configured and observed targets separately.
8505
+
8410
8506
  ```javascript
8411
8507
  import {createCoreLocalAIProvider} from 'arcane-os/ai/core-local';
8412
8508
  import {getAIProviderRuntime} from 'arcane-os/ai-provider-runtime';
@@ -8429,7 +8525,7 @@ separate states; prompts, models and application policy remain caller-owned.
8429
8525
  ### Signature and result
8430
8526
 
8431
8527
  ```text
8432
- createLocalAIService(configuration,{appRoot,runtimes=[],signal,onEvent,prepare}={})
8528
+ createLocalAIService(configuration,{appRoot,runtimes=[],signal,onEvent,prepare,executionDevices}={})
8433
8529
  ```
8434
8530
 
8435
8531
  Import it from `arcane-os/core/local-ai`. `configuration` is the authored local-AI
@@ -8462,6 +8558,19 @@ Explicit load retains startup and exact catalog-membership checks.
8462
8558
  carry original parsed chunks with `streamId`; terminal responses remain complete.
8463
8559
  There is no background health polling or implicit model choice.
8464
8560
 
8561
+ For managed llama.cpp loading, `llama.load` also accepts `assetProjectionId`,
8562
+ `resourcePaths:{model}` and `executionTarget`. The existing model-assets owner
8563
+ retains the exact projected GGUF through actual server exit. An omitted target
8564
+ retains the same model's selection or configured default; `null` requests the
8565
+ runtime default. `{deviceId}` uses this computer's execution-device catalog.
8566
+ Windows NVIDIA selection resolves that physical device to its CUDA identity;
8567
+ unsupported or unavailable targets report their actual failure. Runtime
8568
+ `execution` reports requested, resolved and configured targets with
8569
+ `observedTarget:null`; configuration does not establish physical execution.
8570
+ An optional `executionDevices` catalog is borrowed by ONNX and llama.cpp; its
8571
+ supplying owner retains disposal responsibility. Existing external listeners
8572
+ retain their own model/device selection and reject managed reconfiguration.
8573
+
8465
8574
  ```javascript
8466
8575
  import {createLocalAIService} from 'arcane-os/core/local-ai';
8467
8576
 
@@ -9145,7 +9254,8 @@ portable Node local IPC; other native host adapters do not yet implement these
9145
9254
  document operations. No application is launched, no desktop window is activated,
9146
9255
  and no automatic reconnect or action retry occurs.
9147
9256
 
9148
- `status(options)` reports current app/window/document readiness.
9257
+ `status(options)` reports current app/window/document readiness, including
9258
+ observed window state `Normal`, `Maximized`, `Minimized` or `Fullscreen`.
9149
9259
  `inspect(parameters={},options)` returns complete selected DOM HTML/text
9150
9260
  and live control state. Inspection and actions accept optional `shadowPath`, an
9151
9261
  ordered array of unique CSS host selectors through open shadow roots; their
@@ -9160,7 +9270,8 @@ of the rendered viewport. `act(parameters,options)` requires a returned
9160
9270
  fields.
9161
9271
 
9162
9272
  `key(parameters,options)` requires the observed `documentGeneration` and
9163
- `key:'Tab'`, `'Enter'` or `'Space'`; `shiftKey:true` is supported only for Tab.
9273
+ `key:'Tab'`, `'Enter'`, `'Space'` or `'Escape'`; `shiftKey:true` is supported only
9274
+ for Tab.
9164
9275
  It sends a fixed ordered press/release pair to the selected WebView's current
9165
9276
  focus, without selecting an element or activating the desktop window. Accepted
9166
9277
  pairs share one keyboard owner. Once a press is attempted, its matching release
@@ -9172,9 +9283,9 @@ or navigation can change between phases.
9172
9283
 
9173
9284
  `resize(parameters,options)` accepts positive integral native client `width`
9174
9285
  and `height` supported by WinForms, plus optional `documentGeneration`. It changes
9175
- only `ClientSize` while the actual window is `Normal`; maximized or minimized
9176
- windows fail before size mutation. Results retain immediate `previous`/`actual`
9177
- native dimensions, requested dimensions, attempted/completed flags and a later
9286
+ only `ClientSize` while the actual window is `Normal`; maximized, minimized or
9287
+ fullscreen windows fail before size mutation. Results retain immediate
9288
+ `previous`/`actual` native dimensions, requested dimensions, attempted/completed flags and a later
9178
9289
  observed CSS `viewport`. Platform constraints can change actual dimensions;
9179
9290
  native and CSS observations are not an atomic snapshot. Restore explicitly with
9180
9291
  the same operation's `previous.width` and `previous.height`, not CSS dimensions
@@ -9979,9 +10090,21 @@ selections report its failure without precision fallback. For Laya FP32, pass
9979
10090
  Cancellation terminates that client's Worker and rejects its in-flight work.
9980
10091
  No decisions are inserted into chat history or durable storage.
9981
10092
 
10093
+ For explicitly selected extension-local execution, supply `runtime: {local:
10094
+ true}` after materializing with `browserDecisions: true`. The SDK directly
10095
+ imports its deployment-local `decisions-runtime/transformers.min.js` and
10096
+ matched asyncify module/WASM. Explicit `runtime.moduleUrl` and
10097
+ `runtime.wasmPaths: {mjs, wasm}` may select other compatible files in that same
10098
+ deployment; a directory string is also accepted for `wasmPaths`. This mode
10099
+ disables executable Blob preparation and upstream WASM cache/proxy creation,
10100
+ selects one WASM thread, and preserves the caller's model, precision and
10101
+ inference backend. With `store`, only the exact selected executable URLs bypass
10102
+ it; model, tokenizer, configuration and external-data resources retain that
10103
+ store. Local mode is opt-in; default browser behavior remains unchanged.
10104
+
9982
10105
  Optional `store` is an existing SDK DBOPFS model or speech store exposing
9983
10106
  `fetchResource(input, options)`. It remains outside cloned Worker configuration.
9984
- Explicit activation first stores the complete selected `runtime.moduleUrl`
10107
+ Without `runtime.local`, explicit activation first stores the complete selected `runtime.moduleUrl`
9985
10108
  entry and materializes it as a JavaScript object URL without rewriting source.
9986
10109
  The configured original URL remains the resource selection for later loads.
9987
10110
  The selected loader also routes its actual model/configuration/tokenizer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.88.0",
3
+ "version": "0.89.0",
4
4
  "description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -129,7 +129,7 @@
129
129
  "scripts": {
130
130
  "test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
131
131
  "test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
132
- "test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/app-selection.test.mjs test/contracts.test.mjs test/conversation-closing-report.test.mjs test/doctor.test.mjs test/mail-credentials.test.mjs test/mail-outbox.test.mjs test/mail-public-api.test.mjs test/mail-send.test.mjs test/mail-transport.test.mjs test/native-windows-icon.test.mjs test/targets.test.mjs test/workspace-operation-lock.test.mjs",
132
+ "test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/app-selection.test.mjs test/contracts.test.mjs test/conversation-closing-report.test.mjs test/doctor.test.mjs test/mail-credentials.test.mjs test/mail-outbox.test.mjs test/mail-public-api.test.mjs test/mail-send.test.mjs test/mail-transport.test.mjs test/native-windows-icon.test.mjs test/risk-signal-analyzer.test.mjs test/targets.test.mjs test/workspace-operation-lock.test.mjs",
133
133
  "test:functional": "node ./bin/arcane-test.mjs test/ai-ollama-readiness.test.mjs test/core-ollama-lifecycle.test.mjs test/ai-request-model.test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.test.mjs test/browser-decisions.test.mjs test/browser-kokoro-complete-input.test.mjs test/browser-speech-providers.test.mjs test/browser-speech-artifacts.test.mjs test/browser-speech-resource-bridge.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/chat-input.test.mjs test/chat-stream-speech.test.mjs test/chat-transcript-scroll.test.mjs test/chat-transient-retention.test.mjs test/cli.test.mjs test/continuous-voice-capture.test.mjs test/codex-app-server.test.mjs test/mcp-stdio-server.test.mjs test/core-classic-source.test.mjs test/core-client.test.mjs test/core-client-installation.test.mjs test/core-ai-installation.test.mjs test/core-llama-lifecycle.test.mjs test/core-development.test.mjs test/core-host.test.mjs test/core-shared-host.test.mjs test/core-model-assets.test.mjs test/core-native-services.test.mjs test/core-packaged-web.test.mjs test/core-runtime.test.mjs test/core-stdio.test.mjs test/dbopfs-committed-events.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs-model-resources.test.mjs test/dbopfs-png.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/document-acquisition.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/execution-devices.test.mjs test/file-preview.test.mjs test/import-map.test.mjs test/local-image-editing.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/markdown-editor.test.mjs test/markdown-media.test.mjs test/markdown-source-map.test.mjs test/modal.test.mjs test/model-resource-bridge.test.mjs test/native-decisions.test.mjs test/native-onnx.test.mjs test/native-module-packaging.test.mjs test/native-module-resource-references.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/rendered-print.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/theme-manager.test.mjs test/twin-cloud-images.test.mjs test/twin-cloud.test.mjs test/websocket-client-import-map.test.mjs test/websocket.test.mjs test/update-check.test.mjs test/user-explicit-updates.test.mjs",
134
134
  "test:integration": "node ./bin/arcane-test.mjs test/installed-package-runtime.test.mjs test/root-app-layout.test.mjs test/integrated-shared.test.mjs test/integrated-workspace.test.mjs test/mail-browser.test.mjs test/native-macos-provider.test.mjs test/native-plan.test.mjs test/native-portable-provider.test.mjs test/native-provider-loader.test.mjs test/npm-release.test.mjs test/release-bundle.test.mjs test/release-capability-smoke.test.mjs test/shared-payload-batch.test.mjs test/tarball.test.mjs test/browser-wasm-cpu.test.mjs test/wllama-webgpu-runtime.test.mjs",
135
135
  "test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.test.mjs test/html-import-registration.test.mjs test/html-import-packaged-script.test.mjs test/logging-regression.test.mjs test/markdown-speech.test.mjs test/prepared-speech.test.mjs test/ai-audio-output.test.mjs test/native-provider-generation.test.mjs test/speech-queue-regression.test.mjs test/testing.test.mjs test/test-sets.test.mjs",
@@ -8,12 +8,8 @@ const DEFAULT_LEVELS=[
8
8
  {minimum:0,id:'low'},
9
9
  ];
10
10
 
11
- function normalizeText(value){
12
- return String(value??'').normalize('NFKC');
13
- }
14
-
15
11
  export function analyzeRiskSignals(input,{signals=[],levels=DEFAULT_LEVELS}={}){
16
- const text=normalizeText(input);
12
+ const text=String(input??'');
17
13
  const matches=[];
18
14
  let score=0;
19
15
 
@@ -139,11 +139,12 @@
139
139
  "window": {
140
140
  "type": "object",
141
141
  "additionalProperties": false,
142
- "description": "Initial native client dimensions in logical pixels at 96 DPI, and whether the user can resize the window. Omitted fields retain the host defaults.",
142
+ "description": "Initial normal client dimensions in logical pixels at 96 DPI, resizing behavior, and optional native window state. Omitted fields retain the host defaults.",
143
143
  "properties": {
144
144
  "width": { "type": "integer", "minimum": 1 },
145
145
  "height": { "type": "integer", "minimum": 1 },
146
- "resizable": { "type": "boolean" }
146
+ "resizable": { "type": "boolean" },
147
+ "state": { "enum": ["normal", "maximized", "fullscreen"] }
147
148
  }
148
149
  },
149
150
  "bundledApps": {
@@ -283,7 +284,9 @@
283
284
  "properties": {
284
285
  "id": { "enum": ["llama.cpp", "ollama", "onnx", "stable-diffusion.cpp"] },
285
286
  "version": { "type": "string" },
286
- "url": { "type": "string" }
287
+ "url": { "type": "string" },
288
+ "backend": { "type": "string", "description": "llama.cpp: auto, cpu or cuda. Image runtimes: auto, metal or cpu." },
289
+ "companionUrl": { "type": "string", "description": "Explicit CUDA runtime companion archive for a custom llama.cpp CUDA URL." }
287
290
  }
288
291
  },
289
292
  { "$ref": "#/$defs/whisperRuntime" }
@@ -295,6 +298,13 @@
295
298
  "properties": {
296
299
  "url": { "type": "string" },
297
300
  "model": { "type": "string" },
301
+ "alias": { "type": "string" },
302
+ "executionTarget": {
303
+ "oneOf": [
304
+ { "type": "null" },
305
+ { "type": "object", "required": ["deviceId"], "properties": { "deviceId": { "type": "string" } } }
306
+ ]
307
+ },
298
308
  "modelsDirectory": { "type": "string" },
299
309
  "args": { "type": "array", "items": { "type": "string" } }
300
310
  }
@@ -163,7 +163,9 @@
163
163
  "properties": {
164
164
  "id": { "enum": ["llama.cpp", "ollama", "onnx", "stable-diffusion.cpp"] },
165
165
  "version": { "type": "string" },
166
- "url": { "type": "string" }
166
+ "url": { "type": "string" },
167
+ "backend": { "type": "string", "description": "llama.cpp: auto, cpu or cuda. Image runtimes: auto, metal or cpu." },
168
+ "companionUrl": { "type": "string", "description": "Explicit CUDA runtime companion archive for a custom llama.cpp CUDA URL." }
167
169
  }
168
170
  },
169
171
  { "$ref": "#/$defs/whisperRuntime" }
@@ -175,6 +177,13 @@
175
177
  "properties": {
176
178
  "url": { "type": "string" },
177
179
  "model": { "type": "string" },
180
+ "alias": { "type": "string" },
181
+ "executionTarget": {
182
+ "oneOf": [
183
+ { "type": "null" },
184
+ { "type": "object", "required": ["deviceId"], "properties": { "deviceId": { "type": "string" } } }
185
+ ]
186
+ },
178
187
  "modelsDirectory": { "type": "string" },
179
188
  "args": { "type": "array", "items": { "type": "string" } }
180
189
  }