chainlesschain 0.166.117 → 0.166.121
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/README.md +36 -4
- package/package.json +3 -3
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-C9tSCImB.js → AIOps-DYEi6MCC.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CXKExmT_.js → ActionButton-kLXp42k3.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DE8WBWHo.js → Analytics-BOgKma-R.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BdpwNM08.js → AppLayout-D8z-uRpv.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-94TQpe38.js → Artifacts-TFX0t24T.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DdDEv5Ej.js → Audit-GjRCAv0V.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-TU9nlV7j.js → BackgroundAgents-CpQcMxi1.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-C0GPoUUq.js → Backup-BuQNWrhT.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-iyrVn2I6.js → BaseInput-QuOtSaFy.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Dqo8spwp.js → Chat-BC0W1L_3.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CDqHSzc4.js → ChatBubbleRenderer-B6SagRW5.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BF_XJvQi.js → Checkbox-BsdQ1Ku0.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-5tUWoOa9.js → Codegen-CPsAAoqV.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BQxSeRvG.js → Col-C9LgCMN0.js} +1 -1
- package/src/assets/web-panel/assets/{Community-B2Ra7w-J.js → Community-Xj0vGvD5.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-GjxJwlr0.js → Compact-CXoqTgrf.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BTUP6RTA.js → Compliance-CeshjrVy.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DiwfL16s.js → Cowork-DjXRRrg1.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-1ceOo5ux.js → Cron-vGoU419C.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-aHYxLu0u.js → Crosschain-DZaChZ5s.js} +1 -1
- package/src/assets/web-panel/assets/{DID-eeTjzOLV.js → DID-BojMQqQM.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CT3fpkvX.js → Dashboard-pKAYt4UL.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-B3UpOy-N.js → Dropdown-CqJa8N05.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Ba2Rvoru.js → EmailListRenderer-fZ4_KAvw.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-CGtn93on.js → EvolutionSettings-C3kenWes.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C0IUdHQt.js → FamilyGuardDashboard-B3tqj1Hu.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-1QIDwemn.js → Federation-CS1M1oJN.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-NGbSsdje.js → FormItemContext-C5q-KYPu.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DzVZc2Ba.js → GenericCardRenderer-Bl43BLxD.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B_1TDbSr.js → Git-BiN2Uo-z.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BDl_ySyI.js → Governance-weglJWG4.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CAp-wcCK.js → Inference-B4qBxVSK.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DRjRcxul.js → KnowledgeGraph-iePl1vLv.js} +1 -1
- package/src/assets/web-panel/assets/Logs-t2lKo4HX.js +2 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-CxSrBHYC.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-DfiXL6UO.js → Marketplace-D0Y_OmXs.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-D9rIvFjN.js → McpTools-B7zxPsFE.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-D-p7yDzj.js → Memory-DDpmVHpl.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D0XT5B6m.js → MobileBridge-BLXNdKH0.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CL9cHVIk.js → MobileProjects-CF6DqNfk.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CME4kDmC.js → Mtc-7OFckDUe.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-BjGOXFnn.js → MtcAudit-2QFWPXVR.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-CSzc53e0.js → Multisig-D9opE0iP.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BYjZnaH8.js → NLProgramming-jOD2E7Rs.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BUPOYE0C.js → Notes-CgtysN6L.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BK8YADru.js → NotificationSettings-Itz3X5DM.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-C2ylWgSI.js +1 -0
- package/src/assets/web-panel/assets/{Organization-B0WNlhMd.js → Organization-CKgpbSoy.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CED6ZO87.js → Overflow-C0Pi9LvB.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CBdc8fkP.js → P2P-DdIDorai.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DuauYi03.js → PdhVaultBrowser-DVJz6cGr.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-kfgY0ukN.js → Permissions-D19dtcL0.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CDY3I1Do.js → PersonalDataHub-A-QMQUJW.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-B9x0wKSK.js → Pipeline-7YF1yhkH.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Cqhcr_Qv.js → Privacy-CcsoAU7d.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CFhoyo57.js → ProjectInit-CMU0Z5in.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DCBjV2Hm.js → ProjectSettings-D3l0RXK6.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BCcHN_fQ.js +1 -0
- package/src/assets/web-panel/assets/{Providers-BuCIWZY1.js → Providers-DB0pw3DM.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CEXNL5CB.js → QrScannerModal-SVvLX_VH.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DgK8aLAU.js → QuickAsk-B4dJgwL4.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BxEYQRAs.js → Recommend-9GxSlYt5.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-PRsxNsYU.js → RemoteSession-C5vzfpTM.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-S-tQH70r.js → Reputation-7AeSkJz1.js} +1 -1
- package/src/assets/web-panel/assets/{Row-U1Ws3CJl.js → Row-Dc3RwqWR.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BftCyeii.js → RssFeed-Y8iSgp9o.js} +3 -3
- package/src/assets/web-panel/assets/{Search-o4gK0cPy.js → Search-bxkz-UtW.js} +1 -1
- package/src/assets/web-panel/assets/{Security-U2ZRpsbA.js → Security-cs5yf-AE.js} +3 -3
- package/src/assets/web-panel/assets/{Services-D0tRjWl_.js → Services-2va9CJFi.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DzZ8TFsB.js → Skeleton-n8HOSPcE.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-B1cC7rju.js → Skills-BcV5aWYc.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CtIHOR0J.js → Sla-B73zPlDG.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CD8ZlQpW.js → SpeechSettings-OhhEZsxi.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BilFQWQk.js → SyncSettings-4HT8618-.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Bmg2qLXl.js → Tasks-Dw7nEG94.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-D6yofyC1.js → Templates-BX7zHI9M.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BzttD-Gp.js → Tenant-SrFN1ZTF.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DyRXeYuH.js → Terminal-B-ohas9S.js} +3 -3
- package/src/assets/web-panel/assets/{TimelineRenderer-Bdmkx-Tr.js → TimelineRenderer-exEpcIMD.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-10RcVAVJ.js → Tokens-Fucr0-Ek.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CKV-9DdV.js → Trigger-CPlH4qBU.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BEc-sW1q.js → Trust-DCNisGxI.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BSql8piX.js → UkeySign-Crf4bea0.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C4OH2f4s.js → VideoEditing-JAq5jURW.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BSSvWKmv.js → Wallet-BaOfcyqF.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-LxIzXo9d.js → WebAuthn-DMtCTRsk.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-DWwdfcte.js → WorkflowEditor-BAKiA5p0.js} +1 -1
- package/src/assets/web-panel/assets/{addon-fit-CK6X9sAG.js → addon-fit-CbkjSUJl.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DWxYYLD5.js → chat-D3MA12Z1.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BxEcQqtg.js → colors-C7xYOAG-.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DNbezFOJ.js → compact-item-ClybpIV-.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DaJXc5nx.js → createContext-Dyp-8LSs.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CTazgK0z.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BMrw3sg3.js → hasIn-D4xsaFD1.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cm9DYzhC.js → index-B-eU0wCl.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaByEdBj.js → index-B7jiI-31.js} +1 -1
- package/src/assets/web-panel/assets/{index-T4T84G6X.js → index-BKyEhMUl.js} +1 -1
- package/src/assets/web-panel/assets/{index-LRb0QX6S.js → index-BMybVxE3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dwg38xTG.js → index-BagQvwmy.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bum_aSyd.js → index-BcmJBS7G.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCCd8Rj6.js → index-BfZW-iaH.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9b6UaZl.js → index-BmBqpU1f.js} +1 -1
- package/src/assets/web-panel/assets/{index-6W0V5G2t.js → index-Bn8O_DC1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BR4nWOmR.js → index-BpLWtYzy.js} +1 -1
- package/src/assets/web-panel/assets/{index-2c0bL3SE.js → index-CHJUN9dR.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpSp97J8.js → index-CNBhQp0_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEvdSKT4.js → index-CS7E_bsQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNZDGV2q.js → index-CSOgUWox.js} +1 -1
- package/src/assets/web-panel/assets/{index-VD-dTYQE.js → index-CiRxc4Y0.js} +1 -1
- package/src/assets/web-panel/assets/index-CrZlBv0O.js +1 -0
- package/src/assets/web-panel/assets/{index-CpwgF7vh.js → index-CvsWZZzU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcuytRTa.js → index-Cvwa0445.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_b9_6wQ.js → index-CxSVA3yj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaxzeSIM.js → index-D5aA2hc_.js} +1 -1
- package/src/assets/web-panel/assets/{index-cCqCEa9X.js → index-D6ETHrUX.js} +3 -3
- package/src/assets/web-panel/assets/{index-DzZHBzCb.js → index-D6GFzqYv.js} +1 -1
- package/src/assets/web-panel/assets/{index-hPy_f08G.js → index-DDDP6-gb.js} +1 -1
- package/src/assets/web-panel/assets/{index-NEW5EN2w.js → index-DJVzi25x.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dr18agDl.js → index-DMlcfkHz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DarVV5yG.js → index-DNeHM7F3.js} +1 -1
- package/src/assets/web-panel/assets/{index-7zzLqfvI.js → index-DT57ltH_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAV07GDK.js → index-DWHngGhj.js} +1 -1
- package/src/assets/web-panel/assets/{index-cKBpcInt.js → index-DuGdqMTo.js} +1 -1
- package/src/assets/web-panel/assets/index-DyNp52Uj.js +1 -0
- package/src/assets/web-panel/assets/{index-CL-36X3A.js → index-KvhXXaLi.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiEdfbB9.js → index-Rk6e-ajq.js} +1 -1
- package/src/assets/web-panel/assets/{index-rFaCnjXi.js → index-ZmHm5rHI.js} +1 -1
- package/src/assets/web-panel/assets/{index-IX0g0s2t.js → index-_9loRV63.js} +1 -1
- package/src/assets/web-panel/assets/{index-C25ig6EV.js → index-avRJM_PM.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bs2QyMPQ.js → index-ciHSB-CC.js} +1 -1
- package/src/assets/web-panel/assets/{index-pzYLf5qu.js → index-faSaFsUp.js} +1 -1
- package/src/assets/web-panel/assets/{index-DL0k35f0.js → index-nV4PAkOC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ca2hwPMo.js → index-v5sQDXJx.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BWVLgWhF.js → initDefaultProps-xrojGNIo.js} +1 -1
- package/src/assets/web-panel/assets/{markdown-Yo_c6Krv.js → markdown-Bq2x0XsR.js} +1 -1
- package/src/assets/web-panel/assets/markdown-DUKxbhE4.js +51 -0
- package/src/assets/web-panel/assets/{motion-KJZ1xfDE.js → motion-DzhD1BmE.js} +1 -1
- package/src/assets/web-panel/assets/{move-DGqU6H7M.js → move-BAq6xac8.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-Dg0xkI5J.js → mtc-parser-CbbkHqsY.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B6RnMJAQ.js → omit-DvK7nt_t.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Ctw2i3jb.js → pickAttrs-B8N30ZEF.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-D3F3x8Jm.js → placementArrow-cUxaRoU7.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-W6eHE0nM.js → responsiveObserve-CxMioios.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BlFf8sY5.js → slide-CQx7R0mc.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Cd6LMwmy.js → statusUtils-kUphM3-c.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Bg5-U4Y1.js → styleChecker-BrGZ1xWq.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DTkLS3tn.js → useFlexGapSupport-Sb5Vj_ag.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CwYGzoBt.js → useFs-DrMnDCO5.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-PCgFQXZP.js → usePersonalDataHub-C48ZIkOr.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CJM7MkrB.js → vnode-BQP6PZY_.js} +1 -1
- package/src/assets/web-panel/assets/{xterm-BZcWGsqw.js → xterm-zbskxxGA.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CP0mgaFi.js → zoom-DOJny9bH.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/data/changelog.json +28 -2
- package/src/harness/jsonl-session-store.js +2 -1
- package/src/lib/artifact-store.js +129 -20
- package/src/lib/bounded-artifact-file-read.js +128 -46
- package/src/lib/evolution/agent-evolution-ingress.js +41 -7
- package/src/lib/evolution/agent-evolution-runtime-composition.js +65 -27
- package/src/lib/evolution/evidence-json-text.js +124 -8
- package/src/lib/evolution/evolution-artifact-ports.js +1005 -83
- package/src/lib/evolution/evolution-evidence-artifact-adapter.js +59 -15
- package/src/lib/evolution/evolution-evidence-projector.js +59 -3
- package/src/lib/evolution/evolution-evidence-storage-layout.js +115 -0
- package/src/lib/evolution/evolution-file-witness.js +7 -3
- package/src/lib/evolution/evolution-ledger-file-manifest-catalog.js +6 -2
- package/src/lib/evolution/evolution-ledger-file-manifest-head-store.js +6 -2
- package/src/lib/evolution/evolution-ledger.js +226 -55
- package/src/lib/evolution/evolution-private-storage.js +20 -0
- package/src/lib/evolution/evolution-run-ledger-adapter.js +298 -11
- package/src/lib/evolution/evolution-windows-directory-durability.js +215 -32
- package/src/lib/evolution/local-ui-evolution-runtime.js +3 -38
- package/src/lib/evolution/local-ui-signing-authority.js +69 -0
- package/src/lib/ollama-provider-fetch.js +33 -0
- package/src/lib/provider-stream.js +14 -13
- package/src/lib/session-history-origins.js +71 -32
- package/src/lib/session-message-provenance.js +26 -2
- package/src/lib/session-runtime-retention.js +10 -1
- package/src/lib/session-transcript-migration.js +67 -12
- package/src/lib/storage-failure-cause-codes.js +449 -0
- package/src/runtime/agent-core.js +54 -33
- package/src/runtime/headless-stream.js +89 -10
- package/src/assets/web-panel/assets/Logs-qSuwMwk7.js +0 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BgnYTegK.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-B5F8bnWt.js +0 -1
- package/src/assets/web-panel/assets/Projects-C51w0LnM.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CRyXoGo9.js +0 -1
- package/src/assets/web-panel/assets/index-C-W0Ntk_.js +0 -1
- package/src/assets/web-panel/assets/index-CCk_qhBx.js +0 -1
- package/src/assets/web-panel/assets/markdown-CsiA8-E5.js +0 -50
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
4
5
|
|
|
5
6
|
const require = createRequire(import.meta.url);
|
|
6
7
|
const DIRECTORY_ATTRIBUTE = 0x10;
|
|
@@ -18,16 +19,21 @@ function loadWindowsApi() {
|
|
|
18
19
|
if (windowsApi !== null) return windowsApi;
|
|
19
20
|
const koffi = require("koffi");
|
|
20
21
|
const kernel32 = koffi.load("kernel32.dll");
|
|
22
|
+
// Koffi's named types share a process-wide registry even when this module
|
|
23
|
+
// is loaded in separate realms. Keep each API's ABI declarations private;
|
|
24
|
+
// filesystem brands and the API cache remain local to this module instance.
|
|
25
|
+
const typeScope = randomUUID().replaceAll("-", "");
|
|
26
|
+
const typeName = (name) => `${name}_${typeScope}`;
|
|
21
27
|
const HANDLE = koffi.pointer(
|
|
22
|
-
"ChainlessChainEvolutionDirectoryHandle",
|
|
28
|
+
typeName("ChainlessChainEvolutionDirectoryHandle"),
|
|
23
29
|
koffi.opaque(),
|
|
24
30
|
);
|
|
25
|
-
const FILETIME = koffi.struct("ChainlessChainEvolutionFileTime", {
|
|
31
|
+
const FILETIME = koffi.struct(typeName("ChainlessChainEvolutionFileTime"), {
|
|
26
32
|
lowDateTime: "uint32_t",
|
|
27
33
|
highDateTime: "uint32_t",
|
|
28
34
|
});
|
|
29
35
|
const BY_HANDLE_FILE_INFORMATION = koffi.struct(
|
|
30
|
-
"ChainlessChainEvolutionDirectoryInformation",
|
|
36
|
+
typeName("ChainlessChainEvolutionDirectoryInformation"),
|
|
31
37
|
{
|
|
32
38
|
fileAttributes: "uint32_t",
|
|
33
39
|
creationTime: FILETIME,
|
|
@@ -41,20 +47,86 @@ function loadWindowsApi() {
|
|
|
41
47
|
fileIndexLow: "uint32_t",
|
|
42
48
|
},
|
|
43
49
|
);
|
|
50
|
+
const FILE_ID_128 = koffi.struct(
|
|
51
|
+
typeName("ChainlessChainEvolutionFileId128"),
|
|
52
|
+
{
|
|
53
|
+
low: "uint64_t",
|
|
54
|
+
high: "uint64_t",
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
const FILE_ID_INFO = koffi.struct(
|
|
58
|
+
typeName("ChainlessChainEvolutionFileIdInfo"),
|
|
59
|
+
{
|
|
60
|
+
volumeSerialNumber: "uint64_t",
|
|
61
|
+
fileId: FILE_ID_128,
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
const statFields = {
|
|
65
|
+
fileId: "int64_t",
|
|
66
|
+
creationTime: "int64_t",
|
|
67
|
+
lastAccessTime: "int64_t",
|
|
68
|
+
lastWriteTime: "int64_t",
|
|
69
|
+
changeTime: "int64_t",
|
|
70
|
+
allocationSize: "int64_t",
|
|
71
|
+
endOfFile: "int64_t",
|
|
72
|
+
fileAttributes: "uint32_t",
|
|
73
|
+
reparseTag: "uint32_t",
|
|
74
|
+
numberOfLinks: "uint32_t",
|
|
75
|
+
};
|
|
76
|
+
const FILE_STAT_INFORMATION = koffi.struct(
|
|
77
|
+
typeName("ChainlessChainEvolutionFileStatInformation"),
|
|
78
|
+
{ ...statFields, effectiveAccess: "uint32_t" },
|
|
79
|
+
);
|
|
80
|
+
const FILE_STAT_BASIC_INFORMATION = koffi.struct(
|
|
81
|
+
typeName("ChainlessChainEvolutionFileStatBasicInformation"),
|
|
82
|
+
{
|
|
83
|
+
...statFields,
|
|
84
|
+
deviceType: "uint32_t",
|
|
85
|
+
deviceCharacteristics: "uint32_t",
|
|
86
|
+
reserved: "uint32_t",
|
|
87
|
+
// Windows' documented layout; libuv 1.49 placed these in reverse order.
|
|
88
|
+
volumeSerialNumber: "uint64_t",
|
|
89
|
+
fileId128: FILE_ID_128,
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
const IO_STATUS_BLOCK = koffi.struct(
|
|
93
|
+
typeName("ChainlessChainEvolutionIoStatus"),
|
|
94
|
+
{
|
|
95
|
+
status: "intptr_t",
|
|
96
|
+
information: "uintptr_t",
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
let GetFileInformationByName = null;
|
|
100
|
+
try {
|
|
101
|
+
const fileApi = koffi.load("api-ms-win-core-file-l2-1-4.dll");
|
|
102
|
+
GetFileInformationByName = fileApi.func(
|
|
103
|
+
`bool __stdcall GetFileInformationByName(str16 fileName, int informationClass, _Out_ ${typeName("ChainlessChainEvolutionFileStatBasicInformation")} *information, uint32_t size)`,
|
|
104
|
+
);
|
|
105
|
+
} catch {
|
|
106
|
+
// Older Windows versions use libuv's handle-based stat path instead.
|
|
107
|
+
}
|
|
108
|
+
const ntdll = koffi.load("ntdll.dll");
|
|
44
109
|
windowsApi = Object.freeze({
|
|
45
110
|
koffi,
|
|
46
111
|
HANDLE,
|
|
47
112
|
CreateFileW: kernel32.func(
|
|
48
|
-
"ChainlessChainEvolutionDirectoryHandle __stdcall CreateFileW(str16 fileName, uint32_t desiredAccess, uint32_t shareMode, void *securityAttributes, uint32_t creationDisposition, uint32_t flagsAndAttributes, void *templateFile)
|
|
113
|
+
`${typeName("ChainlessChainEvolutionDirectoryHandle")} __stdcall CreateFileW(str16 fileName, uint32_t desiredAccess, uint32_t shareMode, void *securityAttributes, uint32_t creationDisposition, uint32_t flagsAndAttributes, void *templateFile)`,
|
|
49
114
|
),
|
|
50
115
|
FlushFileBuffers: kernel32.func(
|
|
51
|
-
|
|
116
|
+
`bool __stdcall FlushFileBuffers(${typeName("ChainlessChainEvolutionDirectoryHandle")} handle)`,
|
|
52
117
|
),
|
|
53
118
|
GetDriveTypeW: kernel32.func(
|
|
54
119
|
"uint32_t __stdcall GetDriveTypeW(str16 rootPathName)",
|
|
55
120
|
),
|
|
56
121
|
GetFileInformationByHandle: kernel32.func(
|
|
57
|
-
|
|
122
|
+
`bool __stdcall GetFileInformationByHandle(${typeName("ChainlessChainEvolutionDirectoryHandle")} handle, _Out_ ${typeName("ChainlessChainEvolutionDirectoryInformation")} *information)`,
|
|
123
|
+
),
|
|
124
|
+
GetFileInformationByHandleEx: kernel32.func(
|
|
125
|
+
`bool __stdcall GetFileInformationByHandleEx(${typeName("ChainlessChainEvolutionDirectoryHandle")} handle, int informationClass, _Out_ ${typeName("ChainlessChainEvolutionFileIdInfo")} *information, uint32_t size)`,
|
|
126
|
+
),
|
|
127
|
+
GetFileInformationByName,
|
|
128
|
+
NtQueryInformationFile: ntdll.func(
|
|
129
|
+
`int32_t __stdcall NtQueryInformationFile(${typeName("ChainlessChainEvolutionDirectoryHandle")} handle, _Out_ ${typeName("ChainlessChainEvolutionIoStatus")} *ioStatus, _Out_ ${typeName("ChainlessChainEvolutionFileStatInformation")} *information, uint32_t size, int informationClass)`,
|
|
58
130
|
),
|
|
59
131
|
GetLastError: kernel32.func("uint32_t __stdcall GetLastError()"),
|
|
60
132
|
GetVolumeInformationW: kernel32.func(
|
|
@@ -64,9 +136,13 @@ function loadWindowsApi() {
|
|
|
64
136
|
"bool __stdcall GetVolumePathNameW(str16 fileName, _Out_ uint16_t *volumePathName, uint32_t bufferLength)",
|
|
65
137
|
),
|
|
66
138
|
CloseHandle: kernel32.func(
|
|
67
|
-
|
|
139
|
+
`bool __stdcall CloseHandle(${typeName("ChainlessChainEvolutionDirectoryHandle")} handle)`,
|
|
68
140
|
),
|
|
69
141
|
BY_HANDLE_FILE_INFORMATION,
|
|
142
|
+
FILE_ID_INFO,
|
|
143
|
+
FILE_STAT_INFORMATION,
|
|
144
|
+
FILE_STAT_BASIC_INFORMATION,
|
|
145
|
+
IO_STATUS_BLOCK,
|
|
70
146
|
});
|
|
71
147
|
return windowsApi;
|
|
72
148
|
}
|
|
@@ -137,7 +213,78 @@ function fileId(information) {
|
|
|
137
213
|
);
|
|
138
214
|
}
|
|
139
215
|
|
|
140
|
-
|
|
216
|
+
// These Win32 APIs expose a DWORD serial. Normalize serial representations
|
|
217
|
+
// only across those APIs, retaining full stat.dev for path-change checks.
|
|
218
|
+
// Node 22.12's fast-stat ABI defect needs a separate, handle-bound projection.
|
|
219
|
+
function windowsVolumeSerial(device) {
|
|
220
|
+
return BigInt.asUintN(32, BigInt(device));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function matchesFastNodeDirectoryIdentity(api, handle, directory, expected) {
|
|
224
|
+
if (!api.GetFileInformationByName) return false;
|
|
225
|
+
const handleIdentity = {};
|
|
226
|
+
requireWindowsSuccess(
|
|
227
|
+
api,
|
|
228
|
+
api.GetFileInformationByHandleEx(
|
|
229
|
+
handle,
|
|
230
|
+
18,
|
|
231
|
+
handleIdentity,
|
|
232
|
+
api.koffi.sizeof(api.FILE_ID_INFO),
|
|
233
|
+
),
|
|
234
|
+
);
|
|
235
|
+
const handleStat = {};
|
|
236
|
+
const status = api.NtQueryInformationFile(
|
|
237
|
+
handle,
|
|
238
|
+
{},
|
|
239
|
+
handleStat,
|
|
240
|
+
api.koffi.sizeof(api.FILE_STAT_INFORMATION),
|
|
241
|
+
68,
|
|
242
|
+
);
|
|
243
|
+
if (status < 0) {
|
|
244
|
+
throw new Error("Windows local UI native directory identity unavailable");
|
|
245
|
+
}
|
|
246
|
+
const pathStat = {};
|
|
247
|
+
requireWindowsSuccess(
|
|
248
|
+
api,
|
|
249
|
+
api.GetFileInformationByName(
|
|
250
|
+
win32Path(directory),
|
|
251
|
+
3,
|
|
252
|
+
pathStat,
|
|
253
|
+
api.koffi.sizeof(api.FILE_STAT_BASIC_INFORMATION),
|
|
254
|
+
),
|
|
255
|
+
);
|
|
256
|
+
const pathFileId = BigInt.asUintN(64, BigInt(pathStat.fileId));
|
|
257
|
+
if (
|
|
258
|
+
(Number(pathStat.fileAttributes) & DIRECTORY_ATTRIBUTE) === 0 ||
|
|
259
|
+
(Number(pathStat.fileAttributes) & REPARSE_POINT_ATTRIBUTE) !== 0 ||
|
|
260
|
+
BigInt(pathStat.volumeSerialNumber) !==
|
|
261
|
+
BigInt(handleIdentity.volumeSerialNumber) ||
|
|
262
|
+
BigInt(pathStat.fileId128.low) !== BigInt(handleIdentity.fileId.low) ||
|
|
263
|
+
BigInt(pathStat.fileId128.high) !== BigInt(handleIdentity.fileId.high) ||
|
|
264
|
+
pathFileId !== BigInt.asUintN(64, BigInt(handleStat.fileId))
|
|
265
|
+
) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
"Windows local UI path identity does not match the directory handle",
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
// Same projections as windows-sandbox.cs: pair dev and ino from one stat
|
|
271
|
+
// implementation, only after binding the correctly laid-out path result to
|
|
272
|
+
// this handle's complete volume/128-bit ID. Never treat dev === 0 as a bypass.
|
|
273
|
+
return (
|
|
274
|
+
pathFileId === BigInt(expected.stat.ino) &&
|
|
275
|
+
(BigInt(expected.stat.dev) ===
|
|
276
|
+
windowsVolumeSerial(pathStat.volumeSerialNumber) ||
|
|
277
|
+
BigInt(expected.stat.dev) === BigInt(pathStat.fileId128.high))
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function assertDirectoryHandleIdentity(
|
|
282
|
+
api,
|
|
283
|
+
handle,
|
|
284
|
+
fsImpl,
|
|
285
|
+
directory,
|
|
286
|
+
expected,
|
|
287
|
+
) {
|
|
141
288
|
const information = {};
|
|
142
289
|
requireWindowsSuccess(
|
|
143
290
|
api,
|
|
@@ -147,9 +294,43 @@ function assertDirectoryHandleIdentity(api, handle, fsImpl, directory, expected)
|
|
|
147
294
|
if (
|
|
148
295
|
(attributes & DIRECTORY_ATTRIBUTE) === 0 ||
|
|
149
296
|
(attributes & REPARSE_POINT_ATTRIBUTE) !== 0 ||
|
|
150
|
-
BigInt(information.volumeSerialNumber) !==
|
|
151
|
-
|
|
297
|
+
BigInt(information.volumeSerialNumber) !== expected.volumeSerialNumber
|
|
298
|
+
) {
|
|
299
|
+
throw new Error(
|
|
300
|
+
"Windows local UI durability handle does not match the requested directory",
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
const expectedDevice = BigInt(expected.stat.dev);
|
|
304
|
+
const matchingLegacyFileId =
|
|
305
|
+
fileId(information) === BigInt(expected.stat.ino);
|
|
306
|
+
let legacyIdentityMatches =
|
|
307
|
+
matchingLegacyFileId &&
|
|
308
|
+
BigInt(information.volumeSerialNumber) === expectedDevice;
|
|
309
|
+
if (
|
|
310
|
+
!legacyIdentityMatches &&
|
|
311
|
+
matchingLegacyFileId &&
|
|
312
|
+
expectedDevice > 0xffffffffn
|
|
152
313
|
) {
|
|
314
|
+
const fullIdentity = {};
|
|
315
|
+
requireWindowsSuccess(
|
|
316
|
+
api,
|
|
317
|
+
api.GetFileInformationByHandleEx(
|
|
318
|
+
handle,
|
|
319
|
+
18,
|
|
320
|
+
fullIdentity,
|
|
321
|
+
api.koffi.sizeof(api.FILE_ID_INFO),
|
|
322
|
+
),
|
|
323
|
+
);
|
|
324
|
+
legacyIdentityMatches =
|
|
325
|
+
expectedDevice === BigInt(fullIdentity.volumeSerialNumber);
|
|
326
|
+
}
|
|
327
|
+
const fastIdentityMatches = matchesFastNodeDirectoryIdentity(
|
|
328
|
+
api,
|
|
329
|
+
handle,
|
|
330
|
+
expected.resolvedPath,
|
|
331
|
+
expected,
|
|
332
|
+
);
|
|
333
|
+
if (!legacyIdentityMatches && !fastIdentityMatches) {
|
|
153
334
|
throw new Error(
|
|
154
335
|
"Windows local UI durability handle does not match the requested directory",
|
|
155
336
|
);
|
|
@@ -169,12 +350,15 @@ function assertDirectoryHandleIdentity(api, handle, fsImpl, directory, expected)
|
|
|
169
350
|
/** Flush one verified directory on a local NTFS volume using a write handle. */
|
|
170
351
|
export function flushWindowsLocalNtfsDirectory(
|
|
171
352
|
directory,
|
|
172
|
-
{ fsImpl = fs } = {},
|
|
353
|
+
{ fsImpl = fs, nativeApi = null } = {},
|
|
173
354
|
) {
|
|
174
355
|
if (process.platform !== "win32") {
|
|
175
356
|
throw new Error("Windows directory flush is only available on Windows");
|
|
176
357
|
}
|
|
177
|
-
const api =
|
|
358
|
+
const api =
|
|
359
|
+
typeof nativeApi === "function"
|
|
360
|
+
? nativeApi(loadWindowsApi())
|
|
361
|
+
: nativeApi || loadWindowsApi();
|
|
178
362
|
const before = pathIdentity(fsImpl, directory);
|
|
179
363
|
const volumePath = Buffer.alloc(520);
|
|
180
364
|
requireWindowsSuccess(
|
|
@@ -203,18 +387,18 @@ export function flushWindowsLocalNtfsDirectory(
|
|
|
203
387
|
);
|
|
204
388
|
if (
|
|
205
389
|
driveType !== DRIVE_FIXED ||
|
|
206
|
-
readWideString(fileSystemName).toLowerCase() !== "ntfs"
|
|
207
|
-
BigInt(volumeSerialNumber[0]) !== BigInt(before.stat.dev)
|
|
390
|
+
readWideString(fileSystemName).toLowerCase() !== "ntfs"
|
|
208
391
|
) {
|
|
209
392
|
throw new Error(
|
|
210
393
|
"Windows local UI durable storage requires a local NTFS directory",
|
|
211
394
|
);
|
|
212
395
|
}
|
|
396
|
+
const expected = {
|
|
397
|
+
...before,
|
|
398
|
+
volumeSerialNumber: BigInt(volumeSerialNumber[0]),
|
|
399
|
+
};
|
|
213
400
|
|
|
214
|
-
const invalidHandle = BigInt.asUintN(
|
|
215
|
-
api.koffi.sizeof("intptr_t") * 8,
|
|
216
|
-
-1n,
|
|
217
|
-
);
|
|
401
|
+
const invalidHandle = BigInt.asUintN(api.koffi.sizeof("intptr_t") * 8, -1n);
|
|
218
402
|
const handle = api.CreateFileW(
|
|
219
403
|
win32Path(before.resolvedPath),
|
|
220
404
|
GENERIC_READ_WRITE,
|
|
@@ -231,25 +415,13 @@ export function flushWindowsLocalNtfsDirectory(
|
|
|
231
415
|
|
|
232
416
|
let operationError = null;
|
|
233
417
|
try {
|
|
234
|
-
assertDirectoryHandleIdentity(
|
|
235
|
-
api,
|
|
236
|
-
handle,
|
|
237
|
-
fsImpl,
|
|
238
|
-
directory,
|
|
239
|
-
before,
|
|
240
|
-
);
|
|
418
|
+
assertDirectoryHandleIdentity(api, handle, fsImpl, directory, expected);
|
|
241
419
|
const flushed = api.FlushFileBuffers(handle);
|
|
242
420
|
if (!flushed) {
|
|
243
421
|
const errorCode = api.GetLastError();
|
|
244
422
|
throw windowsError("FlushFileBuffers", errorCode);
|
|
245
423
|
}
|
|
246
|
-
assertDirectoryHandleIdentity(
|
|
247
|
-
api,
|
|
248
|
-
handle,
|
|
249
|
-
fsImpl,
|
|
250
|
-
directory,
|
|
251
|
-
before,
|
|
252
|
-
);
|
|
424
|
+
assertDirectoryHandleIdentity(api, handle, fsImpl, directory, expected);
|
|
253
425
|
} catch (error) {
|
|
254
426
|
operationError = error;
|
|
255
427
|
throw error;
|
|
@@ -282,6 +454,17 @@ export function isLocalUiEvolutionFileSystem(fsImpl) {
|
|
|
282
454
|
);
|
|
283
455
|
}
|
|
284
456
|
|
|
457
|
+
// Only the original adapter inherits every filesystem operation unchanged.
|
|
458
|
+
// A branded object with injected operations must keep its dependency boundary.
|
|
459
|
+
export function isUnmodifiedLocalUiEvolutionFileSystem(fsImpl) {
|
|
460
|
+
return (
|
|
461
|
+
isLocalUiEvolutionFileSystem(fsImpl) &&
|
|
462
|
+
Object.getPrototypeOf(fsImpl) === fs &&
|
|
463
|
+
Reflect.ownKeys(fsImpl).length === 1 &&
|
|
464
|
+
Object.hasOwn(fsImpl, "syncDirectorySync")
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
285
468
|
export function ensureDirectoryTreeDurable(fsImpl, directory) {
|
|
286
469
|
if (typeof fsImpl?.syncDirectorySync !== "function") {
|
|
287
470
|
fsImpl.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
@@ -21,8 +21,10 @@ import {
|
|
|
21
21
|
EVOLUTION_SOURCE_VERIFICATION_SCHEMA,
|
|
22
22
|
} from "./evolution-evidence-projector.js";
|
|
23
23
|
import { createAgentEvolutionRuntimeComposition } from "./agent-evolution-runtime-composition.js";
|
|
24
|
+
import { EVOLUTION_TYPED_EVIDENCE_LAYOUT } from "./evolution-evidence-storage-layout.js";
|
|
24
25
|
import { loadLocalUiEvolutionDeployment } from "./local-ui-evolution-deployment.js";
|
|
25
26
|
import { createLocalUiEvolutionFileSystem } from "./evolution-windows-directory-durability.js";
|
|
27
|
+
import { createLocalUiSigningAuthority as ed25519Authority } from "./local-ui-signing-authority.js";
|
|
26
28
|
import {
|
|
27
29
|
formatLocalUiTenantReferenceId,
|
|
28
30
|
encodeOpaqueHexId,
|
|
@@ -104,44 +106,6 @@ function encodedMessage(message) {
|
|
|
104
106
|
return Buffer.from(canonical(message), "utf8");
|
|
105
107
|
}
|
|
106
108
|
|
|
107
|
-
function ed25519Authority(label, tenantId, privateKeyPem) {
|
|
108
|
-
const privateKey = createPrivateKey(privateKeyPem);
|
|
109
|
-
const publicKey = createPublicKey(privateKey);
|
|
110
|
-
const trust = Object.freeze({
|
|
111
|
-
algorithm: "ed25519",
|
|
112
|
-
keyId: `local://${tenantId}/${label}`,
|
|
113
|
-
trustPolicyDigest: sha256(`chainlesschain.local-ui.${label}.policy/v1`),
|
|
114
|
-
});
|
|
115
|
-
return Object.freeze({
|
|
116
|
-
trust,
|
|
117
|
-
signer: Object.freeze({
|
|
118
|
-
sign({ message }) {
|
|
119
|
-
return {
|
|
120
|
-
...trust,
|
|
121
|
-
value: sign(null, encodedMessage(message), privateKey).toString(
|
|
122
|
-
"base64url",
|
|
123
|
-
),
|
|
124
|
-
};
|
|
125
|
-
},
|
|
126
|
-
}),
|
|
127
|
-
verifier: Object.freeze({
|
|
128
|
-
verify({ message, signature }) {
|
|
129
|
-
return (
|
|
130
|
-
signature?.algorithm === trust.algorithm &&
|
|
131
|
-
signature?.keyId === trust.keyId &&
|
|
132
|
-
signature?.trustPolicyDigest === trust.trustPolicyDigest &&
|
|
133
|
-
verify(
|
|
134
|
-
null,
|
|
135
|
-
encodedMessage(message),
|
|
136
|
-
publicKey,
|
|
137
|
-
Buffer.from(signature.value || "", "base64url"),
|
|
138
|
-
)
|
|
139
|
-
);
|
|
140
|
-
},
|
|
141
|
-
}),
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
109
|
function ed25519ArtifactAuthority(tenantId, privateKeyPem) {
|
|
146
110
|
const privateKey = createPrivateKey(privateKeyPem);
|
|
147
111
|
const publicKey = createPublicKey(privateKey);
|
|
@@ -608,6 +572,7 @@ export async function createLocalUiEvolutionComposition({
|
|
|
608
572
|
audience:
|
|
609
573
|
commandName === "ui" ? "local-ui-quick-ask" : `local-${commandName}`,
|
|
610
574
|
stateRootDir,
|
|
575
|
+
evidenceStorageLayout: EVOLUTION_TYPED_EVIDENCE_LAYOUT,
|
|
611
576
|
witnessId: `local-ui-witness-${identity.tenantId}`,
|
|
612
577
|
authorities: {
|
|
613
578
|
...identity.authorities,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createHash,
|
|
3
|
+
createPrivateKey,
|
|
4
|
+
createPublicKey,
|
|
5
|
+
sign,
|
|
6
|
+
verify,
|
|
7
|
+
} from "node:crypto";
|
|
8
|
+
|
|
9
|
+
function canonical(value) {
|
|
10
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
11
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
12
|
+
return `{${Object.keys(value)
|
|
13
|
+
.sort()
|
|
14
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
15
|
+
.join(",")}}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function encodedMessage(message) {
|
|
19
|
+
if (typeof message === "string") return Buffer.from(message, "utf8");
|
|
20
|
+
if (Buffer.isBuffer(message)) return message;
|
|
21
|
+
return Buffer.from(canonical(message), "utf8");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Process-local immutable key/policy; external revocable authorities differ. */
|
|
25
|
+
export function createLocalUiSigningAuthority(label, tenantId, privateKeyPem) {
|
|
26
|
+
const privateKey = createPrivateKey(privateKeyPem);
|
|
27
|
+
const publicKey = createPublicKey(privateKey);
|
|
28
|
+
const trust = Object.freeze({
|
|
29
|
+
algorithm: "ed25519",
|
|
30
|
+
keyId: `local://${tenantId}/${label}`,
|
|
31
|
+
trustPolicyDigest: `sha256:${createHash("sha256").update(`chainlesschain.local-ui.${label}.policy/v1`).digest("hex")}`,
|
|
32
|
+
});
|
|
33
|
+
// Every verifier input is captured and immutable for this authority's entire
|
|
34
|
+
// lifetime. The existing witness cache still rehashes fresh stored bytes.
|
|
35
|
+
const trustEpoch = `sha256:${createHash("sha256")
|
|
36
|
+
.update(publicKey.export({ type: "spki", format: "der" }))
|
|
37
|
+
.update("\0")
|
|
38
|
+
.update(canonical(trust))
|
|
39
|
+
.digest("hex")}`;
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
trust,
|
|
42
|
+
signer: Object.freeze({
|
|
43
|
+
sign({ message }) {
|
|
44
|
+
return {
|
|
45
|
+
...trust,
|
|
46
|
+
value: sign(null, encodedMessage(message), privateKey).toString(
|
|
47
|
+
"base64url",
|
|
48
|
+
),
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
verifier: Object.freeze({
|
|
53
|
+
getTrustEpoch: () => trustEpoch,
|
|
54
|
+
verify({ message, signature }) {
|
|
55
|
+
return (
|
|
56
|
+
signature?.algorithm === trust.algorithm &&
|
|
57
|
+
signature?.keyId === trust.keyId &&
|
|
58
|
+
signature?.trustPolicyDigest === trust.trustPolicyDigest &&
|
|
59
|
+
verify(
|
|
60
|
+
null,
|
|
61
|
+
encodedMessage(message),
|
|
62
|
+
publicKey,
|
|
63
|
+
Buffer.from(signature.value || "", "base64url"),
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Agent } from "undici";
|
|
2
|
+
import {
|
|
3
|
+
hasFormalBudgetProviderScope,
|
|
4
|
+
providerFetch,
|
|
5
|
+
} from "./formal-budget-provider-scope.js";
|
|
6
|
+
|
|
7
|
+
let dispatcher;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Do not reuse an idle Ollama socket across agent turns. Long synchronous
|
|
11
|
+
* repository work can delay processing a peer's close until the next call.
|
|
12
|
+
* pipelining: 0 disables keep-alive in undici, giving each request a fresh
|
|
13
|
+
* connection without replaying a model call after an ambiguous failure.
|
|
14
|
+
*/
|
|
15
|
+
export function ollamaProviderFetch(
|
|
16
|
+
input,
|
|
17
|
+
init = {},
|
|
18
|
+
fetchImpl = globalThis.fetch,
|
|
19
|
+
) {
|
|
20
|
+
// A governed transport owns its physical HTTP policy and rejects adapter
|
|
21
|
+
// transport overrides. Leave its request shape and dispatcher untouched.
|
|
22
|
+
if (hasFormalBudgetProviderScope())
|
|
23
|
+
return providerFetch(input, init, fetchImpl);
|
|
24
|
+
return providerFetch(
|
|
25
|
+
input,
|
|
26
|
+
{
|
|
27
|
+
...init,
|
|
28
|
+
dispatcher:
|
|
29
|
+
init.dispatcher ?? (dispatcher ??= new Agent({ pipelining: 0 })),
|
|
30
|
+
},
|
|
31
|
+
fetchImpl,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
|
|
11
11
|
import { BUILT_IN_PROVIDERS } from "./llm-providers.js";
|
|
12
12
|
import { normalizeOllamaBaseUrl } from "./ollama-base-url.js";
|
|
13
|
+
import { ollamaProviderFetch } from "./ollama-provider-fetch.js";
|
|
13
14
|
import {
|
|
14
15
|
geminiGenerateContentUrl,
|
|
15
16
|
geminiRequestBody,
|
|
@@ -59,7 +60,7 @@ export async function* ollamaTokenStream({
|
|
|
59
60
|
requireCompletion = false,
|
|
60
61
|
}) {
|
|
61
62
|
const normalizedBaseUrl = normalizeOllamaBaseUrl(baseUrl);
|
|
62
|
-
const res = await
|
|
63
|
+
const res = await ollamaProviderFetch(
|
|
63
64
|
`${normalizedBaseUrl}/api/${messages ? "chat" : "generate"}`,
|
|
64
65
|
{
|
|
65
66
|
method: "POST",
|
|
@@ -107,18 +108,18 @@ export async function* openAIStream({
|
|
|
107
108
|
const res = await providerFetch(
|
|
108
109
|
`${normalizeProviderBaseUrl(baseUrl)}/chat/completions`,
|
|
109
110
|
{
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers: {
|
|
113
|
+
"Content-Type": "application/json",
|
|
114
|
+
Authorization: `Bearer ${apiKey}`,
|
|
115
|
+
},
|
|
116
|
+
body: JSON.stringify({
|
|
117
|
+
model,
|
|
118
|
+
messages: messages ?? [{ role: "user", content: prompt }],
|
|
119
|
+
stream: true,
|
|
120
|
+
...(governed ? { stream_options: { include_usage: true } } : {}),
|
|
121
|
+
}),
|
|
122
|
+
signal,
|
|
122
123
|
},
|
|
123
124
|
);
|
|
124
125
|
if (!res.ok || !res.body) {
|