chainlesschain 0.162.176 → 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 (221) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-BDLu5Elz.js → AIOps-DT-VPWuN.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-DH5us-CK.js → ActionButton-DyOreOVN.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-BZ0u8IFG.js → Analytics-DslNsKux.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-Duu8xR14.js → AppLayout-BNJJAYME.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-Br2YBbh_.js → Artifacts-DoaAtEcY.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-C3C6a482.js → Audit-DPG3WdJ9.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-CzHvebcj.js → BackgroundAgents-C_JO6R5G.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-DZNHYDFd.js → Backup-Cv3BooyX.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-VRiC4fIx.js → BaseInput-D2uQXWu-.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-Cm08WWAD.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-DtByk7d7.js → Checkbox-qTgk4VeP.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-CphXXOfD.js → Codegen-DlLWcIzj.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-Hz86TnLR.js → Col-DNO73qp7.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-HP03ltpD.js → Community-vRDNpc4j.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-B_N1mqQz.js → Compact-CEWtIXni.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CVztRpi8.js → Compliance-KgYC9Fxi.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-wUVOyZih.js → Cowork-zj5wvkpT.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-DW5TLJKw.js → Cron-BLDoCozU.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-CrAFGhWH.js → Crosschain-BYnff4VY.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-pA-oD8ke.js → DID-C75VFsVm.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-CXrd5gAw.js → Dashboard-C4Gutni2.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-EweJcsek.js → Dropdown-B5dbO4Kx.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-C9xVLPG4.js → EmailListRenderer-BDipZtHJ.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CDcEEqlu.js → FamilyGuardDashboard-DmIzt8jj.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-DtZda9A_.js → Federation-D9QOS-uW.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-CiYdcVz3.js → FormItemContext-B47HRwAm.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DA13uwA0.js → GenericCardRenderer-3R4mXmgV.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DzRhZD1O.js → Git-DrNXZf1C.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-D5RMgOSb.js → Governance-BGd7JR98.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BgrPfSNk.js → Inference-D-2he1ed.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DzjnD571.js → KnowledgeGraph-C0sHIbK5.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-BhWzVDtN.js → Logs-DiBQO-jL.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BDape9cx.js → MarkdownRenderer-DL_5eV2D.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-DJtL5GV7.js → Marketplace-VR59OdrC.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-CZcQygI7.js → McpTools-DrPzwSd3.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-DUWSDUUu.js → Memory-CBgKALwQ.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BTwq2WKY.js → MobileBridge-pQ9Tn-Mn.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-mASB6jyj.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-BiGugEQm.js → Mtc-DTMgE7HC.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-DY7a6gUe.js → MtcAudit-CwTnCtiL.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-BQbDu_w7.js → Multisig-ngm6WU8f.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-CM2wXTLv.js → NLProgramming-4YO4Qww9.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-DI6gGs1v.js → Notes-DP1k45mF.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-BsZOLWAB.js → NotificationSettings-r2H8yPKb.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-C48mu09U.js → OrderTableRenderer-CoM_Zw0J.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-Cds0_tXr.js → Organization-DrO7jRWS.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-WunYMD87.js → Overflow-Cnwu4i4t.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-Bo6j4DDv.js → P2P-zi-cDwA_.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Bh2XPD5f.js → PdhVaultBrowser-DBEryDv7.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-EMvMMC8N.js → Permissions-lqXK2C3x.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-AX3ypUDV.js → PersonalDataHub-b63T3HJM.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-BOx8HOgL.js → Pipeline-Kep5KAGW.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-tBeTVQVM.js → Privacy-CHdvOAh1.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-CVYg1W4M.js → ProjectInit-BhKfdwe9.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-Dvt5I7y1.js → ProjectSettings-Yavk9K8A.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-Cc7FC9-e.js → Projects-CN8E_5Mq.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-Ct8xqY-E.js → Providers-Bfym6SQn.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-Cni8Ooja.js → QrScannerModal-CLSQcrlR.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-BJVLKdxr.js → QuickAsk-VPa2WKQN.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-DPG8Rbbm.js → Recommend-CGXR8jVU.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-DsdR7dCt.js → RemoteSession-C_20YfMB.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-Csg1YM-b.js → Reputation-awZh68UV.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-D9DoFoEf.js → Row-Ctb4oiYN.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-BsvKrXMS.js → RssFeed-D3sdjvUV.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-DFUStfgQ.js → Search-C-zfA4kT.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-ILg1zxdK.js → Security-D9XlmHA3.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-Dd41PCaY.js → Services-C3UKnr41.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-Bzo1cN80.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-jl7UTiRY.js → Sla-DveENvek.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-BePa-RF6.js → SpeechSettings-BFjXLNgF.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-DiqqDKGZ.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-JiDeI06L.js → Templates-DEiaoyve.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-hhgI6zwS.js → Tenant-ITg774nY.js} +1 -1
  78. package/src/assets/web-panel/assets/Terminal-D3ZBcvnb.js +3 -0
  79. package/src/assets/web-panel/assets/{TimelineRenderer-B8uB5Llk.js → TimelineRenderer-BsLU6JjU.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-C3b_6r2z.js → Tokens-Cb5Pquc9.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-tIP7HKVe.js → Trigger-Dwfy-m9N.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-vjoc11vJ.js → Trust-CnlSRGG0.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-DUPBnGuH.js → UkeySign-Y4TlvKVo.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-CBvhUY5T.js → VideoEditing-BpKLtTTO.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-DUaQ0uA5.js → Wallet-LLBzIq8x.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-DllSivjE.js → WebAuthn-BeSRiusP.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-xc8D3uO6.js → WorkflowEditor-BK_l1tXO.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-BVicU7oA.js → chat-DYd-X7Wg.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-DJzLmhLm.js → colors-C9uip71j.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-BvYRfThE.js → compact-item-JBrfjUP6.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-DGXxCBKR.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-SBDJB1y4.js → hasIn-DLQOR6l-.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-MjC4G09d.js → index-5YN7F5bO.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Dm_77jgR.js → index-6GuLRpBQ.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CtwqO5qo.js → index-B0X76LjX.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DRfiXB_p.js → index-B46YcgsV.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-VMfwIH5G.js → index-BNCdRmWf.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-hxTODbM1.js → index-BYv-LWdy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DMarmfrv.js → index-Ba-Ps_AJ.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-xfluRkCT.js → index-BcBZwlnG.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BYqz4Wrr.js → index-BdH9l8wl.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BO1itZaY.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-Cf-O_g_8.js → index-BnQgRjsm.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CKyEivf8.js → index-BpYKLrUT.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CoJAO1Lm.js → index-BuNFCZCx.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-1SdDS6lR.js → index-ByHO3SqZ.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DnjPdvR6.js → index-C2zmOsyD.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Bf-ctERC.js → index-CChQBM1d.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DCdRAgjy.js → index-CChaISMb.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Crk5NTb0.js → index-CILVyK3b.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-9gdI7uUW.js → index-CL0ixCxk.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BJZf9DhV.js → index-CS63r8h3.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DCqRS6da.js → index-CVZWiUE_.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DonCnfMr.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-CloCgBUR.js → index-Ck17i0OD.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-wcCL-miV.js → index-Cpn19c9k.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BTrBGBJs.js → index-CqWk1FLG.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-e4fXH_WO.js → index-CwoDpKzW.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DvUTDYXX.js → index-D-1SqEpQ.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CdGY9P8a.js → index-D4UY5Z60.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DKrvvWm4.js → index-D8KvuDz0.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-lpaGoTH3.js → index-DNMAAUVk.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-YJkzy8ZF.js → index-DYLZY4uh.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-FxZzRyE1.js → index-DaokIHt3.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DHVLJ8q0.js → index-Q7mmL6w4.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Cs2rmLFt.js → index-SO6lgMg0.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-z1ZylsLq.js → index-iahNhZwm.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-d5BVd-cL.js → index-l08afIbV.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D7HimK0M.js → index-rIxxfdTN.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-C1zO1KnY.js → initDefaultProps-CG9z1c6w.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-CWLtEGmC.js → motion-B1Ky7Zff.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-DzD_C3Vm.js → move-DME8X2P1.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-D880PYJl.js → mtc-parser-cuJYSWB7.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-CFR_z2ov.js → omit-UI_EE0uZ.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-srIhgrSC.js → pickAttrs-BIoE_Z_V.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-C1rXpibP.js → placementArrow-B48-9HKN.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-C30gRcSS.js → responsiveObserve-D7x_GDgD.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-CvtKiMfV.js → slide-DjzC5_7m.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-DNHO7ITG.js → statusUtils-C6BIymhh.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-Dt_Sl-g4.js → styleChecker-B4dGrdD9.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-Cjev42Gh.js → useFlexGapSupport-Bken4aNE.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-Baxglj2V.js → useFs-BMlqECqv.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-B4NkcLaP.js → usePersonalDataHub-DLdrfmMa.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-mIRkdT7o.js → vnode-CKJQmxa0.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-HzI5DlH_.js → zoom-CaZunuau.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agenda.js +22 -6
  151. package/src/commands/agent.js +3 -3
  152. package/src/commands/background-session.js +11 -4
  153. package/src/commands/batch.js +79 -27
  154. package/src/commands/changelog.js +15 -4
  155. package/src/commands/eval.js +21 -6
  156. package/src/commands/hook.js +2 -2
  157. package/src/commands/loop.js +9 -2
  158. package/src/commands/skill.js +2 -0
  159. package/src/commands/statusline.js +3 -3
  160. package/src/data/changelog.json +293 -51
  161. package/src/harness/background-task-manager.js +18 -6
  162. package/src/harness/background-task-worker.js +11 -18
  163. package/src/harness/mcp-client.js +9 -15
  164. package/src/harness/worktree-isolator.js +18 -21
  165. package/src/lib/agent-core.js +5 -0
  166. package/src/lib/agent-ipc-bus.js +12 -3
  167. package/src/lib/agent-team/team-worktree.js +28 -6
  168. package/src/lib/agent-worktree.js +20 -19
  169. package/src/lib/async-hook-supervisor.cjs +134 -24
  170. package/src/lib/async-hook-supervisor.js +47 -0
  171. package/src/lib/background-agent-supervisor.js +35 -12
  172. package/src/lib/background-task-manager.js +5 -1
  173. package/src/lib/changelog.js +57 -0
  174. package/src/lib/chrome-connector.js +6 -2
  175. package/src/lib/claude-code-bridge.js +24 -8
  176. package/src/lib/cli-anything-bridge.js +82 -25
  177. package/src/lib/doctor-checkup.js +18 -6
  178. package/src/lib/eval/tasks.js +25 -31
  179. package/src/lib/hook-runner.cjs +71 -36
  180. package/src/lib/hook-runner.js +55 -0
  181. package/src/lib/host-adb-bridge.js +18 -3
  182. package/src/lib/lsp/__tests__/benchmark.test.js +61 -0
  183. package/src/lib/lsp/__tests__/lsp-client.test.js +6 -1
  184. package/src/lib/lsp/benchmark.js +20 -4
  185. package/src/lib/lsp/lsp-client.js +6 -4
  186. package/src/lib/lsp/lsp-server-registry.js +0 -2
  187. package/src/lib/orchestrator.js +8 -2
  188. package/src/lib/packer/pack-update-applier.js +14 -2
  189. package/src/lib/packer/smoke-runner.js +18 -5
  190. package/src/lib/paths.js +1 -1
  191. package/src/lib/plugin-monitor-supervisor.js +20 -13
  192. package/src/lib/plugin-runtime/install.js +10 -3
  193. package/src/lib/repl-bang-memorize.js +10 -2
  194. package/src/lib/repl-goal.js +19 -2
  195. package/src/lib/settings-hook-events.cjs +13 -8
  196. package/src/lib/settings-hook-events.js +20 -0
  197. package/src/lib/skill-packs/generator.js +18 -9
  198. package/src/lib/skill-process-broker.js +71 -0
  199. package/src/lib/slash-commands.js +10 -2
  200. package/src/lib/status-line.cjs +28 -7
  201. package/src/lib/status-line.js +33 -0
  202. package/src/repl/agent-repl.js +48 -43
  203. package/src/runtime/agent-core.js +133 -49
  204. package/src/runtime/headless-runner.js +25 -10
  205. package/src/runtime/headless-stream.js +74 -4
  206. package/src/runtime/session-slash-commands.js +506 -0
  207. package/src/skills/video-editing/extractors/audio-extractor.js +13 -5
  208. package/src/skills/video-editing/extractors/video-extractor.js +13 -3
  209. package/src/skills/video-editing/media-process.js +20 -0
  210. package/src/skills/video-editing/render/audio-mix.js +13 -3
  211. package/src/skills/video-editing/render/ffmpeg-concat.js +7 -2
  212. package/src/skills/video-editing/render/ffmpeg-extract.js +7 -2
  213. package/src/workers/background-agent-worker.js +6 -2
  214. package/src/assets/web-panel/assets/ChatBubbleRenderer-CGHYrugQ.js +0 -1
  215. package/src/assets/web-panel/assets/MobileProjects-NZvvfDME.js +0 -1
  216. package/src/assets/web-panel/assets/Skills-BDHh17P0.js +0 -1
  217. package/src/assets/web-panel/assets/Tasks-DYQ_6lxj.js +0 -1
  218. package/src/assets/web-panel/assets/Terminal-CjTZPa37.js +0 -3
  219. package/src/assets/web-panel/assets/devWarning-BT46Y_QI.js +0 -1
  220. package/src/assets/web-panel/assets/index-B7nuIywP.js +0 -1
  221. package/src/assets/web-panel/assets/index-ycwAfC-i.js +0 -1
@@ -5,7 +5,7 @@
5
5
  * Sends messages to parent: { type: "heartbeat"|"result"|"error", ... }
6
6
  */
7
7
 
8
- import { execSync } from "node:child_process";
8
+ import executionBroker from "../lib/process-execution-broker/index.js";
9
9
 
10
10
  const [command, cwd, type] = process.argv.slice(2);
11
11
 
@@ -14,23 +14,16 @@ const heartbeat = setInterval(() => {
14
14
  }, 5000);
15
15
 
16
16
  try {
17
- let result;
18
-
19
- if (type === "shell") {
20
- result = execSync(command, {
21
- cwd: cwd || process.cwd(),
22
- encoding: "utf-8",
23
- timeout: 300000,
24
- maxBuffer: 10 * 1024 * 1024,
25
- });
26
- } else {
27
- result = execSync(command, {
28
- cwd: cwd || process.cwd(),
29
- encoding: "utf-8",
30
- timeout: 300000,
31
- maxBuffer: 10 * 1024 * 1024,
32
- });
33
- }
17
+ const result = executionBroker.execSync(command, {
18
+ cwd: cwd || process.cwd(),
19
+ encoding: "utf-8",
20
+ timeout: 300000,
21
+ maxBuffer: 10 * 1024 * 1024,
22
+ origin: `background-task:command:${type || "unknown"}`,
23
+ policy: "allow",
24
+ scope: "background-task",
25
+ shell: true,
26
+ });
34
27
 
35
28
  if (process.send) {
36
29
  process.send({ type: "result", data: result || "Done" });
@@ -8,7 +8,6 @@
8
8
  * thin re-export shim for backwards compatibility.
9
9
  */
10
10
 
11
- import { spawn } from "child_process";
12
11
  import { executionBroker } from "../lib/process-execution-broker/index.js";
13
12
  import { AsyncLocalStorage } from "node:async_hooks";
14
13
  import { EventEmitter } from "events";
@@ -21,7 +20,7 @@ import { EventRuntimeProducer } from "../lib/event-runtime-producer.js";
21
20
  * `fetch` defaults to the global fetch (Node 18+).
22
21
  */
23
22
  export const _deps = {
24
- spawn,
23
+ spawn: executionBroker.spawn.bind(executionBroker),
25
24
  fetch: (...args) => globalThis.fetch(...args),
26
25
  // Backoff sleep seam (tests override with a no-op so retries don't wait).
27
26
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
@@ -486,10 +485,7 @@ export class MCPClient extends EventEmitter {
486
485
  `stdio transport requires a command (server "${name}")`,
487
486
  );
488
487
  }
489
- const spawnFn = String(config.origin || "").startsWith("plugin:")
490
- ? executionBroker.spawn.bind(executionBroker)
491
- : _deps.spawn;
492
- const proc = spawnFn(config.command, config.args || [], {
488
+ const proc = _deps.spawn(config.command, config.args || [], {
493
489
  stdio: ["pipe", "pipe", "pipe"],
494
490
  // process.env < agent identity (CLAUDECODE / session id) < the
495
491
  // server's own config.env, so an explicit per-server override wins.
@@ -498,15 +494,13 @@ export class MCPClient extends EventEmitter {
498
494
  ...this._agentIdentityEnv(),
499
495
  ...(config.env || {}),
500
496
  },
501
- ...(config.origin
502
- ? {
503
- origin: config.origin,
504
- policy: config.policy || "allow",
505
- pluginId: config.pluginId,
506
- pluginVersion: config.pluginVersion,
507
- pluginSource: config.pluginSource,
508
- }
509
- : {}),
497
+ origin: config.origin || `mcp:server:${name}`,
498
+ policy: config.policy || "allow",
499
+ scope: config.scope || "mcp",
500
+ shell: false,
501
+ pluginId: config.pluginId,
502
+ pluginVersion: config.pluginVersion,
503
+ pluginSource: config.pluginSource,
510
504
  });
511
505
 
512
506
  entry.process = proc;
@@ -7,7 +7,6 @@
7
7
  * Feature-flag gated: WORKTREE_ISOLATION
8
8
  */
9
9
 
10
- import { execSync } from "node:child_process";
11
10
  import { existsSync, rmSync, symlinkSync, mkdirSync } from "node:fs";
12
11
  import { resolve, dirname } from "node:path";
13
12
  import {
@@ -93,10 +92,10 @@ export function removeWorktree(repoDir, worktreePath, options = {}) {
93
92
  let branch = null;
94
93
  if (deleteBranch) {
95
94
  try {
96
- branch = execSync("git rev-parse --abbrev-ref HEAD", {
97
- cwd: worktreePath,
98
- encoding: "utf-8",
99
- }).trim();
95
+ branch = gitExecArgs(
96
+ ["rev-parse", "--abbrev-ref", "HEAD"],
97
+ worktreePath,
98
+ ).trim();
100
99
  } catch (_e) {
101
100
  // Can't determine branch, skip branch deletion.
102
101
  }
@@ -215,11 +214,7 @@ function _worktreeCleanupState(repoDir, wt) {
215
214
 
216
215
  // Dirty state: separate tracked modifications from untracked files.
217
216
  try {
218
- const porcelain = execSync("git status --porcelain", {
219
- cwd: wt.path,
220
- encoding: "utf-8",
221
- maxBuffer: 64 * 1024 * 1024,
222
- });
217
+ const porcelain = gitExecArgs(["status", "--porcelain"], wt.path);
223
218
  const lines = porcelain.split("\n").filter((l) => l.trim().length > 0);
224
219
  state.untracked = lines.some((l) => l.startsWith("??"));
225
220
  state.uncommitted = lines.some((l) => !l.startsWith("??"));
@@ -393,8 +388,16 @@ export function mergeWorktree(repoDir, branchName, options = {}) {
393
388
  };
394
389
  } catch (err) {
395
390
  const errMsg = err.message || String(err);
396
- if (errMsg.includes("CONFLICT") || errMsg.includes("conflict")) {
397
- const conflictPaths = _collectConflictFiles(repoDir);
391
+ // Git can report merge conflicts on stdout while execSync's Error.message
392
+ // contains only the failed command (and localized installations need not
393
+ // include the English word "conflict"). The unmerged index is the
394
+ // authoritative signal.
395
+ const conflictPaths = _collectConflictFiles(repoDir);
396
+ if (
397
+ conflictPaths.length > 0 ||
398
+ errMsg.includes("CONFLICT") ||
399
+ errMsg.includes("conflict")
400
+ ) {
398
401
  if (conflictPaths.length === 0) {
399
402
  conflictPaths.push(..._extractConflictPathsFromMessage(errMsg));
400
403
  }
@@ -690,15 +693,9 @@ function _fileStatus(repoDir, base, branch, filePath) {
690
693
 
691
694
  function _hasUncommittedChanges(worktreePath) {
692
695
  try {
693
- const output = execSync("git status --porcelain", {
694
- cwd: worktreePath,
695
- encoding: "utf-8",
696
- // A worktree with many dirty files produces >1 MB of porcelain output and
697
- // would ENOBUFS on the default cap — the catch below then reports "clean"
698
- // (fail-OPEN), which could let a caller discard a worktree full of
699
- // uncommitted work. Give it the same 64 MB headroom as gitExec.
700
- maxBuffer: 64 * 1024 * 1024,
701
- });
696
+ // gitExecArgs carries the shared 64 MB maxBuffer, avoiding a fail-open
697
+ // "clean" result when a large worktree exceeds Node's default output cap.
698
+ const output = gitExecArgs(["status", "--porcelain"], worktreePath);
702
699
  return output.trim().length > 0;
703
700
  } catch (_e) {
704
701
  return false;
@@ -13,6 +13,8 @@ export {
13
13
  getAgentToolDescriptors,
14
14
  getCachedPython,
15
15
  getEnvironmentInfo,
16
+ _environmentProcessDeps,
17
+ _agentToolProcessDeps,
16
18
  getBaseSystemPrompt,
17
19
  buildSystemPrompt,
18
20
  executeTool,
@@ -29,6 +31,9 @@ export {
29
31
  killBackgroundShellTask,
30
32
  killAllBackgroundShellTasks,
31
33
  killAllBackgroundShellTasksSync,
34
+ _backgroundProcessDeps,
35
+ _runBackgroundTaskkill,
36
+ _gitProcessDeps,
32
37
  reapIdleBackgroundShellTasks,
33
38
  _ingestSearchOutput,
34
39
  writeFileVerified,
@@ -13,6 +13,11 @@ import { EventEmitter } from "node:events";
13
13
  import crypto from "node:crypto";
14
14
  import { EventRuntimeProducer } from "./event-runtime-producer.js";
15
15
  import { EventRuntimeStore } from "./event-runtime-store.js";
16
+ import executionBroker from "./process-execution-broker/index.js";
17
+
18
+ export const _deps = {
19
+ spawn: executionBroker.spawn.bind(executionBroker),
20
+ };
16
21
 
17
22
  /**
18
23
  * @typedef {Object} InteractionRequest
@@ -170,12 +175,12 @@ class AgentIPCBus extends EventEmitter {
170
175
  * @returns {Promise<{process: ChildProcess, agentId: string}>}
171
176
  */
172
177
  async spawnAgentProcess(command, args = [], options = {}) {
173
- const { spawn } = await import("node:child_process");
174
178
  const agentId = options.agentId || crypto.randomUUID();
175
179
  const heartbeatMs = options.heartbeatMs || 30000;
176
180
 
177
181
  return new Promise((resolve, reject) => {
178
- const child = spawn(command, args, {
182
+ const spawnOptions = options.spawnOptions || {};
183
+ const child = _deps.spawn(command, args, {
179
184
  stdio: ["pipe", "pipe", "pipe"],
180
185
  env: {
181
186
  ...process.env,
@@ -184,7 +189,11 @@ class AgentIPCBus extends EventEmitter {
184
189
  CHAINLESSCHAIN_IPC_PROTOCOL: "jsonrpc-stdio-v1",
185
190
  ...options.env,
186
191
  },
187
- ...options.spawnOptions,
192
+ ...spawnOptions,
193
+ origin: spawnOptions.origin || "agent-ipc:subagent",
194
+ policy: spawnOptions.policy || "allow",
195
+ scope: spawnOptions.scope || "agent-ipc",
196
+ shell: spawnOptions.shell === true,
188
197
  });
189
198
 
190
199
  let buffer = "";
@@ -23,7 +23,12 @@ import {
23
23
  } from "../../harness/worktree-isolator.js";
24
24
  import { isGitRepo as _isGitRepo } from "../../lib/git-integration.js";
25
25
  import { normalizeSparsePaths } from "../../lib/worktree-sparse.js";
26
- import { execFileSync, spawn } from "node:child_process";
26
+ import executionBroker from "../../lib/process-execution-broker/index.js";
27
+
28
+ export const _processDeps = {
29
+ spawn: executionBroker.spawn.bind(executionBroker),
30
+ execFileSync: executionBroker.execFileSync.bind(executionBroker),
31
+ };
27
32
 
28
33
  /**
29
34
  * Resolve the sparse-checkout / dependency-symlink options for a task, letting a
@@ -49,7 +54,14 @@ function resolveWorktreeOptions(task, defaults) {
49
54
 
50
55
  function defaultRunShell(command, cwd) {
51
56
  return new Promise((resolve, reject) => {
52
- const child = spawn(command, { cwd, shell: true, env: process.env });
57
+ const child = _processDeps.spawn(command, [], {
58
+ cwd,
59
+ shell: true,
60
+ env: process.env,
61
+ origin: "team-worktree:task-command",
62
+ policy: "allow",
63
+ scope: "team-worktree",
64
+ });
53
65
  let err = "";
54
66
  child.stderr?.on("data", (d) => (err += d.toString("utf8")));
55
67
  child.on("error", (e) => reject(new Error(e.message)));
@@ -61,12 +73,22 @@ function defaultRunShell(command, cwd) {
61
73
  });
62
74
  }
63
75
 
76
+ function runTeamGit(args, cwd) {
77
+ return _processDeps.execFileSync("git", args, {
78
+ cwd,
79
+ stdio: "ignore",
80
+ origin: "team-worktree:commit",
81
+ policy: "allow",
82
+ scope: "team-worktree",
83
+ shell: false,
84
+ });
85
+ }
86
+
64
87
  /** Stage + commit everything in a worktree. Returns true if a commit was made. */
65
88
  function defaultCommit(worktreePath, message) {
66
- execFileSync("git", ["add", "-A"], { cwd: worktreePath, stdio: "ignore" });
89
+ runTeamGit(["add", "-A"], worktreePath);
67
90
  try {
68
- execFileSync(
69
- "git",
91
+ runTeamGit(
70
92
  [
71
93
  "-c",
72
94
  "user.email=team@chainlesschain.local",
@@ -76,7 +98,7 @@ function defaultCommit(worktreePath, message) {
76
98
  "-m",
77
99
  message,
78
100
  ],
79
- { cwd: worktreePath, stdio: "ignore" },
101
+ worktreePath,
80
102
  );
81
103
  return true;
82
104
  } catch {
@@ -11,24 +11,28 @@
11
11
  * a merge hint. Removal failures fail-open to "kept".
12
12
  */
13
13
 
14
- import { execFileSync, execSync } from "child_process";
15
14
  import { existsSync, realpathSync } from "node:fs";
16
15
  import { resolve } from "node:path";
17
16
  import { createWorktree, removeWorktree } from "./worktree-isolator.js";
18
17
  import { findProjectRoot } from "./project-instructions.js";
18
+ import executionBroker from "./process-execution-broker/index.js";
19
19
 
20
20
  export const _deps = {
21
- execFileSync,
22
- execSync,
21
+ execFileSync: executionBroker.execFileSync.bind(executionBroker),
23
22
  createWorktree,
24
23
  removeWorktree,
25
24
  };
26
25
 
27
- function git(cmd, cwd, deps) {
28
- return (deps.execSync || execSync)(`git ${cmd}`, {
26
+ function git(args, cwd, deps, options = {}) {
27
+ const run = deps.execFileSync || _deps.execFileSync;
28
+ return run("git", args, {
29
29
  cwd,
30
30
  encoding: "utf8",
31
- stdio: ["ignore", "pipe", "ignore"],
31
+ stdio: options.stdio || ["ignore", "pipe", "ignore"],
32
+ origin: options.origin || "agent-worktree:query",
33
+ policy: "allow",
34
+ scope: "agent-worktree",
35
+ shell: false,
32
36
  });
33
37
  }
34
38
 
@@ -90,17 +94,17 @@ export function validateAgentWorktree(info, { deps = _deps } = {}) {
90
94
  }
91
95
 
92
96
  const repoTop = canonicalPath(
93
- git("rev-parse --show-toplevel", info.repoRoot, deps).trim(),
97
+ git(["rev-parse", "--show-toplevel"], info.repoRoot, deps).trim(),
94
98
  );
95
99
  const worktreeTop = canonicalPath(
96
- git("rev-parse --show-toplevel", worktreePath, deps).trim(),
100
+ git(["rev-parse", "--show-toplevel"], worktreePath, deps).trim(),
97
101
  );
98
102
  if (repoTop !== repo || worktreeTop !== worktree) {
99
103
  return { valid: false, reason: "worktree path identity mismatch" };
100
104
  }
101
105
 
102
106
  const registered = parseRegisteredWorktrees(
103
- git("worktree list --porcelain", info.repoRoot, deps),
107
+ git(["worktree", "list", "--porcelain"], info.repoRoot, deps),
104
108
  ).find((entry) => {
105
109
  try {
106
110
  return canonicalPath(entry.path) === worktree;
@@ -138,16 +142,13 @@ function removeVerifiedAgentWorktree(info, deps) {
138
142
  return null;
139
143
  }
140
144
 
141
- const run = deps.execFileSync || execFileSync;
142
- run("git", ["worktree", "remove", info.path], {
143
- cwd: info.repoRoot,
144
- encoding: "utf8",
145
+ git(["worktree", "remove", info.path], info.repoRoot, deps, {
146
+ origin: "agent-worktree:cleanup",
145
147
  stdio: ["ignore", "pipe", "pipe"],
146
148
  });
147
149
  try {
148
- run("git", ["branch", "-D", "--", info.branch], {
149
- cwd: info.repoRoot,
150
- encoding: "utf8",
150
+ git(["branch", "-D", "--", info.branch], info.repoRoot, deps, {
151
+ origin: "agent-worktree:cleanup",
151
152
  stdio: ["ignore", "pipe", "pipe"],
152
153
  });
153
154
  return null;
@@ -174,7 +175,7 @@ export function setupAgentWorktree({
174
175
  const suffix = Math.random().toString(36).slice(2, 6);
175
176
  const branch = `cc-agent-${stamp}-${suffix}`;
176
177
  const info = (deps.createWorktree || createWorktree)(repoRoot, branch);
177
- const baseSha = git("rev-parse HEAD", info.path, deps).trim();
178
+ const baseSha = git(["rev-parse", "HEAD"], info.path, deps).trim();
178
179
  return { ...info, repoRoot, baseSha };
179
180
  }
180
181
 
@@ -205,8 +206,8 @@ export function finishAgentWorktree(info, { deps = _deps } = {}) {
205
206
  let headSha = null;
206
207
  let readable = false;
207
208
  try {
208
- dirty = git("status --porcelain", info.path, deps).trim().length > 0;
209
- headSha = git("rev-parse HEAD", info.path, deps).trim();
209
+ dirty = git(["status", "--porcelain"], info.path, deps).trim().length > 0;
210
+ headSha = git(["rev-parse", "HEAD"], info.path, deps).trim();
210
211
  readable = true;
211
212
  } catch {
212
213
  /* unreadable → keep, never destroy work we cannot verify */
@@ -28,11 +28,10 @@
28
28
  * - tree-reaping of every child on stopAll() / process 'exit', so a session
29
29
  * never leaves an orphan hook process.
30
30
  *
31
- * `_deps.spawn` / `_deps.now` are injected so the whole supervisor is unit-
31
+ * Process runners and `_deps.now` are injected so the whole supervisor is unit-
32
32
  * testable with fake child processes (no real shell).
33
33
  */
34
34
 
35
- const cpDefault = require("node:child_process");
36
35
  const { tryParseDecision } = require("./hook-runner.cjs");
37
36
 
38
37
  const DEFAULT_TIMEOUT_MS = 60000;
@@ -52,8 +51,10 @@ class AsyncHookSupervisor {
52
51
  ? Number(opts.maxConcurrent)
53
52
  : DEFAULT_MAX_CONCURRENT;
54
53
  this._deps = {
55
- spawn: opts.spawn || cpDefault.spawn,
56
- spawnSync: opts.spawnSync || cpDefault.spawnSync,
54
+ run: opts.run || opts.spawn || null,
55
+ runSync: opts.runSync || opts.spawnSync || null,
56
+ killProcess: opts.killProcess || process.kill.bind(process),
57
+ platform: opts.platform || process.platform,
57
58
  now: opts.now || (() => Date.now()),
58
59
  };
59
60
  this._running = new Map(); // key -> { child, timer }
@@ -125,8 +126,12 @@ class AsyncHookSupervisor {
125
126
  return;
126
127
  }
127
128
  try {
128
- if (process.platform === "win32") {
129
- const r = this._deps.spawnSync(
129
+ if (this._deps.platform === "win32") {
130
+ // Snapshot before taskkill: a policy-restricted taskkill may terminate
131
+ // the shell and then fail on its descendants, orphaning them so a
132
+ // parent-PID query performed afterward can no longer find the tree.
133
+ const descendants = this._windowsDescendantPids(pid);
134
+ const r = this._deps.runSync(
130
135
  "taskkill",
131
136
  ["/PID", String(pid), "/T", "/F"],
132
137
  { windowsHide: true },
@@ -135,20 +140,27 @@ class AsyncHookSupervisor {
135
140
  // tree snapshot and termination. Repeat once so that race cannot
136
141
  // leave a short-lived hook grandchild orphaned.
137
142
  if (!r?.error && r?.status === 0) {
138
- this._deps.spawnSync(
139
- "taskkill",
140
- ["/PID", String(pid), "/T", "/F"],
141
- { windowsHide: true },
142
- );
143
+ this._deps.runSync("taskkill", ["/PID", String(pid), "/T", "/F"], {
144
+ windowsHide: true,
145
+ });
143
146
  }
144
- // If taskkill couldn't launch, fall back to a direct kill.
147
+ // Restricted runners can launch taskkill but still receive a non-zero
148
+ // access/policy result. Enumerate the tree while it is still alive,
149
+ // then terminate descendants leaf-first before killing the shell.
145
150
  if (r && (r.error || (r.status != null && r.status !== 0))) {
151
+ for (const descendant of descendants.reverse()) {
152
+ try {
153
+ this._deps.killProcess(descendant, "SIGKILL");
154
+ } catch {
155
+ /* already gone */
156
+ }
157
+ }
146
158
  child.kill(signal);
147
159
  }
148
160
  } else {
149
161
  // Negative pid → the whole process group (requires the detached spawn).
150
162
  try {
151
- process.kill(-pid, signal);
163
+ this._deps.killProcess(-pid, signal);
152
164
  } catch {
153
165
  child.kill(signal);
154
166
  }
@@ -162,6 +174,102 @@ class AsyncHookSupervisor {
162
174
  }
163
175
  }
164
176
 
177
+ /**
178
+ * Snapshot a Windows process tree for the taskkill-denied fallback. Query the
179
+ * process table once instead of issuing a quoted WMIC `where` expression for
180
+ * every level: Node/cmd quoting can silently turn that expression into an
181
+ * invalid WMIC alias query. PowerShell/CIM covers systems where WMIC has been
182
+ * removed. Traversal is bounded + cycle-safe so corrupt data cannot stall
183
+ * session teardown.
184
+ */
185
+ _windowsDescendantPids(rootPid) {
186
+ if (this._deps.platform !== "win32") return [];
187
+ const root = Number(rootPid);
188
+ if (!Number.isInteger(root) || root <= 0) return [];
189
+ let output = "";
190
+ let shouldTryCim = false;
191
+ try {
192
+ const result = this._deps.runSync(
193
+ "wmic",
194
+ [
195
+ "path",
196
+ "Win32_Process",
197
+ "get",
198
+ "ParentProcessId,ProcessId",
199
+ "/format:csv",
200
+ ],
201
+ {
202
+ encoding: "utf8",
203
+ windowsHide: true,
204
+ timeout: 3000,
205
+ },
206
+ );
207
+ if (!result?.error && result?.status === 0) {
208
+ output = String(result.stdout || "");
209
+ } else if (result?.error?.code === "ENOENT") {
210
+ shouldTryCim = true;
211
+ }
212
+ } catch {
213
+ shouldTryCim = true;
214
+ }
215
+ if (!output && shouldTryCim) {
216
+ try {
217
+ const result = this._deps.runSync(
218
+ "powershell.exe",
219
+ [
220
+ "-NoProfile",
221
+ "-NonInteractive",
222
+ "-Command",
223
+ "Get-CimInstance Win32_Process | ForEach-Object { '{0},{1}' -f $_.ParentProcessId,$_.ProcessId }",
224
+ ],
225
+ {
226
+ encoding: "utf8",
227
+ windowsHide: true,
228
+ timeout: 3000,
229
+ },
230
+ );
231
+ if (!result?.error && result?.status === 0) {
232
+ output = String(result.stdout || "");
233
+ }
234
+ } catch {
235
+ return [];
236
+ }
237
+ }
238
+
239
+ const childrenByParent = new Map();
240
+ for (const line of output.split(/\r?\n/)) {
241
+ const fields = line.trim().split(",");
242
+ if (fields.length < 2) continue;
243
+ const parentPid = Number(fields.at(-2));
244
+ const childPid = Number(fields.at(-1));
245
+ if (
246
+ !Number.isInteger(parentPid) ||
247
+ parentPid < 0 ||
248
+ !Number.isInteger(childPid) ||
249
+ childPid <= 0
250
+ ) {
251
+ continue;
252
+ }
253
+ const children = childrenByParent.get(parentPid) || [];
254
+ children.push(childPid);
255
+ childrenByParent.set(parentPid, children);
256
+ }
257
+
258
+ const seen = new Set([root]);
259
+ const pending = [root];
260
+ const descendants = [];
261
+ while (pending.length > 0 && seen.size <= 256) {
262
+ const parentPid = pending.shift();
263
+ for (const childPid of childrenByParent.get(parentPid) || []) {
264
+ if (seen.has(childPid)) continue;
265
+ seen.add(childPid);
266
+ descendants.push(childPid);
267
+ pending.push(childPid);
268
+ }
269
+ }
270
+ return descendants;
271
+ }
272
+
165
273
  /** Install the process-exit reaper exactly once, only when a child exists. */
166
274
  _installExitHook() {
167
275
  if (this._exitHookInstalled) return;
@@ -257,22 +365,24 @@ class AsyncHookSupervisor {
257
365
  // POSIX: own process group so a timeout / stopAll can signal the whole
258
366
  // tree (shell + the real hook command), not just the shell wrapper.
259
367
  // No-op on Windows, where the tree is reaped via `taskkill /T` instead.
260
- detached: process.platform !== "win32",
368
+ detached: this._deps.platform !== "win32",
261
369
  env: {
262
370
  ...process.env,
263
371
  CLAUDE_HOOK_EVENT: hook.event || payload.hook_event_name || "",
264
372
  },
265
373
  };
266
- child = opts.broker && hook.origin === "plugin:hook"
267
- ? opts.broker.spawn(hook.command, [], {
268
- ...spawnOptions,
269
- origin: hook.origin,
270
- policy: "allow",
271
- pluginId: hook.pluginId || null,
272
- pluginVersion: hook.pluginVersion || null,
273
- pluginSource: hook.pluginSource || null,
274
- })
275
- : this._deps.spawn(hook.command, spawnOptions);
374
+ if (typeof this._deps.run !== "function") {
375
+ throw new Error("async hook process runner unavailable");
376
+ }
377
+ child = this._deps.run(hook.command, {
378
+ ...spawnOptions,
379
+ origin: hook.origin || "async-hook:command",
380
+ policy: "allow",
381
+ scope: "async-hook",
382
+ pluginId: hook.pluginId || null,
383
+ pluginVersion: hook.pluginVersion || null,
384
+ pluginSource: hook.pluginSource || null,
385
+ });
276
386
  } catch (err) {
277
387
  this._record(hook, {
278
388
  ok: false,
@@ -0,0 +1,47 @@
1
+ import supervisor from "./async-hook-supervisor.cjs";
2
+ import executionBroker from "./process-execution-broker/index.js";
3
+
4
+ const brokerRunner = executionBroker.spawn.bind(executionBroker);
5
+ const brokerSyncRunner = executionBroker.spawnSync.bind(executionBroker);
6
+
7
+ export const _processDeps = {
8
+ run: brokerRunner,
9
+ runSync: brokerSyncRunner,
10
+ };
11
+
12
+ function runAsyncHook(command, options = {}) {
13
+ return _processDeps.run(command, [], {
14
+ ...options,
15
+ origin: options.origin || "async-hook:command",
16
+ policy: "allow",
17
+ scope: "async-hook",
18
+ });
19
+ }
20
+
21
+ function runAsyncHookSupervisorCommand(command, args, options = {}) {
22
+ return _processDeps.runSync(command, args, {
23
+ ...options,
24
+ origin: options.origin || "async-hook:supervisor",
25
+ policy: "allow",
26
+ scope: "async-hook",
27
+ });
28
+ }
29
+
30
+ export class AsyncHookSupervisor extends supervisor.AsyncHookSupervisor {
31
+ constructor(options = {}) {
32
+ super({
33
+ ...options,
34
+ run: options.run || options.spawn || runAsyncHook,
35
+ runSync:
36
+ options.runSync || options.spawnSync || runAsyncHookSupervisorCommand,
37
+ });
38
+ }
39
+ }
40
+
41
+ export const { DEFAULT_TIMEOUT_MS, DEFAULT_MAX_CONCURRENT } = supervisor;
42
+
43
+ export default {
44
+ AsyncHookSupervisor,
45
+ DEFAULT_TIMEOUT_MS,
46
+ DEFAULT_MAX_CONCURRENT,
47
+ };