chainlesschain 0.166.9 → 0.166.13
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 +33 -0
- package/bin/chainlesschain.js +5 -0
- package/package.json +5 -2
- package/src/assets/web-panel/assets/{AIOps-DJvpifGx.js → AIOps-BN25cNOD.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-D2UhuF_A.js → ActionButton-Da9IP-m5.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C9ho9obu.js → Analytics-D7hkEvQm.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BvRCpFK3.js → AppLayout-CCV4Xnbc.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-jxrySvpk.js → Artifacts-CVSnVJeA.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-ovEw0ZYl.js → Audit-CuYdw5Ag.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-D7H5z6zS.js → BackgroundAgents-DZCnK-Tf.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BuXob8za.js → Backup-cV9FdpTy.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CtnszuQ2.js → BaseInput-BRnv6YSp.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-B_mYE8fJ.js → Chat-Bw5w87P4.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-C0GjtUAw.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-ierdtcJD.js → Checkbox-DaoTqcdt.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-3VKPJD_B.js → Codegen-B4dYcEIi.js} +1 -1
- package/src/assets/web-panel/assets/{Col-1EdMyf5P.js → Col-mAhuOeuD.js} +1 -1
- package/src/assets/web-panel/assets/{Community-D6H7UA8K.js → Community-BK6KEdBk.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BBAZvYIm.js → Compact-CCglmNu5.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CgsTrAoj.js → Compliance-Cm8S4of7.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-tnZgLoTE.js → Cowork-Dhs1QLMe.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-CPpZw2Nh.js → Cron-CLlhmXWk.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BymBcp5z.js → Crosschain-BnHVbaNs.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DKPxS_K1.js → DID-UzQP9-0O.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DvJhEmH9.js → Dashboard-CBdlPClz.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-b8iQyQIZ.js → Dropdown-axIRPdq1.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer--0-A-zKb.js → EmailListRenderer-DUalW5rx.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C12iDN06.js → FamilyGuardDashboard-Bl4W9luY.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BRhaE2Z8.js → Federation-C1yP-uEu.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C4dnq7yf.js → FormItemContext-Xh6Ig5q8.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CGAdbR5n.js → GenericCardRenderer-C2cz3bt2.js} +1 -1
- package/src/assets/web-panel/assets/{Git-4eA2fYzL.js → Git-C_z63ixg.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BJuY9Y-9.js → Governance-BYqB33_3.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DZ5Dy4up.js → Inference-CEULuArm.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BMTjRs4A.js → KnowledgeGraph-fgMsu-0H.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BKEeQEfJ.js → Logs-DO9Hdcec.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BkrK04xW.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Dhho4fEg.js → Marketplace-B8zyLiFe.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BW6xxSlU.js → McpTools-BLAN2mWW.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-mPqlpSkq.js → Memory-CrQcFM7u.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Cb9N_DQI.js → MobileBridge-DVnyiiU-.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BNs8tah_.js → MobileProjects-qjCME5OD.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CaxBt8tn.js → Mtc-4JE3C3It.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-DRPQbp3t.js → MtcAudit-CjBuak9I.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CRm-d8bK.js → Multisig-BtPcYQcC.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BCCk3LPs.js → NLProgramming-Cx653_Ba.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cv8SN8gN.js → Notes-CkuD3YzS.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-CggIvTh5.js → NotificationSettings-BgNgU7HH.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BW3_bV12.js → OrderTableRenderer-BTC8W0a-.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BuRE-VCO.js → Organization-Dmd4Hjxz.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DBrm1gju.js → Overflow-C_5DA-G0.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CWVnDYgS.js → P2P-uwslV4XC.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-8RKUIpte.js → PdhVaultBrowser-CuNgwVnX.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-tDUAxogW.js → Permissions-CJayVX9H.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dv2-CFis.js → PersonalDataHub-Cb05H5go.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-DM6kvFni.js → Pipeline-BZlGKapZ.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DzK-yL0d.js → Privacy-BIzZWYzd.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B5UrNpkj.js → ProjectInit-vEYYyQXQ.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CxJD43oW.js → ProjectSettings-MZqTy5CC.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CRARUOl8.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CYl4jRYK.js → Providers-80VAK8PX.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DeH2T0fH.js → QrScannerModal-CzRSoIwK.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Bw9k_kTk.js → QuickAsk-BpwQ2n4C.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bl_jGwoi.js → Recommend-BC03TZ79.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-d6u01Kq0.js → RemoteSession-DE59-jaa.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-Ds_Dfr-k.js → Reputation-DOHmeWyA.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BpQUQTNj.js → Row-hc5RZl1D.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BatpZBGt.js → RssFeed-Cv5uMP7E.js} +3 -3
- package/src/assets/web-panel/assets/{Search-D7hOObzi.js → Search-qohu7Ug0.js} +1 -1
- package/src/assets/web-panel/assets/{Security-dXQ4FKNt.js → Security-drCPFWV5.js} +4 -4
- package/src/assets/web-panel/assets/{Services-D8wInGPa.js → Services-BAEgUeRy.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DXQCoYpM.js → Skeleton-CkJkKBlo.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-D3oLRIVn.js → Skills-bMuKEpez.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BmP4-LnJ.js → Sla-DxWQVHRC.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DLj-xzro.js → SpeechSettings-BpS8idmV.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-8KKaaRMr.js → SyncSettings-BPBLlW5v.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-GyK42I7C.js → Tasks-C4SLEeUh.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-n1y_8UBG.js → Templates-r-wfU39U.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-B39nBph8.js → Tenant-C7vjRmLN.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DCQoOKXe.js → Terminal-DdzeFCV2.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BkhbDfXk.js → TimelineRenderer-CYyWN2KD.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BeHIIU8D.js → Tokens-D8A9osUg.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BHvWXVOb.js → Trigger-DBvrNlyS.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-y2WaOSto.js → Trust-DBVAGc7o.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DBFeJBzt.js → UkeySign-C1vQUCRJ.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BEOWitC-.js → VideoEditing-BKs4dah4.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BITedTe6.js → Wallet-ByIuj3lD.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DMwlJVin.js → WebAuthn-DYlAFCm9.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-CKfGFEu1.js → WorkflowEditor-Dwyadd0c.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DR6gnBGF.js → chat-Bm2f3s08.js} +1 -1
- package/src/assets/web-panel/assets/{colors-C_Hw5NCn.js → colors-C8ORSh7V.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-B8IPwsEu.js → compact-item-1iVlnsM-.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D-u4JVnJ.js → createContext-CmqNyUSL.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-B69RCDDH.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CZubdS4D.js → hasIn-rUMaX3Ul.js} +1 -1
- package/src/assets/web-panel/assets/index-9biMQ-tw.js +1 -0
- package/src/assets/web-panel/assets/{index-C25rOz7O.js → index-B53c3diB.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfniPP5p.js → index-B59OWAs7.js} +1 -1
- package/src/assets/web-panel/assets/{index-11ASqx-A.js → index-B8BrB0Ls.js} +1 -1
- package/src/assets/web-panel/assets/{index-auyNYjLo.js → index-BC32_0xT.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ctje8Vl3.js → index-BC8Wb6cy.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cg2bCeol.js → index-BCOhTILd.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1w96gg9.js → index-BFniLMn9.js} +1 -1
- package/src/assets/web-panel/assets/{index-k1Xa5iw1.js → index-BIvM-uAX.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuOU5apg.js → index-BK-RY0-7.js} +1 -1
- package/src/assets/web-panel/assets/{index-D47jt8ET.js → index-BQL9r73Y.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2VmtNaQ.js → index-BVtnizyC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bizm87cX.js → index-Bf1PlIHp.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dt-w3xQ7.js → index-Bgw4Y2rU.js} +1 -1
- package/src/assets/web-panel/assets/{index-ffe7bvqP.js → index-BgwLNxmr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cl5RpreD.js → index-C-J5-pGH.js} +1 -1
- package/src/assets/web-panel/assets/{index-idZKyADQ.js → index-C-ZIHi42.js} +1 -1
- package/src/assets/web-panel/assets/{index-LvjAKDSl.js → index-C3B1S-mW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmRcgiYT.js → index-C5RfRciU.js} +1 -1
- package/src/assets/web-panel/assets/{index-rYBHSDcJ.js → index-CCEyz58d.js} +1 -1
- package/src/assets/web-panel/assets/index-COie87kl.js +1 -0
- package/src/assets/web-panel/assets/{index-DcFjRT-K.js → index-CTfEmKK0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjRrqooR.js → index-CfiHyVyl.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpjHpRoq.js → index-CkFvuAex.js} +3 -3
- package/src/assets/web-panel/assets/{index-BzoFanEQ.js → index-D98FXb0w.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cbm8F0M2.js → index-DDHk911Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXS6wYJZ.js → index-DJxtPFOh.js} +1 -1
- package/src/assets/web-panel/assets/{index-DY6MHo9D.js → index-DVJ1uKf4.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiXkhOkY.js → index-Dcck8QfW.js} +1 -1
- package/src/assets/web-panel/assets/{index-mYZJxGH6.js → index-DgUE51Pz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqmLw8cs.js → index-Dsve5Gkv.js} +1 -1
- package/src/assets/web-panel/assets/{index-xoNssgz_.js → index-DvedhJeM.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfnzrUxU.js → index-DwMdWKiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bi21Etg4.js → index-OcbzdRpT.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJ6pG79m.js → index-fPK6GFEg.js} +1 -1
- package/src/assets/web-panel/assets/{index-_nL79NT8.js → index-geQinnns.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTosx2Wu.js → index-pHWA7bmM.js} +1 -1
- package/src/assets/web-panel/assets/{index-cVqbas5C.js → index-reUHhuz8.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bv2132PO.js → index-tqTx5wXo.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-lcU_dT2r.js → initDefaultProps-CuA-cpkL.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Dc7UNXZ1.js → motion-CimmE_9b.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cll8ZXaL.js → move-DlGlrltz.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CAb_yLIM.js → mtc-parser-CK6BJQ07.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DurYMyTe.js → omit-BKjBfZfB.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BITQyOlw.js → pickAttrs-B2chVYw9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Dg1rH91i.js → placementArrow-BW8aLJJ3.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-D_EWIeac.js → responsiveObserve-dXlPl66S.js} +1 -1
- package/src/assets/web-panel/assets/{slide-lK4d3zsh.js → slide-hSa89S4G.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Bflq-Git.js → statusUtils-DM6KLt-s.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Ccp0I-wt.js → styleChecker-C9MQAK15.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CYZbByYA.js → useFlexGapSupport-BpQDpP0z.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-B_x0nxFD.js → useFs-C7rtRViA.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DW55rL85.js → usePersonalDataHub-Dm15xafq.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Ca6MH3W8.js → vnode-mEiEytw4.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-NdHelOog.js → zoom-DzPxflhY.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +5 -5
- package/src/commands/agent.js +51 -20
- package/src/commands/compact.js +349 -3
- package/src/commands/context.js +72 -1
- package/src/commands/graph.js +26 -15
- package/src/commands/hmemory.js +21 -0
- package/src/commands/hook.js +256 -28
- package/src/commands/memory.js +209 -42
- package/src/commands/promcomp.js +17 -0
- package/src/commands/serve.js +94 -10
- package/src/commands/session.js +2 -3
- package/src/commands/team.js +7 -1
- package/src/data/changelog.json +41 -2
- package/src/gateways/ws/session-core-protocol.js +62 -3
- package/src/gateways/ws/session-protocol.js +123 -15
- package/src/gateways/ws/ws-agent-handler.js +157 -9
- package/src/gateways/ws/ws-session-gateway.js +204 -30
- package/src/generated/cc-agent-protocol.schema.json +2094 -4
- package/src/harness/prompt-compressor.js +5 -55
- package/src/lib/agent-sandbox.js +14 -4
- package/src/lib/agent-team/team-distributed-graph-bridge.js +3 -3
- package/src/lib/app-server/context-memory-notifications.js +54 -0
- package/src/lib/app-server/rollout-store-factory.js +115 -0
- package/src/lib/app-server/rollout-store.js +310 -23
- package/src/lib/app-server/server.js +184 -7
- package/src/lib/app-server/sqlite-rollout-store.js +59 -16
- package/src/lib/auto-mode-config.js +26 -0
- package/src/lib/causal-observability.js +13 -1
- package/src/lib/context-memory-kernel/authority.js +95 -0
- package/src/lib/context-memory-kernel/durable-memory-port.js +296 -0
- package/src/lib/context-memory-kernel/index.js +8 -0
- package/src/lib/context-memory-kernel/jsonl-session-context-port.js +232 -0
- package/src/lib/context-memory-kernel/live-compaction.js +375 -0
- package/src/lib/context-memory-kernel/memory-service.js +667 -0
- package/src/lib/context-memory-kernel/message-adapter.js +451 -0
- package/src/lib/context-memory-kernel/privacy-purge-port.js +244 -0
- package/src/lib/context-memory-kernel/provider-context.js +192 -0
- package/src/lib/context-memory-kernel/runtime.js +61 -0
- package/src/lib/direct-model-usage.js +22 -18
- package/src/lib/graph-kernel/cutover-authority-resolver.js +4 -5
- package/src/lib/graph-kernel/cutover-ledger.js +2 -2
- package/src/lib/graph-kernel/event-store.js +3 -5
- package/src/lib/graph-kernel/index.js +2 -0
- package/src/lib/graph-kernel/migration-saga.js +2 -2
- package/src/lib/graph-kernel/production-cutover-evidence.js +976 -0
- package/src/lib/graph-kernel/retirement-evidence.js +12 -4
- package/src/lib/hierarchical-memory.js +22 -0
- package/src/lib/hook-audit-store.js +170 -0
- package/src/lib/hook-event-bus.cjs +0 -0
- package/src/lib/hook-manager.js +25 -83
- package/src/lib/hook-runtime-adapters.js +233 -0
- package/src/lib/hook-runtime-contract.cjs +291 -0
- package/src/lib/hook-runtime-contract.js +21 -0
- package/src/lib/hook-trust.js +210 -0
- package/src/lib/hooks-v2-producers.js +52 -6
- package/src/lib/hooks-v2-runtime.js +443 -178
- package/src/lib/llm-pricing.js +10 -0
- package/src/lib/memory-manager.js +29 -4
- package/src/lib/model-context-window.js +52 -0
- package/src/lib/permanent-memory.js +17 -0
- package/src/lib/plan-mode.js +8 -4
- package/src/lib/process-execution-broker/index.js +208 -16
- package/src/lib/runtime-usage-ledger.js +20 -0
- package/src/lib/session-consolidator.js +59 -0
- package/src/lib/session-core-singletons.js +17 -4
- package/src/lib/session-hooks.js +29 -2
- package/src/lib/session-transcript-migration.js +188 -0
- package/src/lib/settings-hook-events.cjs +37 -401
- package/src/lib/settings-hook-events.js +210 -8
- package/src/lib/settings-hooks.cjs +42 -83
- package/src/lib/ws-session-manager.js +4 -1
- package/src/repl/agent-repl.js +186 -53
- package/src/runtime/agent-core.js +376 -350
- package/src/runtime/headless-runner.js +40 -33
- package/src/runtime/headless-stream.js +131 -30
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DaiA6CsX.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-B05DgASC.js +0 -1
- package/src/assets/web-panel/assets/Projects-Z2Orm8Ma.js +0 -1
- package/src/assets/web-panel/assets/devWarning-Cq6lSd_q.js +0 -1
- package/src/assets/web-panel/assets/index-BQ3213fz.js +0 -1
- package/src/assets/web-panel/assets/index-vJagfET2.js +0 -1
|
@@ -120,6 +120,10 @@ function sameMembers(actual, expected) {
|
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
function compareCanonicalText(left, right) {
|
|
124
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
123
127
|
function clone(value) {
|
|
124
128
|
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
125
129
|
}
|
|
@@ -289,7 +293,7 @@ function normalizeMutationProbes(value, contract) {
|
|
|
289
293
|
};
|
|
290
294
|
});
|
|
291
295
|
normalized.sort((left, right) =>
|
|
292
|
-
left.mutationFunction
|
|
296
|
+
compareCanonicalText(left.mutationFunction, right.mutationFunction),
|
|
293
297
|
);
|
|
294
298
|
const actual = normalized.map((entry) => entry.mutationFunction);
|
|
295
299
|
if (
|
|
@@ -347,7 +351,8 @@ function normalizeReplacementJourneys(value, contract, platforms, commitSha) {
|
|
|
347
351
|
};
|
|
348
352
|
});
|
|
349
353
|
normalized.sort((left, right) =>
|
|
350
|
-
|
|
354
|
+
compareCanonicalText(
|
|
355
|
+
`${left.replacementEntryId}:${left.platform}`,
|
|
351
356
|
`${right.replacementEntryId}:${right.platform}`,
|
|
352
357
|
),
|
|
353
358
|
);
|
|
@@ -408,7 +413,10 @@ function normalizeHistoricalReadProbes(value, contract) {
|
|
|
408
413
|
};
|
|
409
414
|
});
|
|
410
415
|
normalized.sort((left, right) =>
|
|
411
|
-
|
|
416
|
+
compareCanonicalText(
|
|
417
|
+
left.historicalReadFunction,
|
|
418
|
+
right.historicalReadFunction,
|
|
419
|
+
),
|
|
412
420
|
);
|
|
413
421
|
const actual = normalized.map((entry) => entry.historicalReadFunction);
|
|
414
422
|
if (
|
|
@@ -524,7 +532,7 @@ export function normalizeGraphLegacyWriterObservation(
|
|
|
524
532
|
}))
|
|
525
533
|
: [];
|
|
526
534
|
writerObservations.sort((left, right) =>
|
|
527
|
-
left.writerFile
|
|
535
|
+
compareCanonicalText(left.writerFile, right.writerFile),
|
|
528
536
|
);
|
|
529
537
|
const actualWriterFiles = writerObservations.map((entry) => entry.writerFile);
|
|
530
538
|
if (
|
|
@@ -6,6 +6,18 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { escapeLike } from "./sql-like.js";
|
|
9
|
+
import {
|
|
10
|
+
assertLegacyMutationAllowed,
|
|
11
|
+
resolveCliContextMemoryCutover,
|
|
12
|
+
} from "./context-memory-kernel/authority.js";
|
|
13
|
+
|
|
14
|
+
function assertLegacyHierarchicalMemoryMutationAllowed(operation) {
|
|
15
|
+
assertLegacyMutationAllowed(
|
|
16
|
+
resolveCliContextMemoryCutover({
|
|
17
|
+
scopeKey: `cli:legacy-hierarchical-memory:${operation}`,
|
|
18
|
+
}),
|
|
19
|
+
);
|
|
20
|
+
}
|
|
9
21
|
|
|
10
22
|
// ─── Configuration ───────────────────────────────────────────────
|
|
11
23
|
export const MEMORY_CONFIG = {
|
|
@@ -123,6 +135,7 @@ function calcRetention(
|
|
|
123
135
|
* Create persistent memory tables for long-term / core / sharing
|
|
124
136
|
*/
|
|
125
137
|
export function ensureMemoryTables(db) {
|
|
138
|
+
assertLegacyHierarchicalMemoryMutationAllowed("ensure-tables");
|
|
126
139
|
db.exec(`
|
|
127
140
|
CREATE TABLE IF NOT EXISTS memory_long_term (
|
|
128
141
|
id TEXT PRIMARY KEY,
|
|
@@ -179,6 +192,7 @@ export function ensureMemoryTables(db) {
|
|
|
179
192
|
* @param {string} [options.namespace] - Namespace for in-memory isolation (default: "global")
|
|
180
193
|
*/
|
|
181
194
|
export function storeMemory(db, content, options = {}) {
|
|
195
|
+
assertLegacyHierarchicalMemoryMutationAllowed("store");
|
|
182
196
|
if (!content || !content.trim()) {
|
|
183
197
|
throw new Error("Memory content cannot be empty");
|
|
184
198
|
}
|
|
@@ -258,6 +272,7 @@ export function storeMemory(db, content, options = {}) {
|
|
|
258
272
|
* the default "global" namespace (backward compatible).
|
|
259
273
|
*/
|
|
260
274
|
export function recallMemory(db, query, options = {}) {
|
|
275
|
+
assertLegacyHierarchicalMemoryMutationAllowed("recall");
|
|
261
276
|
if (!query || !query.trim()) return [];
|
|
262
277
|
|
|
263
278
|
const limit = Math.max(1, parseInt(options.limit) || 20);
|
|
@@ -653,6 +668,7 @@ export function applyForgettingCurve(
|
|
|
653
668
|
* persisted to the DB — lives alongside the in-memory layer.
|
|
654
669
|
*/
|
|
655
670
|
export function attachMetadata(memoryId, metadata, namespace = DEFAULT_NS) {
|
|
671
|
+
assertLegacyHierarchicalMemoryMutationAllowed("attach-metadata");
|
|
656
672
|
if (!memoryId) throw new Error("memoryId is required");
|
|
657
673
|
if (!metadata || typeof metadata !== "object")
|
|
658
674
|
throw new Error("metadata must be an object");
|
|
@@ -682,6 +698,7 @@ function _locateInMemory(memoryId) {
|
|
|
682
698
|
* Promote a memory one layer up: working → short-term → long-term.
|
|
683
699
|
*/
|
|
684
700
|
export function promoteMemoryV2(db, memoryId) {
|
|
701
|
+
assertLegacyHierarchicalMemoryMutationAllowed("promote");
|
|
685
702
|
if (!memoryId) throw new Error("memoryId is required");
|
|
686
703
|
const loc = _locateInMemory(memoryId);
|
|
687
704
|
if (loc && loc.layer === MEMORY_LAYER.WORKING) {
|
|
@@ -728,6 +745,7 @@ export function promoteMemoryV2(db, memoryId) {
|
|
|
728
745
|
* (long-term/core demotion requires DB round-trip; not exposed here.)
|
|
729
746
|
*/
|
|
730
747
|
export function demoteMemoryV2(memoryId) {
|
|
748
|
+
assertLegacyHierarchicalMemoryMutationAllowed("demote");
|
|
731
749
|
if (!memoryId) throw new Error("memoryId is required");
|
|
732
750
|
for (const [ns, nsMap] of _shortTermNS) {
|
|
733
751
|
if (nsMap.has(memoryId)) {
|
|
@@ -790,6 +808,7 @@ export function shareMemoryV2(
|
|
|
790
808
|
}
|
|
791
809
|
|
|
792
810
|
export function revokeShare(shareId) {
|
|
811
|
+
assertLegacyHierarchicalMemoryMutationAllowed("revoke-share");
|
|
793
812
|
const record = _v2Shares.find((s) => s.id === shareId);
|
|
794
813
|
if (!record) throw new Error(`Share not found: ${shareId}`);
|
|
795
814
|
if (record.revokedAt) throw new Error(`Share already revoked`);
|
|
@@ -896,6 +915,7 @@ export function searchSemanticV2(db, options = {}) {
|
|
|
896
915
|
* Delegates to existing consolidateMemory() for promotion logic.
|
|
897
916
|
*/
|
|
898
917
|
export function consolidateV2(db, options = {}) {
|
|
918
|
+
assertLegacyHierarchicalMemoryMutationAllowed("consolidate-v2");
|
|
899
919
|
const id = `consol-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
900
920
|
const record = {
|
|
901
921
|
id,
|
|
@@ -949,6 +969,7 @@ export function listConsolidations(options = {}) {
|
|
|
949
969
|
* Prune with per-layer scope + custom threshold.
|
|
950
970
|
*/
|
|
951
971
|
export function pruneV2(db, options = {}) {
|
|
972
|
+
assertLegacyHierarchicalMemoryMutationAllowed("prune-v2");
|
|
952
973
|
const { layer, maxAge = 720, threshold } = options;
|
|
953
974
|
if (layer && !_LAYER_VALUES.has(layer))
|
|
954
975
|
throw new Error(`Invalid layer: ${layer}`);
|
|
@@ -1000,6 +1021,7 @@ export function startConsolidationTimer({
|
|
|
1000
1021
|
db,
|
|
1001
1022
|
onTick,
|
|
1002
1023
|
} = {}) {
|
|
1024
|
+
assertLegacyHierarchicalMemoryMutationAllowed("start-consolidation-timer");
|
|
1003
1025
|
if (_consolidationTimer) return _consolidationTimer;
|
|
1004
1026
|
if (!db) throw new Error("db is required for consolidation timer");
|
|
1005
1027
|
_consolidationTimer = setInterval(() => {
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getHomeDir } from "./paths.js";
|
|
4
|
+
import {
|
|
5
|
+
mutateSecurityStore,
|
|
6
|
+
readSecurityStore,
|
|
7
|
+
} from "./durable-security-store.js";
|
|
8
|
+
import { stableStringify } from "./hook-runtime-contract.js";
|
|
9
|
+
|
|
10
|
+
export const HOOK_AUDIT_STORE_SCHEMA =
|
|
11
|
+
"chainlesschain.hook-runtime-audit-store/v1";
|
|
12
|
+
export const HOOK_AUDIT_RECORD_SCHEMA = "chainlesschain.hook-runtime-audit/v1";
|
|
13
|
+
export const DEFAULT_MAX_HOOK_AUDIT_RECORDS = 10000;
|
|
14
|
+
|
|
15
|
+
function sha256(label, value) {
|
|
16
|
+
return crypto
|
|
17
|
+
.createHash("sha256")
|
|
18
|
+
.update(`${label}\0`, "utf8")
|
|
19
|
+
.update(String(value), "utf8")
|
|
20
|
+
.digest("hex");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function safeToken(value, max = 256) {
|
|
24
|
+
if (value == null) return null;
|
|
25
|
+
const text = String(value);
|
|
26
|
+
return text.length <= max ? text : text.slice(0, max);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function digestToken(value) {
|
|
30
|
+
if (value == null || value === "") return null;
|
|
31
|
+
const text = String(value);
|
|
32
|
+
return /^[a-f0-9]{64}$/iu.test(text)
|
|
33
|
+
? text.toLowerCase()
|
|
34
|
+
: sha256("chainlesschain.hook-audit-token.v1", text);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeRecord(record = {}) {
|
|
38
|
+
return {
|
|
39
|
+
schema: HOOK_AUDIT_RECORD_SCHEMA,
|
|
40
|
+
timestamp: safeToken(record.timestamp || new Date().toISOString(), 64),
|
|
41
|
+
phase: safeToken(record.phase, 32),
|
|
42
|
+
event: safeToken(record.event, 96),
|
|
43
|
+
eventId: digestToken(record.eventId),
|
|
44
|
+
executionId: digestToken(record.executionId),
|
|
45
|
+
hookId: digestToken(record.hookId),
|
|
46
|
+
hookDigest: digestToken(record.hookDigest),
|
|
47
|
+
sourceKind: safeToken(record.sourceKind, 32),
|
|
48
|
+
sourceDigest: digestToken(record.sourceDigest),
|
|
49
|
+
trustStatus: safeToken(record.trustStatus, 64),
|
|
50
|
+
priority: Number.isFinite(Number(record.priority))
|
|
51
|
+
? Number(record.priority)
|
|
52
|
+
: null,
|
|
53
|
+
executionMode: safeToken(record.executionMode, 32),
|
|
54
|
+
status: safeToken(record.status, 32),
|
|
55
|
+
decision: safeToken(record.decision, 32),
|
|
56
|
+
durationMs: Number.isFinite(Number(record.durationMs))
|
|
57
|
+
? Math.max(0, Math.floor(Number(record.durationMs)))
|
|
58
|
+
: null,
|
|
59
|
+
errorCode: safeToken(record.errorCode, 96),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function hashRecord(prevHash, record) {
|
|
64
|
+
return sha256(
|
|
65
|
+
"chainlesschain.hook-audit-chain.v1",
|
|
66
|
+
`${prevHash || "genesis"}\n${stableStringify(record)}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function validateStore(store) {
|
|
71
|
+
if (Object.keys(store).length === 0) {
|
|
72
|
+
return {
|
|
73
|
+
schema: HOOK_AUDIT_STORE_SCHEMA,
|
|
74
|
+
anchorHash: null,
|
|
75
|
+
headHash: null,
|
|
76
|
+
records: [],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (
|
|
80
|
+
store.schema !== HOOK_AUDIT_STORE_SCHEMA ||
|
|
81
|
+
!Array.isArray(store.records) ||
|
|
82
|
+
(store.anchorHash != null && typeof store.anchorHash !== "string") ||
|
|
83
|
+
(store.headHash != null && typeof store.headHash !== "string")
|
|
84
|
+
) {
|
|
85
|
+
const error = new Error("Hook audit store has an invalid schema");
|
|
86
|
+
error.code = "CC_HOOK_AUDIT_STORE_INVALID";
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
return store;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export class HookAuditStore {
|
|
93
|
+
constructor({ filePath, maxRecords, now } = {}) {
|
|
94
|
+
this.filePath =
|
|
95
|
+
filePath ||
|
|
96
|
+
process.env.CC_HOOK_AUDIT_FILE ||
|
|
97
|
+
path.join(getHomeDir(), "audit", "hook-runtime-v1.json");
|
|
98
|
+
this.maxRecords = Math.max(
|
|
99
|
+
100,
|
|
100
|
+
Math.min(100000, Number(maxRecords) || DEFAULT_MAX_HOOK_AUDIT_RECORDS),
|
|
101
|
+
);
|
|
102
|
+
this.now = typeof now === "function" ? now : () => new Date().toISOString();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
append(record) {
|
|
106
|
+
const normalized = normalizeRecord({
|
|
107
|
+
...record,
|
|
108
|
+
timestamp: record?.timestamp || this.now(),
|
|
109
|
+
});
|
|
110
|
+
return mutateSecurityStore(this.filePath, "Hook runtime audit", (draft) => {
|
|
111
|
+
const store = validateStore(draft);
|
|
112
|
+
if (Object.keys(draft).length === 0) Object.assign(draft, store);
|
|
113
|
+
const prevHash = draft.headHash || draft.anchorHash || null;
|
|
114
|
+
const hash = hashRecord(prevHash, normalized);
|
|
115
|
+
draft.records.push({ record: normalized, prevHash, hash });
|
|
116
|
+
draft.headHash = hash;
|
|
117
|
+
if (draft.records.length > this.maxRecords) {
|
|
118
|
+
const removeCount = draft.records.length - this.maxRecords;
|
|
119
|
+
const removed = draft.records.splice(0, removeCount);
|
|
120
|
+
draft.anchorHash =
|
|
121
|
+
removed[removed.length - 1]?.hash || draft.anchorHash;
|
|
122
|
+
}
|
|
123
|
+
return Object.freeze({ ...normalized, prevHash, hash });
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
list({ limit = 100 } = {}) {
|
|
128
|
+
const store = validateStore(
|
|
129
|
+
readSecurityStore(this.filePath, "Hook runtime audit"),
|
|
130
|
+
);
|
|
131
|
+
return store.records.slice(-Math.max(0, Number(limit) || 0));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
verify() {
|
|
135
|
+
const store = validateStore(
|
|
136
|
+
readSecurityStore(this.filePath, "Hook runtime audit"),
|
|
137
|
+
);
|
|
138
|
+
let prev = store.anchorHash || null;
|
|
139
|
+
for (let index = 0; index < store.records.length; index += 1) {
|
|
140
|
+
const entry = store.records[index];
|
|
141
|
+
if (!entry || entry.prevHash !== prev) {
|
|
142
|
+
return { ok: false, length: store.records.length, brokenAt: index };
|
|
143
|
+
}
|
|
144
|
+
const expected = hashRecord(prev, entry.record);
|
|
145
|
+
if (entry.hash !== expected) {
|
|
146
|
+
return { ok: false, length: store.records.length, brokenAt: index };
|
|
147
|
+
}
|
|
148
|
+
prev = entry.hash;
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
ok: prev === (store.headHash || store.anchorHash || null),
|
|
152
|
+
length: store.records.length,
|
|
153
|
+
brokenAt:
|
|
154
|
+
prev === (store.headHash || store.anchorHash || null)
|
|
155
|
+
? -1
|
|
156
|
+
: store.records.length,
|
|
157
|
+
headHash: store.headHash || null,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let defaultStore;
|
|
163
|
+
export function getDefaultHookAuditStore() {
|
|
164
|
+
if (!defaultStore) defaultStore = new HookAuditStore();
|
|
165
|
+
return defaultStore;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function _resetDefaultHookAuditStore() {
|
|
169
|
+
defaultStore = undefined;
|
|
170
|
+
}
|
|
Binary file
|
package/src/lib/hook-manager.js
CHANGED
|
@@ -6,19 +6,14 @@
|
|
|
6
6
|
import crypto from "crypto";
|
|
7
7
|
import broker from "./process-execution-broker/index.js";
|
|
8
8
|
import hookEnvironment from "./hook-environment.cjs";
|
|
9
|
+
import { HOOK_EVENT_TYPES, HOOK_PRIORITY } from "./hook-runtime-contract.js";
|
|
9
10
|
|
|
10
11
|
const { buildManagedHookEnvironment } = hookEnvironment;
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Hook priority levels — lower values run first.
|
|
14
15
|
*/
|
|
15
|
-
export const HookPriority =
|
|
16
|
-
SYSTEM: 0,
|
|
17
|
-
HIGH: 100,
|
|
18
|
-
NORMAL: 500,
|
|
19
|
-
LOW: 900,
|
|
20
|
-
MONITOR: 1000,
|
|
21
|
-
};
|
|
16
|
+
export const HookPriority = HOOK_PRIORITY;
|
|
22
17
|
|
|
23
18
|
/**
|
|
24
19
|
* Hook execution types.
|
|
@@ -33,47 +28,7 @@ export const HookType = {
|
|
|
33
28
|
/**
|
|
34
29
|
* All supported hook event names.
|
|
35
30
|
*/
|
|
36
|
-
export const HookEvents =
|
|
37
|
-
Setup: "Setup",
|
|
38
|
-
PreIPCCall: "PreIPCCall",
|
|
39
|
-
PostIPCCall: "PostIPCCall",
|
|
40
|
-
IPCError: "IPCError",
|
|
41
|
-
PreToolUse: "PreToolUse",
|
|
42
|
-
PostToolUse: "PostToolUse",
|
|
43
|
-
ToolError: "ToolError",
|
|
44
|
-
SessionStart: "SessionStart",
|
|
45
|
-
SessionEnd: "SessionEnd",
|
|
46
|
-
PreCompact: "PreCompact",
|
|
47
|
-
PostCompact: "PostCompact",
|
|
48
|
-
Notification: "Notification",
|
|
49
|
-
UserPromptSubmit: "UserPromptSubmit",
|
|
50
|
-
AssistantResponse: "AssistantResponse",
|
|
51
|
-
AgentStart: "AgentStart",
|
|
52
|
-
AgentStop: "AgentStop",
|
|
53
|
-
TaskAssigned: "TaskAssigned",
|
|
54
|
-
TaskCompleted: "TaskCompleted",
|
|
55
|
-
PreFileAccess: "PreFileAccess",
|
|
56
|
-
PostFileAccess: "PostFileAccess",
|
|
57
|
-
FileModified: "FileModified",
|
|
58
|
-
MemorySave: "MemorySave",
|
|
59
|
-
MemoryLoad: "MemoryLoad",
|
|
60
|
-
AuditLog: "AuditLog",
|
|
61
|
-
ComplianceCheck: "ComplianceCheck",
|
|
62
|
-
DataSubjectRequest: "DataSubjectRequest",
|
|
63
|
-
PreGitCommit: "PreGitCommit",
|
|
64
|
-
PostGitCommit: "PostGitCommit",
|
|
65
|
-
PreGitPush: "PreGitPush",
|
|
66
|
-
CIFailure: "CIFailure",
|
|
67
|
-
IterationWarning: "IterationWarning",
|
|
68
|
-
IterationBudgetExhausted: "IterationBudgetExhausted",
|
|
69
|
-
/**
|
|
70
|
-
* Stop: Fired at the END of every agent turn/loop iteration, BEFORE waiting
|
|
71
|
-
* for the next user input. Aligns with Claude Code hook spec.
|
|
72
|
-
* Fires regardless of outcome (success, error, max-iterations, stop).
|
|
73
|
-
* Does NOT fire on final process exit (use SessionEnd/Shutdown for that).
|
|
74
|
-
*/
|
|
75
|
-
Stop: "Stop",
|
|
76
|
-
};
|
|
31
|
+
export const HookEvents = HOOK_EVENT_TYPES;
|
|
77
32
|
|
|
78
33
|
/**
|
|
79
34
|
* Ensure hooks table exists in the database.
|
|
@@ -290,8 +245,7 @@ export async function executeHook(hook, context = {}) {
|
|
|
290
245
|
}
|
|
291
246
|
const env = buildManagedHookEnvironment({
|
|
292
247
|
managedAllowlist: hook.managedEnvironmentAllowlist,
|
|
293
|
-
requestedAllowlist:
|
|
294
|
-
hook.environmentAllowlist || hook.envAllowlist,
|
|
248
|
+
requestedAllowlist: hook.environmentAllowlist || hook.envAllowlist,
|
|
295
249
|
values: {
|
|
296
250
|
HOOK_EVENT: hook.event,
|
|
297
251
|
HOOK_CONTEXT: JSON.stringify(context),
|
|
@@ -364,39 +318,27 @@ export async function executeHook(hook, context = {}) {
|
|
|
364
318
|
* Returns array of { hookId, hookName, success, result, error, executionTime }.
|
|
365
319
|
*/
|
|
366
320
|
export async function executeHooks(db, eventName, context = {}) {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
hookName: hook.name,
|
|
389
|
-
...outcome,
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
// Update stats
|
|
393
|
-
updateHookStats(db, hook.id, {
|
|
394
|
-
executionTime: outcome.executionTime,
|
|
395
|
-
success: outcome.success,
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
return results;
|
|
321
|
+
// Compatibility API: SQLite rows are definitions only. All scheduling,
|
|
322
|
+
// timeout, trust, decision and audit semantics belong to Hooks v2.
|
|
323
|
+
const { executeHooksV2Event } = await import("./hooks-v2-producers.js");
|
|
324
|
+
const outcome = await executeHooksV2Event(eventName, context, {
|
|
325
|
+
hookDb: db,
|
|
326
|
+
matchTarget:
|
|
327
|
+
context.target ||
|
|
328
|
+
context.channel ||
|
|
329
|
+
context.tool_name ||
|
|
330
|
+
context.tool ||
|
|
331
|
+
context.file ||
|
|
332
|
+
eventName,
|
|
333
|
+
});
|
|
334
|
+
return (outcome.results || []).map((record) => ({
|
|
335
|
+
hookId: record.sourceHookId || record.hookId,
|
|
336
|
+
hookName: record.hookName || record.hookId,
|
|
337
|
+
success: record.status === "success",
|
|
338
|
+
result: record.result?.raw ?? record.result ?? null,
|
|
339
|
+
error: record.error || null,
|
|
340
|
+
executionTime: record.durationMs || 0,
|
|
341
|
+
}));
|
|
400
342
|
}
|
|
401
343
|
|
|
402
344
|
/**
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import settingsHookLoader from "./settings-hooks.cjs";
|
|
3
|
+
import {
|
|
4
|
+
HOOK_EXECUTION_MODE,
|
|
5
|
+
HOOK_PRIORITY,
|
|
6
|
+
normalizeHookPriority,
|
|
7
|
+
normalizeHookTimeoutMs,
|
|
8
|
+
stableStringify,
|
|
9
|
+
} from "./hook-runtime-contract.js";
|
|
10
|
+
import { computeHookDefinitionDigest } from "./hook-trust.js";
|
|
11
|
+
|
|
12
|
+
const { collectHooks, compileMatcher } = settingsHookLoader;
|
|
13
|
+
|
|
14
|
+
function adapterId(prefix, value) {
|
|
15
|
+
return `${prefix}:${crypto
|
|
16
|
+
.createHash("sha256")
|
|
17
|
+
.update(stableStringify(value), "utf8")
|
|
18
|
+
.digest("hex")}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function settingsAuthority(authoritySource = {}) {
|
|
22
|
+
const scope =
|
|
23
|
+
authoritySource.scope ||
|
|
24
|
+
(authoritySource.kind === "plugin"
|
|
25
|
+
? "plugin"
|
|
26
|
+
: authoritySource.sourceFile
|
|
27
|
+
? "project"
|
|
28
|
+
: "explicit");
|
|
29
|
+
return Object.freeze({
|
|
30
|
+
kind: authoritySource.kind || "settings",
|
|
31
|
+
sourceFile: authoritySource.sourceFile || null,
|
|
32
|
+
digest: authoritySource.digest || null,
|
|
33
|
+
scope,
|
|
34
|
+
requiresConsent: scope === "project",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function adaptSettingsHookDefinitions(
|
|
39
|
+
settingsHooks,
|
|
40
|
+
event,
|
|
41
|
+
matchTarget = "",
|
|
42
|
+
{ cwd = process.cwd(), failureMode = "fail-closed" } = {},
|
|
43
|
+
) {
|
|
44
|
+
const authorityErrors = settingsHooks?._authorityErrors;
|
|
45
|
+
if (Array.isArray(authorityErrors) && authorityErrors.length > 0) {
|
|
46
|
+
const error = new Error(
|
|
47
|
+
"Settings Hook authority could not be loaded safely",
|
|
48
|
+
);
|
|
49
|
+
error.code = authorityErrors[0]?.code || "CC_HOOK_AUTHORITY_INVALID";
|
|
50
|
+
error.authorityErrors = authorityErrors;
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
return collectHooks(settingsHooks, event, matchTarget).map((hook, index) => {
|
|
54
|
+
const authority = settingsAuthority(hook.authoritySource);
|
|
55
|
+
const identity = {
|
|
56
|
+
event,
|
|
57
|
+
command: hook.command,
|
|
58
|
+
shell: hook.shell ?? true,
|
|
59
|
+
authority,
|
|
60
|
+
index,
|
|
61
|
+
};
|
|
62
|
+
const definition = {
|
|
63
|
+
id: adapterId("settings", identity),
|
|
64
|
+
event,
|
|
65
|
+
type: "command",
|
|
66
|
+
command: hook.command,
|
|
67
|
+
shell: hook.shell == null ? true : hook.shell,
|
|
68
|
+
cwd,
|
|
69
|
+
priority: normalizeHookPriority(hook.priority, HOOK_PRIORITY.NORMAL),
|
|
70
|
+
timeoutMs: normalizeHookTimeoutMs(
|
|
71
|
+
hook.timeout == null ? undefined : Number(hook.timeout) * 1000,
|
|
72
|
+
),
|
|
73
|
+
executionMode:
|
|
74
|
+
hook.async === true
|
|
75
|
+
? HOOK_EXECUTION_MODE.ASYNC
|
|
76
|
+
: HOOK_EXECUTION_MODE.BLOCKING,
|
|
77
|
+
asyncRewake: hook.asyncRewake === true,
|
|
78
|
+
legacyPayload: true,
|
|
79
|
+
legacyAdapter: true,
|
|
80
|
+
legacyHook: hook,
|
|
81
|
+
origin: hook.origin || "settings",
|
|
82
|
+
authority,
|
|
83
|
+
environmentAllowlist:
|
|
84
|
+
hook.environmentAllowlist || hook.envAllowlist || undefined,
|
|
85
|
+
sandboxPolicy: hook.sandboxPolicy,
|
|
86
|
+
requiredBoundaries: hook.requiredBoundaries,
|
|
87
|
+
pluginId: hook.pluginId || null,
|
|
88
|
+
pluginVersion: hook.pluginVersion || null,
|
|
89
|
+
pluginSource: hook.pluginSource || null,
|
|
90
|
+
failureMode: failureMode === "ignore" ? "ignore" : "fail-closed",
|
|
91
|
+
};
|
|
92
|
+
definition.definitionDigest = computeHookDefinitionDigest(definition);
|
|
93
|
+
return Object.freeze(definition);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function databaseMatchTarget(context, event) {
|
|
98
|
+
return (
|
|
99
|
+
context.target ||
|
|
100
|
+
context.channel ||
|
|
101
|
+
context.tool_name ||
|
|
102
|
+
context.tool ||
|
|
103
|
+
context.file ||
|
|
104
|
+
event
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function readDatabaseHookRows(db, event) {
|
|
109
|
+
if (!db || typeof db.prepare !== "function") return [];
|
|
110
|
+
return db
|
|
111
|
+
.prepare(
|
|
112
|
+
"SELECT * FROM hooks WHERE event = ? AND enabled = 1 ORDER BY priority ASC, id ASC",
|
|
113
|
+
)
|
|
114
|
+
.all(event);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function databaseReadFailureDefinition(event) {
|
|
118
|
+
const authority = Object.freeze({
|
|
119
|
+
kind: "database",
|
|
120
|
+
scope: "explicit",
|
|
121
|
+
subject: "database:hook-observer",
|
|
122
|
+
requiresConsent: false,
|
|
123
|
+
});
|
|
124
|
+
const definition = {
|
|
125
|
+
id: `database:adapter-read-failure:${event}`,
|
|
126
|
+
event,
|
|
127
|
+
type: "js",
|
|
128
|
+
handler() {
|
|
129
|
+
const error = new Error(
|
|
130
|
+
"Database Hook observer source could not be read",
|
|
131
|
+
);
|
|
132
|
+
error.code = "CC_HOOK_DATABASE_READ_FAILED";
|
|
133
|
+
throw error;
|
|
134
|
+
},
|
|
135
|
+
priority: HOOK_PRIORITY.NORMAL,
|
|
136
|
+
timeoutMs: normalizeHookTimeoutMs(),
|
|
137
|
+
executionMode: HOOK_EXECUTION_MODE.BLOCKING,
|
|
138
|
+
origin: "database",
|
|
139
|
+
authority,
|
|
140
|
+
failureMode: "ignore",
|
|
141
|
+
observeOnly: true,
|
|
142
|
+
databaseHookName: "database-hook-observer",
|
|
143
|
+
};
|
|
144
|
+
definition.definitionDigest = computeHookDefinitionDigest(definition);
|
|
145
|
+
return Object.freeze(definition);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function adaptDatabaseHookDefinitions(db, event, context = {}) {
|
|
149
|
+
const target = databaseMatchTarget(context, event);
|
|
150
|
+
const definitions = [];
|
|
151
|
+
let rows;
|
|
152
|
+
try {
|
|
153
|
+
rows = readDatabaseHookRows(db, event);
|
|
154
|
+
} catch {
|
|
155
|
+
// Database hooks are a legacy, observe-only source. A missing table or an
|
|
156
|
+
// unavailable optional database must remain visible in the canonical
|
|
157
|
+
// audit/event stream, but it cannot acquire decision authority merely by
|
|
158
|
+
// failing to load.
|
|
159
|
+
return [databaseReadFailureDefinition(event)];
|
|
160
|
+
}
|
|
161
|
+
for (const row of rows) {
|
|
162
|
+
if (row.matcher && !compileMatcher(row.matcher)(target)) continue;
|
|
163
|
+
const commandBacked = new Set(["command", "script"]).has(row.type);
|
|
164
|
+
const authority = Object.freeze({
|
|
165
|
+
kind: "database",
|
|
166
|
+
scope: "explicit",
|
|
167
|
+
subject: `database:${row.id}`,
|
|
168
|
+
requiresConsent: false,
|
|
169
|
+
});
|
|
170
|
+
const definition = {
|
|
171
|
+
id: `database:${row.id}`,
|
|
172
|
+
event,
|
|
173
|
+
type: commandBacked ? "command" : "js",
|
|
174
|
+
...(commandBacked
|
|
175
|
+
? { command: row.handler || "", shell: true }
|
|
176
|
+
: { handler: () => null }),
|
|
177
|
+
priority: normalizeHookPriority(row.priority, HOOK_PRIORITY.NORMAL),
|
|
178
|
+
timeoutMs: normalizeHookTimeoutMs(row.timeout),
|
|
179
|
+
executionMode:
|
|
180
|
+
row.type === "async"
|
|
181
|
+
? HOOK_EXECUTION_MODE.ASYNC
|
|
182
|
+
: HOOK_EXECUTION_MODE.BLOCKING,
|
|
183
|
+
legacyPayload: true,
|
|
184
|
+
legacyAdapter: true,
|
|
185
|
+
origin: "database",
|
|
186
|
+
authority,
|
|
187
|
+
failureMode: "ignore",
|
|
188
|
+
observeOnly: true,
|
|
189
|
+
databaseHookId: row.id,
|
|
190
|
+
databaseHookName: row.name,
|
|
191
|
+
recordResult(record) {
|
|
192
|
+
const success = record?.status === "success";
|
|
193
|
+
const executionTime = Math.max(0, Number(record?.durationMs) || 0);
|
|
194
|
+
const current = db
|
|
195
|
+
.prepare(
|
|
196
|
+
"SELECT execution_count, error_count, total_execution_time FROM hooks WHERE id = ?",
|
|
197
|
+
)
|
|
198
|
+
.get(row.id);
|
|
199
|
+
if (!current) return;
|
|
200
|
+
db.prepare(
|
|
201
|
+
`UPDATE hooks
|
|
202
|
+
SET execution_count = ?,
|
|
203
|
+
error_count = ?,
|
|
204
|
+
total_execution_time = ?,
|
|
205
|
+
updated_at = datetime('now')
|
|
206
|
+
WHERE id = ?`,
|
|
207
|
+
).run(
|
|
208
|
+
(current.execution_count || 0) + 1,
|
|
209
|
+
(current.error_count || 0) + (success ? 0 : 1),
|
|
210
|
+
(current.total_execution_time || 0) + executionTime,
|
|
211
|
+
row.id,
|
|
212
|
+
);
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
definition.definitionDigest = computeHookDefinitionDigest(definition);
|
|
216
|
+
definitions.push(Object.freeze(definition));
|
|
217
|
+
}
|
|
218
|
+
return definitions;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function collectCanonicalAdapterHooks(
|
|
222
|
+
event,
|
|
223
|
+
context,
|
|
224
|
+
{ settingsHooks, hookDb, matchTarget, cwd, settingsFailureMode } = {},
|
|
225
|
+
) {
|
|
226
|
+
return [
|
|
227
|
+
...adaptSettingsHookDefinitions(settingsHooks, event, matchTarget, {
|
|
228
|
+
cwd,
|
|
229
|
+
failureMode: settingsFailureMode,
|
|
230
|
+
}),
|
|
231
|
+
...adaptDatabaseHookDefinitions(hookDb, event, context),
|
|
232
|
+
];
|
|
233
|
+
}
|