chainlesschain 0.165.2 → 0.165.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/README.md +151 -29
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/assets/{AIOps-lS1R0Cza.js → AIOps-C__nnraW.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-BmlgBPsj.js → ActionButton-CsKWHEZ-.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-CNKczv1S.js → Analytics-BhIjfjWO.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-BuZGJARQ.js → AppLayout-BjuXFzWZ.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-Dm73Sl-O.js → Artifacts-k6whDkZJ.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-BGMW7A5e.js → Audit-C4FBMYRh.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-BQ6M54JR.js → BackgroundAgents-C_vRrSXk.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-DBzGP0bR.js → Backup-CngFZeVp.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-D1mLCoOV.js → BaseInput-B_H9EBRm.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-BUfcN7p8.js → Chat-DLEC5kt7.js} +4 -4
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-Du0Yg5Pi.js → Checkbox-m3ClR4xh.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-B2RXziHW.js → Codegen-CmLSFrgp.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-DWhekOrR.js → Col-DFGQteHN.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-HQhrkon4.js → Community-EUUVORHE.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-izHNb4uz.js → Compact-2PSg0Yr7.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-BDwLy41-.js → Compliance-CcKur7gB.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DFJhLa7K.js → Cowork-DK_Fezfa.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-DYY9RG7-.js → Cron-Dbw89xl7.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-rr7tJTtv.js → Crosschain-CH66h_Ec.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-D2sfHRLi.js → DID-D1igRb17.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-q_eGdhha.js → Dashboard-DVAsrobk.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-Cufrh2Bz.js → Dropdown-Dl7W8AQs.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-B-kdv10f.js → EmailListRenderer-D5tpaEQC.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D21XjgfG.js → FamilyGuardDashboard-O_VQf8gq.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-CAwRTLZH.js → Federation-BTVcgS9n.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-vx3daNDF.js → FormItemContext-BRnkJAIz.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-CRWY2wU4.js → GenericCardRenderer-8OWSCkDj.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Q6SwauyD.js → Git-WXLYTQj2.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-BJ1q4IQZ.js → Governance-BezaUhtc.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CSWjROgD.js → Inference-CuRay3Gu.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-fzuQsWuY.js → KnowledgeGraph-DfWLDW1m.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-DVF8Mnns.js → Logs-C55y2pPY.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-BQP7LOA4.js → Marketplace-Dk08Uycg.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-BNlJCmNc.js → McpTools-C11fSB5V.js} +4 -4
  39. package/src/assets/web-panel/assets/{Memory-DLLg5WQ4.js → Memory-mr0eZc97.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-CR2skNAF.js → MobileBridge-C0iCSRbb.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-BxVavObl.js → MobileProjects-CY-xMG8Z.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-B17pgqMI.js → Mtc-Bplqc_Wc.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-4UmV5cIL.js → MtcAudit-BlzwsoSM.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-DoMEXEcU.js → Multisig-BBLmTty6.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-h9DMXFX4.js → NLProgramming-DtT1uf1W.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-Cihn-xX8.js → Notes-DcqQAxTK.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-BE6ZGa3R.js → NotificationSettings-cb0kWoZe.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-CnwojoSI.js → OrderTableRenderer-kw9HZQNN.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-B2vpwmjb.js → Organization-DlTlbkUN.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-C4z28R0-.js → Overflow-pp1qs-oY.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BVlFkwhW.js → P2P-BESLnyYz.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-BXbEdL5B.js → PdhVaultBrowser-B-J1mGuf.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-C28xs4lp.js → Permissions-B36PeT1_.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-Dgl_rBjM.js → PersonalDataHub-CTK2ad-3.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-3QUIhrKI.js → Pipeline-zJNGXsac.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-8Gh33OZ5.js → Privacy-b9fz0Qf0.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-BNmg4tZi.js → ProjectInit-DjZbZgiB.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-vgMfeCf9.js → ProjectSettings-C-hHJXlD.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-CXoOa8eX.js → Providers-Dz-CAYYU.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-BhfTF0oS.js → QrScannerModal-A0yoQ3-3.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-C746HEAj.js → QuickAsk-DrFgFJNC.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-BSej8Eo0.js → Recommend-KC8hF1g4.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-f3gmnlG3.js → RemoteSession-DGTPIk6u.js} +3 -3
  65. package/src/assets/web-panel/assets/{Reputation-CyvybpkN.js → Reputation-QmbwM7Yk.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DwV2PeYn.js → Row-BxjJylia.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-11GrzjZy.js → RssFeed-B7Wda1J6.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-BjKXkrAY.js → Search-_plQ_YSA.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-eWiFb9Yk.js → Security-CD2YJZfx.js} +2 -2
  70. package/src/assets/web-panel/assets/{Services-CdCsMAtt.js → Services-qQE7NmwE.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-4Q2UTKQ5.js → Skeleton-DgQqkFf7.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-v4rtYrRj.js → Skills-C4C0oFFt.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-CRhsFvd_.js → Sla-Px2N2B5c.js} +1 -1
  74. package/src/assets/web-panel/assets/SpeechSettings-DN6C3lQd.js +1 -0
  75. package/src/assets/web-panel/assets/{SyncSettings-D16zonl7.js → SyncSettings-jm5aH_m7.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-Xxozz0i_.js → Tasks-DlkJcv67.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-CXJpPtCT.js → Templates-DyrTHUBs.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-XRV0L1ZF.js → Tenant-mC434PtL.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-CYuO1i_w.js → Terminal-o0_s3NgK.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-BXqitWsu.js → TimelineRenderer-DAGlAXOw.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BvPrDHIy.js → Tokens-BnulS_bs.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-DhCrSTf5.js → Trigger-suQbVXYM.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DmsuZD6K.js → Trust-DbIJAPo5.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-D_yrts5C.js → UkeySign-CQNpbIHi.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-B01ihPrm.js → VideoEditing-D8aWCERk.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-DpUjKa-N.js → Wallet-CuLpYfZc.js} +2 -2
  87. package/src/assets/web-panel/assets/{WebAuthn-CL07yON6.js → WebAuthn-CsfG8ZMc.js} +2 -2
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CLMflnRr.js → WorkflowEditor-BYsEAZbd.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-DtUqe8h_.js → chat-FT4Um3-I.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-DPKrgVfy.js → colors-CKbS_Wlt.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-BTt2GtOm.js → compact-item-CFJtzuJM.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-D_VeUc1J.js → createContext-B9kSRW6K.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-1kndiylj.js → hasIn-DR-x0Xpi.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-dVc-CXY4.js → index-7jHr_A_k.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DFIZ_1Sf.js → index-B3ZX81mK.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-fk2Rjacw.js → index-B7osrXqy.js} +3 -3
  98. package/src/assets/web-panel/assets/{index-CHG-KcS4.js → index-BGbPnC5x.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-Dbbymxpj.js → index-BI5hP_qy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BaZAbw9A.js → index-BK3TiKVq.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CzjkKUmR.js → index-BVPU43b1.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BAXfRKfG.js → index-BW-t9n-q.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-8nz_fmlj.js → index-BfFCQHL3.js} +1 -1
  104. package/src/assets/web-panel/assets/index-Bizb4hkw.js +1 -0
  105. package/src/assets/web-panel/assets/{index-DdkplL9R.js → index-Bt4fS2eL.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DG2MZQ3f.js → index-BwR7yc2h.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DfKBRdSS.js → index-Bzz0C1BZ.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-GsoUQ2M7.js → index-C2JvmYRc.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-l1BETrDb.js → index-CGaXUsIs.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-4bna2_SR.js → index-CJmIQ9XR.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DfGl8CqG.js → index-C_enk9I_.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-D95OFht7.js → index-Cr2FLUsD.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DsJCDUJG.js → index-CvrCCqJt.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-sDoMHTNA.js → index-D4LfPvWA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-By5n8up9.js → index-DMGmg497.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-ocf06EVQ.js → index-DShSkww-.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Bhudc_Ez.js → index-DTxNy0ZU.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CX81qSM4.js → index-DW4BWjTk.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BlDNVgBt.js → index-DWdqx7zI.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CcO04eDn.js → index-Dap9c3eD.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Dwqmtk65.js → index-DcfxhKXR.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-2eyAXE3d.js → index-DdkIhfi-.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-brCInrzq.js → index-DeSgw-WV.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DJ8vXGOa.js → index-DiNUEpSQ.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BHpBMWCh.js → index-DjEP7WkH.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-m31Ka8hZ.js → index-DtkFqPVA.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CtF7EN49.js → index-KqDaCApw.js} +1 -1
  128. package/src/assets/web-panel/assets/index-OHSl-xCh.js +1 -0
  129. package/src/assets/web-panel/assets/{index-B9KvcS7R.js → index-OSzjTzGM.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CauoBQkH.js → index-QBSeNVO3.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-FzdwcSy2.js → index-WE08gMJP.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DxdTdBix.js → index-r2VI1Ikk.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BepLZwgB.js → index-tqSEHPRT.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-CvyYJd7z.js → initDefaultProps-Dd7n7VgI.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-DYZkhUdz.js → motion-BwxouRw1.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-slnJRVT1.js → move-DObN0PG9.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-DVc0CKmy.js → mtc-parser-B3nOUQc7.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-BCXAtHCq.js → omit-o9WU2Nbl.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-DBsemz_K.js → pickAttrs-CL_vvfQ_.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-BRtkJ-nv.js → placementArrow-CYrM2tyK.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-Cebq7EZn.js → responsiveObserve-k_06cQ96.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-DT7qvId2.js → slide-CPKG0pxS.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-pQiITLkG.js → statusUtils-BDDEeLTw.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-c-19BVKQ.js → styleChecker-Cmb7RTh7.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-DsS5-0bE.js → useFlexGapSupport-e2IedC4N.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-CxGiMe5n.js → useFs-CE_gxVnm.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-D9btVID5.js → usePersonalDataHub-BkMIIKMz.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-CUYFsFEN.js → vnode-DW886XV1.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-CXmn_35j.js → zoom-Dwo_adaf.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +3 -3
  152. package/src/commands/agent.js +105 -35
  153. package/src/commands/artifacts.js +363 -33
  154. package/src/commands/cowork.js +202 -36
  155. package/src/commands/session-budget.js +155 -0
  156. package/src/commands/session-location.js +948 -0
  157. package/src/commands/session.js +72 -46
  158. package/src/data/changelog.json +26 -0
  159. package/src/gateways/ws/artifact-protocol.js +77 -13
  160. package/src/gateways/ws/session-protocol.js +391 -24
  161. package/src/gateways/ws/ws-agent-handler.js +191 -14
  162. package/src/gateways/ws/ws-server.js +14 -3
  163. package/src/gateways/ws/ws-session-gateway.js +101 -5
  164. package/src/harness/jsonl-session-store.js +1210 -0
  165. package/src/lib/agent-team/team-runner.js +43 -13
  166. package/src/lib/artifact-access-ledger.js +461 -0
  167. package/src/lib/artifact-cleanup-ledger.js +861 -0
  168. package/src/lib/artifact-deletion-ledger.js +677 -0
  169. package/src/lib/artifact-store.js +256 -51
  170. package/src/lib/background-agent-keeper-protocol.js +75 -4
  171. package/src/lib/background-agent-launch-barrier.js +74 -0
  172. package/src/lib/background-agent-supervisor.js +149 -5
  173. package/src/lib/background-turn-bootstrap-protocol.js +4 -0
  174. package/src/lib/command-lifecycle-report.js +8 -4
  175. package/src/lib/cowork-adapter.js +9 -5
  176. package/src/lib/cowork-task-runner.js +20 -0
  177. package/src/lib/cowork-workflow.js +178 -20
  178. package/src/lib/direct-model-usage.js +62 -22
  179. package/src/lib/dynamic-workflow-runtime.js +3184 -111
  180. package/src/lib/execution-location-result-apply.js +339 -0
  181. package/src/lib/execution-location-result-artifact.js +271 -0
  182. package/src/lib/execution-location-result-review.js +253 -0
  183. package/src/lib/execution-location-result-store.js +331 -0
  184. package/src/lib/execution-location-result.js +617 -0
  185. package/src/lib/execution-location-target.js +236 -1
  186. package/src/lib/managed-tool-checkpoint.js +47 -4
  187. package/src/lib/process-execution-broker/workspace-transaction.js +3 -0
  188. package/src/lib/session-budget-production-root.js +376 -0
  189. package/src/lib/session-budget-runtime.js +109 -18
  190. package/src/lib/session-budget-usage.js +63 -0
  191. package/src/lib/session-host-snapshot.js +19 -2
  192. package/src/lib/session-resource-budget.js +766 -105
  193. package/src/lib/sub-agent-context.js +69 -22
  194. package/src/lib/web-ui-server.js +12 -1
  195. package/src/lib/with-file-lock.js +175 -26
  196. package/src/repl/agent-repl.js +237 -16
  197. package/src/runtime/agent-core.js +123 -0
  198. package/src/runtime/headless-runner.js +135 -46
  199. package/src/runtime/headless-stream.js +133 -25
  200. package/src/runtime/policies/agent-policy.js +10 -0
  201. package/src/workers/background-agent-keeper.js +112 -19
  202. package/src/workers/background-agent-worker.js +383 -134
  203. package/src/workers/background-turn-bootstrap.js +13 -8
  204. package/src/assets/web-panel/assets/ChatBubbleRenderer-CY5m8ZHg.js +0 -1
  205. package/src/assets/web-panel/assets/MarkdownRenderer-BLif4QSg.js +0 -1
  206. package/src/assets/web-panel/assets/Projects-BhtAVilc.js +0 -1
  207. package/src/assets/web-panel/assets/SpeechSettings-DLjKsDVu.js +0 -1
  208. package/src/assets/web-panel/assets/devWarning-Bj5nUW2v.js +0 -1
  209. package/src/assets/web-panel/assets/index-CPcM-eqo.js +0 -1
  210. package/src/assets/web-panel/assets/index-xeSyVoRe.js +0 -1
@@ -62,6 +62,13 @@ import {
62
62
  readVerifiedMessages,
63
63
  settleWsTurnClaim,
64
64
  } from "../../harness/jsonl-session-store.js";
65
+ import { sessionBudgetAdmissionError } from "../../lib/session-budget-production-root.js";
66
+ import {
67
+ beginSessionBudgetUsage,
68
+ markSessionBudgetUsageUnknown,
69
+ recordSessionBudgetUsage,
70
+ rejectSessionBudgetUsageUnknown,
71
+ } from "../../lib/session-budget-usage.js";
65
72
 
66
73
  const CANONICAL_WS_TURN_QUEUES = new Map();
67
74
  const CANONICAL_WS_CLAIM_CAS_ATTEMPTS = 4;
@@ -155,6 +162,7 @@ export class WSAgentHandler {
155
162
  compactionChatFn,
156
163
  canonicalSessionStore,
157
164
  sessionHostLease = null,
165
+ sessionBudgetRoot = null,
158
166
  }) {
159
167
  this.session = session;
160
168
  this.interaction = interaction;
@@ -163,6 +171,8 @@ export class WSAgentHandler {
163
171
  this._abortController = null;
164
172
  this._activeRequestId = null;
165
173
  this._sessionHostLease = sessionHostLease;
174
+ this._sessionBudgetRoot = sessionBudgetRoot;
175
+ this._sessionBudget = sessionBudgetRoot?.budget || null;
166
176
  this._onSessionHostLeaseAbort = null;
167
177
  if (sessionHostLease?.signal) {
168
178
  this._onSessionHostLeaseAbort = () => {
@@ -181,6 +191,26 @@ export class WSAgentHandler {
181
191
  );
182
192
  if (sessionHostLease.signal.aborted) this._onSessionHostLeaseAbort();
183
193
  }
194
+ this._sessionBudgetSignal = sessionBudgetRoot?.options?.signal || null;
195
+ this._onSessionBudgetAbort = null;
196
+ if (this._sessionBudgetSignal) {
197
+ this._onSessionBudgetAbort = () => {
198
+ const reason = sessionBudgetAdmissionError(
199
+ this._sessionBudget?.reason?.(),
200
+ "WebSocket session",
201
+ );
202
+ if (this._abortController && !this._abortController.signal.aborted) {
203
+ this._abortController.abort(reason);
204
+ }
205
+ this.interaction?.rejectAllPending?.(reason);
206
+ };
207
+ this._sessionBudgetSignal.addEventListener(
208
+ "abort",
209
+ this._onSessionBudgetAbort,
210
+ { once: true },
211
+ );
212
+ if (this._sessionBudgetSignal.aborted) this._onSessionBudgetAbort();
213
+ }
184
214
  // P0-2: monotonic sequence for crash-safe side-effect ledger op ids. The
185
215
  // session id + a per-op nonce keep ids unique across turns and processes.
186
216
  this._sideEffectSeq = 0;
@@ -251,6 +281,58 @@ export class WSAgentHandler {
251
281
  : Object.freeze(hostSystemPrefix);
252
282
  }
253
283
 
284
+ attachInteraction(interaction) {
285
+ if (!interaction || interaction === this.interaction) return false;
286
+ const previous = this.interaction;
287
+ this.interaction = interaction;
288
+ this.session.interaction = interaction;
289
+ previous?.rejectAllPending?.(
290
+ createAbortError("WebSocket session transport was reattached"),
291
+ );
292
+ return true;
293
+ }
294
+
295
+ assertSessionBudgetAdmission(operation = "WebSocket operation") {
296
+ if (!this._sessionBudget) return true;
297
+ if (
298
+ this._sessionBudget.signal?.aborted === true ||
299
+ this._sessionBudgetSignal?.aborted === true
300
+ ) {
301
+ throw sessionBudgetAdmissionError(
302
+ this._sessionBudget.reason?.(),
303
+ operation,
304
+ );
305
+ }
306
+ return true;
307
+ }
308
+
309
+ _turnSignal(turnSignal) {
310
+ const budgetSignal = this._sessionBudgetSignal;
311
+ if (!budgetSignal) return turnSignal;
312
+ return AbortSignal.any([turnSignal, budgetSignal]);
313
+ }
314
+
315
+ _recordSessionBudgetUsage(event, operation = "WebSocket usage settlement") {
316
+ return recordSessionBudgetUsage(this._sessionBudget, event, operation);
317
+ }
318
+
319
+ _beginSessionBudgetUsage(event, operation = "WebSocket provider call") {
320
+ return beginSessionBudgetUsage(this._sessionBudget, event, operation);
321
+ }
322
+
323
+ _markSessionBudgetUsageUnknown(event) {
324
+ return markSessionBudgetUsageUnknown(this._sessionBudget, event);
325
+ }
326
+
327
+ _consumeSessionBudgetTurn(id, operation) {
328
+ if (!this._sessionBudget?.consumeTurn) return null;
329
+ const admission = this._sessionBudget.consumeTurn({ id });
330
+ if (!admission?.ok) {
331
+ throw sessionBudgetAdmissionError(admission?.reason, operation);
332
+ }
333
+ return admission;
334
+ }
335
+
254
336
  _applyCanonicalMessages(messages) {
255
337
  const canonicalMessages = Array.isArray(messages) ? messages : [];
256
338
  this.session.messages = [
@@ -383,7 +465,12 @@ export class WSAgentHandler {
383
465
  model: this.session.model || null,
384
466
  source: "semantic-compaction",
385
467
  });
468
+ this._consumeSessionBudgetTurn(
469
+ `turn:${call.callId}`,
470
+ "WebSocket compaction",
471
+ );
386
472
  this._persistCanonicalUsageBoundary(call, "started");
473
+ this._beginSessionBudgetUsage(call, "WebSocket compaction provider call");
387
474
  return call;
388
475
  }
389
476
 
@@ -402,19 +489,36 @@ export class WSAgentHandler {
402
489
  },
403
490
  "unknown",
404
491
  );
492
+ if (this._markSessionBudgetUsageUnknown(call)) {
493
+ rejectSessionBudgetUsageUnknown(
494
+ call,
495
+ "WebSocket compaction provider call",
496
+ );
497
+ }
405
498
  return;
406
499
  }
407
500
  if (result?.usageEvent?.usage) {
408
- this._persistCanonicalTokenUsage({
501
+ const usageEvent = {
409
502
  ...call,
410
503
  usage: result.usageEvent.usage,
411
- });
504
+ };
505
+ this._persistCanonicalTokenUsage(usageEvent);
506
+ this._recordSessionBudgetUsage(
507
+ usageEvent,
508
+ "WebSocket compaction usage settlement",
509
+ );
412
510
  return;
413
511
  }
414
512
  this._persistCanonicalUsageBoundary(
415
513
  { ...call, code: "provider_usage_missing" },
416
514
  "unknown",
417
515
  );
516
+ if (this._markSessionBudgetUsageUnknown(call)) {
517
+ rejectSessionBudgetUsageUnknown(
518
+ call,
519
+ "WebSocket compaction provider call",
520
+ );
521
+ }
418
522
  }
419
523
 
420
524
  _settleCanonicalCompactionFailure(call) {
@@ -423,6 +527,7 @@ export class WSAgentHandler {
423
527
  { ...call, code: "provider_call_failed" },
424
528
  "unknown",
425
529
  );
530
+ this._markSessionBudgetUsageUnknown(call);
426
531
  }
427
532
 
428
533
  _latchCompactionSettlement({
@@ -511,7 +616,7 @@ export class WSAgentHandler {
511
616
  return null;
512
617
  }
513
618
 
514
- async _compactCanonicalHistoryBeforeClaim(requestId) {
619
+ async _compactCanonicalHistoryBeforeClaim(requestId, signal = null) {
515
620
  const { session } = this;
516
621
  if (
517
622
  session.autoCompact === false ||
@@ -525,6 +630,8 @@ export class WSAgentHandler {
525
630
  const expectedMessages = [...session.messages];
526
631
  const strictUsageTelemetry =
527
632
  this._resolveCanonicalUsageProtocol() === "call-ledger";
633
+ const meterCompaction =
634
+ strictUsageTelemetry || Boolean(this._sessionBudget);
528
635
  let compactionUsageCall = null;
529
636
  let compactionUsageSettled = false;
530
637
  let result;
@@ -535,7 +642,7 @@ export class WSAgentHandler {
535
642
  model: session.model,
536
643
  baseUrl: session.baseUrl,
537
644
  apiKey: session.apiKey,
538
- signal: this._sessionHostLease?.signal,
645
+ signal: signal || this._sessionBudgetSignal || this._sessionHostLease?.signal,
539
646
  onlyIfNeeded: true,
540
647
  preserveCompletedExchange: true,
541
648
  llmQuery: this._compactionLlmQuery,
@@ -544,7 +651,7 @@ export class WSAgentHandler {
544
651
  cwd: session.projectRoot,
545
652
  sessionId: session.id,
546
653
  },
547
- ...(strictUsageTelemetry
654
+ ...(meterCompaction
548
655
  ? {
549
656
  onProviderCallStart: () => {
550
657
  const call = this._beginCanonicalCompactionUsage();
@@ -1012,6 +1119,7 @@ export class WSAgentHandler {
1012
1119
 
1013
1120
  this._processing = true;
1014
1121
  const abortController = new AbortController();
1122
+ const turnSignal = this._turnSignal(abortController.signal);
1015
1123
  this._abortController = abortController;
1016
1124
  this._activeRequestId = requestId || null;
1017
1125
  let sideEffectLedger = null;
@@ -1022,6 +1130,7 @@ export class WSAgentHandler {
1022
1130
 
1023
1131
  try {
1024
1132
  const { session } = this;
1133
+ this.assertSessionBudgetAdmission("WebSocket turn");
1025
1134
  this._sessionHostLease?.assert?.();
1026
1135
 
1027
1136
  if (session.canonicalJsonlSession === true) {
@@ -1044,7 +1153,10 @@ export class WSAgentHandler {
1044
1153
  this._resolveCanonicalUsageProtocol() === "call-ledger";
1045
1154
  if (strictUsageTelemetry) this._assertUsageLedgerWritable();
1046
1155
  const compacted =
1047
- await this._compactCanonicalHistoryBeforeClaim(requestId);
1156
+ await this._compactCanonicalHistoryBeforeClaim(
1157
+ requestId,
1158
+ turnSignal,
1159
+ );
1048
1160
  if (!compacted) return;
1049
1161
  }
1050
1162
  canonicalTurn ||= this._claimCanonicalTurn(userMessage, requestId);
@@ -1143,7 +1255,8 @@ export class WSAgentHandler {
1143
1255
  shellPolicyOverrides: session.shellPolicyOverrides || null,
1144
1256
  slotFiller,
1145
1257
  interaction: this.interaction,
1146
- signal: abortController.signal,
1258
+ signal: turnSignal,
1259
+ sessionBudget: this._sessionBudget,
1147
1260
  // P0 authority: null unless opted in (byte-identical default — agent
1148
1261
  // core already defaults `options.approvalGate || null`).
1149
1262
  approvalGate: await this._ensureApprovalGate(),
@@ -1374,18 +1487,28 @@ export class WSAgentHandler {
1374
1487
  this._persistCanonicalUsageBoundary(event, "unknown");
1375
1488
  }
1376
1489
  this._emitCompactionUsageUnknown(requestId, event);
1490
+ if (this._markSessionBudgetUsageUnknown(event)) {
1491
+ rejectSessionBudgetUsageUnknown(
1492
+ event,
1493
+ "WebSocket semantic compaction",
1494
+ );
1495
+ }
1377
1496
  break;
1378
1497
 
1379
1498
  case "model-usage-started":
1380
1499
  if (strictUsageTelemetry && event.ledgerPersisted !== true) {
1381
1500
  this._persistCanonicalUsageBoundary(event, "started");
1382
1501
  }
1502
+ this._beginSessionBudgetUsage(event);
1383
1503
  break;
1384
1504
 
1385
1505
  case "model-usage-unknown":
1386
1506
  if (strictUsageTelemetry && event.ledgerPersisted !== true) {
1387
1507
  this._persistCanonicalUsageBoundary(event, "unknown");
1388
1508
  }
1509
+ if (this._markSessionBudgetUsageUnknown(event)) {
1510
+ rejectSessionBudgetUsageUnknown(event, "WebSocket provider call");
1511
+ }
1389
1512
  break;
1390
1513
 
1391
1514
  case "compaction":
@@ -1396,9 +1519,14 @@ export class WSAgentHandler {
1396
1519
  break;
1397
1520
 
1398
1521
  case "token-usage":
1399
- if (strictUsageTelemetry && event.ledgerPersisted !== true) {
1522
+ if (
1523
+ session.canonicalJsonlSession === true &&
1524
+ (strictUsageTelemetry || this._sessionBudget) &&
1525
+ event.ledgerPersisted !== true
1526
+ ) {
1400
1527
  this._persistCanonicalTokenUsage(event);
1401
1528
  }
1529
+ this._recordSessionBudgetUsage(event);
1402
1530
  this.interaction.emit("token-usage", {
1403
1531
  requestId,
1404
1532
  provider: event.provider,
@@ -1473,13 +1601,29 @@ export class WSAgentHandler {
1473
1601
  session.lastActivity = new Date().toISOString();
1474
1602
  } catch (err) {
1475
1603
  let surfacedError = err;
1604
+ const sessionBudgetFailed =
1605
+ err?.code === "CC_SESSION_BUDGET_EXHAUSTED" ||
1606
+ err?.code === "CC_SESSION_BUDGET_USAGE_UNKNOWN" ||
1607
+ this._sessionBudget?.signal?.aborted === true;
1608
+ if (
1609
+ sessionBudgetFailed &&
1610
+ err?.code !== "CC_SESSION_BUDGET_EXHAUSTED" &&
1611
+ err?.code !== "CC_SESSION_BUDGET_USAGE_UNKNOWN"
1612
+ ) {
1613
+ surfacedError = sessionBudgetAdmissionError(
1614
+ this._sessionBudget?.reason?.(),
1615
+ "WebSocket turn",
1616
+ );
1617
+ }
1476
1618
  if (
1477
1619
  canonicalTurn?.claimAcquired &&
1478
1620
  !canonicalTurn.settled &&
1479
1621
  !canonicalTurn.settlementAttempted
1480
1622
  ) {
1481
- const failureCode = isAbortError(err)
1482
- ? "CC_WS_TURN_INTERRUPTED"
1623
+ const failureCode = sessionBudgetFailed
1624
+ ? surfacedError?.code || "CC_SESSION_BUDGET_EXHAUSTED"
1625
+ : isAbortError(err)
1626
+ ? "CC_WS_TURN_INTERRUPTED"
1483
1627
  : err?.code === "CC_WS_EMPTY_ASSISTANT_RESPONSE"
1484
1628
  ? "CC_WS_EMPTY_ASSISTANT_RESPONSE"
1485
1629
  : "CC_WS_TURN_FAILED";
@@ -1521,7 +1665,10 @@ export class WSAgentHandler {
1521
1665
  ) {
1522
1666
  persistSideEffectLedger(this.session.id, sideEffectLedger);
1523
1667
  }
1524
- if (isAbortError(err) || abortController.signal.aborted) {
1668
+ if (
1669
+ !sessionBudgetFailed &&
1670
+ (isAbortError(err) || abortController.signal.aborted)
1671
+ ) {
1525
1672
  return;
1526
1673
  }
1527
1674
 
@@ -1609,8 +1756,34 @@ export class WSAgentHandler {
1609
1756
  );
1610
1757
  }
1611
1758
  this._onSessionHostLeaseAbort = null;
1612
- this._sessionHostLease?.release?.();
1759
+ if (this._sessionBudgetSignal && this._onSessionBudgetAbort) {
1760
+ this._sessionBudgetSignal.removeEventListener(
1761
+ "abort",
1762
+ this._onSessionBudgetAbort,
1763
+ );
1764
+ }
1765
+ this._onSessionBudgetAbort = null;
1766
+ this._sessionBudgetSignal = null;
1767
+ const cleanupErrors = [];
1768
+ try {
1769
+ this._sessionBudgetRoot?.close?.();
1770
+ } catch (error) {
1771
+ cleanupErrors.push(error);
1772
+ }
1773
+ this._sessionBudgetRoot = null;
1774
+ this._sessionBudget = null;
1775
+ try {
1776
+ this._sessionHostLease?.release?.();
1777
+ } catch (error) {
1778
+ cleanupErrors.push(error);
1779
+ }
1613
1780
  this._sessionHostLease = null;
1781
+ if (cleanupErrors.length > 0) {
1782
+ throw new AggregateError(
1783
+ cleanupErrors,
1784
+ "WebSocket session authority cleanup failed",
1785
+ );
1786
+ }
1614
1787
  }
1615
1788
 
1616
1789
  /**
@@ -1727,6 +1900,8 @@ export class WSAgentHandler {
1727
1900
  break;
1728
1901
  }
1729
1902
  }
1903
+ const meterCompaction =
1904
+ strictUsageTelemetry || Boolean(this._sessionBudget);
1730
1905
 
1731
1906
  const before = session.messages.length;
1732
1907
  const expectedMessages = [...session.messages];
@@ -1735,13 +1910,15 @@ export class WSAgentHandler {
1735
1910
  let compactionUsageCall = null;
1736
1911
  let compactionUsageSettled = false;
1737
1912
  try {
1913
+ this.assertSessionBudgetAdmission("WebSocket compaction");
1738
1914
  this._sessionHostLease?.assert?.();
1739
1915
  result = await compactConversationWithProvider(session.messages, {
1740
1916
  provider: session.provider,
1741
1917
  model: session.model,
1742
1918
  baseUrl: session.baseUrl,
1743
1919
  apiKey: session.apiKey,
1744
- signal: this._sessionHostLease?.signal,
1920
+ signal:
1921
+ this._sessionBudgetSignal || this._sessionHostLease?.signal,
1745
1922
  force: true,
1746
1923
  preserveCompletedExchange: true,
1747
1924
  llmQuery: this._compactionLlmQuery,
@@ -1750,7 +1927,7 @@ export class WSAgentHandler {
1750
1927
  cwd: session.projectRoot,
1751
1928
  sessionId: session.id,
1752
1929
  },
1753
- ...(strictUsageTelemetry
1930
+ ...(meterCompaction
1754
1931
  ? {
1755
1932
  onProviderCallStart: () => {
1756
1933
  const call = this._beginCanonicalCompactionUsage();
@@ -415,6 +415,7 @@ export class ChainlessChainWSServer extends EventEmitter {
415
415
 
416
416
  /** Stop the server and clean up */
417
417
  async stop() {
418
+ const cleanupErrors = [];
418
419
  if (this._heartbeatTimer) {
419
420
  clearInterval(this._heartbeatTimer);
420
421
  this._heartbeatTimer = null;
@@ -423,13 +424,17 @@ export class ChainlessChainWSServer extends EventEmitter {
423
424
  // Close all session handlers
424
425
  for (const [sessionId, handler] of this.sessionHandlers) {
425
426
  if (handler && handler.destroy) {
426
- handler.destroy();
427
+ try {
428
+ handler.destroy();
429
+ } catch (error) {
430
+ cleanupErrors.push(error);
431
+ }
427
432
  }
428
433
  if (this.sessionManager) {
429
434
  try {
430
435
  this.sessionManager.closeSession(sessionId);
431
- } catch (_err) {
432
- // Non-critical
436
+ } catch (error) {
437
+ cleanupErrors.push(error);
433
438
  }
434
439
  }
435
440
  }
@@ -477,6 +482,12 @@ export class ChainlessChainWSServer extends EventEmitter {
477
482
  }
478
483
 
479
484
  this.emit("stopped");
485
+ if (cleanupErrors.length > 0) {
486
+ throw new AggregateError(
487
+ cleanupErrors,
488
+ "WebSocket server session authority cleanup failed",
489
+ );
490
+ }
480
491
  }
481
492
 
482
493
  /** @private */
@@ -32,6 +32,7 @@ import {
32
32
  getSession as dbGetSession,
33
33
  listSessions as dbListSessions,
34
34
  updateSession as dbUpdateSession,
35
+ deleteSession as dbDeleteSession,
35
36
  } from "../../lib/session-manager.js";
36
37
  import { buildSystemPrompt } from "../../runtime/agent-core.js";
37
38
  import { SubAgentRegistry } from "../../lib/sub-agent-registry.js";
@@ -136,6 +137,7 @@ function bindSessionHooksV2Workspace(
136
137
  * @property {string|null} rulesContent
137
138
  * @property {string[]} enabledToolNames
138
139
  * @property {object|null} hostManagedToolPolicy
140
+ * @property {object|null} sessionBudgetRoot
139
141
  * @property {Array<object>} externalToolDefinitions
140
142
  * @property {object} externalToolDescriptors
141
143
  * @property {object} externalToolExecutors
@@ -398,6 +400,8 @@ export class WSSessionManager {
398
400
  * @param {string} [options.apiKey]
399
401
  * @param {string} [options.baseUrl]
400
402
  * @param {object} [options.hostManagedToolPolicy]
403
+ * @param {object} [options.sessionBudgetRoot]
404
+ * @param {boolean} [options.requireDurable=false]
401
405
  * @returns {{ sessionId: string }}
402
406
  */
403
407
  createSession(options = {}) {
@@ -407,6 +411,14 @@ export class WSSessionManager {
407
411
  );
408
412
  }
409
413
  const sessionId = this._generateId();
414
+ const requireDurable = options.requireDurable === true;
415
+ if (requireDurable && !this.db) {
416
+ const error = new Error(
417
+ "Durable WebSocket session creation requires a configured database",
418
+ );
419
+ error.code = "CC_WS_DURABLE_SESSION_REQUIRED";
420
+ throw error;
421
+ }
410
422
  const type = options.type || "agent";
411
423
  const baseProjectRoot = options.projectRoot || this.defaultProjectRoot;
412
424
  const hostAuthorizedBaseRoot =
@@ -479,6 +491,7 @@ export class WSSessionManager {
479
491
  const messages = [{ role: "system", content: systemPrompt }];
480
492
 
481
493
  // Persist to DB
494
+ let databaseCreated = false;
482
495
  if (this.db) {
483
496
  try {
484
497
  dbCreateSession(this.db, {
@@ -488,8 +501,17 @@ export class WSSessionManager {
488
501
  model: model || "",
489
502
  messages,
490
503
  });
491
- } catch (_err) {
492
- // Non-critical
504
+ databaseCreated = true;
505
+ } catch (error) {
506
+ if (requireDurable) {
507
+ this._discardSessionResources({
508
+ planManager,
509
+ worktree,
510
+ baseProjectRoot,
511
+ });
512
+ error.code ||= "CC_WS_SESSION_PERSISTENCE_FAILED";
513
+ throw error;
514
+ }
493
515
  }
494
516
  }
495
517
 
@@ -505,6 +527,7 @@ export class WSSessionManager {
505
527
  mcpClient: this.mcpClient,
506
528
  enabledToolNames,
507
529
  hostManagedToolPolicy: options.hostManagedToolPolicy || null,
530
+ sessionBudgetRoot: options.sessionBudgetRoot || null,
508
531
  externalToolDefinitions: externalTools.definitions,
509
532
  externalToolDescriptors: externalTools.descriptors,
510
533
  externalToolExecutors: externalTools.executors,
@@ -540,13 +563,33 @@ export class WSSessionManager {
540
563
  },
541
564
  );
542
565
 
543
- if (this.db) {
566
+ if (this.db && databaseCreated) {
544
567
  try {
545
568
  dbUpdateSession(this.db, sessionId, {
546
569
  metadata: this._serializeSessionMetadata(session),
547
570
  });
548
- } catch (_err) {
549
- // Non-critical
571
+ } catch (error) {
572
+ if (requireDurable) {
573
+ const cleanupErrors = [];
574
+ try {
575
+ this._discardSessionResources(session);
576
+ } catch (cleanupError) {
577
+ cleanupErrors.push(cleanupError);
578
+ }
579
+ try {
580
+ dbDeleteSession(this.db, sessionId);
581
+ } catch (cleanupError) {
582
+ cleanupErrors.push(cleanupError);
583
+ }
584
+ error.code ||= "CC_WS_SESSION_PERSISTENCE_FAILED";
585
+ if (cleanupErrors.length > 0) {
586
+ throw new AggregateError(
587
+ [error, ...cleanupErrors],
588
+ "Durable WebSocket session creation and rollback both failed",
589
+ );
590
+ }
591
+ throw error;
592
+ }
550
593
  }
551
594
  }
552
595
 
@@ -556,6 +599,57 @@ export class WSSessionManager {
556
599
  return { sessionId };
557
600
  }
558
601
 
602
+ /**
603
+ * Remove a newly-created session that was never exposed to a client.
604
+ * Unlike closeSession(), this compensates the database insert instead of
605
+ * persisting a misleading closed session after bootstrap failed.
606
+ */
607
+ rollbackSessionCreation(sessionId) {
608
+ const session = this.sessions.get(sessionId);
609
+ const cleanupErrors = [];
610
+ if (session) {
611
+ try {
612
+ this._discardSessionResources(session);
613
+ } catch (error) {
614
+ cleanupErrors.push(error);
615
+ }
616
+ this.sessions.delete(sessionId);
617
+ }
618
+ if (this.db) {
619
+ try {
620
+ dbDeleteSession(this.db, sessionId);
621
+ } catch (error) {
622
+ cleanupErrors.push(error);
623
+ }
624
+ }
625
+ if (cleanupErrors.length > 0) {
626
+ throw new AggregateError(
627
+ cleanupErrors,
628
+ `WebSocket session creation rollback failed: ${sessionId}`,
629
+ );
630
+ }
631
+ return Boolean(session);
632
+ }
633
+
634
+ _discardSessionResources(session) {
635
+ if (!session) return;
636
+ if (
637
+ session._releaseHooksV2WorkspaceBindingOnClose === true &&
638
+ session.hooksV2WorkspaceBindingId
639
+ ) {
640
+ releaseRegisteredHostHooksV2Workspace(session.hooksV2WorkspaceBindingId);
641
+ }
642
+ if (typeof session._planPersistenceCleanup === "function") {
643
+ session._planPersistenceCleanup();
644
+ }
645
+ session.planManager?.removeAllListeners?.();
646
+ if (session.worktree?.path && session.baseProjectRoot) {
647
+ removeWorktree(session.baseProjectRoot, session.worktree.path, {
648
+ deleteBranch: true,
649
+ });
650
+ }
651
+ }
652
+
559
653
  /**
560
654
  * Resume an existing session from DB.
561
655
  *
@@ -645,6 +739,7 @@ export class WSSessionManager {
645
739
  metadata.enabledToolNames,
646
740
  ),
647
741
  hostManagedToolPolicy: metadata.hostManagedToolPolicy || null,
742
+ sessionBudgetRoot: metadata.sessionBudgetRoot || null,
648
743
  externalToolDefinitions: externalTools.definitions,
649
744
  externalToolDescriptors: externalTools.descriptors,
650
745
  externalToolExecutors: externalTools.executors,
@@ -1545,6 +1640,7 @@ export class WSSessionManager {
1545
1640
  baseProjectRoot: session.baseProjectRoot || session.projectRoot || null,
1546
1641
  baseUrl: session.baseUrl || null,
1547
1642
  hostManagedToolPolicy: session.hostManagedToolPolicy || null,
1643
+ sessionBudgetRoot: session.sessionBudgetRoot || null,
1548
1644
  enabledToolNames: session.enabledToolNames || [],
1549
1645
  canonicalHostSystemPrefix: Array.isArray(
1550
1646
  session._canonicalHostSystemPrefix,