chainlesschain 0.166.16 → 0.166.18
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 +17 -10
- package/completions/_cc +1 -1
- package/completions/cc.bash +1 -1
- package/completions/cc.fish +1 -1
- package/completions/cc.ps1 +1 -1
- package/package.json +2 -2
- package/src/assets/web-panel/assets/{AIOps-CTxa2Wbq.js → AIOps-CQSBl3hY.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DHrQyYhy.js → ActionButton-IQt-vRLt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DQiUqcNl.js → Analytics-DkHC8O7k.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-_Iz4r6wt.js → AppLayout-CSti2P2T.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BNs63YHo.js → Artifacts-C57hxn3v.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BgHIqRLn.js → Audit-DByLntcf.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DUQ5axBB.js → BackgroundAgents-D1Bd2EOa.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CJ51E9Vw.js → Backup-VstHzHng.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-o-5toA_R.js → BaseInput-CLhdexIf.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Cu7a-x1L.js → Chat-BefC_JZS.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BUyYkAXr.js → ChatBubbleRenderer-DbXMvgmn.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DwdmZ71L.js → Checkbox-CEY982vx.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BrH2eYiP.js → Codegen-Dcm_DTvJ.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BVJQEP2j.js → Col-C8SSu4KA.js} +1 -1
- package/src/assets/web-panel/assets/{Community-FGO3vZPq.js → Community-BVV9vwRR.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DYGAFVnT.js → Compact-B7c5Vo18.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CRvc8Bdt.js → Compliance-Tq9jSZ5a.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-D6Ah2m7A.js → Cowork-BJUjT09Q.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-AXbU2ifR.js → Cron-CjV9PEaS.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-C5DXgEFd.js → Crosschain-Dl7ReL8F.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Bxh9QV9v.js → DID-CG8PjWmM.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-B-ZHEta-.js → Dashboard-CDBM6wmF.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-66Ik1jqa.js → Dropdown-8_55bXTD.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CCWk1BIM.js → EmailListRenderer-D3qOU1Cm.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard--9faIULK.js → FamilyGuardDashboard-Bv5oWMZF.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CejW9CgX.js → Federation-hpZ1xkKd.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-acHgcXki.js → FormItemContext-BN7GT191.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-D2ghd_K4.js → GenericCardRenderer-PCRhfA7M.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BOe1YJrV.js → Git-CIlP2vy7.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BjgzJsof.js → Governance-81lgm7YL.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CXCD5rnP.js → Inference-Cs24xMOV.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CHA8QgJT.js → KnowledgeGraph-CrJLVCZo.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DORPMcL6.js → Logs-DLyO8RKT.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-CPPHz01z.js → MarkdownRenderer-DBOqijrP.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BCFlo1Yx.js → Marketplace-Dz7AbQZc.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Dl_uZAKh.js → McpTools-BM0aFiWv.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-DupHamwz.js → Memory-C9SJzENX.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BRSMPJxW.js → MobileBridge-BO2HnNRk.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BHpuzsDl.js → MobileProjects-CK_jMz0l.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Ch0jOQfZ.js → Mtc-CfhrJsdO.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-qzwQrDi7.js → MtcAudit-BE2ELxgI.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-B5wHzUcm.js → Multisig-CgR_E3Jd.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Hy9uAiA0.js → NLProgramming-Cw9445_K.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DkuchMC7.js → Notes-DZimkTrb.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CYZhD7Pi.js → NotificationSettings-DxHVh4uT.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DKOoqajt.js → OrderTableRenderer-Iy-pnX56.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-D9AXn6j8.js → Organization-pPXJtWo4.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Bg15J6hX.js → Overflow-YamdTJwA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P--B54HoQn.js → P2P-BvqP53x7.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Cz9Kk2Nb.js → PdhVaultBrowser-Blz1Xn2n.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DHx38eS1.js → Permissions-CgFRQqFJ.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dw8i2hAs.js → PersonalDataHub-CFeo3-6O.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-B2o6Ur2G.js → Pipeline-BkaRojEK.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-8g-h2fnu.js → Privacy-BNZEx2Qp.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DuVTcINx.js → ProjectInit-HUBOgEEK.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C8njvdZX.js → ProjectSettings-CFMmhROS.js} +2 -2
- package/src/assets/web-panel/assets/Projects-Dc517BXp.js +1 -0
- package/src/assets/web-panel/assets/{Providers-wFqgod_W.js → Providers-DTLf8bgj.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-9NCXrpmA.js → QrScannerModal-BBNjPszk.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-WUTNvFkY.js → QuickAsk-DCsHvk3G.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-xT4nYmju.js → Recommend-Dpr5YSop.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-fak1PBGC.js → RemoteSession-DmKnvqGg.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-D1AkQvD-.js → Reputation-CSRfuurX.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DVfm6xGt.js → Row-CDj3pQ_w.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BDunoLSE.js → RssFeed-DI0a_kNq.js} +2 -2
- package/src/assets/web-panel/assets/{Search-OPycRoU3.js → Search-jAy7B2nU.js} +1 -1
- package/src/assets/web-panel/assets/{Security-C8-Fm3Dt.js → Security-CkHTqYhc.js} +4 -4
- package/src/assets/web-panel/assets/{Services-qM_sMpMl.js → Services-CP-0Nzga.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-D3HBJjVF.js → Skeleton-BkF5-p3n.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-jr67X-tP.js → Skills-BDu-utDE.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CS3ggyCg.js → Sla-CQvq_2Qb.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CTOCYxOi.js → SpeechSettings-T7Ap512g.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D3s3cLT5.js → SyncSettings-Bt-PLvot.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CdfoJ25m.js → Tasks-yLEcCogg.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Drb4AI8X.js → Templates-ByN3fmbr.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DzwR3Qkz.js → Tenant-DBI7hITH.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-D-ESq7xp.js → Terminal-B77_DlGX.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BK-AUuws.js → TimelineRenderer-CsLTB_QO.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-xGTouuNp.js → Tokens-BV2N6cR8.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BvZj-v2H.js → Trigger-CjXWGH3p.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BLC9CGmc.js → Trust-uqXIq3WC.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BSuCXpmE.js → UkeySign-D-6qu4vY.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DT3A-_Ql.js → VideoEditing-CxGmx_2e.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-c1OXao85.js → Wallet-DSq7WlAg.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CuA-F7KJ.js → WebAuthn-CDBiD57O.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-MANZrf82.js → WorkflowEditor-Z8f63TSb.js} +1 -1
- package/src/assets/web-panel/assets/{chat-D4Y4Ey0O.js → chat-B-UZFBjy.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DDH06hKO.js → colors-CFk7K-7Q.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Cym756Xj.js → compact-item-X0IVMvgi.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-By4Nb1k-.js → createContext-DWg4j2n6.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-B2IW1J_J.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Ch7OY258.js → hasIn-CkVCwNNF.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9le0-dc.js → index-5OcZHdzc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CykROEJA.js → index-B273IY-E.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjwgJ5Jd.js → index-B3-B3FWD.js} +1 -1
- package/src/assets/web-panel/assets/{index-CjNUTBwe.js → index-BB4KHHcv.js} +1 -1
- package/src/assets/web-panel/assets/index-BCp2ir-1.js +1 -0
- package/src/assets/web-panel/assets/{index-BFydNU6a.js → index-BG-GaZri.js} +1 -1
- package/src/assets/web-panel/assets/{index-mL3nOcH6.js → index-BPBia4b-.js} +1 -1
- package/src/assets/web-panel/assets/{index-XGeQMvWK.js → index-BipwEbTA.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5sADPGY.js → index-BoskgPt9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CV8qXTvb.js → index-Btqk6zWb.js} +1 -1
- package/src/assets/web-panel/assets/index-Bw4iuzRD.js +1 -0
- package/src/assets/web-panel/assets/{index-CJPks33Y.js → index-BzXjYUmd.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBBUn5v7.js → index-C1ZAOJRW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DiewptTL.js → index-CCLwoATF.js} +1 -1
- package/src/assets/web-panel/assets/{index-OU8DZ9BL.js → index-CDfSD07S.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvnywwUj.js → index-CLlP1E4X.js} +1 -1
- package/src/assets/web-panel/assets/{index-CT-3FYwd.js → index-CMCEXBRh.js} +1 -1
- package/src/assets/web-panel/assets/{index-tZ5BUdad.js → index-CN8HZlLH.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNlxihs8.js → index-CNKFsBlz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CF3Lhyns.js → index-CUBkvFPH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CV1UC4zo.js → index-Ca0mij1n.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAkk1M7x.js → index-CbIAWbma.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNEJzc0r.js → index-CbihMThv.js} +1 -1
- package/src/assets/web-panel/assets/{index-q-s04OKn.js → index-CvOa-IFV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZB9q-Lp.js → index-Cvwu_rA_.js} +1 -1
- package/src/assets/web-panel/assets/{index-oBsfUbyH.js → index-D-EJNxgR.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZY2Qs_Sn.js → index-D8uEL8Uw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtGValNi.js → index-D92g4l3n.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpudmDhf.js → index-DCqpl_E_.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bo20BfdS.js → index-DLSBks8h.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUSUzaB8.js → index-Db_5xhrU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZn6dmZe.js → index-DcDggpWR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGsvEbbq.js → index-DeWKPQGs.js} +1 -1
- package/src/assets/web-panel/assets/{index-jx7kr6mB.js → index-DhVHoqGL.js} +3 -3
- package/src/assets/web-panel/assets/{index-BIrRX-ra.js → index-DyWlOuak.js} +1 -1
- package/src/assets/web-panel/assets/{index-6CpOV3Pn.js → index-LU54bChK.js} +1 -1
- package/src/assets/web-panel/assets/{index-6-2eYBXr.js → index-OjnAwUJo.js} +1 -1
- package/src/assets/web-panel/assets/{index-B51otVoZ.js → index-P83LDnCz.js} +1 -1
- package/src/assets/web-panel/assets/{index-ArMrnx0v.js → index-_-9k3Tna.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DGcX9iPP.js → initDefaultProps-CFNgY8lJ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-2SeL1D8A.js → motion-D-5RrDzn.js} +1 -1
- package/src/assets/web-panel/assets/{move-3cYbQCpS.js → move-DN-L0Bd-.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-IDdxfw0z.js → mtc-parser-6mgLSeCK.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BNswdKO6.js → omit-ClWW8eTP.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BwCnem2I.js → pickAttrs-Vystq3R8.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B9PtZ5rq.js → placementArrow-D0H9hK4W.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CPB6iSzV.js → responsiveObserve-B3iNRU-a.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DZgyblZa.js → slide-CiMXe6am.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-D82IBdsx.js → statusUtils-CGDZJ1uT.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-qSHtosM-.js → styleChecker-D2zA6NkY.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CmWO7h0I.js → useFlexGapSupport-C1sYU6QE.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-yLrs_ub4.js → useFs-BzUHSjg2.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CJ1VW6rh.js → usePersonalDataHub-CxB5yZCq.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CzY_moQ4.js → vnode-BfUfMwtc.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DAa7rEY5.js → zoom-CFYmj1TO.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/command-manifest.json +2 -2
- package/src/commands/agent.js +80 -2
- package/src/commands/evolution.js +61 -42
- package/src/commands/learning.js +24 -8
- package/src/commands/serve.js +11 -2
- package/src/data/changelog.json +13 -0
- package/src/gateways/ws/session-protocol.js +1 -0
- package/src/gateways/ws/ws-agent-handler.js +70 -2
- package/src/gateways/ws/ws-server.js +8 -0
- package/src/lib/app-server/cli-agent-kernel-adapter.js +4 -0
- package/src/lib/app-server/graph-runtime.js +115 -8
- package/src/lib/app-server/server.js +21 -2
- package/src/lib/app-server/websocket-transport.js +2 -0
- package/src/lib/evolution/agent-evolution-ingress.js +300 -0
- package/src/lib/evolution/agent-evolution-runtime-composition-brand.js +22 -0
- package/src/lib/evolution/agent-evolution-runtime-composition.js +369 -0
- package/src/lib/evolution/bounded-skill-improvement-pilot.js +498 -0
- package/src/lib/evolution/evidence-backed-wiki-maintainer.js +752 -0
- package/src/lib/evolution/evolution-artifact-ports.js +48 -0
- package/src/lib/evolution/evolution-eval-gate.js +11 -4
- package/src/lib/evolution/evolution-evidence-artifact-adapter.js +499 -0
- package/src/lib/evolution/evolution-evidence-projector.js +61 -1
- package/src/lib/evolution/evolution-file-witness.js +601 -0
- package/src/lib/evolution/evolution-ledger-file-backend.js +176 -0
- package/src/lib/evolution/evolution-ledger-ports.js +353 -16
- package/src/lib/evolution/evolution-ledger.js +1355 -54
- package/src/lib/evolution/evolution-run-ledger-adapter.js +409 -0
- package/src/lib/evolution/evolution-run-runtime-adapter.js +5 -0
- package/src/lib/evolution/evolution-run-wiki-maintenance-source.js +300 -0
- package/src/lib/evolution/scheduled-batch-wiki-maintenance-source.js +368 -0
- package/src/lib/evolution/skill-candidate-registry.js +649 -0
- package/src/lib/evolution/skill-evaluated-promotion-durability.js +899 -0
- package/src/lib/evolution/skill-evaluated-promotion.js +481 -0
- package/src/lib/evolution/skill-promotion-controller.js +220 -37
- package/src/lib/evolution/skill-promotion-review-ledger-adapter.js +451 -0
- package/src/lib/evolution/skill-promotion-review.js +793 -0
- package/src/lib/evolution/skill-registry-transition-capability.js +55 -0
- package/src/lib/evolution/skill-registry-transition-ledger-adapter.js +1280 -0
- package/src/lib/evolution/skill-registry-transition-source.js +372 -0
- package/src/lib/evolution/skill-release-registry.js +2191 -11
- package/src/lib/evolution/skill-target-matrix-eval.js +133 -10
- package/src/lib/evolution/structured-memory-agent-control-plane.js +222 -0
- package/src/lib/evolution/structured-memory-authority-ledger-adapter.js +351 -0
- package/src/lib/evolution/structured-memory-ledger-adapter.js +220 -0
- package/src/lib/evolution/structured-memory-policy-receipt-writer.js +164 -0
- package/src/lib/evolution/structured-memory-post-compact-hook.js +87 -0
- package/src/lib/evolution/structured-memory-promotion-receipt-writer.js +221 -0
- package/src/lib/evolution/structured-memory-semantic-review-pipeline.js +313 -0
- package/src/lib/evolution/wiki-informed-skill-proposer.js +343 -0
- package/src/lib/evolution/wiki-maintainer-ledger-adapter.js +200 -0
- package/src/lib/evolution/wiki-maintenance-trigger-ledger-adapter.js +687 -0
- package/src/lib/evolution-system.js +65 -30
- package/src/lib/graph-kernel/evolution-run-runtime-adapter.js +5 -0
- package/src/lib/hooks-v2-runtime.js +20 -0
- package/src/lib/mcp-lifecycle-authority.js +21 -2
- package/src/lib/remote-approval-bridge.js +24 -0
- package/src/lib/scheduler-kernel/store.js +62 -14
- package/src/repl/agent-repl.js +45 -0
- package/src/runtime/agent-core.js +68 -0
- package/src/runtime/agent-runtime.js +114 -1
- package/src/runtime/headless-runner.js +19 -0
- package/src/runtime/headless-stream.js +43 -0
- package/src/runtime/runtime-factory.js +19 -1
- package/src/assets/web-panel/assets/Projects-Ca9BAfu7.js +0 -1
- package/src/assets/web-panel/assets/devWarning-KO3pAGeO.js +0 -1
- package/src/assets/web-panel/assets/index-Bh4Rgkg2.js +0 -1
- package/src/assets/web-panel/assets/index-Dox_coTY.js +0 -1
- package/src/lib/learning/learning-hooks.js +0 -123
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createEvolutionFileWitness } from "./evolution-file-witness.js";
|
|
4
|
+
import { EvolutionLedger } from "./evolution-ledger.js";
|
|
5
|
+
|
|
6
|
+
export const EVOLUTION_LEDGER_FILE_BACKEND_SCHEMA =
|
|
7
|
+
"chainlesschain.evolution-ledger-file-backend/v1";
|
|
8
|
+
|
|
9
|
+
const BACKENDS = new WeakSet();
|
|
10
|
+
|
|
11
|
+
function requiredString(value, label) {
|
|
12
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
13
|
+
throw new TypeError(`${label} is required`);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function capture(owner, name, label) {
|
|
19
|
+
if (typeof owner?.[name] !== "function") {
|
|
20
|
+
throw new TypeError(`${label}.${name} port is required`);
|
|
21
|
+
}
|
|
22
|
+
const operation = owner[name].bind(owner);
|
|
23
|
+
return Object.freeze((...args) => operation(...args));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function normalizeAuthority(value, label) {
|
|
27
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
28
|
+
throw new TypeError(`${label} authority is required`);
|
|
29
|
+
}
|
|
30
|
+
const trust = value.trust;
|
|
31
|
+
return Object.freeze({
|
|
32
|
+
trust,
|
|
33
|
+
signerIdentity: value.signer,
|
|
34
|
+
verifierIdentity: value.verifier,
|
|
35
|
+
sign: capture(value.signer, "sign", `${label}.signer`),
|
|
36
|
+
verify: capture(value.verifier, "verify", `${label}.verifier`),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function contained(parent, candidate) {
|
|
41
|
+
const relative = path.relative(parent, candidate);
|
|
42
|
+
return (
|
|
43
|
+
relative === "" ||
|
|
44
|
+
(!relative.startsWith(`..${path.sep}`) &&
|
|
45
|
+
relative !== ".." &&
|
|
46
|
+
!path.isAbsolute(relative))
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function assertIndependentStorage(rootDir, authorityRootDir, witnessFilePath) {
|
|
51
|
+
const eventRoot = path.resolve(rootDir);
|
|
52
|
+
const authorityRoot = path.resolve(authorityRootDir);
|
|
53
|
+
const witnessPath = path.resolve(witnessFilePath);
|
|
54
|
+
if (
|
|
55
|
+
contained(eventRoot, authorityRoot) ||
|
|
56
|
+
contained(authorityRoot, eventRoot) ||
|
|
57
|
+
contained(eventRoot, witnessPath) ||
|
|
58
|
+
contained(authorityRoot, witnessPath)
|
|
59
|
+
) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
"event, authority, and witness storage must be independent and non-overlapping",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return Object.freeze({ eventRoot, authorityRoot, witnessPath });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function assertIndependentAuthorities(ledgerAuthority, witnessAuthority) {
|
|
68
|
+
const ledgerTrust = ledgerAuthority.trust;
|
|
69
|
+
const witnessTrust = witnessAuthority.trust;
|
|
70
|
+
if (
|
|
71
|
+
!ledgerTrust ||
|
|
72
|
+
!witnessTrust ||
|
|
73
|
+
ledgerAuthority.signerIdentity === witnessAuthority.signerIdentity ||
|
|
74
|
+
ledgerAuthority.verifierIdentity === witnessAuthority.verifierIdentity ||
|
|
75
|
+
ledgerTrust === witnessTrust ||
|
|
76
|
+
ledgerTrust.keyId === witnessTrust.keyId ||
|
|
77
|
+
ledgerTrust.trustPolicyDigest === witnessTrust.trustPolicyDigest
|
|
78
|
+
) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
"ledger and witness must use independent trust, signer, verifier, keyId, and policy roots",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function createEvolutionLedgerFileBackend({
|
|
86
|
+
rootDir,
|
|
87
|
+
authorityRootDir,
|
|
88
|
+
witnessFilePath,
|
|
89
|
+
witnessId,
|
|
90
|
+
ledgerAuthority: ledgerAuthorityInput,
|
|
91
|
+
witnessAuthority: witnessAuthorityInput,
|
|
92
|
+
artifactResolver,
|
|
93
|
+
secure = true,
|
|
94
|
+
fsImpl = fs,
|
|
95
|
+
clock = Date.now,
|
|
96
|
+
lockClock = Date.now,
|
|
97
|
+
random,
|
|
98
|
+
lock,
|
|
99
|
+
lockTimeoutMs,
|
|
100
|
+
crashHook = null,
|
|
101
|
+
witnessMaximumBytes,
|
|
102
|
+
} = {}) {
|
|
103
|
+
if (typeof artifactResolver !== "function") {
|
|
104
|
+
throw new TypeError("artifactResolver port is required");
|
|
105
|
+
}
|
|
106
|
+
const paths = assertIndependentStorage(
|
|
107
|
+
requiredString(rootDir, "rootDir"),
|
|
108
|
+
requiredString(authorityRootDir, "authorityRootDir"),
|
|
109
|
+
requiredString(witnessFilePath, "witnessFilePath"),
|
|
110
|
+
);
|
|
111
|
+
const ledgerAuthority = normalizeAuthority(
|
|
112
|
+
ledgerAuthorityInput,
|
|
113
|
+
"ledgerAuthority",
|
|
114
|
+
);
|
|
115
|
+
const witnessAuthority = normalizeAuthority(
|
|
116
|
+
witnessAuthorityInput,
|
|
117
|
+
"witnessAuthority",
|
|
118
|
+
);
|
|
119
|
+
assertIndependentAuthorities(ledgerAuthority, witnessAuthority);
|
|
120
|
+
|
|
121
|
+
const witnessOptions = {
|
|
122
|
+
id: requiredString(witnessId, "witnessId"),
|
|
123
|
+
filePath: paths.witnessPath,
|
|
124
|
+
trust: witnessAuthority.trust,
|
|
125
|
+
signer: { sign: witnessAuthority.sign },
|
|
126
|
+
verifier: { verify: witnessAuthority.verify },
|
|
127
|
+
fsImpl,
|
|
128
|
+
...(lock === undefined ? {} : { lock }),
|
|
129
|
+
...(random === undefined ? {} : { random }),
|
|
130
|
+
...(witnessMaximumBytes === undefined
|
|
131
|
+
? {}
|
|
132
|
+
: { maximumBytes: witnessMaximumBytes }),
|
|
133
|
+
};
|
|
134
|
+
const witness = createEvolutionFileWitness(witnessOptions);
|
|
135
|
+
const ledger = new EvolutionLedger({
|
|
136
|
+
rootDir: paths.eventRoot,
|
|
137
|
+
authorityRootDir: paths.authorityRoot,
|
|
138
|
+
secure,
|
|
139
|
+
fsImpl,
|
|
140
|
+
clock,
|
|
141
|
+
lockClock,
|
|
142
|
+
trust: ledgerAuthority.trust,
|
|
143
|
+
sign: ledgerAuthority.sign,
|
|
144
|
+
verifySignature: ledgerAuthority.verify,
|
|
145
|
+
verifyWitnessSignature: witnessAuthority.verify,
|
|
146
|
+
artifactResolver,
|
|
147
|
+
witness,
|
|
148
|
+
witnessTrust: witnessAuthority.trust,
|
|
149
|
+
...(random === undefined ? {} : { random }),
|
|
150
|
+
...(lock === undefined ? {} : { lock }),
|
|
151
|
+
...(lockTimeoutMs === undefined ? {} : { lockTimeoutMs }),
|
|
152
|
+
crashHook,
|
|
153
|
+
});
|
|
154
|
+
const backend = Object.freeze({
|
|
155
|
+
schema: EVOLUTION_LEDGER_FILE_BACKEND_SCHEMA,
|
|
156
|
+
descriptor: Object.freeze({
|
|
157
|
+
rootDir: paths.eventRoot,
|
|
158
|
+
authorityRootDir: paths.authorityRoot,
|
|
159
|
+
witnessFilePath: paths.witnessPath,
|
|
160
|
+
witnessId: witness.id,
|
|
161
|
+
ledgerTrust: Object.freeze({ ...ledgerAuthority.trust }),
|
|
162
|
+
witnessTrust: Object.freeze({ ...witnessAuthority.trust }),
|
|
163
|
+
}),
|
|
164
|
+
ledger,
|
|
165
|
+
witness,
|
|
166
|
+
});
|
|
167
|
+
BACKENDS.add(backend);
|
|
168
|
+
return backend;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function captureEvolutionLedgerFileBackend(value) {
|
|
172
|
+
if (!BACKENDS.has(value)) {
|
|
173
|
+
throw new TypeError("a branded EvolutionLedger file backend is required");
|
|
174
|
+
}
|
|
175
|
+
return value;
|
|
176
|
+
}
|
|
@@ -31,7 +31,13 @@ import {
|
|
|
31
31
|
verifySkillMutationNonceClaim,
|
|
32
32
|
verifySkillMutationRequest,
|
|
33
33
|
} from "./skill-mutation-authority.js";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
SKILL_RELEASE_LEDGER_PROJECTION_SCHEMA,
|
|
36
|
+
SKILL_RELEASE_STATE_LEDGER_MIGRATION_SCHEMA,
|
|
37
|
+
SKILL_RELEASE_STATE_MIGRATION_RECEIPT_SCHEMA,
|
|
38
|
+
verifySkillReleaseState,
|
|
39
|
+
verifySkillReleaseStateMigrationPlan,
|
|
40
|
+
} from "./skill-release-registry.js";
|
|
35
41
|
|
|
36
42
|
export const EVOLUTION_LEDGER_PORTS_INVALID_CODE =
|
|
37
43
|
"CC_EVOLUTION_LEDGER_PORTS_INVALID";
|
|
@@ -61,27 +67,33 @@ const PROJECTION_PREPARE_RECEIPT_DOMAIN =
|
|
|
61
67
|
"chainlesschain.evolution-ledger-release-prepare-projection/v1\0";
|
|
62
68
|
const PROJECTION_FINALIZE_RECEIPT_DOMAIN =
|
|
63
69
|
"chainlesschain.evolution-ledger-release-finalize-projection/v1\0";
|
|
70
|
+
const PROJECTION_MIGRATION_RECEIPT_DOMAIN =
|
|
71
|
+
"chainlesschain.evolution-ledger-release-migration-projection/v1\0";
|
|
64
72
|
const EVENT_ID_DOMAINS = Object.freeze({
|
|
65
73
|
audit: "chainlesschain.evolution-ledger-event-id/mutation.audit/v1\0",
|
|
66
74
|
finalize: "chainlesschain.evolution-ledger-event-id/release.finalize/v1\0",
|
|
75
|
+
migration: "chainlesschain.evolution-ledger-event-id/release.migration/v1\0",
|
|
67
76
|
nonce: "chainlesschain.evolution-ledger-event-id/mutation.nonce/v1\0",
|
|
68
77
|
prepare: "chainlesschain.evolution-ledger-event-id/release.prepare/v1\0",
|
|
69
78
|
});
|
|
70
79
|
const EVENT_PREFIXES = Object.freeze({
|
|
71
80
|
audit: "mutation.audit",
|
|
72
81
|
finalize: "release.finalize",
|
|
82
|
+
migration: "release.migration",
|
|
73
83
|
nonce: "mutation.nonce",
|
|
74
84
|
prepare: "release.prepare",
|
|
75
85
|
});
|
|
76
86
|
const EVENT_TYPES = Object.freeze({
|
|
77
87
|
audit: "skill.mutation.audit",
|
|
78
88
|
finalize: "skill.release.finalize",
|
|
89
|
+
migration: "skill.release.state-migration",
|
|
79
90
|
nonce: "skill.mutation.nonce",
|
|
80
91
|
prepare: "skill.release.prepare",
|
|
81
92
|
});
|
|
82
93
|
const ARTIFACT_TYPES = Object.freeze({
|
|
83
94
|
audit: "skill-mutation-audit",
|
|
84
95
|
finalize: "skill-release-finalization",
|
|
96
|
+
migration: "skill-release-state-migration",
|
|
85
97
|
nonce: "skill-mutation-nonce-claim",
|
|
86
98
|
prepare: "skill-release-transition-intent",
|
|
87
99
|
});
|
|
@@ -216,6 +228,17 @@ const FINALIZE_KEYS = new Set([
|
|
|
216
228
|
"stateDigest",
|
|
217
229
|
"transactionId",
|
|
218
230
|
]);
|
|
231
|
+
const STATE_MIGRATION_KEYS = new Set(["plan", "receipt", "schema", "state"]);
|
|
232
|
+
const STATE_MIGRATION_RECEIPT_KEYS = new Set([
|
|
233
|
+
"authenticated",
|
|
234
|
+
"authorityId",
|
|
235
|
+
"durable",
|
|
236
|
+
"handlerArtifactDigest",
|
|
237
|
+
"receiptDigest",
|
|
238
|
+
"schema",
|
|
239
|
+
"stateMigrationDigest",
|
|
240
|
+
"trust",
|
|
241
|
+
]);
|
|
219
242
|
const RECEIPT_DIGEST_KEYS = new Set([
|
|
220
243
|
"actor",
|
|
221
244
|
"candidate",
|
|
@@ -995,6 +1018,86 @@ function normalizeFinalize(input) {
|
|
|
995
1018
|
return normalized;
|
|
996
1019
|
}
|
|
997
1020
|
|
|
1021
|
+
function normalizeStateMigration(input) {
|
|
1022
|
+
const value = frozenCanonicalClone(input);
|
|
1023
|
+
assertAllExactRecord(
|
|
1024
|
+
value,
|
|
1025
|
+
STATE_MIGRATION_KEYS,
|
|
1026
|
+
"release state ledger migration",
|
|
1027
|
+
);
|
|
1028
|
+
if (value.schema !== SKILL_RELEASE_STATE_LEDGER_MIGRATION_SCHEMA) {
|
|
1029
|
+
throw portsError(
|
|
1030
|
+
EVOLUTION_LEDGER_PORTS_INVALID_CODE,
|
|
1031
|
+
"release state ledger migration schema is unsupported",
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
let plan;
|
|
1035
|
+
let state;
|
|
1036
|
+
try {
|
|
1037
|
+
plan = verifySkillReleaseStateMigrationPlan(value.plan);
|
|
1038
|
+
state = verifySkillReleaseState(value.state);
|
|
1039
|
+
} catch (cause) {
|
|
1040
|
+
throw portsError(
|
|
1041
|
+
EVOLUTION_LEDGER_PORTS_INVALID_CODE,
|
|
1042
|
+
"release state ledger migration contains an invalid plan or state",
|
|
1043
|
+
{ cause },
|
|
1044
|
+
);
|
|
1045
|
+
}
|
|
1046
|
+
assertAllExactRecord(
|
|
1047
|
+
value.receipt,
|
|
1048
|
+
STATE_MIGRATION_RECEIPT_KEYS,
|
|
1049
|
+
"release state migration receipt",
|
|
1050
|
+
);
|
|
1051
|
+
const receipt = deepFreeze({
|
|
1052
|
+
authenticated: value.receipt.authenticated,
|
|
1053
|
+
authorityId: identifier(
|
|
1054
|
+
value.receipt.authorityId,
|
|
1055
|
+
"state migration receipt authorityId",
|
|
1056
|
+
),
|
|
1057
|
+
durable: value.receipt.durable,
|
|
1058
|
+
handlerArtifactDigest: digest(
|
|
1059
|
+
value.receipt.handlerArtifactDigest,
|
|
1060
|
+
"state migration receipt handlerArtifactDigest",
|
|
1061
|
+
),
|
|
1062
|
+
receiptDigest: digest(
|
|
1063
|
+
value.receipt.receiptDigest,
|
|
1064
|
+
"state migration receipt receiptDigest",
|
|
1065
|
+
),
|
|
1066
|
+
schema: value.receipt.schema,
|
|
1067
|
+
stateMigrationDigest: digest(
|
|
1068
|
+
value.receipt.stateMigrationDigest,
|
|
1069
|
+
"state migration receipt stateMigrationDigest",
|
|
1070
|
+
),
|
|
1071
|
+
trust: value.receipt.trust,
|
|
1072
|
+
});
|
|
1073
|
+
if (
|
|
1074
|
+
receipt.schema !== SKILL_RELEASE_STATE_MIGRATION_RECEIPT_SCHEMA ||
|
|
1075
|
+
receipt.authenticated !== true ||
|
|
1076
|
+
receipt.durable !== true ||
|
|
1077
|
+
receipt.trust !== "trusted" ||
|
|
1078
|
+
receipt.stateMigrationDigest !== plan.stateMigrationDigest ||
|
|
1079
|
+
state.transactionId !== plan.stateMigrationDigest ||
|
|
1080
|
+
state.tenantId !== plan.tenantId ||
|
|
1081
|
+
state.skillName !== plan.skillName ||
|
|
1082
|
+
state.activeReleaseDigest !== plan.activeReleaseDigest ||
|
|
1083
|
+
state.lastKnownGoodReleaseDigest !== plan.lastKnownGoodReleaseDigest ||
|
|
1084
|
+
state.dependencyLockDigest !== plan.dependencyLockDigest ||
|
|
1085
|
+
state.revision !== plan.legacyRevision ||
|
|
1086
|
+
state.fence !== plan.legacyFence
|
|
1087
|
+
) {
|
|
1088
|
+
throw portsError(
|
|
1089
|
+
EVOLUTION_LEDGER_PORTS_INVALID_CODE,
|
|
1090
|
+
"release state ledger migration bindings are invalid",
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
return deepFreeze({
|
|
1094
|
+
plan,
|
|
1095
|
+
receipt,
|
|
1096
|
+
schema: SKILL_RELEASE_STATE_LEDGER_MIGRATION_SCHEMA,
|
|
1097
|
+
state,
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
|
|
998
1101
|
function logicalFinalizeDigest(value) {
|
|
999
1102
|
return domainDigest(RELEASE_FINALIZATION_DOMAIN, value);
|
|
1000
1103
|
}
|
|
@@ -1011,12 +1114,13 @@ function projectionReceiptDigest(kind, projection) {
|
|
|
1011
1114
|
const core = { ...projection };
|
|
1012
1115
|
delete core.current;
|
|
1013
1116
|
delete core.receiptDigest;
|
|
1014
|
-
|
|
1117
|
+
const domain =
|
|
1015
1118
|
kind === "prepare"
|
|
1016
1119
|
? PROJECTION_PREPARE_RECEIPT_DOMAIN
|
|
1017
|
-
:
|
|
1018
|
-
|
|
1019
|
-
|
|
1120
|
+
: kind === "migration"
|
|
1121
|
+
? PROJECTION_MIGRATION_RECEIPT_DOMAIN
|
|
1122
|
+
: PROJECTION_FINALIZE_RECEIPT_DOMAIN;
|
|
1123
|
+
return domainDigest(domain, core);
|
|
1020
1124
|
}
|
|
1021
1125
|
|
|
1022
1126
|
class EvolutionLedgerDomainPorts {
|
|
@@ -1845,6 +1949,80 @@ class EvolutionLedgerDomainPorts {
|
|
|
1845
1949
|
});
|
|
1846
1950
|
}
|
|
1847
1951
|
|
|
1952
|
+
#migrationFromEvent(event, record, expected = null) {
|
|
1953
|
+
const migration = normalizeStateMigration(record.value);
|
|
1954
|
+
const eventId = deterministicEventId("migration", {
|
|
1955
|
+
transactionId: migration.plan.stateMigrationDigest,
|
|
1956
|
+
});
|
|
1957
|
+
const sourceRefs = this.#assertDomainEvent(
|
|
1958
|
+
event,
|
|
1959
|
+
"migration",
|
|
1960
|
+
eventId,
|
|
1961
|
+
migration.plan.stateMigrationDigest,
|
|
1962
|
+
);
|
|
1963
|
+
if (
|
|
1964
|
+
sourceRefs.length !== 0 ||
|
|
1965
|
+
event.decision !== "committed" ||
|
|
1966
|
+
event.correlationId !== migration.plan.stateMigrationDigest ||
|
|
1967
|
+
event.tenantId !== migration.plan.tenantId ||
|
|
1968
|
+
event.skillName !== migration.plan.skillName ||
|
|
1969
|
+
record.audience !== this.#audience
|
|
1970
|
+
) {
|
|
1971
|
+
throw portsError(
|
|
1972
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
1973
|
+
"release state migration event differs from its authenticated plan",
|
|
1974
|
+
);
|
|
1975
|
+
}
|
|
1976
|
+
if (
|
|
1977
|
+
expected !== null &&
|
|
1978
|
+
(expected.transactionId !== migration.plan.stateMigrationDigest ||
|
|
1979
|
+
(expected.stateDigest !== undefined &&
|
|
1980
|
+
expected.stateDigest !== migration.state.stateDigest))
|
|
1981
|
+
) {
|
|
1982
|
+
throw portsError(
|
|
1983
|
+
EVOLUTION_LEDGER_PORTS_COLLISION_CODE,
|
|
1984
|
+
"release state migration collides with another state baseline",
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
1987
|
+
return { event, migration, record };
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
#migrationFromSnapshot(snapshot, event, expected = null) {
|
|
1991
|
+
const record = this.#snapshotSubject(
|
|
1992
|
+
snapshot,
|
|
1993
|
+
event,
|
|
1994
|
+
ARTIFACT_TYPES.migration,
|
|
1995
|
+
);
|
|
1996
|
+
return this.#migrationFromEvent(event, record, expected);
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
#migrationProjection(migrated, current) {
|
|
2000
|
+
const { plan, receipt, state } = migrated.migration;
|
|
2001
|
+
const core = {
|
|
2002
|
+
authenticated: true,
|
|
2003
|
+
authorityReceiptDigest: state.authorityReceiptDigest,
|
|
2004
|
+
current,
|
|
2005
|
+
durable: true,
|
|
2006
|
+
epoch: migrated.event.epoch,
|
|
2007
|
+
headDigest: migrated.event.eventDigest,
|
|
2008
|
+
intentDigest: plan.stateMigrationDigest,
|
|
2009
|
+
ledgerId: migrated.event.ledgerId,
|
|
2010
|
+
pointerDigest: state.stateDigest,
|
|
2011
|
+
prepareReceiptDigest: receipt.receiptDigest,
|
|
2012
|
+
revision: state.revision,
|
|
2013
|
+
schema: SKILL_RELEASE_LEDGER_PROJECTION_SCHEMA,
|
|
2014
|
+
sequence: migrated.event.sequence,
|
|
2015
|
+
skillName: state.skillName,
|
|
2016
|
+
stateDigest: state.stateDigest,
|
|
2017
|
+
status: "committed",
|
|
2018
|
+
transactionId: state.transactionId,
|
|
2019
|
+
};
|
|
2020
|
+
return deepFreeze({
|
|
2021
|
+
...core,
|
|
2022
|
+
receiptDigest: projectionReceiptDigest("migration", core),
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
|
|
1848
2026
|
#finalizeFromEvent(snapshot, event, prepareCache = new Map()) {
|
|
1849
2027
|
const record = this.#snapshotSubject(
|
|
1850
2028
|
snapshot,
|
|
@@ -1917,6 +2095,30 @@ class EvolutionLedgerDomainPorts {
|
|
|
1917
2095
|
#lineages(snapshot, prepareCache = new Map()) {
|
|
1918
2096
|
const groups = new Map();
|
|
1919
2097
|
const byTransaction = new Map();
|
|
2098
|
+
const migrations = new Map();
|
|
2099
|
+
const migrationsByTransaction = new Map();
|
|
2100
|
+
for (const event of snapshot.events) {
|
|
2101
|
+
if (
|
|
2102
|
+
event.schema !== EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA ||
|
|
2103
|
+
event.type !== EVENT_TYPES.migration
|
|
2104
|
+
) {
|
|
2105
|
+
continue;
|
|
2106
|
+
}
|
|
2107
|
+
const migrated = this.#migrationFromSnapshot(snapshot, event);
|
|
2108
|
+
const transactionId = migrated.migration.plan.stateMigrationDigest;
|
|
2109
|
+
const groupKey = `${migrated.migration.plan.tenantId}\0${migrated.migration.plan.skillName}`;
|
|
2110
|
+
if (
|
|
2111
|
+
migrations.has(groupKey) ||
|
|
2112
|
+
migrationsByTransaction.has(transactionId)
|
|
2113
|
+
) {
|
|
2114
|
+
throw portsError(
|
|
2115
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2116
|
+
"release state migration baseline appears more than once",
|
|
2117
|
+
);
|
|
2118
|
+
}
|
|
2119
|
+
migrations.set(groupKey, migrated);
|
|
2120
|
+
migrationsByTransaction.set(transactionId, migrated);
|
|
2121
|
+
}
|
|
1920
2122
|
for (const event of snapshot.events) {
|
|
1921
2123
|
if (
|
|
1922
2124
|
event.schema !== EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA ||
|
|
@@ -1943,14 +2145,24 @@ class EvolutionLedgerDomainPorts {
|
|
|
1943
2145
|
left.finalization.revision - right.finalization.revision ||
|
|
1944
2146
|
left.event.sequence - right.event.sequence,
|
|
1945
2147
|
);
|
|
1946
|
-
|
|
2148
|
+
const first = group[0];
|
|
2149
|
+
const groupKey = `${first.tenantId}\0${first.finalization.skillName}`;
|
|
2150
|
+
const baseline = migrations.get(groupKey) || null;
|
|
2151
|
+
let previous = baseline;
|
|
1947
2152
|
for (let index = 0; index < group.length; index += 1) {
|
|
1948
2153
|
const entry = group[index];
|
|
2154
|
+
const previousRevision =
|
|
2155
|
+
previous?.finalization?.revision ??
|
|
2156
|
+
previous?.migration?.state.revision ??
|
|
2157
|
+
0;
|
|
2158
|
+
const previousStateDigest =
|
|
2159
|
+
previous?.finalization?.stateDigest ??
|
|
2160
|
+
previous?.migration?.state.stateDigest ??
|
|
2161
|
+
null;
|
|
1949
2162
|
if (
|
|
1950
|
-
entry.finalization.revision !==
|
|
1951
|
-
entry.prepared.intent.expectedRevision !==
|
|
1952
|
-
entry.prepared.intent.previousStateDigest !==
|
|
1953
|
-
(previous?.finalization.stateDigest ?? null) ||
|
|
2163
|
+
entry.finalization.revision !== previousRevision + 1 ||
|
|
2164
|
+
entry.prepared.intent.expectedRevision !== previousRevision ||
|
|
2165
|
+
entry.prepared.intent.previousStateDigest !== previousStateDigest ||
|
|
1954
2166
|
(previous !== null && entry.event.sequence <= previous.event.sequence)
|
|
1955
2167
|
) {
|
|
1956
2168
|
throw portsError(
|
|
@@ -1961,7 +2173,7 @@ class EvolutionLedgerDomainPorts {
|
|
|
1961
2173
|
previous = entry;
|
|
1962
2174
|
}
|
|
1963
2175
|
}
|
|
1964
|
-
return { byTransaction, groups };
|
|
2176
|
+
return { byTransaction, groups, migrations, migrationsByTransaction };
|
|
1965
2177
|
}
|
|
1966
2178
|
|
|
1967
2179
|
#committedProjection(finalized, lineages) {
|
|
@@ -2038,11 +2250,21 @@ class EvolutionLedgerDomainPorts {
|
|
|
2038
2250
|
return { existing, lineages, prepared };
|
|
2039
2251
|
}
|
|
2040
2252
|
const groupKey = `${prepared.intent.mutationRequest.tenantId}\0${finalization.skillName}`;
|
|
2041
|
-
const current =
|
|
2253
|
+
const current =
|
|
2254
|
+
lineages.groups.get(groupKey)?.at(-1) ||
|
|
2255
|
+
lineages.migrations.get(groupKey) ||
|
|
2256
|
+
null;
|
|
2257
|
+
const currentRevision =
|
|
2258
|
+
current?.finalization?.revision ??
|
|
2259
|
+
current?.migration?.state.revision ??
|
|
2260
|
+
0;
|
|
2261
|
+
const currentStateDigest =
|
|
2262
|
+
current?.finalization?.stateDigest ??
|
|
2263
|
+
current?.migration?.state.stateDigest ??
|
|
2264
|
+
null;
|
|
2042
2265
|
if (
|
|
2043
|
-
finalization.revision !==
|
|
2044
|
-
prepared.intent.previousStateDigest !==
|
|
2045
|
-
(current?.finalization.stateDigest ?? null)
|
|
2266
|
+
finalization.revision !== currentRevision + 1 ||
|
|
2267
|
+
prepared.intent.previousStateDigest !== currentStateDigest
|
|
2046
2268
|
) {
|
|
2047
2269
|
throw portsError(
|
|
2048
2270
|
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
@@ -2052,6 +2274,99 @@ class EvolutionLedgerDomainPorts {
|
|
|
2052
2274
|
return { existing: null, lineages, prepared };
|
|
2053
2275
|
}
|
|
2054
2276
|
|
|
2277
|
+
migrate(input) {
|
|
2278
|
+
const migration = normalizeStateMigration(input);
|
|
2279
|
+
const transactionId = migration.plan.stateMigrationDigest;
|
|
2280
|
+
const eventId = deterministicEventId("migration", { transactionId });
|
|
2281
|
+
let snapshot = this.#snapshot();
|
|
2282
|
+
for (let attempt = 0; attempt < MAX_FINALIZE_RETRIES; attempt += 1) {
|
|
2283
|
+
const existingEvent = snapshot.byEventId.get(eventId);
|
|
2284
|
+
const lineages = this.#lineages(snapshot);
|
|
2285
|
+
const groupKey = `${migration.plan.tenantId}\0${migration.plan.skillName}`;
|
|
2286
|
+
const existingGroupMigration = lineages.migrations.get(groupKey) || null;
|
|
2287
|
+
const existingFinalizations = lineages.groups.get(groupKey) || [];
|
|
2288
|
+
if (existingEvent) {
|
|
2289
|
+
const existing = this.#migrationFromSnapshot(snapshot, existingEvent, {
|
|
2290
|
+
stateDigest: migration.state.stateDigest,
|
|
2291
|
+
transactionId,
|
|
2292
|
+
});
|
|
2293
|
+
if (
|
|
2294
|
+
existing.migration.plan.stateMigrationDigest !== transactionId ||
|
|
2295
|
+
canonicalJson(existing.migration) !== canonicalJson(migration)
|
|
2296
|
+
) {
|
|
2297
|
+
throw portsError(
|
|
2298
|
+
EVOLUTION_LEDGER_PORTS_COLLISION_CODE,
|
|
2299
|
+
"release state migration event id is bound to other evidence",
|
|
2300
|
+
);
|
|
2301
|
+
}
|
|
2302
|
+
return this.#migrationProjection(
|
|
2303
|
+
existing,
|
|
2304
|
+
existingFinalizations.length === 0,
|
|
2305
|
+
);
|
|
2306
|
+
}
|
|
2307
|
+
if (existingGroupMigration || existingFinalizations.length > 0) {
|
|
2308
|
+
throw portsError(
|
|
2309
|
+
EVOLUTION_LEDGER_PORTS_COLLISION_CODE,
|
|
2310
|
+
"release state migration requires an empty Skill ledger lineage",
|
|
2311
|
+
);
|
|
2312
|
+
}
|
|
2313
|
+
const subjectRef = this.#putSubject(
|
|
2314
|
+
ARTIFACT_TYPES.migration,
|
|
2315
|
+
migration,
|
|
2316
|
+
transactionId,
|
|
2317
|
+
this.#audience,
|
|
2318
|
+
);
|
|
2319
|
+
const previous = snapshot.events.at(-1) || null;
|
|
2320
|
+
try {
|
|
2321
|
+
this.#verifiedAppend(
|
|
2322
|
+
{
|
|
2323
|
+
artifactTenantId: this.#artifactTenantId,
|
|
2324
|
+
correlationId: transactionId,
|
|
2325
|
+
decision: "committed",
|
|
2326
|
+
eventId,
|
|
2327
|
+
reason: transactionId,
|
|
2328
|
+
skillName: migration.plan.skillName,
|
|
2329
|
+
sourceRefs: [],
|
|
2330
|
+
subjectRef,
|
|
2331
|
+
tenantId: migration.plan.tenantId,
|
|
2332
|
+
type: EVENT_TYPES.migration,
|
|
2333
|
+
},
|
|
2334
|
+
{
|
|
2335
|
+
expectedHeadDigest: previous?.eventDigest ?? null,
|
|
2336
|
+
expectedSequence: previous?.sequence ?? 0,
|
|
2337
|
+
},
|
|
2338
|
+
);
|
|
2339
|
+
} catch (cause) {
|
|
2340
|
+
if (isHeadConflict(cause)) {
|
|
2341
|
+
snapshot = this.#snapshot();
|
|
2342
|
+
continue;
|
|
2343
|
+
}
|
|
2344
|
+
if (!isCommitUnknown(cause) && !isEventConflict(cause)) throw cause;
|
|
2345
|
+
}
|
|
2346
|
+
const recovered = this.#snapshot();
|
|
2347
|
+
const persisted = recovered.byEventId.get(eventId);
|
|
2348
|
+
if (!persisted) {
|
|
2349
|
+
throw portsError(
|
|
2350
|
+
EVOLUTION_LEDGER_PORTS_UNAVAILABLE_CODE,
|
|
2351
|
+
"release state migration did not converge to a queryable event",
|
|
2352
|
+
);
|
|
2353
|
+
}
|
|
2354
|
+
const migrated = this.#migrationFromSnapshot(recovered, persisted, {
|
|
2355
|
+
stateDigest: migration.state.stateDigest,
|
|
2356
|
+
transactionId,
|
|
2357
|
+
});
|
|
2358
|
+
const recoveredLineages = this.#lineages(recovered);
|
|
2359
|
+
return this.#migrationProjection(
|
|
2360
|
+
migrated,
|
|
2361
|
+
(recoveredLineages.groups.get(groupKey) || []).length === 0,
|
|
2362
|
+
);
|
|
2363
|
+
}
|
|
2364
|
+
throw portsError(
|
|
2365
|
+
EVOLUTION_LEDGER_PORTS_UNAVAILABLE_CODE,
|
|
2366
|
+
"release state migration could not acquire a stable ledger head",
|
|
2367
|
+
);
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2055
2370
|
prepare(input) {
|
|
2056
2371
|
const intent = normalizeIntent(input);
|
|
2057
2372
|
this.#assertAudience(intent.mutationRequest.audience, "intent audience");
|
|
@@ -2186,11 +2501,32 @@ class EvolutionLedgerDomainPorts {
|
|
|
2186
2501
|
|
|
2187
2502
|
query(transactionIdValue) {
|
|
2188
2503
|
const transactionId = digest(transactionIdValue, "transactionId");
|
|
2504
|
+
const migrationEventId = deterministicEventId("migration", {
|
|
2505
|
+
transactionId,
|
|
2506
|
+
});
|
|
2189
2507
|
const prepareEventId = deterministicEventId("prepare", { transactionId });
|
|
2190
2508
|
const finalizeEventId = deterministicEventId("finalize", { transactionId });
|
|
2191
2509
|
const snapshot = this.#snapshot();
|
|
2510
|
+
const migrationEvent = snapshot.byEventId.get(migrationEventId);
|
|
2192
2511
|
const prepareEvent = snapshot.byEventId.get(prepareEventId);
|
|
2193
2512
|
const finalizeEvent = snapshot.byEventId.get(finalizeEventId);
|
|
2513
|
+
if (migrationEvent) {
|
|
2514
|
+
if (prepareEvent || finalizeEvent) {
|
|
2515
|
+
throw portsError(
|
|
2516
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2517
|
+
"release transaction id has both migration and transition evidence",
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
const migrated = this.#migrationFromSnapshot(snapshot, migrationEvent, {
|
|
2521
|
+
transactionId,
|
|
2522
|
+
});
|
|
2523
|
+
const lineages = this.#lineages(snapshot);
|
|
2524
|
+
const groupKey = `${migrated.migration.plan.tenantId}\0${migrated.migration.plan.skillName}`;
|
|
2525
|
+
return this.#migrationProjection(
|
|
2526
|
+
migrated,
|
|
2527
|
+
(lineages.groups.get(groupKey) || []).length === 0,
|
|
2528
|
+
);
|
|
2529
|
+
}
|
|
2194
2530
|
if (!prepareEvent) {
|
|
2195
2531
|
if (finalizeEvent) {
|
|
2196
2532
|
throw portsError(
|
|
@@ -2675,13 +3011,14 @@ export function createEvolutionLedgerPorts(options = {}) {
|
|
|
2675
3011
|
});
|
|
2676
3012
|
const prepare = Object.freeze((input) => adapter.prepare(input));
|
|
2677
3013
|
const finalize = Object.freeze((input) => adapter.finalize(input));
|
|
3014
|
+
const migrate = Object.freeze((input) => adapter.migrate(input));
|
|
2678
3015
|
const query = Object.freeze((transactionId) => adapter.query(transactionId));
|
|
2679
3016
|
const append = Object.freeze((event) => adapter.appendAudit(event));
|
|
2680
3017
|
const claim = Object.freeze((nonce) => adapter.claimNonce(nonce));
|
|
2681
3018
|
return Object.freeze({
|
|
2682
3019
|
auditSink: Object.freeze({ append }),
|
|
2683
3020
|
nonceStore: Object.freeze({ claim }),
|
|
2684
|
-
transactionLedger: Object.freeze({ finalize, prepare, query }),
|
|
3021
|
+
transactionLedger: Object.freeze({ finalize, migrate, prepare, query }),
|
|
2685
3022
|
});
|
|
2686
3023
|
}
|
|
2687
3024
|
|