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
@@ -25,6 +25,7 @@ import path from "path";
25
25
  import { randomUUID } from "node:crypto";
26
26
  import { isPromise, isProxy } from "node:util/types";
27
27
  import { logger } from "../lib/logger.js";
28
+ import { issueMcpStdioExecutionAuthority } from "../lib/mcp-stdio-execution-authority.js";
28
29
  import { getPlanModeManager, PlanState } from "../lib/plan-mode.js";
29
30
  import { createVimState, feedNormalKey } from "../lib/repl-vim.js";
30
31
  import {
@@ -68,6 +69,17 @@ import {
68
69
  isVerifiedSessionHostSnapshot,
69
70
  readSessionHostResumeState,
70
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";
71
83
  import {
72
84
  preserveDurableSystemMessageProvenance,
73
85
  projectCanonicalResumeMessages,
@@ -126,6 +138,7 @@ import { expandFileRefsAsync } from "../runtime/file-ref-expander.js";
126
138
  import { prepareVisionTurn, resolveVisionLlm } from "../lib/image-input.js";
127
139
  import { composeSystemPrompt } from "../runtime/system-prompt.js";
128
140
  import { installPipeSafety } from "../runtime/pipe-safety.js";
141
+ import { installOutputBackpressure } from "../runtime/output-backpressure.js";
129
142
  import {
130
143
  makeFallbackChatFn,
131
144
  normalizeFallbackModels,
@@ -323,6 +336,9 @@ async function executeTool(name, args) {
323
336
  * unit-testable via the `options._coreLoop` injection seam.
324
337
  */
325
338
  export async function agentLoop(messages, options) {
339
+ const writeOut =
340
+ options.writeOut || ((text) => process.stdout.write(String(text)));
341
+ const waitForOutput = options.waitForOutput;
326
342
  // Resume-degenerate role merge (Claude Code 2.1.187 parity), gated by the
327
343
  // one-shot `mergeRoles` flag so it fires only on the first model call after
328
344
  // resuming a session whose prior run produced no assistant response. Collapse
@@ -343,7 +359,7 @@ export async function agentLoop(messages, options) {
343
359
  const onProviderFallback =
344
360
  options.onProviderFallback ||
345
361
  ((info) =>
346
- process.stdout.write(
362
+ writeOut(
347
363
  chalk.yellow(
348
364
  `\n ⚠️ ${info.message || `已从 "${info.from}" 切换到 "${info.to}"`}\n`,
349
365
  ),
@@ -354,6 +370,9 @@ export async function agentLoop(messages, options) {
354
370
  // The tool-result event carries no args; remember the last tool-executing
355
371
  // pair so a recorded denial can show what was attempted (/permissions denials).
356
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;
357
376
  for await (const event of runCoreLoop(messages, {
358
377
  ...options,
359
378
  // FORCE in-loop compaction off — the REPL runs its OWN post-turn compaction
@@ -380,7 +399,7 @@ export async function agentLoop(messages, options) {
380
399
  event.type === "managed-checkpoint-error" || event.coverage === "none"
381
400
  ? chalk.yellow
382
401
  : chalk.gray;
383
- process.stdout.write(render(`${managedCheckpointLine}\n`));
402
+ writeOut(render(`${managedCheckpointLine}\n`));
384
403
  } else if (event.type === "checkpoint") {
385
404
  // Remember which file snapshot lines up with the live conversation so
386
405
  // `/rewind <n>` can restore code + conversation together (Claude-Code
@@ -393,7 +412,7 @@ export async function agentLoop(messages, options) {
393
412
  tool: event.tool,
394
413
  });
395
414
  }
396
- process.stdout.write(
415
+ writeOut(
397
416
  chalk.gray(` ⎌ checkpoint ${event.id} (before ${event.tool})\n`),
398
417
  );
399
418
  } else if (event.type === "tool-executing") {
@@ -402,12 +421,55 @@ export async function agentLoop(messages, options) {
402
421
  args: event.args,
403
422
  startedAt: options.now ? options.now() : Date.now(),
404
423
  };
405
- process.stdout.write(
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
+ }
449
+ writeOut(
406
450
  chalk.gray(
407
451
  ` [${event.tool}] ${formatToolArgs(event.tool, event.args)}\n`,
408
452
  ),
409
453
  );
410
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
+ }
411
473
  const durationMs =
412
474
  event.result?.toolTelemetryRecord?.durationMs ??
413
475
  (_lastExec?.tool === event.tool
@@ -438,9 +500,7 @@ export async function agentLoop(messages, options) {
438
500
  }
439
501
  }
440
502
  if (event.error || event.result?.error) {
441
- process.stdout.write(
442
- chalk.red(` Error: ${event.error || event.result?.error}\n`),
443
- );
503
+ writeOut(chalk.red(` Error: ${event.error || event.result?.error}\n`));
444
504
  // Record policy denials (not plain tool failures) into the caller's
445
505
  // denial log for review via `/permissions denials` (Claude-Code 2.1.193
446
506
  // recent denials). Caller passes the session log (mirrors checkpointMarks)
@@ -477,13 +537,13 @@ export async function agentLoop(messages, options) {
477
537
  const sid = options?.sessionId;
478
538
  const policy = approval.policy || "strict";
479
539
  if (sid && policy === "strict") {
480
- process.stdout.write(
540
+ writeOut(
481
541
  chalk.yellow(
482
542
  ` Hint: relax policy with cc session policy ${sid} --set trusted\n`,
483
543
  ),
484
544
  );
485
545
  } else if (sid) {
486
- process.stdout.write(
546
+ writeOut(
487
547
  chalk.yellow(
488
548
  ` Hint: per-session policy is "${policy}" — see cc session policy ${sid}\n`,
489
549
  ),
@@ -491,26 +551,26 @@ export async function agentLoop(messages, options) {
491
551
  }
492
552
  }
493
553
  } else if (event.result?.success) {
494
- process.stdout.write(chalk.green(` Done\n`));
554
+ writeOut(chalk.green(` Done\n`));
495
555
  }
496
556
  } else if (event.type === "thinking") {
497
557
  // Intermediate-step reasoning (before a tool call) — dimmed, inline.
498
558
  if (process.env.CC_REPL_THINKING !== "0" && event.text) {
499
- process.stdout.write(
559
+ writeOut(
500
560
  "\n" + chalk.dim("💭 " + event.text.replace(/\n/g, "\n ")) + "\n",
501
561
  );
502
562
  }
503
563
  } else if (event.type === "token-usage") {
504
564
  usageEvents.push(event);
505
565
  } else if (event.type === "iteration-warning") {
506
- process.stdout.write(chalk.yellow(`\n ${event.message}\n`));
566
+ writeOut(chalk.yellow(`\n ${event.message}\n`));
507
567
  } else if (event.type === "iteration-budget-exhausted") {
508
- process.stdout.write(
509
- chalk.red(`\n [Budget Exhausted] ${event.budget}\n`),
510
- );
568
+ writeOut(chalk.red(`\n [Budget Exhausted] ${event.budget}\n`));
511
569
  } else if (event.type === "response-complete") {
570
+ await waitForOutput?.();
512
571
  return { content: event.content, usageEvents, thinking: event.thinking };
513
572
  }
573
+ await waitForOutput?.();
514
574
  }
515
575
  return { content: "", usageEvents };
516
576
  }
@@ -1934,7 +1994,12 @@ export function createReplMcpHostRuntimeManager(dependencies = {}) {
1934
1994
  }
1935
1995
 
1936
1996
  const sink = persistLedger
1937
- ? createLedgerSink(sessionId, { appendEvent: appendLedgerEvent })
1997
+ ? createLedgerSink(
1998
+ sessionId,
1999
+ appendLedgerEvent === appendAuthorityEvent
2000
+ ? { recovery }
2001
+ : { appendEvent: appendLedgerEvent },
2002
+ )
1938
2003
  : null;
1939
2004
  const sharedController =
1940
2005
  !verifiedRecovery && current?.sessionId === sessionId
@@ -2050,6 +2115,21 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2050
2115
  "CC_REPL_STARTUP_DEPENDENCY_INVALID",
2051
2116
  );
2052
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
+ }
2053
2133
  const trustedWorkspaceRoot = readCwd();
2054
2134
  return enterWorkspace(trustedWorkspaceRoot, () =>
2055
2135
  startWorkspace(options, startupAdmission),
@@ -2058,37 +2138,155 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2058
2138
 
2059
2139
  /** Start the agentic REPL with non-overridable production bindings. */
2060
2140
  export async function startAgentRepl(options = {}) {
2061
- return runReplStartupBoundary(options, {
2062
- prepareReplStartupResume,
2063
- refuseReplStartupResume,
2064
- cwd: () => process.cwd(),
2065
- runWithHostHooksV2Workspace,
2066
- startAgentReplInWorkspace,
2067
- });
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
+ }
2068
2161
  }
2069
2162
 
2070
2163
  async function startAgentReplInWorkspace(options = {}, startupAdmission) {
2164
+ let failureHandler = null;
2165
+ let disposePipeSafety = null;
2166
+ const flow = installOutputBackpressure({
2167
+ stdout: process.stdout,
2168
+ stderr: process.stderr,
2169
+ maxQueuedBytes: options.outputBackpressureMaxBytes,
2170
+ onFailure: (error) => failureHandler?.(error),
2171
+ });
2172
+ let restored = false;
2173
+ const outputScope = {
2174
+ flow,
2175
+ setFailureHandler(handler) {
2176
+ failureHandler = handler;
2177
+ },
2178
+ setPipeDisposer(disposer) {
2179
+ disposePipeSafety = disposer;
2180
+ },
2181
+ restore() {
2182
+ if (restored) return;
2183
+ restored = true;
2184
+ disposePipeSafety?.();
2185
+ flow.restore();
2186
+ },
2187
+ };
2188
+ try {
2189
+ return await startAgentReplInWorkspaceOwned(
2190
+ options,
2191
+ startupAdmission,
2192
+ outputScope,
2193
+ );
2194
+ } catch (error) {
2195
+ outputScope.restore();
2196
+ throw error;
2197
+ }
2198
+ }
2199
+
2200
+ async function startAgentReplInWorkspaceOwned(
2201
+ options = {},
2202
+ startupAdmission,
2203
+ outputScope,
2204
+ ) {
2071
2205
  // EPIPE guard: if the REPL's stdout is piped and the consumer closes (e.g.
2072
2206
  // `cc agent | head`), the async stream `error` would otherwise crash the
2073
2207
  // process. Route a broken pipe into the REPL's own graceful shutdown (the
2074
2208
  // rl "close" handler — MCP disconnect, kill background tasks) when the
2075
- // interface exists, else exit cleanly. `_replClosing` makes it fire once so a
2076
- // cleanup write that also EPIPEs can't loop. `_replRl` is set when rl is built.
2209
+ // interface and its close handler exist. `_replClosing` makes it fire once so
2210
+ // a cleanup write that also EPIPEs can't loop. An early EPIPE remains pending
2211
+ // until the close handler is attached; it never skips directly to exit.
2077
2212
  let _replRl = null;
2078
2213
  let _replClosing = false;
2079
- installPipeSafety(undefined, () => {
2214
+ let _replCloseReady = false;
2215
+ let _replCleanupStarted = false;
2216
+ let _turnAbort = null;
2217
+ let _replOutputFailure = null;
2218
+ const _requestReplOutputClose = (error) => {
2219
+ if (error && !_replOutputFailure) _replOutputFailure = error;
2220
+ if (error) {
2221
+ process.exitCode = error.code === "EPIPE" ? 0 : 1;
2222
+ _turnAbort?.abort(error);
2223
+ }
2080
2224
  if (_replClosing) return;
2081
2225
  _replClosing = true;
2082
- if (_replRl) {
2226
+ if (_replRl && _replCloseReady) {
2083
2227
  try {
2084
- _replRl.close(); // triggers the graceful "close" cleanup → process.exit
2085
- return;
2228
+ _replRl.close(); // triggers the graceful "close" cleanup
2086
2229
  } catch {
2087
- /* fall through to a bare exit */
2230
+ // The close handler owns shutdown. Leaving exitCode set allows the
2231
+ // event loop to drain without bypassing the teardown lifecycle.
2088
2232
  }
2089
2233
  }
2090
- process.exit(0);
2091
- });
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();
2274
+ const _replOutputFlow = outputScope.flow;
2275
+ outputScope.setFailureHandler(_requestReplOutputClose);
2276
+ const _disposeReplPipeSafety = installPipeSafety(undefined, () =>
2277
+ _requestReplOutputClose(
2278
+ Object.assign(new Error("REPL output pipe closed"), { code: "EPIPE" }),
2279
+ ),
2280
+ );
2281
+ outputScope.setPipeDisposer(_disposeReplPipeSafety);
2282
+ const _waitForReplOutput = async () => {
2283
+ try {
2284
+ await _replOutputFlow.wait();
2285
+ return true;
2286
+ } catch {
2287
+ return false;
2288
+ }
2289
+ };
2092
2290
  const { useJsonl, candidate: _startupJsonlResume } = startupAdmission;
2093
2291
  let model = options.model || "qwen2.5:7b";
2094
2292
  let provider = options.provider || "ollama";
@@ -2630,10 +2828,20 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
2630
2828
  };
2631
2829
  if (useJsonl) {
2632
2830
  try {
2633
- sessionId = jsonlStartSession(null, meta);
2831
+ sessionId = jsonlStartSession(options.sessionId || null, meta);
2634
2832
  } catch (_err) {
2635
2833
  // Non-critical
2636
2834
  }
2835
+ if (
2836
+ sessionId &&
2837
+ _sessionHostLeaseScope &&
2838
+ !_sessionHostLeaseScope.lease
2839
+ ) {
2840
+ _sessionHostLeaseScope.lease = acquireSessionHostLease(sessionId, {
2841
+ hostKind: "repl",
2842
+ });
2843
+ _attachSessionHostLease();
2844
+ }
2637
2845
  } else if (db) {
2638
2846
  try {
2639
2847
  const session = createSession(db, meta);
@@ -2771,6 +2979,79 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
2771
2979
  let _replBaseSystem = _buildReplBaseSystem();
2772
2980
  let _activeOutputStyle = null; // { name, body }
2773
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;
2774
3055
  let _mcpLedgerRecovery = null;
2775
3056
  let _mcpLedgerRecoveryError = null;
2776
3057
  const _prepareJsonlResumeCandidate = (targetSessionId) =>
@@ -2988,7 +3269,15 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
2988
3269
  let connected = 0;
2989
3270
  for (const [name, cfg] of serverEntries) {
2990
3271
  try {
2991
- await _bundleMcpClient.connect(name, cfg);
3272
+ const authorizedConfig = { ...cfg };
3273
+ authorizedConfig.mcpStdioExecutionAuthority =
3274
+ issueMcpStdioExecutionAuthority({
3275
+ serverName: name,
3276
+ config: authorizedConfig,
3277
+ approvalKind: "explicit-config",
3278
+ approvalSource: `agent-bundle:${path.resolve(options.bundlePath)}`,
3279
+ });
3280
+ await _bundleMcpClient.connect(name, authorizedConfig);
2992
3281
  connected += 1;
2993
3282
  } catch (mcpErr) {
2994
3283
  logger.log(
@@ -3280,6 +3569,12 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
3280
3569
  throw error;
3281
3570
  }
3282
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
+ }
3283
3578
  _commitMcpRecoveryCandidate(messages, prepared.mcp);
3284
3579
  if (prepared.replayMessages.length > 0) {
3285
3580
  messages.push(...prepared.conversationMessages);
@@ -3868,7 +4163,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
3868
4163
  // seam (throwIfAborted at each iteration); partial conversation is kept.
3869
4164
  // Idle Esc presses (no active turn) are ignored, and escape-prefixed key
3870
4165
  // sequences (arrows etc.) never reach here as bare "escape".
3871
- let _turnAbort = null;
4166
+ _turnAbort = null;
3872
4167
  let _lastIdleEscAt = 0;
3873
4168
  let _replKeypressHandler = null;
3874
4169
  if (process.stdin.isTTY) {
@@ -4089,7 +4384,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
4089
4384
  });
4090
4385
  // Custom command wins; otherwise the built-in context-usage render.
4091
4386
  if (_slCfg) {
4092
- return _sl.renderStatusLine(_slCfg, context, { cwd: process.cwd() });
4387
+ return _sl.renderStatusLine(_slCfg, context, {
4388
+ cwd: process.cwd(),
4389
+ });
4093
4390
  }
4094
4391
  const line = _sl.renderDefaultStatusLine(context);
4095
4392
  return line && line.trim() ? line : null;
@@ -4423,7 +4720,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
4423
4720
  ` ${chalk.cyan("/add-dir")} Add an extra working root (/add-dir <dir>; no arg lists roots)`,
4424
4721
  );
4425
4722
  logger.log(
4426
- ` ${chalk.cyan("/reload-skills")} Re-scan skill layers without restarting`,
4723
+ ` ${chalk.cyan("/reload-skills")} Revoke process Skill grants and re-scan layers`,
4427
4724
  );
4428
4725
  logger.log(
4429
4726
  ` ${chalk.cyan("/reload-plugins")} Re-scan installed plugins after add/trust/upgrade`,
@@ -5283,7 +5580,11 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
5283
5580
  dispatchAsyncHooks(
5284
5581
  _settingsHooks,
5285
5582
  "CwdChanged",
5286
- { old_cwd: oldCwd, cwd: newCwd, session_id: sessionId || null },
5583
+ {
5584
+ old_cwd: oldCwd,
5585
+ cwd: newCwd,
5586
+ session_id: sessionId || null,
5587
+ },
5287
5588
  { cwd: newCwd, supervisor: _asyncHookSupervisor },
5288
5589
  );
5289
5590
  } catch {
@@ -5747,39 +6048,87 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
5747
6048
  const resumeId = sessionArg.slice(7).trim();
5748
6049
  try {
5749
6050
  if (useJsonl) {
5750
- const prepared = _prepareJsonlResumeCandidate(resumeId);
5751
- if (!prepared.ok) throw prepared.error;
5752
- const preparedMcpRuntime = _prepareMcpHostRuntime(
5753
- prepared.sessionId,
5754
- prepared.mcp,
5755
- );
5756
- const preparedMcpCommit = _prepareMcpRecoveryCommit(prepared.mcp);
5757
- const hostSystemMessages = _refreshHostSystemMessages();
5758
- const previousState = _captureResumeState();
5759
- const preparedState = Object.freeze({
5760
- sessionId: prepared.sessionId,
5761
- hostSystemMessages,
5762
- canonicalSystemMessages: prepared.canonicalSystemMessages,
5763
- conversationMessages: prepared.conversationMessages,
5764
- mcpCommit: preparedMcpCommit,
5765
- mcpRuntime: preparedMcpRuntime,
5766
- sanitizeRolesNextTurn:
5767
- prepared.conversationMessages.at(-1)?.role === "user",
5768
- logMessage: `Resumed JSONL session ${prepared.sessionId} (${prepared.rebuiltMessages.length} messages)`,
5769
- });
5770
- const committed = commitPreparedReplJsonlResume(
5771
- prepared,
5772
- () => _applyPreparedResumeState(preparedState),
5773
- () => _restoreResumeState(previousState),
5774
- );
5775
- if (!committed) {
5776
- const error = new Error(
5777
- "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
+ },
6063
+ );
6064
+ }
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 },
5778
6077
  );
5779
- error.code = "CC_REPL_SESSION_RESUME_NOT_COMMITTED";
5780
- throw error;
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?.();
5781
6131
  }
5782
- _replCompactPersistence.replace(prepared.rebuiltMessages);
5783
6132
  } else if (db) {
5784
6133
  const existing = getSession(db, resumeId);
5785
6134
  if (existing && existing.messages) {
@@ -5847,6 +6196,11 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
5847
6196
  // layers (incl. .claude/skills) without restarting the session.
5848
6197
  if (trimmed === "/reload-skills") {
5849
6198
  try {
6199
+ _replSkillLoader.revokeExecutionAuthorizations?.({
6200
+ message:
6201
+ "Skill execution authorization was revoked by /reload-skills",
6202
+ reasonCode: "reload-skills",
6203
+ });
5850
6204
  _replSkillLoader.clearCache();
5851
6205
  const n = _replSkillLoader.loadAll().length;
5852
6206
  _replBaseSystem = _buildReplBaseSystem();
@@ -5856,7 +6210,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
5856
6210
  _persistReplContextSources();
5857
6211
  logger.log(
5858
6212
  chalk.green(
5859
- `✔ skills reloaded — ${n} available (${_replSkillLoader.getLayerPaths().length} layers re-scanned)`,
6213
+ `✔ Skill grants revoked and skills reloaded — ${n} available (${_replSkillLoader.getLayerPaths().length} layers re-scanned)`,
5860
6214
  ),
5861
6215
  );
5862
6216
  } catch (err) {
@@ -6069,7 +6423,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
6069
6423
  logger.log(chalk.gray(l));
6070
6424
  } else {
6071
6425
  // set — createReplGoal throws on a malformed spec (caught below).
6072
- _sessionGoal = rg.createReplGoal(cmd.spec, { now: () => Date.now() });
6426
+ _sessionGoal = rg.createReplGoal(cmd.spec, {
6427
+ now: () => Date.now(),
6428
+ });
6073
6429
  for (const l of rg.renderGoalStart(_sessionGoal))
6074
6430
  logger.log(chalk.cyan(l));
6075
6431
  }
@@ -7679,22 +8035,29 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
7679
8035
  // reasoning dimmed before it. Skipped (left undefined) in replay mode.
7680
8036
  let _liveStreamed = false;
7681
8037
  let _liveThinkStarted = false;
8038
+ const _settleLiveWrite = (blocked) =>
8039
+ blocked ? _replOutputFlow.wait() : undefined;
7682
8040
  const liveOpts = _streamLive
7683
8041
  ? {
7684
8042
  onToken: (t) => {
7685
8043
  // Separate the answer from the dimmed reasoning above it (once).
7686
- if (!_liveStreamed && _liveThinkStarted)
7687
- process.stdout.write("\n");
8044
+ let blocked = false;
8045
+ if (!_liveStreamed && _liveThinkStarted) {
8046
+ blocked = process.stdout.write("\n") === false;
8047
+ }
7688
8048
  _liveStreamed = true;
7689
- process.stdout.write(t);
8049
+ blocked = process.stdout.write(t) === false || blocked;
8050
+ return _settleLiveWrite(blocked);
7690
8051
  },
7691
8052
  onThinking: (t) => {
7692
8053
  if (process.env.CC_REPL_THINKING === "0") return;
8054
+ let blocked = false;
7693
8055
  if (!_liveThinkStarted) {
7694
8056
  process.stdout.write(chalk.dim("💭 "));
7695
8057
  _liveThinkStarted = true;
7696
8058
  }
7697
- process.stdout.write(chalk.dim(t));
8059
+ blocked = process.stdout.write(chalk.dim(t)) === false || blocked;
8060
+ return _settleLiveWrite(blocked);
7698
8061
  },
7699
8062
  }
7700
8063
  : {};
@@ -7726,7 +8089,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
7726
8089
  if (_settingsHooks && !_asyncHookSupervisor) {
7727
8090
  const { AsyncHookSupervisor } =
7728
8091
  await import("../lib/async-hook-supervisor.js");
7729
- _asyncHookSupervisor = new AsyncHookSupervisor({ persistStats: true });
8092
+ _asyncHookSupervisor = new AsyncHookSupervisor({
8093
+ persistStats: true,
8094
+ });
7730
8095
  }
7731
8096
  const activeMcpRuntime = _activateMcpHostRuntime();
7732
8097
  const activeRawMcpClient = activeMcpRuntime.rawClient || undefined;
@@ -7736,6 +8101,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
7736
8101
  thinking: reasoning,
7737
8102
  } = await agentLoop(messages, {
7738
8103
  ...liveOpts,
8104
+ waitForOutput: _replOutputFlow.wait,
7739
8105
  mergeRoles: _mergeRolesThisTurn,
7740
8106
  // Visible auto-retry feedback (Claude-Code 2.1.181): when the model's
7741
8107
  // streaming call hits a transient connection drop and retries, tell the
@@ -7808,6 +8174,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
7808
8174
  // Explicit turn-binding producer (null unless a JSONL session): the
7809
8175
  // wrapper folds every loop event into the live table.
7810
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,
7811
8180
  denialLog: _recentDenials,
7812
8181
  persistRecentDenials: true,
7813
8182
  permissionMode: _sessionTier,
@@ -8170,6 +8539,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
8170
8539
  };
8171
8540
 
8172
8541
  rl.on("line", async (input) => {
8542
+ if (!(await _waitForReplOutput())) return;
8173
8543
  if (_processingLine) {
8174
8544
  const concurrentBtw = _slashCommandsDisabled
8175
8545
  ? null
@@ -8193,10 +8563,12 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
8193
8563
  _processingLine = true;
8194
8564
  try {
8195
8565
  await handleLine(input);
8566
+ if (!(await _waitForReplOutput())) return;
8196
8567
  while (_pendingLines.length) {
8197
8568
  const next = _pendingLines.shift();
8198
8569
  logger.log(chalk.cyan(`▶ running queued input: ${next}`));
8199
8570
  await handleLine(next);
8571
+ if (!(await _waitForReplOutput())) return;
8200
8572
  }
8201
8573
  } finally {
8202
8574
  _processingLine = false;
@@ -8204,6 +8576,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
8204
8576
  });
8205
8577
 
8206
8578
  rl.on("close", async () => {
8579
+ if (_replCleanupStarted) return;
8580
+ _replCleanupStarted = true;
8581
+ _replClosing = true;
8207
8582
  _promptInteractions.dispose();
8208
8583
  if (process.stdin.isTTY) {
8209
8584
  if (_replKeypressHandler) {
@@ -8386,11 +8761,42 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
8386
8761
  }
8387
8762
 
8388
8763
  // Shutdown runtime
8764
+ _detachSessionHostLease();
8765
+ try {
8766
+ _sessionHostLeaseScope?.lease?.release?.();
8767
+ } catch (error) {
8768
+ _replOutputFailure ||= error;
8769
+ }
8389
8770
  try {
8390
8771
  await shutdown();
8391
8772
  } catch (_e) {
8392
8773
  // Non-critical
8393
8774
  }
8394
- process.exit(0);
8775
+ if (!_replOutputFailure) {
8776
+ try {
8777
+ await _replOutputFlow.wait();
8778
+ } catch (error) {
8779
+ _replOutputFailure ||= error;
8780
+ }
8781
+ }
8782
+ const outputExitCode = _replOutputFailure
8783
+ ? _replOutputFailure.code === "EPIPE"
8784
+ ? 0
8785
+ : 1
8786
+ : 0;
8787
+ outputScope.restore();
8788
+ process.exit(outputExitCode);
8395
8789
  });
8790
+ _replCloseReady = true;
8791
+ if (_replClosing) {
8792
+ // stdout may have broken before readline and its cleanup handler were fully
8793
+ // initialized. Close only now so the complete async teardown runs.
8794
+ queueMicrotask(() => {
8795
+ try {
8796
+ rl.close();
8797
+ } catch {
8798
+ process.exitCode = 0;
8799
+ }
8800
+ });
8801
+ }
8396
8802
  }