arcane-os 0.1.2 → 0.2.0

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 (52) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/NOTICE +5 -3
  3. package/README.md +73 -24
  4. package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +67 -18
  5. package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +16 -5
  6. package/browser-runtime/ai/browser-kokoro-worker.mjs +3 -0
  7. package/browser-runtime/ai/browser-speech-artifacts.mjs +1108 -0
  8. package/browser-runtime/ai/browser-speech-providers.mjs +475 -0
  9. package/browser-runtime/ai/browser-speech.mjs +9 -0
  10. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +1537 -167
  11. package/browser-runtime/ai/browser-wasm.mjs +46 -1
  12. package/browser-runtime/ai/browser-whisper-worker.mjs +3 -0
  13. package/browser-runtime/ai/browser-wllama-runtime.mjs +677 -132
  14. package/browser-runtime/ai/model-controller.mjs +138 -12
  15. package/browser-runtime/ai/speech-worker-client.mjs +207 -0
  16. package/browser-runtime/ai/speech-worker-runtime.mjs +516 -0
  17. package/browser-runtime/ai/wllama/index.mjs +389 -0
  18. package/docs/architecture.md +132 -22
  19. package/docs/reference/README.md +1 -1
  20. package/docs/reference/ai/browser-wasm.md +101 -42
  21. package/docs/reference/availability-and-normalization.md +19 -5
  22. package/docs/reference/behavioral-testing.md +18 -5
  23. package/docs/reference/cli.md +2 -2
  24. package/docs/reference/inventory/package-api.json +14 -14
  25. package/docs/reference/protocols.md +4 -4
  26. package/docs/reference/sdk-api.md +68 -38
  27. package/docs/work-amplification.md +8 -4
  28. package/package.json +7 -3
  29. package/runtime/ARCANE_RUNTIME_RELEASE.json +50 -20
  30. package/runtime/arcane/components/chat.html +280 -62
  31. package/runtime/arcane/components/speech.html +1113 -265
  32. package/runtime/arcane/entities/Chat.js +246 -43
  33. package/runtime/arcane/modules/AI.js +713 -162
  34. package/runtime/arcane/modules/AIProviderRuntime.js +2289 -0
  35. package/runtime/arcane/modules/AIRuntimeState.js +872 -0
  36. package/runtime/arcane/modules/ConfiguredAIChatSession.js +293 -27
  37. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +682 -0
  38. package/runtime/arcane/modules/DocumentLexicalSearch.js +292 -0
  39. package/runtime/arcane/modules/PersistentAIChatSession.js +268 -0
  40. package/runtime/arcane/modules/StaticDocumentCatalog.js +25 -206
  41. package/schemas/arcane-lock.schema.json +6 -4
  42. package/src/cli/main.mjs +14 -2
  43. package/src/constants.mjs +1 -1
  44. package/src/dev-server.mjs +244 -13
  45. package/src/import-map.mjs +59 -1
  46. package/src/packager/core.mjs +2 -2
  47. package/src/runtime.mjs +14 -4
  48. package/src/sdk-browser-runtime.mjs +28 -75
  49. package/src/templates/workspace-template.mjs +4 -4
  50. package/src/toolchain.mjs +3 -0
  51. package/src/workspace-runtime.mjs +1 -1
  52. package/src/workspace.mjs +1 -1
@@ -2,6 +2,16 @@ import { Wllama } from "./wllama/index.mjs";
2
2
 
3
3
  const MODULE_URL = new URL("./wllama/index.mjs", import.meta.url).href;
4
4
  const WASM_URL = new URL("./wllama/wllama.wasm", import.meta.url).href;
5
+ const WEBGPU_EVIDENCE_PROTOCOL = "arcane-wllama-webgpu-evidence/1";
6
+ const RUNTIME_EVIDENCE_PROTOCOL = "arcane-wllama-runtime-evidence/1";
7
+ const FULL_GPU_LAYERS = 99_999;
8
+ const WEBGPU_ADAPTER_PATTERN = /^ggml_webgpu: adapter_info: vendor_id: (\d+) \| vendor: (.*?) \| architecture: (.*?) \| device_id: (\d+) \| name: (.*?) \| device_desc: (.*)$/u;
9
+ const GPU_OFFLOAD_PATTERN = /^[^:]+: offloaded (\d+)\/(\d+) layers to GPU$/u;
10
+ const PEG_NATIVE_OUTPUT_PREFIX = "common_chat_peg_parse: unparsed peg-native output: ";
11
+ const PEG_NATIVE_FINAL_PREFIX = "<|channel|>final <|constrain|>content<|message|>";
12
+ const PEG_NATIVE_FAILURE = "The model produced output that does not match the expected peg-native format";
13
+ const MAX_RECOVERED_COMPLETION_CHARACTERS = 1_048_576;
14
+ const MAX_RECOVERED_COMPLETION_LINES = 16_384;
5
15
 
6
16
  function deepFreeze(value) {
7
17
  if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
@@ -10,8 +20,19 @@ function deepFreeze(value) {
10
20
  }
11
21
 
12
22
  export const BROWSER_WASM_RUNTIME_AUTHORITY = deepFreeze({
13
- protocol: "arcane-ai-browser-wasm/1",
23
+ protocol: "arcane-ai-browser-wasm/2",
14
24
  provider: "wllama",
25
+ executionPolicy: {
26
+ webgpuRequired: true,
27
+ cpuFallback: false,
28
+ operationalEvidence: RUNTIME_EVIDENCE_PROTOCOL,
29
+ navigatorPresenceIsOperationalEvidence: false,
30
+ cancellation: "abortSignal-plus-llama-cancel-acknowledgement",
31
+ cleanup: "worker-termination-only",
32
+ nativeUnloadClaimed: false,
33
+ physicalVramReclamationClaimed: false,
34
+ telemetryThreatModel: "authenticated-module-closure-and-frozen-prototypes-not-hostile-platform-global-attestation",
35
+ },
15
36
  package: {
16
37
  name: "@wllama/wllama",
17
38
  version: "3.6.0",
@@ -40,9 +61,17 @@ export const BROWSER_WASM_RUNTIME_AUTHORITY = deepFreeze({
40
61
  module: {
41
62
  path: "ai/wllama/index.mjs",
42
63
  url: MODULE_URL,
43
- bytes: 373_519,
44
- sha256: "4637e42d636010493a9b274fbbe70bfd8120365da726b1d9e589d85ca84a00d6",
64
+ bytes: 392_852,
65
+ sha256: "b119a7cdffabc8541dce283381d18ada4027c0560728aac1fe45bdd30cdac8e2",
45
66
  mediaType: "text/javascript",
67
+ projection: {
68
+ protocol: WEBGPU_EVIDENCE_PROTOCOL,
69
+ tool: "tools/project-wllama-webgpu-runtime.mjs",
70
+ sourcePath: "node_modules/@wllama/wllama/esm/index.js",
71
+ sourceBytes: 373_519,
72
+ sourceSha256: "4637e42d636010493a9b274fbbe70bfd8120365da726b1d9e589d85ca84a00d6",
73
+ wasmModified: false,
74
+ },
46
75
  },
47
76
  wasm: {
48
77
  path: "ai/wllama/wllama.wasm",
@@ -59,12 +88,23 @@ export const BROWSER_WASM_RUNTIME_AUTHORITY = deepFreeze({
59
88
  },
60
89
  });
61
90
 
62
- function runtimeCapabilitySnapshot() {
91
+ function runtimeFailure(code, message, cause) {
92
+ const error = new Error(message, cause === undefined ? undefined : { cause });
93
+ error.name = "ArcaneWllamaRuntimeError";
94
+ error.code = code;
95
+ return error;
96
+ }
97
+
98
+ function runtimeCapabilitySnapshot(evidence) {
63
99
  const navigatorObject = globalThis.navigator;
100
+ const webgpuOperational = evidence?.state === "ready" && evidence?.webgpu?.observed === true;
64
101
  return Object.freeze({
65
102
  webAssembly: typeof globalThis.WebAssembly === "object",
66
103
  opfs: typeof navigatorObject?.storage?.getDirectory === "function",
67
- webgpu: Boolean(navigatorObject?.gpu),
104
+ webgpu: webgpuOperational,
105
+ webgpuApiPresent: Boolean(navigatorObject?.gpu),
106
+ webgpuOperational,
107
+ webgpuEvidenceProtocol: RUNTIME_EVIDENCE_PROTOCOL,
68
108
  crossOriginIsolated: globalThis.crossOriginIsolated === true,
69
109
  secureContext: globalThis.isSecureContext === true,
70
110
  hardwareConcurrency: Number.isSafeInteger(navigatorObject?.hardwareConcurrency)
@@ -82,16 +122,308 @@ function normalizePositiveInteger(value, fallback, { maximum = Number.MAX_SAFE_I
82
122
  return number;
83
123
  }
84
124
 
125
+ function createEvidenceLogger(logger) {
126
+ let adapter = null;
127
+ let offload = null;
128
+ let invalid = false;
129
+ let completionCapture = null;
130
+
131
+ function same(left, right) {
132
+ return JSON.stringify(left) === JSON.stringify(right);
133
+ }
134
+
135
+ function observeCompletionLine(level, value) {
136
+ if (!completionCapture) return;
137
+ const line = String(value).replace(/\r$/u, "");
138
+ if (!completionCapture.started) {
139
+ if (level !== "warn" || !line.startsWith(PEG_NATIVE_OUTPUT_PREFIX)) return;
140
+ completionCapture.started = true;
141
+ completionCapture.lines.push(line.slice(PEG_NATIVE_OUTPUT_PREFIX.length));
142
+ } else if (level === "error" && line.trim() === PEG_NATIVE_FAILURE) {
143
+ completionCapture.complete = true;
144
+ return;
145
+ } else if (!completionCapture.complete) {
146
+ if (level !== "log") completionCapture.invalid = true;
147
+ completionCapture.lines.push(line);
148
+ }
149
+ completionCapture.characters += line.length + 1;
150
+ if (
151
+ completionCapture.characters > MAX_RECOVERED_COMPLETION_CHARACTERS
152
+ || completionCapture.lines.length > MAX_RECOVERED_COMPLETION_LINES
153
+ ) {
154
+ completionCapture.invalid = true;
155
+ completionCapture.lines.length = 0;
156
+ }
157
+ }
158
+
159
+ function observeLine(level, value) {
160
+ observeCompletionLine(level, value);
161
+ const line = String(value).trim();
162
+ if (!line) return;
163
+ const adapterMatch = line.match(WEBGPU_ADAPTER_PATTERN);
164
+ if (adapterMatch) {
165
+ const next = Object.freeze({
166
+ vendorId: Number(adapterMatch[1]),
167
+ vendor: adapterMatch[2],
168
+ architecture: adapterMatch[3],
169
+ deviceId: Number(adapterMatch[4]),
170
+ name: adapterMatch[5],
171
+ description: adapterMatch[6],
172
+ });
173
+ if (adapter && !same(adapter, next)) invalid = true;
174
+ else adapter = next;
175
+ }
176
+ const offloadMatch = line.match(GPU_OFFLOAD_PATTERN);
177
+ if (offloadMatch) {
178
+ const next = Object.freeze({
179
+ layers: Number(offloadMatch[1]),
180
+ totalLayers: Number(offloadMatch[2]),
181
+ });
182
+ if (offload && !same(offload, next)) invalid = true;
183
+ else offload = next;
184
+ }
185
+ }
186
+
187
+ function observe(level, args) {
188
+ for (const value of args) {
189
+ if (typeof value !== "string") continue;
190
+ for (const line of value.split(/\r?\n/u)) observeLine(level, line);
191
+ }
192
+ }
193
+
194
+ const wrapped = {};
195
+ for (const level of ["debug", "log", "warn", "error"]) {
196
+ wrapped[level] = (...args) => {
197
+ observe(level, args);
198
+ if (typeof logger?.[level] === "function") logger[level](...args);
199
+ };
200
+ }
201
+
202
+ return Object.freeze({
203
+ logger: Object.freeze(wrapped),
204
+ beginCompletionCapture() {
205
+ if (completionCapture) {
206
+ throw runtimeFailure(
207
+ "ARCANE_AI_RUNTIME_BUSY",
208
+ "A Wllama completion capture is already active.",
209
+ );
210
+ }
211
+ const capture = {
212
+ started: false,
213
+ complete: false,
214
+ invalid: false,
215
+ characters: 0,
216
+ lines: [],
217
+ };
218
+ completionCapture = capture;
219
+
220
+ function release() {
221
+ if (completionCapture === capture) completionCapture = null;
222
+ }
223
+
224
+ return Object.freeze({
225
+ recover(error, { aborted = false } = {}) {
226
+ release();
227
+ const stack = String(error?.stack ?? "");
228
+ if (
229
+ aborted
230
+ || error?.name !== "Error"
231
+ || error?.message !== "Invalid magic number"
232
+ || !stack.includes("glueDeserialize")
233
+ || !stack.includes("ProxyToWorker")
234
+ || !capture.started
235
+ || !capture.complete
236
+ || capture.invalid
237
+ ) return null;
238
+
239
+ const raw = capture.lines.join("\n");
240
+ if (!raw.startsWith(PEG_NATIVE_FINAL_PREFIX)) return null;
241
+ const content = raw.slice(PEG_NATIVE_FINAL_PREFIX.length);
242
+ if (!content.trim() || content.includes("<|")) return null;
243
+ return content;
244
+ },
245
+ release,
246
+ });
247
+ },
248
+ snapshot() {
249
+ return deepFreeze({ adapter, offload, invalid });
250
+ },
251
+ });
252
+ }
253
+
254
+ function createStructuredStreamCapture() {
255
+ let content = "";
256
+ let invalid = false;
257
+ let sawContent = false;
258
+ let chunks = 0;
259
+
260
+ function observe(value) {
261
+ chunks += 1;
262
+ if (!value || typeof value !== "object" || !Array.isArray(value.choices)) {
263
+ invalid = true;
264
+ return;
265
+ }
266
+ if (value.choices.length !== 1) {
267
+ invalid = true;
268
+ return;
269
+ }
270
+ const choice = value.choices[0];
271
+ const delta = choice?.delta;
272
+ if (
273
+ choice?.index !== 0
274
+ || !delta
275
+ || typeof delta !== "object"
276
+ || Array.isArray(delta)
277
+ || (delta.role !== undefined && delta.role !== "assistant")
278
+ || (choice.finish_reason !== undefined && choice.finish_reason !== null)
279
+ || delta.tool_calls !== undefined
280
+ || delta.function_call !== undefined
281
+ || delta.reasoning_content !== undefined
282
+ ) {
283
+ invalid = true;
284
+ return;
285
+ }
286
+ if (delta.content === undefined || delta.content === null) return;
287
+ if (typeof delta.content !== "string") {
288
+ invalid = true;
289
+ return;
290
+ }
291
+ content += delta.content;
292
+ sawContent = true;
293
+ if (content.length > MAX_RECOVERED_COMPLETION_CHARACTERS) invalid = true;
294
+ }
295
+
296
+ return Object.freeze({
297
+ observe,
298
+ matches(value) {
299
+ return chunks > 0
300
+ && !invalid
301
+ && sawContent
302
+ && content.length > 0
303
+ && content === value;
304
+ },
305
+ });
306
+ }
307
+
308
+ function validCounter(value) {
309
+ return Number.isSafeInteger(value) && value >= 0;
310
+ }
311
+
312
+ function verifyProjectedTelemetry(value) {
313
+ const worker = value?.worker;
314
+ if (
315
+ value?.protocol !== WEBGPU_EVIDENCE_PROTOCOL
316
+ || worker?.protocol !== WEBGPU_EVIDENCE_PROTOCOL
317
+ || !validCounter(worker.bufferCount)
318
+ || !validCounter(worker.bufferBytes)
319
+ || !validCounter(worker.queueSubmissions)
320
+ || !validCounter(worker.commandBuffers)
321
+ || !validCounter(worker.queueFenceRequests)
322
+ || !validCounter(worker.queueFenceCompletions)
323
+ || worker.queueFenceCompletions > worker.queueFenceRequests
324
+ || worker.invalid === true
325
+ ) {
326
+ throw runtimeFailure(
327
+ "ARCANE_AI_WEBGPU_EVIDENCE_INVALID",
328
+ "The projected Wllama WebGPU evidence was missing or invalid.",
329
+ );
330
+ }
331
+ return value;
332
+ }
333
+
334
+ function adapterEvidenceConflicts(workerAdapter, loggedAdapter) {
335
+ if (!workerAdapter || !loggedAdapter) return false;
336
+ function loggedText(value) {
337
+ return typeof value === "string" ? value.slice(0, 256) : "";
338
+ }
339
+ return workerAdapter.vendor !== loggedText(loggedAdapter.vendor)
340
+ || workerAdapter.architecture !== loggedText(loggedAdapter.architecture)
341
+ || workerAdapter.name !== loggedText(loggedAdapter.name)
342
+ || workerAdapter.description !== loggedText(loggedAdapter.description);
343
+ }
344
+
345
+ function admittedLoadEvidence(logs, projected) {
346
+ const worker = projected.worker;
347
+ const adapter = worker.adapter;
348
+ const offload = logs.offload;
349
+ const failures = [];
350
+ if (logs.invalid) failures.push("conflicting-log-evidence");
351
+ if (!adapter) failures.push("adapter-selection");
352
+ else if (adapterEvidenceConflicts(adapter, logs.adapter)) failures.push("adapter-log-conflict");
353
+ if (!offload) failures.push("offload-log");
354
+ else if (
355
+ !Number.isSafeInteger(offload.layers)
356
+ || !Number.isSafeInteger(offload.totalLayers)
357
+ || offload.totalLayers < 1
358
+ ) failures.push("offload-shape");
359
+ else if (offload.layers !== offload.totalLayers) {
360
+ failures.push(`full-offload(${offload.layers}/${offload.totalLayers})`);
361
+ }
362
+ if (worker.bufferCount < 1) failures.push(`buffer-count(${worker.bufferCount})`);
363
+ if (worker.bufferBytes < 1) failures.push(`buffer-bytes(${worker.bufferBytes})`);
364
+ if (worker.queueSubmissions < 1) failures.push(`queue-submissions(${worker.queueSubmissions})`);
365
+ if (worker.commandBuffers < 1) failures.push(`command-buffers(${worker.commandBuffers})`);
366
+ if (worker.queueFenceRequests < 1) failures.push(`fence-requests(${worker.queueFenceRequests})`);
367
+ if (worker.queueFenceCompletions < worker.queueFenceRequests) {
368
+ failures.push(`fence-completions(${worker.queueFenceCompletions}/${worker.queueFenceRequests})`);
369
+ }
370
+ if (failures.length > 0) {
371
+ throw runtimeFailure(
372
+ "ARCANE_AI_WEBGPU_REQUIRED",
373
+ `Wllama WebGPU admission failed: ${failures.join(", ")}.`,
374
+ );
375
+ }
376
+ return deepFreeze({
377
+ observed: true,
378
+ adapter,
379
+ offload: { ...offload, allReportedModelLayers: true },
380
+ buffers: { count: worker.bufferCount, descriptorBytes: worker.bufferBytes },
381
+ queue: {
382
+ submissions: worker.queueSubmissions,
383
+ commandBuffers: worker.commandBuffers,
384
+ fenceRequests: worker.queueFenceRequests,
385
+ fenceCompletions: worker.queueFenceCompletions,
386
+ },
387
+ cpuUnusedClaimed: false,
388
+ gpuOnlyClaimed: false,
389
+ });
390
+ }
391
+
392
+ function initialEvidence() {
393
+ return deepFreeze({
394
+ protocol: RUNTIME_EVIDENCE_PROTOCOL,
395
+ state: "unloaded",
396
+ webgpu: {
397
+ observed: false,
398
+ apiPresent: Boolean(globalThis.navigator?.gpu),
399
+ },
400
+ cancellation: null,
401
+ cleanup: null,
402
+ });
403
+ }
404
+
85
405
  /**
86
- * Creates one packaged Wllama session. This factory has no network or browser
87
- * side effects until load() is called. Runtime URLs are fixed relative to this
88
- * module so the same bytes work from npm and a materialized /arcane/sdk tree.
406
+ * Creates one packaged, WebGPU-required Wllama session. This factory has no
407
+ * network or browser side effects until load() is called. Runtime URLs are
408
+ * fixed relative to this module for npm and materialized /arcane/sdk trees.
89
409
  */
90
410
  export function createPackagedWllamaRuntime({ logger = console } = {}) {
91
411
  let engine = null;
92
412
  let pending = null;
413
+ let inferenceActive = false;
414
+ let evidenceState = initialEvidence();
93
415
  const trackedOperations = new Set();
94
416
  const sessionExitPromises = new WeakMap();
417
+ const sessionObservers = new WeakMap();
418
+
419
+ function publishEvidence(update) {
420
+ evidenceState = deepFreeze({
421
+ ...evidenceState,
422
+ ...update,
423
+ protocol: RUNTIME_EVIDENCE_PROTOCOL,
424
+ });
425
+ return evidenceState;
426
+ }
95
427
 
96
428
  function cancellationError(reason, fallback = "The Wllama operation was cancelled.") {
97
429
  return reason instanceof Error ? reason : new Error(reason ? String(reason) : fallback);
@@ -104,11 +436,11 @@ export function createPackagedWllamaRuntime({ logger = console } = {}) {
104
436
  const cancellation = new Promise((_, reject) => {
105
437
  rejectCancellation = reject;
106
438
  });
107
- let cancelled = false;
439
+ let locallySuppressed = false;
108
440
  const record = {
109
441
  cancel(reason) {
110
- if (cancelled) return false;
111
- cancelled = true;
442
+ if (locallySuppressed) return false;
443
+ locallySuppressed = true;
112
444
  rejectCancellation(cancellationError(reason));
113
445
  return true;
114
446
  },
@@ -118,102 +450,48 @@ export function createPackagedWllamaRuntime({ logger = console } = {}) {
118
450
  trackedOperations.delete(record);
119
451
  });
120
452
  result.catch(() => undefined);
121
- return Object.freeze({ raw, result, cancel: record.cancel });
453
+ return Object.freeze({
454
+ raw,
455
+ result,
456
+ cancel: record.cancel,
457
+ locallySuppressed: () => locallySuppressed,
458
+ });
122
459
  }
123
460
 
124
461
  function cancelTrackedOperations(reason) {
125
462
  for (const operation of [...trackedOperations]) operation.cancel(reason);
126
463
  }
127
464
 
128
- function requireConfigurableDataProperty(object, key, label) {
129
- const descriptor = Object.getOwnPropertyDescriptor(object, key);
130
- if (!descriptor?.configurable || !("value" in descriptor) || descriptor.get || descriptor.set) {
131
- throw new Error(`Pinned Wllama ${label} is not a configurable data property.`);
132
- }
133
- return descriptor;
134
- }
135
-
136
- function guardLoadingSession(session) {
137
- const sessionDescriptor = requireConfigurableDataProperty(session, "proxy", "proxy");
138
- let proxy = sessionDescriptor.value;
139
- let cancelled = null;
140
- const workerGuards = new Map();
141
-
142
- function guardProxy(nextProxy) {
143
- if (!nextProxy || workerGuards.has(nextProxy)) return;
144
- const descriptor = requireConfigurableDataProperty(nextProxy, "worker", "proxy worker");
145
- let worker = descriptor.value;
146
- Object.defineProperty(nextProxy, "worker", {
147
- enumerable: descriptor.enumerable,
148
- configurable: true,
149
- get: () => worker,
150
- set(value) {
151
- if (value && cancelled) {
152
- value.terminate?.();
153
- throw cancelled;
154
- }
155
- worker = value;
156
- },
157
- });
158
- workerGuards.set(nextProxy, () => {
159
- Object.defineProperty(nextProxy, "worker", { ...descriptor, value: worker });
160
- });
161
- if (worker && cancelled) {
162
- worker.terminate?.();
163
- throw cancelled;
164
- }
465
+ function recordCleanup(snapshot) {
466
+ let cleanup = snapshot?.cleanup ?? null;
467
+ if (!["worker-terminated", "no-worker-observed-at-exit"].includes(cleanup?.kind)) {
468
+ throw runtimeFailure(
469
+ "ARCANE_AI_WORKER_TERMINATION_UNCONFIRMED",
470
+ "Wllama cleanup did not confirm Worker termination.",
471
+ );
165
472
  }
166
-
167
- if (proxy) guardProxy(proxy);
168
- Object.defineProperty(session, "proxy", {
169
- enumerable: sessionDescriptor.enumerable,
170
- configurable: true,
171
- get: () => proxy,
172
- set(value) {
173
- if (value && cancelled) {
174
- value.worker?.terminate?.();
175
- throw cancelled;
176
- }
177
- proxy = value;
178
- if (value) guardProxy(value);
179
- },
180
- });
181
-
182
- return Object.freeze({
183
- cancel(reason) {
184
- cancelled ||= cancellationError(reason, "The Wllama model load was cancelled.");
185
- const current = proxy;
186
- current?.worker?.terminate?.();
187
- try {
188
- current?.abort?.(cancelled.message, "");
189
- } catch {
190
- // The tracked operation gate remains the stable cancellation result.
191
- }
192
- return cancelled;
193
- },
194
- restore() {
195
- for (const restoreWorker of workerGuards.values()) restoreWorker();
196
- workerGuards.clear();
197
- Object.defineProperty(session, "proxy", { ...sessionDescriptor, value: proxy });
473
+ publishEvidence({
474
+ state: "unloaded",
475
+ webgpu: {
476
+ ...evidenceState.webgpu,
477
+ observed: false,
478
+ lastObservedOperational: evidenceState.webgpu?.observed === true
479
+ || evidenceState.webgpu?.lastObservedOperational === true,
198
480
  },
481
+ cleanup,
199
482
  });
483
+ return cleanup;
200
484
  }
201
485
 
202
- function exitSession(session, reason) {
203
- if (!session) return Promise.resolve(false);
486
+ function exitSession(session) {
487
+ if (!session) return Promise.resolve(null);
204
488
  let exitPromise = sessionExitPromises.get(session);
205
489
  if (!exitPromise) {
206
- const attempt = Promise.resolve().then(() => {
207
- try {
208
- session.proxy?.abort?.(cancellationError(reason).message, "");
209
- } catch {
210
- // Public session.exit() still owns Worker termination.
211
- }
212
- return session.exit();
213
- }).then(() => true);
490
+ const attempt = Promise.resolve().then(() => session.arcaneTerminate()).then((snapshot) => {
491
+ recordCleanup(snapshot);
492
+ return snapshot;
493
+ });
214
494
  exitPromise = attempt.catch((error) => {
215
- // A failed cleanup attempt is not proof that this session is closed.
216
- // Evict only this attempt so a later exit() can retry the same handle.
217
495
  if (sessionExitPromises.get(session) === exitPromise) {
218
496
  sessionExitPromises.delete(session);
219
497
  }
@@ -225,20 +503,31 @@ export function createPackagedWllamaRuntime({ logger = console } = {}) {
225
503
  }
226
504
 
227
505
  function newEngine() {
506
+ const observer = createEvidenceLogger(logger);
228
507
  const next = new Wllama({ default: WASM_URL }, {
229
- logger,
508
+ logger: observer.logger,
230
509
  allowOffline: true,
231
510
  });
232
511
  next.setCompat(null);
512
+ for (const method of ["arcaneLoadModel", "arcaneTelemetry", "arcaneTerminate"]) {
513
+ if (typeof next[method] !== "function") {
514
+ throw new Error(`The packaged Wllama projection is missing public ${method}().`);
515
+ }
516
+ }
233
517
  const resources = next.getWorkerResources();
234
518
  if (resources.compat !== false || resources.wasmPath !== WASM_URL || resources.jsPath) {
235
519
  throw new Error("The packaged Wllama resource projection was not exact.");
236
520
  }
521
+ sessionObservers.set(next, observer);
237
522
  return next;
238
523
  }
239
524
 
240
525
  function capabilities() {
241
- return runtimeCapabilitySnapshot();
526
+ return runtimeCapabilitySnapshot(evidenceState);
527
+ }
528
+
529
+ function evidence() {
530
+ return evidenceState;
242
531
  }
243
532
 
244
533
  async function load(files, options = {}) {
@@ -248,21 +537,29 @@ export function createPackagedWllamaRuntime({ logger = console } = {}) {
248
537
  if (typeof globalThis.WebAssembly !== "object") {
249
538
  throw new Error("WebAssembly is unavailable in this browser.");
250
539
  }
540
+ if (!globalThis.navigator?.gpu) {
541
+ throw runtimeFailure(
542
+ "ARCANE_AI_WEBGPU_REQUIRED",
543
+ "A WebGPU API is required, but navigator presence alone will not establish operational execution.",
544
+ );
545
+ }
251
546
  if (engine || pending) {
252
547
  throw new Error("The packaged Wllama runtime is already loaded or loading.");
253
548
  }
254
549
 
255
- // Wllama defaults to a CDN compatibility runtime. Arcane never admits it.
256
550
  const next = newEngine();
257
-
258
551
  const threads = normalizePositiveInteger(options.threads, 1, { maximum: 64 });
259
552
  const contextTokens = normalizePositiveInteger(options.contextTokens, 4_096, {
260
553
  maximum: 1_048_576,
261
554
  });
555
+ if (options.gpuLayers !== undefined && options.gpuLayers !== FULL_GPU_LAYERS) {
556
+ throw new RangeError(`WebGPU-required Wllama must request exactly ${FULL_GPU_LAYERS} GPU layers.`);
557
+ }
558
+ const gpuLayers = FULL_GPU_LAYERS;
262
559
  const loadOptions = {
263
560
  n_threads: threads,
264
561
  n_ctx: contextTokens,
265
- n_gpu_layers: Number.isSafeInteger(options.gpuLayers) ? options.gpuLayers : 0,
562
+ n_gpu_layers: gpuLayers,
266
563
  };
267
564
  if (options.batchTokens !== undefined) {
268
565
  loadOptions.n_batch = normalizePositiveInteger(options.batchTokens, 512, {
@@ -275,34 +572,56 @@ export function createPackagedWllamaRuntime({ logger = console } = {}) {
275
572
  });
276
573
  }
277
574
 
278
- const loadGuard = guardLoadingSession(next);
279
- const operation = trackOperation(Promise.resolve().then(() => next.loadModel(files, loadOptions)));
280
- operation.raw.finally(loadGuard.restore).catch(() => undefined);
575
+ publishEvidence({
576
+ state: "loading",
577
+ webgpu: { observed: false, apiPresent: true },
578
+ cancellation: null,
579
+ cleanup: null,
580
+ });
581
+ const loadController = new AbortController();
582
+ const loadOperation = Promise.resolve().then(() => (
583
+ next.arcaneLoadModel(files, loadOptions, loadController.signal)
584
+ ));
585
+ loadOperation.catch(() => undefined);
281
586
  const cancel = (reason) => {
282
- const error = loadGuard.cancel(reason);
283
- operation.cancel(error);
587
+ const error = cancellationError(reason, "The Wllama model load was cancelled.");
588
+ loadController.abort(error);
284
589
  };
285
- pending = Object.freeze({
286
- engine: next,
287
- cancel,
288
- });
590
+ pending = Object.freeze({ engine: next, cancel });
289
591
  const signal = options.signal ?? null;
290
592
  const onAbort = () => cancel(signal.reason);
291
593
  if (signal?.aborted) onAbort();
292
594
  else signal?.addEventListener?.("abort", onAbort, { once: true });
293
595
  try {
294
- await operation.result;
596
+ await loadOperation;
295
597
  if (pending?.engine !== next) throw new Error("Wllama load was cancelled.");
598
+ if (typeof next.isModelLoaded !== "function" || next.isModelLoaded() !== true) {
599
+ throw runtimeFailure(
600
+ "ARCANE_AI_LOAD_FAILED",
601
+ "Wllama did not confirm a successfully loaded model.",
602
+ );
603
+ }
604
+ const projected = verifyProjectedTelemetry(await next.arcaneTelemetry());
605
+ const webgpu = admittedLoadEvidence(sessionObservers.get(next).snapshot(), projected);
296
606
  pending = null;
297
607
  engine = next;
608
+ publishEvidence({ state: "ready", webgpu, cancellation: null, cleanup: null });
298
609
  } catch (error) {
610
+ let cleanupFailure = null;
299
611
  try {
300
- await exitSession(next, error);
612
+ await exitSession(next);
301
613
  if (pending?.engine === next) pending = null;
302
- } catch {
303
- // Preserve the exact pending session for a later runtime.exit() retry.
304
- // The original load failure remains the stable public rejection.
614
+ } catch (cleanupError) {
615
+ cleanupFailure = cleanupError?.code === "ARCANE_AI_WORKER_TERMINATION_UNCONFIRMED"
616
+ ? cleanupError
617
+ : runtimeFailure(
618
+ "ARCANE_AI_WORKER_TERMINATION_UNCONFIRMED",
619
+ "Wllama model load failed and Worker termination could not be proved.",
620
+ cleanupError,
621
+ );
622
+ // Preserve the pending handle if Worker termination could not be proved.
305
623
  }
624
+ if (cleanupFailure) throw cleanupFailure;
306
625
  throw error;
307
626
  } finally {
308
627
  signal?.removeEventListener?.("abort", onAbort);
@@ -312,32 +631,261 @@ export function createPackagedWllamaRuntime({ logger = console } = {}) {
312
631
  loaded: true,
313
632
  contextTokens,
314
633
  threads,
634
+ gpuLayers,
635
+ evidence: evidenceState,
315
636
  metadata: engine.getModelMetadata?.() ?? null,
316
637
  });
317
638
  }
318
639
 
319
640
  function assertLoaded() {
320
- if (!engine?.isModelLoaded?.()) throw new Error("The packaged Wllama model is not loaded.");
641
+ if (!engine?.isModelLoaded?.() || evidenceState.state !== "ready") {
642
+ throw new Error("The packaged Wllama model is not loaded with admitted WebGPU evidence.");
643
+ }
321
644
  return engine;
322
645
  }
323
646
 
324
- async function chat(options) {
325
- const session = assertLoaded();
326
- return trackOperation(
327
- Promise.resolve().then(() => session.createChatCompletion({ ...options, stream: false })),
328
- ).result;
647
+ async function terminateUnacknowledgedCancellation(session, reason) {
648
+ let snapshot;
649
+ try {
650
+ snapshot = await exitSession(session);
651
+ } catch (error) {
652
+ throw runtimeFailure(
653
+ "ARCANE_AI_WORKER_TERMINATION_UNCONFIRMED",
654
+ "Wllama cancellation was not acknowledged and Worker termination could not be proved.",
655
+ error,
656
+ );
657
+ }
658
+ if (engine === session) engine = null;
659
+ publishEvidence({
660
+ cancellation: deepFreeze({
661
+ deliverySuppressed: true,
662
+ upstream: {
663
+ kind: "worker-terminated",
664
+ cancellationAcknowledged: false,
665
+ cleanup: snapshot.cleanup,
666
+ },
667
+ nativeUnloadClaimed: false,
668
+ physicalVramReclamationClaimed: false,
669
+ }),
670
+ });
671
+ }
672
+
673
+ async function recordInference(
674
+ session,
675
+ before,
676
+ { aborted, requireCancellationAcknowledgement = false },
677
+ ) {
678
+ let after;
679
+ try {
680
+ after = verifyProjectedTelemetry(await session.arcaneTelemetry());
681
+ } catch (error) {
682
+ if (!aborted && !requireCancellationAcknowledgement) throw error;
683
+ await terminateUnacknowledgedCancellation(session, error);
684
+ if (requireCancellationAcknowledgement) {
685
+ throw runtimeFailure(
686
+ "ARCANE_AI_COMPLETION_RECOVERY_UNCONFIRMED",
687
+ "Wllama completion recovery could not prove request settlement.",
688
+ error,
689
+ );
690
+ }
691
+ return;
692
+ }
693
+ const previousSequence = before?.cancellation?.sequence ?? 0;
694
+ const cancellation = after.cancellation;
695
+ const cancellationAcknowledged = cancellation?.sequence > previousSequence
696
+ && cancellation.responseName === "cncl_res"
697
+ && cancellation.acknowledged === true
698
+ && cancellation.failed === false;
699
+ if (aborted) {
700
+ if (cancellationAcknowledged) {
701
+ publishEvidence({
702
+ cancellation: deepFreeze({
703
+ deliverySuppressed: true,
704
+ upstream: {
705
+ kind: "llama-request-cancel-acknowledged",
706
+ sequence: cancellation.sequence,
707
+ requestId: cancellation.requestId,
708
+ responseName: cancellation.responseName,
709
+ acknowledged: true,
710
+ failed: false,
711
+ },
712
+ immediateGpuKernelPreemptionClaimed: false,
713
+ }),
714
+ });
715
+ return;
716
+ }
717
+ await terminateUnacknowledgedCancellation(
718
+ session,
719
+ "Wllama cancellation was not acknowledged.",
720
+ );
721
+ return;
722
+ }
723
+ if (requireCancellationAcknowledgement && !cancellationAcknowledged) {
724
+ await terminateUnacknowledgedCancellation(
725
+ session,
726
+ "Wllama completion recovery could not prove cancellation acknowledgement.",
727
+ );
728
+ throw runtimeFailure(
729
+ "ARCANE_AI_COMPLETION_RECOVERY_UNCONFIRMED",
730
+ "Wllama completion recovery could not prove request settlement.",
731
+ );
732
+ }
733
+
734
+ const submissions = after.worker.queueSubmissions - before.worker.queueSubmissions;
735
+ const commandBuffers = after.worker.commandBuffers - before.worker.commandBuffers;
736
+ const fenceRequests = after.worker.queueFenceRequests - before.worker.queueFenceRequests;
737
+ const fenceCompletions = after.worker.queueFenceCompletions - before.worker.queueFenceCompletions;
738
+ if (
739
+ submissions < 1
740
+ || commandBuffers < 1
741
+ || fenceRequests < 1
742
+ || fenceCompletions < fenceRequests
743
+ ) {
744
+ await exitSession(session);
745
+ if (engine === session) engine = null;
746
+ throw runtimeFailure(
747
+ "ARCANE_AI_WEBGPU_REQUIRED",
748
+ "Inference completed without positive, settled WebGPU queue evidence.",
749
+ );
750
+ }
751
+ publishEvidence({
752
+ webgpu: deepFreeze({
753
+ ...evidenceState.webgpu,
754
+ queue: {
755
+ submissions: after.worker.queueSubmissions,
756
+ commandBuffers: after.worker.commandBuffers,
757
+ fenceRequests: after.worker.queueFenceRequests,
758
+ fenceCompletions: after.worker.queueFenceCompletions,
759
+ },
760
+ lastInference: { submissions, commandBuffers, fenceRequests, fenceCompletions },
761
+ }),
762
+ cancellation: requireCancellationAcknowledgement
763
+ ? deepFreeze({
764
+ deliverySuppressed: false,
765
+ recovery: "peg-native-final-output",
766
+ upstream: {
767
+ kind: "llama-request-cancel-acknowledged",
768
+ sequence: cancellation.sequence,
769
+ requestId: cancellation.requestId,
770
+ responseName: cancellation.responseName,
771
+ acknowledged: true,
772
+ failed: false,
773
+ },
774
+ immediateGpuKernelPreemptionClaimed: false,
775
+ })
776
+ : null,
777
+ });
778
+ }
779
+
780
+ async function invalidateFatalSession(session, error) {
781
+ try {
782
+ await exitSession(session);
783
+ } catch (cleanupError) {
784
+ throw cleanupError?.code === "ARCANE_AI_WORKER_TERMINATION_UNCONFIRMED"
785
+ ? cleanupError
786
+ : runtimeFailure(
787
+ "ARCANE_AI_WORKER_TERMINATION_UNCONFIRMED",
788
+ "Wllama failed and Worker termination could not be proved.",
789
+ cleanupError,
790
+ );
791
+ }
792
+ if (engine === session) engine = null;
793
+ publishEvidence({
794
+ state: "error",
795
+ webgpu: {
796
+ ...evidenceState.webgpu,
797
+ observed: false,
798
+ lastObservedOperational: evidenceState.webgpu?.lastObservedOperational === true,
799
+ },
800
+ failure: deepFreeze({
801
+ code: typeof error?.code === "string" ? error.code : "ARCANE_AI_RUNTIME_FAILED",
802
+ }),
803
+ });
804
+ }
805
+
806
+ async function inference(options, onData = null) {
807
+ if (!options || typeof options !== "object" || Array.isArray(options)) {
808
+ throw new TypeError("Wllama inference options must be an object.");
809
+ }
810
+ if (Object.hasOwn(options, "signal")) {
811
+ throw new TypeError("Pinned Wllama 3.6.0 accepts abortSignal, not signal.");
812
+ }
813
+ if (inferenceActive) {
814
+ throw runtimeFailure(
815
+ "ARCANE_AI_RUNTIME_BUSY",
816
+ "The packaged Wllama runtime admits one inference at a time.",
817
+ );
818
+ }
819
+ inferenceActive = true;
820
+ try {
821
+ const session = assertLoaded();
822
+ const observer = sessionObservers.get(session);
823
+ const streamCapture = createStructuredStreamCapture();
824
+ let capture = null;
825
+ let operation = null;
826
+ let before = null;
827
+ try {
828
+ before = verifyProjectedTelemetry(await session.arcaneTelemetry());
829
+ const abortSignal = options.abortSignal ?? null;
830
+ capture = observer.beginCompletionCapture();
831
+ const deliver = onData
832
+ ? (chunk) => {
833
+ streamCapture.observe(chunk);
834
+ onData(chunk);
835
+ }
836
+ : null;
837
+ operation = trackOperation(Promise.resolve().then(() => session.createChatCompletion({
838
+ ...options,
839
+ stream: Boolean(onData),
840
+ ...(deliver ? { onData: deliver } : {}),
841
+ abortSignal,
842
+ })));
843
+ const result = await operation.result;
844
+ capture.release();
845
+ await recordInference(session, before, { aborted: false });
846
+ return result;
847
+ } catch (error) {
848
+ const abortSignal = options.abortSignal ?? null;
849
+ const locallySuppressed = operation?.locallySuppressed() === true;
850
+ const aborted = operation !== null
851
+ && !locallySuppressed
852
+ && (abortSignal?.aborted || error?.name === "AbortError");
853
+ const recoveredContent = locallySuppressed
854
+ ? null
855
+ : capture?.recover(error, { aborted }) ?? null;
856
+ if (onData && recoveredContent !== null && streamCapture.matches(recoveredContent)) {
857
+ // Wllama has already awaited cancelRequest() before rejecting here.
858
+ // The streamed text is exact, but the native stop reason is unknown.
859
+ await recordInference(session, before, {
860
+ aborted: false,
861
+ requireCancellationAcknowledgement: true,
862
+ });
863
+ return null;
864
+ }
865
+ if (aborted) {
866
+ await operation.raw.catch(() => undefined);
867
+ await recordInference(session, before, { aborted: true });
868
+ } else if (operation === null || !locallySuppressed) {
869
+ await invalidateFatalSession(session, error);
870
+ }
871
+ throw error;
872
+ } finally {
873
+ capture?.release();
874
+ }
875
+ } finally {
876
+ inferenceActive = false;
877
+ }
878
+ }
879
+
880
+ function chat(options) {
881
+ return inference({ ...options, stream: false });
329
882
  }
330
883
 
331
- async function stream(options, onData) {
884
+ function stream(options, onData) {
332
885
  if (typeof onData !== "function") {
333
886
  throw new TypeError("Wllama stream() requires an onData callback.");
334
887
  }
335
- // Wllama owns the request slot until this promise settles. Its documented
336
- // abortSignal path cancels the llama.cpp request in a finally block.
337
- const session = assertLoaded();
338
- return trackOperation(
339
- Promise.resolve().then(() => session.createChatCompletion({ ...options, stream: true, onData })),
340
- ).result;
888
+ return inference({ ...options, stream: true }, onData);
341
889
  }
342
890
 
343
891
  async function exit() {
@@ -348,10 +896,7 @@ export function createPackagedWllamaRuntime({ logger = console } = {}) {
348
896
  cancelTrackedOperations(reason);
349
897
  const sessions = new Set([current, loading?.engine].filter(Boolean));
350
898
  if (!sessions.size) return false;
351
- await Promise.all([...sessions].map((session) => exitSession(session, reason)));
352
- // Retain ownership through cleanup failure. Clear only the exact handles
353
- // whose exit attempts completed successfully; replacement sessions, if
354
- // any, remain owned by the runtime.
899
+ await Promise.all([...sessions].map((session) => exitSession(session)));
355
900
  if (engine === current) engine = null;
356
901
  if (pending === loading) pending = null;
357
902
  return true;
@@ -363,14 +908,13 @@ export function createPackagedWllamaRuntime({ logger = console } = {}) {
363
908
  throw new TypeError("Wllama probe args must be an array of strings.");
364
909
  }
365
910
  const temporary = newEngine();
366
- // testBackendOps owns and closes its temporary worker. It requires browser
367
- // multithreading, but that is a probe capability—not model admission.
368
911
  const result = await temporary.testBackendOps(args);
369
912
  return Object.freeze({
370
913
  ...result,
371
914
  args: Object.freeze([...args]),
372
915
  origin: globalThis.location?.origin ?? null,
373
916
  capabilities: capabilities(),
917
+ evidence: evidenceState,
374
918
  runtime: BROWSER_WASM_RUNTIME_AUTHORITY,
375
919
  });
376
920
  }
@@ -379,12 +923,13 @@ export function createPackagedWllamaRuntime({ logger = console } = {}) {
379
923
  authority: BROWSER_WASM_RUNTIME_AUTHORITY,
380
924
  runtimeAssets: BROWSER_WASM_RUNTIME_AUTHORITY.runtimeAssets,
381
925
  capabilities,
926
+ evidence,
382
927
  load,
383
928
  chat,
384
929
  stream,
385
930
  probe,
386
931
  exit,
387
- isLoaded: () => Boolean(engine?.isModelLoaded?.()),
932
+ isLoaded: () => Boolean(engine?.isModelLoaded?.()) && evidenceState.state === "ready",
388
933
  isLoading: () => Boolean(pending),
389
934
  });
390
935
  }