chainlesschain 0.162.197 → 0.162.200
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/bin/chainlesschain.js +14 -6
- package/package.json +4 -1
- package/scripts/command-lifecycle-report.mjs +94 -0
- package/src/assets/web-panel/assets/{AIOps-D3Gu1a5d.js → AIOps-DcQ0bWKy.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DrwAoKaz.js → ActionButton-GecwfFPt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C7Q-gd_3.js → Analytics-CD6iSt9v.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DgBtae-n.js → AppLayout-CDEtuEYf.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-pQ6LcA-W.js → Artifacts-DS3jAjeW.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DK61tocB.js → Audit-CcSJA048.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BOKgDFRh.js → BackgroundAgents-DRo9Morb.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Bo9G4vTP.js → Backup-Vw5MrWft.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Do_dE9_v.js → BaseInput-DNHIu823.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-COVvZzOL.js → Chat-DvAsO8-b.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-9aDuObay.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Z9MJMolt.js → Checkbox-CKCa7tvn.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BVzWKVbf.js → Codegen-CIFvZdaT.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C8MnYcbT.js → Col-C79yz7bK.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DQxqw15l.js → Community-DVvZskLJ.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BAUmrh3y.js → Compact-oaew61hV.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-jPAkQ3zK.js → Compliance-DqA7wmRx.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BBQyxbh8.js → Cowork-B-qGadxa.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-W5koq8Z5.js → Cron-CUlCl4sS.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BQhnu5hS.js → Crosschain-CNOlr9DU.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DD7PlI18.js → DID-DJqFCT_3.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-w_ILuwKI.js → Dashboard-CznGpphf.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-qiDUAyAo.js → Dropdown-_cJw218E.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-PDeBcI5H.js → EmailListRenderer-Dr-1BZ9C.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-SuArscRM.js → FamilyGuardDashboard-Dxf4xtSk.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BCovZaUn.js → Federation-DOiG3i_1.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DjlFdVFk.js → FormItemContext-BzbMcFB9.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DoEZ7fcS.js → GenericCardRenderer-VbBrYttP.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CV0-6GPI.js → Git-CLG8E6ED.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B_SFN50w.js → Governance-LGJQfk_q.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-heI0Eo6k.js → Inference-DlQKx9lF.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cz5INS5p.js → KnowledgeGraph-DU8vEouV.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BSFxiHkg.js → Logs-FZIQOdln.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BJNWIrbw.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-0TWTpE7k.js → Marketplace-C_xd-zZI.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CV4-wBLm.js → McpTools-q-YktphS.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-x5bH5rw5.js → Memory-Cq9OhDin.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DaBmUu9G.js → MobileBridge-B03_R98T.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-DZrk-qmF.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-B_zKEBif.js → Mtc-BidCKW28.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BCfKD2yg.js → MtcAudit-r-iaMVM7.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DgYfj6c_.js → Multisig-CgTUFvff.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dz09lFUm.js → NLProgramming-B7NvzfAV.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cuj6ugBE.js → Notes-80CWNqJI.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DK2WXjhg.js → NotificationSettings-0H6xdiY3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-D1IAOQoo.js → OrderTableRenderer-CKP4AFWy.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-9fqnPvTw.js → Organization-B8sxCV_h.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-n5xHkhow.js → Overflow-CW9JBBhQ.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-B5AUWwH-.js → P2P-bdSQV7oZ.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CoUDV6dy.js → PdhVaultBrowser-DQad42nW.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-DPRmQf_H.js → Permissions-CueexxTR.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-B37bsbEN.js → PersonalDataHub-fqxflhR8.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-CCAuPqu9.js → Pipeline-KFwwnIMC.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B81msZdv.js → Privacy-ClnD9buY.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B-Py2NbF.js → ProjectInit-CZEz6sm3.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CYsneiRB.js → ProjectSettings-B6NzpmOI.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Y975Mh6R.js → Projects-DYyuwPCs.js} +1 -1
- package/src/assets/web-panel/assets/Providers-C3tz_jdh.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-Bj_6gJ0l.js → QrScannerModal-BzuOBbqB.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BgpBCMPE.js → QuickAsk-CmNO9qSj.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DhUW3YUA.js → Recommend-VJNj3_NF.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DdeyfbkA.js → RemoteSession-CrbdbSBF.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-fxW421xd.js → Reputation-DTyMd45j.js} +1 -1
- package/src/assets/web-panel/assets/{Row-De73w1og.js → Row-DHrQ-Icz.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-3US_Q_lj.js → RssFeed-DqWxY9c5.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DbZ_DU74.js → Search-ctF1Ks3J.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DoIU1vMF.js → Security-BrQz8uo5.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DB_RGqpc.js → Services-D4scVFar.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DnzLaayC.js → Skeleton-oUScaOjv.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-VzVXHENF.js → Skills-CL-y0eLt.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DMfvjCAI.js → Sla-DQExhwXj.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DQQOYWbF.js → SpeechSettings-iNp_I7kN.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Bmx3FG1P.js → SyncSettings-CTDlMo0A.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DOQyfb-2.js → Tasks-4kb8l3sc.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-D6hTRT7c.js → Templates-BXcL9oXW.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CTPQZAzs.js → Tenant-DLE8eS0D.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B7vII3oH.js → Terminal-DGpUZPPK.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BkiFJeXV.js → TimelineRenderer-C1pWXGPC.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BPPdQMNo.js → Tokens-BYfbfqhZ.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BxG8JAEI.js → Trigger-K9YfLOCw.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DHD7nMpM.js → Trust-Ctkq77Hy.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B4BJpU47.js → UkeySign-C6_CSe9J.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CRiGpFx2.js → VideoEditing-Be3rpOWs.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BSDSNiem.js → Wallet-D0UIplxk.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DLTUh5Zv.js → WebAuthn-CF9bM9I3.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-tW2MwjhD.js → WorkflowEditor-D0H1XVPY.js} +1 -1
- package/src/assets/web-panel/assets/{chat-SgsLQ3FC.js → chat-Cgzzbe0o.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BHjjLIKO.js → colors-BwNxEVS0.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-VceErdKq.js → compact-item-D60Z4Hhs.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BZ332MYl.js → createContext-MFk58XB0.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BJiw369i.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CBTjJOjn.js → hasIn-CRj2SvGO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaCCy6lq.js → index--s45_1cb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMVvxIS8.js → index--tquQ2ZG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhXZEUAc.js → index-1T1rhokn.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQSmZGgs.js → index-B-M82_rx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BeiqkxJG.js → index-BE_el1Zs.js} +1 -1
- package/src/assets/web-panel/assets/{index-29wVzi-k.js → index-BhRkR7IO.js} +1 -1
- package/src/assets/web-panel/assets/{index-iYQNU9za.js → index-Bo0y96-K.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRWhLbWF.js → index-CJgu5JUK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPXFLYdW.js → index-CLWwdebO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cr4rjlW5.js → index-CNAK5QG_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaIS_TNX.js → index-CQjJjPRD.js} +1 -1
- package/src/assets/web-panel/assets/{index-1n7xjKDU.js → index-CTDbAfae.js} +1 -1
- package/src/assets/web-panel/assets/{index-BETEjuNr.js → index-CVI_9sRK.js} +1 -1
- package/src/assets/web-panel/assets/{index-By5jBtkV.js → index-CY14oMQK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgPvuIn2.js → index-CbiTJkZf.js} +1 -1
- package/src/assets/web-panel/assets/index-CiMouosn.js +1 -0
- package/src/assets/web-panel/assets/{index-CawpdGTW.js → index-CqWpxMqx.js} +1 -1
- package/src/assets/web-panel/assets/{index-_PHrIO4O.js → index-CvB8lYUR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOtvPdHt.js → index-D51XwtCY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrUgDQkf.js → index-D6HvK_1O.js} +1 -1
- package/src/assets/web-panel/assets/{index-BY1wexYG.js → index-D9jVIwLJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_B9rFQE.js → index-DA4tE98u.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5KAGxNo.js → index-DBwOtXw0.js} +1 -1
- package/src/assets/web-panel/assets/{index-BC1xuQgX.js → index-DKdNzTIb.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2u26AS3.js → index-DRySdmvN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DePOeMDM.js → index-DWzyTev7.js} +1 -1
- package/src/assets/web-panel/assets/index-DXcKlwy7.js +1 -0
- package/src/assets/web-panel/assets/{index-BTlc-Bve.js → index-DYjs7jAP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLc19alH.js → index-DbROzMBp.js} +1 -1
- package/src/assets/web-panel/assets/{index-fU7wNHDB.js → index-DhFtk5PR.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_SePDWj.js → index-Di2AbMLg.js} +1 -1
- package/src/assets/web-panel/assets/{index-vIXOpcjD.js → index-DvmocQsM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcUenmCX.js → index-Dw-o0zEF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUkVN794.js → index-DysPoP38.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtjLcCNU.js → index-LX9pJxm_.js} +1 -1
- package/src/assets/web-panel/assets/{index-B40dQn0G.js → index-OI_fXWx0.js} +3 -3
- package/src/assets/web-panel/assets/{index-FJ3zC9LK.js → index-eDTOX8Xj.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdefYBSw.js → index-gWKDsTzF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpumS98o.js → index-q_GDUVAA.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D1oZNz0D.js → initDefaultProps-C61kZqEd.js} +1 -1
- package/src/assets/web-panel/assets/{motion-C8k6doMO.js → motion-DZnT5U1L.js} +1 -1
- package/src/assets/web-panel/assets/{move-DJ4qSbYt.js → move-L8wzwbel.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CdpJD85j.js → mtc-parser-DLdYSejc.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B0V4slKA.js → omit-D53cbS59.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Di5NiW1F.js → pickAttrs-Bbc5HPtu.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B5OMHgEM.js → placementArrow-BkvmO860.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C6U9IWql.js → responsiveObserve-u2y1ryS1.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BMyTH3rN.js → slide-BBqj7svU.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C7d6BG7Z.js → statusUtils-GN0mqu2d.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CR50QnQy.js → styleChecker-BvwPKoBT.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DBEeoMb8.js → useFlexGapSupport-CCt8tnfY.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-3r099cjv.js → useFs-DXAxl2SV.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-C5IYNdTK.js → usePersonalDataHub-DihlR2dj.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DmEOLRHf.js → vnode-D7BOX2gi.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DnpUiVCB.js → zoom-CVbO-nL7.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +4 -4
- package/src/commands/agent.js +3 -3
- package/src/commands/background-session.js +145 -0
- package/src/commands/mcp.js +244 -7
- package/src/commands/serve.js +1 -1
- package/src/commands/session-show.js +129 -0
- package/src/commands/session.js +97 -145
- package/src/commands/skill.js +87 -50
- package/src/data/changelog.json +40 -1
- package/src/gateways/ws/session-protocol.js +14 -4
- package/src/gateways/ws/ws-agent-handler.js +39 -1
- package/src/harness/jsonl-session-store.js +1547 -127
- package/src/harness/mcp-client.js +2690 -409
- package/src/harness/session-index.js +80 -80
- package/src/harness/session-list-index.js +210 -7
- package/src/lazy-dispatch.js +76 -8
- package/src/lib/abort-utils.js +68 -0
- package/src/lib/agent-core.js +1 -59
- package/src/lib/background-interaction-journal.js +2 -2
- package/src/lib/chat-core.js +24 -6
- package/src/lib/command-lifecycle-report.js +516 -0
- package/src/lib/cowork-mcp-tools.js +47 -2
- package/src/lib/cowork-task-runner.js +11 -3
- package/src/lib/doctor-checkup.js +9 -7
- package/src/lib/learning/skill-improver.js +21 -2
- package/src/lib/mcp-call-ledger-store.js +116 -3
- package/src/lib/mcp-call-ledger.js +59 -14
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-stdio-environment.js +48 -0
- package/src/lib/mcp-stdio-executable-identity.js +1133 -0
- package/src/lib/mcp-stdio-execution-authority.js +363 -0
- package/src/lib/mcp-stdio-package-materialization.js +1424 -0
- package/src/lib/mcp-tool-metadata.js +436 -0
- package/src/lib/mcp-tool-result.js +367 -0
- package/src/lib/observability/index.js +71 -0
- package/src/lib/packer/native-update-state.js +979 -1
- package/src/lib/packer/pack-update-applier.js +1050 -125
- package/src/lib/packer/pkg-config-generator.js +1 -0
- package/src/lib/paths.js +53 -0
- package/src/lib/pr-link-ledger.js +8 -27
- package/src/lib/pr-link-store.js +26 -0
- package/src/lib/process-execution-broker/index.js +192 -35
- package/src/lib/process-execution-broker/platform-sandbox.js +291 -24
- package/src/lib/process-tree-termination.js +253 -0
- package/src/lib/recent-session.js +17 -17
- package/src/lib/secure-file-identity.js +47 -0
- package/src/lib/secure-fs.js +21 -2
- package/src/lib/session-anti-rollback-anchor.js +766 -0
- package/src/lib/session-host-lease.js +492 -0
- package/src/lib/session-host-snapshot.js +9 -5
- package/src/lib/session-persistence-failure.js +109 -0
- package/src/lib/session-picker.js +26 -3
- package/src/lib/session-projection.js +5 -1
- package/src/lib/session-search.js +25 -11
- package/src/lib/session-tail.js +31 -1
- package/src/lib/side-effect-ledger-store.js +2 -2
- package/src/lib/skill-budget.js +222 -0
- package/src/lib/skill-execution-authority.js +385 -0
- package/src/lib/skill-execution-identity.js +39 -2
- package/src/lib/skill-loader.js +322 -86
- package/src/lib/skill-mcp.js +43 -1
- package/src/lib/turn-binding-store.js +2 -2
- package/src/repl/agent-repl.js +483 -77
- package/src/repl/bg-dashboard.js +81 -28
- package/src/repl/chat-repl.js +77 -12
- package/src/runtime/agent-core.js +428 -113
- package/src/runtime/agent-runtime.js +26 -3
- package/src/runtime/cleanup-deadline.js +143 -0
- package/src/runtime/headless-runner.js +494 -149
- package/src/runtime/headless-stream.js +504 -75
- package/src/runtime/mcp-config.js +157 -10
- package/src/runtime/mcp-tool-search.js +0 -0
- package/src/runtime/output-backpressure.js +419 -0
- package/src/runtime/pipe-safety.js +56 -21
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-DqnoFPgR.js +0 -1
- package/src/assets/web-panel/assets/Providers-uO-10aP1.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +0 -1
- package/src/assets/web-panel/assets/index-0O5ePnUq.js +0 -1
- package/src/assets/web-panel/assets/index-ChAzPigF.js +0 -1
- package/src/lib/skill-process-broker.js +0 -85
|
@@ -92,11 +92,13 @@ import {
|
|
|
92
92
|
readVerifiedProjection as jsonlReadVerifiedProjection,
|
|
93
93
|
findLatestEvent as jsonlFindLatestEvent,
|
|
94
94
|
sessionExists as jsonlSessionExists,
|
|
95
|
+
sessionHasPersistedEvidence as jsonlSessionHasPersistedEvidence,
|
|
95
96
|
} from "../harness/jsonl-session-store.js";
|
|
96
97
|
import {
|
|
97
98
|
isVerifiedSessionHostSnapshot,
|
|
98
99
|
readSessionHostResumeState,
|
|
99
100
|
} from "../lib/session-host-snapshot.js";
|
|
101
|
+
import { acquireSessionHostLease } from "../lib/session-host-lease.js";
|
|
100
102
|
import { classifyStreamRetryReason } from "../lib/stream-retry.js";
|
|
101
103
|
import {
|
|
102
104
|
SideEffectLedger,
|
|
@@ -123,6 +125,15 @@ import {
|
|
|
123
125
|
parseSessionSlashCommandEvent,
|
|
124
126
|
} from "./session-slash-commands.js";
|
|
125
127
|
import { extractPluginUsageAttribution } from "../lib/plugin-usage-attribution.js";
|
|
128
|
+
import { createHeadlessOutputBackpressure } from "./output-backpressure.js";
|
|
129
|
+
import {
|
|
130
|
+
cleanupDeadlineError,
|
|
131
|
+
createCleanupDeadline,
|
|
132
|
+
} from "./cleanup-deadline.js";
|
|
133
|
+
import {
|
|
134
|
+
createSessionPersistenceFailure,
|
|
135
|
+
projectSessionPersistenceFailure,
|
|
136
|
+
} from "../lib/session-persistence-failure.js";
|
|
126
137
|
|
|
127
138
|
function invalidMcpRecoveryTransaction(capability, expected) {
|
|
128
139
|
const error = new TypeError(
|
|
@@ -778,6 +789,7 @@ async function runTurn(
|
|
|
778
789
|
sideEffects,
|
|
779
790
|
turnNumber,
|
|
780
791
|
turnBindingFeed,
|
|
792
|
+
waitForOutput,
|
|
781
793
|
now = Date.now,
|
|
782
794
|
},
|
|
783
795
|
) {
|
|
@@ -886,6 +898,7 @@ async function runTurn(
|
|
|
886
898
|
})
|
|
887
899
|
.start(opId);
|
|
888
900
|
sideEffects.persist();
|
|
901
|
+
sideEffects.assert?.();
|
|
889
902
|
}
|
|
890
903
|
}
|
|
891
904
|
break;
|
|
@@ -1017,6 +1030,7 @@ async function runTurn(
|
|
|
1017
1030
|
if (event.type) emit(event);
|
|
1018
1031
|
break;
|
|
1019
1032
|
}
|
|
1033
|
+
await waitForOutput?.();
|
|
1020
1034
|
// Hard cost cap reached — stop consuming the loop (break the for-await, not
|
|
1021
1035
|
// just the switch) so no further paid LLM call is made.
|
|
1022
1036
|
if (stopForCost) break;
|
|
@@ -1044,25 +1058,305 @@ async function runTurn(
|
|
|
1044
1058
|
* writeOut, writeErr, expandFileRefs }
|
|
1045
1059
|
* @returns {Promise<{exitCode:number, turns:number}>}
|
|
1046
1060
|
*/
|
|
1061
|
+
function createHeadlessStreamCleanupScope({ input, timeoutMs, onReport }) {
|
|
1062
|
+
let mcpClient = null;
|
|
1063
|
+
let remoteApproval = null;
|
|
1064
|
+
let inputCancel = null;
|
|
1065
|
+
let interactionCleanup = null;
|
|
1066
|
+
let outputCleanup = null;
|
|
1067
|
+
let sessionEnd = null;
|
|
1068
|
+
let reason = "error";
|
|
1069
|
+
let stopRequested = false;
|
|
1070
|
+
let interactionCleanupPromise = Promise.resolve();
|
|
1071
|
+
let cleanupPromise = null;
|
|
1072
|
+
|
|
1073
|
+
const runBestEffort = async (operation) => {
|
|
1074
|
+
if (typeof operation !== "function") return;
|
|
1075
|
+
try {
|
|
1076
|
+
await operation();
|
|
1077
|
+
} catch {
|
|
1078
|
+
// Cleanup is exhaustive: one failing disposer must not prevent the
|
|
1079
|
+
// remaining resources from being retired.
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1083
|
+
const stopInputOwner = () => {
|
|
1084
|
+
try {
|
|
1085
|
+
if (input && input.readableEnded !== true && input.destroyed !== true) {
|
|
1086
|
+
if (typeof input.destroy === "function") input.destroy();
|
|
1087
|
+
else input.pause?.();
|
|
1088
|
+
}
|
|
1089
|
+
} catch {
|
|
1090
|
+
// Best-effort for non-Node custom input objects.
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
const cancelInputIterator = () => {
|
|
1094
|
+
try {
|
|
1095
|
+
Promise.resolve(inputCancel?.()).catch(() => {});
|
|
1096
|
+
} catch {
|
|
1097
|
+
// A custom async iterator may reject cancellation synchronously.
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
const settleInteractions = () => {
|
|
1101
|
+
interactionCleanupPromise = runBestEffort(() =>
|
|
1102
|
+
interactionCleanup?.(reason),
|
|
1103
|
+
);
|
|
1104
|
+
};
|
|
1105
|
+
const requestStop = () => {
|
|
1106
|
+
if (stopRequested) return;
|
|
1107
|
+
stopRequested = true;
|
|
1108
|
+
stopInputOwner();
|
|
1109
|
+
cancelInputIterator();
|
|
1110
|
+
settleInteractions();
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
return {
|
|
1114
|
+
setReason(nextReason) {
|
|
1115
|
+
if (!stopRequested && !cleanupPromise && nextReason) {
|
|
1116
|
+
reason = String(nextReason);
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
setMcpClient(client) {
|
|
1120
|
+
mcpClient = client || null;
|
|
1121
|
+
},
|
|
1122
|
+
setRemoteApproval(approval) {
|
|
1123
|
+
remoteApproval = approval || null;
|
|
1124
|
+
},
|
|
1125
|
+
setInputCancel(cancel) {
|
|
1126
|
+
inputCancel = typeof cancel === "function" ? cancel : null;
|
|
1127
|
+
if (stopRequested) cancelInputIterator();
|
|
1128
|
+
},
|
|
1129
|
+
setInteractionCleanup(cleanup) {
|
|
1130
|
+
interactionCleanup = typeof cleanup === "function" ? cleanup : null;
|
|
1131
|
+
if (stopRequested) settleInteractions();
|
|
1132
|
+
},
|
|
1133
|
+
setOutputCleanup(cleanup) {
|
|
1134
|
+
outputCleanup = typeof cleanup === "function" ? cleanup : null;
|
|
1135
|
+
},
|
|
1136
|
+
setSessionEnd(cleanup) {
|
|
1137
|
+
sessionEnd = typeof cleanup === "function" ? cleanup : null;
|
|
1138
|
+
},
|
|
1139
|
+
requestStop,
|
|
1140
|
+
cleanup() {
|
|
1141
|
+
if (!cleanupPromise) {
|
|
1142
|
+
cleanupPromise = (async () => {
|
|
1143
|
+
const deadline = createCleanupDeadline({
|
|
1144
|
+
timeoutMs,
|
|
1145
|
+
label: "headless-stream-cleanup",
|
|
1146
|
+
});
|
|
1147
|
+
// Stop the live stdin owner before network/process resources. A
|
|
1148
|
+
// flowing stdin iterator can otherwise keep the process alive after
|
|
1149
|
+
// an early return or exception.
|
|
1150
|
+
requestStop();
|
|
1151
|
+
await deadline.run("interactions", () => interactionCleanupPromise);
|
|
1152
|
+
await deadline.run("output-coalescer", () => outputCleanup?.(reason));
|
|
1153
|
+
await deadline.run("mcp", () => mcpClient?.disconnectAll?.());
|
|
1154
|
+
await deadline.run("remote-approval", () =>
|
|
1155
|
+
remoteApproval?.close?.(),
|
|
1156
|
+
);
|
|
1157
|
+
await deadline.run("session-end", () => sessionEnd?.(reason));
|
|
1158
|
+
const report = deadline.report();
|
|
1159
|
+
try {
|
|
1160
|
+
onReport?.(report);
|
|
1161
|
+
} catch {
|
|
1162
|
+
// Metrics/reporting cannot change cleanup semantics.
|
|
1163
|
+
}
|
|
1164
|
+
if (report.timedOut) throw cleanupDeadlineError(report);
|
|
1165
|
+
return report;
|
|
1166
|
+
})();
|
|
1167
|
+
}
|
|
1168
|
+
return cleanupPromise;
|
|
1169
|
+
},
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
const STREAM_SESSION_ID = Symbol("streamSessionId");
|
|
1174
|
+
const STREAM_SESSION_HOST_LEASE = Symbol("streamSessionHostLease");
|
|
1175
|
+
|
|
1176
|
+
async function withStreamSessionHostLease(options, deps, streamCleanup, task) {
|
|
1177
|
+
const sessionId =
|
|
1178
|
+
options.sessionId || `headless-stream-${Date.now()}-${process.pid}`;
|
|
1179
|
+
const persist = Boolean(options.sessionId) && options.ephemeral !== true;
|
|
1180
|
+
const hasInjectedSessionStore =
|
|
1181
|
+
typeof deps.sessionHasPersistedEvidence === "function" ||
|
|
1182
|
+
typeof deps.sessionExists === "function" ||
|
|
1183
|
+
typeof deps.rebuildMessages === "function" ||
|
|
1184
|
+
typeof deps.appendEvent === "function" ||
|
|
1185
|
+
typeof deps.appendAuthorityEvent === "function" ||
|
|
1186
|
+
typeof deps.readVerifiedEvents === "function" ||
|
|
1187
|
+
typeof deps.readVerifiedProjection === "function";
|
|
1188
|
+
const acquireHostLease =
|
|
1189
|
+
deps.acquireSessionHostLease ||
|
|
1190
|
+
(!hasInjectedSessionStore ? acquireSessionHostLease : null);
|
|
1191
|
+
let lease = null;
|
|
1192
|
+
let onLeaseAbort = null;
|
|
1193
|
+
try {
|
|
1194
|
+
if (persist && typeof acquireHostLease === "function") {
|
|
1195
|
+
lease = acquireHostLease(sessionId, { hostKind: "headless-stream" });
|
|
1196
|
+
if (lease?.signal) {
|
|
1197
|
+
onLeaseAbort = () => {
|
|
1198
|
+
streamCleanup.setReason("session_host_lease_lost");
|
|
1199
|
+
streamCleanup.requestStop();
|
|
1200
|
+
};
|
|
1201
|
+
lease.signal.addEventListener("abort", onLeaseAbort, { once: true });
|
|
1202
|
+
if (lease.signal.aborted) onLeaseAbort();
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
const scopedOptions = lease?.signal
|
|
1206
|
+
? {
|
|
1207
|
+
...options,
|
|
1208
|
+
signal: options.signal
|
|
1209
|
+
? AbortSignal.any([options.signal, lease.signal])
|
|
1210
|
+
: lease.signal,
|
|
1211
|
+
}
|
|
1212
|
+
: options;
|
|
1213
|
+
return await task(scopedOptions, {
|
|
1214
|
+
...deps,
|
|
1215
|
+
[STREAM_SESSION_ID]: sessionId,
|
|
1216
|
+
[STREAM_SESSION_HOST_LEASE]: lease,
|
|
1217
|
+
});
|
|
1218
|
+
} finally {
|
|
1219
|
+
if (lease?.signal && onLeaseAbort) {
|
|
1220
|
+
lease.signal.removeEventListener("abort", onLeaseAbort);
|
|
1221
|
+
}
|
|
1222
|
+
lease?.release?.();
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1047
1226
|
export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
1048
1227
|
const trustedWorkspaceRoot = options.cwd || process.cwd();
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1228
|
+
const input = deps.input || process.stdin;
|
|
1229
|
+
const pipeState = { closed: false };
|
|
1230
|
+
const streamCleanup = createHeadlessStreamCleanupScope({
|
|
1231
|
+
input,
|
|
1232
|
+
timeoutMs: deps.cleanupDeadlineMs,
|
|
1233
|
+
onReport: deps.onCleanupReport,
|
|
1234
|
+
});
|
|
1235
|
+
const stdout = deps.stdout || process.stdout;
|
|
1236
|
+
const stderr = deps.stderr || process.stderr;
|
|
1237
|
+
const shouldInstallPipeSafety =
|
|
1238
|
+
typeof deps.installPipeSafety === "function" ||
|
|
1239
|
+
(!deps.writeOut && !deps.writeErr);
|
|
1240
|
+
const pipeAbort = shouldInstallPipeSafety ? new AbortController() : null;
|
|
1241
|
+
const managesNativeOutput = !deps.writeOut || !deps.writeErr;
|
|
1242
|
+
const outputAbort = managesNativeOutput ? new AbortController() : null;
|
|
1243
|
+
const handlePipeClosed = () => {
|
|
1244
|
+
if (pipeState.closed) return;
|
|
1245
|
+
pipeState.closed = true;
|
|
1246
|
+
streamCleanup.setReason("pipe_closed");
|
|
1247
|
+
process.exitCode = 0;
|
|
1248
|
+
pipeAbort?.abort();
|
|
1249
|
+
streamCleanup.requestStop();
|
|
1250
|
+
};
|
|
1251
|
+
const outputFlow =
|
|
1252
|
+
deps.outputFlow ||
|
|
1253
|
+
createHeadlessOutputBackpressure({
|
|
1254
|
+
stdout,
|
|
1255
|
+
stderr,
|
|
1256
|
+
writeOut: deps.writeOut,
|
|
1257
|
+
writeErr: deps.writeErr,
|
|
1258
|
+
maxQueuedBytes: deps.outputBackpressureMaxBytes,
|
|
1259
|
+
drainTimeoutMs: deps.outputBackpressureTimeoutMs,
|
|
1260
|
+
onFailure: (error) => {
|
|
1261
|
+
outputAbort?.abort(error);
|
|
1262
|
+
if (error?.code === "EPIPE") handlePipeClosed();
|
|
1263
|
+
},
|
|
1264
|
+
});
|
|
1265
|
+
const abortSignals = [
|
|
1266
|
+
options.signal,
|
|
1267
|
+
pipeAbort?.signal,
|
|
1268
|
+
outputAbort?.signal,
|
|
1269
|
+
].filter(Boolean);
|
|
1270
|
+
const runtimeOptions =
|
|
1271
|
+
abortSignals.length === 0
|
|
1272
|
+
? options
|
|
1273
|
+
: {
|
|
1274
|
+
...options,
|
|
1275
|
+
signal:
|
|
1276
|
+
abortSignals.length === 1
|
|
1277
|
+
? abortSignals[0]
|
|
1278
|
+
: AbortSignal.any(abortSignals),
|
|
1279
|
+
};
|
|
1280
|
+
const runtimeDeps = {
|
|
1281
|
+
...deps,
|
|
1282
|
+
writeOut: outputFlow.writeOut,
|
|
1283
|
+
writeErr: outputFlow.writeErr,
|
|
1284
|
+
outputFlow,
|
|
1285
|
+
};
|
|
1286
|
+
const onCallerAbort = () => {
|
|
1287
|
+
streamCleanup.setReason("aborted");
|
|
1288
|
+
streamCleanup.requestStop();
|
|
1289
|
+
};
|
|
1290
|
+
options.signal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
1291
|
+
if (options.signal?.aborted) onCallerAbort();
|
|
1292
|
+
const installSafety = deps.installPipeSafety || installPipeSafety;
|
|
1293
|
+
let disposePipeSafety = null;
|
|
1294
|
+
|
|
1295
|
+
let outcome;
|
|
1296
|
+
let failure = null;
|
|
1297
|
+
try {
|
|
1298
|
+
disposePipeSafety = shouldInstallPipeSafety
|
|
1299
|
+
? installSafety([stdout, stderr], handlePipeClosed)
|
|
1300
|
+
: null;
|
|
1301
|
+
|
|
1302
|
+
outcome = await runWithHostHooksV2Workspace(trustedWorkspaceRoot, () =>
|
|
1303
|
+
withStreamSessionHostLease(
|
|
1304
|
+
runtimeOptions,
|
|
1305
|
+
runtimeDeps,
|
|
1306
|
+
streamCleanup,
|
|
1307
|
+
(scopedOptions, scopedDeps) =>
|
|
1308
|
+
runAgentHeadlessStreamInWorkspace(
|
|
1309
|
+
scopedOptions,
|
|
1310
|
+
scopedDeps,
|
|
1311
|
+
pipeState,
|
|
1312
|
+
streamCleanup,
|
|
1313
|
+
),
|
|
1314
|
+
),
|
|
1315
|
+
);
|
|
1316
|
+
} catch (error) {
|
|
1317
|
+
failure = error;
|
|
1318
|
+
} finally {
|
|
1319
|
+
try {
|
|
1320
|
+
await streamCleanup.cleanup();
|
|
1321
|
+
} catch (error) {
|
|
1322
|
+
failure ||= error;
|
|
1323
|
+
}
|
|
1324
|
+
try {
|
|
1325
|
+
await outputFlow.wait();
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
failure ||= error;
|
|
1328
|
+
}
|
|
1329
|
+
outputFlow.dispose();
|
|
1330
|
+
options.signal?.removeEventListener("abort", onCallerAbort);
|
|
1331
|
+
disposePipeSafety?.();
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
if (pipeState.closed && failure?.isCleanupDeadlineFailure !== true) {
|
|
1335
|
+
return { ...(outcome || { turns: 0 }), exitCode: 0 };
|
|
1336
|
+
}
|
|
1337
|
+
if (failure) throw failure;
|
|
1338
|
+
return outcome;
|
|
1052
1339
|
}
|
|
1053
1340
|
|
|
1054
|
-
async function runAgentHeadlessStreamInWorkspace(
|
|
1341
|
+
async function runAgentHeadlessStreamInWorkspace(
|
|
1342
|
+
options = {},
|
|
1343
|
+
deps = {},
|
|
1344
|
+
pipeState = { closed: false },
|
|
1345
|
+
streamCleanup,
|
|
1346
|
+
) {
|
|
1055
1347
|
const model = options.model || "qwen2.5:7b";
|
|
1056
1348
|
const provider = options.provider || "ollama";
|
|
1057
1349
|
const baseUrl = options.baseUrl || "http://localhost:11434";
|
|
1058
1350
|
const apiKey = options.apiKey || null;
|
|
1059
|
-
const writeOut = deps.writeOut
|
|
1060
|
-
const writeErr = deps.writeErr
|
|
1061
|
-
if (!deps.writeOut && !deps.writeErr) installPipeSafety();
|
|
1351
|
+
const writeOut = deps.writeOut;
|
|
1352
|
+
const writeErr = deps.writeErr;
|
|
1062
1353
|
|
|
1063
1354
|
const sessionId =
|
|
1064
|
-
|
|
1355
|
+
deps[STREAM_SESSION_ID] ||
|
|
1356
|
+
options.sessionId ||
|
|
1357
|
+
`headless-stream-${Date.now()}-${process.pid}`;
|
|
1065
1358
|
const persist = Boolean(options.sessionId) && options.ephemeral !== true;
|
|
1359
|
+
const sessionHostLease = deps[STREAM_SESSION_HOST_LEASE] || null;
|
|
1066
1360
|
const traceId = resolveTraceId(options, process.env, deps);
|
|
1067
1361
|
const fieldGate = {
|
|
1068
1362
|
seq: true,
|
|
@@ -1079,8 +1373,10 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1079
1373
|
fieldGate,
|
|
1080
1374
|
});
|
|
1081
1375
|
const emit = streamCoalescer.emit;
|
|
1376
|
+
streamCleanup.setOutputCleanup(() => streamCoalescer.flush?.());
|
|
1082
1377
|
|
|
1083
1378
|
const hasInjectedSessionStore =
|
|
1379
|
+
typeof deps.sessionHasPersistedEvidence === "function" ||
|
|
1084
1380
|
typeof deps.sessionExists === "function" ||
|
|
1085
1381
|
typeof deps.rebuildMessages === "function" ||
|
|
1086
1382
|
typeof deps.appendEvent === "function" ||
|
|
@@ -1118,7 +1414,10 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1118
1414
|
? jsonlReadVerifiedProjection
|
|
1119
1415
|
: null;
|
|
1120
1416
|
const resumeDependencies = {
|
|
1121
|
-
sessionExists:
|
|
1417
|
+
sessionExists:
|
|
1418
|
+
deps.sessionHasPersistedEvidence ||
|
|
1419
|
+
deps.sessionExists ||
|
|
1420
|
+
jsonlSessionHasPersistedEvidence,
|
|
1122
1421
|
readVerifiedEvents: (...args) =>
|
|
1123
1422
|
requireSynchronousRecoveryResult(
|
|
1124
1423
|
readVerifiedEvents(...args),
|
|
@@ -1293,7 +1592,7 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1293
1592
|
cwd,
|
|
1294
1593
|
settingsFile: options.settingsFile,
|
|
1295
1594
|
});
|
|
1296
|
-
if (notice)
|
|
1595
|
+
if (notice) writeErr(notice + "\n");
|
|
1297
1596
|
} catch {
|
|
1298
1597
|
/* trust notice is best-effort */
|
|
1299
1598
|
}
|
|
@@ -1334,6 +1633,20 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1334
1633
|
const doExpand = deps.expandFileRefs || expandFileRefsAsync;
|
|
1335
1634
|
const runSessionSlashCommand =
|
|
1336
1635
|
deps.executeSessionSlashCommand || executeSessionSlashCommand;
|
|
1636
|
+
let turns = 0;
|
|
1637
|
+
|
|
1638
|
+
streamCleanup.setSessionEnd(async (reason) => {
|
|
1639
|
+
if (!settingsHooks) return;
|
|
1640
|
+
const runObserveHooks =
|
|
1641
|
+
deps.runObserveHooks ||
|
|
1642
|
+
(await import("../lib/settings-hook-events.js")).runObserveHooks;
|
|
1643
|
+
runObserveHooks(
|
|
1644
|
+
settingsHooks,
|
|
1645
|
+
"SessionEnd",
|
|
1646
|
+
{ reason, cwd, session_id: sessionId },
|
|
1647
|
+
{ cwd },
|
|
1648
|
+
);
|
|
1649
|
+
});
|
|
1337
1650
|
|
|
1338
1651
|
const getApprovalGate =
|
|
1339
1652
|
deps.getApprovalGate ||
|
|
@@ -1388,6 +1701,7 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1388
1701
|
result: reason,
|
|
1389
1702
|
session_id: sessionId,
|
|
1390
1703
|
});
|
|
1704
|
+
streamCleanup.setReason("blocked");
|
|
1391
1705
|
return { exitCode: 2, turns: 0 };
|
|
1392
1706
|
}
|
|
1393
1707
|
|
|
@@ -1430,6 +1744,7 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1430
1744
|
(hasInjectedSessionStore ? () => true : jsonlAppendEvent),
|
|
1431
1745
|
appendAuthorityEvent:
|
|
1432
1746
|
deps.appendAuthorityEvent ||
|
|
1747
|
+
deps.appendEvent ||
|
|
1433
1748
|
(hasInjectedSessionStore
|
|
1434
1749
|
? unavailableAuthorityCapability("appendAuthorityEvent")
|
|
1435
1750
|
: jsonlAppendAuthorityEvent),
|
|
@@ -1444,11 +1759,7 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1444
1759
|
(hasInjectedSessionStore ? null : jsonlReadVerifiedProjection),
|
|
1445
1760
|
findLatestEvent: storeFindLatestEvent,
|
|
1446
1761
|
};
|
|
1447
|
-
|
|
1448
|
-
? createSessionMcpLedgerSink(sessionId, {
|
|
1449
|
-
appendEvent: store.appendAuthorityEvent,
|
|
1450
|
-
})
|
|
1451
|
-
: null;
|
|
1762
|
+
let mcpLedgerSink = null;
|
|
1452
1763
|
|
|
1453
1764
|
// ── P0-2: crash-safe side-effect ledger — the stream twin of the
|
|
1454
1765
|
// single-prompt runner (headless-runner.js) and the WS bridge
|
|
@@ -1494,6 +1805,7 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1494
1805
|
return persisted;
|
|
1495
1806
|
},
|
|
1496
1807
|
nextOpId: () => `${sideEffectRunNonce}:${sideEffectSeq++}`,
|
|
1808
|
+
assert: () => sessionHostLease?.assert?.(),
|
|
1497
1809
|
}
|
|
1498
1810
|
: null;
|
|
1499
1811
|
|
|
@@ -1566,7 +1878,7 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1566
1878
|
if (!turnBindingFeed?.isDirty()) return true;
|
|
1567
1879
|
if (turnBindingPersistenceError) return false;
|
|
1568
1880
|
try {
|
|
1569
|
-
const persisted = store.
|
|
1881
|
+
const persisted = store.appendAuthorityEvent(
|
|
1570
1882
|
sessionId,
|
|
1571
1883
|
TURN_BINDING_EVENT,
|
|
1572
1884
|
turnBindingLog.toJSON(),
|
|
@@ -1740,6 +2052,37 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1740
2052
|
e.code = "USER_TIMEOUT"; // handler → user_timeout (model proceeds, not a failure)
|
|
1741
2053
|
p.reject(e);
|
|
1742
2054
|
};
|
|
2055
|
+
streamCleanup.setInteractionCleanup((reason) => {
|
|
2056
|
+
const via = reason === "pipe_closed" ? "pipe-closed" : "session-closed";
|
|
2057
|
+
for (const [id, pending] of [...pendingApprovals.entries()]) {
|
|
2058
|
+
pendingApprovals.delete(id);
|
|
2059
|
+
clearTimeout(pending.timer);
|
|
2060
|
+
try {
|
|
2061
|
+
emit({
|
|
2062
|
+
type: "approval_resolved",
|
|
2063
|
+
id,
|
|
2064
|
+
approved: false,
|
|
2065
|
+
via,
|
|
2066
|
+
session_id: sessionId,
|
|
2067
|
+
});
|
|
2068
|
+
} catch {
|
|
2069
|
+
// The output pipe may itself be the reason cleanup is running.
|
|
2070
|
+
}
|
|
2071
|
+
pending.resolve(false);
|
|
2072
|
+
}
|
|
2073
|
+
for (const [id, pending] of [...pendingQuestions.entries()]) {
|
|
2074
|
+
pendingQuestions.delete(id);
|
|
2075
|
+
clearTimeout(pending.timer);
|
|
2076
|
+
try {
|
|
2077
|
+
emit({ type: "question_resolved", id, via, session_id: sessionId });
|
|
2078
|
+
} catch {
|
|
2079
|
+
// Best-effort cleanup notification.
|
|
2080
|
+
}
|
|
2081
|
+
const error = new Error(`ask_user_question ${via}`);
|
|
2082
|
+
error.code = "USER_TIMEOUT";
|
|
2083
|
+
pending.reject(error);
|
|
2084
|
+
}
|
|
2085
|
+
});
|
|
1743
2086
|
const interactionAskUser = ({
|
|
1744
2087
|
question,
|
|
1745
2088
|
options: qOptions,
|
|
@@ -1951,18 +2294,41 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
1951
2294
|
}),
|
|
1952
2295
|
"startSession",
|
|
1953
2296
|
);
|
|
1954
|
-
} catch {
|
|
2297
|
+
} catch (error) {
|
|
1955
2298
|
const code = "CC_SESSION_PERSISTENCE_START_FAILED";
|
|
2299
|
+
const persistenceError = createSessionPersistenceFailure(error, {
|
|
2300
|
+
sessionId,
|
|
2301
|
+
operation: "session-start",
|
|
2302
|
+
});
|
|
2303
|
+
const persistence = projectSessionPersistenceFailure(persistenceError, {
|
|
2304
|
+
phase: "before-model",
|
|
2305
|
+
});
|
|
2306
|
+
if (persistence) {
|
|
2307
|
+
try {
|
|
2308
|
+
deps.onPersistenceFailure?.(persistence);
|
|
2309
|
+
} catch {
|
|
2310
|
+
// Observability cannot change persistence semantics.
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
1956
2313
|
emit({
|
|
1957
2314
|
type: "result",
|
|
1958
|
-
subtype:
|
|
2315
|
+
subtype: persistence
|
|
2316
|
+
? "error_persistence"
|
|
2317
|
+
: "error_session_persistence",
|
|
1959
2318
|
is_error: true,
|
|
1960
|
-
code,
|
|
2319
|
+
code: persistence?.code || code,
|
|
1961
2320
|
error: "Canonical stream session could not be created",
|
|
1962
2321
|
session_id: sessionId,
|
|
2322
|
+
...(persistence ? { persistence } : {}),
|
|
1963
2323
|
});
|
|
1964
|
-
writeErr(
|
|
1965
|
-
|
|
2324
|
+
writeErr(
|
|
2325
|
+
`${persistence?.code || code}: canonical stream session creation was refused\n`,
|
|
2326
|
+
);
|
|
2327
|
+
return {
|
|
2328
|
+
exitCode: 1,
|
|
2329
|
+
turns: 0,
|
|
2330
|
+
...(persistence ? { persistence } : {}),
|
|
2331
|
+
};
|
|
1966
2332
|
}
|
|
1967
2333
|
}
|
|
1968
2334
|
}
|
|
@@ -2089,6 +2455,7 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
2089
2455
|
loadJetbrainsMcp: deps.loadJetbrainsMcp,
|
|
2090
2456
|
},
|
|
2091
2457
|
);
|
|
2458
|
+
streamCleanup.setMcpClient(mcp?.mcpClient);
|
|
2092
2459
|
// MCP tool search (context scaling): defer big tool schemas behind the
|
|
2093
2460
|
// internal tool_search tool. Below-threshold / disabled → no-op.
|
|
2094
2461
|
if (mcp) {
|
|
@@ -2114,6 +2481,14 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
2114
2481
|
}
|
|
2115
2482
|
}
|
|
2116
2483
|
|
|
2484
|
+
mcpLedgerSink = persist
|
|
2485
|
+
? createSessionMcpLedgerSink(
|
|
2486
|
+
sessionId,
|
|
2487
|
+
hasInjectedSessionStore
|
|
2488
|
+
? { appendEvent: store.appendAuthorityEvent }
|
|
2489
|
+
: { recovery: mcpLedgerRecovery },
|
|
2490
|
+
)
|
|
2491
|
+
: null;
|
|
2117
2492
|
const mcpRecoveryRuntime = createMcpHostRecoveryRuntime({
|
|
2118
2493
|
bundle: mcp,
|
|
2119
2494
|
sessionId,
|
|
@@ -2156,7 +2531,7 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
2156
2531
|
});
|
|
2157
2532
|
}
|
|
2158
2533
|
if (mcp?.mcpClient?.setElicitationHandler && interactiveQuestions) {
|
|
2159
|
-
|
|
2534
|
+
const elicitationHandler = async (request) => {
|
|
2160
2535
|
const hookContext = {
|
|
2161
2536
|
schema_version: 1,
|
|
2162
2537
|
server: request.server || null,
|
|
@@ -2214,6 +2589,9 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
2214
2589
|
});
|
|
2215
2590
|
throw error;
|
|
2216
2591
|
}
|
|
2592
|
+
};
|
|
2593
|
+
mcp.mcpClient.setElicitationHandler(elicitationHandler, {
|
|
2594
|
+
signal: options.signal || null,
|
|
2217
2595
|
});
|
|
2218
2596
|
}
|
|
2219
2597
|
|
|
@@ -2247,7 +2625,6 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
2247
2625
|
is_error: true,
|
|
2248
2626
|
error: err.message,
|
|
2249
2627
|
});
|
|
2250
|
-
if (mcp?.mcpClient) await mcp.mcpClient.disconnectAll().catch(() => {});
|
|
2251
2628
|
return { exitCode: 1, turns: 0 };
|
|
2252
2629
|
}
|
|
2253
2630
|
if (approvalGate && typeof approvalGate.setConfirmer === "function") {
|
|
@@ -2273,11 +2650,14 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
2273
2650
|
options.interactiveApprovals !== true
|
|
2274
2651
|
) {
|
|
2275
2652
|
try {
|
|
2276
|
-
const
|
|
2277
|
-
|
|
2278
|
-
|
|
2653
|
+
const startRemoteApproval =
|
|
2654
|
+
deps.startHeadlessRemoteApproval ||
|
|
2655
|
+
(await import("../lib/remote-approval-bridge.js"))
|
|
2656
|
+
.startHeadlessRemoteApproval;
|
|
2657
|
+
_remoteApproval = await startRemoteApproval({
|
|
2279
2658
|
agentSessionId: sessionId,
|
|
2280
2659
|
});
|
|
2660
|
+
streamCleanup.setRemoteApproval(_remoteApproval);
|
|
2281
2661
|
emit({
|
|
2282
2662
|
type: "remote_control",
|
|
2283
2663
|
subtype: "pairing",
|
|
@@ -2414,8 +2794,8 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
2414
2794
|
})
|
|
2415
2795
|
: null;
|
|
2416
2796
|
|
|
2417
|
-
let turns = 0;
|
|
2418
2797
|
let sawError = false;
|
|
2798
|
+
let terminalPersistenceFailure = null;
|
|
2419
2799
|
// Session-scoped tool-call correlation ids ("tu-<n>", additive protocol-v1
|
|
2420
2800
|
// field): one counter across ALL turns so ids never repeat within a session.
|
|
2421
2801
|
// Gated by the capability handshake (docs/PROTOCOL.md §1.3): a client that
|
|
@@ -2447,9 +2827,11 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
2447
2827
|
let inputDone = false;
|
|
2448
2828
|
let currentAbort = null;
|
|
2449
2829
|
let slashRequestSeq = 0;
|
|
2830
|
+
const inputLines = readJsonLines(input);
|
|
2831
|
+
streamCleanup.setInputCancel(() => inputLines.return?.());
|
|
2450
2832
|
(async () => {
|
|
2451
2833
|
try {
|
|
2452
|
-
for await (const line of
|
|
2834
|
+
for await (const line of inputLines) {
|
|
2453
2835
|
const parsed = parseInputEvent(line);
|
|
2454
2836
|
if (parsed == null) continue;
|
|
2455
2837
|
if (parsed.hello) {
|
|
@@ -2537,17 +2919,28 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
2537
2919
|
const nextEvent = async () => {
|
|
2538
2920
|
for (;;) {
|
|
2539
2921
|
if (queue.length > 0) return queue.shift();
|
|
2540
|
-
if (inputDone) return null;
|
|
2922
|
+
if (inputDone || options.signal?.aborted) return null;
|
|
2541
2923
|
await new Promise((r) => {
|
|
2542
|
-
|
|
2924
|
+
let settled = false;
|
|
2925
|
+
const wake = () => {
|
|
2926
|
+
if (settled) return;
|
|
2927
|
+
settled = true;
|
|
2928
|
+
options.signal?.removeEventListener("abort", wake);
|
|
2929
|
+
if (wakeQueue === wake) wakeQueue = null;
|
|
2930
|
+
r();
|
|
2931
|
+
};
|
|
2932
|
+
wakeQueue = wake;
|
|
2933
|
+
options.signal?.addEventListener("abort", wake, { once: true });
|
|
2934
|
+
if (options.signal?.aborted) wake();
|
|
2543
2935
|
});
|
|
2544
|
-
wakeQueue = null;
|
|
2545
2936
|
}
|
|
2546
2937
|
};
|
|
2547
2938
|
|
|
2548
2939
|
for (;;) {
|
|
2940
|
+
await deps.outputFlow?.wait?.();
|
|
2549
2941
|
const parsed = await nextEvent();
|
|
2550
2942
|
if (parsed == null) break; // stdin closed
|
|
2943
|
+
sessionHostLease?.assert?.();
|
|
2551
2944
|
if (turnBindingPersistenceError) {
|
|
2552
2945
|
emitTurnBindingFailure();
|
|
2553
2946
|
sawError = true;
|
|
@@ -3036,14 +3429,44 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
3036
3429
|
}
|
|
3037
3430
|
|
|
3038
3431
|
messages.push({ role: "user", content: turnContent });
|
|
3432
|
+
let persistenceFailure = null;
|
|
3039
3433
|
if (persist) {
|
|
3040
3434
|
try {
|
|
3041
3435
|
store.appendUserMessage(sessionId, turnContent);
|
|
3042
|
-
} catch {
|
|
3043
|
-
|
|
3436
|
+
} catch (error) {
|
|
3437
|
+
const persistenceError = createSessionPersistenceFailure(error, {
|
|
3438
|
+
sessionId,
|
|
3439
|
+
operation: "user-turn-append",
|
|
3440
|
+
});
|
|
3441
|
+
persistenceFailure = projectSessionPersistenceFailure(
|
|
3442
|
+
persistenceError,
|
|
3443
|
+
{ phase: "before-model" },
|
|
3444
|
+
);
|
|
3445
|
+
if (persistenceFailure) {
|
|
3446
|
+
try {
|
|
3447
|
+
deps.onPersistenceFailure?.(persistenceFailure);
|
|
3448
|
+
} catch {
|
|
3449
|
+
// Observability cannot change persistence semantics.
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3044
3452
|
}
|
|
3045
3453
|
}
|
|
3046
3454
|
turns += 1;
|
|
3455
|
+
if (persistenceFailure) {
|
|
3456
|
+
terminalPersistenceFailure = persistenceFailure;
|
|
3457
|
+
sawError = true;
|
|
3458
|
+
emit({
|
|
3459
|
+
type: "result",
|
|
3460
|
+
subtype: "error_persistence",
|
|
3461
|
+
is_error: true,
|
|
3462
|
+
code: persistenceFailure.code,
|
|
3463
|
+
error: "Session user turn was not durably persisted",
|
|
3464
|
+
session_id: sessionId,
|
|
3465
|
+
turn: turns,
|
|
3466
|
+
persistence: persistenceFailure,
|
|
3467
|
+
});
|
|
3468
|
+
break;
|
|
3469
|
+
}
|
|
3047
3470
|
turnBindingFeed?.beginTurn(messages.length, {
|
|
3048
3471
|
worktreeId: options.worktreeId ?? null,
|
|
3049
3472
|
});
|
|
@@ -3118,6 +3541,7 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
3118
3541
|
sideEffects,
|
|
3119
3542
|
turnNumber: turns,
|
|
3120
3543
|
turnBindingFeed,
|
|
3544
|
+
waitForOutput: deps.outputFlow?.wait,
|
|
3121
3545
|
now: deps.now || Date.now,
|
|
3122
3546
|
},
|
|
3123
3547
|
);
|
|
@@ -3222,8 +3646,23 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
3222
3646
|
durationMs: call.durationMs,
|
|
3223
3647
|
});
|
|
3224
3648
|
}
|
|
3225
|
-
} catch {
|
|
3226
|
-
|
|
3649
|
+
} catch (error) {
|
|
3650
|
+
const persistenceError = createSessionPersistenceFailure(error, {
|
|
3651
|
+
sessionId,
|
|
3652
|
+
operation: "assistant-turn-append",
|
|
3653
|
+
});
|
|
3654
|
+
persistenceFailure = projectSessionPersistenceFailure(
|
|
3655
|
+
persistenceError,
|
|
3656
|
+
{ phase: "after-model" },
|
|
3657
|
+
);
|
|
3658
|
+
if (persistenceFailure) {
|
|
3659
|
+
terminalPersistenceFailure = persistenceFailure;
|
|
3660
|
+
try {
|
|
3661
|
+
deps.onPersistenceFailure?.(persistenceFailure);
|
|
3662
|
+
} catch {
|
|
3663
|
+
// Observability cannot change persistence semantics.
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3227
3666
|
}
|
|
3228
3667
|
}
|
|
3229
3668
|
|
|
@@ -3232,7 +3671,10 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
3232
3671
|
outcome.endReason === "max_turns";
|
|
3233
3672
|
const costStopped = outcome.endReason === "cost-budget-exhausted";
|
|
3234
3673
|
const isError =
|
|
3235
|
-
exhausted ||
|
|
3674
|
+
exhausted ||
|
|
3675
|
+
costStopped ||
|
|
3676
|
+
outcome.endReason === "no-response" ||
|
|
3677
|
+
Boolean(persistenceFailure);
|
|
3236
3678
|
if (isError) sawError = true;
|
|
3237
3679
|
|
|
3238
3680
|
if (costStopped) {
|
|
@@ -3251,14 +3693,17 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
3251
3693
|
? "error_max_turns"
|
|
3252
3694
|
: costStopped
|
|
3253
3695
|
? "error_max_budget"
|
|
3254
|
-
:
|
|
3255
|
-
? "
|
|
3256
|
-
:
|
|
3696
|
+
: persistenceFailure
|
|
3697
|
+
? "error_persistence"
|
|
3698
|
+
: isError
|
|
3699
|
+
? "error"
|
|
3700
|
+
: "success",
|
|
3257
3701
|
is_error: isError,
|
|
3258
3702
|
result: outcome.finalText,
|
|
3259
3703
|
session_id: sessionId,
|
|
3260
3704
|
turn: turns,
|
|
3261
3705
|
usage: outcome.usage,
|
|
3706
|
+
...(persistenceFailure ? { persistence: persistenceFailure } : {}),
|
|
3262
3707
|
});
|
|
3263
3708
|
|
|
3264
3709
|
// --json-schema (P2): emit this turn's structured verdict right after its
|
|
@@ -3278,7 +3723,7 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
3278
3723
|
}
|
|
3279
3724
|
|
|
3280
3725
|
// Session-wide cost cap reached → stop accepting further turns.
|
|
3281
|
-
if (costStopped) break;
|
|
3726
|
+
if (costStopped || persistenceFailure) break;
|
|
3282
3727
|
|
|
3283
3728
|
// While planning, blocked tool calls grew the plan during this turn —
|
|
3284
3729
|
// push the fresh snapshot so the panel's plan card stays live.
|
|
@@ -3294,41 +3739,25 @@ async function runAgentHeadlessStreamInWorkspace(options = {}, deps = {}) {
|
|
|
3294
3739
|
}
|
|
3295
3740
|
}
|
|
3296
3741
|
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
// server so the stream session's port never outlives the invocation.
|
|
3308
|
-
if (_remoteApproval) {
|
|
3309
|
-
try {
|
|
3310
|
-
await _remoteApproval.close();
|
|
3311
|
-
} catch {
|
|
3312
|
-
// best-effort
|
|
3313
|
-
}
|
|
3314
|
-
}
|
|
3742
|
+
streamCleanup.setReason(
|
|
3743
|
+
pipeState.closed
|
|
3744
|
+
? "pipe_closed"
|
|
3745
|
+
: options.signal?.aborted
|
|
3746
|
+
? "aborted"
|
|
3747
|
+
: inputDone
|
|
3748
|
+
? "stdin_closed"
|
|
3749
|
+
: "completed",
|
|
3750
|
+
);
|
|
3751
|
+
await streamCleanup.cleanup();
|
|
3315
3752
|
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
try {
|
|
3319
|
-
const { runObserveHooks } =
|
|
3320
|
-
await import("../lib/settings-hook-events.js");
|
|
3321
|
-
runObserveHooks(
|
|
3322
|
-
settingsHooks,
|
|
3323
|
-
"SessionEnd",
|
|
3324
|
-
{ reason: "stdin_closed", cwd, session_id: sessionId },
|
|
3325
|
-
{ cwd },
|
|
3326
|
-
);
|
|
3327
|
-
} catch {
|
|
3328
|
-
// observe-only
|
|
3329
|
-
}
|
|
3753
|
+
if (!pipeState.closed) {
|
|
3754
|
+
emit({ type: "system", subtype: "end", session_id: sessionId, turns });
|
|
3330
3755
|
}
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3756
|
+
return {
|
|
3757
|
+
exitCode: pipeState.closed ? 0 : sawError ? 1 : 0,
|
|
3758
|
+
turns,
|
|
3759
|
+
...(terminalPersistenceFailure
|
|
3760
|
+
? { persistence: terminalPersistenceFailure }
|
|
3761
|
+
: {}),
|
|
3762
|
+
};
|
|
3334
3763
|
}
|