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.
- package/CHANGELOG.md +17 -0
- package/NOTICE +5 -3
- package/README.md +73 -24
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +67 -18
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +16 -5
- package/browser-runtime/ai/browser-kokoro-worker.mjs +3 -0
- package/browser-runtime/ai/browser-speech-artifacts.mjs +1108 -0
- package/browser-runtime/ai/browser-speech-providers.mjs +475 -0
- package/browser-runtime/ai/browser-speech.mjs +9 -0
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +1537 -167
- package/browser-runtime/ai/browser-wasm.mjs +46 -1
- package/browser-runtime/ai/browser-whisper-worker.mjs +3 -0
- package/browser-runtime/ai/browser-wllama-runtime.mjs +677 -132
- package/browser-runtime/ai/model-controller.mjs +138 -12
- package/browser-runtime/ai/speech-worker-client.mjs +207 -0
- package/browser-runtime/ai/speech-worker-runtime.mjs +516 -0
- package/browser-runtime/ai/wllama/index.mjs +389 -0
- package/docs/architecture.md +132 -22
- package/docs/reference/README.md +1 -1
- package/docs/reference/ai/browser-wasm.md +101 -42
- package/docs/reference/availability-and-normalization.md +19 -5
- package/docs/reference/behavioral-testing.md +18 -5
- package/docs/reference/cli.md +2 -2
- package/docs/reference/inventory/package-api.json +14 -14
- package/docs/reference/protocols.md +4 -4
- package/docs/reference/sdk-api.md +68 -38
- package/docs/work-amplification.md +8 -4
- package/package.json +7 -3
- package/runtime/ARCANE_RUNTIME_RELEASE.json +50 -20
- package/runtime/arcane/components/chat.html +280 -62
- package/runtime/arcane/components/speech.html +1113 -265
- package/runtime/arcane/entities/Chat.js +246 -43
- package/runtime/arcane/modules/AI.js +713 -162
- package/runtime/arcane/modules/AIProviderRuntime.js +2289 -0
- package/runtime/arcane/modules/AIRuntimeState.js +872 -0
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +293 -27
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +682 -0
- package/runtime/arcane/modules/DocumentLexicalSearch.js +292 -0
- package/runtime/arcane/modules/PersistentAIChatSession.js +268 -0
- package/runtime/arcane/modules/StaticDocumentCatalog.js +25 -206
- package/schemas/arcane-lock.schema.json +6 -4
- package/src/cli/main.mjs +14 -2
- package/src/constants.mjs +1 -1
- package/src/dev-server.mjs +244 -13
- package/src/import-map.mjs +59 -1
- package/src/packager/core.mjs +2 -2
- package/src/runtime.mjs +14 -4
- package/src/sdk-browser-runtime.mjs +28 -75
- package/src/templates/workspace-template.mjs +4 -4
- package/src/toolchain.mjs +3 -0
- package/src/workspace-runtime.mjs +1 -1
- package/src/workspace.mjs +1 -1
|
@@ -3476,6 +3476,395 @@ var Wllama = class {
|
|
|
3476
3476
|
return workerResources;
|
|
3477
3477
|
}
|
|
3478
3478
|
};
|
|
3479
|
+
(function applyArcaneWllamaProjection() {
|
|
3480
|
+
const protocol = "arcane-wllama-webgpu-evidence/1";
|
|
3481
|
+
const emptyWorkerTelemetry = `{protocol:"${protocol}",adapter:null,bufferCount:0,bufferBytes:0,queueSubmissions:0,commandBuffers:0,queueFenceRequests:0,queueFenceCompletions:0,invalid:false}`;
|
|
3482
|
+
|
|
3483
|
+
function replaceSingle(source, search, replacement, label) {
|
|
3484
|
+
const first = source.indexOf(search);
|
|
3485
|
+
const last = source.lastIndexOf(search);
|
|
3486
|
+
if (first < 0 || first !== last) {
|
|
3487
|
+
throw new Error(`Pinned Wllama ${label} projection anchor must occur exactly once.`);
|
|
3488
|
+
}
|
|
3489
|
+
return `${source.slice(0, first)}${replacement}${source.slice(first + search.length)}`;
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
const adapterAnchor = "if(adapter){WebGPU.Internals.jsObjectInsert(adapterPtr,adapter)";
|
|
3493
|
+
const adapterProjection = `if(adapter){(function arcaneRecordSelectedWebgpuAdapter(){const key="__arcaneWllamaWebgpuTelemetry";const previous=globalThis[key]??${emptyWorkerTelemetry};const info=adapter.info??{};const text=value=>typeof value==="string"?value.slice(0,256):"";const next={selected:true,vendorId:null,vendor:text(info.vendor),architecture:text(info.architecture),deviceId:null,name:text(info.device),description:text(info.description)};const conflicts=previous.adapter!==null&&JSON.stringify(previous.adapter)!==JSON.stringify(next);globalThis[key]={protocol:"${protocol}",adapter:previous.adapter??next,bufferCount:previous.bufferCount,bufferBytes:previous.bufferBytes,queueSubmissions:previous.queueSubmissions,commandBuffers:previous.commandBuffers,queueFenceRequests:previous.queueFenceRequests,queueFenceCompletions:previous.queueFenceCompletions,invalid:previous.invalid||conflicts}})();WebGPU.Internals.jsObjectInsert(adapterPtr,adapter)`;
|
|
3494
|
+
WLLAMA_EMSCRIPTEN_CODE = replaceSingle(
|
|
3495
|
+
WLLAMA_EMSCRIPTEN_CODE,
|
|
3496
|
+
adapterAnchor,
|
|
3497
|
+
adapterProjection,
|
|
3498
|
+
"WebGPU adapter selection",
|
|
3499
|
+
);
|
|
3500
|
+
|
|
3501
|
+
const bufferAnchor = "var buffer;try{buffer=device.createBuffer(desc)}catch(ex){return false}WebGPU.Internals.jsObjectInsert(bufferPtr,buffer)";
|
|
3502
|
+
const bufferProjection = `${bufferAnchor};(function arcaneRecordWebgpuBuffer(){const key="__arcaneWllamaWebgpuTelemetry";const previous=globalThis[key]??${emptyWorkerTelemetry};const size=desc.size;const bufferCount=previous.bufferCount+1;const bufferBytes=previous.bufferBytes+size;globalThis[key]={protocol:"${protocol}",adapter:previous.adapter,bufferCount:Number.isSafeInteger(bufferCount)?bufferCount:previous.bufferCount,bufferBytes:Number.isSafeInteger(size)&&size>0&&Number.isSafeInteger(bufferBytes)?bufferBytes:previous.bufferBytes,queueSubmissions:previous.queueSubmissions,commandBuffers:previous.commandBuffers,queueFenceRequests:previous.queueFenceRequests,queueFenceCompletions:previous.queueFenceCompletions,invalid:previous.invalid||!Number.isSafeInteger(size)||size<1||!Number.isSafeInteger(bufferCount)||!Number.isSafeInteger(bufferBytes)}})()`;
|
|
3503
|
+
WLLAMA_EMSCRIPTEN_CODE = replaceSingle(
|
|
3504
|
+
WLLAMA_EMSCRIPTEN_CODE,
|
|
3505
|
+
bufferAnchor,
|
|
3506
|
+
bufferProjection,
|
|
3507
|
+
"WebGPU buffer",
|
|
3508
|
+
);
|
|
3509
|
+
|
|
3510
|
+
const queueAnchor = "queue.submit(cmds)};function _wgpuQueueWriteBuffer";
|
|
3511
|
+
const queueProjection = `queue.submit(cmds);(function arcaneRecordWebgpuSubmission(){const key="__arcaneWllamaWebgpuTelemetry";const previous=globalThis[key]??${emptyWorkerTelemetry};const queueSubmissions=previous.queueSubmissions+1;const commandBuffers=previous.commandBuffers+cmds.length;globalThis[key]={protocol:"${protocol}",adapter:previous.adapter,bufferCount:previous.bufferCount,bufferBytes:previous.bufferBytes,queueSubmissions:Number.isSafeInteger(queueSubmissions)?queueSubmissions:previous.queueSubmissions,commandBuffers:Number.isSafeInteger(commandBuffers)?commandBuffers:previous.commandBuffers,queueFenceRequests:previous.queueFenceRequests,queueFenceCompletions:previous.queueFenceCompletions,invalid:previous.invalid||!Number.isSafeInteger(queueSubmissions)||!Number.isSafeInteger(commandBuffers)}})()};function _wgpuQueueWriteBuffer`;
|
|
3512
|
+
WLLAMA_EMSCRIPTEN_CODE = replaceSingle(
|
|
3513
|
+
WLLAMA_EMSCRIPTEN_CODE,
|
|
3514
|
+
queueAnchor,
|
|
3515
|
+
queueProjection,
|
|
3516
|
+
"WebGPU queue",
|
|
3517
|
+
);
|
|
3518
|
+
|
|
3519
|
+
const fenceAnchor = "runtimeKeepalivePush();WebGPU.Internals.futureInsert(futureId,queue.onSubmittedWorkDone().then(()=>{";
|
|
3520
|
+
const fenceProjection = `runtimeKeepalivePush();(function arcaneRecordWebgpuFenceRequest(){const key="__arcaneWllamaWebgpuTelemetry";const previous=globalThis[key]??${emptyWorkerTelemetry};const queueFenceRequests=previous.queueFenceRequests+1;globalThis[key]={protocol:"${protocol}",adapter:previous.adapter,bufferCount:previous.bufferCount,bufferBytes:previous.bufferBytes,queueSubmissions:previous.queueSubmissions,commandBuffers:previous.commandBuffers,queueFenceRequests:Number.isSafeInteger(queueFenceRequests)?queueFenceRequests:previous.queueFenceRequests,queueFenceCompletions:previous.queueFenceCompletions,invalid:previous.invalid||!Number.isSafeInteger(queueFenceRequests)}})();WebGPU.Internals.futureInsert(futureId,queue.onSubmittedWorkDone().then(function arcaneRecordWebgpuFenceCompletion(){(function arcaneCommitWebgpuFenceCompletion(){const key="__arcaneWllamaWebgpuTelemetry";const previous=globalThis[key]??${emptyWorkerTelemetry};const queueFenceCompletions=previous.queueFenceCompletions+1;globalThis[key]={protocol:"${protocol}",adapter:previous.adapter,bufferCount:previous.bufferCount,bufferBytes:previous.bufferBytes,queueSubmissions:previous.queueSubmissions,commandBuffers:previous.commandBuffers,queueFenceRequests:previous.queueFenceRequests,queueFenceCompletions:Number.isSafeInteger(queueFenceCompletions)?queueFenceCompletions:previous.queueFenceCompletions,invalid:previous.invalid||!Number.isSafeInteger(queueFenceCompletions)||queueFenceCompletions>previous.queueFenceRequests}})();`;
|
|
3521
|
+
WLLAMA_EMSCRIPTEN_CODE = replaceSingle(
|
|
3522
|
+
WLLAMA_EMSCRIPTEN_CODE,
|
|
3523
|
+
fenceAnchor,
|
|
3524
|
+
fenceProjection,
|
|
3525
|
+
"WebGPU submitted-work fence",
|
|
3526
|
+
);
|
|
3527
|
+
|
|
3528
|
+
const workerAnchor = " if (verb === 'module.init') {";
|
|
3529
|
+
const workerProjection = ` if (verb === 'arcane.telemetry') {\n const observed = globalThis.__arcaneWllamaWebgpuTelemetry;\n const adapter = observed?.adapter?.selected === true ? {\n selected: true,\n vendorId: null,\n vendor: typeof observed.adapter.vendor === 'string' ? observed.adapter.vendor.slice(0, 256) : '',\n architecture: typeof observed.adapter.architecture === 'string' ? observed.adapter.architecture.slice(0, 256) : '',\n deviceId: null,\n name: typeof observed.adapter.name === 'string' ? observed.adapter.name.slice(0, 256) : '',\n description: typeof observed.adapter.description === 'string' ? observed.adapter.description.slice(0, 256) : '',\n } : null;\n msg({\n callbackId,\n result: {\n protocol: '${protocol}',\n adapter,\n bufferCount: Number.isSafeInteger(observed?.bufferCount) ? observed.bufferCount : 0,\n bufferBytes: Number.isSafeInteger(observed?.bufferBytes) ? observed.bufferBytes : 0,\n queueSubmissions: Number.isSafeInteger(observed?.queueSubmissions) ? observed.queueSubmissions : 0,\n commandBuffers: Number.isSafeInteger(observed?.commandBuffers) ? observed.commandBuffers : 0,\n queueFenceRequests: Number.isSafeInteger(observed?.queueFenceRequests) ? observed.queueFenceRequests : 0,\n queueFenceCompletions: Number.isSafeInteger(observed?.queueFenceCompletions) ? observed.queueFenceCompletions : 0,\n invalid: observed?.invalid === true,\n },\n });\n return;\n }\n\n${workerAnchor}`;
|
|
3530
|
+
LLAMA_CPP_WORKER_CODE = replaceSingle(
|
|
3531
|
+
LLAMA_CPP_WORKER_CODE,
|
|
3532
|
+
workerAnchor,
|
|
3533
|
+
workerProjection,
|
|
3534
|
+
"Worker telemetry",
|
|
3535
|
+
);
|
|
3536
|
+
|
|
3537
|
+
function sanitizeWorkerTelemetry(value) {
|
|
3538
|
+
function nonNegativeCounter(candidate) {
|
|
3539
|
+
return Number.isSafeInteger(candidate) && candidate >= 0 ? candidate : 0;
|
|
3540
|
+
}
|
|
3541
|
+
function adapterText(candidate) {
|
|
3542
|
+
return typeof candidate === "string" && candidate.length <= 256 ? candidate : "";
|
|
3543
|
+
}
|
|
3544
|
+
const rawAdapter = value?.adapter;
|
|
3545
|
+
const adapterInvalid = rawAdapter !== undefined && rawAdapter !== null && (
|
|
3546
|
+
typeof rawAdapter !== "object"
|
|
3547
|
+
|| rawAdapter.selected !== true
|
|
3548
|
+
|| rawAdapter.vendorId !== null
|
|
3549
|
+
|| typeof rawAdapter.vendor !== "string"
|
|
3550
|
+
|| rawAdapter.vendor.length > 256
|
|
3551
|
+
|| typeof rawAdapter.architecture !== "string"
|
|
3552
|
+
|| rawAdapter.architecture.length > 256
|
|
3553
|
+
|| rawAdapter.deviceId !== null
|
|
3554
|
+
|| typeof rawAdapter.name !== "string"
|
|
3555
|
+
|| rawAdapter.name.length > 256
|
|
3556
|
+
|| typeof rawAdapter.description !== "string"
|
|
3557
|
+
|| rawAdapter.description.length > 256
|
|
3558
|
+
);
|
|
3559
|
+
const adapter = rawAdapter?.selected === true ? Object.freeze({
|
|
3560
|
+
selected: true,
|
|
3561
|
+
vendorId: null,
|
|
3562
|
+
vendor: adapterText(rawAdapter.vendor),
|
|
3563
|
+
architecture: adapterText(rawAdapter.architecture),
|
|
3564
|
+
deviceId: null,
|
|
3565
|
+
name: adapterText(rawAdapter.name),
|
|
3566
|
+
description: adapterText(rawAdapter.description),
|
|
3567
|
+
}) : null;
|
|
3568
|
+
return Object.freeze({
|
|
3569
|
+
protocol,
|
|
3570
|
+
adapter,
|
|
3571
|
+
bufferCount: nonNegativeCounter(value?.bufferCount),
|
|
3572
|
+
bufferBytes: nonNegativeCounter(value?.bufferBytes),
|
|
3573
|
+
queueSubmissions: nonNegativeCounter(value?.queueSubmissions),
|
|
3574
|
+
commandBuffers: nonNegativeCounter(value?.commandBuffers),
|
|
3575
|
+
queueFenceRequests: nonNegativeCounter(value?.queueFenceRequests),
|
|
3576
|
+
queueFenceCompletions: nonNegativeCounter(value?.queueFenceCompletions),
|
|
3577
|
+
invalid: value?.invalid === true || value?.protocol !== protocol || adapterInvalid,
|
|
3578
|
+
});
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
const proxyEvidence = new WeakMap();
|
|
3582
|
+
const sessionEvidence = new WeakMap();
|
|
3583
|
+
|
|
3584
|
+
function proxyRecord(proxy) {
|
|
3585
|
+
let record = proxyEvidence.get(proxy);
|
|
3586
|
+
if (!record) {
|
|
3587
|
+
record = {
|
|
3588
|
+
cancellationSequence: 0,
|
|
3589
|
+
cancellation: null,
|
|
3590
|
+
cleanupSequence: 0,
|
|
3591
|
+
cleanup: null,
|
|
3592
|
+
worker: sanitizeWorkerTelemetry(null),
|
|
3593
|
+
};
|
|
3594
|
+
proxyEvidence.set(proxy, record);
|
|
3595
|
+
}
|
|
3596
|
+
return record;
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
function cancellationRecord(proxy, value) {
|
|
3600
|
+
const evidence = proxyRecord(proxy);
|
|
3601
|
+
const sequence = evidence.cancellationSequence + 1;
|
|
3602
|
+
evidence.cancellationSequence = sequence;
|
|
3603
|
+
const record = Object.freeze({
|
|
3604
|
+
sequence,
|
|
3605
|
+
requestId: typeof value.requestId === "number" || typeof value.requestId === "string"
|
|
3606
|
+
? value.requestId
|
|
3607
|
+
: null,
|
|
3608
|
+
responseName: value.responseName === "cncl_res" ? value.responseName : null,
|
|
3609
|
+
acknowledged: value.acknowledged === true,
|
|
3610
|
+
failed: value.failed === true,
|
|
3611
|
+
});
|
|
3612
|
+
evidence.cancellation = record;
|
|
3613
|
+
return record;
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
const originalAction = ProxyToWorker.prototype.wllamaAction;
|
|
3617
|
+
Object.defineProperty(ProxyToWorker.prototype, "wllamaAction", {
|
|
3618
|
+
configurable: false,
|
|
3619
|
+
writable: false,
|
|
3620
|
+
value: async function arcaneWllamaAction(name, body) {
|
|
3621
|
+
if (name !== "cancel") return originalAction.call(this, name, body);
|
|
3622
|
+
try {
|
|
3623
|
+
const result = await originalAction.call(this, name, body);
|
|
3624
|
+
cancellationRecord(this, {
|
|
3625
|
+
requestId: body?.req_id,
|
|
3626
|
+
responseName: result?._name,
|
|
3627
|
+
acknowledged: result?._name === "cncl_res" && result?.success === true,
|
|
3628
|
+
failed: false,
|
|
3629
|
+
});
|
|
3630
|
+
return result;
|
|
3631
|
+
} catch (error) {
|
|
3632
|
+
cancellationRecord(this, {
|
|
3633
|
+
requestId: body?.req_id,
|
|
3634
|
+
responseName: null,
|
|
3635
|
+
acknowledged: false,
|
|
3636
|
+
failed: true,
|
|
3637
|
+
});
|
|
3638
|
+
throw error;
|
|
3639
|
+
}
|
|
3640
|
+
},
|
|
3641
|
+
});
|
|
3642
|
+
|
|
3643
|
+
Object.defineProperty(ProxyToWorker.prototype, "arcaneTelemetry", {
|
|
3644
|
+
configurable: false,
|
|
3645
|
+
writable: false,
|
|
3646
|
+
value: async function arcaneTelemetry() {
|
|
3647
|
+
const value = await this.pushTask({
|
|
3648
|
+
verb: "arcane.telemetry",
|
|
3649
|
+
args: [],
|
|
3650
|
+
callbackId: this.taskId++,
|
|
3651
|
+
});
|
|
3652
|
+
const evidence = proxyRecord(this);
|
|
3653
|
+
evidence.worker = sanitizeWorkerTelemetry(value);
|
|
3654
|
+
return Object.freeze({
|
|
3655
|
+
protocol,
|
|
3656
|
+
worker: evidence.worker,
|
|
3657
|
+
cancellation: evidence.cancellation,
|
|
3658
|
+
cleanup: evidence.cleanup,
|
|
3659
|
+
});
|
|
3660
|
+
},
|
|
3661
|
+
});
|
|
3662
|
+
|
|
3663
|
+
const originalWorkerExit = ProxyToWorker.prototype.wllamaExit;
|
|
3664
|
+
Object.defineProperty(ProxyToWorker.prototype, "wllamaExit", {
|
|
3665
|
+
configurable: false,
|
|
3666
|
+
writable: false,
|
|
3667
|
+
value: async function arcaneWllamaExit(...args) {
|
|
3668
|
+
const hadWorker = Boolean(this.worker);
|
|
3669
|
+
const result = await originalWorkerExit.apply(this, args);
|
|
3670
|
+
if (hadWorker) {
|
|
3671
|
+
const evidence = proxyRecord(this);
|
|
3672
|
+
const sequence = evidence.cleanupSequence + 1;
|
|
3673
|
+
evidence.cleanupSequence = sequence;
|
|
3674
|
+
evidence.cleanup = Object.freeze({
|
|
3675
|
+
sequence,
|
|
3676
|
+
kind: "worker-terminated",
|
|
3677
|
+
nativeUnload: false,
|
|
3678
|
+
physicalVramReclamation: "not-observed",
|
|
3679
|
+
});
|
|
3680
|
+
}
|
|
3681
|
+
return result;
|
|
3682
|
+
},
|
|
3683
|
+
});
|
|
3684
|
+
|
|
3685
|
+
function projectionFailure(code, message, cause) {
|
|
3686
|
+
const error = new Error(message, cause === undefined ? undefined : { cause });
|
|
3687
|
+
error.name = "ArcaneWllamaProjectionError";
|
|
3688
|
+
error.code = code;
|
|
3689
|
+
return error;
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
function cancellationError(reason) {
|
|
3693
|
+
if (reason instanceof Error) return reason;
|
|
3694
|
+
const error = new Error(reason ? String(reason) : "The Wllama operation was cancelled.");
|
|
3695
|
+
error.name = "AbortError";
|
|
3696
|
+
return error;
|
|
3697
|
+
}
|
|
3698
|
+
|
|
3699
|
+
function noWorkerCleanup() {
|
|
3700
|
+
return Object.freeze({
|
|
3701
|
+
sequence: 0,
|
|
3702
|
+
kind: "no-worker-observed-at-exit",
|
|
3703
|
+
nativeUnload: false,
|
|
3704
|
+
physicalVramReclamation: "not-applicable",
|
|
3705
|
+
});
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
function proxySnapshot(proxy, cleanup = null) {
|
|
3709
|
+
const evidence = proxy ? proxyRecord(proxy) : null;
|
|
3710
|
+
return Object.freeze({
|
|
3711
|
+
protocol,
|
|
3712
|
+
worker: evidence?.worker ?? sanitizeWorkerTelemetry(null),
|
|
3713
|
+
cancellation: evidence?.cancellation ?? null,
|
|
3714
|
+
cleanup: evidence?.cleanup ?? cleanup,
|
|
3715
|
+
});
|
|
3716
|
+
}
|
|
3717
|
+
|
|
3718
|
+
Object.defineProperty(Wllama.prototype, "arcaneTelemetry", {
|
|
3719
|
+
configurable: false,
|
|
3720
|
+
writable: false,
|
|
3721
|
+
value: async function arcaneTelemetry() {
|
|
3722
|
+
const exited = sessionEvidence.get(this) ?? null;
|
|
3723
|
+
if (!this.proxy || exited?.cleanup) return exited ?? proxySnapshot(null);
|
|
3724
|
+
return this.proxy.arcaneTelemetry();
|
|
3725
|
+
},
|
|
3726
|
+
});
|
|
3727
|
+
|
|
3728
|
+
const originalExit = Wllama.prototype.exit;
|
|
3729
|
+
Object.defineProperty(Wllama.prototype, "exit", {
|
|
3730
|
+
configurable: false,
|
|
3731
|
+
writable: false,
|
|
3732
|
+
value: async function arcaneExit(...args) {
|
|
3733
|
+
const proxy = this.proxy;
|
|
3734
|
+
const hadWorker = Boolean(proxy?.worker);
|
|
3735
|
+
let result;
|
|
3736
|
+
try {
|
|
3737
|
+
result = await originalExit.apply(this, args);
|
|
3738
|
+
} catch (error) {
|
|
3739
|
+
throw projectionFailure(
|
|
3740
|
+
"ARCANE_AI_WORKER_TERMINATION_UNCONFIRMED",
|
|
3741
|
+
"Wllama exit failed before Worker termination could be confirmed.",
|
|
3742
|
+
error,
|
|
3743
|
+
);
|
|
3744
|
+
}
|
|
3745
|
+
let snapshot = proxySnapshot(proxy, hadWorker ? null : noWorkerCleanup());
|
|
3746
|
+
if (hadWorker && snapshot.cleanup?.kind !== "worker-terminated") {
|
|
3747
|
+
throw projectionFailure(
|
|
3748
|
+
"ARCANE_AI_WORKER_TERMINATION_UNCONFIRMED",
|
|
3749
|
+
"Wllama exit did not confirm Worker termination.",
|
|
3750
|
+
);
|
|
3751
|
+
}
|
|
3752
|
+
sessionEvidence.set(this, snapshot);
|
|
3753
|
+
return result;
|
|
3754
|
+
},
|
|
3755
|
+
});
|
|
3756
|
+
|
|
3757
|
+
Object.defineProperty(Wllama.prototype, "arcaneTerminate", {
|
|
3758
|
+
configurable: false,
|
|
3759
|
+
writable: false,
|
|
3760
|
+
value: async function arcaneTerminate() {
|
|
3761
|
+
const existing = sessionEvidence.get(this) ?? null;
|
|
3762
|
+
if (!this.proxy) {
|
|
3763
|
+
const snapshot = existing ?? proxySnapshot(null, noWorkerCleanup());
|
|
3764
|
+
sessionEvidence.set(this, snapshot);
|
|
3765
|
+
return snapshot;
|
|
3766
|
+
}
|
|
3767
|
+
await this.exit();
|
|
3768
|
+
const snapshot = await this.arcaneTelemetry();
|
|
3769
|
+
if (!["worker-terminated", "no-worker-observed-at-exit"].includes(snapshot.cleanup?.kind)) {
|
|
3770
|
+
throw projectionFailure(
|
|
3771
|
+
"ARCANE_AI_WORKER_TERMINATION_UNCONFIRMED",
|
|
3772
|
+
"Wllama cleanup did not confirm its Worker termination boundary.",
|
|
3773
|
+
);
|
|
3774
|
+
}
|
|
3775
|
+
return snapshot;
|
|
3776
|
+
},
|
|
3777
|
+
});
|
|
3778
|
+
|
|
3779
|
+
const originalLoadModel = Wllama.prototype.loadModel;
|
|
3780
|
+
Object.defineProperty(Wllama.prototype, "arcaneLoadModel", {
|
|
3781
|
+
configurable: false,
|
|
3782
|
+
writable: false,
|
|
3783
|
+
value: async function arcaneLoadModel(files, params = {}, signal = null) {
|
|
3784
|
+
if (signal !== null && typeof signal !== "object") {
|
|
3785
|
+
throw new TypeError("Arcane Wllama load cancellation requires an AbortSignal or null.");
|
|
3786
|
+
}
|
|
3787
|
+
if (signal?.aborted) throw cancellationError(signal.reason);
|
|
3788
|
+
const descriptor = Object.getOwnPropertyDescriptor(this, "proxy");
|
|
3789
|
+
if (!descriptor?.configurable || !("value" in descriptor) || descriptor.get || descriptor.set) {
|
|
3790
|
+
throw projectionFailure(
|
|
3791
|
+
"ARCANE_AI_WLLAMA_PROJECTION_INVALID",
|
|
3792
|
+
"Pinned Wllama proxy state is not an inspectable configurable data property.",
|
|
3793
|
+
);
|
|
3794
|
+
}
|
|
3795
|
+
|
|
3796
|
+
sessionEvidence.delete(this);
|
|
3797
|
+
const session = this;
|
|
3798
|
+
let proxy = descriptor.value;
|
|
3799
|
+
let resolveProxyAssigned;
|
|
3800
|
+
const proxyAssigned = new Promise(function captureAssignedProxy(resolve) {
|
|
3801
|
+
resolveProxyAssigned = resolve;
|
|
3802
|
+
});
|
|
3803
|
+
if (proxy) resolveProxyAssigned(proxy);
|
|
3804
|
+
Object.defineProperty(this, "proxy", {
|
|
3805
|
+
enumerable: descriptor.enumerable,
|
|
3806
|
+
configurable: true,
|
|
3807
|
+
get: function arcaneReadLoadingProxy() {
|
|
3808
|
+
return proxy;
|
|
3809
|
+
},
|
|
3810
|
+
set: function arcaneRecordLoadingProxy(value) {
|
|
3811
|
+
proxy = value;
|
|
3812
|
+
if (value) resolveProxyAssigned(value);
|
|
3813
|
+
},
|
|
3814
|
+
});
|
|
3815
|
+
|
|
3816
|
+
let restored = false;
|
|
3817
|
+
function restoreProxyProperty() {
|
|
3818
|
+
if (restored) return;
|
|
3819
|
+
restored = true;
|
|
3820
|
+
Object.defineProperty(session, "proxy", { ...descriptor, value: proxy });
|
|
3821
|
+
}
|
|
3822
|
+
const loadPromise = Promise.resolve().then(function beginProjectedModelLoad() {
|
|
3823
|
+
return originalLoadModel.call(session, files, params);
|
|
3824
|
+
});
|
|
3825
|
+
loadPromise.finally(restoreProxyProperty).catch(function ignoreObservedLoadSettlement() {});
|
|
3826
|
+
if (!signal) return loadPromise;
|
|
3827
|
+
|
|
3828
|
+
let onAbort;
|
|
3829
|
+
const aborted = new Promise(function captureProjectedLoadCancellation(resolve, reject) {
|
|
3830
|
+
onAbort = function terminateProjectedLoadAfterAbort() {
|
|
3831
|
+
const reason = cancellationError(signal.reason);
|
|
3832
|
+
const activeProxy = proxy
|
|
3833
|
+
? Promise.resolve(proxy)
|
|
3834
|
+
: Promise.race([
|
|
3835
|
+
proxyAssigned,
|
|
3836
|
+
loadPromise.then(
|
|
3837
|
+
function projectedLoadFinishedWithoutProxy() { return null; },
|
|
3838
|
+
function projectedLoadFailedWithoutProxy() { return null; },
|
|
3839
|
+
),
|
|
3840
|
+
]);
|
|
3841
|
+
activeProxy.then(function terminateAssignedLoadProxy() {
|
|
3842
|
+
return session.arcaneTerminate();
|
|
3843
|
+
}).then(
|
|
3844
|
+
function rejectAfterConfirmedLoadTermination() { reject(reason); },
|
|
3845
|
+
function rejectWithLoadTerminationFailure(error) { reject(error); },
|
|
3846
|
+
);
|
|
3847
|
+
};
|
|
3848
|
+
});
|
|
3849
|
+
signal.addEventListener?.("abort", onAbort, { once: true });
|
|
3850
|
+
if (signal.aborted) onAbort();
|
|
3851
|
+
try {
|
|
3852
|
+
const result = await Promise.race([loadPromise, aborted]);
|
|
3853
|
+
if (signal.aborted) {
|
|
3854
|
+
await session.arcaneTerminate();
|
|
3855
|
+
throw cancellationError(signal.reason);
|
|
3856
|
+
}
|
|
3857
|
+
return result;
|
|
3858
|
+
} finally {
|
|
3859
|
+
signal.removeEventListener?.("abort", onAbort);
|
|
3860
|
+
}
|
|
3861
|
+
},
|
|
3862
|
+
});
|
|
3863
|
+
|
|
3864
|
+
Object.freeze(ProxyToWorker.prototype);
|
|
3865
|
+
Object.freeze(Wllama.prototype);
|
|
3866
|
+
})();
|
|
3867
|
+
|
|
3479
3868
|
export {
|
|
3480
3869
|
CacheManager,
|
|
3481
3870
|
LogLevel,
|
package/docs/architecture.md
CHANGED
|
@@ -7,37 +7,142 @@ same structured event stream; the GUI is not a second build system.
|
|
|
7
7
|
```text
|
|
8
8
|
external app repository -----+
|
|
9
9
|
|
|
|
10
|
-
|
|
10
|
+
Arcane OS consumer checkout --+-- CLI / future GUI / Codex / CI
|
|
11
11
|
|
|
|
12
12
|
shared toolchain API
|
|
13
13
|
|
|
|
14
14
|
browser package or explicit target adapter
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
## Canonical ownership and portability boundary
|
|
18
|
+
|
|
19
|
+
The SDK repository is the canonical source for every mechanism that can be
|
|
20
|
+
reused by a portable Arcane application. That includes shared modules,
|
|
21
|
+
entities, components, themes, browser runtimes, providers, workers and assets;
|
|
22
|
+
protocol, state, startup, readiness, progress, cancellation, unload and dispose
|
|
23
|
+
machinery; public native contracts and adapters; the development source mount;
|
|
24
|
+
and the packaging, license, receipt and verification boundaries for those
|
|
25
|
+
portable bytes. In particular, shared AI selected-role hydration,
|
|
26
|
+
startup-settled state and events, fail-closed role readiness, lifecycle and
|
|
27
|
+
cancellation contracts, and the shared chat and speech components are
|
|
28
|
+
SDK-owned source and contracts rather than Arcane OS–owned snapshots.
|
|
29
|
+
|
|
30
|
+
Every portable application artifact materializes an immutable, locked and
|
|
31
|
+
verified projection of the SDK runtime bytes, assets, workers, licenses and
|
|
32
|
+
public contracts it uses. It remains self-contained whether it runs as plain
|
|
33
|
+
HTML or inside an executable wrapper. It has no runtime dependency on an
|
|
34
|
+
Arcane OS installation, source checkout or private Arcane OS import.
|
|
35
|
+
|
|
36
|
+
Arcane OS is an SDK consumer like other applications. Its orchestrator,
|
|
37
|
+
launcher, Shell, Provisioner, system AI application and internal tools use the
|
|
38
|
+
same SDK modules and components rather than maintaining private runtime copies.
|
|
39
|
+
Arcane OS and Core own the privileged host implementations, app/session
|
|
40
|
+
admission and authorization, native transport and lifecycle, launcher and
|
|
41
|
+
Shell orchestration, and system-AI policy specific to the Shell. The SDK may
|
|
42
|
+
publish the capability-neutral Core bridge contract and adapters, but it does
|
|
43
|
+
not embed Core or inherit another application's policy.
|
|
44
|
+
|
|
45
|
+
Each application owns its branding, prompts, data, tools, business policy,
|
|
46
|
+
model authorities and app-specific orchestration. Apply this decision order:
|
|
47
|
+
|
|
48
|
+
| Responsibility | Canonical owner |
|
|
49
|
+
|---|---|
|
|
50
|
+
| Reusable by any portable application | Arcane SDK |
|
|
51
|
+
| Host privilege, launcher, Shell or app/session admission | Arcane OS / Core |
|
|
52
|
+
| Behavior unique to one product | That application |
|
|
53
|
+
|
|
54
|
+
Do not copy a reusable implementation between the SDK, Arcane OS and an app,
|
|
55
|
+
and do not create a hidden Arcane OS source dependency. Extend one neutral SDK
|
|
56
|
+
contract and keep product policy in the consumer.
|
|
57
|
+
|
|
58
|
+
Development and distribution use different authority. The explicit
|
|
59
|
+
`arcane dev --sdk-runtime-source <sdk-root>` development-only live source mount
|
|
60
|
+
lets a refresh read the saved SDK source without copying it into the app.
|
|
61
|
+
Distribution never follows that mount. It embeds and verifies the application's
|
|
62
|
+
locked immutable SDK projection.
|
|
63
|
+
|
|
64
|
+
`tools/runtime-source.json` declares SDK-canonical authority for
|
|
65
|
+
`runtime/arcane/` and retains the prior Arcane OS source only as legacy
|
|
66
|
+
provenance for migrated compatibility bytes. The old OS-to-SDK synchronization
|
|
67
|
+
direction is retired and fails closed. Arcane OS must consume a locked SDK
|
|
68
|
+
projection through the same package/source-mount boundary as other apps; its
|
|
69
|
+
repository-side consumer cutover is coordinated separately and does not create
|
|
70
|
+
a co-equal source.
|
|
71
|
+
|
|
17
72
|
## Workspace profiles
|
|
18
73
|
|
|
19
74
|
An external workspace maps the exact runtime shipped by its locked `arcane-os`
|
|
20
|
-
dependency. An integrated
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
75
|
+
dependency. An Arcane OS checkout is an integrated SDK consumer, not the owner
|
|
76
|
+
of portable runtime source. For live shared development, the explicit
|
|
77
|
+
development-only SDK source mount maps the canonical SDK runtime and dependency
|
|
78
|
+
paths into that consumer. Without the mount, the workspace uses its locked SDK
|
|
79
|
+
projection. The development server and packager consume the same route
|
|
80
|
+
destinations in both cases, so app imports do not change. Integrated
|
|
81
|
+
initialization creates only app-owned files and never rewrites Arcane OS or SDK
|
|
82
|
+
root configuration.
|
|
25
83
|
|
|
26
84
|
The shared/Core development profile is a separate integrated-only scope selected
|
|
27
85
|
with `--scope shared`. The SDK loads exactly
|
|
28
|
-
`tools/integrated-development-provider.mjs` from the selected Arcane checkout
|
|
29
|
-
one process generation.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
86
|
+
`tools/integrated-development-provider.mjs` from the selected Arcane OS checkout
|
|
87
|
+
as one process generation. This is a privileged host-development provider, not
|
|
88
|
+
a source of portable SDK runtime bytes. That provider admits only one exact
|
|
89
|
+
repository-relative focused `.test.mjs` through Arcane's canonical focused
|
|
90
|
+
runner or Arcane's canonical development check. External workspaces cannot use
|
|
91
|
+
the scope, and shared operations never enter app discovery, packaging, target
|
|
92
|
+
planning, build, verification, or run paths. Provider bytes and filesystem
|
|
93
|
+
identity are authenticated before and after the owned child operation; a
|
|
94
|
+
generation change poisons that pairing and requires a new CLI process.
|
|
36
95
|
Integrated app testing remains isolated to the selected `apps/<id>/test/`
|
|
37
96
|
tree; it cannot recursively select Arcane root tests or another app's tests.
|
|
38
97
|
External repositories retain their existing workspace-root plus selected-app
|
|
39
98
|
test layout.
|
|
40
99
|
|
|
100
|
+
## Development and release serving boundary
|
|
101
|
+
|
|
102
|
+
Arcane applications keep one browser-first plain HTML, CSS, and JavaScript
|
|
103
|
+
baseline. A native target runs that same application and progressively enhances
|
|
104
|
+
it through capability-gated Arcane Core access. Browser operation must not
|
|
105
|
+
depend on Core being present. A feature that genuinely requires Core fails
|
|
106
|
+
closed and explains its unavailability without breaking unrelated browser
|
|
107
|
+
behavior or claiming that the capability exists.
|
|
108
|
+
|
|
109
|
+
Rapid development uses `arcane dev`. The development server maps the selected
|
|
110
|
+
application's canonical source tree and the live installed SDK/runtime routes.
|
|
111
|
+
Each request reads the current saved source into the existing bounded response
|
|
112
|
+
snapshot, so a browser refresh shows source changes without packaging, copying
|
|
113
|
+
files into `dist`, or restarting the server. Restarting is not a content
|
|
114
|
+
synchronization step; when a refresh is stale, first verify the command, URL,
|
|
115
|
+
workspace, selected app, and resolved source route.
|
|
116
|
+
|
|
117
|
+
Development is an intentionally fast feedback loop. Keep each increment small
|
|
118
|
+
and independently understandable so its effect has one clear cause and a
|
|
119
|
+
mistake can be isolated without untangling unrelated work. A development
|
|
120
|
+
operation does not implicitly run tests, checks, packaging, builds, or release
|
|
121
|
+
verification. The developer invokes a focused test or check deliberately at an
|
|
122
|
+
explicit checkpoint; merely refreshing source does not trigger one.
|
|
123
|
+
|
|
124
|
+
Executable development uses an Arcane-owned native development wrapper around
|
|
125
|
+
the same source-serving browser surface. The wrapper is an escalated browser,
|
|
126
|
+
not a packaged application: it loads current source files and adds only the
|
|
127
|
+
selected application's declared, capability-gated local Arcane Core access.
|
|
128
|
+
It preserves the browser behavior when Core is absent, fails closed for an
|
|
129
|
+
unavailable native-only capability, and never silently substitutes a release
|
|
130
|
+
tree. Starting or refreshing this wrapper does not package, copy to `dist`, or
|
|
131
|
+
run tests automatically. The SDK must not describe native source development as
|
|
132
|
+
available until this wrapper and its explicit capability boundary are actually
|
|
133
|
+
implemented.
|
|
134
|
+
|
|
135
|
+
Package and release verification use a separate explicit boundary. Run
|
|
136
|
+
`arcane package` to generate and verify `dist/<id>`, then use
|
|
137
|
+
`arcane run --target browser` to serve only that verified release. Distribution
|
|
138
|
+
automatically runs the selected application's required tests before accepting,
|
|
139
|
+
serving, or launching `dist` and fails closed on any test failure. The browser
|
|
140
|
+
run command does not substitute source files. If source changes after
|
|
141
|
+
packaging, the prior `dist` remains intentionally unchanged until the next
|
|
142
|
+
explicit package operation. Never use packaged `dist` as the everyday
|
|
143
|
+
development tree, and never treat source-serving behavior as evidence for the
|
|
144
|
+
release artifact.
|
|
145
|
+
|
|
41
146
|
## App and release contract
|
|
42
147
|
|
|
43
148
|
The first SDK version deliberately preserves Arcane's current repository-shaped
|
|
@@ -55,11 +160,13 @@ exact schema-1 `arcane-package.json` for current consumers. Existing Arcane
|
|
|
55
160
|
apps synthesize that descriptor from their schema-1 package plus the current
|
|
56
161
|
native registry during migration.
|
|
57
162
|
|
|
58
|
-
An external app's `arcane-packager.json` has three exact shared routes. They map
|
|
59
|
-
installed SDK runtime to `/arcane`, its vendored strong-type dependency to
|
|
163
|
+
An external app's `arcane-packager.json` has three exact shared routes. They map
|
|
164
|
+
the installed SDK runtime to `/arcane`, its vendored strong-type dependency to
|
|
60
165
|
`/node_modules/strong-type`, and the SDK's `LICENSE`,
|
|
61
|
-
`COMMERCIAL-LICENSE.md`, and `NOTICE` to `/licenses/arcane-os`.
|
|
62
|
-
copy
|
|
166
|
+
`COMMERCIAL-LICENSE.md`, and `NOTICE` to `/licenses/arcane-os`. Development does
|
|
167
|
+
not copy SDK runtime source into the app repository. Distribution materializes
|
|
168
|
+
those exact locked SDK routes inside the portable artifact and verifies their
|
|
169
|
+
immutable inventory, so the finished app has no Arcane OS runtime dependency.
|
|
63
170
|
|
|
64
171
|
Release schema 1 and builder identity `arcane-app-packager-v1` remain unchanged
|
|
65
172
|
because current Arcane native admission treats them as exact contracts. Native
|
|
@@ -93,7 +200,7 @@ length with an EOF growth probe, and rechecked by handle and pathname identity.
|
|
|
93
200
|
Every cumulative path prefix has one case-folded spelling and one file/directory
|
|
94
201
|
kind; prefix topology conflicts and the complete portable Windows device-name
|
|
95
202
|
set fail before creation or admission.
|
|
96
|
-
The current SDK admits only the explicitly compatible `0.
|
|
203
|
+
The current SDK admits only the explicitly compatible `0.2.0` bundle
|
|
97
204
|
generation and rejects zero-byte payload releases.
|
|
98
205
|
|
|
99
206
|
Promotion retains any prior output as an identity-bound backup until the new
|
|
@@ -150,8 +257,11 @@ failure propagation. No app or target loop exists in that scope.
|
|
|
150
257
|
|
|
151
258
|
## Verification receipts
|
|
152
259
|
|
|
153
|
-
Runtime verification binds the exact SDK version,
|
|
154
|
-
runtime inventory, byte counts, and SHA-256 hashes.
|
|
260
|
+
Runtime verification binds the exact SDK version, canonical SDK source
|
|
261
|
+
identity, runtime inventory, byte counts, and SHA-256 hashes. During the
|
|
262
|
+
source-ownership migration it may additionally record imported Arcane OS
|
|
263
|
+
provenance for compatibility bytes, but that field does not transfer canonical
|
|
264
|
+
ownership. Packaging writes the full
|
|
155
265
|
schema-1 release inventory to `ARCANE_APP_RELEASE.json`; its operation result
|
|
156
266
|
returns a deeply immutable, process-authenticated receipt that binds the
|
|
157
267
|
canonical location, app policy, complete inventory, content digest, and verified
|
|
@@ -210,7 +320,7 @@ through SDK-bound verified readers rather than accepting a mutable source path
|
|
|
210
320
|
as authority. Build completion requires provider verification, and later
|
|
211
321
|
verify/run calls receive the exact artifact receipt.
|
|
212
322
|
|
|
213
|
-
The SDK `0.
|
|
323
|
+
The SDK `0.2.0` runtime requires Arcane `0.8.12` or newer. Compatibility
|
|
214
324
|
is contractual rather than exact-version pinning: the prepared Core must meet
|
|
215
325
|
the highest minimum declared by the runtime, selected app, and bundled app
|
|
216
326
|
dependencies; keep each app's Arcane protocol generation; and provide every
|
package/docs/reference/README.md
CHANGED
|
@@ -38,7 +38,7 @@ This repository contains two related, explicitly versioned surfaces:
|
|
|
38
38
|
|
|
39
39
|
| Surface | Source identity | Meaning |
|
|
40
40
|
| --- | --- | --- |
|
|
41
|
-
| SDK and CLI | `arcane-os` `0.1.
|
|
41
|
+
| SDK and CLI | `arcane-os` `0.1.2` | The Node.js toolchain plus the browser-only `arcane-os/ai/browser-wasm` entrypoint in this checkout. |
|
|
42
42
|
| Browser runtime | Arcane OS commit `567ad110bf57a1c2d4a3daa22ae93716cc5f4d7e`, bundle `0.8.12`, protocol `arcane/1` | The exact 155-file runtime snapshot shipped under `runtime/`. |
|
|
43
43
|
| Core reference snapshot | Arcane OS commit `567ad110bf57a1c2d4a3daa22ae93716cc5f4d7e`, protocol `arcane/1` | The application-facing Core contract derived into `docs/reference/core/`. Canonical inventory and focused-member content was verified unchanged at Arcane OS `main` commit `13f3ce0ae34f77a3495331c8b4c30b1bb105f8ed`; SDK-local provenance, link, and package-boundary annotations are added explicitly. |
|
|
44
44
|
|