chainlesschain 0.162.175 → 0.162.177

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 (311) hide show
  1. package/package.json +10 -10
  2. package/src/assets/web-panel/assets/{AIOps-DherW0Rh.js → AIOps-DT-VPWuN.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CDGfqt2Y.js → ActionButton-DyOreOVN.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-BYhkzdgD.js → Analytics-DslNsKux.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-cEKk1cpb.js → AppLayout-BNJJAYME.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-_aXR3WWc.js → Artifacts-DoaAtEcY.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CYAf-gY7.js → Audit-DPG3WdJ9.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-D4Q20phR.js → BackgroundAgents-C_JO6R5G.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-Cq0Tyk58.js → Backup-Cv3BooyX.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-CPj_CdAv.js → BaseInput-D2uQXWu-.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-BQ-Qq0es.js → Chat-B4Vczyf3.js} +4 -4
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-DE6reP99.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-rke6l3yg.js → Checkbox-qTgk4VeP.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-DiacrIPq.js → Codegen-DlLWcIzj.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-Cx3BY-Up.js → Col-DNO73qp7.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DfmWiQy8.js → Community-vRDNpc4j.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DdQnpxy5.js → Compact-CEWtIXni.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-lzliOH_r.js → Compliance-KgYC9Fxi.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CYx4Un5k.js → Cowork-zj5wvkpT.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-BGDcPFlo.js → Cron-BLDoCozU.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-0_X5g2ri.js → Crosschain-BYnff4VY.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-C0PEf3Eu.js → DID-C75VFsVm.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-DULQjQ58.js → Dashboard-C4Gutni2.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-DGRHRbW6.js → Dropdown-B5dbO4Kx.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-ChX8XPwB.js → EmailListRenderer-BDipZtHJ.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Dyq6IBNu.js → FamilyGuardDashboard-DmIzt8jj.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-DH1NfAL2.js → Federation-D9QOS-uW.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-BN1I_uNi.js → FormItemContext-B47HRwAm.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-Dq_2UYpe.js → GenericCardRenderer-3R4mXmgV.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-BAy1yvI2.js → Git-DrNXZf1C.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-Db6Q2iSy.js → Governance-BGd7JR98.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-0ub6yHcr.js → Inference-D-2he1ed.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DtLetM36.js → KnowledgeGraph-C0sHIbK5.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-Bcoy7CUd.js → Logs-DiBQO-jL.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BfEMFAXf.js → MarkdownRenderer-DL_5eV2D.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-tt2rzwRR.js → Marketplace-VR59OdrC.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-BhOgX-MU.js → McpTools-DrPzwSd3.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-BJUxeWBL.js → Memory-CBgKALwQ.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BwUY2G9w.js → MobileBridge-pQ9Tn-Mn.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-BLRfHhqi.js → MobileProjects-mASB6jyj.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-B2WMboOm.js → Mtc-DTMgE7HC.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-8pNEXO63.js → MtcAudit-CwTnCtiL.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-C1wm5a_1.js → Multisig-ngm6WU8f.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-C1QpKF7g.js → NLProgramming-4YO4Qww9.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CiQUcxFu.js → Notes-DP1k45mF.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-BblDPJ_x.js → NotificationSettings-r2H8yPKb.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DRjYcPtb.js → OrderTableRenderer-CoM_Zw0J.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-Dp3VKB0X.js → Organization-DrO7jRWS.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Czx161m9.js → Overflow-Cnwu4i4t.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-ClFi6wOs.js → P2P-zi-cDwA_.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CRweW0lz.js → PdhVaultBrowser-DBEryDv7.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-BzcRoqTn.js → Permissions-lqXK2C3x.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-ChGTpNMd.js → PersonalDataHub-b63T3HJM.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-BMOOpKWA.js → Pipeline-Kep5KAGW.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-D0AZHObH.js → Privacy-CHdvOAh1.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-yYFlZ6Ja.js → ProjectInit-BhKfdwe9.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-DJNVuMQs.js → ProjectSettings-Yavk9K8A.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-Br0rBWH1.js → Projects-CN8E_5Mq.js} +1 -1
  59. package/src/assets/web-panel/assets/Providers-Bfym6SQn.js +1 -0
  60. package/src/assets/web-panel/assets/{QrScannerModal-81NZOitV.js → QrScannerModal-CLSQcrlR.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-Dic2lCbJ.js → QuickAsk-VPa2WKQN.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-Dh9iwViE.js → Recommend-CGXR8jVU.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-Bq9VYEFy.js → RemoteSession-C_20YfMB.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-CxFZ-NbJ.js → Reputation-awZh68UV.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-BVSLcaX2.js → Row-Ctb4oiYN.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-BbC-5DOH.js → RssFeed-D3sdjvUV.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-BfjZ1GCr.js → Search-C-zfA4kT.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CVjhOV5r.js → Security-D9XlmHA3.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-D45u-Aqe.js → Services-C3UKnr41.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-WkGoK4he.js → Skeleton-BQxagZfF.js} +1 -1
  71. package/src/assets/web-panel/assets/Skills-q6eb6KPZ.js +1 -0
  72. package/src/assets/web-panel/assets/{Sla-Bf4QTF3E.js → Sla-DveENvek.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-DVpZdbMN.js → SpeechSettings-BFjXLNgF.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-DEEojoMS.js → SyncSettings-BpHgbaP9.js} +2 -2
  75. package/src/assets/web-panel/assets/Tasks-DvepRb8-.js +1 -0
  76. package/src/assets/web-panel/assets/{Templates-Bzl-9wY5.js → Templates-DEiaoyve.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-0khxUOB8.js → Tenant-ITg774nY.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-DWGYhAYU.js → Terminal-D3ZBcvnb.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-B0Y4LONc.js → TimelineRenderer-BsLU6JjU.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-DEE07SFh.js → Tokens-Cb5Pquc9.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-BM7xvA5z.js → Trigger-Dwfy-m9N.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-Dv3ijvvZ.js → Trust-CnlSRGG0.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B1-enmMt.js → UkeySign-Y4TlvKVo.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-DYiIPByd.js → VideoEditing-BpKLtTTO.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-D-y374BQ.js → Wallet-LLBzIq8x.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-HBDjcUhi.js → WebAuthn-BeSRiusP.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-sKn4qTID.js → WorkflowEditor-BK_l1tXO.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-TVYhaBXj.js → chat-DYd-X7Wg.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-DMADxY9v.js → colors-C9uip71j.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-OftcHpO_.js → compact-item-JBrfjUP6.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-DW1zVUSN.js → createContext-D03fbuiq.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-B8LQ8leS.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-CaUathNp.js → hasIn-DLQOR6l-.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-Cmdv6Dev.js → index-5YN7F5bO.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-iDmgT2Dg.js → index-6GuLRpBQ.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-77kgkUgY.js → index-B0X76LjX.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CM8W07sc.js → index-B46YcgsV.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-C9JoaYSs.js → index-BNCdRmWf.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CpDtN0dl.js → index-BYv-LWdy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CZXYI5LU.js → index-Ba-Ps_AJ.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DWVZ3T0t.js → index-BcBZwlnG.js} +1 -1
  102. package/src/assets/web-panel/assets/{index--5UvwqH_.js → index-BdH9l8wl.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BBNg08cD.js → index-BfOs_bmg.js} +3 -3
  104. package/src/assets/web-panel/assets/index-Bh8BPtqR.js +1 -0
  105. package/src/assets/web-panel/assets/{index-s-EnkUCn.js → index-BnQgRjsm.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Lr0MO-GU.js → index-BpYKLrUT.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B-19mFhn.js → index-BuNFCZCx.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CeCIYYG7.js → index-ByHO3SqZ.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-hXq7QRA_.js → index-C2zmOsyD.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-8YelceUU.js → index-CChQBM1d.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-B52Ztt8Q.js → index-CChaISMb.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DKjqFpYa.js → index-CILVyK3b.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DAfT3EMd.js → index-CL0ixCxk.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-sm1sGIxW.js → index-CS63r8h3.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DHi634g9.js → index-CVZWiUE_.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BEzLLVYd.js → index-Ce0Bf1YF.js} +1 -1
  117. package/src/assets/web-panel/assets/index-CeVrHLzZ.js +1 -0
  118. package/src/assets/web-panel/assets/{index-pSVCBsG4.js → index-Ck17i0OD.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-GUKz4Pv9.js → index-Cpn19c9k.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CKNdnJRo.js → index-CqWk1FLG.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Bg-7FpaL.js → index-CwoDpKzW.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DYwwUKDg.js → index-D-1SqEpQ.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DZdT8nio.js → index-D4UY5Z60.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BvRw4Fna.js → index-D8KvuDz0.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-kdMl_0zL.js → index-DNMAAUVk.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CK-p7Hji.js → index-DYLZY4uh.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DXcqvWBp.js → index-DaokIHt3.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BFVbDpqg.js → index-Q7mmL6w4.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BJ1PjRY3.js → index-SO6lgMg0.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BSp1Ofvx.js → index-iahNhZwm.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BupSWDcl.js → index-l08afIbV.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D63QVAiJ.js → index-rIxxfdTN.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-DdcKTlQR.js → initDefaultProps-CG9z1c6w.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-CL-hTBQj.js → motion-B1Ky7Zff.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-qYSAkxNG.js → move-DME8X2P1.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-ZwRSZtRP.js → mtc-parser-cuJYSWB7.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-Bld-8ftI.js → omit-UI_EE0uZ.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DGHs-a9Z.js → pickAttrs-BIoE_Z_V.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-DRQPMyI5.js → placementArrow-B48-9HKN.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-C5MJG_Yo.js → responsiveObserve-D7x_GDgD.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-VZ-fpZCD.js → slide-DjzC5_7m.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-9ocfCyHk.js → statusUtils-C6BIymhh.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-B6QAwAN5.js → styleChecker-B4dGrdD9.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-Bq1twor3.js → useFlexGapSupport-Bken4aNE.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DKZq96tz.js → useFs-BMlqECqv.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-D0RmRVXf.js → usePersonalDataHub-DLdrfmMa.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-D6kEj_tt.js → vnode-CKJQmxa0.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-2yfasoQf.js → zoom-CaZunuau.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/auth/npm-auth.js +30 -5
  151. package/src/commands/agenda.js +83 -5
  152. package/src/commands/agent.js +42 -4
  153. package/src/commands/background-session.js +11 -4
  154. package/src/commands/batch.js +79 -27
  155. package/src/commands/changelog.js +15 -4
  156. package/src/commands/config.js +70 -5
  157. package/src/commands/context.js +36 -2
  158. package/src/commands/eval.js +21 -6
  159. package/src/commands/hook.js +2 -2
  160. package/src/commands/hub.js +19 -1
  161. package/src/commands/loop.js +9 -2
  162. package/src/commands/memory.js +21 -6
  163. package/src/commands/permissions.js +57 -1
  164. package/src/commands/plugin.js +2 -0
  165. package/src/commands/review.js +23 -11
  166. package/src/commands/routine.js +31 -8
  167. package/src/commands/session.js +11 -3
  168. package/src/commands/skill.js +2 -0
  169. package/src/commands/statusline.js +3 -3
  170. package/src/commands/team.js +20 -7
  171. package/src/commands/update.js +65 -10
  172. package/src/data/changelog.json +293 -51
  173. package/src/gateways/terminal/PtyManager.js +8 -1
  174. package/src/gateways/ws/message-dispatcher.js +3 -0
  175. package/src/gateways/ws/session-protocol.js +26 -0
  176. package/src/gateways/ws/ws-agent-handler.js +107 -69
  177. package/src/gateways/ws/ws-server.js +74 -2
  178. package/src/harness/background-task-manager.js +18 -6
  179. package/src/harness/background-task-worker.js +11 -18
  180. package/src/harness/jsonl-session-store.js +13 -6
  181. package/src/harness/mcp-client.js +195 -2
  182. package/src/harness/worktree-isolator.js +18 -21
  183. package/src/lib/__tests__/personal-data-hub-aichat-wizard.test.js +15 -0
  184. package/src/lib/agent-coordinator.js +4 -0
  185. package/src/lib/agent-core.js +5 -0
  186. package/src/lib/agent-ipc-bus.js +44 -5
  187. package/src/lib/agent-sandbox.js +16 -2
  188. package/src/lib/agent-schedule-store.js +55 -0
  189. package/src/lib/agent-team/team-worktree.js +28 -6
  190. package/src/lib/agent-tool-admission.js +104 -0
  191. package/src/lib/agent-worktree.js +20 -19
  192. package/src/lib/api-key-helper.js +10 -2
  193. package/src/lib/async-hook-supervisor.cjs +135 -15
  194. package/src/lib/async-hook-supervisor.js +47 -0
  195. package/src/lib/background-agent-supervisor.js +35 -12
  196. package/src/lib/background-task-manager.js +5 -1
  197. package/src/lib/changelog.js +76 -11
  198. package/src/lib/channels/channel-manager.js +6 -0
  199. package/src/lib/channels/telegram-channel.js +11 -1
  200. package/src/lib/channels/webhook-channel.js +15 -3
  201. package/src/lib/checkpoint-store.js +10 -2
  202. package/src/lib/chrome-connector.js +6 -2
  203. package/src/lib/claude-code-bridge.js +24 -8
  204. package/src/lib/cli-anything-bridge.js +82 -25
  205. package/src/lib/cloud/bundle.js +12 -2
  206. package/src/lib/code-review.js +32 -8
  207. package/src/lib/computer-use/control-backend.js +18 -3
  208. package/src/lib/context-breakdown.js +46 -0
  209. package/src/lib/context-source-ledger.js +43 -0
  210. package/src/lib/diff-review-follow-up.js +154 -0
  211. package/src/lib/doctor-checkup.js +18 -6
  212. package/src/lib/downloader.js +17 -3
  213. package/src/lib/ensure-utf8.js +11 -3
  214. package/src/lib/eval/tasks.js +25 -31
  215. package/src/lib/event-runtime-producer.js +58 -0
  216. package/src/lib/event-runtime-store.js +354 -0
  217. package/src/lib/event-runtime-worker.js +109 -0
  218. package/src/lib/execution-backend.js +72 -21
  219. package/src/lib/git-integration.js +19 -4
  220. package/src/lib/hook-manager.js +6 -15
  221. package/src/lib/hook-runner.cjs +88 -8
  222. package/src/lib/hook-runner.js +55 -0
  223. package/src/lib/hooks-v2-producers.js +15 -0
  224. package/src/lib/hooks-v2-runtime.js +70 -21
  225. package/src/lib/host-adb-bridge.js +129 -24
  226. package/src/lib/ide-context.js +128 -1
  227. package/src/lib/interaction-adapter.js +39 -0
  228. package/src/lib/json-schema-validate.js +190 -36
  229. package/src/lib/lan-pairing-preflight.js +13 -2
  230. package/src/lib/lsp/__tests__/benchmark.test.js +61 -0
  231. package/src/lib/lsp/__tests__/lsp-client.test.js +6 -1
  232. package/src/lib/lsp/__tests__/lsp-server-registry.test.js +18 -0
  233. package/src/lib/lsp/benchmark.js +20 -4
  234. package/src/lib/lsp/lsp-client.js +22 -3
  235. package/src/lib/lsp/lsp-manager.js +4 -0
  236. package/src/lib/lsp/lsp-server-registry.js +11 -2
  237. package/src/lib/mcp-oauth.js +8 -2
  238. package/src/lib/npm-invocation.js +29 -0
  239. package/src/lib/orchestrator.js +8 -2
  240. package/src/lib/packer/pack-update-applier.js +14 -2
  241. package/src/lib/packer/pkg-runner.js +13 -2
  242. package/src/lib/packer/precheck.js +33 -16
  243. package/src/lib/packer/smoke-runner.js +18 -5
  244. package/src/lib/packer/web-panel-builder.js +16 -2
  245. package/src/lib/paths.js +1 -1
  246. package/src/lib/permission-rules.cjs +5 -1
  247. package/src/lib/personal-data-hub-aichat-wizard.js +22 -6
  248. package/src/lib/personal-data-hub-wiring.js +27 -3
  249. package/src/lib/plan-mode.js +204 -11
  250. package/src/lib/plugin-monitor-supervisor.js +48 -6
  251. package/src/lib/plugin-runtime/bin.js +45 -0
  252. package/src/lib/plugin-runtime/hooks.js +14 -1
  253. package/src/lib/plugin-runtime/install.js +11 -3
  254. package/src/lib/plugin-runtime/lsp.js +3 -0
  255. package/src/lib/plugin-runtime/mcp.js +8 -1
  256. package/src/lib/plugin-runtime/monitors.js +4 -0
  257. package/src/lib/plugin-runtime/plugin-options.js +74 -7
  258. package/src/lib/plugin-runtime/signature.js +45 -0
  259. package/src/lib/pr-create.js +21 -8
  260. package/src/lib/pr-link-ledger.js +15 -3
  261. package/src/lib/process-execution-broker/credential-agent.js +23 -3
  262. package/src/lib/process-execution-broker/index.js +315 -38
  263. package/src/lib/process-execution-broker/patch-child-process.js +2 -3
  264. package/src/lib/process-manager.js +18 -3
  265. package/src/lib/publish-workspace.js +14 -6
  266. package/src/lib/repl-bang-memorize.js +10 -2
  267. package/src/lib/repl-goal.js +19 -2
  268. package/src/lib/secret-store.js +159 -0
  269. package/src/lib/service-manager.js +64 -29
  270. package/src/lib/settings-hook-events.cjs +74 -9
  271. package/src/lib/settings-hook-events.js +20 -0
  272. package/src/lib/settings-hooks.cjs +4 -0
  273. package/src/lib/side-effect-ledger.js +22 -0
  274. package/src/lib/skill-packs/generator.js +18 -9
  275. package/src/lib/skill-process-broker.js +71 -0
  276. package/src/lib/slash-commands.js +10 -2
  277. package/src/lib/status-line.cjs +28 -7
  278. package/src/lib/status-line.js +33 -0
  279. package/src/lib/sub-agent-context.js +5 -0
  280. package/src/lib/sub-agent-profiles.js +4 -0
  281. package/src/lib/turn-context.js +14 -8
  282. package/src/lib/unattended-action-policy.js +83 -0
  283. package/src/lib/update-notice.js +6 -2
  284. package/src/lib/with-file-lock.js +7 -0
  285. package/src/lib/ws-server.js +1 -0
  286. package/src/repl/agent-repl.js +60 -23
  287. package/src/repl/clipboard-copy.js +8 -2
  288. package/src/repl/pr-comments.js +30 -7
  289. package/src/runtime/agent-core.js +554 -118
  290. package/src/runtime/agent-runtime.js +1 -0
  291. package/src/runtime/coding-agent-contract-shared.cjs +52 -0
  292. package/src/runtime/coding-agent-policy.cjs +20 -0
  293. package/src/runtime/diagnostics.js +73 -8
  294. package/src/runtime/headless-runner.js +88 -10
  295. package/src/runtime/headless-stream.js +343 -21
  296. package/src/runtime/mcp-config.js +16 -1
  297. package/src/runtime/session-slash-commands.js +506 -0
  298. package/src/skills/video-editing/extractors/audio-extractor.js +13 -5
  299. package/src/skills/video-editing/extractors/video-extractor.js +13 -3
  300. package/src/skills/video-editing/media-process.js +20 -0
  301. package/src/skills/video-editing/render/audio-mix.js +13 -3
  302. package/src/skills/video-editing/render/ffmpeg-concat.js +7 -2
  303. package/src/skills/video-editing/render/ffmpeg-extract.js +7 -2
  304. package/src/workers/background-agent-worker.js +6 -2
  305. package/src/assets/web-panel/assets/ChatBubbleRenderer-Ci97vNTs.js +0 -1
  306. package/src/assets/web-panel/assets/Providers-zZRhNtSu.js +0 -1
  307. package/src/assets/web-panel/assets/Skills-D_jvQZcj.js +0 -1
  308. package/src/assets/web-panel/assets/Tasks-DFwxpLIk.js +0 -1
  309. package/src/assets/web-panel/assets/devWarning-CLOk6rdE.js +0 -1
  310. package/src/assets/web-panel/assets/index-CSgUfMTM.js +0 -1
  311. package/src/assets/web-panel/assets/index-DliSZvAP.js +0 -1
@@ -16,14 +16,14 @@
16
16
 
17
17
  import fs from "fs";
18
18
  import path from "path";
19
- import { execSync, spawn, spawnSync } from "child_process";
19
+ import broker from "../lib/process-execution-broker/index.js";
20
20
  import os from "os";
21
21
  import sharedCodingAgentPolicy from "./coding-agent-policy.cjs";
22
22
  import sharedShellPolicy from "./coding-agent-shell-policy.cjs";
23
23
  import sharedPermissionRules from "../lib/permission-rules.cjs";
24
24
  import sharedSettingsHooks from "../lib/settings-hooks.cjs";
25
- import sharedHookRunner from "../lib/hook-runner.cjs";
26
- import sharedHookEvents from "../lib/settings-hook-events.cjs";
25
+ import sharedHookRunner from "../lib/hook-runner.js";
26
+ import sharedHookEvents from "../lib/settings-hook-events.js";
27
27
  import { mergeProviderOptions } from "../lib/provider-options.js";
28
28
  import { applyCredentialProxy } from "../lib/credential-proxy.js";
29
29
  import { describeBackgroundCommand } from "../lib/terminal-context.js";
@@ -47,6 +47,7 @@ import {
47
47
  import {
48
48
  getCodingAgentFunctionToolDefinitions,
49
49
  listCodingAgentToolNames,
50
+ getCodingAgentToolPolicy,
50
51
  } from "./coding-agent-contract.js";
51
52
  import { createToolContext } from "../tools/tool-context.js";
52
53
  import { createToolTelemetryRecord } from "../tools/tool-telemetry.js";
@@ -78,6 +79,12 @@ import {
78
79
  imageUrlBlockToAnthropic,
79
80
  } from "../lib/image-input.js";
80
81
  import { executeToolSearch, gateDeferredMcpCall } from "./mcp-tool-search.js";
82
+ import { emitHooksV2Event } from "../lib/hooks-v2-producers.js";
83
+ import {
84
+ admitTool,
85
+ buildToolAttribution,
86
+ } from "../lib/agent-tool-admission.js";
87
+ import { evaluateUnattendedShellAction } from "../lib/unattended-action-policy.js";
81
88
 
82
89
  /**
83
90
  * Names of MCP servers currently mounted by an in-flight run_skill call.
@@ -105,6 +112,33 @@ const {
105
112
  withDeliveryId,
106
113
  } = sharedHookEvents;
107
114
 
115
+ const searchProcessRunner = broker.execSync.bind(broker);
116
+ const runCodeProcessRunner = broker.execFileSync.bind(broker);
117
+
118
+ export const _agentToolProcessDeps = {
119
+ runSearch: searchProcessRunner,
120
+ runCode: runCodeProcessRunner,
121
+ };
122
+
123
+ function runSearchProcess(command, options = {}) {
124
+ return _agentToolProcessDeps.runSearch(command, {
125
+ ...options,
126
+ origin: "agent-core:search-files",
127
+ policy: "allow",
128
+ scope: "agent-core",
129
+ });
130
+ }
131
+
132
+ function runCodeProcess(file, args, options = {}, origin = "run-code") {
133
+ return _agentToolProcessDeps.runCode(file, args, {
134
+ ...options,
135
+ origin: `agent-core:${origin}`,
136
+ policy: "allow",
137
+ scope: "agent-core",
138
+ shell: false,
139
+ });
140
+ }
141
+
108
142
  // ─── Background shell tasks ────────────────────────────────────────────────
109
143
  //
110
144
  // run_shell is synchronous (execSync) and capped at a foreground timeout, which
@@ -223,25 +257,54 @@ function _releaseBgChildHandles(task) {
223
257
  }
224
258
  }
225
259
 
260
+ const backgroundTaskRunner = broker.spawn.bind(broker);
261
+ const backgroundTaskSyncRunner = broker.spawnSync.bind(broker);
262
+
263
+ export const _backgroundProcessDeps = {
264
+ run: backgroundTaskRunner,
265
+ runSync: backgroundTaskSyncRunner,
266
+ };
267
+
268
+ export function _runBackgroundTaskkill(pid, { sync = false } = {}) {
269
+ const args = ["/pid", String(pid), "/T", "/F"];
270
+ const options = {
271
+ windowsHide: true,
272
+ origin: "agent-core:background-taskkill",
273
+ policy: "allow",
274
+ scope: "agent-core",
275
+ };
276
+ return sync
277
+ ? _backgroundProcessDeps.runSync("taskkill", args, options)
278
+ : _backgroundProcessDeps.run("taskkill", args, options);
279
+ }
280
+
281
+ const gitProcessRunner = broker.spawnSync.bind(broker);
282
+
283
+ export const _gitProcessDeps = {
284
+ run: gitProcessRunner,
285
+ };
286
+
226
287
  function _killTask(task) {
227
288
  const child = task?.child;
228
289
  if (!child || child.killed || task?.status !== "running") return false;
229
290
  try {
230
291
  if (process.platform === "win32") {
231
292
  if (child.pid) {
232
- const tk = spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
233
- windowsHide: true,
234
- });
235
- // A spawn that fails to launch emits an async 'error' event — with no
236
- // listener Node rethrows it as an UNCAUGHT exception (the try/catch
237
- // here can't catch the async emit), crashing the CLI. Handle it and
238
- // fall back to a direct kill.
239
- tk.on("error", () => {
293
+ const tk = _runBackgroundTaskkill(child.pid);
294
+ const fallbackDirectKill = () => {
240
295
  try {
241
- child.kill();
296
+ if (!child.killed) child.kill("SIGKILL");
242
297
  } catch {
243
298
  /* already dead */
244
299
  }
300
+ };
301
+ // A launch failure emits 'error'; a permissions/policy failure instead
302
+ // launches taskkill successfully and exits non-zero. Both must fall
303
+ // back or the background command remains alive while teardown reports
304
+ // success.
305
+ tk.once("error", fallbackDirectKill);
306
+ tk.once("close", (code) => {
307
+ if (code !== 0) fallbackDirectKill();
245
308
  });
246
309
  } else {
247
310
  child.kill();
@@ -302,9 +365,13 @@ function _killTaskSync(task) {
302
365
  try {
303
366
  if (process.platform === "win32") {
304
367
  if (child.pid) {
305
- spawnSync("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
306
- windowsHide: true,
307
- });
368
+ const killed = _runBackgroundTaskkill(child.pid, { sync: true });
369
+ if (killed.error || killed.status !== 0) {
370
+ // Restricted runners can execute taskkill but receive a non-zero
371
+ // access/policy result. Direct termination is the synchronous
372
+ // last-resort for the child handle we own.
373
+ child.kill("SIGKILL");
374
+ }
308
375
  } else {
309
376
  child.kill();
310
377
  }
@@ -470,8 +537,9 @@ async function runSettingsPreToolUseHooks(name, args, context, cwd) {
470
537
  ? await runCommandHooksParallel(matched, payload, {
471
538
  cwd,
472
539
  event: "PreToolUse",
540
+ broker,
473
541
  })
474
- : runCommandHooks(matched, payload, { cwd, event: "PreToolUse" });
542
+ : runCommandHooks(matched, payload, { cwd, event: "PreToolUse", broker });
475
543
  if (outcome.decision === "block") {
476
544
  return { blocked: true, reason: outcome.reason, hook: outcome.hook };
477
545
  }
@@ -552,6 +620,7 @@ function runSettingsPermissionRequestHooks(name, args, context, cwd, reason) {
552
620
  cwd,
553
621
  event: "PermissionRequest",
554
622
  mergeStrict: _hookStrictMergeEnabled(),
623
+ broker,
555
624
  });
556
625
  // Precedence: deny > ask > allow > defer. The runner normalizes deny/block →
557
626
  // "block" and short-circuits block/ask, but it COLLAPSES an "allow" into the
@@ -683,6 +752,21 @@ export function reloadSkills() {
683
752
  let _cachedPython = null;
684
753
  let _cachedEnvInfo = null;
685
754
 
755
+ const environmentProbeRunner = broker.execFileSync.bind(broker);
756
+
757
+ export const _environmentProcessDeps = {
758
+ run: environmentProbeRunner,
759
+ };
760
+
761
+ function runEnvironmentProbe(file, args, options = {}) {
762
+ return _environmentProcessDeps.run(file, args, {
763
+ ...options,
764
+ origin: "agent-core:environment-probe",
765
+ policy: "allow",
766
+ scope: "agent-core",
767
+ });
768
+ }
769
+
686
770
  /**
687
771
  * Get cached Python interpreter info (reuses cli-anything-bridge detection).
688
772
  * @returns {{ found: boolean, command?: string, version?: string }}
@@ -706,7 +790,7 @@ export function getEnvironmentInfo() {
706
790
  let pipAvailable = false;
707
791
  if (py.found) {
708
792
  try {
709
- execSync(`${py.command} -m pip --version`, {
793
+ runEnvironmentProbe(py.command, ["-m", "pip", "--version"], {
710
794
  encoding: "utf-8",
711
795
  timeout: 10000,
712
796
  stdio: ["pipe", "pipe", "pipe"],
@@ -719,7 +803,7 @@ export function getEnvironmentInfo() {
719
803
 
720
804
  let nodeVersion = null;
721
805
  try {
722
- nodeVersion = execSync("node --version", {
806
+ nodeVersion = runEnvironmentProbe(process.execPath, ["--version"], {
723
807
  encoding: "utf-8",
724
808
  timeout: 5000,
725
809
  }).trim();
@@ -729,7 +813,7 @@ export function getEnvironmentInfo() {
729
813
 
730
814
  let gitAvailable = false;
731
815
  try {
732
- execSync("git --version", {
816
+ runEnvironmentProbe("git", ["--version"], {
733
817
  encoding: "utf-8",
734
818
  timeout: 5000,
735
819
  stdio: ["pipe", "pipe", "pipe"],
@@ -770,6 +854,7 @@ You have access to tools that let you read files, write files, edit files, run s
770
854
  Key behaviors:
771
855
  - When asked to modify code, read the file first, then edit it
772
856
  - When asked to create something, use write_file to create it
857
+ - When asked to remove or rename a file, use delete_file or move_file so the filesystem intent is explicit and reviewable
773
858
  - When asked to run/test something, use run_shell to execute it
774
859
  - For long-running commands (builds, full test suites, dev servers) set run_shell { run_in_background: true } to get a task_id back immediately, then poll output and completion with check_shell { task_id }. Kill a backgrounded server with check_shell { task_id, kill: true } when finished
775
860
  - When asked about git status, diff, log, or other repository operations, use the git tool instead of run_shell
@@ -926,6 +1011,9 @@ export function buildSystemPrompt(cwd, opts = {}) {
926
1011
  const personaSkills = allSkills.filter(
927
1012
  (s) => s.category === "persona" && s.activation === "auto",
928
1013
  );
1014
+ if (typeof opts.onSkillsLoaded === "function") {
1015
+ opts.onSkillsLoaded(personaSkills);
1016
+ }
929
1017
  for (const p of personaSkills) {
930
1018
  if (p.body?.trim()) {
931
1019
  prompt += `\n\n## Persona: ${p.displayName}\n${p.body}`;
@@ -1016,8 +1104,21 @@ const IDE_DIFF_EDIT_TOOLS = new Set([
1016
1104
  "write_file",
1017
1105
  "edit_file",
1018
1106
  "edit_file_hashed",
1107
+ "delete_file",
1108
+ "move_file",
1109
+ ]);
1110
+ const GUARDED_FILE_MUTATION_TOOLS = new Set([
1111
+ ...IDE_DIFF_EDIT_TOOLS,
1112
+ "notebook_edit",
1019
1113
  ]);
1020
1114
 
1115
+ function fileMutationPaths(name, args = {}) {
1116
+ return [
1117
+ args.path,
1118
+ ...(name === "move_file" ? [args.target_path] : []),
1119
+ ].filter(Boolean);
1120
+ }
1121
+
1021
1122
  /**
1022
1123
  * Quote-aware shell tokenizer (mirrors gateways/ws `tokenizeCommand`). The `git`
1023
1124
  * tool runs the model-supplied command via argv (spawnSync, NO shell), so a
@@ -1086,12 +1187,11 @@ function applyLiteralEdit(content, oldStr, newStr, replaceAll) {
1086
1187
  /**
1087
1188
  * Compute the content an edit tool WOULD write, without writing it — the
1088
1189
  * left/right sides for an IDE diff review. Mirrors the corresponding
1089
- * executeToolInner cases exactly (write_file / edit_file / edit_file_hashed,
1090
- * the latter via the same pure replaceByHash). Returns
1091
- * `{ filePath, newContent, originalText|null }` or null when the edit cannot
1092
- * be computed (missing file, anchor/old_string miss, bad args) — the caller
1093
- * then falls back to the normal confirmation path so the tool can produce its
1094
- * own diagnostics.
1190
+ * executeToolInner cases exactly (write/edit/delete/move; hashed edits use the
1191
+ * same pure replaceByHash). Returns an explicit operation plus the left/right
1192
+ * text and optional rename destination, or null when the proposal cannot be
1193
+ * computed — the caller then falls back to normal confirmation so the tool can
1194
+ * produce its own diagnostics.
1095
1195
  */
1096
1196
  export function computeProposedEdit(name, args = {}, cwd = process.cwd()) {
1097
1197
  try {
@@ -1099,10 +1199,49 @@ export function computeProposedEdit(name, args = {}, cwd = process.cwd()) {
1099
1199
  const filePath = path.resolve(cwd, args.path);
1100
1200
  if (name === "write_file") {
1101
1201
  if (typeof args.content !== "string") return null;
1102
- const originalText = fs.existsSync(filePath)
1103
- ? fs.readFileSync(filePath, "utf8")
1104
- : "";
1105
- return { filePath, newContent: args.content, originalText };
1202
+ const exists = fs.existsSync(filePath);
1203
+ const originalText = exists ? fs.readFileSync(filePath, "utf8") : "";
1204
+ return {
1205
+ filePath,
1206
+ newContent: args.content,
1207
+ originalText,
1208
+ operation: exists ? "modify" : "create",
1209
+ };
1210
+ }
1211
+ if (name === "delete_file") {
1212
+ if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
1213
+ return null;
1214
+ }
1215
+ const originalText = fs.readFileSync(filePath, "utf8");
1216
+ return {
1217
+ filePath,
1218
+ newContent: "",
1219
+ originalText,
1220
+ operation: "delete",
1221
+ };
1222
+ }
1223
+ if (name === "move_file") {
1224
+ if (
1225
+ !args.target_path ||
1226
+ !fs.existsSync(filePath) ||
1227
+ fs.statSync(filePath).isDirectory()
1228
+ ) {
1229
+ return null;
1230
+ }
1231
+ const targetPath = path.resolve(cwd, args.target_path);
1232
+ if (targetPath === filePath || fs.existsSync(targetPath)) return null;
1233
+ const targetDir = path.dirname(targetPath);
1234
+ if (!fs.existsSync(targetDir) || !fs.statSync(targetDir).isDirectory()) {
1235
+ return null;
1236
+ }
1237
+ const originalText = fs.readFileSync(filePath, "utf8");
1238
+ return {
1239
+ filePath,
1240
+ targetPath,
1241
+ newContent: originalText,
1242
+ originalText,
1243
+ operation: "rename",
1244
+ };
1106
1245
  }
1107
1246
  if (name === "edit_file") {
1108
1247
  if (!fs.existsSync(filePath)) return null;
@@ -1124,7 +1263,12 @@ export function computeProposedEdit(name, args = {}, cwd = process.cwd()) {
1124
1263
  // No match, or a non-unique match without replace_all → the edit will be
1125
1264
  // rejected, so show no (misleading) diff and let the tool report it.
1126
1265
  if (count === 0 || (count > 1 && !replaceAll)) return null;
1127
- return { filePath, newContent, originalText: content };
1266
+ return {
1267
+ filePath,
1268
+ newContent,
1269
+ originalText: content,
1270
+ operation: "modify",
1271
+ };
1128
1272
  }
1129
1273
  if (name === "edit_file_hashed") {
1130
1274
  if (!fs.existsSync(filePath)) return null;
@@ -1136,7 +1280,12 @@ export function computeProposedEdit(name, args = {}, cwd = process.cwd()) {
1136
1280
  newLine: args.new_line,
1137
1281
  });
1138
1282
  if (!result.success) return null;
1139
- return { filePath, newContent: result.content, originalText: original };
1283
+ return {
1284
+ filePath,
1285
+ newContent: result.content,
1286
+ originalText: original,
1287
+ operation: "modify",
1288
+ };
1140
1289
  }
1141
1290
  } catch {
1142
1291
  // unreadable file etc. → no proposal, normal path handles it
@@ -1184,6 +1333,11 @@ async function tryIdeDiffApprovalForEdit(
1184
1333
  modifiedText: proposal.newContent,
1185
1334
  originalText: proposal.originalText,
1186
1335
  title: `cc agent: ${name} ${path.basename(proposal.filePath)}`,
1336
+ operation: proposal.operation,
1337
+ targetPath: proposal.targetPath,
1338
+ sessionId: context.sessionId || null,
1339
+ turnId: context.turnId || null,
1340
+ toolUseId: context.toolCallId || null,
1187
1341
  });
1188
1342
  if (verdict?.outcome === "accepted") {
1189
1343
  // When the reviewer amended the proposal in the diff before accepting,
@@ -1196,24 +1350,32 @@ async function tryIdeDiffApprovalForEdit(
1196
1350
  : null;
1197
1351
  return {
1198
1352
  outcome: "accepted",
1199
- result: {
1200
- success: true,
1201
- path: proposal.filePath,
1202
- appliedVia: "ide-diff",
1203
- ...(amendments
1204
- ? { userEdited: true, userAmendments: amendments }
1205
- : {}),
1206
- policy: { decision: "allow", rule, via: "ide-diff" },
1207
- },
1353
+ result: attachDiffReviewAudit(
1354
+ {
1355
+ success: true,
1356
+ path: proposal.filePath,
1357
+ operation: proposal.operation,
1358
+ ...(proposal.targetPath ? { targetPath: proposal.targetPath } : {}),
1359
+ appliedVia: "ide-diff",
1360
+ ...(amendments
1361
+ ? { userEdited: true, userAmendments: amendments }
1362
+ : {}),
1363
+ policy: { decision: "allow", rule, via: "ide-diff" },
1364
+ },
1365
+ verdict.audit,
1366
+ ),
1208
1367
  };
1209
1368
  }
1210
1369
  if (verdict?.outcome === "rejected") {
1211
1370
  return {
1212
1371
  outcome: "rejected",
1213
- result: {
1214
- error: `[Permission] "${name}" was rejected in the IDE diff review (${source}: ${rule}).`,
1215
- policy: { decision: "deny", rule, via: "ide-diff" },
1216
- },
1372
+ result: attachDiffReviewAudit(
1373
+ {
1374
+ error: `[Permission] "${name}" was rejected in the IDE diff review (${source}: ${rule}).`,
1375
+ policy: { decision: "deny", rule, via: "ide-diff" },
1376
+ },
1377
+ verdict.audit,
1378
+ ),
1217
1379
  };
1218
1380
  }
1219
1381
  if (verdict?.outcome === "changes-requested") {
@@ -1225,14 +1387,17 @@ async function tryIdeDiffApprovalForEdit(
1225
1387
  "The user requested changes in the IDE diff review (no specific notes).";
1226
1388
  return {
1227
1389
  outcome: "changes-requested",
1228
- result: {
1229
- error:
1230
- `[IDE review] "${name}" was NOT applied — the user requested changes:\n` +
1231
- `${feedback}\n` +
1232
- "Revise the edit to address this feedback, then propose it again.",
1233
- policy: { decision: "deny", rule, via: "ide-diff-review" },
1234
- reviewComments: verdict.comments,
1235
- },
1390
+ result: attachDiffReviewAudit(
1391
+ {
1392
+ error:
1393
+ `[IDE review] "${name}" was NOT applied — the user requested changes:\n` +
1394
+ `${feedback}\n` +
1395
+ "Revise the edit to address this feedback, then propose it again.",
1396
+ policy: { decision: "deny", rule, via: "ide-diff-review" },
1397
+ reviewComments: verdict.comments,
1398
+ },
1399
+ verdict.audit,
1400
+ ),
1236
1401
  };
1237
1402
  }
1238
1403
  } catch (_err) {
@@ -1241,6 +1406,20 @@ async function tryIdeDiffApprovalForEdit(
1241
1406
  return null;
1242
1407
  }
1243
1408
 
1409
+ /**
1410
+ * Keep the audit available to stream/WS ledger consumers without serializing
1411
+ * it into the model-facing tool result (which would waste context tokens).
1412
+ */
1413
+ function attachDiffReviewAudit(result, audit) {
1414
+ if (!result || typeof result !== "object" || !audit) return result;
1415
+ Object.defineProperty(result, "_diffReviewAudit", {
1416
+ value: audit,
1417
+ enumerable: false,
1418
+ configurable: true,
1419
+ });
1420
+ return result;
1421
+ }
1422
+
1244
1423
  /**
1245
1424
  * Execute a single tool call with plan-mode filtering and hook pipeline.
1246
1425
  *
@@ -1264,6 +1443,56 @@ export async function executeTool(name, args, context = {}) {
1264
1443
  : null;
1265
1444
  const runtimeDescriptor =
1266
1445
  getRuntimeToolDescriptor(name) || localToolDescriptor;
1446
+ const admission = context.toolAdmission;
1447
+ if (admission?.enforce === true) {
1448
+ const override =
1449
+ admission.tools && typeof admission.tools === "object"
1450
+ ? admission.tools[name] || {}
1451
+ : {};
1452
+ const tier =
1453
+ override.tier ||
1454
+ runtimeDescriptor?.tier ||
1455
+ getCodingAgentToolPolicy(name)?.tier ||
1456
+ (localToolDescriptor ? "extension" : "mvp");
1457
+ const decision = admitTool({
1458
+ tool: name,
1459
+ tier,
1460
+ capabilityGranted:
1461
+ override.capabilityGranted ?? admission.capabilityGranted,
1462
+ policyAllowed:
1463
+ override.policyAllowed ??
1464
+ (context.hostManagedToolPolicy
1465
+ ? (context.hostManagedToolPolicy?.tools ||
1466
+ context.hostManagedToolPolicy?.toolPolicies ||
1467
+ {})[name]?.allowed !== false
1468
+ : admission.policyAllowed),
1469
+ permissionGranted:
1470
+ override.permissionGranted ?? admission.permissionGranted,
1471
+ budgetOk: override.budgetOk ?? admission.budgetOk,
1472
+ uiSupported: override.uiSupported ?? admission.uiSupported,
1473
+ });
1474
+ const attribution = buildToolAttribution({
1475
+ tool: name,
1476
+ source:
1477
+ override.source ||
1478
+ runtimeDescriptor?.source ||
1479
+ localToolDescriptor?.source ||
1480
+ admission.source ||
1481
+ null,
1482
+ version: override.version || runtimeDescriptor?.version || null,
1483
+ scope: override.scope || localToolDescriptor?.scope || null,
1484
+ callId: context.toolCallId || null,
1485
+ decision,
1486
+ });
1487
+ if (!decision.admitted) {
1488
+ return {
1489
+ error: `[Tool Admission] Tool "${name}" was not admitted: ${decision.unmet.join(", ")}.`,
1490
+ toolAttribution: attribution,
1491
+ policy: { decision: "blocked", via: "tool-admission" },
1492
+ };
1493
+ }
1494
+ context = { ...context, toolAttribution: attribution };
1495
+ }
1267
1496
  const toolContext = createToolContext({
1268
1497
  toolName: runtimeDescriptor?.name || name,
1269
1498
  cwd,
@@ -1397,26 +1626,29 @@ export async function executeTool(name, args, context = {}) {
1397
1626
  // An explicit settings `allow` rule is the only bypass (exact user
1398
1627
  // pre-authorization); headless without a confirmer fails closed.
1399
1628
  if (
1400
- (name === "write_file" ||
1401
- name === "edit_file" ||
1402
- name === "notebook_edit") &&
1403
- settingsVerdict.decision !== "allow" &&
1404
- args?.path
1629
+ GUARDED_FILE_MUTATION_TOOLS.has(name) &&
1630
+ settingsVerdict.decision !== "allow"
1405
1631
  ) {
1406
1632
  const { sensitiveFileReason, autoExecConfigReason } =
1407
1633
  await import("../lib/sensitive-file-guard.js");
1408
- const sensReason =
1409
- sensitiveFileReason(args.path) || autoExecConfigReason(args.path);
1410
- if (sensReason) {
1634
+ const guarded = fileMutationPaths(name, args)
1635
+ .map((candidatePath) => ({
1636
+ path: candidatePath,
1637
+ reason:
1638
+ sensitiveFileReason(candidatePath) ||
1639
+ autoExecConfigReason(candidatePath),
1640
+ }))
1641
+ .find((candidate) => candidate.reason);
1642
+ if (guarded) {
1411
1643
  const ok = await requestInteractivePermission(name, args, context, cwd, {
1412
1644
  tool: name,
1413
1645
  args,
1414
1646
  rule: null,
1415
- reason: `sensitive file: ${sensReason}`,
1647
+ reason: `sensitive file: ${guarded.reason}`,
1416
1648
  });
1417
1649
  if (!ok) {
1418
1650
  return {
1419
- error: `[Sensitive File] Writing "${args.path}" requires confirmation (${sensReason}) — denied. Add a settings allow rule to pre-authorize.`,
1651
+ error: `[Sensitive File] Mutating "${guarded.path}" requires confirmation (${guarded.reason}) — denied. Add a settings allow rule to pre-authorize.`,
1420
1652
  policy: { decision: "ask", via: "sensitive-file" },
1421
1653
  };
1422
1654
  }
@@ -1430,25 +1662,27 @@ export async function executeTool(name, args, context = {}) {
1430
1662
  // sensitive-file guard: confirm-first, an explicit settings `allow` rule is
1431
1663
  // the only bypass, headless without a confirmer fails closed.
1432
1664
  if (
1433
- (name === "write_file" ||
1434
- name === "edit_file" ||
1435
- name === "notebook_edit") &&
1436
- settingsVerdict.decision !== "allow" &&
1437
- args?.path
1665
+ GUARDED_FILE_MUTATION_TOOLS.has(name) &&
1666
+ settingsVerdict.decision !== "allow"
1438
1667
  ) {
1439
1668
  const { sessionStorePathReason } =
1440
1669
  await import("../lib/session-store-guard.js");
1441
- const storeReason = sessionStorePathReason(args.path, { cwd });
1442
- if (storeReason) {
1670
+ const guarded = fileMutationPaths(name, args)
1671
+ .map((candidatePath) => ({
1672
+ path: candidatePath,
1673
+ reason: sessionStorePathReason(candidatePath, { cwd }),
1674
+ }))
1675
+ .find((candidate) => candidate.reason);
1676
+ if (guarded) {
1443
1677
  const ok = await requestInteractivePermission(name, args, context, cwd, {
1444
1678
  tool: name,
1445
1679
  args,
1446
1680
  rule: null,
1447
- reason: `session store: ${storeReason}`,
1681
+ reason: `session store: ${guarded.reason}`,
1448
1682
  });
1449
1683
  if (!ok) {
1450
1684
  return {
1451
- error: `[Session Store] Writing "${args.path}" would modify a session transcript (${storeReason}) — denied. Add a settings allow rule to pre-authorize.`,
1685
+ error: `[Session Store] Mutating "${guarded.path}" would modify a session transcript (${guarded.reason}) — denied. Add a settings allow rule to pre-authorize.`,
1452
1686
  policy: { decision: "ask", via: "session-store-guard" },
1453
1687
  };
1454
1688
  }
@@ -1532,31 +1766,42 @@ export async function executeTool(name, args, context = {}) {
1532
1766
  }
1533
1767
  }
1534
1768
 
1535
- // Plan mode: check if tool is allowed (a settings `allow` rule pre-authorizes)
1769
+ // Plan mode: settings/host allow rules may help exploration, but they cannot
1770
+ // widen the immutable tool set issued when the user approved the plan.
1771
+ const executionLockActive = planManager.executionLock != null;
1536
1772
  if (
1537
1773
  planManager.isActive() &&
1538
- !ruleAllowed &&
1774
+ (!ruleAllowed || executionLockActive) &&
1539
1775
  !(name === "git" && isReadOnlyGitCommand(args.command)) &&
1540
1776
  !planManager.isToolAllowed(name) &&
1541
- !(isExternalHostTool && hostToolPolicy?.allowed === true) &&
1542
- !localReadOnlyAllowedInPlanMode
1777
+ !(
1778
+ !executionLockActive &&
1779
+ isExternalHostTool &&
1780
+ hostToolPolicy?.allowed === true
1781
+ ) &&
1782
+ !(!executionLockActive && localReadOnlyAllowedInPlanMode)
1543
1783
  ) {
1544
- planManager.addPlanItem({
1545
- title: `${name}: ${formatToolArgs(name, args)}`,
1546
- tool: name,
1547
- params: args,
1548
- estimatedImpact:
1549
- name === "run_shell" ||
1550
- name === "run_code" ||
1551
- name === "git" ||
1552
- localToolDescriptor?.riskLevel === "high"
1553
- ? "high"
1554
- : name === "write_file" || localToolDescriptor?.riskLevel === "medium"
1555
- ? "medium"
1556
- : "low",
1557
- });
1784
+ if (!executionLockActive) {
1785
+ planManager.addPlanItem({
1786
+ title: `${name}: ${formatToolArgs(name, args)}`,
1787
+ tool: name,
1788
+ params: args,
1789
+ estimatedImpact:
1790
+ name === "run_shell" ||
1791
+ name === "run_code" ||
1792
+ name === "git" ||
1793
+ localToolDescriptor?.riskLevel === "high"
1794
+ ? "high"
1795
+ : GUARDED_FILE_MUTATION_TOOLS.has(name) ||
1796
+ localToolDescriptor?.riskLevel === "medium"
1797
+ ? "medium"
1798
+ : "low",
1799
+ });
1800
+ }
1558
1801
  return {
1559
- error: `[Plan Mode] Tool "${name}" is blocked during planning. It has been added to the plan. Use /plan approve to execute.`,
1802
+ error: executionLockActive
1803
+ ? `[Plan Execution Lock] Tool "${name}" was not in the approved tool set. Request a plan revision before using it.`
1804
+ : `[Plan Mode] Tool "${name}" is blocked during planning. It has been added to the plan. Use /plan approve to execute.`,
1560
1805
  };
1561
1806
  }
1562
1807
 
@@ -1636,6 +1881,8 @@ export async function executeTool(name, args, context = {}) {
1636
1881
  settingsHooks: context.settingsHooks || null,
1637
1882
  signal: context.signal || null,
1638
1883
  backgroundSubAgents: context.backgroundSubAgents || null,
1884
+ toolAdmission: context.toolAdmission || null,
1885
+ unattendedActionPolicy: context.unattendedActionPolicy || null,
1639
1886
  });
1640
1887
  } catch (err) {
1641
1888
  if (hookDb) {
@@ -1663,6 +1910,9 @@ export async function executeTool(name, args, context = {}) {
1663
1910
  });
1664
1911
  if (toolResult && typeof toolResult === "object") {
1665
1912
  toolResult.toolTelemetryRecord = telemetryRecord;
1913
+ if (context.toolAttribution) {
1914
+ toolResult.toolAttribution = context.toolAttribution;
1915
+ }
1666
1916
  }
1667
1917
 
1668
1918
  // PostToolUse hook
@@ -1717,6 +1967,7 @@ export async function executeTool(name, args, context = {}) {
1717
1967
  const outcome = runCommandHooks(sync, payload, {
1718
1968
  cwd,
1719
1969
  event: "PostToolUse",
1970
+ broker,
1720
1971
  });
1721
1972
  if (outcome.decision === "block" && outcome.reason) {
1722
1973
  toolResult.hookFeedback = outcome.reason;
@@ -1725,7 +1976,7 @@ export async function executeTool(name, args, context = {}) {
1725
1976
  // Fire-and-forget the async hooks onto the REPL-owned supervisor when
1726
1977
  // one is wired; results/rewakes drain into the next turn's context.
1727
1978
  if (asyncHooks.length > 0 && context.hookSupervisor) {
1728
- context.hookSupervisor.dispatch(asyncHooks, payload, { cwd });
1979
+ context.hookSupervisor.dispatch(asyncHooks, payload, { cwd, broker });
1729
1980
  }
1730
1981
  }
1731
1982
  } catch (_err) {
@@ -2433,6 +2684,8 @@ async function executeToolInner(
2433
2684
  signal = null,
2434
2685
  backgroundSubAgents = null,
2435
2686
  subAgentUsageSink = null,
2687
+ toolAdmission = null,
2688
+ unattendedActionPolicy = null,
2436
2689
  // Hook-envelope tracing: this run's trace id, threaded into child loops
2437
2690
  // (spawn_sub_agent / isolated run_skill) as their parent_id.
2438
2691
  hookTraceId = null,
@@ -2599,6 +2852,86 @@ async function executeToolInner(
2599
2852
  );
2600
2853
  }
2601
2854
 
2855
+ case "delete_file": {
2856
+ if (!args.path) {
2857
+ return attachDescriptor({ error: "path is required" });
2858
+ }
2859
+ const filePath = path.resolve(cwd, args.path);
2860
+ if (!fs.existsSync(filePath)) {
2861
+ return attachDescriptor({ error: `File not found: ${filePath}` });
2862
+ }
2863
+ if (fs.lstatSync(filePath).isDirectory()) {
2864
+ return attachDescriptor({
2865
+ error: `Refusing to delete a directory: ${filePath}`,
2866
+ });
2867
+ }
2868
+ const stale = _checkFileFreshness(filePath);
2869
+ if (stale) return attachDescriptor({ error: stale });
2870
+ fs.unlinkSync(filePath);
2871
+ _fileObservedMtimes.delete(filePath);
2872
+ return attachDescriptor({
2873
+ success: true,
2874
+ path: filePath,
2875
+ operation: "delete",
2876
+ });
2877
+ }
2878
+
2879
+ case "move_file": {
2880
+ if (!args.path || !args.target_path) {
2881
+ return attachDescriptor({
2882
+ error: "path and target_path are required",
2883
+ });
2884
+ }
2885
+ const filePath = path.resolve(cwd, args.path);
2886
+ const targetPath = path.resolve(cwd, args.target_path);
2887
+ if (filePath === targetPath) {
2888
+ return attachDescriptor({
2889
+ error: "Source and target paths are the same",
2890
+ });
2891
+ }
2892
+ if (!fs.existsSync(filePath)) {
2893
+ return attachDescriptor({ error: `File not found: ${filePath}` });
2894
+ }
2895
+ if (fs.lstatSync(filePath).isDirectory()) {
2896
+ return attachDescriptor({
2897
+ error: `Refusing to move a directory: ${filePath}`,
2898
+ });
2899
+ }
2900
+ if (fs.existsSync(targetPath)) {
2901
+ return attachDescriptor({
2902
+ error: `Move target already exists: ${targetPath}`,
2903
+ });
2904
+ }
2905
+ const targetDir = path.dirname(targetPath);
2906
+ if (!fs.existsSync(targetDir) || !fs.statSync(targetDir).isDirectory()) {
2907
+ return attachDescriptor({
2908
+ error: `Move target directory not found: ${targetDir}`,
2909
+ });
2910
+ }
2911
+ const stale = _checkFileFreshness(filePath);
2912
+ if (stale) return attachDescriptor({ error: stale });
2913
+ fs.renameSync(filePath, targetPath);
2914
+ _fileObservedMtimes.delete(filePath);
2915
+ _recordFileObservation(targetPath);
2916
+ return attachDescriptor(
2917
+ await _withSubtreeInstructions(
2918
+ await _withPostEditDiagnostics(
2919
+ {
2920
+ success: true,
2921
+ path: filePath,
2922
+ targetPath,
2923
+ operation: "rename",
2924
+ },
2925
+ targetPath,
2926
+ cwd,
2927
+ additionalDirectories,
2928
+ ),
2929
+ targetPath,
2930
+ cwd,
2931
+ ),
2932
+ );
2933
+ }
2934
+
2602
2935
  case "notebook_edit": {
2603
2936
  const filePath = path.resolve(cwd, args.path);
2604
2937
  if (!fs.existsSync(filePath)) {
@@ -2763,6 +3096,23 @@ async function executeToolInner(
2763
3096
  }
2764
3097
 
2765
3098
  case "run_shell": {
3099
+ if (unattendedActionPolicy?.unattended === true) {
3100
+ const unattendedVerdict = evaluateUnattendedShellAction(args.command, {
3101
+ ...unattendedActionPolicy,
3102
+ attended: false,
3103
+ });
3104
+ if (!unattendedVerdict.allow) {
3105
+ return attachDescriptor({
3106
+ error: `[Unattended Action] ${unattendedVerdict.reason}; shell command was not run.`,
3107
+ unattendedAction: {
3108
+ actionClass: unattendedVerdict.actionClass || null,
3109
+ ...unattendedVerdict,
3110
+ },
3111
+ policy: { decision: "deny", via: "unattended-action-policy" },
3112
+ });
3113
+ }
3114
+ }
3115
+
2766
3116
  const shellPolicyOpts = {
2767
3117
  ...(shellPolicyOverrides
2768
3118
  ? { overrideRuleIds: shellPolicyOverrides }
@@ -2877,6 +3227,28 @@ async function executeToolInner(
2877
3227
  : {}
2878
3228
  : { shell: shellInv.kind };
2879
3229
 
3230
+ // PATH-based plugin binaries need explicit provenance at the Broker
3231
+ // boundary; otherwise a trusted plugin executable is indistinguishable
3232
+ // from an ambient system command in the process audit log.
3233
+ let pluginBinProvenance = null;
3234
+ try {
3235
+ const { resolvePluginBinCommand } =
3236
+ await import("../lib/plugin-runtime/bin.js");
3237
+ pluginBinProvenance = resolvePluginBinCommand(args.command, { cwd });
3238
+ } catch {
3239
+ pluginBinProvenance = null;
3240
+ }
3241
+ const processOrigin = pluginBinProvenance
3242
+ ? "plugin:bin"
3243
+ : "tool:run_shell";
3244
+ const processProvenance = pluginBinProvenance
3245
+ ? {
3246
+ pluginId: pluginBinProvenance.pluginId,
3247
+ pluginVersion: pluginBinProvenance.pluginVersion,
3248
+ pluginSource: pluginBinProvenance.pluginSource,
3249
+ }
3250
+ : {};
3251
+
2880
3252
  // Background: spawn, register, return a task_id immediately. The agent
2881
3253
  // polls output + completion via check_shell. No timeout — that's the whole
2882
3254
  // point of backgrounding (builds, test suites, dev servers).
@@ -2936,9 +3308,16 @@ async function executeToolInner(
2936
3308
  };
2937
3309
  // PowerShell route: explicit argv (shell:false above); default route
2938
3310
  // is the historical spawn(command, {shell:true}) byte-for-byte.
3311
+ const brokerOpts = {
3312
+ ...bgSpawnOpts,
3313
+ origin: processOrigin,
3314
+ policy: "allow",
3315
+ scope: "agent",
3316
+ ...processProvenance,
3317
+ };
2939
3318
  const child = shellInv.useDefaultShell
2940
- ? spawn(args.command, bgSpawnOpts)
2941
- : spawn(shellInv.file, shellInv.argv, bgSpawnOpts);
3319
+ ? broker.spawn(args.command, [], brokerOpts)
3320
+ : broker.spawn(shellInv.file, shellInv.argv, brokerOpts);
2942
3321
  task.child = child;
2943
3322
  if (child.stdout) {
2944
3323
  child.stdout.setEncoding("utf8");
@@ -3111,15 +3490,22 @@ async function executeToolInner(
3111
3490
  }).env,
3112
3491
  };
3113
3492
  let output;
3493
+ const brokerExecOpts = {
3494
+ ...fgExecOpts,
3495
+ origin: processOrigin,
3496
+ policy: "allow",
3497
+ scope: "agent",
3498
+ ...processProvenance,
3499
+ };
3114
3500
  if (shellInv.useDefaultShell) {
3115
- output = execSync(args.command, fgExecOpts);
3501
+ output = broker.execSync(args.command, brokerExecOpts);
3116
3502
  } else {
3117
3503
  // PowerShell route: explicit argv, no intermediate default shell.
3118
3504
  // Reproduce execSync's contract so the shared catch shapes errors
3119
3505
  // identically: throw on spawn error; throw an Error carrying
3120
3506
  // status/stdout/stderr on non-zero exit.
3121
- const res = spawnSync(shellInv.file, shellInv.argv, {
3122
- ...fgExecOpts,
3507
+ const res = broker.spawnSync(shellInv.file, shellInv.argv, {
3508
+ ...brokerExecOpts,
3123
3509
  windowsHide: true,
3124
3510
  });
3125
3511
  if (res.error) throw res.error;
@@ -3238,12 +3624,15 @@ async function executeToolInner(
3238
3624
  // command-execution bypass for a prompt-injected agent. Quoted args (a
3239
3625
  // commit message) keep their content via the quote-aware tokenizer.
3240
3626
  const gitArgs = tokenizeShellWords(normalizedCommand);
3241
- const res = spawnSync("git", gitArgs, {
3627
+ const res = _gitProcessDeps.run("git", gitArgs, {
3242
3628
  cwd: args.cwd || cwd,
3243
3629
  encoding: "utf8",
3244
3630
  timeout: 60000,
3245
3631
  maxBuffer: 1024 * 1024,
3246
3632
  windowsHide: true,
3633
+ origin: "agent-core:git-command",
3634
+ policy: "allow",
3635
+ scope: "agent-core",
3247
3636
  });
3248
3637
  const readOnly = isReadOnlyGitCommand(normalizedCommand);
3249
3638
  if (res.error) {
@@ -3351,6 +3740,7 @@ async function executeToolInner(
3351
3740
  signal,
3352
3741
  backgroundSubAgents,
3353
3742
  subAgentUsageSink,
3743
+ toolAdmission,
3354
3744
  }),
3355
3745
  );
3356
3746
  }
@@ -3843,7 +4233,7 @@ async function executeToolInner(
3843
4233
  };
3844
4234
  try {
3845
4235
  if (!fs.existsSync(root)) continue;
3846
- const output = execSync(cmd, {
4236
+ const output = runSearchProcess(cmd, {
3847
4237
  cwd: root,
3848
4238
  encoding: "utf8",
3849
4239
  timeout: 10000,
@@ -4057,6 +4447,7 @@ async function executeToolInner(
4057
4447
  task: `Execute the "${args.skill_name}" skill with input: ${(args.input || "").substring(0, 200)}`,
4058
4448
  allowedTools: ["read_file", "search_files", "list_dir"],
4059
4449
  hookParentTraceId: hookTraceId || null,
4450
+ toolAdmission,
4060
4451
  cwd,
4061
4452
  onUsage: skillUsageSink
4062
4453
  ? (u) => {
@@ -4113,7 +4504,7 @@ async function executeToolInner(
4113
4504
  onWarn: (msg) => {
4114
4505
  // Non-fatal — logged as warning, skipped servers captured
4115
4506
  // in mountResult.skipped.
4116
- // eslint-disable-next-line no-console
4507
+
4117
4508
  console.warn(msg);
4118
4509
  },
4119
4510
  });
@@ -4448,7 +4839,7 @@ async function _executeRunCode(args, cwd) {
4448
4839
  const start = Date.now();
4449
4840
  let output;
4450
4841
  try {
4451
- output = execSync(`${interpreter} "${scriptPath}"`, {
4842
+ output = runCodeProcess(interpreter, [scriptPath], {
4452
4843
  cwd,
4453
4844
  encoding: "utf8",
4454
4845
  timeout: timeoutSec * 1000,
@@ -4548,12 +4939,18 @@ async function _executeRunCode(args, cwd) {
4548
4939
 
4549
4940
  // Attempt pip install
4550
4941
  try {
4551
- execSync(`${interpreter} -m pip install ${packageName}`, {
4552
- encoding: "utf-8",
4553
- timeout: 120000,
4554
- maxBuffer: 2 * 1024 * 1024,
4555
- stdio: ["pipe", "pipe", "pipe"],
4556
- });
4942
+ runCodeProcess(
4943
+ interpreter,
4944
+ ["-m", "pip", "install", packageName],
4945
+ {
4946
+ cwd,
4947
+ encoding: "utf-8",
4948
+ timeout: 120000,
4949
+ maxBuffer: 2 * 1024 * 1024,
4950
+ stdio: ["pipe", "pipe", "pipe"],
4951
+ },
4952
+ "run-code-install",
4953
+ );
4557
4954
  recordInstallAudit({
4558
4955
  package: packageName,
4559
4956
  interpreter,
@@ -4564,12 +4961,17 @@ async function _executeRunCode(args, cwd) {
4564
4961
 
4565
4962
  // Retry execution
4566
4963
  const retryStart = Date.now();
4567
- const retryOutput = execSync(`${interpreter} "${scriptPath}"`, {
4568
- cwd,
4569
- encoding: "utf8",
4570
- timeout: timeoutSec * 1000,
4571
- maxBuffer: 5 * 1024 * 1024,
4572
- });
4964
+ const retryOutput = runCodeProcess(
4965
+ interpreter,
4966
+ [scriptPath],
4967
+ {
4968
+ cwd,
4969
+ encoding: "utf8",
4970
+ timeout: timeoutSec * 1000,
4971
+ maxBuffer: 5 * 1024 * 1024,
4972
+ },
4973
+ "run-code-retry",
4974
+ );
4573
4975
  const retryDuration = Date.now() - retryStart;
4574
4976
 
4575
4977
  return {
@@ -5290,10 +5692,29 @@ async function _executeSpawnSubAgent(args, ctx) {
5290
5692
  // seeded with the mode's tier gates the child's run_shell / browser_act;
5291
5693
  // absent (ungated) for a plain default spawn = byte-identical.
5292
5694
  ...(childApprovalGate ? { approvalGate: childApprovalGate } : {}),
5695
+ ...(ctx.toolAdmission ? { toolAdmission: ctx.toolAdmission } : {}),
5293
5696
  });
5294
5697
  subCtxRef = subCtx;
5295
5698
 
5296
5699
  const emit = (type, payload) => {
5700
+ if (type === "sub-agent.started") {
5701
+ emitHooksV2Event("TaskCreated", {
5702
+ task_id: subCtx.id,
5703
+ session_id: parentSessionId,
5704
+ role: subCtx.role,
5705
+ task: subCtx.task,
5706
+ background: payload?.background === true,
5707
+ });
5708
+ } else if (type === "sub-agent.completed" || type === "sub-agent.failed") {
5709
+ emitHooksV2Event("TaskCompleted", {
5710
+ task_id: subCtx.id,
5711
+ session_id: parentSessionId,
5712
+ role: subCtx.role,
5713
+ status: type === "sub-agent.failed" ? "failed" : "completed",
5714
+ error: payload?.error || null,
5715
+ completed_at: payload?.completedAt || null,
5716
+ });
5717
+ }
5297
5718
  if (!interaction || typeof interaction.emit !== "function") return;
5298
5719
  try {
5299
5720
  interaction.emit(type, {
@@ -6915,6 +7336,12 @@ export async function* agentLoop(messages, options) {
6915
7336
  hostManagedToolPolicy: options.hostManagedToolPolicy || null,
6916
7337
  externalToolDescriptors: options.externalToolDescriptors || null,
6917
7338
  externalToolExecutors: options.externalToolExecutors || null,
7339
+ // Optional session-level Extension Tier gate. Hosts that can provide
7340
+ // capability/policy/permission/budget/UI signals opt in with
7341
+ // { enforce: true }; omitting it preserves the CLI's existing per-call
7342
+ // permission pipeline for backwards compatibility.
7343
+ toolAdmission: options.toolAdmission || null,
7344
+ unattendedActionPolicy: options.unattendedActionPolicy || null,
6918
7345
  // MCP tool DEFINITIONS the LLM sees (mcp__server__tool). Threaded here so a
6919
7346
  // spawn can inherit the parent's MCP tools into the child (filtered by the
6920
7347
  // contract's mcpServers allow-list). Otherwise consumed only at agentLoop.
@@ -7607,11 +8034,11 @@ export async function* agentLoop(messages, options) {
7607
8034
  } catch {
7608
8035
  toolArgs = {};
7609
8036
  }
7610
- const promise = executeTool(
7611
- call.function.name,
7612
- toolArgs,
7613
- toolContext,
7614
- ).then(
8037
+ const promise = executeTool(call.function.name, toolArgs, {
8038
+ ...toolContext,
8039
+ toolCallId: call.id,
8040
+ turnId: `${runId}:t${budget.consumed}`,
8041
+ }).then(
7615
8042
  (result) => ({ result, error: null }),
7616
8043
  (err) => ({ result: { error: err.message }, error: err.message }),
7617
8044
  );
@@ -7724,7 +8151,12 @@ export async function* agentLoop(messages, options) {
7724
8151
  recorder,
7725
8152
  "agent.tool",
7726
8153
  { "tool.name": toolName, ...(toolIdAttrs || {}) },
7727
- () => executeTool(toolName, toolArgs, toolContext),
8154
+ () =>
8155
+ executeTool(toolName, toolArgs, {
8156
+ ...toolContext,
8157
+ toolCallId: call.id,
8158
+ turnId: `${runId}:t${budget.consumed}`,
8159
+ }),
7728
8160
  (span, r) => {
7729
8161
  span.setAttribute(
7730
8162
  "tool.is_error",
@@ -7819,6 +8251,10 @@ export function formatToolArgs(name, args) {
7819
8251
  return args.path;
7820
8252
  case "edit_file_hashed":
7821
8253
  return `${args.path} @${args.anchor_hash}`;
8254
+ case "delete_file":
8255
+ return args.path;
8256
+ case "move_file":
8257
+ return `${args.path} → ${args.target_path}`;
7822
8258
  case "run_shell":
7823
8259
  return args.run_in_background
7824
8260
  ? `${args.command} (background)`