chainlesschain 0.162.198 → 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 (195) 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-CSN-D2xb.js → AIOps-DcQ0bWKy.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-CBSMlGzv.js → ActionButton-GecwfFPt.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-3yr3Nt2t.js → Analytics-CD6iSt9v.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-1b-o3lMH.js → AppLayout-CDEtuEYf.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-Dr8rJi17.js → Artifacts-DS3jAjeW.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-2V689HC2.js → Audit-CcSJA048.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-DIxkNrD_.js → BackgroundAgents-DRo9Morb.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-Pg6BcwrE.js → Backup-Vw5MrWft.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-CLUpqyfH.js → BaseInput-DNHIu823.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-qVS8rD4G.js → Chat-DvAsO8-b.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-ti49X12Q.js → ChatBubbleRenderer-9aDuObay.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-Bs9AaEay.js → Checkbox-CKCa7tvn.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-Dvc1mq8q.js → Codegen-CIFvZdaT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-C4_biO7A.js → Col-C79yz7bK.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-al25XQUV.js → Community-DVvZskLJ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-D-ZDB8M1.js → Compact-oaew61hV.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-6lIfDz1W.js → Compliance-DqA7wmRx.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DdI48XMJ.js → Cowork-B-qGadxa.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-DF5HZd2g.js → Cron-CUlCl4sS.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-DuxeSvt3.js → Crosschain-CNOlr9DU.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-B9yHkUTd.js → DID-DJqFCT_3.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-Z5EuI3IM.js → Dashboard-CznGpphf.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-vuRfO3dY.js → Dropdown-_cJw218E.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-QFLCc1uL.js → EmailListRenderer-Dr-1BZ9C.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BZ7EXkFJ.js → FamilyGuardDashboard-Dxf4xtSk.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-DycIEg2s.js → Federation-DOiG3i_1.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-Ba0H_7yG.js → FormItemContext-BzbMcFB9.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer--JgSTK-C.js → GenericCardRenderer-VbBrYttP.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-DEvTbldE.js → Git-CLG8E6ED.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-BJIljy18.js → Governance-LGJQfk_q.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-CVyW3G3q.js → Inference-DlQKx9lF.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-v8-borge.js → KnowledgeGraph-DU8vEouV.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BCdS7gGo.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-AN6tGA2B.js → Marketplace-C_xd-zZI.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-DGViXASN.js → McpTools-q-YktphS.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-Bpvmx9X4.js → Memory-Cq9OhDin.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-CTP-DOTC.js → MobileBridge-B03_R98T.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-C8qfktxn.js → MobileProjects-DZrk-qmF.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-ndYr0M1s.js → Mtc-BidCKW28.js} +2 -2
  44. package/src/assets/web-panel/assets/{MtcAudit-Ci0-YK7W.js → MtcAudit-r-iaMVM7.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-BehSTFW7.js → Multisig-CgTUFvff.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-B91_YBLv.js → NLProgramming-B7NvzfAV.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-BQlJ0-gq.js → Notes-80CWNqJI.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-CDgYMjYK.js → NotificationSettings-0H6xdiY3.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-BJkSlKeH.js → OrderTableRenderer-CKP4AFWy.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-B-b3cWCp.js → Organization-B8sxCV_h.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-CILUL4nL.js → Overflow-CW9JBBhQ.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-BTCFBkF0.js → P2P-bdSQV7oZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-BAR6AUx5.js → PdhVaultBrowser-DQad42nW.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-RRf6HbWJ.js → Permissions-CueexxTR.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-BGTMiICa.js → PersonalDataHub-fqxflhR8.js} +6 -6
  56. package/src/assets/web-panel/assets/{Pipeline-945qY9YF.js → Pipeline-KFwwnIMC.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-DPZwI6lJ.js → Privacy-ClnD9buY.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-BPH2NRKc.js → ProjectInit-CZEz6sm3.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-BZCRNCvb.js → ProjectSettings-B6NzpmOI.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-BTI__tBt.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-N6rbqt1r.js → QrScannerModal-BzuOBbqB.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-DrwjPS7M.js → QuickAsk-CmNO9qSj.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-Cog9Wd99.js → Recommend-VJNj3_NF.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DC9pynjM.js → RemoteSession-CrbdbSBF.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-D4th4gKS.js → Reputation-DTyMd45j.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-ogiWR6v9.js → Row-DHrQ-Icz.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-uYlk-2Oj.js → RssFeed-DqWxY9c5.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-DYzzwvtM.js → Search-ctF1Ks3J.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-Da7YOyX6.js → Security-BrQz8uo5.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-CLI1VM1X.js → Services-D4scVFar.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-DK77dzDC.js → Skeleton-oUScaOjv.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-C_z7Lmhl.js → Skills-CL-y0eLt.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-D6413Q8L.js → Sla-DQExhwXj.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-C07ydbj2.js → SpeechSettings-iNp_I7kN.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-DGOm2W22.js → SyncSettings-CTDlMo0A.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-Cr_PVyLM.js → Tasks-4kb8l3sc.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-BhL2mZ0t.js → Templates-BXcL9oXW.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-CPk5oyUr.js → Tenant-DLE8eS0D.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-ClhqUsqV.js → Terminal-DGpUZPPK.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BCC7MV5a.js → TimelineRenderer-C1pWXGPC.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-DD1Jt4oO.js → Tokens-BYfbfqhZ.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BCydFvN2.js → Trigger-K9YfLOCw.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-DDHT1noa.js → Trust-Ctkq77Hy.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-CWPZC7oI.js → UkeySign-C6_CSe9J.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-9Ea0nJR0.js → VideoEditing-Be3rpOWs.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-BjxhLxTf.js → Wallet-D0UIplxk.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-C6uCye67.js → WebAuthn-CF9bM9I3.js} +3 -3
  89. package/src/assets/web-panel/assets/{WorkflowEditor-d2g4reYo.js → WorkflowEditor-D0H1XVPY.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-frTc6ScX.js → chat-Cgzzbe0o.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-B5K6lG3v.js → colors-BwNxEVS0.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-Cdn46r8D.js → compact-item-D60Z4Hhs.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-DU1jqVQi.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-DdNH-aW_.js → hasIn-CRj2SvGO.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DM5FmPZU.js → index--s45_1cb.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-D4l82Me1.js → index--tquQ2ZG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CQorP1Ng.js → index-1T1rhokn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CHmkkdfT.js → index-B-M82_rx.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CGiwfwB9.js → index-BE_el1Zs.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BwlIxAmX.js → index-BhRkR7IO.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cj6P9Nst.js → index-Bo0y96-K.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BoFak7le.js → index-CJgu5JUK.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dca_i7H8.js → index-CLWwdebO.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BVLL-mq_.js → index-CNAK5QG_.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-LaivuXyF.js → index-CQjJjPRD.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-t_OBzIP4.js → index-CTDbAfae.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BcGwGIaR.js → index-CVI_9sRK.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-D23xGYjM.js → index-CY14oMQK.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-VDM2KKo9.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-32ajXmTb.js → index-CqWpxMqx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-PjAS0sIZ.js → index-CvB8lYUR.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-7ehytj1K.js → index-D51XwtCY.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DtBCCkC4.js → index-D6HvK_1O.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Be4OtL-N.js → index-D9jVIwLJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CUoNAmE8.js → index-DA4tE98u.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-cYaWV4Mq.js → index-DBwOtXw0.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-MM4Zz2OE.js → index-DKdNzTIb.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DIVyTmZ3.js → index-DRySdmvN.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CtRPi4YC.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-kVO_MDm-.js → index-DYjs7jAP.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D4miNkWv.js → index-DbROzMBp.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-yZRuZpwK.js → index-DhFtk5PR.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BXKhhx9V.js → index-Di2AbMLg.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BC4eGkar.js → index-DvmocQsM.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-NmEXVjzt.js → index-Dw-o0zEF.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DRzBBdQ0.js → index-DysPoP38.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-HjCvXHf5.js → index-LX9pJxm_.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-gg3QPC3i.js → index-OI_fXWx0.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-mgU3Q-lN.js → index-eDTOX8Xj.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-D1qIC4GR.js → index-gWKDsTzF.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-DftZuyA7.js → index-q_GDUVAA.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-DwLTRNdo.js → initDefaultProps-C61kZqEd.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-BchZDXnh.js → motion-DZnT5U1L.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-e8M-zZFl.js → move-L8wzwbel.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-Ci40fQb3.js → mtc-parser-DLdYSejc.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-C7110_f2.js → omit-D53cbS59.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-skteMgnM.js → pickAttrs-Bbc5HPtu.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-MGZ-lh2B.js → placementArrow-BkvmO860.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-N4Sd7uy-.js → responsiveObserve-u2y1ryS1.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-C7HDGRaa.js → slide-BBqj7svU.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-BhRexlgy.js → statusUtils-GN0mqu2d.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-ChEoQkN2.js → styleChecker-BvwPKoBT.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-CDbG1s2Y.js → useFlexGapSupport-CCt8tnfY.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-CUIx0tcJ.js → useFs-DXAxl2SV.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-bkO7L7ec.js → usePersonalDataHub-DihlR2dj.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-B_ZvABIG.js → vnode-D7BOX2gi.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-D5shjpS8.js → zoom-CVbO-nL7.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +1 -1
  153. package/src/commands/mcp.js +119 -0
  154. package/src/commands/session-show.js +8 -10
  155. package/src/commands/session.js +60 -52
  156. package/src/data/changelog.json +27 -1
  157. package/src/gateways/ws/session-protocol.js +14 -4
  158. package/src/gateways/ws/ws-agent-handler.js +31 -0
  159. package/src/harness/jsonl-session-store.js +1147 -130
  160. package/src/harness/mcp-client.js +44 -11
  161. package/src/harness/session-index.js +80 -80
  162. package/src/harness/session-list-index.js +147 -2
  163. package/src/lib/background-interaction-journal.js +2 -2
  164. package/src/lib/command-lifecycle-report.js +516 -0
  165. package/src/lib/mcp-stdio-environment.js +48 -0
  166. package/src/lib/mcp-stdio-executable-identity.js +552 -17
  167. package/src/lib/mcp-stdio-package-materialization.js +1424 -0
  168. package/src/lib/packer/native-update-state.js +979 -1
  169. package/src/lib/packer/pack-update-applier.js +1050 -125
  170. package/src/lib/packer/pkg-config-generator.js +1 -0
  171. package/src/lib/paths.js +53 -0
  172. package/src/lib/process-execution-broker/index.js +180 -37
  173. package/src/lib/process-execution-broker/platform-sandbox.js +291 -24
  174. package/src/lib/recent-session.js +17 -17
  175. package/src/lib/secure-file-identity.js +47 -0
  176. package/src/lib/secure-fs.js +21 -2
  177. package/src/lib/session-anti-rollback-anchor.js +766 -0
  178. package/src/lib/session-host-lease.js +492 -0
  179. package/src/lib/session-persistence-failure.js +109 -0
  180. package/src/lib/session-picker.js +26 -3
  181. package/src/lib/session-search.js +25 -11
  182. package/src/lib/side-effect-ledger-store.js +2 -2
  183. package/src/lib/skill-execution-authority.js +13 -9
  184. package/src/lib/turn-binding-store.js +2 -2
  185. package/src/repl/agent-repl.js +309 -39
  186. package/src/runtime/agent-core.js +88 -5
  187. package/src/runtime/cleanup-deadline.js +143 -0
  188. package/src/runtime/headless-runner.js +357 -139
  189. package/src/runtime/headless-stream.js +207 -28
  190. package/src/runtime/output-backpressure.js +53 -0
  191. package/src/assets/web-panel/assets/MarkdownRenderer-CU-JB2Ns.js +0 -1
  192. package/src/assets/web-panel/assets/Providers-DDCf_nIw.js +0 -1
  193. package/src/assets/web-panel/assets/devWarning-CP-jEUyw.js +0 -1
  194. package/src/assets/web-panel/assets/index-CzrCHrFy.js +0 -1
  195. package/src/assets/web-panel/assets/index-OJsGskZw.js +0 -1
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import {
14
- readEvents,
14
+ readVerifiedEvents,
15
15
  listJsonlSessions,
16
16
  } from "../harness/jsonl-session-store.js";
17
17
 
@@ -54,7 +54,7 @@ export class SessionSearchIndex {
54
54
  * @returns {Array<{role: string, content: string, timestamp: number}>}
55
55
  */
56
56
  extractMessages(sessionId) {
57
- const events = readEvents(sessionId);
57
+ const events = readVerifiedEvents(sessionId);
58
58
  const messages = [];
59
59
 
60
60
  for (const event of events) {
@@ -86,13 +86,22 @@ export class SessionSearchIndex {
86
86
  if (!this._db) return { indexed: 0 };
87
87
  if (!this._initialized) this.ensureTables();
88
88
 
89
- const messages = this.extractMessages(sessionId);
90
- if (messages.length === 0) return { indexed: 0 };
89
+ const deleteExisting = () =>
90
+ this._db.exec(
91
+ `DELETE FROM session_fts WHERE session_id = '${sessionId.replace(/'/g, "''")}'`,
92
+ );
93
+ let messages;
94
+ try {
95
+ messages = this.extractMessages(sessionId);
96
+ } catch (error) {
97
+ deleteExisting();
98
+ throw error;
99
+ }
91
100
 
92
- // Remove existing entries for this session (idempotent re-index)
93
- this._db.exec(
94
- `DELETE FROM session_fts WHERE session_id = '${sessionId.replace(/'/g, "''")}'`,
95
- );
101
+ // Remove existing entries before publishing the newly verified sample.
102
+ // Empty and damaged sessions must not leave older searchable content.
103
+ deleteExisting();
104
+ if (messages.length === 0) return { indexed: 0 };
96
105
 
97
106
  const insert = this._db.prepare(
98
107
  `INSERT INTO session_fts (session_id, role, content, timestamp) VALUES (?, ?, ?, ?)`,
@@ -164,13 +173,18 @@ export class SessionSearchIndex {
164
173
 
165
174
  const sessions = listJsonlSessions({ limit: 10000 });
166
175
  let totalMessages = 0;
176
+ let rejected = 0;
167
177
 
168
178
  for (const session of sessions) {
169
- const result = this.indexSession(session.id);
170
- totalMessages += result.indexed;
179
+ try {
180
+ const result = this.indexSession(session.id);
181
+ totalMessages += result.indexed;
182
+ } catch {
183
+ rejected += 1;
184
+ }
171
185
  }
172
186
 
173
- return { sessions: sessions.length, messages: totalMessages };
187
+ return { sessions: sessions.length, messages: totalMessages, rejected };
174
188
  }
175
189
 
176
190
  /**
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import {
13
- appendEvent as storeAppendEvent,
13
+ appendAuthorityEvent as storeAppendAuthorityEvent,
14
14
  readEvents as storeReadEvents,
15
15
  findLatestEvent as storeFindLatestEvent,
16
16
  } from "../harness/jsonl-session-store.js";
@@ -23,7 +23,7 @@ import {
23
23
  export const SIDE_EFFECT_LEDGER_EVENT = "side_effect_ledger";
24
24
 
25
25
  export const _deps = {
26
- appendEvent: storeAppendEvent,
26
+ appendEvent: storeAppendAuthorityEvent,
27
27
  readEvents: storeReadEvents,
28
28
  findLatestEvent: storeFindLatestEvent,
29
29
  };
@@ -1,20 +1,21 @@
1
1
  /**
2
2
  * Durable, same-machine Skill execution authority.
3
3
  *
4
- * Every CLI process that shares CHAINLESSCHAIN_HOME observes the same
5
- * monotonically increasing generation. Revocation is serialized by the
6
- * canonical cross-process state lock and persisted with an atomic rename
7
- * before any caller reports success. Active leases poll while they are alive,
8
- * and every authority-sensitive transition also re-reads the store as a hard
9
- * fence. Corruption, rollback observed by a live host, and lock/write failures
10
- * fail closed.
4
+ * Every CLI process for the same OS user observes the same monotonically
5
+ * increasing generation, even when processes use different
6
+ * CHAINLESSCHAIN_HOME roots. Revocation is serialized by the canonical
7
+ * cross-process state lock in independent OS user security state and persisted
8
+ * with an atomic rename before any caller reports success. Active leases poll
9
+ * while they are alive, and every authority-sensitive transition also re-reads
10
+ * the store as a hard fence. Corruption, rollback observed by a live host, and
11
+ * lock/write failures fail closed.
11
12
  */
12
13
 
13
14
  import fs from "node:fs";
14
15
  import path from "node:path";
15
16
  import { randomUUID } from "node:crypto";
16
17
  import { threadId } from "node:worker_threads";
17
- import { getStatePath } from "./paths.js";
18
+ import { getMachineSecurityAnchorDir } from "./paths.js";
18
19
  import {
19
20
  mutateSecurityStore,
20
21
  readSecurityStore,
@@ -172,7 +173,10 @@ function safeMessage(options) {
172
173
  }
173
174
 
174
175
  export function getSkillExecutionAuthorityPath() {
175
- return path.join(getStatePath(), "skill-execution-authority.json");
176
+ return path.join(
177
+ getMachineSecurityAnchorDir(),
178
+ "skill-execution-authority-v1.json",
179
+ );
176
180
  }
177
181
 
178
182
  export class DurableSkillExecutionAuthority {
@@ -16,7 +16,7 @@
16
16
  */
17
17
 
18
18
  import {
19
- appendEvent as storeAppendEvent,
19
+ appendAuthorityEvent as storeAppendAuthorityEvent,
20
20
  readEvents as storeReadEvents,
21
21
  findLatestEvent as storeFindLatestEvent,
22
22
  } from "../harness/jsonl-session-store.js";
@@ -33,7 +33,7 @@ export const TURN_BINDING_TIMELINE_EVENT = "checkpoint_timeline_commit";
33
33
 
34
34
  // Injected so tests can drive persistence without touching the real sessions dir.
35
35
  export const _deps = {
36
- appendEvent: storeAppendEvent,
36
+ appendEvent: storeAppendAuthorityEvent,
37
37
  readEvents: storeReadEvents,
38
38
  findLatestEvent: storeFindLatestEvent,
39
39
  };
@@ -69,6 +69,17 @@ import {
69
69
  isVerifiedSessionHostSnapshot,
70
70
  readSessionHostResumeState,
71
71
  } from "../lib/session-host-snapshot.js";
72
+ import { acquireSessionHostLease } from "../lib/session-host-lease.js";
73
+ import {
74
+ SideEffectLedger,
75
+ classifyToolSideEffect,
76
+ reconcileSideEffects,
77
+ } from "../lib/side-effect-ledger.js";
78
+ import {
79
+ loadSideEffectLedger,
80
+ persistSideEffectLedger,
81
+ } from "../lib/side-effect-ledger-store.js";
82
+ import { operationIdempotencyKey } from "../lib/idempotency.js";
72
83
  import {
73
84
  preserveDurableSystemMessageProvenance,
74
85
  projectCanonicalResumeMessages,
@@ -359,6 +370,9 @@ export async function agentLoop(messages, options) {
359
370
  // The tool-result event carries no args; remember the last tool-executing
360
371
  // pair so a recorded denial can show what was attempted (/permissions denials).
361
372
  let _lastExec = null;
373
+ // The core loop runs tools serially. A classified non-MCP side effect stays
374
+ // live from its yielded `tool-executing` boundary until the matching result.
375
+ let _currentSideEffectOpId = null;
362
376
  for await (const event of runCoreLoop(messages, {
363
377
  ...options,
364
378
  // FORCE in-loop compaction off — the REPL runs its OWN post-turn compaction
@@ -407,12 +421,55 @@ export async function agentLoop(messages, options) {
407
421
  args: event.args,
408
422
  startedAt: options.now ? options.now() : Date.now(),
409
423
  };
424
+ _currentSideEffectOpId = null;
425
+ if (options.sideEffects) {
426
+ const sideEffect = classifyToolSideEffect(event.tool, event.args);
427
+ if (sideEffect) {
428
+ const opId = options.sideEffects.nextOpId();
429
+ _currentSideEffectOpId = opId;
430
+ options.sideEffects.ledger
431
+ .prepare(opId, {
432
+ kind: sideEffect.kind,
433
+ key: sideEffect.key,
434
+ meta: {
435
+ tool: event.tool,
436
+ idempotencyKey: operationIdempotencyKey({
437
+ tool: event.tool,
438
+ args: event.args,
439
+ }),
440
+ },
441
+ })
442
+ .start(opId);
443
+ // The generator has yielded before executing the tool. Persist the
444
+ // started marker and recheck the host lease before resuming it.
445
+ options.sideEffects.persist();
446
+ options.sideEffects.assert?.();
447
+ }
448
+ }
410
449
  writeOut(
411
450
  chalk.gray(
412
451
  ` [${event.tool}] ${formatToolArgs(event.tool, event.args)}\n`,
413
452
  ),
414
453
  );
415
454
  } else if (event.type === "tool-result") {
455
+ if (options.sideEffects && _currentSideEffectOpId) {
456
+ const sideEffectError = event.error || event.result?.error || null;
457
+ if (event.permission_decision) {
458
+ options.sideEffects.ledger.annotate(_currentSideEffectOpId, {
459
+ permissionDecision: event.permission_decision,
460
+ });
461
+ }
462
+ if (sideEffectError) {
463
+ options.sideEffects.ledger.fail(
464
+ _currentSideEffectOpId,
465
+ String(sideEffectError).slice(0, 200),
466
+ );
467
+ } else {
468
+ options.sideEffects.ledger.commit(_currentSideEffectOpId);
469
+ }
470
+ options.sideEffects.persist();
471
+ _currentSideEffectOpId = null;
472
+ }
416
473
  const durationMs =
417
474
  event.result?.toolTelemetryRecord?.durationMs ??
418
475
  (_lastExec?.tool === event.tool
@@ -2058,6 +2115,21 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2058
2115
  "CC_REPL_STARTUP_DEPENDENCY_INVALID",
2059
2116
  );
2060
2117
  }
2118
+ const acquireHostLease = dependencyDataFunction(
2119
+ startupDependencies,
2120
+ "acquireSessionHostLease",
2121
+ null,
2122
+ );
2123
+ const leaseScope = options._sessionHostLeaseScope || null;
2124
+ const leaseSessionId = startupCandidate?.sessionId || options.sessionId;
2125
+ if (
2126
+ leaseScope &&
2127
+ !leaseScope.lease &&
2128
+ leaseSessionId &&
2129
+ typeof acquireHostLease === "function"
2130
+ ) {
2131
+ leaseScope.lease = acquireHostLease(leaseSessionId, { hostKind: "repl" });
2132
+ }
2061
2133
  const trustedWorkspaceRoot = readCwd();
2062
2134
  return enterWorkspace(trustedWorkspaceRoot, () =>
2063
2135
  startWorkspace(options, startupAdmission),
@@ -2066,13 +2138,26 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2066
2138
 
2067
2139
  /** Start the agentic REPL with non-overridable production bindings. */
2068
2140
  export async function startAgentRepl(options = {}) {
2069
- return runReplStartupBoundary(options, {
2070
- prepareReplStartupResume,
2071
- refuseReplStartupResume,
2072
- cwd: () => process.cwd(),
2073
- runWithHostHooksV2Workspace,
2074
- startAgentReplInWorkspace,
2075
- });
2141
+ const sessionHostLeaseScope = { lease: null };
2142
+ const runtimeOptions = {
2143
+ ...options,
2144
+ _sessionHostLeaseScope: sessionHostLeaseScope,
2145
+ };
2146
+ try {
2147
+ const result = await runReplStartupBoundary(runtimeOptions, {
2148
+ prepareReplStartupResume,
2149
+ refuseReplStartupResume,
2150
+ acquireSessionHostLease,
2151
+ cwd: () => process.cwd(),
2152
+ runWithHostHooksV2Workspace,
2153
+ startAgentReplInWorkspace,
2154
+ });
2155
+ if (result?.started === false) sessionHostLeaseScope.lease?.release?.();
2156
+ return result;
2157
+ } catch (error) {
2158
+ sessionHostLeaseScope.lease?.release?.();
2159
+ throw error;
2160
+ }
2076
2161
  }
2077
2162
 
2078
2163
  async function startAgentReplInWorkspace(options = {}, startupAdmission) {
@@ -2147,6 +2232,45 @@ async function startAgentReplInWorkspaceOwned(
2147
2232
  }
2148
2233
  }
2149
2234
  };
2235
+ const _sessionHostLeaseScope = options._sessionHostLeaseScope || null;
2236
+ let _sessionHostLeaseAbortListener = null;
2237
+ let _sessionHostLeaseAbortSignal = null;
2238
+ const _detachSessionHostLease = () => {
2239
+ if (_sessionHostLeaseAbortListener && _sessionHostLeaseAbortSignal) {
2240
+ _sessionHostLeaseAbortSignal.removeEventListener(
2241
+ "abort",
2242
+ _sessionHostLeaseAbortListener,
2243
+ );
2244
+ }
2245
+ _sessionHostLeaseAbortListener = null;
2246
+ _sessionHostLeaseAbortSignal = null;
2247
+ };
2248
+ const _attachSessionHostLease = () => {
2249
+ const signal = _sessionHostLeaseScope?.lease?.signal;
2250
+ if (!signal || _sessionHostLeaseAbortListener) return;
2251
+ _sessionHostLeaseAbortListener = () =>
2252
+ _requestReplOutputClose(
2253
+ signal.reason ||
2254
+ Object.assign(new Error("Session host lease was lost"), {
2255
+ code: "CC_SESSION_HOST_LEASE_FENCED",
2256
+ }),
2257
+ );
2258
+ _sessionHostLeaseAbortSignal = signal;
2259
+ signal.addEventListener("abort", _sessionHostLeaseAbortListener, {
2260
+ once: true,
2261
+ });
2262
+ if (signal.aborted) _sessionHostLeaseAbortListener();
2263
+ };
2264
+ const _replaceSessionHostLease = (lease) => {
2265
+ if (!_sessionHostLeaseScope) return null;
2266
+ const previous = _sessionHostLeaseScope.lease;
2267
+ if (previous === lease) return previous;
2268
+ _detachSessionHostLease();
2269
+ _sessionHostLeaseScope.lease = lease;
2270
+ _attachSessionHostLease();
2271
+ return previous;
2272
+ };
2273
+ _attachSessionHostLease();
2150
2274
  const _replOutputFlow = outputScope.flow;
2151
2275
  outputScope.setFailureHandler(_requestReplOutputClose);
2152
2276
  const _disposeReplPipeSafety = installPipeSafety(undefined, () =>
@@ -2704,10 +2828,20 @@ async function startAgentReplInWorkspaceOwned(
2704
2828
  };
2705
2829
  if (useJsonl) {
2706
2830
  try {
2707
- sessionId = jsonlStartSession(null, meta);
2831
+ sessionId = jsonlStartSession(options.sessionId || null, meta);
2708
2832
  } catch (_err) {
2709
2833
  // Non-critical
2710
2834
  }
2835
+ if (
2836
+ sessionId &&
2837
+ _sessionHostLeaseScope &&
2838
+ !_sessionHostLeaseScope.lease
2839
+ ) {
2840
+ _sessionHostLeaseScope.lease = acquireSessionHostLease(sessionId, {
2841
+ hostKind: "repl",
2842
+ });
2843
+ _attachSessionHostLease();
2844
+ }
2711
2845
  } else if (db) {
2712
2846
  try {
2713
2847
  const session = createSession(db, meta);
@@ -2845,6 +2979,79 @@ async function startAgentReplInWorkspaceOwned(
2845
2979
  let _replBaseSystem = _buildReplBaseSystem();
2846
2980
  let _activeOutputStyle = null; // { name, body }
2847
2981
  const messages = [{ role: "system", content: _replBaseSystem }];
2982
+ let _replSideEffects = null;
2983
+ const _prepareReplSideEffects = (
2984
+ targetSessionId,
2985
+ { recover = false } = {},
2986
+ ) => {
2987
+ if (!useJsonl || !targetSessionId) {
2988
+ return Object.freeze({
2989
+ sideEffects: null,
2990
+ noticeMessage: null,
2991
+ warning: null,
2992
+ });
2993
+ }
2994
+ let ledger;
2995
+ let loadError = null;
2996
+ try {
2997
+ ledger = loadSideEffectLedger(targetSessionId);
2998
+ } catch (error) {
2999
+ ledger = new SideEffectLedger();
3000
+ loadError = error;
3001
+ }
3002
+ const runNonce = `${Date.now()}:${randomUUID()}`;
3003
+ let sequence = 0;
3004
+ let noticeMessage = null;
3005
+ let warning = null;
3006
+ if (recover && !loadError) {
3007
+ const plan = reconcileSideEffects(ledger);
3008
+ if (plan.inspect.length > 0) {
3009
+ const lines = plan.plans
3010
+ .filter((entry) => entry.action === "inspect")
3011
+ .map((entry) => {
3012
+ const operation = ledger.get(entry.opId);
3013
+ const key = operation?.key ? ` (${operation.key})` : "";
3014
+ return ` - [${operation?.kind || "unknown"}]${key} - ${entry.reason}`;
3015
+ });
3016
+ noticeMessage = Object.freeze({
3017
+ role: "system",
3018
+ content:
3019
+ "Recovery notice: the previous REPL host stopped while irreversible operations were in flight. Their outcome is UNKNOWN. Do not blindly repeat them; verify whether each operation already took effect and ask the user when uncertain:\n" +
3020
+ lines.join("\n"),
3021
+ });
3022
+ warning = `Recovery: ${plan.inspect.length} interrupted non-MCP side effect(s) require verification before replay.`;
3023
+ }
3024
+ } else if (recover && loadError) {
3025
+ noticeMessage = Object.freeze({
3026
+ role: "system",
3027
+ content:
3028
+ "Recovery notice: the durable non-MCP side-effect ledger is unavailable. Read-only work may continue, but irreversible tools are blocked until recovery authority is restored.",
3029
+ });
3030
+ warning = `Recovery: non-MCP side-effect ledger is unavailable for ${targetSessionId}; irreversible tools are fail-closed.`;
3031
+ }
3032
+ const sideEffects = Object.freeze({
3033
+ ledger,
3034
+ nextOpId: () => `${runNonce}:${sequence++}`,
3035
+ persist: () => {
3036
+ if (loadError) throw loadError;
3037
+ const persisted = persistSideEffectLedger(targetSessionId, ledger);
3038
+ if (persisted === false) {
3039
+ const error = new Error(
3040
+ `Side-effect ledger persistence was rejected for ${targetSessionId}`,
3041
+ );
3042
+ error.code = "SIDE_EFFECT_LEDGER_PERSIST_FAILED";
3043
+ throw error;
3044
+ }
3045
+ return persisted;
3046
+ },
3047
+ assert: () => _sessionHostLeaseScope?.lease?.assert?.(),
3048
+ });
3049
+ return Object.freeze({ sideEffects, noticeMessage, warning });
3050
+ };
3051
+ const _startupReplSideEffects = _prepareReplSideEffects(sessionId, {
3052
+ recover: Boolean(options.sessionId && _startupJsonlResume?.ok),
3053
+ });
3054
+ _replSideEffects = _startupReplSideEffects.sideEffects;
2848
3055
  let _mcpLedgerRecovery = null;
2849
3056
  let _mcpLedgerRecoveryError = null;
2850
3057
  const _prepareJsonlResumeCandidate = (targetSessionId) =>
@@ -3362,6 +3569,12 @@ async function startAgentReplInWorkspaceOwned(
3362
3569
  throw error;
3363
3570
  }
3364
3571
  messages.push(...prepared.canonicalSystemMessages);
3572
+ if (_startupReplSideEffects.noticeMessage) {
3573
+ messages.push(_startupReplSideEffects.noticeMessage);
3574
+ }
3575
+ if (_startupReplSideEffects.warning) {
3576
+ logger.warn(_startupReplSideEffects.warning);
3577
+ }
3365
3578
  _commitMcpRecoveryCandidate(messages, prepared.mcp);
3366
3579
  if (prepared.replayMessages.length > 0) {
3367
3580
  messages.push(...prepared.conversationMessages);
@@ -5835,39 +6048,87 @@ async function startAgentReplInWorkspaceOwned(
5835
6048
  const resumeId = sessionArg.slice(7).trim();
5836
6049
  try {
5837
6050
  if (useJsonl) {
5838
- const prepared = _prepareJsonlResumeCandidate(resumeId);
5839
- if (!prepared.ok) throw prepared.error;
5840
- const preparedMcpRuntime = _prepareMcpHostRuntime(
5841
- prepared.sessionId,
5842
- prepared.mcp,
5843
- );
5844
- const preparedMcpCommit = _prepareMcpRecoveryCommit(prepared.mcp);
5845
- const hostSystemMessages = _refreshHostSystemMessages();
5846
- const previousState = _captureResumeState();
5847
- const preparedState = Object.freeze({
5848
- sessionId: prepared.sessionId,
5849
- hostSystemMessages,
5850
- canonicalSystemMessages: prepared.canonicalSystemMessages,
5851
- conversationMessages: prepared.conversationMessages,
5852
- mcpCommit: preparedMcpCommit,
5853
- mcpRuntime: preparedMcpRuntime,
5854
- sanitizeRolesNextTurn:
5855
- prepared.conversationMessages.at(-1)?.role === "user",
5856
- logMessage: `Resumed JSONL session ${prepared.sessionId} (${prepared.rebuiltMessages.length} messages)`,
5857
- });
5858
- const committed = commitPreparedReplJsonlResume(
5859
- prepared,
5860
- () => _applyPreparedResumeState(preparedState),
5861
- () => _restoreResumeState(previousState),
5862
- );
5863
- if (!committed) {
5864
- const error = new Error(
5865
- "Prepared JSONL session resume was not committed",
6051
+ const initialCandidate = _prepareJsonlResumeCandidate(resumeId);
6052
+ if (!initialCandidate.ok) throw initialCandidate.error;
6053
+ const currentLease = _sessionHostLeaseScope?.lease || null;
6054
+ const reuseCurrentLease = initialCandidate.sessionId === sessionId;
6055
+ let targetLease = reuseCurrentLease ? currentLease : null;
6056
+ let adoptedTargetLease = reuseCurrentLease && Boolean(currentLease);
6057
+ if (!targetLease) {
6058
+ targetLease = acquireSessionHostLease(
6059
+ initialCandidate.sessionId,
6060
+ {
6061
+ hostKind: "repl",
6062
+ },
5866
6063
  );
5867
- error.code = "CC_REPL_SESSION_RESUME_NOT_COMMITTED";
5868
- throw error;
5869
6064
  }
5870
- _replCompactPersistence.replace(prepared.rebuiltMessages);
6065
+ let prepared;
6066
+ try {
6067
+ // Re-sample after acquiring the target's write authority. The
6068
+ // first sample resolves/refuses the request; this one is the
6069
+ // exact snapshot committed into the live REPL.
6070
+ prepared = _prepareJsonlResumeCandidate(
6071
+ initialCandidate.sessionId,
6072
+ );
6073
+ if (!prepared.ok) throw prepared.error;
6074
+ const preparedSideEffects = _prepareReplSideEffects(
6075
+ prepared.sessionId,
6076
+ { recover: true },
6077
+ );
6078
+ const preparedMcpRuntime = _prepareMcpHostRuntime(
6079
+ prepared.sessionId,
6080
+ prepared.mcp,
6081
+ );
6082
+ const preparedMcpCommit = _prepareMcpRecoveryCommit(prepared.mcp);
6083
+ const hostSystemMessages = _refreshHostSystemMessages();
6084
+ const previousState = _captureResumeState();
6085
+ const preparedState = Object.freeze({
6086
+ sessionId: prepared.sessionId,
6087
+ hostSystemMessages,
6088
+ canonicalSystemMessages: Object.freeze([
6089
+ ...prepared.canonicalSystemMessages,
6090
+ ...(preparedSideEffects.noticeMessage
6091
+ ? [preparedSideEffects.noticeMessage]
6092
+ : []),
6093
+ ]),
6094
+ conversationMessages: prepared.conversationMessages,
6095
+ mcpCommit: preparedMcpCommit,
6096
+ mcpRuntime: preparedMcpRuntime,
6097
+ sanitizeRolesNextTurn:
6098
+ prepared.conversationMessages.at(-1)?.role === "user",
6099
+ logMessage: `Resumed JSONL session ${prepared.sessionId} (${prepared.rebuiltMessages.length} messages)`,
6100
+ });
6101
+ const committed = commitPreparedReplJsonlResume(
6102
+ prepared,
6103
+ () => _applyPreparedResumeState(preparedState),
6104
+ () => _restoreResumeState(previousState),
6105
+ );
6106
+ if (!committed) {
6107
+ const error = new Error(
6108
+ "Prepared JSONL session resume was not committed",
6109
+ );
6110
+ error.code = "CC_REPL_SESSION_RESUME_NOT_COMMITTED";
6111
+ throw error;
6112
+ }
6113
+ const previousLease = _replaceSessionHostLease(targetLease);
6114
+ adoptedTargetLease = true;
6115
+ _replSideEffects = preparedSideEffects.sideEffects;
6116
+ if (preparedSideEffects.warning) {
6117
+ logger.warn(preparedSideEffects.warning);
6118
+ }
6119
+ if (previousLease && previousLease !== targetLease) {
6120
+ try {
6121
+ previousLease.release?.();
6122
+ } catch (releaseError) {
6123
+ logger.warn(
6124
+ `Session switch committed, but the prior host lease could not be released: ${releaseError.message}`,
6125
+ );
6126
+ }
6127
+ }
6128
+ _replCompactPersistence.replace(prepared.rebuiltMessages);
6129
+ } finally {
6130
+ if (!adoptedTargetLease) targetLease?.release?.();
6131
+ }
5871
6132
  } else if (db) {
5872
6133
  const existing = getSession(db, resumeId);
5873
6134
  if (existing && existing.messages) {
@@ -7913,6 +8174,9 @@ async function startAgentReplInWorkspaceOwned(
7913
8174
  // Explicit turn-binding producer (null unless a JSONL session): the
7914
8175
  // wrapper folds every loop event into the live table.
7915
8176
  turnBindingFeed: _turnBindingProducer,
8177
+ // Non-MCP irreversible tools use the same prepare/start/settle ledger
8178
+ // and final lease assertion as headless, stream, and WebSocket hosts.
8179
+ sideEffects: _replSideEffects,
7916
8180
  denialLog: _recentDenials,
7917
8181
  persistRecentDenials: true,
7918
8182
  permissionMode: _sessionTier,
@@ -8497,6 +8761,12 @@ async function startAgentReplInWorkspaceOwned(
8497
8761
  }
8498
8762
 
8499
8763
  // Shutdown runtime
8764
+ _detachSessionHostLease();
8765
+ try {
8766
+ _sessionHostLeaseScope?.lease?.release?.();
8767
+ } catch (error) {
8768
+ _replOutputFailure ||= error;
8769
+ }
8500
8770
  try {
8501
8771
  await shutdown();
8502
8772
  } catch (_e) {