arcane-os 0.66.0 → 0.67.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.
@@ -199,14 +199,16 @@ ordered `Blob` or `File` members after its resumable download. Range support
199
199
  allows concurrent transfer within a member; those transfer parts remain the
200
200
  store's concern. Use `offline: true` to require an already complete stored model.
201
201
  The store does not select or run the image engine. In source development,
202
- load the development Core bootstrap described
203
- in [Local AI through Core](local-ai.md#development) before constructing the
204
- accessor. Native hosts install their Core client through the existing host path.
202
+ use the development Core bootstrap described
203
+ in [Local AI through Core](local-ai.md#development). The accessor can be
204
+ constructed before that client is installed; perform model operations once
205
+ the service is available. Native hosts install their Core client through the
206
+ existing host path.
205
207
  An ordinary browser without Core reports local image generation as unavailable.
206
208
 
207
209
  | Member | Contract |
208
210
  |---|---|
209
- | `createCoreImageRuntime({client?, signal?, onEvent?})` | Uses the installed Core client by default, subscribes to lifecycle events and asynchronously reads one initial status snapshot. It returns immediately. |
211
+ | `createCoreImageRuntime({client?, signal?, onEvent?})` | Follows Core installation and lifecycle events by default, asynchronously reading status for each client. An explicit `client`, including `null`, remains caller-owned. It returns immediately. |
210
212
  | `load({model, context?, assetProjectionId?, resourcePaths?, signal?})` | Loads the selected model and returns authoritative service state after native completion. `context` supplies native context options. |
211
213
  | `generate({model, prompt, parameters?, assetProjectionId?, resourcePaths?, signal?, onProgress?})` | Returns the complete result with `images: [{blob, mediaType, width, height}]`. Additional runtime result metadata remains present. |
212
214
  | `unload({signal?})` | Explicitly unloads the shared selected native context and returns its resulting state. |
@@ -229,6 +231,15 @@ model definitions, including their resource metadata before engine preparation
229
231
  finishes. Installed files do not imply a loaded model. A late status
230
232
  response never overwrites a newer lifecycle event.
231
233
 
234
+ With `client` omitted, the accessor consumes the shared
235
+ [`subscribeCoreClient`](core-client.md) installation stream. A late installation
236
+ attaches the service listener and reads current status; it never installs an
237
+ engine or loads a model. Retirement or replacement synchronously clears
238
+ readiness and transient `Thinking`, cancels owned requests and detaches the
239
+ retired service listener. Old state, progress and results cannot update the
240
+ replacement connection. `close()` also removes the installation subscription;
241
+ it does not claim that the shared native model was unloaded.
242
+
232
243
  Prompts and parameters pass unchanged to the native runtime. Native option
233
244
  names, supported model operations and defaults belong to that runtime and the
234
245
  selected model definition. A prompt containing U+0000 or an unpaired UTF-16
@@ -1,6 +1,6 @@
1
- # Native working files from stored model assets
1
+ # Native working files from model assets
2
2
 
3
- These public APIs were first published in `arcane-os@0.61.0`. Browser
3
+ Browser preparation and native retain/release ownership were first published in `arcane-os@0.61.0`. Browser
4
4
  preparation is exported by `arcane-os/ai/core-model-assets`, and the native
5
5
  service by `arcane-os/core/model-assets`. The image example below uses
6
6
  `arcane-os/ai/core-image`, published in the same release.
@@ -87,6 +87,53 @@ cleanup failures remain observable.
87
87
 
88
88
  ## Native ownership
89
89
 
90
+ Native services can prepare explicitly selected upstream members without a
91
+ renderer or browser store. Obtain the registered shared service through
92
+ `await context.getService('model-assets')`, then use its native preparation API:
93
+
94
+ ```javascript
95
+ const modelAssets = await context.getService('model-assets');
96
+ const projection = await modelAssets.prepare({
97
+ id: selectedOperationId,
98
+ workingDirectory: selectedWorkingDirectory,
99
+ members: selectedMembers, // [{path: 'onnx/model.onnx', url: selectedURL}, ...]
100
+ signal,
101
+ onProgress: renderPreparationProgress
102
+ });
103
+ const use = modelAssets.retain(projection.id);
104
+ await modelAssets.release(projection.id);
105
+ try {
106
+ await ownNativeModelLifetime(use.members);
107
+ } finally {
108
+ // Join actual native unload/worker exit before releasing these files.
109
+ await use.release();
110
+ }
111
+ ```
112
+
113
+ `prepare({id,workingDirectory,members,signal,onProgress})` performs standard
114
+ fetches only when explicitly called. Every member has its original relative
115
+ `path` and selected `url`; the caller supplies the complete companion-file set.
116
+ Independent members download concurrently. Each complete response stream is
117
+ written directly through the existing ordered native file owner, without a
118
+ whole-model buffer or base64 conversion. No model or runtime is selected or
119
+ downloaded by service startup. The result is the ready projection snapshot with
120
+ ordered `members: [{path,nativePath}]`.
121
+
122
+ Progress uses `open`, `download`, `complete` and `ready` phases, with
123
+ `completed`, `total` and `unit:'files'`; member progress additionally includes
124
+ `memberIndex` and `path`. Progress callbacks may return a promise, which the
125
+ preparation observes. They must not await cleanup of the preparation currently
126
+ calling them. Callback failures remain preparation failures. HTTP failures
127
+ retain `url`, `status` and the complete response text in `response`.
128
+
129
+ `release(id)` relinquishes preparation ownership without a request signal.
130
+ For active native downloads, release or disposal aborts their fetches and joins
131
+ the transfer tasks and native writes before deleting only their owned child
132
+ directory. Preparation cancellation and failure follow that same cleanup path;
133
+ the original failures and cleanup failures remain observable. A native retain
134
+ handle continues to protect completed files through actual engine release.
135
+ Native preparation leaves browser DBOPFS originals and unrelated files alone.
136
+
90
137
  ```javascript
91
138
  import {createModelAssetService} from 'arcane-os/core/model-assets';
92
139
 
@@ -103,7 +150,7 @@ try {
103
150
  ```
104
151
 
105
152
  `createModelAssetService({appRoot})` returns a Core service with `current()`,
106
- `retain(id)` and `dispose()`. `retain()` accepts only a completed projection
153
+ `prepare(options)`, `retain(id)`, `release(id)` and `dispose()`. `retain()` accepts only a completed projection
107
154
  and returns `{id,directory,members,release}`. A native engine takes this handle
108
155
  before loading and releases it after actual unload, failed-load cleanup or
109
156
  worker exit. A rejected inference/load promise alone does not establish native
@@ -0,0 +1,181 @@
1
+ # Native Laya typed decisions
2
+
3
+ `arcane-os/local-ai/decisions` and `arcane-os/core/decisions` run the selected
4
+ Laya FP32 graph through the SDK's existing native ONNX owner. Tokenization runs
5
+ in its own Node Worker. A Core service can load and evaluate decisions without
6
+ a renderer, browser session, WebGPU browser context or chat provider.
7
+
8
+ The application owns the model selection, original state text, questions,
9
+ options and interpretation. The SDK owns model preparation, token framing,
10
+ inference and cleanup. This is option scoring, not a conversational LLM, a
11
+ replacement for a configured chat model or the cloud System One API.
12
+
13
+ ## Compose with existing Core services
14
+
15
+ Register a decision service alongside the selected local-AI and model-assets
16
+ services in the application's existing Core runtime:
17
+
18
+ ```js
19
+ import {createNativeDecisionService} from 'arcane-os/core/decisions';
20
+
21
+ const decisions = createNativeDecisionService({
22
+ model: 'onnx-community/laya-typed-decisions-ONNX',
23
+ dtype: 'fp32',
24
+ workingDirectory: '.models/laya'
25
+ }, {appRoot});
26
+
27
+ // Include decisions in the existing createCoreRuntime({services}) selection.
28
+ // That runtime already owns local-ai with ONNX selected and model-assets.
29
+ await runtime.getService('decisions');
30
+ await decisions.load(); // Explicit activation; no model download at registration.
31
+ try {
32
+ const result = await decisions.evaluate([{
33
+ state: 'The moon chef has one kettle and seventeen impatient dragons.',
34
+ question: 'What should the chef prepare?',
35
+ options: ['Tea', 'An inflatable castle']
36
+ }]);
37
+ console.log(result.decisions[0].value);
38
+ } finally {
39
+ await decisions.unload();
40
+ }
41
+ ```
42
+
43
+ Here `runtime` and `appRoot` are the application's existing Core runtime and
44
+ root. Add the service before starting that runtime; the example does not create
45
+ a second Core host or engine. Development and packaged native compositions
46
+ already register one model-assets service when ONNX is selected. The decision
47
+ service obtains those exact owners with `context.getService('local-ai')`,
48
+ `localAI.getONNXRuntime()` and `context.getService('model-assets')` on explicit
49
+ load. It reacquires the current ONNX owner after a later runtime recovery.
50
+ See [Core composition](core-runtime.md#native-service-composition) and
51
+ [native service owners](local-ai.md#native-service-owners).
52
+
53
+ `createNativeDecisionService(configuration, {appRoot})` defaults its service
54
+ name to `decisions`. It exposes `load`, `evaluate`, `classify` (an alias for
55
+ `evaluate`), `current`, `subscribe`, `unload` and `dispose`, plus Core lifecycle
56
+ `start(context)`. Its RPC methods are:
57
+
58
+ | Method | Parameters and result |
59
+ | --- | --- |
60
+ | `decisions.status` | Current lifecycle snapshot; does not load anything. |
61
+ | `decisions.load` | Explicit activation; returns the ready lifecycle snapshot. The request signal cancels activation. |
62
+ | `decisions.evaluate` | `{rows, runOptions?}`; returns `{decisions, outputs}`. The request signal cancels the active model operation. |
63
+ | `decisions.unload` | Releases the activation. Service-lifetime cleanup continues after the caller disconnects. |
64
+
65
+ RPC evaluation uses the SDK's existing complete JSON tensor encoding for
66
+ `outputs` at the transport boundary. Direct native evaluation returns native
67
+ typed tensor records. Service notifications use `decisions.state`; direct
68
+ `subscribe(listener, {emitCurrent:true, signal})` replays current state by
69
+ default and returns an unsubscribe function. Startup stores the service
70
+ context only: importing or registering this service starts no tokenizer,
71
+ model download or inference.
72
+
73
+ ## Direct native model
74
+
75
+ `createNativeDecisionModel(options)` from `arcane-os/local-ai/decisions` returns
76
+ `{load, evaluate, classify, current, status, subscribe, unload, dispose}`.
77
+ `status` aliases `current`; `classify` aliases `evaluate`. Supply the existing
78
+ native `onnx` owner, plus either the existing `modelAssets` owner and selected
79
+ `workingDirectory`, or caller-owned native files:
80
+
81
+ ```js
82
+ import {createNativeDecisionModel} from 'arcane-os/local-ai/decisions';
83
+
84
+ const decisions = createNativeDecisionModel({
85
+ onnx: localAI.getONNXRuntime(),
86
+ paths: {
87
+ model: selectedGraphPath,
88
+ tokenizer: selectedTokenizerPath,
89
+ tokenizerConfig: selectedTokenizerConfigPath
90
+ }
91
+ });
92
+ ```
93
+
94
+ These direct paths are native filesystem paths. Keep the graph's external data
95
+ beside it under the exact filename required by the graph. The Core service
96
+ resolves configured `paths` relative to `appRoot`; it does not copy or remove
97
+ caller-owned files. For SDK-prepared files, the default selected upstream is
98
+ `onnx-community/laya-typed-decisions-ONNX`, revision `main`, with exactly:
99
+
100
+ - `onnx/model.onnx` and `onnx/model.onnx_data`;
101
+ - `tokenizer.json` and `tokenizer_config.json`.
102
+
103
+ `model` selects the upstream Hugging Face repository and `revision` its
104
+ revision. Native preparation performs normal upstream fetches only during
105
+ explicit `load()`. It streams complete members through the existing working
106
+ file owner; it neither vendors the weights nor creates a second persistent
107
+ model store. The approved `@huggingface/tokenizers@0.2.0` dependency reads the
108
+ selected tokenizer files in its Worker. No Transformers or second ONNX runtime
109
+ is loaded by this API.
110
+
111
+ This graph selection requires `dtype:'fp32'`, which is also the default; another
112
+ dtype reports the incompatible selection rather than substituting a graph.
113
+ `sessionOptions` and `runOptions` pass to the existing native ONNX API unchanged.
114
+ `executionPreference` defaults to `gpu` here: actual advertised native GPU
115
+ provider creation is attempted, followed by an honest CPU fallback. Explicit
116
+ `sessionOptions.executionProviders` takes precedence. The underlying generic
117
+ ONNX factory continues to default to CPU. Accepted provider configuration is
118
+ not evidence that GPU nodes executed. Full provider-attempt diagnostics remain
119
+ in `execution`; see [native ONNX sessions](local-ai.md#native-onnx-sessions).
120
+
121
+ ## Complete inputs and outputs
122
+
123
+ `evaluate(rows, {signal, runOptions})` requires an explicitly loaded or loading
124
+ activation; it does not silently load an inactive model. Rows retain their
125
+ caller order, complete `state`, `question` and string-array `options`, plus
126
+ optional `type` (`choice`, `score` or `noul`). Additional row fields remain in
127
+ the returned row. Strings are not trimmed, rewritten or clipped. The shared
128
+ browser/native encoding owner constructs only the model's necessary token
129
+ framing; repeated identical state is tokenized once per batch. Tokenization
130
+ and ONNX execution run outside Core's foreground thread.
131
+
132
+ The result is `{decisions, outputs}`. Each decision contains its original
133
+ `row`, complete option `logits`, unrounded raw-softmax `probabilities`,
134
+ `answerIndex` and `value`. `choice` returns the selected complete option;
135
+ `score` returns the probability-weighted zero-based option index; `noul`
136
+ requires `[falseOption, trueOption]` and returns the probability of option 1.
137
+ When the graph actually supplies `act_logits`, action logits, probabilities
138
+ and `actProbability` are also returned. Every named graph output is retained
139
+ as `{type, dims, data}`, including extra tensors and padding. Raw model scores
140
+ are not claimed calibrated confidence. Results do not enter chat history,
141
+ memory extraction or DBOPFS automatically.
142
+
143
+ The model's real graph/context limits still apply. There is no SDK token cap,
144
+ silent chunking or fabricated result aggregation. Native/browser token-ID or
145
+ numerical parity requires execution evidence for the selected tokenizer and
146
+ runtime; sharing the encoding implementation alone does not establish it.
147
+
148
+ ## Lifetime and cancellation
149
+
150
+ `current()` returns `{family, model, revision, dtype, state, loaded, busy,
151
+ activeRequests, progress, error, execution}`. States include `unloaded`,
152
+ `loading`, `ready`, `unloading`, `disposing`, `disposed` and `error`. Semantic
153
+ progress includes model preparation, tokenization and evaluation; downloaded
154
+ members report files, not byte progress. Complete technical errors belong in
155
+ developer diagnostics, while the application owns its user-facing status.
156
+
157
+ Concurrent callers share one explicit activation. Cancelling a load or
158
+ evaluation cancels that activation and its outstanding operations, because the
159
+ native session and tokenizer have one owner. Use separately owned models when
160
+ independent cancellation is required. The construction `signal` governs the
161
+ whole model lifetime. `unload()` permits another explicit load after successful
162
+ cleanup; `dispose()` is terminal. Readiness is revoked as disposal begins.
163
+
164
+ Retained model files remain owned until the ONNX and tokenizer Workers really
165
+ exit. A rejected load, inference or termination request alone does not permit
166
+ file deletion. Cleanup joins active operations and native output delivery,
167
+ preserves cleanup errors, then releases only its working projection. It never
168
+ deletes supplied `paths`. A failed ordinary row does not destroy a healthy
169
+ loaded session or cancel sibling evaluations. The ONNX owner's `stopping:true`
170
+ retires decision readiness immediately on terminal Worker failure or shutdown,
171
+ even while its physical `loaded` state remains true pending actual exit. Cancelled or
172
+ retired operations cannot return a successful late result.
173
+
174
+ Shared Node/Worker/filesystem code is portable across Windows, Linux and macOS;
175
+ the selected ONNX Runtime build and provider determine actual platform/device
176
+ availability. Android requires the host's supported native adaptation. Static
177
+ source review and synthetic authored fixtures are not model execution or a
178
+ claim of tested platform, precision or accelerator behavior.
179
+
180
+ Upstream selection: [Laya FP32 model files](https://huggingface.co/onnx-community/laya-typed-decisions-ONNX/tree/main),
181
+ [Tokenizer package](https://www.npmjs.com/package/@huggingface/tokenizers).
@@ -108,7 +108,7 @@ runtime layouts.
108
108
  | `arcane-os/pwa` | Nonblocking PWA registration, worker updates, native installation state and a dismissible installation component. |
109
109
  | `arcane-os/ai/browser-wasm` | Caller-selected browser-local Wllama inference, complete DBOPFS model storage, streaming, cancellation, and structural tool-call results. |
110
110
  | `arcane-os/ai/core-local` | Browser llama.cpp provider through an available Core connection; browser-local Wllama and ONNX remain independently available. |
111
- | `arcane-os/ai/browser-decisions` | Explicit-load Laya FP16 and Julia-1 FP32 typed decisions through the shared Transformers.js Worker owner. |
111
+ | `arcane-os/ai/browser-decisions` | Explicit-load Laya and Julia-1 typed decisions with caller-selected precision through the shared Transformers.js Worker owner. |
112
112
  | `arcane-os/ai/tool-text-stream` | Shared selected tool-argument text observer for provider integration. |
113
113
  | `arcane-os/ai/twin-cloud` | Stateless chat, System One state/questions and image generation with explicit key/model, complete results and operation-specific retry/cancellation. |
114
114
  | `arcane-os/ai/browser-speech` | Caller-selected Whisper/Kokoro, native browser recognition/synthesis, cloud TTS, upstream assets, role lifecycle and cancellation. |
@@ -3742,11 +3742,19 @@ Runs one fast-forward-only pull after proving the selected repository worktree i
3742
3742
  ### Signature and result
3743
3743
 
3744
3744
  ```text
3745
- async repositoryPull({ workspaceRoot=process.cwd(), signal, onEvent, run=runProcess }={})
3745
+ async repositoryPull({ workspaceRoot=process.cwd(), target, signal, onEvent, run=runProcess }={})
3746
3746
  ```
3747
3747
 
3748
3748
  Import it from `arcane-os`. The signature above states whether settlement is synchronous or promise-based. The overview and owning group define result authority, side effects, callbacks, events, cancellation, and lifecycle.
3749
3749
 
3750
+ Optional `target:{remote,ref}` captures exact nonempty native-representable
3751
+ strings before any wait and selects `git pull --ff-only -- <remote> <ref>:`.
3752
+ The current clean branch receives a fast-forward-only integration; the SDK
3753
+ never switches branches, resets or changes configuration. Without a target,
3754
+ the existing configured pull and result remain unchanged. Targeted results
3755
+ add `target`, complete `stdout` and `stderr` to the existing pull result.
3756
+ See [checkout configuration and selected targets](core-repositories.md#existing-checkout-configuration-and-selected-targets).
3757
+
3750
3758
  ### Availability and normalization
3751
3759
 
3752
3760
  **Node; network-assisted Git.** SDK-normalized inputs, errors, events, and documented result. Deep protocol: [Node ESM](protocols.md).
@@ -3770,7 +3778,7 @@ Pushes the selected attached branch through the repository's configured remote a
3770
3778
  ### Signature, parameters, and result
3771
3779
 
3772
3780
  ```text
3773
- async repositoryPush({ workspaceRoot=process.cwd(), signal, onEvent, run=runProcess }={})
3781
+ async repositoryPush({ workspaceRoot=process.cwd(), target, signal, onEvent, run=runProcess }={})
3774
3782
  ```
3775
3783
 
3776
3784
  Import it from `arcane-os`. `workspaceRoot` selects the Git worktree. `signal`
@@ -3781,10 +3789,16 @@ should keep the default.
3781
3789
 
3782
3790
  Before pushing, the SDK runs `git rev-parse --show-toplevel`,
3783
3791
  `git branch --show-current`, and `git status --short --branch` in order. It rejects a
3784
- detached HEAD, then runs plain `git push`; the repository's current branch,
3792
+ detached HEAD, then, without `target`, runs plain `git push`; the repository's current branch,
3785
3793
  configured upstream/remote, Git credentials, hooks, and server policy remain
3786
3794
  authoritative. Unlike `repositoryPull()`, this function does **not** require a
3787
- clean worktree. It does not create a commit or select a remote/refspec for you.
3795
+ clean worktree. It does not create a commit or choose an application destination.
3796
+ Optional `target:{remote,ref}` captures the original nonempty
3797
+ native-representable strings before any wait and runs
3798
+ `git push --no-follow-tags -- <remote> HEAD:<ref>`. This publishes current HEAD
3799
+ to the caller-selected ref without force or implicit configured tag following.
3800
+ It changes no branch or Git configuration; credentials, URL interpretation,
3801
+ hooks and remote rejection remain Git-owned.
3788
3802
 
3789
3803
  The promise resolves to
3790
3804
  `{action:'push', repositoryRoot, branch, output}`. `output` is trimmed stdout,
@@ -3792,6 +3806,9 @@ or trimmed stderr when stdout is empty. A missing Git executable, failed status
3792
3806
  probe, rejected/nonzero push, cancellation, or event-callback failure rejects
3793
3807
  with the normalized process error. Cancellation stops the local process tree;
3794
3808
  it cannot prove that a remote accepted no objects before the interruption.
3809
+ Targeted results also contain the captured `target` and complete
3810
+ `stdout`/`stderr`; omitting the target preserves the existing result shape.
3811
+ See [checkout configuration and selected targets](core-repositories.md#existing-checkout-configuration-and-selected-targets).
3795
3812
 
3796
3813
  ### Availability and normalization
3797
3814
 
@@ -7894,8 +7911,8 @@ explicit existing path. The application owns the remote, branch and Core API.
7894
7911
  createRepositoryWorkspace({name,directory,dataRoot,remote,branch,gitIdentity,onEvent,run=runProcess}={})
7895
7912
  ```
7896
7913
 
7897
- Returns `{directory,open,status,pull,push,write,close,drain,dispose}`. Construction does
7898
- no I/O. Operations accept `{signal}`; a missing or empty destination clones once,
7914
+ Returns `{directory,open,status,configuration,pull,push,write,close,drain,dispose}`. Construction does
7915
+ no I/O. Preparing operations accept `{signal}`; a missing or empty destination clones once,
7899
7916
  while existing checkout files, branches, remotes and local changes remain intact.
7900
7917
  Same-directory calls are ordered within the process. Shutdown drains accepted
7901
7918
  work and preserves the checkout. No CLI cwd, launch state, preference, snapshot
@@ -7908,7 +7925,25 @@ Selected author fields override inherited child author/committer fields;
7908
7925
  omitted fields remain inherited. Username remains a credential hint, with
7909
7926
  authentication owned by ordinary Git and its configured helper.
7910
7927
 
7911
- `write({files:[{path,content}],message,signal})` snapshots the exact selected
7928
+ `configuration({signal})` observes an existing working root without clone,
7929
+ initialization or fetch. It returns `{repositoryRoot,headRef,origin,upstream}`:
7930
+ full symbolic HEAD (including unborn, null when detached), origin URL/pushURL
7931
+ arrays and current branch remote/merge/URL/pushURL arrays. Unset, empty and
7932
+ repeated configuration values remain distinct. These are ordinary configured
7933
+ values, not authentication or endpoint proof, and are not atomic with later
7934
+ operations. Raw stdout is decoded without replacement; unrepresentable text
7935
+ reports `ARCANE_GIT_CONFIGURATION_NOT_TEXT` with full diagnostics.
7936
+
7937
+ `pull({target,signal})`, `push({target,signal})` and
7938
+ `write({files:[{path,content}],message,target,signal})` accept optional
7939
+ `target:{remote,ref}`, captured as original strings at acceptance. The target
7940
+ selects a fast-forward-only pull into the current branch or a non-force push
7941
+ of current HEAD to the selected ref, without switching branches or changing
7942
+ configuration. It is independent of initial-clone `remote`/`branch`; omission
7943
+ preserves ordinary configured behavior. See the [complete configuration and
7944
+ target contract](core-repositories.md#existing-checkout-configuration-and-selected-targets).
7945
+
7946
+ `write` snapshots the exact selected
7912
7947
  strings, writes those repository-relative files, commits only their literal
7913
7948
  paths and performs an ordinary non-force push. It retains unrelated staged
7914
7949
  paths and existing Git filters/hooks. Results and causal failures distinguish
@@ -9075,14 +9110,19 @@ native installation eligibility and URL-bar promotion.
9075
9110
 
9076
9111
  ### Overview
9077
9112
 
9078
- Creates an explicitly loaded browser model for Laya typed decisions in FP16 or
9079
- Julia-1 in FP32. The shared Worker owns Transformers.js loading, full-input
9113
+ Creates an explicitly loaded browser model for Laya typed decisions or
9114
+ Julia-1. Laya defaults to FP16 and Julia to FP32; callers may select `dtype`
9115
+ explicitly. The shared Worker owns Transformers.js loading, full-input
9080
9116
  encoding, batched inference, complete results, cancellation and resource lifetime.
9081
9117
  Applications own model selection, questions, option meanings and decision policy.
9082
9118
  Existing chat and speech providers are unchanged.
9083
9119
 
9084
9120
  ### Signature and result
9085
9121
 
9122
+ ```text
9123
+ createBrowserDecisionModel({family,model,revision,device,dtype,runtime}={})
9124
+ ```
9125
+
9086
9126
  ```js
9087
9127
  import {createBrowserDecisionModel} from 'arcane-os/ai/browser-decisions';
9088
9128
 
@@ -9107,8 +9147,11 @@ precision, tokenization, output meanings, lifecycle and model limitations.
9107
9147
  ### Availability and normalization
9108
9148
 
9109
9149
  Browser module Workers plus the selected Transformers.js backend are required
9110
- on explicit use. Import and construction do not activate a model. Laya fixes
9111
- `dtype: 'fp16'`; Julia fixes `dtype: 'fp32'`; there is no precision fallback.
9150
+ on explicit use. Import and construction do not activate a model. The optional
9151
+ nonempty `dtype` string defaults to `fp16` for Laya and `fp32` for Julia. An
9152
+ explicit value is forwarded unchanged to the upstream loader; unsupported
9153
+ selections report its failure without precision fallback. For Laya FP32, pass
9154
+ `dtype: 'fp32'`.
9112
9155
  Cancellation terminates that client's Worker and rejects its in-flight work.
9113
9156
  No decisions are inserted into chat history or durable storage.
9114
9157
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.66.0",
3
+ "version": "0.67.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",
@@ -69,6 +69,7 @@
69
69
  "./core/preferences": "./src/core/services/preferences.mjs",
70
70
  "./core/diarization": "./src/diarization/service.mjs",
71
71
  "./core/local-ai": "./src/core/services/local-ai.mjs",
72
+ "./core/decisions": "./src/core/services/decisions.mjs",
72
73
  "./core/image": "./src/core/services/image.mjs",
73
74
  "./core/speech": "./src/core/services/speech.mjs",
74
75
  "./core/model-assets": "./src/core/services/model-assets.mjs",
@@ -76,6 +77,7 @@
76
77
  "./diarization/build": "./src/diarization/build.mjs",
77
78
  "./local-ai": "./src/local-ai/install.mjs",
78
79
  "./local-ai/onnx": "./src/local-ai/onnx.mjs",
80
+ "./local-ai/decisions": "./src/local-ai/decisions.mjs",
79
81
  "./local-ai/image": "./src/local-ai/image-runtime.mjs",
80
82
  "./local-ai/whisper": "./src/local-ai/whisper/index.mjs",
81
83
  "./local-ai/whisper/build": "./src/local-ai/whisper/build.mjs",
@@ -121,7 +123,7 @@
121
123
  "test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
122
124
  "test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
123
125
  "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",
124
- "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-speech-providers.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-development.test.mjs test/core-host.test.mjs test/core-packaged-web.test.mjs test/core-runtime.test.mjs test/core-stdio.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs-png.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.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/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",
126
+ "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-speech-providers.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-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-document-library.test.mjs test/dbopfs-png.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.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/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",
125
127
  "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",
126
128
  "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/native-provider-generation.test.mjs test/speech-queue-regression.test.mjs test/testing.test.mjs test/test-sets.test.mjs",
127
129
  "check": "node tools/check-source.mjs && npm test",
@@ -158,6 +160,7 @@
158
160
  "tag": "latest"
159
161
  },
160
162
  "dependencies": {
163
+ "@huggingface/tokenizers": "0.2.0",
161
164
  "event-pubsub": "6.1.1",
162
165
  "js-queue": "3.1.1",
163
166
  "node-http-server": "10.0.0",
@@ -4,7 +4,8 @@ import path from 'node:path';
4
4
  import Is from 'strong-type';
5
5
  import {ArcaneError, throwIfAborted} from '../errors.mjs';
6
6
  import {runProcess} from '../process.mjs';
7
- import {repositoryPull, repositoryPush, repositoryStatus} from '../repository.mjs';
7
+ import {captureRepositoryTarget, repositoryConfiguration, repositoryPull, repositoryPush,
8
+ repositoryPushArguments, repositoryStatus} from '../repository.mjs';
8
9
  import {createGitIdentityRunner} from '../git-identity.mjs';
9
10
 
10
11
  export {readGitIdentity} from '../git-identity.mjs';
@@ -152,29 +153,46 @@ export function createRepositoryWorkspace(
152
153
  );
153
154
  }
154
155
 
155
- function pull({signal} = {}) {
156
+ function configuration({signal} = {}) {
157
+ return accept(
158
+ function readRepositoryConfiguration() {
159
+ return repositoryConfiguration({workspaceRoot: repositoryDirectory, signal, onEvent, run: execute});
160
+ },
161
+ signal
162
+ );
163
+ }
164
+
165
+ function pull({target, signal} = {}) {
166
+ let selectedTarget;
167
+ try { selectedTarget = captureRepositoryTarget(target); }
168
+ catch (error) { return Promise.reject(error); }
156
169
  return accept(
157
170
  async function pullRepository() {
158
171
  await prepare(signal);
159
- return repositoryPull({workspaceRoot: repositoryDirectory, signal, onEvent, run: execute});
172
+ return repositoryPull({workspaceRoot: repositoryDirectory, target: selectedTarget, signal, onEvent, run: execute});
160
173
  },
161
174
  signal
162
175
  );
163
176
  }
164
177
 
165
- function push({signal} = {}) {
178
+ function push({target, signal} = {}) {
179
+ let selectedTarget;
180
+ try { selectedTarget = captureRepositoryTarget(target); }
181
+ catch (error) { return Promise.reject(error); }
166
182
  return accept(
167
183
  async function pushRepository() {
168
184
  await prepare(signal);
169
- return repositoryPush({workspaceRoot: repositoryDirectory, signal, onEvent, run: execute});
185
+ return repositoryPush({workspaceRoot: repositoryDirectory, target: selectedTarget, signal, onEvent, run: execute});
170
186
  },
171
187
  signal
172
188
  );
173
189
  }
174
190
 
175
- function write({files, message, signal} = {}) {
191
+ function write({files, message, target, signal} = {}) {
176
192
  let selectedFiles;
193
+ let selectedTarget;
177
194
  try {
195
+ selectedTarget = captureRepositoryTarget(target);
178
196
  requireString(message, 'message');
179
197
  if (!message.isWellFormed()) throw new TypeError('message cannot be represented completely as UTF-8 text.');
180
198
  if (!is.array(files) || files.length === 0) {
@@ -210,6 +228,7 @@ export function createRepositoryWorkspace(
210
228
  async function writeRepositoryFiles() {
211
229
  const outcome = {
212
230
  directory: repositoryDirectory,
231
+ ...(selectedTarget === undefined ? {} : {target: {...selectedTarget}}),
213
232
  state: 'local',
214
233
  stage: 'prepare',
215
234
  paths: selectedFiles.map(function selectedPath(file) { return file.path; }),
@@ -321,7 +340,7 @@ export function createRepositoryWorkspace(
321
340
  ['--literal-pathspecs', 'commit', '--only', '--cleanup=verbatim', '--file', '-', '--', ...outcome.paths],
322
341
  commitMessage()
323
342
  );
324
- await executeGit('push', ['push']);
343
+ await executeGit('push', repositoryPushArguments(selectedTarget));
325
344
  outcome.stage = 'complete';
326
345
  return outcome;
327
346
  } catch (cause) {
@@ -357,7 +376,7 @@ export function createRepositoryWorkspace(
357
376
  return closing;
358
377
  }
359
378
 
360
- return {directory: repositoryDirectory, open, status, pull, push, write, close, drain: close, dispose: close};
379
+ return {directory: repositoryDirectory, open, status, configuration, pull, push, write, close, drain: close, dispose: close};
361
380
  }
362
381
 
363
382
  function requireString(value, name) {
@@ -150,11 +150,22 @@ class CoreRuntime {
150
150
  };
151
151
  }
152
152
 
153
+ async getService(name) {
154
+ if (this.#closing) throw failure('CORE_CLOSING', 'Core is closing.');
155
+ const service = this.#services.get(name);
156
+ if (!service) throw failure('CORE_SERVICE_UNAVAILABLE', `Core service ${String(name)} is not registered.`);
157
+ await this.#startService(service);
158
+ return service.definition;
159
+ }
160
+
153
161
  #serviceContext(service) {
154
162
  const runtime = this;
155
163
  return {
156
164
  application: this.#application,
157
165
  service: service.name,
166
+ getService(name) {
167
+ return runtime.getService(name);
168
+ },
158
169
  emit(event, data) {
159
170
  return runtime.emit(event, data);
160
171
  }