arcane-os 0.5.13 → 0.5.15
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 +32 -0
- package/README.md +33 -8
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +11 -10
- package/browser-runtime/ai/browser-wllama-runtime.mjs +2 -1
- package/browser-runtime/ai/model-controller.mjs +6 -5
- package/browser-runtime/ai/speech-worker-client.mjs +45 -3
- package/browser-runtime/event-manager.mjs +2 -1
- package/browser-runtime/logging.mjs +58 -0
- package/docs/architecture.md +1 -1
- package/docs/reference/README.md +9 -7
- package/docs/reference/ai/browser-speech.md +90 -1
- package/docs/reference/ai/twin-cloud.md +1 -1
- package/docs/reference/cli.md +1 -1
- package/docs/reference/core/arcane-ai-contracts.md +1 -1
- package/docs/reference/inventory/package-api.json +34 -6
- package/docs/reference/inventory/runtime-components.json +1 -1
- package/docs/reference/inventory/runtime-modules.json +4 -4
- package/docs/reference/protocols.md +5 -5
- package/docs/reference/runtime-modules.md +70 -15
- package/docs/reference/sdk-api.md +120 -11
- package/package.json +4 -3
- package/runtime/arcane/components/app-bar.html +3 -1
- package/runtime/arcane/components/chat.html +22 -20
- package/runtime/arcane/components/dashboard-config.html +3 -1
- package/runtime/arcane/components/data-maintenance.html +4 -2
- package/runtime/arcane/components/data-view.html +3 -1
- package/runtime/arcane/components/directory-picker.html +3 -1
- package/runtime/arcane/components/file-manager.html +11 -9
- package/runtime/arcane/components/header.html +5 -3
- package/runtime/arcane/components/markdown-document.html +3 -1
- package/runtime/arcane/components/markdown-editor.html +4 -2
- package/runtime/arcane/components/modal.html +3 -1
- package/runtime/arcane/components/screen-capture.html +4 -2
- package/runtime/arcane/components/speech.html +10 -8
- package/runtime/arcane/components/table.html +3 -1
- package/runtime/arcane/components/voice-transcription.html +8 -6
- package/runtime/arcane/entities/Chat.js +5 -4
- package/runtime/arcane/entities/User.js +2 -1
- package/runtime/arcane/modules/AI.js +442 -292
- package/runtime/arcane/modules/AIProviderRuntime.js +359 -82
- package/runtime/arcane/modules/CommunicationAppController.js +2 -1
- package/runtime/arcane/modules/ComponentContracts.js +3 -2
- package/runtime/arcane/modules/ConversationTimebox.js +2 -1
- package/runtime/arcane/modules/DBOPFS.js +5 -4
- package/runtime/arcane/modules/Errors.js +3 -14
- package/runtime/arcane/modules/HTMLImport.js +4 -3
- package/runtime/arcane/modules/LocalAIReadinessController.js +2 -1
- package/runtime/arcane/modules/MD.js +3 -2
- package/runtime/arcane/modules/MailOutbox.mjs +2 -1
- package/runtime/arcane/modules/PersistentAIChatSession.js +2 -1
- package/runtime/arcane/modules/ScreenCapture.js +2 -1
- package/runtime/arcane/modules/SpeechPlayback.js +206 -40
- package/runtime/arcane/modules/ThemeBootstrap.js +3 -2
- package/runtime/arcane/modules/ToolCallRouter.js +2 -1
- package/src/import-map.mjs +65 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.5.15
|
|
6
|
+
|
|
7
|
+
- Let `SpeechPlayback.prepare()` submit every complete segment immediately when
|
|
8
|
+
an `AI.fetchTTS` client advertises positive TTS execution capacity. The AI
|
|
9
|
+
provider queue retains bounded FIFO admission (four synthesis slots by
|
|
10
|
+
default), while indexed Blob URLs keep playback in exact input order even
|
|
11
|
+
when later segments finish first.
|
|
12
|
+
- Preserve the serialized one-segment-lookahead path for native and custom
|
|
13
|
+
speech clients that do not advertise provider execution capacity. Pause and
|
|
14
|
+
Resume keep the same audio element; Stop aborts all owned synthesis; Replay
|
|
15
|
+
retains completed and pending provider segments while retrying only failed
|
|
16
|
+
missing segments, including failures with falsy rejection values.
|
|
17
|
+
- Add basic copyable `SpeechPlayback` examples and synchronize the installed
|
|
18
|
+
reference, current release identities, generated documentation site, and
|
|
19
|
+
focused behavioral contract source for the new admission model.
|
|
20
|
+
|
|
21
|
+
## 0.5.14
|
|
22
|
+
|
|
23
|
+
- Add the shared `arcane-os/logging` console owner using the existing
|
|
24
|
+
`user.developer` preference. Route first-party runtime diagnostics and
|
|
25
|
+
default model loggers through this owner; preserve warnings and errors
|
|
26
|
+
when developer mode is disabled.
|
|
27
|
+
- Trace complete speech API inputs, generation queues, Worker requests and
|
|
28
|
+
responses, decoded audio, scheduled playback, natural completion,
|
|
29
|
+
cancellation, and failure under that same developer preference. Preserve
|
|
30
|
+
caller text, voice, speed, language selection, and playback behavior.
|
|
31
|
+
- Preserve complete cloud AI error responses and retry HTTP 429 overload
|
|
32
|
+
responses after three seconds with the original request and cancellation
|
|
33
|
+
signal. Keep partial streams and tool callbacks outside the retry path.
|
|
34
|
+
- Preserve exact URL import-map aliases while adding matching versioned
|
|
35
|
+
aliases, including authored imports, scopes, and generated dependency paths.
|
|
36
|
+
|
|
5
37
|
## 0.5.13
|
|
6
38
|
|
|
7
39
|
- Derive local browser resource queries from the selected SDK package version
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ version-locked SDK runtime, while an integrated Arcane checkout uses its live
|
|
|
19
19
|
`arcane/` runtime. Both profiles preserve the same app URLs, theme, packaging,
|
|
20
20
|
event, cancellation, and browser run contracts.
|
|
21
21
|
|
|
22
|
-
This checkout defines the `0.5.
|
|
22
|
+
This checkout defines the `0.5.15` SDK contract. Applications pin one exact npm
|
|
23
23
|
version and lockfile; registry state is deliberately not baked into application
|
|
24
24
|
artifacts.
|
|
25
25
|
|
|
@@ -35,7 +35,7 @@ Create one browser application, install its pinned SDK, and start its source
|
|
|
35
35
|
server:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npx arcane-os@0.5.
|
|
38
|
+
npx arcane-os@0.5.15 new hello-speech --path ./hello-speech --target browser
|
|
39
39
|
cd hello-speech
|
|
40
40
|
npm install
|
|
41
41
|
npm run dev
|
|
@@ -103,6 +103,31 @@ order, but playback waits for earlier segments and plays exact input order.
|
|
|
103
103
|
Each slot owns a Worker/model session, so raising capacity trades memory for
|
|
104
104
|
latency.
|
|
105
105
|
|
|
106
|
+
If your app already has complete segments in an array, let the shared
|
|
107
|
+
`SpeechPlayback` owner submit them as soon as they are available:
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
import SpeechPlayback from 'arcane/SpeechPlayback';
|
|
111
|
+
|
|
112
|
+
const audio = document.body.appendChild(document.createElement('audio'));
|
|
113
|
+
audio.controls = true;
|
|
114
|
+
const narration = new SpeechPlayback({audio, speech: ai});
|
|
115
|
+
const speakAll = document.body.appendChild(document.createElement('button'));
|
|
116
|
+
speakAll.textContent = 'Speak all segments';
|
|
117
|
+
speakAll.addEventListener('click', async function speakAllSegments() {
|
|
118
|
+
await ai.setSpeechMuted(false);
|
|
119
|
+
await narration.prepare({
|
|
120
|
+
parts: ['First segment.', 'Second segment.', 'Third segment.'],
|
|
121
|
+
autoplay: true
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
With browser speech's default `auto` / 4 configuration, all three complete
|
|
127
|
+
segments enter the provider queue immediately, up to four synthesize at once,
|
|
128
|
+
and the audio still plays first, second, third. Native or custom speech without
|
|
129
|
+
advertised provider execution capacity stays serialized with one lookahead.
|
|
130
|
+
|
|
106
131
|
Continue with [streaming chunks, device selection, cancellation, status, and cleanup](https://github.com/TheWizardNexus/arcane-os-sdk/blob/main/docs/reference/ai/browser-speech.md),
|
|
107
132
|
the [tiny TWiN Cloud request and saved-preference migration](https://github.com/TheWizardNexus/arcane-os-sdk/blob/main/docs/reference/ai/twin-cloud.md),
|
|
108
133
|
or the [maintained WASM voice-chat example](https://github.com/TheWizardNexus/arcane-os-sdk/tree/main/examples/wasm-ai-demo).
|
|
@@ -297,7 +322,7 @@ uses the same controller for automatic memory extraction.
|
|
|
297
322
|
Create a new repository-shaped Arcane application with the exact stable SDK:
|
|
298
323
|
|
|
299
324
|
```bash
|
|
300
|
-
npx arcane-os@0.5.
|
|
325
|
+
npx arcane-os@0.5.15 new my-app --path ./my-app --target portable --git
|
|
301
326
|
cd my-app
|
|
302
327
|
npm install
|
|
303
328
|
npm run dev
|
|
@@ -307,7 +332,7 @@ To enroll an existing repository, install the exact SDK and initialize only
|
|
|
307
332
|
missing Arcane files:
|
|
308
333
|
|
|
309
334
|
```bash
|
|
310
|
-
npm install --save-dev --save-exact arcane-os@0.5.
|
|
335
|
+
npm install --save-dev --save-exact arcane-os@0.5.15
|
|
311
336
|
npm exec -- arcane init my-app --target portable
|
|
312
337
|
```
|
|
313
338
|
|
|
@@ -323,7 +348,7 @@ npm exec -- arcane-os targets
|
|
|
323
348
|
No global SDK install or standalone Arcane CLI is required. The application
|
|
324
349
|
repository's exact npm dependency and lockfile own the CLI and toolchain version.
|
|
325
350
|
|
|
326
|
-
Use `npx arcane-os@0.5.
|
|
351
|
+
Use `npx arcane-os@0.5.15` for the initial bootstrap because it names this npm
|
|
327
352
|
package explicitly; bare `npx arcane` outside an installed project could resolve
|
|
328
353
|
a different package. Both installed commands invoke the same headless toolchain.
|
|
329
354
|
Project-local npm scripts use the SDK pinned by that app's `package-lock.json`,
|
|
@@ -343,7 +368,7 @@ node ./bin/arcane.mjs new local-app --path ../local-app --target portable --git
|
|
|
343
368
|
|
|
344
369
|
# From the generated app repository
|
|
345
370
|
cd ../local-app
|
|
346
|
-
npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.5.
|
|
371
|
+
npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.5.15.tgz
|
|
347
372
|
npm ci
|
|
348
373
|
```
|
|
349
374
|
|
|
@@ -352,7 +377,7 @@ same location. The lockfile retains the selected package dependency while
|
|
|
352
377
|
Arcane uses the installed package name and version. Local directory `file:` dependencies are not
|
|
353
378
|
accepted because npm may install them as links; use a packed `.tgz`. A GitHub
|
|
354
379
|
runner also needs that tarball at the locked path. After publication, replace
|
|
355
|
-
the local declaration with the exact `arcane-os@0.5.
|
|
380
|
+
the local declaration with the exact `arcane-os@0.5.15` registry package and
|
|
356
381
|
commit the regenerated lock.
|
|
357
382
|
|
|
358
383
|
Generated repositories use `npm ci --ignore-scripts` in CI. Run dependency
|
|
@@ -491,7 +516,7 @@ package installation, or assertions.
|
|
|
491
516
|
|
|
492
517
|
## Current target support
|
|
493
518
|
|
|
494
|
-
Version `0.5.
|
|
519
|
+
Version `0.5.15` exposes one browser target and five explicitly paired
|
|
495
520
|
native development targets: a non-runnable portable directory, a
|
|
496
521
|
Windows x64 unsigned-local-test EXE bundle, Linux x64 and Linux ARM64
|
|
497
522
|
unsigned-local-test DEBs, and an Android development-signed APK. The
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from '../logging.mjs';
|
|
1
2
|
import {
|
|
2
3
|
ARCANE_AI_ADAPTER_PROTOCOL,
|
|
3
4
|
ArcaneAIError,
|
|
@@ -1148,7 +1149,7 @@ export function createDbopfsModelStore({
|
|
|
1148
1149
|
try {
|
|
1149
1150
|
await removeMemberRangeParts(modelName, { total });
|
|
1150
1151
|
} catch (error) {
|
|
1151
|
-
|
|
1152
|
+
arcaneLogging.warn?.(
|
|
1152
1153
|
"Arcane could not remove superseded browser model range parts.",
|
|
1153
1154
|
error,
|
|
1154
1155
|
);
|
|
@@ -1163,7 +1164,7 @@ export function createDbopfsModelStore({
|
|
|
1163
1164
|
total: ranges[0]?.total ?? null,
|
|
1164
1165
|
});
|
|
1165
1166
|
} catch (error) {
|
|
1166
|
-
|
|
1167
|
+
arcaneLogging.warn?.(
|
|
1167
1168
|
"Arcane could not remove superseded browser model range parts.",
|
|
1168
1169
|
error,
|
|
1169
1170
|
);
|
|
@@ -1195,7 +1196,7 @@ export function createDbopfsModelStore({
|
|
|
1195
1196
|
await removeMemberRangeParts(modelName, { total });
|
|
1196
1197
|
}
|
|
1197
1198
|
} catch (error) {
|
|
1198
|
-
|
|
1199
|
+
arcaneLogging.warn?.(
|
|
1199
1200
|
"Arcane could not remove superseded browser model range parts.",
|
|
1200
1201
|
error,
|
|
1201
1202
|
);
|
|
@@ -2404,7 +2405,7 @@ function callbackStreamHandle({ runtime, request, signal, onSettled }) {
|
|
|
2404
2405
|
Promise.resolve().then(() => this.cancel(
|
|
2405
2406
|
"The stream consumer stopped before completion.",
|
|
2406
2407
|
)).catch(function reportBrowserWasmStreamReturnCancellationFailure(error) {
|
|
2407
|
-
|
|
2408
|
+
arcaneLogging.error("Arcane browser-WASM stream early-return cancellation failed.", error);
|
|
2408
2409
|
});
|
|
2409
2410
|
return { value, done: true };
|
|
2410
2411
|
},
|
|
@@ -2432,7 +2433,7 @@ function validatedV1StreamHandle(opened, request) {
|
|
|
2432
2433
|
Promise.resolve().then(function cancelInvalidV1StreamHandle() {
|
|
2433
2434
|
return opened.cancel("The v1 provider returned an invalid stream handle.");
|
|
2434
2435
|
}).catch(function reportInvalidV1StreamCleanupFailure(error) {
|
|
2435
|
-
|
|
2436
|
+
arcaneLogging.error("Arcane invalid v1 stream cleanup failed.", error);
|
|
2436
2437
|
});
|
|
2437
2438
|
}
|
|
2438
2439
|
throw fail(
|
|
@@ -2447,7 +2448,7 @@ function validatedV1StreamHandle(opened, request) {
|
|
|
2447
2448
|
Promise.resolve().then(function cancelRejectedV1Iterator() {
|
|
2448
2449
|
return opened.cancel(error);
|
|
2449
2450
|
}).catch(function reportRejectedV1IteratorCleanupFailure(cleanupError) {
|
|
2450
|
-
|
|
2451
|
+
arcaneLogging.error("Arcane rejected v1 stream iterator cleanup failed.", cleanupError);
|
|
2451
2452
|
});
|
|
2452
2453
|
throw error;
|
|
2453
2454
|
}
|
|
@@ -2455,7 +2456,7 @@ function validatedV1StreamHandle(opened, request) {
|
|
|
2455
2456
|
Promise.resolve().then(function cancelInvalidV1Iterator() {
|
|
2456
2457
|
return opened.cancel("The v1 provider returned an invalid stream iterator.");
|
|
2457
2458
|
}).catch(function reportInvalidV1IteratorCleanupFailure(error) {
|
|
2458
|
-
|
|
2459
|
+
arcaneLogging.error("Arcane invalid v1 stream iterator cleanup failed.", error);
|
|
2459
2460
|
});
|
|
2460
2461
|
throw fail(
|
|
2461
2462
|
"ARCANE_AI_INVALID_PROVIDER_RESULT",
|
|
@@ -2539,13 +2540,13 @@ function validatedV1StreamHandle(opened, request) {
|
|
|
2539
2540
|
Promise.resolve().then(function returnUnderlyingV1Stream() {
|
|
2540
2541
|
return iterator.return(value);
|
|
2541
2542
|
}).catch(function reportUnderlyingV1StreamReturnFailure(error) {
|
|
2542
|
-
|
|
2543
|
+
arcaneLogging.error("Arcane v1 stream iterator return failed.", error);
|
|
2543
2544
|
});
|
|
2544
2545
|
}
|
|
2545
2546
|
Promise.resolve().then(function cancelReturnedV1Stream() {
|
|
2546
2547
|
return opened.cancel("The stream consumer stopped before completion.");
|
|
2547
2548
|
}).catch(function reportReturnedV1StreamCancellationFailure(error) {
|
|
2548
|
-
|
|
2549
|
+
arcaneLogging.error("Arcane v1 stream early-return cancellation failed.", error);
|
|
2549
2550
|
});
|
|
2550
2551
|
return { value, done: true };
|
|
2551
2552
|
},
|
|
@@ -2778,7 +2779,7 @@ export function createBrowserWasmLlmProvider({
|
|
|
2778
2779
|
store,
|
|
2779
2780
|
loadDefaults = {},
|
|
2780
2781
|
security,
|
|
2781
|
-
logger =
|
|
2782
|
+
logger = arcaneLogging,
|
|
2782
2783
|
} = {}) {
|
|
2783
2784
|
const configuredModels = providerModelSources(sources);
|
|
2784
2785
|
const modelSources = configuredModels.sources;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from '../logging.mjs';
|
|
1
2
|
import { Wllama } from "./wllama/index.mjs";
|
|
2
3
|
|
|
3
4
|
const completeValue = (value) => value;
|
|
@@ -276,7 +277,7 @@ function initialEvidence() {
|
|
|
276
277
|
* network or browser side effects until load() is called. Runtime URLs are
|
|
277
278
|
* fixed relative to this module for npm and materialized /arcane/sdk trees.
|
|
278
279
|
*/
|
|
279
|
-
export function createPackagedWllamaRuntime({ logger =
|
|
280
|
+
export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
|
|
280
281
|
let engine = null;
|
|
281
282
|
let pending = null;
|
|
282
283
|
let inferenceActive = false;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcaneLogging } from '../logging.mjs';
|
|
1
2
|
import { createArcaneEventSource } from "arcane-os/event-manager";
|
|
2
3
|
|
|
3
4
|
export const ARCANE_AI_ADAPTER_PROTOCOL = "arcane-ai-adapter/1";
|
|
@@ -1372,7 +1373,7 @@ export class ModelController {
|
|
|
1372
1373
|
Promise.resolve().then(()=>value.cancel(
|
|
1373
1374
|
"The provider returned an invalid stream handle.",
|
|
1374
1375
|
)).catch(function reportInvalidModelStreamCleanupFailure(error){
|
|
1375
|
-
|
|
1376
|
+
arcaneLogging.error("Arcane invalid model stream cleanup failed.",error);
|
|
1376
1377
|
});
|
|
1377
1378
|
}
|
|
1378
1379
|
throw new ArcaneAIError(
|
|
@@ -1386,7 +1387,7 @@ export class ModelController {
|
|
|
1386
1387
|
}catch(error){
|
|
1387
1388
|
Promise.resolve().then(()=>value.cancel(error)).catch(
|
|
1388
1389
|
function reportRejectedModelIteratorCleanupFailure(cleanupError){
|
|
1389
|
-
|
|
1390
|
+
arcaneLogging.error("Arcane rejected model stream iterator cleanup failed.",cleanupError);
|
|
1390
1391
|
},
|
|
1391
1392
|
);
|
|
1392
1393
|
throw error;
|
|
@@ -1395,7 +1396,7 @@ export class ModelController {
|
|
|
1395
1396
|
Promise.resolve().then(()=>value.cancel(
|
|
1396
1397
|
"The provider returned an invalid stream iterator.",
|
|
1397
1398
|
)).catch(function reportInvalidModelIteratorCleanupFailure(error){
|
|
1398
|
-
|
|
1399
|
+
arcaneLogging.error("Arcane invalid model stream iterator cleanup failed.",error);
|
|
1399
1400
|
});
|
|
1400
1401
|
throw new ArcaneAIError(
|
|
1401
1402
|
"ARCANE_AI_INVALID_PROVIDER_RESULT",
|
|
@@ -1468,7 +1469,7 @@ export class ModelController {
|
|
|
1468
1469
|
const value = opened ?? await openPromise;
|
|
1469
1470
|
await value.cancel?.(reason);
|
|
1470
1471
|
} catch (error) {
|
|
1471
|
-
|
|
1472
|
+
arcaneLogging.error("Arcane model provider cancellation failed.",error);
|
|
1472
1473
|
}
|
|
1473
1474
|
try {
|
|
1474
1475
|
await result;
|
|
@@ -1491,7 +1492,7 @@ export class ModelController {
|
|
|
1491
1492
|
Promise.resolve().then(()=>this.cancel(
|
|
1492
1493
|
"The stream consumer stopped before completion.",
|
|
1493
1494
|
)).catch(function reportModelStreamReturnCancellationFailure(error){
|
|
1494
|
-
|
|
1495
|
+
arcaneLogging.error("Arcane model stream early-return cancellation failed.",error);
|
|
1495
1496
|
});
|
|
1496
1497
|
return { value, done: true };
|
|
1497
1498
|
},
|
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
normalizeSpeechWorkerErrorEnvelope,
|
|
4
4
|
SPEECH_WORKER_PROTOCOL,
|
|
5
5
|
} from "./speech-worker-runtime.mjs";
|
|
6
|
+
import { arcaneLogging } from "../logging.mjs";
|
|
6
7
|
|
|
7
8
|
const completeValue = (value) => value;
|
|
9
|
+
let nextSpeechWorkerClientId = 0;
|
|
8
10
|
|
|
9
11
|
const PUBLIC_WORKER_OPERATIONS = new Set([
|
|
10
12
|
"load",
|
|
@@ -57,6 +59,7 @@ const WORKER_CLIENTS = new WeakSet();
|
|
|
57
59
|
* Workers remain available.
|
|
58
60
|
*/
|
|
59
61
|
class SpeechWorkerClient {
|
|
62
|
+
#diagnosticId = ++nextSpeechWorkerClientId;
|
|
60
63
|
#role;
|
|
61
64
|
#createWorker;
|
|
62
65
|
#worker = null;
|
|
@@ -84,6 +87,29 @@ class SpeechWorkerClient {
|
|
|
84
87
|
});
|
|
85
88
|
this.#onTermination = onTermination;
|
|
86
89
|
WORKER_CLIENTS.add(this);
|
|
90
|
+
this.#trace("created", { workerUrl: workerUrl.href });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#trace(phase, detail = {}, beforeTransfer = false) {
|
|
94
|
+
if (!arcaneLogging.enabled) return;
|
|
95
|
+
let snapshot = detail;
|
|
96
|
+
if (beforeTransfer) {
|
|
97
|
+
try {
|
|
98
|
+
// Preserve complete request content before postMessage transfers it.
|
|
99
|
+
snapshot = structuredClone(detail);
|
|
100
|
+
} catch {
|
|
101
|
+
// Retain the complete original if native cloning cannot represent it.
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
arcaneLogging.debug("[Arcane speech worker]", {
|
|
105
|
+
clientId: this.#diagnosticId,
|
|
106
|
+
role: this.#role,
|
|
107
|
+
phase,
|
|
108
|
+
timestamp: new Date().toISOString(),
|
|
109
|
+
timeMs: globalThis.performance?.now?.() ?? Date.now(),
|
|
110
|
+
pendingRequests: this.#pending.size,
|
|
111
|
+
...snapshot,
|
|
112
|
+
});
|
|
87
113
|
}
|
|
88
114
|
|
|
89
115
|
#listen(target, type, listener) {
|
|
@@ -103,6 +129,7 @@ class SpeechWorkerClient {
|
|
|
103
129
|
}
|
|
104
130
|
const worker = validateWorker(this.#createWorker());
|
|
105
131
|
this.#worker = worker;
|
|
132
|
+
this.#trace("started");
|
|
106
133
|
this.#listen(worker, "message", (event) => {
|
|
107
134
|
this.#handleMessage(event.data);
|
|
108
135
|
});
|
|
@@ -126,6 +153,7 @@ class SpeechWorkerClient {
|
|
|
126
153
|
}
|
|
127
154
|
|
|
128
155
|
#handleMessage(message) {
|
|
156
|
+
this.#trace("response", { message });
|
|
129
157
|
if (message?.protocol !== SPEECH_WORKER_PROTOCOL) {
|
|
130
158
|
void this.terminate(clientError(
|
|
131
159
|
"ARCANE_AI_ADAPTER_PROTOCOL_MISMATCH",
|
|
@@ -177,7 +205,9 @@ class SpeechWorkerClient {
|
|
|
177
205
|
}
|
|
178
206
|
|
|
179
207
|
request(op, payload, { signal = null } = {}) {
|
|
208
|
+
this.#trace("request.call", { op, payload, aborted: signal?.aborted });
|
|
180
209
|
if (!PUBLIC_WORKER_OPERATIONS.has(op)) {
|
|
210
|
+
this.#trace("request.rejected", { op, reason: "unknown-operation" });
|
|
181
211
|
return Promise.reject(clientError(
|
|
182
212
|
"ARCANE_AI_INVALID_REQUEST",
|
|
183
213
|
"The speech worker operation is not part of its protocol.",
|
|
@@ -185,12 +215,16 @@ class SpeechWorkerClient {
|
|
|
185
215
|
`${this.#role}-worker-operation-unknown`,
|
|
186
216
|
));
|
|
187
217
|
}
|
|
188
|
-
if (signal?.aborted)
|
|
218
|
+
if (signal?.aborted) {
|
|
219
|
+
this.#trace("request.cancelled", { op, reason: signal.reason });
|
|
220
|
+
return Promise.reject(abortError(signal, this.#role, op));
|
|
221
|
+
}
|
|
189
222
|
let worker;
|
|
190
223
|
try {
|
|
191
224
|
worker = this.#start();
|
|
192
225
|
this.#transport = worker;
|
|
193
226
|
} catch (error) {
|
|
227
|
+
this.#trace("request.error", { op, error });
|
|
194
228
|
return Promise.reject(error);
|
|
195
229
|
}
|
|
196
230
|
const id = this.#nextId;
|
|
@@ -206,6 +240,7 @@ class SpeechWorkerClient {
|
|
|
206
240
|
function onAbort() {
|
|
207
241
|
const pending = client.#pending.get(id);
|
|
208
242
|
if (!pending) return;
|
|
243
|
+
client.#trace("request.cancelled", { id, op, reason: signal?.reason });
|
|
209
244
|
if (client.#role === "tts" && op === "use") {
|
|
210
245
|
client.#pending.delete(id);
|
|
211
246
|
pending.cleanup();
|
|
@@ -213,13 +248,16 @@ class SpeechWorkerClient {
|
|
|
213
248
|
const cancelId = client.#nextId;
|
|
214
249
|
client.#nextId += 1;
|
|
215
250
|
try {
|
|
216
|
-
|
|
251
|
+
const cancellation = {
|
|
217
252
|
protocol: SPEECH_WORKER_PROTOCOL,
|
|
218
253
|
id: cancelId,
|
|
219
254
|
op: "cancel",
|
|
220
255
|
payload: { targetId: id },
|
|
221
|
-
}
|
|
256
|
+
};
|
|
257
|
+
client.#trace("request.dispatch", { message: cancellation }, true);
|
|
258
|
+
client.#transport.postMessage(cancellation);
|
|
222
259
|
} catch (error) {
|
|
260
|
+
client.#trace("request.error", { id: cancelId, op: "cancel", error });
|
|
223
261
|
const failure = clientError(
|
|
224
262
|
"ARCANE_AI_WORKER_MESSAGE_ERROR",
|
|
225
263
|
"Unable to cancel an operation in the speech Worker.",
|
|
@@ -251,8 +289,10 @@ class SpeechWorkerClient {
|
|
|
251
289
|
};
|
|
252
290
|
const transfers = collectSpeechTransferables(payload);
|
|
253
291
|
try {
|
|
292
|
+
client.#trace("request.dispatch", { message }, true);
|
|
254
293
|
client.#transport.postMessage(message, transfers);
|
|
255
294
|
} catch (error) {
|
|
295
|
+
client.#trace("request.error", { id, op, error });
|
|
256
296
|
client.#pending.delete(id);
|
|
257
297
|
cleanup();
|
|
258
298
|
const failure = clientError(
|
|
@@ -272,6 +312,7 @@ class SpeechWorkerClient {
|
|
|
272
312
|
}
|
|
273
313
|
|
|
274
314
|
async terminate(reason = null, { intentional = true } = {}) {
|
|
315
|
+
this.#trace("terminate.call", { reason, intentional });
|
|
275
316
|
if (typeof intentional !== "boolean") {
|
|
276
317
|
throw new TypeError("Speech Worker termination intent must be a boolean.");
|
|
277
318
|
}
|
|
@@ -303,6 +344,7 @@ class SpeechWorkerClient {
|
|
|
303
344
|
if (termination && typeof termination.then === "function") await termination;
|
|
304
345
|
} finally {
|
|
305
346
|
for (const pending of pendingOperations) pending.reject(terminationReason);
|
|
347
|
+
this.#trace("terminated", { reason: terminationReason, intentional });
|
|
306
348
|
this.#onTermination(completeValue({ reason: terminationReason, intentional }));
|
|
307
349
|
}
|
|
308
350
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import EventPubSub from 'event-pubsub';
|
|
2
|
+
import {arcaneLogging} from './logging.mjs';
|
|
2
3
|
import {createDOMInstrumentation} from './dom-event-instrumentation.mjs';
|
|
3
4
|
|
|
4
5
|
export {
|
|
@@ -1368,7 +1369,7 @@ function createArcaneEventAuthority(){
|
|
|
1368
1369
|
return;
|
|
1369
1370
|
}
|
|
1370
1371
|
}catch{}
|
|
1371
|
-
try{
|
|
1372
|
+
try{arcaneLogging.error('Arcane event listener failed.',error);}
|
|
1372
1373
|
catch{}
|
|
1373
1374
|
}
|
|
1374
1375
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** Read the shared user preference; null means the user has not loaded yet. */
|
|
2
|
+
export function readArcaneDeveloperMode(target=globalThis){
|
|
3
|
+
try{
|
|
4
|
+
if(target?.user?.ready!==true){
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return target.user.developer===true;
|
|
9
|
+
}catch{
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function emitArcaneLog(method,diagnostic,args){
|
|
15
|
+
try{
|
|
16
|
+
if(diagnostic&&readArcaneDeveloperMode()!==true){
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
globalThis.console?.[method]?.(...args);
|
|
20
|
+
}catch{
|
|
21
|
+
// Console diagnostics must never change the operation being observed.
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const loggingOwnerKey=Symbol.for('arcane.logging');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The shared developer console owner. Diagnostic methods read user.developer
|
|
29
|
+
* for every emission and use info so they remain visible at normal console
|
|
30
|
+
* levels. Warnings, errors, and failure traces remain visible in every mode.
|
|
31
|
+
* Arguments pass through unchanged and are never retained by this owner.
|
|
32
|
+
*/
|
|
33
|
+
export const arcaneLogging=globalThis[loggingOwnerKey]??{
|
|
34
|
+
get enabled(){
|
|
35
|
+
return readArcaneDeveloperMode()===true;
|
|
36
|
+
},
|
|
37
|
+
log(...args){
|
|
38
|
+
emitArcaneLog('info',true,args);
|
|
39
|
+
},
|
|
40
|
+
info(...args){
|
|
41
|
+
emitArcaneLog('info',true,args);
|
|
42
|
+
},
|
|
43
|
+
debug(...args){
|
|
44
|
+
emitArcaneLog('info',true,args);
|
|
45
|
+
},
|
|
46
|
+
warn(...args){
|
|
47
|
+
emitArcaneLog('warn',false,args);
|
|
48
|
+
},
|
|
49
|
+
error(...args){
|
|
50
|
+
emitArcaneLog('error',false,args);
|
|
51
|
+
},
|
|
52
|
+
trace(...args){
|
|
53
|
+
emitArcaneLog('trace',false,args);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
globalThis[loggingOwnerKey]=arcaneLogging;
|
|
58
|
+
globalThis.arcaneLogging=arcaneLogging;
|
package/docs/architecture.md
CHANGED
|
@@ -279,7 +279,7 @@ paths are withheld from the native provider. The provider copies the complete
|
|
|
279
279
|
selected release rather than accepting an unrelated source path. Verification
|
|
280
280
|
is a separate explicit operation for a selected release artifact.
|
|
281
281
|
|
|
282
|
-
The SDK `0.5.
|
|
282
|
+
The SDK `0.5.15` runtime requires Arcane `0.8.12` or newer. Compatibility
|
|
283
283
|
is contractual rather than exact-version pinning: the prepared Core must meet
|
|
284
284
|
the highest minimum declared by the runtime, selected app, and bundled app
|
|
285
285
|
dependencies; keep each app's Arcane protocol generation; and provide every
|
package/docs/reference/README.md
CHANGED
|
@@ -17,7 +17,7 @@ high-level page links to the relevant deep section instead of repeating it.
|
|
|
17
17
|
Install the SDK in your application:
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
npm install --save-exact arcane-os@0.5.
|
|
20
|
+
npm install --save-exact arcane-os@0.5.15
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
For your first AI call, follow the [TWiN Cloud quick start](ai/twin-cloud.md).
|
|
@@ -34,12 +34,14 @@ alone does not make bare module names resolve in a browser.
|
|
|
34
34
|
| --- | --- |
|
|
35
35
|
| Use the Node.js package API | [SDK JavaScript API](sdk-api.md) |
|
|
36
36
|
| Publish central events, capture complete time-travel history, or observe the DOM | [EventManager and event-stack reference](event-manager.md) |
|
|
37
|
+
| Inspect complete AI and speech calls using the shared developer-mode preference | [Shared logger](sdk-api.md#arcanelogging) and [speech developer diagnostics](ai/browser-speech.md#developer-diagnostics) |
|
|
37
38
|
| Use the `arcane` command | [CLI reference](cli.md) |
|
|
38
39
|
| Generate named browser imports or inspect the selected physical runtime | [`arcane import-map`](cli.md#arcane-import-map) and [browser runtime delivery](protocols.md#browser-runtime-delivery) |
|
|
39
40
|
| Choose browser, native, cloud, or cross-host behavior | [Availability and normalization](availability-and-normalization.md) |
|
|
40
41
|
| Import a shipped renderer module | [Runtime module catalog](runtime-modules.md) |
|
|
41
42
|
| Use a shared entity | [Runtime entity modules](runtime-entities.md) and [exact export contracts](core/arcane-entities.md) |
|
|
42
43
|
| Load a reusable HTML component | [Runtime component catalog](runtime-components.md) |
|
|
44
|
+
| 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) |
|
|
43
45
|
| Call `globalThis.Arcane` | [Arcane Core API](core/arcane-api.md) |
|
|
44
46
|
| Subscribe to native events | [Arcane event reference](core/arcane-events.md) |
|
|
45
47
|
| Use provider-neutral AI lifecycle, chat, speech, persistence, or document context | [Normalized AI](#normalized-ai) |
|
|
@@ -56,9 +58,9 @@ This repository contains explicitly versioned surfaces with different owners:
|
|
|
56
58
|
|
|
57
59
|
| Surface | Source identity | Meaning |
|
|
58
60
|
| --- | --- | --- |
|
|
59
|
-
| SDK and CLI | `arcane-os` `0.5.
|
|
60
|
-
| Browser runtime | SDK `0.5.
|
|
61
|
-
| Browser SDK runtime | SDK `0.5.
|
|
61
|
+
| SDK and CLI | `arcane-os` `0.5.15` | The Node.js toolchain, portable `arcane-os/event-manager`, `arcane-os/logging`, `arcane-os/mail`, `arcane-os/preference-store`, and `arcane-os/speech-playback` entrypoints, plus the browser-only `arcane-os/ai/browser-wasm` and `arcane-os/ai/browser-speech` entrypoints in this checkout. |
|
|
62
|
+
| Browser runtime | SDK `0.5.15`, protocol `arcane/1`, `runtime/` | The SDK-canonical runtime tree. `listRuntimeFiles()`, `readRuntimeFile()`, and `loadRuntimeRelease()` derive its current inventory directly from the selected directory. |
|
|
63
|
+
| Browser SDK runtime | SDK `0.5.15`, `browser-runtime/` | The browser closure for events, shared logging, Wllama, and Browser Speech mechanisms. `listSdkBrowserRuntimeFiles()`, `readSdkBrowserRuntimeFile()`, and `loadSdkBrowserRuntimeRelease()` derive its current inventory directly from the selected directory. |
|
|
62
64
|
| Core reference snapshot | Arcane OS commit `567ad110bf57a1c2d4a3daa22ae93716cc5f4d7e`, protocol `arcane/1` | The application-facing Core contract imported into `docs/reference/core/`, with SDK-local links and package-boundary notes added explicitly. |
|
|
63
65
|
|
|
64
66
|
The SDK runtime source and Core reference have different owners. A browser
|
|
@@ -73,7 +75,7 @@ and the distinction between a documentation snapshot and the selected runtime.
|
|
|
73
75
|
|
|
74
76
|
## Installed documentation and release identity
|
|
75
77
|
|
|
76
|
-
This reference accompanies `arcane-os@0.5.
|
|
78
|
+
This reference accompanies `arcane-os@0.5.15`. The installed package includes
|
|
77
79
|
the maintained `docs/` tree and `examples/wasm-ai-demo/` source alongside
|
|
78
80
|
README and CHANGELOG. Open `node_modules/arcane-os/docs/reference/README.md`
|
|
79
81
|
for the matching local reference. The generated website and test suites remain
|
|
@@ -122,10 +124,10 @@ Public reference entries follow the established Arcane documentation model:
|
|
|
122
124
|
|
|
123
125
|
## Public runtime inventory
|
|
124
126
|
|
|
125
|
-
The package exposes
|
|
127
|
+
The package exposes 198 semantic JavaScript records across 17 JavaScript
|
|
126
128
|
entrypoints, plus eight JSON Schemas and package metadata. Ten entrypoints are
|
|
127
129
|
Node.js control-plane surfaces,
|
|
128
|
-
`arcane-os/event-manager`, `arcane-os/mail`, `arcane-os/preference-store`, and
|
|
130
|
+
`arcane-os/event-manager`, `arcane-os/logging`, `arcane-os/mail`, `arcane-os/preference-store`, and
|
|
129
131
|
`arcane-os/speech-playback` run in Node and browsers, and
|
|
130
132
|
`arcane-os/ai/browser-wasm` plus `arcane-os/ai/browser-speech` are browser-only.
|
|
131
133
|
The [machine-readable package
|