chainlesschain 0.166.106 → 0.166.108
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 +187 -8
- package/package.json +2 -2
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-CKwEku2e.js → AIOps-B5HWUFaS.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BSUOuTpW.js → ActionButton-DCIKpxoQ.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CGxvL_FI.js → Analytics-dsgvjCEQ.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-bCPwz-5w.js → AppLayout-DWDH5Qbm.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-2_BOWo6e.js → Artifacts-BD8S8kzU.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CBOruzgW.js → Audit-CDXv4Aoj.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-lg0IU5HN.js → BackgroundAgents-BLAQ5Uy0.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-OspCi452.js → Backup-moPac6tF.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BMkliqlz.js → BaseInput-C_6nzsLU.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BfoiUZwp.js → Chat-cYuslkFG.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BjnfMwep.js → ChatBubbleRenderer-Ceh3MG6M.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-jTMlkNQZ.js → Checkbox-Qjb-XOX_.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CycyJcnU.js → Codegen-IYRWXlMA.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BVWlReQj.js → Col-CsQ9jv8M.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CKRHl4xK.js → Community-BbNmfTwe.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BtpfMdhT.js → Compact-SShDx-nr.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DtHB1Ct1.js → Compliance-CLWhKIJp.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-D_8X7-RK.js → Cowork-D4tK9_s3.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-DMCOyvEa.js → Cron-D7GnfQ1z.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CeHdsu7m.js → Crosschain-TG86SH3X.js} +1 -1
- package/src/assets/web-panel/assets/{DID-CL6mR6zX.js → DID-6pue0NCO.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-oz9n6DC7.js → Dashboard-BwkJ29du.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CbWm8mWu.js → Dropdown-DEvpYDof.js} +1 -1
- package/src/assets/web-panel/assets/EmailListRenderer-CQj7UlKJ.js +1 -0
- package/src/assets/web-panel/assets/{EvolutionSettings-DPwqgRYm.js → EvolutionSettings-tpvPMM9W.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DkV4YMFj.js → FamilyGuardDashboard-CWYFW75O.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CpUKHIOH.js → Federation-gRIi96rM.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C6KXRgjR.js → FormItemContext-B7Nr3YkV.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-C5Y4L5E3.js → GenericCardRenderer-b1BgGgLT.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CBwKdF8s.js → Git-Dis1jjO7.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Dt-OR7Aa.js → Governance-C4Yg99Am.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Czkl-Lhl.js → Inference-D4UQRbGu.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-C7NE4949.js → KnowledgeGraph-Co_kLMAq.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CoAuZiMG.js → Logs-D4BRp7bg.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DTkjTwGI.js → MarkdownRenderer-Dgy8arQI.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-IzrV-7zH.js → Marketplace-ys0j1vr9.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DYEea7aS.js → McpTools-B3ZvRZe_.js} +2 -2
- package/src/assets/web-panel/assets/{Memory-Uqe7ZrV6.js → Memory-BOa0jbr-.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D092EGyM.js → MobileBridge-CWJRmUbj.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-7ty8vTpj.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-gg4jzfB-.js → Mtc-CCcOk5m5.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BibGfs71.js → MtcAudit-vlrrScbQ.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BmB5yRXP.js → Multisig-C8K59S_8.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BBSlPDyY.js → NLProgramming-oTpFYVme.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Bui3-KiC.js → Notes-BhqAJWiz.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-ovXtbHe5.js → NotificationSettings-DCnV1CJM.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Bq-qD7eG.js → OrderTableRenderer-CZGnsrVW.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DacIY9oJ.js → Organization-DX1LuQlJ.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CX8osheZ.js → Overflow-CbfKrFdT.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-1lnikqVp.js → P2P-BEwYZuHL.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DCrU-V6A.js → PdhVaultBrowser-YNmWbwd7.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Dfcb6kM2.js → Permissions-DyZ18LiA.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-dvgai4hf.js → PersonalDataHub-B2J3aw2g.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-nymAEtnK.js → Pipeline-CXl2ZlhQ.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-D0Vd91oM.js → Privacy-I5SDmgAp.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BpUBcu7o.js → ProjectInit-Crl9tXw3.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DIzwTT-M.js → ProjectSettings-DCFtFlSK.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Den4RUw6.js → Projects-Do5BQq1i.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Cq8p-M0d.js → Providers-Cb7OWa3y.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DSmqOfYk.js → QrScannerModal-CJmhAU-s.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BDHCeSnA.js → QuickAsk-Cu6lEWou.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-B_9M8oF4.js → Recommend-Crip3jLB.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-5ot4leHy.js → RemoteSession-KxYY-2CB.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-B3F013PY.js → Reputation-D0vN-htI.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BMyBJnZw.js → Row-wqzd_xCD.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-WmV0l52g.js → RssFeed-8lsPfb0a.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Wl80yXnR.js → Search-C-e8qAeU.js} +1 -1
- package/src/assets/web-panel/assets/{Security-gbDMKb5m.js → Security-CMM9WgFy.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Bx2Bjhah.js → Services-CZdxjN2B.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BvJNybjV.js → Skeleton-BI3XNk35.js} +1 -1
- package/src/assets/web-panel/assets/Skills-DMlsh9It.js +1 -0
- package/src/assets/web-panel/assets/{Sla-BM9gyZwc.js → Sla-HQruznKY.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-Bu9FL9Tz.js → SpeechSettings-Dl-MFq3w.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D8ICJsMt.js → SyncSettings-BU224vDm.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-DESg3yCs.js +1 -0
- package/src/assets/web-panel/assets/{Templates-BJzpTD0W.js → Templates-Jb3TPz4Q.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BN8eX4R2.js → Tenant-CQ-GTrfa.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CjtP9bpq.js → Terminal-BbMl3Rue.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CpNRa4XW.js → TimelineRenderer-CS_vstKF.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-2lNNvNAH.js → Tokens-BaopVl1f.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DxtxXyPg.js → Trigger-DtnD4WUN.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CakzGeF9.js → Trust-CsvG4Tmf.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CvJeRXwV.js → UkeySign-C1-A-cOV.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D80pMzcO.js → VideoEditing-CNgJPdSL.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BF-VOBmZ.js → Wallet-CdF41501.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DXyoAzE_.js → WebAuthn-D-JmxD5g.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-X6CDxzrF.js → WorkflowEditor-C0XunR13.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BcUhqjeA.js → chat-BBAZpD_G.js} +1 -1
- package/src/assets/web-panel/assets/{colors-Dw4bKaCm.js → colors-BxSxO5BX.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BWhu5Eri.js → compact-item-BoxL8pg5.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BLJ9JKoN.js → createContext-W5JAyZmY.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BI5TZ7EX.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DIkCiJbt.js → hasIn-HpiKDPVC.js} +1 -1
- package/src/assets/web-panel/assets/index-9-JLDK5n.js +1 -0
- package/src/assets/web-panel/assets/{index-BAwcMKaD.js → index-9caMEwQ-.js} +1 -1
- package/src/assets/web-panel/assets/{index-0mHbawfQ.js → index-B05_YRHZ.js} +1 -1
- package/src/assets/web-panel/assets/index-B7m8ioaQ.js +1 -0
- package/src/assets/web-panel/assets/{index-D3wp_jgy.js → index-B9VxE1C-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BotMuGS2.js → index-BA3WWQAT.js} +1 -1
- package/src/assets/web-panel/assets/{index-LOsZYbic.js → index-BSJ0cAle.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5qms36i.js → index-BW4PP5GN.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDfB6hWf.js → index-BZAbaY64.js} +1 -1
- package/src/assets/web-panel/assets/{index-BipG-tDs.js → index-Ba4usRFM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgsVg5Up.js → index-BgMViZIY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CTPoBbIR.js → index-BtFAEG08.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKzG3kBM.js → index-Bwn6k_qw.js} +1 -1
- package/src/assets/web-panel/assets/{index-PRYboDFm.js → index-C4uI7WCB.js} +1 -1
- package/src/assets/web-panel/assets/{index-DQbWGvE4.js → index-C5kqii-G.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1fufz1A.js → index-C7GIUWWF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjEP26Te.js → index-CM_gdohF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcTch6Fp.js → index-CRg3aOoj.js} +1 -1
- package/src/assets/web-panel/assets/{index-04SKBH8l.js → index-CcyzZloJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHLW27Je.js → index-CehxfRUR.js} +1 -1
- package/src/assets/web-panel/assets/{index-5vzPcr1g.js → index-D6m6B4Wy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DV4FSjmI.js → index-DSyfxxQS.js} +3 -3
- package/src/assets/web-panel/assets/{index-D5vFJmmX.js → index-DaJk9U9V.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxLDNxEQ.js → index-DcmAiq_Y.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-Erv8A9.js → index-DdEcQ1rp.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqGqoID5.js → index-DinY7IQa.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGMzh2oj.js → index-DkSnlgK_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxfimJOH.js → index-DlPvZgv8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOjQi7oU.js → index-DuM9I1If.js} +1 -1
- package/src/assets/web-panel/assets/{index-uMtx6e3D.js → index-DwidpDEE.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXJ8t1hK.js → index-IO2I7nzu.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cmc7qH2C.js → index-JrMBRi62.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaStonJm.js → index-QzlEbtRL.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cr05twNa.js → index-V6VIJe2N.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGFqk1-E.js → index-_38IJqoP.js} +1 -1
- package/src/assets/web-panel/assets/{index-u0tqS0iq.js → index-_p4e3KfB.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1r2R4a-.js → index-gvfIRZxJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFGOV-YT.js → index-ooP1pQCt.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUC4TZWg.js → index-p6rNtYud.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Bx6Hj_FG.js → initDefaultProps-Dfn6M05J.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BSWivB70.js → motion-BRRwFqGO.js} +1 -1
- package/src/assets/web-panel/assets/{move-Dv1Pg9R7.js → move-DpDPN6zR.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-Bqtl8fsk.js → mtc-parser-BHlSE87F.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DpOBjOSQ.js → omit-CEOri_L5.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D-M-dz48.js → pickAttrs-DqICh-ad.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DsTLAifk.js → placementArrow-DQizFFiV.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BVTetMpc.js → responsiveObserve-DmFHPdvC.js} +1 -1
- package/src/assets/web-panel/assets/{slide-abEcz-Cv.js → slide-s2s1XS3T.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CBhrdU2G.js → statusUtils-IwEIbBan.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CFq8ZCKa.js → styleChecker-CIFLlbXH.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-o4eE7fDV.js → useFlexGapSupport-CFkN2Cgh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-Hs9W8ihi.js → useFs-BSY6O48s.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Bnet5Jho.js → usePersonalDataHub-iJl3VnU0.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DJfN4kWs.js → vnode-vu8ReMs7.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BzV-2e7X.js → zoom-CeSrYAHp.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/commands/agent.js +20 -20
- package/src/commands/goal.js +299 -1
- package/src/commands/plugin.js +129 -68
- package/src/constants.js +2 -2
- package/src/data/changelog.json +29 -3
- package/src/harness/jsonl-session-store.js +128 -5
- package/src/harness/prompt-compressor.js +18 -10
- package/src/harness/structured-handoff.js +151 -13
- package/src/lib/agent-session-export.js +20 -0
- package/src/lib/background-agent-supervisor.js +54 -0
- package/src/lib/cli-goal-catalog.js +129 -0
- package/src/lib/cli-goal-source.js +119 -0
- package/src/lib/coding-task-guidance.js +2 -1
- package/src/lib/config-schema.js +1 -1
- package/src/lib/context-memory-kernel/live-compaction.js +75 -12
- package/src/lib/eval/stream-terminal.js +27 -2
- package/src/lib/evolution/evolution-artifact-ports.js +22 -11
- package/src/lib/evolution/evolution-file-witness.js +86 -63
- package/src/lib/evolution/evolution-ledger-file-backend.js +22 -0
- package/src/lib/evolution/evolution-ledger-file-manifest-catalog.js +34 -35
- package/src/lib/evolution/evolution-ledger-file-manifest-head-store.js +32 -39
- package/src/lib/evolution/evolution-ledger.js +367 -169
- package/src/lib/evolution/evolution-manifest-state-reader.js +58 -0
- package/src/lib/evolution/evolution-physical-state-reader.js +308 -0
- package/src/lib/evolution/evolution-witness-segments.js +12 -13
- package/src/lib/evolution/governed-wiki-pruning-ledger-adapter.js +82 -56
- package/src/lib/evolution/rrsi-observed-text-index.js +17 -1
- package/src/lib/evolution/rrsi-tenant-index-anchor-history.js +5 -0
- package/src/lib/execution-location-failure-site.js +20 -3
- package/src/lib/goal-store.js +8 -1
- package/src/lib/image-input.js +43 -14
- package/src/lib/llm-providers.js +1 -0
- package/src/lib/plugin-runtime/capabilities.js +10 -0
- package/src/lib/plugin-runtime/cli-goal-draft-request.js +138 -0
- package/src/lib/plugin-runtime/cli-goal-drafts.js +413 -0
- package/src/lib/plugin-runtime/cli-goal-extension-host.js +234 -0
- package/src/lib/plugin-runtime/examples/goal-description-drafter/plugin.json +17 -0
- package/src/lib/plugin-runtime/examples/goal-evidence-viewer/plugin.json +25 -0
- package/src/lib/plugin-runtime/examples/goal-progress-viewer/plugin.json +21 -0
- package/src/lib/plugin-runtime/examples/project-delivery-assistant/plugin.json +38 -0
- package/src/lib/plugin-runtime/goal-extension-contract.js +190 -0
- package/src/lib/plugin-runtime/goal-extensions.js +277 -0
- package/src/lib/plugin-runtime/install.js +40 -40
- package/src/lib/plugin-runtime/manifest.js +12 -0
- package/src/lib/plugin-runtime/policy.js +1 -0
- package/src/lib/project-goal-connection.js +225 -0
- package/src/lib/project-goal-draft-connection.js +217 -0
- package/src/lib/read-file-loop-guard.js +124 -8
- package/src/lib/session-host-snapshot.js +3 -0
- package/src/lib/session-message-provenance.js +24 -5
- package/src/lib/session-tool-round.js +39 -0
- package/src/lib/session-transcript-history.js +2 -0
- package/src/lib/session-transcript-page.js +4 -1
- package/src/lib/session-transcript-structure.js +4 -0
- package/src/lib/task-model-selector.js +1 -1
- package/src/repl/agent-repl.js +14 -7
- package/src/repl/config-summary.js +1 -1
- package/src/runtime/__tests__/message-roles.test.js +29 -0
- package/src/runtime/agent-core.js +116 -44
- package/src/runtime/headless-runner.js +191 -34
- package/src/runtime/headless-stream.js +144 -48
- package/src/runtime/message-roles.js +43 -0
- package/src/runtime/policies/agent-policy.js +1 -0
- package/src/workers/background-agent-worker.js +146 -47
- package/src/assets/web-panel/assets/EmailListRenderer-DXlmbC45.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-CwA1VoVJ.js +0 -1
- package/src/assets/web-panel/assets/Skills-Tz4rQcDp.js +0 -1
- package/src/assets/web-panel/assets/Tasks-B6MowBDo.js +0 -1
- package/src/assets/web-panel/assets/devWarning-D_MJgDGJ.js +0 -1
- package/src/assets/web-panel/assets/index-BP8log78.js +0 -1
- package/src/assets/web-panel/assets/index-PexF9zYf.js +0 -1
|
@@ -13,6 +13,11 @@ import {
|
|
|
13
13
|
import { JsonlSessionContextPort } from "./jsonl-session-context-port.js";
|
|
14
14
|
import { createCliContextMemoryRuntime } from "./runtime.js";
|
|
15
15
|
import { projectRuntimeTokenUsage } from "../runtime-usage-ledger.js";
|
|
16
|
+
import {
|
|
17
|
+
projectCanonicalResumeMessages,
|
|
18
|
+
getDurableSystemMessageProvenance,
|
|
19
|
+
isRuntimeOnlySessionMessage,
|
|
20
|
+
} from "../session-message-provenance.js";
|
|
16
21
|
|
|
17
22
|
const rejectedCompactionUsage = new WeakMap();
|
|
18
23
|
|
|
@@ -138,7 +143,7 @@ class CallbackSessionContextPort extends InMemorySessionContextPort {
|
|
|
138
143
|
}
|
|
139
144
|
}
|
|
140
145
|
|
|
141
|
-
function kernelBudget(compressor) {
|
|
146
|
+
function kernelBudget(compressor, ephemeralTokens = 0) {
|
|
142
147
|
const inputBudget = Math.max(
|
|
143
148
|
256,
|
|
144
149
|
Math.trunc(Number(compressor?.maxTokens)) || 16_384,
|
|
@@ -150,7 +155,9 @@ function kernelBudget(compressor) {
|
|
|
150
155
|
return {
|
|
151
156
|
modelWindowTokens: inputBudget + reservedOutputTokens,
|
|
152
157
|
reservedOutputTokens,
|
|
153
|
-
|
|
158
|
+
// Live host policy/retry text still consumes the provider window even
|
|
159
|
+
// though it is deliberately absent from the durable authority partition.
|
|
160
|
+
safetyMarginTokens: ephemeralTokens,
|
|
154
161
|
recoveryReserveTokens: Math.min(
|
|
155
162
|
256,
|
|
156
163
|
Math.max(1, Math.floor(inputBudget * 0.05)),
|
|
@@ -202,7 +209,7 @@ export async function compactLiveMessagesCanonical(
|
|
|
202
209
|
messagesInput,
|
|
203
210
|
options = {},
|
|
204
211
|
) {
|
|
205
|
-
const
|
|
212
|
+
const liveMessages = Array.isArray(messagesInput) ? [...messagesInput] : [];
|
|
206
213
|
const sessionId = String(options.sessionId || `live-${randomUUID()}`);
|
|
207
214
|
const operationId = String(options.operationId || `compact-${randomUUID()}`);
|
|
208
215
|
const trigger = options.trigger || "auto";
|
|
@@ -224,6 +231,24 @@ export async function compactLiveMessagesCanonical(
|
|
|
224
231
|
options.persist !== false &&
|
|
225
232
|
Boolean(options.sessionId) &&
|
|
226
233
|
sessionExists(sessionId);
|
|
234
|
+
// Host policy is live-only. Durable summary/lineage systems retain their
|
|
235
|
+
// private provenance and remain part of the exact authority comparison.
|
|
236
|
+
const messages = persistentSession
|
|
237
|
+
? projectCanonicalResumeMessages(liveMessages, { strict: true })
|
|
238
|
+
: liveMessages;
|
|
239
|
+
const ephemeralMessages = [];
|
|
240
|
+
if (persistentSession) {
|
|
241
|
+
let sequence = 0;
|
|
242
|
+
for (const message of liveMessages) {
|
|
243
|
+
if (
|
|
244
|
+
isRuntimeOnlySessionMessage(message) ||
|
|
245
|
+
(message.role === "system" &&
|
|
246
|
+
!getDurableSystemMessageProvenance(message))
|
|
247
|
+
)
|
|
248
|
+
ephemeralMessages.push({ sequence, message });
|
|
249
|
+
else sequence += 1;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
227
252
|
if (typeof options.commit === "function") {
|
|
228
253
|
const bootstrapRuntime = createCliContextMemoryRuntime({
|
|
229
254
|
sessionId,
|
|
@@ -323,12 +348,29 @@ export async function compactLiveMessagesCanonical(
|
|
|
323
348
|
const provider = options.provider || "provider.local";
|
|
324
349
|
const model = options.model || "default";
|
|
325
350
|
const memoryRevision = await runtime.memoryPort.getRevision();
|
|
351
|
+
let settledOutputItems = null;
|
|
352
|
+
if (ephemeralMessages.length) {
|
|
353
|
+
const appendCompaction = runtime.sessionPort.appendCompaction.bind(
|
|
354
|
+
runtime.sessionPort,
|
|
355
|
+
);
|
|
356
|
+
runtime.sessionPort.appendCompaction = async (event, expectedHead) => {
|
|
357
|
+
const result = await appendCompaction(event, expectedHead);
|
|
358
|
+
if (result?.ok === true) settledOutputItems = event.outputItems;
|
|
359
|
+
return result;
|
|
360
|
+
};
|
|
361
|
+
}
|
|
326
362
|
let receipt;
|
|
327
363
|
try {
|
|
328
364
|
receipt = await runtime.kernel.compactContext({
|
|
329
365
|
operationId,
|
|
330
366
|
sessionId,
|
|
331
|
-
...kernelBudget(
|
|
367
|
+
...kernelBudget(
|
|
368
|
+
compressor,
|
|
369
|
+
messagesToContextItems(
|
|
370
|
+
ephemeralMessages.map(({ message }) => message),
|
|
371
|
+
{ sessionId },
|
|
372
|
+
).reduce((total, item) => total + item.tokenEstimate, 0),
|
|
373
|
+
),
|
|
332
374
|
sink: provider,
|
|
333
375
|
scopeAdmissions: [{ scope: "session", scopeId: sessionId }],
|
|
334
376
|
policyVersion: "cli-context-policy-v1",
|
|
@@ -421,7 +463,7 @@ export async function compactLiveMessagesCanonical(
|
|
|
421
463
|
}
|
|
422
464
|
if (!["committed", "degraded"].includes(receipt.status)) {
|
|
423
465
|
return {
|
|
424
|
-
messages,
|
|
466
|
+
messages: liveMessages,
|
|
425
467
|
stats: {
|
|
426
468
|
...(summaryStats || {}),
|
|
427
469
|
strategy: "none",
|
|
@@ -433,20 +475,41 @@ export async function compactLiveMessagesCanonical(
|
|
|
433
475
|
};
|
|
434
476
|
}
|
|
435
477
|
const snapshot = await runtime.sessionPort.readSnapshot(sessionId);
|
|
436
|
-
|
|
478
|
+
// Reattach host policy and private retry nudges at their durable sequence
|
|
479
|
+
// boundaries. They remain live, never enter the receipt, and do not move a
|
|
480
|
+
// retry instruction behind tool exchanges that happened after that retry.
|
|
481
|
+
const restoredMessages = [];
|
|
482
|
+
let ephemeralIndex = 0;
|
|
483
|
+
for (const item of [...(settledOutputItems || snapshot.items)].sort(
|
|
484
|
+
(left, right) =>
|
|
485
|
+
(left.sourceRef.eventSequence ?? Number.MAX_SAFE_INTEGER) -
|
|
486
|
+
(right.sourceRef.eventSequence ?? Number.MAX_SAFE_INTEGER) ||
|
|
487
|
+
left.itemId.localeCompare(right.itemId, "en"),
|
|
488
|
+
)) {
|
|
489
|
+
const sequence = item.sourceRef.eventSequence ?? Number.MAX_SAFE_INTEGER;
|
|
490
|
+
while (
|
|
491
|
+
ephemeralIndex < ephemeralMessages.length &&
|
|
492
|
+
ephemeralMessages[ephemeralIndex].sequence <= sequence
|
|
493
|
+
)
|
|
494
|
+
restoredMessages.push(ephemeralMessages[ephemeralIndex++].message);
|
|
495
|
+
restoredMessages.push(...contextItemsToMessages([item]));
|
|
496
|
+
}
|
|
497
|
+
restoredMessages.push(
|
|
498
|
+
...ephemeralMessages.slice(ephemeralIndex).map(({ message }) => message),
|
|
499
|
+
);
|
|
437
500
|
return {
|
|
438
|
-
messages:
|
|
501
|
+
messages: restoredMessages,
|
|
439
502
|
stats: {
|
|
440
503
|
...(summaryStats || {}),
|
|
441
504
|
strategy:
|
|
442
505
|
receipt.status === "degraded" ? "deterministic-fallback" : "canonical",
|
|
443
|
-
originalMessages:
|
|
444
|
-
compressedMessages:
|
|
445
|
-
originalTokens: estimatedTokens(
|
|
446
|
-
compressedTokens: estimatedTokens(
|
|
506
|
+
originalMessages: liveMessages.length,
|
|
507
|
+
compressedMessages: restoredMessages.length,
|
|
508
|
+
originalTokens: estimatedTokens(liveMessages),
|
|
509
|
+
compressedTokens: estimatedTokens(restoredMessages),
|
|
447
510
|
saved: Math.max(
|
|
448
511
|
0,
|
|
449
|
-
estimatedTokens(
|
|
512
|
+
estimatedTokens(liveMessages) - estimatedTokens(restoredMessages),
|
|
450
513
|
),
|
|
451
514
|
canonicalAlreadySettled: true,
|
|
452
515
|
canonicalReceipt: receipt,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Parse raw agent NDJSON. A declaration of success is never terminal evidence. */
|
|
2
|
-
export function verifyAgentTerminal(output) {
|
|
2
|
+
export function verifyAgentTerminal(output, { allowSessionEnd = false } = {}) {
|
|
3
3
|
try {
|
|
4
4
|
const events = output
|
|
5
5
|
.split(/\r?\n/u)
|
|
@@ -7,6 +7,31 @@ export function verifyAgentTerminal(output) {
|
|
|
7
7
|
.map((line) => JSON.parse(line));
|
|
8
8
|
const results = events.filter((event) => event?.type === "result");
|
|
9
9
|
const terminal = results[0];
|
|
10
|
+
const last = events.at(-1);
|
|
11
|
+
const init = events.find(
|
|
12
|
+
(event) => event?.type === "system" && event.subtype === "init",
|
|
13
|
+
);
|
|
14
|
+
const sessionEnded =
|
|
15
|
+
allowSessionEnd &&
|
|
16
|
+
events.at(-2) === terminal &&
|
|
17
|
+
last?.type === "system" &&
|
|
18
|
+
last.subtype === "end" &&
|
|
19
|
+
typeof terminal?.session_id === "string" &&
|
|
20
|
+
terminal.session_id.length > 0 &&
|
|
21
|
+
last.session_id === terminal.session_id &&
|
|
22
|
+
init?.session_id === terminal.session_id &&
|
|
23
|
+
typeof terminal.trace_id === "string" &&
|
|
24
|
+
terminal.trace_id.length > 0 &&
|
|
25
|
+
last.trace_id === terminal.trace_id &&
|
|
26
|
+
Number.isSafeInteger(terminal.seq) &&
|
|
27
|
+
terminal.seq > 0 &&
|
|
28
|
+
last.seq === terminal.seq + 1 &&
|
|
29
|
+
Number.isSafeInteger(terminal.turn) &&
|
|
30
|
+
terminal.turn > 0 &&
|
|
31
|
+
last.turns === terminal.turn &&
|
|
32
|
+
events.filter(
|
|
33
|
+
(event) => event?.type === "system" && event.subtype === "end",
|
|
34
|
+
).length === 1;
|
|
10
35
|
const usage = terminal?.usage;
|
|
11
36
|
const normalizedUsage =
|
|
12
37
|
usage &&
|
|
@@ -26,7 +51,7 @@ export function verifyAgentTerminal(output) {
|
|
|
26
51
|
return {
|
|
27
52
|
terminalVerified:
|
|
28
53
|
results.length === 1 &&
|
|
29
|
-
|
|
54
|
+
(last === terminal || sessionEnded) &&
|
|
30
55
|
terminal.subtype === "success" &&
|
|
31
56
|
terminal.is_error === false &&
|
|
32
57
|
!events.some((event) => event?.type === "error"),
|
|
@@ -23,6 +23,11 @@ import {
|
|
|
23
23
|
} from "./evolution-ledger.js";
|
|
24
24
|
|
|
25
25
|
const ARTIFACT_STORE_DIRECTORY_BOUNDARIES = new WeakMap();
|
|
26
|
+
// Capture repository function identity before binding instance methods. Only
|
|
27
|
+
// these exact defaults may skip legacy list/get I/O; overrides still execute
|
|
28
|
+
// and must match the independently authenticated descriptor snapshots.
|
|
29
|
+
const DEFAULT_STORE_LIST = ArtifactStore.prototype.list;
|
|
30
|
+
const DEFAULT_STORE_GET = ArtifactStore.prototype.get;
|
|
26
31
|
// Only the shared identity helper's directory opens use this adapter. A POSIX
|
|
27
32
|
// directory/FIFO swap must fail before reaching a potentially blocking open.
|
|
28
33
|
const INDEX_DIRECTORY_FS = Object.freeze({
|
|
@@ -1014,7 +1019,7 @@ function normalizeKeyId(value, label) {
|
|
|
1014
1019
|
return value;
|
|
1015
1020
|
}
|
|
1016
1021
|
|
|
1017
|
-
function
|
|
1022
|
+
function captureDataMethod(port, method, label) {
|
|
1018
1023
|
if (!port || (typeof port !== "object" && typeof port !== "function")) {
|
|
1019
1024
|
throw new TypeError(`EvolutionArtifactPorts requires ${label}.${method}`);
|
|
1020
1025
|
}
|
|
@@ -1029,13 +1034,17 @@ function captureMethod(port, method, label) {
|
|
|
1029
1034
|
`EvolutionArtifactPorts requires ${label}.${method} as a data method`,
|
|
1030
1035
|
);
|
|
1031
1036
|
}
|
|
1032
|
-
return
|
|
1037
|
+
return descriptor.value;
|
|
1033
1038
|
}
|
|
1034
1039
|
cursor = Object.getPrototypeOf(cursor);
|
|
1035
1040
|
}
|
|
1036
1041
|
throw new TypeError(`EvolutionArtifactPorts requires ${label}.${method}`);
|
|
1037
1042
|
}
|
|
1038
1043
|
|
|
1044
|
+
function captureMethod(port, method, label) {
|
|
1045
|
+
return Object.freeze(captureDataMethod(port, method, label).bind(port));
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1039
1048
|
function captureArtifactStore(store) {
|
|
1040
1049
|
rejectProxy(store, "artifactStore");
|
|
1041
1050
|
if (!hasStablePrototype(store, ArtifactStore.prototype, "artifactStore")) {
|
|
@@ -1049,19 +1058,21 @@ function captureArtifactStore(store) {
|
|
|
1049
1058
|
}
|
|
1050
1059
|
const resolvedDir = path.resolve(dir);
|
|
1051
1060
|
const layout = establishStoreLayout(resolvedDir);
|
|
1061
|
+
const list = captureDataMethod(store, "list", "artifactStore");
|
|
1062
|
+
const get = captureDataMethod(store, "get", "artifactStore");
|
|
1052
1063
|
const captured = Object.freeze({
|
|
1053
1064
|
dir: resolvedDir,
|
|
1054
|
-
get:
|
|
1065
|
+
get: get === DEFAULT_STORE_GET ? null : Object.freeze(get.bind(store)),
|
|
1055
1066
|
layout,
|
|
1056
|
-
list:
|
|
1067
|
+
list: list === DEFAULT_STORE_LIST ? null : Object.freeze(list.bind(store)),
|
|
1057
1068
|
publishDataOnce: captureMethod(store, "publishDataOnce", "artifactStore"),
|
|
1058
1069
|
storedPath: captureMethod(store, "storedPath", "artifactStore"),
|
|
1059
1070
|
verifyIntegrity: captureMethod(store, "verifyIntegrity", "artifactStore"),
|
|
1060
1071
|
});
|
|
1061
1072
|
// ArtifactStore methods consult public instance fields such as `dir` on
|
|
1062
1073
|
// every call. Freeze the trusted instance after capturing its methods so a
|
|
1063
|
-
// later collaborator cannot redirect a bound get/list/integrity call
|
|
1064
|
-
// different store root.
|
|
1074
|
+
// later collaborator cannot redirect a bound custom get/list/integrity call
|
|
1075
|
+
// to a different store root. Null index methods select trusted snapshots.
|
|
1065
1076
|
Object.freeze(store);
|
|
1066
1077
|
return captured;
|
|
1067
1078
|
}
|
|
@@ -2164,7 +2175,7 @@ export class EvolutionArtifactPorts {
|
|
|
2164
2175
|
let entries;
|
|
2165
2176
|
try {
|
|
2166
2177
|
entries = rejectPromise(
|
|
2167
|
-
this.#store.list(),
|
|
2178
|
+
this.#store.list ? this.#store.list() : trustedBefore.entries,
|
|
2168
2179
|
"ArtifactStore.list",
|
|
2169
2180
|
EVOLUTION_ARTIFACT_STORE_FAILED_CODE,
|
|
2170
2181
|
);
|
|
@@ -2187,9 +2198,9 @@ export class EvolutionArtifactPorts {
|
|
|
2187
2198
|
"ArtifactStore.list did not return an array",
|
|
2188
2199
|
);
|
|
2189
2200
|
}
|
|
2190
|
-
//
|
|
2191
|
-
//
|
|
2192
|
-
//
|
|
2201
|
+
// Default entries already came from a bounded descriptor read. Custom
|
|
2202
|
+
// list hooks retain their original execution and exact cross-check; their
|
|
2203
|
+
// own I/O cannot be constrained by this adapter's snapshot byte limit.
|
|
2193
2204
|
if (entries.length > EVOLUTION_ARTIFACT_MAX_INDEX_ENTRIES) {
|
|
2194
2205
|
throw artifactError(
|
|
2195
2206
|
EVOLUTION_ARTIFACT_STORE_FAILED_CODE,
|
|
@@ -2280,7 +2291,7 @@ export class EvolutionArtifactPorts {
|
|
|
2280
2291
|
let current;
|
|
2281
2292
|
try {
|
|
2282
2293
|
current = rejectPromise(
|
|
2283
|
-
this.#store.get(artifactId),
|
|
2294
|
+
this.#store.get ? this.#store.get(artifactId) : listed[0],
|
|
2284
2295
|
"ArtifactStore.get",
|
|
2285
2296
|
EVOLUTION_ARTIFACT_STORE_FAILED_CODE,
|
|
2286
2297
|
);
|
|
@@ -4,8 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { types } from "node:util";
|
|
5
5
|
import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
|
|
6
6
|
import { withFileLock } from "../with-file-lock.js";
|
|
7
|
-
import {
|
|
8
|
-
import { withEvolutionFileIdentity } from "./evolution-file-identity.js";
|
|
7
|
+
import { createEvolutionPhysicalStateReader } from "./evolution-physical-state-reader.js";
|
|
9
8
|
import {
|
|
10
9
|
createEvolutionWitnessSegments,
|
|
11
10
|
EVOLUTION_SEGMENTED_WITNESS_SCHEMA,
|
|
@@ -17,6 +16,8 @@ import {
|
|
|
17
16
|
|
|
18
17
|
export const EVOLUTION_FILE_WITNESS_STORE_SCHEMA =
|
|
19
18
|
"chainlesschain.evolution-file-witness-store/v1";
|
|
19
|
+
export const EVOLUTION_WITNESS_STATE_READ_FAILED_CODE =
|
|
20
|
+
"CC_EVOLUTION_WITNESS_STATE_READ_FAILED";
|
|
20
21
|
|
|
21
22
|
const WITNESS_DOMAIN = `${EVOLUTION_LEDGER_WITNESS_SCHEMA}\0`;
|
|
22
23
|
const ANCESTRY_DOMAIN = `${EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA}\0`;
|
|
@@ -247,6 +248,63 @@ export function createEvolutionFileWitness({
|
|
|
247
248
|
}
|
|
248
249
|
const secureOptions = { deps: { fs: fsImpl }, failIfUnavailable: true };
|
|
249
250
|
ensurePrivateDirectory(directory, secureOptions);
|
|
251
|
+
const readerOptions = {
|
|
252
|
+
fsImpl,
|
|
253
|
+
maximumBytes,
|
|
254
|
+
minimumBytes: 2,
|
|
255
|
+
directoryMode: process.platform === "win32" ? null : 0o700n,
|
|
256
|
+
sizeError: "store size is invalid",
|
|
257
|
+
label: "witness",
|
|
258
|
+
errorCode: EVOLUTION_WITNESS_STATE_READ_FAILED_CODE,
|
|
259
|
+
readFailureMessage: "witness bounded read failed",
|
|
260
|
+
};
|
|
261
|
+
const rootReader = createEvolutionPhysicalStateReader({
|
|
262
|
+
...readerOptions,
|
|
263
|
+
directoryPath: directory,
|
|
264
|
+
});
|
|
265
|
+
const segmentDirectory = `${target}.segments-v2`;
|
|
266
|
+
let segmentReader = null;
|
|
267
|
+
const captureSegments = () => {
|
|
268
|
+
rootReader.recheck();
|
|
269
|
+
segmentReader = createEvolutionPhysicalStateReader({
|
|
270
|
+
...readerOptions,
|
|
271
|
+
label: "witness segment",
|
|
272
|
+
readFailureMessage: "witness segment bounded read failed",
|
|
273
|
+
directoryPath: segmentDirectory,
|
|
274
|
+
});
|
|
275
|
+
rootReader.recheck();
|
|
276
|
+
return segmentReader;
|
|
277
|
+
};
|
|
278
|
+
// A constructor may secure existing local storage. Read paths never create
|
|
279
|
+
// or repair a missing segment directory; first publication retains that job.
|
|
280
|
+
let existingSegments = false;
|
|
281
|
+
try {
|
|
282
|
+
fsImpl.lstatSync(segmentDirectory, { bigint: true });
|
|
283
|
+
existingSegments = true;
|
|
284
|
+
} catch (cause) {
|
|
285
|
+
if (cause?.code !== "ENOENT") throw cause;
|
|
286
|
+
}
|
|
287
|
+
if (existingSegments) {
|
|
288
|
+
ensurePrivateDirectory(segmentDirectory, secureOptions);
|
|
289
|
+
captureSegments();
|
|
290
|
+
}
|
|
291
|
+
const checkSegments = () => {
|
|
292
|
+
const reader = segmentReader ?? captureSegments();
|
|
293
|
+
rootReader.recheck();
|
|
294
|
+
reader.recheck();
|
|
295
|
+
return reader;
|
|
296
|
+
};
|
|
297
|
+
const prepareSegments = () => {
|
|
298
|
+
rootReader.recheck();
|
|
299
|
+
if (segmentReader !== null) return segmentReader.recheck();
|
|
300
|
+
ensurePrivateDirectory(segmentDirectory, secureOptions);
|
|
301
|
+
captureSegments();
|
|
302
|
+
};
|
|
303
|
+
const recheckStorage = () => {
|
|
304
|
+
rootReader.recheck();
|
|
305
|
+
segmentReader?.recheck();
|
|
306
|
+
rootReader.recheck();
|
|
307
|
+
};
|
|
250
308
|
// Cache immutable encodings, never an authority decision. Keep the earliest
|
|
251
309
|
// entries when full so full-history scans cannot churn the bounded cache.
|
|
252
310
|
const recordEncodings = new Map();
|
|
@@ -587,67 +645,25 @@ export function createEvolutionFileWitness({
|
|
|
587
645
|
return store;
|
|
588
646
|
};
|
|
589
647
|
|
|
590
|
-
const readBytes = (file, byteLimit = maximumBytes) =>
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
),
|
|
597
|
-
);
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
ensurePrivateFile(file, secureOptions);
|
|
601
|
-
const stat = fsImpl.lstatSync(file);
|
|
602
|
-
if (
|
|
603
|
-
!stat.isFile() ||
|
|
604
|
-
stat.isSymbolicLink() ||
|
|
605
|
-
stat.nlink !== 1 ||
|
|
606
|
-
stat.size < 2 ||
|
|
607
|
-
stat.size > byteLimit
|
|
608
|
-
) {
|
|
609
|
-
throw new Error("witness store size is invalid");
|
|
610
|
-
}
|
|
611
|
-
let descriptor;
|
|
612
|
-
const sameFile = (observed) =>
|
|
613
|
-
observed.isFile() &&
|
|
614
|
-
observed.nlink === 1 &&
|
|
615
|
-
observed.dev === stat.dev &&
|
|
616
|
-
observed.ino === stat.ino &&
|
|
617
|
-
observed.size === stat.size &&
|
|
618
|
-
observed.mtimeMs === stat.mtimeMs &&
|
|
619
|
-
observed.ctimeMs === stat.ctimeMs;
|
|
620
|
-
try {
|
|
621
|
-
descriptor = fsImpl.openSync(
|
|
622
|
-
file,
|
|
623
|
-
fsImpl.constants.O_RDONLY | (fsImpl.constants.O_NOFOLLOW || 0),
|
|
624
|
-
);
|
|
625
|
-
if (!samePathHandle(stat, fsImpl.fstatSync(descriptor))) {
|
|
626
|
-
throw new Error("witness store changed while opening");
|
|
627
|
-
}
|
|
628
|
-
const bytes = readBoundedDescriptor(
|
|
629
|
-
fsImpl,
|
|
630
|
-
descriptor,
|
|
631
|
-
stat.size,
|
|
632
|
-
byteLimit,
|
|
633
|
-
);
|
|
634
|
-
const afterPath = fsImpl.lstatSync(file);
|
|
635
|
-
if (
|
|
636
|
-
!samePathHandle(stat, fsImpl.fstatSync(descriptor)) ||
|
|
637
|
-
afterPath.isSymbolicLink() ||
|
|
638
|
-
!sameFile(afterPath)
|
|
639
|
-
) {
|
|
640
|
-
throw new Error("witness store changed while reading");
|
|
641
|
-
}
|
|
642
|
-
return bytes;
|
|
643
|
-
} finally {
|
|
644
|
-
if (descriptor !== undefined) fsImpl.closeSync(descriptor);
|
|
645
|
-
}
|
|
648
|
+
const readBytes = (file, byteLimit = maximumBytes, allowAbsent = false) => {
|
|
649
|
+
recheckStorage();
|
|
650
|
+
const reader = file === target ? rootReader : checkSegments();
|
|
651
|
+
const bytes = reader.read(file, {
|
|
652
|
+
maximumBytes: byteLimit,
|
|
653
|
+
allowAbsent,
|
|
654
|
+
prepareFile: (name) => ensurePrivateFile(name, secureOptions),
|
|
655
|
+
});
|
|
656
|
+
recheckStorage();
|
|
657
|
+
return bytes;
|
|
646
658
|
};
|
|
647
659
|
|
|
648
660
|
const readStore = (options = {}) => {
|
|
649
|
-
|
|
650
|
-
|
|
661
|
+
const bytes = readBytes(target, maximumHistoryBytes, true);
|
|
662
|
+
if (bytes === null) {
|
|
663
|
+
const store = emptyStore();
|
|
664
|
+
recheckStorage();
|
|
665
|
+
return store;
|
|
666
|
+
}
|
|
651
667
|
const parsed = JSON.parse(
|
|
652
668
|
new TextDecoder("utf-8", { fatal: true }).decode(bytes),
|
|
653
669
|
);
|
|
@@ -660,7 +676,10 @@ export function createEvolutionFileWitness({
|
|
|
660
676
|
headBytes: bytes.length,
|
|
661
677
|
})
|
|
662
678
|
: validateStore(parsed, trustEpoch);
|
|
663
|
-
if (trustEpoch === currentTrustEpoch())
|
|
679
|
+
if (trustEpoch === currentTrustEpoch()) {
|
|
680
|
+
recheckStorage();
|
|
681
|
+
return store;
|
|
682
|
+
}
|
|
664
683
|
}
|
|
665
684
|
throw new Error("witness verifier trust epoch changed while reading");
|
|
666
685
|
};
|
|
@@ -716,6 +735,8 @@ export function createEvolutionFileWitness({
|
|
|
716
735
|
const segments = createEvolutionWitnessSegments({
|
|
717
736
|
filePath: target,
|
|
718
737
|
fsImpl,
|
|
738
|
+
checkDirectory: checkSegments,
|
|
739
|
+
prepareDirectory: prepareSegments,
|
|
719
740
|
readBytes,
|
|
720
741
|
writeBytes,
|
|
721
742
|
validateStore,
|
|
@@ -760,8 +781,10 @@ export function createEvolutionFileWitness({
|
|
|
760
781
|
return store.current;
|
|
761
782
|
};
|
|
762
783
|
|
|
763
|
-
const underLock = (operation) =>
|
|
764
|
-
|
|
784
|
+
const underLock = (operation) => {
|
|
785
|
+
recheckStorage();
|
|
786
|
+
return lock(target, operation, { failIfUnavailable: true, _fs: fsImpl });
|
|
787
|
+
};
|
|
765
788
|
const discarded = (store, snapshot) =>
|
|
766
789
|
segments.isDiscarded(store, snapshot) ||
|
|
767
790
|
store.discardedAnchors.some(
|
|
@@ -4,6 +4,7 @@ import { createEvolutionFileWitness } from "./evolution-file-witness.js";
|
|
|
4
4
|
import {
|
|
5
5
|
EvolutionLedger,
|
|
6
6
|
captureEvolutionLedgerMigrationSource,
|
|
7
|
+
captureEvolutionLedgerDirectoryBoundary,
|
|
7
8
|
} from "./evolution-ledger.js";
|
|
8
9
|
import { createEvolutionLedgerV2JournalFromFactory } from "./evolution-ledger-v2-journal.js";
|
|
9
10
|
|
|
@@ -12,6 +13,7 @@ export const EVOLUTION_LEDGER_FILE_BACKEND_SCHEMA =
|
|
|
12
13
|
|
|
13
14
|
const BACKENDS = new WeakSet();
|
|
14
15
|
const BACKEND_BINDINGS = new WeakMap();
|
|
16
|
+
const SOURCE_DIRECTORY_BOUNDARIES = new WeakMap();
|
|
15
17
|
|
|
16
18
|
function requiredString(value, label) {
|
|
17
19
|
if (typeof value !== "string" || value.trim() === "") {
|
|
@@ -205,7 +207,10 @@ export function createEvolutionLedgerFileBackend({
|
|
|
205
207
|
ledger,
|
|
206
208
|
witness,
|
|
207
209
|
});
|
|
210
|
+
const sourceDirectoryBoundary =
|
|
211
|
+
captureEvolutionLedgerDirectoryBoundary(sourceLedger);
|
|
208
212
|
BACKENDS.add(backend);
|
|
213
|
+
SOURCE_DIRECTORY_BOUNDARIES.set(backend, sourceDirectoryBoundary);
|
|
209
214
|
BACKEND_BINDINGS.set(
|
|
210
215
|
backend,
|
|
211
216
|
Object.freeze({
|
|
@@ -234,3 +239,20 @@ export function captureEvolutionLedgerFileBackendBinding(value) {
|
|
|
234
239
|
);
|
|
235
240
|
return binding;
|
|
236
241
|
}
|
|
242
|
+
|
|
243
|
+
/** Captured private v1 source even when the public journal is a v2 wrapper. */
|
|
244
|
+
export function captureEvolutionLedgerFileBackendSourceDirectoryBoundary(
|
|
245
|
+
value,
|
|
246
|
+
) {
|
|
247
|
+
if (arguments.length !== 1)
|
|
248
|
+
throw new TypeError(
|
|
249
|
+
"source directory boundary capture takes exactly one original backend",
|
|
250
|
+
);
|
|
251
|
+
const boundary = SOURCE_DIRECTORY_BOUNDARIES.get(value);
|
|
252
|
+
if (!boundary)
|
|
253
|
+
throw new TypeError(
|
|
254
|
+
"a branded file backend source directory boundary is required",
|
|
255
|
+
);
|
|
256
|
+
boundary.recheck();
|
|
257
|
+
return boundary;
|
|
258
|
+
}
|
|
@@ -5,7 +5,10 @@ import { types as utilTypes } from "node:util";
|
|
|
5
5
|
|
|
6
6
|
import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
|
|
7
7
|
import { withFileLock } from "../with-file-lock.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
createEvolutionManifestStateReader,
|
|
10
|
+
captureEvolutionManifestStateReaderDirectoryBoundary,
|
|
11
|
+
} from "./evolution-manifest-state-reader.js";
|
|
9
12
|
import { EVOLUTION_LEDGER_MANIFEST_CATALOG_APPEND_RESULT_SCHEMA } from "./evolution-ledger-manifest-catalog.js";
|
|
10
13
|
|
|
11
14
|
export const EVOLUTION_LEDGER_FILE_MANIFEST_CATALOG_SCHEMA =
|
|
@@ -15,6 +18,7 @@ const STATE_SCHEMA =
|
|
|
15
18
|
"chainlesschain.evolution-ledger-file-manifest-catalog-state/v2";
|
|
16
19
|
const STATE_KEYS = new Set(["manifests", "schema"]);
|
|
17
20
|
const DEFAULT_MAXIMUM_BYTES = 64 * 1024 * 1024;
|
|
21
|
+
const DIRECTORY_BOUNDARIES = new WeakMap();
|
|
18
22
|
|
|
19
23
|
function exactRecord(value, keys, label) {
|
|
20
24
|
if (
|
|
@@ -75,39 +79,13 @@ export function createEvolutionLedgerFileManifestCatalogBackend({
|
|
|
75
79
|
const secureOptions = { deps: { fs: fsImpl }, failIfUnavailable: true };
|
|
76
80
|
ensurePrivateDirectory(directory, secureOptions);
|
|
77
81
|
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
throw new Error("manifest catalog exceeds maximumBytes");
|
|
86
|
-
let descriptor;
|
|
87
|
-
try {
|
|
88
|
-
descriptor = fsImpl.openSync(
|
|
89
|
-
file,
|
|
90
|
-
fsImpl.constants.O_RDONLY | (fsImpl.constants.O_NOFOLLOW || 0),
|
|
91
|
-
);
|
|
92
|
-
const opened = fsImpl.fstatSync(descriptor);
|
|
93
|
-
if (!opened.isFile() || opened.nlink !== 1 || opened.size !== stat.size) {
|
|
94
|
-
throw new Error("manifest catalog changed while opening");
|
|
95
|
-
}
|
|
96
|
-
const bytes = readBoundedDescriptor(
|
|
97
|
-
fsImpl,
|
|
98
|
-
descriptor,
|
|
99
|
-
stat.size,
|
|
100
|
-
maximumBytes,
|
|
101
|
-
);
|
|
102
|
-
const after = fsImpl.lstatSync(file);
|
|
103
|
-
if (after.isSymbolicLink() || after.size !== stat.size) {
|
|
104
|
-
throw new Error("manifest catalog changed while reading");
|
|
105
|
-
}
|
|
106
|
-
return bytes;
|
|
107
|
-
} finally {
|
|
108
|
-
if (descriptor !== undefined) fsImpl.closeSync(descriptor);
|
|
109
|
-
}
|
|
110
|
-
};
|
|
82
|
+
const stateReader = createEvolutionManifestStateReader({
|
|
83
|
+
file,
|
|
84
|
+
fsImpl,
|
|
85
|
+
maximumBytes,
|
|
86
|
+
label: "manifest catalog",
|
|
87
|
+
});
|
|
88
|
+
const readBytes = stateReader.read;
|
|
111
89
|
|
|
112
90
|
const readState = () => {
|
|
113
91
|
const bytes = readBytes();
|
|
@@ -173,13 +151,14 @@ export function createEvolutionLedgerFileManifestCatalogBackend({
|
|
|
173
151
|
}
|
|
174
152
|
};
|
|
175
153
|
|
|
176
|
-
|
|
154
|
+
const backend = Object.freeze({
|
|
177
155
|
descriptor: Object.freeze({
|
|
178
156
|
file,
|
|
179
157
|
localOnly: true,
|
|
180
158
|
schema: EVOLUTION_LEDGER_FILE_MANIFEST_CATALOG_SCHEMA,
|
|
181
159
|
}),
|
|
182
160
|
compareAndAppend(request) {
|
|
161
|
+
stateReader.recheck();
|
|
183
162
|
return lock(
|
|
184
163
|
file,
|
|
185
164
|
() => {
|
|
@@ -215,4 +194,24 @@ export function createEvolutionLedgerFileManifestCatalogBackend({
|
|
|
215
194
|
return structuredClone(readState().manifests.at(-1) ?? null);
|
|
216
195
|
},
|
|
217
196
|
});
|
|
197
|
+
DIRECTORY_BOUNDARIES.set(
|
|
198
|
+
backend,
|
|
199
|
+
captureEvolutionManifestStateReaderDirectoryBoundary(stateReader),
|
|
200
|
+
);
|
|
201
|
+
return backend;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Original local catalog directory only; structural wrappers are not owners. */
|
|
205
|
+
export function captureEvolutionLedgerFileManifestCatalogDirectoryBoundary(
|
|
206
|
+
backend,
|
|
207
|
+
) {
|
|
208
|
+
if (arguments.length !== 1)
|
|
209
|
+
throw new TypeError("catalog directory capture takes one original backend");
|
|
210
|
+
const boundary = DIRECTORY_BOUNDARIES.get(backend);
|
|
211
|
+
if (!boundary)
|
|
212
|
+
throw new TypeError(
|
|
213
|
+
"an original file manifest catalog backend is required",
|
|
214
|
+
);
|
|
215
|
+
boundary.recheck();
|
|
216
|
+
return boundary;
|
|
218
217
|
}
|