chainlesschain 0.166.16 → 0.166.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -10
- package/completions/_cc +1 -1
- package/completions/cc.bash +1 -1
- package/completions/cc.fish +1 -1
- package/completions/cc.ps1 +1 -1
- package/package.json +2 -2
- package/src/assets/web-panel/assets/{AIOps-CTxa2Wbq.js → AIOps-CQSBl3hY.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DHrQyYhy.js → ActionButton-IQt-vRLt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DQiUqcNl.js → Analytics-DkHC8O7k.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-_Iz4r6wt.js → AppLayout-CSti2P2T.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BNs63YHo.js → Artifacts-C57hxn3v.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BgHIqRLn.js → Audit-DByLntcf.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DUQ5axBB.js → BackgroundAgents-D1Bd2EOa.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CJ51E9Vw.js → Backup-VstHzHng.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-o-5toA_R.js → BaseInput-CLhdexIf.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Cu7a-x1L.js → Chat-BefC_JZS.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BUyYkAXr.js → ChatBubbleRenderer-DbXMvgmn.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DwdmZ71L.js → Checkbox-CEY982vx.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BrH2eYiP.js → Codegen-Dcm_DTvJ.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BVJQEP2j.js → Col-C8SSu4KA.js} +1 -1
- package/src/assets/web-panel/assets/{Community-FGO3vZPq.js → Community-BVV9vwRR.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DYGAFVnT.js → Compact-B7c5Vo18.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CRvc8Bdt.js → Compliance-Tq9jSZ5a.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-D6Ah2m7A.js → Cowork-BJUjT09Q.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-AXbU2ifR.js → Cron-CjV9PEaS.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-C5DXgEFd.js → Crosschain-Dl7ReL8F.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Bxh9QV9v.js → DID-CG8PjWmM.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-B-ZHEta-.js → Dashboard-CDBM6wmF.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-66Ik1jqa.js → Dropdown-8_55bXTD.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CCWk1BIM.js → EmailListRenderer-D3qOU1Cm.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard--9faIULK.js → FamilyGuardDashboard-Bv5oWMZF.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CejW9CgX.js → Federation-hpZ1xkKd.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-acHgcXki.js → FormItemContext-BN7GT191.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-D2ghd_K4.js → GenericCardRenderer-PCRhfA7M.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BOe1YJrV.js → Git-CIlP2vy7.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BjgzJsof.js → Governance-81lgm7YL.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CXCD5rnP.js → Inference-Cs24xMOV.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CHA8QgJT.js → KnowledgeGraph-CrJLVCZo.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DORPMcL6.js → Logs-DLyO8RKT.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-CPPHz01z.js → MarkdownRenderer-DBOqijrP.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BCFlo1Yx.js → Marketplace-Dz7AbQZc.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Dl_uZAKh.js → McpTools-BM0aFiWv.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-DupHamwz.js → Memory-C9SJzENX.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BRSMPJxW.js → MobileBridge-BO2HnNRk.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BHpuzsDl.js → MobileProjects-CK_jMz0l.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Ch0jOQfZ.js → Mtc-CfhrJsdO.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-qzwQrDi7.js → MtcAudit-BE2ELxgI.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-B5wHzUcm.js → Multisig-CgR_E3Jd.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Hy9uAiA0.js → NLProgramming-Cw9445_K.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DkuchMC7.js → Notes-DZimkTrb.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CYZhD7Pi.js → NotificationSettings-DxHVh4uT.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DKOoqajt.js → OrderTableRenderer-Iy-pnX56.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-D9AXn6j8.js → Organization-pPXJtWo4.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Bg15J6hX.js → Overflow-YamdTJwA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P--B54HoQn.js → P2P-BvqP53x7.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Cz9Kk2Nb.js → PdhVaultBrowser-Blz1Xn2n.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DHx38eS1.js → Permissions-CgFRQqFJ.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dw8i2hAs.js → PersonalDataHub-CFeo3-6O.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-B2o6Ur2G.js → Pipeline-BkaRojEK.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-8g-h2fnu.js → Privacy-BNZEx2Qp.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DuVTcINx.js → ProjectInit-HUBOgEEK.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C8njvdZX.js → ProjectSettings-CFMmhROS.js} +2 -2
- package/src/assets/web-panel/assets/Projects-Dc517BXp.js +1 -0
- package/src/assets/web-panel/assets/{Providers-wFqgod_W.js → Providers-DTLf8bgj.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-9NCXrpmA.js → QrScannerModal-BBNjPszk.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-WUTNvFkY.js → QuickAsk-DCsHvk3G.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-xT4nYmju.js → Recommend-Dpr5YSop.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-fak1PBGC.js → RemoteSession-DmKnvqGg.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-D1AkQvD-.js → Reputation-CSRfuurX.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DVfm6xGt.js → Row-CDj3pQ_w.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BDunoLSE.js → RssFeed-DI0a_kNq.js} +2 -2
- package/src/assets/web-panel/assets/{Search-OPycRoU3.js → Search-jAy7B2nU.js} +1 -1
- package/src/assets/web-panel/assets/{Security-C8-Fm3Dt.js → Security-CkHTqYhc.js} +4 -4
- package/src/assets/web-panel/assets/{Services-qM_sMpMl.js → Services-CP-0Nzga.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-D3HBJjVF.js → Skeleton-BkF5-p3n.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-jr67X-tP.js → Skills-BDu-utDE.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CS3ggyCg.js → Sla-CQvq_2Qb.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CTOCYxOi.js → SpeechSettings-T7Ap512g.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D3s3cLT5.js → SyncSettings-Bt-PLvot.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CdfoJ25m.js → Tasks-yLEcCogg.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Drb4AI8X.js → Templates-ByN3fmbr.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DzwR3Qkz.js → Tenant-DBI7hITH.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-D-ESq7xp.js → Terminal-B77_DlGX.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BK-AUuws.js → TimelineRenderer-CsLTB_QO.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-xGTouuNp.js → Tokens-BV2N6cR8.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BvZj-v2H.js → Trigger-CjXWGH3p.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BLC9CGmc.js → Trust-uqXIq3WC.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BSuCXpmE.js → UkeySign-D-6qu4vY.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DT3A-_Ql.js → VideoEditing-CxGmx_2e.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-c1OXao85.js → Wallet-DSq7WlAg.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CuA-F7KJ.js → WebAuthn-CDBiD57O.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-MANZrf82.js → WorkflowEditor-Z8f63TSb.js} +1 -1
- package/src/assets/web-panel/assets/{chat-D4Y4Ey0O.js → chat-B-UZFBjy.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DDH06hKO.js → colors-CFk7K-7Q.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Cym756Xj.js → compact-item-X0IVMvgi.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-By4Nb1k-.js → createContext-DWg4j2n6.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-B2IW1J_J.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Ch7OY258.js → hasIn-CkVCwNNF.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9le0-dc.js → index-5OcZHdzc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CykROEJA.js → index-B273IY-E.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjwgJ5Jd.js → index-B3-B3FWD.js} +1 -1
- package/src/assets/web-panel/assets/{index-CjNUTBwe.js → index-BB4KHHcv.js} +1 -1
- package/src/assets/web-panel/assets/index-BCp2ir-1.js +1 -0
- package/src/assets/web-panel/assets/{index-BFydNU6a.js → index-BG-GaZri.js} +1 -1
- package/src/assets/web-panel/assets/{index-mL3nOcH6.js → index-BPBia4b-.js} +1 -1
- package/src/assets/web-panel/assets/{index-XGeQMvWK.js → index-BipwEbTA.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5sADPGY.js → index-BoskgPt9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CV8qXTvb.js → index-Btqk6zWb.js} +1 -1
- package/src/assets/web-panel/assets/index-Bw4iuzRD.js +1 -0
- package/src/assets/web-panel/assets/{index-CJPks33Y.js → index-BzXjYUmd.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBBUn5v7.js → index-C1ZAOJRW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DiewptTL.js → index-CCLwoATF.js} +1 -1
- package/src/assets/web-panel/assets/{index-OU8DZ9BL.js → index-CDfSD07S.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvnywwUj.js → index-CLlP1E4X.js} +1 -1
- package/src/assets/web-panel/assets/{index-CT-3FYwd.js → index-CMCEXBRh.js} +1 -1
- package/src/assets/web-panel/assets/{index-tZ5BUdad.js → index-CN8HZlLH.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNlxihs8.js → index-CNKFsBlz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CF3Lhyns.js → index-CUBkvFPH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CV1UC4zo.js → index-Ca0mij1n.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAkk1M7x.js → index-CbIAWbma.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNEJzc0r.js → index-CbihMThv.js} +1 -1
- package/src/assets/web-panel/assets/{index-q-s04OKn.js → index-CvOa-IFV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZB9q-Lp.js → index-Cvwu_rA_.js} +1 -1
- package/src/assets/web-panel/assets/{index-oBsfUbyH.js → index-D-EJNxgR.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZY2Qs_Sn.js → index-D8uEL8Uw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtGValNi.js → index-D92g4l3n.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpudmDhf.js → index-DCqpl_E_.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bo20BfdS.js → index-DLSBks8h.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUSUzaB8.js → index-Db_5xhrU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZn6dmZe.js → index-DcDggpWR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGsvEbbq.js → index-DeWKPQGs.js} +1 -1
- package/src/assets/web-panel/assets/{index-jx7kr6mB.js → index-DhVHoqGL.js} +3 -3
- package/src/assets/web-panel/assets/{index-BIrRX-ra.js → index-DyWlOuak.js} +1 -1
- package/src/assets/web-panel/assets/{index-6CpOV3Pn.js → index-LU54bChK.js} +1 -1
- package/src/assets/web-panel/assets/{index-6-2eYBXr.js → index-OjnAwUJo.js} +1 -1
- package/src/assets/web-panel/assets/{index-B51otVoZ.js → index-P83LDnCz.js} +1 -1
- package/src/assets/web-panel/assets/{index-ArMrnx0v.js → index-_-9k3Tna.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DGcX9iPP.js → initDefaultProps-CFNgY8lJ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-2SeL1D8A.js → motion-D-5RrDzn.js} +1 -1
- package/src/assets/web-panel/assets/{move-3cYbQCpS.js → move-DN-L0Bd-.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-IDdxfw0z.js → mtc-parser-6mgLSeCK.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BNswdKO6.js → omit-ClWW8eTP.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BwCnem2I.js → pickAttrs-Vystq3R8.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B9PtZ5rq.js → placementArrow-D0H9hK4W.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CPB6iSzV.js → responsiveObserve-B3iNRU-a.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DZgyblZa.js → slide-CiMXe6am.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-D82IBdsx.js → statusUtils-CGDZJ1uT.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-qSHtosM-.js → styleChecker-D2zA6NkY.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CmWO7h0I.js → useFlexGapSupport-C1sYU6QE.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-yLrs_ub4.js → useFs-BzUHSjg2.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CJ1VW6rh.js → usePersonalDataHub-CxB5yZCq.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CzY_moQ4.js → vnode-BfUfMwtc.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DAa7rEY5.js → zoom-CFYmj1TO.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/command-manifest.json +2 -2
- package/src/commands/agent.js +80 -2
- package/src/commands/evolution.js +61 -42
- package/src/commands/learning.js +24 -8
- package/src/commands/serve.js +11 -2
- package/src/data/changelog.json +13 -0
- package/src/gateways/ws/session-protocol.js +1 -0
- package/src/gateways/ws/ws-agent-handler.js +70 -2
- package/src/gateways/ws/ws-server.js +8 -0
- package/src/lib/app-server/cli-agent-kernel-adapter.js +4 -0
- package/src/lib/app-server/graph-runtime.js +115 -8
- package/src/lib/app-server/server.js +21 -2
- package/src/lib/app-server/websocket-transport.js +2 -0
- package/src/lib/evolution/agent-evolution-ingress.js +300 -0
- package/src/lib/evolution/agent-evolution-runtime-composition-brand.js +22 -0
- package/src/lib/evolution/agent-evolution-runtime-composition.js +369 -0
- package/src/lib/evolution/bounded-skill-improvement-pilot.js +498 -0
- package/src/lib/evolution/evidence-backed-wiki-maintainer.js +752 -0
- package/src/lib/evolution/evolution-artifact-ports.js +48 -0
- package/src/lib/evolution/evolution-eval-gate.js +11 -4
- package/src/lib/evolution/evolution-evidence-artifact-adapter.js +499 -0
- package/src/lib/evolution/evolution-evidence-projector.js +61 -1
- package/src/lib/evolution/evolution-file-witness.js +601 -0
- package/src/lib/evolution/evolution-ledger-file-backend.js +176 -0
- package/src/lib/evolution/evolution-ledger-ports.js +353 -16
- package/src/lib/evolution/evolution-ledger.js +1355 -54
- package/src/lib/evolution/evolution-run-ledger-adapter.js +409 -0
- package/src/lib/evolution/evolution-run-runtime-adapter.js +5 -0
- package/src/lib/evolution/evolution-run-wiki-maintenance-source.js +300 -0
- package/src/lib/evolution/scheduled-batch-wiki-maintenance-source.js +368 -0
- package/src/lib/evolution/skill-candidate-registry.js +649 -0
- package/src/lib/evolution/skill-evaluated-promotion-durability.js +899 -0
- package/src/lib/evolution/skill-evaluated-promotion.js +481 -0
- package/src/lib/evolution/skill-promotion-controller.js +220 -37
- package/src/lib/evolution/skill-promotion-review-ledger-adapter.js +451 -0
- package/src/lib/evolution/skill-promotion-review.js +793 -0
- package/src/lib/evolution/skill-registry-transition-capability.js +55 -0
- package/src/lib/evolution/skill-registry-transition-ledger-adapter.js +1280 -0
- package/src/lib/evolution/skill-registry-transition-source.js +372 -0
- package/src/lib/evolution/skill-release-registry.js +2191 -11
- package/src/lib/evolution/skill-target-matrix-eval.js +133 -10
- package/src/lib/evolution/structured-memory-agent-control-plane.js +222 -0
- package/src/lib/evolution/structured-memory-authority-ledger-adapter.js +351 -0
- package/src/lib/evolution/structured-memory-ledger-adapter.js +220 -0
- package/src/lib/evolution/structured-memory-policy-receipt-writer.js +164 -0
- package/src/lib/evolution/structured-memory-post-compact-hook.js +87 -0
- package/src/lib/evolution/structured-memory-promotion-receipt-writer.js +221 -0
- package/src/lib/evolution/structured-memory-semantic-review-pipeline.js +313 -0
- package/src/lib/evolution/wiki-informed-skill-proposer.js +343 -0
- package/src/lib/evolution/wiki-maintainer-ledger-adapter.js +200 -0
- package/src/lib/evolution/wiki-maintenance-trigger-ledger-adapter.js +687 -0
- package/src/lib/evolution-system.js +65 -30
- package/src/lib/graph-kernel/evolution-run-runtime-adapter.js +5 -0
- package/src/lib/hooks-v2-runtime.js +20 -0
- package/src/lib/mcp-lifecycle-authority.js +21 -2
- package/src/lib/remote-approval-bridge.js +24 -0
- package/src/lib/scheduler-kernel/store.js +62 -14
- package/src/repl/agent-repl.js +45 -0
- package/src/runtime/agent-core.js +68 -0
- package/src/runtime/agent-runtime.js +114 -1
- package/src/runtime/headless-runner.js +19 -0
- package/src/runtime/headless-stream.js +43 -0
- package/src/runtime/runtime-factory.js +19 -1
- package/src/assets/web-panel/assets/Projects-Ca9BAfu7.js +0 -1
- package/src/assets/web-panel/assets/devWarning-KO3pAGeO.js +0 -1
- package/src/assets/web-panel/assets/index-Bh4Rgkg2.js +0 -1
- package/src/assets/web-panel/assets/index-Dox_coTY.js +0 -1
- package/src/lib/learning/learning-hooks.js +0 -123
|
@@ -416,12 +416,28 @@ const RULESET = Object.freeze({
|
|
|
416
416
|
"disable safety",
|
|
417
417
|
"bypass policy",
|
|
418
418
|
"you are now",
|
|
419
|
+
"override developer instructions",
|
|
420
|
+
"override system instructions",
|
|
421
|
+
"treat tool output as trusted",
|
|
422
|
+
"execute commands without approval",
|
|
423
|
+
"send credentials to",
|
|
424
|
+
"grant yourself permission",
|
|
425
|
+
"turn off guardrails",
|
|
426
|
+
"mark this task successful",
|
|
427
|
+
"fabricate test results",
|
|
419
428
|
"忽略之前的指令",
|
|
420
429
|
"忽略之前的所有指令",
|
|
421
430
|
"忽略以前的所有指令",
|
|
422
431
|
"显示系统提示",
|
|
423
432
|
"泄露秘密",
|
|
424
433
|
"绕过安全策略",
|
|
434
|
+
"覆盖开发者指令",
|
|
435
|
+
"覆盖系统指令",
|
|
436
|
+
"未经批准执行命令",
|
|
437
|
+
"发送凭据到",
|
|
438
|
+
"授予自己权限",
|
|
439
|
+
"关闭安全护栏",
|
|
440
|
+
"伪造测试结果",
|
|
425
441
|
]),
|
|
426
442
|
});
|
|
427
443
|
|
|
@@ -687,6 +703,39 @@ function assertNoKnownSecrets(content) {
|
|
|
687
703
|
}
|
|
688
704
|
}
|
|
689
705
|
|
|
706
|
+
/**
|
|
707
|
+
* Shared plaintext persistence guard for downstream Wiki/Skill artifacts.
|
|
708
|
+
* Callers receive the same Unicode-aware secret/PII policy used by the
|
|
709
|
+
* model-visible projection boundary instead of maintaining a weaker regex set.
|
|
710
|
+
*/
|
|
711
|
+
export function assertEvolutionContentContainsNoKnownSecrets(content) {
|
|
712
|
+
if (typeof content !== "string") {
|
|
713
|
+
throw projectionError(
|
|
714
|
+
EVOLUTION_PROJECTION_INVALID_CODE,
|
|
715
|
+
"plaintext persistence content must be a string",
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
assertNoKnownSecrets(content);
|
|
719
|
+
const skeleton = confusableSkeleton(content);
|
|
720
|
+
if (skeleton !== content) assertNoKnownSecrets(skeleton);
|
|
721
|
+
return true;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/** Return stable rule ids for prompt-injection text without exposing rules. */
|
|
725
|
+
export function inspectEvolutionContentInjectionRisks(content) {
|
|
726
|
+
if (typeof content !== "string") {
|
|
727
|
+
throw projectionError(
|
|
728
|
+
EVOLUTION_PROJECTION_INVALID_CODE,
|
|
729
|
+
"prompt-injection inspection content must be a string",
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
return Object.freeze(
|
|
733
|
+
injectionMatches(content)
|
|
734
|
+
.map(({ id }) => id)
|
|
735
|
+
.sort(),
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
|
|
690
739
|
function normalizeId(value, label, maximum = 256) {
|
|
691
740
|
if (
|
|
692
741
|
typeof value !== "string" ||
|
|
@@ -908,9 +957,14 @@ function normalizedInjectionText(value) {
|
|
|
908
957
|
.trim();
|
|
909
958
|
}
|
|
910
959
|
|
|
960
|
+
function compactInjectionText(value) {
|
|
961
|
+
return normalizedInjectionText(value).replace(/[\p{P}\p{S}\s]+/gu, "");
|
|
962
|
+
}
|
|
963
|
+
|
|
911
964
|
const NORMALIZED_INJECTION_PATTERNS = Object.freeze(
|
|
912
965
|
RULESET.injectionPatterns.map((pattern) => ({
|
|
913
966
|
text: normalizedInjectionText(pattern),
|
|
967
|
+
compact: compactInjectionText(pattern),
|
|
914
968
|
id: digest(pattern, "chainlesschain.evolution-injection-pattern/v2"),
|
|
915
969
|
})),
|
|
916
970
|
);
|
|
@@ -938,8 +992,14 @@ const ALLOWED_INJECTION_PATTERN_IDS = new Set([
|
|
|
938
992
|
function injectionMatches(value) {
|
|
939
993
|
const normalized = normalizedInjectionText(value);
|
|
940
994
|
const skeleton = normalizedInjectionText(confusableSkeleton(value));
|
|
995
|
+
const compact = compactInjectionText(value);
|
|
996
|
+
const compactSkeleton = compactInjectionText(confusableSkeleton(value));
|
|
941
997
|
const matches = NORMALIZED_INJECTION_PATTERNS.filter(
|
|
942
|
-
({ text }) =>
|
|
998
|
+
({ text, compact: patternCompact }) =>
|
|
999
|
+
normalized.includes(text) ||
|
|
1000
|
+
skeleton.includes(text) ||
|
|
1001
|
+
compact.includes(patternCompact) ||
|
|
1002
|
+
compactSkeleton.includes(patternCompact),
|
|
943
1003
|
);
|
|
944
1004
|
DEFAULT_IGNORABLE_PATTERN.lastIndex = 0;
|
|
945
1005
|
if (DEFAULT_IGNORABLE_PATTERN.test(value) || hasSuspiciousControl(value)) {
|
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
|
|
5
|
+
import { withFileLock } from "../with-file-lock.js";
|
|
6
|
+
import {
|
|
7
|
+
EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA,
|
|
8
|
+
EVOLUTION_LEDGER_WITNESS_SCHEMA,
|
|
9
|
+
} from "./evolution-ledger.js";
|
|
10
|
+
|
|
11
|
+
export const EVOLUTION_FILE_WITNESS_STORE_SCHEMA =
|
|
12
|
+
"chainlesschain.evolution-file-witness-store/v1";
|
|
13
|
+
|
|
14
|
+
const WITNESS_DOMAIN = `${EVOLUTION_LEDGER_WITNESS_SCHEMA}\0`;
|
|
15
|
+
const ANCESTRY_DOMAIN = `${EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA}\0`;
|
|
16
|
+
const DISCARD_DOMAIN =
|
|
17
|
+
"chainlesschain.evolution-witness-discard-accumulator/v1\0";
|
|
18
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
19
|
+
const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,159}$/u;
|
|
20
|
+
const DEFAULT_MAXIMUM_BYTES = 64 * 1024 * 1024;
|
|
21
|
+
const STORE_KEYS = new Set([
|
|
22
|
+
"current",
|
|
23
|
+
"discardedAnchors",
|
|
24
|
+
"history",
|
|
25
|
+
"schema",
|
|
26
|
+
]);
|
|
27
|
+
const RECORD_KEYS = new Set([
|
|
28
|
+
"algorithm",
|
|
29
|
+
"anchorDigest",
|
|
30
|
+
"authenticated",
|
|
31
|
+
"discardAccumulatorDigest",
|
|
32
|
+
"durable",
|
|
33
|
+
"epoch",
|
|
34
|
+
"generation",
|
|
35
|
+
"headDigest",
|
|
36
|
+
"identityDigest",
|
|
37
|
+
"keyId",
|
|
38
|
+
"ledgerId",
|
|
39
|
+
"payloadDigest",
|
|
40
|
+
"previousWitnessDigest",
|
|
41
|
+
"schema",
|
|
42
|
+
"segmentDigest",
|
|
43
|
+
"sequence",
|
|
44
|
+
"signature",
|
|
45
|
+
"status",
|
|
46
|
+
"storeMarkerDigest",
|
|
47
|
+
"storeMarkerEntryDigest",
|
|
48
|
+
"storeMarkerId",
|
|
49
|
+
"trustPolicyDigest",
|
|
50
|
+
"witnessDigest",
|
|
51
|
+
"witnessId",
|
|
52
|
+
]);
|
|
53
|
+
const SIGNATURE_KEYS = new Set([
|
|
54
|
+
"algorithm",
|
|
55
|
+
"keyId",
|
|
56
|
+
"trustPolicyDigest",
|
|
57
|
+
"value",
|
|
58
|
+
]);
|
|
59
|
+
const DISCARD_KEYS = new Set([
|
|
60
|
+
"anchorDigest",
|
|
61
|
+
"headDigest",
|
|
62
|
+
"segmentDigest",
|
|
63
|
+
"sequence",
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
function canonical(value) {
|
|
67
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
68
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
69
|
+
return `{${Object.keys(value)
|
|
70
|
+
.sort()
|
|
71
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
72
|
+
.join(",")}}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function hash(value) {
|
|
76
|
+
return `sha256:${crypto.createHash("sha256").update(value).digest("hex")}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function requiredString(value, name) {
|
|
80
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
81
|
+
throw new TypeError(`${name} is required`);
|
|
82
|
+
}
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function normalizeTrust(value) {
|
|
87
|
+
const algorithm = requiredString(value?.algorithm, "trust.algorithm");
|
|
88
|
+
const keyId = requiredString(value?.keyId, "trust.keyId");
|
|
89
|
+
if (algorithm.length > 64 || keyId.length > 256) {
|
|
90
|
+
throw new TypeError("witness trust descriptor is too large");
|
|
91
|
+
}
|
|
92
|
+
if (!DIGEST.test(value?.trustPolicyDigest || "")) {
|
|
93
|
+
throw new TypeError("trust.trustPolicyDigest must be sha256-bound");
|
|
94
|
+
}
|
|
95
|
+
return Object.freeze({
|
|
96
|
+
algorithm,
|
|
97
|
+
keyId,
|
|
98
|
+
trustPolicyDigest: value.trustPolicyDigest,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function assertExactKeys(value, expected, label) {
|
|
103
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
104
|
+
throw new Error(`${label} is invalid`);
|
|
105
|
+
}
|
|
106
|
+
const keys = Object.keys(value);
|
|
107
|
+
if (keys.length !== expected.size || keys.some((key) => !expected.has(key))) {
|
|
108
|
+
throw new Error(`${label} fields are invalid`);
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function safeCounter(value, label) {
|
|
114
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
115
|
+
throw new Error(`${label} is invalid`);
|
|
116
|
+
}
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function nullableDigest(value, label) {
|
|
121
|
+
if (value !== null && !DIGEST.test(value || "")) {
|
|
122
|
+
throw new Error(`${label} is invalid`);
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function nullableIdentifier(value, label) {
|
|
128
|
+
if (value !== null && !IDENTIFIER.test(value || "")) {
|
|
129
|
+
throw new Error(`${label} is invalid`);
|
|
130
|
+
}
|
|
131
|
+
return value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function capture(owner, name) {
|
|
135
|
+
if (typeof owner?.[name] !== "function")
|
|
136
|
+
throw new TypeError(`${name} port is required`);
|
|
137
|
+
return (...args) => Reflect.apply(owner[name], owner, args);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function sameTrust(value, trust) {
|
|
141
|
+
return (
|
|
142
|
+
value?.algorithm === trust.algorithm &&
|
|
143
|
+
value.keyId === trust.keyId &&
|
|
144
|
+
value.trustPolicyDigest === trust.trustPolicyDigest
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function clone(value) {
|
|
149
|
+
return structuredClone(value);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function createEvolutionFileWitness({
|
|
153
|
+
id,
|
|
154
|
+
filePath,
|
|
155
|
+
trust: trustInput,
|
|
156
|
+
signer,
|
|
157
|
+
verifier,
|
|
158
|
+
fsImpl = fs,
|
|
159
|
+
lock = withFileLock,
|
|
160
|
+
random = () => crypto.randomBytes(16).toString("hex"),
|
|
161
|
+
maximumBytes = DEFAULT_MAXIMUM_BYTES,
|
|
162
|
+
} = {}) {
|
|
163
|
+
if (!IDENTIFIER.test(id || "")) throw new TypeError("witness id is invalid");
|
|
164
|
+
const target = path.resolve(requiredString(filePath, "filePath"));
|
|
165
|
+
const directory = path.dirname(target);
|
|
166
|
+
const trust = normalizeTrust(trustInput);
|
|
167
|
+
const sign = capture(signer, "sign");
|
|
168
|
+
const verify = capture(verifier, "verify");
|
|
169
|
+
if (typeof lock !== "function" || typeof random !== "function") {
|
|
170
|
+
throw new TypeError("witness lock and random ports are required");
|
|
171
|
+
}
|
|
172
|
+
if (!Number.isSafeInteger(maximumBytes) || maximumBytes < 4096) {
|
|
173
|
+
throw new TypeError("maximumBytes must be a safe integer of at least 4096");
|
|
174
|
+
}
|
|
175
|
+
const secureOptions = { deps: { fs: fsImpl }, failIfUnavailable: true };
|
|
176
|
+
ensurePrivateDirectory(directory, secureOptions);
|
|
177
|
+
|
|
178
|
+
const signed = (domain, core, digestField, purpose) => {
|
|
179
|
+
const message = Buffer.from(`${domain}${canonical(core)}`, "utf8");
|
|
180
|
+
const signature = sign({ message, purpose, trust });
|
|
181
|
+
if (
|
|
182
|
+
!sameTrust(signature, trust) ||
|
|
183
|
+
typeof signature.value !== "string" ||
|
|
184
|
+
signature.value === "" ||
|
|
185
|
+
signature.value.length > 16_384
|
|
186
|
+
) {
|
|
187
|
+
throw new Error("witness signer returned an invalid trust binding");
|
|
188
|
+
}
|
|
189
|
+
assertExactKeys(signature, SIGNATURE_KEYS, "witness signature");
|
|
190
|
+
return Object.freeze({
|
|
191
|
+
...core,
|
|
192
|
+
[digestField]: hash(message),
|
|
193
|
+
signature: Object.freeze({ ...signature }),
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const verifySigned = (record, domain, digestField, purpose) => {
|
|
198
|
+
assertExactKeys(record, RECORD_KEYS, "witness record");
|
|
199
|
+
const core = clone(record);
|
|
200
|
+
const signature = core.signature;
|
|
201
|
+
const observedDigest = core[digestField];
|
|
202
|
+
delete core.signature;
|
|
203
|
+
delete core[digestField];
|
|
204
|
+
const message = Buffer.from(`${domain}${canonical(core)}`, "utf8");
|
|
205
|
+
assertExactKeys(signature, SIGNATURE_KEYS, "witness signature");
|
|
206
|
+
if (
|
|
207
|
+
!DIGEST.test(observedDigest || "") ||
|
|
208
|
+
observedDigest !== hash(message) ||
|
|
209
|
+
!sameTrust(signature, trust) ||
|
|
210
|
+
verify({ message, purpose, signature, trust }) !== true
|
|
211
|
+
) {
|
|
212
|
+
throw new Error("witness record authentication failed");
|
|
213
|
+
}
|
|
214
|
+
if (
|
|
215
|
+
record.schema !== EVOLUTION_LEDGER_WITNESS_SCHEMA ||
|
|
216
|
+
record.authenticated !== true ||
|
|
217
|
+
record.durable !== true ||
|
|
218
|
+
record.witnessId !== id ||
|
|
219
|
+
!sameTrust(record, trust) ||
|
|
220
|
+
!["absent", "committed"].includes(record.status)
|
|
221
|
+
) {
|
|
222
|
+
throw new Error("witness record authority is invalid");
|
|
223
|
+
}
|
|
224
|
+
safeCounter(record.generation, "witness generation");
|
|
225
|
+
nullableDigest(record.anchorDigest, "witness anchorDigest");
|
|
226
|
+
nullableDigest(record.headDigest, "witness headDigest");
|
|
227
|
+
nullableDigest(record.identityDigest, "witness identityDigest");
|
|
228
|
+
nullableDigest(record.payloadDigest, "witness payloadDigest");
|
|
229
|
+
nullableDigest(
|
|
230
|
+
record.previousWitnessDigest,
|
|
231
|
+
"witness previousWitnessDigest",
|
|
232
|
+
);
|
|
233
|
+
nullableDigest(record.segmentDigest, "witness segmentDigest");
|
|
234
|
+
nullableDigest(record.storeMarkerDigest, "witness storeMarkerDigest");
|
|
235
|
+
nullableDigest(
|
|
236
|
+
record.storeMarkerEntryDigest,
|
|
237
|
+
"witness storeMarkerEntryDigest",
|
|
238
|
+
);
|
|
239
|
+
if (!DIGEST.test(record.discardAccumulatorDigest || "")) {
|
|
240
|
+
throw new Error("witness discardAccumulatorDigest is invalid");
|
|
241
|
+
}
|
|
242
|
+
nullableIdentifier(record.epoch, "witness epoch");
|
|
243
|
+
nullableIdentifier(record.ledgerId, "witness ledgerId");
|
|
244
|
+
nullableIdentifier(record.storeMarkerId, "witness storeMarkerId");
|
|
245
|
+
if (record.sequence !== null) {
|
|
246
|
+
safeCounter(record.sequence, "witness sequence");
|
|
247
|
+
}
|
|
248
|
+
const committedRequired = [
|
|
249
|
+
record.anchorDigest,
|
|
250
|
+
record.epoch,
|
|
251
|
+
record.identityDigest,
|
|
252
|
+
record.ledgerId,
|
|
253
|
+
record.payloadDigest,
|
|
254
|
+
record.storeMarkerDigest,
|
|
255
|
+
record.storeMarkerEntryDigest,
|
|
256
|
+
record.storeMarkerId,
|
|
257
|
+
];
|
|
258
|
+
if (
|
|
259
|
+
(record.status === "absent" &&
|
|
260
|
+
(record.generation !== 0 ||
|
|
261
|
+
record.previousWitnessDigest !== null ||
|
|
262
|
+
record.discardAccumulatorDigest !== emptyDiscardDigest ||
|
|
263
|
+
[
|
|
264
|
+
...committedRequired,
|
|
265
|
+
record.headDigest,
|
|
266
|
+
record.segmentDigest,
|
|
267
|
+
record.sequence,
|
|
268
|
+
].some((entry) => entry !== null))) ||
|
|
269
|
+
(record.status === "committed" &&
|
|
270
|
+
(record.generation < 1 ||
|
|
271
|
+
record.previousWitnessDigest === null ||
|
|
272
|
+
record.sequence === null ||
|
|
273
|
+
committedRequired.some((entry) => entry === null) ||
|
|
274
|
+
(record.sequence === 0 &&
|
|
275
|
+
(record.headDigest !== null || record.segmentDigest !== null)) ||
|
|
276
|
+
(record.sequence > 0 &&
|
|
277
|
+
(record.headDigest === null || record.segmentDigest === null))))
|
|
278
|
+
) {
|
|
279
|
+
throw new Error("witness record state is inconsistent");
|
|
280
|
+
}
|
|
281
|
+
return record;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const emptyDiscardDigest = hash(
|
|
285
|
+
Buffer.from(`${DISCARD_DOMAIN}${canonical([])}`, "utf8"),
|
|
286
|
+
);
|
|
287
|
+
const witnessRecord = (snapshot = null, previous = null, discard = null) => {
|
|
288
|
+
const discardAccumulatorDigest = discard
|
|
289
|
+
? hash(
|
|
290
|
+
Buffer.from(
|
|
291
|
+
`${DISCARD_DOMAIN}${canonical({
|
|
292
|
+
discard,
|
|
293
|
+
previousDiscardAccumulatorDigest:
|
|
294
|
+
previous.discardAccumulatorDigest,
|
|
295
|
+
previousWitnessDigest: previous.witnessDigest,
|
|
296
|
+
})}`,
|
|
297
|
+
"utf8",
|
|
298
|
+
),
|
|
299
|
+
)
|
|
300
|
+
: (previous?.discardAccumulatorDigest ?? emptyDiscardDigest);
|
|
301
|
+
const core = {
|
|
302
|
+
...trust,
|
|
303
|
+
anchorDigest: snapshot?.anchorDigest ?? null,
|
|
304
|
+
authenticated: true,
|
|
305
|
+
durable: true,
|
|
306
|
+
discardAccumulatorDigest,
|
|
307
|
+
epoch: snapshot?.epoch ?? null,
|
|
308
|
+
generation: previous ? previous.generation + 1 : 0,
|
|
309
|
+
headDigest: snapshot?.headDigest ?? null,
|
|
310
|
+
identityDigest: snapshot?.identityDigest ?? null,
|
|
311
|
+
ledgerId: snapshot?.ledgerId ?? null,
|
|
312
|
+
payloadDigest: snapshot?.payloadDigest ?? null,
|
|
313
|
+
previousWitnessDigest: previous?.witnessDigest ?? null,
|
|
314
|
+
schema: EVOLUTION_LEDGER_WITNESS_SCHEMA,
|
|
315
|
+
segmentDigest: snapshot?.segmentDigest ?? null,
|
|
316
|
+
sequence: snapshot?.sequence ?? null,
|
|
317
|
+
status: snapshot ? "committed" : "absent",
|
|
318
|
+
storeMarkerDigest: snapshot?.storeMarkerDigest ?? null,
|
|
319
|
+
storeMarkerEntryDigest: snapshot?.storeMarkerEntryDigest ?? null,
|
|
320
|
+
storeMarkerId: snapshot?.storeMarkerId ?? null,
|
|
321
|
+
witnessId: id,
|
|
322
|
+
};
|
|
323
|
+
const record = signed(
|
|
324
|
+
WITNESS_DOMAIN,
|
|
325
|
+
core,
|
|
326
|
+
"witnessDigest",
|
|
327
|
+
"evolution-ledger-witness",
|
|
328
|
+
);
|
|
329
|
+
return verifySigned(
|
|
330
|
+
record,
|
|
331
|
+
WITNESS_DOMAIN,
|
|
332
|
+
"witnessDigest",
|
|
333
|
+
"evolution-ledger-witness",
|
|
334
|
+
);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const emptyStore = () => {
|
|
338
|
+
const current = witnessRecord();
|
|
339
|
+
return {
|
|
340
|
+
schema: EVOLUTION_FILE_WITNESS_STORE_SCHEMA,
|
|
341
|
+
current,
|
|
342
|
+
history: [current],
|
|
343
|
+
discardedAnchors: [],
|
|
344
|
+
};
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const validateStore = (store) => {
|
|
348
|
+
assertExactKeys(store, STORE_KEYS, "witness store");
|
|
349
|
+
if (
|
|
350
|
+
store.schema !== EVOLUTION_FILE_WITNESS_STORE_SCHEMA ||
|
|
351
|
+
!Array.isArray(store.history) ||
|
|
352
|
+
!Array.isArray(store.discardedAnchors) ||
|
|
353
|
+
store.history.length < 1
|
|
354
|
+
) {
|
|
355
|
+
throw new Error("witness store schema is invalid");
|
|
356
|
+
}
|
|
357
|
+
const genesis = store.history[0];
|
|
358
|
+
if (
|
|
359
|
+
genesis.status !== "absent" ||
|
|
360
|
+
genesis.generation !== 0 ||
|
|
361
|
+
genesis.previousWitnessDigest !== null ||
|
|
362
|
+
genesis.discardAccumulatorDigest !== emptyDiscardDigest ||
|
|
363
|
+
[
|
|
364
|
+
genesis.anchorDigest,
|
|
365
|
+
genesis.epoch,
|
|
366
|
+
genesis.headDigest,
|
|
367
|
+
genesis.identityDigest,
|
|
368
|
+
genesis.ledgerId,
|
|
369
|
+
genesis.payloadDigest,
|
|
370
|
+
genesis.segmentDigest,
|
|
371
|
+
genesis.sequence,
|
|
372
|
+
genesis.storeMarkerDigest,
|
|
373
|
+
genesis.storeMarkerEntryDigest,
|
|
374
|
+
genesis.storeMarkerId,
|
|
375
|
+
].some((entry) => entry !== null)
|
|
376
|
+
) {
|
|
377
|
+
throw new Error("witness genesis is invalid");
|
|
378
|
+
}
|
|
379
|
+
for (const discard of store.discardedAnchors) {
|
|
380
|
+
assertExactKeys(discard, DISCARD_KEYS, "witness discard descriptor");
|
|
381
|
+
if (
|
|
382
|
+
!DIGEST.test(discard.anchorDigest || "") ||
|
|
383
|
+
!DIGEST.test(discard.headDigest || "") ||
|
|
384
|
+
!DIGEST.test(discard.segmentDigest || "")
|
|
385
|
+
) {
|
|
386
|
+
throw new Error("witness discard descriptor digest is invalid");
|
|
387
|
+
}
|
|
388
|
+
safeCounter(discard.sequence, "witness discard sequence");
|
|
389
|
+
}
|
|
390
|
+
let discardIndex = 0;
|
|
391
|
+
for (const [index, record] of store.history.entries()) {
|
|
392
|
+
verifySigned(
|
|
393
|
+
record,
|
|
394
|
+
WITNESS_DOMAIN,
|
|
395
|
+
"witnessDigest",
|
|
396
|
+
"evolution-ledger-witness",
|
|
397
|
+
);
|
|
398
|
+
if (index === 0) continue;
|
|
399
|
+
const previous = store.history[index - 1];
|
|
400
|
+
if (
|
|
401
|
+
record.status !== "committed" ||
|
|
402
|
+
record.previousWitnessDigest !== previous.witnessDigest ||
|
|
403
|
+
record.generation !== previous.generation + 1
|
|
404
|
+
) {
|
|
405
|
+
throw new Error("witness history is not monotonic");
|
|
406
|
+
}
|
|
407
|
+
if (
|
|
408
|
+
record.discardAccumulatorDigest !== previous.discardAccumulatorDigest
|
|
409
|
+
) {
|
|
410
|
+
const discard = store.discardedAnchors[discardIndex++];
|
|
411
|
+
const expected = hash(
|
|
412
|
+
Buffer.from(
|
|
413
|
+
`${DISCARD_DOMAIN}${canonical({
|
|
414
|
+
discard,
|
|
415
|
+
previousDiscardAccumulatorDigest:
|
|
416
|
+
previous.discardAccumulatorDigest,
|
|
417
|
+
previousWitnessDigest: previous.witnessDigest,
|
|
418
|
+
})}`,
|
|
419
|
+
"utf8",
|
|
420
|
+
),
|
|
421
|
+
);
|
|
422
|
+
if (!discard || record.discardAccumulatorDigest !== expected) {
|
|
423
|
+
throw new Error("witness discard history is invalid");
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (
|
|
428
|
+
discardIndex !== store.discardedAnchors.length ||
|
|
429
|
+
store.current?.witnessDigest !== store.history.at(-1).witnessDigest ||
|
|
430
|
+
canonical(
|
|
431
|
+
verifySigned(
|
|
432
|
+
store.current,
|
|
433
|
+
WITNESS_DOMAIN,
|
|
434
|
+
"witnessDigest",
|
|
435
|
+
"evolution-ledger-witness",
|
|
436
|
+
),
|
|
437
|
+
) !== canonical(store.history.at(-1))
|
|
438
|
+
) {
|
|
439
|
+
throw new Error("witness current state is not its durable history");
|
|
440
|
+
}
|
|
441
|
+
return store;
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
const readStore = () => {
|
|
445
|
+
if (!fsImpl.existsSync(target)) return emptyStore();
|
|
446
|
+
ensurePrivateFile(target, secureOptions);
|
|
447
|
+
const stat = fsImpl.statSync(target);
|
|
448
|
+
if (!stat.isFile() || stat.size < 2 || stat.size > maximumBytes) {
|
|
449
|
+
throw new Error("witness store size is invalid");
|
|
450
|
+
}
|
|
451
|
+
const bytes = fsImpl.readFileSync(target, "utf8");
|
|
452
|
+
return validateStore(JSON.parse(bytes));
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const syncDirectory = () => {
|
|
456
|
+
let descriptor;
|
|
457
|
+
try {
|
|
458
|
+
descriptor = fsImpl.openSync(directory, "r");
|
|
459
|
+
fsImpl.fsyncSync(descriptor);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
if (
|
|
462
|
+
process.platform !== "win32" ||
|
|
463
|
+
!["EACCES", "EINVAL", "EISDIR", "EPERM"].includes(error?.code)
|
|
464
|
+
)
|
|
465
|
+
throw error;
|
|
466
|
+
} finally {
|
|
467
|
+
if (descriptor !== undefined) fsImpl.closeSync(descriptor);
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
const publish = (store) => {
|
|
472
|
+
const temporary = `${target}.${requiredString(random(), "random token")}.tmp`;
|
|
473
|
+
const bytes = Buffer.from(`${canonical(store)}\n`, "utf8");
|
|
474
|
+
if (bytes.length > maximumBytes) {
|
|
475
|
+
throw new Error("witness store exceeds its configured maximum size");
|
|
476
|
+
}
|
|
477
|
+
let descriptor;
|
|
478
|
+
try {
|
|
479
|
+
descriptor = fsImpl.openSync(temporary, "wx", 0o600);
|
|
480
|
+
fsImpl.writeFileSync(descriptor, bytes);
|
|
481
|
+
fsImpl.fsyncSync(descriptor);
|
|
482
|
+
fsImpl.closeSync(descriptor);
|
|
483
|
+
descriptor = undefined;
|
|
484
|
+
fsImpl.renameSync(temporary, target);
|
|
485
|
+
ensurePrivateFile(target, secureOptions);
|
|
486
|
+
descriptor = fsImpl.openSync(target, "r+");
|
|
487
|
+
fsImpl.fsyncSync(descriptor);
|
|
488
|
+
fsImpl.closeSync(descriptor);
|
|
489
|
+
descriptor = undefined;
|
|
490
|
+
syncDirectory();
|
|
491
|
+
return store.current;
|
|
492
|
+
} finally {
|
|
493
|
+
if (descriptor !== undefined) fsImpl.closeSync(descriptor);
|
|
494
|
+
try {
|
|
495
|
+
if (fsImpl.existsSync(temporary)) fsImpl.unlinkSync(temporary);
|
|
496
|
+
} catch {
|
|
497
|
+
// A private orphan cannot affect the committed path.
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const underLock = (operation) =>
|
|
503
|
+
lock(target, operation, { failIfUnavailable: true, _fs: fsImpl });
|
|
504
|
+
const discarded = (store, snapshot) =>
|
|
505
|
+
store.discardedAnchors.some(
|
|
506
|
+
(entry) =>
|
|
507
|
+
entry.anchorDigest === snapshot.anchorDigest ||
|
|
508
|
+
entry.headDigest === snapshot.headDigest ||
|
|
509
|
+
entry.segmentDigest === snapshot.segmentDigest,
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
const port = {
|
|
513
|
+
id,
|
|
514
|
+
read: () => readStore().current,
|
|
515
|
+
initialize: ({ expected, snapshot }) =>
|
|
516
|
+
underLock(() => {
|
|
517
|
+
const store = readStore();
|
|
518
|
+
if (store.current.witnessDigest !== expected.witnessDigest) {
|
|
519
|
+
return store.current;
|
|
520
|
+
}
|
|
521
|
+
if (store.current.status !== "absent") return store.current;
|
|
522
|
+
store.current = witnessRecord(snapshot, store.current);
|
|
523
|
+
store.history.push(store.current);
|
|
524
|
+
return publish(store);
|
|
525
|
+
}),
|
|
526
|
+
compareAndSwap: ({ discard = null, expected, next }) =>
|
|
527
|
+
underLock(() => {
|
|
528
|
+
const store = readStore();
|
|
529
|
+
if (store.current.witnessDigest !== expected.witnessDigest) {
|
|
530
|
+
return store.current;
|
|
531
|
+
}
|
|
532
|
+
if (!discard && discarded(store, next)) return store.current;
|
|
533
|
+
if (discard) {
|
|
534
|
+
assertExactKeys(discard, DISCARD_KEYS, "witness discard descriptor");
|
|
535
|
+
if (
|
|
536
|
+
!DIGEST.test(discard.anchorDigest || "") ||
|
|
537
|
+
!DIGEST.test(discard.headDigest || "") ||
|
|
538
|
+
!DIGEST.test(discard.segmentDigest || "")
|
|
539
|
+
) {
|
|
540
|
+
throw new Error("witness discard descriptor digest is invalid");
|
|
541
|
+
}
|
|
542
|
+
safeCounter(discard.sequence, "witness discard sequence");
|
|
543
|
+
if (discarded(store, discard)) return store.current;
|
|
544
|
+
store.discardedAnchors.push(clone(discard));
|
|
545
|
+
}
|
|
546
|
+
store.current = witnessRecord(next, store.current, discard);
|
|
547
|
+
store.history.push(store.current);
|
|
548
|
+
return publish(store);
|
|
549
|
+
}),
|
|
550
|
+
proveAncestry: ({ ancestor, descendant }) => {
|
|
551
|
+
const store = readStore();
|
|
552
|
+
const first = store.history.findIndex(
|
|
553
|
+
(entry) => entry.witnessDigest === ancestor.witnessDigest,
|
|
554
|
+
);
|
|
555
|
+
const last = store.history.findIndex(
|
|
556
|
+
(entry) => entry.witnessDigest === descendant.witnessDigest,
|
|
557
|
+
);
|
|
558
|
+
if (first < 0 || last < first)
|
|
559
|
+
throw new Error("witness ancestry is absent");
|
|
560
|
+
const persistedAncestor = store.history[first];
|
|
561
|
+
const persistedDescendant = store.history[last];
|
|
562
|
+
if (
|
|
563
|
+
ancestor.generation !== persistedAncestor.generation ||
|
|
564
|
+
descendant.generation !== persistedDescendant.generation
|
|
565
|
+
) {
|
|
566
|
+
throw new Error("witness ancestry checkpoint is not exactly bound");
|
|
567
|
+
}
|
|
568
|
+
for (let index = first + 1; index <= last; index += 1) {
|
|
569
|
+
if (
|
|
570
|
+
store.history[index].previousWitnessDigest !==
|
|
571
|
+
store.history[index - 1].witnessDigest ||
|
|
572
|
+
store.history[index].generation !==
|
|
573
|
+
store.history[index - 1].generation + 1
|
|
574
|
+
) {
|
|
575
|
+
throw new Error("witness ancestry is not contiguous");
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const core = {
|
|
579
|
+
...trust,
|
|
580
|
+
ancestorDigest: persistedAncestor.witnessDigest,
|
|
581
|
+
ancestorGeneration: persistedAncestor.generation,
|
|
582
|
+
authenticated: true,
|
|
583
|
+
descendantDigest: persistedDescendant.witnessDigest,
|
|
584
|
+
descendantGeneration: persistedDescendant.generation,
|
|
585
|
+
durable: true,
|
|
586
|
+
epoch: persistedAncestor.epoch,
|
|
587
|
+
included: true,
|
|
588
|
+
ledgerId: persistedAncestor.ledgerId,
|
|
589
|
+
schema: EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA,
|
|
590
|
+
witnessId: id,
|
|
591
|
+
};
|
|
592
|
+
return signed(
|
|
593
|
+
ANCESTRY_DOMAIN,
|
|
594
|
+
core,
|
|
595
|
+
"proofDigest",
|
|
596
|
+
"evolution-ledger-witness-ancestry",
|
|
597
|
+
);
|
|
598
|
+
},
|
|
599
|
+
};
|
|
600
|
+
return Object.freeze(port);
|
|
601
|
+
}
|