chainlesschain 0.162.174 → 0.162.176

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 (281) hide show
  1. package/package.json +10 -10
  2. package/src/assets/web-panel/assets/{AIOps-Dcl-X1Hw.js → AIOps-BDLu5Elz.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-ClMh9HzT.js → ActionButton-DH5us-CK.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DPj0rSQ5.js → Analytics-BZ0u8IFG.js} +3 -3
  5. package/src/assets/web-panel/assets/AppLayout-Duu8xR14.js +9 -0
  6. package/src/assets/web-panel/assets/{Artifacts-L8781rE8.js → Artifacts-Br2YBbh_.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CffB6WIP.js → Audit-C3C6a482.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-BkLhayyY.js → BackgroundAgents-CzHvebcj.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BQnwFI78.js → Backup-DZNHYDFd.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-xuUGHQch.js → BaseInput-VRiC4fIx.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-feQXwWrg.js → Chat-Cm08WWAD.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-CGHYrugQ.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-DVaIiUGW.js → Checkbox-DtByk7d7.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-D2kEnzeU.js → Codegen-CphXXOfD.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-azDoyusS.js → Col-Hz86TnLR.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-OZdnPWM5.js → Community-HP03ltpD.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-VDMvBN-C.js → Compact-B_N1mqQz.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CPJ_zgQ1.js → Compliance-CVztRpi8.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-iRBwcsOA.js → Cowork-wUVOyZih.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-DrxrspUz.js → Cron-DW5TLJKw.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-D-rkAZXF.js → Crosschain-CrAFGhWH.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-5Pp1Xnmr.js → DID-pA-oD8ke.js} +2 -2
  23. package/src/assets/web-panel/assets/Dashboard-CXrd5gAw.js +3 -0
  24. package/src/assets/web-panel/assets/{Dropdown-BWj_E0Ie.js → Dropdown-EweJcsek.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-BT-wgSEh.js → EmailListRenderer-C9xVLPG4.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-1PzH23pa.js → FamilyGuardDashboard-CDcEEqlu.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-BxMIXGC6.js → Federation-DtZda9A_.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-CyJobBln.js → FormItemContext-CiYdcVz3.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-YkjFuQTN.js → GenericCardRenderer-DA13uwA0.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-C2a98Wp_.js → Git-DzRhZD1O.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-DKL3Fl84.js → Governance-D5RMgOSb.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BJ2cQC_3.js → Inference-BgrPfSNk.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DBAsOngt.js → KnowledgeGraph-DzjnD571.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-COt8g9LO.js → Logs-BhWzVDtN.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-3Lfht1Fd.js → MarkdownRenderer-BDape9cx.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-C9Zk9Pb2.js → Marketplace-DJtL5GV7.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DDWDFWPO.js → McpTools-CZcQygI7.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-BRmVye_o.js → Memory-DUWSDUUu.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-CppBbWpD.js → MobileBridge-BTwq2WKY.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-NZvvfDME.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-CcWpeGa6.js → Mtc-BiGugEQm.js} +6 -6
  42. package/src/assets/web-panel/assets/{MtcAudit-BcllEq0K.js → MtcAudit-DY7a6gUe.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-BFD1CarL.js → Multisig-BQbDu_w7.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-QNXXYvjz.js → NLProgramming-CM2wXTLv.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-07YwgQVy.js → Notes-DI6gGs1v.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-bOccEInE.js → NotificationSettings-BsZOLWAB.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DlRi29WL.js → OrderTableRenderer-C48mu09U.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-CBaezzEr.js → Organization-Cds0_tXr.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Bl5NohFO.js → Overflow-WunYMD87.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-CLMal9Aw.js → P2P-Bo6j4DDv.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CGO0NITk.js → PdhVaultBrowser-Bh2XPD5f.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-CPSw48E2.js → Permissions-EMvMMC8N.js} +3 -3
  53. package/src/assets/web-panel/assets/{PersonalDataHub-CDM5n134.js → PersonalDataHub-AX3ypUDV.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-BdsqG7WV.js → Pipeline-BOx8HOgL.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-B6L4E-M7.js → Privacy-tBeTVQVM.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-DXPyZKRs.js → ProjectInit-CVYg1W4M.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BrCC3xYT.js → ProjectSettings-Dvt5I7y1.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-BYmuHC2q.js → Projects-Cc7FC9-e.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-CbCe2eUl.js → Providers-Ct8xqY-E.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DkukgCrR.js → QrScannerModal-Cni8Ooja.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-D-htOrV8.js → QuickAsk-BJVLKdxr.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-Bm7SIfzu.js → Recommend-DPG8Rbbm.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C9rP-iMc.js → RemoteSession-DsdR7dCt.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-kGlUgbsl.js → Reputation-Csg1YM-b.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-PBupbbUA.js → Row-D9DoFoEf.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-njlwKUGK.js → RssFeed-BsvKrXMS.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-DLdEmgBw.js → Search-DFUStfgQ.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-SXGPSHdu.js → Security-ILg1zxdK.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-CuFGnNsb.js → Services-Dd41PCaY.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-CtUC0mDa.js → Skeleton-Bzo1cN80.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-P1qgOVFn.js → Skills-BDHh17P0.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-CsppE8YV.js → Sla-jl7UTiRY.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-Cq1oG56m.js → SpeechSettings-BePa-RF6.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-DdKW6V-C.js → SyncSettings-DiqqDKGZ.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-BCc9san5.js → Tasks-DYQ_6lxj.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-CcHC1dMA.js → Templates-JiDeI06L.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-CA4PqU22.js → Tenant-hhgI6zwS.js} +1 -1
  78. package/src/assets/web-panel/assets/Terminal-CjTZPa37.js +3 -0
  79. package/src/assets/web-panel/assets/{TimelineRenderer-DHWTSvOy.js → TimelineRenderer-B8uB5Llk.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-Ddz-P_yh.js → Tokens-C3b_6r2z.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-ePmrmLZ1.js → Trigger-tIP7HKVe.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DmuK1CCE.js → Trust-vjoc11vJ.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CqqBuRrT.js → UkeySign-DUPBnGuH.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BGlR3uWM.js → VideoEditing-CBvhUY5T.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-Ba_Qx6Ga.js → Wallet-DUaQ0uA5.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-29neXKt2.js → WebAuthn-DllSivjE.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-lD0lR0UQ.js → WorkflowEditor-xc8D3uO6.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-B3stH01u.js → chat-BVicU7oA.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-Czly56MN.js → colors-DJzLmhLm.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-C0TkbkAJ.js → compact-item-BvYRfThE.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-BJmpSlf1.js → createContext-DGXxCBKR.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-BT46Y_QI.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-D1q-zw7u.js → hasIn-SBDJB1y4.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DV-twjNO.js → index-1SdDS6lR.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-B-kTvMq7.js → index-9gdI7uUW.js} +1 -1
  96. package/src/assets/web-panel/assets/index-B7nuIywP.js +1 -0
  97. package/src/assets/web-panel/assets/{index-Bm_-RKal.js → index-BJZf9DhV.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CQftlZn-.js → index-BO1itZaY.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-Dw3L_smW.js → index-BTrBGBJs.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-C7sFKQ53.js → index-BYqz4Wrr.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DlvsSuBC.js → index-Bf-ctERC.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-ChESIe5r.js → index-CKyEivf8.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DaqEHaCg.js → index-CdGY9P8a.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DZeoKL6t.js → index-Cf-O_g_8.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-XWV8JWMc.js → index-CloCgBUR.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CpMIdPJ0.js → index-CoJAO1Lm.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CgS8XUBU.js → index-Crk5NTb0.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DmUta2lw.js → index-Cs2rmLFt.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Dpz_7sf-.js → index-CtwqO5qo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DRcQsPez.js → index-D7HimK0M.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BEt2-3x5.js → index-DCdRAgjy.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CHvPlzWJ.js → index-DCqRS6da.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BDHXx65P.js → index-DHVLJ8q0.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-K4YaPDOn.js → index-DKrvvWm4.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C1pOqU7D.js → index-DMarmfrv.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DAhrn9EJ.js → index-DRfiXB_p.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-cPjWjvmW.js → index-Dm_77jgR.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-C27TnOoT.js → index-DnjPdvR6.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-upY9rkGZ.js → index-DonCnfMr.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CbvNJUwu.js → index-DvUTDYXX.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BVwJ3jht.js → index-FxZzRyE1.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DIF6XHIi.js → index-MjC4G09d.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-B8KqsomG.js → index-VMfwIH5G.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-8GKykbS4.js → index-YJkzy8ZF.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DpGs96FC.js → index-d5BVd-cL.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DO8xOwxi.js → index-e4fXH_WO.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Cu_4HaUS.js → index-hxTODbM1.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Cf7Gr3NG.js → index-lpaGoTH3.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DBqAm8es.js → index-wcCL-miV.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-ZDUhX41Z.js → index-xfluRkCT.js} +1 -1
  131. package/src/assets/web-panel/assets/index-ycwAfC-i.js +1 -0
  132. package/src/assets/web-panel/assets/{index-z8ATo2aQ.js → index-z1ZylsLq.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-QWEXi6-t.js → initDefaultProps-C1zO1KnY.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BfrQwvV1.js → motion-CWLtEGmC.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-Dn7x0H9t.js → move-DzD_C3Vm.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-F61D_TgE.js → mtc-parser-D880PYJl.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-4SQZ39PF.js → omit-CFR_z2ov.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-C7DTe126.js → pickAttrs-srIhgrSC.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-40NeyzDK.js → placementArrow-C1rXpibP.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-BiUJ6qWs.js → responsiveObserve-C30gRcSS.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-CacJTM3f.js → slide-CvtKiMfV.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-K812HB-k.js → statusUtils-DNHO7ITG.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-CKeHA2eM.js → styleChecker-Dt_Sl-g4.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-CKl4FqXF.js → useFlexGapSupport-Cjev42Gh.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DPoiJjSZ.js → useFs-Baxglj2V.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-aHwBspmt.js → usePersonalDataHub-B4NkcLaP.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-DrA-GuCT.js → vnode-mIRkdT7o.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-G3LMswgy.js → zoom-HzI5DlH_.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 +64 -2
  152. package/src/commands/agent.js +45 -3
  153. package/src/commands/background-session.js +103 -0
  154. package/src/commands/config.js +70 -5
  155. package/src/commands/context.js +36 -2
  156. package/src/commands/hub.js +19 -1
  157. package/src/commands/memory.js +21 -6
  158. package/src/commands/permissions.js +57 -1
  159. package/src/commands/plugin.js +2 -0
  160. package/src/commands/review.js +23 -11
  161. package/src/commands/routine.js +31 -8
  162. package/src/commands/session.js +11 -3
  163. package/src/commands/team.js +20 -7
  164. package/src/commands/update.js +65 -10
  165. package/src/esm-bridge.js +3 -0
  166. package/src/gateways/terminal/PtyManager.js +8 -1
  167. package/src/gateways/ws/message-dispatcher.js +3 -0
  168. package/src/gateways/ws/session-protocol.js +26 -0
  169. package/src/gateways/ws/ws-agent-handler.js +107 -69
  170. package/src/gateways/ws/ws-server.js +92 -4
  171. package/src/harness/jsonl-session-store.js +13 -6
  172. package/src/harness/mcp-client.js +200 -1
  173. package/src/index.js +123 -55
  174. package/src/lazy-dispatch.js +86 -85
  175. package/src/lib/__tests__/personal-data-hub-aichat-wizard.test.js +15 -0
  176. package/src/lib/agent-coordinator.js +4 -0
  177. package/src/lib/agent-ipc-bus.js +32 -2
  178. package/src/lib/agent-sandbox.js +16 -2
  179. package/src/lib/agent-schedule-store.js +55 -0
  180. package/src/lib/agent-tool-admission.js +104 -0
  181. package/src/lib/api-key-helper.js +10 -2
  182. package/src/lib/async-hook-supervisor.cjs +26 -4
  183. package/src/lib/background-interaction-resolver.js +71 -0
  184. package/src/lib/background-session-transport.js +59 -1
  185. package/src/lib/changelog.js +19 -11
  186. package/src/lib/channels/channel-manager.js +6 -0
  187. package/src/lib/channels/telegram-channel.js +11 -1
  188. package/src/lib/channels/webhook-channel.js +15 -3
  189. package/src/lib/checkpoint-store.js +10 -2
  190. package/src/lib/cloud/bundle.js +12 -2
  191. package/src/lib/code-review.js +32 -8
  192. package/src/lib/computer-use/control-backend.js +18 -3
  193. package/src/lib/context-breakdown.js +46 -0
  194. package/src/lib/context-source-ledger.js +43 -0
  195. package/src/lib/crash-reporter.js +6 -0
  196. package/src/lib/diff-review-follow-up.js +154 -0
  197. package/src/lib/downloader.js +17 -3
  198. package/src/lib/ensure-utf8.js +11 -3
  199. package/src/lib/event-runtime-producer.js +58 -0
  200. package/src/lib/event-runtime-store.js +354 -0
  201. package/src/lib/event-runtime-worker.js +109 -0
  202. package/src/lib/execution-backend.js +72 -21
  203. package/src/lib/git-integration.js +19 -4
  204. package/src/lib/hook-manager.js +34 -15
  205. package/src/lib/hook-runner.cjs +48 -3
  206. package/src/lib/hooks-v2-producers.js +15 -0
  207. package/src/lib/hooks-v2-runtime.js +70 -21
  208. package/src/lib/host-adb-bridge.js +111 -21
  209. package/src/lib/ide-context.js +128 -1
  210. package/src/lib/interaction-adapter.js +39 -0
  211. package/src/lib/ipc-attach-protocol.js +98 -0
  212. package/src/lib/json-schema-validate.js +190 -36
  213. package/src/lib/lan-pairing-preflight.js +13 -2
  214. package/src/lib/lsp/__tests__/lsp-server-registry.test.js +18 -0
  215. package/src/lib/lsp/lsp-client.js +18 -1
  216. package/src/lib/lsp/lsp-manager.js +4 -0
  217. package/src/lib/lsp/lsp-server-registry.js +11 -0
  218. package/src/lib/mcp-oauth.js +8 -2
  219. package/src/lib/npm-invocation.js +29 -0
  220. package/src/lib/packer/pkg-runner.js +13 -2
  221. package/src/lib/packer/precheck.js +33 -16
  222. package/src/lib/packer/web-panel-builder.js +16 -2
  223. package/src/lib/permission-rules.cjs +5 -1
  224. package/src/lib/personal-data-hub-aichat-wizard.js +22 -6
  225. package/src/lib/personal-data-hub-wiring.js +27 -3
  226. package/src/lib/plan-mode.js +204 -11
  227. package/src/lib/plugin-monitor-supervisor.js +39 -4
  228. package/src/lib/plugin-runtime/bin.js +45 -0
  229. package/src/lib/plugin-runtime/hooks.js +14 -1
  230. package/src/lib/plugin-runtime/install.js +2 -1
  231. package/src/lib/plugin-runtime/lsp.js +3 -0
  232. package/src/lib/plugin-runtime/mcp.js +8 -1
  233. package/src/lib/plugin-runtime/monitors.js +4 -0
  234. package/src/lib/plugin-runtime/plugin-options.js +74 -7
  235. package/src/lib/plugin-runtime/signature.js +45 -0
  236. package/src/lib/pr-create.js +21 -8
  237. package/src/lib/pr-link-ledger.js +15 -3
  238. package/src/lib/process-execution-broker/credential-agent.js +295 -0
  239. package/src/lib/process-execution-broker/index.js +440 -14
  240. package/src/lib/process-execution-broker/patch-child-process.js +2 -3
  241. package/src/lib/process-execution-broker/platform-sandbox.js +351 -0
  242. package/src/lib/process-manager.js +18 -3
  243. package/src/lib/provenance-api.js +6 -0
  244. package/src/lib/proxy-bootstrap.js +19 -0
  245. package/src/lib/publish-workspace.js +14 -6
  246. package/src/lib/repl-rewind.js +9 -4
  247. package/src/lib/secret-store.js +159 -0
  248. package/src/lib/service-manager.js +64 -29
  249. package/src/lib/session-hooks.js +328 -473
  250. package/src/lib/session-telemetry-middleware.js +6 -0
  251. package/src/lib/settings-hook-events.cjs +61 -1
  252. package/src/lib/settings-hooks.cjs +4 -0
  253. package/src/lib/side-effect-ledger.js +22 -0
  254. package/src/lib/sub-agent-context.js +5 -0
  255. package/src/lib/sub-agent-profiles.js +4 -0
  256. package/src/lib/trace-api.js +6 -0
  257. package/src/lib/turn-context.js +14 -8
  258. package/src/lib/unattended-action-policy.js +83 -0
  259. package/src/lib/update-notice.js +9 -45
  260. package/src/lib/with-file-lock.js +7 -0
  261. package/src/lib/ws-server.js +1 -0
  262. package/src/repl/agent-repl.js +71 -13
  263. package/src/repl/clipboard-copy.js +8 -2
  264. package/src/repl/pr-comments.js +30 -7
  265. package/src/runtime/agent-core.js +439 -87
  266. package/src/runtime/agent-runtime.js +1 -0
  267. package/src/runtime/coding-agent-contract-shared.cjs +52 -0
  268. package/src/runtime/coding-agent-policy.cjs +20 -0
  269. package/src/runtime/diagnostics.js +73 -8
  270. package/src/runtime/headless-runner.js +63 -0
  271. package/src/runtime/headless-stream.js +269 -17
  272. package/src/runtime/mcp-config.js +16 -1
  273. package/src/workers/background-agent-worker.js +80 -1
  274. package/src/assets/web-panel/assets/AppLayout-7iSKDmlI.js +0 -9
  275. package/src/assets/web-panel/assets/ChatBubbleRenderer-DcbUHeGW.js +0 -1
  276. package/src/assets/web-panel/assets/Dashboard-BAKYNavw.js +0 -3
  277. package/src/assets/web-panel/assets/MobileProjects-C4GKn1dH.js +0 -1
  278. package/src/assets/web-panel/assets/Terminal-Fj7ea0In.js +0 -3
  279. package/src/assets/web-panel/assets/devWarning-CDI8Sbaj.js +0 -1
  280. package/src/assets/web-panel/assets/index-96LBR96V.js +0 -1
  281. package/src/assets/web-panel/assets/index-DyQkiW24.js +0 -1
@@ -17,6 +17,7 @@
17
17
  import fs from "fs";
18
18
  import path from "path";
19
19
  import { execSync, spawn, spawnSync } from "child_process";
20
+ import broker from "../lib/process-execution-broker/index.js";
20
21
  import os from "os";
21
22
  import sharedCodingAgentPolicy from "./coding-agent-policy.cjs";
22
23
  import sharedShellPolicy from "./coding-agent-shell-policy.cjs";
@@ -47,6 +48,7 @@ import {
47
48
  import {
48
49
  getCodingAgentFunctionToolDefinitions,
49
50
  listCodingAgentToolNames,
51
+ getCodingAgentToolPolicy,
50
52
  } from "./coding-agent-contract.js";
51
53
  import { createToolContext } from "../tools/tool-context.js";
52
54
  import { createToolTelemetryRecord } from "../tools/tool-telemetry.js";
@@ -78,6 +80,12 @@ import {
78
80
  imageUrlBlockToAnthropic,
79
81
  } from "../lib/image-input.js";
80
82
  import { executeToolSearch, gateDeferredMcpCall } from "./mcp-tool-search.js";
83
+ import { emitHooksV2Event } from "../lib/hooks-v2-producers.js";
84
+ import {
85
+ admitTool,
86
+ buildToolAttribution,
87
+ } from "../lib/agent-tool-admission.js";
88
+ import { evaluateUnattendedShellAction } from "../lib/unattended-action-policy.js";
81
89
 
82
90
  /**
83
91
  * Names of MCP servers currently mounted by an in-flight run_skill call.
@@ -470,8 +478,9 @@ async function runSettingsPreToolUseHooks(name, args, context, cwd) {
470
478
  ? await runCommandHooksParallel(matched, payload, {
471
479
  cwd,
472
480
  event: "PreToolUse",
481
+ broker,
473
482
  })
474
- : runCommandHooks(matched, payload, { cwd, event: "PreToolUse" });
483
+ : runCommandHooks(matched, payload, { cwd, event: "PreToolUse", broker });
475
484
  if (outcome.decision === "block") {
476
485
  return { blocked: true, reason: outcome.reason, hook: outcome.hook };
477
486
  }
@@ -552,6 +561,7 @@ function runSettingsPermissionRequestHooks(name, args, context, cwd, reason) {
552
561
  cwd,
553
562
  event: "PermissionRequest",
554
563
  mergeStrict: _hookStrictMergeEnabled(),
564
+ broker,
555
565
  });
556
566
  // Precedence: deny > ask > allow > defer. The runner normalizes deny/block →
557
567
  // "block" and short-circuits block/ask, but it COLLAPSES an "allow" into the
@@ -770,6 +780,7 @@ You have access to tools that let you read files, write files, edit files, run s
770
780
  Key behaviors:
771
781
  - When asked to modify code, read the file first, then edit it
772
782
  - When asked to create something, use write_file to create it
783
+ - When asked to remove or rename a file, use delete_file or move_file so the filesystem intent is explicit and reviewable
773
784
  - When asked to run/test something, use run_shell to execute it
774
785
  - 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
786
  - When asked about git status, diff, log, or other repository operations, use the git tool instead of run_shell
@@ -926,6 +937,9 @@ export function buildSystemPrompt(cwd, opts = {}) {
926
937
  const personaSkills = allSkills.filter(
927
938
  (s) => s.category === "persona" && s.activation === "auto",
928
939
  );
940
+ if (typeof opts.onSkillsLoaded === "function") {
941
+ opts.onSkillsLoaded(personaSkills);
942
+ }
929
943
  for (const p of personaSkills) {
930
944
  if (p.body?.trim()) {
931
945
  prompt += `\n\n## Persona: ${p.displayName}\n${p.body}`;
@@ -1016,8 +1030,21 @@ const IDE_DIFF_EDIT_TOOLS = new Set([
1016
1030
  "write_file",
1017
1031
  "edit_file",
1018
1032
  "edit_file_hashed",
1033
+ "delete_file",
1034
+ "move_file",
1035
+ ]);
1036
+ const GUARDED_FILE_MUTATION_TOOLS = new Set([
1037
+ ...IDE_DIFF_EDIT_TOOLS,
1038
+ "notebook_edit",
1019
1039
  ]);
1020
1040
 
1041
+ function fileMutationPaths(name, args = {}) {
1042
+ return [
1043
+ args.path,
1044
+ ...(name === "move_file" ? [args.target_path] : []),
1045
+ ].filter(Boolean);
1046
+ }
1047
+
1021
1048
  /**
1022
1049
  * Quote-aware shell tokenizer (mirrors gateways/ws `tokenizeCommand`). The `git`
1023
1050
  * tool runs the model-supplied command via argv (spawnSync, NO shell), so a
@@ -1086,12 +1113,11 @@ function applyLiteralEdit(content, oldStr, newStr, replaceAll) {
1086
1113
  /**
1087
1114
  * Compute the content an edit tool WOULD write, without writing it — the
1088
1115
  * 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.
1116
+ * executeToolInner cases exactly (write/edit/delete/move; hashed edits use the
1117
+ * same pure replaceByHash). Returns an explicit operation plus the left/right
1118
+ * text and optional rename destination, or null when the proposal cannot be
1119
+ * computed — the caller then falls back to normal confirmation so the tool can
1120
+ * produce its own diagnostics.
1095
1121
  */
1096
1122
  export function computeProposedEdit(name, args = {}, cwd = process.cwd()) {
1097
1123
  try {
@@ -1099,10 +1125,49 @@ export function computeProposedEdit(name, args = {}, cwd = process.cwd()) {
1099
1125
  const filePath = path.resolve(cwd, args.path);
1100
1126
  if (name === "write_file") {
1101
1127
  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 };
1128
+ const exists = fs.existsSync(filePath);
1129
+ const originalText = exists ? fs.readFileSync(filePath, "utf8") : "";
1130
+ return {
1131
+ filePath,
1132
+ newContent: args.content,
1133
+ originalText,
1134
+ operation: exists ? "modify" : "create",
1135
+ };
1136
+ }
1137
+ if (name === "delete_file") {
1138
+ if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
1139
+ return null;
1140
+ }
1141
+ const originalText = fs.readFileSync(filePath, "utf8");
1142
+ return {
1143
+ filePath,
1144
+ newContent: "",
1145
+ originalText,
1146
+ operation: "delete",
1147
+ };
1148
+ }
1149
+ if (name === "move_file") {
1150
+ if (
1151
+ !args.target_path ||
1152
+ !fs.existsSync(filePath) ||
1153
+ fs.statSync(filePath).isDirectory()
1154
+ ) {
1155
+ return null;
1156
+ }
1157
+ const targetPath = path.resolve(cwd, args.target_path);
1158
+ if (targetPath === filePath || fs.existsSync(targetPath)) return null;
1159
+ const targetDir = path.dirname(targetPath);
1160
+ if (!fs.existsSync(targetDir) || !fs.statSync(targetDir).isDirectory()) {
1161
+ return null;
1162
+ }
1163
+ const originalText = fs.readFileSync(filePath, "utf8");
1164
+ return {
1165
+ filePath,
1166
+ targetPath,
1167
+ newContent: originalText,
1168
+ originalText,
1169
+ operation: "rename",
1170
+ };
1106
1171
  }
1107
1172
  if (name === "edit_file") {
1108
1173
  if (!fs.existsSync(filePath)) return null;
@@ -1124,7 +1189,12 @@ export function computeProposedEdit(name, args = {}, cwd = process.cwd()) {
1124
1189
  // No match, or a non-unique match without replace_all → the edit will be
1125
1190
  // rejected, so show no (misleading) diff and let the tool report it.
1126
1191
  if (count === 0 || (count > 1 && !replaceAll)) return null;
1127
- return { filePath, newContent, originalText: content };
1192
+ return {
1193
+ filePath,
1194
+ newContent,
1195
+ originalText: content,
1196
+ operation: "modify",
1197
+ };
1128
1198
  }
1129
1199
  if (name === "edit_file_hashed") {
1130
1200
  if (!fs.existsSync(filePath)) return null;
@@ -1136,7 +1206,12 @@ export function computeProposedEdit(name, args = {}, cwd = process.cwd()) {
1136
1206
  newLine: args.new_line,
1137
1207
  });
1138
1208
  if (!result.success) return null;
1139
- return { filePath, newContent: result.content, originalText: original };
1209
+ return {
1210
+ filePath,
1211
+ newContent: result.content,
1212
+ originalText: original,
1213
+ operation: "modify",
1214
+ };
1140
1215
  }
1141
1216
  } catch {
1142
1217
  // unreadable file etc. → no proposal, normal path handles it
@@ -1184,6 +1259,11 @@ async function tryIdeDiffApprovalForEdit(
1184
1259
  modifiedText: proposal.newContent,
1185
1260
  originalText: proposal.originalText,
1186
1261
  title: `cc agent: ${name} ${path.basename(proposal.filePath)}`,
1262
+ operation: proposal.operation,
1263
+ targetPath: proposal.targetPath,
1264
+ sessionId: context.sessionId || null,
1265
+ turnId: context.turnId || null,
1266
+ toolUseId: context.toolCallId || null,
1187
1267
  });
1188
1268
  if (verdict?.outcome === "accepted") {
1189
1269
  // When the reviewer amended the proposal in the diff before accepting,
@@ -1196,24 +1276,32 @@ async function tryIdeDiffApprovalForEdit(
1196
1276
  : null;
1197
1277
  return {
1198
1278
  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
- },
1279
+ result: attachDiffReviewAudit(
1280
+ {
1281
+ success: true,
1282
+ path: proposal.filePath,
1283
+ operation: proposal.operation,
1284
+ ...(proposal.targetPath ? { targetPath: proposal.targetPath } : {}),
1285
+ appliedVia: "ide-diff",
1286
+ ...(amendments
1287
+ ? { userEdited: true, userAmendments: amendments }
1288
+ : {}),
1289
+ policy: { decision: "allow", rule, via: "ide-diff" },
1290
+ },
1291
+ verdict.audit,
1292
+ ),
1208
1293
  };
1209
1294
  }
1210
1295
  if (verdict?.outcome === "rejected") {
1211
1296
  return {
1212
1297
  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
- },
1298
+ result: attachDiffReviewAudit(
1299
+ {
1300
+ error: `[Permission] "${name}" was rejected in the IDE diff review (${source}: ${rule}).`,
1301
+ policy: { decision: "deny", rule, via: "ide-diff" },
1302
+ },
1303
+ verdict.audit,
1304
+ ),
1217
1305
  };
1218
1306
  }
1219
1307
  if (verdict?.outcome === "changes-requested") {
@@ -1225,14 +1313,17 @@ async function tryIdeDiffApprovalForEdit(
1225
1313
  "The user requested changes in the IDE diff review (no specific notes).";
1226
1314
  return {
1227
1315
  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
- },
1316
+ result: attachDiffReviewAudit(
1317
+ {
1318
+ error:
1319
+ `[IDE review] "${name}" was NOT applied — the user requested changes:\n` +
1320
+ `${feedback}\n` +
1321
+ "Revise the edit to address this feedback, then propose it again.",
1322
+ policy: { decision: "deny", rule, via: "ide-diff-review" },
1323
+ reviewComments: verdict.comments,
1324
+ },
1325
+ verdict.audit,
1326
+ ),
1236
1327
  };
1237
1328
  }
1238
1329
  } catch (_err) {
@@ -1241,6 +1332,20 @@ async function tryIdeDiffApprovalForEdit(
1241
1332
  return null;
1242
1333
  }
1243
1334
 
1335
+ /**
1336
+ * Keep the audit available to stream/WS ledger consumers without serializing
1337
+ * it into the model-facing tool result (which would waste context tokens).
1338
+ */
1339
+ function attachDiffReviewAudit(result, audit) {
1340
+ if (!result || typeof result !== "object" || !audit) return result;
1341
+ Object.defineProperty(result, "_diffReviewAudit", {
1342
+ value: audit,
1343
+ enumerable: false,
1344
+ configurable: true,
1345
+ });
1346
+ return result;
1347
+ }
1348
+
1244
1349
  /**
1245
1350
  * Execute a single tool call with plan-mode filtering and hook pipeline.
1246
1351
  *
@@ -1264,6 +1369,56 @@ export async function executeTool(name, args, context = {}) {
1264
1369
  : null;
1265
1370
  const runtimeDescriptor =
1266
1371
  getRuntimeToolDescriptor(name) || localToolDescriptor;
1372
+ const admission = context.toolAdmission;
1373
+ if (admission?.enforce === true) {
1374
+ const override =
1375
+ admission.tools && typeof admission.tools === "object"
1376
+ ? admission.tools[name] || {}
1377
+ : {};
1378
+ const tier =
1379
+ override.tier ||
1380
+ runtimeDescriptor?.tier ||
1381
+ getCodingAgentToolPolicy(name)?.tier ||
1382
+ (localToolDescriptor ? "extension" : "mvp");
1383
+ const decision = admitTool({
1384
+ tool: name,
1385
+ tier,
1386
+ capabilityGranted:
1387
+ override.capabilityGranted ?? admission.capabilityGranted,
1388
+ policyAllowed:
1389
+ override.policyAllowed ??
1390
+ (context.hostManagedToolPolicy ?
1391
+ ((context.hostManagedToolPolicy?.tools ||
1392
+ context.hostManagedToolPolicy?.toolPolicies || {})[name]
1393
+ ?.allowed !== false) :
1394
+ admission.policyAllowed),
1395
+ permissionGranted:
1396
+ override.permissionGranted ?? admission.permissionGranted,
1397
+ budgetOk: override.budgetOk ?? admission.budgetOk,
1398
+ uiSupported: override.uiSupported ?? admission.uiSupported,
1399
+ });
1400
+ const attribution = buildToolAttribution({
1401
+ tool: name,
1402
+ source:
1403
+ override.source ||
1404
+ runtimeDescriptor?.source ||
1405
+ localToolDescriptor?.source ||
1406
+ admission.source ||
1407
+ null,
1408
+ version: override.version || runtimeDescriptor?.version || null,
1409
+ scope: override.scope || localToolDescriptor?.scope || null,
1410
+ callId: context.toolCallId || null,
1411
+ decision,
1412
+ });
1413
+ if (!decision.admitted) {
1414
+ return {
1415
+ error: `[Tool Admission] Tool "${name}" was not admitted: ${decision.unmet.join(", ")}.`,
1416
+ toolAttribution: attribution,
1417
+ policy: { decision: "blocked", via: "tool-admission" },
1418
+ };
1419
+ }
1420
+ context = { ...context, toolAttribution: attribution };
1421
+ }
1267
1422
  const toolContext = createToolContext({
1268
1423
  toolName: runtimeDescriptor?.name || name,
1269
1424
  cwd,
@@ -1397,26 +1552,29 @@ export async function executeTool(name, args, context = {}) {
1397
1552
  // An explicit settings `allow` rule is the only bypass (exact user
1398
1553
  // pre-authorization); headless without a confirmer fails closed.
1399
1554
  if (
1400
- (name === "write_file" ||
1401
- name === "edit_file" ||
1402
- name === "notebook_edit") &&
1403
- settingsVerdict.decision !== "allow" &&
1404
- args?.path
1555
+ GUARDED_FILE_MUTATION_TOOLS.has(name) &&
1556
+ settingsVerdict.decision !== "allow"
1405
1557
  ) {
1406
1558
  const { sensitiveFileReason, autoExecConfigReason } =
1407
1559
  await import("../lib/sensitive-file-guard.js");
1408
- const sensReason =
1409
- sensitiveFileReason(args.path) || autoExecConfigReason(args.path);
1410
- if (sensReason) {
1560
+ const guarded = fileMutationPaths(name, args)
1561
+ .map((candidatePath) => ({
1562
+ path: candidatePath,
1563
+ reason:
1564
+ sensitiveFileReason(candidatePath) ||
1565
+ autoExecConfigReason(candidatePath),
1566
+ }))
1567
+ .find((candidate) => candidate.reason);
1568
+ if (guarded) {
1411
1569
  const ok = await requestInteractivePermission(name, args, context, cwd, {
1412
1570
  tool: name,
1413
1571
  args,
1414
1572
  rule: null,
1415
- reason: `sensitive file: ${sensReason}`,
1573
+ reason: `sensitive file: ${guarded.reason}`,
1416
1574
  });
1417
1575
  if (!ok) {
1418
1576
  return {
1419
- error: `[Sensitive File] Writing "${args.path}" requires confirmation (${sensReason}) — denied. Add a settings allow rule to pre-authorize.`,
1577
+ error: `[Sensitive File] Mutating "${guarded.path}" requires confirmation (${guarded.reason}) — denied. Add a settings allow rule to pre-authorize.`,
1420
1578
  policy: { decision: "ask", via: "sensitive-file" },
1421
1579
  };
1422
1580
  }
@@ -1430,25 +1588,27 @@ export async function executeTool(name, args, context = {}) {
1430
1588
  // sensitive-file guard: confirm-first, an explicit settings `allow` rule is
1431
1589
  // the only bypass, headless without a confirmer fails closed.
1432
1590
  if (
1433
- (name === "write_file" ||
1434
- name === "edit_file" ||
1435
- name === "notebook_edit") &&
1436
- settingsVerdict.decision !== "allow" &&
1437
- args?.path
1591
+ GUARDED_FILE_MUTATION_TOOLS.has(name) &&
1592
+ settingsVerdict.decision !== "allow"
1438
1593
  ) {
1439
1594
  const { sessionStorePathReason } =
1440
1595
  await import("../lib/session-store-guard.js");
1441
- const storeReason = sessionStorePathReason(args.path, { cwd });
1442
- if (storeReason) {
1596
+ const guarded = fileMutationPaths(name, args)
1597
+ .map((candidatePath) => ({
1598
+ path: candidatePath,
1599
+ reason: sessionStorePathReason(candidatePath, { cwd }),
1600
+ }))
1601
+ .find((candidate) => candidate.reason);
1602
+ if (guarded) {
1443
1603
  const ok = await requestInteractivePermission(name, args, context, cwd, {
1444
1604
  tool: name,
1445
1605
  args,
1446
1606
  rule: null,
1447
- reason: `session store: ${storeReason}`,
1607
+ reason: `session store: ${guarded.reason}`,
1448
1608
  });
1449
1609
  if (!ok) {
1450
1610
  return {
1451
- error: `[Session Store] Writing "${args.path}" would modify a session transcript (${storeReason}) — denied. Add a settings allow rule to pre-authorize.`,
1611
+ error: `[Session Store] Mutating "${guarded.path}" would modify a session transcript (${guarded.reason}) — denied. Add a settings allow rule to pre-authorize.`,
1452
1612
  policy: { decision: "ask", via: "session-store-guard" },
1453
1613
  };
1454
1614
  }
@@ -1532,31 +1692,42 @@ export async function executeTool(name, args, context = {}) {
1532
1692
  }
1533
1693
  }
1534
1694
 
1535
- // Plan mode: check if tool is allowed (a settings `allow` rule pre-authorizes)
1695
+ // Plan mode: settings/host allow rules may help exploration, but they cannot
1696
+ // widen the immutable tool set issued when the user approved the plan.
1697
+ const executionLockActive = planManager.executionLock != null;
1536
1698
  if (
1537
1699
  planManager.isActive() &&
1538
- !ruleAllowed &&
1700
+ (!ruleAllowed || executionLockActive) &&
1539
1701
  !(name === "git" && isReadOnlyGitCommand(args.command)) &&
1540
1702
  !planManager.isToolAllowed(name) &&
1541
- !(isExternalHostTool && hostToolPolicy?.allowed === true) &&
1542
- !localReadOnlyAllowedInPlanMode
1703
+ !(
1704
+ !executionLockActive &&
1705
+ isExternalHostTool &&
1706
+ hostToolPolicy?.allowed === true
1707
+ ) &&
1708
+ !(!executionLockActive && localReadOnlyAllowedInPlanMode)
1543
1709
  ) {
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
- });
1710
+ if (!executionLockActive) {
1711
+ planManager.addPlanItem({
1712
+ title: `${name}: ${formatToolArgs(name, args)}`,
1713
+ tool: name,
1714
+ params: args,
1715
+ estimatedImpact:
1716
+ name === "run_shell" ||
1717
+ name === "run_code" ||
1718
+ name === "git" ||
1719
+ localToolDescriptor?.riskLevel === "high"
1720
+ ? "high"
1721
+ : GUARDED_FILE_MUTATION_TOOLS.has(name) ||
1722
+ localToolDescriptor?.riskLevel === "medium"
1723
+ ? "medium"
1724
+ : "low",
1725
+ });
1726
+ }
1558
1727
  return {
1559
- error: `[Plan Mode] Tool "${name}" is blocked during planning. It has been added to the plan. Use /plan approve to execute.`,
1728
+ error: executionLockActive
1729
+ ? `[Plan Execution Lock] Tool "${name}" was not in the approved tool set. Request a plan revision before using it.`
1730
+ : `[Plan Mode] Tool "${name}" is blocked during planning. It has been added to the plan. Use /plan approve to execute.`,
1560
1731
  };
1561
1732
  }
1562
1733
 
@@ -1636,6 +1807,8 @@ export async function executeTool(name, args, context = {}) {
1636
1807
  settingsHooks: context.settingsHooks || null,
1637
1808
  signal: context.signal || null,
1638
1809
  backgroundSubAgents: context.backgroundSubAgents || null,
1810
+ toolAdmission: context.toolAdmission || null,
1811
+ unattendedActionPolicy: context.unattendedActionPolicy || null,
1639
1812
  });
1640
1813
  } catch (err) {
1641
1814
  if (hookDb) {
@@ -1663,6 +1836,9 @@ export async function executeTool(name, args, context = {}) {
1663
1836
  });
1664
1837
  if (toolResult && typeof toolResult === "object") {
1665
1838
  toolResult.toolTelemetryRecord = telemetryRecord;
1839
+ if (context.toolAttribution) {
1840
+ toolResult.toolAttribution = context.toolAttribution;
1841
+ }
1666
1842
  }
1667
1843
 
1668
1844
  // PostToolUse hook
@@ -1717,6 +1893,7 @@ export async function executeTool(name, args, context = {}) {
1717
1893
  const outcome = runCommandHooks(sync, payload, {
1718
1894
  cwd,
1719
1895
  event: "PostToolUse",
1896
+ broker,
1720
1897
  });
1721
1898
  if (outcome.decision === "block" && outcome.reason) {
1722
1899
  toolResult.hookFeedback = outcome.reason;
@@ -1725,7 +1902,7 @@ export async function executeTool(name, args, context = {}) {
1725
1902
  // Fire-and-forget the async hooks onto the REPL-owned supervisor when
1726
1903
  // one is wired; results/rewakes drain into the next turn's context.
1727
1904
  if (asyncHooks.length > 0 && context.hookSupervisor) {
1728
- context.hookSupervisor.dispatch(asyncHooks, payload, { cwd });
1905
+ context.hookSupervisor.dispatch(asyncHooks, payload, { cwd, broker });
1729
1906
  }
1730
1907
  }
1731
1908
  } catch (_err) {
@@ -2433,6 +2610,8 @@ async function executeToolInner(
2433
2610
  signal = null,
2434
2611
  backgroundSubAgents = null,
2435
2612
  subAgentUsageSink = null,
2613
+ toolAdmission = null,
2614
+ unattendedActionPolicy = null,
2436
2615
  // Hook-envelope tracing: this run's trace id, threaded into child loops
2437
2616
  // (spawn_sub_agent / isolated run_skill) as their parent_id.
2438
2617
  hookTraceId = null,
@@ -2599,6 +2778,86 @@ async function executeToolInner(
2599
2778
  );
2600
2779
  }
2601
2780
 
2781
+ case "delete_file": {
2782
+ if (!args.path) {
2783
+ return attachDescriptor({ error: "path is required" });
2784
+ }
2785
+ const filePath = path.resolve(cwd, args.path);
2786
+ if (!fs.existsSync(filePath)) {
2787
+ return attachDescriptor({ error: `File not found: ${filePath}` });
2788
+ }
2789
+ if (fs.lstatSync(filePath).isDirectory()) {
2790
+ return attachDescriptor({
2791
+ error: `Refusing to delete a directory: ${filePath}`,
2792
+ });
2793
+ }
2794
+ const stale = _checkFileFreshness(filePath);
2795
+ if (stale) return attachDescriptor({ error: stale });
2796
+ fs.unlinkSync(filePath);
2797
+ _fileObservedMtimes.delete(filePath);
2798
+ return attachDescriptor({
2799
+ success: true,
2800
+ path: filePath,
2801
+ operation: "delete",
2802
+ });
2803
+ }
2804
+
2805
+ case "move_file": {
2806
+ if (!args.path || !args.target_path) {
2807
+ return attachDescriptor({
2808
+ error: "path and target_path are required",
2809
+ });
2810
+ }
2811
+ const filePath = path.resolve(cwd, args.path);
2812
+ const targetPath = path.resolve(cwd, args.target_path);
2813
+ if (filePath === targetPath) {
2814
+ return attachDescriptor({
2815
+ error: "Source and target paths are the same",
2816
+ });
2817
+ }
2818
+ if (!fs.existsSync(filePath)) {
2819
+ return attachDescriptor({ error: `File not found: ${filePath}` });
2820
+ }
2821
+ if (fs.lstatSync(filePath).isDirectory()) {
2822
+ return attachDescriptor({
2823
+ error: `Refusing to move a directory: ${filePath}`,
2824
+ });
2825
+ }
2826
+ if (fs.existsSync(targetPath)) {
2827
+ return attachDescriptor({
2828
+ error: `Move target already exists: ${targetPath}`,
2829
+ });
2830
+ }
2831
+ const targetDir = path.dirname(targetPath);
2832
+ if (!fs.existsSync(targetDir) || !fs.statSync(targetDir).isDirectory()) {
2833
+ return attachDescriptor({
2834
+ error: `Move target directory not found: ${targetDir}`,
2835
+ });
2836
+ }
2837
+ const stale = _checkFileFreshness(filePath);
2838
+ if (stale) return attachDescriptor({ error: stale });
2839
+ fs.renameSync(filePath, targetPath);
2840
+ _fileObservedMtimes.delete(filePath);
2841
+ _recordFileObservation(targetPath);
2842
+ return attachDescriptor(
2843
+ await _withSubtreeInstructions(
2844
+ await _withPostEditDiagnostics(
2845
+ {
2846
+ success: true,
2847
+ path: filePath,
2848
+ targetPath,
2849
+ operation: "rename",
2850
+ },
2851
+ targetPath,
2852
+ cwd,
2853
+ additionalDirectories,
2854
+ ),
2855
+ targetPath,
2856
+ cwd,
2857
+ ),
2858
+ );
2859
+ }
2860
+
2602
2861
  case "notebook_edit": {
2603
2862
  const filePath = path.resolve(cwd, args.path);
2604
2863
  if (!fs.existsSync(filePath)) {
@@ -2763,6 +3022,23 @@ async function executeToolInner(
2763
3022
  }
2764
3023
 
2765
3024
  case "run_shell": {
3025
+ if (unattendedActionPolicy?.unattended === true) {
3026
+ const unattendedVerdict = evaluateUnattendedShellAction(args.command, {
3027
+ ...unattendedActionPolicy,
3028
+ attended: false,
3029
+ });
3030
+ if (!unattendedVerdict.allow) {
3031
+ return attachDescriptor({
3032
+ error: `[Unattended Action] ${unattendedVerdict.reason}; shell command was not run.`,
3033
+ unattendedAction: {
3034
+ actionClass: unattendedVerdict.actionClass || null,
3035
+ ...unattendedVerdict,
3036
+ },
3037
+ policy: { decision: "deny", via: "unattended-action-policy" },
3038
+ });
3039
+ }
3040
+ }
3041
+
2766
3042
  const shellPolicyOpts = {
2767
3043
  ...(shellPolicyOverrides
2768
3044
  ? { overrideRuleIds: shellPolicyOverrides }
@@ -2877,6 +3153,29 @@ async function executeToolInner(
2877
3153
  : {}
2878
3154
  : { shell: shellInv.kind };
2879
3155
 
3156
+ // PATH-based plugin binaries need explicit provenance at the Broker
3157
+ // boundary; otherwise a trusted plugin executable is indistinguishable
3158
+ // from an ambient system command in the process audit log.
3159
+ let pluginBinProvenance = null;
3160
+ try {
3161
+ const { resolvePluginBinCommand } = await import(
3162
+ "../lib/plugin-runtime/bin.js"
3163
+ );
3164
+ pluginBinProvenance = resolvePluginBinCommand(args.command, { cwd });
3165
+ } catch {
3166
+ pluginBinProvenance = null;
3167
+ }
3168
+ const processOrigin = pluginBinProvenance
3169
+ ? "plugin:bin"
3170
+ : "tool:run_shell";
3171
+ const processProvenance = pluginBinProvenance
3172
+ ? {
3173
+ pluginId: pluginBinProvenance.pluginId,
3174
+ pluginVersion: pluginBinProvenance.pluginVersion,
3175
+ pluginSource: pluginBinProvenance.pluginSource,
3176
+ }
3177
+ : {};
3178
+
2880
3179
  // Background: spawn, register, return a task_id immediately. The agent
2881
3180
  // polls output + completion via check_shell. No timeout — that's the whole
2882
3181
  // point of backgrounding (builds, test suites, dev servers).
@@ -2936,9 +3235,16 @@ async function executeToolInner(
2936
3235
  };
2937
3236
  // PowerShell route: explicit argv (shell:false above); default route
2938
3237
  // is the historical spawn(command, {shell:true}) byte-for-byte.
3238
+ const brokerOpts = {
3239
+ ...bgSpawnOpts,
3240
+ origin: processOrigin,
3241
+ policy: "allow",
3242
+ scope: "agent",
3243
+ ...processProvenance,
3244
+ };
2939
3245
  const child = shellInv.useDefaultShell
2940
- ? spawn(args.command, bgSpawnOpts)
2941
- : spawn(shellInv.file, shellInv.argv, bgSpawnOpts);
3246
+ ? broker.spawn(args.command, [], brokerOpts)
3247
+ : broker.spawn(shellInv.file, shellInv.argv, brokerOpts);
2942
3248
  task.child = child;
2943
3249
  if (child.stdout) {
2944
3250
  child.stdout.setEncoding("utf8");
@@ -3111,15 +3417,22 @@ async function executeToolInner(
3111
3417
  }).env,
3112
3418
  };
3113
3419
  let output;
3420
+ const brokerExecOpts = {
3421
+ ...fgExecOpts,
3422
+ origin: processOrigin,
3423
+ policy: "allow",
3424
+ scope: "agent",
3425
+ ...processProvenance,
3426
+ };
3114
3427
  if (shellInv.useDefaultShell) {
3115
- output = execSync(args.command, fgExecOpts);
3428
+ output = broker.execSync(args.command, brokerExecOpts);
3116
3429
  } else {
3117
3430
  // PowerShell route: explicit argv, no intermediate default shell.
3118
3431
  // Reproduce execSync's contract so the shared catch shapes errors
3119
3432
  // identically: throw on spawn error; throw an Error carrying
3120
3433
  // status/stdout/stderr on non-zero exit.
3121
- const res = spawnSync(shellInv.file, shellInv.argv, {
3122
- ...fgExecOpts,
3434
+ const res = broker.spawnSync(shellInv.file, shellInv.argv, {
3435
+ ...brokerExecOpts,
3123
3436
  windowsHide: true,
3124
3437
  });
3125
3438
  if (res.error) throw res.error;
@@ -3351,6 +3664,7 @@ async function executeToolInner(
3351
3664
  signal,
3352
3665
  backgroundSubAgents,
3353
3666
  subAgentUsageSink,
3667
+ toolAdmission,
3354
3668
  }),
3355
3669
  );
3356
3670
  }
@@ -4057,6 +4371,7 @@ async function executeToolInner(
4057
4371
  task: `Execute the "${args.skill_name}" skill with input: ${(args.input || "").substring(0, 200)}`,
4058
4372
  allowedTools: ["read_file", "search_files", "list_dir"],
4059
4373
  hookParentTraceId: hookTraceId || null,
4374
+ toolAdmission,
4060
4375
  cwd,
4061
4376
  onUsage: skillUsageSink
4062
4377
  ? (u) => {
@@ -5290,10 +5605,32 @@ async function _executeSpawnSubAgent(args, ctx) {
5290
5605
  // seeded with the mode's tier gates the child's run_shell / browser_act;
5291
5606
  // absent (ungated) for a plain default spawn = byte-identical.
5292
5607
  ...(childApprovalGate ? { approvalGate: childApprovalGate } : {}),
5608
+ ...(ctx.toolAdmission ? { toolAdmission: ctx.toolAdmission } : {}),
5293
5609
  });
5294
5610
  subCtxRef = subCtx;
5295
5611
 
5296
5612
  const emit = (type, payload) => {
5613
+ if (type === "sub-agent.started") {
5614
+ emitHooksV2Event("TaskCreated", {
5615
+ task_id: subCtx.id,
5616
+ session_id: parentSessionId,
5617
+ role: subCtx.role,
5618
+ task: subCtx.task,
5619
+ background: payload?.background === true,
5620
+ });
5621
+ } else if (
5622
+ type === "sub-agent.completed" ||
5623
+ type === "sub-agent.failed"
5624
+ ) {
5625
+ emitHooksV2Event("TaskCompleted", {
5626
+ task_id: subCtx.id,
5627
+ session_id: parentSessionId,
5628
+ role: subCtx.role,
5629
+ status: type === "sub-agent.failed" ? "failed" : "completed",
5630
+ error: payload?.error || null,
5631
+ completed_at: payload?.completedAt || null,
5632
+ });
5633
+ }
5297
5634
  if (!interaction || typeof interaction.emit !== "function") return;
5298
5635
  try {
5299
5636
  interaction.emit(type, {
@@ -6915,6 +7252,12 @@ export async function* agentLoop(messages, options) {
6915
7252
  hostManagedToolPolicy: options.hostManagedToolPolicy || null,
6916
7253
  externalToolDescriptors: options.externalToolDescriptors || null,
6917
7254
  externalToolExecutors: options.externalToolExecutors || null,
7255
+ // Optional session-level Extension Tier gate. Hosts that can provide
7256
+ // capability/policy/permission/budget/UI signals opt in with
7257
+ // { enforce: true }; omitting it preserves the CLI's existing per-call
7258
+ // permission pipeline for backwards compatibility.
7259
+ toolAdmission: options.toolAdmission || null,
7260
+ unattendedActionPolicy: options.unattendedActionPolicy || null,
6918
7261
  // MCP tool DEFINITIONS the LLM sees (mcp__server__tool). Threaded here so a
6919
7262
  // spawn can inherit the parent's MCP tools into the child (filtered by the
6920
7263
  // contract's mcpServers allow-list). Otherwise consumed only at agentLoop.
@@ -7601,17 +7944,17 @@ export async function* agentLoop(messages, options) {
7601
7944
  let toolArgs;
7602
7945
  try {
7603
7946
  toolArgs =
7604
- typeof call.function.arguments === "string"
7605
- ? JSON.parse(call.function.arguments)
7606
- : call.function.arguments;
7947
+ typeof call.function.arguments === "string"
7948
+ ? JSON.parse(call.function.arguments)
7949
+ : call.function.arguments;
7607
7950
  } catch {
7608
7951
  toolArgs = {};
7609
7952
  }
7610
- const promise = executeTool(
7611
- call.function.name,
7612
- toolArgs,
7613
- toolContext,
7614
- ).then(
7953
+ const promise = executeTool(call.function.name, toolArgs, {
7954
+ ...toolContext,
7955
+ toolCallId: call.id,
7956
+ turnId: `${runId}:t${budget.consumed}`,
7957
+ }).then(
7615
7958
  (result) => ({ result, error: null }),
7616
7959
  (err) => ({ result: { error: err.message }, error: err.message }),
7617
7960
  );
@@ -7724,7 +8067,12 @@ export async function* agentLoop(messages, options) {
7724
8067
  recorder,
7725
8068
  "agent.tool",
7726
8069
  { "tool.name": toolName, ...(toolIdAttrs || {}) },
7727
- () => executeTool(toolName, toolArgs, toolContext),
8070
+ () =>
8071
+ executeTool(toolName, toolArgs, {
8072
+ ...toolContext,
8073
+ toolCallId: call.id,
8074
+ turnId: `${runId}:t${budget.consumed}`,
8075
+ }),
7728
8076
  (span, r) => {
7729
8077
  span.setAttribute(
7730
8078
  "tool.is_error",
@@ -7819,6 +8167,10 @@ export function formatToolArgs(name, args) {
7819
8167
  return args.path;
7820
8168
  case "edit_file_hashed":
7821
8169
  return `${args.path} @${args.anchor_hash}`;
8170
+ case "delete_file":
8171
+ return args.path;
8172
+ case "move_file":
8173
+ return `${args.path} → ${args.target_path}`;
7822
8174
  case "run_shell":
7823
8175
  return args.run_in_background
7824
8176
  ? `${args.command} (background)`