chainlesschain 0.166.45 → 0.166.47
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 +6 -4
- package/package.json +3 -1
- package/src/assets/web-panel/assets/{AIOps-CrMhYNeH.js → AIOps-CDpT0WXV.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DLY5blZ3.js → ActionButton-BNfUljWx.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C0NhKQB0.js → Analytics-C9323DEx.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DLyepgMq.js → AppLayout-CRrjb2db.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-Depk01Sa.js → Artifacts-Db-DGqJS.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-jKe6NQ3J.js → Audit-CJdHRJ0W.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CG4GKT9s.js → BackgroundAgents-_RSCnTYs.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DYWCkzwM.js → Backup-8GFQi-dM.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-xm5vodmJ.js → BaseInput-C87NtgUZ.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-B20_PNd3.js → Chat-DcTMWS8G.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-D4d-eh0F.js → ChatBubbleRenderer-5ZDPTW_H.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CfgwkV_r.js → Checkbox-DM9EmYee.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DbnoWI44.js → Codegen-M2YqDRNN.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CfzzhgGw.js → Col-oB1LLxyf.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BU7mGM8p.js → Community-TcVZK5tF.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DpYim0an.js → Compact-XKmq-3K_.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BNJcO6ra.js → Compliance-C6CBiDRy.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CecZ-pWj.js → Cowork-Dz9CGqXH.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-BG_C1fe3.js → Cron-CxJDgDVL.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BAeILQLL.js → Crosschain-DMP0JsaQ.js} +1 -1
- package/src/assets/web-panel/assets/{DID-CYqOEkXi.js → DID-I5IQeogb.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BYU8wmL1.js → Dashboard-BfLW2MXj.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DYlJeTTa.js → Dropdown-BK8EjAlh.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C0M6py3K.js → EmailListRenderer-C3iqSv5I.js} +1 -1
- package/src/assets/web-panel/assets/EvolutionSettings-BJmBzpW6.js +1 -0
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Du90iDch.js → FamilyGuardDashboard-T19rnqm-.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DkFp_hzB.js → Federation-CozrbPMy.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BGgtGTfG.js → FormItemContext-k1dCLNNJ.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BIDsSCei.js → GenericCardRenderer-DbaW_Jvt.js} +1 -1
- package/src/assets/web-panel/assets/{Git--d3Te93J.js → Git-CQHRyEIt.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CZwD0vmO.js → Governance-BMJYZgMy.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DiDcSFEW.js → Inference-B7mpNU7V.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Bi0hSfzg.js → KnowledgeGraph-Br0Blg6B.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-C8NHSc0K.js → Logs-HIdk0S6M.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DfDuADqF.js → MarkdownRenderer-HxeocBHy.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-D6kD0bNf.js → Marketplace-Bkfq69UP.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-mTSamCr5.js → McpTools-dtmx0bHO.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-YQBXfLjU.js → Memory-CrRlOuEf.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D2awuupp.js → MobileBridge-D4ZIZiAH.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-Bl7lZc_-.js → MobileProjects-C90Rw7GE.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CBqjLK2Q.js → Mtc-CC6k_uh4.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-C4poRInZ.js → MtcAudit-T_CUIYUy.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-C_J0zXYZ.js → Multisig-N_e0RRWk.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Ci43sWOU.js → NLProgramming-Iq3VzEv0.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DN7A-GFZ.js → Notes-B5sKJUJt.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-D5jNm71V.js → NotificationSettings-K8UVp8qQ.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C9UsPOG8.js → OrderTableRenderer-BWNyM-6m.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BapYMJrP.js → Organization-DMO1NJxI.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BwxwjhFw.js → Overflow-BBY6b31Z.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-TObXXOu3.js → P2P-DmsL0y0v.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-UwJvMbFO.js → PdhVaultBrowser-C65AX_RY.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Damnxv-O.js → Permissions-CWcpRtWa.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-d_7jlBb-.js → PersonalDataHub--ZNDIdsn.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DA87VjLw.js → Pipeline-DCdj5NGb.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DzO_UvWn.js → Privacy-BRKu7Q3E.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CwqECDhs.js → ProjectInit-BJ7ozIAb.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CT5YdIl6.js → ProjectSettings-B2cVLiXW.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-D3KdZPCv.js → Projects-Ce9r_W-p.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BjBIS8kk.js → Providers-BDespvGZ.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-mMFqiKkI.js → QrScannerModal-BUNO4fDU.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BwGi9ZU1.js → QuickAsk-D2TUqeBV.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BY65SR5s.js → Recommend-BNNQPHBH.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BGsVH_XU.js → RemoteSession-D2t1V_Cm.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DpPbxSgI.js → Reputation-DVK5hEqI.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B5YBYoVz.js → Row-BB9RTwmE.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DvM5jaoh.js → RssFeed-DmEO7liZ.js} +2 -2
- package/src/assets/web-panel/assets/{Search-6RX39EPO.js → Search-UwEYjnDB.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Beal-7cM.js → Security-DebqSsMp.js} +4 -4
- package/src/assets/web-panel/assets/{Services-D6rjVLe-.js → Services-CuuPts3C.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Dur7R7vz.js → Skeleton-DbWpmbgf.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-D1QkGwMk.js → Skills-BaK7SxzT.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-3ObrX8Es.js → Sla-BFHv51Jb.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DG_KAiH1.js → SpeechSettings-B9b1R6uK.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BaHq0SJ2.js → SyncSettings-BnO0CN02.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BDpAkBAB.js → Tasks-Dy01Vq49.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-tYdoRd-c.js → Templates-BCy2AMHc.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-yGS5zIQ1.js → Tenant-4Tx9y_Y1.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B3QbmVFa.js → Terminal-CPOoUH8J.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-B_7Pr5dS.js → TimelineRenderer-C85GlJzu.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DPvpDa53.js → Tokens-_xneFBGF.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DcSAD0GZ.js → Trigger-BizG4rTr.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BBzEq-uy.js → Trust-Dh8EIn3n.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Dssav7-5.js → UkeySign-C5NWoHWQ.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CPOZf4yi.js → VideoEditing-0YfrySL6.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BzBVRXEq.js → Wallet-BZVH8Ags.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DdSGaCCh.js → WebAuthn-kNofa6vm.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CKjBFoSs.js → WorkflowEditor-BNO34_j0.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B9aMGLg0.js → chat-ifQ2FlwD.js} +1 -1
- package/src/assets/web-panel/assets/{colors-GFCMctSV.js → colors-1TL8H6EH.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BqSDFAYJ.js → compact-item-DBfRjs_T.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BwUm3Ffp.js → createContext-Bs6V4iBf.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BHnbnuYL.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DN1kMJOT.js → hasIn-Bz6LMY_0.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4dI_csl.js → index--WvT2Il3.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7Yu5wwV.js → index-84mi5rsv.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAK28GY8.js → index-B5SJCUZ7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmEHzOsw.js → index-B6RClMZl.js} +1 -1
- package/src/assets/web-panel/assets/{index-By9GbJhu.js → index-BB1Af7Fv.js} +1 -1
- package/src/assets/web-panel/assets/{index-7W1e9Vi4.js → index-BQL_tldd.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDL2MwxN.js → index-BXJXMaZe.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEMl-Qte.js → index-BhPDhmvN.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCnbC2z7.js → index-BiZ7Nz5-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cwih67e8.js → index-BoCTDcn6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DI90Wo_P.js → index-BxqC3LiO.js} +1 -1
- package/src/assets/web-panel/assets/{index-THsr_UUJ.js → index-C6c0j-Ki.js} +1 -1
- package/src/assets/web-panel/assets/{index-PKwZkQDC.js → index-C7d6QlfR.js} +1 -1
- package/src/assets/web-panel/assets/{index-rCTtYCGu.js → index-C9QYxq61.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ci0zIpSX.js → index-CBh48_Mh.js} +1 -1
- package/src/assets/web-panel/assets/{index-tMF_deZb.js → index-CNKHoXWI.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9Pe-EyB.js → index-CXVBQ82k.js} +3 -3
- package/src/assets/web-panel/assets/{index-B4MfxM5W.js → index-CXtiLagi.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaFr7XB1.js → index-Cn7EIWmI.js} +1 -1
- package/src/assets/web-panel/assets/index-CsRz9-ZH.js +1 -0
- package/src/assets/web-panel/assets/{index-D8jcp9Rj.js → index-CtB45Hok.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cli0VREv.js → index-Cu_V6ZJH.js} +1 -1
- package/src/assets/web-panel/assets/{index-DO-IKERp.js → index-Cykt9BbN.js} +1 -1
- package/src/assets/web-panel/assets/{index-y7cfh9rP.js → index-DDhy4eCS.js} +1 -1
- package/src/assets/web-panel/assets/{index-CH2wK5dx.js → index-DEPPzM8I.js} +1 -1
- package/src/assets/web-panel/assets/{index-BeycSy7n.js → index-DMVktlxG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwssFWI1.js → index-DOww5Qny.js} +1 -1
- package/src/assets/web-panel/assets/{index-PJt1Zrx2.js → index-DWPp-d07.js} +1 -1
- package/src/assets/web-panel/assets/index-DnjMFf3m.js +1 -0
- package/src/assets/web-panel/assets/{index-BPGlcDvs.js → index-DvM2xBUA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BL_0ndQc.js → index-EK3XtMxA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnJt9DaX.js → index-XJIV7HEw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpiR6uzH.js → index-cNwsev6n.js} +1 -1
- package/src/assets/web-panel/assets/{index-bwb4cg_D.js → index-f_7L9GvP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DbDhKroq.js → index-lUL3bD2z.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvvJWwT8.js → index-pOUFfRdN.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBMW8bQO.js → index-rGhS02f8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRikhGgx.js → index-vHtnQsnV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHUxz4dy.js → index-vb1XIxEB.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-B6e5CZ5p.js → initDefaultProps-DpRIjwTt.js} +1 -1
- package/src/assets/web-panel/assets/{motion-riMM1KfJ.js → motion-CG4_9o-Z.js} +1 -1
- package/src/assets/web-panel/assets/{move-C1NSDMjc.js → move-Dku1fXtf.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-Bibuny9B.js → mtc-parser-BvAVeX-3.js} +1 -1
- package/src/assets/web-panel/assets/{omit-Cmf6FoRn.js → omit-DFu0x78D.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BSsYBUCY.js → pickAttrs-CmI-FNcK.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-gSB7-GAS.js → placementArrow-CGERYewB.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-qaqttvKa.js → responsiveObserve-D7G8bF5J.js} +1 -1
- package/src/assets/web-panel/assets/{slide-RX2gxl3k.js → slide-B2sXwWXy.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DEJmkhqw.js → statusUtils-DI5yh0n3.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CIWmJ78O.js → styleChecker-deGJ5dp_.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D-UPSQBE.js → useFlexGapSupport-OqHBXEmi.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-9QFtqADc.js → useFs-Dqgx0Qv_.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DM6kyift.js → usePersonalDataHub-Be-lveh2.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C-q6FNSJ.js → vnode-CFxB876W.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-D4WJY2m8.js → zoom-BFOYrl4e.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +11 -7
- package/src/commands/background-session.js +31 -3
- package/src/commands/chat.js +5 -1
- package/src/commands/eval.js +86 -28
- package/src/commands/llm.js +104 -1
- package/src/commands/plugin.js +130 -1
- package/src/commands/sandbox.js +121 -2
- package/src/commands/serve.js +11 -2
- package/src/commands/skill.js +29 -0
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/session-protocol.js +13 -0
- package/src/gateways/ws/ws-agent-handler.js +17 -0
- package/src/gateways/ws/ws-server.js +3 -0
- package/src/gateways/ws/ws-session-state.js +131 -0
- package/src/generated/cc-agent-protocol.schema.json +4 -0
- package/src/lazy-dispatch.js +8 -1
- package/src/lib/agent-sandbox.js +337 -53
- package/src/lib/app-server/cli-agent-kernel-adapter.js +28 -0
- package/src/lib/app-server/server.js +93 -0
- package/src/lib/background-agent-keeper-client.js +2 -0
- package/src/lib/background-agent-keeper-protocol.js +7 -0
- package/src/lib/background-agent-supervisor.js +194 -47
- package/src/lib/codex-app-server-adapter.js +81 -27
- package/src/lib/context-memory-kernel/durable-memory-port.js +31 -8
- package/src/lib/context-memory-kernel/provider-context.js +53 -19
- package/src/lib/deferred-question-context.js +122 -0
- package/src/lib/eval/evidence.js +202 -64
- package/src/lib/eval/plugin-suite.js +917 -0
- package/src/lib/eval/runner.js +10 -3
- package/src/lib/evolution/agent-evolution-ingress.js +60 -8
- package/src/lib/evolution/agent-evolution-runtime-composition.js +236 -1
- package/src/lib/evolution/agent-model-projection.js +97 -1
- package/src/lib/evolution/evidence-backed-wiki-maintainer.js +154 -1
- package/src/lib/evolution/evolution-artifact-ports.js +6 -0
- package/src/lib/evolution/evolution-deployment-loader.js +188 -10
- package/src/lib/evolution/evolution-eval-gate.js +8 -0
- package/src/lib/evolution/evolution-eval-runtime-composition.js +255 -0
- package/src/lib/evolution/evolution-evidence-artifact-adapter.js +58 -2
- package/src/lib/evolution/evolution-ledger-file-backend.js +23 -2
- package/src/lib/evolution/evolution-ledger-manifest-chain.js +223 -7
- package/src/lib/evolution/evolution-ledger-v2-journal.js +525 -0
- package/src/lib/evolution/evolution-ledger-v2-manifest-backend.js +46 -2
- package/src/lib/evolution/evolution-ledger.js +57 -1
- package/src/lib/evolution/evolution-run-ledger-adapter.js +93 -0
- package/src/lib/evolution/evolution-run-wiki-evidence-resolver.js +259 -0
- package/src/lib/evolution/governed-hub-llm.js +238 -5
- package/src/lib/evolution/governed-hub-resolver.js +82 -4
- package/src/lib/evolution/governed-hub-skill.js +11 -5
- package/src/lib/evolution/governed-skill-synthesis-provider-chat.js +10 -1
- package/src/lib/evolution/skill-runtime-revalidation-ledger-adapter.js +254 -0
- package/src/lib/evolution/skill-runtime-revalidation.js +530 -0
- package/src/lib/evolution/skill-target-matrix-eval.js +4 -4
- package/src/lib/evolution/wiki-evidence-commit-guard.js +366 -0
- package/src/lib/evolution/wiki-informed-skill-proposer.js +13 -1
- package/src/lib/goal-context.js +14 -5
- package/src/lib/interaction-adapter.js +166 -1
- package/src/lib/model-capabilities.js +271 -0
- package/src/lib/model-context-catalog.js +105 -0
- package/src/lib/model-context-window.js +4 -49
- package/src/lib/openai-responses.js +428 -0
- package/src/lib/plugin-runtime/scopes.js +7 -1
- package/src/lib/remote-read-loop-guard.js +2 -2
- package/src/lib/runtime-usage-ledger.js +16 -0
- package/src/lib/skill-loader.js +12 -0
- package/src/lib/with-file-lock.js +3 -1
- package/src/repl/agent-repl.js +6 -2
- package/src/runtime/agent-core.js +357 -44
- package/src/runtime/agent-runtime.js +7 -0
- package/src/runtime/coding-agent-contract-shared.cjs +13 -1
- package/src/runtime/headless-runner.js +4 -1
- package/src/runtime/headless-stream.js +133 -44
- package/src/runtime/runtime-factory.js +26 -2
- package/src/workers/background-agent-keeper.js +23 -13
- package/src/workers/background-agent-worker.js +27 -13
- package/src/assets/web-panel/assets/EvolutionSettings-_QnjPFZJ.js +0 -1
- package/src/assets/web-panel/assets/devWarning-LxtRBsCm.js +0 -1
- package/src/assets/web-panel/assets/index-36hxBH-o.js +0 -1
- package/src/assets/web-panel/assets/index-C0b78zWZ.js +0 -1
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { types as utilTypes } from "node:util";
|
|
4
|
+
import { withFileLock } from "../with-file-lock.js";
|
|
5
|
+
import { EvolutionArtifactPorts } from "./evolution-artifact-ports.js";
|
|
6
|
+
import {
|
|
7
|
+
captureEvolutionLedgerMigrationSource,
|
|
8
|
+
EVOLUTION_LEDGER_MAX_EVENTS,
|
|
9
|
+
EVOLUTION_LEDGER_V2_MIGRATION_INTENT,
|
|
10
|
+
EVOLUTION_LEDGER_V2_CUTOVER_COMPLETED,
|
|
11
|
+
} from "./evolution-ledger.js";
|
|
12
|
+
import { captureEvolutionLedgerV2ManifestBackend } from "./evolution-ledger-v2-manifest-backend.js";
|
|
13
|
+
|
|
14
|
+
export const EVOLUTION_LEDGER_V2_JOURNAL_SCHEMA =
|
|
15
|
+
"chainlesschain.evolution-ledger-v2-journal/v2";
|
|
16
|
+
export const EVOLUTION_LEDGER_V2_FINALIZATION_SCHEMA =
|
|
17
|
+
"chainlesschain.evolution-ledger-v2-finalization/v2";
|
|
18
|
+
const JOURNALS = new WeakSet();
|
|
19
|
+
const RESERVED = new Set([
|
|
20
|
+
EVOLUTION_LEDGER_V2_MIGRATION_INTENT,
|
|
21
|
+
EVOLUTION_LEDGER_V2_CUTOVER_COMPLETED,
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
function fail(message, cause, code = "CC_EVOLUTION_LEDGER_V2_JOURNAL_INVALID") {
|
|
25
|
+
throw Object.assign(new Error(message, cause ? { cause } : undefined), {
|
|
26
|
+
code,
|
|
27
|
+
...(code.endsWith("COMMIT_UNKNOWN")
|
|
28
|
+
? {
|
|
29
|
+
commitState: "unknown",
|
|
30
|
+
details: Object.freeze({ ...cause?.details, commitState: "unknown" }),
|
|
31
|
+
}
|
|
32
|
+
: {}),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function canonical(value) {
|
|
36
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
37
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
38
|
+
return `{${Object.keys(value)
|
|
39
|
+
.sort()
|
|
40
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
41
|
+
.join(",")}}`;
|
|
42
|
+
}
|
|
43
|
+
function digest(value) {
|
|
44
|
+
return `sha256:${createHash("sha256").update(canonical(value)).digest("hex")}`;
|
|
45
|
+
}
|
|
46
|
+
function record(value, required, optional = []) {
|
|
47
|
+
if (
|
|
48
|
+
!value ||
|
|
49
|
+
typeof value !== "object" ||
|
|
50
|
+
utilTypes.isProxy(value) ||
|
|
51
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value))
|
|
52
|
+
)
|
|
53
|
+
fail("journal configuration must be a plain data record");
|
|
54
|
+
const keys = Reflect.ownKeys(value);
|
|
55
|
+
if (
|
|
56
|
+
required.some((key) => !keys.includes(key)) ||
|
|
57
|
+
keys.some((key) => ![...required, ...optional].includes(key))
|
|
58
|
+
)
|
|
59
|
+
fail("journal configuration fields differ");
|
|
60
|
+
const result = Object.create(null);
|
|
61
|
+
for (const key of keys) {
|
|
62
|
+
const field = Object.getOwnPropertyDescriptor(value, key);
|
|
63
|
+
if (!("value" in field))
|
|
64
|
+
fail("journal configuration requires own data properties");
|
|
65
|
+
result[key] = field.value;
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
function identifier(value) {
|
|
70
|
+
if (
|
|
71
|
+
typeof value !== "string" ||
|
|
72
|
+
!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,159}$/u.test(value)
|
|
73
|
+
)
|
|
74
|
+
fail("journal scope identifier is invalid");
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Authenticated, restartable cutover. V1 remains a signed write-ahead journal
|
|
80
|
+
* and the authority for legacy receipts; every consumer success additionally
|
|
81
|
+
* requires the complete payload to be read back from witnessed v2 storage.
|
|
82
|
+
* No file-backed WORM authority or key material is synthesized here.
|
|
83
|
+
*/
|
|
84
|
+
export function createEvolutionLedgerV2Journal(options) {
|
|
85
|
+
const config = record(
|
|
86
|
+
options,
|
|
87
|
+
[
|
|
88
|
+
"sourceLedger",
|
|
89
|
+
"backend",
|
|
90
|
+
"artifactPorts",
|
|
91
|
+
"descriptor",
|
|
92
|
+
"minimumRetainedUntil",
|
|
93
|
+
],
|
|
94
|
+
["lock", "lockTimeoutMs"],
|
|
95
|
+
);
|
|
96
|
+
const scope = record(config.descriptor, [
|
|
97
|
+
"tenantId",
|
|
98
|
+
"artifactTenantId",
|
|
99
|
+
"audience",
|
|
100
|
+
]);
|
|
101
|
+
for (const key of Object.keys(scope)) identifier(scope[key]);
|
|
102
|
+
const descriptor = Object.freeze({
|
|
103
|
+
...scope,
|
|
104
|
+
schema: EVOLUTION_LEDGER_V2_JOURNAL_SCHEMA,
|
|
105
|
+
});
|
|
106
|
+
const source = captureEvolutionLedgerMigrationSource(config.sourceLedger);
|
|
107
|
+
const ledger = source.ledger;
|
|
108
|
+
const backend = captureEvolutionLedgerV2ManifestBackend(config.backend);
|
|
109
|
+
const retention = config.minimumRetainedUntil;
|
|
110
|
+
if (
|
|
111
|
+
typeof retention !== "string" ||
|
|
112
|
+
!Number.isFinite(Date.parse(retention)) ||
|
|
113
|
+
new Date(retention).toISOString() !== retention
|
|
114
|
+
)
|
|
115
|
+
fail("canonical minimumRetainedUntil is required");
|
|
116
|
+
const artifactPorts = config.artifactPorts;
|
|
117
|
+
if (
|
|
118
|
+
utilTypes.isProxy(artifactPorts) ||
|
|
119
|
+
Object.getPrototypeOf(artifactPorts) !== EvolutionArtifactPorts.prototype
|
|
120
|
+
)
|
|
121
|
+
fail("constructed EvolutionArtifactPorts are required");
|
|
122
|
+
// Calling the original private-field method also rejects prototype forgeries.
|
|
123
|
+
const put = EvolutionArtifactPorts.prototype.putCanonical.bind(artifactPorts);
|
|
124
|
+
const lock = config.lock ?? withFileLock;
|
|
125
|
+
if (typeof lock !== "function" || utilTypes.isProxy(lock))
|
|
126
|
+
fail("synchronous journal lock is required");
|
|
127
|
+
const lockTimeoutMs = config.lockTimeoutMs ?? 10_000;
|
|
128
|
+
if (
|
|
129
|
+
!Number.isSafeInteger(lockTimeoutMs) ||
|
|
130
|
+
lockTimeoutMs < 1 ||
|
|
131
|
+
lockTimeoutMs > 60_000
|
|
132
|
+
)
|
|
133
|
+
fail("journal lock timeout is invalid");
|
|
134
|
+
const locked = (operation) => {
|
|
135
|
+
let calls = 0;
|
|
136
|
+
const result = lock(
|
|
137
|
+
path.join(ledger.authorityRootDir, "manifest-cutover-v2"),
|
|
138
|
+
(context) => {
|
|
139
|
+
if (++calls !== 1)
|
|
140
|
+
fail("journal lock callback must execute exactly once");
|
|
141
|
+
if (context?.locked !== true)
|
|
142
|
+
fail("exclusive journal lock unavailable");
|
|
143
|
+
return operation();
|
|
144
|
+
},
|
|
145
|
+
{ failIfUnavailable: true, timeoutMs: lockTimeoutMs },
|
|
146
|
+
);
|
|
147
|
+
if (calls !== 1 || utilTypes.isPromise(result))
|
|
148
|
+
fail("journal lock must complete synchronously");
|
|
149
|
+
return result;
|
|
150
|
+
};
|
|
151
|
+
const sourceEvents = () =>
|
|
152
|
+
ledger.read({ limit: EVOLUTION_LEDGER_MAX_EVENTS });
|
|
153
|
+
|
|
154
|
+
function binding(authority) {
|
|
155
|
+
if (
|
|
156
|
+
backend.descriptor.epoch !== authority.epoch ||
|
|
157
|
+
backend.descriptor.ledgerId !== authority.ledgerId ||
|
|
158
|
+
backend.descriptor.tenantId !== scope.tenantId
|
|
159
|
+
)
|
|
160
|
+
fail("manifest backend and source ledger scope differ");
|
|
161
|
+
return Object.freeze({
|
|
162
|
+
authorityRootDigest: backend.descriptor.authorityRootDigest,
|
|
163
|
+
epoch: authority.epoch,
|
|
164
|
+
identityDigest: authority.identityDigest,
|
|
165
|
+
ledgerId: authority.ledgerId,
|
|
166
|
+
maximumEventsPerSegment: backend.descriptor.maximumEventsPerSegment,
|
|
167
|
+
minimumRetainedUntil: retention,
|
|
168
|
+
schema: EVOLUTION_LEDGER_V2_JOURNAL_SCHEMA,
|
|
169
|
+
storeMarkerDigest: authority.storeMarkerDigest,
|
|
170
|
+
storeMarkerEntryDigest: authority.storeMarkerEntryDigest,
|
|
171
|
+
tenantId: scope.tenantId,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function writeMarker(type, payload, before) {
|
|
175
|
+
const reason = canonical(payload);
|
|
176
|
+
const published = put("evolution-ledger-v2-journal", payload, {
|
|
177
|
+
audience: scope.audience,
|
|
178
|
+
purpose: "evolution-ledger",
|
|
179
|
+
retention: "ledger",
|
|
180
|
+
});
|
|
181
|
+
if (
|
|
182
|
+
!published?.ref ||
|
|
183
|
+
published.receipt?.persisted !== true ||
|
|
184
|
+
published.receipt.readbackVerified !== true ||
|
|
185
|
+
published.receipt.integrityVerified !== true ||
|
|
186
|
+
published.receipt.retention !== "ledger"
|
|
187
|
+
)
|
|
188
|
+
fail("migration journal artifact is not authenticated and durable");
|
|
189
|
+
source.appendDomainEvent(
|
|
190
|
+
{
|
|
191
|
+
artifactTenantId: scope.artifactTenantId,
|
|
192
|
+
correlationId: null,
|
|
193
|
+
decision: "committed",
|
|
194
|
+
eventId: `ledger-v2-${digest({ type, payload }).slice(7)}`,
|
|
195
|
+
reason,
|
|
196
|
+
skillName: null,
|
|
197
|
+
sourceRefs: [],
|
|
198
|
+
subjectRef: published.ref,
|
|
199
|
+
tenantId: scope.tenantId,
|
|
200
|
+
type,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
expectedSequence: before.length,
|
|
204
|
+
expectedHeadDigest: before.at(-1)?.eventDigest ?? null,
|
|
205
|
+
},
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
function payload(marker) {
|
|
209
|
+
try {
|
|
210
|
+
return JSON.parse(marker.reason);
|
|
211
|
+
} catch (cause) {
|
|
212
|
+
fail("signed migration journal payload is invalid", cause);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function prefix(events) {
|
|
216
|
+
const persisted = backend.readEvents();
|
|
217
|
+
if (
|
|
218
|
+
persisted.length > events.length ||
|
|
219
|
+
persisted.some(
|
|
220
|
+
(event, index) => canonical(event) !== canonical(events[index]),
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
fail("v2 payload is not the exact authenticated v1 prefix");
|
|
224
|
+
return persisted;
|
|
225
|
+
}
|
|
226
|
+
function copySuffix(events, persisted) {
|
|
227
|
+
let receipt = null;
|
|
228
|
+
for (
|
|
229
|
+
let offset = persisted.length;
|
|
230
|
+
offset < events.length;
|
|
231
|
+
offset += backend.descriptor.maximumEventsPerSegment
|
|
232
|
+
) {
|
|
233
|
+
const current = backend.read();
|
|
234
|
+
receipt = backend.appendSegment({
|
|
235
|
+
events: events.slice(
|
|
236
|
+
offset,
|
|
237
|
+
offset + backend.descriptor.maximumEventsPerSegment,
|
|
238
|
+
),
|
|
239
|
+
expectedHeadDigest: current.head?.headDigest ?? null,
|
|
240
|
+
expectedWitnessDigest: current.witness.witnessDigest,
|
|
241
|
+
minimumRetainedUntil: retention,
|
|
242
|
+
});
|
|
243
|
+
if (receipt.conflict === true)
|
|
244
|
+
fail(
|
|
245
|
+
"v2 journal finalize conflicted; reopen before retrying",
|
|
246
|
+
undefined,
|
|
247
|
+
"CC_EVOLUTION_LEDGER_V2_JOURNAL_COMMIT_UNKNOWN",
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
if (prefix(events).length !== events.length)
|
|
251
|
+
fail("v2 journal readback is incomplete");
|
|
252
|
+
return receipt;
|
|
253
|
+
}
|
|
254
|
+
function recover() {
|
|
255
|
+
const authority = ledger.verify();
|
|
256
|
+
const expectedBinding = binding(authority);
|
|
257
|
+
let events = sourceEvents();
|
|
258
|
+
let intents = events.filter(
|
|
259
|
+
(event) => event.type === EVOLUTION_LEDGER_V2_MIGRATION_INTENT,
|
|
260
|
+
);
|
|
261
|
+
let completed = events.filter(
|
|
262
|
+
(event) => event.type === EVOLUTION_LEDGER_V2_CUTOVER_COMPLETED,
|
|
263
|
+
);
|
|
264
|
+
if (
|
|
265
|
+
intents.length > 1 ||
|
|
266
|
+
completed.length > 1 ||
|
|
267
|
+
(!intents.length && completed.length)
|
|
268
|
+
)
|
|
269
|
+
fail("migration journal markers are incoherent");
|
|
270
|
+
if (!intents.length) {
|
|
271
|
+
if (backend.read().head !== null)
|
|
272
|
+
fail("an unbound manifest backend cannot be adopted");
|
|
273
|
+
if (events.length + 2 > EVOLUTION_LEDGER_MAX_EVENTS)
|
|
274
|
+
fail("ledger lacks capacity for migration journal markers");
|
|
275
|
+
writeMarker(
|
|
276
|
+
EVOLUTION_LEDGER_V2_MIGRATION_INTENT,
|
|
277
|
+
{
|
|
278
|
+
binding: expectedBinding,
|
|
279
|
+
sourceEventDigest: events.at(-1)?.eventDigest ?? null,
|
|
280
|
+
sourceSequence: events.length,
|
|
281
|
+
},
|
|
282
|
+
events,
|
|
283
|
+
);
|
|
284
|
+
events = sourceEvents();
|
|
285
|
+
intents = events.filter(
|
|
286
|
+
(event) => event.type === EVOLUTION_LEDGER_V2_MIGRATION_INTENT,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
const intent = intents[0];
|
|
290
|
+
const intentPayload = payload(intent);
|
|
291
|
+
if (
|
|
292
|
+
canonical(intentPayload.binding) !== canonical(expectedBinding) ||
|
|
293
|
+
intentPayload.sourceSequence !== intent.sequence - 1 ||
|
|
294
|
+
intentPayload.sourceEventDigest !==
|
|
295
|
+
(events[intent.sequence - 2]?.eventDigest ?? null)
|
|
296
|
+
)
|
|
297
|
+
fail("migration journal authority or source prefix binding differs");
|
|
298
|
+
let persisted = prefix(events);
|
|
299
|
+
if (completed.length) {
|
|
300
|
+
const complete = completed[0];
|
|
301
|
+
const completion = payload(complete);
|
|
302
|
+
if (
|
|
303
|
+
complete.sequence <= intent.sequence ||
|
|
304
|
+
canonical(completion.binding) !== canonical(expectedBinding) ||
|
|
305
|
+
completion.intentDigest !== intent.eventDigest ||
|
|
306
|
+
completion.sourceSequence !== complete.sequence - 1 ||
|
|
307
|
+
completion.sourceEventDigest !==
|
|
308
|
+
events[complete.sequence - 2]?.eventDigest ||
|
|
309
|
+
persisted.length < completion.sourceSequence
|
|
310
|
+
)
|
|
311
|
+
fail("completed v2 cutover was rolled back or rebound");
|
|
312
|
+
}
|
|
313
|
+
copySuffix(events, persisted);
|
|
314
|
+
if (!completed.length) {
|
|
315
|
+
writeMarker(
|
|
316
|
+
EVOLUTION_LEDGER_V2_CUTOVER_COMPLETED,
|
|
317
|
+
{
|
|
318
|
+
binding: expectedBinding,
|
|
319
|
+
intentDigest: intent.eventDigest,
|
|
320
|
+
sourceEventDigest: events.at(-1).eventDigest,
|
|
321
|
+
sourceSequence: events.length,
|
|
322
|
+
},
|
|
323
|
+
events,
|
|
324
|
+
);
|
|
325
|
+
persisted = events;
|
|
326
|
+
events = sourceEvents();
|
|
327
|
+
copySuffix(events, persisted);
|
|
328
|
+
completed = events.filter(
|
|
329
|
+
(event) => event.type === EVOLUTION_LEDGER_V2_CUTOVER_COMPLETED,
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
events: backend.readEvents(),
|
|
334
|
+
checkpoint: backend.read(),
|
|
335
|
+
intent: intents[0],
|
|
336
|
+
completed: completed[0],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function readOperation(operation) {
|
|
340
|
+
return locked(() => {
|
|
341
|
+
const current = recover();
|
|
342
|
+
return operation(current);
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
function append(method, inputs, options, batch, finalized) {
|
|
346
|
+
return locked(() => {
|
|
347
|
+
const before = recover().events;
|
|
348
|
+
const entries = batch ? inputs : [inputs];
|
|
349
|
+
if (
|
|
350
|
+
!Array.isArray(entries) ||
|
|
351
|
+
utilTypes.isProxy(entries) ||
|
|
352
|
+
entries.length < 1 ||
|
|
353
|
+
entries.length > backend.descriptor.maximumEventsPerSegment
|
|
354
|
+
)
|
|
355
|
+
fail("live batch must fit one manifest segment");
|
|
356
|
+
for (let index = 0; index < entries.length; index++) {
|
|
357
|
+
const entry = Object.getOwnPropertyDescriptor(entries, String(index));
|
|
358
|
+
if (
|
|
359
|
+
!entry ||
|
|
360
|
+
!("value" in entry) ||
|
|
361
|
+
!entry.value ||
|
|
362
|
+
utilTypes.isProxy(entry.value)
|
|
363
|
+
)
|
|
364
|
+
fail("live batch requires dense data events");
|
|
365
|
+
const type = Object.getOwnPropertyDescriptor(entry.value, "type");
|
|
366
|
+
const tenant = Object.getOwnPropertyDescriptor(entry.value, "tenantId");
|
|
367
|
+
if (
|
|
368
|
+
!type ||
|
|
369
|
+
!("value" in type) ||
|
|
370
|
+
RESERVED.has(type.value) ||
|
|
371
|
+
!tenant ||
|
|
372
|
+
!("value" in tenant) ||
|
|
373
|
+
tenant.value !== scope.tenantId
|
|
374
|
+
)
|
|
375
|
+
fail("live event type or tenant is invalid");
|
|
376
|
+
}
|
|
377
|
+
let receipt;
|
|
378
|
+
try {
|
|
379
|
+
receipt = source[method](inputs, options);
|
|
380
|
+
} catch (cause) {
|
|
381
|
+
let changed = true;
|
|
382
|
+
try {
|
|
383
|
+
changed = sourceEvents().length !== before.length;
|
|
384
|
+
} catch {
|
|
385
|
+
/* Unknown WAL state remains fail-closed. */
|
|
386
|
+
}
|
|
387
|
+
if (changed)
|
|
388
|
+
fail(
|
|
389
|
+
"v1 batch may have partially committed; reopen the v2 journal before retrying",
|
|
390
|
+
cause,
|
|
391
|
+
"CC_EVOLUTION_LEDGER_V2_JOURNAL_COMMIT_UNKNOWN",
|
|
392
|
+
);
|
|
393
|
+
throw cause;
|
|
394
|
+
}
|
|
395
|
+
let checkpoint;
|
|
396
|
+
try {
|
|
397
|
+
const events = sourceEvents();
|
|
398
|
+
if (events.length !== before.length + entries.length)
|
|
399
|
+
fail("v1 batch readback differs");
|
|
400
|
+
checkpoint = copySuffix(events, before);
|
|
401
|
+
} catch (cause) {
|
|
402
|
+
fail(
|
|
403
|
+
"v1 WAL committed; v2 finalization must be recovered before success",
|
|
404
|
+
cause,
|
|
405
|
+
"CC_EVOLUTION_LEDGER_V2_JOURNAL_COMMIT_UNKNOWN",
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
return finalized
|
|
409
|
+
? Object.freeze({
|
|
410
|
+
schema: EVOLUTION_LEDGER_V2_FINALIZATION_SCHEMA,
|
|
411
|
+
receipt,
|
|
412
|
+
checkpoint,
|
|
413
|
+
})
|
|
414
|
+
: receipt;
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
const journal = Object.freeze({
|
|
418
|
+
descriptor,
|
|
419
|
+
rootDir: ledger.rootDir,
|
|
420
|
+
authorityRootDir: ledger.authorityRootDir,
|
|
421
|
+
append: (input, options = {}) =>
|
|
422
|
+
append("append", input, options, false, false),
|
|
423
|
+
appendBatch: (inputs, options = {}) =>
|
|
424
|
+
append("appendBatch", inputs, options, true, false),
|
|
425
|
+
appendDomainEvent: (input, options = {}) =>
|
|
426
|
+
append("appendDomainEvent", input, options, false, false),
|
|
427
|
+
appendDomainEventBatch: (inputs, options = {}) =>
|
|
428
|
+
append("appendDomainEventBatch", inputs, options, true, false),
|
|
429
|
+
finalizeBatch: (inputs, options = {}) =>
|
|
430
|
+
append("appendBatch", inputs, options, true, true),
|
|
431
|
+
finalizeDomainEventBatch: (inputs, options = {}) =>
|
|
432
|
+
append("appendDomainEventBatch", inputs, options, true, true),
|
|
433
|
+
read: (options = {}) =>
|
|
434
|
+
readOperation(({ events }) => {
|
|
435
|
+
// Preserve exact v1 read validation and return events recovered from v2.
|
|
436
|
+
const selected = ledger.read(options);
|
|
437
|
+
return Object.freeze(
|
|
438
|
+
selected.map((event) => events[event.sequence - 1]),
|
|
439
|
+
);
|
|
440
|
+
}),
|
|
441
|
+
query: (selector, options = {}) =>
|
|
442
|
+
readOperation(({ events }) => {
|
|
443
|
+
const result = ledger.query(selector, options);
|
|
444
|
+
return result
|
|
445
|
+
? Object.freeze({
|
|
446
|
+
...result,
|
|
447
|
+
event: events[result.event.sequence - 1],
|
|
448
|
+
})
|
|
449
|
+
: null;
|
|
450
|
+
}),
|
|
451
|
+
queryMany: (selectors, options = {}) =>
|
|
452
|
+
readOperation(({ events }) =>
|
|
453
|
+
Object.freeze(
|
|
454
|
+
ledger.queryMany(selectors, options).map((result) =>
|
|
455
|
+
result
|
|
456
|
+
? Object.freeze({
|
|
457
|
+
...result,
|
|
458
|
+
event: events[result.event.sequence - 1],
|
|
459
|
+
})
|
|
460
|
+
: null,
|
|
461
|
+
),
|
|
462
|
+
),
|
|
463
|
+
),
|
|
464
|
+
findByEventId: (eventId) =>
|
|
465
|
+
readOperation(({ events }) => {
|
|
466
|
+
const event = ledger.findByEventId(eventId);
|
|
467
|
+
return event ? events[event.sequence - 1] : null;
|
|
468
|
+
}),
|
|
469
|
+
recoverReceipt: (selector) =>
|
|
470
|
+
readOperation(() => ledger.recoverReceipt(selector)),
|
|
471
|
+
verifyReceipt: (receipt, options = {}) =>
|
|
472
|
+
readOperation(() => ledger.verifyReceipt(receipt, options)),
|
|
473
|
+
verify: () => readOperation(() => ledger.verify()),
|
|
474
|
+
getAuthority: () => readOperation(() => ledger.getAuthority()),
|
|
475
|
+
checkpointState: () => readOperation(() => ledger.checkpointState()),
|
|
476
|
+
exportAuditBundle: () => readOperation(() => ledger.exportAuditBundle()),
|
|
477
|
+
recover: () =>
|
|
478
|
+
readOperation(({ checkpoint, intent, completed }) =>
|
|
479
|
+
Object.freeze({ checkpoint, intent, completed }),
|
|
480
|
+
),
|
|
481
|
+
});
|
|
482
|
+
locked(recover);
|
|
483
|
+
JOURNALS.add(journal);
|
|
484
|
+
return journal;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export function isEvolutionLedgerV2Journal(value) {
|
|
488
|
+
return JOURNALS.has(value);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Production factory receives authenticated scope, not a structural ledger or a
|
|
492
|
+
// capability to bypass cutover. Capturing the result enforces the backend brand.
|
|
493
|
+
export function createEvolutionLedgerV2JournalFromFactory(
|
|
494
|
+
sourceLedger,
|
|
495
|
+
configuration,
|
|
496
|
+
options = {},
|
|
497
|
+
) {
|
|
498
|
+
const config = record(configuration, [
|
|
499
|
+
"createBackend",
|
|
500
|
+
"artifactPorts",
|
|
501
|
+
"descriptor",
|
|
502
|
+
"minimumRetainedUntil",
|
|
503
|
+
]);
|
|
504
|
+
if (
|
|
505
|
+
typeof config.createBackend !== "function" ||
|
|
506
|
+
utilTypes.isProxy(config.createBackend)
|
|
507
|
+
)
|
|
508
|
+
fail("explicit manifest backend construction authority is required");
|
|
509
|
+
const authority =
|
|
510
|
+
captureEvolutionLedgerMigrationSource(sourceLedger).ledger.getAuthority();
|
|
511
|
+
const scope = Object.freeze({
|
|
512
|
+
...record(config.descriptor, ["tenantId", "artifactTenantId", "audience"]),
|
|
513
|
+
});
|
|
514
|
+
const backend = config.createBackend(
|
|
515
|
+
Object.freeze({ authority, descriptor: scope }),
|
|
516
|
+
);
|
|
517
|
+
return createEvolutionLedgerV2Journal({
|
|
518
|
+
sourceLedger,
|
|
519
|
+
backend,
|
|
520
|
+
artifactPorts: config.artifactPorts,
|
|
521
|
+
descriptor: scope,
|
|
522
|
+
minimumRetainedUntil: config.minimumRetainedUntil,
|
|
523
|
+
...options,
|
|
524
|
+
});
|
|
525
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { types as utilTypes } from "node:util";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
|
|
3
4
|
import { captureImmutableLedgerSegmentStorePort } from "./evolution-immutable-ledger-segment-store.js";
|
|
4
5
|
import {
|
|
@@ -6,6 +7,7 @@ import {
|
|
|
6
7
|
deriveEvolutionLedgerManifestHead,
|
|
7
8
|
sealEvolutionLedgerManifestSegment,
|
|
8
9
|
verifyEvolutionLedgerManifestChain,
|
|
10
|
+
readEvolutionLedgerManifestEvents,
|
|
9
11
|
} from "./evolution-ledger-manifest-chain.js";
|
|
10
12
|
import { captureEvolutionLedgerManifestHeadStore } from "./evolution-ledger-manifest-head-store.js";
|
|
11
13
|
import { captureEvolutionLedgerManifestCatalog } from "./evolution-ledger-manifest-catalog.js";
|
|
@@ -63,6 +65,15 @@ function failure(code, message, options = undefined) {
|
|
|
63
65
|
return new EvolutionLedgerV2ManifestBackendError(code, message, options);
|
|
64
66
|
}
|
|
65
67
|
|
|
68
|
+
function canonical(value) {
|
|
69
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
70
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
71
|
+
return `{${Object.keys(value)
|
|
72
|
+
.sort()
|
|
73
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
74
|
+
.join(",")}}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
66
77
|
function rejectProxy(
|
|
67
78
|
value,
|
|
68
79
|
label,
|
|
@@ -419,15 +430,32 @@ export function createEvolutionLedgerV2ManifestBackend(options = undefined) {
|
|
|
419
430
|
const backend = Object.freeze({
|
|
420
431
|
descriptor: Object.freeze({
|
|
421
432
|
...normalizedDescriptor,
|
|
433
|
+
authorityRootDigest: `sha256:${createHash("sha256")
|
|
434
|
+
.update(
|
|
435
|
+
canonical({
|
|
436
|
+
manifestAuthority: manifestAuthority.descriptor,
|
|
437
|
+
segmentStore: segmentStore.descriptor,
|
|
438
|
+
witnessAdapter: witnessAdapter.descriptor,
|
|
439
|
+
}),
|
|
440
|
+
)
|
|
441
|
+
.digest("hex")}`,
|
|
422
442
|
schema: EVOLUTION_LEDGER_V2_MANIFEST_BACKEND_SCHEMA,
|
|
423
443
|
}),
|
|
424
444
|
read() {
|
|
425
445
|
return recoverCheckpoint();
|
|
426
446
|
},
|
|
427
447
|
appendSegment(input) {
|
|
448
|
+
const fullEvents =
|
|
449
|
+
input && !utilTypes.isProxy(input) && Object.hasOwn(input, "events");
|
|
428
450
|
const request = exactRecord(
|
|
429
451
|
input,
|
|
430
|
-
|
|
452
|
+
fullEvents
|
|
453
|
+
? new Set(
|
|
454
|
+
[...APPEND_KEYS]
|
|
455
|
+
.filter((key) => key !== "eventDigests")
|
|
456
|
+
.concat("events"),
|
|
457
|
+
)
|
|
458
|
+
: APPEND_KEYS,
|
|
431
459
|
"v2 manifest append request",
|
|
432
460
|
);
|
|
433
461
|
const expectedHeadDigest = nullableDigest(
|
|
@@ -448,7 +476,9 @@ export function createEvolutionLedgerV2ManifestBackend(options = undefined) {
|
|
|
448
476
|
const sealed = sealEvolutionLedgerManifestSegment({
|
|
449
477
|
authority: manifestAuthority,
|
|
450
478
|
descriptor: normalizedDescriptor,
|
|
451
|
-
|
|
479
|
+
...(fullEvents
|
|
480
|
+
? { events: data(request, "events") }
|
|
481
|
+
: { eventDigests: data(request, "eventDigests") }),
|
|
452
482
|
minimumRetainedUntil: data(request, "minimumRetainedUntil"),
|
|
453
483
|
now,
|
|
454
484
|
previousHead: current.head,
|
|
@@ -528,7 +558,9 @@ export function createEvolutionLedgerV2ManifestBackend(options = undefined) {
|
|
|
528
558
|
durable: true,
|
|
529
559
|
headDigest: sealed.head.headDigest,
|
|
530
560
|
immutable: true,
|
|
561
|
+
head: sealed.head,
|
|
531
562
|
manifestDigest: sealed.manifest.manifestDigest,
|
|
563
|
+
manifest: sealed.manifest,
|
|
532
564
|
manifestSequence: sealed.manifest.manifestSequence,
|
|
533
565
|
readbackVerified: true,
|
|
534
566
|
schema: EVOLUTION_LEDGER_V2_MANIFEST_APPEND_RECEIPT_SCHEMA,
|
|
@@ -536,6 +568,7 @@ export function createEvolutionLedgerV2ManifestBackend(options = undefined) {
|
|
|
536
568
|
sequenceEnd: sealed.manifest.sequenceEnd,
|
|
537
569
|
sequenceStart: sealed.manifest.sequenceStart,
|
|
538
570
|
witnessed: true,
|
|
571
|
+
witness: final.witness.record,
|
|
539
572
|
witnessDigest: final.witness.witnessDigest,
|
|
540
573
|
});
|
|
541
574
|
},
|
|
@@ -556,6 +589,17 @@ export function createEvolutionLedgerV2ManifestBackend(options = undefined) {
|
|
|
556
589
|
segmentStore,
|
|
557
590
|
});
|
|
558
591
|
},
|
|
592
|
+
readEvents() {
|
|
593
|
+
const snapshot = recoverCheckpoint();
|
|
594
|
+
if (snapshot.head === null) return Object.freeze([]);
|
|
595
|
+
return readEvolutionLedgerManifestEvents({
|
|
596
|
+
authority: manifestAuthority,
|
|
597
|
+
descriptor: normalizedDescriptor,
|
|
598
|
+
head: snapshot.head,
|
|
599
|
+
manifests: catalog.list(),
|
|
600
|
+
segmentStore,
|
|
601
|
+
}).events;
|
|
602
|
+
},
|
|
559
603
|
});
|
|
560
604
|
BACKENDS.add(backend);
|
|
561
605
|
return backend;
|