arcane-os 0.3.1 → 0.3.3

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 +27 -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 +113 -69
  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 +19 -149
  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 +42 -327
  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 +48 -66
  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 +492 -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 +48 -79
  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 +55 -135
  103. package/src/testing-loader.mjs +164 -0
  104. package/src/testing.mjs +1 -1
  105. package/src/toolchain.mjs +133 -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
@@ -1,23 +1,20 @@
1
- import { createStreamingSha256 } from "./internal/sha256.mjs";
2
1
  import {
3
2
  normalizeModelSecurity,
4
- resolveModelSecurity,
5
3
  } from "./model-controller.mjs";
6
4
 
5
+ const completeValue = (value) => value;
6
+
7
7
  export const BROWSER_SPEECH_ARTIFACT_PROTOCOL =
8
8
  "arcane-ai-browser-speech-artifacts/1";
9
9
  export const BROWSER_SPEECH_ARTIFACT_GRAPH_PROTOCOL =
10
10
  "arcane-ai-browser-speech-artifact-graph/1";
11
11
 
12
12
  const MODEL_AUTHORITY_PROTOCOL = "arcane-ai-model-authority/1";
13
- const MANIFEST_SCHEMA = "arcane.ai.browser-speech.assets.v1";
14
- const ARTIFACT_GRAPH_MANIFEST_SCHEMA =
15
- "arcane.ai.browser-speech.authenticated-artifact-graph.v1";
16
13
  const ARTIFACT_GRAPH_KIND = "browser-speech-authenticated-artifact-graph";
17
14
  const ARTIFACT_GRAPH_MODULE_KIND =
18
15
  "browser-speech-authenticated-artifact-graph";
19
16
  const ARTIFACT_GRAPH_GUARDS = "__arcaneBrowserSpeechArtifactGraphGuardsV1";
20
- const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
17
+ const ARTIFACT_MODULE_ROUTER = "__arcaneBrowserSpeechModuleRouterV1";
21
18
  const MUTABLE_PATH_PATTERN = /\/(?:resolve\/)?(?:main|master|latest)(?:\/|$)/iu;
22
19
  const ARTIFACT_GRAPH_MUTABLE_SOURCE_PATTERN =
23
20
  /\/(?:refs\/heads\/(?:main|master)|resolve\/(?:main|master)|(?:main|master|latest))(?:\/|$)|@(?:latest|next)(?:\/|$)/iu;
@@ -33,10 +30,7 @@ const PLATFORM_CREATE_OBJECT_URL = typeof globalThis.URL?.createObjectURL === "f
33
30
  const PLATFORM_REVOKE_OBJECT_URL = typeof globalThis.URL?.revokeObjectURL === "function"
34
31
  ? globalThis.URL.revokeObjectURL.bind(globalThis.URL)
35
32
  : null;
36
- const PLATFORM_FETCH = typeof globalThis.fetch === "function"
37
- ? globalThis.fetch.bind(globalThis)
38
- : null;
39
- const LEGACY_ARTIFACT_ERROR_REASONS = Object.freeze({
33
+ const LEGACY_ARTIFACT_ERROR_REASONS = completeValue({
40
34
  ARCANE_AI_REQUEST_ABORTED: "browser-speech-artifact-preparation-cancelled",
41
35
  ARCANE_AI_STORAGE_BUSY: "browser-speech-artifact-dbopfs-write-lock-unavailable",
42
36
  ARCANE_AI_STORAGE_UNAVAILABLE: "browser-speech-artifact-dbopfs-table-unavailable",
@@ -47,8 +41,6 @@ const LEGACY_ARTIFACT_ERROR_REASONS = Object.freeze({
47
41
  ARCANE_AI_ARTIFACT_SOURCE_UNAVAILABLE: "browser-speech-artifact-fetch-unavailable",
48
42
  ARCANE_AI_ARTIFACT_DOWNLOAD_FAILED: "browser-speech-artifact-fetch-rejected",
49
43
  ARCANE_AI_ARTIFACT_SOURCE_CHANGED: "browser-speech-artifact-source-redirected",
50
- ARCANE_AI_ARTIFACT_SIZE_MISMATCH: "browser-speech-artifact-byte-length-mismatch",
51
- ARCANE_AI_ARTIFACT_DIGEST_MISMATCH: "browser-speech-artifact-sha256-mismatch",
52
44
  ARCANE_AI_ARTIFACT_CACHE_REJECTED: "browser-speech-artifact-dbopfs-cache-rejected",
53
45
  ARCANE_AI_ARTIFACT_OFFLINE_MISS: "browser-speech-artifact-offline-cache-miss",
54
46
  });
@@ -131,11 +123,7 @@ function requiredText(value, label) {
131
123
  }
132
124
 
133
125
  function identifier(value, label) {
134
- const result = requiredText(value, label);
135
- if (result.length > 128) {
136
- throw new TypeError(`${label} must not exceed 128 characters.`);
137
- }
138
- return result;
126
+ return requiredText(value, label);
139
127
  }
140
128
 
141
129
  function artifactGraphText(value, label, reason = "artifact-graph-field-text-required") {
@@ -151,11 +139,20 @@ function artifactGraphIdentifier(
151
139
  missingReason = "artifact-graph-identifier-missing",
152
140
  lengthReason = "artifact-graph-identifier-length-exceeded",
153
141
  ) {
154
- const result = artifactGraphText(value, label, missingReason);
155
- if (result.length > 128) {
156
- throw artifactGraphTypeError(lengthReason, `${label} must not exceed 128 characters.`);
142
+ return artifactGraphText(value, label, missingReason);
143
+ }
144
+
145
+ function ordinarySourceUrl(value, label) {
146
+ let result;
147
+ try {
148
+ result = new URL(value, globalThis.location?.href);
149
+ } catch {
150
+ throw new TypeError(`${label} must be a valid URL.`);
157
151
  }
158
- return result;
152
+ if (result.username || result.password) {
153
+ throw new TypeError(`${label} must not contain credentials.`);
154
+ }
155
+ return result.href;
159
156
  }
160
157
 
161
158
  function immutableUrl(value, label) {
@@ -209,26 +206,6 @@ function canonicalArtifactPath(
209
206
  return path;
210
207
  }
211
208
 
212
- function graphSha256(
213
- value,
214
- label,
215
- missingReason = "artifact-graph-file-sha256-missing",
216
- formatReason = "artifact-graph-file-sha256-format-mismatch",
217
- ) {
218
- const sha256 = artifactGraphText(
219
- value,
220
- label,
221
- missingReason,
222
- );
223
- if (sha256 !== value || !SHA256_PATTERN.test(sha256)) {
224
- throw artifactGraphTypeError(
225
- formatReason,
226
- `${label} must contain exactly 64 lowercase hexadecimal characters.`,
227
- );
228
- }
229
- return sha256;
230
- }
231
-
232
209
  function graphPositiveInteger(
233
210
  value,
234
211
  label,
@@ -306,16 +283,10 @@ function graphRuntimeRequestUrl(value, label) {
306
283
  } catch (error) {
307
284
  throw artifactGraphTypeError(
308
285
  "artifact-graph-runtime-request-url-not-absolute",
309
- `${label} must be an absolute HTTPS URL.`,
286
+ `${label} must be an absolute URL.`,
310
287
  error,
311
288
  );
312
289
  }
313
- if (result.protocol !== "https:") {
314
- throw artifactGraphTypeError(
315
- "artifact-graph-runtime-request-url-protocol-not-https",
316
- `${label} must use HTTPS.`,
317
- );
318
- }
319
290
  if (result.username || result.password) {
320
291
  throw artifactGraphTypeError(
321
292
  "artifact-graph-runtime-request-url-credentials-rejected",
@@ -349,16 +320,10 @@ function graphRedirectFinalOrigin(value, label) {
349
320
  } catch (error) {
350
321
  throw artifactGraphTypeError(
351
322
  "artifact-graph-source-redirect-final-origin-not-absolute",
352
- `${label} must be an absolute HTTPS origin.`,
323
+ `${label} must be an absolute origin.`,
353
324
  error,
354
325
  );
355
326
  }
356
- if (result.protocol !== "https:") {
357
- throw artifactGraphTypeError(
358
- "artifact-graph-source-redirect-final-origin-protocol-not-https",
359
- `${label} must use HTTPS.`,
360
- );
361
- }
362
327
  if (result.username || result.password) {
363
328
  throw artifactGraphTypeError(
364
329
  "artifact-graph-source-redirect-final-origin-credentials-rejected",
@@ -387,7 +352,7 @@ function graphRedirectFinalOrigin(value, label) {
387
352
  }
388
353
 
389
354
  function normalizeGraphRedirectFinalOrigins(value, path) {
390
- if (value === undefined) return Object.freeze([]);
355
+ if (value === undefined) return completeValue([]);
391
356
  if (!Array.isArray(value)) {
392
357
  throw artifactGraphTypeError(
393
358
  "artifact-graph-source-redirect-final-origins-not-array",
@@ -411,10 +376,10 @@ function normalizeGraphRedirectFinalOrigins(value, path) {
411
376
  `Artifact graph file ${path} redirectFinalOrigins must be unique after canonicalization.`,
412
377
  );
413
378
  }
414
- return Object.freeze([...origins].sort(lexicalCompare));
379
+ return completeValue([...origins].sort(lexicalCompare));
415
380
  }
416
381
 
417
- function graphImmutableUrl(value, label, revision, sha256) {
382
+ function graphImmutableUrl(value, label, revision) {
418
383
  let url;
419
384
  try {
420
385
  url = immutableUrl(
@@ -433,47 +398,41 @@ function graphImmutableUrl(value, label, revision, sha256) {
433
398
  error,
434
399
  );
435
400
  }
436
- const identityUrl = url.toLowerCase();
437
401
  if (ARTIFACT_GRAPH_MUTABLE_SOURCE_PATTERN.test(new URL(url).pathname)) {
438
402
  throw artifactGraphTypeError(
439
403
  "artifact-graph-source-url-mutable",
440
404
  `${label} names a mutable branch, channel, or release alias.`,
441
405
  );
442
406
  }
443
- if (
444
- !identityUrl.includes(revision.toLowerCase())
445
- && !identityUrl.includes(sha256)
446
- ) {
407
+ if (!url.toLowerCase().includes(revision.toLowerCase())) {
447
408
  throw artifactGraphTypeError(
448
409
  "artifact-graph-source-revision-unbound",
449
- `${label} must contain the file revision or SHA-256 identity.`,
410
+ `${label} must contain the file revision.`,
450
411
  );
451
412
  }
452
413
  return url;
453
414
  }
454
415
 
455
- function canonicalJson(value) {
416
+ function lexicalCompare(left, right) {
417
+ return left < right ? -1 : left > right ? 1 : 0;
418
+ }
419
+
420
+ // Structural comparison for dormant authenticated-graph compatibility
421
+ // normalizers only. Ordinary graph creation treats edges and transforms as
422
+ // inert metadata and never calls these helpers. Do not enable them, including
423
+ // for secure mode, without an explicit review with the user.
424
+ function compatibilityRecordKey(value) {
456
425
  if (Array.isArray(value)) {
457
- return `[${value.map(canonicalJson).join(",")}]`;
426
+ return `[${value.map(compatibilityRecordKey).join(",")}]`;
458
427
  }
459
428
  if (value && typeof value === "object") {
460
429
  return `{${Object.keys(value).sort().map((key) =>
461
- `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
430
+ `${JSON.stringify(key)}:${compatibilityRecordKey(value[key])}`).join(",")}}`;
462
431
  }
463
432
  return JSON.stringify(value);
464
433
  }
465
434
 
466
- function lexicalCompare(left, right) {
467
- return left < right ? -1 : left > right ? 1 : 0;
468
- }
469
-
470
- function sha256Text(value) {
471
- const digest = createStreamingSha256();
472
- digest.update(new TextEncoder().encode(value));
473
- return digest.digestHex();
474
- }
475
-
476
- function normalizeFile(value, kind, index, revision) {
435
+ function normalizeFile(value, kind, index, revision, secure) {
477
436
  if (!value || typeof value !== "object" || Array.isArray(value)) {
478
437
  throw new TypeError(`${kind} file ${String(index)} must be an object.`);
479
438
  }
@@ -485,37 +444,19 @@ function normalizeFile(value, kind, index, revision) {
485
444
  ) {
486
445
  throw new TypeError(`${kind} file path must be a normalized relative path.`);
487
446
  }
488
- const url = immutableUrl(value.url, `${kind} file url`);
489
- let bytes = null;
490
- if (value.bytes !== undefined) {
491
- if (!Number.isSafeInteger(value.bytes) || value.bytes < 1) {
492
- throw new TypeError(`${kind} file bytes must be a positive safe integer.`);
493
- }
494
- bytes = value.bytes;
495
- }
496
- let sha256 = null;
497
- if (value.sha256 !== undefined) {
498
- sha256 = requiredText(value.sha256, `${kind} file sha256`).toLowerCase();
499
- if (!SHA256_PATTERN.test(sha256)) {
500
- throw new TypeError(`${kind} file sha256 must be 64 lowercase hexadecimal characters.`);
501
- }
502
- }
503
- const identityUrl = url.toLowerCase();
504
- if (
505
- !identityUrl.includes(revision.toLowerCase())
506
- && (sha256 === null || !identityUrl.includes(sha256))
507
- ) {
447
+ const url = secure
448
+ ? immutableUrl(value.url, `${kind} file url`)
449
+ : ordinarySourceUrl(value.url, `${kind} file url`);
450
+ if (secure && !url.toLowerCase().includes(revision.toLowerCase())) {
508
451
  throw new TypeError(
509
- `${kind} file URL must contain its caller-supplied revision or SHA-256 identity.`,
452
+ `${kind} file URL must contain its caller-supplied revision.`,
510
453
  );
511
454
  }
512
- return Object.freeze({
455
+ return completeValue({
513
456
  kind,
514
457
  index,
515
458
  path,
516
459
  url,
517
- bytes,
518
- sha256,
519
460
  mediaType: typeof value.mediaType === "string" && value.mediaType.trim()
520
461
  ? value.mediaType.trim()
521
462
  : kind === "runtime" && /\.(?:m?js)$/iu.test(path)
@@ -524,7 +465,10 @@ function normalizeFile(value, kind, index, revision) {
524
465
  });
525
466
  }
526
467
 
527
- function uniqueFiles(files, label, kind, revision, { allowEmpty = false } = {}) {
468
+ function uniqueFiles(files, label, kind, revision, {
469
+ allowEmpty = false,
470
+ secure = false,
471
+ } = {}) {
528
472
  if (!Array.isArray(files) || (!allowEmpty && files.length < 1)) {
529
473
  throw new TypeError(
530
474
  allowEmpty
@@ -534,8 +478,8 @@ function uniqueFiles(files, label, kind, revision, { allowEmpty = false } = {})
534
478
  }
535
479
  const paths = new Set();
536
480
  const urls = new Set();
537
- return Object.freeze(files.map((value, index) => {
538
- const file = normalizeFile(value, kind, index, revision);
481
+ return completeValue(files.map((value, index) => {
482
+ const file = normalizeFile(value, kind, index, revision, secure);
539
483
  if (paths.has(file.path) || urls.has(file.url)) {
540
484
  throw new TypeError(`${label} file paths and URLs must be unique.`);
541
485
  }
@@ -551,12 +495,10 @@ function publicFile(file) {
551
495
  url: file.url,
552
496
  mediaType: file.mediaType,
553
497
  };
554
- if (file.bytes !== null) result.bytes = file.bytes;
555
- if (file.sha256 !== null) result.sha256 = file.sha256;
556
- return Object.freeze(result);
498
+ return completeValue(result);
557
499
  }
558
500
 
559
- function normalizeArtifactGraphFile(value, index) {
501
+ function normalizeArtifactGraphFile(value, index, secure = false) {
560
502
  if (!value || typeof value !== "object" || Array.isArray(value)) {
561
503
  throw artifactGraphTypeError(
562
504
  "artifact-graph-file-descriptor-not-object",
@@ -584,54 +526,24 @@ function normalizeArtifactGraphFile(value, index) {
584
526
  "artifact-graph-file-revision-missing",
585
527
  "artifact-graph-file-revision-length-exceeded",
586
528
  );
587
- const bytes = graphPositiveInteger(
588
- value.bytes,
589
- `Artifact graph file ${path} bytes`,
590
- "artifact-graph-file-byte-length-positive-safe-integer-required",
591
- );
592
- const sha256 = graphSha256(
593
- value.sha256,
594
- `Artifact graph file ${path} sha256`,
595
- );
596
- const sourceUrl = graphImmutableUrl(
597
- value.sourceUrl ?? value.url,
598
- `Artifact graph file ${path} sourceUrl`,
599
- revision,
600
- sha256,
601
- );
529
+ const sourceUrl = secure
530
+ ? graphImmutableUrl(
531
+ value.sourceUrl ?? value.url,
532
+ `Artifact graph file ${path} sourceUrl`,
533
+ revision,
534
+ )
535
+ : ordinarySourceUrl(
536
+ value.sourceUrl ?? value.url,
537
+ `Artifact graph file ${path} sourceUrl`,
538
+ );
602
539
  const redirectFinalOrigins = normalizeGraphRedirectFinalOrigins(
603
540
  value.redirectFinalOrigins,
604
541
  path,
605
542
  );
606
- const license = artifactGraphText(
607
- value.license,
608
- `Artifact graph file ${path} license`,
609
- "artifact-graph-file-license-missing",
610
- );
611
- if (license !== value.license) {
612
- throw artifactGraphTypeError(
613
- "artifact-graph-file-license-whitespace-rejected",
614
- `Artifact graph file ${path} license must not contain surrounding whitespace.`,
615
- );
616
- }
617
- if (license !== license.normalize("NFC")) {
618
- throw artifactGraphTypeError(
619
- "artifact-graph-file-license-not-nfc",
620
- `Artifact graph file ${path} license must be NFC-normalized.`,
621
- );
622
- }
623
- if (/[\u0000-\u001f\u007f]/u.test(license)) {
624
- throw artifactGraphTypeError(
625
- "artifact-graph-file-license-control-character-rejected",
626
- `Artifact graph file ${path} license must not contain control characters.`,
627
- );
628
- }
629
- if (license.length > 256) {
630
- throw artifactGraphTypeError(
631
- "artifact-graph-file-license-length-exceeded",
632
- `Artifact graph file ${path} license must not exceed 256 characters.`,
633
- );
634
- }
543
+ // Legal metadata belongs to the selected upstream distribution. Preserve a
544
+ // caller-supplied value as inert metadata, but never require or interpret it
545
+ // as part of ordinary runtime materialization.
546
+ const license = value.license;
635
547
  const mediaType = exactMediaType(
636
548
  value.mediaType,
637
549
  `Artifact graph file ${path} mediaType`,
@@ -682,35 +594,31 @@ function normalizeArtifactGraphFile(value, index) {
682
594
  `Artifact graph file ${path} runtimeRequestUrls must be unique.`,
683
595
  );
684
596
  }
685
- return Object.freeze({
597
+ return completeValue({
686
598
  kind,
687
599
  index,
688
600
  path,
689
601
  sourceUrl,
690
602
  revision,
691
- license,
603
+ ...(license === undefined ? {} : { license }),
692
604
  mediaType,
693
605
  sourceMediaType,
694
- bytes,
695
- sha256,
696
- runtimeRequestUrls: Object.freeze(normalizedRequestUrls),
606
+ runtimeRequestUrls: completeValue(normalizedRequestUrls),
697
607
  redirectFinalOrigins,
698
608
  });
699
609
  }
700
610
 
701
611
  function publicArtifactGraphFile(file) {
702
- return Object.freeze({
612
+ return completeValue({
703
613
  kind: file.kind,
704
614
  path: file.path,
705
615
  sourceUrl: file.sourceUrl,
706
616
  revision: file.revision,
707
- license: file.license,
617
+ ...(file.license === undefined ? {} : { license: file.license }),
708
618
  mediaType: file.mediaType,
709
619
  ...(file.sourceMediaType === file.mediaType
710
620
  ? {}
711
621
  : { sourceMediaType: file.sourceMediaType }),
712
- bytes: file.bytes,
713
- sha256: file.sha256,
714
622
  runtimeRequestUrls: file.runtimeRequestUrls,
715
623
  ...(file.redirectFinalOrigins.length < 1
716
624
  ? {}
@@ -814,17 +722,18 @@ function normalizeGraphTargets(value, label, filesByPath, {
814
722
  "artifact-graph-edge-target-specifier-missing",
815
723
  )
816
724
  : null;
817
- return Object.freeze({ match, targetPath, exactSpecifier });
725
+ return completeValue({ match, targetPath, exactSpecifier });
818
726
  });
819
- targets.sort((left, right) => lexicalCompare(canonicalJson(left), canonicalJson(right)));
820
- const identities = new Set(targets.map(canonicalJson));
727
+ targets.sort((left, right) =>
728
+ lexicalCompare(compatibilityRecordKey(left), compatibilityRecordKey(right)));
729
+ const identities = new Set(targets.map(compatibilityRecordKey));
821
730
  if (identities.size !== targets.length) {
822
731
  throw artifactGraphTypeError(
823
732
  "artifact-graph-edge-target-duplicate",
824
733
  `${label} targets must be unique.`,
825
734
  );
826
735
  }
827
- return Object.freeze(targets);
736
+ return completeValue(targets);
828
737
  }
829
738
 
830
739
  function normalizeArtifactGraphEdges(value, filesByPath, negativeRuntimeRequestUrls) {
@@ -870,7 +779,8 @@ function normalizeArtifactGraphEdges(value, filesByPath, negativeRuntimeRequestU
870
779
  const occurrence = normalizeGraphOccurrence(edge.occurrence, label);
871
780
  return normalize(edge, label, modulePath, occurrence);
872
781
  });
873
- normalized.sort((left, right) => lexicalCompare(canonicalJson(left), canonicalJson(right)));
782
+ normalized.sort((left, right) =>
783
+ lexicalCompare(compatibilityRecordKey(left), compatibilityRecordKey(right)));
874
784
  const occurrences = new Set();
875
785
  for (const edge of normalized) {
876
786
  const key = `${edge.modulePath}\u0000${String(edge.occurrence)}`;
@@ -882,12 +792,12 @@ function normalizeArtifactGraphEdges(value, filesByPath, negativeRuntimeRequestU
882
792
  }
883
793
  occurrences.add(key);
884
794
  }
885
- return Object.freeze(normalized);
795
+ return completeValue(normalized);
886
796
  }
887
797
 
888
798
  const staticImports = normalizeArray(
889
799
  "staticImports",
890
- (edge, label, modulePath, occurrence) => Object.freeze({
800
+ (edge, label, modulePath, occurrence) => completeValue({
891
801
  modulePath,
892
802
  occurrence,
893
803
  specifier: artifactGraphText(
@@ -918,7 +828,7 @@ function normalizeArtifactGraphEdges(value, filesByPath, negativeRuntimeRequestU
918
828
  `${label} must have targets exactly when its edgePolicy admits artifact targets.`,
919
829
  );
920
830
  }
921
- return Object.freeze({ modulePath, occurrence, edgePolicy, targets });
831
+ return completeValue({ modulePath, occurrence, edgePolicy, targets });
922
832
  },
923
833
  );
924
834
  const moduleWorkers = normalizeArray(
@@ -944,7 +854,7 @@ function normalizeArtifactGraphEdges(value, filesByPath, negativeRuntimeRequestU
944
854
  `${label} self-module-url target must equal modulePath.`,
945
855
  );
946
856
  }
947
- return Object.freeze({ modulePath, occurrence, edgePolicy, targets });
857
+ return completeValue({ modulePath, occurrence, edgePolicy, targets });
948
858
  },
949
859
  );
950
860
  const fetches = normalizeArray(
@@ -1021,13 +931,13 @@ function normalizeArtifactGraphEdges(value, filesByPath, negativeRuntimeRequestU
1021
931
  `${label} rejected inactive branch must not name fetch targets.`,
1022
932
  );
1023
933
  }
1024
- return Object.freeze({
934
+ return completeValue({
1025
935
  modulePath,
1026
936
  occurrence,
1027
937
  edgePolicy,
1028
- methods: Object.freeze(["GET"]),
1029
- targetPaths: Object.freeze(normalizedTargetPaths),
1030
- negativeRuntimeRequestUrls: Object.freeze(normalizedNegativeUrls),
938
+ methods: completeValue(["GET"]),
939
+ targetPaths: completeValue(normalizedTargetPaths),
940
+ negativeRuntimeRequestUrls: completeValue(normalizedNegativeUrls),
1031
941
  allowMaterializedUrls: edge.allowMaterializedUrls === true,
1032
942
  });
1033
943
  },
@@ -1071,16 +981,16 @@ function normalizeArtifactGraphEdges(value, filesByPath, negativeRuntimeRequestU
1071
981
  `${label} must have targets exactly when its edgePolicy admits authenticated cache reads.`,
1072
982
  );
1073
983
  }
1074
- return Object.freeze({
984
+ return completeValue({
1075
985
  modulePath,
1076
986
  occurrence,
1077
987
  edgePolicy,
1078
988
  cacheName,
1079
- targetPaths: Object.freeze(normalizedTargetPaths),
989
+ targetPaths: completeValue(normalizedTargetPaths),
1080
990
  });
1081
991
  },
1082
992
  );
1083
- return Object.freeze({
993
+ return completeValue({
1084
994
  staticImports,
1085
995
  dynamicImports,
1086
996
  moduleWorkers,
@@ -1113,21 +1023,22 @@ function normalizeArtifactGraphTransforms(value, filesByPath) {
1113
1023
  `${label} kind is not admitted.`,
1114
1024
  );
1115
1025
  }
1116
- return Object.freeze({
1026
+ return completeValue({
1117
1027
  kind: transform.kind,
1118
1028
  modulePath: normalizeGraphModulePath(transform.modulePath, label, filesByPath),
1119
1029
  occurrence: normalizeGraphOccurrence(transform.occurrence, label),
1120
1030
  });
1121
1031
  });
1122
- normalized.sort((left, right) => lexicalCompare(canonicalJson(left), canonicalJson(right)));
1123
- const identities = new Set(normalized.map(canonicalJson));
1032
+ normalized.sort((left, right) =>
1033
+ lexicalCompare(compatibilityRecordKey(left), compatibilityRecordKey(right)));
1034
+ const identities = new Set(normalized.map(compatibilityRecordKey));
1124
1035
  if (identities.size !== normalized.length) {
1125
1036
  throw artifactGraphTypeError(
1126
1037
  "artifact-graph-transform-occurrence-duplicate",
1127
1038
  "Artifact graph transform occurrences must be unique.",
1128
1039
  );
1129
1040
  }
1130
- return Object.freeze(normalized);
1041
+ return completeValue(normalized);
1131
1042
  }
1132
1043
 
1133
1044
  function normalizeArtifactGraphVoices(value, defaultVoice, filesByPath) {
@@ -1161,7 +1072,7 @@ function normalizeArtifactGraphVoices(value, defaultVoice, filesByPath) {
1161
1072
  `Artifact graph voice ${id} must name a voice-style-binary file.`,
1162
1073
  );
1163
1074
  }
1164
- return Object.freeze({ id, path });
1075
+ return completeValue({ id, path });
1165
1076
  });
1166
1077
  voices.sort((left, right) => lexicalCompare(left.id, right.id));
1167
1078
  const ids = new Set(voices.map(({ id }) => id));
@@ -1178,10 +1089,10 @@ function normalizeArtifactGraphVoices(value, defaultVoice, filesByPath) {
1178
1089
  "Artifact graph defaultVoice must name one declared voice.",
1179
1090
  );
1180
1091
  }
1181
- return Object.freeze(voices);
1092
+ return completeValue(voices);
1182
1093
  }
1183
1094
 
1184
- function artifactGraphIdentityProjection({
1095
+ function artifactGraphProjection({
1185
1096
  providerId,
1186
1097
  role,
1187
1098
  model,
@@ -1190,27 +1101,27 @@ function artifactGraphIdentityProjection({
1190
1101
  edges,
1191
1102
  transforms,
1192
1103
  }) {
1193
- return Object.freeze({
1104
+ return completeValue({
1194
1105
  protocol: BROWSER_SPEECH_ARTIFACT_GRAPH_PROTOCOL,
1195
1106
  kind: ARTIFACT_GRAPH_KIND,
1196
1107
  providerId,
1197
1108
  role,
1198
1109
  model,
1199
1110
  runtime,
1200
- files: Object.freeze(files.map(publicArtifactGraphFile)),
1111
+ files: completeValue(files.map((file) => publicArtifactGraphFile(file))),
1201
1112
  edges,
1202
1113
  transforms,
1203
1114
  });
1204
1115
  }
1205
1116
 
1206
1117
  /**
1207
- * Creates one closed, caller-selected browser speech artifact graph. Every
1208
- * executable and data byte is immutable, content-addressed, and reachable only
1209
- * through an explicit graph edge or exact local runtime request route.
1118
+ * Creates one caller-selected browser speech artifact graph. Ordinary mode
1119
+ * preserves the functional runtime/model closure without computing or
1120
+ * publishing byte identities.
1210
1121
  */
1211
1122
  export function createBrowserSpeechArtifactGraph({
1212
1123
  kind = ARTIFACT_GRAPH_KIND,
1213
- identitySha256,
1124
+ security,
1214
1125
  providerId = null,
1215
1126
  role,
1216
1127
  model,
@@ -1231,6 +1142,10 @@ export function createBrowserSpeechArtifactGraph({
1231
1142
  'Browser speech artifact graph role must be "stt" or "tts".',
1232
1143
  );
1233
1144
  }
1145
+ const normalizedSecurity = normalizeModelSecurity(
1146
+ security,
1147
+ "Browser speech artifact graph security",
1148
+ );
1234
1149
  const normalizedProviderId = providerId === null || providerId === undefined
1235
1150
  ? null
1236
1151
  : artifactGraphIdentifier(
@@ -1257,7 +1172,8 @@ export function createBrowserSpeechArtifactGraph({
1257
1172
  "Browser speech artifact graph requires a nonempty files array.",
1258
1173
  );
1259
1174
  }
1260
- const normalizedFiles = files.map(normalizeArtifactGraphFile);
1175
+ const normalizedFiles = files.map((file, index) =>
1176
+ normalizeArtifactGraphFile(file, index, false));
1261
1177
  normalizedFiles.sort((left, right) => lexicalCompare(left.path, right.path));
1262
1178
  const filesByPath = new Map();
1263
1179
  const lowercasePaths = new Set();
@@ -1292,7 +1208,7 @@ export function createBrowserSpeechArtifactGraph({
1292
1208
  if (sourceUrls.has(url)) {
1293
1209
  throw artifactGraphTypeError(
1294
1210
  "artifact-graph-runtime-request-route-ambiguous",
1295
- `Artifact graph runtime request URL ${url} overlaps an immutable source URL.`,
1211
+ `Artifact graph runtime request URL ${url} overlaps a declared source URL.`,
1296
1212
  );
1297
1213
  }
1298
1214
  }
@@ -1402,35 +1318,10 @@ export function createBrowserSpeechArtifactGraph({
1402
1318
  );
1403
1319
  }
1404
1320
 
1405
- const negativeRoutes = runtime.negativeRuntimeRequestUrls ?? [];
1406
- if (!Array.isArray(negativeRoutes)) {
1407
- throw artifactGraphTypeError(
1408
- "artifact-graph-negative-runtime-routes-not-array",
1409
- "Browser speech artifact graph runtime negativeRuntimeRequestUrls must be an array.",
1410
- );
1411
- }
1412
- const normalizedNegativeRoutes = [...new Set(negativeRoutes.map((url, index) =>
1413
- graphRuntimeRequestUrl(
1414
- url,
1415
- `Browser speech artifact graph negativeRuntimeRequestUrls[${String(index)}]`,
1416
- )))].sort();
1417
- if (normalizedNegativeRoutes.length !== negativeRoutes.length) {
1418
- throw artifactGraphTypeError(
1419
- "artifact-graph-negative-runtime-route-duplicate",
1420
- "Browser speech artifact graph negative runtime request routes must be unique.",
1421
- );
1422
- }
1423
- for (const url of normalizedNegativeRoutes) {
1424
- if (
1425
- requestUrls.has(url)
1426
- || sourceUrls.has(url)
1427
- ) {
1428
- throw artifactGraphTypeError(
1429
- "artifact-graph-negative-runtime-route-ambiguous",
1430
- `Artifact graph negative runtime request URL ${url} must not overlap a positive graph route.`,
1431
- );
1432
- }
1433
- }
1321
+ // Negative-route admission belonged to the former authenticated graph. The
1322
+ // ordinary materializer now routes known files and lets every unknown URL
1323
+ // continue through the browser's native operation.
1324
+ const normalizedNegativeRoutes = completeValue([]);
1434
1325
 
1435
1326
  const modelId = artifactGraphIdentifier(
1436
1327
  model.id,
@@ -1485,64 +1376,38 @@ export function createBrowserSpeechArtifactGraph({
1485
1376
  }
1486
1377
  const voices = role === "tts"
1487
1378
  ? normalizeArtifactGraphVoices(model.voices, defaultVoice, filesByPath)
1488
- : Object.freeze([]);
1489
-
1490
- const negativeRuntimeRequestUrlSet = new Set(normalizedNegativeRoutes);
1491
- const normalizedEdges = normalizeArtifactGraphEdges(
1492
- edges,
1493
- filesByPath,
1494
- negativeRuntimeRequestUrlSet,
1495
- );
1496
- const normalizedTransforms = normalizeArtifactGraphTransforms(transforms, filesByPath);
1497
- const referencedPaths = new Set([entrypoint, mjsPath, wasmPath]);
1498
- const referencedNegativeRoutes = new Set();
1499
- for (const edge of normalizedEdges.staticImports) referencedPaths.add(edge.targetPath);
1500
- for (const edge of normalizedEdges.dynamicImports) {
1501
- for (const target of edge.targets) referencedPaths.add(target.targetPath);
1502
- }
1503
- for (const edge of normalizedEdges.moduleWorkers) {
1504
- for (const target of edge.targets) referencedPaths.add(target.targetPath);
1505
- }
1506
- for (const edge of normalizedEdges.fetches) {
1507
- for (const path of edge.targetPaths) referencedPaths.add(path);
1508
- for (const url of edge.negativeRuntimeRequestUrls) referencedNegativeRoutes.add(url);
1509
- }
1510
- for (const edge of normalizedEdges.cacheOpens) {
1511
- for (const path of edge.targetPaths) referencedPaths.add(path);
1512
- }
1513
- for (const voice of voices) referencedPaths.add(voice.path);
1514
- for (const file of normalizedFiles) {
1515
- if (
1516
- file.kind !== "runtime-entrypoint-javascript"
1517
- && !referencedPaths.has(file.path)
1518
- ) {
1519
- throw artifactGraphTypeError(
1520
- "artifact-graph-file-unreachable",
1521
- `Artifact graph file ${file.path} is not reachable from a declared runtime, model, or voice capability.`,
1522
- );
1523
- }
1524
- if (
1525
- file.runtimeRequestUrls.length > 0
1526
- && !referencedPaths.has(file.path)
1527
- ) {
1528
- throw artifactGraphTypeError(
1529
- "artifact-graph-runtime-request-route-unreachable",
1530
- `Artifact graph runtime request routes for ${file.path} have no declared edge.`,
1531
- );
1532
- }
1533
- }
1534
- for (const url of normalizedNegativeRoutes) {
1535
- if (!referencedNegativeRoutes.has(url)) {
1536
- throw artifactGraphTypeError(
1537
- "artifact-graph-negative-runtime-route-unreachable",
1538
- `Artifact graph negative runtime request URL ${url} has no declared fetch edge.`,
1539
- );
1540
- }
1541
- }
1379
+ : completeValue([]);
1380
+
1381
+ // Edge and transform declarations are retained only as inert compatibility
1382
+ // metadata. Ordinary execution discovers routing from the complete file
1383
+ // inventory and never admits or rejects runtime operations through them.
1384
+ const edgeRecord = edges && typeof edges === "object" && !Array.isArray(edges)
1385
+ ? edges
1386
+ : {};
1387
+ const normalizedEdges = completeValue({
1388
+ staticImports: completeValue(Array.isArray(edgeRecord.staticImports)
1389
+ ? edgeRecord.staticImports.map((edge) => completeValue({ ...edge }))
1390
+ : []),
1391
+ dynamicImports: completeValue(Array.isArray(edgeRecord.dynamicImports)
1392
+ ? edgeRecord.dynamicImports.map((edge) => completeValue({ ...edge }))
1393
+ : []),
1394
+ moduleWorkers: completeValue(Array.isArray(edgeRecord.moduleWorkers)
1395
+ ? edgeRecord.moduleWorkers.map((edge) => completeValue({ ...edge }))
1396
+ : []),
1397
+ fetches: completeValue(Array.isArray(edgeRecord.fetches)
1398
+ ? edgeRecord.fetches.map((edge) => completeValue({ ...edge }))
1399
+ : []),
1400
+ cacheOpens: completeValue(Array.isArray(edgeRecord.cacheOpens)
1401
+ ? edgeRecord.cacheOpens.map((edge) => completeValue({ ...edge }))
1402
+ : []),
1403
+ });
1404
+ const normalizedTransforms = completeValue(Array.isArray(transforms)
1405
+ ? transforms.map((transform) => completeValue({ ...transform }))
1406
+ : []);
1542
1407
 
1543
- const runtimeFiles = Object.freeze(normalizedFiles.filter((file) =>
1408
+ const runtimeFiles = completeValue(normalizedFiles.filter((file) =>
1544
1409
  file.kind.startsWith("runtime-")));
1545
- const modelFiles = Object.freeze(normalizedFiles.filter((file) =>
1410
+ const modelFiles = completeValue(normalizedFiles.filter((file) =>
1546
1411
  !file.kind.startsWith("runtime-")));
1547
1412
  if (modelFiles.length < 1) {
1548
1413
  throw artifactGraphTypeError(
@@ -1566,30 +1431,26 @@ export function createBrowserSpeechArtifactGraph({
1566
1431
  );
1567
1432
  }
1568
1433
  }
1569
- if (modelFiles.some((file) => file.revision !== modelRevision)) {
1570
- throw artifactGraphTypeError(
1571
- "artifact-graph-model-file-revision-mismatch",
1572
- "Every artifact graph model and voice file revision must equal the model revision.",
1573
- );
1574
- }
1575
- const publicRuntimeFiles = Object.freeze(runtimeFiles.map(publicArtifactGraphFile));
1576
- const publicModelFiles = Object.freeze(modelFiles.map(publicArtifactGraphFile));
1577
- const normalizedRuntime = Object.freeze({
1434
+ const publicRuntimeFiles = completeValue(runtimeFiles.map((file) =>
1435
+ publicArtifactGraphFile(file)));
1436
+ const publicModelFiles = completeValue(modelFiles.map((file) =>
1437
+ publicArtifactGraphFile(file)));
1438
+ const normalizedRuntime = completeValue({
1578
1439
  adapter: runtimeAdapter,
1579
1440
  version: runtimeVersion,
1580
1441
  revision: runtimeRevision,
1581
1442
  entry: entrypoint,
1582
1443
  moduleGraph: ARTIFACT_GRAPH_MODULE_KIND,
1583
1444
  files: publicRuntimeFiles,
1584
- onnxWasm: Object.freeze({
1445
+ onnxWasm: completeValue({
1585
1446
  namespace,
1586
1447
  mjsPath,
1587
1448
  wasmPath,
1588
1449
  ...(numThreads === null ? {} : { numThreads }),
1589
1450
  }),
1590
- negativeRuntimeRequestUrls: Object.freeze(normalizedNegativeRoutes),
1451
+ negativeRuntimeRequestUrls: completeValue(normalizedNegativeRoutes),
1591
1452
  });
1592
- const normalizedModel = Object.freeze({
1453
+ const normalizedModel = completeValue({
1593
1454
  id: modelId,
1594
1455
  repository,
1595
1456
  revision: modelRevision,
@@ -1599,7 +1460,7 @@ export function createBrowserSpeechArtifactGraph({
1599
1460
  ...(role === "tts" ? { defaultVoice, voices } : {}),
1600
1461
  files: publicModelFiles,
1601
1462
  });
1602
- const projection = artifactGraphIdentityProjection({
1463
+ const projection = artifactGraphProjection({
1603
1464
  providerId: normalizedProviderId,
1604
1465
  role,
1605
1466
  model: normalizedModel,
@@ -1608,31 +1469,14 @@ export function createBrowserSpeechArtifactGraph({
1608
1469
  edges: normalizedEdges,
1609
1470
  transforms: normalizedTransforms,
1610
1471
  });
1611
- const computedIdentitySha256 = sha256Text(canonicalJson(projection));
1612
- if (
1613
- identitySha256 !== undefined
1614
- && graphSha256(
1615
- identitySha256,
1616
- "Browser speech artifact graph identitySha256",
1617
- "artifact-graph-identity-sha256-text-required",
1618
- "artifact-graph-identity-sha256-format-mismatch",
1619
- )
1620
- !== computedIdentitySha256
1621
- ) {
1622
- throw artifactGraphTypeError(
1623
- "artifact-graph-identity-sha256-mismatch",
1624
- "Browser speech artifact graph identitySha256 does not match its canonical descriptor.",
1625
- );
1626
- }
1627
- const graph = Object.freeze({
1472
+ const graph = completeValue({
1628
1473
  ...projection,
1629
- identitySha256: computedIdentitySha256,
1630
- artifactGraphStatus: "artifact-graph-descriptor-verified",
1474
+ ...(normalizedSecurity ? { security: normalizedSecurity } : {}),
1631
1475
  });
1632
1476
  ARTIFACT_GRAPHS.add(graph);
1633
- ARTIFACT_GRAPH_METADATA.set(graph, Object.freeze({
1477
+ ARTIFACT_GRAPH_METADATA.set(graph, completeValue({
1634
1478
  graph,
1635
- files: Object.freeze(normalizedFiles),
1479
+ files: completeValue(normalizedFiles),
1636
1480
  filesByPath,
1637
1481
  runtimeFiles,
1638
1482
  modelFiles,
@@ -1640,13 +1484,14 @@ export function createBrowserSpeechArtifactGraph({
1640
1484
  runtime: normalizedRuntime,
1641
1485
  edges: normalizedEdges,
1642
1486
  transforms: normalizedTransforms,
1487
+ ...(normalizedSecurity ? { security: normalizedSecurity } : {}),
1643
1488
  }));
1644
1489
  return graph;
1645
1490
  }
1646
1491
 
1647
1492
  /**
1648
- * Admits one caller-owned browser speech model/runtime description. The SDK
1649
- * supplies no model URL or profile; applications choose every immutable byte.
1493
+ * Accepts one caller-owned browser speech model/runtime description. The SDK
1494
+ * supplies no model URL or profile; applications choose every source.
1650
1495
  */
1651
1496
  export function createBrowserSpeechAuthority({
1652
1497
  providerId,
@@ -1672,12 +1517,18 @@ export function createBrowserSpeechAuthority({
1672
1517
  const modelId = identifier(model.id, "Browser speech model id");
1673
1518
  const modelRevision = identifier(model.revision, "Browser speech model revision");
1674
1519
  const repository = identifier(model.repository, "Browser speech model repository");
1520
+ const dtype = model.dtype === undefined
1521
+ ? null
1522
+ : identifier(model.dtype, "Browser speech model dtype");
1675
1523
  const modelFiles = uniqueFiles(
1676
1524
  model.files ?? [],
1677
1525
  "Browser speech model",
1678
1526
  "model",
1679
1527
  modelRevision,
1680
- { allowEmpty: normalizedSecurity.secure !== true },
1528
+ {
1529
+ allowEmpty: true,
1530
+ secure: false,
1531
+ },
1681
1532
  );
1682
1533
  const runtimeAdapter = requiredText(runtime.adapter, "Browser speech runtime adapter");
1683
1534
  const expectedAdapter = role === "stt"
@@ -1693,15 +1544,11 @@ export function createBrowserSpeechAuthority({
1693
1544
  "Browser speech runtime",
1694
1545
  "runtime",
1695
1546
  runtimeRevision,
1547
+ { secure: false },
1696
1548
  );
1697
1549
  const wasmPaths = runtime.wasmPaths === undefined
1698
1550
  ? null
1699
- : immutableUrl(runtime.wasmPaths, "Browser speech runtime wasmPaths");
1700
- if (normalizedSecurity.secure === true && wasmPaths !== null) {
1701
- throw new TypeError(
1702
- "Secure browser speech must materialize its ONNX runtime files instead of using remote wasmPaths.",
1703
- );
1704
- }
1551
+ : ordinarySourceUrl(runtime.wasmPaths, "Browser speech runtime wasmPaths");
1705
1552
  const entry = requiredText(runtime.entry, "Browser speech runtime entry");
1706
1553
  const entryFile = runtimeFiles.find((file) => file.path === entry);
1707
1554
  if (!entryFile) {
@@ -1710,16 +1557,17 @@ export function createBrowserSpeechAuthority({
1710
1557
  if (!/\.(?:m?js)$/iu.test(entryFile.path) || entryFile.mediaType !== "text/javascript") {
1711
1558
  throw new TypeError("Browser speech runtime entry must be a JavaScript module.");
1712
1559
  }
1713
- const normalizedModel = Object.freeze({
1560
+ const normalizedModel = completeValue({
1714
1561
  id: modelId,
1715
1562
  repository,
1716
1563
  revision: modelRevision,
1564
+ ...(dtype === null ? {} : { dtype }),
1717
1565
  defaultVoice: role === "tts"
1718
1566
  ? identifier(model.defaultVoice, "Browser Kokoro defaultVoice")
1719
1567
  : null,
1720
1568
  files: modelFiles,
1721
1569
  });
1722
- const normalizedRuntime = Object.freeze({
1570
+ const normalizedRuntime = completeValue({
1723
1571
  adapter: runtimeAdapter,
1724
1572
  version: runtimeVersion,
1725
1573
  revision: runtimeRevision,
@@ -1727,7 +1575,7 @@ export function createBrowserSpeechAuthority({
1727
1575
  ...(wasmPaths === null ? {} : { wasmPaths }),
1728
1576
  files: runtimeFiles,
1729
1577
  });
1730
- const files = Object.freeze([...runtimeFiles, ...modelFiles]);
1578
+ const files = completeValue([...runtimeFiles, ...modelFiles]);
1731
1579
  const allPaths = new Set();
1732
1580
  const allUrls = new Set();
1733
1581
  for (const file of files) {
@@ -1737,28 +1585,30 @@ export function createBrowserSpeechAuthority({
1737
1585
  allPaths.add(file.path);
1738
1586
  allUrls.add(file.url);
1739
1587
  }
1740
- const authority = Object.freeze({
1588
+ const authority = completeValue({
1741
1589
  protocol: MODEL_AUTHORITY_PROTOCOL,
1742
1590
  providerId: normalizedProviderId,
1743
1591
  modelId,
1744
- admitted: true,
1745
1592
  role,
1746
1593
  repository,
1747
1594
  revision: modelRevision,
1595
+ ...(dtype === null ? {} : { dtype }),
1748
1596
  defaultVoice: normalizedModel.defaultVoice,
1749
- runtime: Object.freeze({
1597
+ runtime: completeValue({
1750
1598
  adapter: normalizedRuntime.adapter,
1751
1599
  version: normalizedRuntime.version,
1752
1600
  revision: normalizedRuntime.revision,
1753
1601
  entry: normalizedRuntime.entry,
1754
1602
  ...(wasmPaths === null ? {} : { wasmPaths }),
1755
- files: Object.freeze(runtimeFiles.map(publicFile)),
1603
+ files: completeValue(runtimeFiles.map((file) =>
1604
+ publicFile(file))),
1756
1605
  }),
1757
- files: Object.freeze(modelFiles.map(publicFile)),
1758
- security: normalizedSecurity,
1606
+ files: completeValue(modelFiles.map((file) =>
1607
+ publicFile(file))),
1608
+ ...(normalizedSecurity ? { security: normalizedSecurity } : {}),
1759
1609
  });
1760
1610
  AUTHORITIES.add(authority);
1761
- AUTHORITY_METADATA.set(authority, Object.freeze({
1611
+ AUTHORITY_METADATA.set(authority, completeValue({
1762
1612
  model: normalizedModel,
1763
1613
  runtime: normalizedRuntime,
1764
1614
  files,
@@ -1767,74 +1617,103 @@ export function createBrowserSpeechAuthority({
1767
1617
  }
1768
1618
 
1769
1619
  function authorityProjection(authority) {
1770
- return Object.freeze({
1620
+ return completeValue({
1771
1621
  protocol: authority.protocol,
1772
1622
  providerId: authority.providerId,
1773
1623
  modelId: authority.modelId,
1774
1624
  role: authority.role,
1775
1625
  repository: authority.repository,
1776
1626
  revision: authority.revision,
1627
+ ...(authority.dtype === undefined ? {} : { dtype: authority.dtype }),
1777
1628
  runtime: authority.runtime,
1778
1629
  files: authority.files,
1779
1630
  });
1780
1631
  }
1781
1632
 
1782
- function storedArtifactProjection(authority) {
1783
- if (ARTIFACT_GRAPHS.has(authority)) {
1784
- return Object.freeze({
1785
- protocol: authority.protocol,
1786
- kind: authority.kind,
1787
- identitySha256: authority.identitySha256,
1788
- providerId: authority.providerId,
1789
- role: authority.role,
1790
- model: authority.model,
1791
- runtime: authority.runtime,
1792
- files: authority.files,
1793
- edges: authority.edges,
1794
- transforms: authority.transforms,
1795
- });
1796
- }
1797
- return authorityProjection(authority);
1798
- }
1799
-
1800
1633
  function artifactMetadata(authority) {
1801
1634
  return ARTIFACT_GRAPH_METADATA.get(authority)
1802
1635
  ?? AUTHORITY_METADATA.get(authority)
1803
1636
  ?? null;
1804
1637
  }
1805
1638
 
1639
+ function functionalArtifactFile(file) {
1640
+ const result = { ...file };
1641
+ delete result.license;
1642
+ return completeValue(result);
1643
+ }
1644
+
1806
1645
  function isSpeechArtifactAuthority(authority) {
1807
1646
  return AUTHORITIES.has(authority) || ARTIFACT_GRAPHS.has(authority);
1808
1647
  }
1809
1648
 
1810
1649
  function storageKey(authority) {
1811
- if (ARTIFACT_GRAPHS.has(authority)) return authority.identitySha256;
1812
- const digest = createStreamingSha256();
1813
- digest.update(new TextEncoder().encode(JSON.stringify(authorityProjection(authority))));
1814
- return digest.digestHex();
1650
+ if (ARTIFACT_GRAPHS.has(authority)) {
1651
+ const metadata = ARTIFACT_GRAPH_METADATA.get(authority);
1652
+ return `speech-${encodeURIComponent(JSON.stringify({
1653
+ kind: authority.kind,
1654
+ providerId: authority.providerId,
1655
+ role: authority.role,
1656
+ modelId: metadata.model.id,
1657
+ modelRepository: metadata.model.repository,
1658
+ modelRevision: metadata.model.revision,
1659
+ runtimeAdapter: metadata.runtime.adapter,
1660
+ runtimeVersion: metadata.runtime.version,
1661
+ runtimeRevision: metadata.runtime.revision,
1662
+ }))}`;
1663
+ }
1664
+ const metadata = AUTHORITY_METADATA.get(authority);
1665
+ return `speech-${encodeURIComponent(JSON.stringify({
1666
+ providerId: authority.providerId,
1667
+ role: authority.role,
1668
+ modelId: metadata.model.id,
1669
+ modelRepository: metadata.model.repository,
1670
+ modelRevision: metadata.model.revision,
1671
+ runtimeAdapter: metadata.runtime.adapter,
1672
+ runtimeVersion: metadata.runtime.version,
1673
+ runtimeRevision: metadata.runtime.revision,
1674
+ }))}`;
1675
+ }
1676
+
1677
+ function cacheSelection(authority) {
1678
+ const metadata = artifactMetadata(authority);
1679
+ if (ARTIFACT_GRAPHS.has(authority)) {
1680
+ const model = completeValue({
1681
+ ...metadata.model,
1682
+ files: completeValue(metadata.model.files.map(functionalArtifactFile)),
1683
+ });
1684
+ const runtime = completeValue({
1685
+ ...metadata.runtime,
1686
+ files: completeValue(metadata.runtime.files.map(functionalArtifactFile)),
1687
+ });
1688
+ return completeValue({
1689
+ protocol: authority.protocol,
1690
+ kind: authority.kind,
1691
+ providerId: authority.providerId,
1692
+ role: authority.role,
1693
+ model,
1694
+ runtime,
1695
+ files: completeValue(metadata.files.map(functionalArtifactFile)),
1696
+ });
1697
+ }
1698
+ return completeValue({
1699
+ authority: authorityProjection(authority),
1700
+ model: metadata.model,
1701
+ runtime: metadata.runtime,
1702
+ files: metadata.files,
1703
+ });
1815
1704
  }
1816
1705
 
1817
1706
  function storageNames(authority, files) {
1818
1707
  const prefix = `arcane-speech-${storageKey(authority)}`;
1819
- return Object.freeze({
1708
+ return completeValue({
1820
1709
  key: prefix,
1821
- manifest: `${prefix}.complete.json`,
1822
- files: Object.freeze(files.map((_, index) =>
1710
+ selection: `${prefix}.selection.json`,
1711
+ legacyManifest: `${prefix}.complete.json`,
1712
+ files: completeValue(files.map((_, index) =>
1823
1713
  `${prefix}.${String(index).padStart(4, "0")}.artifact`)),
1824
1714
  });
1825
1715
  }
1826
1716
 
1827
- function manifestMatches(manifest, authority, files) {
1828
- const expectedSchema = ARTIFACT_GRAPHS.has(authority)
1829
- ? ARTIFACT_GRAPH_MANIFEST_SCHEMA
1830
- : MANIFEST_SCHEMA;
1831
- return manifest?.schema === expectedSchema
1832
- && manifest.complete === true
1833
- && JSON.stringify(manifest.authority) === JSON.stringify(storedArtifactProjection(authority))
1834
- && Array.isArray(manifest.files)
1835
- && manifest.files.length === files.length;
1836
- }
1837
-
1838
1717
  async function* byteChunks(body, signal) {
1839
1718
  if (body instanceof Uint8Array || body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
1840
1719
  throwIfAborted(signal);
@@ -1866,10 +1745,6 @@ async function* byteChunks(body, signal) {
1866
1745
  );
1867
1746
  }
1868
1747
 
1869
- function providerProgress(phase, completed, total, heartbeat = false) {
1870
- return Object.freeze({ phase, completed, total, unit: "bytes", heartbeat });
1871
- }
1872
-
1873
1748
  // Runtime entry bytes use one deliberately closed capability grammar. The only
1874
1749
  // module reference is import.meta and the only artifact transport is fetch(),
1875
1750
  // which the Worker replaces with its admitted object-URL map before import.
@@ -1928,11 +1803,11 @@ function assertSelfContainedModuleSource(source, label) {
1928
1803
 
1929
1804
  function recordLiteral(start, end, value) {
1930
1805
  assertLiteral(value);
1931
- literalFragments.push(Object.freeze({
1806
+ literalFragments.push(completeValue({
1932
1807
  start,
1933
1808
  end,
1934
1809
  value,
1935
- templateIds: Object.freeze([...templateStack]),
1810
+ templateIds: completeValue([...templateStack]),
1936
1811
  }));
1937
1812
  }
1938
1813
 
@@ -2009,7 +1884,7 @@ function assertSelfContainedModuleSource(source, label) {
2009
1884
  if (source[index] === "\n") index += 1;
2010
1885
  return "";
2011
1886
  }
2012
- return Object.freeze({
1887
+ return completeValue({
2013
1888
  "0": "\0",
2014
1889
  b: "\b",
2015
1890
  f: "\f",
@@ -2186,7 +2061,10 @@ function assertSelfContainedModuleSource(source, label) {
2186
2061
  assertStaticLiteralChains();
2187
2062
  }
2188
2063
 
2189
- async function assertSelfContainedRuntime(admitted, metadata, security) {
2064
+ // Dormant hardening retained for future review only. Ordinary speech artifact
2065
+ // preparation never calls this closed-module inspection, and it must not be
2066
+ // enabled for secure mode without an explicit review with the user.
2067
+ async function assertSelfContainedRuntime(admitted, metadata) {
2190
2068
  const javascriptFiles = admitted.files.filter(({ descriptor }) =>
2191
2069
  descriptor.kind === "runtime" && descriptor.mediaType === "text/javascript");
2192
2070
  if (
@@ -2199,9 +2077,7 @@ async function assertSelfContainedRuntime(admitted, metadata, security) {
2199
2077
  );
2200
2078
  }
2201
2079
  const [{ descriptor, file }] = javascriptFiles;
2202
- if (security?.secure === true) {
2203
- assertSelfContainedModuleSource(await file.text(), descriptor.path);
2204
- }
2080
+ void file;
2205
2081
  }
2206
2082
 
2207
2083
  function tokenizeArtifactGraphModule(source, modulePath) {
@@ -2260,7 +2136,7 @@ function tokenizeArtifactGraphModule(source, modulePath) {
2260
2136
  if (source[index] === "\n") index += 1;
2261
2137
  return "";
2262
2138
  }
2263
- return Object.freeze({
2139
+ return completeValue({
2264
2140
  "0": "\0",
2265
2141
  b: "\b",
2266
2142
  f: "\f",
@@ -2281,7 +2157,7 @@ function tokenizeArtifactGraphModule(source, modulePath) {
2281
2157
  if (character === "\\") {
2282
2158
  value += readEscape();
2283
2159
  } else if (character === quote) {
2284
- tokens.push(Object.freeze({ type: "string", value, start, end: index }));
2160
+ tokens.push(completeValue({ type: "string", value, start, end: index }));
2285
2161
  return;
2286
2162
  } else if (character === "\n" || character === "\r") {
2287
2163
  fail("artifact-graph-javascript-quoted-string-line-break-rejected", "quoted string contains a line break.");
@@ -2369,19 +2245,19 @@ function tokenizeArtifactGraphModule(source, modulePath) {
2369
2245
  }
2370
2246
  if (character === "`") {
2371
2247
  readTemplate();
2372
- lastToken = Object.freeze({ type: "template", value: "template" });
2248
+ lastToken = completeValue({ type: "template", value: "template" });
2373
2249
  continue;
2374
2250
  }
2375
2251
  if (character === "/" && canStartRegex(lastToken)) {
2376
2252
  skipRegex();
2377
- lastToken = Object.freeze({ type: "regexp", value: "regexp" });
2253
+ lastToken = completeValue({ type: "regexp", value: "regexp" });
2378
2254
  continue;
2379
2255
  }
2380
2256
  if (identifierStart(character)) {
2381
2257
  const start = index;
2382
2258
  index += 1;
2383
2259
  while (identifierPart(source[index])) index += 1;
2384
- const token = Object.freeze({
2260
+ const token = completeValue({
2385
2261
  type: "identifier",
2386
2262
  value: source.slice(start, index),
2387
2263
  start,
@@ -2409,7 +2285,7 @@ function tokenizeArtifactGraphModule(source, modulePath) {
2409
2285
  ].includes(twoCharacters)
2410
2286
  ? twoCharacters
2411
2287
  : character;
2412
- const token = Object.freeze({
2288
+ const token = completeValue({
2413
2289
  type: "punctuation",
2414
2290
  value,
2415
2291
  start: index,
@@ -2425,7 +2301,7 @@ function tokenizeArtifactGraphModule(source, modulePath) {
2425
2301
  }
2426
2302
 
2427
2303
  scanCode();
2428
- return Object.freeze(tokens);
2304
+ return completeValue(tokens);
2429
2305
  }
2430
2306
 
2431
2307
  function artifactGraphDeclarationsByModule(values) {
@@ -2511,7 +2387,7 @@ function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
2511
2387
  }
2512
2388
  start = previous(index, 2).start;
2513
2389
  }
2514
- target.push(Object.freeze({ start, end: opening.end }));
2390
+ target.push(completeValue({ start, end: opening.end }));
2515
2391
  }
2516
2392
 
2517
2393
  function typedArrayConstructor(index) {
@@ -2520,7 +2396,7 @@ function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
2520
2396
  const property = previous(index, 2);
2521
2397
  if (property?.type !== "identifier") return null;
2522
2398
  if (previous(index, 3)?.value === "new") {
2523
- return Object.freeze({
2399
+ return completeValue({
2524
2400
  start: property.start,
2525
2401
  end: tokens[index].end,
2526
2402
  receiver: source.slice(property.start, property.end),
@@ -2534,7 +2410,7 @@ function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
2534
2410
  && previous(index, 7)?.type === "identifier"
2535
2411
  && previous(index, 8)?.value === "new"
2536
2412
  ) {
2537
- return Object.freeze({
2413
+ return completeValue({
2538
2414
  start: previous(index, 7).start,
2539
2415
  end: tokens[index].end,
2540
2416
  receiver: source.slice(previous(index, 7).start, property.end),
@@ -2620,7 +2496,7 @@ function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
2620
2496
  warnings.add("Function");
2621
2497
  continue;
2622
2498
  }
2623
- returnThisTransforms.push(Object.freeze({
2499
+ returnThisTransforms.push(completeValue({
2624
2500
  start: token.start,
2625
2501
  end: next(index, 5).end,
2626
2502
  }));
@@ -2643,7 +2519,7 @@ function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
2643
2519
  }
2644
2520
  start = previous(index, 2).start;
2645
2521
  }
2646
- cacheOpens.push(Object.freeze({ start, end: next(index, 3).end }));
2522
+ cacheOpens.push(completeValue({ start, end: next(index, 3).end }));
2647
2523
  continue;
2648
2524
  }
2649
2525
  if (token.value === "caches") {
@@ -2674,7 +2550,7 @@ function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
2674
2550
  continue;
2675
2551
  }
2676
2552
  if (next(index)?.value === "(") {
2677
- dynamicImports.push(Object.freeze({ start: token.start, end: next(index).end }));
2553
+ dynamicImports.push(completeValue({ start: token.start, end: next(index).end }));
2678
2554
  continue;
2679
2555
  }
2680
2556
  let specifier = next(index)?.type === "string" ? next(index) : null;
@@ -2693,7 +2569,7 @@ function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
2693
2569
  `${modulePath} contains a static import without one literal specifier.`,
2694
2570
  );
2695
2571
  }
2696
- staticImports.push(Object.freeze({
2572
+ staticImports.push(completeValue({
2697
2573
  start: specifier.start,
2698
2574
  end: specifier.end,
2699
2575
  specifier: specifier.value,
@@ -2712,7 +2588,7 @@ function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
2712
2588
  if (["export", "import"].includes(tokens[cursor].value)) break;
2713
2589
  }
2714
2590
  if (specifier) {
2715
- staticImports.push(Object.freeze({
2591
+ staticImports.push(completeValue({
2716
2592
  start: specifier.start,
2717
2593
  end: specifier.end,
2718
2594
  specifier: specifier.value,
@@ -2753,7 +2629,7 @@ function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
2753
2629
  start = previous(index, 2).start;
2754
2630
  if (previous(index, 3)?.value === "new") start = previous(index, 3).start;
2755
2631
  }
2756
- moduleWorkers.push(Object.freeze({ start, end: opening.end }));
2632
+ moduleWorkers.push(completeValue({ start, end: opening.end }));
2757
2633
  }
2758
2634
  }
2759
2635
 
@@ -2820,16 +2696,16 @@ function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
2820
2696
  );
2821
2697
  }
2822
2698
  }
2823
- return Object.freeze({
2699
+ return completeValue({
2824
2700
  source,
2825
- staticImports: Object.freeze(staticImports),
2826
- dynamicImports: Object.freeze(dynamicImports),
2827
- fetches: Object.freeze(fetches),
2828
- moduleWorkers: Object.freeze(moduleWorkers),
2829
- cacheOpens: Object.freeze(cacheOpens),
2830
- returnThisTransforms: Object.freeze(returnThisTransforms),
2831
- typedArrayConstructors: Object.freeze(typedArrayConstructors),
2832
- warnings: Object.freeze([...warnings].sort()),
2701
+ staticImports: completeValue(staticImports),
2702
+ dynamicImports: completeValue(dynamicImports),
2703
+ fetches: completeValue(fetches),
2704
+ moduleWorkers: completeValue(moduleWorkers),
2705
+ cacheOpens: completeValue(cacheOpens),
2706
+ returnThisTransforms: completeValue(returnThisTransforms),
2707
+ typedArrayConstructors: completeValue(typedArrayConstructors),
2708
+ warnings: completeValue([...warnings].sort()),
2833
2709
  declarations,
2834
2710
  });
2835
2711
  }
@@ -2859,10 +2735,13 @@ function assertArtifactGraphStaticImportClosure(metadata) {
2859
2735
  order.push(path);
2860
2736
  }
2861
2737
  for (const path of [...dependencies.keys()].sort()) visit(path);
2862
- return Object.freeze(order);
2738
+ return completeValue(order);
2863
2739
  }
2864
2740
 
2865
- async function inspectArtifactGraphRuntime(admitted, metadata, signal, security) {
2741
+ // Dormant hardening retained for future review only. Ordinary materialization
2742
+ // uses the permissive router below; these declaration, capability, and closed
2743
+ // inspection controls must not be enabled without explicit user review.
2744
+ async function inspectArtifactGraphRuntime(admitted, metadata, signal) {
2866
2745
  const admittedByPath = new Map(admitted.files.map((entry) => [entry.descriptor.path, entry]));
2867
2746
  const plans = new Map();
2868
2747
  for (const descriptor of metadata.runtimeFiles) {
@@ -2889,7 +2768,7 @@ async function inspectArtifactGraphRuntime(admitted, metadata, signal, security)
2889
2768
  plans.set(
2890
2769
  descriptor.path,
2891
2770
  inspectArtifactGraphModuleSource(source, descriptor.path, metadata, {
2892
- strict: security?.secure === true,
2771
+ strict: false,
2893
2772
  }),
2894
2773
  );
2895
2774
  }
@@ -2897,7 +2776,7 @@ async function inspectArtifactGraphRuntime(admitted, metadata, signal, security)
2897
2776
  const warnings = [...new Set([...plans.values()].flatMap((plan) => plan.warnings))]
2898
2777
  .sort();
2899
2778
  throwIfAborted(signal);
2900
- return Object.freeze({ plans, order, warnings: Object.freeze(warnings) });
2779
+ return completeValue({ plans, order, warnings: completeValue(warnings) });
2901
2780
  }
2902
2781
 
2903
2782
  function assertArtifactGraphRuntimeInspection(inspection) {
@@ -2998,37 +2877,17 @@ function applyArtifactGraphModuleTransforms(plan, materializedByPath, guardCapab
2998
2877
  }
2999
2878
 
3000
2879
  function artifactGraphGuardCapability() {
3001
- const crypto = globalThis.crypto;
3002
- if (typeof crypto?.getRandomValues !== "function") {
3003
- throw artifactGraphError(
3004
- "artifact-graph-guard-capability-unavailable",
3005
- "Authenticated artifact graph materialization requires cryptographic random values.",
3006
- );
3007
- }
3008
- const bytes = new Uint8Array(32);
3009
- crypto.getRandomValues(bytes);
3010
- return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
3011
- }
3012
-
3013
- async function blobDigest(blob) {
3014
- const digest = createStreamingSha256();
3015
- let bytes = 0;
3016
- for await (const chunk of byteChunks(blob.stream())) {
3017
- digest.update(chunk);
3018
- bytes += chunk.byteLength;
3019
- }
3020
- return Object.freeze({ bytes, sha256: digest.digestHex() });
2880
+ return "ordinary";
3021
2881
  }
3022
2882
 
3023
- async function createArtifactGraphObjectUrls(admitted, metadata, inspection, security) {
2883
+ async function createArtifactGraphObjectUrls(admitted, metadata, inspection) {
3024
2884
  if (
3025
2885
  typeof PLATFORM_CREATE_OBJECT_URL !== "function"
3026
2886
  || typeof PLATFORM_REVOKE_OBJECT_URL !== "function"
3027
- || typeof PLATFORM_FETCH !== "function"
3028
2887
  ) {
3029
2888
  throw artifactGraphError(
3030
2889
  "artifact-graph-object-url-platform-unavailable",
3031
- "Authenticated artifact graph materialization requires native Blob URL creation, revocation, and fetch.",
2890
+ "Artifact graph materialization requires native Blob URL creation and revocation.",
3032
2891
  );
3033
2892
  }
3034
2893
  const admittedByPath = new Map(admitted.files.map((entry) => [entry.descriptor.path, entry]));
@@ -3041,16 +2900,6 @@ async function createArtifactGraphObjectUrls(admitted, metadata, inspection, sec
3041
2900
  const blob = body instanceof Blob && body.type === descriptor.mediaType
3042
2901
  ? body
3043
2902
  : new Blob([body], { type: descriptor.mediaType });
3044
- const source = await blobDigest(blob);
3045
- const transformed = ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(descriptor.kind);
3046
- const expected = Object.freeze({
3047
- bytes: transformed || security.checks.byteLength !== true
3048
- ? source.bytes
3049
- : descriptor.bytes,
3050
- sha256: transformed || security.checks.sha256 !== true
3051
- ? source.sha256
3052
- : descriptor.sha256,
3053
- });
3054
2903
  const moduleUrl = PLATFORM_CREATE_OBJECT_URL(blob);
3055
2904
  if (typeof moduleUrl !== "string" || !moduleUrl.startsWith("blob:")) {
3056
2905
  throw artifactGraphError(
@@ -3066,66 +2915,16 @@ async function createArtifactGraphObjectUrls(admitted, metadata, inspection, sec
3066
2915
  }
3067
2916
  createdIdentities.add(moduleUrl);
3068
2917
  created.push(moduleUrl);
3069
- let response;
3070
- try {
3071
- response = await PLATFORM_FETCH(moduleUrl, {
3072
- method: "GET",
3073
- credentials: "omit",
3074
- redirect: "error",
3075
- });
3076
- } catch (error) {
3077
- throw artifactGraphError(
3078
- "artifact-graph-object-url-readback-unavailable",
3079
- `Materialized artifact graph file ${descriptor.path} could not be read back from its Blob URL.`,
3080
- error,
3081
- );
3082
- }
3083
- if (!response.ok) {
3084
- throw artifactGraphError(
3085
- "artifact-graph-object-url-readback-http-status-rejected",
3086
- `Materialized artifact graph file ${descriptor.path} returned a non-success Blob URL response.`,
3087
- );
3088
- }
3089
- if (response.redirected || response.url !== moduleUrl) {
3090
- throw artifactGraphError(
3091
- "artifact-graph-object-url-readback-identity-mismatch",
3092
- `Materialized artifact graph file ${descriptor.path} did not retain its exact Blob URL identity.`,
3093
- );
3094
- }
3095
- const observedMediaType = response.headers.get("content-type")?.split(";", 1)[0].trim() ?? "";
3096
- if (observedMediaType !== descriptor.mediaType) {
3097
- throw artifactGraphError(
3098
- "artifact-graph-object-url-media-type-mismatch",
3099
- `Materialized artifact graph file ${descriptor.path} did not retain its declared media type.`,
3100
- );
3101
- }
3102
- const observedBlob = await response.blob();
3103
- const observed = await blobDigest(observedBlob);
3104
- if (observed.bytes !== expected.bytes) {
3105
- throw artifactGraphError(
3106
- "artifact-graph-object-url-byte-length-mismatch",
3107
- `Materialized artifact graph file ${descriptor.path} did not retain its exact byte length.`,
3108
- );
3109
- }
3110
- if (observed.sha256 !== expected.sha256) {
3111
- throw artifactGraphError(
3112
- "artifact-graph-object-url-sha256-mismatch",
3113
- `Materialized artifact graph file ${descriptor.path} did not retain its exact bytes.`,
3114
- );
3115
- }
3116
- materializedByPath.set(descriptor.path, Object.freeze({
2918
+ materializedByPath.set(descriptor.path, completeValue({
3117
2919
  kind: descriptor.kind,
3118
2920
  path: descriptor.path,
3119
2921
  sourceUrl: descriptor.sourceUrl,
3120
2922
  revision: descriptor.revision,
3121
- license: descriptor.license,
3122
2923
  moduleUrl,
3123
2924
  mediaType: descriptor.mediaType,
3124
2925
  ...(descriptor.sourceMediaType === descriptor.mediaType
3125
2926
  ? {}
3126
2927
  : { sourceMediaType: descriptor.sourceMediaType }),
3127
- bytes: descriptor.bytes,
3128
- sha256: descriptor.sha256,
3129
2928
  runtimeRequestUrls: descriptor.runtimeRequestUrls,
3130
2929
  ...(descriptor.redirectFinalOrigins.length < 1
3131
2930
  ? {}
@@ -3168,9 +2967,9 @@ async function createArtifactGraphObjectUrls(admitted, metadata, inspection, sec
3168
2967
  }
3169
2968
  return materialized;
3170
2969
  });
3171
- return Object.freeze({
2970
+ return completeValue({
3172
2971
  guardCapability,
3173
- files: Object.freeze(files),
2972
+ files: completeValue(files),
3174
2973
  release() {
3175
2974
  for (const url of created.splice(0).reverse()) {
3176
2975
  try {
@@ -3193,73 +2992,497 @@ async function createArtifactGraphObjectUrls(admitted, metadata, inspection, sec
3193
2992
  }
3194
2993
  }
3195
2994
 
3196
- function artifactGraphAdmissionStatus(cache, offline, security) {
3197
- const verification = security.checks.byteLength && security.checks.sha256
3198
- ? "verified"
3199
- : security.checks.byteLength || security.checks.sha256
3200
- ? "partially-checked"
3201
- : "unchecked";
3202
- const source = cache === "installed"
3203
- ? "network-dbopfs"
3204
- : offline
3205
- ? "offline-dbopfs-cache"
3206
- : "dbopfs-cache";
3207
- return `artifact-graph-${source}-${verification}`;
2995
+ function ordinaryArtifactUrl(value, base) {
2996
+ try {
2997
+ return new URL(value, base).href;
2998
+ } catch {
2999
+ return null;
3000
+ }
3208
3001
  }
3209
3002
 
3210
- function createObjectUrls(files, factory) {
3211
- const create = factory?.create ?? ((blob) => URL.createObjectURL(blob));
3212
- const revoke = factory?.revoke ?? ((url) => URL.revokeObjectURL(url));
3213
- if (typeof create !== "function" || typeof revoke !== "function") {
3214
- throw new TypeError("Browser speech objectUrlFactory requires create() and revoke().");
3003
+ function isBareModuleSpecifier(value) {
3004
+ return typeof value === "string"
3005
+ && !value.startsWith("./")
3006
+ && !value.startsWith("../")
3007
+ && !value.startsWith("/")
3008
+ && !/^[A-Za-z][A-Za-z\d+.-]*:/u.test(value);
3009
+ }
3010
+
3011
+ function ordinaryArtifactRoutes(metadata) {
3012
+ const byUrl = new Map();
3013
+ const ambiguous = new Set();
3014
+ function add(value, descriptor) {
3015
+ const url = ordinaryArtifactUrl(value, descriptor.sourceUrl);
3016
+ if (!url || ambiguous.has(url)) return;
3017
+ const existing = byUrl.get(url);
3018
+ if (existing && existing.path !== descriptor.path) {
3019
+ byUrl.delete(url);
3020
+ ambiguous.add(url);
3021
+ return;
3022
+ }
3023
+ byUrl.set(url, descriptor);
3215
3024
  }
3216
- const created = [];
3025
+ for (const descriptor of metadata.files) {
3026
+ add(descriptor.sourceUrl, descriptor);
3027
+ for (const route of descriptor.runtimeRequestUrls ?? []) add(route, descriptor);
3028
+ }
3029
+ return completeValue({ byUrl, ambiguous });
3030
+ }
3031
+
3032
+ function resolveOrdinaryArtifactRoute(routes, value, sourceDescriptor) {
3033
+ const url = ordinaryArtifactUrl(value, sourceDescriptor?.sourceUrl);
3034
+ return url && !routes.ambiguous.has(url)
3035
+ ? completeValue({ descriptor: routes.byUrl.get(url) ?? null, url })
3036
+ : null;
3037
+ }
3038
+
3039
+ function scanOrdinaryModuleRouting(source, modulePath) {
3040
+ let tokens;
3217
3041
  try {
3218
- const materialized = files.map(({ descriptor, file }) => {
3219
- const blob = file.type === descriptor.mediaType
3220
- ? file
3221
- : new Blob([file], { type: descriptor.mediaType });
3222
- const url = create(blob);
3223
- created.push(url);
3224
- return Object.freeze({
3225
- kind: descriptor.kind,
3226
- path: descriptor.path,
3227
- sourceUrl: descriptor.url,
3228
- moduleUrl: url,
3229
- mediaType: descriptor.mediaType,
3230
- bytes: file.size,
3231
- });
3232
- });
3233
- return Object.freeze({
3234
- files: Object.freeze(materialized),
3235
- release() {
3236
- for (const url of created.splice(0).reverse()) {
3237
- try {
3238
- revoke(url);
3239
- } catch {
3240
- // Object URL revocation follows worker termination and is best effort.
3241
- }
3242
- }
3243
- },
3042
+ tokens = tokenizeArtifactGraphModule(source, modulePath);
3043
+ } catch {
3044
+ // Routing discovery is best effort. A scanner limitation must never become
3045
+ // an ordinary runtime admission gate; unchanged source uses native URLs.
3046
+ return completeValue({
3047
+ source,
3048
+ staticImports: completeValue([]),
3049
+ dynamicImports: completeValue([]),
3050
+ fetches: completeValue([]),
3051
+ moduleWorkers: completeValue([]),
3052
+ cacheOpens: completeValue([]),
3244
3053
  });
3245
- } catch (error) {
3246
- for (const url of created.splice(0).reverse()) {
3247
- try {
3248
- revoke(url);
3249
- } catch {
3250
- // Preserve the materialization error.
3054
+ }
3055
+ const staticImports = [];
3056
+ const dynamicImports = [];
3057
+ const fetches = [];
3058
+ const moduleWorkers = [];
3059
+ const cacheOpens = [];
3060
+ const next = (index, offset = 1) => tokens[index + offset] ?? null;
3061
+ const previous = (index, offset = 1) => tokens[index - offset] ?? null;
3062
+ const shadowedGlobals = new Set();
3063
+ const routableGlobals = new Set(["fetch", "caches", "Worker"]);
3064
+
3065
+ function closingParenthesis(opening) {
3066
+ let depth = 0;
3067
+ for (let cursor = opening; cursor < tokens.length; cursor += 1) {
3068
+ if (tokens[cursor].value === "(") depth += 1;
3069
+ if (tokens[cursor].value !== ")") continue;
3070
+ depth -= 1;
3071
+ if (depth === 0) return cursor;
3072
+ }
3073
+ return -1;
3074
+ }
3075
+
3076
+ function markBindings(start, end) {
3077
+ for (let cursor = start; cursor < end; cursor += 1) {
3078
+ if (routableGlobals.has(tokens[cursor].value)) {
3079
+ shadowedGlobals.add(tokens[cursor].value);
3251
3080
  }
3252
3081
  }
3253
- throw error;
3254
3082
  }
3255
- }
3256
3083
 
3257
- /**
3258
- * Stores an authority's complete runtime/model closure in an existing DBOPFS
3259
- * table. The completion manifest is always the final mutation.
3260
- */
3261
- export function createDbopfsSpeechArtifactStore({
3262
- dbopfs,
3084
+ for (let index = 0; index < tokens.length; index += 1) {
3085
+ const token = tokens[index];
3086
+ if (routableGlobals.has(token.value) && next(index)?.value === "=>") {
3087
+ shadowedGlobals.add(token.value);
3088
+ continue;
3089
+ }
3090
+ if (token.value === "import") {
3091
+ for (let cursor = index + 1; cursor < tokens.length; cursor += 1) {
3092
+ const candidate = tokens[cursor];
3093
+ if (candidate.value === "from" || candidate.type === "string"
3094
+ || candidate.value === ";") break;
3095
+ if (routableGlobals.has(candidate.value)) {
3096
+ shadowedGlobals.add(candidate.value);
3097
+ }
3098
+ }
3099
+ continue;
3100
+ }
3101
+ if (["function", "catch"].includes(token.value)) {
3102
+ let opening = index + 1;
3103
+ while (opening < tokens.length && tokens[opening].value !== "(") {
3104
+ if (routableGlobals.has(tokens[opening].value)) {
3105
+ shadowedGlobals.add(tokens[opening].value);
3106
+ }
3107
+ opening += 1;
3108
+ }
3109
+ const closing = opening < tokens.length ? closingParenthesis(opening) : -1;
3110
+ if (closing > opening) markBindings(opening + 1, closing);
3111
+ continue;
3112
+ }
3113
+ if (token.value === "class" && routableGlobals.has(next(index)?.value)) {
3114
+ shadowedGlobals.add(next(index).value);
3115
+ continue;
3116
+ }
3117
+ if (!["const", "let", "var"].includes(token.value)) continue;
3118
+ let binding = true;
3119
+ let round = 0;
3120
+ let square = 0;
3121
+ let curly = 0;
3122
+ for (let cursor = index + 1; cursor < tokens.length; cursor += 1) {
3123
+ const candidate = tokens[cursor];
3124
+ const topLevel = round === 0 && square === 0 && curly === 0;
3125
+ if (topLevel && candidate.value === ";") break;
3126
+ if (topLevel && candidate.value === ",") {
3127
+ binding = true;
3128
+ continue;
3129
+ }
3130
+ if (topLevel && ["=", "in", "of"].includes(candidate.value)) {
3131
+ binding = false;
3132
+ continue;
3133
+ }
3134
+ if (binding && routableGlobals.has(candidate.value)) {
3135
+ shadowedGlobals.add(candidate.value);
3136
+ }
3137
+ if (candidate.value === "(") round += 1;
3138
+ else if (candidate.value === ")") round = Math.max(0, round - 1);
3139
+ else if (candidate.value === "[") square += 1;
3140
+ else if (candidate.value === "]") square = Math.max(0, square - 1);
3141
+ else if (candidate.value === "{") curly += 1;
3142
+ else if (candidate.value === "}") curly = Math.max(0, curly - 1);
3143
+ }
3144
+ }
3145
+
3146
+ const controlParentheses = new Set(["catch", "for", "if", "switch", "while", "with"]);
3147
+ for (let index = 0; index < tokens.length; index += 1) {
3148
+ if (tokens[index].value !== "(") continue;
3149
+ const closing = closingParenthesis(index);
3150
+ const nextToken = closing < 0 ? null : next(closing);
3151
+ const owner = previous(index)?.value;
3152
+ const callable = nextToken?.value === "=>"
3153
+ || (nextToken?.value === "{" && !controlParentheses.has(owner));
3154
+ if (callable) markBindings(index + 1, closing);
3155
+ }
3156
+
3157
+ function directCall(target, index) {
3158
+ const opening = next(index);
3159
+ if (opening?.value !== "(") return false;
3160
+ let start = tokens[index].start;
3161
+ if (previous(index)?.value === ".") {
3162
+ if (!["globalThis", "self"].includes(previous(index, 2)?.value)) return false;
3163
+ start = previous(index, 2).start;
3164
+ }
3165
+ target.push(completeValue({ start, end: opening.end }));
3166
+ return true;
3167
+ }
3168
+
3169
+ function isExplicitGlobal(index) {
3170
+ return previous(index)?.value === "."
3171
+ && ["globalThis", "self"].includes(previous(index, 2)?.value);
3172
+ }
3173
+
3174
+ for (let index = 0; index < tokens.length; index += 1) {
3175
+ const token = tokens[index];
3176
+ if (token.type !== "identifier") continue;
3177
+ if (token.value === "import") {
3178
+ if (next(index)?.value === "." && next(index, 2)?.value === "meta") {
3179
+ index += 2;
3180
+ continue;
3181
+ }
3182
+ if (next(index)?.value === "(") {
3183
+ dynamicImports.push(completeValue({ start: token.start, end: next(index).end }));
3184
+ continue;
3185
+ }
3186
+ let specifier = next(index)?.type === "string" ? next(index) : null;
3187
+ if (!specifier) {
3188
+ for (let cursor = index + 1; cursor < tokens.length; cursor += 1) {
3189
+ if (tokens[cursor].value === ";") break;
3190
+ if (tokens[cursor].value === "from" && next(cursor)?.type === "string") {
3191
+ specifier = next(cursor);
3192
+ break;
3193
+ }
3194
+ }
3195
+ }
3196
+ if (specifier) staticImports.push(completeValue({
3197
+ start: specifier.start,
3198
+ end: specifier.end,
3199
+ specifier: specifier.value,
3200
+ }));
3201
+ continue;
3202
+ }
3203
+ if (token.value === "export" && ["*", "{"].includes(next(index)?.value)) {
3204
+ for (let cursor = index + 1; cursor < tokens.length; cursor += 1) {
3205
+ if (tokens[cursor].value === ";") break;
3206
+ if (tokens[cursor].value === "from" && next(cursor)?.type === "string") {
3207
+ const specifier = next(cursor);
3208
+ staticImports.push(completeValue({
3209
+ start: specifier.start,
3210
+ end: specifier.end,
3211
+ specifier: specifier.value,
3212
+ }));
3213
+ break;
3214
+ }
3215
+ }
3216
+ continue;
3217
+ }
3218
+ if (
3219
+ token.value === "fetch"
3220
+ && (isExplicitGlobal(index) || !shadowedGlobals.has("fetch"))
3221
+ ) {
3222
+ directCall(fetches, index);
3223
+ continue;
3224
+ }
3225
+ if (
3226
+ token.value === "caches"
3227
+ && (isExplicitGlobal(index) || !shadowedGlobals.has("caches"))
3228
+ && next(index)?.value === "."
3229
+ && next(index, 2)?.value === "open"
3230
+ && next(index, 3)?.value === "("
3231
+ ) {
3232
+ let start = token.start;
3233
+ if (previous(index)?.value === "."
3234
+ && ["globalThis", "self"].includes(previous(index, 2)?.value)) {
3235
+ start = previous(index, 2).start;
3236
+ }
3237
+ cacheOpens.push(completeValue({ start, end: next(index, 3).end }));
3238
+ continue;
3239
+ }
3240
+ if (
3241
+ token.value === "Worker"
3242
+ && (isExplicitGlobal(index) || !shadowedGlobals.has("Worker"))
3243
+ && next(index)?.value === "("
3244
+ ) {
3245
+ let start = token.start;
3246
+ if (previous(index)?.value === "new") {
3247
+ start = previous(index).start;
3248
+ } else if (previous(index)?.value === ".") {
3249
+ if (!["globalThis", "self"].includes(previous(index, 2)?.value)) continue;
3250
+ start = previous(index, 2).start;
3251
+ if (previous(index, 3)?.value === "new") start = previous(index, 3).start;
3252
+ }
3253
+ moduleWorkers.push(completeValue({ start, end: next(index).end }));
3254
+ }
3255
+ }
3256
+ return completeValue({
3257
+ source,
3258
+ staticImports: completeValue(staticImports),
3259
+ dynamicImports: completeValue(dynamicImports),
3260
+ fetches: completeValue(fetches),
3261
+ moduleWorkers: completeValue(moduleWorkers),
3262
+ cacheOpens: completeValue(cacheOpens),
3263
+ });
3264
+ }
3265
+
3266
+ async function planOrdinaryMaterializedRuntime(admitted, metadata, signal) {
3267
+ const admittedByPath = new Map(admitted.files.map((entry) => [entry.descriptor.path, entry]));
3268
+ const routes = ordinaryArtifactRoutes(metadata);
3269
+ const plans = new Map();
3270
+ for (const descriptor of metadata.runtimeFiles) {
3271
+ if (!ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(descriptor.kind)) continue;
3272
+ throwIfAborted(signal);
3273
+ const file = admittedByPath.get(descriptor.path)?.file;
3274
+ if (!file) continue;
3275
+ const source = await file.text();
3276
+ plans.set(descriptor.path, scanOrdinaryModuleRouting(source, descriptor.path));
3277
+ }
3278
+ const visiting = new Set();
3279
+ const visited = new Set();
3280
+ const order = [];
3281
+ function visit(path) {
3282
+ if (visited.has(path) || visiting.has(path)) return;
3283
+ visiting.add(path);
3284
+ const descriptor = metadata.filesByPath.get(path);
3285
+ const plan = plans.get(path);
3286
+ for (const observed of plan?.staticImports ?? []) {
3287
+ if (isBareModuleSpecifier(observed.specifier)) continue;
3288
+ const target = resolveOrdinaryArtifactRoute(routes, observed.specifier, descriptor)?.descriptor;
3289
+ if (target && plans.has(target.path)) visit(target.path);
3290
+ }
3291
+ visiting.delete(path);
3292
+ visited.add(path);
3293
+ order.push(path);
3294
+ }
3295
+ for (const path of plans.keys()) visit(path);
3296
+ return completeValue({ order: completeValue(order), plans, routes });
3297
+ }
3298
+
3299
+ function applyOrdinaryModuleRouting(plan, sourceDescriptor, materializedByPath, routes) {
3300
+ const replacements = [];
3301
+ for (const observed of plan.staticImports) {
3302
+ if (isBareModuleSpecifier(observed.specifier)) continue;
3303
+ const resolution = resolveOrdinaryArtifactRoute(routes, observed.specifier, sourceDescriptor);
3304
+ const mapped = resolution?.descriptor
3305
+ ? materializedByPath.get(resolution.descriptor.path)?.moduleUrl
3306
+ : null;
3307
+ const target = mapped ?? resolution?.url;
3308
+ if (target) replacements.push({
3309
+ start: observed.start,
3310
+ end: observed.end,
3311
+ value: JSON.stringify(target),
3312
+ });
3313
+ }
3314
+ for (const observed of plan.dynamicImports) replacements.push({
3315
+ start: observed.start,
3316
+ end: observed.end,
3317
+ value: `globalThis.${ARTIFACT_MODULE_ROUTER}.dynamicImport(${JSON.stringify(sourceDescriptor.path)},`,
3318
+ });
3319
+ for (const observed of plan.fetches) replacements.push({
3320
+ start: observed.start,
3321
+ end: observed.end,
3322
+ value: `globalThis.${ARTIFACT_MODULE_ROUTER}.fetch(${JSON.stringify(sourceDescriptor.path)},`,
3323
+ });
3324
+ for (const observed of plan.moduleWorkers) replacements.push({
3325
+ start: observed.start,
3326
+ end: observed.end,
3327
+ value: `globalThis.${ARTIFACT_MODULE_ROUTER}.createWorker(${JSON.stringify(sourceDescriptor.path)},`,
3328
+ });
3329
+ for (const observed of plan.cacheOpens) replacements.push({
3330
+ start: observed.start,
3331
+ end: observed.end,
3332
+ value: `globalThis.${ARTIFACT_MODULE_ROUTER}.openCache(${JSON.stringify(sourceDescriptor.path)},`,
3333
+ });
3334
+ replacements.sort((left, right) => right.start - left.start);
3335
+ let source = plan.source;
3336
+ let previousStart = source.length;
3337
+ for (const replacement of replacements) {
3338
+ if (replacement.end > previousStart) continue;
3339
+ source = `${source.slice(0, replacement.start)}${replacement.value}${source.slice(replacement.end)}`;
3340
+ previousStart = replacement.start;
3341
+ }
3342
+ return source;
3343
+ }
3344
+
3345
+ async function createOrdinaryArtifactObjectUrls(
3346
+ admitted,
3347
+ metadata,
3348
+ routing,
3349
+ objectUrlFactory,
3350
+ ) {
3351
+ const create = objectUrlFactory?.create ?? PLATFORM_CREATE_OBJECT_URL;
3352
+ const revoke = objectUrlFactory?.revoke ?? PLATFORM_REVOKE_OBJECT_URL;
3353
+ if (typeof create !== "function" || typeof revoke !== "function") {
3354
+ throw artifactGraphError(
3355
+ "artifact-graph-object-url-platform-unavailable",
3356
+ "Artifact materialization requires native Blob URL creation and revocation.",
3357
+ );
3358
+ }
3359
+ const admittedByPath = new Map(admitted.files.map((entry) => [entry.descriptor.path, entry]));
3360
+ const materializedByPath = new Map();
3361
+ const created = [];
3362
+ async function materialize(descriptor, body) {
3363
+ const blob = body instanceof Blob && body.type === descriptor.mediaType
3364
+ ? body
3365
+ : new Blob([body], { type: descriptor.mediaType });
3366
+ const moduleUrl = create(blob);
3367
+ if (typeof moduleUrl !== "string") {
3368
+ throw artifactGraphError(
3369
+ "artifact-graph-object-url-platform-unavailable",
3370
+ `Artifact file ${descriptor.path} did not produce an object URL.`,
3371
+ );
3372
+ }
3373
+ created.push(moduleUrl);
3374
+ materializedByPath.set(descriptor.path, completeValue({
3375
+ kind: descriptor.kind,
3376
+ path: descriptor.path,
3377
+ sourceUrl: descriptor.sourceUrl,
3378
+ revision: descriptor.revision,
3379
+ moduleUrl,
3380
+ mediaType: descriptor.mediaType,
3381
+ ...(descriptor.sourceMediaType === descriptor.mediaType
3382
+ ? {}
3383
+ : { sourceMediaType: descriptor.sourceMediaType }),
3384
+ runtimeRequestUrls: descriptor.runtimeRequestUrls,
3385
+ ...(descriptor.redirectFinalOrigins.length < 1
3386
+ ? {}
3387
+ : { redirectFinalOrigins: descriptor.redirectFinalOrigins }),
3388
+ }));
3389
+ }
3390
+ try {
3391
+ for (const descriptor of metadata.files) {
3392
+ if (ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(descriptor.kind)) continue;
3393
+ const file = admittedByPath.get(descriptor.path)?.file;
3394
+ if (file) await materialize(descriptor, file);
3395
+ }
3396
+ for (const path of routing.order) {
3397
+ const descriptor = metadata.filesByPath.get(path);
3398
+ const plan = routing.plans.get(path);
3399
+ if (!descriptor || !plan) continue;
3400
+ await materialize(
3401
+ descriptor,
3402
+ applyOrdinaryModuleRouting(plan, descriptor, materializedByPath, routing.routes),
3403
+ );
3404
+ }
3405
+ for (const descriptor of metadata.files) {
3406
+ if (materializedByPath.has(descriptor.path)) continue;
3407
+ const file = admittedByPath.get(descriptor.path)?.file;
3408
+ if (file) await materialize(descriptor, file);
3409
+ }
3410
+ return completeValue({
3411
+ files: completeValue(metadata.files.map((descriptor) => materializedByPath.get(descriptor.path))),
3412
+ release() {
3413
+ for (const url of created.splice(0).reverse()) {
3414
+ try {
3415
+ revoke(url);
3416
+ } catch {
3417
+ // Object URL revocation follows Worker termination and is best effort.
3418
+ }
3419
+ }
3420
+ },
3421
+ });
3422
+ } catch (error) {
3423
+ for (const url of created.splice(0).reverse()) {
3424
+ try {
3425
+ revoke(url);
3426
+ } catch {
3427
+ // Preserve the functional materialization failure.
3428
+ }
3429
+ }
3430
+ throw error;
3431
+ }
3432
+ }
3433
+
3434
+ function createObjectUrls(files, factory) {
3435
+ const create = factory?.create ?? ((blob) => URL.createObjectURL(blob));
3436
+ const revoke = factory?.revoke ?? ((url) => URL.revokeObjectURL(url));
3437
+ if (typeof create !== "function" || typeof revoke !== "function") {
3438
+ throw new TypeError("Browser speech objectUrlFactory requires create() and revoke().");
3439
+ }
3440
+ const created = [];
3441
+ try {
3442
+ const materialized = files.map(({ descriptor, file }) => {
3443
+ const blob = file.type === descriptor.mediaType
3444
+ ? file
3445
+ : new Blob([file], { type: descriptor.mediaType });
3446
+ const url = create(blob);
3447
+ created.push(url);
3448
+ return completeValue({
3449
+ kind: descriptor.kind,
3450
+ path: descriptor.path,
3451
+ sourceUrl: descriptor.url,
3452
+ moduleUrl: url,
3453
+ mediaType: descriptor.mediaType,
3454
+ });
3455
+ });
3456
+ return completeValue({
3457
+ files: completeValue(materialized),
3458
+ release() {
3459
+ for (const url of created.splice(0).reverse()) {
3460
+ try {
3461
+ revoke(url);
3462
+ } catch {
3463
+ // Object URL revocation follows worker termination and is best effort.
3464
+ }
3465
+ }
3466
+ },
3467
+ });
3468
+ } catch (error) {
3469
+ for (const url of created.splice(0).reverse()) {
3470
+ try {
3471
+ revoke(url);
3472
+ } catch {
3473
+ // Preserve the materialization error.
3474
+ }
3475
+ }
3476
+ throw error;
3477
+ }
3478
+ }
3479
+
3480
+ /**
3481
+ * Stores an authority's runtime/model files in an existing DBOPFS table.
3482
+ * Ordinary cache reuse creates no receipt, manifest, or byte identity.
3483
+ */
3484
+ export function createDbopfsSpeechArtifactStore({
3485
+ dbopfs,
3263
3486
  tableName = "arcane_ai_browser_speech",
3264
3487
  fetchImpl = null,
3265
3488
  objectUrlFactory = null,
@@ -3333,20 +3556,27 @@ export function createDbopfsSpeechArtifactStore({
3333
3556
  }
3334
3557
  }
3335
3558
 
3336
- async function writeFile(name, body, { signal, onChunk } = {}) {
3559
+ async function readJsonFile(name) {
3560
+ const file = await readFile(name);
3561
+ if (!file) return null;
3562
+ try {
3563
+ return JSON.parse(await file.text());
3564
+ } catch {
3565
+ return null;
3566
+ }
3567
+ }
3568
+
3569
+ async function writeFile(name, body, { signal } = {}) {
3337
3570
  const directory = await table();
3338
3571
  const handle = await directory.getFileHandle(name, { create: true });
3339
3572
  const writable = await handle.createWritable();
3340
- let written = 0;
3341
3573
  try {
3342
3574
  for await (const chunk of byteChunks(body, signal)) {
3343
3575
  await writable.write(chunk);
3344
- written += chunk.byteLength;
3345
- onChunk?.(chunk, written);
3346
3576
  }
3347
3577
  throwIfAborted(signal);
3348
3578
  await writable.close();
3349
- return written;
3579
+ return undefined;
3350
3580
  } catch (error) {
3351
3581
  await writable.abort?.(error).catch(() => undefined);
3352
3582
  await directory.removeEntry(name).catch(() => undefined);
@@ -3360,40 +3590,24 @@ export function createDbopfsSpeechArtifactStore({
3360
3590
  }
3361
3591
  const metadata = artifactMetadata(authority);
3362
3592
  const names = storageNames(authority, metadata.files);
3593
+ const priorSelection = await readJsonFile(names.selection);
3594
+ const legacySelection = await readJsonFile(names.legacyManifest);
3595
+ const priorCount = Math.max(
3596
+ Array.isArray(priorSelection?.files) ? priorSelection.files.length : 0,
3597
+ Array.isArray(legacySelection?.files) ? legacySelection.files.length : 0,
3598
+ );
3599
+ const fileNames = Array.from(
3600
+ { length: Math.max(names.files.length, priorCount) },
3601
+ (_, index) => `${names.key}.${String(index).padStart(4, "0")}.artifact`,
3602
+ );
3363
3603
  const results = await Promise.all([
3364
- removeEntry(names.manifest),
3365
- ...names.files.map(removeEntry),
3604
+ removeEntry(names.selection),
3605
+ removeEntry(names.legacyManifest),
3606
+ ...fileNames.map(removeEntry),
3366
3607
  ]);
3367
3608
  return results.some(Boolean);
3368
3609
  }
3369
3610
 
3370
- async function readManifest(name) {
3371
- const file = await readFile(name);
3372
- if (!file) return null;
3373
- try {
3374
- return JSON.parse(await file.text());
3375
- } catch {
3376
- await removeEntry(name);
3377
- return null;
3378
- }
3379
- }
3380
-
3381
- async function verifyFile(file, descriptor, security, signal, onProgress, phase) {
3382
- if (security.checks.byteLength && file.size !== descriptor.bytes) return false;
3383
- if (!security.checks.sha256) {
3384
- onProgress?.(providerProgress(phase, file.size, file.size));
3385
- return true;
3386
- }
3387
- const digest = createStreamingSha256();
3388
- let completed = 0;
3389
- for await (const chunk of byteChunks(file.stream(), signal)) {
3390
- digest.update(chunk);
3391
- completed += chunk.byteLength;
3392
- onProgress?.(providerProgress(phase, completed, file.size));
3393
- }
3394
- return completed === file.size && digest.digestHex() === descriptor.sha256;
3395
- }
3396
-
3397
3611
  function graphFileReason(descriptor, boundary) {
3398
3612
  const subject = descriptor.kind === "runtime-entrypoint-javascript"
3399
3613
  ? "entrypoint"
@@ -3405,27 +3619,12 @@ export function createDbopfsSpeechArtifactStore({
3405
3619
  return artifactGraphError(graphFileReason(descriptor, boundary), message);
3406
3620
  }
3407
3621
 
3408
- function assertSecurityDescriptors(files, security) {
3409
- for (const descriptor of files) {
3410
- if (security.checks.byteLength && descriptor.bytes === null) {
3411
- throw new TypeError(
3412
- `${descriptor.kind} file ${descriptor.path} requires bytes under the effective security policy.`,
3413
- );
3414
- }
3415
- if (security.checks.sha256 && descriptor.sha256 === null) {
3416
- throw new TypeError(
3417
- `${descriptor.kind} file ${descriptor.path} requires sha256 under the effective security policy.`,
3418
- );
3419
- }
3420
- }
3421
- }
3422
-
3423
- async function openCached(authority, { signal, onProgress, security } = {}) {
3622
+ async function openCached(authority, { signal, onProgress } = {}) {
3424
3623
  const graph = ARTIFACT_GRAPHS.has(authority);
3425
3624
  const metadata = artifactMetadata(authority);
3426
3625
  const names = storageNames(authority, metadata.files);
3427
- const manifest = await readManifest(names.manifest);
3428
- if (!manifestMatches(manifest, authority, metadata.files)) {
3626
+ const storedSelection = await readJsonFile(names.selection);
3627
+ if (JSON.stringify(storedSelection) !== JSON.stringify(cacheSelection(authority))) {
3429
3628
  await removeUnlocked(authority);
3430
3629
  return null;
3431
3630
  }
@@ -3434,42 +3633,21 @@ export function createDbopfsSpeechArtifactStore({
3434
3633
  throwIfAborted(signal);
3435
3634
  const descriptor = metadata.files[index];
3436
3635
  const file = await readFile(names.files[index]);
3437
- const observed = manifest.files[index];
3438
- if (
3439
- !file
3440
- || observed?.path !== descriptor.path
3441
- || observed?.bytes !== file.size
3442
- || (graph && observed?.sha256 !== descriptor.sha256)
3443
- ) {
3444
- await removeUnlocked(authority);
3445
- return null;
3446
- }
3447
- if (!await verifyFile(
3448
- file,
3449
- descriptor,
3450
- security,
3451
- signal,
3452
- onProgress,
3453
- graph
3454
- ? security.checks.sha256
3455
- ? "artifact-graph-dbopfs-cache-rehash"
3456
- : "artifact-graph-dbopfs-cache-readback"
3457
- : "verify-cache",
3458
- )) {
3636
+ if (!file) {
3459
3637
  await removeUnlocked(authority);
3460
3638
  return null;
3461
3639
  }
3462
3640
  files.push({ descriptor, file });
3463
3641
  }
3464
3642
  try {
3465
- const inspection = graph
3466
- ? await inspectArtifactGraphRuntime({ files }, metadata, signal, security)
3467
- : (await assertSelfContainedRuntime({ files }, metadata, security), null);
3643
+ const routing = graph
3644
+ ? await planOrdinaryMaterializedRuntime({ files }, metadata, signal)
3645
+ : null;
3468
3646
  throwIfAborted(signal);
3469
- return Object.freeze({
3470
- files: Object.freeze(files),
3647
+ return completeValue({
3648
+ files: completeValue(files),
3471
3649
  cache: "cached",
3472
- inspection,
3650
+ routing,
3473
3651
  });
3474
3652
  } catch (error) {
3475
3653
  await removeUnlocked(authority);
@@ -3477,7 +3655,7 @@ export function createDbopfsSpeechArtifactStore({
3477
3655
  }
3478
3656
  }
3479
3657
 
3480
- async function install(authority, { signal, onProgress, security } = {}) {
3658
+ async function install(authority, { signal, onProgress } = {}) {
3481
3659
  const graph = ARTIFACT_GRAPHS.has(authority);
3482
3660
  const metadata = artifactMetadata(authority);
3483
3661
  const names = storageNames(authority, metadata.files);
@@ -3488,21 +3666,22 @@ export function createDbopfsSpeechArtifactStore({
3488
3666
  await removeUnlocked(authority);
3489
3667
  const installed = [];
3490
3668
  try {
3669
+ // This mutable cache selection is written before content. It is only an
3670
+ // invalidation record, never a completion, byte-identity, or integrity receipt.
3671
+ await writeFile(
3672
+ names.selection,
3673
+ new TextEncoder().encode(`${JSON.stringify(cacheSelection(authority))}\n`),
3674
+ { signal },
3675
+ );
3491
3676
  for (let index = 0; index < metadata.files.length; index += 1) {
3492
3677
  throwIfAborted(signal);
3493
3678
  const descriptor = metadata.files[index];
3494
3679
  const sourceUrl = graph ? descriptor.sourceUrl : descriptor.url;
3495
- const redirectFinalOrigins = graph
3496
- ? descriptor.redirectFinalOrigins
3497
- : Object.freeze([]);
3498
3680
  let response;
3499
3681
  try {
3500
3682
  response = await fetchFunction(sourceUrl, {
3501
3683
  cache: "no-store",
3502
- credentials: "omit",
3503
- mode: "cors",
3504
- redirect: redirectFinalOrigins.length < 1 ? "error" : "follow",
3505
- referrerPolicy: "no-referrer",
3684
+ redirect: "follow",
3506
3685
  signal,
3507
3686
  });
3508
3687
  } catch (error) {
@@ -3517,28 +3696,18 @@ export function createDbopfsSpeechArtifactStore({
3517
3696
  throw speechError("ARCANE_AI_ARTIFACT_DOWNLOAD_FAILED", "A speech artifact source fetch was rejected.", error);
3518
3697
  }
3519
3698
  let responseBody;
3520
- let responseHeaders;
3521
3699
  let responseOk;
3522
- let responseRedirected;
3523
3700
  let responseStatus;
3524
- let responseUrl;
3525
3701
  try {
3526
3702
  if (!response || typeof response !== "object") {
3527
3703
  throw new TypeError("The artifact fetch result is not an object.");
3528
3704
  }
3529
3705
  responseBody = response.body;
3530
- responseHeaders = response.headers;
3531
3706
  responseOk = response.ok;
3532
- responseRedirected = response.redirected;
3533
3707
  responseStatus = response.status;
3534
- responseUrl = response.url;
3535
3708
  if (
3536
3709
  typeof responseOk !== "boolean"
3537
- || typeof responseRedirected !== "boolean"
3538
3710
  || !Number.isInteger(responseStatus)
3539
- || typeof responseUrl !== "string"
3540
- || !responseHeaders
3541
- || typeof responseHeaders.get !== "function"
3542
3711
  || (responseBody !== null && typeof responseBody?.getReader !== "function")
3543
3712
  ) {
3544
3713
  throw new TypeError("The artifact fetch result is not a readable Fetch Response.");
@@ -3557,81 +3726,6 @@ export function createDbopfsSpeechArtifactStore({
3557
3726
  error,
3558
3727
  );
3559
3728
  }
3560
- let finalUrl = null;
3561
- let finalUrlRecord = null;
3562
- try {
3563
- finalUrlRecord = responseUrl
3564
- ? new URL(responseUrl)
3565
- : null;
3566
- finalUrl = finalUrlRecord?.href ?? null;
3567
- } catch {
3568
- finalUrlRecord = null;
3569
- finalUrl = null;
3570
- }
3571
- if (graph && responseRedirected && !finalUrlRecord) {
3572
- await responseBody?.cancel?.().catch(() => undefined);
3573
- throw artifactGraphError(
3574
- "artifact-graph-source-response-url-unreadable",
3575
- `Artifact graph redirected source response for ${descriptor.path} did not expose a readable final URL.`,
3576
- );
3577
- }
3578
- if (graph && responseRedirected && redirectFinalOrigins.length < 1) {
3579
- await responseBody?.cancel?.().catch(() => undefined);
3580
- throw artifactGraphError(
3581
- "artifact-graph-source-redirected",
3582
- `Artifact graph source response for ${descriptor.path} did not match its immutable URL.`,
3583
- );
3584
- }
3585
- if (graph && responseRedirected && finalUrlRecord.protocol !== "https:") {
3586
- await responseBody?.cancel?.().catch(() => undefined);
3587
- throw artifactGraphError(
3588
- "artifact-graph-source-response-url-protocol-not-https",
3589
- `Artifact graph redirected source response for ${descriptor.path} did not end at HTTPS.`,
3590
- );
3591
- }
3592
- if (
3593
- graph
3594
- && responseRedirected
3595
- && (finalUrlRecord.username || finalUrlRecord.password)
3596
- ) {
3597
- await responseBody?.cancel?.().catch(() => undefined);
3598
- throw artifactGraphError(
3599
- "artifact-graph-source-response-url-credentials-rejected",
3600
- `Artifact graph redirected source response for ${descriptor.path} exposed credentials in its final URL.`,
3601
- );
3602
- }
3603
- if (graph && responseRedirected && finalUrlRecord.hash) {
3604
- await responseBody?.cancel?.().catch(() => undefined);
3605
- throw artifactGraphError(
3606
- "artifact-graph-source-response-url-fragment-rejected",
3607
- `Artifact graph redirected source response for ${descriptor.path} exposed a fragment in its final URL.`,
3608
- );
3609
- }
3610
- if (
3611
- graph
3612
- && responseRedirected
3613
- && !redirectFinalOrigins.includes(finalUrlRecord.origin)
3614
- ) {
3615
- await responseBody?.cancel?.().catch(() => undefined);
3616
- throw artifactGraphError(
3617
- "artifact-graph-source-redirect-final-origin-mismatch",
3618
- `Artifact graph redirected source response for ${descriptor.path} ended at an undeclared final origin.`,
3619
- );
3620
- }
3621
- if (graph && !responseRedirected && finalUrl !== sourceUrl) {
3622
- await responseBody?.cancel?.().catch(() => undefined);
3623
- throw artifactGraphError(
3624
- "artifact-graph-source-response-url-mismatch",
3625
- `Artifact graph non-redirected source response for ${descriptor.path} did not retain its immutable URL.`,
3626
- );
3627
- }
3628
- if (!graph && (responseRedirected || finalUrl !== sourceUrl)) {
3629
- await responseBody?.cancel?.().catch(() => undefined);
3630
- throw speechError(
3631
- "ARCANE_AI_ARTIFACT_SOURCE_CHANGED",
3632
- "A speech artifact response did not match its admitted URL.",
3633
- );
3634
- }
3635
3729
  if (!responseOk || !responseBody) {
3636
3730
  await responseBody?.cancel?.().catch(() => undefined);
3637
3731
  if (graph) {
@@ -3645,140 +3739,28 @@ export function createDbopfsSpeechArtifactStore({
3645
3739
  `A speech artifact server returned HTTP ${String(responseStatus)}.`,
3646
3740
  );
3647
3741
  }
3648
- const header = responseHeaders.get("content-length");
3649
- const reportedBytes = header ? Number(header) : null;
3650
- const contentEncoding = responseHeaders.get("content-encoding")?.trim() ?? "";
3651
- if (graph) {
3652
- const reportedMediaType = responseHeaders.get("content-type")
3653
- ?.split(";", 1)[0]
3654
- ?.trim()
3655
- ?.toLowerCase() ?? null;
3656
- if (reportedMediaType !== descriptor.sourceMediaType) {
3657
- await responseBody.cancel?.().catch(() => undefined);
3658
- throw graphVerificationError(
3659
- descriptor,
3660
- descriptor.sourceMediaType === descriptor.mediaType
3661
- ? "media-type-mismatch"
3662
- : "source-media-type-mismatch",
3663
- `Artifact graph source media type for ${descriptor.path} did not match ${descriptor.sourceMediaType}.`,
3664
- );
3665
- }
3666
- }
3667
- if (
3668
- security.checks.byteLength
3669
- && Number.isSafeInteger(reportedBytes)
3670
- && (!graph || !contentEncoding)
3671
- && reportedBytes !== descriptor.bytes
3672
- ) {
3673
- await responseBody.cancel?.().catch(() => undefined);
3674
- if (graph) {
3675
- throw graphVerificationError(
3676
- descriptor,
3677
- "byte-length-mismatch",
3678
- `Artifact graph source Content-Length for ${descriptor.path} changed.`,
3679
- );
3680
- }
3681
- throw speechError("ARCANE_AI_ARTIFACT_SIZE_MISMATCH", "Speech artifact Content-Length changed.");
3682
- }
3683
- const digest = security.checks.sha256
3684
- ? createStreamingSha256()
3685
- : null;
3686
- const written = await writeFile(names.files[index], responseBody, {
3687
- signal,
3688
- onChunk(chunk, completed) {
3689
- digest?.update(chunk);
3690
- if (security.checks.byteLength && completed > descriptor.bytes) {
3691
- if (graph) {
3692
- throw graphVerificationError(
3693
- descriptor,
3694
- "byte-length-mismatch",
3695
- `Artifact graph source ${descriptor.path} exceeded its declared byte length.`,
3696
- );
3697
- }
3698
- throw speechError("ARCANE_AI_ARTIFACT_SIZE_MISMATCH", "A speech artifact exceeded its expected size.");
3699
- }
3700
- onProgress?.(providerProgress(
3701
- graph ? "artifact-graph-network-download" : "download",
3702
- completed,
3703
- security.checks.byteLength ? descriptor.bytes : null,
3704
- ));
3705
- },
3706
- });
3707
- if (security.checks.byteLength && written !== descriptor.bytes) {
3708
- if (graph) {
3709
- throw graphVerificationError(
3710
- descriptor,
3711
- "byte-length-mismatch",
3712
- `Artifact graph source ${descriptor.path} byte length changed.`,
3713
- );
3714
- }
3715
- throw speechError("ARCANE_AI_ARTIFACT_SIZE_MISMATCH", "A speech artifact byte count changed.");
3716
- }
3717
- if (digest && digest.digestHex() !== descriptor.sha256) {
3718
- if (graph) {
3719
- throw graphVerificationError(
3720
- descriptor,
3721
- "sha256-mismatch",
3722
- `Artifact graph source ${descriptor.path} SHA-256 changed.`,
3723
- );
3724
- }
3725
- throw speechError("ARCANE_AI_ARTIFACT_DIGEST_MISMATCH", "A speech artifact SHA-256 changed.");
3726
- }
3742
+ await writeFile(names.files[index], responseBody, { signal });
3727
3743
  const file = await readFile(names.files[index]);
3728
- if (!file || file.size !== written) {
3744
+ if (!file) {
3729
3745
  if (graph) {
3730
3746
  throw graphVerificationError(
3731
3747
  descriptor,
3732
- "dbopfs-persisted-byte-length-mismatch",
3748
+ "dbopfs-persisted-file-missing",
3733
3749
  `DBOPFS did not preserve artifact graph file ${descriptor.path}.`,
3734
3750
  );
3735
3751
  }
3736
3752
  throw speechError("ARCANE_AI_ARTIFACT_CACHE_REJECTED", "DBOPFS did not preserve a speech artifact.");
3737
3753
  }
3738
- if (!await verifyFile(
3739
- file,
3740
- descriptor,
3741
- security,
3742
- signal,
3743
- onProgress,
3744
- graph
3745
- ? security.checks.sha256
3746
- ? "artifact-graph-dbopfs-persisted-rehash"
3747
- : "artifact-graph-dbopfs-persisted-readback"
3748
- : "verify-cache",
3749
- )) {
3750
- if (graph) {
3751
- throw graphVerificationError(
3752
- descriptor,
3753
- "dbopfs-persisted-sha256-mismatch",
3754
- `DBOPFS persisted bytes for artifact graph file ${descriptor.path} were rejected during re-verification.`,
3755
- );
3756
- }
3757
- throw speechError("ARCANE_AI_ARTIFACT_CACHE_REJECTED", "DBOPFS persisted bytes were rejected during verification.");
3758
- }
3759
3754
  installed.push({ descriptor, file });
3760
3755
  }
3761
- const inspection = graph
3762
- ? await inspectArtifactGraphRuntime({ files: installed }, metadata, signal, security)
3763
- : (await assertSelfContainedRuntime({ files: installed }, metadata, security), null);
3756
+ const routing = graph
3757
+ ? await planOrdinaryMaterializedRuntime({ files: installed }, metadata, signal)
3758
+ : null;
3764
3759
  throwIfAborted(signal);
3765
- const manifest = Object.freeze({
3766
- schema: graph ? ARTIFACT_GRAPH_MANIFEST_SCHEMA : MANIFEST_SCHEMA,
3767
- complete: true,
3768
- authority: storedArtifactProjection(authority),
3769
- files: Object.freeze(installed.map(({ descriptor, file }) => Object.freeze({
3770
- path: descriptor.path,
3771
- bytes: file.size,
3772
- ...(graph ? { sha256: descriptor.sha256 } : {}),
3773
- }))),
3774
- completedAt: new Date().toISOString(),
3775
- });
3776
- const encoded = new TextEncoder().encode(`${JSON.stringify(manifest)}\n`);
3777
- await writeFile(names.manifest, encoded, { signal });
3778
- return Object.freeze({
3779
- files: Object.freeze(installed),
3760
+ return completeValue({
3761
+ files: completeValue(installed),
3780
3762
  cache: "installed",
3781
- inspection,
3763
+ routing,
3782
3764
  });
3783
3765
  } catch (error) {
3784
3766
  await removeUnlocked(authority).catch(() => undefined);
@@ -3797,75 +3779,52 @@ export function createDbopfsSpeechArtifactStore({
3797
3779
  }
3798
3780
  throwIfAborted(signal);
3799
3781
  const graph = ARTIFACT_GRAPHS.has(authority);
3800
- let effectiveSecurity;
3801
- try {
3802
- effectiveSecurity = resolveModelSecurity({ load: security });
3803
- } catch (error) {
3804
- if (graph) {
3805
- throw artifactGraphTypeError(
3806
- "artifact-graph-load-security-contract-rejected",
3807
- "Browser speech artifact graph load security does not satisfy the required contract.",
3808
- error,
3809
- );
3810
- }
3811
- throw error;
3812
- }
3782
+ void security;
3783
+ // Security is an intent-only seam. Artifact checks remain disabled until
3784
+ // secure mode is explicitly reviewed with the user and implemented.
3813
3785
  const metadata = artifactMetadata(authority);
3814
- assertSecurityDescriptors(metadata.files, effectiveSecurity);
3815
3786
  const cached = await openCached(authority, {
3816
3787
  signal,
3817
3788
  onProgress,
3818
- security: effectiveSecurity,
3819
3789
  });
3820
3790
  const admitted = cached ?? (offline
3821
3791
  ? null
3822
3792
  : await install(authority, {
3823
3793
  signal,
3824
3794
  onProgress,
3825
- security: effectiveSecurity,
3826
3795
  }));
3827
3796
  if (!admitted) {
3828
3797
  if (graph) {
3829
3798
  throw artifactGraphError(
3830
3799
  "artifact-graph-offline-cache-miss",
3831
- "No complete admitted offline artifact graph cache is available.",
3800
+ "No cached offline artifact graph is available.",
3832
3801
  );
3833
3802
  }
3834
- throw speechError("ARCANE_AI_ARTIFACT_OFFLINE_MISS", "No admitted offline speech cache is available.");
3803
+ throw speechError("ARCANE_AI_ARTIFACT_OFFLINE_MISS", "No cached offline speech artifacts are available.");
3835
3804
  }
3836
3805
  if (graph) {
3837
- const artifactGraphAdmission = artifactGraphAdmissionStatus(
3838
- admitted.cache,
3839
- offline,
3840
- effectiveSecurity,
3841
- );
3842
- const materialized = await createArtifactGraphObjectUrls(
3806
+ const materialized = await createOrdinaryArtifactObjectUrls(
3843
3807
  admitted,
3844
3808
  metadata,
3845
- admitted.inspection,
3846
- effectiveSecurity,
3809
+ admitted.routing,
3810
+ objectUrlFactory,
3847
3811
  );
3848
- const runtimeFiles = Object.freeze(materialized.files.filter((file) =>
3812
+ const runtimeFiles = completeValue(materialized.files.filter((file) =>
3849
3813
  file.kind.startsWith("runtime-")));
3850
- const modelFiles = Object.freeze(materialized.files.filter((file) =>
3814
+ const modelFiles = completeValue(materialized.files.filter((file) =>
3851
3815
  !file.kind.startsWith("runtime-")));
3852
- return Object.freeze({
3853
- cache: artifactGraphAdmission,
3854
- artifactGraphId: authority.identitySha256,
3855
- artifactGraphAdmission,
3856
- warnings: admitted.inspection.warnings,
3857
- security: effectiveSecurity,
3858
- runtime: Object.freeze({
3859
- ...metadata.runtime,
3816
+ return completeValue({
3817
+ cache: admitted.cache,
3818
+ runtime: completeValue({
3819
+ adapter: metadata.runtime.adapter,
3820
+ version: metadata.runtime.version,
3821
+ revision: metadata.runtime.revision,
3822
+ entry: metadata.runtime.entry,
3823
+ moduleGraph: metadata.runtime.moduleGraph,
3824
+ onnxWasm: metadata.runtime.onnxWasm,
3860
3825
  files: runtimeFiles,
3861
- edges: metadata.edges,
3862
- transforms: metadata.transforms,
3863
- warnings: admitted.inspection.warnings,
3864
- guardCapability: materialized.guardCapability,
3865
- artifactGraphId: authority.identitySha256,
3866
- artifactGraphAdmission,
3867
3826
  }),
3868
- model: Object.freeze({
3827
+ model: completeValue({
3869
3828
  ...metadata.model,
3870
3829
  files: modelFiles,
3871
3830
  }),
@@ -3875,9 +3834,9 @@ export function createDbopfsSpeechArtifactStore({
3875
3834
  const materialized = createObjectUrls(admitted.files, objectUrlFactory);
3876
3835
  const runtimeFiles = materialized.files.filter((file) => file.kind === "runtime");
3877
3836
  const modelFiles = materialized.files.filter((file) => file.kind === "model");
3878
- return Object.freeze({
3837
+ return completeValue({
3879
3838
  cache: admitted.cache,
3880
- runtime: Object.freeze({
3839
+ runtime: completeValue({
3881
3840
  adapter: metadata.runtime.adapter,
3882
3841
  version: metadata.runtime.version,
3883
3842
  revision: metadata.runtime.revision,
@@ -3886,14 +3845,15 @@ export function createDbopfsSpeechArtifactStore({
3886
3845
  ? {}
3887
3846
  : { wasmPaths: metadata.runtime.wasmPaths }),
3888
3847
  moduleGraph: "self-contained",
3889
- files: Object.freeze(runtimeFiles),
3848
+ files: completeValue(runtimeFiles),
3890
3849
  }),
3891
- model: Object.freeze({
3850
+ model: completeValue({
3892
3851
  id: metadata.model.id,
3893
3852
  repository: metadata.model.repository,
3894
3853
  revision: metadata.model.revision,
3854
+ ...(metadata.model.dtype === undefined ? {} : { dtype: metadata.model.dtype }),
3895
3855
  defaultVoice: metadata.model.defaultVoice,
3896
- files: Object.freeze(modelFiles),
3856
+ files: completeValue(modelFiles),
3897
3857
  }),
3898
3858
  release: materialized.release,
3899
3859
  });
@@ -3932,7 +3892,7 @@ export function createDbopfsSpeechArtifactStore({
3932
3892
  return serializeAuthority(authority, () => removeUnlocked(authority));
3933
3893
  }
3934
3894
 
3935
- const store = Object.freeze({
3895
+ const store = completeValue({
3936
3896
  protocol: BROWSER_SPEECH_ARTIFACT_PROTOCOL,
3937
3897
  tableName,
3938
3898
  prepare,