arcane-os 0.5.13 → 0.5.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +11 -10
  3. package/browser-runtime/ai/browser-wllama-runtime.mjs +2 -1
  4. package/browser-runtime/ai/model-controller.mjs +6 -5
  5. package/browser-runtime/ai/speech-worker-client.mjs +45 -3
  6. package/browser-runtime/event-manager.mjs +2 -1
  7. package/browser-runtime/logging.mjs +58 -0
  8. package/docs/reference/README.md +8 -7
  9. package/docs/reference/ai/browser-speech.md +46 -1
  10. package/docs/reference/inventory/package-api.json +30 -2
  11. package/docs/reference/runtime-modules.md +18 -0
  12. package/docs/reference/sdk-api.md +94 -8
  13. package/package.json +3 -2
  14. package/runtime/arcane/components/app-bar.html +3 -1
  15. package/runtime/arcane/components/chat.html +22 -20
  16. package/runtime/arcane/components/dashboard-config.html +3 -1
  17. package/runtime/arcane/components/data-maintenance.html +4 -2
  18. package/runtime/arcane/components/data-view.html +3 -1
  19. package/runtime/arcane/components/directory-picker.html +3 -1
  20. package/runtime/arcane/components/file-manager.html +11 -9
  21. package/runtime/arcane/components/header.html +5 -3
  22. package/runtime/arcane/components/markdown-document.html +3 -1
  23. package/runtime/arcane/components/markdown-editor.html +4 -2
  24. package/runtime/arcane/components/modal.html +3 -1
  25. package/runtime/arcane/components/screen-capture.html +4 -2
  26. package/runtime/arcane/components/speech.html +10 -8
  27. package/runtime/arcane/components/table.html +3 -1
  28. package/runtime/arcane/components/voice-transcription.html +8 -6
  29. package/runtime/arcane/entities/Chat.js +5 -4
  30. package/runtime/arcane/entities/User.js +2 -1
  31. package/runtime/arcane/modules/AI.js +442 -292
  32. package/runtime/arcane/modules/AIProviderRuntime.js +359 -82
  33. package/runtime/arcane/modules/CommunicationAppController.js +2 -1
  34. package/runtime/arcane/modules/ComponentContracts.js +3 -2
  35. package/runtime/arcane/modules/ConversationTimebox.js +2 -1
  36. package/runtime/arcane/modules/DBOPFS.js +5 -4
  37. package/runtime/arcane/modules/Errors.js +3 -14
  38. package/runtime/arcane/modules/HTMLImport.js +4 -3
  39. package/runtime/arcane/modules/LocalAIReadinessController.js +2 -1
  40. package/runtime/arcane/modules/MD.js +3 -2
  41. package/runtime/arcane/modules/MailOutbox.mjs +2 -1
  42. package/runtime/arcane/modules/PersistentAIChatSession.js +2 -1
  43. package/runtime/arcane/modules/ScreenCapture.js +2 -1
  44. package/runtime/arcane/modules/ThemeBootstrap.js +3 -2
  45. package/runtime/arcane/modules/ToolCallRouter.js +2 -1
  46. package/src/import-map.mjs +65 -10
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.5.14
6
+
7
+ - Add the shared `arcane-os/logging` console owner using the existing
8
+ `user.developer` preference. Route first-party runtime diagnostics and
9
+ default model loggers through this owner; preserve warnings and errors
10
+ when developer mode is disabled.
11
+ - Trace complete speech API inputs, generation queues, Worker requests and
12
+ responses, decoded audio, scheduled playback, natural completion,
13
+ cancellation, and failure under that same developer preference. Preserve
14
+ caller text, voice, speed, language selection, and playback behavior.
15
+ - Preserve complete cloud AI error responses and retry HTTP 429 overload
16
+ responses after three seconds with the original request and cancellation
17
+ signal. Keep partial streams and tool callbacks outside the retry path.
18
+ - Preserve exact URL import-map aliases while adding matching versioned
19
+ aliases, including authored imports, scopes, and generated dependency paths.
20
+
5
21
  ## 0.5.13
6
22
 
7
23
  - Derive local browser resource queries from the selected SDK package version
@@ -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
- globalThis.console?.warn?.(
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
- globalThis.console?.warn?.(
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
- globalThis.console?.warn?.(
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
- console.error("Arcane browser-WASM stream early-return cancellation failed.", error);
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
- console.error("Arcane invalid v1 stream cleanup failed.", error);
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
- console.error("Arcane rejected v1 stream iterator cleanup failed.", cleanupError);
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
- console.error("Arcane invalid v1 stream iterator cleanup failed.", error);
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
- console.error("Arcane v1 stream iterator return failed.", error);
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
- console.error("Arcane v1 stream early-return cancellation failed.", error);
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 = console,
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 = console } = {}) {
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
- console.error("Arcane invalid model stream cleanup failed.",error);
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
- console.error("Arcane rejected model stream iterator cleanup failed.",cleanupError);
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
- console.error("Arcane invalid model stream iterator cleanup failed.",error);
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
- console.error("Arcane model provider cancellation failed.",error);
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
- console.error("Arcane model stream early-return cancellation failed.",error);
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) return Promise.reject(abortError(signal, this.#role, op));
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
- client.#transport.postMessage({
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{globalThis.console?.error?.('Arcane event listener failed.',error);}
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;
@@ -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.12
20
+ npm install --save-exact arcane-os@0.5.14
21
21
  ```
22
22
 
23
23
  For your first AI call, follow the [TWiN Cloud quick start](ai/twin-cloud.md).
@@ -34,6 +34,7 @@ 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) |
@@ -56,9 +57,9 @@ This repository contains explicitly versioned surfaces with different owners:
56
57
 
57
58
  | Surface | Source identity | Meaning |
58
59
  | --- | --- | --- |
59
- | SDK and CLI | `arcane-os` `0.5.12` | The Node.js toolchain, portable `arcane-os/event-manager`, `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. |
60
- | Browser runtime | SDK `0.5.12`, protocol `arcane/1`, `runtime/` | The SDK-canonical runtime tree. `listRuntimeFiles()`, `readRuntimeFile()`, and `loadRuntimeRelease()` derive its current inventory directly from the selected directory. |
61
- | Browser SDK runtime | SDK `0.5.12`, `browser-runtime/` | The browser closure for events, Wllama, and Browser Speech mechanisms. `listSdkBrowserRuntimeFiles()`, `readSdkBrowserRuntimeFile()`, and `loadSdkBrowserRuntimeRelease()` derive its current inventory directly from the selected directory. |
60
+ | SDK and CLI | `arcane-os` `0.5.14` | 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. |
61
+ | Browser runtime | SDK `0.5.14`, protocol `arcane/1`, `runtime/` | The SDK-canonical runtime tree. `listRuntimeFiles()`, `readRuntimeFile()`, and `loadRuntimeRelease()` derive its current inventory directly from the selected directory. |
62
+ | Browser SDK runtime | SDK `0.5.14`, `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
63
  | 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
64
 
64
65
  The SDK runtime source and Core reference have different owners. A browser
@@ -73,7 +74,7 @@ and the distinction between a documentation snapshot and the selected runtime.
73
74
 
74
75
  ## Installed documentation and release identity
75
76
 
76
- This reference accompanies `arcane-os@0.5.12`. The installed package includes
77
+ This reference accompanies `arcane-os@0.5.14`. The installed package includes
77
78
  the maintained `docs/` tree and `examples/wasm-ai-demo/` source alongside
78
79
  README and CHANGELOG. Open `node_modules/arcane-os/docs/reference/README.md`
79
80
  for the matching local reference. The generated website and test suites remain
@@ -122,10 +123,10 @@ Public reference entries follow the established Arcane documentation model:
122
123
 
123
124
  ## Public runtime inventory
124
125
 
125
- The package exposes 204 semantic JavaScript records across 16 JavaScript
126
+ The package exposes 198 semantic JavaScript records across 17 JavaScript
126
127
  entrypoints, plus eight JSON Schemas and package metadata. Ten entrypoints are
127
128
  Node.js control-plane surfaces,
128
- `arcane-os/event-manager`, `arcane-os/mail`, `arcane-os/preference-store`, and
129
+ `arcane-os/event-manager`, `arcane-os/logging`, `arcane-os/mail`, `arcane-os/preference-store`, and
129
130
  `arcane-os/speech-playback` run in Node and browsers, and
130
131
  `arcane-os/ai/browser-wasm` plus `arcane-os/ai/browser-speech` are browser-only.
131
132
  The [machine-readable package
@@ -13,7 +13,7 @@ import map resolves `arcane/AI` and `arcane/DBOPFS`. These browser modules are
13
13
  not Node inference APIs. To create an application:
14
14
 
15
15
  ```bash
16
- npx arcane-os@0.5.12 new hello-speech --path ./hello-speech --target browser
16
+ npx arcane-os@0.5.14 new hello-speech --path ./hello-speech --target browser
17
17
  cd hello-speech
18
18
  npm install
19
19
  npm run dev
@@ -121,6 +121,51 @@ window.addEventListener('ai-tts-failure', function reportSpeechFailure(event) {
121
121
 
122
122
  Call `speechEvents.abort()` when disposing that interface to remove the listener.
123
123
 
124
+ ## Developer diagnostics
125
+
126
+ The shared logging API and speech traces are available in SDK `0.5.14`.
127
+
128
+ Arcane uses the existing shared `user.developer` preference for diagnostic
129
+ logging. Enable **developer mode** in the application's profile settings; the
130
+ logger reads that preference on every emission after the shared user is ready.
131
+ There is no separate speech verbosity or language setting. Ordinary warnings
132
+ and errors remain visible with developer mode disabled.
133
+
134
+ Applications can use the same owner for their complete AI requests and parsed
135
+ responses:
136
+
137
+ ```javascript
138
+ import { arcaneLogging } from 'arcane-os/logging';
139
+
140
+ arcaneLogging.info('AI request', request);
141
+ arcaneLogging.info('AI response', response);
142
+ ```
143
+
144
+ `arcaneLogging.log`, `.info`, and `.debug` use the developer preference and
145
+ appear at the browser console's normal Info level. `.warn`, `.error`, and
146
+ failure `.trace` calls remain visible in either mode. The same logger is
147
+ available as `globalThis.arcaneLogging`; it stores no diagnostic history.
148
+
149
+ Speech diagnostics include complete API inputs and results, selected provider
150
+ and model, exact segment text, voice and speed, generation queue state, Worker
151
+ request IDs and responses, and decoding/playback events. Worker requests are
152
+ copied only for developer diagnostics before native transfer detaches their
153
+ audio buffers; the original request still goes to the Worker unchanged.
154
+
155
+ Follow a speech `jobId` through `queue.add`, `generation.request`,
156
+ `generation.result`, `decode.result`, `playback.scheduled`, `playback.ended`,
157
+ and `queue.complete`. Cancellation and failure appear as `queue.cancelled`
158
+ and `queue.failed`. Each playback record includes the audio clock,
159
+ sample rate, duration, playback rate, and scheduled start/end when available.
160
+ For ready adjacent buffers, `gapSeconds` is zero on the same audio clock;
161
+ `audioEnd` marks the end of audio and `scheduledEnd` includes the caller's
162
+ requested pause. A positive gap can expose generation arriving too late to
163
+ fill the audio clock continuously. A scheduled event alone does not establish
164
+ that the buffer finished; use its `playback.ended` event.
165
+
166
+ Diagnostics do not change text, voice, speed, language selection, segmentation,
167
+ generation capacity, or playback scheduling. They stay outside chat history.
168
+
124
169
  ## Four synthesis slots and exact-order playback
125
170
 
126
171
  Capacity 4 means up to four segments synthesize at once. Segment 5 and later
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "sdkVersion": "0.5.12",
3
+ "sdkVersion": "0.5.14",
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": 204,
9
+ "memberCount": 198,
10
10
  "members": [
11
11
  {
12
12
  "id": "root:APP_BUNDLE_DESCRIPTOR_NAME",
@@ -3189,6 +3189,34 @@
3189
3189
  "availability": "Node and browser",
3190
3190
  "protocol": "Arcane Mail HTTP transport",
3191
3191
  "normalization": "Returns compact JSON and rejects non-object or non-serializable reports"
3192
+ },
3193
+ {
3194
+ "id": "logging:arcaneLogging",
3195
+ "name": "arcaneLogging",
3196
+ "displayName": "arcaneLogging",
3197
+ "kind": "singleton",
3198
+ "signature": "const arcaneLogging",
3199
+ "entrypoints": ["arcane-os/logging"],
3200
+ "primaryImport": "arcane-os/logging",
3201
+ "group": "Shared logging",
3202
+ "summary": "Shared console owner controlled by the existing user.developer preference.",
3203
+ "availability": "Node and browser",
3204
+ "protocol": "Shared user.developer preference and host console",
3205
+ "normalization": "Reads developer mode on every diagnostic emission; log, info, and debug use console.info only when enabled; warn, error, and failure trace remain visible in either mode; arguments pass through unchanged without retained history"
3206
+ },
3207
+ {
3208
+ "id": "logging:readArcaneDeveloperMode",
3209
+ "name": "readArcaneDeveloperMode",
3210
+ "displayName": "readArcaneDeveloperMode()",
3211
+ "kind": "function",
3212
+ "signature": "readArcaneDeveloperMode(target=globalThis)",
3213
+ "entrypoints": ["arcane-os/logging"],
3214
+ "primaryImport": "arcane-os/logging",
3215
+ "group": "Shared logging",
3216
+ "summary": "Reads the existing developer-mode preference without creating or changing a setting.",
3217
+ "availability": "Node and browser",
3218
+ "protocol": "Shared user.developer preference",
3219
+ "normalization": "Returns null until target.user.ready is true, then whether target.user.developer is exactly true; returns false if preference access throws"
3192
3220
  }
3193
3221
  ]
3194
3222
  }
@@ -152,6 +152,24 @@ entry points. `streamMessage(...)` and `streamRequest(options)` deliver
152
152
  incremental responses. The positional and object forms share the existing
153
153
  provider implementations; neither form is a retired compatibility API.
154
154
 
155
+ Built-in cloud chat decodes an HTTP error body once as JSON or text and rejects
156
+ with that complete value unchanged. It does not reconstruct an Error, replace
157
+ the message, or add `providerMessage`, `status`, or an SDK failure code to the
158
+ provider body. Network and decoding errors also pass through; cancellation
159
+ retains the existing `ARCANE_AI_REQUEST_ABORTED` contract.
160
+
161
+ When the HTTP status is `429` and the existing `error.message`, `message`, or
162
+ plain-text body contains `overload`, ignoring case, the request retries after
163
+ three seconds without a retry-count limit. Each warning shows the complete
164
+ message followed by `Retrying in ${retryDelayMs / 1000} seconds` through the
165
+ shared console logger, separately from the provider error. Every attempt uses
166
+ the same destination, headers, complete serialized body, and cancellation
167
+ signal; `onRequest` runs once for the logical request. Cancellation stops the
168
+ delay and prevents another attempt. Retrying happens before a successful
169
+ response is consumed, so partial streams and tool callbacks are never replayed.
170
+ Native Ollama and externally supplied provider adapters retain their own
171
+ transport behavior.
172
+
155
173
  Initialization uses the canonical realm user's actual readiness state. If
156
174
  `window.user?.ready` is already true, AI initializes immediately. Otherwise one
157
175
  shared registration observes `user-entity-loaded`, then rechecks readiness