chainlesschain 0.163.2 → 0.163.4
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/package.json +3 -2
- package/scripts/command-lifecycle-report.mjs +330 -38
- package/src/assets/web-panel/assets/{AIOps-DnGsmsqJ.js → AIOps-DB5X7qqV.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CrO6fcQ7.js → ActionButton-DxHRtKLk.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DsBdOaP2.js → Analytics-DIH5QrxT.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DmQslKUB.js → AppLayout-uu0oxVz7.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BvyrT_QQ.js → Artifacts-BNSMiW9Y.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-prndoLiC.js → Audit-DcAzB4_x.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DOTFyA55.js → BackgroundAgents-ClSsvjIc.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-9zOnPp_8.js → Backup-BMciY0js.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C4PC6sAG.js → BaseInput-Hn3b57Sh.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BBAF-W9v.js → Chat-CoSlC53V.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CWhvvloO.js → ChatBubbleRenderer-CFevJ3So.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BI9iculd.js → Checkbox-B4L4Eu4w.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-3DP9BCKb.js → Codegen-C77FgRxF.js} +1 -1
- package/src/assets/web-panel/assets/{Col-df-2TnJX.js → Col-BrLtJLH3.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CHs8gG_A.js → Community-Be7pXXhA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CDfF6q7V.js → Compact-DuGPecjo.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CthrDnO7.js → Compliance-CzS66mjP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-HjIqsWJw.js → Cowork-CEhjCoyK.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BKc196fb.js → Cron-D7ir8Vja.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CJnjdc8W.js → Crosschain-BcXmuLxf.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BHUy-8Q2.js → DID-Cto1dtBy.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-QDB7sKeY.js → Dashboard-B7KjgwOX.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DStcVK11.js → Dropdown-COMqxijg.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BwSUskMJ.js → EmailListRenderer-BZrya-nf.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgF_kurT.js → FamilyGuardDashboard-AmVeMq0J.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-k8wvv-wN.js → Federation-kZU8c-27.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C11qoq2r.js → FormItemContext-sOV22v2-.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BIIpr9kg.js → GenericCardRenderer-BUuLtGCz.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BlA1exTe.js → Git-K-ZxsLsH.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CYa1ZYM4.js → Governance-D3OEnawI.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-6_kKVIt6.js → Inference-Bll0gRmP.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CB4AwxYy.js → KnowledgeGraph-DBXwjr53.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-xf7nHFAe.js → Logs-CEzPwEfP.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-Bf3DEqP6.js → MarkdownRenderer-CLmCbHx6.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-aPg8e4Jt.js → Marketplace-BYyRyOPz.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C-TNlVUf.js → McpTools-DVu8w_rM.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-DMTPIpGa.js → Memory-tenuZ_hO.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BxyvveRq.js → MobileBridge-Bxgy4ozS.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DyPCrAyQ.js → MobileProjects-Dgx1D6sc.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BdhqNXp6.js → Mtc-CRfJ8r8j.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Cz-HFxzW.js → MtcAudit-BBJq4Aiz.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CIUaFNsx.js → Multisig-B3ii9lai.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CWe6-J17.js → NLProgramming-FIi-Cubv.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-vYAKeviU.js → Notes-DIq8MPf1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DQL9-2d7.js → NotificationSettings-BFOD9eGd.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DnXj1jBn.js → OrderTableRenderer--41u7vre.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-B3spU8AN.js → Organization-B_0AWztW.js} +2 -2
- package/src/assets/web-panel/assets/{Overflow-DgEwo7vm.js → Overflow-B6I7TuXv.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-hujMRH8X.js → P2P-Cdf6qens.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-C_qhdg9h.js → PdhVaultBrowser-B_0FcQJY.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-vp7LMcSf.js → Permissions-6nFBjsQp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dxr5Hgnq.js → PersonalDataHub-nQYV7xpn.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DEPZqqH4.js → Pipeline-CBVod5jm.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DTpDiKPe.js → Privacy-D52jW1Nt.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-jDJsRWmF.js → ProjectInit-Bdx3BYhf.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Dhor5RWH.js → ProjectSettings-b7xr4DUa.js} +2 -2
- package/src/assets/web-panel/assets/Projects-DhfifCKx.js +1 -0
- package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-CjIw-wIb.js → QrScannerModal-FQKSmc3e.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DX6jcqoi.js → QuickAsk-CSTrhzcF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DopXkhar.js → Recommend-CxMABOUh.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BLvqJE9c.js → RemoteSession-Cv2T8yRa.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DhRj2Me7.js → Reputation-CE1k4Vvu.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DQ53vuPq.js → Row-BNgNcAV_.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BslvwiBD.js → RssFeed-5ZiBZ3ph.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DJKBDNKZ.js → Search-mDOIF1UV.js} +1 -1
- package/src/assets/web-panel/assets/{Security-COhpxHER.js → Security-CtuCTC-e.js} +3 -3
- package/src/assets/web-panel/assets/{Services-dbulD6cI.js → Services-DxFO2Zg7.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-ciIVNfhC.js → Skeleton-CKmKkH9y.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-AiRTkTCk.js → Skills-Bmzl9qkk.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DDMcWYBc.js → Sla-9RqaWHOC.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BPxWbpXO.js → SpeechSettings-CGdzFIYw.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BoEOXCyg.js → SyncSettings-CvNuCeBT.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-B_sLdcnw.js +1 -0
- package/src/assets/web-panel/assets/{Templates-UowY8Ixe.js → Templates-D9_YijdK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DauSLQXN.js → Tenant-Cuc7BUSP.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DBj7Jneq.js → Terminal-6VYRnJgd.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-UMrPwoZC.js → TimelineRenderer-GtK8xd8Y.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-4ea96-bn.js → Tokens-BZ94Lt5W.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger--ISCqt4b.js → Trigger-DEcZgDaX.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D4GVimRJ.js → Trust-CC3z9THe.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Ci-nXRQc.js → UkeySign-Cvtw8cV3.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Cjk7mKPw.js → VideoEditing-CL4e6Bn3.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BgOLOO3Z.js → Wallet-ycEemP27.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CCX0K29i.js → WebAuthn-DgZ4X_aw.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-Ch3zgIsE.js → WorkflowEditor-D0qMhSv7.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DKOU_2Vb.js → chat-DsAg01Zn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-6OVo4u3C.js → colors-B0oqAhrQ.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BH78Hlsq.js → compact-item-CmX0Rabz.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CFRpSbbC.js → createContext-Be4cn1Ub.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DNQhgcVi.js → hasIn-D5v12kVq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkN3iTTp.js → index-0eWLkQKT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkNEMANR.js → index-301ogenv.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3RtTT4K.js → index-5LGY0JbG.js} +1 -1
- package/src/assets/web-panel/assets/{index-dDkoL4Pe.js → index-7VKDn-t1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHa4WyTZ.js → index-8WvYGnON.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaYqY35Z.js → index-B1YGjOWC.js} +1 -1
- package/src/assets/web-panel/assets/{index-bKm3TUDp.js → index-B3mUHuVc.js} +1 -1
- package/src/assets/web-panel/assets/{index--rq90rL9.js → index-BA4oulaP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaDgdJYj.js → index-BBREnn1V.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_Z-YoxX.js → index-BBvyHguy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkqHgG-b.js → index-BKmsS2jj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrcLHN2F.js → index-BRIqLy1m.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaVE6E8y.js → index-BXGSbelg.js} +3 -3
- package/src/assets/web-panel/assets/{index-BYrnm5Ob.js → index-BaE0Ui07.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDSR7ecX.js → index-BoAIAZ5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNuCHHQT.js → index-BqltWojQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Eag8e_mA.js → index-CGiIyTZw.js} +1 -1
- package/src/assets/web-panel/assets/index-CKB0vbJU.js +1 -0
- package/src/assets/web-panel/assets/{index-BepBTpa8.js → index-CZ2wUEgr.js} +1 -1
- package/src/assets/web-panel/assets/{index-HNGGVF6o.js → index-CZpfzcuy.js} +1 -1
- package/src/assets/web-panel/assets/{index-AuBhLqOm.js → index-Ckrk3pIV.js} +1 -1
- package/src/assets/web-panel/assets/{index-7TC3S8SN.js → index-CqoCZ5vR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFPJ8yzF.js → index-CyWBir7N.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bb6kDrUx.js → index-CyyGTMKd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKN2noIF.js → index-D0BSkxYQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Du0rnksC.js → index-D5YVopyr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxRy5LPR.js → index-DKUBj36_.js} +1 -1
- package/src/assets/web-panel/assets/{index-sm7W8AXy.js → index-DM724NgU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BujsOZfc.js → index-DQRsQen0.js} +1 -1
- package/src/assets/web-panel/assets/index-DgoXmwqB.js +1 -0
- package/src/assets/web-panel/assets/{index-Y9MJv28e.js → index-DikJ0Qqe.js} +1 -1
- package/src/assets/web-panel/assets/{index-caye7AOE.js → index-Dn2dSdXx.js} +1 -1
- package/src/assets/web-panel/assets/{index-cdf7fd7d.js → index-DyUaFWEk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnnXQot_.js → index-Hfe_dEGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvHGtNnF.js → index-_JjRLSD0.js} +1 -1
- package/src/assets/web-panel/assets/{index-nbfHl1oV.js → index-frSojpHj.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cu3L4r5l.js → index-jK3ax6Z6.js} +1 -1
- package/src/assets/web-panel/assets/{index-D84id2Y4.js → index-qMuZ-NRR.js} +1 -1
- package/src/assets/web-panel/assets/{index-hPY0fFrd.js → index-q_fQ94SE.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BZ9w6jPL.js → initDefaultProps-D4VdhJ9_.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BnwUiOho.js → motion-CZJA0MQW.js} +1 -1
- package/src/assets/web-panel/assets/{move-ByUKJnNm.js → move-BqEs7Sb1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-V7yK1KEl.js → mtc-parser-BpHEtsu1.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BEXBBt4j.js → omit-BW94tY9W.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-qDm_46Gi.js → pickAttrs-BnBjPWP9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BIbhEo_0.js → placementArrow-uUkjQ_hu.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-NbPbDgA_.js → responsiveObserve-B-klUqXK.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BnUWglZ0.js → slide-CvY1cNAs.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BX8_wZI5.js → statusUtils-Ix5sEDT2.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BV4OycQX.js → styleChecker-C8qcXOne.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Od_H5llY.js → useFlexGapSupport-KNF6Kj8f.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BWI-PyiX.js → useFs-DGJr345n.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BqMuhG3g.js → usePersonalDataHub-BboN99rI.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DNd23z6f.js → vnode-Bgt8Vgte.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Cjaxnm7l.js → zoom-BhJtErgt.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/commands/agenda.js +6 -5
- package/src/commands/agent.js +237 -75
- package/src/commands/agents.js +6 -0
- package/src/commands/artifacts.js +98 -0
- package/src/commands/background-session.js +11 -1
- package/src/commands/mcp.js +218 -31
- package/src/commands/review.js +221 -22
- package/src/commands/session-mcp-recovery.js +5 -5
- package/src/data/changelog.json +27 -1
- package/src/gateways/ws/ws-agent-handler.js +492 -14
- package/src/harness/jsonl-session-store.js +39 -1
- package/src/harness/mcp-client.js +414 -71
- package/src/harness/prompt-compressor.js +24 -0
- package/src/harness/provider-backed-compaction.js +205 -0
- package/src/harness/worktree-isolator.js +26 -3
- package/src/lib/agent-worktree.js +41 -2
- package/src/lib/app-preview.js +3 -0
- package/src/lib/background-agent-supervisor.js +790 -186
- package/src/lib/background-command-argv.js +318 -0
- package/src/lib/background-launch-profile.js +1 -0
- package/src/lib/background-phase-reporter.js +22 -2
- package/src/lib/background-worktree-policy.js +109 -0
- package/src/lib/command-lifecycle-report.js +1340 -145
- package/src/lib/cowork-mcp-tools.js +51 -6
- package/src/lib/cowork-task-runner.js +1 -0
- package/src/lib/delivery-coordinator.js +30 -5
- package/src/lib/delivery-production-adapter.js +1777 -0
- package/src/lib/delivery-production-runner.js +240 -0
- package/src/lib/durable-security-store.js +29 -17
- package/src/lib/execution-trace/trace-context.cjs +179 -0
- package/src/lib/execution-trace/trace-context.js +8 -175
- package/src/lib/mcp-call-ledger-store.js +58 -6
- package/src/lib/mcp-headers-helper.js +76 -8
- package/src/lib/mcp-host-recovery-runtime.js +2 -0
- package/src/lib/mcp-ledger-recovery-admission.js +39 -3
- package/src/lib/mcp-recovery-adjudication.js +256 -19
- package/src/lib/mcp-sandbox-policy.js +184 -0
- package/src/lib/mcp-stdio-capsule-builder-worker.cjs +495 -0
- package/src/lib/mcp-stdio-executable-identity.js +33 -2
- package/src/lib/mcp-stdio-execution-authority.js +163 -0
- package/src/lib/mcp-stdio-immutable-vfs-resolver.cjs +830 -0
- package/src/lib/mcp-stdio-package-materialization.js +721 -333
- package/src/lib/mcp-stdio-workspace-authority.js +252 -0
- package/src/lib/observability/index.js +2 -0
- package/src/lib/observability/otlp-exporter.js +2 -2
- package/src/lib/packer/pack-update-applier.js +8 -1
- package/src/lib/packer/pkg-config-generator.js +34 -0
- package/src/lib/plugin-runtime/sandbox-policy.js +42 -2
- package/src/lib/process-execution-broker/index.js +213 -109
- package/src/lib/process-execution-broker/platform-sandbox.js +1024 -169
- package/src/lib/process-tree-termination.js +17 -1
- package/src/lib/scheduler-kernel/contract.js +315 -0
- package/src/lib/scheduler-kernel/store.js +1345 -0
- package/src/lib/session-host-lease.js +403 -30
- package/src/lib/session-persistence-failure.js +4 -1
- package/src/lib/skill-mcp.js +65 -4
- package/src/lib/sub-agent-context.js +8 -0
- package/src/lib/web-fetch.js +4 -4
- package/src/repl/agent-repl.js +285 -38
- package/src/runtime/agent-core.js +1044 -90
- package/src/runtime/agent-runtime.js +61 -8
- package/src/runtime/headless-runner.js +260 -166
- package/src/runtime/headless-stream.js +254 -25
- package/src/runtime/mcp-config.js +235 -28
- package/src/workers/background-agent-worker.js +492 -71
- package/src/assets/web-panel/assets/Projects-08xUDZGt.js +0 -1
- package/src/assets/web-panel/assets/Providers-BGs47s1a.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CEYFJwmr.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DEMblAIm.js +0 -1
- package/src/assets/web-panel/assets/index-BTvq-36D.js +0 -1
- package/src/assets/web-panel/assets/index-hUpvk6Jl.js +0 -1
|
@@ -34,12 +34,17 @@ import {
|
|
|
34
34
|
import { createWsApprovalGate } from "./ws-approval-gate.js";
|
|
35
35
|
import { createSessionMcpLedgerSink } from "../../lib/mcp-call-ledger-store.js";
|
|
36
36
|
import { createMcpHostRecoveryRuntime } from "../../lib/mcp-host-recovery-runtime.js";
|
|
37
|
+
import { compactConversationWithProvider } from "../../harness/provider-backed-compaction.js";
|
|
38
|
+
import { projectCanonicalResumeMessages } from "../../lib/session-message-provenance.js";
|
|
39
|
+
import { feature } from "../../lib/feature-flags.js";
|
|
37
40
|
import {
|
|
41
|
+
appendCompactEventIfMessagesMatch,
|
|
38
42
|
claimWsTurnIfHead,
|
|
39
43
|
computeWsTurnInputDigest,
|
|
40
44
|
createWsTurnClaimId,
|
|
41
45
|
normalizeWsTurnRequestId,
|
|
42
46
|
readVerifiedWsTurnState,
|
|
47
|
+
readVerifiedMessages,
|
|
43
48
|
settleWsTurnClaim,
|
|
44
49
|
} from "../../harness/jsonl-session-store.js";
|
|
45
50
|
|
|
@@ -78,6 +83,8 @@ export class WSAgentHandler {
|
|
|
78
83
|
db,
|
|
79
84
|
approvalGate,
|
|
80
85
|
agentLoop: runAgentLoop,
|
|
86
|
+
compactionLlmQuery,
|
|
87
|
+
compactionChatFn,
|
|
81
88
|
canonicalSessionStore,
|
|
82
89
|
sessionHostLease = null,
|
|
83
90
|
}) {
|
|
@@ -122,12 +129,22 @@ export class WSAgentHandler {
|
|
|
122
129
|
this._mcpRecoveryRevision = null;
|
|
123
130
|
this._mcpRecoveryClient = null;
|
|
124
131
|
this._agentLoop = runAgentLoop || agentLoop;
|
|
132
|
+
this._compactionLlmQuery = compactionLlmQuery || null;
|
|
133
|
+
this._compactionSettlementBlock = null;
|
|
134
|
+
this._compactionChatFn =
|
|
135
|
+
compactionChatFn ||
|
|
136
|
+
(async (...args) => {
|
|
137
|
+
const { chatWithTools } = await import("../../runtime/agent-core.js");
|
|
138
|
+
return chatWithTools(...args);
|
|
139
|
+
});
|
|
125
140
|
this._canonicalSessionStore = {
|
|
141
|
+
appendCompactEventIfMessagesMatch,
|
|
126
142
|
claimWsTurnIfHead,
|
|
127
143
|
computeWsTurnInputDigest,
|
|
128
144
|
createWsTurnClaimId,
|
|
129
145
|
normalizeWsTurnRequestId,
|
|
130
146
|
readVerifiedWsTurnState,
|
|
147
|
+
readVerifiedMessages,
|
|
131
148
|
settleWsTurnClaim,
|
|
132
149
|
...(canonicalSessionStore || {}),
|
|
133
150
|
};
|
|
@@ -171,9 +188,56 @@ export class WSAgentHandler {
|
|
|
171
188
|
return this.session.messages;
|
|
172
189
|
}
|
|
173
190
|
|
|
191
|
+
_latchCompactionSettlement({
|
|
192
|
+
code,
|
|
193
|
+
reason,
|
|
194
|
+
commitState = "unknown",
|
|
195
|
+
usageOutcome = null,
|
|
196
|
+
}) {
|
|
197
|
+
this._compactionSettlementBlock = Object.freeze({
|
|
198
|
+
code: code || "CC_COMPACTION_SETTLEMENT_UNKNOWN",
|
|
199
|
+
reason: reason || "canonical_compaction_settlement_failed",
|
|
200
|
+
commitState,
|
|
201
|
+
...(usageOutcome ? { usageOutcome } : {}),
|
|
202
|
+
});
|
|
203
|
+
return this._compactionSettlementBlock;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
_reconcileCompactionSettlementBlock() {
|
|
207
|
+
if (
|
|
208
|
+
this._compactionSettlementBlock?.commitState !== "provider-usage-unknown"
|
|
209
|
+
) {
|
|
210
|
+
this._compactionSettlementBlock = null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
_emitCompactionUsage(requestId, usageEvent) {
|
|
215
|
+
if (!usageEvent) return;
|
|
216
|
+
this.interaction.emit("token-usage", {
|
|
217
|
+
requestId,
|
|
218
|
+
...usageEvent,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
_emitCompactionUsageUnknown(requestId, usageUnknownEvent) {
|
|
223
|
+
const block = this._latchCompactionSettlement({
|
|
224
|
+
code: "CC_COMPACTION_USAGE_UNKNOWN",
|
|
225
|
+
reason: usageUnknownEvent?.reason || "provider_transport_outcome_unknown",
|
|
226
|
+
commitState: "provider-usage-unknown",
|
|
227
|
+
usageOutcome: "unknown",
|
|
228
|
+
});
|
|
229
|
+
this.interaction.emit("compaction-usage-unknown", {
|
|
230
|
+
requestId,
|
|
231
|
+
...usageUnknownEvent,
|
|
232
|
+
code: block.code,
|
|
233
|
+
});
|
|
234
|
+
return block;
|
|
235
|
+
}
|
|
236
|
+
|
|
174
237
|
_canonicalTurnFromAuthority(state, requestId, userMessage, inputDigest) {
|
|
175
238
|
const status = state?.status || (state?.turn ? "completed" : "none");
|
|
176
239
|
this._applyCanonicalMessages(state?.messages || []);
|
|
240
|
+
this._reconcileCompactionSettlementBlock();
|
|
177
241
|
if (status === "completed" && state.turn) {
|
|
178
242
|
return {
|
|
179
243
|
requestId,
|
|
@@ -210,6 +274,193 @@ export class WSAgentHandler {
|
|
|
210
274
|
return null;
|
|
211
275
|
}
|
|
212
276
|
|
|
277
|
+
async _compactCanonicalHistoryBeforeClaim(requestId) {
|
|
278
|
+
const { session } = this;
|
|
279
|
+
if (
|
|
280
|
+
session.autoCompact === false ||
|
|
281
|
+
!feature("PROMPT_COMPRESSOR") ||
|
|
282
|
+
this._compactionSettlementBlock?.commitState === "provider-usage-unknown"
|
|
283
|
+
) {
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const before = session.messages.length;
|
|
288
|
+
const expectedMessages = [...session.messages];
|
|
289
|
+
let result;
|
|
290
|
+
try {
|
|
291
|
+
this._sessionHostLease?.assert?.();
|
|
292
|
+
result = await compactConversationWithProvider(session.messages, {
|
|
293
|
+
provider: session.provider,
|
|
294
|
+
model: session.model,
|
|
295
|
+
baseUrl: session.baseUrl,
|
|
296
|
+
apiKey: session.apiKey,
|
|
297
|
+
signal: this._sessionHostLease?.signal,
|
|
298
|
+
onlyIfNeeded: true,
|
|
299
|
+
preserveCompletedExchange: true,
|
|
300
|
+
llmQuery: this._compactionLlmQuery,
|
|
301
|
+
chatFn: this._compactionChatFn,
|
|
302
|
+
chatOptions: {
|
|
303
|
+
cwd: session.projectRoot,
|
|
304
|
+
sessionId: session.id,
|
|
305
|
+
},
|
|
306
|
+
maxMessages: session.compactionMaxMessages,
|
|
307
|
+
maxTokens: session.compactionMaxTokens,
|
|
308
|
+
maxOutputTokens: session.compactionMaxOutputTokens,
|
|
309
|
+
summaryInputMaxChars: session.compactionInputMaxChars,
|
|
310
|
+
});
|
|
311
|
+
this._sessionHostLease?.assert?.();
|
|
312
|
+
} catch (error) {
|
|
313
|
+
if (isAbortError(error) || this._sessionHostLease?.signal?.aborted) {
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
const block = this._latchCompactionSettlement({
|
|
317
|
+
code: error?.code || "CC_COMPACTION_FAILED",
|
|
318
|
+
reason: `compaction_failed:${String(error?.message || error).slice(0, 240)}`,
|
|
319
|
+
commitState: "provider-outcome-unknown",
|
|
320
|
+
});
|
|
321
|
+
this.interaction.emit("compaction-degraded", {
|
|
322
|
+
requestId,
|
|
323
|
+
reason: block.reason,
|
|
324
|
+
summaryMode: "none",
|
|
325
|
+
code: block.code,
|
|
326
|
+
});
|
|
327
|
+
this.interaction.emit("error", {
|
|
328
|
+
requestId,
|
|
329
|
+
code: block.code,
|
|
330
|
+
message: "Canonical history could not be compacted before turn claim",
|
|
331
|
+
});
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const {
|
|
336
|
+
messages: compacted,
|
|
337
|
+
stats,
|
|
338
|
+
degradedEvent,
|
|
339
|
+
usageEvent,
|
|
340
|
+
usageUnknownEvent,
|
|
341
|
+
} = result;
|
|
342
|
+
if (degradedEvent) {
|
|
343
|
+
this.interaction.emit("compaction-degraded", {
|
|
344
|
+
requestId,
|
|
345
|
+
...degradedEvent,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
if (usageUnknownEvent) {
|
|
349
|
+
const block = this._emitCompactionUsageUnknown(
|
|
350
|
+
requestId,
|
|
351
|
+
usageUnknownEvent,
|
|
352
|
+
);
|
|
353
|
+
this.interaction.emit("error", {
|
|
354
|
+
requestId,
|
|
355
|
+
code: block.code,
|
|
356
|
+
message:
|
|
357
|
+
"Compaction provider usage is unknown; the paid compaction is not retried",
|
|
358
|
+
});
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const messagesChanged =
|
|
363
|
+
session.messages.length !== expectedMessages.length ||
|
|
364
|
+
session.messages.some(
|
|
365
|
+
(message, index) => message !== expectedMessages[index],
|
|
366
|
+
);
|
|
367
|
+
const shouldSettle = stats?.strategy !== "none" && stats?.saved > 0;
|
|
368
|
+
let settlementError = null;
|
|
369
|
+
if (!messagesChanged && shouldSettle) {
|
|
370
|
+
try {
|
|
371
|
+
const settlement =
|
|
372
|
+
this._canonicalSessionStore.appendCompactEventIfMessagesMatch(
|
|
373
|
+
session.id,
|
|
374
|
+
{
|
|
375
|
+
...stats,
|
|
376
|
+
trigger: "auto",
|
|
377
|
+
messages: projectCanonicalResumeMessages(compacted, {
|
|
378
|
+
strict: true,
|
|
379
|
+
}),
|
|
380
|
+
},
|
|
381
|
+
expectedMessages,
|
|
382
|
+
);
|
|
383
|
+
if (settlement && typeof settlement.then === "function") {
|
|
384
|
+
void Promise.resolve(settlement).catch(() => {});
|
|
385
|
+
const error = new TypeError(
|
|
386
|
+
"Canonical compact settlement must be synchronous",
|
|
387
|
+
);
|
|
388
|
+
error.code = "CC_COMPACTION_SETTLEMENT_ASYNC";
|
|
389
|
+
throw error;
|
|
390
|
+
}
|
|
391
|
+
} catch (error) {
|
|
392
|
+
settlementError = error;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
this._emitCompactionUsage(requestId, usageEvent);
|
|
397
|
+
if (messagesChanged || settlementError) {
|
|
398
|
+
const stale = settlementError?.code === "SESSION_REVISION_STALE";
|
|
399
|
+
let reconciled = false;
|
|
400
|
+
if (stale) {
|
|
401
|
+
try {
|
|
402
|
+
const authoritativeMessages =
|
|
403
|
+
this._canonicalSessionStore.readVerifiedMessages(session.id);
|
|
404
|
+
if (!Array.isArray(authoritativeMessages)) {
|
|
405
|
+
throw new TypeError(
|
|
406
|
+
"Canonical message refresh must be synchronous",
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
this._applyCanonicalMessages(authoritativeMessages);
|
|
410
|
+
this._reconcileCompactionSettlementBlock();
|
|
411
|
+
reconciled = true;
|
|
412
|
+
} catch {
|
|
413
|
+
// Latch below until a later verified canonical refresh succeeds.
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const block =
|
|
417
|
+
reconciled && stale
|
|
418
|
+
? {
|
|
419
|
+
code: "CC_COMPACTION_REVISION_STALE",
|
|
420
|
+
reason: "session_messages_changed_during_compaction",
|
|
421
|
+
commitState: "reconciled-stale",
|
|
422
|
+
}
|
|
423
|
+
: this._latchCompactionSettlement({
|
|
424
|
+
code:
|
|
425
|
+
settlementError?.code ||
|
|
426
|
+
(messagesChanged
|
|
427
|
+
? "CC_COMPACTION_LOCAL_STATE_CHANGED"
|
|
428
|
+
: "CC_COMPACTION_SETTLEMENT_UNKNOWN"),
|
|
429
|
+
reason:
|
|
430
|
+
messagesChanged || stale
|
|
431
|
+
? "session_messages_changed_during_compaction"
|
|
432
|
+
: "canonical_compaction_settlement_failed",
|
|
433
|
+
commitState: "unknown",
|
|
434
|
+
});
|
|
435
|
+
this.interaction.emit("compaction-degraded", {
|
|
436
|
+
requestId,
|
|
437
|
+
reason: block.reason,
|
|
438
|
+
summaryMode: "none",
|
|
439
|
+
code: block.code,
|
|
440
|
+
});
|
|
441
|
+
this.interaction.emit("error", {
|
|
442
|
+
requestId,
|
|
443
|
+
code: block.code,
|
|
444
|
+
message:
|
|
445
|
+
"Canonical compaction did not settle; the paid compaction is not retried",
|
|
446
|
+
});
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (shouldSettle) {
|
|
451
|
+
session.messages.length = 0;
|
|
452
|
+
session.messages.push(...compacted);
|
|
453
|
+
this.interaction.emit("compaction", {
|
|
454
|
+
requestId,
|
|
455
|
+
stats,
|
|
456
|
+
trigger: "auto",
|
|
457
|
+
messagesBefore: before,
|
|
458
|
+
messagesAfter: session.messages.length,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
|
|
213
464
|
_claimCanonicalTurn(userMessage, requestId) {
|
|
214
465
|
const inputDigest =
|
|
215
466
|
this._canonicalSessionStore.computeWsTurnInputDigest(userMessage);
|
|
@@ -396,6 +647,7 @@ export class WSAgentHandler {
|
|
|
396
647
|
sink: this._mcpRecoverySink,
|
|
397
648
|
recovery: session.mcpLedgerRecovery || null,
|
|
398
649
|
controller,
|
|
650
|
+
dispatchAdmission: this._sessionHostLease?.admitMcpDispatch || null,
|
|
399
651
|
});
|
|
400
652
|
this._mcpRecoveryClient = rawClient;
|
|
401
653
|
this._mcpRecoveryRevision = revision;
|
|
@@ -509,7 +761,26 @@ export class WSAgentHandler {
|
|
|
509
761
|
this._sessionHostLease?.assert?.();
|
|
510
762
|
|
|
511
763
|
if (session.canonicalJsonlSession === true) {
|
|
512
|
-
|
|
764
|
+
const inputDigest =
|
|
765
|
+
this._canonicalSessionStore.computeWsTurnInputDigest(userMessage);
|
|
766
|
+
const preflightState =
|
|
767
|
+
this._canonicalSessionStore.readVerifiedWsTurnState(
|
|
768
|
+
session.id,
|
|
769
|
+
requestId,
|
|
770
|
+
{ inputDigest },
|
|
771
|
+
);
|
|
772
|
+
canonicalTurn = this._canonicalTurnFromAuthority(
|
|
773
|
+
preflightState,
|
|
774
|
+
requestId,
|
|
775
|
+
userMessage,
|
|
776
|
+
inputDigest,
|
|
777
|
+
);
|
|
778
|
+
if (!canonicalTurn) {
|
|
779
|
+
const compacted =
|
|
780
|
+
await this._compactCanonicalHistoryBeforeClaim(requestId);
|
|
781
|
+
if (!compacted) return;
|
|
782
|
+
}
|
|
783
|
+
canonicalTurn ||= this._claimCanonicalTurn(userMessage, requestId);
|
|
513
784
|
if (canonicalTurn.status === "completed") {
|
|
514
785
|
this.interaction.emit("response-complete", {
|
|
515
786
|
requestId,
|
|
@@ -601,6 +872,7 @@ export class WSAgentHandler {
|
|
|
601
872
|
// headless and REPL sessions. For unknown/write/destructive effects a
|
|
602
873
|
// failed prewrite blocks the network call inside agent-core.
|
|
603
874
|
mcpCallLedger: mcpRecoveryRuntime.ledger,
|
|
875
|
+
mcpDispatchAdmission: this._sessionHostLease?.admitMcpDispatch || null,
|
|
604
876
|
shellPolicyOverrides: session.shellPolicyOverrides || null,
|
|
605
877
|
slotFiller,
|
|
606
878
|
interaction: this.interaction,
|
|
@@ -608,6 +880,12 @@ export class WSAgentHandler {
|
|
|
608
880
|
// P0 authority: null unless opted in (byte-identical default — agent
|
|
609
881
|
// core already defaults `options.approvalGate || null`).
|
|
610
882
|
approvalGate: await this._ensureApprovalGate(),
|
|
883
|
+
// Canonical WS history is compacted before ws_turn_prepare. Compaction
|
|
884
|
+
// inside agent-core would include the staged pending user, which is not
|
|
885
|
+
// part of the canonical message projection until turn settlement.
|
|
886
|
+
...(session.canonicalJsonlSession === true
|
|
887
|
+
? { autoCompact: false }
|
|
888
|
+
: {}),
|
|
611
889
|
};
|
|
612
890
|
|
|
613
891
|
// P0-2: crash-safe side-effect ledger for the IDE/bridge path — mirrors
|
|
@@ -718,6 +996,37 @@ export class WSAgentHandler {
|
|
|
718
996
|
}
|
|
719
997
|
break;
|
|
720
998
|
|
|
999
|
+
case "compaction-degraded":
|
|
1000
|
+
this.interaction.emit("compaction-degraded", {
|
|
1001
|
+
requestId,
|
|
1002
|
+
reason: event.reason,
|
|
1003
|
+
summaryMode: event.summaryMode,
|
|
1004
|
+
...(event.code ? { code: event.code } : {}),
|
|
1005
|
+
stats: event.stats,
|
|
1006
|
+
});
|
|
1007
|
+
break;
|
|
1008
|
+
|
|
1009
|
+
case "compaction-usage-unknown":
|
|
1010
|
+
this._emitCompactionUsageUnknown(requestId, event);
|
|
1011
|
+
break;
|
|
1012
|
+
|
|
1013
|
+
case "compaction":
|
|
1014
|
+
this.interaction.emit("compaction", {
|
|
1015
|
+
requestId,
|
|
1016
|
+
stats: event.stats,
|
|
1017
|
+
});
|
|
1018
|
+
break;
|
|
1019
|
+
|
|
1020
|
+
case "token-usage":
|
|
1021
|
+
this.interaction.emit("token-usage", {
|
|
1022
|
+
requestId,
|
|
1023
|
+
provider: event.provider,
|
|
1024
|
+
model: event.model,
|
|
1025
|
+
usage: event.usage,
|
|
1026
|
+
...(event.source ? { source: event.source } : {}),
|
|
1027
|
+
});
|
|
1028
|
+
break;
|
|
1029
|
+
|
|
721
1030
|
case "response-complete":
|
|
722
1031
|
if (session.canonicalJsonlSession === true) {
|
|
723
1032
|
const settled = this._settleCanonicalSuccess(
|
|
@@ -983,25 +1292,194 @@ export class WSAgentHandler {
|
|
|
983
1292
|
});
|
|
984
1293
|
break;
|
|
985
1294
|
|
|
986
|
-
case "/compact":
|
|
987
|
-
if (
|
|
988
|
-
const
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1295
|
+
case "/compact": {
|
|
1296
|
+
if (this._compactionSettlementBlock) {
|
|
1297
|
+
const block = this._compactionSettlementBlock;
|
|
1298
|
+
this.interaction.emit("compaction-degraded", {
|
|
1299
|
+
requestId,
|
|
1300
|
+
reason: block.reason,
|
|
1301
|
+
summaryMode: "none",
|
|
1302
|
+
code: block.code,
|
|
1303
|
+
});
|
|
1304
|
+
this.interaction.emit("command-response", {
|
|
1305
|
+
requestId,
|
|
1306
|
+
command: cmd,
|
|
1307
|
+
result: {
|
|
1308
|
+
messageCount: session.messages.length,
|
|
1309
|
+
error: block,
|
|
1310
|
+
},
|
|
1311
|
+
});
|
|
1312
|
+
break;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
const before = session.messages.length;
|
|
1316
|
+
const expectedMessages = [...session.messages];
|
|
1317
|
+
let result = null;
|
|
1318
|
+
let commandError = null;
|
|
1319
|
+
try {
|
|
1320
|
+
this._sessionHostLease?.assert?.();
|
|
1321
|
+
result = await compactConversationWithProvider(session.messages, {
|
|
1322
|
+
provider: session.provider,
|
|
1323
|
+
model: session.model,
|
|
1324
|
+
baseUrl: session.baseUrl,
|
|
1325
|
+
apiKey: session.apiKey,
|
|
1326
|
+
signal: this._sessionHostLease?.signal,
|
|
1327
|
+
force: true,
|
|
1328
|
+
preserveCompletedExchange: true,
|
|
1329
|
+
llmQuery: this._compactionLlmQuery,
|
|
1330
|
+
chatFn: this._compactionChatFn,
|
|
1331
|
+
chatOptions: {
|
|
1332
|
+
cwd: session.projectRoot,
|
|
1333
|
+
sessionId: session.id,
|
|
1334
|
+
},
|
|
1335
|
+
maxOutputTokens: session.compactionMaxOutputTokens,
|
|
1336
|
+
summaryInputMaxChars: session.compactionInputMaxChars,
|
|
1337
|
+
});
|
|
1338
|
+
this._sessionHostLease?.assert?.();
|
|
1339
|
+
} catch (error) {
|
|
1340
|
+
if (isAbortError(error) || this._sessionHostLease?.signal?.aborted) {
|
|
1341
|
+
throw error;
|
|
1342
|
+
}
|
|
1343
|
+
commandError = this._latchCompactionSettlement({
|
|
1344
|
+
code: error?.code || "CC_COMPACTION_FAILED",
|
|
1345
|
+
reason: `compaction_failed:${String(error?.message || error).slice(0, 240)}`,
|
|
1346
|
+
commitState: "provider-outcome-unknown",
|
|
1347
|
+
});
|
|
1348
|
+
this.interaction.emit("compaction-degraded", {
|
|
1349
|
+
requestId,
|
|
1350
|
+
reason: commandError.reason,
|
|
1351
|
+
summaryMode: "none",
|
|
1352
|
+
code: commandError.code,
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
if (result) {
|
|
1356
|
+
const {
|
|
1357
|
+
messages,
|
|
1358
|
+
stats,
|
|
1359
|
+
degradedEvent,
|
|
1360
|
+
usageEvent,
|
|
1361
|
+
usageUnknownEvent,
|
|
1362
|
+
} = result;
|
|
1363
|
+
if (degradedEvent) {
|
|
1364
|
+
this.interaction.emit("compaction-degraded", {
|
|
1365
|
+
requestId,
|
|
1366
|
+
...degradedEvent,
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
if (usageUnknownEvent) {
|
|
1370
|
+
commandError = this._emitCompactionUsageUnknown(
|
|
1371
|
+
requestId,
|
|
1372
|
+
usageUnknownEvent,
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
const messagesChanged =
|
|
1376
|
+
session.messages.length !== expectedMessages.length ||
|
|
1377
|
+
session.messages.some(
|
|
1378
|
+
(message, index) => message !== expectedMessages[index],
|
|
1379
|
+
);
|
|
1380
|
+
let settlementError = null;
|
|
1381
|
+
if (
|
|
1382
|
+
!commandError &&
|
|
1383
|
+
!messagesChanged &&
|
|
1384
|
+
session.canonicalJsonlSession === true
|
|
1385
|
+
) {
|
|
1386
|
+
if (stats?.strategy !== "none") {
|
|
1387
|
+
try {
|
|
1388
|
+
const settlement =
|
|
1389
|
+
this._canonicalSessionStore.appendCompactEventIfMessagesMatch(
|
|
1390
|
+
session.id,
|
|
1391
|
+
{
|
|
1392
|
+
...stats,
|
|
1393
|
+
trigger: "manual",
|
|
1394
|
+
messages: projectCanonicalResumeMessages(messages, {
|
|
1395
|
+
strict: true,
|
|
1396
|
+
}),
|
|
1397
|
+
},
|
|
1398
|
+
expectedMessages,
|
|
1399
|
+
);
|
|
1400
|
+
if (settlement && typeof settlement.then === "function") {
|
|
1401
|
+
void Promise.resolve(settlement).catch(() => {});
|
|
1402
|
+
const error = new TypeError(
|
|
1403
|
+
"Canonical compact settlement must be synchronous",
|
|
1404
|
+
);
|
|
1405
|
+
error.code = "CC_COMPACTION_SETTLEMENT_ASYNC";
|
|
1406
|
+
throw error;
|
|
1407
|
+
}
|
|
1408
|
+
} catch (error) {
|
|
1409
|
+
settlementError = error;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
if (!commandError && !messagesChanged && !settlementError) {
|
|
1415
|
+
session.messages.length = 0;
|
|
1416
|
+
session.messages.push(...messages);
|
|
1417
|
+
this.interaction.emit("compaction", {
|
|
1418
|
+
requestId,
|
|
1419
|
+
stats,
|
|
1420
|
+
messagesBefore: before,
|
|
1421
|
+
messagesAfter: session.messages.length,
|
|
1422
|
+
});
|
|
1423
|
+
} else if (!commandError) {
|
|
1424
|
+
const stale = settlementError?.code === "SESSION_REVISION_STALE";
|
|
1425
|
+
let reconciled = false;
|
|
1426
|
+
if (stale) {
|
|
1427
|
+
try {
|
|
1428
|
+
const authoritativeMessages =
|
|
1429
|
+
this._canonicalSessionStore.readVerifiedMessages(session.id);
|
|
1430
|
+
if (!Array.isArray(authoritativeMessages)) {
|
|
1431
|
+
throw new TypeError(
|
|
1432
|
+
"Canonical message refresh must be synchronous",
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
this._applyCanonicalMessages(authoritativeMessages);
|
|
1436
|
+
this._reconcileCompactionSettlementBlock();
|
|
1437
|
+
reconciled = true;
|
|
1438
|
+
} catch {
|
|
1439
|
+
// Latch below until a later verified canonical refresh.
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
commandError =
|
|
1443
|
+
reconciled && stale
|
|
1444
|
+
? {
|
|
1445
|
+
code: "CC_COMPACTION_REVISION_STALE",
|
|
1446
|
+
reason: "session_messages_changed_during_compaction",
|
|
1447
|
+
commitState: "reconciled-stale",
|
|
1448
|
+
}
|
|
1449
|
+
: this._latchCompactionSettlement({
|
|
1450
|
+
code:
|
|
1451
|
+
settlementError?.code ||
|
|
1452
|
+
(messagesChanged
|
|
1453
|
+
? "CC_COMPACTION_LOCAL_STATE_CHANGED"
|
|
1454
|
+
: "CC_COMPACTION_SETTLEMENT_UNKNOWN"),
|
|
1455
|
+
reason:
|
|
1456
|
+
messagesChanged || stale
|
|
1457
|
+
? "session_messages_changed_during_compaction"
|
|
1458
|
+
: "canonical_compaction_settlement_failed",
|
|
1459
|
+
commitState: "unknown",
|
|
1460
|
+
});
|
|
1461
|
+
this.interaction.emit("compaction-degraded", {
|
|
1462
|
+
requestId,
|
|
1463
|
+
reason: commandError.reason,
|
|
1464
|
+
summaryMode: "none",
|
|
1465
|
+
code: commandError.code,
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
// Provider usage is emitted exactly once even when the message CAS
|
|
1469
|
+
// rejects the candidate; the paid request already happened and is
|
|
1470
|
+
// never retried by this command.
|
|
1471
|
+
this._emitCompactionUsage(requestId, usageEvent);
|
|
998
1472
|
}
|
|
999
1473
|
this.interaction.emit("command-response", {
|
|
1000
1474
|
requestId,
|
|
1001
1475
|
command: cmd,
|
|
1002
|
-
result: {
|
|
1476
|
+
result: {
|
|
1477
|
+
messageCount: session.messages.length,
|
|
1478
|
+
...(commandError ? { error: commandError } : {}),
|
|
1479
|
+
},
|
|
1003
1480
|
});
|
|
1004
1481
|
break;
|
|
1482
|
+
}
|
|
1005
1483
|
|
|
1006
1484
|
case "/task":
|
|
1007
1485
|
if (arg === "clear") {
|
|
@@ -18,8 +18,9 @@ import fs, {
|
|
|
18
18
|
readSync,
|
|
19
19
|
writeSync,
|
|
20
20
|
ftruncateSync,
|
|
21
|
+
fsyncSync,
|
|
21
22
|
} from "node:fs";
|
|
22
|
-
import { join, basename, resolve } from "node:path";
|
|
23
|
+
import { join, basename, dirname, resolve } from "node:path";
|
|
23
24
|
import { createHash, randomUUID } from "node:crypto";
|
|
24
25
|
import { TextDecoder } from "node:util";
|
|
25
26
|
import { getHomeDir } from "../lib/paths.js";
|
|
@@ -83,6 +84,10 @@ let securedSessionsDirIdentity = null;
|
|
|
83
84
|
// keeps production behavior and ordinary tests on the exact same append path.
|
|
84
85
|
export const _sessionScaleFaultHooks = Object.seal({
|
|
85
86
|
beforeTranscriptAppend: null,
|
|
87
|
+
beforeTranscriptFsync: null,
|
|
88
|
+
afterTranscriptFsync: null,
|
|
89
|
+
beforeTranscriptDirectoryFsync: null,
|
|
90
|
+
afterTranscriptDirectoryFsync: null,
|
|
86
91
|
afterTranscriptAppend: null,
|
|
87
92
|
afterForkCopy: null,
|
|
88
93
|
afterForkLineage: null,
|
|
@@ -981,6 +986,39 @@ function appendTranscriptEventAtPath({
|
|
|
981
986
|
wroteBytes = true;
|
|
982
987
|
}
|
|
983
988
|
appendCompleted = true;
|
|
989
|
+
// The sidecar and independent anti-rollback witness are published after
|
|
990
|
+
// this function returns. Make the transcript bytes durable first so a
|
|
991
|
+
// power loss cannot preserve a newer authority head while losing the event
|
|
992
|
+
// that head authenticates. A full-write fsync failure is outcome-unknown:
|
|
993
|
+
// the caller must verify the transcript and must not advance either anchor.
|
|
994
|
+
runSessionScaleFaultHook("beforeTranscriptFsync", payload);
|
|
995
|
+
fsyncSync(fd);
|
|
996
|
+
runSessionScaleFaultHook("afterTranscriptFsync", payload);
|
|
997
|
+
|
|
998
|
+
// fsync(file) does not durably publish a newly-created directory entry on
|
|
999
|
+
// POSIX. Flush the sessions directory before any sidecar/external witness
|
|
1000
|
+
// can advance. Windows does not expose a usable directory fsync through
|
|
1001
|
+
// Node; FlushFileBuffers on the newly-created file is the available host
|
|
1002
|
+
// durability boundary there.
|
|
1003
|
+
if (expectedState === null && process.platform !== "win32") {
|
|
1004
|
+
runSessionScaleFaultHook("beforeTranscriptDirectoryFsync", payload);
|
|
1005
|
+
let directoryDescriptor = null;
|
|
1006
|
+
try {
|
|
1007
|
+
const directoryFlags =
|
|
1008
|
+
fsConstants.O_RDONLY |
|
|
1009
|
+
Number(fsConstants.O_DIRECTORY ?? 0) |
|
|
1010
|
+
Number(fsConstants.O_NOFOLLOW ?? 0);
|
|
1011
|
+
directoryDescriptor = openSync(dirname(filePath), directoryFlags);
|
|
1012
|
+
const directoryStats = fstatSync(directoryDescriptor);
|
|
1013
|
+
if (!directoryStats.isDirectory()) {
|
|
1014
|
+
throw new Error("Session transcript parent is not a directory");
|
|
1015
|
+
}
|
|
1016
|
+
fsyncSync(directoryDescriptor);
|
|
1017
|
+
runSessionScaleFaultHook("afterTranscriptDirectoryFsync", payload);
|
|
1018
|
+
} finally {
|
|
1019
|
+
if (directoryDescriptor !== null) closeSync(directoryDescriptor);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
984
1022
|
runSessionScaleFaultHook("afterTranscriptAppend", payload);
|
|
985
1023
|
const descriptorStats = fstatSync(fd, { bigint: true });
|
|
986
1024
|
const descriptorState = physicalTranscriptStateFromStats(descriptorStats);
|