arcane-os 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/CHANGELOG.md +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 +887 -909
  5. package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
  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 -146
  11. package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
  12. package/browser-runtime/dom-event-instrumentation.mjs +55 -147
  13. package/browser-runtime/event-manager.mjs +239 -624
  14. package/package.json +5 -6
  15. package/runtime/arcane/components/app-bar.html +3 -15
  16. package/runtime/arcane/components/assistant-panel.html +10 -10
  17. package/runtime/arcane/components/calculator.html +1 -1
  18. package/runtime/arcane/components/chat.html +1359 -135
  19. package/runtime/arcane/components/conversation-view.html +2 -2
  20. package/runtime/arcane/components/document-inspector.html +11 -17
  21. package/runtime/arcane/components/file-manager.html +13 -56
  22. package/runtime/arcane/components/markdown-document.html +82 -281
  23. package/runtime/arcane/components/markdown-editor.html +7 -10
  24. package/runtime/arcane/components/media-embed.html +6 -6
  25. package/runtime/arcane/components/screen-capture.html +4 -4
  26. package/runtime/arcane/components/source-explanation.html +2 -2
  27. package/runtime/arcane/components/speech.html +112 -68
  28. package/runtime/arcane/components/terminal-workspace.html +4 -4
  29. package/runtime/arcane/components/theme-editor.html +1 -1
  30. package/runtime/arcane/components/unified-inbox.html +2 -2
  31. package/runtime/arcane/components/voice-transcription.html +31 -21
  32. package/runtime/arcane/entities/Calculation.js +2 -3
  33. package/runtime/arcane/entities/Chat.js +228 -43
  34. package/runtime/arcane/entities/Preference.js +3 -5
  35. package/runtime/arcane/entities/Weather.js +5 -5
  36. package/runtime/arcane/modules/AI.js +1050 -427
  37. package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
  38. package/runtime/arcane/modules/AIResponseLength.js +9 -19
  39. package/runtime/arcane/modules/AIRuntimeState.js +109 -72
  40. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
  41. package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
  42. package/runtime/arcane/modules/CalculatorEngine.js +9 -9
  43. package/runtime/arcane/modules/CommunicationAppController.js +3 -7
  44. package/runtime/arcane/modules/ComponentContracts.js +30 -32
  45. package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
  46. package/runtime/arcane/modules/ConversationActionItems.js +26 -59
  47. package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
  48. package/runtime/arcane/modules/ConversationTimebox.js +27 -15
  49. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
  50. package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
  51. package/runtime/arcane/modules/HTMLImport.js +54 -1
  52. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
  53. package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
  54. package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
  55. package/runtime/arcane/modules/MD.js +1 -45
  56. package/runtime/arcane/modules/Mail.js +51 -103
  57. package/runtime/arcane/modules/MailOutbox.mjs +95 -193
  58. package/runtime/arcane/modules/MailTransport.mjs +36 -57
  59. package/runtime/arcane/modules/ModelDefinition.js +22 -106
  60. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
  61. package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
  62. package/runtime/arcane/modules/PreferenceStore.js +102 -30
  63. package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
  64. package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
  65. package/runtime/arcane/modules/ScreenCapture.js +175 -128
  66. package/runtime/arcane/modules/SpeechPlayback.js +46 -149
  67. package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
  68. package/runtime/arcane/modules/ToolCallRouter.js +25 -12
  69. package/runtime/arcane/modules/YouTubeMedia.js +6 -5
  70. package/schemas/arcane-app-bundle.schema.json +13 -78
  71. package/schemas/arcane-app.schema.json +9 -25
  72. package/schemas/arcane-lock.schema.json +18 -151
  73. package/schemas/arcane-package.schema.json +2 -16
  74. package/schemas/native-build-plan.schema.json +119 -122
  75. package/src/app-descriptor.mjs +75 -132
  76. package/src/application-tests.mjs +200 -0
  77. package/src/cli/main.mjs +27 -46
  78. package/src/constants.mjs +3 -4
  79. package/src/dev-server.mjs +30 -324
  80. package/src/doctor.mjs +92 -154
  81. package/src/dom-event-instrumentation.mjs +55 -147
  82. package/src/errors.mjs +2 -3
  83. package/src/event-manager.mjs +239 -624
  84. package/src/event-queue.mjs +3 -3
  85. package/src/import-map.mjs +273 -1028
  86. package/src/index.mjs +14 -16
  87. package/src/installed-sdk-runtime.mjs +40 -62
  88. package/src/integrated-provider-loader.mjs +53 -382
  89. package/src/mail-api.mjs +0 -2
  90. package/src/mail-server.mjs +224 -580
  91. package/src/mail.mjs +4 -10
  92. package/src/native-plan.mjs +163 -598
  93. package/src/native-provider-loader.mjs +104 -1063
  94. package/src/packager/core.mjs +485 -3229
  95. package/src/process.mjs +5 -10
  96. package/src/release-bundle.mjs +292 -2405
  97. package/src/runtime.mjs +76 -396
  98. package/src/scaffold.mjs +30 -80
  99. package/src/sdk-browser-runtime.mjs +70 -626
  100. package/src/source-server.mjs +588 -0
  101. package/src/targets/index.mjs +78 -188
  102. package/src/templates/workspace-template.mjs +19 -135
  103. package/src/testing-loader.mjs +164 -0
  104. package/src/testing.mjs +1 -1
  105. package/src/toolchain.mjs +131 -544
  106. package/src/update-check.mjs +26 -64
  107. package/src/workspace-operation-lock.mjs +139 -430
  108. package/src/workspace-runtime.mjs +112 -779
  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 -187
  120. package/docs/reference/ai/browser-speech-package-authority.json +0 -835
  121. package/docs/reference/ai/browser-speech.md +0 -1252
  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 -677
  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 -2960
  150. package/docs/reference/sdk-api.md +0 -6694
  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,17 +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) {
2866
- if (security?.secure !== true) {
2867
- return Object.freeze({
2868
- plans: new Map(),
2869
- order: Object.freeze([]),
2870
- warnings: Object.freeze(["artifact-graph-runtime-unchecked"]),
2871
- });
2872
- }
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) {
2873
2745
  const admittedByPath = new Map(admitted.files.map((entry) => [entry.descriptor.path, entry]));
2874
2746
  const plans = new Map();
2875
2747
  for (const descriptor of metadata.runtimeFiles) {
@@ -2896,20 +2768,30 @@ async function inspectArtifactGraphRuntime(admitted, metadata, signal, security)
2896
2768
  plans.set(
2897
2769
  descriptor.path,
2898
2770
  inspectArtifactGraphModuleSource(source, descriptor.path, metadata, {
2899
- strict: security?.secure === true,
2771
+ strict: false,
2900
2772
  }),
2901
2773
  );
2902
2774
  }
2903
2775
  const order = assertArtifactGraphStaticImportClosure(metadata);
2904
2776
  const warnings = [...new Set([...plans.values()].flatMap((plan) => plan.warnings))]
2905
2777
  .sort();
2906
- if (security?.secure !== true && warnings.length > 0) {
2907
- globalThis.console?.warn?.(
2908
- `Arcane browser speech warn-first mode allowed runtime capabilities: ${warnings.join(", ")}.`,
2778
+ throwIfAborted(signal);
2779
+ return completeValue({ plans, order, warnings: completeValue(warnings) });
2780
+ }
2781
+
2782
+ function assertArtifactGraphRuntimeInspection(inspection) {
2783
+ if (
2784
+ !inspection
2785
+ || !(inspection.plans instanceof Map)
2786
+ || !Array.isArray(inspection.order)
2787
+ || !Array.isArray(inspection.warnings)
2788
+ ) {
2789
+ throw artifactGraphError(
2790
+ "artifact-graph-runtime-inspection-missing",
2791
+ "Artifact graph runtime inspection is required before executable materialization.",
2909
2792
  );
2910
2793
  }
2911
- throwIfAborted(signal);
2912
- return Object.freeze({ plans, order, warnings: Object.freeze(warnings) });
2794
+ return inspection;
2913
2795
  }
2914
2796
 
2915
2797
  function applyArtifactGraphModuleTransforms(plan, materializedByPath, guardCapability) {
@@ -2995,37 +2877,17 @@ function applyArtifactGraphModuleTransforms(plan, materializedByPath, guardCapab
2995
2877
  }
2996
2878
 
2997
2879
  function artifactGraphGuardCapability() {
2998
- const crypto = globalThis.crypto;
2999
- if (typeof crypto?.getRandomValues !== "function") {
3000
- throw artifactGraphError(
3001
- "artifact-graph-guard-capability-unavailable",
3002
- "Authenticated artifact graph materialization requires cryptographic random values.",
3003
- );
3004
- }
3005
- const bytes = new Uint8Array(32);
3006
- crypto.getRandomValues(bytes);
3007
- return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
3008
- }
3009
-
3010
- async function blobDigest(blob) {
3011
- const digest = createStreamingSha256();
3012
- let bytes = 0;
3013
- for await (const chunk of byteChunks(blob.stream())) {
3014
- digest.update(chunk);
3015
- bytes += chunk.byteLength;
3016
- }
3017
- return Object.freeze({ bytes, sha256: digest.digestHex() });
2880
+ return "ordinary";
3018
2881
  }
3019
2882
 
3020
- async function createArtifactGraphObjectUrls(admitted, metadata, inspection, security) {
2883
+ async function createArtifactGraphObjectUrls(admitted, metadata, inspection) {
3021
2884
  if (
3022
2885
  typeof PLATFORM_CREATE_OBJECT_URL !== "function"
3023
2886
  || typeof PLATFORM_REVOKE_OBJECT_URL !== "function"
3024
- || typeof PLATFORM_FETCH !== "function"
3025
2887
  ) {
3026
2888
  throw artifactGraphError(
3027
2889
  "artifact-graph-object-url-platform-unavailable",
3028
- "Authenticated artifact graph materialization requires native Blob URL creation, revocation, and fetch.",
2890
+ "Artifact graph materialization requires native Blob URL creation and revocation.",
3029
2891
  );
3030
2892
  }
3031
2893
  const admittedByPath = new Map(admitted.files.map((entry) => [entry.descriptor.path, entry]));
@@ -3033,20 +2895,11 @@ async function createArtifactGraphObjectUrls(admitted, metadata, inspection, sec
3033
2895
  const created = [];
3034
2896
  const createdIdentities = new Set();
3035
2897
  const guardCapability = artifactGraphGuardCapability();
2898
+ const runtimeInspection = assertArtifactGraphRuntimeInspection(inspection);
3036
2899
  async function materialize(descriptor, body) {
3037
2900
  const blob = body instanceof Blob && body.type === descriptor.mediaType
3038
2901
  ? body
3039
2902
  : new Blob([body], { type: descriptor.mediaType });
3040
- const source = await blobDigest(blob);
3041
- const transformed = ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(descriptor.kind);
3042
- const expected = Object.freeze({
3043
- bytes: transformed || security.checks.byteLength !== true
3044
- ? source.bytes
3045
- : descriptor.bytes,
3046
- sha256: transformed || security.checks.sha256 !== true
3047
- ? source.sha256
3048
- : descriptor.sha256,
3049
- });
3050
2903
  const moduleUrl = PLATFORM_CREATE_OBJECT_URL(blob);
3051
2904
  if (typeof moduleUrl !== "string" || !moduleUrl.startsWith("blob:")) {
3052
2905
  throw artifactGraphError(
@@ -3062,66 +2915,16 @@ async function createArtifactGraphObjectUrls(admitted, metadata, inspection, sec
3062
2915
  }
3063
2916
  createdIdentities.add(moduleUrl);
3064
2917
  created.push(moduleUrl);
3065
- let response;
3066
- try {
3067
- response = await PLATFORM_FETCH(moduleUrl, {
3068
- method: "GET",
3069
- credentials: "omit",
3070
- redirect: "error",
3071
- });
3072
- } catch (error) {
3073
- throw artifactGraphError(
3074
- "artifact-graph-object-url-readback-unavailable",
3075
- `Materialized artifact graph file ${descriptor.path} could not be read back from its Blob URL.`,
3076
- error,
3077
- );
3078
- }
3079
- if (!response.ok) {
3080
- throw artifactGraphError(
3081
- "artifact-graph-object-url-readback-http-status-rejected",
3082
- `Materialized artifact graph file ${descriptor.path} returned a non-success Blob URL response.`,
3083
- );
3084
- }
3085
- if (response.redirected || response.url !== moduleUrl) {
3086
- throw artifactGraphError(
3087
- "artifact-graph-object-url-readback-identity-mismatch",
3088
- `Materialized artifact graph file ${descriptor.path} did not retain its exact Blob URL identity.`,
3089
- );
3090
- }
3091
- const observedMediaType = response.headers.get("content-type")?.split(";", 1)[0].trim() ?? "";
3092
- if (observedMediaType !== descriptor.mediaType) {
3093
- throw artifactGraphError(
3094
- "artifact-graph-object-url-media-type-mismatch",
3095
- `Materialized artifact graph file ${descriptor.path} did not retain its declared media type.`,
3096
- );
3097
- }
3098
- const observedBlob = await response.blob();
3099
- const observed = await blobDigest(observedBlob);
3100
- if (observed.bytes !== expected.bytes) {
3101
- throw artifactGraphError(
3102
- "artifact-graph-object-url-byte-length-mismatch",
3103
- `Materialized artifact graph file ${descriptor.path} did not retain its exact byte length.`,
3104
- );
3105
- }
3106
- if (observed.sha256 !== expected.sha256) {
3107
- throw artifactGraphError(
3108
- "artifact-graph-object-url-sha256-mismatch",
3109
- `Materialized artifact graph file ${descriptor.path} did not retain its exact bytes.`,
3110
- );
3111
- }
3112
- materializedByPath.set(descriptor.path, Object.freeze({
2918
+ materializedByPath.set(descriptor.path, completeValue({
3113
2919
  kind: descriptor.kind,
3114
2920
  path: descriptor.path,
3115
2921
  sourceUrl: descriptor.sourceUrl,
3116
2922
  revision: descriptor.revision,
3117
- license: descriptor.license,
3118
2923
  moduleUrl,
3119
2924
  mediaType: descriptor.mediaType,
3120
2925
  ...(descriptor.sourceMediaType === descriptor.mediaType
3121
2926
  ? {}
3122
2927
  : { sourceMediaType: descriptor.sourceMediaType }),
3123
- bytes: descriptor.bytes,
3124
- sha256: descriptor.sha256,
3125
2928
  runtimeRequestUrls: descriptor.runtimeRequestUrls,
3126
2929
  ...(descriptor.redirectFinalOrigins.length < 1
3127
2930
  ? {}
@@ -3131,20 +2934,42 @@ async function createArtifactGraphObjectUrls(admitted, metadata, inspection, sec
3131
2934
  try {
3132
2935
  for (const descriptor of metadata.files) {
3133
2936
  if (ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(descriptor.kind)) continue;
3134
- await materialize(descriptor, admittedByPath.get(descriptor.path).file);
2937
+ const admittedFile = admittedByPath.get(descriptor.path)?.file;
2938
+ if (!admittedFile) {
2939
+ throw artifactGraphError(
2940
+ "artifact-graph-materialized-source-file-missing",
2941
+ `Artifact graph file ${descriptor.path} is unavailable for materialization.`,
2942
+ );
2943
+ }
2944
+ await materialize(descriptor, admittedFile);
3135
2945
  }
3136
- for (const path of inspection.order) {
2946
+ for (const path of runtimeInspection.order) {
3137
2947
  const descriptor = metadata.filesByPath.get(path);
3138
- const plan = inspection.plans.get(path);
2948
+ const plan = runtimeInspection.plans.get(path);
2949
+ if (!descriptor || !plan) {
2950
+ throw artifactGraphError(
2951
+ "artifact-graph-runtime-inspection-incomplete",
2952
+ `Artifact graph runtime inspection is incomplete for ${path}.`,
2953
+ );
2954
+ }
3139
2955
  await materialize(
3140
2956
  descriptor,
3141
2957
  applyArtifactGraphModuleTransforms(plan, materializedByPath, guardCapability),
3142
2958
  );
3143
2959
  }
3144
- return Object.freeze({
2960
+ const files = metadata.files.map((descriptor) => {
2961
+ const materialized = materializedByPath.get(descriptor.path);
2962
+ if (!materialized) {
2963
+ throw artifactGraphError(
2964
+ "artifact-graph-materialization-incomplete",
2965
+ `Artifact graph file ${descriptor.path} was not materialized.`,
2966
+ );
2967
+ }
2968
+ return materialized;
2969
+ });
2970
+ return completeValue({
3145
2971
  guardCapability,
3146
- files: Object.freeze(metadata.files.map((descriptor) =>
3147
- materializedByPath.get(descriptor.path))),
2972
+ files: completeValue(files),
3148
2973
  release() {
3149
2974
  for (const url of created.splice(0).reverse()) {
3150
2975
  try {
@@ -3167,58 +2992,482 @@ async function createArtifactGraphObjectUrls(admitted, metadata, inspection, sec
3167
2992
  }
3168
2993
  }
3169
2994
 
3170
- function artifactGraphAdmissionStatus(cache, offline, security) {
3171
- const verification = security.checks.byteLength && security.checks.sha256
3172
- ? "verified"
3173
- : security.checks.byteLength || security.checks.sha256
3174
- ? "partially-checked"
3175
- : "unchecked";
3176
- const source = cache === "installed"
3177
- ? "network-dbopfs"
3178
- : offline
3179
- ? "offline-dbopfs-cache"
3180
- : "dbopfs-cache";
3181
- 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
+ }
3182
3001
  }
3183
3002
 
3184
- function createObjectUrls(files, factory) {
3185
- const create = factory?.create ?? ((blob) => URL.createObjectURL(blob));
3186
- const revoke = factory?.revoke ?? ((url) => URL.revokeObjectURL(url));
3187
- if (typeof create !== "function" || typeof revoke !== "function") {
3188
- 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);
3189
3024
  }
3190
- 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;
3191
3041
  try {
3192
- const materialized = files.map(({ descriptor, file }) => {
3193
- const blob = file.type === descriptor.mediaType
3194
- ? file
3195
- : new Blob([file], { type: descriptor.mediaType });
3196
- const url = create(blob);
3197
- created.push(url);
3198
- return Object.freeze({
3199
- kind: descriptor.kind,
3200
- path: descriptor.path,
3201
- sourceUrl: descriptor.url,
3202
- moduleUrl: url,
3203
- mediaType: descriptor.mediaType,
3204
- bytes: file.size,
3205
- });
3206
- });
3207
- return Object.freeze({
3208
- files: Object.freeze(materialized),
3209
- release() {
3210
- for (const url of created.splice(0).reverse()) {
3211
- try {
3212
- revoke(url);
3213
- } catch {
3214
- // Object URL revocation follows worker termination and is best effort.
3215
- }
3216
- }
3217
- },
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([]),
3218
3053
  });
3219
- } catch (error) {
3220
- for (const url of created.splice(0).reverse()) {
3221
- try {
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);
3080
+ }
3081
+ }
3082
+ }
3083
+
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 {
3222
3471
  revoke(url);
3223
3472
  } catch {
3224
3473
  // Preserve the materialization error.
@@ -3229,8 +3478,8 @@ function createObjectUrls(files, factory) {
3229
3478
  }
3230
3479
 
3231
3480
  /**
3232
- * Stores an authority's complete runtime/model closure in an existing DBOPFS
3233
- * table. The completion manifest is always the final mutation.
3481
+ * Stores an authority's runtime/model files in an existing DBOPFS table.
3482
+ * Ordinary cache reuse creates no receipt, manifest, or byte identity.
3234
3483
  */
3235
3484
  export function createDbopfsSpeechArtifactStore({
3236
3485
  dbopfs,
@@ -3307,20 +3556,27 @@ export function createDbopfsSpeechArtifactStore({
3307
3556
  }
3308
3557
  }
3309
3558
 
3310
- 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 } = {}) {
3311
3570
  const directory = await table();
3312
3571
  const handle = await directory.getFileHandle(name, { create: true });
3313
3572
  const writable = await handle.createWritable();
3314
- let written = 0;
3315
3573
  try {
3316
3574
  for await (const chunk of byteChunks(body, signal)) {
3317
3575
  await writable.write(chunk);
3318
- written += chunk.byteLength;
3319
- onChunk?.(chunk, written);
3320
3576
  }
3321
3577
  throwIfAborted(signal);
3322
3578
  await writable.close();
3323
- return written;
3579
+ return undefined;
3324
3580
  } catch (error) {
3325
3581
  await writable.abort?.(error).catch(() => undefined);
3326
3582
  await directory.removeEntry(name).catch(() => undefined);
@@ -3334,40 +3590,24 @@ export function createDbopfsSpeechArtifactStore({
3334
3590
  }
3335
3591
  const metadata = artifactMetadata(authority);
3336
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
+ );
3337
3603
  const results = await Promise.all([
3338
- removeEntry(names.manifest),
3339
- ...names.files.map(removeEntry),
3604
+ removeEntry(names.selection),
3605
+ removeEntry(names.legacyManifest),
3606
+ ...fileNames.map(removeEntry),
3340
3607
  ]);
3341
3608
  return results.some(Boolean);
3342
3609
  }
3343
3610
 
3344
- async function readManifest(name) {
3345
- const file = await readFile(name);
3346
- if (!file) return null;
3347
- try {
3348
- return JSON.parse(await file.text());
3349
- } catch {
3350
- await removeEntry(name);
3351
- return null;
3352
- }
3353
- }
3354
-
3355
- async function verifyFile(file, descriptor, security, signal, onProgress, phase) {
3356
- if (security.checks.byteLength && file.size !== descriptor.bytes) return false;
3357
- if (!security.checks.sha256) {
3358
- onProgress?.(providerProgress(phase, file.size, file.size));
3359
- return true;
3360
- }
3361
- const digest = createStreamingSha256();
3362
- let completed = 0;
3363
- for await (const chunk of byteChunks(file.stream(), signal)) {
3364
- digest.update(chunk);
3365
- completed += chunk.byteLength;
3366
- onProgress?.(providerProgress(phase, completed, file.size));
3367
- }
3368
- return completed === file.size && digest.digestHex() === descriptor.sha256;
3369
- }
3370
-
3371
3611
  function graphFileReason(descriptor, boundary) {
3372
3612
  const subject = descriptor.kind === "runtime-entrypoint-javascript"
3373
3613
  ? "entrypoint"
@@ -3379,27 +3619,12 @@ export function createDbopfsSpeechArtifactStore({
3379
3619
  return artifactGraphError(graphFileReason(descriptor, boundary), message);
3380
3620
  }
3381
3621
 
3382
- function assertSecurityDescriptors(files, security) {
3383
- for (const descriptor of files) {
3384
- if (security.checks.byteLength && descriptor.bytes === null) {
3385
- throw new TypeError(
3386
- `${descriptor.kind} file ${descriptor.path} requires bytes under the effective security policy.`,
3387
- );
3388
- }
3389
- if (security.checks.sha256 && descriptor.sha256 === null) {
3390
- throw new TypeError(
3391
- `${descriptor.kind} file ${descriptor.path} requires sha256 under the effective security policy.`,
3392
- );
3393
- }
3394
- }
3395
- }
3396
-
3397
- async function openCached(authority, { signal, onProgress, security } = {}) {
3622
+ async function openCached(authority, { signal, onProgress } = {}) {
3398
3623
  const graph = ARTIFACT_GRAPHS.has(authority);
3399
3624
  const metadata = artifactMetadata(authority);
3400
3625
  const names = storageNames(authority, metadata.files);
3401
- const manifest = await readManifest(names.manifest);
3402
- if (!manifestMatches(manifest, authority, metadata.files)) {
3626
+ const storedSelection = await readJsonFile(names.selection);
3627
+ if (JSON.stringify(storedSelection) !== JSON.stringify(cacheSelection(authority))) {
3403
3628
  await removeUnlocked(authority);
3404
3629
  return null;
3405
3630
  }
@@ -3408,44 +3633,21 @@ export function createDbopfsSpeechArtifactStore({
3408
3633
  throwIfAborted(signal);
3409
3634
  const descriptor = metadata.files[index];
3410
3635
  const file = await readFile(names.files[index]);
3411
- const observed = manifest.files[index];
3412
- if (
3413
- !file
3414
- || observed?.path !== descriptor.path
3415
- || observed?.bytes !== file.size
3416
- || (graph && observed?.sha256 !== descriptor.sha256)
3417
- ) {
3418
- await removeUnlocked(authority);
3419
- return null;
3420
- }
3421
- if (!await verifyFile(
3422
- file,
3423
- descriptor,
3424
- security,
3425
- signal,
3426
- onProgress,
3427
- graph
3428
- ? security.checks.sha256
3429
- ? "artifact-graph-dbopfs-cache-rehash"
3430
- : "artifact-graph-dbopfs-cache-readback"
3431
- : "verify-cache",
3432
- )) {
3636
+ if (!file) {
3433
3637
  await removeUnlocked(authority);
3434
3638
  return null;
3435
3639
  }
3436
3640
  files.push({ descriptor, file });
3437
3641
  }
3438
3642
  try {
3439
- const inspection = graph && security.secure
3440
- ? await inspectArtifactGraphRuntime({ files }, metadata, signal, security)
3441
- : graph
3442
- ? null
3443
- : (await assertSelfContainedRuntime({ files }, metadata, security), null);
3643
+ const routing = graph
3644
+ ? await planOrdinaryMaterializedRuntime({ files }, metadata, signal)
3645
+ : null;
3444
3646
  throwIfAborted(signal);
3445
- return Object.freeze({
3446
- files: Object.freeze(files),
3647
+ return completeValue({
3648
+ files: completeValue(files),
3447
3649
  cache: "cached",
3448
- inspection,
3650
+ routing,
3449
3651
  });
3450
3652
  } catch (error) {
3451
3653
  await removeUnlocked(authority);
@@ -3453,7 +3655,7 @@ export function createDbopfsSpeechArtifactStore({
3453
3655
  }
3454
3656
  }
3455
3657
 
3456
- async function install(authority, { signal, onProgress, security } = {}) {
3658
+ async function install(authority, { signal, onProgress } = {}) {
3457
3659
  const graph = ARTIFACT_GRAPHS.has(authority);
3458
3660
  const metadata = artifactMetadata(authority);
3459
3661
  const names = storageNames(authority, metadata.files);
@@ -3464,21 +3666,22 @@ export function createDbopfsSpeechArtifactStore({
3464
3666
  await removeUnlocked(authority);
3465
3667
  const installed = [];
3466
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
+ );
3467
3676
  for (let index = 0; index < metadata.files.length; index += 1) {
3468
3677
  throwIfAborted(signal);
3469
3678
  const descriptor = metadata.files[index];
3470
3679
  const sourceUrl = graph ? descriptor.sourceUrl : descriptor.url;
3471
- const redirectFinalOrigins = graph
3472
- ? descriptor.redirectFinalOrigins
3473
- : Object.freeze([]);
3474
3680
  let response;
3475
3681
  try {
3476
3682
  response = await fetchFunction(sourceUrl, {
3477
3683
  cache: "no-store",
3478
- credentials: "omit",
3479
- mode: "cors",
3480
- redirect: redirectFinalOrigins.length < 1 ? "error" : "follow",
3481
- referrerPolicy: "no-referrer",
3684
+ redirect: "follow",
3482
3685
  signal,
3483
3686
  });
3484
3687
  } catch (error) {
@@ -3493,28 +3696,18 @@ export function createDbopfsSpeechArtifactStore({
3493
3696
  throw speechError("ARCANE_AI_ARTIFACT_DOWNLOAD_FAILED", "A speech artifact source fetch was rejected.", error);
3494
3697
  }
3495
3698
  let responseBody;
3496
- let responseHeaders;
3497
3699
  let responseOk;
3498
- let responseRedirected;
3499
3700
  let responseStatus;
3500
- let responseUrl;
3501
3701
  try {
3502
3702
  if (!response || typeof response !== "object") {
3503
3703
  throw new TypeError("The artifact fetch result is not an object.");
3504
3704
  }
3505
3705
  responseBody = response.body;
3506
- responseHeaders = response.headers;
3507
3706
  responseOk = response.ok;
3508
- responseRedirected = response.redirected;
3509
3707
  responseStatus = response.status;
3510
- responseUrl = response.url;
3511
3708
  if (
3512
3709
  typeof responseOk !== "boolean"
3513
- || typeof responseRedirected !== "boolean"
3514
3710
  || !Number.isInteger(responseStatus)
3515
- || typeof responseUrl !== "string"
3516
- || !responseHeaders
3517
- || typeof responseHeaders.get !== "function"
3518
3711
  || (responseBody !== null && typeof responseBody?.getReader !== "function")
3519
3712
  ) {
3520
3713
  throw new TypeError("The artifact fetch result is not a readable Fetch Response.");
@@ -3533,81 +3726,6 @@ export function createDbopfsSpeechArtifactStore({
3533
3726
  error,
3534
3727
  );
3535
3728
  }
3536
- let finalUrl = null;
3537
- let finalUrlRecord = null;
3538
- try {
3539
- finalUrlRecord = responseUrl
3540
- ? new URL(responseUrl)
3541
- : null;
3542
- finalUrl = finalUrlRecord?.href ?? null;
3543
- } catch {
3544
- finalUrlRecord = null;
3545
- finalUrl = null;
3546
- }
3547
- if (graph && responseRedirected && !finalUrlRecord) {
3548
- await responseBody?.cancel?.().catch(() => undefined);
3549
- throw artifactGraphError(
3550
- "artifact-graph-source-response-url-unreadable",
3551
- `Artifact graph redirected source response for ${descriptor.path} did not expose a readable final URL.`,
3552
- );
3553
- }
3554
- if (graph && responseRedirected && redirectFinalOrigins.length < 1) {
3555
- await responseBody?.cancel?.().catch(() => undefined);
3556
- throw artifactGraphError(
3557
- "artifact-graph-source-redirected",
3558
- `Artifact graph source response for ${descriptor.path} did not match its immutable URL.`,
3559
- );
3560
- }
3561
- if (graph && responseRedirected && finalUrlRecord.protocol !== "https:") {
3562
- await responseBody?.cancel?.().catch(() => undefined);
3563
- throw artifactGraphError(
3564
- "artifact-graph-source-response-url-protocol-not-https",
3565
- `Artifact graph redirected source response for ${descriptor.path} did not end at HTTPS.`,
3566
- );
3567
- }
3568
- if (
3569
- graph
3570
- && responseRedirected
3571
- && (finalUrlRecord.username || finalUrlRecord.password)
3572
- ) {
3573
- await responseBody?.cancel?.().catch(() => undefined);
3574
- throw artifactGraphError(
3575
- "artifact-graph-source-response-url-credentials-rejected",
3576
- `Artifact graph redirected source response for ${descriptor.path} exposed credentials in its final URL.`,
3577
- );
3578
- }
3579
- if (graph && responseRedirected && finalUrlRecord.hash) {
3580
- await responseBody?.cancel?.().catch(() => undefined);
3581
- throw artifactGraphError(
3582
- "artifact-graph-source-response-url-fragment-rejected",
3583
- `Artifact graph redirected source response for ${descriptor.path} exposed a fragment in its final URL.`,
3584
- );
3585
- }
3586
- if (
3587
- graph
3588
- && responseRedirected
3589
- && !redirectFinalOrigins.includes(finalUrlRecord.origin)
3590
- ) {
3591
- await responseBody?.cancel?.().catch(() => undefined);
3592
- throw artifactGraphError(
3593
- "artifact-graph-source-redirect-final-origin-mismatch",
3594
- `Artifact graph redirected source response for ${descriptor.path} ended at an undeclared final origin.`,
3595
- );
3596
- }
3597
- if (graph && !responseRedirected && finalUrl !== sourceUrl) {
3598
- await responseBody?.cancel?.().catch(() => undefined);
3599
- throw artifactGraphError(
3600
- "artifact-graph-source-response-url-mismatch",
3601
- `Artifact graph non-redirected source response for ${descriptor.path} did not retain its immutable URL.`,
3602
- );
3603
- }
3604
- if (!graph && (responseRedirected || finalUrl !== sourceUrl)) {
3605
- await responseBody?.cancel?.().catch(() => undefined);
3606
- throw speechError(
3607
- "ARCANE_AI_ARTIFACT_SOURCE_CHANGED",
3608
- "A speech artifact response did not match its admitted URL.",
3609
- );
3610
- }
3611
3729
  if (!responseOk || !responseBody) {
3612
3730
  await responseBody?.cancel?.().catch(() => undefined);
3613
3731
  if (graph) {
@@ -3621,142 +3739,28 @@ export function createDbopfsSpeechArtifactStore({
3621
3739
  `A speech artifact server returned HTTP ${String(responseStatus)}.`,
3622
3740
  );
3623
3741
  }
3624
- const header = responseHeaders.get("content-length");
3625
- const reportedBytes = header ? Number(header) : null;
3626
- const contentEncoding = responseHeaders.get("content-encoding")?.trim() ?? "";
3627
- if (graph) {
3628
- const reportedMediaType = responseHeaders.get("content-type")
3629
- ?.split(";", 1)[0]
3630
- ?.trim()
3631
- ?.toLowerCase() ?? null;
3632
- if (reportedMediaType !== descriptor.sourceMediaType) {
3633
- await responseBody.cancel?.().catch(() => undefined);
3634
- throw graphVerificationError(
3635
- descriptor,
3636
- descriptor.sourceMediaType === descriptor.mediaType
3637
- ? "media-type-mismatch"
3638
- : "source-media-type-mismatch",
3639
- `Artifact graph source media type for ${descriptor.path} did not match ${descriptor.sourceMediaType}.`,
3640
- );
3641
- }
3642
- }
3643
- if (
3644
- security.checks.byteLength
3645
- && Number.isSafeInteger(reportedBytes)
3646
- && (!graph || !contentEncoding)
3647
- && reportedBytes !== descriptor.bytes
3648
- ) {
3649
- await responseBody.cancel?.().catch(() => undefined);
3650
- if (graph) {
3651
- throw graphVerificationError(
3652
- descriptor,
3653
- "byte-length-mismatch",
3654
- `Artifact graph source Content-Length for ${descriptor.path} changed.`,
3655
- );
3656
- }
3657
- throw speechError("ARCANE_AI_ARTIFACT_SIZE_MISMATCH", "Speech artifact Content-Length changed.");
3658
- }
3659
- const digest = security.checks.sha256
3660
- ? createStreamingSha256()
3661
- : null;
3662
- const written = await writeFile(names.files[index], responseBody, {
3663
- signal,
3664
- onChunk(chunk, completed) {
3665
- digest?.update(chunk);
3666
- if (security.checks.byteLength && completed > descriptor.bytes) {
3667
- if (graph) {
3668
- throw graphVerificationError(
3669
- descriptor,
3670
- "byte-length-mismatch",
3671
- `Artifact graph source ${descriptor.path} exceeded its declared byte length.`,
3672
- );
3673
- }
3674
- throw speechError("ARCANE_AI_ARTIFACT_SIZE_MISMATCH", "A speech artifact exceeded its expected size.");
3675
- }
3676
- onProgress?.(providerProgress(
3677
- graph ? "artifact-graph-network-download" : "download",
3678
- completed,
3679
- security.checks.byteLength ? descriptor.bytes : null,
3680
- ));
3681
- },
3682
- });
3683
- if (security.checks.byteLength && written !== descriptor.bytes) {
3684
- if (graph) {
3685
- throw graphVerificationError(
3686
- descriptor,
3687
- "byte-length-mismatch",
3688
- `Artifact graph source ${descriptor.path} byte length changed.`,
3689
- );
3690
- }
3691
- throw speechError("ARCANE_AI_ARTIFACT_SIZE_MISMATCH", "A speech artifact byte count changed.");
3692
- }
3693
- if (digest && digest.digestHex() !== descriptor.sha256) {
3694
- if (graph) {
3695
- throw graphVerificationError(
3696
- descriptor,
3697
- "sha256-mismatch",
3698
- `Artifact graph source ${descriptor.path} SHA-256 changed.`,
3699
- );
3700
- }
3701
- throw speechError("ARCANE_AI_ARTIFACT_DIGEST_MISMATCH", "A speech artifact SHA-256 changed.");
3702
- }
3742
+ await writeFile(names.files[index], responseBody, { signal });
3703
3743
  const file = await readFile(names.files[index]);
3704
- if (!file || file.size !== written) {
3744
+ if (!file) {
3705
3745
  if (graph) {
3706
3746
  throw graphVerificationError(
3707
3747
  descriptor,
3708
- "dbopfs-persisted-byte-length-mismatch",
3748
+ "dbopfs-persisted-file-missing",
3709
3749
  `DBOPFS did not preserve artifact graph file ${descriptor.path}.`,
3710
3750
  );
3711
3751
  }
3712
3752
  throw speechError("ARCANE_AI_ARTIFACT_CACHE_REJECTED", "DBOPFS did not preserve a speech artifact.");
3713
3753
  }
3714
- if (!await verifyFile(
3715
- file,
3716
- descriptor,
3717
- security,
3718
- signal,
3719
- onProgress,
3720
- graph
3721
- ? security.checks.sha256
3722
- ? "artifact-graph-dbopfs-persisted-rehash"
3723
- : "artifact-graph-dbopfs-persisted-readback"
3724
- : "verify-cache",
3725
- )) {
3726
- if (graph) {
3727
- throw graphVerificationError(
3728
- descriptor,
3729
- "dbopfs-persisted-sha256-mismatch",
3730
- `DBOPFS persisted bytes for artifact graph file ${descriptor.path} were rejected during re-verification.`,
3731
- );
3732
- }
3733
- throw speechError("ARCANE_AI_ARTIFACT_CACHE_REJECTED", "DBOPFS persisted bytes were rejected during verification.");
3734
- }
3735
3754
  installed.push({ descriptor, file });
3736
3755
  }
3737
- const inspection = graph && security.secure
3738
- ? await inspectArtifactGraphRuntime({ files: installed }, metadata, signal, security)
3739
- : graph
3740
- ? null
3741
- : (await assertSelfContainedRuntime({ files: installed }, metadata, security), null);
3756
+ const routing = graph
3757
+ ? await planOrdinaryMaterializedRuntime({ files: installed }, metadata, signal)
3758
+ : null;
3742
3759
  throwIfAborted(signal);
3743
- const manifest = Object.freeze({
3744
- schema: graph ? ARTIFACT_GRAPH_MANIFEST_SCHEMA : MANIFEST_SCHEMA,
3745
- complete: true,
3746
- authority: storedArtifactProjection(authority),
3747
- files: Object.freeze(installed.map(({ descriptor, file }) => Object.freeze({
3748
- path: descriptor.path,
3749
- bytes: file.size,
3750
- ...(graph ? { sha256: descriptor.sha256 } : {}),
3751
- }))),
3752
- completedAt: new Date().toISOString(),
3753
- });
3754
- const encoded = new TextEncoder().encode(`${JSON.stringify(manifest)}\n`);
3755
- await writeFile(names.manifest, encoded, { signal });
3756
- return Object.freeze({
3757
- files: Object.freeze(installed),
3760
+ return completeValue({
3761
+ files: completeValue(installed),
3758
3762
  cache: "installed",
3759
- inspection,
3763
+ routing,
3760
3764
  });
3761
3765
  } catch (error) {
3762
3766
  await removeUnlocked(authority).catch(() => undefined);
@@ -3775,79 +3779,52 @@ export function createDbopfsSpeechArtifactStore({
3775
3779
  }
3776
3780
  throwIfAborted(signal);
3777
3781
  const graph = ARTIFACT_GRAPHS.has(authority);
3778
- let effectiveSecurity;
3779
- try {
3780
- effectiveSecurity = resolveModelSecurity({ load: security });
3781
- } catch (error) {
3782
- if (graph) {
3783
- throw artifactGraphTypeError(
3784
- "artifact-graph-load-security-contract-rejected",
3785
- "Browser speech artifact graph load security does not satisfy the required contract.",
3786
- error,
3787
- );
3788
- }
3789
- throw error;
3790
- }
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.
3791
3785
  const metadata = artifactMetadata(authority);
3792
- if (graph && effectiveSecurity.secure !== true) {
3793
- throw artifactGraphError(
3794
- "artifact-graph-secure-mode-required",
3795
- "Browser speech artifact graphs require explicit secure:true.",
3796
- );
3797
- }
3798
- assertSecurityDescriptors(metadata.files, effectiveSecurity);
3799
3786
  const cached = await openCached(authority, {
3800
3787
  signal,
3801
3788
  onProgress,
3802
- security: effectiveSecurity,
3803
3789
  });
3804
3790
  const admitted = cached ?? (offline
3805
3791
  ? null
3806
3792
  : await install(authority, {
3807
3793
  signal,
3808
3794
  onProgress,
3809
- security: effectiveSecurity,
3810
3795
  }));
3811
3796
  if (!admitted) {
3812
3797
  if (graph) {
3813
3798
  throw artifactGraphError(
3814
3799
  "artifact-graph-offline-cache-miss",
3815
- "No complete verified offline artifact graph cache is available.",
3800
+ "No cached offline artifact graph is available.",
3816
3801
  );
3817
3802
  }
3818
- 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.");
3819
3804
  }
3820
3805
  if (graph) {
3821
- const artifactGraphAdmission = artifactGraphAdmissionStatus(
3822
- admitted.cache,
3823
- offline,
3824
- effectiveSecurity,
3825
- );
3826
- const materialized = await createArtifactGraphObjectUrls(
3806
+ const materialized = await createOrdinaryArtifactObjectUrls(
3827
3807
  admitted,
3828
3808
  metadata,
3829
- admitted.inspection,
3830
- effectiveSecurity,
3809
+ admitted.routing,
3810
+ objectUrlFactory,
3831
3811
  );
3832
- const runtimeFiles = Object.freeze(materialized.files.filter((file) =>
3812
+ const runtimeFiles = completeValue(materialized.files.filter((file) =>
3833
3813
  file.kind.startsWith("runtime-")));
3834
- const modelFiles = Object.freeze(materialized.files.filter((file) =>
3814
+ const modelFiles = completeValue(materialized.files.filter((file) =>
3835
3815
  !file.kind.startsWith("runtime-")));
3836
- return Object.freeze({
3837
- cache: artifactGraphAdmission,
3838
- artifactGraphId: authority.identitySha256,
3839
- artifactGraphAdmission,
3840
- security: effectiveSecurity,
3841
- runtime: Object.freeze({
3842
- ...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,
3843
3825
  files: runtimeFiles,
3844
- edges: metadata.edges,
3845
- transforms: metadata.transforms,
3846
- guardCapability: materialized.guardCapability,
3847
- artifactGraphId: authority.identitySha256,
3848
- artifactGraphAdmission,
3849
3826
  }),
3850
- model: Object.freeze({
3827
+ model: completeValue({
3851
3828
  ...metadata.model,
3852
3829
  files: modelFiles,
3853
3830
  }),
@@ -3857,9 +3834,9 @@ export function createDbopfsSpeechArtifactStore({
3857
3834
  const materialized = createObjectUrls(admitted.files, objectUrlFactory);
3858
3835
  const runtimeFiles = materialized.files.filter((file) => file.kind === "runtime");
3859
3836
  const modelFiles = materialized.files.filter((file) => file.kind === "model");
3860
- return Object.freeze({
3837
+ return completeValue({
3861
3838
  cache: admitted.cache,
3862
- runtime: Object.freeze({
3839
+ runtime: completeValue({
3863
3840
  adapter: metadata.runtime.adapter,
3864
3841
  version: metadata.runtime.version,
3865
3842
  revision: metadata.runtime.revision,
@@ -3868,14 +3845,15 @@ export function createDbopfsSpeechArtifactStore({
3868
3845
  ? {}
3869
3846
  : { wasmPaths: metadata.runtime.wasmPaths }),
3870
3847
  moduleGraph: "self-contained",
3871
- files: Object.freeze(runtimeFiles),
3848
+ files: completeValue(runtimeFiles),
3872
3849
  }),
3873
- model: Object.freeze({
3850
+ model: completeValue({
3874
3851
  id: metadata.model.id,
3875
3852
  repository: metadata.model.repository,
3876
3853
  revision: metadata.model.revision,
3854
+ ...(metadata.model.dtype === undefined ? {} : { dtype: metadata.model.dtype }),
3877
3855
  defaultVoice: metadata.model.defaultVoice,
3878
- files: Object.freeze(modelFiles),
3856
+ files: completeValue(modelFiles),
3879
3857
  }),
3880
3858
  release: materialized.release,
3881
3859
  });
@@ -3914,7 +3892,7 @@ export function createDbopfsSpeechArtifactStore({
3914
3892
  return serializeAuthority(authority, () => removeUnlocked(authority));
3915
3893
  }
3916
3894
 
3917
- const store = Object.freeze({
3895
+ const store = completeValue({
3918
3896
  protocol: BROWSER_SPEECH_ARTIFACT_PROTOCOL,
3919
3897
  tableName,
3920
3898
  prepare,