chainlesschain 0.166.22 → 0.166.25
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 +46 -3
- package/package.json +4 -3
- package/src/assets/web-panel/assets/{AIOps-oJ7USzMC.js → AIOps-RaxL_QQD.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BlgnZe_C.js → ActionButton-DD3ieFO-.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D2rRbTLu.js → Analytics-CATDJukS.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-FwS3f0B2.js → AppLayout-BHGsnRDS.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-iF-_08Hi.js → Artifacts-BL6NunjN.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-D24p0zQw.js → Audit-D0nEWfbA.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Cg3mRVi9.js → BackgroundAgents-BAP1MUol.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-EOqht3H-.js → Backup-R4y2OeSi.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-9XWYq2hj.js → BaseInput-ByQZ2z6S.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Dq5yardA.js → Chat-32uV--xF.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-t1enanwz.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-CuyCBoBf.js → Checkbox-D1CB-7bq.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DjWtYHNe.js → Codegen-BPx3dQ2q.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CrVr_iuG.js → Col-DkPTQ2Gl.js} +1 -1
- package/src/assets/web-panel/assets/{Community-C6QxpfGc.js → Community-ZriaxRlr.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-B-v5r6Pa.js → Compact-C_qJ0P7F.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-zNiLiFgD.js → Compliance-Bgoy-saS.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-D9K7lMOu.js → Cowork-B5i9y6XK.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-DAjO36z4.js → Cron-D7vBJ5-J.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Bxqn0aqB.js → Crosschain-D4kvoGI_.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DfRmLUe-.js → DID-BLOeBd-r.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Cz8KF3fr.js → Dashboard-mecJaonC.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-C4oVfkP0.js → Dropdown-GPKwaFer.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-ivyZKN8k.js → EmailListRenderer-BML9m7Dd.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DoxY1CPW.js → FamilyGuardDashboard-DN5bAtJC.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-C93gZxnb.js → Federation-DmD6Gecd.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-D_lEjYJE.js → FormItemContext-6wwplN_K.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BNFN9YjN.js → GenericCardRenderer-DUhpLkDL.js} +1 -1
- package/src/assets/web-panel/assets/{Git-mUu2HcLI.js → Git-eJfyXkwf.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CNuosrge.js → Governance-swAUW59x.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DwY5pbCB.js → Inference-CbdwJPDJ.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-aRUxhACD.js → KnowledgeGraph-UuYpg1nm.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DFfsWYy_.js → Logs-CS0G057y.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BxIIB4k3.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-COI-ciM-.js → Marketplace-CZRjuvvI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BJJwaIpe.js → McpTools-BOJ9A_Rf.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CDO6SGDZ.js → Memory-BnkrR-Ok.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BlZ5tFf4.js → MobileBridge-Do4mPi13.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DuoxbgWz.js → MobileProjects-2JxWa6-p.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-6ec9j70b.js → Mtc-CS_mnpGd.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-BuL7HkC7.js → MtcAudit-DZ4J_351.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CJxR85lk.js → Multisig-Jh74zKXu.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-B71O1S4l.js → NLProgramming-_4n2BfnS.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Bsw5zbpV.js → Notes-CtENcKHv.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BnAhXuK_.js → NotificationSettings-dgDCvpK9.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BJ0R5GGz.js → OrderTableRenderer-D5VxCrmG.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-B7wwNxoP.js → Organization-CpIF8Xkd.js} +2 -2
- package/src/assets/web-panel/assets/{Overflow-CfAGr0gM.js → Overflow-aHnBZaez.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-KhMg22Fz.js → P2P-8ppzog2V.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-NdAXI9ft.js → PdhVaultBrowser-D3p1BXkp.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CIYjqyT7.js → Permissions-D7vflD5q.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-D2ht3P_R.js → PersonalDataHub-D2hY-Ek2.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-i6flZW1m.js → Pipeline-qK2VqhJC.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DE0lhYVc.js → Privacy-DHJr4uu0.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BL3YbyxS.js → ProjectInit-DMBS6trm.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CEtdg7ni.js → ProjectSettings-D3Ng74BT.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-CvtU5peJ.js → Projects-BfJFZiIW.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DKt_Cg-j.js → Providers-DlNA95G0.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DA61jNSx.js → QrScannerModal-CMOIYxIm.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-fS9ryUmq.js → QuickAsk-D6MyGp5I.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CR9dZU5Y.js → Recommend-W6WcShtf.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BV0BR2Dz.js → RemoteSession-Dm_MjlV_.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-BIeGUvYD.js → Reputation-Ctvij6eX.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DHDT1aro.js → Row-DivbeDE-.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Cq6YKb1h.js → RssFeed-D4famoAw.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DEF6vsy6.js → Search-6gOAh3zG.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CHtYiKY9.js → Security-DApb9kK8.js} +4 -4
- package/src/assets/web-panel/assets/{Services-u16lvBJN.js → Services-B8OomyE2.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CGbizyDj.js → Skeleton-BTJ2g2E3.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BZMQcAa7.js → Skills-NIRr4DD1.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Bm-CycVi.js → Sla-BQHP_6px.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BudpA5kr.js → SpeechSettings-BVQTDSVS.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B8q1C1eC.js → SyncSettings-BOD4Mar6.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-SU1pBWc-.js → Tasks-Ciu7H2-y.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CyOfJzmt.js → Templates-CtlwavRU.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-xqWSm2GP.js → Tenant-QLWYJdL6.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-D1Ttgjsi.js → Terminal-B3q10mjm.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Da2lYs5D.js → TimelineRenderer-CqltBA_8.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-B34-SduO.js → Tokens-CJOKrj4y.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-C8zyYm4R.js → Trigger-CPMGgfdM.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D1Pvqems.js → Trust-DiJms7Bl.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Do3VVnAy.js → UkeySign-rakR8eel.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Dz7egVIA.js → VideoEditing-DAWZUM5o.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Dcuw-OxR.js → Wallet-DqUPnl7K.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-DPlw-QsB.js → WebAuthn-BnVUwvMm.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-h4OsnU5q.js → WorkflowEditor-B1CAwxgs.js} +1 -1
- package/src/assets/web-panel/assets/{chat-eO9wEwrW.js → chat-k2-0Jzfy.js} +1 -1
- package/src/assets/web-panel/assets/{colors-34lSmOXw.js → colors-BAu8xBmW.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CK4kEWJ4.js → compact-item-DMlqcdfI.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-E0A57xed.js → createContext-BU9kfiSw.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DuGrUMAS.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BD9ZMGAl.js → hasIn-2vacgdcl.js} +1 -1
- package/src/assets/web-panel/assets/{index--N0u6kTG.js → index-B-C-Ruft.js} +1 -1
- package/src/assets/web-panel/assets/{index-DzXjP44T.js → index-B7wDjYUd.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZBaqe3d.js → index-BMq0frKK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DrOalGut.js → index-BUX4W-2v.js} +1 -1
- package/src/assets/web-panel/assets/{index-CT1_eRL-.js → index-BZz2eB33.js} +1 -1
- package/src/assets/web-panel/assets/{index-DrsDA_md.js → index-BblNQlaA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DP-UZAQN.js → index-BdfKoqsK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DiT_REby.js → index-Bf8YofPE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cx3Jn1r0.js → index-Bn8tARil.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0rkHxKK.js → index-Bpe2CJd-.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_G_ElKO.js → index-BxctzdYQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-MIg5Z8qe.js → index-C24NoPpy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmNbU2Z6.js → index-C5C4kSi8.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsGmo6v1.js → index-C6ICn9Y6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CsrKJJcX.js → index-C8ldqZi6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbX_Dted.js → index-C9WsM29I.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ch175NUw.js → index-CONPVaxw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DONOUtlz.js → index-CPyYXKFB.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFLE2qO9.js → index-CSvSf9px.js} +1 -1
- package/src/assets/web-panel/assets/{index-o8pYehRq.js → index-CXmpR6N3.js} +3 -3
- package/src/assets/web-panel/assets/{index-Cz3unNfL.js → index-Chybe7Ge.js} +1 -1
- package/src/assets/web-panel/assets/{index-yAGBu2E0.js → index-CyXmP3PW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CuSau5ya.js → index-DCusBOqR.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcE1BspQ.js → index-DH01oIaj.js} +1 -1
- package/src/assets/web-panel/assets/{index-DrSAAuJj.js → index-DHg3LMNP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZkFMeWL.js → index-DWLTSIka.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvKcroix.js → index-DaUGdvtU.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bjs90Eh9.js → index-DcA-tEQd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGRxt5wj.js → index-DfWvcu4p.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCn-ECvK.js → index-DmZNnZz7.js} +1 -1
- package/src/assets/web-panel/assets/index-DvFfMAux.js +1 -0
- package/src/assets/web-panel/assets/{index-8qMA1DWQ.js → index-JsyGqKmD.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4bB_x_v.js → index-TtrpHmYx.js} +1 -1
- package/src/assets/web-panel/assets/{index-Brqc9Emx.js → index-Ygh0wmKz.js} +1 -1
- package/src/assets/web-panel/assets/{index-Da5fmOHB.js → index-dIYqw3f0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpfisY1g.js → index-hDHHJYno.js} +1 -1
- package/src/assets/web-panel/assets/index-n6p-1A77.js +1 -0
- package/src/assets/web-panel/assets/{index-DeXNCyQJ.js → index-oElehY4F.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcbgVLOH.js → index-z4_YwZDW.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DMTkAomF.js → initDefaultProps-B9OXWu2e.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Cnti1F5g.js → motion-IQ47MCDQ.js} +1 -1
- package/src/assets/web-panel/assets/{move-CGxmHUga.js → move-CLCU4kjV.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CuwwLoq7.js → mtc-parser-CXnVlSPq.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BpdlTkPM.js → omit-BJjLXB0f.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D7nWrThp.js → pickAttrs-BHdtvdy6.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Bhx0TV8F.js → placementArrow-oq8X_Bwc.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CZ1KesZh.js → responsiveObserve-DTD02LAO.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CnldF8J6.js → slide-DbEeq8to.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CZfo_GOA.js → statusUtils-Do2fy1CJ.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B2QCcAv5.js → styleChecker-CFWD63T-.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DzzjIS4J.js → useFlexGapSupport-BeozAWOo.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-B_atg9M0.js → useFs-0-rIgnBL.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DhZs3QYN.js → usePersonalDataHub-CeiNoI31.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DcePTYAm.js → vnode-Dj0ReRNg.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BYmlnjRk.js → zoom-DD7MC4Dy.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 +18 -1
- package/src/commands/evolution-benchmark.js +72 -0
- package/src/commands/evolution-knowledge.js +40 -1
- package/src/commands/evolution.js +2 -0
- package/src/commands/governed-skill-marketplace.js +142 -0
- package/src/commands/marketplace.js +4 -1
- package/src/commands/serve.js +21 -0
- package/src/commands/skill.js +3 -0
- package/src/data/changelog.json +39 -0
- package/src/gateways/ws/ws-agent-handler.js +59 -2
- package/src/gateways/ws/ws-server.js +2 -0
- package/src/generated/cc-agent-protocol.schema.json +3 -1
- package/src/harness/golden-transcript.js +1 -0
- package/src/harness/prompt-compressor.js +51 -5
- package/src/harness/provider-backed-compaction.js +28 -1
- package/src/harness/remote-session-registry.js +15 -3
- package/src/index.js +20 -2
- package/src/lazy-dispatch.js +6 -0
- package/src/lib/advisor-runtime.js +55 -2
- package/src/lib/app-server/cli-agent-kernel-adapter.js +4 -0
- package/src/lib/app-server/graph-runtime.js +32 -0
- package/src/lib/app-server/server.js +94 -1
- package/src/lib/app-server/websocket-transport.js +15 -0
- package/src/lib/autonomous-agent.js +63 -13
- package/src/lib/cowork/ab-comparator-cli.js +4 -3
- package/src/lib/cowork/debate-review-cli.js +4 -3
- package/src/lib/cowork/decision-kb-cli.js +5 -0
- package/src/lib/cowork-adapter.js +38 -16
- package/src/lib/evolution/agent-evolution-ingress.js +95 -15
- package/src/lib/evolution/agent-evolution-runtime-composition.js +82 -0
- package/src/lib/evolution/agent-evolution-session-lifecycle.js +46 -0
- package/src/lib/evolution/agent-model-projection.js +437 -0
- package/src/lib/evolution/bounded-descriptor-read.js +29 -0
- package/src/lib/evolution/controlled-skill-pilot-ledger-adapter.js +551 -0
- package/src/lib/evolution/controlled-skill-production-pilot.js +308 -18
- package/src/lib/evolution/evidence-backed-wiki-maintainer.js +32 -10
- package/src/lib/evolution/evidence-json-text.js +236 -0
- package/src/lib/evolution/evolution-artifact-ports.js +196 -21
- package/src/lib/evolution/evolution-deployment-loader.js +240 -2
- package/src/lib/evolution/evolution-eval-child-evidence-ledger-adapter.js +373 -0
- package/src/lib/evolution/evolution-eval-gate.js +14 -1
- package/src/lib/evolution/evolution-eval-process-supervisor-worker.mjs +14 -0
- package/src/lib/evolution/evolution-eval-process-supervisor.js +607 -0
- package/src/lib/evolution/evolution-evidence-artifact-adapter.js +14 -1
- package/src/lib/evolution/evolution-evidence-projector.js +271 -37
- package/src/lib/evolution/evolution-file-witness.js +153 -30
- package/src/lib/evolution/evolution-ledger-ports.js +1219 -66
- package/src/lib/evolution/evolution-ledger.js +66 -2
- package/src/lib/evolution/evolution-raw-crypto-shred.js +29 -15
- package/src/lib/evolution/evolution-raw-deletion-ledger-adapter.js +865 -0
- package/src/lib/evolution/evolution-release-train-domain-stages.js +769 -0
- package/src/lib/evolution/evolution-release-train-stage-output-ledger-adapter.js +333 -0
- package/src/lib/evolution/evolution-release-train.js +54 -6
- package/src/lib/evolution/evolution-workbench-batch-executor.js +101 -144
- package/src/lib/evolution/evolution-workbench-control-ports.js +203 -0
- package/src/lib/evolution/evolution-workbench-file-resources.js +403 -0
- package/src/lib/evolution/evolution-workbench-projection.js +134 -26
- package/src/lib/evolution/evolution-workbench-registry-source.js +482 -0
- package/src/lib/evolution/evolution-workbench-review-ledger-adapter.js +823 -0
- package/src/lib/evolution/evolution-workbench-review-protocol.js +157 -0
- package/src/lib/evolution/evolution-workbench-rollback-authorization.js +57 -0
- package/src/lib/evolution/evolution-workbench-rollback-ledger-adapter.js +1016 -0
- package/src/lib/evolution/evolution-workbench-runtime.js +133 -0
- package/src/lib/evolution/evolution-workbench-version-control.js +70 -36
- package/src/lib/evolution/evolvable-artifact-ledger-adapter.js +48 -0
- package/src/lib/evolution/governed-knowledge-candidate-rejection.js +343 -0
- package/src/lib/evolution/governed-knowledge-dependency-authority.js +227 -44
- package/src/lib/evolution/governed-knowledge-dependency-inventory.js +392 -0
- package/src/lib/evolution/governed-knowledge-dependency-ledger-executor.js +217 -82
- package/src/lib/evolution/governed-knowledge-quarantine-release.js +1013 -0
- package/src/lib/evolution/governed-knowledge-record.js +148 -0
- package/src/lib/evolution/governed-knowledge-revocation-host.js +59 -0
- package/src/lib/evolution/governed-knowledge-revocation-record.js +135 -0
- package/src/lib/evolution/governed-knowledge-skill-rollback.js +510 -0
- package/src/lib/evolution/governed-knowledge-sync.js +181 -95
- package/src/lib/evolution/governed-knowledge-wiki-tombstone.js +516 -0
- package/src/lib/evolution/governed-skill-marketplace-badge.js +208 -0
- package/src/lib/evolution/governed-skill-marketplace-candidate.js +330 -0
- package/src/lib/evolution/governed-skill-marketplace-cli-host.js +308 -0
- package/src/lib/evolution/governed-skill-marketplace-ledger-adapter.js +420 -0
- package/src/lib/evolution/governed-skill-marketplace.js +288 -5
- package/src/lib/evolution/governed-wiki-pruning-journal.js +302 -0
- package/src/lib/evolution/governed-wiki-pruning-ledger-adapter.js +459 -0
- package/src/lib/evolution/governed-wiki-pruning-maintenance.js +576 -0
- package/src/lib/evolution/governed-wiki-pruning-plan-authority.js +169 -0
- package/src/lib/evolution/governed-wiki-pruning-raw-shred.js +207 -0
- package/src/lib/evolution/governed-wiki-pruning-retrieval.js +587 -0
- package/src/lib/evolution/governed-wiki-pruning-runtime.js +153 -0
- package/src/lib/evolution/governed-wiki-pruning-skill-rollback.js +360 -0
- package/src/lib/evolution/governed-wiki-pruning.js +322 -143
- package/src/lib/evolution/independent-skill-revocation-source.js +231 -0
- package/src/lib/evolution/knowledge-wiki-source-admission.js +283 -0
- package/src/lib/evolution/knowledge-wiki-source-provenance.js +360 -0
- package/src/lib/evolution/progressive-canary-external-watchdog.js +578 -0
- package/src/lib/evolution/progressive-canary-traffic-worker.js +628 -0
- package/src/lib/evolution/progressive-canary-watchdog-file-store.js +285 -0
- package/src/lib/evolution/progressive-canary-watchdog-node-pki.js +265 -0
- package/src/lib/evolution/skill-candidate-registry.js +43 -3
- package/src/lib/evolution/skill-promotion-controller.js +25 -0
- package/src/lib/evolution/skill-promotion-review-ledger-adapter.js +100 -0
- package/src/lib/evolution/skill-registry-transition-ledger-adapter.js +131 -2
- package/src/lib/evolution/skill-release-registry.js +168 -4
- package/src/lib/evolution/skill-retrieval-revocation-authority.js +361 -0
- package/src/lib/evolution/skill-retrieval-revocation-ledger-adapter.js +346 -0
- package/src/lib/evolution/skill-revocation-propagation.js +467 -0
- package/src/lib/evolution/skill-target-matrix-eval.js +162 -11
- package/src/lib/evolution/skill-wiki-reconciliation.js +985 -0
- package/src/lib/evolution/skill-writer-inventory-manifest.js +8 -6
- package/src/lib/evolution/statistical-progressive-canary.js +165 -3
- package/src/lib/evolution/structured-memory-agent-control-plane.js +120 -0
- package/src/lib/evolution/structured-memory-authority-ledger-adapter.js +5 -1
- package/src/lib/evolution/structured-memory-promotion-receipt-writer.js +126 -0
- package/src/lib/evolution/wiki-informed-skill-proposer.js +127 -21
- package/src/lib/evolution/wiki-maintainer-ledger-adapter.js +822 -101
- package/src/lib/evolution/wiki-revision-protocol.js +120 -0
- package/src/lib/evolution/wiki-skill-proposal-ledger-adapter.js +307 -0
- package/src/lib/evolution/wiki-target-tombstone-revision.js +109 -0
- package/src/lib/evolution/wikiskill-benchmark-cli-host.js +178 -0
- package/src/lib/evolution/wikiskill-benchmark-execution-host.js +702 -0
- package/src/lib/evolution/wikiskill-benchmark-ledger-adapter.js +613 -0
- package/src/lib/evolution/wikiskill-benchmark.js +80 -16
- package/src/lib/goal-assess.js +17 -10
- package/src/lib/interactive-planner.js +9 -1
- package/src/lib/iteration-budget.js +16 -2
- package/src/lib/micro-compact.js +4 -1
- package/src/lib/model-failure-policy.js +21 -0
- package/src/lib/process-execution-broker/index.js +1 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +33 -15
- package/src/lib/read-file-loop-guard.js +113 -0
- package/src/lib/read-file-page.js +191 -0
- package/src/lib/runnable-provider.js +2 -1
- package/src/lib/skill-retrieval-router.js +22 -1
- package/src/repl/agent-repl.js +133 -34
- package/src/runtime/agent-core.js +196 -74
- package/src/runtime/agent-runtime.js +43 -0
- package/src/runtime/coding-agent-contract-shared.cjs +7 -1
- package/src/runtime/fallback-model.js +143 -62
- package/src/runtime/headless-runner.js +54 -10
- package/src/runtime/headless-stream.js +50 -6
- package/src/runtime/runtime-factory.js +41 -2
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Ds3pA-ET.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-DqTx7_u1.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CF3xafX2.js +0 -1
- package/src/assets/web-panel/assets/index-C907rPwd.js +0 -1
- package/src/assets/web-panel/assets/index-wueRSK2d.js +0 -1
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
EVOLUTION_ARTIFACT_STORE_FAILED_CODE,
|
|
13
13
|
EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA,
|
|
14
14
|
EvolutionArtifactPorts,
|
|
15
|
+
captureEvolutionLedgerBatchResolver,
|
|
15
16
|
} from "./evolution-artifact-ports.js";
|
|
16
17
|
import {
|
|
17
18
|
EVOLUTION_ARTIFACT_REF_SCHEMA,
|
|
@@ -35,18 +36,47 @@ import {
|
|
|
35
36
|
SKILL_RELEASE_LEDGER_PROJECTION_SCHEMA,
|
|
36
37
|
SKILL_RELEASE_STATE_LEDGER_MIGRATION_SCHEMA,
|
|
37
38
|
SKILL_RELEASE_STATE_MIGRATION_RECEIPT_SCHEMA,
|
|
39
|
+
verifySkillRelease,
|
|
38
40
|
verifySkillReleaseState,
|
|
39
41
|
verifySkillReleaseStateMigrationPlan,
|
|
40
42
|
} from "./skill-release-registry.js";
|
|
43
|
+
import {
|
|
44
|
+
GOVERNED_KNOWLEDGE_DEPENDENCY_PREPARED_EVENT_TYPE,
|
|
45
|
+
verifyGovernedKnowledgeDependencyPrepared,
|
|
46
|
+
} from "./governed-knowledge-dependency-ledger-executor.js";
|
|
47
|
+
import {
|
|
48
|
+
GOVERNED_KNOWLEDGE_QUARANTINE_RELEASE_EVENT_TYPE,
|
|
49
|
+
verifyGovernedKnowledgeQuarantineReleaseRecord,
|
|
50
|
+
} from "./governed-knowledge-quarantine-release.js";
|
|
51
|
+
import {
|
|
52
|
+
WIKI_LEDGER_EVENT_TYPE,
|
|
53
|
+
WikiMaintainerLedgerAdapter,
|
|
54
|
+
captureWikiRevisionReader,
|
|
55
|
+
} from "./wiki-maintainer-ledger-adapter.js";
|
|
41
56
|
|
|
42
57
|
export const EVOLUTION_LEDGER_PORTS_INVALID_CODE =
|
|
43
58
|
"CC_EVOLUTION_LEDGER_PORTS_INVALID";
|
|
59
|
+
const RELEASE_OPERATION_READERS = new WeakMap();
|
|
60
|
+
|
|
61
|
+
// This read-only recovery surface is minted only alongside real Ledger ports.
|
|
62
|
+
// Matching JSON, inherited methods or a caller-supplied query function cannot
|
|
63
|
+
// establish that a release operation actually committed.
|
|
64
|
+
export function captureSkillReleaseOperationReader(transactionLedger) {
|
|
65
|
+
const reader = RELEASE_OPERATION_READERS.get(transactionLedger);
|
|
66
|
+
if (!reader)
|
|
67
|
+
throw new TypeError("a branded release transaction ledger is required");
|
|
68
|
+
return reader;
|
|
69
|
+
}
|
|
44
70
|
export const EVOLUTION_LEDGER_PORTS_COLLISION_CODE =
|
|
45
71
|
"CC_EVOLUTION_LEDGER_PORTS_COLLISION";
|
|
46
72
|
export const EVOLUTION_LEDGER_PORTS_CORRUPT_CODE =
|
|
47
73
|
"CC_EVOLUTION_LEDGER_PORTS_CORRUPT";
|
|
48
74
|
export const EVOLUTION_LEDGER_PORTS_UNAVAILABLE_CODE =
|
|
49
75
|
"CC_EVOLUTION_LEDGER_PORTS_UNAVAILABLE";
|
|
76
|
+
export const EVOLUTION_LEDGER_CANDIDATE_REVOKED_CODE =
|
|
77
|
+
"CC_EVOLUTION_LEDGER_CANDIDATE_REVOKED";
|
|
78
|
+
export const EVOLUTION_LEDGER_SOURCE_REVOKED_CODE =
|
|
79
|
+
"CC_EVOLUTION_LEDGER_SOURCE_REVOKED";
|
|
50
80
|
export const EVOLUTION_ARTIFACT_DURABILITY_BINDING_SCHEMA =
|
|
51
81
|
"chainlesschain.evolution-artifact-durability-binding/v1";
|
|
52
82
|
export const EVOLUTION_ARTIFACT_DURABILITY_RETAIN_REQUEST_SCHEMA =
|
|
@@ -1127,6 +1157,7 @@ class EvolutionLedgerDomainPorts {
|
|
|
1127
1157
|
#appendDomainEvent;
|
|
1128
1158
|
#artifactPut;
|
|
1129
1159
|
#artifactResolve;
|
|
1160
|
+
#artifactResolveBatch;
|
|
1130
1161
|
#artifactTenantId;
|
|
1131
1162
|
#audience;
|
|
1132
1163
|
#durabilityAuthority;
|
|
@@ -1134,6 +1165,7 @@ class EvolutionLedgerDomainPorts {
|
|
|
1134
1165
|
#durabilityRetain;
|
|
1135
1166
|
#ledgerQuery;
|
|
1136
1167
|
#ledgerRead;
|
|
1168
|
+
#ledgerVerify;
|
|
1137
1169
|
#ledgerVerifyReceipt;
|
|
1138
1170
|
#purpose;
|
|
1139
1171
|
|
|
@@ -1161,6 +1193,9 @@ class EvolutionLedgerDomainPorts {
|
|
|
1161
1193
|
this.#ledgerRead = Object.freeze(
|
|
1162
1194
|
EvolutionLedger.prototype.read.bind(ledger),
|
|
1163
1195
|
);
|
|
1196
|
+
this.#ledgerVerify = Object.freeze(
|
|
1197
|
+
EvolutionLedger.prototype.verify.bind(ledger),
|
|
1198
|
+
);
|
|
1164
1199
|
this.#ledgerVerifyReceipt = Object.freeze(
|
|
1165
1200
|
EvolutionLedger.prototype.verifyReceipt.bind(ledger),
|
|
1166
1201
|
);
|
|
@@ -1172,6 +1207,9 @@ class EvolutionLedgerDomainPorts {
|
|
|
1172
1207
|
artifactPorts,
|
|
1173
1208
|
Object.freeze({ purpose: this.#purpose }),
|
|
1174
1209
|
);
|
|
1210
|
+
this.#artifactResolveBatch = captureEvolutionLedgerBatchResolver(
|
|
1211
|
+
this.#artifactResolve,
|
|
1212
|
+
);
|
|
1175
1213
|
Object.freeze(this);
|
|
1176
1214
|
}
|
|
1177
1215
|
|
|
@@ -1452,26 +1490,32 @@ class EvolutionLedgerDomainPorts {
|
|
|
1452
1490
|
return ref;
|
|
1453
1491
|
}
|
|
1454
1492
|
|
|
1455
|
-
#resolveSubject(
|
|
1493
|
+
#resolveSubject(
|
|
1494
|
+
event,
|
|
1495
|
+
expectedType,
|
|
1496
|
+
expectedAudience = null,
|
|
1497
|
+
capturedResolution = null,
|
|
1498
|
+
) {
|
|
1456
1499
|
const ref = normalizeArtifactRef(
|
|
1457
1500
|
event.subjectRef,
|
|
1458
1501
|
"domain event subjectRef",
|
|
1459
1502
|
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
1460
1503
|
);
|
|
1461
1504
|
const durable = this.#resolveDurably(ref, expectedType);
|
|
1462
|
-
let resolution =
|
|
1505
|
+
let resolution = capturedResolution;
|
|
1463
1506
|
try {
|
|
1464
|
-
resolution
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1507
|
+
if (resolution === null)
|
|
1508
|
+
resolution = assertSynchronous(
|
|
1509
|
+
this.#artifactResolve(
|
|
1510
|
+
deepFreeze({
|
|
1511
|
+
epoch: event.epoch,
|
|
1512
|
+
ledgerId: event.ledgerId,
|
|
1513
|
+
ref,
|
|
1514
|
+
tenantId: this.#artifactTenantId,
|
|
1515
|
+
}),
|
|
1516
|
+
),
|
|
1517
|
+
"artifact resolver result",
|
|
1518
|
+
);
|
|
1475
1519
|
} catch {
|
|
1476
1520
|
// A removed/corrupt local cache is recoverable only through the trusted
|
|
1477
1521
|
// durability authority's exact canonical-byte replica above.
|
|
@@ -1698,7 +1742,40 @@ class EvolutionLedgerDomainPorts {
|
|
|
1698
1742
|
}
|
|
1699
1743
|
|
|
1700
1744
|
#snapshot() {
|
|
1701
|
-
|
|
1745
|
+
const snapshot = this.#indexSnapshot(this.#events());
|
|
1746
|
+
const types = new Set([
|
|
1747
|
+
EVENT_TYPES.prepare,
|
|
1748
|
+
EVENT_TYPES.finalize,
|
|
1749
|
+
EVENT_TYPES.migration,
|
|
1750
|
+
EVENT_TYPES.audit,
|
|
1751
|
+
]);
|
|
1752
|
+
const subjects = new Map();
|
|
1753
|
+
for (const event of snapshot.events) {
|
|
1754
|
+
if (types.has(event.type)) subjects.set(event.subjectRef.ref, event);
|
|
1755
|
+
// Bound retained byte buffers independently of total ledger size. Larger
|
|
1756
|
+
// histories keep the existing individually verified streaming path.
|
|
1757
|
+
if (subjects.size > 16) return snapshot;
|
|
1758
|
+
}
|
|
1759
|
+
if (subjects.size) {
|
|
1760
|
+
const events = [...subjects.values()];
|
|
1761
|
+
try {
|
|
1762
|
+
const results = this.#artifactResolveBatch(
|
|
1763
|
+
events.map((event) => ({
|
|
1764
|
+
epoch: event.epoch,
|
|
1765
|
+
ledgerId: event.ledgerId,
|
|
1766
|
+
ref: event.subjectRef,
|
|
1767
|
+
tenantId: this.#artifactTenantId,
|
|
1768
|
+
})),
|
|
1769
|
+
);
|
|
1770
|
+
snapshot.localResolutions = new Map(
|
|
1771
|
+
events.map((event, index) => [event.subjectRef.ref, results[index]]),
|
|
1772
|
+
);
|
|
1773
|
+
} catch {
|
|
1774
|
+
// As on individual reads, a missing local cache may recover only from
|
|
1775
|
+
// the independently authenticated exact-byte durability authority.
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
return snapshot;
|
|
1702
1779
|
}
|
|
1703
1780
|
|
|
1704
1781
|
#snapshotWithVerifiedEvent(snapshot, verification) {
|
|
@@ -1743,7 +1820,12 @@ class EvolutionLedgerDomainPorts {
|
|
|
1743
1820
|
const cacheKey = `${expectedType}\0${expectedAudience ?? ""}\0${ref.ref}\0${ref.digest}`;
|
|
1744
1821
|
let record = snapshot.subjects.get(cacheKey);
|
|
1745
1822
|
if (!record) {
|
|
1746
|
-
record = this.#resolveSubject(
|
|
1823
|
+
record = this.#resolveSubject(
|
|
1824
|
+
event,
|
|
1825
|
+
expectedType,
|
|
1826
|
+
expectedAudience,
|
|
1827
|
+
snapshot.localResolutions?.get(ref.ref) ?? null,
|
|
1828
|
+
);
|
|
1747
1829
|
snapshot.subjects.set(cacheKey, record);
|
|
1748
1830
|
}
|
|
1749
1831
|
return record;
|
|
@@ -2274,7 +2356,7 @@ class EvolutionLedgerDomainPorts {
|
|
|
2274
2356
|
return { existing: null, lineages, prepared };
|
|
2275
2357
|
}
|
|
2276
2358
|
|
|
2277
|
-
migrate(input) {
|
|
2359
|
+
migrate(input, releases) {
|
|
2278
2360
|
const migration = normalizeStateMigration(input);
|
|
2279
2361
|
const transactionId = migration.plan.stateMigrationDigest;
|
|
2280
2362
|
const eventId = deterministicEventId("migration", { transactionId });
|
|
@@ -2310,6 +2392,29 @@ class EvolutionLedgerDomainPorts {
|
|
|
2310
2392
|
"release state migration requires an empty Skill ledger lineage",
|
|
2311
2393
|
);
|
|
2312
2394
|
}
|
|
2395
|
+
if (releases !== undefined) {
|
|
2396
|
+
assertAllExactRecord(
|
|
2397
|
+
releases,
|
|
2398
|
+
new Set(["active", "lastKnownGood"]),
|
|
2399
|
+
"migration release evidence",
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2402
|
+
for (const [key, releaseDigest] of [
|
|
2403
|
+
["active", migration.plan.activeReleaseDigest],
|
|
2404
|
+
["lastKnownGood", migration.plan.lastKnownGoodReleaseDigest],
|
|
2405
|
+
]) {
|
|
2406
|
+
this.#assertReleaseNotRevoked(
|
|
2407
|
+
snapshot,
|
|
2408
|
+
{
|
|
2409
|
+
tenantId: migration.plan.tenantId,
|
|
2410
|
+
skillName: migration.plan.skillName,
|
|
2411
|
+
audience: this.#audience,
|
|
2412
|
+
releaseDigest,
|
|
2413
|
+
candidateId: null,
|
|
2414
|
+
},
|
|
2415
|
+
releases?.[key],
|
|
2416
|
+
);
|
|
2417
|
+
}
|
|
2313
2418
|
const subjectRef = this.#putSubject(
|
|
2314
2419
|
ARTIFACT_TYPES.migration,
|
|
2315
2420
|
migration,
|
|
@@ -2367,65 +2472,553 @@ class EvolutionLedgerDomainPorts {
|
|
|
2367
2472
|
);
|
|
2368
2473
|
}
|
|
2369
2474
|
|
|
2370
|
-
|
|
2475
|
+
*#knowledgeRevocations(snapshot, tenantId, audience) {
|
|
2476
|
+
// A prepared revocation is already a durable admission fence. Waiting for
|
|
2477
|
+
// every dependency effect to settle would allow promotion while offline
|
|
2478
|
+
// recovery, active rollback or another dependency is still outstanding.
|
|
2479
|
+
// Scan all streams/devices in this tenant; a new controller cannot opt out.
|
|
2480
|
+
for (const event of snapshot.events) {
|
|
2481
|
+
if (
|
|
2482
|
+
event.type !== GOVERNED_KNOWLEDGE_DEPENDENCY_PREPARED_EVENT_TYPE ||
|
|
2483
|
+
event.tenantId !== tenantId
|
|
2484
|
+
)
|
|
2485
|
+
continue;
|
|
2486
|
+
if (
|
|
2487
|
+
event.schema !== EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA ||
|
|
2488
|
+
event.artifactTenantId !== this.#artifactTenantId ||
|
|
2489
|
+
event.decision !== "prepared" ||
|
|
2490
|
+
!Array.isArray(event.sourceRefs) ||
|
|
2491
|
+
event.sourceRefs.length !== 0
|
|
2492
|
+
) {
|
|
2493
|
+
throw portsError(
|
|
2494
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2495
|
+
"candidate revocation event binding is invalid",
|
|
2496
|
+
);
|
|
2497
|
+
}
|
|
2498
|
+
const ref = normalizeArtifactRef(
|
|
2499
|
+
event.subjectRef,
|
|
2500
|
+
"candidate revocation subjectRef",
|
|
2501
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2502
|
+
);
|
|
2503
|
+
// Knowledge records use their own durable ArtifactStore, not the release
|
|
2504
|
+
// replica. Missing or corrupt retained bytes must fail closed, never be
|
|
2505
|
+
// interpreted as the absence of a revocation.
|
|
2506
|
+
const resolution = assertSynchronous(
|
|
2507
|
+
this.#artifactResolve(
|
|
2508
|
+
deepFreeze({
|
|
2509
|
+
epoch: event.epoch,
|
|
2510
|
+
ledgerId: event.ledgerId,
|
|
2511
|
+
ref,
|
|
2512
|
+
tenantId: this.#artifactTenantId,
|
|
2513
|
+
}),
|
|
2514
|
+
),
|
|
2515
|
+
"candidate revocation artifact resolution",
|
|
2516
|
+
);
|
|
2517
|
+
assertAllExactRecord(
|
|
2518
|
+
resolution,
|
|
2519
|
+
LEDGER_RESOLUTION_KEYS,
|
|
2520
|
+
"candidate revocation artifact resolution",
|
|
2521
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2522
|
+
);
|
|
2523
|
+
if (
|
|
2524
|
+
resolution.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
|
|
2525
|
+
resolution.authenticated !== true ||
|
|
2526
|
+
resolution.found !== true ||
|
|
2527
|
+
resolution.ref !== ref.ref ||
|
|
2528
|
+
resolution.digest !== ref.digest
|
|
2529
|
+
) {
|
|
2530
|
+
throw portsError(
|
|
2531
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2532
|
+
"candidate revocation artifact is not authenticated and exactly bound",
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
2535
|
+
digest(
|
|
2536
|
+
resolution.receiptDigest,
|
|
2537
|
+
"candidate revocation artifact receiptDigest",
|
|
2538
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2539
|
+
);
|
|
2540
|
+
const bytes = copyBytes(resolution.bytes);
|
|
2541
|
+
if (sha256(bytes) !== ref.digest) {
|
|
2542
|
+
throw portsError(
|
|
2543
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2544
|
+
"candidate revocation artifact digest mismatch",
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
const json = bufferToString(bytes, "utf8");
|
|
2548
|
+
let record;
|
|
2549
|
+
try {
|
|
2550
|
+
record = JSON.parse(json);
|
|
2551
|
+
} catch (cause) {
|
|
2552
|
+
throw portsError(
|
|
2553
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2554
|
+
"candidate revocation artifact is not JSON",
|
|
2555
|
+
{ cause },
|
|
2556
|
+
);
|
|
2557
|
+
}
|
|
2558
|
+
assertAllExactRecord(
|
|
2559
|
+
record,
|
|
2560
|
+
ARTIFACT_RECORD_KEYS,
|
|
2561
|
+
"candidate revocation artifact",
|
|
2562
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2563
|
+
);
|
|
2564
|
+
if (
|
|
2565
|
+
canonicalJson(record) !== json ||
|
|
2566
|
+
record.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
|
|
2567
|
+
record.tenantId !== this.#artifactTenantId ||
|
|
2568
|
+
record.type !== "governed-knowledge-dependency-operation" ||
|
|
2569
|
+
record.retention !== "ledger" ||
|
|
2570
|
+
record.purpose !== this.#purpose ||
|
|
2571
|
+
record.audience !== audience
|
|
2572
|
+
) {
|
|
2573
|
+
throw portsError(
|
|
2574
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2575
|
+
"candidate revocation artifact boundary is invalid",
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
const prepared = verifyGovernedKnowledgeDependencyPrepared(record.value, {
|
|
2579
|
+
tenantId: event.tenantId,
|
|
2580
|
+
deviceId: record.value?.deviceId,
|
|
2581
|
+
});
|
|
2582
|
+
if (
|
|
2583
|
+
event.eventId !==
|
|
2584
|
+
`${GOVERNED_KNOWLEDGE_DEPENDENCY_PREPARED_EVENT_TYPE}.${prepared.operationDigest.slice(7)}`
|
|
2585
|
+
) {
|
|
2586
|
+
throw portsError(
|
|
2587
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2588
|
+
"candidate revocation event does not bind its prepared operation",
|
|
2589
|
+
);
|
|
2590
|
+
}
|
|
2591
|
+
yield { event, prepared };
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
*#knowledgeQuarantineReleases(snapshot, tenantId, audience) {
|
|
2596
|
+
const operations = new Set();
|
|
2597
|
+
for (const event of snapshot.events) {
|
|
2598
|
+
if (
|
|
2599
|
+
event.type !== GOVERNED_KNOWLEDGE_QUARANTINE_RELEASE_EVENT_TYPE ||
|
|
2600
|
+
event.tenantId !== tenantId
|
|
2601
|
+
) {
|
|
2602
|
+
continue;
|
|
2603
|
+
}
|
|
2604
|
+
if (
|
|
2605
|
+
event.schema !== EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA ||
|
|
2606
|
+
event.artifactTenantId !== this.#artifactTenantId ||
|
|
2607
|
+
event.decision !== "committed" ||
|
|
2608
|
+
!Array.isArray(event.sourceRefs) ||
|
|
2609
|
+
event.sourceRefs.length !== 0
|
|
2610
|
+
) {
|
|
2611
|
+
throw portsError(
|
|
2612
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2613
|
+
"quarantine release event binding is invalid",
|
|
2614
|
+
);
|
|
2615
|
+
}
|
|
2616
|
+
const ref = normalizeArtifactRef(
|
|
2617
|
+
event.subjectRef,
|
|
2618
|
+
"quarantine release subjectRef",
|
|
2619
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2620
|
+
);
|
|
2621
|
+
const resolution = assertSynchronous(
|
|
2622
|
+
this.#artifactResolve(
|
|
2623
|
+
deepFreeze({
|
|
2624
|
+
epoch: event.epoch,
|
|
2625
|
+
ledgerId: event.ledgerId,
|
|
2626
|
+
ref,
|
|
2627
|
+
tenantId: this.#artifactTenantId,
|
|
2628
|
+
}),
|
|
2629
|
+
),
|
|
2630
|
+
"quarantine release artifact resolution",
|
|
2631
|
+
);
|
|
2632
|
+
assertAllExactRecord(
|
|
2633
|
+
resolution,
|
|
2634
|
+
LEDGER_RESOLUTION_KEYS,
|
|
2635
|
+
"quarantine release artifact resolution",
|
|
2636
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2637
|
+
);
|
|
2638
|
+
if (
|
|
2639
|
+
resolution.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
|
|
2640
|
+
resolution.authenticated !== true ||
|
|
2641
|
+
resolution.found !== true ||
|
|
2642
|
+
resolution.ref !== ref.ref ||
|
|
2643
|
+
resolution.digest !== ref.digest
|
|
2644
|
+
) {
|
|
2645
|
+
throw portsError(
|
|
2646
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2647
|
+
"quarantine release artifact is not authenticated and exactly bound",
|
|
2648
|
+
);
|
|
2649
|
+
}
|
|
2650
|
+
const bytes = copyBytes(resolution.bytes);
|
|
2651
|
+
if (sha256(bytes) !== ref.digest) {
|
|
2652
|
+
throw portsError(
|
|
2653
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2654
|
+
"quarantine release artifact digest mismatch",
|
|
2655
|
+
);
|
|
2656
|
+
}
|
|
2657
|
+
let artifact;
|
|
2658
|
+
try {
|
|
2659
|
+
artifact = JSON.parse(bufferToString(bytes, "utf8"));
|
|
2660
|
+
} catch (cause) {
|
|
2661
|
+
throw portsError(
|
|
2662
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2663
|
+
"quarantine release artifact is not JSON",
|
|
2664
|
+
{ cause },
|
|
2665
|
+
);
|
|
2666
|
+
}
|
|
2667
|
+
assertAllExactRecord(
|
|
2668
|
+
artifact,
|
|
2669
|
+
ARTIFACT_RECORD_KEYS,
|
|
2670
|
+
"quarantine release artifact",
|
|
2671
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2672
|
+
);
|
|
2673
|
+
if (
|
|
2674
|
+
canonicalJson(artifact) !== bufferToString(bytes, "utf8") ||
|
|
2675
|
+
artifact.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
|
|
2676
|
+
artifact.tenantId !== this.#artifactTenantId ||
|
|
2677
|
+
artifact.type !== "governed-knowledge-quarantine-release" ||
|
|
2678
|
+
artifact.retention !== "ledger" ||
|
|
2679
|
+
artifact.purpose !== this.#purpose ||
|
|
2680
|
+
artifact.audience !== audience
|
|
2681
|
+
) {
|
|
2682
|
+
throw portsError(
|
|
2683
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2684
|
+
"quarantine release artifact boundary is invalid",
|
|
2685
|
+
);
|
|
2686
|
+
}
|
|
2687
|
+
let release;
|
|
2688
|
+
try {
|
|
2689
|
+
release = verifyGovernedKnowledgeQuarantineReleaseRecord(
|
|
2690
|
+
artifact.value,
|
|
2691
|
+
{ tenantId },
|
|
2692
|
+
);
|
|
2693
|
+
} catch (cause) {
|
|
2694
|
+
throw portsError(
|
|
2695
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2696
|
+
"quarantine release record is invalid",
|
|
2697
|
+
{ cause },
|
|
2698
|
+
);
|
|
2699
|
+
}
|
|
2700
|
+
if (
|
|
2701
|
+
event.eventId !==
|
|
2702
|
+
`${GOVERNED_KNOWLEDGE_QUARANTINE_RELEASE_EVENT_TYPE}.${release.operationDigest.slice(7)}` ||
|
|
2703
|
+
event.timestamp !== release.committedAt ||
|
|
2704
|
+
operations.has(release.operationDigest)
|
|
2705
|
+
) {
|
|
2706
|
+
throw portsError(
|
|
2707
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2708
|
+
"quarantine release event identity is invalid or ambiguous",
|
|
2709
|
+
);
|
|
2710
|
+
}
|
|
2711
|
+
operations.add(release.operationDigest);
|
|
2712
|
+
yield release;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
#assertReleaseNotRevoked(snapshot, binding, targetInput) {
|
|
2717
|
+
const releases = new Map(
|
|
2718
|
+
[
|
|
2719
|
+
...this.#knowledgeQuarantineReleases(
|
|
2720
|
+
snapshot,
|
|
2721
|
+
binding.tenantId,
|
|
2722
|
+
binding.audience,
|
|
2723
|
+
),
|
|
2724
|
+
].map((release) => [release.operationDigest, release]),
|
|
2725
|
+
);
|
|
2726
|
+
const seenOperations = new Set();
|
|
2727
|
+
const releaseFor = (prepared) => {
|
|
2728
|
+
const release = releases.get(prepared.operationDigest) ?? null;
|
|
2729
|
+
if (!release) return null;
|
|
2730
|
+
seenOperations.add(prepared.operationDigest);
|
|
2731
|
+
const quarantines = prepared.knowledge.dependencies.filter(
|
|
2732
|
+
(dependency) => dependency.disposition === "quarantine",
|
|
2733
|
+
);
|
|
2734
|
+
if (
|
|
2735
|
+
release.knowledgeId !== prepared.knowledge.knowledgeId ||
|
|
2736
|
+
release.contentDigest !== prepared.knowledge.contentDigest ||
|
|
2737
|
+
release.preparedRecordDigest !== prepared.recordDigest ||
|
|
2738
|
+
canonicalJson(release.dependencies) !== canonicalJson(quarantines)
|
|
2739
|
+
) {
|
|
2740
|
+
throw portsError(
|
|
2741
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2742
|
+
"quarantine release does not bind its prepared revocation",
|
|
2743
|
+
);
|
|
2744
|
+
}
|
|
2745
|
+
return release;
|
|
2746
|
+
};
|
|
2747
|
+
const assertCandidate = (prepared, candidateId) => {
|
|
2748
|
+
const blocked = prepared.knowledge.dependencies.find(
|
|
2749
|
+
(dependency) =>
|
|
2750
|
+
dependency.kind === "candidate" &&
|
|
2751
|
+
dependency.digest === candidateId &&
|
|
2752
|
+
["reject-candidate", "quarantine"].includes(dependency.disposition),
|
|
2753
|
+
);
|
|
2754
|
+
if (blocked) {
|
|
2755
|
+
throw portsError(
|
|
2756
|
+
EVOLUTION_LEDGER_CANDIDATE_REVOKED_CODE,
|
|
2757
|
+
"candidate activation is blocked by a durable knowledge revocation",
|
|
2758
|
+
{
|
|
2759
|
+
candidateId,
|
|
2760
|
+
operationDigest: prepared.operationDigest,
|
|
2761
|
+
},
|
|
2762
|
+
);
|
|
2763
|
+
}
|
|
2764
|
+
};
|
|
2765
|
+
let target = null;
|
|
2766
|
+
const readTarget = () => {
|
|
2767
|
+
if (target) return target;
|
|
2768
|
+
let verified;
|
|
2769
|
+
try {
|
|
2770
|
+
verified = verifySkillRelease(targetInput);
|
|
2771
|
+
} catch (cause) {
|
|
2772
|
+
throw portsError(
|
|
2773
|
+
EVOLUTION_LEDGER_PORTS_INVALID_CODE,
|
|
2774
|
+
"release admission requires verified immutable target evidence",
|
|
2775
|
+
{ cause },
|
|
2776
|
+
);
|
|
2777
|
+
}
|
|
2778
|
+
if (
|
|
2779
|
+
verified.releaseDigest !== binding.releaseDigest ||
|
|
2780
|
+
verified.tenantId !== binding.tenantId ||
|
|
2781
|
+
verified.skillName !== binding.skillName ||
|
|
2782
|
+
(binding.candidateId !== null &&
|
|
2783
|
+
verified.candidateId !== binding.candidateId)
|
|
2784
|
+
) {
|
|
2785
|
+
throw portsError(
|
|
2786
|
+
EVOLUTION_LEDGER_PORTS_INVALID_CODE,
|
|
2787
|
+
"release admission evidence differs from the intended target",
|
|
2788
|
+
);
|
|
2789
|
+
}
|
|
2790
|
+
target = verified;
|
|
2791
|
+
return target;
|
|
2792
|
+
};
|
|
2793
|
+
// Stream authenticated revocations: do not retain every full Knowledge
|
|
2794
|
+
// artifact in memory or reuse authorization evidence across attempts.
|
|
2795
|
+
for (const { prepared } of this.#knowledgeRevocations(
|
|
2796
|
+
snapshot,
|
|
2797
|
+
binding.tenantId,
|
|
2798
|
+
binding.audience,
|
|
2799
|
+
)) {
|
|
2800
|
+
const release = releaseFor(prepared);
|
|
2801
|
+
assertCandidate(prepared, binding.candidateId);
|
|
2802
|
+
readTarget();
|
|
2803
|
+
assertCandidate(prepared, target.candidateId);
|
|
2804
|
+
const knowledge = prepared.knowledge;
|
|
2805
|
+
const wiki = this.#releaseWikiProvenance(
|
|
2806
|
+
snapshot,
|
|
2807
|
+
binding,
|
|
2808
|
+
target,
|
|
2809
|
+
knowledge,
|
|
2810
|
+
release,
|
|
2811
|
+
);
|
|
2812
|
+
const sourceRef = `knowledge://${encodeURIComponent(binding.tenantId)}/${encodeURIComponent(knowledge.knowledgeId)}`;
|
|
2813
|
+
const targetDependencies = knowledge.dependencies.filter(
|
|
2814
|
+
(dependency) =>
|
|
2815
|
+
(dependency.kind === "active-skill" &&
|
|
2816
|
+
dependency.digest === target.releaseDigest) ||
|
|
2817
|
+
(dependency.kind === "candidate" &&
|
|
2818
|
+
dependency.digest === target.candidateId),
|
|
2819
|
+
);
|
|
2820
|
+
// Immutable candidate/release identities named by the quarantine remain
|
|
2821
|
+
// fenced forever. A release restores only the reviewed Wiki source so a
|
|
2822
|
+
// newly evaluated successor can traverse the ordinary promotion gates.
|
|
2823
|
+
const revokedRelease = targetDependencies.length > 0;
|
|
2824
|
+
const supersedingTarget = release !== null && !revokedRelease;
|
|
2825
|
+
if (
|
|
2826
|
+
revokedRelease ||
|
|
2827
|
+
wiki?.unsafePatternIds.length ||
|
|
2828
|
+
(!supersedingTarget &&
|
|
2829
|
+
target.candidate.sourceEvidenceRefs.some(
|
|
2830
|
+
(item) =>
|
|
2831
|
+
item.ref === sourceRef || item.digest === knowledge.contentDigest,
|
|
2832
|
+
))
|
|
2833
|
+
) {
|
|
2834
|
+
throw portsError(
|
|
2835
|
+
EVOLUTION_LEDGER_SOURCE_REVOKED_CODE,
|
|
2836
|
+
"release activation references a durably revoked Knowledge source or release",
|
|
2837
|
+
{
|
|
2838
|
+
candidateId: target.candidateId,
|
|
2839
|
+
releaseDigest: target.releaseDigest,
|
|
2840
|
+
operationDigest: prepared.operationDigest,
|
|
2841
|
+
},
|
|
2842
|
+
);
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
if (seenOperations.size !== releases.size) {
|
|
2846
|
+
throw portsError(
|
|
2847
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2848
|
+
"quarantine release has no authenticated prepared revocation",
|
|
2849
|
+
);
|
|
2850
|
+
}
|
|
2851
|
+
// Bare legacy calls remain compatible only before any Knowledge revocation.
|
|
2852
|
+
// Once a fence exists, missing provenance must not mean unrelated/safe.
|
|
2853
|
+
if (targetInput !== undefined) readTarget();
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
#releaseWikiProvenance(snapshot, binding, target, knowledge, release = null) {
|
|
2857
|
+
if (target.candidate.derivationMode === "wiki") {
|
|
2858
|
+
// Discover the original run on the same authenticated ledger. Neither a
|
|
2859
|
+
// configurable reader nor a self-reported safe subset establishes origin.
|
|
2860
|
+
const event = snapshot.byEventId.get(
|
|
2861
|
+
`wiki.revision.${target.candidate.wikiRevision.slice(5)}`,
|
|
2862
|
+
);
|
|
2863
|
+
if (
|
|
2864
|
+
!event ||
|
|
2865
|
+
event.type !== WIKI_LEDGER_EVENT_TYPE ||
|
|
2866
|
+
event.tenantId !== binding.tenantId ||
|
|
2867
|
+
event.artifactTenantId !== this.#artifactTenantId
|
|
2868
|
+
) {
|
|
2869
|
+
throw portsError(
|
|
2870
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2871
|
+
"release admission cannot authenticate its original Wiki revision",
|
|
2872
|
+
);
|
|
2873
|
+
}
|
|
2874
|
+
const readOnly = () => {
|
|
2875
|
+
throw new TypeError("admission Wiki reader is read-only");
|
|
2876
|
+
};
|
|
2877
|
+
const adapter = new WikiMaintainerLedgerAdapter({
|
|
2878
|
+
descriptor: {
|
|
2879
|
+
tenantId: binding.tenantId,
|
|
2880
|
+
artifactTenantId: this.#artifactTenantId,
|
|
2881
|
+
evolutionRunId: event.correlationId,
|
|
2882
|
+
audience: binding.audience,
|
|
2883
|
+
purpose: this.#purpose,
|
|
2884
|
+
},
|
|
2885
|
+
artifactPorts: { putCanonical: readOnly },
|
|
2886
|
+
ledger: {
|
|
2887
|
+
read: this.#ledgerRead,
|
|
2888
|
+
verify: this.#ledgerVerify,
|
|
2889
|
+
appendDomainEvent: readOnly,
|
|
2890
|
+
},
|
|
2891
|
+
ledgerArtifactResolver: this.#artifactResolve,
|
|
2892
|
+
});
|
|
2893
|
+
const reader = captureWikiRevisionReader(adapter);
|
|
2894
|
+
const revision = reader.readKnowledgeProvenance({
|
|
2895
|
+
tenantId: binding.tenantId,
|
|
2896
|
+
revisionId: target.candidate.wikiRevision,
|
|
2897
|
+
knowledgeId: knowledge.knowledgeId,
|
|
2898
|
+
contentDigest: knowledge.contentDigest,
|
|
2899
|
+
});
|
|
2900
|
+
const tail = snapshot.events.at(-1);
|
|
2901
|
+
if (
|
|
2902
|
+
revision.ledgerHead.sequence !== tail.sequence ||
|
|
2903
|
+
revision.ledgerHead.headDigest !== tail.eventDigest ||
|
|
2904
|
+
["epoch", "ledgerId", "identityDigest"].some(
|
|
2905
|
+
(key) => revision.ledgerHead[key] !== tail[key],
|
|
2906
|
+
)
|
|
2907
|
+
) {
|
|
2908
|
+
throw portsError(
|
|
2909
|
+
EVOLUTION_LEDGER_PORTS_UNAVAILABLE_CODE,
|
|
2910
|
+
"ledger changed while checking immutable Wiki provenance",
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2913
|
+
const releasedDependency = release?.dependencies.find(
|
|
2914
|
+
(dependency) =>
|
|
2915
|
+
dependency.kind === "wiki" &&
|
|
2916
|
+
dependency.disposition === "quarantine" &&
|
|
2917
|
+
dependency.digest === revision.stateDigest,
|
|
2918
|
+
);
|
|
2919
|
+
if (releasedDependency) {
|
|
2920
|
+
const current = reader.loadWiki();
|
|
2921
|
+
const restored = revision.affectedPatternIds.every(
|
|
2922
|
+
(patternId) =>
|
|
2923
|
+
current.state.patterns[patternId]?.status !== "quarantined" &&
|
|
2924
|
+
current.state.evolutionLog.some(
|
|
2925
|
+
(entry) =>
|
|
2926
|
+
entry.type === "pattern-quarantine-released" &&
|
|
2927
|
+
entry.subjectId === patternId &&
|
|
2928
|
+
entry.details?.operationDigest === release.operationDigest &&
|
|
2929
|
+
entry.details?.releaseRecordDigest === release.recordDigest,
|
|
2930
|
+
),
|
|
2931
|
+
);
|
|
2932
|
+
if (restored) {
|
|
2933
|
+
return deepFreeze({ ...revision, unsafePatternIds: [] });
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
return revision;
|
|
2937
|
+
}
|
|
2938
|
+
return null;
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
prepare(input, targetRelease) {
|
|
2371
2942
|
const intent = normalizeIntent(input);
|
|
2372
2943
|
this.#assertAudience(intent.mutationRequest.audience, "intent audience");
|
|
2373
2944
|
const eventId = deterministicEventId("prepare", {
|
|
2374
2945
|
transactionId: intent.transactionId,
|
|
2375
2946
|
});
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2947
|
+
for (let attempt = 0; attempt < MAX_FINALIZE_RETRIES; attempt += 1) {
|
|
2948
|
+
const snapshot = this.#snapshot();
|
|
2949
|
+
const existing = snapshot.byEventId.get(eventId);
|
|
2950
|
+
if (existing) {
|
|
2951
|
+
return this.#prepareProjection(
|
|
2952
|
+
this.#prepareFromSnapshot(snapshot, existing, {
|
|
2953
|
+
authorityReceiptDigest: intent.authorityReceiptDigest,
|
|
2954
|
+
intentDigest: intent.intentDigest,
|
|
2955
|
+
transactionId: intent.transactionId,
|
|
2956
|
+
}),
|
|
2957
|
+
);
|
|
2958
|
+
}
|
|
2959
|
+
const audit = this.#auditFromSnapshot(snapshot, intent.authorityReceipt);
|
|
2960
|
+
this.#assertReleaseNotRevoked(
|
|
2961
|
+
snapshot,
|
|
2962
|
+
{
|
|
2963
|
+
tenantId: intent.mutationRequest.tenantId,
|
|
2964
|
+
skillName: intent.skillName,
|
|
2965
|
+
audience: intent.mutationRequest.audience,
|
|
2966
|
+
releaseDigest: intent.targetReleaseDigest,
|
|
2967
|
+
candidateId: intent.candidateId,
|
|
2968
|
+
},
|
|
2969
|
+
targetRelease,
|
|
2970
|
+
);
|
|
2971
|
+
const subjectRef = this.#putSubject(
|
|
2972
|
+
ARTIFACT_TYPES.prepare,
|
|
2973
|
+
intent,
|
|
2974
|
+
intent.intentDigest,
|
|
2975
|
+
intent.mutationRequest.audience,
|
|
2976
|
+
);
|
|
2977
|
+
const eventInput = {
|
|
2978
|
+
artifactTenantId: this.#artifactTenantId,
|
|
2979
|
+
correlationId: intent.transactionId,
|
|
2980
|
+
decision: "prepared",
|
|
2981
|
+
eventId,
|
|
2982
|
+
reason: intent.intentDigest,
|
|
2983
|
+
skillName: intent.skillName,
|
|
2984
|
+
sourceRefs: [audit.event.subjectRef],
|
|
2985
|
+
subjectRef,
|
|
2986
|
+
tenantId: intent.mutationRequest.tenantId,
|
|
2987
|
+
type: EVENT_TYPES.prepare,
|
|
2988
|
+
};
|
|
2989
|
+
let persistedSnapshot;
|
|
2990
|
+
try {
|
|
2991
|
+
const previous = snapshot.events.at(-1) || null;
|
|
2992
|
+
const verification = this.#verifiedAppend(eventInput, {
|
|
2993
|
+
expectedHeadDigest: previous?.eventDigest ?? null,
|
|
2994
|
+
expectedSequence: previous?.sequence ?? 0,
|
|
2995
|
+
});
|
|
2996
|
+
persistedSnapshot =
|
|
2997
|
+
this.#snapshotWithVerifiedEvent(snapshot, verification) ||
|
|
2998
|
+
this.#snapshot();
|
|
2999
|
+
} catch (cause) {
|
|
3000
|
+
if (isHeadConflict(cause)) continue;
|
|
3001
|
+
if (!isCommitUnknown(cause) && !isEventConflict(cause)) throw cause;
|
|
3002
|
+
persistedSnapshot = this.#snapshot();
|
|
3003
|
+
}
|
|
3004
|
+
const persisted = persistedSnapshot.byEventId.get(eventId);
|
|
3005
|
+
if (!persisted) {
|
|
3006
|
+
throw portsError(
|
|
3007
|
+
EVOLUTION_LEDGER_PORTS_UNAVAILABLE_CODE,
|
|
3008
|
+
"release prepare append did not converge to a queryable event",
|
|
3009
|
+
);
|
|
3010
|
+
}
|
|
2379
3011
|
return this.#prepareProjection(
|
|
2380
|
-
this.#prepareFromSnapshot(
|
|
3012
|
+
this.#prepareFromSnapshot(persistedSnapshot, persisted, {
|
|
2381
3013
|
authorityReceiptDigest: intent.authorityReceiptDigest,
|
|
2382
3014
|
intentDigest: intent.intentDigest,
|
|
2383
3015
|
transactionId: intent.transactionId,
|
|
2384
3016
|
}),
|
|
2385
3017
|
);
|
|
2386
3018
|
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
intent,
|
|
2391
|
-
intent.intentDigest,
|
|
2392
|
-
intent.mutationRequest.audience,
|
|
2393
|
-
);
|
|
2394
|
-
const eventInput = {
|
|
2395
|
-
artifactTenantId: this.#artifactTenantId,
|
|
2396
|
-
correlationId: intent.transactionId,
|
|
2397
|
-
decision: "prepared",
|
|
2398
|
-
eventId,
|
|
2399
|
-
reason: intent.intentDigest,
|
|
2400
|
-
skillName: intent.skillName,
|
|
2401
|
-
sourceRefs: [audit.event.subjectRef],
|
|
2402
|
-
subjectRef,
|
|
2403
|
-
tenantId: intent.mutationRequest.tenantId,
|
|
2404
|
-
type: EVENT_TYPES.prepare,
|
|
2405
|
-
};
|
|
2406
|
-
let persistedSnapshot;
|
|
2407
|
-
try {
|
|
2408
|
-
const verification = this.#verifiedAppend(eventInput);
|
|
2409
|
-
persistedSnapshot =
|
|
2410
|
-
this.#snapshotWithVerifiedEvent(snapshot, verification) ||
|
|
2411
|
-
this.#snapshot();
|
|
2412
|
-
} catch (cause) {
|
|
2413
|
-
if (!isCommitUnknown(cause) && !isEventConflict(cause)) throw cause;
|
|
2414
|
-
persistedSnapshot = this.#snapshot();
|
|
2415
|
-
}
|
|
2416
|
-
const persisted = persistedSnapshot.byEventId.get(eventId);
|
|
2417
|
-
if (!persisted) {
|
|
2418
|
-
throw portsError(
|
|
2419
|
-
EVOLUTION_LEDGER_PORTS_UNAVAILABLE_CODE,
|
|
2420
|
-
"release prepare append did not converge to a queryable event",
|
|
2421
|
-
);
|
|
2422
|
-
}
|
|
2423
|
-
return this.#prepareProjection(
|
|
2424
|
-
this.#prepareFromSnapshot(persistedSnapshot, persisted, {
|
|
2425
|
-
authorityReceiptDigest: intent.authorityReceiptDigest,
|
|
2426
|
-
intentDigest: intent.intentDigest,
|
|
2427
|
-
transactionId: intent.transactionId,
|
|
2428
|
-
}),
|
|
3019
|
+
throw portsError(
|
|
3020
|
+
EVOLUTION_LEDGER_PORTS_UNAVAILABLE_CODE,
|
|
3021
|
+
"release prepare could not acquire a stable ledger head",
|
|
2429
3022
|
);
|
|
2430
3023
|
}
|
|
2431
3024
|
|
|
@@ -2560,6 +3153,513 @@ class EvolutionLedgerDomainPorts {
|
|
|
2560
3153
|
return this.#committedProjection(finalized, lineages);
|
|
2561
3154
|
}
|
|
2562
3155
|
|
|
3156
|
+
resolveReleaseOperation(inputValue) {
|
|
3157
|
+
return this.#resolveRelease(inputValue, false);
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
resolveReleaseOrigin(inputValue) {
|
|
3161
|
+
return this.#resolveRelease(inputValue, true);
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3164
|
+
readReleaseHistory(inputValue) {
|
|
3165
|
+
return this.#resolveRelease(inputValue, false, true);
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
resolveKnowledgeRevocation(inputValue) {
|
|
3169
|
+
if (this.#audience === null)
|
|
3170
|
+
throw new TypeError(
|
|
3171
|
+
"Knowledge revocation proof requires audience-bound ledger ports",
|
|
3172
|
+
);
|
|
3173
|
+
const input = frozenCanonicalClone(inputValue);
|
|
3174
|
+
assertAllExactRecord(
|
|
3175
|
+
input,
|
|
3176
|
+
new Set(["tenantId", "operationDigest", "context"]),
|
|
3177
|
+
"Knowledge revocation query",
|
|
3178
|
+
);
|
|
3179
|
+
const tenantId = identifier(input.tenantId, "tenantId");
|
|
3180
|
+
const operationDigest = digest(input.operationDigest, "operationDigest");
|
|
3181
|
+
const keys = [
|
|
3182
|
+
"epoch",
|
|
3183
|
+
"ledgerId",
|
|
3184
|
+
"identityDigest",
|
|
3185
|
+
"sequence",
|
|
3186
|
+
"headDigest",
|
|
3187
|
+
];
|
|
3188
|
+
assertAllExactRecord(
|
|
3189
|
+
input.context,
|
|
3190
|
+
new Set(["mode", "checkpoint"]),
|
|
3191
|
+
"Knowledge revocation context",
|
|
3192
|
+
);
|
|
3193
|
+
assertAllExactRecord(
|
|
3194
|
+
input.context.checkpoint,
|
|
3195
|
+
new Set(keys),
|
|
3196
|
+
"Knowledge revocation checkpoint",
|
|
3197
|
+
);
|
|
3198
|
+
const head = this.#ledgerVerify();
|
|
3199
|
+
if (
|
|
3200
|
+
input.context.mode !== "current" ||
|
|
3201
|
+
keys.some((key) => input.context.checkpoint[key] !== head[key])
|
|
3202
|
+
)
|
|
3203
|
+
throw portsError(
|
|
3204
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3205
|
+
"Knowledge revocation requires the current authenticated head",
|
|
3206
|
+
);
|
|
3207
|
+
const snapshot = this.#snapshot();
|
|
3208
|
+
if (
|
|
3209
|
+
snapshot.events.length !== head.sequence ||
|
|
3210
|
+
(snapshot.events.at(-1)?.eventDigest ?? null) !== head.headDigest
|
|
3211
|
+
)
|
|
3212
|
+
throw portsError(
|
|
3213
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3214
|
+
"Knowledge revocation snapshot changed",
|
|
3215
|
+
);
|
|
3216
|
+
let fence = null;
|
|
3217
|
+
for (const entry of this.#knowledgeRevocations(
|
|
3218
|
+
snapshot,
|
|
3219
|
+
tenantId,
|
|
3220
|
+
this.#audience,
|
|
3221
|
+
)) {
|
|
3222
|
+
if (entry.prepared.operationDigest !== operationDigest) continue;
|
|
3223
|
+
fence = {
|
|
3224
|
+
record: entry.prepared,
|
|
3225
|
+
sequence: entry.event.sequence,
|
|
3226
|
+
evidence: {
|
|
3227
|
+
timestamp: entry.event.timestamp,
|
|
3228
|
+
signature: entry.event.signature,
|
|
3229
|
+
eventDigest: entry.event.eventDigest,
|
|
3230
|
+
},
|
|
3231
|
+
};
|
|
3232
|
+
}
|
|
3233
|
+
const fresh = this.#ledgerVerify();
|
|
3234
|
+
if (keys.some((key) => fresh[key] !== head[key]))
|
|
3235
|
+
throw portsError(
|
|
3236
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3237
|
+
"Knowledge revocation changed during verification",
|
|
3238
|
+
);
|
|
3239
|
+
return deepFreeze({
|
|
3240
|
+
authenticated: true,
|
|
3241
|
+
durable: true,
|
|
3242
|
+
tenantId,
|
|
3243
|
+
operationDigest,
|
|
3244
|
+
checkpoint: { ...input.context.checkpoint },
|
|
3245
|
+
fence,
|
|
3246
|
+
});
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
resolveCandidateRevocation(inputValue) {
|
|
3250
|
+
return this.#resolveCandidateDisposition(inputValue, "reject-candidate");
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
resolveCandidateQuarantine(inputValue) {
|
|
3254
|
+
return this.#resolveCandidateDisposition(inputValue, "quarantine");
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
#resolveCandidateDisposition(inputValue, disposition) {
|
|
3258
|
+
if (this.#audience === null)
|
|
3259
|
+
throw new TypeError(
|
|
3260
|
+
"candidate revocation proof requires audience-bound ledger ports",
|
|
3261
|
+
);
|
|
3262
|
+
const input = frozenCanonicalClone(inputValue);
|
|
3263
|
+
assertAllExactRecord(
|
|
3264
|
+
input,
|
|
3265
|
+
new Set([
|
|
3266
|
+
"tenantId",
|
|
3267
|
+
"skillName",
|
|
3268
|
+
"candidateId",
|
|
3269
|
+
"operationDigest",
|
|
3270
|
+
"context",
|
|
3271
|
+
]),
|
|
3272
|
+
"candidate revocation query",
|
|
3273
|
+
);
|
|
3274
|
+
const tenantId = identifier(input.tenantId, "tenantId");
|
|
3275
|
+
const name = skillName(input.skillName, "skillName");
|
|
3276
|
+
const candidateId = digest(input.candidateId, "candidateId");
|
|
3277
|
+
const operationDigest = digest(input.operationDigest, "operationDigest");
|
|
3278
|
+
const keys = [
|
|
3279
|
+
"epoch",
|
|
3280
|
+
"ledgerId",
|
|
3281
|
+
"identityDigest",
|
|
3282
|
+
"sequence",
|
|
3283
|
+
"headDigest",
|
|
3284
|
+
];
|
|
3285
|
+
assertAllExactRecord(
|
|
3286
|
+
input.context,
|
|
3287
|
+
new Set(["mode", "checkpoint"]),
|
|
3288
|
+
"candidate revocation context",
|
|
3289
|
+
);
|
|
3290
|
+
assertAllExactRecord(
|
|
3291
|
+
input.context.checkpoint,
|
|
3292
|
+
new Set(keys),
|
|
3293
|
+
"candidate revocation checkpoint",
|
|
3294
|
+
);
|
|
3295
|
+
if (input.context.mode !== "current")
|
|
3296
|
+
throw new TypeError("candidate revocation requires a current context");
|
|
3297
|
+
const head = this.#ledgerVerify();
|
|
3298
|
+
if (keys.some((key) => input.context.checkpoint[key] !== head[key])) {
|
|
3299
|
+
throw portsError(
|
|
3300
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3301
|
+
"candidate revocation checkpoint is stale",
|
|
3302
|
+
);
|
|
3303
|
+
}
|
|
3304
|
+
const snapshot = this.#snapshot();
|
|
3305
|
+
if (
|
|
3306
|
+
snapshot.events.length !== head.sequence ||
|
|
3307
|
+
(snapshot.events.at(-1)?.eventDigest ?? null) !== head.headDigest
|
|
3308
|
+
) {
|
|
3309
|
+
throw portsError(
|
|
3310
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3311
|
+
"candidate revocation snapshot changed",
|
|
3312
|
+
);
|
|
3313
|
+
}
|
|
3314
|
+
let fence = null;
|
|
3315
|
+
// This is the exact same authenticated parser as mandatory prepare, not a
|
|
3316
|
+
// separate provider's assertion that admission has supposedly been denied.
|
|
3317
|
+
for (const entry of this.#knowledgeRevocations(
|
|
3318
|
+
snapshot,
|
|
3319
|
+
tenantId,
|
|
3320
|
+
this.#audience,
|
|
3321
|
+
)) {
|
|
3322
|
+
if (entry.prepared.operationDigest !== operationDigest) continue;
|
|
3323
|
+
const dependency = entry.prepared.knowledge.dependencies.find(
|
|
3324
|
+
(item) =>
|
|
3325
|
+
item.kind === "candidate" &&
|
|
3326
|
+
item.digest === candidateId &&
|
|
3327
|
+
item.disposition === disposition,
|
|
3328
|
+
);
|
|
3329
|
+
if (dependency) fence = entry;
|
|
3330
|
+
}
|
|
3331
|
+
const cache = new Map();
|
|
3332
|
+
const lineages = this.#lineages(snapshot, cache);
|
|
3333
|
+
const history = [];
|
|
3334
|
+
const pendingTransactions = [];
|
|
3335
|
+
for (const event of snapshot.events) {
|
|
3336
|
+
if (event.type !== EVENT_TYPES.prepare || event.tenantId !== tenantId)
|
|
3337
|
+
continue;
|
|
3338
|
+
const entry =
|
|
3339
|
+
cache.get(event.eventId) ?? this.#prepareFromSnapshot(snapshot, event);
|
|
3340
|
+
const intent = entry.intent;
|
|
3341
|
+
if (intent.candidateId === candidateId && intent.skillName !== name) {
|
|
3342
|
+
throw portsError(
|
|
3343
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3344
|
+
"candidate history belongs to another Skill",
|
|
3345
|
+
);
|
|
3346
|
+
}
|
|
3347
|
+
if (intent.skillName !== name) continue;
|
|
3348
|
+
const finalized = lineages.byTransaction.get(intent.transactionId);
|
|
3349
|
+
if (!finalized) pendingTransactions.push(intent.transactionId);
|
|
3350
|
+
if (intent.candidateId !== candidateId) continue;
|
|
3351
|
+
history.push({
|
|
3352
|
+
transactionId: intent.transactionId,
|
|
3353
|
+
releaseDigest: intent.targetReleaseDigest,
|
|
3354
|
+
preparationSequence: event.sequence,
|
|
3355
|
+
committed: Boolean(finalized),
|
|
3356
|
+
});
|
|
3357
|
+
}
|
|
3358
|
+
const groupKey = `${tenantId}\0${name}`;
|
|
3359
|
+
const latest = lineages.groups.get(groupKey)?.at(-1);
|
|
3360
|
+
const migrated = lineages.migrations.get(groupKey);
|
|
3361
|
+
const current = latest
|
|
3362
|
+
? {
|
|
3363
|
+
releaseDigest: latest.prepared.intent.targetReleaseDigest,
|
|
3364
|
+
revision: latest.finalization.revision,
|
|
3365
|
+
stateDigest: latest.finalization.stateDigest,
|
|
3366
|
+
projection: this.#committedProjection(latest, lineages),
|
|
3367
|
+
}
|
|
3368
|
+
: migrated
|
|
3369
|
+
? {
|
|
3370
|
+
releaseDigest: migrated.migration.state.activeReleaseDigest,
|
|
3371
|
+
revision: migrated.migration.state.revision,
|
|
3372
|
+
stateDigest: migrated.migration.state.stateDigest,
|
|
3373
|
+
projection: this.#migrationProjection(migrated, true),
|
|
3374
|
+
}
|
|
3375
|
+
: null;
|
|
3376
|
+
const fresh = this.#ledgerVerify();
|
|
3377
|
+
if (keys.some((key) => fresh[key] !== head[key])) {
|
|
3378
|
+
throw portsError(
|
|
3379
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3380
|
+
"candidate revocation ledger changed during verification",
|
|
3381
|
+
);
|
|
3382
|
+
}
|
|
3383
|
+
return deepFreeze({
|
|
3384
|
+
authenticated: true,
|
|
3385
|
+
durable: true,
|
|
3386
|
+
tenantId,
|
|
3387
|
+
skillName: name,
|
|
3388
|
+
candidateId,
|
|
3389
|
+
operationDigest,
|
|
3390
|
+
checkpoint: { ...input.context.checkpoint },
|
|
3391
|
+
fence: fence
|
|
3392
|
+
? {
|
|
3393
|
+
record: fence.prepared,
|
|
3394
|
+
sequence: fence.event.sequence,
|
|
3395
|
+
evidence: {
|
|
3396
|
+
timestamp: fence.event.timestamp,
|
|
3397
|
+
signature: fence.event.signature,
|
|
3398
|
+
eventDigest: fence.event.eventDigest,
|
|
3399
|
+
},
|
|
3400
|
+
}
|
|
3401
|
+
: null,
|
|
3402
|
+
pendingTransactions,
|
|
3403
|
+
history,
|
|
3404
|
+
current,
|
|
3405
|
+
});
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
#resolveRelease(inputValue, origin, history = false) {
|
|
3409
|
+
const input = frozenCanonicalClone(inputValue);
|
|
3410
|
+
assertAllExactRecord(
|
|
3411
|
+
input,
|
|
3412
|
+
new Set([
|
|
3413
|
+
"tenantId",
|
|
3414
|
+
"skillName",
|
|
3415
|
+
...(history ? [] : [origin ? "releaseDigest" : "operationId"]),
|
|
3416
|
+
"context",
|
|
3417
|
+
]),
|
|
3418
|
+
"release operation query",
|
|
3419
|
+
);
|
|
3420
|
+
const tenantId = identifier(input.tenantId, "tenantId");
|
|
3421
|
+
const name = skillName(input.skillName, "skillName");
|
|
3422
|
+
const operationId =
|
|
3423
|
+
origin || history ? null : identifier(input.operationId, "operationId");
|
|
3424
|
+
const releaseDigest = origin
|
|
3425
|
+
? digest(
|
|
3426
|
+
input.releaseDigest,
|
|
3427
|
+
"releaseDigest",
|
|
3428
|
+
EVOLUTION_LEDGER_PORTS_INVALID_CODE,
|
|
3429
|
+
)
|
|
3430
|
+
: null;
|
|
3431
|
+
const context = input.context;
|
|
3432
|
+
assertAllExactRecord(
|
|
3433
|
+
context,
|
|
3434
|
+
new Set(["mode", "checkpoint"]),
|
|
3435
|
+
"release operation context",
|
|
3436
|
+
);
|
|
3437
|
+
const keys = [
|
|
3438
|
+
"epoch",
|
|
3439
|
+
"ledgerId",
|
|
3440
|
+
"identityDigest",
|
|
3441
|
+
"sequence",
|
|
3442
|
+
"headDigest",
|
|
3443
|
+
];
|
|
3444
|
+
assertAllExactRecord(
|
|
3445
|
+
context.checkpoint,
|
|
3446
|
+
new Set(keys),
|
|
3447
|
+
"release operation checkpoint",
|
|
3448
|
+
);
|
|
3449
|
+
if (!["current", "checkpoint"].includes(context.mode))
|
|
3450
|
+
throw new TypeError("invalid release operation mode");
|
|
3451
|
+
const snapshot = this.#snapshot();
|
|
3452
|
+
const tail = snapshot.events.at(-1);
|
|
3453
|
+
const head = tail
|
|
3454
|
+
? { ...tail, headDigest: tail.eventDigest }
|
|
3455
|
+
: this.#ledgerVerify();
|
|
3456
|
+
if (
|
|
3457
|
+
snapshot.events.length !== head.sequence ||
|
|
3458
|
+
snapshot.events.some((event) =>
|
|
3459
|
+
["epoch", "ledgerId", "identityDigest"].some(
|
|
3460
|
+
(key) => event[key] !== head[key],
|
|
3461
|
+
),
|
|
3462
|
+
)
|
|
3463
|
+
)
|
|
3464
|
+
throw portsError(
|
|
3465
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3466
|
+
"release operation snapshot is not the full current ledger",
|
|
3467
|
+
);
|
|
3468
|
+
const checkpoint = context.checkpoint;
|
|
3469
|
+
const historical =
|
|
3470
|
+
checkpoint.sequence === 0
|
|
3471
|
+
? null
|
|
3472
|
+
: snapshot.bySequence.get(checkpoint.sequence);
|
|
3473
|
+
if (
|
|
3474
|
+
!Number.isSafeInteger(checkpoint.sequence) ||
|
|
3475
|
+
checkpoint.sequence < 0 ||
|
|
3476
|
+
checkpoint.sequence > head.sequence ||
|
|
3477
|
+
["epoch", "ledgerId", "identityDigest"].some(
|
|
3478
|
+
(key) => checkpoint[key] !== head[key],
|
|
3479
|
+
) ||
|
|
3480
|
+
checkpoint.headDigest !== (historical?.eventDigest ?? null) ||
|
|
3481
|
+
(context.mode === "current" &&
|
|
3482
|
+
keys.some((key) => checkpoint[key] !== head[key]))
|
|
3483
|
+
) {
|
|
3484
|
+
throw portsError(
|
|
3485
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3486
|
+
"release operation checkpoint differs from the authenticated ledger",
|
|
3487
|
+
);
|
|
3488
|
+
}
|
|
3489
|
+
// Authenticate the whole present lineage even for a historical query, then
|
|
3490
|
+
// derive the exact as-of prefix. A later finalize cannot prove an earlier ack.
|
|
3491
|
+
const prepareCache = new Map();
|
|
3492
|
+
const presentLineages = this.#lineages(snapshot, prepareCache);
|
|
3493
|
+
const prefix = this.#indexSnapshot(
|
|
3494
|
+
snapshot.events.slice(0, checkpoint.sequence),
|
|
3495
|
+
snapshot.subjects,
|
|
3496
|
+
);
|
|
3497
|
+
// Reuse only work authenticated in THIS read, and only when all release
|
|
3498
|
+
// events and their consumed authority precede the requested checkpoint.
|
|
3499
|
+
// Unrelated later Wiki/journal events do not change the release lineage.
|
|
3500
|
+
// A later prepare/finalize/migration still forces exact prefix replay.
|
|
3501
|
+
const unchangedReleaseHistory =
|
|
3502
|
+
!snapshot.events
|
|
3503
|
+
.slice(checkpoint.sequence)
|
|
3504
|
+
.some((event) =>
|
|
3505
|
+
[
|
|
3506
|
+
EVENT_TYPES.prepare,
|
|
3507
|
+
EVENT_TYPES.finalize,
|
|
3508
|
+
EVENT_TYPES.migration,
|
|
3509
|
+
].includes(event.type),
|
|
3510
|
+
) &&
|
|
3511
|
+
[...prepareCache.values()].every((entry) =>
|
|
3512
|
+
prefix.byEventId.has(entry.consumeAudit.event.eventId),
|
|
3513
|
+
);
|
|
3514
|
+
const lineages = unchangedReleaseHistory
|
|
3515
|
+
? presentLineages
|
|
3516
|
+
: this.#lineages(prefix);
|
|
3517
|
+
const prepared = [];
|
|
3518
|
+
for (const event of prefix.events) {
|
|
3519
|
+
if (event.type !== EVENT_TYPES.prepare) continue;
|
|
3520
|
+
const cached = prepareCache.get(event.eventId);
|
|
3521
|
+
const entry =
|
|
3522
|
+
cached && prefix.byEventId.has(cached.consumeAudit.event.eventId)
|
|
3523
|
+
? cached
|
|
3524
|
+
: this.#prepareFromSnapshot(prefix, event);
|
|
3525
|
+
if (
|
|
3526
|
+
entry.intent.mutationRequest.tenantId === tenantId &&
|
|
3527
|
+
entry.intent.skillName === name &&
|
|
3528
|
+
(history ||
|
|
3529
|
+
(origin
|
|
3530
|
+
? entry.intent.operation === "promote" &&
|
|
3531
|
+
entry.intent.targetReleaseDigest === releaseDigest
|
|
3532
|
+
: entry.intent.operationId === operationId))
|
|
3533
|
+
)
|
|
3534
|
+
prepared.push(entry);
|
|
3535
|
+
}
|
|
3536
|
+
if (!history && prepared.length > 1)
|
|
3537
|
+
throw portsError(
|
|
3538
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3539
|
+
"release operation has multiple transaction intents",
|
|
3540
|
+
);
|
|
3541
|
+
if (history && prepared.length > 10_000)
|
|
3542
|
+
throw portsError(
|
|
3543
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3544
|
+
"release history exceeds its bounded budget",
|
|
3545
|
+
);
|
|
3546
|
+
if (
|
|
3547
|
+
history &&
|
|
3548
|
+
new Set(prepared.map((entry) => entry.intent.operationId)).size !==
|
|
3549
|
+
prepared.length
|
|
3550
|
+
)
|
|
3551
|
+
throw portsError(
|
|
3552
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3553
|
+
"release operation has multiple transaction intents",
|
|
3554
|
+
);
|
|
3555
|
+
const groupKey = `${tenantId}\0${name}`;
|
|
3556
|
+
const baseline = lineages.migrations.get(groupKey)?.migration.state;
|
|
3557
|
+
let previous = baseline
|
|
3558
|
+
? {
|
|
3559
|
+
revision: baseline.revision,
|
|
3560
|
+
stateDigest: baseline.stateDigest,
|
|
3561
|
+
activeReleaseDigest: baseline.activeReleaseDigest,
|
|
3562
|
+
lastKnownGoodReleaseDigest: baseline.lastKnownGoodReleaseDigest,
|
|
3563
|
+
}
|
|
3564
|
+
: {
|
|
3565
|
+
revision: 0,
|
|
3566
|
+
stateDigest: null,
|
|
3567
|
+
activeReleaseDigest: null,
|
|
3568
|
+
lastKnownGoodReleaseDigest: null,
|
|
3569
|
+
};
|
|
3570
|
+
const predecessors = new Map([[previous.revision, previous]]);
|
|
3571
|
+
for (const prior of lineages.groups.get(groupKey) ?? []) {
|
|
3572
|
+
const intent = prior.prepared.intent;
|
|
3573
|
+
if (
|
|
3574
|
+
intent.operation === "rollback" &&
|
|
3575
|
+
intent.targetReleaseDigest !== previous.lastKnownGoodReleaseDigest
|
|
3576
|
+
)
|
|
3577
|
+
throw portsError(
|
|
3578
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3579
|
+
"historical rollback does not target the last-known-good release",
|
|
3580
|
+
);
|
|
3581
|
+
previous = {
|
|
3582
|
+
revision: prior.finalization.revision,
|
|
3583
|
+
stateDigest: prior.finalization.stateDigest,
|
|
3584
|
+
activeReleaseDigest: intent.targetReleaseDigest,
|
|
3585
|
+
lastKnownGoodReleaseDigest:
|
|
3586
|
+
intent.operation === "promote"
|
|
3587
|
+
? (previous.activeReleaseDigest ?? intent.targetReleaseDigest)
|
|
3588
|
+
: intent.targetReleaseDigest,
|
|
3589
|
+
};
|
|
3590
|
+
predecessors.set(previous.revision, previous);
|
|
3591
|
+
}
|
|
3592
|
+
const results = prepared.map((entry) => {
|
|
3593
|
+
const previous = predecessors.get(entry.intent.expectedRevision);
|
|
3594
|
+
if (
|
|
3595
|
+
!previous ||
|
|
3596
|
+
previous.revision !== entry.intent.expectedRevision ||
|
|
3597
|
+
previous.stateDigest !== entry.intent.previousStateDigest
|
|
3598
|
+
)
|
|
3599
|
+
throw portsError(
|
|
3600
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3601
|
+
"release operation has no exact predecessor",
|
|
3602
|
+
);
|
|
3603
|
+
const finalized = lineages.byTransaction.get(entry.intent.transactionId);
|
|
3604
|
+
return {
|
|
3605
|
+
intent: entry.intent,
|
|
3606
|
+
previous,
|
|
3607
|
+
preparationCheckpoint: {
|
|
3608
|
+
epoch: entry.event.epoch,
|
|
3609
|
+
ledgerId: entry.event.ledgerId,
|
|
3610
|
+
identityDigest: entry.event.identityDigest,
|
|
3611
|
+
sequence: entry.event.sequence,
|
|
3612
|
+
headDigest: entry.event.eventDigest,
|
|
3613
|
+
},
|
|
3614
|
+
projection: finalized
|
|
3615
|
+
? this.#committedProjection(finalized, lineages)
|
|
3616
|
+
: this.#prepareProjection(entry),
|
|
3617
|
+
finalizationEvidence: finalized
|
|
3618
|
+
? {
|
|
3619
|
+
timestamp: finalized.event.timestamp,
|
|
3620
|
+
signature: finalized.event.signature,
|
|
3621
|
+
eventDigest: finalized.event.eventDigest,
|
|
3622
|
+
}
|
|
3623
|
+
: null,
|
|
3624
|
+
};
|
|
3625
|
+
});
|
|
3626
|
+
const result = results[0] ?? null;
|
|
3627
|
+
const fresh = this.#ledgerVerify();
|
|
3628
|
+
if (keys.some((key) => fresh[key] !== head[key]))
|
|
3629
|
+
throw portsError(
|
|
3630
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
3631
|
+
"release operation ledger changed during verification",
|
|
3632
|
+
);
|
|
3633
|
+
if (history) {
|
|
3634
|
+
const migration = lineages.migrations.get(`${tenantId}\0${name}`);
|
|
3635
|
+
return deepFreeze({
|
|
3636
|
+
authenticated: true,
|
|
3637
|
+
durable: true,
|
|
3638
|
+
tenantId,
|
|
3639
|
+
skillName: name,
|
|
3640
|
+
checkpoint: { ...checkpoint },
|
|
3641
|
+
migration: migration
|
|
3642
|
+
? {
|
|
3643
|
+
value: migration.migration,
|
|
3644
|
+
artifactRef: migration.event.subjectRef,
|
|
3645
|
+
sequence: migration.event.sequence,
|
|
3646
|
+
eventDigest: migration.event.eventDigest,
|
|
3647
|
+
}
|
|
3648
|
+
: null,
|
|
3649
|
+
operations: results,
|
|
3650
|
+
});
|
|
3651
|
+
}
|
|
3652
|
+
return deepFreeze({
|
|
3653
|
+
authenticated: true,
|
|
3654
|
+
durable: true,
|
|
3655
|
+
tenantId,
|
|
3656
|
+
skillName: name,
|
|
3657
|
+
operationId: origin ? (result?.intent.operationId ?? null) : operationId,
|
|
3658
|
+
checkpoint: { ...checkpoint },
|
|
3659
|
+
result,
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3662
|
+
|
|
2563
3663
|
appendAudit(input) {
|
|
2564
3664
|
const value = frozenCanonicalClone(input);
|
|
2565
3665
|
const audit = verifySkillMutationAuditEvent(value);
|
|
@@ -3009,16 +4109,69 @@ export function createEvolutionLedgerPorts(options = {}) {
|
|
|
3009
4109
|
)
|
|
3010
4110
|
: "evolution-ledger",
|
|
3011
4111
|
});
|
|
3012
|
-
const prepare = Object.freeze((input) =>
|
|
4112
|
+
const prepare = Object.freeze((input, targetRelease) =>
|
|
4113
|
+
adapter.prepare(input, targetRelease),
|
|
4114
|
+
);
|
|
3013
4115
|
const finalize = Object.freeze((input) => adapter.finalize(input));
|
|
3014
|
-
const migrate = Object.freeze((input) =>
|
|
4116
|
+
const migrate = Object.freeze((input, releases) =>
|
|
4117
|
+
adapter.migrate(input, releases),
|
|
4118
|
+
);
|
|
3015
4119
|
const query = Object.freeze((transactionId) => adapter.query(transactionId));
|
|
3016
4120
|
const append = Object.freeze((event) => adapter.appendAudit(event));
|
|
3017
4121
|
const claim = Object.freeze((nonce) => adapter.claimNonce(nonce));
|
|
4122
|
+
const transactionLedger = Object.freeze({
|
|
4123
|
+
finalize,
|
|
4124
|
+
migrate,
|
|
4125
|
+
prepare,
|
|
4126
|
+
query,
|
|
4127
|
+
});
|
|
4128
|
+
const readCurrentHead = ledger.verify.bind(ledger);
|
|
4129
|
+
RELEASE_OPERATION_READERS.set(
|
|
4130
|
+
transactionLedger,
|
|
4131
|
+
Object.freeze({
|
|
4132
|
+
currentContext: Object.freeze(() => {
|
|
4133
|
+
const head = readCurrentHead();
|
|
4134
|
+
return deepFreeze({
|
|
4135
|
+
mode: "current",
|
|
4136
|
+
checkpoint: Object.fromEntries(
|
|
4137
|
+
[
|
|
4138
|
+
"epoch",
|
|
4139
|
+
"ledgerId",
|
|
4140
|
+
"identityDigest",
|
|
4141
|
+
"sequence",
|
|
4142
|
+
"headDigest",
|
|
4143
|
+
].map((key) => [key, head[key]]),
|
|
4144
|
+
),
|
|
4145
|
+
});
|
|
4146
|
+
}),
|
|
4147
|
+
matchesWikiAdapter: Object.freeze((wikiAdapter) =>
|
|
4148
|
+
captureWikiRevisionReader(wikiAdapter).matchesLedger(ledger),
|
|
4149
|
+
),
|
|
4150
|
+
matchesLedger: Object.freeze((value) => value === ledger),
|
|
4151
|
+
resolveOperation: Object.freeze((input) =>
|
|
4152
|
+
adapter.resolveReleaseOperation(input),
|
|
4153
|
+
),
|
|
4154
|
+
resolveReleaseOrigin: Object.freeze((input) =>
|
|
4155
|
+
adapter.resolveReleaseOrigin(input),
|
|
4156
|
+
),
|
|
4157
|
+
readReleaseHistory: Object.freeze((input) =>
|
|
4158
|
+
adapter.readReleaseHistory(input),
|
|
4159
|
+
),
|
|
4160
|
+
resolveCandidateRevocation: Object.freeze((input) =>
|
|
4161
|
+
adapter.resolveCandidateRevocation(input),
|
|
4162
|
+
),
|
|
4163
|
+
resolveCandidateQuarantine: Object.freeze((input) =>
|
|
4164
|
+
adapter.resolveCandidateQuarantine(input),
|
|
4165
|
+
),
|
|
4166
|
+
resolveKnowledgeRevocation: Object.freeze((input) =>
|
|
4167
|
+
adapter.resolveKnowledgeRevocation(input),
|
|
4168
|
+
),
|
|
4169
|
+
}),
|
|
4170
|
+
);
|
|
3018
4171
|
return Object.freeze({
|
|
3019
4172
|
auditSink: Object.freeze({ append }),
|
|
3020
4173
|
nonceStore: Object.freeze({ claim }),
|
|
3021
|
-
transactionLedger
|
|
4174
|
+
transactionLedger,
|
|
3022
4175
|
});
|
|
3023
4176
|
}
|
|
3024
4177
|
|