arcane-os 0.71.0 → 0.71.2
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.
- package/CHANGELOG.md +19 -0
- package/README.md +1 -1
- package/browser-runtime/ai/browser-decisions.mjs +90 -13
- package/browser-runtime/ai/kokoro-complete-input.mjs +116 -0
- package/browser-runtime/ai/speech-worker-runtime.mjs +62 -45
- package/docs/reference/README.md +5 -0
- package/docs/reference/ai/browser-decisions.md +26 -11
- package/docs/reference/ai/browser-speech.md +27 -6
- package/docs/reference/ai/browser-wasm.md +24 -13
- package/docs/reference/inventory/package-api.json +63 -7
- package/docs/reference/inventory/runtime-modules.json +2 -2
- package/docs/reference/pwa.md +38 -0
- package/docs/reference/sdk-api.md +185 -1
- package/package.json +2 -2
- package/src/dev-server.mjs +25 -0
- package/src/pwa-worker.mjs +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.71.2
|
|
4
|
+
|
|
5
|
+
- Preserve complete browser Kokoro synthesis input through the selected runtime's
|
|
6
|
+
phonemization and tokenizer. Process every token in ordered model-capacity
|
|
7
|
+
segments and retain all resulting audio, with one model owner and request-local
|
|
8
|
+
cancellation. Keep short-input behavior, voice, speed and the public API intact.
|
|
9
|
+
- Include the browser decision runtime entry in an explicitly configured DBOPFS
|
|
10
|
+
model store. Retain the original resource URL across activations, import the
|
|
11
|
+
complete stored self-contained module, and join cancellation cleanup before
|
|
12
|
+
settling callers. Preserve direct loading when no store is configured.
|
|
13
|
+
|
|
14
|
+
## 0.71.1
|
|
15
|
+
|
|
16
|
+
- Correct explicit disabled PWA lifecycle in standalone source development by
|
|
17
|
+
serving a retirement worker when the root worker file is absent. Native
|
|
18
|
+
installation requests activation and unregisters only its own registration,
|
|
19
|
+
preserving saved data, authored workers, open-page content and other modes.
|
|
20
|
+
Browser update timing remains native; no page reload or polling is added.
|
|
21
|
+
|
|
3
22
|
## 0.71.0
|
|
4
23
|
|
|
5
24
|
- Add public theme-switcher CSS properties for group radius, option radius,
|
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.71.
|
|
23
|
+
This checkout defines the `0.71.2` 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;
|
|
@@ -204,6 +204,9 @@ class BrowserDecisionModel {
|
|
|
204
204
|
pending: new Map(),
|
|
205
205
|
evaluations: new Set(),
|
|
206
206
|
resources: null,
|
|
207
|
+
runtimeController: null,
|
|
208
|
+
runtimePreparation: null,
|
|
209
|
+
runtimeUrl: null,
|
|
207
210
|
cleanup: null,
|
|
208
211
|
reason: undefined
|
|
209
212
|
};
|
|
@@ -263,9 +266,58 @@ class BrowserDecisionModel {
|
|
|
263
266
|
}
|
|
264
267
|
const id = this.#nextId++;
|
|
265
268
|
activation.pending.set(id, activation.load);
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
+
if (this.#store) {
|
|
270
|
+
activation.runtimeController = new AbortController();
|
|
271
|
+
// Retain the operation before a store progress callback can
|
|
272
|
+
// synchronously unload this activation.
|
|
273
|
+
activation.runtimePreparation = Promise.resolve().then(
|
|
274
|
+
async function prepareDecisionRuntimeEntry() {
|
|
275
|
+
if (!model.#isCurrent(activation)) return;
|
|
276
|
+
const configuration = model.#configuration;
|
|
277
|
+
const sourceUrl = new URL(configuration.runtime.moduleUrl, import.meta.url).href;
|
|
278
|
+
const response = await model.#store.fetchResource(sourceUrl, {
|
|
279
|
+
signal: activation.runtimeController.signal,
|
|
280
|
+
onProgress: function reportRuntimeEntryProgress(progress) {
|
|
281
|
+
if (!model.#isCurrent(activation)) return;
|
|
282
|
+
model.#progress = progress;
|
|
283
|
+
model.#publish();
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
if (!model.#isCurrent(activation)) return;
|
|
287
|
+
if (response.status < 200 || response.status >= 300) {
|
|
288
|
+
const error = new Error(
|
|
289
|
+
`The decision runtime returned HTTP ${response.status} ${response.statusText}.`
|
|
290
|
+
);
|
|
291
|
+
error.code = 'ARCANE_DECISION_RUNTIME_DOWNLOAD_FAILED';
|
|
292
|
+
error.response = response;
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
// Import the complete stored module without source
|
|
296
|
+
// rewriting. This activation owns its URL until stop.
|
|
297
|
+
activation.runtimeUrl = URL.createObjectURL(
|
|
298
|
+
new Blob([response.file], {type: 'text/javascript'})
|
|
299
|
+
);
|
|
300
|
+
worker.postMessage({
|
|
301
|
+
id,
|
|
302
|
+
op: 'load',
|
|
303
|
+
payload: {
|
|
304
|
+
...configuration,
|
|
305
|
+
runtime: {...configuration.runtime, moduleUrl: activation.runtimeUrl}
|
|
306
|
+
},
|
|
307
|
+
storedResources: true
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
activation.runtimePreparation.catch(
|
|
312
|
+
function stopFailedRuntimePreparation(error) {
|
|
313
|
+
if (model.#isCurrent(activation)) model.#stop(activation, error, 'error');
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
} else {
|
|
317
|
+
worker.postMessage(
|
|
318
|
+
{id, op: 'load', payload: this.#configuration, storedResources: false}
|
|
319
|
+
);
|
|
320
|
+
}
|
|
269
321
|
} catch (error) {
|
|
270
322
|
this.#stop(activation, error, 'error');
|
|
271
323
|
}
|
|
@@ -333,11 +385,42 @@ class BrowserDecisionModel {
|
|
|
333
385
|
this.#state = state;
|
|
334
386
|
this.#progress = null;
|
|
335
387
|
this.#error = reason;
|
|
336
|
-
|
|
388
|
+
const cleanup = [];
|
|
389
|
+
activation.runtimeController?.abort(reason);
|
|
390
|
+
if (activation.runtimePreparation) {
|
|
391
|
+
cleanup.push(activation.runtimePreparation.catch(
|
|
392
|
+
function preserveRuntimePreparationFailure(error) {
|
|
393
|
+
if (error === reason || error?.name === 'AbortError') return;
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
396
|
+
));
|
|
397
|
+
}
|
|
398
|
+
if (activation.resources) cleanup.push(activation.resources.close(reason));
|
|
399
|
+
if (activation.worker) {
|
|
400
|
+
for (const [type, listener] of Object.entries(activation.listeners ?? {})) {
|
|
401
|
+
activation.worker.removeEventListener(type, listener);
|
|
402
|
+
}
|
|
403
|
+
activation.worker.terminate();
|
|
404
|
+
}
|
|
405
|
+
if (activation.runtimeUrl !== null) {
|
|
406
|
+
try {
|
|
407
|
+
URL.revokeObjectURL(activation.runtimeUrl);
|
|
408
|
+
} catch (error) {
|
|
409
|
+
cleanup.push(Promise.reject(error));
|
|
410
|
+
}
|
|
411
|
+
activation.runtimeUrl = null;
|
|
412
|
+
}
|
|
413
|
+
if (cleanup.length > 0) {
|
|
337
414
|
const model = this;
|
|
338
|
-
activation.cleanup =
|
|
339
|
-
function
|
|
340
|
-
|
|
415
|
+
activation.cleanup = Promise.allSettled(cleanup).then(
|
|
416
|
+
function settleDecisionResourceCleanup(results) {
|
|
417
|
+
const failures = results.filter(function failedCleanup(result) {
|
|
418
|
+
return result.status === 'rejected';
|
|
419
|
+
}).map(function cleanupReason(result) {
|
|
420
|
+
return result.reason;
|
|
421
|
+
});
|
|
422
|
+
if (failures.length === 0) return;
|
|
423
|
+
activation.reason = new AggregateError([reason, ...failures], 'Unable to settle decision model resources.');
|
|
341
424
|
throw activation.reason;
|
|
342
425
|
}
|
|
343
426
|
);
|
|
@@ -350,12 +433,6 @@ class BrowserDecisionModel {
|
|
|
350
433
|
}
|
|
351
434
|
);
|
|
352
435
|
}
|
|
353
|
-
if (activation.worker) {
|
|
354
|
-
for (const [type, listener] of Object.entries(activation.listeners ?? {})) {
|
|
355
|
-
activation.worker.removeEventListener(type, listener);
|
|
356
|
-
}
|
|
357
|
-
activation.worker.terminate();
|
|
358
|
-
}
|
|
359
436
|
activation.load.reject(reason);
|
|
360
437
|
for (const operation of activation.pending.values()) operation.reject(reason);
|
|
361
438
|
activation.pending.clear();
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import Is from '../dependencies/strong-type/index.js';
|
|
2
|
+
|
|
3
|
+
const is = new Is(false);
|
|
4
|
+
|
|
5
|
+
function throwIfCancelled(signal) {
|
|
6
|
+
if (signal?.aborted) {
|
|
7
|
+
throw signal.reason ?? new DOMException('Speech synthesis was cancelled.', 'AbortError');
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Compose the selected Kokoro public classes without changing loaded instances.
|
|
13
|
+
* The original synthesizer remains the sole owner of its model and disposal.
|
|
14
|
+
*/
|
|
15
|
+
export async function createCompleteKokoroSynthesis({
|
|
16
|
+
KokoroTTS,
|
|
17
|
+
synthesizer,
|
|
18
|
+
repository,
|
|
19
|
+
signal,
|
|
20
|
+
onProgress,
|
|
21
|
+
}) {
|
|
22
|
+
throwIfCancelled(signal);
|
|
23
|
+
const Tokenizer = synthesizer.tokenizer.constructor;
|
|
24
|
+
class CompleteKokoroTokenizer extends Tokenizer {
|
|
25
|
+
_call(text, options = {}) {
|
|
26
|
+
return super._call(text, { ...options, truncation: false });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Kokoro 1.2.1's factory uses the tokenizer's default revision. Reuse that
|
|
30
|
+
// same public loading contract through the existing Worker resource router.
|
|
31
|
+
const tokenizer = await CompleteKokoroTokenizer.from_pretrained(repository, {
|
|
32
|
+
progress_callback: onProgress,
|
|
33
|
+
});
|
|
34
|
+
throwIfCancelled(signal);
|
|
35
|
+
const contextLength = tokenizer.model_max_length;
|
|
36
|
+
if (!is.safeInteger(contextLength) || contextLength < 3) {
|
|
37
|
+
throw new Error('The selected Kokoro tokenizer does not expose its model context length.');
|
|
38
|
+
}
|
|
39
|
+
const contentCapacity = contextLength - 2;
|
|
40
|
+
function boundaryIds(text) {
|
|
41
|
+
const encoded = tokenizer(text, {
|
|
42
|
+
add_special_tokens: false,
|
|
43
|
+
return_tensor: false,
|
|
44
|
+
truncation: false,
|
|
45
|
+
});
|
|
46
|
+
return new Set(encoded.input_ids.map(function tokenId(id) { return BigInt(id); }));
|
|
47
|
+
}
|
|
48
|
+
const punctuation = boundaryIds(';:,.!?—…');
|
|
49
|
+
const spaces = boundaryIds(' ');
|
|
50
|
+
|
|
51
|
+
function segmentEnd(tokens, start, limit, contentEnd) {
|
|
52
|
+
if (limit === contentEnd) return limit;
|
|
53
|
+
for (const boundaries of [punctuation, spaces]) {
|
|
54
|
+
for (let index = limit - 1; index >= start; index -= 1) {
|
|
55
|
+
if (boundaries.has(tokens[index])) return index + 1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return limit;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class CompleteKokoroTTS extends KokoroTTS {
|
|
62
|
+
#signal;
|
|
63
|
+
|
|
64
|
+
constructor(requestSignal) {
|
|
65
|
+
super(synthesizer.model, tokenizer);
|
|
66
|
+
this.#signal = requestSignal;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async generate_from_ids(inputIds, options = {}) {
|
|
70
|
+
throwIfCancelled(this.#signal);
|
|
71
|
+
if (inputIds.dims.at(-1) <= contextLength) {
|
|
72
|
+
const output = await super.generate_from_ids(inputIds, options);
|
|
73
|
+
throwIfCancelled(this.#signal);
|
|
74
|
+
return output;
|
|
75
|
+
}
|
|
76
|
+
const Tensor = inputIds.constructor;
|
|
77
|
+
const tokens = inputIds.data;
|
|
78
|
+
const contentEnd = tokens.length - 1;
|
|
79
|
+
const outputs = [];
|
|
80
|
+
let sampleCount = 0;
|
|
81
|
+
for (let start = 1; start < contentEnd;) {
|
|
82
|
+
throwIfCancelled(this.#signal);
|
|
83
|
+
const end = segmentEnd(tokens, start, Math.min(start + contentCapacity, contentEnd), contentEnd);
|
|
84
|
+
// Only the original outer padding positions are replaced. All
|
|
85
|
+
// content tokens, including boundary punctuation/space, survive.
|
|
86
|
+
const data = new BigInt64Array(end - start + 2);
|
|
87
|
+
data[0] = tokens[0];
|
|
88
|
+
data.set(tokens.subarray(start, end), 1);
|
|
89
|
+
data[data.length - 1] = tokens[tokens.length - 1];
|
|
90
|
+
const ids = new Tensor('int64', data, [1, data.length]);
|
|
91
|
+
const output = await super.generate_from_ids(ids, options);
|
|
92
|
+
throwIfCancelled(this.#signal);
|
|
93
|
+
outputs.push(output);
|
|
94
|
+
sampleCount += output.audio.length;
|
|
95
|
+
start = end;
|
|
96
|
+
}
|
|
97
|
+
const audio = new Float32Array(sampleCount);
|
|
98
|
+
let offset = 0;
|
|
99
|
+
for (const output of outputs) {
|
|
100
|
+
audio.set(output.audio, offset);
|
|
101
|
+
offset += output.audio.length;
|
|
102
|
+
}
|
|
103
|
+
throwIfCancelled(this.#signal);
|
|
104
|
+
const RawAudio = outputs[0].constructor;
|
|
105
|
+
return new RawAudio(audio, outputs[0].sampling_rate);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return async function synthesizeCompleteKokoro(text, { voice, speed, signal: requestSignal } = {}) {
|
|
110
|
+
throwIfCancelled(requestSignal);
|
|
111
|
+
// Each invocation retains its own signal while sharing the one loaded
|
|
112
|
+
// model. Inherited generate() still owns all upstream phonemization.
|
|
113
|
+
const request = new CompleteKokoroTTS(requestSignal);
|
|
114
|
+
return request.generate(text, { voice, speed });
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Is from "../dependencies/strong-type/index.js";
|
|
2
2
|
import { createModelResourceClient, createModelResourceHost } from './model-resource-bridge.mjs';
|
|
3
|
+
import { createCompleteKokoroSynthesis } from './kokoro-complete-input.mjs';
|
|
3
4
|
|
|
4
5
|
const is = new Is(false);
|
|
5
6
|
|
|
@@ -1493,54 +1494,70 @@ async function createWhisperEngine(namespace, configuration, signal, report) {
|
|
|
1493
1494
|
}
|
|
1494
1495
|
|
|
1495
1496
|
async function createKokoroEngine(namespace, configuration, signal, report) {
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1497
|
+
if (!is.function(namespace?.KokoroTTS?.from_pretrained)) {
|
|
1498
|
+
throw workerError(
|
|
1499
|
+
'ARCANE_AI_PROVIDER_UNAVAILABLE',
|
|
1500
|
+
'The Kokoro runtime does not export KokoroTTS.',
|
|
1501
|
+
undefined,
|
|
1502
|
+
'kokoro-tts-constructor-export-missing',
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
throwIfAborted(signal, 'tts-load-cancelled');
|
|
1506
|
+
const synthesizer = await namespace.KokoroTTS.from_pretrained(
|
|
1507
|
+
configuration.model.repository,
|
|
1508
|
+
{
|
|
1509
|
+
device: configuration.execution?.device ?? 'wasm',
|
|
1510
|
+
dtype: configuration.model.dtype ?? 'q8',
|
|
1511
|
+
revision: configuration.model.revision,
|
|
1512
|
+
progress_callback: report,
|
|
1513
|
+
},
|
|
1502
1514
|
);
|
|
1503
|
-
|
|
1504
|
-
throwIfAborted(signal, "tts-load-cancelled");
|
|
1505
|
-
const synthesizer = await namespace.KokoroTTS.from_pretrained(
|
|
1506
|
-
configuration.model.repository,
|
|
1507
|
-
{
|
|
1508
|
-
device: configuration.execution?.device ?? "wasm",
|
|
1509
|
-
dtype: configuration.model.dtype ?? "q8",
|
|
1510
|
-
revision: configuration.model.revision,
|
|
1511
|
-
progress_callback: report,
|
|
1512
|
-
},
|
|
1513
|
-
);
|
|
1514
|
-
try {
|
|
1515
|
-
throwIfAborted(signal, "tts-load-cancelled");
|
|
1516
|
-
} catch (error) {
|
|
1515
|
+
let synthesize;
|
|
1517
1516
|
try {
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1517
|
+
throwIfAborted(signal, 'tts-load-cancelled');
|
|
1518
|
+
synthesize = await createCompleteKokoroSynthesis({
|
|
1519
|
+
KokoroTTS: namespace.KokoroTTS,
|
|
1520
|
+
synthesizer,
|
|
1521
|
+
repository: configuration.model.repository,
|
|
1522
|
+
signal,
|
|
1523
|
+
onProgress: report,
|
|
1524
|
+
});
|
|
1525
|
+
throwIfAborted(signal, 'tts-load-cancelled');
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
try {
|
|
1528
|
+
await disposeEngine(synthesizer);
|
|
1529
|
+
} catch (cleanupError) {
|
|
1530
|
+
throw new AggregateError([error, cleanupError], 'Kokoro preparation and model cleanup failed.', { cause: error });
|
|
1531
|
+
}
|
|
1532
|
+
throwIfAborted(signal, 'tts-load-cancelled');
|
|
1533
|
+
throw error;
|
|
1521
1534
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1535
|
+
return completeValue({
|
|
1536
|
+
async synthesize(input, { signal: requestSignal } = {}) {
|
|
1537
|
+
throwIfAborted(requestSignal, 'tts-synthesis-cancelled');
|
|
1538
|
+
let output;
|
|
1539
|
+
try {
|
|
1540
|
+
output = await synthesize(input.text, {
|
|
1541
|
+
voice: input.voice,
|
|
1542
|
+
speed: input.speed,
|
|
1543
|
+
signal: requestSignal,
|
|
1544
|
+
});
|
|
1545
|
+
} catch (error) {
|
|
1546
|
+
throwIfAborted(requestSignal, 'tts-synthesis-cancelled');
|
|
1547
|
+
throw error;
|
|
1548
|
+
}
|
|
1549
|
+
throwIfAborted(requestSignal, 'tts-synthesis-cancelled');
|
|
1550
|
+
const audio = is.instanceCheck(output?.audio, Float32Array)
|
|
1551
|
+
? output.audio
|
|
1552
|
+
: new Float32Array(output?.audio ?? []);
|
|
1553
|
+
return completeValue({
|
|
1554
|
+
audio,
|
|
1555
|
+
sampleRate: output?.sampling_rate,
|
|
1556
|
+
voice: input.voice,
|
|
1557
|
+
});
|
|
1558
|
+
},
|
|
1559
|
+
dispose: function disposeKokoroEngine() { return disposeEngine(synthesizer); },
|
|
1560
|
+
});
|
|
1544
1561
|
}
|
|
1545
1562
|
|
|
1546
1563
|
function callerVoiceIds(configuration) {
|
package/docs/reference/README.md
CHANGED
|
@@ -50,6 +50,7 @@ alone does not make bare module names resolve in a browser.
|
|
|
50
50
|
| Use a shared entity | [Runtime entity modules](runtime-entities.md) and [exact export contracts](core/arcane-entities.md) |
|
|
51
51
|
| Load a reusable HTML component | [Runtime component catalog](runtime-components.md) |
|
|
52
52
|
| Present Light / Dark / System choices with complete labels | [Theme-switcher configuration](runtime-components.md#theme-switcherhtml) |
|
|
53
|
+
| Choose theme-switcher corner radii, label font and group background through host CSS | [Theme-switcher styling properties](runtime-components.md#theme-switcherhtml) |
|
|
53
54
|
| Use optional Core preferences and appearance without masking real failures | [PreferenceStore](runtime-modules.md#preferencestorejs), [SystemAppearance](runtime-modules.md#systemappearancejs), and [Core lookup errors](core-runtime.md#state-and-frames) |
|
|
54
55
|
| Submit complete speech parts immediately and play them in order | [`SpeechPlayback`](runtime-modules.md#speechplaybackjs) and the [basic browser example](ai/browser-speech.md#play-a-complete-array-with-speechplayback) |
|
|
55
56
|
| Preserve exact text in an explicit complete synthesis call | [Exact complete synthesis input](ai/browser-speech.md#exact-complete-synthesis-input) and [managed AI narration](sdk-api.md#managed-ai-narration) |
|
|
@@ -60,6 +61,10 @@ alone does not make bare module names resolve in a browser.
|
|
|
60
61
|
| Compose native Core dispatch, service lifecycle, and stdio hosting | [Native Core runtime](core-runtime.md) |
|
|
61
62
|
| Reuse dependency-ready native services without a renderer | [Core service composition](core-runtime.md#native-service-composition) and [existing ONNX owner](local-ai.md#native-service-owners) |
|
|
62
63
|
| Compose application Core services during source development, independently of local AI | [Development Core services](core-development.md) |
|
|
64
|
+
| Share one application service runtime between its native UI and MCP process | [Shared Core host](core-shared-host.md) |
|
|
65
|
+
| Store complete browser model resources through one DBOPFS owner | [Model resources](ai/browser-decisions.md#shared-dbopfs-model-resources) and [speech routing](ai/browser-speech.md#ordinary-module-routing) |
|
|
66
|
+
| Observe committed storage changes across live application documents | [DBOPFS change subscriptions](runtime-modules.md#dbopfsjs) |
|
|
67
|
+
| Cancel cold speech preparation while preserving other activation interests | [Prepared narration](ai/browser-speech.md#prepare-narration-once-and-replay-stored-audio) |
|
|
63
68
|
| Own an explicitly selected native Codex App Server session | [Codex App Server](codex-app-server.md) |
|
|
64
69
|
| Expose application-owned tools and static resources through Node STDIO | [MCP STDIO server](mcp-stdio.md) |
|
|
65
70
|
| Run application-selected llama.cpp, Ollama or ONNX through Core | [Local AI through Core](local-ai.md) |
|
|
@@ -68,9 +68,9 @@ it does not substitute a quantized graph or another model.
|
|
|
68
68
|
| `revision` | Upstream revision, default `main`; select a stable revision when repeatable model selection is needed. |
|
|
69
69
|
| `device` | Transformers.js backend, default `webgpu`; an explicit supported alternative is caller-owned. No automatic backend fallback. |
|
|
70
70
|
| `dtype` | Exact upstream precision selection; defaults to `fp16` for Laya and `fp32` for Julia. For Laya FP32, supply `dtype: 'fp32'`. Unsupported selections fail at the selected upstream loader rather than being replaced. |
|
|
71
|
-
| `runtime.moduleUrl` | Defaults to the self-contained CDN entry `https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0/dist/transformers.min.js`. The `.web.js` build expects a bundler to resolve its bare ONNX Runtime import and is unsuitable for a native module Worker. An explicit compatible public runtime URL may be supplied. |
|
|
71
|
+
| `runtime.moduleUrl` | Defaults to the self-contained CDN entry `https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0/dist/transformers.min.js`. The `.web.js` build expects a bundler to resolve its bare ONNX Runtime import and is unsuitable for a native module Worker. An explicit compatible public runtime URL may be supplied. With `store`, the SDK stores and materializes the complete selected entry for that activation. |
|
|
72
72
|
| `runtime.wasmPaths` | Optional upstream ONNX WASM location passed to the selected runtime's public environment API. |
|
|
73
|
-
| `store` | Optional existing SDK DBOPFS model or speech artifact store exposing `fetchResource()`. The store remains outside Worker configuration; the SDK bridges the loader's actual resource requests to it. |
|
|
73
|
+
| `store` | Optional existing SDK DBOPFS model or speech artifact store exposing `fetchResource()`. The store remains outside Worker configuration; the SDK uses it for the runtime entry and bridges the loader's actual resource requests to it. |
|
|
74
74
|
|
|
75
75
|
Without `store`, the runtime, tokenizer and weights use normal upstream loading
|
|
76
76
|
and caching. With `store`, the selected loader still owns filenames and model
|
|
@@ -110,17 +110,27 @@ await decisions.load();
|
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
The same `store` may serve other SDK model clients. The application supplies no
|
|
113
|
-
tokenizer/configuration/graph manifest and performs no file routing.
|
|
114
|
-
|
|
113
|
+
tokenizer/configuration/graph manifest and performs no file routing. Explicit
|
|
114
|
+
activation first opens `runtime.moduleUrl` through `store.fetchResource()`.
|
|
115
|
+
The SDK materializes the complete stored entry as a JavaScript object URL for
|
|
116
|
+
the dedicated Worker, without rewriting its content. The configured source URL
|
|
117
|
+
is retained for later activations, so they reuse the same stored resource rather
|
|
118
|
+
than saving a temporary object URL. Construction still starts no download.
|
|
119
|
+
|
|
120
|
+
Inside the dedicated decision Worker, Transformers.js's public `env.fetch` hook routes the
|
|
115
121
|
actual selected tokenizer, configuration, ONNX graph and external-data requests
|
|
116
122
|
to `store.fetchResource(input, options)`. Its competing browser, custom and
|
|
117
123
|
filesystem model caches are disabled only for this explicit stored mode. The
|
|
118
124
|
default upstream ONNX WASM binary and factory preloads use this same hook.
|
|
119
125
|
Native support-file fetches in the dedicated Worker also use the store. The
|
|
120
|
-
runtime
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
default runtime entry is self-contained. Entry materialization does not rewrite
|
|
127
|
+
dependency imports in custom modules or preserve their original `import.meta.url`
|
|
128
|
+
base. Custom stored entries must work from an object URL; relative imports or
|
|
129
|
+
support-file URLs resolved against that module's original location require a
|
|
130
|
+
compatible self-contained entry. Native absolute ESM dependencies and an upstream
|
|
131
|
+
custom factory import outside its preload path remain browser-owned. No second
|
|
132
|
+
application cache or model substitution is introduced. Without `store`, the
|
|
133
|
+
configured runtime URL keeps its direct native module-loading behavior.
|
|
124
134
|
|
|
125
135
|
The store returns `{file, status, statusText, headers, url, redirected}`. The
|
|
126
136
|
Worker reconstructs the complete response with its actual HTTP status and
|
|
@@ -138,9 +148,14 @@ url}`. `completed` counts closed persistent shards; `total` is `null` until the
|
|
|
138
148
|
resource finishes. This progress carries no network-size, rate or ETA fields.
|
|
139
149
|
Cancellation aborts the owned fetch and joins reader/writer cleanup before the
|
|
140
150
|
resource owner becomes available to a later request. Interrupted closed shards
|
|
141
|
-
are retained.
|
|
142
|
-
|
|
143
|
-
|
|
151
|
+
are retained. Each activation owns its runtime object URL and revokes it after
|
|
152
|
+
terminating the Worker on cancellation, failure, unload or disposal. A cancelled
|
|
153
|
+
runtime-entry fetch is joined before its waiting calls settle; a late response
|
|
154
|
+
cannot activate a replacement Worker. Runtime-entry HTTP failures retain the
|
|
155
|
+
complete stored response in `error.response` for developer diagnostics.
|
|
156
|
+
`unload()` and `dispose()` still acknowledge their lifecycle transition
|
|
157
|
+
synchronously; outstanding `load()`/`evaluate()` calls settle after their
|
|
158
|
+
resource cleanup.
|
|
144
159
|
|
|
145
160
|
## Complete rows and results
|
|
146
161
|
|
|
@@ -1065,11 +1065,32 @@ owners. Only `'plain'` selects exact input; omission and other values retain
|
|
|
1065
1065
|
the existing narration cleanup. Applications never supply the internal
|
|
1066
1066
|
`speechInputPrepared` argument.
|
|
1067
1067
|
|
|
1068
|
-
This selection governs SDK text treatment, not
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1068
|
+
This selection governs SDK text treatment, not pronunciation. The browser Kokoro
|
|
1069
|
+
adapter preserves the selected runtime's own text normalization and phonemization,
|
|
1070
|
+
then uses a genuine tokenizer subclass with truncation disabled. Its Kokoro
|
|
1071
|
+
subclass sends every resulting content token through the public
|
|
1072
|
+
`generate_from_ids()` method. Short inputs retain one inference; longer inputs
|
|
1073
|
+
use ordered segments within the selected engine's inference capacity and return
|
|
1074
|
+
all audio in order. This is token-based engine transport, not a character limit,
|
|
1075
|
+
text estimate, summary, or input discard.
|
|
1076
|
+
|
|
1077
|
+
The [selected Kokoro model](https://huggingface.co/onnx-community/Kokoro-82M-v1.0-ONNX/blob/1939ad2a8e416c0acfeecc08a694d14ef25f2231/README.md#python)
|
|
1078
|
+
accepts 510 content tokens plus its two outer padding positions per inference.
|
|
1079
|
+
Each segment receives that padding independently. Upstream voice-style selection,
|
|
1080
|
+
speed, waveform generation and sample rate remain upstream-owned. The SDK reuses
|
|
1081
|
+
one loaded model; it does not copy the private phonemizer, replace instance methods,
|
|
1082
|
+
load a second model, or import another Transformers runtime. Its tokenizer
|
|
1083
|
+
reconstruction uses the same effective selection as the
|
|
1084
|
+
[Kokoro 1.2.1 factory](https://github.com/hexgrad/kokoro/blob/664c76a704021239ba59c84dcbaa4d3dece01fe9/kokoro.js/src/kokoro.js).
|
|
1085
|
+
That factory ignores a `revision` option; this change does not establish revision
|
|
1086
|
+
pinning that the upstream factory does not implement.
|
|
1087
|
+
|
|
1088
|
+
Cancellation is request-local and checked between inference segments and before
|
|
1089
|
+
returning audio. As with the existing Worker contract, cancellation prevents later
|
|
1090
|
+
work and output while an already-running upstream inference may finish. Unload
|
|
1091
|
+
and disposal retain their existing Worker-termination boundary. Source and
|
|
1092
|
+
controlled-fixture coverage of token/audio preservation are distinct from speech
|
|
1093
|
+
quality or execution evidence for a particular model, browser and device.
|
|
1073
1094
|
|
|
1074
1095
|
## Choose a device or reduce memory use
|
|
1075
1096
|
|
|
@@ -1443,7 +1464,7 @@ const store = createDbopfsSpeechArtifactStore({
|
|
|
1443
1464
|
});
|
|
1444
1465
|
```
|
|
1445
1466
|
|
|
1446
|
-
The store exposes `{protocol,tableName,prepare,remove}`. It serializes updates
|
|
1467
|
+
The store exposes `{protocol,tableName,prepare,remove,fetchResource}`. It serializes updates
|
|
1447
1468
|
to one selected authority with Web Locks, downloads each caller-selected file
|
|
1448
1469
|
after explicit provider activation, stores it in DBOPFS, and reopens the stored
|
|
1449
1470
|
file before materialization. Missing storage, an unreadable response, a failed
|
|
@@ -655,35 +655,46 @@ when the default estimator is unavailable or an application owns a more precise
|
|
|
655
655
|
quota view. `downloadConcurrency` must be a positive safe integer and bounds
|
|
656
656
|
the selected file or Range worker pool; the default is four. The mutable result
|
|
657
657
|
contains `kind`, `tableName`, `downloadConcurrency`, the original `adapter`,
|
|
658
|
-
and `ready`, `install`, `ensure`, and `
|
|
658
|
+
and `ready`, `install`, `ensure`, `remove`, and `fetchResource`. `ensure()` preserves the complete
|
|
659
659
|
ordered model set and reports whether it was cached or installed.
|
|
660
660
|
`install(source,{signal,onProgress})` and
|
|
661
661
|
`ensure(source,{signal,onProgress,offline})` publish `cache-check` and
|
|
662
|
-
`download` records using ordered file counts
|
|
663
|
-
when `onProgress` is supplied.
|
|
664
|
-
|
|
665
|
-
|
|
662
|
+
`download` records using ordered file counts and per-member closed-part progress
|
|
663
|
+
when `onProgress` is supplied. Each member reports `completed`, `total`, and
|
|
664
|
+
`unit:'shards'`; streaming totals remain `null` until completion. Records retain
|
|
665
|
+
`activeTransfers`, `transferLimit`, and `transferMode`, with prompt part/lifecycle
|
|
666
|
+
updates and an existing 250 ms heartbeat. They contain no transfer rate or ETA.
|
|
667
|
+
Multi-file sources use up to that many concurrent member workers
|
|
666
668
|
while retaining descriptor order, preserve completed members and completed
|
|
667
669
|
Range parts within members across an interrupted attempt, and fetch only
|
|
668
|
-
missing work on retry.
|
|
669
|
-
`bytes
|
|
670
|
-
start; otherwise those fields remain `null` until an honest aggregate total is
|
|
671
|
-
known. Each missing source member first requests `bytes=0-0`. If a followed
|
|
670
|
+
missing work on retry. Each missing source member first requests the HTTP
|
|
671
|
+
Range `bytes=0-0`. If a followed
|
|
672
672
|
redirect turns that probe into a full `200`, the source probes the final URL directly;
|
|
673
673
|
confirmed support cancels the original body and starts parallel Range workers,
|
|
674
674
|
while refusal keeps the original full response as the single-fetch fallback. A
|
|
675
675
|
valid `Content-Range`, or optional descriptor `bytes` when that header is not
|
|
676
676
|
exposed, supplies the total for deterministic resumable Range parts of roughly
|
|
677
|
-
4 MB each, up to 4,096 parts.
|
|
678
|
-
|
|
677
|
+
4 MB each, up to 4,096 parts. These values belong only to HTTP Range framing,
|
|
678
|
+
not product progress or content admission. Without an observable or declared
|
|
679
|
+
total, one full fetch streams into ordered persistent parts without requiring
|
|
680
|
+
a response length. Existing complete whole files remain readable without
|
|
681
|
+
migration. A later non-206,
|
|
679
682
|
contradictory exposed Range response, or incorrectly framed Range body fails
|
|
680
683
|
rather than silently assembling a partial model. A transfer failure lets peer
|
|
681
684
|
transfers settle; explicit cancellation stops active transfers. Both preserve
|
|
682
685
|
completed members and exact Range parts for retry, while unfinished active
|
|
683
686
|
parts restart after refresh. A complete whole member supersedes its current
|
|
684
687
|
Range fragments.
|
|
685
|
-
Cleanup failure is warned without replacing a usable model.
|
|
686
|
-
|
|
688
|
+
Cleanup failure is warned without replacing a usable model.
|
|
689
|
+
|
|
690
|
+
`fetchResource(input,{signal,onProgress,...requestOptions})` returns the complete
|
|
691
|
+
stored response as `{file,status,statusText,headers,url,redirected}`. SDK model
|
|
692
|
+
loaders share this owner for actual configuration, tokenizer, graph,
|
|
693
|
+
external-data and support-file requests. Complete successful GET resources may
|
|
694
|
+
be reused. Cancellation joins reader/writer cleanup and preserves closed parts
|
|
695
|
+
for supported HTTP resume; a full response restarts that resource without
|
|
696
|
+
duplicating its saved prefix. Resource progress uses `unit:'shards'` with
|
|
697
|
+
`total:null` until completion. Independent resource requests remain concurrent.
|
|
687
698
|
|
|
688
699
|
### Availability and normalization
|
|
689
700
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"sdkVersion": "0.
|
|
3
|
+
"sdkVersion": "0.71.0",
|
|
4
4
|
"environment": {
|
|
5
5
|
"runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
|
|
6
6
|
"minimumVersion": "22.23.2 for Node entrypoints",
|
|
7
7
|
"moduleSystem": "ESM"
|
|
8
8
|
},
|
|
9
|
-
"memberCount":
|
|
9
|
+
"memberCount": 297,
|
|
10
10
|
"runtimeSubpathPatterns": {
|
|
11
11
|
"arcane-os/modules/*": "./runtime/arcane/modules/*",
|
|
12
12
|
"arcane-os/entities/*": "./runtime/arcane/entities/*"
|
|
@@ -535,14 +535,14 @@
|
|
|
535
535
|
"name": "createBrowserDecisionModel",
|
|
536
536
|
"displayName": "createBrowserDecisionModel()",
|
|
537
537
|
"kind": "function",
|
|
538
|
-
"signature": "createBrowserDecisionModel({family,model,revision,device,dtype,runtime}={})",
|
|
538
|
+
"signature": "createBrowserDecisionModel({family,model,revision,device,dtype,runtime,store}={})",
|
|
539
539
|
"entrypoints": ["arcane-os/ai/browser-decisions"],
|
|
540
540
|
"primaryImport": "arcane-os/ai/browser-decisions",
|
|
541
541
|
"group": "Browser typed decisions",
|
|
542
542
|
"summary": "Explicit-load Laya and Julia-1 decisions with caller-selected precision, complete inputs and graph results.",
|
|
543
543
|
"availability": "Browser module Workers and the selected Transformers.js backend; import/construction starts no model load",
|
|
544
544
|
"protocol": "Caller-owned state/question/options rows; five-input decision graph; native Worker request/result lifecycle",
|
|
545
|
-
"normalization": "Full field tokenization, raw logits and unrounded softmax; dtype defaults to fp16 for Laya and fp32 for Julia, with explicit values forwarded unchanged
|
|
545
|
+
"normalization": "Full field tokenization, raw logits and unrounded softmax; dtype defaults to fp16 for Laya and fp32 for Julia, with explicit values forwarded unchanged. Optional existing SDK store routes actual loader resources through fetchResource outside cloned Worker configuration; stored mode disables competing model caches, preserves complete HTTP responses and joins resource cleanup before outstanding calls settle. Omission retains upstream loading; no decision-result persistence or precision fallback"
|
|
546
546
|
},
|
|
547
547
|
{
|
|
548
548
|
"id": "root:acceptWebSocket",
|
|
@@ -3529,10 +3529,10 @@
|
|
|
3529
3529
|
],
|
|
3530
3530
|
"primaryImport": "arcane-os/ai/browser-wasm",
|
|
3531
3531
|
"group": "Browser-WASM local AI",
|
|
3532
|
-
"summary": "Adapts an existing DBOPFS instance into
|
|
3532
|
+
"summary": "Adapts an existing DBOPFS instance into complete resumable model and Fetch-resource storage with bounded transfers and semantic shard progress.",
|
|
3533
3533
|
"availability": "Browser with a ready DBOPFS instance and OPFS support",
|
|
3534
3534
|
"protocol": "Resumable ordered model members and deterministic per-member Range parts over existing DBOPFS method semantics",
|
|
3535
|
-
"normalization": "
|
|
3535
|
+
"normalization": "New full and Range responses stream into ordered persistent parts; complete logical files preserve existing formats without migration. Progress uses file and closed-shard counts with unknown totals until completion. fetchResource(input,{signal,onProgress,...requestOptions}) returns complete {file,status,statusText,headers,url,redirected}; successful complete GET resources may be reused. Cancellation joins reader/writer cleanup and retains closed parts for supported resume. Independent resources remain concurrent"
|
|
3536
3536
|
},
|
|
3537
3537
|
{
|
|
3538
3538
|
"id": "browser-device:getBrowserDeviceClass",
|
|
@@ -3772,7 +3772,7 @@
|
|
|
3772
3772
|
"summary": "Adapts an existing DBOPFS instance into a caller-scoped speech runtime/model artifact store and ordinary module router.",
|
|
3773
3773
|
"availability": "Browser with a ready DBOPFS instance, Web Locks, Fetch or an explicit fetch implementation, File/Blob, and object URLs",
|
|
3774
3774
|
"protocol": "arcane-ai-browser-speech-artifacts/1 ordinary DBOPFS storage and module routing",
|
|
3775
|
-
"normalization": "
|
|
3775
|
+
"normalization": "Exposes prepare, remove and fetchResource; streams new declared files and fetched resources into ordered DBOPFS parts, preserves complete old files and closed parts, reports file/shard progress and joins cancellation cleanup. Known graph mappings remain explicit; dedicated speech and nested module Worker fetches share the store with complete HTTP response metadata. Competing native caches are unavailable only in that Worker lifetime and restored afterward without saved-cache deletion. Native ESM imports and unmapped classic Worker internals remain platform-owned"
|
|
3776
3776
|
},
|
|
3777
3777
|
{
|
|
3778
3778
|
"id": "mail:MAIL_OUTBOX_IDEMPOTENCY_WINDOW_MS",
|
|
@@ -4557,6 +4557,62 @@
|
|
|
4557
4557
|
"availability": "Node with readable input and writable output streams",
|
|
4558
4558
|
"protocol": "arcane/1 over Content-Length framed stdio",
|
|
4559
4559
|
"normalization": "Returns the existing {runtime,closed,close} transport owner; an optional abort signal invokes the same idempotent drain without killing the process, replacing service readiness or cancelling accepted service-lifetime work"
|
|
4560
|
+
},
|
|
4561
|
+
{
|
|
4562
|
+
"id": "core-host:startSharedCoreHost",
|
|
4563
|
+
"name": "startSharedCoreHost",
|
|
4564
|
+
"displayName": "startSharedCoreHost()",
|
|
4565
|
+
"kind": "function",
|
|
4566
|
+
"signature": "startSharedCoreHost({endpoint,application,version,configure,getReplayEvents,signal,onError}={})",
|
|
4567
|
+
"entrypoints": ["arcane-os/core/host"],
|
|
4568
|
+
"primaryImport": "arcane-os/core/host",
|
|
4569
|
+
"group": "Shared Core host",
|
|
4570
|
+
"summary": "Claims an app-selected local endpoint before composing one shared Core runtime.",
|
|
4571
|
+
"availability": "Node named pipes on Windows and Unix-domain sockets on Linux/macOS; Android requires its host adaptation",
|
|
4572
|
+
"protocol": "arcane/1 over local IPC with per-connection request correlation and current-state replay",
|
|
4573
|
+
"normalization": "Returns {runtime,endpoint,closed,close}; configure(runtime,{signal}) registers independently started services after the endpoint claim. Close joins composition, accepted service work, disposal and queued frames before releasing the endpoint. Complete transport/startup/shutdown errors remain observable; an occupied endpoint never invokes the factory"
|
|
4574
|
+
},
|
|
4575
|
+
{
|
|
4576
|
+
"id": "core-host:connectSharedCoreHost",
|
|
4577
|
+
"name": "connectSharedCoreHost",
|
|
4578
|
+
"displayName": "connectSharedCoreHost()",
|
|
4579
|
+
"kind": "function",
|
|
4580
|
+
"signature": "connectSharedCoreHost({endpoint,start,signal,onError}={})",
|
|
4581
|
+
"entrypoints": ["arcane-os/core/host"],
|
|
4582
|
+
"primaryImport": "arcane-os/core/host",
|
|
4583
|
+
"group": "Shared Core host",
|
|
4584
|
+
"summary": "Connects to one selected host and optionally starts an explicit headless entry when absent.",
|
|
4585
|
+
"availability": "Node local IPC and optional explicit child process with diagnostic file",
|
|
4586
|
+
"protocol": "arcane/1 Core client; optional Node startup IPC handshake",
|
|
4587
|
+
"normalization": "Returns {client,endpoint,closed,close,shutdown}. Only ENOENT/ECONNREFUSED selects start {command,args,cwd,env,logFile}; no request retry. Close cancels this connection's request work while preserving the host and accepted service work. Explicit shutdown drains all clients' shared host; cancelling a launch wait does not kill the host"
|
|
4588
|
+
},
|
|
4589
|
+
{
|
|
4590
|
+
"id": "core-host:runSharedCoreHost",
|
|
4591
|
+
"name": "runSharedCoreHost",
|
|
4592
|
+
"displayName": "runSharedCoreHost()",
|
|
4593
|
+
"kind": "function",
|
|
4594
|
+
"signature": "runSharedCoreHost(options)",
|
|
4595
|
+
"entrypoints": ["arcane-os/core/host"],
|
|
4596
|
+
"primaryImport": "arcane-os/core/host",
|
|
4597
|
+
"group": "Shared Core host",
|
|
4598
|
+
"summary": "Runs the explicit headless entry with one endpoint winner and startup result reporting.",
|
|
4599
|
+
"availability": "Node host entry; parent IPC reporting when present",
|
|
4600
|
+
"protocol": "Shared local Core endpoint and Node startup handshake",
|
|
4601
|
+
"normalization": "Accepts startSharedCoreHost options; returns the winning host handle or null after a losing launcher connects to the existing owner and confirms dispatcher response. The loser does not construct services. Reports complete startup errors and never calls process.exit"
|
|
4602
|
+
},
|
|
4603
|
+
{
|
|
4604
|
+
"id": "core-host:startSharedCoreBridge",
|
|
4605
|
+
"name": "startSharedCoreBridge",
|
|
4606
|
+
"displayName": "startSharedCoreBridge()",
|
|
4607
|
+
"kind": "function",
|
|
4608
|
+
"signature": "startSharedCoreBridge({endpoint,start,input=process.stdin,output=process.stdout,signal,onError}={})",
|
|
4609
|
+
"entrypoints": ["arcane-os/core/host"],
|
|
4610
|
+
"primaryImport": "arcane-os/core/host",
|
|
4611
|
+
"group": "Shared Core host",
|
|
4612
|
+
"summary": "Connects a complete framed stdio stream to the shared application's Core host.",
|
|
4613
|
+
"availability": "Node readable/writable streams and local IPC",
|
|
4614
|
+
"protocol": "arcane/1 Content-Length stdio to the selected shared Core endpoint",
|
|
4615
|
+
"normalization": "Returns {endpoint,closed,close}; EOF or cancellation disconnects this client without shutting down shared services. Terminal stream errors settle pending writes and reject closed. Native launch opts in through sharedHost:{endpoint,logFile}; omitted sharedHost preserves ordinary stdio ownership"
|
|
4560
4616
|
}
|
|
4561
4617
|
]
|
|
4562
4618
|
}
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"summary": "Owns provider-selectable chat and the one-time caller-authority browser STT/TTS configuration, lifecycle, synthesis, transcription, and playback boundary.",
|
|
30
30
|
"availability": "Browser + native bridge + TWiN Cloud",
|
|
31
31
|
"protocol": "arcane-ai-browser-speech-configuration/1, AIProviderRuntime arcane-ai-provider/2 routes, globalThis.arcaneEvents, TWiN Cloud HTTPS, Arcane.ollama, Arcane.speech, Android WebView bridge",
|
|
32
|
-
"normalization": "Complete mutable caller-owned browser speech authority, SDK-owned provider registration/replacement/disposal, explicit STT/TTS activation, normalized Whisper STT and Kokoro TTS execution with default capacities 1 and 4 respectively, explicit provider-neutral execution snapshots through status(role,{execution:true}), sticky readiness, canonical window.user readiness without compatibility-event object-identity admission or polling, exact ordered structural calls with required arguments.message, atomic nonblank all-ID tool-result sequencing, complete all-choice streaming/data validation, native Ollama structural adaptation, complete-response then all validated tool callbacks then completion ordering, observed async callbacks, Blob/File transcription, default repeated-formatting-mark removal from cloned outbound TTS input, explicit textFormat:'plain' preservation for complete synthesis/preparation while streaming cleanup remains automatic and caller/visible content stays exact, immediate exact-segment synthesis, per-call voice/speed capture, optional final-segment pause on the existing audio clock, optional per-call terminal playback completion, detached preparation with complete semantic DBOPFS audio reuse and same-owner pending request sharing, independent prepared playback without a model load for cached audio, ordered audio-clock scheduling, playable audio, active-generation TTS operation-failure routing, mute, and cancellation are normalized; provider/model/runtime/voice policy remains caller-owned. Both speech roles default execution.device to auto: webnn-npu when navigator.ml.createContext is exposed, then webgpu when navigator.gpu is exposed, then wasm for CPU execution. Failed backend loads clean up the pool before trying fresh Workers with the same prepared model and dtype; explicit devices never fall back. STT capacity only accepts 1; TTS accepts 1 through 4. Execution snapshots expose requestedDevice, selectedDevice, maxConcurrentRequests, and activeRequestCount for both roles; selectedDevice is null while unloaded and names the successful upstream session backend request when loaded. It does not prove every operation used the physical NPU: WebNN unsupported operations may use WASM, and compatibility depends on the browser, driver, hardware, and graph.",
|
|
32
|
+
"normalization": "Complete mutable caller-owned browser speech authority, SDK-owned provider registration/replacement/disposal, explicit STT/TTS activation, normalized Whisper STT and Kokoro TTS execution with default capacities 1 and 4 respectively, explicit provider-neutral execution snapshots through status(role,{execution:true}), sticky readiness, canonical window.user readiness without compatibility-event object-identity admission or polling, exact ordered structural calls with required arguments.message, atomic nonblank all-ID tool-result sequencing, complete all-choice streaming/data validation, native Ollama structural adaptation, complete-response then all validated tool callbacks then completion ordering, observed async callbacks, Blob/File transcription, default repeated-formatting-mark removal from cloned outbound TTS input, explicit textFormat:'plain' preservation for complete synthesis/preparation while streaming cleanup remains automatic and caller/visible content stays exact, immediate exact-segment synthesis, per-call voice/speed capture, optional final-segment pause on the existing audio clock, optional per-call terminal playback completion, detached preparation with complete semantic DBOPFS audio reuse and same-owner pending request sharing; each preparation owns its pending activation interest, final-interest cancellation aborts that activation while other interests preserve it, complete cache hits start no model, and cancellation after readiness leaves the ready model loaded; independent prepared playback without a model load for cached audio, ordered audio-clock scheduling, playable audio, active-generation TTS operation-failure routing, mute, and cancellation are normalized; provider/model/runtime/voice policy remains caller-owned. Both speech roles default execution.device to auto: webnn-npu when navigator.ml.createContext is exposed, then webgpu when navigator.gpu is exposed, then wasm for CPU execution. Failed backend loads clean up the pool before trying fresh Workers with the same prepared model and dtype; explicit devices never fall back. STT capacity only accepts 1; TTS accepts 1 through 4. Execution snapshots expose requestedDevice, selectedDevice, maxConcurrentRequests, and activeRequestCount for both roles; selectedDevice is null while unloaded and names the successful upstream session backend request when loaded. It does not prove every operation used the physical NPU: WebNN unsupported operations may use WASM, and compatibility depends on the browser, driver, hardware, and graph.",
|
|
33
33
|
"surface": "Native fetchTTS(payload,signal) and fetchSTT(audio,signal) forward caller cancellation separately from their complete Core speech payloads, normalize aborts to AbortError and suppress late results; actual engine interruption remains service-owned. prepareTTSPlayback(payload,signal) silently returns a real Blob or native-speech descriptor; native controls expose finished/released/state/error/pause/resume/stop, and actual release owns the playback lane. fetchTTS and durable prepareTTS report ARCANE_AI_TTS_AUDIO_EXPORT_UNAVAILABLE for native voices. Request-local model overrides remain exact and never mutate the configured model. Browser-speech protocol/event/error/reason constants; default `AI`; read-only `providerRuntime`, `browserSpeechConfiguration`, and `browserSpeechDescriptor`; explicit `providerRuntime.status(role,{execution:true})` snapshots; `streamTTS(text,end,options={})` with optional voice, speed, pauseAfterMs, and waitForPlayback; default speech-input cleanup with SDK-internal preparation metadata and explicit complete-call textFormat:'plain' consumed before engine dispatch; `prepareTTS({parts,storage,identity,textFormat,signal,onState})` returning ordered segments, state, ready, getAudio(index), and cancel(); `playPreparedTTS(prepared,{signal,onState})` returning state, error, finished, pause(), resume(), and stop() with one playback lane per AI and observational state/error callbacks; configure/dispose speech, route lifecycle, declaration-validated chat/stream requests with exact ordered calls; optional streamRequest toolText:{name,field} and onToolText(text,call,displayId) for decoded selected argument text with actual call identity, distinct from ordinary prose and terminal tool execution; synthesis/transcription and playback controls; initializes from current canonical user readiness, installs `window.ai`, and projects `ai-ready` plus active-generation `ai-tts-failure`."
|
|
34
34
|
},
|
|
35
35
|
{
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"availability": "Cross-host in-process runtime; registered providers remain browser, native, or cloud specific",
|
|
92
92
|
"protocol": "arcane-ai-runtime/2, arcane-ai-provider/2, arcane-ai-model-authority/1",
|
|
93
93
|
"normalization": "Normalizes mutable complete per-role routes, lifecycle/status, cancellation, streaming cleanup, capacity-1 FIFO LLM/STT lanes, bounded provider-declared parallel TTS requests with FIFO overflow, default repeated-formatting-mark removal from cloned direct TTS payloads or explicit textFormat:'plain' preservation with the control consumed before engine dispatch, local-only selection, direct LLM history/declaration/terminal structural contracts, complete all-choice content/reasoning FIFO iteration, private automatic provider draining with result-first buffering, terminal-only structural calls, exact per-choice observed/terminal correlation, and a separate complete validated terminal result without creating a fallback.",
|
|
94
|
-
"surface": "Protocol constants; singleton-only `AIProviderRuntime`; `aiProviderRuntime`; `getAIProviderRuntime()`; provider registration/identity/selection, closed three-role and STT/TTS-only validation/configuration/replacement, status/catalog/inspection, startup, per-role load/unload/dispose/cancel, dispose-all, request/stream/speech aliases, SDK-internal speech-input preparation metadata, and
|
|
94
|
+
"surface": "Protocol constants; singleton-only `AIProviderRuntime`; `aiProviderRuntime`; `getAIProviderRuntime()`; provider registration/identity/selection, closed three-role and STT/TTS-only validation/configuration/replacement, status/catalog/inspection, startup, per-role load/unload/dispose/cancel, dispose-all, request/stream/speech aliases, SDK-internal speech-input preparation metadata, and setSpeechMuted(muted,{signal}={}) controls. Each pending unmute caller owns its abortable interest; the final interest aborts the owned activation while other preparation/playback or joining direct TTS-load interests preserve it. A pre-aborted unmute starts no activation. Explicit mute retains global cancellation and joined unload even with an aborted observer signal."
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
"file": "runtime/arcane/modules/AIResponseURLPolicy.js",
|
package/docs/reference/pwa.md
CHANGED
|
@@ -169,6 +169,44 @@ Reference updates finish before file removal begins. Cancellation or a file
|
|
|
169
169
|
error is reported without undoing earlier completed changes; a later call can
|
|
170
170
|
finish retirement. The operation does not claim an all-or-nothing transaction.
|
|
171
171
|
|
|
172
|
+
### Disabled worker retirement on the source server
|
|
173
|
+
|
|
174
|
+
`startDevServer()` and `developApplication()` serve a retirement worker at
|
|
175
|
+
`/arcane-sw.js` when source mode selects a standalone `appsRoot:"."` application
|
|
176
|
+
with explicit `pwa.enabled:false` and that root worker file is absent. This
|
|
177
|
+
browser lifecycle is separate from the import-map filesystem operation above.
|
|
178
|
+
The existing server returns JavaScript with `Cache-Control: no-cache`, including
|
|
179
|
+
when the request carries a former enabled worker's `If-Modified-Since` date.
|
|
180
|
+
It writes no replacement worker file and performs no offline-inventory scan.
|
|
181
|
+
|
|
182
|
+
When the browser updates the existing registration from that URL, the
|
|
183
|
+
retirement worker calls `skipWaiting()` during installation and unregisters its
|
|
184
|
+
own registration during activation. It has no fetch handler, so subsequent
|
|
185
|
+
requests reaching it use the ordinary network path. It never clears caches,
|
|
186
|
+
DBOPFS, profiles or other saved data; it does not enumerate or claim clients,
|
|
187
|
+
reload pages, or start a polling loop. Other service-worker registrations remain
|
|
188
|
+
untouched.
|
|
189
|
+
|
|
190
|
+
Native service-worker update and activation timing remains browser-owned. An
|
|
191
|
+
already open page may retain the neutral retirement worker as its controller
|
|
192
|
+
for the rest of its lifetime, and its already rendered content stays unchanged.
|
|
193
|
+
Unregistration removes the registration from future navigation matching;
|
|
194
|
+
retirement does not promise immediate `controller === null` or automatic page
|
|
195
|
+
refresh. These boundaries follow the platform's
|
|
196
|
+
[activation](https://w3c.github.io/ServiceWorker/#activate-algorithm) and
|
|
197
|
+
[unregistration](https://w3c.github.io/ServiceWorker/#dom-serviceworkerregistration-unregister)
|
|
198
|
+
contracts.
|
|
199
|
+
|
|
200
|
+
An existing root `arcane-sw.js` retains its ordinary authored-file routing,
|
|
201
|
+
including its normal application selection; only an `ENOENT` filesystem result
|
|
202
|
+
selects the retirement response. Omitted PWA configuration, enabled PWA,
|
|
203
|
+
multi-app source workspaces, other URLs and packaged serving keep their existing
|
|
204
|
+
behavior. A separately operated static host must own its worker lifecycle;
|
|
205
|
+
filesystem retirement alone does not establish browser retirement on that host.
|
|
206
|
+
The server path uses the existing portable Node serving owner, and browser
|
|
207
|
+
retirement requires the browser's native service-worker support and an existing
|
|
208
|
+
registration eligible for that update.
|
|
209
|
+
|
|
172
210
|
### Authored host root and app entry
|
|
173
211
|
|
|
174
212
|
An authored `index.html` at the source host root keeps `/` separate from the
|
|
@@ -142,6 +142,10 @@ audio through application-owned DBOPFS. Its immediate handle exposes ordered
|
|
|
142
142
|
segments, preparation state, `ready`, `getAudio(index)`, and `cancel()`.
|
|
143
143
|
`ai.playPreparedTTS(prepared,{signal,onState})` attaches ordered playback and
|
|
144
144
|
returns `state`, `error`, `finished`, `pause()`, `resume()`, and `stop()`.
|
|
145
|
+
Each preparation owns its pending activation interest. Cancelling the final
|
|
146
|
+
interest aborts that activation; other preparation, playback or joining direct
|
|
147
|
+
TTS-load interests preserve it. Cancelling after readiness leaves the model
|
|
148
|
+
loaded, and complete cache hits start no model. Actual cleanup is provider-owned.
|
|
145
149
|
Each AI has one playback lane; replacing playback preserves independent
|
|
146
150
|
preparation. Fully cached replay does not load the speech
|
|
147
151
|
model. See [prepared narration](ai/browser-speech.md#prepare-narration-once-and-replay-stored-audio)
|
|
@@ -421,6 +425,10 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
|
|
|
421
425
|
| `arcaneNativeBuilderProvider` | singleton | `arcane-os/native/portable-provider` | Portable native provider | Node; produces a portable payload rather than an executable host |
|
|
422
426
|
| `arcaneNativeBuilderProvider default export` | singleton | `arcane-os/native/portable-provider` | Portable native provider | Node; produces a portable payload rather than an executable host |
|
|
423
427
|
| `startCoreHost()` | function | `arcane-os/core/host` | Native Core host composition | Node with readable input and writable output streams |
|
|
428
|
+
| `startSharedCoreHost()` | function | `arcane-os/core/host` | Shared Core host | Node local IPC on Windows, Linux and macOS |
|
|
429
|
+
| `connectSharedCoreHost()` | function | `arcane-os/core/host` | Shared Core host | Node local IPC and optional explicit headless startup |
|
|
430
|
+
| `runSharedCoreHost()` | function | `arcane-os/core/host` | Shared Core host | Node headless entry and optional parent startup IPC |
|
|
431
|
+
| `startSharedCoreBridge()` | function | `arcane-os/core/host` | Shared Core host | Node readable/writable streams and local IPC |
|
|
424
432
|
| `readCoreLaunchContext()` | function | `arcane-os/core/host` | Native Core host composition | Node; reads explicitly selected launch JSON |
|
|
425
433
|
| `resolveArcaneDataPaths()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node on Windows, Linux and macOS; Android supplies its data root |
|
|
426
434
|
| `createRepositoryWorkspace()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node with Git; Android requires host process adaptation |
|
|
@@ -4240,6 +4248,17 @@ External source mode accepts both the installed-package route layout and the
|
|
|
4240
4248
|
existing physical runtime layout. Installed-package mode serves the configured
|
|
4241
4249
|
SDK resources directly from `node_modules` at their logical browser URLs;
|
|
4242
4250
|
it does not create a workspace `arcane/` directory or require `arcane.lock.json`.
|
|
4251
|
+
|
|
4252
|
+
For a standalone `appsRoot:'.'` source application with explicit
|
|
4253
|
+
`pwa.enabled:false` and an absent root `arcane-sw.js`, the exact worker URL
|
|
4254
|
+
returns a small retirement worker. Its native install requests `skipWaiting()`;
|
|
4255
|
+
activation unregisters only its own registration, without a fetch handler,
|
|
4256
|
+
storage/cache changes, client claiming or page reload. The browser owns update
|
|
4257
|
+
timing and an open page may retain the neutral controller until its lifetime
|
|
4258
|
+
ends. An existing authored worker, omitted/enabled PWA, multi-app source routes
|
|
4259
|
+
and packaged mode stay unchanged. See the complete
|
|
4260
|
+
[disabled-worker lifecycle](pwa.md#disabled-worker-retirement-on-the-source-server).
|
|
4261
|
+
|
|
4243
4262
|
`host` defaults to `127.0.0.1` and accepts an
|
|
4244
4263
|
explicit network address or hostname. Use `0.0.0.0` for all IPv4 interfaces or
|
|
4245
4264
|
`::` for the platform's IPv6 wildcard listeners. `port` selects the HTTPS
|
|
@@ -4749,6 +4768,14 @@ lifecycle. Package-only apps remain unchanged. The operation generates
|
|
|
4749
4768
|
no packaged output; enabled PWA manifests
|
|
4750
4769
|
are served directly from the selected source resources.
|
|
4751
4770
|
|
|
4771
|
+
The source listener also supplies the
|
|
4772
|
+
[disabled-worker retirement lifecycle](pwa.md#disabled-worker-retirement-on-the-source-server)
|
|
4773
|
+
for explicit disabled standalone PWA configuration when the root worker is
|
|
4774
|
+
absent. This does not grant filesystem retirement authority or change the
|
|
4775
|
+
refresh operation's preservation of authored files. Browser activation and
|
|
4776
|
+
unregistration proceed through the native lifecycle without clearing saved data
|
|
4777
|
+
or reloading an open page.
|
|
4778
|
+
|
|
4752
4779
|
### Signature and result
|
|
4753
4780
|
|
|
4754
4781
|
```text
|
|
@@ -8658,8 +8685,156 @@ async function bundleMoonCheeseRuntime(outputRoot){
|
|
|
8658
8685
|
The ordinary provider invokes this owner only for an explicit native local-AI
|
|
8659
8686
|
selection. See [selected local-AI runtimes](core-native-packaging.md#selected-local-ai-runtimes).
|
|
8660
8687
|
|
|
8688
|
+
## startSharedCoreHost()
|
|
8689
|
+
|
|
8690
|
+
### Overview
|
|
8691
|
+
|
|
8692
|
+
Owns one application's Core runtime at an explicitly selected local endpoint.
|
|
8693
|
+
It claims the endpoint before constructing services, so UI and MCP processes
|
|
8694
|
+
can share the same service state without constructing duplicate owners.
|
|
8695
|
+
|
|
8696
|
+
### Signature and result
|
|
8697
|
+
|
|
8698
|
+
```text
|
|
8699
|
+
startSharedCoreHost({endpoint,application,version,configure,getReplayEvents,signal,onError}={})
|
|
8700
|
+
```
|
|
8701
|
+
|
|
8702
|
+
Resolves `{runtime,endpoint,closed,close}`. `configure(runtime,{signal})`
|
|
8703
|
+
registers services through the existing runtime; services start independently.
|
|
8704
|
+
Optional synchronous `getReplayEvents()` supplies current `{event,data}`
|
|
8705
|
+
snapshots, never historical replies. `close()` joins composition, accepted
|
|
8706
|
+
service work, service cleanup and queued frames before releasing the endpoint.
|
|
8707
|
+
Request-lifetime work is cancelled; accepted service-lifetime work remains
|
|
8708
|
+
service-owned. The supplied signal begins the same shutdown. `closed` rejects
|
|
8709
|
+
on shutdown failure; `onError` observes transport/background failures, with
|
|
8710
|
+
complete console diagnostics by default.
|
|
8711
|
+
|
|
8712
|
+
### Availability and example
|
|
8713
|
+
|
|
8714
|
+
Node supports app-selected named pipes on Windows and Unix-domain sockets on
|
|
8715
|
+
Linux/macOS; Android needs its host adaptation. Parent directories must exist.
|
|
8716
|
+
An occupied endpoint returns native `EADDRINUSE` with
|
|
8717
|
+
`coreHostPhase:'listen'`, without calling `configure`. Native failures remain
|
|
8718
|
+
observable; the SDK does not delete an existing endpoint to take ownership.
|
|
8719
|
+
|
|
8720
|
+
```js
|
|
8721
|
+
import {startSharedCoreHost} from 'arcane-os/core/host';
|
|
8722
|
+
|
|
8723
|
+
const host = await startSharedCoreHost(
|
|
8724
|
+
{
|
|
8725
|
+
endpoint: selectedEndpoint,
|
|
8726
|
+
application: {id: 'moon-cheese-ledger'},
|
|
8727
|
+
configure(runtime) {
|
|
8728
|
+
runtime.registerService(ledgerService);
|
|
8729
|
+
}
|
|
8730
|
+
}
|
|
8731
|
+
);
|
|
8732
|
+
// The application owner later calls await host.close().
|
|
8733
|
+
```
|
|
8734
|
+
|
|
8735
|
+
`selectedEndpoint` and `ledgerService` belong to the application. See
|
|
8736
|
+
[shared Core hosting](core-shared-host.md) for correlation, replay and cleanup.
|
|
8737
|
+
|
|
8738
|
+
## connectSharedCoreHost()
|
|
8739
|
+
|
|
8740
|
+
### Overview and signature
|
|
8741
|
+
|
|
8742
|
+
Connects to the selected application host, optionally starting an explicit
|
|
8743
|
+
headless entry when that endpoint is absent.
|
|
8744
|
+
|
|
8745
|
+
```text
|
|
8746
|
+
connectSharedCoreHost({endpoint,start,signal,onError}={})
|
|
8747
|
+
```
|
|
8748
|
+
|
|
8749
|
+
Resolves `{client,endpoint,closed,close,shutdown}`. `client` is the existing Core
|
|
8750
|
+
client with current-runtime replay. Optional `start` is
|
|
8751
|
+
`{command,args=[],cwd,env,logFile}`; its entry calls `runSharedCoreHost()`.
|
|
8752
|
+
Only `ENOENT` or `ECONNREFUSED` selects startup. Arguments/environment are
|
|
8753
|
+
captured at acceptance, diagnostics append completely to the selected log,
|
|
8754
|
+
and requests are never automatically repeated. Other connection/start errors
|
|
8755
|
+
propagate; `closed` rejects on transport failure. Node/platform availability
|
|
8756
|
+
matches `startSharedCoreHost()`.
|
|
8757
|
+
|
|
8758
|
+
`close()` disconnects this client and cancels only its request-lifetime work;
|
|
8759
|
+
it preserves the host and accepted service work. Cancelling startup wait does
|
|
8760
|
+
not kill a host another client may use. No startup timeout is imposed; supply
|
|
8761
|
+
`signal` when needed. `shutdown(invokeOptions)` explicitly invokes
|
|
8762
|
+
`core.host.shutdown`, drains all shared services and returns `{state:'closed'}`
|
|
8763
|
+
before disconnecting. It affects every client; cancellation after acceptance
|
|
8764
|
+
does not reverse it. A drain failure returns the full Core error.
|
|
8765
|
+
|
|
8766
|
+
```js
|
|
8767
|
+
import {connectSharedCoreHost} from 'arcane-os/core/host';
|
|
8768
|
+
|
|
8769
|
+
const connection = await connectSharedCoreHost(
|
|
8770
|
+
{endpoint: selectedEndpoint}
|
|
8771
|
+
);
|
|
8772
|
+
try {
|
|
8773
|
+
const catalog = await connection.client.invoke(
|
|
8774
|
+
'catalog.read',
|
|
8775
|
+
{}
|
|
8776
|
+
);
|
|
8777
|
+
console.log(catalog);
|
|
8778
|
+
} finally {
|
|
8779
|
+
await connection.close();
|
|
8780
|
+
}
|
|
8781
|
+
```
|
|
8782
|
+
|
|
8783
|
+
## runSharedCoreHost()
|
|
8784
|
+
|
|
8785
|
+
Accepts the same options as `startSharedCoreHost(options)` and adds the headless
|
|
8786
|
+
startup handshake. The winning launcher returns the host handle. A losing
|
|
8787
|
+
launcher connects to the existing owner, confirms dispatcher response and
|
|
8788
|
+
returns `null` without constructing its own services. Startup success or the
|
|
8789
|
+
complete error reaches an existing parent Node IPC channel; the helper never
|
|
8790
|
+
calls `process.exit()`. Host lifetime, errors and platform requirements retain
|
|
8791
|
+
the contracts above.
|
|
8792
|
+
|
|
8793
|
+
```js
|
|
8794
|
+
import {runSharedCoreHost} from 'arcane-os/core/host';
|
|
8795
|
+
|
|
8796
|
+
const host = await runSharedCoreHost(applicationHostOptions);
|
|
8797
|
+
await host?.closed;
|
|
8798
|
+
```
|
|
8799
|
+
|
|
8800
|
+
The application supplies `applicationHostOptions`, including its endpoint and
|
|
8801
|
+
service factory. This is the entry selected by the `start` option of
|
|
8802
|
+
`connectSharedCoreHost()`.
|
|
8803
|
+
|
|
8804
|
+
## startSharedCoreBridge()
|
|
8805
|
+
|
|
8806
|
+
Connects one framed stdio client to the shared host, preserving complete
|
|
8807
|
+
requests, responses and events through the existing `arcane/1` transport.
|
|
8808
|
+
|
|
8809
|
+
```text
|
|
8810
|
+
startSharedCoreBridge({endpoint,start,input=process.stdin,output=process.stdout,signal,onError}={})
|
|
8811
|
+
```
|
|
8812
|
+
|
|
8813
|
+
Resolves `{endpoint,closed,close}`. `start` and connection errors follow
|
|
8814
|
+
`connectSharedCoreHost()`. EOF, `close()` or cancellation disconnects this
|
|
8815
|
+
client only. Terminal stream errors settle pending writes and reject `closed`;
|
|
8816
|
+
service shutdown is separate. Node readable/writable streams and the selected
|
|
8817
|
+
local IPC endpoint are required. This is Core framing, not external MCP framing.
|
|
8818
|
+
|
|
8819
|
+
```js
|
|
8820
|
+
import {startSharedCoreBridge} from 'arcane-os/core/host';
|
|
8821
|
+
|
|
8822
|
+
const bridge = await startSharedCoreBridge(
|
|
8823
|
+
{endpoint: selectedEndpoint}
|
|
8824
|
+
);
|
|
8825
|
+
await bridge.closed;
|
|
8826
|
+
```
|
|
8827
|
+
|
|
8828
|
+
Generated native entries select this bridge only through explicit launch
|
|
8829
|
+
context `sharedHost:{endpoint,logFile}`. Omitting it preserves ordinary
|
|
8830
|
+
stdio-owned Core lifetime. See [native shared-host launch](core-shared-host.md#native-launch-and-platforms).
|
|
8831
|
+
|
|
8661
8832
|
## startCoreHost()
|
|
8662
8833
|
|
|
8834
|
+
For independent UI and MCP processes sharing one application runtime, use the
|
|
8835
|
+
[shared host functions](#startsharedcorehost) above. Ordinary `startCoreHost()`
|
|
8836
|
+
retains its existing stdio-owned lifetime.
|
|
8837
|
+
|
|
8663
8838
|
### Overview
|
|
8664
8839
|
|
|
8665
8840
|
Composes explicitly supplied application services with the shared Core runtime
|
|
@@ -9338,7 +9513,7 @@ Existing chat and speech providers are unchanged.
|
|
|
9338
9513
|
### Signature and result
|
|
9339
9514
|
|
|
9340
9515
|
```text
|
|
9341
|
-
createBrowserDecisionModel({family,model,revision,device,dtype,runtime}={})
|
|
9516
|
+
createBrowserDecisionModel({family,model,revision,device,dtype,runtime,store}={})
|
|
9342
9517
|
```
|
|
9343
9518
|
|
|
9344
9519
|
```js
|
|
@@ -9373,6 +9548,15 @@ selections report its failure without precision fallback. For Laya FP32, pass
|
|
|
9373
9548
|
Cancellation terminates that client's Worker and rejects its in-flight work.
|
|
9374
9549
|
No decisions are inserted into chat history or durable storage.
|
|
9375
9550
|
|
|
9551
|
+
Optional `store` is an existing SDK DBOPFS model or speech store exposing
|
|
9552
|
+
`fetchResource(input, options)`. It remains outside cloned Worker configuration.
|
|
9553
|
+
The selected loader routes its actual model/configuration/tokenizer resources
|
|
9554
|
+
through that owner and disables competing model caches in this explicit mode.
|
|
9555
|
+
Omission preserves ordinary upstream loading. Native ESM imports retain their
|
|
9556
|
+
platform loader. Resource progress counts closed shards; cancellation joins
|
|
9557
|
+
resource cleanup before outstanding `load()`/`evaluate()` calls settle.
|
|
9558
|
+
`unload()` and `dispose()` acknowledge their state transition synchronously.
|
|
9559
|
+
|
|
9376
9560
|
### Example
|
|
9377
9561
|
|
|
9378
9562
|
```js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arcane-os",
|
|
3
|
-
"version": "0.71.
|
|
3
|
+
"version": "0.71.2",
|
|
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",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
|
|
124
124
|
"test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
|
|
125
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",
|
|
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-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/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/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",
|
|
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-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/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/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",
|
|
127
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",
|
|
128
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",
|
|
129
129
|
"check": "node tools/check-source.mjs && npm test",
|
package/src/dev-server.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
readWorkspaceAssetVersion,resolveAssetReference,rewriteAssetReferences,versionAssetUrl
|
|
18
18
|
} from './import-map.mjs';
|
|
19
19
|
import {createPwaArtifacts,selectPwaFiles} from './pwa.mjs';
|
|
20
|
+
import {createPwaRetirementWorkerScript} from './pwa-worker.mjs';
|
|
20
21
|
|
|
21
22
|
const is = new Is(false);
|
|
22
23
|
|
|
@@ -1301,6 +1302,30 @@ async function startOwnedDevServer({
|
|
|
1301
1302
|
);
|
|
1302
1303
|
return;
|
|
1303
1304
|
}
|
|
1305
|
+
if (mode === 'source' && selectedRoutes.config.appsRoot === '.'
|
|
1306
|
+
&& selectedRoutes.app?.pwa?.enabled === false && target.path === '/arcane-sw.js') {
|
|
1307
|
+
let rootWorkerExists = true;
|
|
1308
|
+
try {
|
|
1309
|
+
await lstat(path.join(selectedRoutes.workspaceRoot, 'arcane-sw.js'));
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
1312
|
+
rootWorkerExists = false;
|
|
1313
|
+
}
|
|
1314
|
+
if (!rootWorkerExists) {
|
|
1315
|
+
response.setHeader('Cache-Control', 'no-cache');
|
|
1316
|
+
await serveGeneratedRepresentation(
|
|
1317
|
+
fileServer,
|
|
1318
|
+
request,
|
|
1319
|
+
response,
|
|
1320
|
+
{
|
|
1321
|
+
// A former enabled worker's date cannot validate this retirement script.
|
|
1322
|
+
contentType: contentTypes.js,
|
|
1323
|
+
body: createPwaRetirementWorkerScript
|
|
1324
|
+
}
|
|
1325
|
+
);
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1304
1329
|
const rootRequest = segments.length === 0;
|
|
1305
1330
|
if (rootRequest && !selectedRoutes.rootDocument) {
|
|
1306
1331
|
response.writeHead(302,{location:`${selectedRoutes.startPath}${target.search}`});
|
package/src/pwa-worker.mjs
CHANGED
|
@@ -2,6 +2,25 @@ export function createPwaWorkerScript(manifest, clientUrl = 'arcane/sdk/pwa.mjs'
|
|
|
2
2
|
return `(${installPwaWorker.toString()})(${JSON.stringify(manifest, null, 4)}, ${JSON.stringify(clientUrl)});\n`;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
export function createPwaRetirementWorkerScript() {
|
|
6
|
+
return `(${installPwaRetirementWorker.toString()})();\n`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function installPwaRetirementWorker() {
|
|
10
|
+
self.addEventListener(
|
|
11
|
+
'install',
|
|
12
|
+
function installRetiringWorker(event) {
|
|
13
|
+
event.waitUntil(self.skipWaiting());
|
|
14
|
+
}
|
|
15
|
+
);
|
|
16
|
+
self.addEventListener(
|
|
17
|
+
'activate',
|
|
18
|
+
function activateRetiringWorker(event) {
|
|
19
|
+
event.waitUntil(self.registration.unregister());
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
5
24
|
function installPwaWorker(manifest, clientUrl) {
|
|
6
25
|
const scope = self.registration.scope;
|
|
7
26
|
const scopeOrigin = new URL(scope).origin;
|