arcane-os 0.5.16 → 0.5.18

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 (190) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +42 -19
  3. package/bin/arcane-test.mjs +16 -13
  4. package/browser-runtime/ai/browser-speech-artifacts.mjs +41 -38
  5. package/browser-runtime/ai/browser-speech-providers.mjs +53 -46
  6. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +127 -124
  7. package/browser-runtime/ai/browser-wasm.mjs +6 -3
  8. package/browser-runtime/ai/browser-wllama-runtime.mjs +26 -23
  9. package/browser-runtime/ai/model-controller.mjs +94 -91
  10. package/browser-runtime/ai/speech-worker-client.mjs +12 -9
  11. package/browser-runtime/ai/speech-worker-runtime.mjs +56 -53
  12. package/browser-runtime/dependencies/strong-type/index.js +207 -82
  13. package/browser-runtime/dependencies/strong-type/package.json +15 -6
  14. package/browser-runtime/dom-event-instrumentation.mjs +24 -20
  15. package/browser-runtime/event-manager.mjs +111 -108
  16. package/browser-runtime/speech-text.mjs +35 -0
  17. package/docs/architecture.md +1 -1
  18. package/docs/reference/README.md +8 -8
  19. package/docs/reference/ai/browser-speech.md +230 -16
  20. package/docs/reference/ai/twin-cloud.md +1 -1
  21. package/docs/reference/availability-and-normalization.md +42 -1
  22. package/docs/reference/cli.md +1 -1
  23. package/docs/reference/core/arcane-ai-contracts.md +1 -1
  24. package/docs/reference/inventory/package-api.json +60 -4
  25. package/docs/reference/inventory/runtime-components.json +2 -2
  26. package/docs/reference/inventory/runtime-modules.json +29 -31
  27. package/docs/reference/protocols.md +17 -13
  28. package/docs/reference/runtime-components.md +6 -5
  29. package/docs/reference/runtime-modules.md +212 -70
  30. package/docs/reference/sdk-api.md +197 -11
  31. package/examples/wasm-ai-demo/index.html +1 -0
  32. package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
  33. package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
  34. package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
  35. package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
  36. package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
  37. package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
  38. package/node_modules/strong-type/README.md +42 -24
  39. package/node_modules/strong-type/benchmark/core.js +324 -0
  40. package/node_modules/strong-type/index.js +207 -82
  41. package/node_modules/strong-type/package.json +14 -6
  42. package/package.json +4 -3
  43. package/runtime/arcane/components/app-bar.html +5 -2
  44. package/runtime/arcane/components/assistant-panel.html +5 -2
  45. package/runtime/arcane/components/chart.html +15 -12
  46. package/runtime/arcane/components/chat.html +118 -141
  47. package/runtime/arcane/components/dashboard-config.html +6 -3
  48. package/runtime/arcane/components/data-view.html +4 -1
  49. package/runtime/arcane/components/directory-picker.html +5 -2
  50. package/runtime/arcane/components/document-inspector.html +6 -3
  51. package/runtime/arcane/components/file-drop.html +5 -2
  52. package/runtime/arcane/components/file-inspector.html +9 -6
  53. package/runtime/arcane/components/file-manager.html +16 -13
  54. package/runtime/arcane/components/local-ai-status.html +5 -2
  55. package/runtime/arcane/components/markdown-document.html +14 -11
  56. package/runtime/arcane/components/markdown-editor.html +4 -1
  57. package/runtime/arcane/components/modal.html +4 -1
  58. package/runtime/arcane/components/output-panel.html +6 -3
  59. package/runtime/arcane/components/preferences-form.html +4 -1
  60. package/runtime/arcane/components/record-timeline.html +8 -5
  61. package/runtime/arcane/components/relationship-board.html +9 -6
  62. package/runtime/arcane/components/source-code-viewer.html +12 -9
  63. package/runtime/arcane/components/source-explanation.html +8 -5
  64. package/runtime/arcane/components/speech.html +24 -21
  65. package/runtime/arcane/components/summary-strip.html +4 -1
  66. package/runtime/arcane/components/task-progress.html +6 -3
  67. package/runtime/arcane/components/terminal-workspace.html +4 -1
  68. package/runtime/arcane/components/voice-transcription.html +11 -8
  69. package/runtime/arcane/components/web-navigator.html +5 -2
  70. package/runtime/arcane/entities/ApiModelRecord.js +5 -2
  71. package/runtime/arcane/entities/Calculation.js +5 -2
  72. package/runtime/arcane/entities/Chat.js +33 -33
  73. package/runtime/arcane/entities/CommunicationMessage.js +4 -1
  74. package/runtime/arcane/entities/CommunicationThread.js +4 -1
  75. package/runtime/arcane/entities/File.js +1 -1
  76. package/runtime/arcane/entities/Image.js +8 -5
  77. package/runtime/arcane/entities/IntentEnvelope.js +20 -17
  78. package/runtime/arcane/entities/Preference.js +9 -6
  79. package/runtime/arcane/entities/Theme.js +5 -2
  80. package/runtime/arcane/entities/User.js +11 -11
  81. package/runtime/arcane/entities/Weather.js +5 -2
  82. package/runtime/arcane/modules/AI.js +503 -187
  83. package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
  84. package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
  85. package/runtime/arcane/modules/AIProviderRuntime.js +117 -92
  86. package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
  87. package/runtime/arcane/modules/AIRuntimeState.js +29 -26
  88. package/runtime/arcane/modules/AnsiText.js +4 -1
  89. package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
  90. package/runtime/arcane/modules/AppDataScope.js +8 -5
  91. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
  92. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
  93. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
  94. package/runtime/arcane/modules/AsyncBoundary.js +13 -10
  95. package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
  96. package/runtime/arcane/modules/CalculatorEngine.js +5 -2
  97. package/runtime/arcane/modules/ChatRecords.js +18 -15
  98. package/runtime/arcane/modules/CommunicationAppController.js +9 -6
  99. package/runtime/arcane/modules/CommunicationHub.js +9 -6
  100. package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
  101. package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
  102. package/runtime/arcane/modules/ComponentContracts.js +56 -53
  103. package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
  104. package/runtime/arcane/modules/ConversationActionItems.js +15 -12
  105. package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
  106. package/runtime/arcane/modules/ConversationTimebox.js +28 -25
  107. package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
  108. package/runtime/arcane/modules/DBLS.js +7 -4
  109. package/runtime/arcane/modules/DBOPFS.js +7 -7
  110. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
  111. package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
  112. package/runtime/arcane/modules/DirectoryPicker.js +8 -5
  113. package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
  114. package/runtime/arcane/modules/DocumentNavigation.js +7 -4
  115. package/runtime/arcane/modules/Errors.js +28 -25
  116. package/runtime/arcane/modules/HTMLImport.js +7 -4
  117. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
  118. package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
  119. package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
  120. package/runtime/arcane/modules/MD.js +1 -1
  121. package/runtime/arcane/modules/Mail.js +46 -43
  122. package/runtime/arcane/modules/MailOutbox.mjs +48 -45
  123. package/runtime/arcane/modules/MailTransport.mjs +29 -26
  124. package/runtime/arcane/modules/MarkdownSpeech.js +1 -50
  125. package/runtime/arcane/modules/MemoryRecords.js +7 -4
  126. package/runtime/arcane/modules/MessageAdvisory.js +6 -3
  127. package/runtime/arcane/modules/ModelDefinition.js +7 -4
  128. package/runtime/arcane/modules/Ollama.js +6 -3
  129. package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
  130. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
  131. package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
  132. package/runtime/arcane/modules/PreferenceStore.js +20 -17
  133. package/runtime/arcane/modules/PreparedSpeech.js +485 -0
  134. package/runtime/arcane/modules/Questionnaire.js +6 -3
  135. package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
  136. package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
  137. package/runtime/arcane/modules/RecordReviewStore.js +12 -9
  138. package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
  139. package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
  140. package/runtime/arcane/modules/ScreenCapture.js +20 -17
  141. package/runtime/arcane/modules/SpeechPlayback.js +61 -28
  142. package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
  143. package/runtime/arcane/modules/SystemAppearance.js +5 -2
  144. package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
  145. package/runtime/arcane/modules/TerminalClient.js +14 -11
  146. package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
  147. package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
  148. package/runtime/arcane/modules/ThemeManager.js +5 -2
  149. package/runtime/arcane/modules/TimeGuard.js +1 -1
  150. package/runtime/arcane/modules/ToolCallRouter.js +11 -8
  151. package/runtime/arcane/modules/WaitForComponent.js +19 -16
  152. package/runtime/arcane/modules/YouTubeMedia.js +4 -1
  153. package/runtime/strong-type/index.js +1276 -352
  154. package/runtime/strong-type/package.json +69 -45
  155. package/src/app-descriptor.mjs +17 -14
  156. package/src/application-tests.mjs +4 -1
  157. package/src/cli/main.mjs +8 -5
  158. package/src/constants.mjs +4 -1
  159. package/src/dev-server.mjs +8 -5
  160. package/src/doctor.mjs +5 -2
  161. package/src/dom-event-instrumentation.mjs +24 -20
  162. package/src/errors.mjs +7 -3
  163. package/src/event-manager.mjs +111 -108
  164. package/src/event-queue.mjs +8 -5
  165. package/src/events.mjs +12 -9
  166. package/src/import-map.mjs +36 -27
  167. package/src/installed-sdk-runtime.mjs +4 -1
  168. package/src/integrated-provider-loader.mjs +9 -6
  169. package/src/mail-credentials.mjs +16 -13
  170. package/src/mail-server.mjs +53 -50
  171. package/src/mail.mjs +18 -15
  172. package/src/native-plan.mjs +12 -9
  173. package/src/native-provider-loader.mjs +7 -4
  174. package/src/packager/core.mjs +18 -15
  175. package/src/process.mjs +6 -3
  176. package/src/release-bundle.mjs +14 -11
  177. package/src/runtime.mjs +4 -1
  178. package/src/scaffold.mjs +9 -6
  179. package/src/sdk-browser-runtime.mjs +4 -1
  180. package/src/source-server.mjs +17 -14
  181. package/src/targets/index.mjs +5 -2
  182. package/src/templates/workspace-template.mjs +13 -6
  183. package/src/testing-loader.mjs +7 -4
  184. package/src/testing.mjs +10 -7
  185. package/src/toolchain.mjs +13 -10
  186. package/src/update-check.mjs +9 -6
  187. package/src/workspace-operation-lock.mjs +14 -11
  188. package/src/workspace-runtime.mjs +4 -1
  189. package/src/workspace.mjs +17 -14
  190. package/runtime/arcane/modules/AIResponseLength.js +0 -32
@@ -1,3 +1,4 @@
1
+ import Is from "../dependencies/strong-type/index.js";
1
2
  import { arcaneLogging } from '../logging.mjs';
2
3
  import {
3
4
  ARCANE_AI_ADAPTER_PROTOCOL,
@@ -9,6 +10,8 @@ import {
9
10
  import { createPackagedWllamaRuntime } from "./browser-wllama-runtime.mjs";
10
11
  import { arcaneEvents } from "../event-manager.mjs";
11
12
 
13
+ const is = new Is(false);
14
+
12
15
  const completeValue = (value) => value;
13
16
 
14
17
  const BROWSER_MODEL_SOURCES = new WeakSet();
@@ -76,9 +79,9 @@ function httpContentRange(value) {
76
79
  const end = Number(match[2]);
77
80
  const total = Number(match[3]);
78
81
  if (
79
- !Number.isSafeInteger(start)
80
- || !Number.isSafeInteger(end)
81
- || !Number.isSafeInteger(total)
82
+ !is.safeInteger(start)
83
+ || !is.safeInteger(end)
84
+ || !is.safeInteger(total)
82
85
  || start < 0
83
86
  || end < start
84
87
  || total <= end
@@ -88,11 +91,11 @@ function httpContentRange(value) {
88
91
 
89
92
  function httpContentLength(value) {
90
93
  const length = Number(String(value ?? "").trim());
91
- return Number.isSafeInteger(length) && length > 0 ? length : null;
94
+ return is.safeInteger(length) && length > 0 ? length : null;
92
95
  }
93
96
 
94
97
  function downloadConcurrencyValue(value) {
95
- if (!Number.isSafeInteger(value) || value < 1) {
98
+ if (!is.safeInteger(value) || value < 1) {
96
99
  throw new RangeError("Model downloadConcurrency must be a positive safe integer.");
97
100
  }
98
101
  return value;
@@ -126,7 +129,7 @@ function modelHttpRanges(total) {
126
129
  }
127
130
 
128
131
  function progressClock() {
129
- return typeof globalThis.performance?.now === "function"
132
+ return is.function(globalThis.performance?.now)
130
133
  ? globalThis.performance.now()
131
134
  : Date.now();
132
135
  }
@@ -142,7 +145,7 @@ function modelSourceUrl(value) {
142
145
  }
143
146
 
144
147
  function requiredText(value, field) {
145
- if (typeof value !== "string" || !value.trim()) {
148
+ if (!is.string(value) || !value.trim()) {
146
149
  throw new TypeError(`Browser model ${field} must be a nonempty string.`);
147
150
  }
148
151
  return value.trim();
@@ -196,7 +199,7 @@ function descriptorFileName(value, url) {
196
199
  }
197
200
 
198
201
  function descriptorFile(value) {
199
- if (!value || typeof value !== "object" || Array.isArray(value)) {
202
+ if (!value || !is.object(value) || is.array(value)) {
200
203
  throw new TypeError("Each browser model file descriptor must be an object.");
201
204
  }
202
205
  if (Object.hasOwn(value, "immutableUrl")) {
@@ -210,14 +213,14 @@ function descriptorFile(value) {
210
213
  name: descriptorFileName(value, url),
211
214
  url: url.href,
212
215
  };
213
- if (Number.isSafeInteger(value.bytes) && value.bytes > 0) {
216
+ if (is.safeInteger(value.bytes) && value.bytes > 0) {
214
217
  file.bytes = value.bytes;
215
218
  }
216
219
  return completeValue(file);
217
220
  }
218
221
 
219
222
  function modelDescriptor(value) {
220
- if (!value || typeof value !== "object" || Array.isArray(value)) {
223
+ if (!value || !is.object(value) || is.array(value)) {
221
224
  throw new TypeError("A browser model descriptor is required.");
222
225
  }
223
226
  const id = modelIdText(value.id);
@@ -225,7 +228,7 @@ function modelDescriptor(value) {
225
228
  .some((field) => Object.hasOwn(value, field))) {
226
229
  throw new TypeError("Browser model descriptor files must be declared in files[].");
227
230
  }
228
- if (!Array.isArray(value.files) || value.files.length === 0) {
231
+ if (!is.array(value.files) || value.files.length === 0) {
229
232
  throw new TypeError("Browser model files must be a nonempty ordered array.");
230
233
  }
231
234
  let files = value.files.map((file) => descriptorFile(file));
@@ -282,7 +285,7 @@ function highPerformanceGpuBrowser() {
282
285
  if (brands.has("Vivaldi") || /\bVivaldi\//u.test(userAgent)) {
283
286
  return { name: "Vivaldi", url: "vivaldi://flags/#force-high-performance-gpu" };
284
287
  }
285
- if (brands.has("Brave") || typeof navigatorObject?.brave?.isBrave === "function") {
288
+ if (brands.has("Brave") || is.function(navigatorObject?.brave?.isBrave)) {
286
289
  return { name: "Brave", url: "brave://flags/#force-high-performance-gpu" };
287
290
  }
288
291
  if (brands.has("Opera") || /\bOPR\//u.test(userAgent)) {
@@ -368,7 +371,7 @@ export function createBrowserModelSource(descriptor, {
368
371
  const rangeRequestUrls = new Array(metadata.files.length).fill(null);
369
372
 
370
373
  function selectedMember(memberIndex) {
371
- if (!Number.isSafeInteger(memberIndex)) {
374
+ if (!is.safeInteger(memberIndex)) {
372
375
  throw new TypeError("A browser model file index must be a safe integer.");
373
376
  }
374
377
  if (memberIndex < 0 || memberIndex >= metadata.files.length) {
@@ -381,7 +384,7 @@ export function createBrowserModelSource(descriptor, {
381
384
  const member = selectedMember(memberIndex);
382
385
  throwIfAborted(signal, "install");
383
386
  const fetchFunction = fetchImpl ?? globalThis.fetch?.bind(globalThis);
384
- if (typeof fetchFunction !== "function") {
387
+ if (!is.function(fetchFunction)) {
385
388
  throw fail("ARCANE_AI_MODEL_SOURCE_UNAVAILABLE", "Browser fetch is unavailable.");
386
389
  }
387
390
  const requestOptions = {
@@ -418,7 +421,7 @@ export function createBrowserModelSource(descriptor, {
418
421
 
419
422
  function openedDownload(download) {
420
423
  const { member, response, finalUrl } = download;
421
- if (!response.body || typeof response.body.getReader !== "function") {
424
+ if (!response.body || !is.function(response.body.getReader)) {
422
425
  throw fail("ARCANE_AI_MODEL_SOURCE_INVALID", "The model response did not provide a byte stream.");
423
426
  }
424
427
  async function cancel(reason) {
@@ -464,7 +467,7 @@ export function createBrowserModelSource(descriptor, {
464
467
  if (
465
468
  (!header || observed)
466
469
  && (!observed || (observed.start === 0 && observed.end === 0))
467
- && Number.isSafeInteger(total)
470
+ && is.safeInteger(total)
468
471
  && total > 0
469
472
  ) {
470
473
  await cancelReadableBody(download.response.body);
@@ -502,9 +505,9 @@ export function createBrowserModelSource(descriptor, {
502
505
 
503
506
  async function openRange(memberIndex, { signal, start, end, total } = {}) {
504
507
  if (
505
- !Number.isSafeInteger(start)
506
- || !Number.isSafeInteger(end)
507
- || !Number.isSafeInteger(total)
508
+ !is.safeInteger(start)
509
+ || !is.safeInteger(end)
510
+ || !is.safeInteger(total)
508
511
  || start < 0
509
512
  || end < start
510
513
  || end >= total
@@ -550,18 +553,18 @@ export function createBrowserModelSource(descriptor, {
550
553
  }
551
554
 
552
555
  async function* byteChunks(body, signal) {
553
- if (body instanceof Uint8Array || body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
556
+ if (is.instanceCheck(body, Uint8Array) || is.instanceCheck(body, ArrayBuffer) || is.arrayBufferView(body)) {
554
557
  throwIfAborted(signal, "install");
555
- yield body instanceof Uint8Array
558
+ yield is.instanceCheck(body, Uint8Array)
556
559
  ? body
557
560
  : new Uint8Array(body.buffer ?? body, body.byteOffset ?? 0, body.byteLength);
558
561
  return;
559
562
  }
560
- if (body && typeof body.stream === "function") {
563
+ if (body && is.function(body.stream)) {
561
564
  yield* byteChunks(body.stream(), signal);
562
565
  return;
563
566
  }
564
- if (body && typeof body.getReader === "function") {
567
+ if (body && is.function(body.getReader)) {
565
568
  const reader = body.getReader();
566
569
  try {
567
570
  while (true) {
@@ -588,7 +591,7 @@ async function* byteChunks(body, signal) {
588
591
  }
589
592
  const { done, value } = step;
590
593
  if (done) return;
591
- yield value instanceof Uint8Array ? value : new Uint8Array(value);
594
+ yield is.instanceCheck(value, Uint8Array) ? value : new Uint8Array(value);
592
595
  }
593
596
  } finally {
594
597
  if (signal?.aborted) await reader.cancel(signal.reason).catch(() => undefined);
@@ -633,9 +636,9 @@ function rangePartDetails(modelName, name) {
633
636
  const end = Number(match[2]);
634
637
  const total = Number(match[3]);
635
638
  if (
636
- !Number.isSafeInteger(start)
637
- || !Number.isSafeInteger(end)
638
- || !Number.isSafeInteger(total)
639
+ !is.safeInteger(start)
640
+ || !is.safeInteger(end)
641
+ || !is.safeInteger(total)
639
642
  || start < 0
640
643
  || end < start
641
644
  || total <= end
@@ -659,16 +662,16 @@ export function createDbopfsModelStore({
659
662
  estimateStorage = null,
660
663
  downloadConcurrency = DEFAULT_MODEL_DOWNLOAD_CONCURRENCY,
661
664
  } = {}) {
662
- if (!dbopfs || (typeof dbopfs !== "object" && typeof dbopfs !== "function")) {
665
+ if (!dbopfs || (!is.object(dbopfs) && !is.function(dbopfs))) {
663
666
  throw new TypeError("createDbopfsModelStore requires an existing DBOPFS instance.");
664
667
  }
665
- if (typeof dbopfs.getTableHandle !== "function") {
668
+ if (!is.function(dbopfs.getTableHandle)) {
666
669
  throw new TypeError("The DBOPFS instance is missing getTableHandle().");
667
670
  }
668
- if (dbopfs.readyPromise !== undefined && typeof dbopfs.readyPromise?.then !== "function") {
671
+ if (dbopfs.readyPromise !== undefined && !is.function(dbopfs.readyPromise?.then)) {
669
672
  throw new TypeError("The DBOPFS readyPromise must be thenable.");
670
673
  }
671
- if (estimateStorage !== null && typeof estimateStorage !== "function") {
674
+ if (estimateStorage !== null && !is.function(estimateStorage)) {
672
675
  throw new TypeError("estimateStorage must be a function or null.");
673
676
  }
674
677
  const workerLimit = downloadConcurrencyValue(downloadConcurrency);
@@ -678,7 +681,7 @@ export function createDbopfsModelStore({
678
681
  if (dbopfs.readyPromise) await dbopfs.readyPromise;
679
682
  tablePromise ||= Promise.resolve(dbopfs.getTableHandle(tableName));
680
683
  const handle = await tablePromise;
681
- if (!handle || typeof handle.getFileHandle !== "function" || typeof handle.removeEntry !== "function") {
684
+ if (!handle || !is.function(handle.getFileHandle) || !is.function(handle.removeEntry)) {
682
685
  throw fail("ARCANE_AI_STORAGE_UNAVAILABLE", "DBOPFS did not provide an OPFS table handle.");
683
686
  }
684
687
  return handle;
@@ -717,7 +720,7 @@ export function createDbopfsModelStore({
717
720
  function totalBytesValue() {
718
721
  if (memberTotals.some((value) => value === null)) return null;
719
722
  const totalBytes = memberTotals.reduce((sum, value) => sum + value, 0);
720
- return Number.isSafeInteger(totalBytes) ? totalBytes : null;
723
+ return is.safeInteger(totalBytes) ? totalBytes : null;
721
724
  }
722
725
 
723
726
  function progressRecord(now) {
@@ -782,20 +785,20 @@ export function createDbopfsModelStore({
782
785
  }
783
786
 
784
787
  function setMemberTotal(memberIndex, totalBytes) {
785
- if (!Number.isSafeInteger(totalBytes) || totalBytes <= 0) return;
788
+ if (!is.safeInteger(totalBytes) || totalBytes <= 0) return;
786
789
  if (memberTotals[memberIndex] === totalBytes) return;
787
790
  memberTotals[memberIndex] = totalBytes;
788
791
  publishSafely({ force: true });
789
792
  }
790
793
 
791
794
  function addBytes(value) {
792
- if (!Number.isSafeInteger(value) || value <= 0) return;
795
+ if (!is.safeInteger(value) || value <= 0) return;
793
796
  loadedBytes += value;
794
797
  publishSafely();
795
798
  }
796
799
 
797
800
  function discardBytes(value) {
798
- if (!Number.isSafeInteger(value) || value <= 0) return;
801
+ if (!is.safeInteger(value) || value <= 0) return;
799
802
  loadedBytes = Math.max(0, loadedBytes - value);
800
803
  samples = [];
801
804
  publishSafely({ force: true });
@@ -808,7 +811,7 @@ export function createDbopfsModelStore({
808
811
  publishSafely({ force: true });
809
812
  },
810
813
  completeMember(memberIndex, totalBytes) {
811
- if (Number.isSafeInteger(totalBytes) && totalBytes > 0) {
814
+ if (is.safeInteger(totalBytes) && totalBytes > 0) {
812
815
  memberTotals[memberIndex] = totalBytes;
813
816
  }
814
817
  completed += 1;
@@ -830,13 +833,13 @@ export function createDbopfsModelStore({
830
833
  publishSafely({ force: true });
831
834
  },
832
835
  restoreBytes(value) {
833
- if (!Number.isSafeInteger(value) || value <= 0) return;
836
+ if (!is.safeInteger(value) || value <= 0) return;
834
837
  loadedBytes += value;
835
838
  samples = [];
836
839
  publishSafely({ force: true });
837
840
  },
838
841
  restoreMember(memberIndex, totalBytes) {
839
- if (!Number.isSafeInteger(totalBytes) || totalBytes <= 0) return;
842
+ if (!is.safeInteger(totalBytes) || totalBytes <= 0) return;
840
843
  memberTotals[memberIndex] = totalBytes;
841
844
  loadedBytes += totalBytes;
842
845
  completed += 1;
@@ -971,7 +974,7 @@ export function createDbopfsModelStore({
971
974
  const expected = range.end - range.start + 1;
972
975
  for await (const chunk of byteChunks(opened.body, downloadSignal)) {
973
976
  const nextReceived = received + chunk.byteLength;
974
- if (!Number.isSafeInteger(nextReceived) || nextReceived > expected) {
977
+ if (!is.safeInteger(nextReceived) || nextReceived > expected) {
975
978
  throw fail(
976
979
  "ARCANE_AI_MODEL_DOWNLOAD_FAILED",
977
980
  "The model server returned more content than the requested HTTP byte range.",
@@ -1113,7 +1116,7 @@ export function createDbopfsModelStore({
1113
1116
 
1114
1117
  async function memberRangePartNames(modelName) {
1115
1118
  const directory = await table();
1116
- if (typeof directory.entries !== "function") return null;
1119
+ if (!is.function(directory.entries)) return null;
1117
1120
  const prefix = rangePartPrefix(modelName);
1118
1121
  const names = [];
1119
1122
  for await (const [name] of directory.entries()) {
@@ -1133,7 +1136,7 @@ export function createDbopfsModelStore({
1133
1136
  } = {}) {
1134
1137
  const existingNames = await memberRangePartNames(modelName);
1135
1138
  let names = existingNames;
1136
- if (names === null && Number.isSafeInteger(total) && total > 0) {
1139
+ if (names === null && is.safeInteger(total) && total > 0) {
1137
1140
  names = modelHttpRanges(total).map((range) => rangePartName(modelName, range));
1138
1141
  }
1139
1142
  names ??= [];
@@ -1183,7 +1186,7 @@ export function createDbopfsModelStore({
1183
1186
  if (completeMembers.length === 0) return;
1184
1187
  try {
1185
1188
  const directory = await table();
1186
- if (typeof directory.entries === "function") {
1189
+ if (is.function(directory.entries)) {
1187
1190
  const prefixes = completeMembers.map(({ modelName }) => rangePartPrefix(modelName));
1188
1191
  const entries = [];
1189
1192
  for await (const [name] of directory.entries()) {
@@ -1206,7 +1209,7 @@ export function createDbopfsModelStore({
1206
1209
  async function removeRangeParts(source, names) {
1207
1210
  const members = sourceMetadata(source).files;
1208
1211
  const directory = await table();
1209
- if (typeof directory.entries === "function") {
1212
+ if (is.function(directory.entries)) {
1210
1213
  const prefixes = names.models.map((entry) => rangePartPrefix(entry.name));
1211
1214
  const entries = [];
1212
1215
  for await (const [name] of directory.entries()) {
@@ -1219,7 +1222,7 @@ export function createDbopfsModelStore({
1219
1222
  const removed = [];
1220
1223
  for (let memberIndex = 0; memberIndex < members.length; memberIndex += 1) {
1221
1224
  const total = members[memberIndex].bytes;
1222
- if (!Number.isSafeInteger(total) || total <= 0) continue;
1225
+ if (!is.safeInteger(total) || total <= 0) continue;
1223
1226
  removed.push(await removeMemberRangeParts(
1224
1227
  names.models[memberIndex].name,
1225
1228
  { total },
@@ -1280,7 +1283,7 @@ export function createDbopfsModelStore({
1280
1283
  return null;
1281
1284
  }
1282
1285
  partFiles.push(partFile);
1283
- if (Number.isFinite(partFile.lastModified)) {
1286
+ if (is.finite(partFile.lastModified)) {
1284
1287
  lastModified = Math.max(lastModified, partFile.lastModified);
1285
1288
  }
1286
1289
  }
@@ -1310,7 +1313,7 @@ export function createDbopfsModelStore({
1310
1313
  async function storedRangeMember(member, modelName, signal) {
1311
1314
  const names = await memberRangePartNames(modelName);
1312
1315
  const availableNames = names === null ? null : new Set(names);
1313
- const declaredTotal = Number.isSafeInteger(member.bytes) && member.bytes > 0
1316
+ const declaredTotal = is.safeInteger(member.bytes) && member.bytes > 0
1314
1317
  ? member.bytes
1315
1318
  : null;
1316
1319
  if (declaredTotal !== null) {
@@ -1387,7 +1390,7 @@ export function createDbopfsModelStore({
1387
1390
  }
1388
1391
 
1389
1392
  async function install(source, { signal, onProgress = null } = {}) {
1390
- if (onProgress !== null && typeof onProgress !== "function") {
1393
+ if (onProgress !== null && !is.function(onProgress)) {
1391
1394
  throw new TypeError("Model store onProgress must be a function or null.");
1392
1395
  }
1393
1396
  const names = storageName(source);
@@ -1510,7 +1513,7 @@ export function createDbopfsModelStore({
1510
1513
  onProgress = null,
1511
1514
  offline = false,
1512
1515
  } = {}) {
1513
- if (onProgress !== null && typeof onProgress !== "function") {
1516
+ if (onProgress !== null && !is.function(onProgress)) {
1514
1517
  throw new TypeError("Model store onProgress must be a function or null.");
1515
1518
  }
1516
1519
  const total = sourceMetadata(source).files.length;
@@ -1675,7 +1678,7 @@ function responseFormat(structuredOutput) {
1675
1678
  if (structuredOutput === true || structuredOutput === "json") {
1676
1679
  return completeValue({ type: "json_object" });
1677
1680
  }
1678
- if (typeof structuredOutput !== "object" || Array.isArray(structuredOutput)) {
1681
+ if (!is.object(structuredOutput) || is.array(structuredOutput)) {
1679
1682
  throw new TypeError("structuredOutput must be false, true, \"json\", or a JSON Schema object.");
1680
1683
  }
1681
1684
  return completeValue({
@@ -1685,14 +1688,14 @@ function responseFormat(structuredOutput) {
1685
1688
  }
1686
1689
 
1687
1690
  function plainStructuralRecord(value) {
1688
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1691
+ if (!value || !is.object(value) || is.array(value)) return false;
1689
1692
  const prototype = Object.getPrototypeOf(value);
1690
1693
  return prototype === Object.prototype || prototype === null;
1691
1694
  }
1692
1695
 
1693
1696
  function validateToolMessageSchemas(value) {
1694
1697
  if (value === undefined) return;
1695
- if (!Array.isArray(value)) throw new TypeError("tools must be an array.");
1698
+ if (!is.array(value)) throw new TypeError("tools must be an array.");
1696
1699
  for (const [index, tool] of value.entries()) {
1697
1700
  const parameters = tool?.function?.parameters;
1698
1701
  const messageSchema = parameters?.properties?.message;
@@ -1705,9 +1708,9 @@ function validateToolMessageSchemas(value) {
1705
1708
  || !plainStructuralRecord(parameters.properties)
1706
1709
  || !plainStructuralRecord(messageSchema)
1707
1710
  || messageSchema.type !== "string"
1708
- || !Number.isInteger(messageSchema.minLength)
1711
+ || !is.integer(messageSchema.minLength)
1709
1712
  || messageSchema.minLength < 1
1710
- || !Array.isArray(parameters.required)
1713
+ || !is.array(parameters.required)
1711
1714
  || !parameters.required.includes("message")
1712
1715
  ) {
1713
1716
  throw fail(
@@ -1746,7 +1749,7 @@ function validateRequestMessages(messages) {
1746
1749
  }
1747
1750
  }
1748
1751
  if (message?.role === "tool") {
1749
- if (typeof message.content !== "string" || !message.content.trim()) {
1752
+ if (!is.string(message.content) || !message.content.trim()) {
1750
1753
  throw fail(
1751
1754
  "ARCANE_AI_INVALID_TOOL_MESSAGE",
1752
1755
  `messages[${String(messageIndex)}] must contain a nonblank user-facing tool result.`,
@@ -1754,7 +1757,7 @@ function validateRequestMessages(messages) {
1754
1757
  }
1755
1758
  if (
1756
1759
  !pendingToolCallIds.size
1757
- || typeof message.tool_call_id !== "string"
1760
+ || !is.string(message.tool_call_id)
1758
1761
  || !pendingToolCallIds.has(message.tool_call_id)
1759
1762
  ) {
1760
1763
  throw fail(
@@ -1790,12 +1793,12 @@ function validateStructuralRequest(request) {
1790
1793
  if (!plainStructuralRecord(request)) {
1791
1794
  throw new TypeError("The browser-WASM LLM request must be a plain object.");
1792
1795
  }
1793
- if (!Array.isArray(request.messages)) throw new TypeError("messages must be an array.");
1796
+ if (!is.array(request.messages)) throw new TypeError("messages must be an array.");
1794
1797
  validateRequestMessages(request.messages);
1795
1798
  validateToolMessageSchemas(request.tools);
1796
1799
  const parallelValues = [request.parallelToolCalls, request.parallel_tool_calls];
1797
1800
  if (parallelValues.some(function invalidParallelBrowserWasmPreference(value) {
1798
- return value !== undefined && typeof value !== "boolean";
1801
+ return value !== undefined && !is.boolean(value);
1799
1802
  })) {
1800
1803
  throw new TypeError("parallelToolCalls must be a boolean when provided.");
1801
1804
  }
@@ -1830,13 +1833,13 @@ function completionOptions(request, abortSignal, stream) {
1830
1833
  if (request[source] !== undefined) options[target] = request[source];
1831
1834
  }
1832
1835
  if (request.templateOptions !== undefined) {
1833
- if (!request.templateOptions || typeof request.templateOptions !== "object" || Array.isArray(request.templateOptions)) {
1836
+ if (!request.templateOptions || !is.object(request.templateOptions) || is.array(request.templateOptions)) {
1834
1837
  throw new TypeError("templateOptions must be a plain object when provided.");
1835
1838
  }
1836
1839
  options.chat_template_kwargs = { ...request.templateOptions };
1837
1840
  }
1838
1841
  if (request.tools !== undefined) {
1839
- if (!Array.isArray(request.tools)) throw new TypeError("tools must be an array.");
1842
+ if (!is.array(request.tools)) throw new TypeError("tools must be an array.");
1840
1843
  options.tools = request.tools;
1841
1844
  }
1842
1845
  if (request.toolChoice !== undefined) options.tool_choice = request.toolChoice;
@@ -1863,7 +1866,7 @@ function validateToolCalls(message, location = "The model response") {
1863
1866
  }
1864
1867
  if (!Object.hasOwn(message, "tool_calls")) return [];
1865
1868
  const descriptor = Object.getOwnPropertyDescriptor(message, "tool_calls");
1866
- if (!descriptor || !Object.hasOwn(descriptor, "value") || !Array.isArray(descriptor.value)) {
1869
+ if (!descriptor || !Object.hasOwn(descriptor, "value") || !is.array(descriptor.value)) {
1867
1870
  throw fail("ARCANE_AI_TOOL_CALL_INVALID", `${location} contains malformed tool calls.`);
1868
1871
  }
1869
1872
  const calls = descriptor.value;
@@ -1871,14 +1874,14 @@ function validateToolCalls(message, location = "The model response") {
1871
1874
  for (const call of calls) {
1872
1875
  if (
1873
1876
  !plainStructuralRecord(call)
1874
- || typeof call.id !== "string"
1877
+ || !is.string(call.id)
1875
1878
  || !call.id.trim()
1876
1879
  || ids.has(call.id)
1877
1880
  || call.type !== "function"
1878
1881
  || !plainStructuralRecord(call.function)
1879
- || typeof call.function.name !== "string"
1882
+ || !is.string(call.function.name)
1880
1883
  || !call.function.name.trim()
1881
- || typeof call.function.arguments !== "string"
1884
+ || !is.string(call.function.arguments)
1882
1885
  ) {
1883
1886
  throw fail("ARCANE_AI_TOOL_CALL_INVALID", `${location} contains malformed tool calls.`);
1884
1887
  }
@@ -1898,7 +1901,7 @@ function validateToolCalls(message, location = "The model response") {
1898
1901
  `${location} contains structural tool arguments that are not a JSON object.`,
1899
1902
  );
1900
1903
  }
1901
- if (typeof argumentsRecord.message !== "string" || !argumentsRecord.message.trim()) {
1904
+ if (!is.string(argumentsRecord.message) || !argumentsRecord.message.trim()) {
1902
1905
  throw fail(
1903
1906
  "ARCANE_AI_TOOL_MESSAGE_REQUIRED",
1904
1907
  `${location} structural tool arguments must include a nonempty user-facing message.`,
@@ -1940,7 +1943,7 @@ function validateCompletion(value, requestId) {
1940
1943
  if (
1941
1944
  !choicesDescriptor
1942
1945
  || !Object.hasOwn(choicesDescriptor, "value")
1943
- || !Array.isArray(choicesDescriptor.value)
1946
+ || !is.array(choicesDescriptor.value)
1944
1947
  || choicesDescriptor.value.length === 0
1945
1948
  ) {
1946
1949
  throw fail("ARCANE_AI_INVALID_PROVIDER_RESULT", "The model returned an invalid chat completion.");
@@ -1954,7 +1957,7 @@ function validateCompletion(value, requestId) {
1954
1957
  : null;
1955
1958
  if (
1956
1959
  !plainStructuralRecord(choice)
1957
- || !Number.isSafeInteger(choice.index)
1960
+ || !is.safeInteger(choice.index)
1958
1961
  || choice.index < 0
1959
1962
  || indexes.has(choice.index)
1960
1963
  || !messageDescriptor
@@ -1977,7 +1980,7 @@ function selectedCompletionToolCalls(completion) {
1977
1980
  const message = Object.hasOwn(completion ?? {}, "message")
1978
1981
  ? completion.message
1979
1982
  : completion?.choices?.[0]?.message;
1980
- return Array.isArray(message?.tool_calls) ? message.tool_calls : [];
1983
+ return is.array(message?.tool_calls) ? message.tool_calls : [];
1981
1984
  }
1982
1985
 
1983
1986
  function sameCanonicalToolCalls(left, right) {
@@ -1995,9 +1998,9 @@ function sameCompleteStreamValue(left, right, leftToRight = new Map(), rightToLe
1995
1998
  if (
1996
1999
  !left
1997
2000
  || !right
1998
- || typeof left !== "object"
1999
- || typeof right !== "object"
2000
- || Array.isArray(left) !== Array.isArray(right)
2001
+ || !is.object(left)
2002
+ || !is.object(right)
2003
+ || is.array(left) !== is.array(right)
2001
2004
  ) return false;
2002
2005
  if (leftToRight.has(left) || rightToLeft.has(right)) {
2003
2006
  return leftToRight.get(left) === right && rightToLeft.get(right) === left;
@@ -2051,9 +2054,9 @@ function isPublicStreamStructuralKey(key) {
2051
2054
  const OMITTED_PUBLIC_STREAM_DATA = Symbol("omitted-public-stream-data");
2052
2055
 
2053
2056
  function projectPublicStreamData(value, seen = new Map()) {
2054
- if (value === null || value === undefined || typeof value !== "object") return value;
2057
+ if (value === null || value === undefined || !is.object(value)) return value;
2055
2058
  if (seen.has(value)) return seen.get(value);
2056
- if (Array.isArray(value)) {
2059
+ if (is.array(value)) {
2057
2060
  const result = [];
2058
2061
  seen.set(value, result);
2059
2062
  for (const item of value) {
@@ -2067,7 +2070,7 @@ function projectPublicStreamData(value, seen = new Map()) {
2067
2070
  let sourceDataFields = 0;
2068
2071
  const descriptors = Object.getOwnPropertyDescriptors(value);
2069
2072
  for (const key of Reflect.ownKeys(descriptors)) {
2070
- if (typeof key === "symbol") continue;
2073
+ if (is.symbol(key)) continue;
2071
2074
  const descriptor = descriptors[key];
2072
2075
  if (!Object.hasOwn(descriptor, "value")) continue;
2073
2076
  sourceDataFields += 1;
@@ -2090,7 +2093,7 @@ function createCompletionAccumulator(modelId, requestId) {
2090
2093
 
2091
2094
  function choice(index) {
2092
2095
  const key = index ?? 0;
2093
- if (!Number.isSafeInteger(key) || key < 0) {
2096
+ if (!is.safeInteger(key) || key < 0) {
2094
2097
  throw fail("ARCANE_AI_INVALID_PROVIDER_RESULT", "The model returned an invalid stream choice index.");
2095
2098
  }
2096
2099
  if (!choices.has(key)) {
@@ -2114,9 +2117,9 @@ function createCompletionAccumulator(modelId, requestId) {
2114
2117
  }
2115
2118
 
2116
2119
  function push(value) {
2117
- if (!value || typeof value !== "object") return;
2120
+ if (!value || !is.object(value)) return;
2118
2121
  base = { ...base, ...value, id: requestId ?? value.id ?? base.id, choices: [] };
2119
- for (const item of Array.isArray(value.choices) ? value.choices : []) {
2122
+ for (const item of is.array(value.choices) ? value.choices : []) {
2120
2123
  const record = choice(item.index);
2121
2124
  for (const [key, fieldValue] of Object.entries(item)) {
2122
2125
  if (key !== "delta" && key !== "message") record.choiceMetadata[key] = fieldValue;
@@ -2127,14 +2130,14 @@ function createCompletionAccumulator(modelId, requestId) {
2127
2130
  if (!source) continue;
2128
2131
  for (const [key, fieldValue] of Object.entries(source)) {
2129
2132
  if (isPublicStreamStructuralKey(key)) continue;
2130
- if (key === "role" && typeof fieldValue === "string") {
2133
+ if (key === "role" && is.string(fieldValue)) {
2131
2134
  record.role = fieldValue;
2132
2135
  } else if (key === "content") {
2133
2136
  if (
2134
2137
  !replaceText
2135
2138
  && record.sawContent
2136
- && typeof record.content === "string"
2137
- && typeof fieldValue === "string"
2139
+ && is.string(record.content)
2140
+ && is.string(fieldValue)
2138
2141
  ) record.content += fieldValue;
2139
2142
  else record.content = fieldValue;
2140
2143
  record.sawContent = true;
@@ -2142,8 +2145,8 @@ function createCompletionAccumulator(modelId, requestId) {
2142
2145
  if (
2143
2146
  !replaceText
2144
2147
  && record.sawReasoning
2145
- && typeof record.reasoning === "string"
2146
- && typeof fieldValue === "string"
2148
+ && is.string(record.reasoning)
2149
+ && is.string(fieldValue)
2147
2150
  ) record.reasoning += fieldValue;
2148
2151
  else record.reasoning = fieldValue;
2149
2152
  record.sawReasoning = true;
@@ -2151,8 +2154,8 @@ function createCompletionAccumulator(modelId, requestId) {
2151
2154
  if (
2152
2155
  !replaceText
2153
2156
  && record.sawReasoningText
2154
- && typeof record.reasoningText === "string"
2155
- && typeof fieldValue === "string"
2157
+ && is.string(record.reasoningText)
2158
+ && is.string(fieldValue)
2156
2159
  ) record.reasoningText += fieldValue;
2157
2160
  else record.reasoningText = fieldValue;
2158
2161
  record.sawReasoningText = true;
@@ -2168,11 +2171,11 @@ function createCompletionAccumulator(modelId, requestId) {
2168
2171
  );
2169
2172
  }
2170
2173
  if (item.finish_reason !== undefined) record.finish_reason = item.finish_reason;
2171
- if (delta.tool_calls !== undefined && !Array.isArray(delta.tool_calls)) {
2174
+ if (delta.tool_calls !== undefined && !is.array(delta.tool_calls)) {
2172
2175
  throw fail("ARCANE_AI_INVALID_PROVIDER_RESULT", "The model returned malformed streamed tool calls.");
2173
2176
  }
2174
2177
  for (const fragment of delta.tool_calls ?? []) {
2175
- if (!Number.isSafeInteger(fragment?.index) || fragment.index < 0) {
2178
+ if (!is.safeInteger(fragment?.index) || fragment.index < 0) {
2176
2179
  throw fail("ARCANE_AI_INVALID_PROVIDER_RESULT", "A streamed tool call had no valid index.");
2177
2180
  }
2178
2181
  const tool = record.tools.get(fragment.index) ?? {
@@ -2185,25 +2188,25 @@ function createCompletionAccumulator(modelId, requestId) {
2185
2188
  arguments: "",
2186
2189
  };
2187
2190
  if (fragment.id !== undefined) {
2188
- if (typeof fragment.id !== "string" || !fragment.id || tool.id && tool.id !== fragment.id) {
2191
+ if (!is.string(fragment.id) || !fragment.id || tool.id && tool.id !== fragment.id) {
2189
2192
  tool.invalidIdentity = true;
2190
2193
  } else {
2191
2194
  tool.id = fragment.id;
2192
2195
  }
2193
2196
  }
2194
2197
  if (fragment.type !== undefined) {
2195
- if (typeof fragment.type !== "string" || !fragment.type || tool.type && tool.type !== fragment.type) {
2198
+ if (!is.string(fragment.type) || !fragment.type || tool.type && tool.type !== fragment.type) {
2196
2199
  tool.invalidIdentity = true;
2197
2200
  } else {
2198
2201
  tool.type = fragment.type;
2199
2202
  }
2200
2203
  }
2201
2204
  if (fragment.function?.name !== undefined) {
2202
- if (typeof fragment.function.name !== "string") tool.invalidIdentity = true;
2205
+ if (!is.string(fragment.function.name)) tool.invalidIdentity = true;
2203
2206
  else tool.name += fragment.function.name;
2204
2207
  }
2205
2208
  if (fragment.function?.arguments !== undefined) {
2206
- if (typeof fragment.function.arguments !== "string") tool.invalidArguments = true;
2209
+ if (!is.string(fragment.function.arguments)) tool.invalidArguments = true;
2207
2210
  else tool.arguments += fragment.function.arguments;
2208
2211
  }
2209
2212
  record.tools.set(fragment.index, tool);
@@ -2298,7 +2301,7 @@ function createCompletionAccumulator(modelId, requestId) {
2298
2301
  }
2299
2302
  if (
2300
2303
  fragmentCalls !== null
2301
- && (!Array.isArray(terminalCalls) || !sameCanonicalToolCalls(fragmentCalls, terminalCalls))
2304
+ && (!is.array(terminalCalls) || !sameCanonicalToolCalls(fragmentCalls, terminalCalls))
2302
2305
  ) {
2303
2306
  throw fail(
2304
2307
  "ARCANE_AI_TOOL_CALL_INVALID",
@@ -2423,13 +2426,13 @@ function callbackStreamHandle({ runtime, request, signal, onSettled }) {
2423
2426
  function validatedV1StreamHandle(opened, request) {
2424
2427
  if (
2425
2428
  !opened
2426
- || typeof opened !== "object"
2427
- || typeof opened[Symbol.asyncIterator] !== "function"
2428
- || typeof opened.cancel !== "function"
2429
+ || !is.object(opened)
2430
+ || !is.function(opened[Symbol.asyncIterator])
2431
+ || !is.function(opened.cancel)
2429
2432
  || !opened.result
2430
- || typeof opened.result.then !== "function"
2433
+ || !is.function(opened.result.then)
2431
2434
  ) {
2432
- if (typeof opened?.cancel === "function") {
2435
+ if (is.function(opened?.cancel)) {
2433
2436
  Promise.resolve().then(function cancelInvalidV1StreamHandle() {
2434
2437
  return opened.cancel("The v1 provider returned an invalid stream handle.");
2435
2438
  }).catch(function reportInvalidV1StreamCleanupFailure(error) {
@@ -2452,7 +2455,7 @@ function validatedV1StreamHandle(opened, request) {
2452
2455
  });
2453
2456
  throw error;
2454
2457
  }
2455
- if (!iterator || typeof iterator.next !== "function") {
2458
+ if (!iterator || !is.function(iterator.next)) {
2456
2459
  Promise.resolve().then(function cancelInvalidV1Iterator() {
2457
2460
  return opened.cancel("The v1 provider returned an invalid stream iterator.");
2458
2461
  }).catch(function reportInvalidV1IteratorCleanupFailure(error) {
@@ -2536,7 +2539,7 @@ function validatedV1StreamHandle(opened, request) {
2536
2539
  });
2537
2540
  },
2538
2541
  async return(value) {
2539
- if (typeof iterator.return === "function") {
2542
+ if (is.function(iterator.return)) {
2540
2543
  Promise.resolve().then(function returnUnderlyingV1Stream() {
2541
2544
  return iterator.return(value);
2542
2545
  }).catch(function reportUnderlyingV1StreamReturnFailure(error) {
@@ -2563,7 +2566,7 @@ function validatedV1StreamHandle(opened, request) {
2563
2566
 
2564
2567
  function positiveLoadInteger(value, field, fallback) {
2565
2568
  const resolved = value === undefined ? fallback : value;
2566
- if (!Number.isSafeInteger(resolved) || resolved < 1) {
2569
+ if (!is.safeInteger(resolved) || resolved < 1) {
2567
2570
  throw new RangeError(`${field} must be a positive safe integer.`);
2568
2571
  }
2569
2572
  return resolved;
@@ -2571,19 +2574,19 @@ function positiveLoadInteger(value, field, fallback) {
2571
2574
 
2572
2575
  function optionalLoadBoolean(value, field) {
2573
2576
  if (value === undefined) return undefined;
2574
- if (typeof value !== "boolean") throw new TypeError(`${field} must be a boolean when provided.`);
2577
+ if (!is.boolean(value)) throw new TypeError(`${field} must be a boolean when provided.`);
2575
2578
  return value;
2576
2579
  }
2577
2580
 
2578
2581
  function optionalLoadText(value, field) {
2579
2582
  if (value === undefined) return undefined;
2580
- if (typeof value !== "string") throw new TypeError(`${field} must be a string when provided.`);
2583
+ if (!is.string(value)) throw new TypeError(`${field} must be a string when provided.`);
2581
2584
  return value;
2582
2585
  }
2583
2586
 
2584
2587
  function optionalTemplateDefaults(value) {
2585
2588
  if (value === undefined) return undefined;
2586
- if (!value || typeof value !== "object" || Array.isArray(value)) {
2589
+ if (!value || !is.object(value) || is.array(value)) {
2587
2590
  throw new TypeError("templateDefaults must be a plain object when provided.");
2588
2591
  }
2589
2592
  return { ...value };
@@ -2591,7 +2594,7 @@ function optionalTemplateDefaults(value) {
2591
2594
 
2592
2595
  function measuredRuntimeCapabilities(runtimeCapabilities) {
2593
2596
  const measuredDeviceMemory = Number(globalThis.navigator?.deviceMemory);
2594
- const deviceMemory = Number.isFinite(measuredDeviceMemory) && measuredDeviceMemory > 0
2597
+ const deviceMemory = is.finite(measuredDeviceMemory) && measuredDeviceMemory > 0
2595
2598
  ? measuredDeviceMemory
2596
2599
  : null;
2597
2600
  return completeValue({ ...runtimeCapabilities, deviceMemory });
@@ -2599,7 +2602,7 @@ function measuredRuntimeCapabilities(runtimeCapabilities) {
2599
2602
 
2600
2603
  function capabilityLoadPlan(runtimeCapabilities, defaults, options = {}) {
2601
2604
  const configured = { ...defaults, ...options };
2602
- const hardwareConcurrency = Number.isSafeInteger(runtimeCapabilities.hardwareConcurrency)
2605
+ const hardwareConcurrency = is.safeInteger(runtimeCapabilities.hardwareConcurrency)
2603
2606
  && runtimeCapabilities.hardwareConcurrency > 0
2604
2607
  ? runtimeCapabilities.hardwareConcurrency
2605
2608
  : 1;
@@ -2666,8 +2669,8 @@ function sameLoadPlan(left, right) {
2666
2669
  }
2667
2670
 
2668
2671
  function stableModelFailure(error) {
2669
- const code = typeof error?.code === "string" ? error.code : "";
2670
- const message = typeof error?.message === "string" ? error.message : "";
2672
+ const code = is.string(error?.code) ? error.code : "";
2673
+ const message = is.string(error?.message) ? error.message : "";
2671
2674
  if (code === "ARCANE_AI_MODEL_SHARD_TOO_LARGE") {
2672
2675
  return completeValue({ code });
2673
2676
  }
@@ -2755,7 +2758,7 @@ function capabilityPolicy(
2755
2758
  }
2756
2759
 
2757
2760
  function providerModelSources(sources) {
2758
- if (!Array.isArray(sources) || sources.length === 0) {
2761
+ if (!is.array(sources) || sources.length === 0) {
2759
2762
  throw new TypeError("createBrowserWasmLlmProvider requires a nonempty sources array.");
2760
2763
  }
2761
2764
  const ids = new Set();
@@ -2921,8 +2924,8 @@ export function createBrowserWasmLlmProvider({
2921
2924
  : "unloaded";
2922
2925
  errorState = state === "error"
2923
2926
  ? completeValue({
2924
- code: typeof error?.code === "string" ? error.code : "ARCANE_AI_RUNTIME_FAILED",
2925
- message: typeof error?.message === "string"
2927
+ code: is.string(error?.code) ? error.code : "ARCANE_AI_RUNTIME_FAILED",
2928
+ message: is.string(error?.message)
2926
2929
  ? error.message
2927
2930
  : "The browser-WASM runtime failed.",
2928
2931
  })
@@ -2985,7 +2988,7 @@ export function createBrowserWasmLlmProvider({
2985
2988
  if (
2986
2989
  options.onProgress !== undefined
2987
2990
  && options.onProgress !== null
2988
- && typeof options.onProgress !== "function"
2991
+ && !is.function(options.onProgress)
2989
2992
  ) {
2990
2993
  throw new TypeError("Browser-WASM load onProgress must be a function or null.");
2991
2994
  }
@@ -2993,7 +2996,7 @@ export function createBrowserWasmLlmProvider({
2993
2996
  const linked = linkAbortSignal(externalSignal);
2994
2997
  const signal = linked.controller.signal;
2995
2998
  const generation = ++lifecycleGeneration;
2996
- const reportProgress = typeof context.reportProgress === "function"
2999
+ const reportProgress = is.function(context.reportProgress)
2997
3000
  ? context.reportProgress
2998
3001
  : options.onProgress ?? null;
2999
3002
  const progressStartedAt = Date.now();
@@ -3057,7 +3060,7 @@ export function createBrowserWasmLlmProvider({
3057
3060
  heartbeat: false,
3058
3061
  });
3059
3062
  const modelFiles = admitted.files.map((file, index) => (
3060
- typeof globalThis.File === "function"
3063
+ is.function(globalThis.File)
3061
3064
  ? new File([file], members[index].name, { type: "application/octet-stream" })
3062
3065
  : file
3063
3066
  ));
@@ -3282,8 +3285,8 @@ function assertV1LlmAdapterSelection(selection, providerId, modelIds, role) {
3282
3285
  }
3283
3286
  if (
3284
3287
  !selection
3285
- || typeof selection !== "object"
3286
- || Array.isArray(selection)
3288
+ || !is.object(selection)
3289
+ || is.array(selection)
3287
3290
  || selection.providerId !== providerId
3288
3291
  || !modelIds.has(selection.modelId)
3289
3292
  || selection.localOnly !== true
@@ -3301,7 +3304,7 @@ function assertV1LlmAdapterSelection(selection, providerId, modelIds, role) {
3301
3304
  * The adapter is local-only, never falls back, and never executes tool calls.
3302
3305
  */
3303
3306
  export function adaptV1LlmProvider(provider) {
3304
- if (!provider || typeof provider !== "object" || Array.isArray(provider)) {
3307
+ if (!provider || !is.object(provider) || is.array(provider)) {
3305
3308
  throw new TypeError("adaptV1LlmProvider requires an Arcane browser-WASM LLM provider.");
3306
3309
  }
3307
3310
  const existing = V1_LLM_PROVIDER_ADAPTERS.get(provider);
@@ -3315,7 +3318,7 @@ export function adaptV1LlmProvider(provider) {
3315
3318
  const requiredMethods = ["capabilities", "status", "load", "unload", "chat", "stream", "dispose"];
3316
3319
  const methods = Object.create(null);
3317
3320
  for (const method of requiredMethods) {
3318
- if (typeof provider[method] !== "function") {
3321
+ if (!is.function(provider[method])) {
3319
3322
  throw new TypeError(`The browser-WASM LLM provider is missing ${method}().`);
3320
3323
  }
3321
3324
  methods[method] = provider[method].bind(provider);
@@ -3325,10 +3328,10 @@ export function adaptV1LlmProvider(provider) {
3325
3328
  }
3326
3329
 
3327
3330
  const fallbackCatalog = completeValue([model]);
3328
- const initialCatalog = typeof provider.catalog === "function"
3331
+ const initialCatalog = is.function(provider.catalog)
3329
3332
  ? provider.catalog()
3330
3333
  : fallbackCatalog;
3331
- if (!Array.isArray(initialCatalog) || initialCatalog.length === 0) {
3334
+ if (!is.array(initialCatalog) || initialCatalog.length === 0) {
3332
3335
  throw new TypeError("The browser-WASM provider catalog must be a nonempty array.");
3333
3336
  }
3334
3337
  const catalogModels = new Map();
@@ -3371,10 +3374,10 @@ export function adaptV1LlmProvider(provider) {
3371
3374
  const value = methods.status();
3372
3375
  if (
3373
3376
  !value
3374
- || typeof value !== "object"
3375
- || typeof value.state !== "string"
3376
- || typeof value.loaded !== "boolean"
3377
- || typeof value.busy !== "boolean"
3377
+ || !is.object(value)
3378
+ || !is.string(value.state)
3379
+ || !is.boolean(value.loaded)
3380
+ || !is.boolean(value.busy)
3378
3381
  ) {
3379
3382
  throw fail("ARCANE_AI_PROVIDER_STATUS_INVALID", "The browser-WASM provider returned an invalid status.");
3380
3383
  }
@@ -3394,7 +3397,7 @@ export function adaptV1LlmProvider(provider) {
3394
3397
  role: "llm",
3395
3398
  id: providerId,
3396
3399
  localOnly: true,
3397
- catalog: () => typeof provider.catalog === "function"
3400
+ catalog: () => is.function(provider.catalog)
3398
3401
  ? provider.catalog()
3399
3402
  : fallbackCatalog,
3400
3403
  async inspect(selection, { role = "llm", signal = null } = {}) {
@@ -3435,7 +3438,7 @@ export function adaptV1LlmProvider(provider) {
3435
3438
  } = {}) {
3436
3439
  assertSelection(selection, role);
3437
3440
  throwIfAborted(signal, "load");
3438
- if (progress !== null && typeof progress !== "function") {
3441
+ if (progress !== null && !is.function(progress)) {
3439
3442
  throw new TypeError("The provider/2 progress sink must be a function or null.");
3440
3443
  }
3441
3444
  await methods.load({