chainlesschain 0.166.17 → 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 +14 -7
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-CVW2n2rh.js → AIOps-CQSBl3hY.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DtuM163O.js → ActionButton-IQt-vRLt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BXvl_qHu.js → Analytics-DkHC8O7k.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-VDBTFKOp.js → AppLayout-CSti2P2T.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-BNz2xCiG.js → Artifacts-C57hxn3v.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DTu9Hn9P.js → Audit-DByLntcf.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-D2OCPzLe.js → BackgroundAgents-D1Bd2EOa.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-C9M-vG7G.js → Backup-VstHzHng.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BUkCNiUQ.js → BaseInput-CLhdexIf.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-OJ2ajF7l.js → Chat-BefC_JZS.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BdZt76Ab.js → ChatBubbleRenderer-DbXMvgmn.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-C1gXKDsc.js → Checkbox-CEY982vx.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BXbznLg7.js → Codegen-Dcm_DTvJ.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CbCq5N0i.js → Col-C8SSu4KA.js} +1 -1
- package/src/assets/web-panel/assets/{Community-V8Ks6Yun.js → Community-BVV9vwRR.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DkeHQTpQ.js → Compact-B7c5Vo18.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-JeLKwn4Q.js → Compliance-Tq9jSZ5a.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-azfetcBz.js → Cowork-BJUjT09Q.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Q691i_Gp.js → Cron-CjV9PEaS.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BLjDRbr_.js → Crosschain-Dl7ReL8F.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D8ptXplK.js → DID-CG8PjWmM.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Ctj2VVZL.js → Dashboard-CDBM6wmF.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-D8VgkMJ8.js → Dropdown-8_55bXTD.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CQPe5S6q.js → EmailListRenderer-D3qOU1Cm.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-ySA-7SiN.js → FamilyGuardDashboard-Bv5oWMZF.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-1PMD-fBf.js → Federation-hpZ1xkKd.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C_jr6U4Q.js → FormItemContext-BN7GT191.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Bu3bKf2B.js → GenericCardRenderer-PCRhfA7M.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Dv4Fa_T5.js → Git-CIlP2vy7.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DOEy1n1k.js → Governance-81lgm7YL.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DkvqzWcJ.js → Inference-Cs24xMOV.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DdUJK949.js → KnowledgeGraph-CrJLVCZo.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CRelcdUZ.js → Logs-DLyO8RKT.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-0AHbbTA4.js → MarkdownRenderer-DBOqijrP.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BuuBKsev.js → Marketplace-Dz7AbQZc.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DlgpEmA1.js → McpTools-BM0aFiWv.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-DTfdqUGy.js → Memory-C9SJzENX.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CXLE5ZFO.js → MobileBridge-BO2HnNRk.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-Dz25OB0n.js → MobileProjects-CK_jMz0l.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-kEL0fhgD.js → Mtc-CfhrJsdO.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-BE4UvFJU.js → MtcAudit-BE2ELxgI.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-CSApSn8c.js → Multisig-CgR_E3Jd.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-D11FpKWx.js → NLProgramming-Cw9445_K.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CDL-D2CS.js → Notes-DZimkTrb.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C0Q6lO-c.js → NotificationSettings-DxHVh4uT.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-Iy-pnX56.js +1 -0
- package/src/assets/web-panel/assets/{Organization-Dc-maVAq.js → Organization-pPXJtWo4.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-B5mUpSvi.js → Overflow-YamdTJwA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-TjXV53wq.js → P2P-BvqP53x7.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-Blz1Xn2n.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-DKRzQ2O6.js → Permissions-CgFRQqFJ.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dx2eqft8.js → PersonalDataHub-CFeo3-6O.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-CnYZMB5p.js → Pipeline-BkaRojEK.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BonKpPnp.js → Privacy-BNZEx2Qp.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DWO0Du3J.js → ProjectInit-HUBOgEEK.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C9jqyYRv.js → ProjectSettings-CFMmhROS.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-CYpKwXbz.js → Projects-Dc517BXp.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CW-fkwJq.js → Providers-DTLf8bgj.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-Cw0J3Zud.js → QrScannerModal-BBNjPszk.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BH4A4IpR.js → QuickAsk-DCsHvk3G.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CDwMwJus.js → Recommend-Dpr5YSop.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DKIPwzTf.js → RemoteSession-DmKnvqGg.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CklDj7qS.js → Reputation-CSRfuurX.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DJWZKsH0.js → Row-CDj3pQ_w.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BfSLCewo.js → RssFeed-DI0a_kNq.js} +2 -2
- package/src/assets/web-panel/assets/{Search-B0IST--x.js → Search-jAy7B2nU.js} +1 -1
- package/src/assets/web-panel/assets/{Security-h1sYNWtt.js → Security-CkHTqYhc.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Gh0-oxI_.js → Services-CP-0Nzga.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CNDV_jOs.js → Skeleton-BkF5-p3n.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-ByCLTh1n.js → Skills-BDu-utDE.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BfyrJTJz.js → Sla-CQvq_2Qb.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BQzo9kNX.js → SpeechSettings-T7Ap512g.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B6fS6z_1.js → SyncSettings-Bt-PLvot.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-C-uKCP-l.js → Tasks-yLEcCogg.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-D6oDy7wL.js → Templates-ByN3fmbr.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-D3LT2wNQ.js → Tenant-DBI7hITH.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BteLcv_O.js → Terminal-B77_DlGX.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DHHuxfqH.js → TimelineRenderer-CsLTB_QO.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BRQeykXw.js → Tokens-BV2N6cR8.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-p2XaFWEu.js → Trigger-CjXWGH3p.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BxadBMft.js → Trust-uqXIq3WC.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DRBP7LDk.js → UkeySign-D-6qu4vY.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CZ3NZkh7.js → VideoEditing-CxGmx_2e.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-C3NTBN7G.js → Wallet-DSq7WlAg.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-C7LVgMlb.js → WebAuthn-CDBiD57O.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-0_Iveog7.js → WorkflowEditor-Z8f63TSb.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DzzluYo3.js → chat-B-UZFBjy.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DSMYXpaj.js → colors-CFk7K-7Q.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-W76u1VSV.js → compact-item-X0IVMvgi.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-otsESjb5.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-3sNLrvug.js → hasIn-CkVCwNNF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BdtmEKat.js → index-5OcZHdzc.js} +1 -1
- package/src/assets/web-panel/assets/{index-DS9WYDMf.js → index-B273IY-E.js} +1 -1
- package/src/assets/web-panel/assets/{index-NaO9AE-F.js → index-B3-B3FWD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcGJtmHM.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-pVm_iUre.js → index-BG-GaZri.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpcQYzP7.js → index-BPBia4b-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXLx9HfA.js → index-BipwEbTA.js} +1 -1
- package/src/assets/web-panel/assets/{index-uSCkbpZH.js → index-BoskgPt9.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMmdIpLA.js → index-Btqk6zWb.js} +1 -1
- package/src/assets/web-panel/assets/index-Bw4iuzRD.js +1 -0
- package/src/assets/web-panel/assets/{index-DLDh1OW3.js → index-BzXjYUmd.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9X_jqXe.js → index-C1ZAOJRW.js} +1 -1
- package/src/assets/web-panel/assets/{index-vBKPcmAP.js → index-CCLwoATF.js} +1 -1
- package/src/assets/web-panel/assets/{index-vUmYkqmu.js → index-CDfSD07S.js} +1 -1
- package/src/assets/web-panel/assets/{index-D7A5U2IH.js → index-CLlP1E4X.js} +1 -1
- package/src/assets/web-panel/assets/{index-ClOFeZ-t.js → index-CMCEXBRh.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cg86EzNp.js → index-CN8HZlLH.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ddd4mird.js → index-CNKFsBlz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CE7tSow-.js → index-CUBkvFPH.js} +1 -1
- package/src/assets/web-panel/assets/{index-GlIOE4ja.js → index-Ca0mij1n.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdU7IkKX.js → index-CbIAWbma.js} +1 -1
- package/src/assets/web-panel/assets/{index-UUHCwfNq.js → index-CbihMThv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRsD1PT2.js → index-CvOa-IFV.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2aht9-M.js → index-Cvwu_rA_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJsQio5O.js → index-D-EJNxgR.js} +1 -1
- package/src/assets/web-panel/assets/{index-q7ftLqNt.js → index-D8uEL8Uw.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1I1AYJs.js → index-D92g4l3n.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZKAzEge.js → index-DCqpl_E_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKoFyHYQ.js → index-DLSBks8h.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDlDO2px.js → index-Db_5xhrU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CENWwjef.js → index-DcDggpWR.js} +1 -1
- package/src/assets/web-panel/assets/{index-DH4cl52p.js → index-DeWKPQGs.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTA8Iavv.js → index-DhVHoqGL.js} +3 -3
- package/src/assets/web-panel/assets/{index-DmpaC2yk.js → index-DyWlOuak.js} +1 -1
- package/src/assets/web-panel/assets/{index-BA8mZxd-.js → index-LU54bChK.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtFOt5x1.js → index-OjnAwUJo.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5LOYdYm.js → index-P83LDnCz.js} +1 -1
- package/src/assets/web-panel/assets/{index-MhQQ3h8q.js → index-_-9k3Tna.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DF6llUXV.js → initDefaultProps-CFNgY8lJ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-vtDV8_5K.js → motion-D-5RrDzn.js} +1 -1
- package/src/assets/web-panel/assets/{move-B6c0bdy2.js → move-DN-L0Bd-.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DlLYWuBK.js → mtc-parser-6mgLSeCK.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BH0zOxLN.js → omit-ClWW8eTP.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D9di8_In.js → pickAttrs-Vystq3R8.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BDSngufM.js → placementArrow-D0H9hK4W.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C_EawMvP.js → responsiveObserve-B3iNRU-a.js} +1 -1
- package/src/assets/web-panel/assets/{slide-B_4wShxA.js → slide-CiMXe6am.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CbQu6qJ-.js → statusUtils-CGDZJ1uT.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CvXJWYXb.js → styleChecker-D2zA6NkY.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-jjY1XknO.js → useFlexGapSupport-C1sYU6QE.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CMXBdsK_.js → useFs-BzUHSjg2.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-YbAhhPXx.js → usePersonalDataHub-CxB5yZCq.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BFCTKc0H.js → vnode-BfUfMwtc.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CY9aBl5O.js → zoom-CFYmj1TO.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/agent.js +80 -2
- package/src/commands/evolution.js +33 -27
- package/src/commands/serve.js +11 -2
- package/src/data/changelog.json +5 -5
- 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/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-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-promotion-controller.js +109 -38
- 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-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/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-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/OrderTableRenderer-ZRE0OKbj.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-DvV2btIr.js +0 -7
- package/src/assets/web-panel/assets/devWarning-BztaXF84.js +0 -1
- package/src/assets/web-panel/assets/index-4jP_hG_4.js +0 -1
- package/src/assets/web-panel/assets/index-zxwuAXL5.js +0 -1
- package/src/lib/learning/learning-hooks.js +0 -123
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import structuredMemory from "@chainlesschain/session-core/structured-evolution-memory";
|
|
2
|
+
|
|
3
|
+
const { StructuredEvolutionMemory, createStructuredMemoryAuthorityReceipt } =
|
|
4
|
+
structuredMemory;
|
|
5
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
6
|
+
const REVIEWERS = new WeakSet();
|
|
7
|
+
|
|
8
|
+
function requiredString(value, name) {
|
|
9
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
10
|
+
throw new TypeError(`${name} is required`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function requiredDigest(value, name) {
|
|
16
|
+
if (!DIGEST.test(value || "")) {
|
|
17
|
+
throw new TypeError(`${name} must be sha256-bound`);
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function strings(value, name, { allowEmpty = true } = {}) {
|
|
23
|
+
if (
|
|
24
|
+
!Array.isArray(value) ||
|
|
25
|
+
(!allowEmpty && value.length === 0) ||
|
|
26
|
+
value.length > 256 ||
|
|
27
|
+
value.some((item) => typeof item !== "string" || item.trim() === "")
|
|
28
|
+
) {
|
|
29
|
+
throw new TypeError(`${name} must be a bounded string list`);
|
|
30
|
+
}
|
|
31
|
+
return [...new Set(value)].sort();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function capture(owner, name) {
|
|
35
|
+
if (typeof owner?.[name] !== "function") {
|
|
36
|
+
throw new TypeError(`${name} port is required`);
|
|
37
|
+
}
|
|
38
|
+
return (...args) => Reflect.apply(owner[name], owner, args);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function descriptor(input) {
|
|
42
|
+
if (!["critic", "evaluator"].includes(input?.kind)) {
|
|
43
|
+
throw new TypeError("semantic reviewer kind is invalid");
|
|
44
|
+
}
|
|
45
|
+
for (const field of ["issuerRevision", "verifierRevision"]) {
|
|
46
|
+
if (!Number.isSafeInteger(input[field]) || input[field] < 1) {
|
|
47
|
+
throw new TypeError(`${field} must be a positive integer`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const value = Object.freeze({
|
|
51
|
+
tenantId: requiredString(input.tenantId, "tenantId"),
|
|
52
|
+
kind: input.kind,
|
|
53
|
+
issuerId: requiredString(input.issuerId, "issuerId"),
|
|
54
|
+
issuerRevision: input.issuerRevision,
|
|
55
|
+
issuerHandlerDigest: requiredDigest(
|
|
56
|
+
input.issuerHandlerDigest,
|
|
57
|
+
"issuerHandlerDigest",
|
|
58
|
+
),
|
|
59
|
+
verifierId: requiredString(input.verifierId, "verifierId"),
|
|
60
|
+
verifierRevision: input.verifierRevision,
|
|
61
|
+
verifierHandlerDigest: requiredDigest(
|
|
62
|
+
input.verifierHandlerDigest,
|
|
63
|
+
"verifierHandlerDigest",
|
|
64
|
+
),
|
|
65
|
+
});
|
|
66
|
+
if (
|
|
67
|
+
value.issuerId === value.verifierId ||
|
|
68
|
+
value.issuerHandlerDigest === value.verifierHandlerDigest
|
|
69
|
+
) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"semantic review producer and verifier must be independent",
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeRequest(input, tenantId) {
|
|
78
|
+
return Object.freeze({
|
|
79
|
+
tenantId,
|
|
80
|
+
memoryId: requiredString(input?.memoryId, "memoryId"),
|
|
81
|
+
layer:
|
|
82
|
+
input?.layer === "semantic"
|
|
83
|
+
? "semantic"
|
|
84
|
+
: (() => {
|
|
85
|
+
throw new TypeError("semantic layer is required");
|
|
86
|
+
})(),
|
|
87
|
+
action:
|
|
88
|
+
input?.action === "accept"
|
|
89
|
+
? "accept"
|
|
90
|
+
: (() => {
|
|
91
|
+
throw new TypeError("semantic accept action is required");
|
|
92
|
+
})(),
|
|
93
|
+
contentDigest: requiredDigest(input?.contentDigest, "contentDigest"),
|
|
94
|
+
artifactRef: requiredString(input?.artifactRef, "artifactRef"),
|
|
95
|
+
evidenceRefs: strings(input?.evidenceRefs, "evidenceRefs", {
|
|
96
|
+
allowEmpty: false,
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function createStructuredMemorySemanticReviewer({
|
|
102
|
+
descriptor: input,
|
|
103
|
+
producer,
|
|
104
|
+
attestor,
|
|
105
|
+
verifier,
|
|
106
|
+
clock = () => new Date().toISOString(),
|
|
107
|
+
} = {}) {
|
|
108
|
+
const identity = descriptor(input);
|
|
109
|
+
const review = capture(producer, "review");
|
|
110
|
+
const attest = capture(attestor, "attest");
|
|
111
|
+
const verify = capture(verifier, "verify");
|
|
112
|
+
if (typeof clock !== "function")
|
|
113
|
+
throw new TypeError("clock must be a function");
|
|
114
|
+
const reviewer = Object.freeze({
|
|
115
|
+
identity,
|
|
116
|
+
async issue(requestInput) {
|
|
117
|
+
const request = normalizeRequest(requestInput, identity.tenantId);
|
|
118
|
+
const outcome = await review(request);
|
|
119
|
+
if (!outcome || !["accepted", "rejected"].includes(outcome.decision)) {
|
|
120
|
+
throw new Error(`${identity.kind} returned an invalid review decision`);
|
|
121
|
+
}
|
|
122
|
+
const reasonCodes = strings(outcome.reasonCodes || [], "reasonCodes");
|
|
123
|
+
if (outcome.decision !== "accepted") {
|
|
124
|
+
return Object.freeze({
|
|
125
|
+
accepted: false,
|
|
126
|
+
kind: identity.kind,
|
|
127
|
+
reasonCodes,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
const issuedAt = requiredString(clock(), "issuedAt");
|
|
131
|
+
if (!Number.isFinite(Date.parse(issuedAt))) {
|
|
132
|
+
throw new TypeError("issuedAt must be an ISO timestamp");
|
|
133
|
+
}
|
|
134
|
+
const receipt = createStructuredMemoryAuthorityReceipt({
|
|
135
|
+
...request,
|
|
136
|
+
kind: identity.kind,
|
|
137
|
+
decision: "accepted",
|
|
138
|
+
issuerId: identity.issuerId,
|
|
139
|
+
issuerRevision: identity.issuerRevision,
|
|
140
|
+
issuerHandlerDigest: identity.issuerHandlerDigest,
|
|
141
|
+
issuedAt,
|
|
142
|
+
});
|
|
143
|
+
const attestation = await attest({
|
|
144
|
+
purpose: `structured-memory-${identity.kind}-receipt`,
|
|
145
|
+
payloadDigest: receipt.receiptDigest,
|
|
146
|
+
receipt,
|
|
147
|
+
reasonCodes,
|
|
148
|
+
});
|
|
149
|
+
if (attestation === null || attestation === undefined) {
|
|
150
|
+
throw new Error(`${identity.kind} attestor returned no attestation`);
|
|
151
|
+
}
|
|
152
|
+
const signed = Object.freeze({ ...receipt, attestation });
|
|
153
|
+
if (
|
|
154
|
+
(await verify({
|
|
155
|
+
descriptor: identity,
|
|
156
|
+
request,
|
|
157
|
+
receipt: signed,
|
|
158
|
+
reasonCodes,
|
|
159
|
+
})) !== true
|
|
160
|
+
) {
|
|
161
|
+
throw new Error(`${identity.kind} receipt authentication failed`);
|
|
162
|
+
}
|
|
163
|
+
return Object.freeze({
|
|
164
|
+
accepted: true,
|
|
165
|
+
kind: identity.kind,
|
|
166
|
+
reasonCodes,
|
|
167
|
+
receipt: signed,
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
REVIEWERS.add(reviewer);
|
|
172
|
+
return reviewer;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function captureReviewer(value, kind, tenantId) {
|
|
176
|
+
if (
|
|
177
|
+
!REVIEWERS.has(value) ||
|
|
178
|
+
value.identity.kind !== kind ||
|
|
179
|
+
value.identity.tenantId !== tenantId
|
|
180
|
+
) {
|
|
181
|
+
throw new TypeError(`a branded tenant-scoped ${kind} reviewer is required`);
|
|
182
|
+
}
|
|
183
|
+
return Object.freeze({
|
|
184
|
+
identity: value.identity,
|
|
185
|
+
issue: capture(value, "issue"),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function createStructuredMemorySemanticReviewPipeline({
|
|
190
|
+
tenantId: tenantIdInput,
|
|
191
|
+
memory,
|
|
192
|
+
authorityStore,
|
|
193
|
+
critic,
|
|
194
|
+
evaluator,
|
|
195
|
+
proposerAuthority,
|
|
196
|
+
governorAuthority,
|
|
197
|
+
} = {}) {
|
|
198
|
+
const tenantId = requiredString(tenantIdInput, "tenantId");
|
|
199
|
+
if (
|
|
200
|
+
!(memory instanceof StructuredEvolutionMemory) ||
|
|
201
|
+
memory.tenantId !== tenantId
|
|
202
|
+
) {
|
|
203
|
+
throw new TypeError(
|
|
204
|
+
"a tenant-scoped StructuredEvolutionMemory is required",
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const retainReceipt = capture(authorityStore, "retainReceipt");
|
|
208
|
+
const reviewers = Object.freeze({
|
|
209
|
+
critic: captureReviewer(critic, "critic", tenantId),
|
|
210
|
+
evaluator: captureReviewer(evaluator, "evaluator", tenantId),
|
|
211
|
+
});
|
|
212
|
+
const authorityIds = [
|
|
213
|
+
reviewers.critic.identity.issuerId,
|
|
214
|
+
reviewers.critic.identity.verifierId,
|
|
215
|
+
reviewers.evaluator.identity.issuerId,
|
|
216
|
+
reviewers.evaluator.identity.verifierId,
|
|
217
|
+
];
|
|
218
|
+
const handlerDigests = [
|
|
219
|
+
reviewers.critic.identity.issuerHandlerDigest,
|
|
220
|
+
reviewers.critic.identity.verifierHandlerDigest,
|
|
221
|
+
reviewers.evaluator.identity.issuerHandlerDigest,
|
|
222
|
+
reviewers.evaluator.identity.verifierHandlerDigest,
|
|
223
|
+
];
|
|
224
|
+
if (
|
|
225
|
+
new Set(authorityIds).size !== authorityIds.length ||
|
|
226
|
+
new Set(handlerDigests).size !== handlerDigests.length
|
|
227
|
+
) {
|
|
228
|
+
throw new Error("critic and evaluator authorities must be independent");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return Object.freeze({
|
|
232
|
+
tenantId,
|
|
233
|
+
async propose(input = {}) {
|
|
234
|
+
return memory.append({
|
|
235
|
+
eventId: requiredString(input.eventId, "eventId"),
|
|
236
|
+
memoryId: requiredString(input.memoryId, "memoryId"),
|
|
237
|
+
layer: "semantic",
|
|
238
|
+
action: "propose",
|
|
239
|
+
authority: proposerAuthority,
|
|
240
|
+
automatic: input.automatic !== false,
|
|
241
|
+
contentDigest: requiredDigest(input.contentDigest, "contentDigest"),
|
|
242
|
+
artifactRef: requiredString(input.artifactRef, "artifactRef"),
|
|
243
|
+
evidenceRefs: strings(input.evidenceRefs, "evidenceRefs", {
|
|
244
|
+
allowEmpty: false,
|
|
245
|
+
}),
|
|
246
|
+
supersedes: strings(input.supersedes || [], "supersedes"),
|
|
247
|
+
timestamp: requiredString(input.timestamp, "timestamp"),
|
|
248
|
+
metadata: input.metadata || {},
|
|
249
|
+
});
|
|
250
|
+
},
|
|
251
|
+
async reviewAndAccept({
|
|
252
|
+
memoryId,
|
|
253
|
+
eventId,
|
|
254
|
+
timestamp,
|
|
255
|
+
metadata = {},
|
|
256
|
+
} = {}) {
|
|
257
|
+
const proposed =
|
|
258
|
+
memory.projection().memories[requiredString(memoryId, "memoryId")];
|
|
259
|
+
if (
|
|
260
|
+
!proposed ||
|
|
261
|
+
proposed.layer !== "semantic" ||
|
|
262
|
+
proposed.status !== "proposed"
|
|
263
|
+
) {
|
|
264
|
+
throw new Error("semantic memory is not awaiting review");
|
|
265
|
+
}
|
|
266
|
+
const request = {
|
|
267
|
+
tenantId,
|
|
268
|
+
memoryId: proposed.memoryId,
|
|
269
|
+
layer: "semantic",
|
|
270
|
+
action: "accept",
|
|
271
|
+
contentDigest: proposed.contentDigest,
|
|
272
|
+
artifactRef: proposed.artifactRef,
|
|
273
|
+
evidenceRefs: proposed.evidenceRefs,
|
|
274
|
+
};
|
|
275
|
+
const [criticResult, evaluatorResult] = await Promise.all([
|
|
276
|
+
reviewers.critic.issue(request),
|
|
277
|
+
reviewers.evaluator.issue(request),
|
|
278
|
+
]);
|
|
279
|
+
if (!criticResult.accepted || !evaluatorResult.accepted) {
|
|
280
|
+
throw new Error("semantic memory was rejected by independent review");
|
|
281
|
+
}
|
|
282
|
+
for (const result of [criticResult, evaluatorResult]) {
|
|
283
|
+
const retained = await retainReceipt(result.receipt);
|
|
284
|
+
if (
|
|
285
|
+
retained?.persisted !== true ||
|
|
286
|
+
retained.receiptDigest !== result.receipt.receiptDigest
|
|
287
|
+
) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
`${result.kind} receipt was not durably acknowledged`,
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return memory.append({
|
|
294
|
+
eventId: requiredString(eventId, "eventId"),
|
|
295
|
+
memoryId: proposed.memoryId,
|
|
296
|
+
layer: "semantic",
|
|
297
|
+
action: "accept",
|
|
298
|
+
authority: governorAuthority,
|
|
299
|
+
automatic: false,
|
|
300
|
+
contentDigest: proposed.contentDigest,
|
|
301
|
+
artifactRef: proposed.artifactRef,
|
|
302
|
+
evidenceRefs: proposed.evidenceRefs,
|
|
303
|
+
supersedes: proposed.supersedes,
|
|
304
|
+
receiptRefs: {
|
|
305
|
+
critic: criticResult.receipt.receiptDigest,
|
|
306
|
+
evaluator: evaluatorResult.receipt.receiptDigest,
|
|
307
|
+
},
|
|
308
|
+
timestamp: requiredString(timestamp, "timestamp"),
|
|
309
|
+
metadata,
|
|
310
|
+
});
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA,
|
|
3
|
+
isEvolutionLedgerArtifactResolver,
|
|
4
|
+
} from "./evolution-artifact-ports.js";
|
|
5
|
+
import {
|
|
6
|
+
EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA,
|
|
7
|
+
EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA,
|
|
8
|
+
} from "./evolution-ledger.js";
|
|
9
|
+
import {
|
|
10
|
+
WIKI_REVISION_SCHEMA,
|
|
11
|
+
WIKI_STATE_SCHEMA,
|
|
12
|
+
createEmptyWikiState,
|
|
13
|
+
digestWikiState,
|
|
14
|
+
} from "./evidence-backed-wiki-maintainer.js";
|
|
15
|
+
|
|
16
|
+
export const WIKI_LEDGER_EVENT_TYPE = "wiki.revision.committed";
|
|
17
|
+
export const WIKI_LEDGER_CONFLICT_CODE = "CC_EVOLUTION_WIKI_REVISION_CONFLICT";
|
|
18
|
+
export const WIKI_LEDGER_CORRUPT_CODE = "CC_EVOLUTION_WIKI_LEDGER_CORRUPT";
|
|
19
|
+
|
|
20
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
21
|
+
const REVISION_ID = /^wiki:[a-f0-9]{64}$/u;
|
|
22
|
+
|
|
23
|
+
function requiredString(value, name) {
|
|
24
|
+
if (typeof value !== "string" || value.trim() === "") throw new TypeError(`${name} is required`);
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function fail(code, message) {
|
|
29
|
+
const error = new Error(message);
|
|
30
|
+
error.code = code;
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function captureMethod(owner, name, label) {
|
|
35
|
+
const method = owner?.[name];
|
|
36
|
+
if (typeof method !== "function") throw new TypeError(`${label}.${name} is required`);
|
|
37
|
+
return (...args) => Reflect.apply(method, owner, args);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseRecord(resolution, descriptor) {
|
|
41
|
+
if (resolution?.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA || resolution.authenticated !== true ||
|
|
42
|
+
resolution.found !== true || !DIGEST.test(resolution.digest ?? "") ||
|
|
43
|
+
!DIGEST.test(resolution.receiptDigest ?? "") || !Buffer.isBuffer(resolution.bytes)) {
|
|
44
|
+
fail(WIKI_LEDGER_CORRUPT_CODE, "Wiki artifact resolution is not authenticated and complete");
|
|
45
|
+
}
|
|
46
|
+
let record;
|
|
47
|
+
try {
|
|
48
|
+
record = JSON.parse(resolution.bytes.toString("utf8"));
|
|
49
|
+
} catch {
|
|
50
|
+
fail(WIKI_LEDGER_CORRUPT_CODE, "Wiki artifact is not canonical JSON");
|
|
51
|
+
}
|
|
52
|
+
const revision = record?.value;
|
|
53
|
+
if (record?.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA || record.tenantId !== descriptor.artifactTenantId ||
|
|
54
|
+
record.audience !== descriptor.audience || record.purpose !== descriptor.purpose || record.retention !== "ledger" ||
|
|
55
|
+
record.type !== "wiki-revision" || revision?.schema !== WIKI_REVISION_SCHEMA ||
|
|
56
|
+
revision.tenantId !== descriptor.tenantId || revision.evolutionRunId !== descriptor.evolutionRunId ||
|
|
57
|
+
revision.state?.schema !== WIKI_STATE_SCHEMA || revision.state.tenantId !== descriptor.tenantId ||
|
|
58
|
+
revision.stateDigest !== digestWikiState(revision.state) || revision.state.revisionId !== revision.revisionId ||
|
|
59
|
+
!REVISION_ID.test(revision.revisionId ?? "")) {
|
|
60
|
+
fail(WIKI_LEDGER_CORRUPT_CODE, "Wiki artifact record or revision binding is invalid");
|
|
61
|
+
}
|
|
62
|
+
return revision;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function normalizeDescriptor(input) {
|
|
66
|
+
return Object.freeze({
|
|
67
|
+
tenantId: requiredString(input?.tenantId, "tenantId"),
|
|
68
|
+
artifactTenantId: requiredString(input?.artifactTenantId, "artifactTenantId"),
|
|
69
|
+
evolutionRunId: requiredString(input?.evolutionRunId, "evolutionRunId"),
|
|
70
|
+
audience: requiredString(input?.audience, "audience"),
|
|
71
|
+
purpose: requiredString(input?.purpose, "purpose"),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class WikiMaintainerLedgerAdapter {
|
|
76
|
+
constructor({ descriptor, artifactPorts, ledger, ledgerArtifactResolver } = {}) {
|
|
77
|
+
this.descriptor = normalizeDescriptor(descriptor);
|
|
78
|
+
this._putCanonical = captureMethod(artifactPorts, "putCanonical", "artifactPorts");
|
|
79
|
+
this._readLedger = captureMethod(ledger, "read", "ledger");
|
|
80
|
+
this._verifyLedger = captureMethod(ledger, "verify", "ledger");
|
|
81
|
+
this._appendDomainEvent = captureMethod(ledger, "appendDomainEvent", "ledger");
|
|
82
|
+
if (!isEvolutionLedgerArtifactResolver(ledgerArtifactResolver)) {
|
|
83
|
+
throw new TypeError("a branded EvolutionArtifactPorts ledger resolver is required");
|
|
84
|
+
}
|
|
85
|
+
this._resolveArtifact = ledgerArtifactResolver;
|
|
86
|
+
Object.freeze(this);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
_matchingEvents() {
|
|
90
|
+
const events = this._readLedger();
|
|
91
|
+
if (!Array.isArray(events)) fail(WIKI_LEDGER_CORRUPT_CODE, "EvolutionLedger read did not return events");
|
|
92
|
+
return events.filter((event) => event.schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA &&
|
|
93
|
+
event.type === WIKI_LEDGER_EVENT_TYPE && event.tenantId === this.descriptor.tenantId &&
|
|
94
|
+
event.correlationId === this.descriptor.evolutionRunId);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
_resolveEvent(event) {
|
|
98
|
+
const authority = this._verifyLedger();
|
|
99
|
+
const resolution = this._resolveArtifact({
|
|
100
|
+
epoch: authority.epoch,
|
|
101
|
+
ledgerId: authority.ledgerId,
|
|
102
|
+
ref: event.subjectRef,
|
|
103
|
+
tenantId: this.descriptor.artifactTenantId,
|
|
104
|
+
});
|
|
105
|
+
if (resolution?.ref !== event.subjectRef.ref || resolution.digest !== event.subjectRef.digest) {
|
|
106
|
+
fail(WIKI_LEDGER_CORRUPT_CODE, "Wiki ledger event resolved a substituted artifact");
|
|
107
|
+
}
|
|
108
|
+
return parseRecord(resolution, this.descriptor);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
_latest() {
|
|
112
|
+
const matches = this._matchingEvents();
|
|
113
|
+
if (matches.length === 0) return null;
|
|
114
|
+
for (let index = 1; index < matches.length; index += 1) {
|
|
115
|
+
if (matches[index].sequence <= matches[index - 1].sequence) {
|
|
116
|
+
fail(WIKI_LEDGER_CORRUPT_CODE, "Wiki ledger revisions are not strictly ordered");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const event = matches.at(-1);
|
|
120
|
+
const revision = this._resolveEvent(event);
|
|
121
|
+
if (revision.revision !== matches.length) {
|
|
122
|
+
fail(WIKI_LEDGER_CORRUPT_CODE, "Wiki revision sequence has a gap or duplicate");
|
|
123
|
+
}
|
|
124
|
+
return { event, revision };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
loadWiki = () => {
|
|
128
|
+
const latest = this._latest();
|
|
129
|
+
const state = latest?.revision.state ?? createEmptyWikiState(this.descriptor.tenantId);
|
|
130
|
+
return Object.freeze({ trusted: true, state, stateDigest: digestWikiState(state) });
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
commitRevision = ({ expectedStateDigest, revision } = {}) => {
|
|
134
|
+
if (!DIGEST.test(expectedStateDigest ?? "") || revision?.schema !== WIKI_REVISION_SCHEMA ||
|
|
135
|
+
revision.tenantId !== this.descriptor.tenantId || revision.evolutionRunId !== this.descriptor.evolutionRunId ||
|
|
136
|
+
revision.stateDigest !== digestWikiState(revision.state) || revision.state.revisionId !== revision.revisionId ||
|
|
137
|
+
!REVISION_ID.test(revision.revisionId ?? "")) {
|
|
138
|
+
throw new TypeError("Wiki revision commit request is invalid");
|
|
139
|
+
}
|
|
140
|
+
const latest = this._latest();
|
|
141
|
+
const currentState = latest?.revision.state ?? createEmptyWikiState(this.descriptor.tenantId);
|
|
142
|
+
const currentDigest = digestWikiState(currentState);
|
|
143
|
+
if (currentDigest !== expectedStateDigest) {
|
|
144
|
+
if (latest?.revision.revisionId === revision.revisionId && currentDigest === revision.stateDigest) {
|
|
145
|
+
return Object.freeze({ committed: true, recovered: true, revisionId: revision.revisionId,
|
|
146
|
+
stateDigest: revision.stateDigest, evolutionRunId: this.descriptor.evolutionRunId });
|
|
147
|
+
}
|
|
148
|
+
fail(WIKI_LEDGER_CONFLICT_CODE, "Wiki state changed before revision commit");
|
|
149
|
+
}
|
|
150
|
+
if (revision.revision !== currentState.revision + 1 || revision.priorStateDigest !== currentDigest) {
|
|
151
|
+
fail(WIKI_LEDGER_CONFLICT_CODE, "Wiki revision does not extend the current state");
|
|
152
|
+
}
|
|
153
|
+
const head = this._verifyLedger();
|
|
154
|
+
const published = this._putCanonical("wiki-revision", revision, {
|
|
155
|
+
audience: this.descriptor.audience,
|
|
156
|
+
purpose: this.descriptor.purpose,
|
|
157
|
+
retention: "ledger",
|
|
158
|
+
});
|
|
159
|
+
if (!published?.ref || published.receipt?.persisted !== true || published.receipt?.readbackVerified !== true ||
|
|
160
|
+
published.receipt?.integrityVerified !== true || published.receipt?.retention !== "ledger") {
|
|
161
|
+
fail(WIKI_LEDGER_CORRUPT_CODE, "Wiki revision artifact was not durably read back");
|
|
162
|
+
}
|
|
163
|
+
const eventId = `wiki.revision.${revision.revisionId.slice("wiki:".length)}`;
|
|
164
|
+
const receipt = this._appendDomainEvent({
|
|
165
|
+
artifactTenantId: this.descriptor.artifactTenantId,
|
|
166
|
+
correlationId: this.descriptor.evolutionRunId,
|
|
167
|
+
decision: "committed",
|
|
168
|
+
eventId,
|
|
169
|
+
reason: `wiki revision ${revision.revision} committed`,
|
|
170
|
+
skillName: null,
|
|
171
|
+
sourceRefs: latest ? [latest.event.subjectRef] : [],
|
|
172
|
+
subjectRef: published.ref,
|
|
173
|
+
tenantId: this.descriptor.tenantId,
|
|
174
|
+
timestamp: revision.effectiveAt,
|
|
175
|
+
type: WIKI_LEDGER_EVENT_TYPE,
|
|
176
|
+
}, { expectedHeadDigest: head.headDigest, expectedSequence: head.sequence });
|
|
177
|
+
if (receipt?.authenticated !== true || receipt?.committed !== true || receipt?.durable !== true ||
|
|
178
|
+
receipt.eventId !== eventId || !DIGEST.test(receipt.receiptDigest ?? "")) {
|
|
179
|
+
fail(WIKI_LEDGER_CORRUPT_CODE, "Wiki ledger did not confirm a durable authenticated append");
|
|
180
|
+
}
|
|
181
|
+
const stored = this._latest();
|
|
182
|
+
if (stored?.revision.revisionId !== revision.revisionId || stored.revision.stateDigest !== revision.stateDigest) {
|
|
183
|
+
fail(WIKI_LEDGER_CORRUPT_CODE, "Wiki revision readback differs after ledger commit");
|
|
184
|
+
}
|
|
185
|
+
return Object.freeze({ committed: true, recovered: false, revisionId: revision.revisionId,
|
|
186
|
+
stateDigest: revision.stateDigest, evolutionRunId: this.descriptor.evolutionRunId,
|
|
187
|
+
ledgerReceiptDigest: receipt.receiptDigest });
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
maintainerPorts({ resolveEvidence, derive } = {}) {
|
|
191
|
+
if (typeof resolveEvidence !== "function" || typeof derive !== "function") {
|
|
192
|
+
throw new TypeError("resolveEvidence and derive ports are required");
|
|
193
|
+
}
|
|
194
|
+
return Object.freeze({ loadWiki: this.loadWiki, commitRevision: this.commitRevision, resolveEvidence, derive });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function createWikiMaintainerLedgerAdapter(options) {
|
|
199
|
+
return new WikiMaintainerLedgerAdapter(options);
|
|
200
|
+
}
|