arcane-os 0.3.1 → 0.3.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.
Files changed (153) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +86 -117
  3. package/bin/arcane-test.mjs +170 -46
  4. package/browser-runtime/ai/browser-speech-artifacts.mjs +855 -895
  5. package/browser-runtime/ai/browser-speech-providers.mjs +80 -204
  6. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
  7. package/browser-runtime/ai/browser-wasm.mjs +24 -35
  8. package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
  9. package/browser-runtime/ai/model-controller.mjs +584 -181
  10. package/browser-runtime/ai/speech-worker-client.mjs +8 -148
  11. package/browser-runtime/ai/speech-worker-runtime.mjs +642 -374
  12. package/browser-runtime/dom-event-instrumentation.mjs +55 -147
  13. package/browser-runtime/event-manager.mjs +239 -624
  14. package/package.json +5 -6
  15. package/runtime/arcane/components/app-bar.html +3 -15
  16. package/runtime/arcane/components/assistant-panel.html +10 -10
  17. package/runtime/arcane/components/calculator.html +1 -1
  18. package/runtime/arcane/components/chat.html +1359 -135
  19. package/runtime/arcane/components/conversation-view.html +2 -2
  20. package/runtime/arcane/components/document-inspector.html +11 -17
  21. package/runtime/arcane/components/file-manager.html +13 -56
  22. package/runtime/arcane/components/markdown-document.html +82 -281
  23. package/runtime/arcane/components/markdown-editor.html +7 -10
  24. package/runtime/arcane/components/media-embed.html +6 -6
  25. package/runtime/arcane/components/screen-capture.html +4 -4
  26. package/runtime/arcane/components/source-explanation.html +2 -2
  27. package/runtime/arcane/components/speech.html +112 -68
  28. package/runtime/arcane/components/terminal-workspace.html +4 -4
  29. package/runtime/arcane/components/theme-editor.html +1 -1
  30. package/runtime/arcane/components/unified-inbox.html +2 -2
  31. package/runtime/arcane/components/voice-transcription.html +31 -21
  32. package/runtime/arcane/entities/Calculation.js +2 -3
  33. package/runtime/arcane/entities/Chat.js +228 -43
  34. package/runtime/arcane/entities/Preference.js +3 -5
  35. package/runtime/arcane/entities/Weather.js +5 -5
  36. package/runtime/arcane/modules/AI.js +1042 -427
  37. package/runtime/arcane/modules/AIProviderRuntime.js +618 -359
  38. package/runtime/arcane/modules/AIResponseLength.js +9 -19
  39. package/runtime/arcane/modules/AIRuntimeState.js +109 -72
  40. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
  41. package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
  42. package/runtime/arcane/modules/CalculatorEngine.js +9 -9
  43. package/runtime/arcane/modules/CommunicationAppController.js +3 -7
  44. package/runtime/arcane/modules/ComponentContracts.js +30 -32
  45. package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
  46. package/runtime/arcane/modules/ConversationActionItems.js +26 -59
  47. package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
  48. package/runtime/arcane/modules/ConversationTimebox.js +27 -15
  49. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
  50. package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
  51. package/runtime/arcane/modules/HTMLImport.js +54 -1
  52. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
  53. package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
  54. package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
  55. package/runtime/arcane/modules/MD.js +1 -45
  56. package/runtime/arcane/modules/Mail.js +51 -103
  57. package/runtime/arcane/modules/MailOutbox.mjs +95 -193
  58. package/runtime/arcane/modules/MailTransport.mjs +36 -57
  59. package/runtime/arcane/modules/ModelDefinition.js +22 -106
  60. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
  61. package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
  62. package/runtime/arcane/modules/PreferenceStore.js +102 -30
  63. package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
  64. package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
  65. package/runtime/arcane/modules/ScreenCapture.js +175 -128
  66. package/runtime/arcane/modules/SpeechPlayback.js +46 -149
  67. package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
  68. package/runtime/arcane/modules/ToolCallRouter.js +25 -12
  69. package/runtime/arcane/modules/YouTubeMedia.js +6 -5
  70. package/schemas/arcane-app-bundle.schema.json +13 -78
  71. package/schemas/arcane-app.schema.json +9 -25
  72. package/schemas/arcane-lock.schema.json +18 -151
  73. package/schemas/arcane-package.schema.json +2 -16
  74. package/schemas/native-build-plan.schema.json +119 -122
  75. package/src/app-descriptor.mjs +75 -132
  76. package/src/application-tests.mjs +200 -0
  77. package/src/cli/main.mjs +27 -46
  78. package/src/constants.mjs +3 -4
  79. package/src/dev-server.mjs +30 -324
  80. package/src/doctor.mjs +92 -154
  81. package/src/dom-event-instrumentation.mjs +55 -147
  82. package/src/errors.mjs +2 -3
  83. package/src/event-manager.mjs +239 -624
  84. package/src/event-queue.mjs +3 -3
  85. package/src/import-map.mjs +273 -1028
  86. package/src/index.mjs +14 -16
  87. package/src/installed-sdk-runtime.mjs +27 -67
  88. package/src/integrated-provider-loader.mjs +53 -382
  89. package/src/mail-api.mjs +0 -2
  90. package/src/mail-server.mjs +224 -580
  91. package/src/mail.mjs +4 -10
  92. package/src/native-plan.mjs +163 -598
  93. package/src/native-provider-loader.mjs +104 -1063
  94. package/src/packager/core.mjs +485 -3229
  95. package/src/process.mjs +5 -10
  96. package/src/release-bundle.mjs +292 -2405
  97. package/src/runtime.mjs +76 -396
  98. package/src/scaffold.mjs +30 -80
  99. package/src/sdk-browser-runtime.mjs +70 -626
  100. package/src/source-server.mjs +588 -0
  101. package/src/targets/index.mjs +78 -188
  102. package/src/templates/workspace-template.mjs +19 -135
  103. package/src/testing-loader.mjs +164 -0
  104. package/src/testing.mjs +1 -1
  105. package/src/toolchain.mjs +131 -544
  106. package/src/update-check.mjs +26 -64
  107. package/src/workspace-operation-lock.mjs +139 -430
  108. package/src/workspace-runtime.mjs +109 -1558
  109. package/src/workspace.mjs +40 -302
  110. package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
  111. package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
  112. package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
  113. package/browser-runtime/ai/internal/sha256.mjs +0 -166
  114. package/docs/architecture.md +0 -344
  115. package/docs/compatibility.md +0 -36
  116. package/docs/event-manager.md +0 -294
  117. package/docs/platform-targets.md +0 -108
  118. package/docs/publishing.md +0 -201
  119. package/docs/reference/README.md +0 -185
  120. package/docs/reference/ai/browser-speech-package-authority.json +0 -835
  121. package/docs/reference/ai/browser-speech.md +0 -1295
  122. package/docs/reference/ai/browser-wasm.md +0 -530
  123. package/docs/reference/arcane-ollama.md +0 -288
  124. package/docs/reference/availability-and-normalization.md +0 -183
  125. package/docs/reference/behavioral-testing.md +0 -133
  126. package/docs/reference/cli.md +0 -779
  127. package/docs/reference/core/README.md +0 -62
  128. package/docs/reference/core/arcane-ai-contracts.md +0 -907
  129. package/docs/reference/core/arcane-api.md +0 -601
  130. package/docs/reference/core/arcane-entities.md +0 -65
  131. package/docs/reference/core/arcane-events.md +0 -134
  132. package/docs/reference/core/ollama-module.md +0 -181
  133. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
  134. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
  135. package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
  136. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
  137. package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
  138. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
  139. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
  140. package/docs/reference/event-manager.md +0 -1511
  141. package/docs/reference/inventory/package-api.json +0 -3284
  142. package/docs/reference/inventory/runtime-components.json +0 -1011
  143. package/docs/reference/inventory/runtime-entities.json +0 -26
  144. package/docs/reference/inventory/runtime-modules.json +0 -1431
  145. package/docs/reference/mail.md +0 -316
  146. package/docs/reference/protocols.md +0 -719
  147. package/docs/reference/runtime-components.md +0 -1366
  148. package/docs/reference/runtime-entities.md +0 -303
  149. package/docs/reference/runtime-modules.md +0 -2965
  150. package/docs/reference/sdk-api.md +0 -6698
  151. package/docs/roadmap.md +0 -79
  152. package/docs/work-amplification.md +0 -129
  153. package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
@@ -5,11 +5,8 @@ import {
5
5
  isBrowserSpeechAuthority,
6
6
  isDbopfsSpeechArtifactStore,
7
7
  } from "./browser-speech-artifacts.mjs";
8
- import {
9
- normalizeModelSecurity,
10
- resolveModelSecurity,
11
- sameModelSecurity,
12
- } from "./model-controller.mjs";
8
+
9
+ const completeValue = (value) => value;
13
10
  import {
14
11
  createSpeechWorkerClient,
15
12
  isSpeechWorkerClient,
@@ -18,18 +15,16 @@ import {
18
15
 
19
16
  const AI_PROVIDER_PROTOCOL = "arcane-ai-provider/2";
20
17
  const AI_MODEL_AUTHORITY_PROTOCOL = "arcane-ai-model-authority/1";
21
- const ROLE_OPERATION = Object.freeze({ stt: "transcribe", tts: "synthesize" });
18
+ const ROLE_OPERATION = completeValue({ stt: "transcribe", tts: "synthesize" });
22
19
  const STT_SAMPLE_RATE = 16_000;
23
20
  const TTS_SAMPLE_RATE = 24_000;
24
21
  const TTS_RESPONSE_FORMAT = "wav";
25
- const ROLE_REQUEST_REASON = Object.freeze({
22
+ const ROLE_REQUEST_REASON = completeValue({
26
23
  stt: "stt-transcription-cancelled",
27
24
  tts: "tts-synthesis-cancelled",
28
25
  });
29
26
  const UNMAPPED_PROVIDER_REASON = "browser-speech-provider-error-reason-unmapped";
30
- const WARN_FIRST_WARNING = "browser-speech-warn-first-secure-mode-disabled";
31
- const NO_PROVIDER_WARNINGS = Object.freeze([]);
32
- const WARN_FIRST_PROVIDER_WARNINGS = Object.freeze([WARN_FIRST_WARNING]);
27
+ const NO_PROVIDER_WARNINGS = completeValue([]);
33
28
  const WORKER_FAILURE_CODES = new Set([
34
29
  "ARCANE_AI_WORKER_CRASHED",
35
30
  "ARCANE_AI_WORKER_MESSAGE_ERROR",
@@ -69,7 +64,9 @@ function trustedLoadFailure(error, role) {
69
64
  if (isTrustedSpeechError(error)) return error;
70
65
  return providerError(
71
66
  "ARCANE_AI_PROVIDER_LOAD_FAILED",
72
- `The browser ${role} provider load was rejected.`,
67
+ typeof error?.message === "string" && error.message
68
+ ? error.message
69
+ : `The browser ${role} provider load was rejected.`,
73
70
  error,
74
71
  `${role}-provider-load-rejected`,
75
72
  );
@@ -79,7 +76,9 @@ function trustedRequestFailure(error, role) {
79
76
  if (isTrustedSpeechError(error)) return error;
80
77
  return providerError(
81
78
  "ARCANE_AI_PROVIDER_REQUEST_FAILED",
82
- `The browser ${role} engine operation was rejected.`,
79
+ typeof error?.message === "string" && error.message
80
+ ? error.message
81
+ : `The browser ${role} engine operation was rejected.`,
83
82
  error,
84
83
  role === "stt"
85
84
  ? "stt-transcription-engine-operation-rejected"
@@ -91,7 +90,9 @@ function trustedWorkerFailure(error, role) {
91
90
  if (isTrustedSpeechError(error)) return error;
92
91
  return providerError(
93
92
  "ARCANE_AI_WORKER_MESSAGE_REJECTED",
94
- `The browser ${role} Worker returned an untrusted error envelope.`,
93
+ typeof error?.message === "string" && error.message
94
+ ? error.message
95
+ : `The browser ${role} Worker returned an unreadable error envelope.`,
95
96
  error,
96
97
  `${role}-worker-error-envelope-rejected`,
97
98
  );
@@ -101,7 +102,9 @@ function trustedLifecycleFailure(error, role, operation) {
101
102
  if (isTrustedSpeechError(error)) return error;
102
103
  return providerError(
103
104
  "ARCANE_AI_INVALID_REQUEST",
104
- `The browser ${role} ${operation} context could not be read.`,
105
+ typeof error?.message === "string" && error.message
106
+ ? error.message
107
+ : `The browser ${role} ${operation} context could not be read.`,
105
108
  error,
106
109
  `${role}-provider-${operation}-context-read-rejected`,
107
110
  );
@@ -132,8 +135,8 @@ function workerFailureCode(error) {
132
135
  }
133
136
 
134
137
  function requiredIdentifier(value, label) {
135
- if (typeof value !== "string" || !value.trim() || value.trim().length > 128) {
136
- throw new TypeError(`${label} must be a trimmed 1-128 character string.`);
138
+ if (typeof value !== "string" || !value.trim()) {
139
+ throw new TypeError(`${label} must be a nonempty trimmed string.`);
137
140
  }
138
141
  return value.trim();
139
142
  }
@@ -144,7 +147,6 @@ function createProviderAuthority({
144
147
  graph,
145
148
  model,
146
149
  runtime,
147
- security,
148
150
  }) {
149
151
  if (graph !== undefined) {
150
152
  if (model !== undefined || runtime !== undefined) {
@@ -161,9 +163,8 @@ function createProviderAuthority({
161
163
  && graph.providerId !== providerId) {
162
164
  throw new TypeError("Browser speech graph providerId must match the provider id.");
163
165
  }
164
- return Object.freeze({
166
+ return completeValue({
165
167
  protocol: AI_MODEL_AUTHORITY_PROTOCOL,
166
- admitted: true,
167
168
  graph,
168
169
  artifactGraphProtocol: graph.protocol,
169
170
  providerId,
@@ -173,13 +174,11 @@ function createProviderAuthority({
173
174
  revision: graph.model.revision,
174
175
  dtype: graph.model.dtype,
175
176
  defaultVoice: graph.model.defaultVoice,
176
- voices: graph.model.voices ?? Object.freeze([]),
177
+ voices: graph.model.voices ?? completeValue([]),
177
178
  inputSampleRate: graph.model.inputSampleRate,
178
179
  outputSampleRate: graph.model.outputSampleRate,
179
180
  runtime: graph.runtime,
180
181
  files: graph.files,
181
- security,
182
- artifactGraphId: graph.identitySha256,
183
182
  });
184
183
  }
185
184
  const authority = createBrowserSpeechAuthority({
@@ -187,7 +186,6 @@ function createProviderAuthority({
187
186
  role,
188
187
  model,
189
188
  runtime,
190
- security,
191
189
  });
192
190
  if (!isBrowserSpeechAuthority(authority)) {
193
191
  throw new TypeError("Browser speech authority construction did not return an SDK authority.");
@@ -210,7 +208,7 @@ function linkSignal(signal) {
210
208
  const forwardExternalAbort = () => controller.abort(signal?.reason);
211
209
  if (signal?.aborted) forwardExternalAbort();
212
210
  else signal?.addEventListener?.("abort", forwardExternalAbort, { once: true });
213
- return Object.freeze({
211
+ return completeValue({
214
212
  controller,
215
213
  abort(reason, code = "ARCANE_AI_REQUEST_ABORTED") {
216
214
  if (controller.signal.aborted) return;
@@ -268,7 +266,7 @@ function isAbortSignal(value) {
268
266
  }
269
267
 
270
268
  function providerContext(context, role, operation) {
271
- if (context === undefined) return Object.freeze({ signal: null });
269
+ if (context === undefined) return completeValue({ signal: null });
272
270
  if (!context || typeof context !== "object" || Array.isArray(context)) {
273
271
  throw providerError(
274
272
  "ARCANE_AI_INVALID_REQUEST",
@@ -294,39 +292,25 @@ function providerContext(context, role, operation) {
294
292
  `${role}-provider-${operation}-signal-not-abort-signal`,
295
293
  );
296
294
  }
297
- return Object.freeze({ context, signal });
295
+ return completeValue({ context, signal });
298
296
  }
299
297
 
300
- function graphSecurity(scope, label) {
301
- return normalizeModelSecurity(scope, label);
302
- }
303
-
304
- function providerIntegrity(security, outcome = "pending") {
305
- const enabledCount = Number(security.checks.byteLength)
306
- + Number(security.checks.sha256);
307
- const state = enabledCount === 0 ? "unchecked" : outcome;
308
- if (!["unchecked", "pending", "verified", "failed"].includes(state)) {
309
- throw new TypeError("Browser speech integrity outcome is invalid.");
298
+ function reportedSecureIntent(...scopes) {
299
+ for (const scope of scopes) {
300
+ try {
301
+ if (scope?.secure === true) return completeValue({ secure: true });
302
+ } catch {
303
+ // Intent reporting never turns a security-shaped value into an ordinary
304
+ // provider admission check. Future hardening requires user review.
305
+ }
310
306
  }
311
- return Object.freeze({
312
- state,
313
- byteLength: Object.freeze({
314
- enabled: security.checks.byteLength,
315
- state: security.checks.byteLength ? state : "unchecked",
316
- }),
317
- sha256: Object.freeze({
318
- enabled: security.checks.sha256,
319
- state: security.checks.sha256 ? state : "unchecked",
320
- }),
321
- });
307
+ return null;
322
308
  }
323
309
 
324
- function providerWarnings(security, runtimeWarnings = NO_PROVIDER_WARNINGS) {
325
- const warnings = security.secure === true
326
- ? runtimeWarnings
327
- : [...WARN_FIRST_PROVIDER_WARNINGS, ...runtimeWarnings];
310
+ function providerWarnings(runtimeWarnings = NO_PROVIDER_WARNINGS) {
311
+ const warnings = runtimeWarnings;
328
312
  if (warnings.length === 0) return NO_PROVIDER_WARNINGS;
329
- return Object.freeze([...new Set(warnings)]);
313
+ return completeValue([...new Set(warnings)]);
330
314
  }
331
315
 
332
316
  function publicStatus({
@@ -340,12 +324,10 @@ function publicStatus({
340
324
  cache,
341
325
  lifecycleReason,
342
326
  activeOperation,
343
- artifactGraphAdmission,
344
- security,
345
- integrity,
327
+ secureIntent,
346
328
  warnings,
347
329
  }) {
348
- return Object.freeze({
330
+ return completeValue({
349
331
  role,
350
332
  providerId: id,
351
333
  modelId: authority.modelId,
@@ -358,11 +340,8 @@ function publicStatus({
358
340
  generation,
359
341
  errorCode,
360
342
  cache,
361
- security,
362
- integrity,
343
+ ...(secureIntent ? { security: secureIntent } : {}),
363
344
  warnings,
364
- artifactGraphId: authority.artifactGraphId ?? null,
365
- artifactGraphAdmission,
366
345
  });
367
346
  }
368
347
 
@@ -542,9 +521,9 @@ function observeLoadOperation(record, { signal, progress }, role) {
542
521
  settle(reject, abortError(signal, `${role}-load-cancelled`));
543
522
  abandonIfUnobserved(`${role}-load-cancelled`);
544
523
  };
545
- const observer = Object.freeze({
524
+ const observer = completeValue({
546
525
  progress(value) {
547
- if (settled) return;
526
+ if (settled || typeof progress !== "function") return;
548
527
  try {
549
528
  progress(value);
550
529
  } catch (error) {
@@ -578,7 +557,6 @@ function observeLoadOperation(record, { signal, progress }, role) {
578
557
  );
579
558
  return;
580
559
  }
581
- if (record.hasProgress) observer.progress(record.latestProgress);
582
560
  });
583
561
  }
584
562
 
@@ -797,8 +775,8 @@ async function decodeSharedTranscriptionPayload(
797
775
  mono[index] = sample;
798
776
  }
799
777
  throwIfAborted(signal, cancellationReason);
800
- return Object.freeze({
801
- payload: Object.freeze({ audio: mono, sampleRate }),
778
+ return completeValue({
779
+ payload: completeValue({ audio: mono, sampleRate }),
802
780
  shared: true,
803
781
  });
804
782
  }
@@ -847,8 +825,8 @@ function cloneNativeTranscriptionPayload(payload, authority) {
847
825
  );
848
826
  }
849
827
  }
850
- return Object.freeze({
851
- payload: Object.freeze({
828
+ return completeValue({
829
+ payload: completeValue({
852
830
  audio: new Float32Array(descriptors.audio.value),
853
831
  sampleRate,
854
832
  }),
@@ -892,15 +870,13 @@ function normalizeSynthesisPayload(payload, authority) {
892
870
  assertPayloadModel(payload, authority, { operationSubject: "tts-synthesis" });
893
871
  textValue = descriptors.text.value;
894
872
  }
895
- const text = typeof textValue === "string" ? textValue.trim() : "";
873
+ const text = typeof textValue === "string" ? textValue : "";
896
874
  const voiceValue = Object.hasOwn(descriptors, "voice")
897
875
  ? descriptors.voice.value
898
876
  : authority.defaultVoice;
899
- const voice = typeof voiceValue === "string"
900
- ? voiceValue.trim()
901
- : "";
877
+ const voice = typeof voiceValue === "string" ? voiceValue : "";
902
878
  const speed = Object.hasOwn(descriptors, "speed") ? descriptors.speed.value : 1;
903
- if (!text) {
879
+ if (!text.trim()) {
904
880
  throw providerError(
905
881
  "ARCANE_AI_INVALID_REQUEST",
906
882
  "Kokoro requires nonempty text.",
@@ -908,7 +884,7 @@ function normalizeSynthesisPayload(payload, authority) {
908
884
  "tts-synthesis-text-empty",
909
885
  );
910
886
  }
911
- if (!voice) {
887
+ if (!voice.trim()) {
912
888
  throw providerError(
913
889
  "ARCANE_AI_INVALID_REQUEST",
914
890
  "Kokoro requires a nonempty voice id.",
@@ -916,12 +892,12 @@ function normalizeSynthesisPayload(payload, authority) {
916
892
  "tts-synthesis-voice-empty",
917
893
  );
918
894
  }
919
- if (!Number.isFinite(speed) || speed <= 0 || speed > 4) {
895
+ if (!Number.isFinite(speed) || speed <= 0) {
920
896
  throw providerError(
921
897
  "ARCANE_AI_INVALID_REQUEST",
922
- "Kokoro speed must be greater than 0 and at most 4.",
898
+ "Kokoro speed must be greater than 0.",
923
899
  undefined,
924
- "tts-synthesis-speed-out-of-range",
900
+ "tts-synthesis-speed-not-positive",
925
901
  );
926
902
  }
927
903
  if (authority.graph
@@ -933,8 +909,8 @@ function normalizeSynthesisPayload(payload, authority) {
933
909
  "tts-synthesis-voice-not-declared",
934
910
  );
935
911
  }
936
- return Object.freeze({
937
- payload: Object.freeze({ text, voice, speed }),
912
+ return completeValue({
913
+ payload: completeValue({ text, voice, speed }),
938
914
  shared,
939
915
  });
940
916
  }
@@ -1054,7 +1030,7 @@ function encodeSharedSynthesisResult(result, authority) {
1054
1030
  true,
1055
1031
  );
1056
1032
  }
1057
- return Object.freeze({ audio: bytes, contentType: "audio/wav" });
1033
+ return completeValue({ audio: bytes, contentType: "audio/wav" });
1058
1034
  }
1059
1035
 
1060
1036
  function createBrowserSpeechProvider({
@@ -1079,35 +1055,23 @@ function createBrowserSpeechProvider({
1079
1055
  if (!isDbopfsSpeechArtifactStore(store)) {
1080
1056
  throw new TypeError("Browser speech providers require an SDK-created DBOPFS artifact store.");
1081
1057
  }
1082
- const configuredAppSecurity = normalizeModelSecurity(
1083
- appSecurity,
1084
- "Browser speech app security",
1085
- );
1086
- const configuredProviderSecurity = normalizeModelSecurity(
1087
- security,
1088
- "Browser speech provider security",
1089
- );
1090
1058
  const authority = createProviderAuthority({
1091
1059
  providerId,
1092
1060
  role,
1093
1061
  graph,
1094
1062
  model,
1095
1063
  runtime,
1096
- security: configuredProviderSecurity,
1097
- });
1098
- const defaultSecurity = resolveModelSecurity({
1099
- app: configuredAppSecurity,
1100
- binding: configuredProviderSecurity,
1101
1064
  });
1065
+ const defaultSecureIntent = reportedSecureIntent(appSecurity, security);
1102
1066
  const operation = ROLE_OPERATION[role];
1103
1067
  const speech = role === "stt"
1104
- ? Object.freeze({ inputSampleRate: inputSampleRate(authority) })
1105
- : Object.freeze({
1068
+ ? completeValue({ inputSampleRate: inputSampleRate(authority) })
1069
+ : completeValue({
1106
1070
  outputSampleRate: outputSampleRate(authority),
1107
- responseFormats: Object.freeze([TTS_RESPONSE_FORMAT]),
1071
+ responseFormats: completeValue([TTS_RESPONSE_FORMAT]),
1108
1072
  defaultResponseFormat: TTS_RESPONSE_FORMAT,
1109
1073
  });
1110
- const catalogEntry = Object.freeze({
1074
+ const catalogEntry = completeValue({
1111
1075
  id: authority.modelId,
1112
1076
  providerId,
1113
1077
  role,
@@ -1119,7 +1083,6 @@ function createBrowserSpeechProvider({
1119
1083
  runtime: authority.runtime,
1120
1084
  files: authority.files,
1121
1085
  ...(authority.graph ? {
1122
- artifactGraphId: authority.artifactGraphId,
1123
1086
  voices: authority.voices,
1124
1087
  } : {}),
1125
1088
  speech,
@@ -1127,7 +1090,6 @@ function createBrowserSpeechProvider({
1127
1090
  let state = "unloaded";
1128
1091
  let errorCode = null;
1129
1092
  let cache = null;
1130
- let artifactGraphAdmission = null;
1131
1093
  let lifecycleReason = `${role}-provider-created`;
1132
1094
  let activeOperation = null;
1133
1095
  let generation = 0;
@@ -1137,18 +1099,9 @@ function createBrowserSpeechProvider({
1137
1099
  let disposeOperation = null;
1138
1100
  let requestOperation = null;
1139
1101
  let lastWarnings = NO_PROVIDER_WARNINGS;
1140
- let lastIntegrity = Object.freeze({
1141
- security: defaultSecurity,
1142
- integrity: providerIntegrity(defaultSecurity),
1143
- });
1102
+ let secureIntent = defaultSecureIntent;
1144
1103
 
1145
1104
  function status() {
1146
- const effectiveSecurity = active?.security
1147
- ?? loadOperation?.security
1148
- ?? lastIntegrity.security;
1149
- const integrity = active?.integrity
1150
- ?? loadOperation?.integrity
1151
- ?? lastIntegrity.integrity;
1152
1105
  const runtimeWarnings = active?.warnings
1153
1106
  ?? loadOperation?.warnings
1154
1107
  ?? lastWarnings;
@@ -1163,10 +1116,8 @@ function createBrowserSpeechProvider({
1163
1116
  cache,
1164
1117
  lifecycleReason,
1165
1118
  activeOperation,
1166
- artifactGraphAdmission,
1167
- security: effectiveSecurity,
1168
- integrity,
1169
- warnings: providerWarnings(effectiveSecurity, runtimeWarnings),
1119
+ secureIntent,
1120
+ warnings: providerWarnings(runtimeWarnings),
1170
1121
  });
1171
1122
  }
1172
1123
 
@@ -1207,7 +1158,7 @@ function createBrowserSpeechProvider({
1207
1158
  localOnly: true,
1208
1159
 
1209
1160
  catalog() {
1210
- return Object.freeze([catalogEntry]);
1161
+ return completeValue([catalogEntry]);
1211
1162
  },
1212
1163
 
1213
1164
  inspect(selection, context = {}) {
@@ -1219,14 +1170,14 @@ function createBrowserSpeechProvider({
1219
1170
  && (!Object.hasOwn(selection ?? {}, "role") || selection.role === role)
1220
1171
  && selection?.localOnly !== false;
1221
1172
  if (!available) {
1222
- return Object.freeze({
1173
+ return completeValue({
1223
1174
  available: false,
1224
1175
  code: "ARCANE_AI_MODEL_AUTHORITY_REQUIRED",
1225
1176
  message: "The selected browser speech model does not match this provider authority.",
1226
1177
  reason: `${role}-provider-inspection-selection-authority-mismatch`,
1227
1178
  });
1228
1179
  }
1229
- return Object.freeze({ available: true, authority });
1180
+ return completeValue({ available: true, authority });
1230
1181
  },
1231
1182
 
1232
1183
  status,
@@ -1285,10 +1236,10 @@ function createBrowserSpeechProvider({
1285
1236
  } catch (error) {
1286
1237
  return Promise.reject(trustedLoadFailure(error, role));
1287
1238
  }
1288
- if (typeof loadProgress !== "function") {
1239
+ if (loadProgress !== undefined && typeof loadProgress !== "function") {
1289
1240
  return Promise.reject(providerError(
1290
1241
  "ARCANE_AI_INVALID_REQUEST",
1291
- "Browser speech load progress must be a function.",
1242
+ "Browser speech load progress must be a function when supplied.",
1292
1243
  undefined,
1293
1244
  `${role}-load-progress-callback-not-function`,
1294
1245
  ));
@@ -1304,54 +1255,20 @@ function createBrowserSpeechProvider({
1304
1255
  `${role}-load-selection-authority-mismatch`,
1305
1256
  ));
1306
1257
  }
1307
- let effectiveSecurity;
1258
+ secureIntent = reportedSecureIntent(appSecurity, security, context.security);
1308
1259
  try {
1309
- if (authority.graph) {
1310
- effectiveSecurity = resolveModelSecurity({
1311
- app: graphSecurity(
1312
- configuredAppSecurity,
1313
- "Browser speech graph app security",
1314
- ),
1315
- binding: graphSecurity(
1316
- configuredProviderSecurity,
1317
- "Browser speech graph provider security",
1318
- ),
1319
- load: graphSecurity(
1320
- context.security,
1321
- "Browser speech graph load security",
1322
- ),
1323
- });
1324
- } else {
1325
- effectiveSecurity = resolveModelSecurity({
1326
- app: configuredAppSecurity,
1327
- binding: configuredProviderSecurity,
1328
- load: context.security,
1329
- });
1330
- }
1331
1260
  throwIfAborted(loadSignal, `${role}-load-cancelled`);
1332
1261
  } catch (error) {
1333
1262
  return Promise.reject(trustedLoadFailure(error, role));
1334
1263
  }
1335
1264
  if (state === "ready" && active) {
1336
- if (sameModelSecurity(active.security, effectiveSecurity)) {
1337
- return Promise.resolve(status());
1338
- }
1339
- lifecycleReason = `${role}-load-superseded-by-security-change`;
1340
- return provider.unload().then(() => provider.load(context));
1265
+ return Promise.resolve(status());
1341
1266
  }
1342
1267
  if (loadOperation) {
1343
- if (sameModelSecurity(loadOperation.security, effectiveSecurity)) {
1344
- return observeLoadOperation(loadOperation, {
1345
- signal: loadSignal,
1346
- progress: loadProgress,
1347
- }, role);
1348
- }
1349
- lifecycleReason = `${role}-load-superseded-by-security-change`;
1350
- loadOperation.abort(
1351
- `${role}-load-superseded-by-security-change`,
1352
- "ARCANE_AI_OPERATION_SUPERSEDED",
1353
- );
1354
- return loadOperation.promise.catch(() => undefined).then(() => provider.load(context));
1268
+ return observeLoadOperation(loadOperation, {
1269
+ signal: loadSignal,
1270
+ progress: loadProgress,
1271
+ }, role);
1355
1272
  }
1356
1273
  generation += 1;
1357
1274
  const operationGeneration = generation;
@@ -1362,27 +1279,14 @@ function createBrowserSpeechProvider({
1362
1279
  errorCode = null;
1363
1280
  const record = {
1364
1281
  promise: null,
1365
- security: effectiveSecurity,
1366
- integrity: providerIntegrity(effectiveSecurity),
1367
1282
  warnings: NO_PROVIDER_WARNINGS,
1368
1283
  observers: new Set(),
1369
1284
  settled: false,
1370
- hasProgress: false,
1371
- latestProgress: null,
1372
1285
  abort: (
1373
1286
  reason = `${role}-load-cancelled`,
1374
1287
  code = "ARCANE_AI_REQUEST_ABORTED",
1375
1288
  ) => linked.abort(reason, code),
1376
- publishProgress(progress) {
1377
- record.latestProgress = progress;
1378
- record.hasProgress = true;
1379
- for (const observer of [...record.observers]) observer.progress(progress);
1380
- },
1381
1289
  };
1382
- lastIntegrity = Object.freeze({
1383
- security: effectiveSecurity,
1384
- integrity: record.integrity,
1385
- });
1386
1290
  lastWarnings = NO_PROVIDER_WARNINGS;
1387
1291
  const promise = Promise.resolve().then(async () => {
1388
1292
  let prepared = null;
@@ -1390,18 +1294,11 @@ function createBrowserSpeechProvider({
1390
1294
  try {
1391
1295
  prepared = await store.prepare(authority.graph ?? authority, {
1392
1296
  signal: linked.controller.signal,
1393
- onProgress: record.publishProgress,
1394
1297
  offline,
1395
- security: effectiveSecurity,
1396
1298
  });
1397
- record.integrity = providerIntegrity(effectiveSecurity, "verified");
1398
1299
  record.warnings = Array.isArray(prepared.warnings)
1399
- ? Object.freeze([...prepared.warnings])
1300
+ ? completeValue([...prepared.warnings])
1400
1301
  : NO_PROVIDER_WARNINGS;
1401
- lastIntegrity = Object.freeze({
1402
- security: effectiveSecurity,
1403
- integrity: record.integrity,
1404
- });
1405
1302
  lastWarnings = record.warnings;
1406
1303
  throwIfAborted(
1407
1304
  linked.controller.signal,
@@ -1412,17 +1309,13 @@ function createBrowserSpeechProvider({
1412
1309
  "ARCANE_AI_OPERATION_SUPERSEDED",
1413
1310
  "Browser speech loading was superseded.",
1414
1311
  undefined,
1415
- lifecycleReason === `${role}-load-superseded-by-security-change`
1416
- ? lifecycleReason
1417
- : `${role}-load-superseded-by-unload`,
1312
+ `${role}-load-superseded-by-unload`,
1418
1313
  );
1419
1314
  }
1420
1315
  slot = {
1421
1316
  prepared,
1422
1317
  released: false,
1423
1318
  client: null,
1424
- security: effectiveSecurity,
1425
- integrity: record.integrity,
1426
1319
  warnings: record.warnings,
1427
1320
  };
1428
1321
  slot.client = createSpeechWorkerClient({
@@ -1443,7 +1336,6 @@ function createBrowserSpeechProvider({
1443
1336
  ? `${role}-worker-protocol-mismatch`
1444
1337
  : `${role}-worker-crashed`);
1445
1338
  activeOperation = null;
1446
- artifactGraphAdmission = null;
1447
1339
  }
1448
1340
  try {
1449
1341
  releaseSlot(slot);
@@ -1454,7 +1346,6 @@ function createBrowserSpeechProvider({
1454
1346
  errorCode = releaseFailure.code;
1455
1347
  lifecycleReason = releaseFailure.reason;
1456
1348
  activeOperation = null;
1457
- artifactGraphAdmission = null;
1458
1349
  }
1459
1350
  throw releaseFailure;
1460
1351
  }
@@ -1469,19 +1360,16 @@ function createBrowserSpeechProvider({
1469
1360
  `${role}-worker-client-authority-mismatch`,
1470
1361
  );
1471
1362
  }
1472
- const configuration = Object.freeze({
1363
+ const configuration = completeValue({
1473
1364
  role,
1474
1365
  runtime: prepared.runtime,
1475
1366
  model: prepared.model,
1476
- security: effectiveSecurity,
1477
1367
  ...(authority.graph ? {
1478
- artifactGraphId: authority.artifactGraphId,
1479
1368
  artifactGraphProtocol: authority.graph.protocol,
1480
1369
  } : {}),
1481
1370
  });
1482
1371
  await slot.client.request("load", { configuration }, {
1483
1372
  signal: linked.controller.signal,
1484
- progress: record.publishProgress,
1485
1373
  });
1486
1374
  throwIfAborted(
1487
1375
  linked.controller.signal,
@@ -1492,14 +1380,11 @@ function createBrowserSpeechProvider({
1492
1380
  "ARCANE_AI_OPERATION_SUPERSEDED",
1493
1381
  "Browser speech loading was superseded.",
1494
1382
  undefined,
1495
- lifecycleReason === `${role}-load-superseded-by-security-change`
1496
- ? lifecycleReason
1497
- : `${role}-load-superseded-by-unload`,
1383
+ `${role}-load-superseded-by-unload`,
1498
1384
  );
1499
1385
  }
1500
1386
  active = slot;
1501
1387
  cache = prepared.cache;
1502
- artifactGraphAdmission = prepared.artifactGraphAdmission ?? null;
1503
1388
  state = "ready";
1504
1389
  lifecycleReason = `${role}-load-completed`;
1505
1390
  activeOperation = null;
@@ -1508,13 +1393,6 @@ function createBrowserSpeechProvider({
1508
1393
  let failure = linked.controller.signal.aborted
1509
1394
  ? linkedAbortFailure(linked, `${role}-load-cancelled`)
1510
1395
  : trustedLoadFailure(error, role);
1511
- if (!linked.controller.signal.aborted && prepared === null) {
1512
- record.integrity = providerIntegrity(effectiveSecurity, "failed");
1513
- lastIntegrity = Object.freeze({
1514
- security: effectiveSecurity,
1515
- integrity: record.integrity,
1516
- });
1517
- }
1518
1396
  try {
1519
1397
  if (slot) await terminateSlot(slot, failure);
1520
1398
  else prepared?.release();
@@ -1644,7 +1522,7 @@ function createBrowserSpeechProvider({
1644
1522
  ? encodeSharedSynthesisResult(result, authority)
1645
1523
  : result;
1646
1524
  })();
1647
- requestOperation = Object.freeze({
1525
+ requestOperation = completeValue({
1648
1526
  promise,
1649
1527
  abort: (
1650
1528
  reason = ROLE_REQUEST_REASON[role],
@@ -1673,7 +1551,6 @@ function createBrowserSpeechProvider({
1673
1551
  failure = trustedWorkerFailure(cleanupError, role);
1674
1552
  }
1675
1553
  state = "unloaded";
1676
- artifactGraphAdmission = null;
1677
1554
  }
1678
1555
  lifecycleReason = failure?.reason
1679
1556
  ?? (failure?.code === "ARCANE_AI_REQUEST_ABORTED"
@@ -1746,7 +1623,6 @@ function createBrowserSpeechProvider({
1746
1623
  `${role}-worker-terminated-by-unload`,
1747
1624
  ));
1748
1625
  cache = null;
1749
- artifactGraphAdmission = null;
1750
1626
  state = "unloaded";
1751
1627
  lifecycleReason = `${role}-unload-completed`;
1752
1628
  activeOperation = null;
@@ -1813,7 +1689,7 @@ function createBrowserSpeechProvider({
1813
1689
  );
1814
1690
  },
1815
1691
  };
1816
- return Object.freeze(provider);
1692
+ return completeValue(provider);
1817
1693
  }
1818
1694
 
1819
1695
  export function createBrowserWhisperProvider(options = {}) {