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
|
@@ -53,6 +53,8 @@ import {
|
|
|
53
53
|
findLatestEvent as jsonlFindLatestEvent,
|
|
54
54
|
rebuildMessages as jsonlRebuildMessages,
|
|
55
55
|
sessionExists as jsonlSessionExists,
|
|
56
|
+
sessionHasPersistedEvidence as jsonlSessionHasPersistedEvidence,
|
|
57
|
+
resolveSessionAuthority as jsonlResolveSessionAuthority,
|
|
56
58
|
getLastSessionId as jsonlGetLastSessionId,
|
|
57
59
|
verifySession as jsonlVerifySession,
|
|
58
60
|
} from "../harness/jsonl-session-store.js";
|
|
@@ -71,6 +73,7 @@ import {
|
|
|
71
73
|
} from "../lib/mcp-call-ledger-store.js";
|
|
72
74
|
import { createMcpHostRecoveryRuntime } from "../lib/mcp-host-recovery-runtime.js";
|
|
73
75
|
import { readSessionHostResumeState } from "../lib/session-host-snapshot.js";
|
|
76
|
+
import { acquireSessionHostLease } from "../lib/session-host-lease.js";
|
|
74
77
|
import { TurnBindingLog, createTurnBindingFeed } from "../lib/turn-binding.js";
|
|
75
78
|
import { TURN_BINDING_EVENT } from "../lib/turn-binding-store.js";
|
|
76
79
|
import { operationIdempotencyKey } from "../lib/idempotency.js";
|
|
@@ -153,6 +156,15 @@ const VALID_OUTPUT_FORMATS = Object.freeze(["text", "json", "stream-json"]);
|
|
|
153
156
|
// the stream-json driver + REPL); re-exported here for existing importers.
|
|
154
157
|
export { installPipeSafety } from "./pipe-safety.js";
|
|
155
158
|
import { installPipeSafety } from "./pipe-safety.js";
|
|
159
|
+
import { createHeadlessOutputBackpressure } from "./output-backpressure.js";
|
|
160
|
+
import {
|
|
161
|
+
cleanupDeadlineError,
|
|
162
|
+
createCleanupDeadline,
|
|
163
|
+
} from "./cleanup-deadline.js";
|
|
164
|
+
import {
|
|
165
|
+
createSessionPersistenceFailure,
|
|
166
|
+
projectSessionPersistenceFailure,
|
|
167
|
+
} from "../lib/session-persistence-failure.js";
|
|
156
168
|
|
|
157
169
|
/**
|
|
158
170
|
* Normalize a public --permission-mode spelling to the canonical internal mode.
|
|
@@ -288,6 +300,19 @@ export function resolveHeadlessSession(options = {}, store = {}, fallbackId) {
|
|
|
288
300
|
} else if (typeof resume === "string" && resume.trim()) {
|
|
289
301
|
resumeId = resume.trim();
|
|
290
302
|
}
|
|
303
|
+
if (resumeId && typeof store.resolveSessionAuthority === "function") {
|
|
304
|
+
const authority = store.resolveSessionAuthority(resumeId);
|
|
305
|
+
if (authority && !authority.readable) {
|
|
306
|
+
const error = new Error(
|
|
307
|
+
`Canonical session ${authority.id} is not resumable (${authority.presence})`,
|
|
308
|
+
);
|
|
309
|
+
error.code = "SESSION_CANONICAL_UNAVAILABLE";
|
|
310
|
+
error.sessionId = authority.id;
|
|
311
|
+
error.presence = authority.presence;
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
if (authority?.id) resumeId = authority.id;
|
|
315
|
+
}
|
|
291
316
|
const persist =
|
|
292
317
|
ephemeral === true
|
|
293
318
|
? false
|
|
@@ -361,14 +386,161 @@ export function applyForkSession(opts = {}, store = {}) {
|
|
|
361
386
|
* @param {object} [deps] Injection seam for tests.
|
|
362
387
|
* @returns {Promise<{ exitCode:number, result:string, isError:boolean, sessionSnapshot?:object }>}
|
|
363
388
|
*/
|
|
389
|
+
const HEADLESS_SESSION_RESOLUTION = Symbol("headlessSessionResolution");
|
|
390
|
+
const HEADLESS_SESSION_HOST_LEASE = Symbol("headlessSessionHostLease");
|
|
391
|
+
|
|
392
|
+
async function withHeadlessSessionHostLease(options, deps, task) {
|
|
393
|
+
const prompt = (options.prompt || "").trim();
|
|
394
|
+
if (!prompt) {
|
|
395
|
+
throw new Error(
|
|
396
|
+
"runAgentHeadless requires a non-empty prompt (use -p, a positional arg, or pipe stdin).",
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
const outputFormat = options.outputFormat || "text";
|
|
400
|
+
if (!VALID_OUTPUT_FORMATS.includes(outputFormat)) {
|
|
401
|
+
throw new Error(
|
|
402
|
+
`Invalid --output-format "${outputFormat}". Expected one of: ${VALID_OUTPUT_FORMATS.join(", ")}`,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
const hasInjectedSessionStore =
|
|
406
|
+
typeof deps.sessionHasPersistedEvidence === "function" ||
|
|
407
|
+
typeof deps.sessionExists === "function" ||
|
|
408
|
+
typeof deps.rebuildMessages === "function" ||
|
|
409
|
+
typeof deps.appendEvent === "function" ||
|
|
410
|
+
typeof deps.appendAuthorityEvent === "function" ||
|
|
411
|
+
typeof deps.readVerifiedEvents === "function";
|
|
412
|
+
const resolution = resolveHeadlessSession(
|
|
413
|
+
options,
|
|
414
|
+
{
|
|
415
|
+
getLastSessionId: deps.getLastSessionId || jsonlGetLastSessionId,
|
|
416
|
+
resolveSessionAuthority:
|
|
417
|
+
deps.resolveSessionAuthority ||
|
|
418
|
+
(!hasInjectedSessionStore ? jsonlResolveSessionAuthority : undefined),
|
|
419
|
+
},
|
|
420
|
+
`headless-${Date.now()}-${process.pid}`,
|
|
421
|
+
);
|
|
422
|
+
const authoritySessionId =
|
|
423
|
+
resolution.resumeId || (resolution.persist ? resolution.sessionId : null);
|
|
424
|
+
const acquireHostLease =
|
|
425
|
+
deps.acquireSessionHostLease ||
|
|
426
|
+
(!hasInjectedSessionStore ? acquireSessionHostLease : null);
|
|
427
|
+
let lease = null;
|
|
428
|
+
try {
|
|
429
|
+
if (authoritySessionId && typeof acquireHostLease === "function") {
|
|
430
|
+
lease = acquireHostLease(authoritySessionId, { hostKind: "headless" });
|
|
431
|
+
}
|
|
432
|
+
const scopedOptions = lease?.signal
|
|
433
|
+
? {
|
|
434
|
+
...options,
|
|
435
|
+
signal: options.signal
|
|
436
|
+
? AbortSignal.any([options.signal, lease.signal])
|
|
437
|
+
: lease.signal,
|
|
438
|
+
}
|
|
439
|
+
: options;
|
|
440
|
+
return await task(scopedOptions, {
|
|
441
|
+
...deps,
|
|
442
|
+
[HEADLESS_SESSION_RESOLUTION]: resolution,
|
|
443
|
+
[HEADLESS_SESSION_HOST_LEASE]: lease,
|
|
444
|
+
});
|
|
445
|
+
} finally {
|
|
446
|
+
lease?.release?.();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
364
450
|
export async function runAgentHeadless(options = {}, deps = {}) {
|
|
365
451
|
const trustedWorkspaceRoot = options.cwd || process.cwd();
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
452
|
+
const pipeState = { closed: false };
|
|
453
|
+
const stdout = deps.stdout || process.stdout;
|
|
454
|
+
const stderr = deps.stderr || process.stderr;
|
|
455
|
+
const shouldInstallPipeSafety =
|
|
456
|
+
typeof deps.installPipeSafety === "function" ||
|
|
457
|
+
(!deps.writeOut && !deps.writeErr);
|
|
458
|
+
const pipeAbort = shouldInstallPipeSafety ? new AbortController() : null;
|
|
459
|
+
const managesNativeOutput = !deps.writeOut || !deps.writeErr;
|
|
460
|
+
const outputAbort = managesNativeOutput ? new AbortController() : null;
|
|
461
|
+
const handlePipeClosed = () => {
|
|
462
|
+
if (pipeState.closed) return;
|
|
463
|
+
pipeState.closed = true;
|
|
464
|
+
process.exitCode = 0;
|
|
465
|
+
pipeAbort?.abort();
|
|
466
|
+
};
|
|
467
|
+
const outputFlow =
|
|
468
|
+
deps.outputFlow ||
|
|
469
|
+
createHeadlessOutputBackpressure({
|
|
470
|
+
stdout,
|
|
471
|
+
stderr,
|
|
472
|
+
writeOut: deps.writeOut,
|
|
473
|
+
writeErr: deps.writeErr,
|
|
474
|
+
maxQueuedBytes: deps.outputBackpressureMaxBytes,
|
|
475
|
+
drainTimeoutMs: deps.outputBackpressureTimeoutMs,
|
|
476
|
+
onFailure: (error) => {
|
|
477
|
+
outputAbort?.abort(error);
|
|
478
|
+
if (error?.code === "EPIPE") handlePipeClosed();
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
const abortSignals = [
|
|
482
|
+
options.signal,
|
|
483
|
+
pipeAbort?.signal,
|
|
484
|
+
outputAbort?.signal,
|
|
485
|
+
].filter(Boolean);
|
|
486
|
+
const runtimeOptions =
|
|
487
|
+
abortSignals.length === 0
|
|
488
|
+
? options
|
|
489
|
+
: {
|
|
490
|
+
...options,
|
|
491
|
+
signal:
|
|
492
|
+
abortSignals.length === 1
|
|
493
|
+
? abortSignals[0]
|
|
494
|
+
: AbortSignal.any(abortSignals),
|
|
495
|
+
};
|
|
496
|
+
const runtimeDeps = {
|
|
497
|
+
...deps,
|
|
498
|
+
writeOut: outputFlow.writeOut,
|
|
499
|
+
writeErr: outputFlow.writeErr,
|
|
500
|
+
outputFlow,
|
|
501
|
+
};
|
|
502
|
+
const installSafety = deps.installPipeSafety || installPipeSafety;
|
|
503
|
+
const disposePipeSafety = shouldInstallPipeSafety
|
|
504
|
+
? installSafety([stdout, stderr], handlePipeClosed)
|
|
505
|
+
: null;
|
|
506
|
+
|
|
507
|
+
let outcome;
|
|
508
|
+
let failure = null;
|
|
509
|
+
try {
|
|
510
|
+
outcome = await runWithHostHooksV2Workspace(trustedWorkspaceRoot, () =>
|
|
511
|
+
withHeadlessSessionHostLease(
|
|
512
|
+
runtimeOptions,
|
|
513
|
+
runtimeDeps,
|
|
514
|
+
(scopedOptions, scopedDeps) =>
|
|
515
|
+
runAgentHeadlessInWorkspace(scopedOptions, scopedDeps, pipeState),
|
|
516
|
+
),
|
|
517
|
+
);
|
|
518
|
+
} catch (error) {
|
|
519
|
+
failure = error;
|
|
520
|
+
} finally {
|
|
521
|
+
try {
|
|
522
|
+
await outputFlow.wait();
|
|
523
|
+
} catch (error) {
|
|
524
|
+
failure ||= error;
|
|
525
|
+
}
|
|
526
|
+
outputFlow.dispose();
|
|
527
|
+
disposePipeSafety?.();
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (pipeState.closed) {
|
|
531
|
+
return outcome
|
|
532
|
+
? { ...outcome, exitCode: 0, isError: false }
|
|
533
|
+
: { exitCode: 0, result: "", isError: false };
|
|
534
|
+
}
|
|
535
|
+
if (failure) throw failure;
|
|
536
|
+
return outcome;
|
|
369
537
|
}
|
|
370
538
|
|
|
371
|
-
async function runAgentHeadlessInWorkspace(
|
|
539
|
+
async function runAgentHeadlessInWorkspace(
|
|
540
|
+
options = {},
|
|
541
|
+
deps = {},
|
|
542
|
+
pipeState = { closed: false },
|
|
543
|
+
) {
|
|
372
544
|
const prompt = (options.prompt || "").trim();
|
|
373
545
|
if (!prompt) {
|
|
374
546
|
throw new Error(
|
|
@@ -386,21 +558,33 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
386
558
|
const isStream = outputFormat === "stream-json";
|
|
387
559
|
const isJson = outputFormat === "json";
|
|
388
560
|
const isText = outputFormat === "text";
|
|
389
|
-
const writeOut = deps.writeOut
|
|
390
|
-
const writeErr = deps.writeErr
|
|
391
|
-
|
|
561
|
+
const writeOut = deps.writeOut;
|
|
562
|
+
const writeErr = deps.writeErr;
|
|
563
|
+
const cleanupDeadline = createCleanupDeadline({
|
|
564
|
+
timeoutMs: deps.cleanupDeadlineMs,
|
|
565
|
+
label: "headless-runner-cleanup",
|
|
566
|
+
});
|
|
392
567
|
|
|
393
568
|
const hasInjectedSessionStore =
|
|
569
|
+
typeof deps.sessionHasPersistedEvidence === "function" ||
|
|
394
570
|
typeof deps.sessionExists === "function" ||
|
|
395
571
|
typeof deps.rebuildMessages === "function" ||
|
|
396
572
|
typeof deps.appendEvent === "function" ||
|
|
397
573
|
typeof deps.appendAuthorityEvent === "function" ||
|
|
398
574
|
typeof deps.readVerifiedEvents === "function";
|
|
399
|
-
const { sessionId, resumeId, persist } =
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
575
|
+
const { sessionId, resumeId, persist } =
|
|
576
|
+
deps[HEADLESS_SESSION_RESOLUTION] ||
|
|
577
|
+
resolveHeadlessSession(
|
|
578
|
+
options,
|
|
579
|
+
{
|
|
580
|
+
getLastSessionId: deps.getLastSessionId || jsonlGetLastSessionId,
|
|
581
|
+
resolveSessionAuthority:
|
|
582
|
+
deps.resolveSessionAuthority ||
|
|
583
|
+
(!hasInjectedSessionStore ? jsonlResolveSessionAuthority : undefined),
|
|
584
|
+
},
|
|
585
|
+
`headless-${Date.now()}-${process.pid}`,
|
|
586
|
+
);
|
|
587
|
+
const sessionHostLease = deps[HEADLESS_SESSION_HOST_LEASE] || null;
|
|
404
588
|
|
|
405
589
|
const emitHeadlessError = (resultMsg) => {
|
|
406
590
|
if (isStream) {
|
|
@@ -435,27 +619,36 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
435
619
|
// settings/plugin loading, bootstrap, model access, hooks, MCP and tools.
|
|
436
620
|
const canReadCanonicalResume =
|
|
437
621
|
!hasInjectedSessionStore || typeof deps.readVerifiedEvents === "function";
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
622
|
+
const canonicalAdmissionId = resumeId || (persist ? sessionId : null);
|
|
623
|
+
const canonicalAdmission =
|
|
624
|
+
canonicalAdmissionId && canReadCanonicalResume
|
|
625
|
+
? readSessionHostResumeState(canonicalAdmissionId, {
|
|
626
|
+
sessionHasPersistedEvidence:
|
|
627
|
+
deps.sessionHasPersistedEvidence ||
|
|
628
|
+
(!hasInjectedSessionStore
|
|
629
|
+
? jsonlSessionHasPersistedEvidence
|
|
630
|
+
: undefined),
|
|
441
631
|
sessionExists: deps.sessionExists || jsonlSessionExists,
|
|
442
632
|
readVerifiedEvents:
|
|
443
633
|
deps.readVerifiedEvents || jsonlReadVerifiedEvents,
|
|
444
634
|
})
|
|
445
635
|
: null;
|
|
446
|
-
if (
|
|
636
|
+
if (canonicalAdmission && !canonicalAdmission.snapshot.verified) {
|
|
447
637
|
const message =
|
|
448
638
|
"CC_SESSION_HOST_SNAPSHOT_UNVERIFIED: canonical JSONL session is not " +
|
|
449
|
-
"fully verified; resume was refused";
|
|
639
|
+
"fully verified; resume/write admission was refused";
|
|
450
640
|
emitHeadlessError(message);
|
|
451
641
|
writeErr(`${message}\n`);
|
|
452
642
|
return {
|
|
453
643
|
exitCode: 1,
|
|
454
644
|
result: message,
|
|
455
645
|
isError: true,
|
|
456
|
-
sessionSnapshot:
|
|
646
|
+
sessionSnapshot: canonicalAdmission.snapshot,
|
|
457
647
|
};
|
|
458
648
|
}
|
|
649
|
+
// A persist-only target must pass the same canonical admission boundary,
|
|
650
|
+
// but it does not opt into replaying the target's prior conversation.
|
|
651
|
+
const canonicalResume = resumeId ? canonicalAdmission : null;
|
|
459
652
|
|
|
460
653
|
// `let` (not const): a custom-command macro's `model:` frontmatter may
|
|
461
654
|
// override it below (when the user passed no explicit --model), mirroring
|
|
@@ -533,7 +726,7 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
533
726
|
cwd,
|
|
534
727
|
settingsFile: options.settingsFile,
|
|
535
728
|
});
|
|
536
|
-
if (notice)
|
|
729
|
+
if (notice) writeErr(notice + "\n");
|
|
537
730
|
} catch {
|
|
538
731
|
/* trust notice is best-effort */
|
|
539
732
|
}
|
|
@@ -935,7 +1128,7 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
935
1128
|
if (!persist) return;
|
|
936
1129
|
if (sideEffectLedgerLoadError) throw sideEffectLedgerLoadError;
|
|
937
1130
|
try {
|
|
938
|
-
const persisted = store.
|
|
1131
|
+
const persisted = store.appendAuthorityEvent(
|
|
939
1132
|
sessionId,
|
|
940
1133
|
SIDE_EFFECT_LEDGER_EVENT,
|
|
941
1134
|
sideEffectLedger.toJSON(),
|
|
@@ -990,13 +1183,16 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
990
1183
|
// (createTurnBindingFeed) so the interactive REPL producer can never drift
|
|
991
1184
|
// from this runner's mapping. No feed at all for non-persisted runs.
|
|
992
1185
|
const turnBindingFeed = persist
|
|
993
|
-
? createTurnBindingFeed({
|
|
1186
|
+
? createTurnBindingFeed({
|
|
1187
|
+
log: turnBindingLog,
|
|
1188
|
+
nonce: sideEffectRunNonce,
|
|
1189
|
+
})
|
|
994
1190
|
: null;
|
|
995
1191
|
const persistTurnBindingLog = () => {
|
|
996
1192
|
if (!turnBindingFeed || !turnBindingFeed.isDirty()) return;
|
|
997
1193
|
if (turnBindingLoadError) throw turnBindingLoadError;
|
|
998
1194
|
try {
|
|
999
|
-
const persisted = store.
|
|
1195
|
+
const persisted = store.appendAuthorityEvent(
|
|
1000
1196
|
sessionId,
|
|
1001
1197
|
TURN_BINDING_EVENT,
|
|
1002
1198
|
turnBindingLog.toJSON(),
|
|
@@ -1180,7 +1376,9 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
1180
1376
|
}
|
|
1181
1377
|
|
|
1182
1378
|
const budget = Number.isFinite(options.maxTurns)
|
|
1183
|
-
? new IterationBudget({
|
|
1379
|
+
? new IterationBudget({
|
|
1380
|
+
limit: Math.max(1, Math.floor(options.maxTurns)),
|
|
1381
|
+
})
|
|
1184
1382
|
: new IterationBudget();
|
|
1185
1383
|
|
|
1186
1384
|
// Effective system prompt: built-in base, optionally replaced by
|
|
@@ -1372,9 +1570,10 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
1372
1570
|
{ role: "user", content: userMessageContent },
|
|
1373
1571
|
]);
|
|
1374
1572
|
|
|
1375
|
-
// Persist the user turn
|
|
1376
|
-
//
|
|
1377
|
-
// the
|
|
1573
|
+
// Persist the user turn before model/tool side effects. A full or read-only
|
|
1574
|
+
// session disk is a recoverability contract failure, so it fails closed
|
|
1575
|
+
// while the requested turn is still side-effect free. startSession is
|
|
1576
|
+
// append-safe: only seed the header when the file does not yet exist.
|
|
1378
1577
|
if (persist) {
|
|
1379
1578
|
try {
|
|
1380
1579
|
if (!store.sessionExists(sessionId)) {
|
|
@@ -1387,8 +1586,62 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
1387
1586
|
// Persist the expanded content so a resumed session faithfully replays
|
|
1388
1587
|
// what the model actually saw (the file snapshot, not just the @token).
|
|
1389
1588
|
store.appendUserMessage(sessionId, userContent);
|
|
1390
|
-
} catch {
|
|
1391
|
-
|
|
1589
|
+
} catch (error) {
|
|
1590
|
+
const persistenceError = createSessionPersistenceFailure(error, {
|
|
1591
|
+
sessionId,
|
|
1592
|
+
operation: "user-turn-append",
|
|
1593
|
+
});
|
|
1594
|
+
const persistence = projectSessionPersistenceFailure(persistenceError, {
|
|
1595
|
+
phase: "before-model",
|
|
1596
|
+
});
|
|
1597
|
+
if (persistence) {
|
|
1598
|
+
try {
|
|
1599
|
+
deps.onPersistenceFailure?.(persistence);
|
|
1600
|
+
} catch {
|
|
1601
|
+
// Observability cannot change persistence semantics.
|
|
1602
|
+
}
|
|
1603
|
+
const resultMsg = `${persistence.code}: session turn was not durably persisted`;
|
|
1604
|
+
if (isStream) {
|
|
1605
|
+
writeOut(
|
|
1606
|
+
JSON.stringify({
|
|
1607
|
+
type: "result",
|
|
1608
|
+
subtype: "error_persistence",
|
|
1609
|
+
is_error: true,
|
|
1610
|
+
error: resultMsg,
|
|
1611
|
+
session_id: sessionId,
|
|
1612
|
+
persistence,
|
|
1613
|
+
}) + "\n",
|
|
1614
|
+
);
|
|
1615
|
+
} else if (isJson) {
|
|
1616
|
+
writeOut(
|
|
1617
|
+
JSON.stringify(
|
|
1618
|
+
buildResultEnvelope({
|
|
1619
|
+
subtype: "error_persistence",
|
|
1620
|
+
isError: true,
|
|
1621
|
+
result: resultMsg,
|
|
1622
|
+
sessionId,
|
|
1623
|
+
toolCalls: [],
|
|
1624
|
+
usage: {},
|
|
1625
|
+
numTurns: 0,
|
|
1626
|
+
durationMs: 0,
|
|
1627
|
+
persistence,
|
|
1628
|
+
}),
|
|
1629
|
+
) + "\n",
|
|
1630
|
+
);
|
|
1631
|
+
} else {
|
|
1632
|
+
writeErr(
|
|
1633
|
+
`${resultMsg} (${persistence.fs_code}; ${persistence.commit_state})\n`,
|
|
1634
|
+
);
|
|
1635
|
+
}
|
|
1636
|
+
return {
|
|
1637
|
+
exitCode: 1,
|
|
1638
|
+
result: resultMsg,
|
|
1639
|
+
isError: true,
|
|
1640
|
+
persistence,
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
// Preserve legacy best-effort handling for errors outside the explicit
|
|
1644
|
+
// ENOSPC/EROFS durability contract.
|
|
1392
1645
|
}
|
|
1393
1646
|
}
|
|
1394
1647
|
|
|
@@ -1465,9 +1718,12 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
1465
1718
|
bundle: mcp,
|
|
1466
1719
|
sessionId,
|
|
1467
1720
|
sink: persist
|
|
1468
|
-
? createSessionMcpLedgerSink(
|
|
1469
|
-
|
|
1470
|
-
|
|
1721
|
+
? createSessionMcpLedgerSink(
|
|
1722
|
+
sessionId,
|
|
1723
|
+
hasInjectedSessionStore
|
|
1724
|
+
? { appendEvent: store.appendAuthorityEvent }
|
|
1725
|
+
: { recovery: resumeMcpRecovery },
|
|
1726
|
+
)
|
|
1471
1727
|
: null,
|
|
1472
1728
|
recovery: resumeMcpRecovery,
|
|
1473
1729
|
recoveryError: resumeMcpRecoveryError,
|
|
@@ -1536,7 +1792,13 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
1536
1792
|
ppt = resolvePermissionPromptTool(mcp, options.permissionPromptTool);
|
|
1537
1793
|
} catch (err) {
|
|
1538
1794
|
writeErr(`Error: ${err.message}\n`);
|
|
1539
|
-
|
|
1795
|
+
await cleanupDeadline.run("mcp", () => mcp?.mcpClient?.disconnectAll?.());
|
|
1796
|
+
const cleanupReport = cleanupDeadline.report();
|
|
1797
|
+
try {
|
|
1798
|
+
deps.onCleanupReport?.(cleanupReport);
|
|
1799
|
+
} catch {
|
|
1800
|
+
// Metrics/reporting cannot change cleanup semantics.
|
|
1801
|
+
}
|
|
1540
1802
|
emitHeadlessError(err.message);
|
|
1541
1803
|
// A bad --permission-prompt-tool reference is a CONFIG error.
|
|
1542
1804
|
return {
|
|
@@ -1831,6 +2093,24 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
1831
2093
|
const suffix = retryIn > 0 ? ` · will retry in ${retryIn}s` : "";
|
|
1832
2094
|
writeErr(` ⏳ waiting for API response (silent ${silent}s)${suffix}…\n`);
|
|
1833
2095
|
},
|
|
2096
|
+
onProviderFallback: (info) => {
|
|
2097
|
+
if (isStream) {
|
|
2098
|
+
emitStream({
|
|
2099
|
+
type: "raw",
|
|
2100
|
+
subtype: "provider_fallback",
|
|
2101
|
+
text:
|
|
2102
|
+
info.message || `provider switched from ${info.from} to ${info.to}`,
|
|
2103
|
+
from: info.from,
|
|
2104
|
+
to: info.to,
|
|
2105
|
+
reason: info.reason,
|
|
2106
|
+
session_id: sessionId,
|
|
2107
|
+
});
|
|
2108
|
+
} else {
|
|
2109
|
+
writeErr(
|
|
2110
|
+
`[provider] ${info.message || `switched from ${info.from} to ${info.to}`}\n`,
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
},
|
|
1834
2114
|
};
|
|
1835
2115
|
|
|
1836
2116
|
// Goal binding (cc goal, Phase 1). `--goal <id>` binds explicitly; `--goal`
|
|
@@ -2149,8 +2429,11 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2149
2429
|
process.once("SIGINT", _onHardSignal);
|
|
2150
2430
|
process.once("SIGTERM", _onHardSignal);
|
|
2151
2431
|
|
|
2432
|
+
let loopFailureOutcome = null;
|
|
2433
|
+
let cleanupFailure = null;
|
|
2152
2434
|
try {
|
|
2153
2435
|
while (true) {
|
|
2436
|
+
sessionHostLease?.assert?.();
|
|
2154
2437
|
// P1 turn→checkpoint binding: one drain of the loop = one turn. Anchor
|
|
2155
2438
|
// the conversation offset before the model sees the new user turn. A
|
|
2156
2439
|
// `--worktree` run stamps its isolation worktree (branch name, threaded
|
|
@@ -2174,7 +2457,11 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2174
2457
|
case "checkpoint": {
|
|
2175
2458
|
if (isText)
|
|
2176
2459
|
writeErr(` ⎌ checkpoint ${event.id} (before ${event.tool})\n`);
|
|
2177
|
-
emitStream({
|
|
2460
|
+
emitStream({
|
|
2461
|
+
type: "checkpoint",
|
|
2462
|
+
id: event.id,
|
|
2463
|
+
tool: event.tool,
|
|
2464
|
+
});
|
|
2178
2465
|
break;
|
|
2179
2466
|
}
|
|
2180
2467
|
case "tool-executing": {
|
|
@@ -2218,6 +2505,11 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2218
2505
|
})
|
|
2219
2506
|
.start(opId);
|
|
2220
2507
|
persistSideEffectLedger();
|
|
2508
|
+
// The generator executes the tool only after this yielded
|
|
2509
|
+
// prewrite is handled. Recheck the same durable lease at that
|
|
2510
|
+
// final host boundary so a fenced host never resumes into the
|
|
2511
|
+
// external operation.
|
|
2512
|
+
sessionHostLease?.assert?.();
|
|
2221
2513
|
}
|
|
2222
2514
|
}
|
|
2223
2515
|
break;
|
|
@@ -2421,6 +2713,7 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2421
2713
|
if (isStream && event.type) emitStream(event);
|
|
2422
2714
|
break;
|
|
2423
2715
|
}
|
|
2716
|
+
await deps.outputFlow?.wait?.();
|
|
2424
2717
|
// Hard cost cap reached: stop consuming the loop so no further paid LLM
|
|
2425
2718
|
// call is made (break out of the for-await, not just the switch).
|
|
2426
2719
|
if (stopForCost) break;
|
|
@@ -2611,16 +2904,18 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2611
2904
|
}
|
|
2612
2905
|
// Provider/transport failures get their own exit code (5) so CI can tell
|
|
2613
2906
|
// "the model call failed" from "the run itself errored" (1).
|
|
2614
|
-
|
|
2907
|
+
loopFailureOutcome = {
|
|
2908
|
+
exitCode: classifyLoopError(err),
|
|
2909
|
+
result: message,
|
|
2910
|
+
isError: true,
|
|
2911
|
+
};
|
|
2615
2912
|
} finally {
|
|
2616
2913
|
// Capture body-cache hits/misses caused by list_skills/run_skill during the
|
|
2617
2914
|
// turn. `cc context --sources` reads the newest snapshot.
|
|
2618
2915
|
persistContextSources();
|
|
2619
|
-
|
|
2620
|
-
_backgroundInteraction?.close?.()
|
|
2621
|
-
|
|
2622
|
-
// Best-effort listener cleanup; never mask the run result.
|
|
2623
|
-
}
|
|
2916
|
+
await cleanupDeadline.run("background-interaction", () =>
|
|
2917
|
+
_backgroundInteraction?.close?.(),
|
|
2918
|
+
);
|
|
2624
2919
|
// Drop the signal handlers first — a normal return must not leave a
|
|
2625
2920
|
// process-wide SIGINT/SIGTERM listener behind (a later Ctrl-C would wrongly
|
|
2626
2921
|
// exit with 130, and repeated in-process runs would leak listeners).
|
|
@@ -2629,136 +2924,158 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2629
2924
|
// Tear down ad-hoc MCP servers (--mcp-config) before returning, whether the
|
|
2630
2925
|
// loop completed or threw. Best-effort: a failed disconnect never masks the
|
|
2631
2926
|
// run's own outcome.
|
|
2632
|
-
|
|
2633
|
-
try {
|
|
2634
|
-
await mcp.mcpClient.disconnectAll();
|
|
2635
|
-
} catch {
|
|
2636
|
-
// ignore — disconnect is best-effort
|
|
2637
|
-
}
|
|
2638
|
-
}
|
|
2927
|
+
await cleanupDeadline.run("mcp", () => mcp?.mcpClient?.disconnectAll?.());
|
|
2639
2928
|
// Kill any background run_shell tasks this run spawned so a backgrounded
|
|
2640
2929
|
// command (e.g. a dev server) doesn't outlive the headless invocation.
|
|
2641
|
-
|
|
2642
|
-
killAllBackgroundShellTasks()
|
|
2643
|
-
|
|
2644
|
-
// best-effort — never mask the run's own outcome
|
|
2645
|
-
}
|
|
2930
|
+
await cleanupDeadline.run("background-shells", () =>
|
|
2931
|
+
killAllBackgroundShellTasks(),
|
|
2932
|
+
);
|
|
2646
2933
|
// Tear down the --remote-control approval bridge + its self-hosted WS
|
|
2647
2934
|
// server so the run's port/socket never outlives the invocation.
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
} catch {
|
|
2652
|
-
// best-effort — never mask the run's own outcome
|
|
2653
|
-
}
|
|
2654
|
-
}
|
|
2935
|
+
await cleanupDeadline.run("remote-approval", () =>
|
|
2936
|
+
_remoteApproval?.close?.(),
|
|
2937
|
+
);
|
|
2655
2938
|
// settings.json Stop + SessionEnd hooks when the run finishes. Stop is the
|
|
2656
2939
|
// canonical async-hook trigger ("run the test suite at turn end"); fire its
|
|
2657
2940
|
// async hooks fire-and-forget, then settle so a fast background check can
|
|
2658
2941
|
// report back within this one-shot run.
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
"Stop",
|
|
2667
|
-
stopPayload,
|
|
2668
|
-
{ cwd },
|
|
2669
|
-
);
|
|
2670
|
-
const stopFailures = (stopOutcome?.results || []).filter(
|
|
2671
|
-
(result) =>
|
|
2672
|
-
result?.nonBlockingError === true ||
|
|
2673
|
-
result?.malformedDecision === true ||
|
|
2674
|
-
result?.breakerOpen === true,
|
|
2675
|
-
);
|
|
2676
|
-
if (stopFailures.length > 0) {
|
|
2677
|
-
emitHooksV2Event("StopFailure", {
|
|
2678
|
-
schema_version: 1,
|
|
2679
|
-
session_id: sessionId,
|
|
2680
|
-
phase: "stop-hook",
|
|
2681
|
-
failures: stopFailures.map((failure) => ({
|
|
2682
|
-
command: failure.command || null,
|
|
2683
|
-
exit_code: failure.exitCode ?? null,
|
|
2684
|
-
reason: failure.reason || failure.error || null,
|
|
2685
|
-
})),
|
|
2686
|
-
});
|
|
2687
|
-
}
|
|
2688
|
-
// Skip the async Stop dispatch/settle here if the auto-rewake re-drive
|
|
2689
|
-
// loop already fired + drained it this run (avoids double-execution).
|
|
2690
|
-
if (_hookSupervisor && !_asyncStopHandled) {
|
|
2691
|
-
dispatchAsyncHooks(settingsHooks, "Stop", stopPayload, {
|
|
2942
|
+
await cleanupDeadline.run("settings-hooks", async () => {
|
|
2943
|
+
if (settingsHooks) {
|
|
2944
|
+
try {
|
|
2945
|
+
const { runObserveHooks, dispatchAsyncHooks } =
|
|
2946
|
+
await import("../lib/settings-hook-events.js");
|
|
2947
|
+
const stopPayload = {
|
|
2948
|
+
reason: pipeState.closed ? "pipe_closed" : endReason,
|
|
2692
2949
|
cwd,
|
|
2693
|
-
|
|
2694
|
-
}
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
const
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
// Surface any results/rewakes out-of-band on stderr (never touches the
|
|
2709
|
-
// stdout envelope). A rewake means a background check FAILED and would
|
|
2710
|
-
// re-engage the agent in an interactive session — flag it clearly so a
|
|
2711
|
-
// headless caller/CI can react.
|
|
2712
|
-
for (const rw of _hookSupervisor.drainRewakes()) {
|
|
2950
|
+
session_id: sessionId,
|
|
2951
|
+
};
|
|
2952
|
+
const stopOutcome = runObserveHooks(
|
|
2953
|
+
settingsHooks,
|
|
2954
|
+
"Stop",
|
|
2955
|
+
stopPayload,
|
|
2956
|
+
{ cwd },
|
|
2957
|
+
);
|
|
2958
|
+
const stopFailures = (stopOutcome?.results || []).filter(
|
|
2959
|
+
(result) =>
|
|
2960
|
+
result?.nonBlockingError === true ||
|
|
2961
|
+
result?.malformedDecision === true ||
|
|
2962
|
+
result?.breakerOpen === true,
|
|
2963
|
+
);
|
|
2964
|
+
if (stopFailures.length > 0) {
|
|
2713
2965
|
emitHooksV2Event("StopFailure", {
|
|
2714
2966
|
schema_version: 1,
|
|
2715
2967
|
session_id: sessionId,
|
|
2716
|
-
phase: "
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2968
|
+
phase: "stop-hook",
|
|
2969
|
+
failures: stopFailures.map((failure) => ({
|
|
2970
|
+
command: failure.command || null,
|
|
2971
|
+
exit_code: failure.exitCode ?? null,
|
|
2972
|
+
reason: failure.reason || failure.error || null,
|
|
2973
|
+
})),
|
|
2720
2974
|
});
|
|
2721
|
-
writeErr(
|
|
2722
|
-
`[async-hook REWAKE] ${rw.command} failed` +
|
|
2723
|
-
`${rw.exitCode != null ? ` (exit ${rw.exitCode})` : ""}` +
|
|
2724
|
-
`${rw.error ? `: ${rw.error}` : ""}\n`,
|
|
2725
|
-
);
|
|
2726
2975
|
}
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2976
|
+
// Skip the async Stop dispatch/settle here if the auto-rewake re-drive
|
|
2977
|
+
// loop already fired + drained it this run (avoids double-execution).
|
|
2978
|
+
if (_hookSupervisor && !_asyncStopHandled) {
|
|
2979
|
+
dispatchAsyncHooks(settingsHooks, "Stop", stopPayload, {
|
|
2980
|
+
cwd,
|
|
2981
|
+
supervisor: _hookSupervisor,
|
|
2982
|
+
});
|
|
2983
|
+
// Bounded settle: wait for in-flight async hooks (Stop + any
|
|
2984
|
+
// PostToolUse dispatched during the loop) to finish, capped so a
|
|
2985
|
+
// slow/hung check can never stall the run. Default 5s; tunable.
|
|
2986
|
+
const waitMs = Number.isFinite(options.asyncHookWaitMs)
|
|
2987
|
+
? options.asyncHookWaitMs
|
|
2988
|
+
: 5000;
|
|
2989
|
+
const started = Date.now();
|
|
2990
|
+
while (
|
|
2991
|
+
_hookSupervisor.runningCount() > 0 &&
|
|
2992
|
+
Date.now() - started < waitMs
|
|
2993
|
+
) {
|
|
2994
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
2995
|
+
}
|
|
2996
|
+
// Surface any results/rewakes out-of-band on stderr (never touches the
|
|
2997
|
+
// stdout envelope). A rewake means a background check FAILED and would
|
|
2998
|
+
// re-engage the agent in an interactive session — flag it clearly so a
|
|
2999
|
+
// headless caller/CI can react.
|
|
3000
|
+
for (const rw of _hookSupervisor.drainRewakes()) {
|
|
3001
|
+
emitHooksV2Event("StopFailure", {
|
|
3002
|
+
schema_version: 1,
|
|
3003
|
+
session_id: sessionId,
|
|
3004
|
+
phase: "async-stop-hook",
|
|
3005
|
+
command: rw.command || null,
|
|
3006
|
+
exit_code: rw.exitCode ?? null,
|
|
3007
|
+
reason: rw.error || "async stop hook requested rewake",
|
|
3008
|
+
});
|
|
3009
|
+
writeErr(
|
|
3010
|
+
`[async-hook REWAKE] ${rw.command} failed` +
|
|
3011
|
+
`${rw.exitCode != null ? ` (exit ${rw.exitCode})` : ""}` +
|
|
3012
|
+
`${rw.error ? `: ${rw.error}` : ""}\n`,
|
|
3013
|
+
);
|
|
3014
|
+
}
|
|
3015
|
+
for (const rs of _hookSupervisor.drainResults()) {
|
|
3016
|
+
if (rs.additionalContext) {
|
|
3017
|
+
writeErr(
|
|
3018
|
+
`[async-hook] ${rs.command}: ${rs.additionalContext}\n`,
|
|
3019
|
+
);
|
|
3020
|
+
}
|
|
2730
3021
|
}
|
|
2731
3022
|
}
|
|
3023
|
+
// Always reap the supervisor (kills any straggler child + detaches the
|
|
3024
|
+
// exit reaper), whether the async Stop was handled here or in re-drive.
|
|
3025
|
+
if (_hookSupervisor) _hookSupervisor.stopAll();
|
|
3026
|
+
runObserveHooks(
|
|
3027
|
+
settingsHooks,
|
|
3028
|
+
"SessionEnd",
|
|
3029
|
+
{
|
|
3030
|
+
reason: pipeState.closed ? "pipe_closed" : "completed",
|
|
3031
|
+
cwd,
|
|
3032
|
+
session_id: sessionId,
|
|
3033
|
+
},
|
|
3034
|
+
{ cwd },
|
|
3035
|
+
);
|
|
3036
|
+
} catch (error) {
|
|
3037
|
+
emitHooksV2Event("StopFailure", {
|
|
3038
|
+
schema_version: 1,
|
|
3039
|
+
session_id: sessionId,
|
|
3040
|
+
phase: "stop-hook-dispatch",
|
|
3041
|
+
reason: error?.message || String(error),
|
|
3042
|
+
});
|
|
3043
|
+
// observe-only + best-effort async surfacing
|
|
2732
3044
|
}
|
|
2733
|
-
// Always reap the supervisor (kills any straggler child + detaches the
|
|
2734
|
-
// exit reaper), whether the async Stop was handled here or in re-drive.
|
|
2735
|
-
if (_hookSupervisor) _hookSupervisor.stopAll();
|
|
2736
|
-
runObserveHooks(
|
|
2737
|
-
settingsHooks,
|
|
2738
|
-
"SessionEnd",
|
|
2739
|
-
{ reason: "completed", cwd, session_id: sessionId },
|
|
2740
|
-
{ cwd },
|
|
2741
|
-
);
|
|
2742
|
-
} catch (error) {
|
|
2743
|
-
emitHooksV2Event("StopFailure", {
|
|
2744
|
-
schema_version: 1,
|
|
2745
|
-
session_id: sessionId,
|
|
2746
|
-
phase: "stop-hook-dispatch",
|
|
2747
|
-
reason: error?.message || String(error),
|
|
2748
|
-
});
|
|
2749
|
-
// observe-only + best-effort async surfacing
|
|
2750
3045
|
}
|
|
3046
|
+
});
|
|
3047
|
+
const cleanupReport = cleanupDeadline.report();
|
|
3048
|
+
try {
|
|
3049
|
+
deps.onCleanupReport?.(cleanupReport);
|
|
3050
|
+
} catch {
|
|
3051
|
+
// Metrics/reporting cannot change cleanup semantics.
|
|
3052
|
+
}
|
|
3053
|
+
if (cleanupReport.timedOut) {
|
|
3054
|
+
cleanupFailure = cleanupDeadlineError(cleanupReport);
|
|
2751
3055
|
}
|
|
2752
3056
|
}
|
|
2753
3057
|
|
|
3058
|
+
// Preserve the primary model/runtime failure. A simultaneous cleanup timeout
|
|
3059
|
+
// remains observable through onCleanupReport but must not replace it.
|
|
3060
|
+
if (loopFailureOutcome) return loopFailureOutcome;
|
|
3061
|
+
if (cleanupFailure) throw cleanupFailure;
|
|
3062
|
+
|
|
3063
|
+
// A downstream consumer closed stdout/stderr. The abort above unwound the
|
|
3064
|
+
// model loop through the same `finally` as every other termination, so MCP,
|
|
3065
|
+
// background tasks, approval bridges, and hooks are settled. Do not attempt
|
|
3066
|
+
// more writes to the closed pipe or persist a partial assistant answer.
|
|
3067
|
+
if (pipeState.closed) {
|
|
3068
|
+
return { exitCode: 0, result: finalText, isError: false };
|
|
3069
|
+
}
|
|
3070
|
+
|
|
2754
3071
|
// coreAgentLoop emits run-ended reason "budget-exhausted" when the iteration
|
|
2755
3072
|
// cap is hit; treat that as the max-turns error surface. A cost-budget stop
|
|
2756
3073
|
// (--max-budget-usd) is its own error surface so callers can tell them apart.
|
|
2757
3074
|
const exhausted =
|
|
2758
3075
|
endReason === "budget-exhausted" || endReason === "max_turns";
|
|
2759
3076
|
const costStopped = endReason === "cost-budget-exhausted";
|
|
2760
|
-
|
|
2761
|
-
|
|
3077
|
+
let isError = exhausted || costStopped || endReason === "no-response";
|
|
3078
|
+
let subtype = exhausted
|
|
2762
3079
|
? "error_max_turns"
|
|
2763
3080
|
: costStopped
|
|
2764
3081
|
? "error_max_budget"
|
|
@@ -2766,6 +3083,7 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2766
3083
|
? "error"
|
|
2767
3084
|
: "success";
|
|
2768
3085
|
const durationMs = (deps.now ? deps.now() : Date.now()) - startedAt;
|
|
3086
|
+
let persistenceFailure = null;
|
|
2769
3087
|
|
|
2770
3088
|
// Persist the assistant turn so a later --resume / --continue replays it.
|
|
2771
3089
|
// The user turn was already recorded up front; only append on a clean run.
|
|
@@ -2792,8 +3110,30 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2792
3110
|
store.appendTokenUsage(sessionId, au);
|
|
2793
3111
|
}
|
|
2794
3112
|
store.appendTokenUsage(sessionId, usage);
|
|
2795
|
-
} catch {
|
|
2796
|
-
|
|
3113
|
+
} catch (error) {
|
|
3114
|
+
const persistenceError = createSessionPersistenceFailure(error, {
|
|
3115
|
+
sessionId,
|
|
3116
|
+
operation: "assistant-turn-append",
|
|
3117
|
+
});
|
|
3118
|
+
persistenceFailure = projectSessionPersistenceFailure(persistenceError, {
|
|
3119
|
+
phase: "after-model",
|
|
3120
|
+
});
|
|
3121
|
+
if (persistenceFailure) {
|
|
3122
|
+
isError = true;
|
|
3123
|
+
subtype = "error_persistence";
|
|
3124
|
+
try {
|
|
3125
|
+
deps.onPersistenceFailure?.(persistenceFailure);
|
|
3126
|
+
} catch {
|
|
3127
|
+
// Observability cannot change persistence semantics.
|
|
3128
|
+
}
|
|
3129
|
+
if (isText) {
|
|
3130
|
+
writeErr(
|
|
3131
|
+
`CC_SESSION_PERSISTENCE_FAILED: assistant turn durability is ${persistenceFailure.commit_state} (${persistenceFailure.fs_code})\n`,
|
|
3132
|
+
);
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
// Preserve legacy best-effort handling for errors outside the explicit
|
|
3136
|
+
// ENOSPC/EROFS durability contract.
|
|
2797
3137
|
}
|
|
2798
3138
|
}
|
|
2799
3139
|
|
|
@@ -2854,7 +3194,9 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2854
3194
|
if (isText) {
|
|
2855
3195
|
writeErr(
|
|
2856
3196
|
`\n ${denials.length} tool call(s) were denied by policy this run:\n` +
|
|
2857
|
-
formatDenials(denials, {
|
|
3197
|
+
formatDenials(denials, {
|
|
3198
|
+
now: deps.now ? deps.now() : Date.now(),
|
|
3199
|
+
}) +
|
|
2858
3200
|
"\n",
|
|
2859
3201
|
);
|
|
2860
3202
|
} else if (isStream) {
|
|
@@ -2887,6 +3229,7 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2887
3229
|
...(compactionDegradations.length
|
|
2888
3230
|
? { compaction_degradations: compactionDegradations }
|
|
2889
3231
|
: {}),
|
|
3232
|
+
...(persistenceFailure ? { persistence: persistenceFailure } : {}),
|
|
2890
3233
|
});
|
|
2891
3234
|
} else if (isJson) {
|
|
2892
3235
|
writeOut(
|
|
@@ -2902,6 +3245,7 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2902
3245
|
durationMs,
|
|
2903
3246
|
denials,
|
|
2904
3247
|
compactionDegradations,
|
|
3248
|
+
persistence: persistenceFailure,
|
|
2905
3249
|
}),
|
|
2906
3250
|
) + "\n",
|
|
2907
3251
|
);
|
|
@@ -2923,12 +3267,10 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2923
3267
|
);
|
|
2924
3268
|
if (!isStream) {
|
|
2925
3269
|
const sum = _otlpRecorder.summary();
|
|
2926
|
-
|
|
2927
|
-
`[otlp] ${sum.spanCount} span(s) → ${options.otlp}\n`,
|
|
2928
|
-
);
|
|
3270
|
+
writeErr(`[otlp] ${sum.spanCount} span(s) → ${options.otlp}\n`);
|
|
2929
3271
|
}
|
|
2930
3272
|
} catch (err) {
|
|
2931
|
-
|
|
3273
|
+
writeErr(`[otlp] export failed: ${err.message}\n`);
|
|
2932
3274
|
}
|
|
2933
3275
|
}
|
|
2934
3276
|
if (_collectorEnabled) {
|
|
@@ -2948,6 +3290,7 @@ async function runAgentHeadlessInWorkspace(options = {}, deps = {}) {
|
|
|
2948
3290
|
exitCode: exitCodeForEndReason(endReason, isError),
|
|
2949
3291
|
result: finalText,
|
|
2950
3292
|
isError,
|
|
3293
|
+
...(persistenceFailure ? { persistence: persistenceFailure } : {}),
|
|
2951
3294
|
};
|
|
2952
3295
|
}
|
|
2953
3296
|
|
|
@@ -2962,6 +3305,7 @@ function buildResultEnvelope({
|
|
|
2962
3305
|
durationMs,
|
|
2963
3306
|
denials,
|
|
2964
3307
|
compactionDegradations,
|
|
3308
|
+
persistence,
|
|
2965
3309
|
}) {
|
|
2966
3310
|
const env = {
|
|
2967
3311
|
type: "result",
|
|
@@ -2980,5 +3324,6 @@ function buildResultEnvelope({
|
|
|
2980
3324
|
if (Array.isArray(compactionDegradations) && compactionDegradations.length) {
|
|
2981
3325
|
env.compaction_degradations = compactionDegradations;
|
|
2982
3326
|
}
|
|
3327
|
+
if (persistence) env.persistence = persistence;
|
|
2983
3328
|
return env;
|
|
2984
3329
|
}
|