arcane-os 0.5.17 → 0.5.19

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 (186) hide show
  1. package/CHANGELOG.md +34 -1
  2. package/README.md +9 -9
  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 +46 -43
  6. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +158 -135
  7. package/browser-runtime/ai/browser-wasm.mjs +6 -3
  8. package/browser-runtime/ai/browser-wllama-runtime.mjs +104 -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 +5 -1
  17. package/docs/architecture.md +1 -1
  18. package/docs/publishing.md +10 -4
  19. package/docs/reference/ai/browser-speech.md +198 -5
  20. package/docs/reference/ai/browser-wasm.md +29 -6
  21. package/docs/reference/availability-and-normalization.md +40 -0
  22. package/docs/reference/inventory/package-api.json +1 -1
  23. package/docs/reference/inventory/runtime-components.json +1 -1
  24. package/docs/reference/inventory/runtime-modules.json +20 -24
  25. package/docs/reference/protocols.md +1 -1
  26. package/docs/reference/runtime-modules.md +125 -35
  27. package/docs/reference/sdk-api.md +23 -7
  28. package/examples/wasm-ai-demo/index.html +1 -0
  29. package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
  30. package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
  31. package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
  32. package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
  33. package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
  34. package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
  35. package/node_modules/strong-type/README.md +42 -24
  36. package/node_modules/strong-type/benchmark/core.js +324 -0
  37. package/node_modules/strong-type/index.js +207 -82
  38. package/node_modules/strong-type/package.json +14 -6
  39. package/package.json +3 -3
  40. package/runtime/arcane/components/app-bar.html +5 -2
  41. package/runtime/arcane/components/assistant-panel.html +5 -2
  42. package/runtime/arcane/components/chart.html +15 -12
  43. package/runtime/arcane/components/chat.html +170 -148
  44. package/runtime/arcane/components/dashboard-config.html +6 -3
  45. package/runtime/arcane/components/data-view.html +4 -1
  46. package/runtime/arcane/components/directory-picker.html +5 -2
  47. package/runtime/arcane/components/document-inspector.html +6 -3
  48. package/runtime/arcane/components/file-drop.html +5 -2
  49. package/runtime/arcane/components/file-inspector.html +9 -6
  50. package/runtime/arcane/components/file-manager.html +16 -13
  51. package/runtime/arcane/components/local-ai-status.html +5 -2
  52. package/runtime/arcane/components/markdown-document.html +14 -11
  53. package/runtime/arcane/components/markdown-editor.html +4 -1
  54. package/runtime/arcane/components/modal.html +4 -1
  55. package/runtime/arcane/components/output-panel.html +6 -3
  56. package/runtime/arcane/components/preferences-form.html +4 -1
  57. package/runtime/arcane/components/record-timeline.html +8 -5
  58. package/runtime/arcane/components/relationship-board.html +9 -6
  59. package/runtime/arcane/components/source-code-viewer.html +12 -9
  60. package/runtime/arcane/components/source-explanation.html +8 -5
  61. package/runtime/arcane/components/speech.html +24 -21
  62. package/runtime/arcane/components/summary-strip.html +4 -1
  63. package/runtime/arcane/components/task-progress.html +6 -3
  64. package/runtime/arcane/components/terminal-workspace.html +4 -1
  65. package/runtime/arcane/components/voice-transcription.html +11 -8
  66. package/runtime/arcane/components/web-navigator.html +5 -2
  67. package/runtime/arcane/entities/ApiModelRecord.js +5 -2
  68. package/runtime/arcane/entities/Calculation.js +5 -2
  69. package/runtime/arcane/entities/Chat.js +33 -33
  70. package/runtime/arcane/entities/CommunicationMessage.js +4 -1
  71. package/runtime/arcane/entities/CommunicationThread.js +4 -1
  72. package/runtime/arcane/entities/File.js +1 -1
  73. package/runtime/arcane/entities/Image.js +8 -5
  74. package/runtime/arcane/entities/IntentEnvelope.js +20 -17
  75. package/runtime/arcane/entities/Preference.js +9 -6
  76. package/runtime/arcane/entities/Theme.js +5 -2
  77. package/runtime/arcane/entities/User.js +11 -11
  78. package/runtime/arcane/entities/Weather.js +5 -2
  79. package/runtime/arcane/modules/AI.js +491 -165
  80. package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
  81. package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
  82. package/runtime/arcane/modules/AIProviderRuntime.js +88 -85
  83. package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
  84. package/runtime/arcane/modules/AIRuntimeState.js +29 -26
  85. package/runtime/arcane/modules/AnsiText.js +4 -1
  86. package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
  87. package/runtime/arcane/modules/AppDataScope.js +8 -5
  88. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
  89. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
  90. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
  91. package/runtime/arcane/modules/AsyncBoundary.js +13 -10
  92. package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
  93. package/runtime/arcane/modules/CalculatorEngine.js +5 -2
  94. package/runtime/arcane/modules/ChatRecords.js +18 -15
  95. package/runtime/arcane/modules/CommunicationAppController.js +9 -6
  96. package/runtime/arcane/modules/CommunicationHub.js +9 -6
  97. package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
  98. package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
  99. package/runtime/arcane/modules/ComponentContracts.js +56 -53
  100. package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
  101. package/runtime/arcane/modules/ConversationActionItems.js +15 -12
  102. package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
  103. package/runtime/arcane/modules/ConversationTimebox.js +28 -25
  104. package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
  105. package/runtime/arcane/modules/DBLS.js +7 -4
  106. package/runtime/arcane/modules/DBOPFS.js +7 -7
  107. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
  108. package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
  109. package/runtime/arcane/modules/DirectoryPicker.js +8 -5
  110. package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
  111. package/runtime/arcane/modules/DocumentNavigation.js +7 -4
  112. package/runtime/arcane/modules/Errors.js +28 -25
  113. package/runtime/arcane/modules/HTMLImport.js +7 -4
  114. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
  115. package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
  116. package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
  117. package/runtime/arcane/modules/MD.js +1 -1
  118. package/runtime/arcane/modules/Mail.js +46 -43
  119. package/runtime/arcane/modules/MailOutbox.mjs +48 -45
  120. package/runtime/arcane/modules/MailTransport.mjs +29 -26
  121. package/runtime/arcane/modules/MemoryRecords.js +7 -4
  122. package/runtime/arcane/modules/MessageAdvisory.js +6 -3
  123. package/runtime/arcane/modules/ModelDefinition.js +7 -4
  124. package/runtime/arcane/modules/Ollama.js +6 -3
  125. package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
  126. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
  127. package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
  128. package/runtime/arcane/modules/PreferenceStore.js +20 -17
  129. package/runtime/arcane/modules/PreparedSpeech.js +485 -0
  130. package/runtime/arcane/modules/Questionnaire.js +6 -3
  131. package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
  132. package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
  133. package/runtime/arcane/modules/RecordReviewStore.js +12 -9
  134. package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
  135. package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
  136. package/runtime/arcane/modules/ScreenCapture.js +20 -17
  137. package/runtime/arcane/modules/SpeechPlayback.js +29 -26
  138. package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
  139. package/runtime/arcane/modules/SystemAppearance.js +5 -2
  140. package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
  141. package/runtime/arcane/modules/TerminalClient.js +14 -11
  142. package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
  143. package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
  144. package/runtime/arcane/modules/ThemeManager.js +5 -2
  145. package/runtime/arcane/modules/TimeGuard.js +1 -1
  146. package/runtime/arcane/modules/ToolCallRouter.js +11 -8
  147. package/runtime/arcane/modules/WaitForComponent.js +19 -16
  148. package/runtime/arcane/modules/YouTubeMedia.js +4 -1
  149. package/runtime/strong-type/index.js +1276 -352
  150. package/runtime/strong-type/package.json +69 -45
  151. package/src/app-descriptor.mjs +17 -14
  152. package/src/application-tests.mjs +4 -1
  153. package/src/cli/main.mjs +8 -5
  154. package/src/constants.mjs +4 -1
  155. package/src/dev-server.mjs +8 -5
  156. package/src/doctor.mjs +5 -2
  157. package/src/dom-event-instrumentation.mjs +24 -20
  158. package/src/errors.mjs +7 -3
  159. package/src/event-manager.mjs +111 -108
  160. package/src/event-queue.mjs +8 -5
  161. package/src/events.mjs +12 -9
  162. package/src/import-map.mjs +35 -27
  163. package/src/installed-sdk-runtime.mjs +4 -1
  164. package/src/integrated-provider-loader.mjs +9 -6
  165. package/src/mail-credentials.mjs +16 -13
  166. package/src/mail-server.mjs +53 -50
  167. package/src/mail.mjs +18 -15
  168. package/src/native-plan.mjs +12 -9
  169. package/src/native-provider-loader.mjs +7 -4
  170. package/src/packager/core.mjs +18 -15
  171. package/src/process.mjs +6 -3
  172. package/src/release-bundle.mjs +14 -11
  173. package/src/runtime.mjs +4 -1
  174. package/src/scaffold.mjs +9 -6
  175. package/src/sdk-browser-runtime.mjs +4 -1
  176. package/src/source-server.mjs +17 -14
  177. package/src/targets/index.mjs +5 -2
  178. package/src/templates/workspace-template.mjs +13 -6
  179. package/src/testing-loader.mjs +7 -4
  180. package/src/testing.mjs +10 -7
  181. package/src/toolchain.mjs +13 -10
  182. package/src/update-check.mjs +9 -6
  183. package/src/workspace-operation-lock.mjs +14 -11
  184. package/src/workspace-runtime.mjs +4 -1
  185. package/src/workspace.mjs +17 -14
  186. package/runtime/arcane/modules/AIResponseLength.js +0 -32
@@ -1,3 +1,4 @@
1
+ import Is from "../dependencies/strong-type/index.js";
1
2
  import {
2
3
  completeValueText,
3
4
  createModelController,
@@ -11,6 +12,8 @@ import {
11
12
  } from "./browser-wasm-llm-provider.mjs";
12
13
  import { BROWSER_WASM_RUNTIME_AUTHORITY } from "./browser-wllama-runtime.mjs";
13
14
 
15
+ const is = new Is(false);
16
+
14
17
  /**
15
18
  * Creates the public Arcane browser-local AI API module. The SDK owns lifecycle,
16
19
  * cache, streaming, complete response projection, and structural tool visibility; applications
@@ -24,12 +27,12 @@ function createArcaneAI({
24
27
  } = {}) {
25
28
  const selected = llm ?? provider;
26
29
  if (!selected) throw new TypeError("createArcaneAI requires an llm provider.");
27
- if (selected instanceof ModelController && security !== undefined) {
30
+ if (is.instanceCheck(selected, ModelController) && security !== undefined) {
28
31
  throw new TypeError(
29
32
  "createArcaneAI security must be configured when the existing ModelController is created.",
30
33
  );
31
34
  }
32
- const controller = selected instanceof ModelController
35
+ const controller = is.instanceCheck(selected, ModelController)
33
36
  ? selected
34
37
  : createModelController({ provider: selected, loadPolicy, security });
35
38
  const api = {
@@ -46,7 +49,7 @@ function createArcaneAI({
46
49
  };
47
50
 
48
51
  async function createChatSession(options = {}) {
49
- if (!options || typeof options !== "object" || Array.isArray(options)) {
52
+ if (!options || !is.object(options) || is.array(options)) {
50
53
  throw new TypeError("createChatSession options must be a plain object.");
51
54
  }
52
55
  if (Object.getPrototypeOf(options) !== Object.prototype) {
@@ -1,6 +1,9 @@
1
+ import Is from "../dependencies/strong-type/index.js";
1
2
  import { arcaneLogging } from '../logging.mjs';
2
3
  import { Wllama } from "./wllama/index.mjs";
3
4
 
5
+ const is = new Is(false);
6
+
4
7
  const completeValue = (value) => value;
5
8
 
6
9
  const MODULE_URL = new URL("./wllama/index.mjs", import.meta.url).href;
@@ -63,15 +66,15 @@ function runtimeCapabilitySnapshot(evidence) {
63
66
  const navigatorObject = globalThis.navigator;
64
67
  const webgpuOperational = evidence?.state === "ready" && evidence?.webgpu?.observed === true;
65
68
  return completeValue({
66
- webAssembly: typeof globalThis.WebAssembly === "object",
67
- opfs: typeof navigatorObject?.storage?.getDirectory === "function",
69
+ webAssembly: is.object(globalThis.WebAssembly),
70
+ opfs: is.function(navigatorObject?.storage?.getDirectory),
68
71
  webgpu: webgpuOperational,
69
72
  webgpuApiPresent: Boolean(navigatorObject?.gpu),
70
73
  webgpuOperational,
71
74
  webgpuEvidenceProtocol: RUNTIME_EVIDENCE_PROTOCOL,
72
75
  crossOriginIsolated: globalThis.crossOriginIsolated === true,
73
76
  secureContext: globalThis.isSecureContext === true,
74
- hardwareConcurrency: Number.isSafeInteger(navigatorObject?.hardwareConcurrency)
77
+ hardwareConcurrency: is.safeInteger(navigatorObject?.hardwareConcurrency)
75
78
  ? navigatorObject.hardwareConcurrency
76
79
  : null,
77
80
  });
@@ -80,7 +83,7 @@ function runtimeCapabilitySnapshot(evidence) {
80
83
  function normalizePositiveInteger(value, fallback) {
81
84
  if (value === undefined || value === null) return fallback;
82
85
  const number = Number(value);
83
- if (!Number.isSafeInteger(number) || number < 1) {
86
+ if (!is.safeInteger(number) || number < 1) {
84
87
  throw new RangeError("Expected a positive safe integer.");
85
88
  }
86
89
  return number;
@@ -91,6 +94,59 @@ function createEvidenceLogger(logger) {
91
94
  let offload = null;
92
95
  let invalid = false;
93
96
  let completionCapture = null;
97
+ let loadProgress = null;
98
+ let tensorCount = null;
99
+
100
+ function observeLoadLine(line) {
101
+ if (!loadProgress) return;
102
+ let stage;
103
+ let message;
104
+ const metadata = line.match(/^llama_model_loader: loaded meta data with \d+ key-value pairs and (\d+) tensors from /u);
105
+ const layers = line.match(GPU_OFFLOAD_PATTERN);
106
+ if (line.startsWith('Loading "wllama.wasm" from ')) {
107
+ stage = "runtime";
108
+ message = "Loading the WebAssembly runtime";
109
+ } else if (line === "Calling wllamaStart...") {
110
+ stage = "backend";
111
+ message = "Starting the inference engine";
112
+ } else if (line === "Loading model...") {
113
+ stage = "metadata";
114
+ message = "Reading model metadata";
115
+ } else if (WEBGPU_ADAPTER_PATTERN.test(line)) {
116
+ stage = "gpu";
117
+ message = "Graphics device initialized; preparing the model";
118
+ } else if (metadata) {
119
+ tensorCount = Number(metadata[1]);
120
+ stage = "metadata";
121
+ message = `Model metadata read: ${tensorCount} tensors`;
122
+ } else if (line.startsWith("load_tensors: loading model tensors,")) {
123
+ stage = "weights";
124
+ message = tensorCount === null
125
+ ? "Loading model weights"
126
+ : `Loading model weights for ${tensorCount} tensors`;
127
+ } else if (layers) {
128
+ // Upstream reports layer assignment before the weight reads finish.
129
+ stage = "weights";
130
+ message = `Loading model weights; ${layers[1]} of ${layers[2]} layers assigned to the GPU`;
131
+ } else if (line === "llama_context: constructing llama_context") {
132
+ stage = "context";
133
+ message = "Preparing the inference context";
134
+ } else if (/^[^:]+:\s+graph (?:nodes|splits)\s+=\s+\d+/u.test(line)) {
135
+ stage = "graph";
136
+ message = "Preparing the inference graph";
137
+ } else if (/^cmn\s+common_init_:\s+warming up the model with an empty run\b/u.test(line)) {
138
+ stage = "warmup";
139
+ message = "Warming up the model with an empty run";
140
+ }
141
+ if (stage) {
142
+ loadProgress({
143
+ phase: "initialize",
144
+ stage,
145
+ message,
146
+ total: null,
147
+ });
148
+ }
149
+ }
94
150
 
95
151
  function same(left, right) {
96
152
  return JSON.stringify(left) === JSON.stringify(right);
@@ -116,6 +172,7 @@ function createEvidenceLogger(logger) {
116
172
  observeCompletionLine(level, value);
117
173
  const line = String(value).trim();
118
174
  if (!line) return;
175
+ observeLoadLine(line);
119
176
  const adapterMatch = line.match(WEBGPU_ADAPTER_PATTERN);
120
177
  if (adapterMatch) {
121
178
  const next = completeValue({
@@ -141,8 +198,10 @@ function createEvidenceLogger(logger) {
141
198
  }
142
199
 
143
200
  function observe(level, args) {
201
+ // Activity without a new stage updates its age without repainting the UI.
202
+ loadProgress?.(null);
144
203
  for (const value of args) {
145
- if (typeof value !== "string") continue;
204
+ if (!is.string(value)) continue;
146
205
  for (const line of value.split(/\r?\n/u)) observeLine(level, line);
147
206
  }
148
207
  }
@@ -151,12 +210,19 @@ function createEvidenceLogger(logger) {
151
210
  for (const level of ["debug", "log", "warn", "error"]) {
152
211
  wrapped[level] = (...args) => {
153
212
  observe(level, args);
154
- if (typeof logger?.[level] === "function") logger[level](...args);
213
+ if (is.function(logger?.[level])) logger[level](...args);
155
214
  };
156
215
  }
157
216
 
158
217
  return completeValue({
159
218
  logger: completeValue(wrapped),
219
+ beginLoadProgress(report) {
220
+ loadProgress = report;
221
+ tensorCount = null;
222
+ return function releaseLoadProgress() {
223
+ loadProgress = null;
224
+ };
225
+ },
160
226
  beginCompletionCapture() {
161
227
  if (completionCapture) {
162
228
  throw runtimeFailure(
@@ -214,7 +280,7 @@ function createStructuredStreamCapture() {
214
280
 
215
281
  function observe(value) {
216
282
  chunks += 1;
217
- if (!value || typeof value !== "object" || !Array.isArray(value.choices)) {
283
+ if (!value || !is.object(value) || !is.array(value.choices)) {
218
284
  invalid = true;
219
285
  return;
220
286
  }
@@ -227,8 +293,8 @@ function createStructuredStreamCapture() {
227
293
  if (
228
294
  choice?.index !== 0
229
295
  || !delta
230
- || typeof delta !== "object"
231
- || Array.isArray(delta)
296
+ || !is.object(delta)
297
+ || is.array(delta)
232
298
  || (delta.role !== undefined && delta.role !== "assistant")
233
299
  || (choice.finish_reason !== undefined && choice.finish_reason !== null)
234
300
  || delta.tool_calls !== undefined
@@ -239,7 +305,7 @@ function createStructuredStreamCapture() {
239
305
  return;
240
306
  }
241
307
  if (delta.content === undefined || delta.content === null) return;
242
- if (typeof delta.content !== "string") {
308
+ if (!is.string(delta.content)) {
243
309
  invalid = true;
244
310
  return;
245
311
  }
@@ -296,7 +362,7 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
296
362
  }
297
363
 
298
364
  function cancellationError(reason, fallback = "The Wllama operation was cancelled.") {
299
- return reason instanceof Error ? reason : new Error(reason ? String(reason) : fallback);
365
+ return is.error(reason) ? reason : new Error(reason ? String(reason) : fallback);
300
366
  }
301
367
 
302
368
  function trackOperation(rawOperation) {
@@ -380,7 +446,7 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
380
446
  });
381
447
  next.setCompat(null);
382
448
  for (const method of ["arcaneLoadModel", "arcaneTelemetry", "arcaneTerminate"]) {
383
- if (typeof next[method] !== "function") {
449
+ if (!is.function(next[method])) {
384
450
  throw new Error(`The packaged Wllama projection is missing public ${method}().`);
385
451
  }
386
452
  }
@@ -401,10 +467,10 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
401
467
  }
402
468
 
403
469
  async function load(files, options = {}) {
404
- if (!Array.isArray(files) || files.length === 0) {
470
+ if (!is.array(files) || files.length === 0) {
405
471
  throw new TypeError("Wllama load() requires at least one File or Blob.");
406
472
  }
407
- if (typeof globalThis.WebAssembly !== "object") {
473
+ if (!is.object(globalThis.WebAssembly)) {
408
474
  throw new Error("WebAssembly is unavailable in this browser.");
409
475
  }
410
476
  if (!globalThis.navigator?.gpu) {
@@ -435,25 +501,25 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
435
501
  loadOptions.n_ubatch = normalizePositiveInteger(options.microBatchTokens, 128);
436
502
  }
437
503
  if (options.reasoning !== undefined) {
438
- if (typeof options.reasoning !== "boolean") {
504
+ if (!is.boolean(options.reasoning)) {
439
505
  throw new TypeError("reasoning must be a boolean when provided.");
440
506
  }
441
507
  loadOptions.reasoning = options.reasoning;
442
508
  }
443
509
  if (options.chatTemplate !== undefined) {
444
- if (typeof options.chatTemplate !== "string") {
510
+ if (!is.string(options.chatTemplate)) {
445
511
  throw new TypeError("chatTemplate must be a string when provided.");
446
512
  }
447
513
  loadOptions.chat_template = options.chatTemplate;
448
514
  }
449
515
  if (options.jinja !== undefined) {
450
- if (typeof options.jinja !== "boolean") {
516
+ if (!is.boolean(options.jinja)) {
451
517
  throw new TypeError("jinja must be a boolean when provided.");
452
518
  }
453
519
  loadOptions.jinja = options.jinja;
454
520
  }
455
521
  if (options.templateDefaults !== undefined) {
456
- if (!options.templateDefaults || typeof options.templateDefaults !== "object" || Array.isArray(options.templateDefaults)) {
522
+ if (!options.templateDefaults || !is.object(options.templateDefaults) || is.array(options.templateDefaults)) {
457
523
  throw new TypeError("templateDefaults must be a plain object when provided.");
458
524
  }
459
525
  loadOptions.default_template_kwargs = { ...options.templateDefaults };
@@ -466,6 +532,18 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
466
532
  cleanup: null,
467
533
  });
468
534
  const loadController = new AbortController();
535
+ let progressFailure = null;
536
+ const releaseLoadProgress = sessionObservers.get(next).beginLoadProgress(
537
+ function reportRuntimeLoadProgress(progress) {
538
+ if (loadController.signal.aborted || !is.function(options.onProgress)) return;
539
+ try {
540
+ options.onProgress(progress);
541
+ } catch (error) {
542
+ progressFailure = error;
543
+ loadController.abort(error);
544
+ }
545
+ },
546
+ );
469
547
  const loadOperation = Promise.resolve().then(() => (
470
548
  next.arcaneLoadModel(files, loadOptions, loadController.signal)
471
549
  ));
@@ -481,8 +559,9 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
481
559
  else signal?.addEventListener?.("abort", onAbort, { once: true });
482
560
  try {
483
561
  await loadOperation;
562
+ if (progressFailure) throw progressFailure;
484
563
  if (pending?.engine !== next) throw new Error("Wllama load was cancelled.");
485
- if (typeof next.isModelLoaded !== "function" || next.isModelLoaded() !== true) {
564
+ if (!is.function(next.isModelLoaded) || next.isModelLoaded() !== true) {
486
565
  throw runtimeFailure(
487
566
  "ARCANE_AI_LOAD_FAILED",
488
567
  "Wllama did not confirm a successfully loaded model.",
@@ -493,6 +572,7 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
493
572
  engine = next;
494
573
  publishEvidence({ state: "ready", webgpu, cancellation: null, cleanup: null });
495
574
  } catch (error) {
575
+ releaseLoadProgress();
496
576
  let cleanupFailure = null;
497
577
  try {
498
578
  await exitSession(next);
@@ -510,6 +590,7 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
510
590
  if (cleanupFailure) throw cleanupFailure;
511
591
  throw error;
512
592
  } finally {
593
+ releaseLoadProgress();
513
594
  signal?.removeEventListener?.("abort", onAbort);
514
595
  }
515
596
 
@@ -553,13 +634,13 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
553
634
  lastObservedOperational: evidenceState.webgpu?.lastObservedOperational === true,
554
635
  },
555
636
  failure: completeValue({
556
- code: typeof error?.code === "string" ? error.code : "ARCANE_AI_RUNTIME_FAILED",
637
+ code: is.string(error?.code) ? error.code : "ARCANE_AI_RUNTIME_FAILED",
557
638
  }),
558
639
  });
559
640
  }
560
641
 
561
642
  async function inference(options, onData = null) {
562
- if (!options || typeof options !== "object" || Array.isArray(options)) {
643
+ if (!options || !is.object(options) || is.array(options)) {
563
644
  throw new TypeError("Wllama inference options must be an object.");
564
645
  }
565
646
  if (Object.hasOwn(options, "signal")) {
@@ -629,7 +710,7 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
629
710
  }
630
711
 
631
712
  function stream(options, onData) {
632
- if (typeof onData !== "function") {
713
+ if (!is.function(onData)) {
633
714
  throw new TypeError("Wllama stream() requires an onData callback.");
634
715
  }
635
716
  return inference({ ...options, stream: true }, onData);
@@ -653,7 +734,7 @@ export function createPackagedWllamaRuntime({ logger = arcaneLogging } = {}) {
653
734
 
654
735
  async function probe({ args = ["-o", "ADD"] } = {}) {
655
736
  if (engine) throw new Error("The no-model Wllama probe cannot run while a model is loaded.");
656
- if (!Array.isArray(args) || args.some((value) => typeof value !== "string")) {
737
+ if (!is.array(args) || args.some((value) => !is.string(value))) {
657
738
  throw new TypeError("Wllama probe args must be an array of strings.");
658
739
  }
659
740
  const temporary = newEngine();