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.
Files changed (233) hide show
  1. package/bin/chainlesschain.js +14 -6
  2. package/package.json +4 -1
  3. package/scripts/command-lifecycle-report.mjs +94 -0
  4. package/src/assets/web-panel/assets/{AIOps-D3Gu1a5d.js → AIOps-DcQ0bWKy.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-DrwAoKaz.js → ActionButton-GecwfFPt.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C7Q-gd_3.js → Analytics-CD6iSt9v.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-DgBtae-n.js → AppLayout-CDEtuEYf.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-pQ6LcA-W.js → Artifacts-DS3jAjeW.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-DK61tocB.js → Audit-CcSJA048.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-BOKgDFRh.js → BackgroundAgents-DRo9Morb.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-Bo9G4vTP.js → Backup-Vw5MrWft.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-Do_dE9_v.js → BaseInput-DNHIu823.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-COVvZzOL.js → Chat-DvAsO8-b.js} +4 -4
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-9aDuObay.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-Z9MJMolt.js → Checkbox-CKCa7tvn.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-BVzWKVbf.js → Codegen-CIFvZdaT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-C8MnYcbT.js → Col-C79yz7bK.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-DQxqw15l.js → Community-DVvZskLJ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-BAUmrh3y.js → Compact-oaew61hV.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-jPAkQ3zK.js → Compliance-DqA7wmRx.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-BBQyxbh8.js → Cowork-B-qGadxa.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-W5koq8Z5.js → Cron-CUlCl4sS.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BQhnu5hS.js → Crosschain-CNOlr9DU.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-DD7PlI18.js → DID-DJqFCT_3.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-w_ILuwKI.js → Dashboard-CznGpphf.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-qiDUAyAo.js → Dropdown-_cJw218E.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-PDeBcI5H.js → EmailListRenderer-Dr-1BZ9C.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-SuArscRM.js → FamilyGuardDashboard-Dxf4xtSk.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-BCovZaUn.js → Federation-DOiG3i_1.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-DjlFdVFk.js → FormItemContext-BzbMcFB9.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-DoEZ7fcS.js → GenericCardRenderer-VbBrYttP.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-CV0-6GPI.js → Git-CLG8E6ED.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-B_SFN50w.js → Governance-LGJQfk_q.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-heI0Eo6k.js → Inference-DlQKx9lF.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-Cz5INS5p.js → KnowledgeGraph-DU8vEouV.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BSFxiHkg.js → Logs-FZIQOdln.js} +2 -2
  37. package/src/assets/web-panel/assets/MarkdownRenderer-BJNWIrbw.js +1 -0
  38. package/src/assets/web-panel/assets/{Marketplace-0TWTpE7k.js → Marketplace-C_xd-zZI.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-CV4-wBLm.js → McpTools-q-YktphS.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-x5bH5rw5.js → Memory-Cq9OhDin.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-DaBmUu9G.js → MobileBridge-B03_R98T.js} +1 -1
  42. package/src/assets/web-panel/assets/MobileProjects-DZrk-qmF.js +1 -0
  43. package/src/assets/web-panel/assets/{Mtc-B_zKEBif.js → Mtc-BidCKW28.js} +5 -5
  44. package/src/assets/web-panel/assets/{MtcAudit-BCfKD2yg.js → MtcAudit-r-iaMVM7.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-DgYfj6c_.js → Multisig-CgTUFvff.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-Dz09lFUm.js → NLProgramming-B7NvzfAV.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-Cuj6ugBE.js → Notes-80CWNqJI.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-DK2WXjhg.js → NotificationSettings-0H6xdiY3.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-D1IAOQoo.js → OrderTableRenderer-CKP4AFWy.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-9fqnPvTw.js → Organization-B8sxCV_h.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-n5xHkhow.js → Overflow-CW9JBBhQ.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-B5AUWwH-.js → P2P-bdSQV7oZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-CoUDV6dy.js → PdhVaultBrowser-DQad42nW.js} +4 -4
  54. package/src/assets/web-panel/assets/{Permissions-DPRmQf_H.js → Permissions-CueexxTR.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-B37bsbEN.js → PersonalDataHub-fqxflhR8.js} +6 -6
  56. package/src/assets/web-panel/assets/{Pipeline-CCAuPqu9.js → Pipeline-KFwwnIMC.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-B81msZdv.js → Privacy-ClnD9buY.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-B-Py2NbF.js → ProjectInit-CZEz6sm3.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-CYsneiRB.js → ProjectSettings-B6NzpmOI.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-Y975Mh6R.js → Projects-DYyuwPCs.js} +1 -1
  61. package/src/assets/web-panel/assets/Providers-C3tz_jdh.js +1 -0
  62. package/src/assets/web-panel/assets/{QrScannerModal-Bj_6gJ0l.js → QrScannerModal-BzuOBbqB.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-BgpBCMPE.js → QuickAsk-CmNO9qSj.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-DhUW3YUA.js → Recommend-VJNj3_NF.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DdeyfbkA.js → RemoteSession-CrbdbSBF.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-fxW421xd.js → Reputation-DTyMd45j.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-De73w1og.js → Row-DHrQ-Icz.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-3US_Q_lj.js → RssFeed-DqWxY9c5.js} +2 -2
  69. package/src/assets/web-panel/assets/{Search-DbZ_DU74.js → Search-ctF1Ks3J.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-DoIU1vMF.js → Security-BrQz8uo5.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-DB_RGqpc.js → Services-D4scVFar.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-DnzLaayC.js → Skeleton-oUScaOjv.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-VzVXHENF.js → Skills-CL-y0eLt.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-DMfvjCAI.js → Sla-DQExhwXj.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-DQQOYWbF.js → SpeechSettings-iNp_I7kN.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-Bmx3FG1P.js → SyncSettings-CTDlMo0A.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-DOQyfb-2.js → Tasks-4kb8l3sc.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-D6hTRT7c.js → Templates-BXcL9oXW.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-CTPQZAzs.js → Tenant-DLE8eS0D.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-B7vII3oH.js → Terminal-DGpUZPPK.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BkiFJeXV.js → TimelineRenderer-C1pWXGPC.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-BPPdQMNo.js → Tokens-BYfbfqhZ.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BxG8JAEI.js → Trigger-K9YfLOCw.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-DHD7nMpM.js → Trust-Ctkq77Hy.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-B4BJpU47.js → UkeySign-C6_CSe9J.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-CRiGpFx2.js → VideoEditing-Be3rpOWs.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-BSDSNiem.js → Wallet-D0UIplxk.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-DLTUh5Zv.js → WebAuthn-CF9bM9I3.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-tW2MwjhD.js → WorkflowEditor-D0H1XVPY.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-SgsLQ3FC.js → chat-Cgzzbe0o.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-BHjjLIKO.js → colors-BwNxEVS0.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-VceErdKq.js → compact-item-D60Z4Hhs.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-BZ332MYl.js → createContext-MFk58XB0.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BJiw369i.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-CBTjJOjn.js → hasIn-CRj2SvGO.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DaCCy6lq.js → index--s45_1cb.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CMVvxIS8.js → index--tquQ2ZG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BhXZEUAc.js → index-1T1rhokn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BQSmZGgs.js → index-B-M82_rx.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BeiqkxJG.js → index-BE_el1Zs.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-29wVzi-k.js → index-BhRkR7IO.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-iYQNU9za.js → index-Bo0y96-K.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BRWhLbWF.js → index-CJgu5JUK.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BPXFLYdW.js → index-CLWwdebO.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Cr4rjlW5.js → index-CNAK5QG_.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CaIS_TNX.js → index-CQjJjPRD.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-1n7xjKDU.js → index-CTDbAfae.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BETEjuNr.js → index-CVI_9sRK.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-By5jBtkV.js → index-CY14oMQK.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DgPvuIn2.js → index-CbiTJkZf.js} +1 -1
  111. package/src/assets/web-panel/assets/index-CiMouosn.js +1 -0
  112. package/src/assets/web-panel/assets/{index-CawpdGTW.js → index-CqWpxMqx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-_PHrIO4O.js → index-CvB8lYUR.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BOtvPdHt.js → index-D51XwtCY.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CrUgDQkf.js → index-D6HvK_1O.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BY1wexYG.js → index-D9jVIwLJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C_B9rFQE.js → index-DA4tE98u.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-B5KAGxNo.js → index-DBwOtXw0.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BC1xuQgX.js → index-DKdNzTIb.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-B2u26AS3.js → index-DRySdmvN.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DePOeMDM.js → index-DWzyTev7.js} +1 -1
  122. package/src/assets/web-panel/assets/index-DXcKlwy7.js +1 -0
  123. package/src/assets/web-panel/assets/{index-BTlc-Bve.js → index-DYjs7jAP.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CLc19alH.js → index-DbROzMBp.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-fU7wNHDB.js → index-DhFtk5PR.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D_SePDWj.js → index-Di2AbMLg.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-vIXOpcjD.js → index-DvmocQsM.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BcUenmCX.js → index-Dw-o0zEF.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DUkVN794.js → index-DysPoP38.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CtjLcCNU.js → index-LX9pJxm_.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B40dQn0G.js → index-OI_fXWx0.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-FJ3zC9LK.js → index-eDTOX8Xj.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DdefYBSw.js → index-gWKDsTzF.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-CpumS98o.js → index-q_GDUVAA.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-D1oZNz0D.js → initDefaultProps-C61kZqEd.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-C8k6doMO.js → motion-DZnT5U1L.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-DJ4qSbYt.js → move-L8wzwbel.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-CdpJD85j.js → mtc-parser-DLdYSejc.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-B0V4slKA.js → omit-D53cbS59.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-Di5NiW1F.js → pickAttrs-Bbc5HPtu.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-B5OMHgEM.js → placementArrow-BkvmO860.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-C6U9IWql.js → responsiveObserve-u2y1ryS1.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-BMyTH3rN.js → slide-BBqj7svU.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-C7d6BG7Z.js → statusUtils-GN0mqu2d.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-CR50QnQy.js → styleChecker-BvwPKoBT.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-DBEeoMb8.js → useFlexGapSupport-CCt8tnfY.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-3r099cjv.js → useFs-DXAxl2SV.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-C5IYNdTK.js → usePersonalDataHub-DihlR2dj.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-DmEOLRHf.js → vnode-D7BOX2gi.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-DnpUiVCB.js → zoom-CVbO-nL7.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +4 -4
  153. package/src/commands/agent.js +3 -3
  154. package/src/commands/background-session.js +145 -0
  155. package/src/commands/mcp.js +244 -7
  156. package/src/commands/serve.js +1 -1
  157. package/src/commands/session-show.js +129 -0
  158. package/src/commands/session.js +97 -145
  159. package/src/commands/skill.js +87 -50
  160. package/src/data/changelog.json +40 -1
  161. package/src/gateways/ws/session-protocol.js +14 -4
  162. package/src/gateways/ws/ws-agent-handler.js +39 -1
  163. package/src/harness/jsonl-session-store.js +1547 -127
  164. package/src/harness/mcp-client.js +2690 -409
  165. package/src/harness/session-index.js +80 -80
  166. package/src/harness/session-list-index.js +210 -7
  167. package/src/lazy-dispatch.js +76 -8
  168. package/src/lib/abort-utils.js +68 -0
  169. package/src/lib/agent-core.js +1 -59
  170. package/src/lib/background-interaction-journal.js +2 -2
  171. package/src/lib/chat-core.js +24 -6
  172. package/src/lib/command-lifecycle-report.js +516 -0
  173. package/src/lib/cowork-mcp-tools.js +47 -2
  174. package/src/lib/cowork-task-runner.js +11 -3
  175. package/src/lib/doctor-checkup.js +9 -7
  176. package/src/lib/learning/skill-improver.js +21 -2
  177. package/src/lib/mcp-call-ledger-store.js +116 -3
  178. package/src/lib/mcp-call-ledger.js +59 -14
  179. package/src/lib/mcp-client.js +1 -0
  180. package/src/lib/mcp-stdio-environment.js +48 -0
  181. package/src/lib/mcp-stdio-executable-identity.js +1133 -0
  182. package/src/lib/mcp-stdio-execution-authority.js +363 -0
  183. package/src/lib/mcp-stdio-package-materialization.js +1424 -0
  184. package/src/lib/mcp-tool-metadata.js +436 -0
  185. package/src/lib/mcp-tool-result.js +367 -0
  186. package/src/lib/observability/index.js +71 -0
  187. package/src/lib/packer/native-update-state.js +979 -1
  188. package/src/lib/packer/pack-update-applier.js +1050 -125
  189. package/src/lib/packer/pkg-config-generator.js +1 -0
  190. package/src/lib/paths.js +53 -0
  191. package/src/lib/pr-link-ledger.js +8 -27
  192. package/src/lib/pr-link-store.js +26 -0
  193. package/src/lib/process-execution-broker/index.js +192 -35
  194. package/src/lib/process-execution-broker/platform-sandbox.js +291 -24
  195. package/src/lib/process-tree-termination.js +253 -0
  196. package/src/lib/recent-session.js +17 -17
  197. package/src/lib/secure-file-identity.js +47 -0
  198. package/src/lib/secure-fs.js +21 -2
  199. package/src/lib/session-anti-rollback-anchor.js +766 -0
  200. package/src/lib/session-host-lease.js +492 -0
  201. package/src/lib/session-host-snapshot.js +9 -5
  202. package/src/lib/session-persistence-failure.js +109 -0
  203. package/src/lib/session-picker.js +26 -3
  204. package/src/lib/session-projection.js +5 -1
  205. package/src/lib/session-search.js +25 -11
  206. package/src/lib/session-tail.js +31 -1
  207. package/src/lib/side-effect-ledger-store.js +2 -2
  208. package/src/lib/skill-budget.js +222 -0
  209. package/src/lib/skill-execution-authority.js +385 -0
  210. package/src/lib/skill-execution-identity.js +39 -2
  211. package/src/lib/skill-loader.js +322 -86
  212. package/src/lib/skill-mcp.js +43 -1
  213. package/src/lib/turn-binding-store.js +2 -2
  214. package/src/repl/agent-repl.js +483 -77
  215. package/src/repl/bg-dashboard.js +81 -28
  216. package/src/repl/chat-repl.js +77 -12
  217. package/src/runtime/agent-core.js +428 -113
  218. package/src/runtime/agent-runtime.js +26 -3
  219. package/src/runtime/cleanup-deadline.js +143 -0
  220. package/src/runtime/headless-runner.js +494 -149
  221. package/src/runtime/headless-stream.js +504 -75
  222. package/src/runtime/mcp-config.js +157 -10
  223. package/src/runtime/mcp-tool-search.js +0 -0
  224. package/src/runtime/output-backpressure.js +419 -0
  225. package/src/runtime/pipe-safety.js +56 -21
  226. package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +0 -1
  227. package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +0 -1
  228. package/src/assets/web-panel/assets/MobileProjects-DqnoFPgR.js +0 -1
  229. package/src/assets/web-panel/assets/Providers-uO-10aP1.js +0 -1
  230. package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +0 -1
  231. package/src/assets/web-panel/assets/index-0O5ePnUq.js +0 -1
  232. package/src/assets/web-panel/assets/index-ChAzPigF.js +0 -1
  233. 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
- return runWithHostHooksV2Workspace(trustedWorkspaceRoot, () =>
1050
- runAgentHeadlessStreamInWorkspace(options, deps),
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(options = {}, deps = {}) {
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 || ((s) => process.stdout.write(s));
1060
- const writeErr = deps.writeErr || ((s) => process.stderr.write(s));
1061
- if (!deps.writeOut && !deps.writeErr) installPipeSafety();
1351
+ const writeOut = deps.writeOut;
1352
+ const writeErr = deps.writeErr;
1062
1353
 
1063
1354
  const sessionId =
1064
- options.sessionId || `headless-stream-${Date.now()}-${process.pid}`;
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: deps.sessionExists || jsonlSessionExists,
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) process.stderr.write(notice + "\n");
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
- const mcpLedgerSink = persist
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.appendEvent(
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: "error_session_persistence",
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(`${code}: canonical stream session creation was refused\n`);
1965
- return { exitCode: 1, turns: 0 };
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
- mcp.mcpClient.setElicitationHandler(async (request) => {
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 { startHeadlessRemoteApproval } =
2277
- await import("../lib/remote-approval-bridge.js");
2278
- _remoteApproval = await startHeadlessRemoteApproval({
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 readJsonLines(input)) {
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
- wakeQueue = r;
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
- /* best-effort */
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
- /* best-effort */
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 || costStopped || outcome.endReason === "no-response";
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
- : isError
3255
- ? "error"
3256
- : "success",
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
- // Tear down ad-hoc MCP servers (--mcp-config) when stdin closes.
3298
- if (mcp?.mcpClient) {
3299
- try {
3300
- await mcp.mcpClient.disconnectAll();
3301
- } catch {
3302
- // ignore — disconnect is best-effort
3303
- }
3304
- }
3305
-
3306
- // Tear down the --remote-control approval bridge + its self-hosted WS
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
- // settings.json SessionEnd hooks (observe-only) when stdin closes.
3317
- if (settingsHooks) {
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
- emit({ type: "system", subtype: "end", session_id: sessionId, turns });
3333
- return { exitCode: sawError ? 1 : 0, turns };
3756
+ return {
3757
+ exitCode: pipeState.closed ? 0 : sawError ? 1 : 0,
3758
+ turns,
3759
+ ...(terminalPersistenceFailure
3760
+ ? { persistence: terminalPersistenceFailure }
3761
+ : {}),
3762
+ };
3334
3763
  }