arcane-os 0.66.0 → 0.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +1 -1
  3. package/browser-runtime/ai/core-image.mjs +84 -34
  4. package/browser-runtime/ai/core-local.mjs +195 -43
  5. package/browser-runtime/ai/core-onnx.mjs +71 -12
  6. package/docs/reference/README.md +6 -0
  7. package/docs/reference/availability-and-normalization.md +1 -0
  8. package/docs/reference/core-native-packaging.md +11 -4
  9. package/docs/reference/core-repositories.md +93 -9
  10. package/docs/reference/core-runtime.md +46 -5
  11. package/docs/reference/inventory/package-api.json +62 -20
  12. package/docs/reference/inventory/runtime-components.json +2 -1
  13. package/docs/reference/inventory/runtime-modules.json +3 -3
  14. package/docs/reference/local-ai.md +112 -2
  15. package/docs/reference/local-image-generation.md +15 -4
  16. package/docs/reference/model-assets.md +50 -3
  17. package/docs/reference/native-decisions.md +181 -0
  18. package/docs/reference/runtime-components.md +62 -5
  19. package/docs/reference/runtime-modules.md +44 -7
  20. package/docs/reference/sdk-api.md +229 -19
  21. package/package.json +5 -2
  22. package/runtime/arcane/components/theme-switcher.html +112 -13
  23. package/runtime/arcane/modules/PreferenceStore.js +18 -13
  24. package/runtime/arcane/modules/SystemAppearance.js +24 -6
  25. package/src/core/repositories.mjs +27 -8
  26. package/src/core/runtime.mjs +26 -1
  27. package/src/core/services/decisions.mjs +144 -0
  28. package/src/core/services/local-ai.mjs +31 -9
  29. package/src/core/services/model-assets.mjs +98 -13
  30. package/src/git-identity.mjs +22 -58
  31. package/src/local-ai/decision-tokenizer-worker.mjs +31 -0
  32. package/src/local-ai/decision-tokenizer.mjs +109 -0
  33. package/src/local-ai/decisions.mjs +273 -0
  34. package/src/local-ai/dev.mjs +8 -2
  35. package/src/local-ai/onnx-worker.mjs +67 -7
  36. package/src/local-ai/onnx.mjs +23 -12
  37. package/src/native/portable-layout.mjs +8 -3
  38. package/src/repository.mjs +144 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.68.0
4
+
5
+ - Add per-instance theme-switcher `configure({modes,labels})` and initial
6
+ `data-modes` / `data-system-label` presentation. Applications can select
7
+ Light / Dark / System with complete readable labels while the default
8
+ Auto / Light / Dark / Skin choices remain unchanged. Preserve selected,
9
+ disabled and keyboard-focus states with wrapping based on component space.
10
+ - Distinguish a wholly absent Core namespace from a missing method in a
11
+ registered service. Retain `METHOD_NOT_ALLOWED` and add exact `method`,
12
+ `namespace` and `reason` lookup metadata; genuine handler errors remain intact.
13
+ - Let the default preference adapter complete single operations through its
14
+ existing app-scoped local store only after an exact absent preferences
15
+ namespace response, retaining cancellation, disposal and full values.
16
+ An advertised rejected atomic batch is never retried as serial writes.
17
+ - Treat an exactly absent optional appearance namespace as unsupported, keeping
18
+ the original diagnostic. Preserve partial-service and genuine errors, the
19
+ existing theme persistence order, native window theming and app-owned host
20
+ preferences. No OS-wide appearance change, migration or dependency is added.
21
+
22
+ ## 0.67.0
23
+
24
+ - Add native Laya FP32 typed decisions through `arcane-os/local-ai/decisions`
25
+ and `arcane-os/core/decisions`, using the existing native ONNX and model-assets
26
+ owners plus the approved `@huggingface/tokenizers@0.2.0` Worker dependency.
27
+ Explicit activation preserves complete rows, graph outputs, ordered results,
28
+ current-state replay, cancellation and actual worker-exit file lifetime.
29
+ - Compose selected native services through one Core owner, prepare complete
30
+ model files through the existing asset owner, and expose actual GPU-provider
31
+ attempts with CPU fallback. Accepted configuration does not prove GPU execution.
32
+ - Follow late Core client installation and retirement in default AI accessors.
33
+ Keep explicit clients fixed, model observation non-loading and disposal tied
34
+ to actual retained ownership. Preserve recoverable native inference errors
35
+ without retiring a healthy session or cancelling sibling work; terminal
36
+ stopping revokes readiness while resources remain retained until actual exit.
37
+ - Add repository `configuration()` observations and captured `target:{remote,ref}`
38
+ options for ordinary fast-forward pull, non-force push and write. Preserve
39
+ complete configuration values, payloads and failures without switching branches,
40
+ rewriting Git configuration or changing omitted-target behavior.
41
+ - Keep theme controls, shared same-app host discovery and optional NPU asset
42
+ transport follow-through separate. No app data migration or automatic model
43
+ activation is introduced; applications own their selections and orchestration.
44
+
3
45
  ## 0.66.0
4
46
 
5
47
  - Add `readGitIdentity` from `arcane-os/core/repositories` for separate global,
package/README.md CHANGED
@@ -20,7 +20,7 @@ selected SDK projection or an explicit live SDK source mount. Both profiles
20
20
  share the theme, packaging, event, cancellation,
21
21
  and browser run contracts while retaining their selected application layout.
22
22
 
23
- This checkout defines the `0.66.0` SDK contract. Development applications declare
23
+ This checkout defines the `0.68.0` SDK contract. Development applications declare
24
24
  `arcane-os` as `latest` and retain their lockfile. Exact SDK pins begin when Roshi
25
25
  designates a production build. Follow [Consumer updates](docs/consumer-updates.md)
26
26
  to refresh and compare the installed SDK and lockfile with the published channel;
@@ -1,5 +1,5 @@
1
1
  import Is from '../dependencies/strong-type/index.js';
2
- import {getInstalledCoreClient} from '../core/client.mjs';
2
+ import {subscribeCoreClient} from '../core/client.mjs';
3
3
  import {CoreError, serializeCoreError} from '../core/contracts.mjs';
4
4
  import {createArcaneEventSource} from '../event-manager.mjs';
5
5
 
@@ -50,7 +50,7 @@ async function encodeImage(image, signal) {
50
50
 
51
51
  /** Browser access to the selected native image service; no result persistence. */
52
52
  export function createCoreImageRuntime(
53
- {client = getInstalledCoreClient(), signal, onEvent} = {}
53
+ {client: suppliedClient, signal, onEvent} = {}
54
54
  ) {
55
55
  const owner = {};
56
56
  const events = createArcaneEventSource(
@@ -60,26 +60,37 @@ export function createCoreImageRuntime(
60
60
  const lifetime = new AbortController();
61
61
  const lifetimeSignal = signal ? AbortSignal.any([signal, lifetime.signal]) : lifetime.signal;
62
62
  const pending = new Set();
63
- let state = {
64
- id: 'stable-diffusion.cpp',
65
- installed: false,
66
- available: false,
67
- selectedModel: null,
68
- loaded: false,
69
- state: client ? 'unknown' : 'unavailable',
70
- backend: null,
71
- devices: [],
72
- resources: {},
73
- progress: null,
74
- error: null,
75
- models: []
76
- };
63
+ let client = null;
64
+ let connection = null;
65
+ let stopInstallation;
66
+ let state = emptyState();
77
67
  let revision = 0;
78
68
  let closed = false;
79
69
  let closing;
80
70
 
71
+ function emptyState(error = null) {
72
+ return {
73
+ id: 'stable-diffusion.cpp',
74
+ installed: false,
75
+ available: false,
76
+ selectedModel: null,
77
+ loaded: false,
78
+ state: client ? 'unknown' : 'unavailable',
79
+ backend: null,
80
+ devices: [],
81
+ resources: {},
82
+ progress: null,
83
+ error: error ? serializeCoreError(error) : null,
84
+ models: []
85
+ };
86
+ }
87
+
81
88
  function current() {
82
- const requests = [...pending].map(
89
+ const requests = [...pending].filter(
90
+ function currentConnectionRequest(request) {
91
+ return request.connection === connection && !request.signal.aborted;
92
+ }
93
+ ).map(
83
94
  function requestState(request) {
84
95
  return {
85
96
  method: request.method,
@@ -97,7 +108,7 @@ export function createCoreImageRuntime(
97
108
  return request.status === 'Thinking';
98
109
  }
99
110
  ) ? 'Thinking' : null,
100
- busy: pending.size > 0,
111
+ busy: requests.length > 0,
101
112
  closed
102
113
  };
103
114
  }
@@ -146,8 +157,38 @@ export function createCoreImageRuntime(
146
157
  publish();
147
158
  }
148
159
 
149
- const unsubscribe = client?.events.on('image.state', accept)
150
- ?? function noCoreSubscription() {};
160
+ function bindClient({client: nextClient, error = null}) {
161
+ if (closed || (connection && nextClient === client)) {
162
+ return;
163
+ }
164
+ const previous = connection;
165
+ client = nextClient;
166
+ const currentConnection = {client, controller: new AbortController(), unsubscribe: null};
167
+ connection = currentConnection;
168
+ revision += 1;
169
+ state = emptyState(error);
170
+ previous?.unsubscribe?.();
171
+ previous?.controller.abort();
172
+ if (client) {
173
+ currentConnection.unsubscribe = client.events.on(
174
+ 'image.state',
175
+ function currentImageState(value) {
176
+ if (connection === currentConnection) accept(value);
177
+ }
178
+ );
179
+ }
180
+ publish();
181
+ if (!closed && connection === currentConnection && client) {
182
+ // Observe only; installation never loads an engine or model.
183
+ inspect().catch(
184
+ function initialImageStateUnavailable(error) {
185
+ if (!closed && connection === currentConnection && !currentConnection.controller.signal.aborted) {
186
+ reportObserverFailure(error);
187
+ }
188
+ }
189
+ );
190
+ }
191
+ }
151
192
 
152
193
  function invoke(method, parameters, {signal: requestSignal, onProgress} = {}) {
153
194
  if (closed) {
@@ -156,11 +197,15 @@ export function createCoreImageRuntime(
156
197
  if (!client) {
157
198
  throw unavailable('Local image generation requires an available Core connection.');
158
199
  }
159
- const operationSignal = requestSignal
160
- ? AbortSignal.any([requestSignal, lifetimeSignal]) : lifetimeSignal;
200
+ const currentConnection = connection;
201
+ const operationSignal = AbortSignal.any(
202
+ [lifetimeSignal, currentConnection.controller.signal, ...(requestSignal ? [requestSignal] : [])]
203
+ );
161
204
  operationSignal.throwIfAborted();
162
205
  const request = {
163
206
  method,
207
+ connection: currentConnection,
208
+ signal: operationSignal,
164
209
  revision,
165
210
  streamId: parameters.streamId,
166
211
  status: method === 'image.generate' || method === 'image.edit' ? 'Thinking' : null,
@@ -169,7 +214,7 @@ export function createCoreImageRuntime(
169
214
  };
170
215
  let stopProgress;
171
216
  if (request.streamId) {
172
- stopProgress = client.events.on(
217
+ stopProgress = currentConnection.client.events.on(
173
218
  'image.progress',
174
219
  function imageProgress(value) {
175
220
  if (closed || operationSignal.aborted || value.streamId !== request.streamId) {
@@ -202,7 +247,7 @@ export function createCoreImageRuntime(
202
247
  {type: 'image.request', data: {method, parameters: requestParameters}}
203
248
  );
204
249
  operationSignal.throwIfAborted();
205
- const result = await client.invoke(
250
+ const result = await currentConnection.client.invoke(
206
251
  method,
207
252
  requestParameters,
208
253
  {signal: operationSignal, timeoutMs: 0}
@@ -238,7 +283,7 @@ export function createCoreImageRuntime(
238
283
  function releaseImageRequest() {
239
284
  stopProgress?.();
240
285
  pending.delete(request);
241
- if (!closed) {
286
+ if (!closed && connection === currentConnection) {
242
287
  publish();
243
288
  }
244
289
  }
@@ -251,12 +296,14 @@ export function createCoreImageRuntime(
251
296
 
252
297
  async function inspect({signal: requestSignal} = {}) {
253
298
  const before = revision;
299
+ const currentConnection = connection;
254
300
  try {
255
301
  const result = await invoke(
256
302
  'image.status',
257
303
  {},
258
304
  {signal: requestSignal}
259
305
  );
306
+ currentConnection.controller.signal.throwIfAborted();
260
307
  if (!closed && revision === before) {
261
308
  accept(result);
262
309
  }
@@ -273,11 +320,13 @@ export function createCoreImageRuntime(
273
320
 
274
321
  async function load({model, context, assetProjectionId, resourcePaths, signal: requestSignal} = {}) {
275
322
  const before = revision;
323
+ const currentConnection = connection;
276
324
  const result = await invoke(
277
325
  'image.load',
278
326
  {model, context, assetProjectionId, resourcePaths},
279
327
  {signal: requestSignal}
280
328
  );
329
+ currentConnection.controller.signal.throwIfAborted();
281
330
  if (!closed && revision === before) {
282
331
  accept(result);
283
332
  }
@@ -308,11 +357,13 @@ export function createCoreImageRuntime(
308
357
 
309
358
  async function unload({signal: requestSignal} = {}) {
310
359
  const before = revision;
360
+ const currentConnection = connection;
311
361
  const result = await invoke(
312
362
  'image.unload',
313
363
  {},
314
364
  {signal: requestSignal}
315
365
  );
366
+ currentConnection.controller.signal.throwIfAborted();
316
367
  if (!closed && revision === before) {
317
368
  accept(result);
318
369
  }
@@ -365,8 +416,10 @@ export function createCoreImageRuntime(
365
416
  );
366
417
  lifetime.abort();
367
418
  lifetimeSignal.removeEventListener('abort', close);
368
- unsubscribe();
369
- state = {...state, available: false, state: 'closed'};
419
+ stopInstallation?.();
420
+ connection?.unsubscribe?.();
421
+ connection?.controller.abort();
422
+ state = {...state, available: false, loaded: false, state: 'closed'};
370
423
  publish();
371
424
  events.dispose();
372
425
  return closing;
@@ -379,13 +432,10 @@ export function createCoreImageRuntime(
379
432
  );
380
433
  if (lifetimeSignal.aborted) {
381
434
  close();
382
- } else if (client) {
383
- // Subscribe before the snapshot request so a later lifecycle event wins.
384
- inspect().catch(
385
- function initialImageStateUnavailable(error) {
386
- globalThis.console?.error('Core image initial state unavailable.', error);
387
- }
388
- );
435
+ } else if (suppliedClient === undefined) {
436
+ stopInstallation = subscribeCoreClient(bindClient, {signal: lifetimeSignal});
437
+ } else {
438
+ bindClient({client: suppliedClient});
389
439
  }
390
440
 
391
441
  return {load, generate, edit, unload, inspect, current, subscribe, close};