chainlesschain 0.162.182 → 0.162.184

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 (202) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-DzZ5_xoj.js → AIOps-DzAl5fGx.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-DeLXddIg.js → ActionButton-DAHtMGeC.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-rvDCmet1.js → Analytics-BHQaexv5.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-Yt6QVDnp.js → AppLayout-Phz_pD0n.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-BS112K5L.js → Artifacts-DHt47kCA.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-0wJuQrNu.js → Audit--Y_8FULl.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-B90PMgbn.js → BackgroundAgents-N1zchH7D.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-TkgMglQQ.js → Backup-B_BoyN30.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-CQVS9p1l.js → BaseInput-Gl4uQHcT.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-YTjQHoC7.js → Chat-Cg-eIyAZ.js} +5 -5
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-BzeUi3JH.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-5bwaOabq.js → Checkbox-D0MGR2wA.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-BFmNQ_Xe.js → Codegen-BdObJEFn.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-BT_u4Dzp.js → Col--ghxXT3z.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-CJP52u5b.js → Community-BUFeXOqz.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-BpzhJE9i.js → Compact-DhQO2_Mr.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-Cg7rfslp.js → Compliance-3HoTqKYd.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-JisxL6Yy.js → Cowork-B3zmO4VW.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-Bu7Ug_MC.js → Cron-CJDrZOcc.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CvuqoLdj.js → Crosschain-SMl15obd.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-C2u9G8Pu.js → DID-CgTrTWrl.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-BFt5Yv3d.js → Dashboard-dXFKaKol.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DIaT9Wxq.js → Dropdown-qQHEhCvI.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-D_A601Y4.js → EmailListRenderer-C_ej0MWH.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard--IyQ8I8w.js → FamilyGuardDashboard-D6ajoE7I.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-BihWHxuY.js → Federation-Cio2y49h.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BB8dOXc_.js → FormItemContext-ClJz1cYp.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-DQYhj1HH.js → GenericCardRenderer-Dr_8Maca.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-B-2QZxGf.js → Git-CevLwp5u.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-CusSb-nG.js → Governance-CW9YyQUY.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-BAjmvPPL.js → Inference-CQqeAyER.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-DoNYWKYq.js → KnowledgeGraph-cuGVxHrw.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-B8fKWD-n.js → Logs-DIuHri5d.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-BlJm1J7E.js → MarkdownRenderer-R91Qw5JA.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-CyOscbSL.js → Marketplace-CY1VhFGx.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-Ex6Om5da.js → McpTools-BBAbi1vt.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-CDcD8l8r.js → Memory-BWYGHxJA.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-xfBqoFDg.js → MobileBridge-CPIFkW7A.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-BwObMMA2.js → MobileProjects-DQyWxCho.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-CIWd36OV.js → Mtc-Bo3GnQbx.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-CPX0ouuA.js → MtcAudit-C8-c4rSW.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-BDJsIUuW.js → Multisig-ChqMTtoq.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-DEkbuD3N.js → NLProgramming-C48TyhvI.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-DJpHRhH1.js → Notes-zogUFWxp.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-C1UZ-Qd5.js → NotificationSettings-v-K9XpSB.js} +1 -1
  48. package/src/assets/web-panel/assets/OrderTableRenderer-P94JGGNz.js +1 -0
  49. package/src/assets/web-panel/assets/{Organization-Bbhj0fac.js → Organization-Boi1dQfR.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-1H04x3kx.js → Overflow-DM5YWwgA.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-CDpPe3et.js → P2P-C57LKpKK.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-MUX0Bpvw.js → PdhVaultBrowser-BFkPqu6Q.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-A07-0Ci1.js → Permissions-D6fB9YSp.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-DhhDWxAZ.js → PersonalDataHub-Bp7EefEo.js} +5 -5
  55. package/src/assets/web-panel/assets/{Pipeline-CyfbJfe-.js → Pipeline-BVln3hZf.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-CNFps3ED.js → Privacy-Bf8P3nF6.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit--T-u3Dr5.js → ProjectInit-DM6wA2E2.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-DJmrK2fC.js → ProjectSettings-CGbc5Iho.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-D0p9EOxm.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-DLzzbOZd.js → Providers-B2kJcxS6.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-CexOomKS.js → QrScannerModal-Cxu7dXYy.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-QklGi19_.js → QuickAsk-DTlrTPms.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-Bb9kwSw8.js → Recommend-Cm3X-cCj.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DZuVNNN5.js → RemoteSession-DNb2kjvt.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-Dr5cLP0N.js → Reputation-CZUFFjc1.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-oCQHYv5t.js → Row-DAPgloxA.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-IFDEykIR.js → RssFeed-dsx9S793.js} +2 -2
  68. package/src/assets/web-panel/assets/{Search-CspVIxDc.js → Search-DnnC8kz-.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-Bm66o_DH.js → Security-VuZknRMK.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-DvanNqXl.js → Services-B3XNX8Vx.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-CHjEe9_O.js → Skeleton-Dl1nVtCj.js} +1 -1
  72. package/src/assets/web-panel/assets/Skills-CY09CZ_W.js +1 -0
  73. package/src/assets/web-panel/assets/{Sla-DxZm1491.js → Sla-HzU56n1z.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-DWxEEclC.js → SpeechSettings-CRle7Jl2.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-FIvOFw5p.js → SyncSettings-DWadz7Sl.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-CNVTDGg8.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-CaNjzV_t.js → Templates-BBAgvrlM.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-DjlP6TmH.js → Tenant-BsbbvGN8.js} +1 -1
  79. package/src/assets/web-panel/assets/Terminal-uscpZI4q.js +3 -0
  80. package/src/assets/web-panel/assets/{TimelineRenderer-DoZ8y7y8.js → TimelineRenderer-mTyjNE6r.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-390cg2TC.js → Tokens-Bz97oBgs.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-CLrtCj5_.js → Trigger-BTRKC140.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DpESfSPn.js → Trust-B4A_KuyX.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-CN6279O0.js → UkeySign-DES9TmLU.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-DPtAUlSe.js → VideoEditing-ClXs7Qxp.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-nKOMV_1n.js → Wallet-IzZqFz-7.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-HuXlSOc4.js → WebAuthn-BtOP04g_.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CUfFjARi.js → WorkflowEditor-_0cqnopY.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-6Eq0PcZJ.js → chat-ax7193uU.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-BP_Sf6Iy.js → colors-Bu4WGJah.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-BufMKIh5.js → compact-item-BX1dlTW0.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-BBMXxWSW.js → createContext-CktA_Dj7.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-x877iKKS.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-fUK_tRO2.js → hasIn-D5wd817l.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-t8N1tSeX.js → index-5PYYkr8S.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DLpIs_P_.js → index-A4uBPQCz.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DVsqvI4M.js → index-BAtBNfkr.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Bkg4ZePE.js → index-BLLsUNDc.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-cpEli44X.js → index-BQtoRyTO.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-wKTLbKyc.js → index-BRfXoRuh.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B7Etk0Te.js → index-Bad2CYeH.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CwSf5pnf.js → index-BcGdL3sG.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-3FuwBewR.js → index-C3HmzCLt.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BHy6yNiF.js → index-C8DkWyNr.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CgiECHu2.js → index-C8tLR2TG.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-C44atv4k.js → index-CP8ELJBc.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CW_G-MXw.js → index-CUForzsG.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-pum7HYBA.js → index-CYRE0NJA.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BWKoBU8R.js → index-CeVZaJSC.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-D4_MuGkd.js → index-CfzxR61U.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Dyngbd29.js → index-Ci3y1ZAv.js} +19 -19
  112. package/src/assets/web-panel/assets/{index-fzaMtSJ8.js → index-CjEJjl5C.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Cp8vhyZy.js → index-Cv7bVWJ5.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DPdUNk1i.js → index-D-o4Rwjk.js} +1 -1
  115. package/src/assets/web-panel/assets/index-D7pfPouB.js +1 -0
  116. package/src/assets/web-panel/assets/{index-CeASi4fr.js → index-D9op8poE.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DASluSqY.js → index-DF1TysIF.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DocdRcbd.js → index-DYo9S5OT.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-2A5ph8b2.js → index-D_cycOET.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-wV6iGdpY.js → index-Db3CpWTr.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BMLHv389.js → index-DkD1K-fF.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B9WYSn4m.js → index-DmPJ6T6F.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-uCbEN7cY.js → index-DmVUeWUk.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DmkMXwnR.js → index-Dzdr0zDf.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CZ6Yt17L.js → index-PBLzwYnu.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CE2DdTP2.js → index-bFCaM7Eq.js} +1 -1
  127. package/src/assets/web-panel/assets/index-e4i-VLBd.js +1 -0
  128. package/src/assets/web-panel/assets/{index-GwdRUZ-x.js → index-h3KJZr95.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-B2vS66eI.js → index-iHcPtQ2e.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-6GmBa4Kg.js → index-jzlzm4VK.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-D9vU25ij.js → index-o14NNnxw.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-CGjmILDm.js → index-tYPSfGbd.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-LvCBfTLo.js → index-ytp2H5Zx.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-V5Fo95FM.js → initDefaultProps-D2Iut83B.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BYWU0hUT.js → motion-BuaJVdK9.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-Cr1M-J2h.js → move-CaEuM92y.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-BysQKi2N.js → mtc-parser-wuKNBwys.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-D1zHU8GC.js → omit-7a3eklaq.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-CUArxtfi.js → pickAttrs-Dn4z7KvJ.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-ChoYEys0.js → placementArrow-DVn_qT4-.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-BQRs3Mo7.js → responsiveObserve-Dodz1OMQ.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-B8zbsCkg.js → slide-CxoGskbp.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-C2U8Gs6h.js → statusUtils-DBh_epQH.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-Dd8MgL_V.js → styleChecker-RXQ4gG5P.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-hknqY5NH.js → useFlexGapSupport-1Wxm0HEG.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-CLTAmcfw.js → useFs-CI_ySNxJ.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-ByegHbiB.js → usePersonalDataHub-C6973rZP.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-DqlAy5DP.js → vnode-CalynwKr.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-DiXiztQn.js → zoom-CM1cd-Q0.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/agenda.js +74 -1
  152. package/src/commands/agent.js +9 -0
  153. package/src/commands/background-session.js +60 -1
  154. package/src/commands/batch.js +160 -5
  155. package/src/commands/plugin.js +318 -43
  156. package/src/commands/session.js +44 -1
  157. package/src/commands/team.js +212 -6
  158. package/src/data/changelog.json +26 -0
  159. package/src/harness/jsonl-session-store.js +59 -6
  160. package/src/harness/mcp-client.js +65 -43
  161. package/src/lib/agent-batch.js +4 -1
  162. package/src/lib/agent-team/team-runner.js +19 -1
  163. package/src/lib/agent-team/team-worktree.js +3 -0
  164. package/src/lib/async-hook-supervisor.cjs +82 -10
  165. package/src/lib/async-hook-supervisor.js +50 -4
  166. package/src/lib/background-agent-supervisor.js +49 -0
  167. package/src/lib/collaboration-run-store.js +362 -0
  168. package/src/lib/hook-runner.cjs +98 -17
  169. package/src/lib/hook-runner.js +77 -6
  170. package/src/lib/hook-shell-command.cjs +148 -0
  171. package/src/lib/hooks-v2-runtime.js +188 -77
  172. package/src/lib/jsonl-session-store.js +2 -0
  173. package/src/lib/lsp/__tests__/lsp-client.test.js +18 -0
  174. package/src/lib/lsp/lsp-client.js +12 -1
  175. package/src/lib/plugin-monitor-supervisor.js +53 -16
  176. package/src/lib/plugin-runtime/bin.js +1 -1
  177. package/src/lib/plugin-runtime/install.js +465 -54
  178. package/src/lib/plugin-runtime/mcp.js +21 -2
  179. package/src/lib/plugin-runtime/monitors.js +19 -3
  180. package/src/lib/plugin-runtime/sandbox-policy.js +99 -0
  181. package/src/lib/plugin-runtime/scopes.js +13 -2
  182. package/src/lib/plugin-usage-attribution.js +44 -0
  183. package/src/lib/process-execution-broker/index.js +705 -13
  184. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1347 -0
  185. package/src/lib/process-execution-broker/linux-generic-bwrap.js +1260 -0
  186. package/src/lib/process-execution-broker/platform-sandbox.js +1831 -157
  187. package/src/lib/session-usage.js +278 -1
  188. package/src/lib/stream-retry.js +29 -0
  189. package/src/repl/agent-repl.js +38 -9
  190. package/src/runtime/agent-core.js +32 -4
  191. package/src/runtime/headless-runner.js +46 -1
  192. package/src/runtime/headless-stream.js +54 -3
  193. package/src/runtime/mcp-config.js +15 -5
  194. package/src/assets/web-panel/assets/ChatBubbleRenderer-PR074hUN.js +0 -1
  195. package/src/assets/web-panel/assets/OrderTableRenderer-GlJtqLjT.js +0 -1
  196. package/src/assets/web-panel/assets/Projects-Qx_MdX9k.js +0 -1
  197. package/src/assets/web-panel/assets/Skills-Cb8jWjMX.js +0 -1
  198. package/src/assets/web-panel/assets/Tasks-CyuLnDFR.js +0 -1
  199. package/src/assets/web-panel/assets/Terminal-DwzGMniO.js +0 -3
  200. package/src/assets/web-panel/assets/devWarning-DgVKduH2.js +0 -1
  201. package/src/assets/web-panel/assets/index-DOZTSoS7.js +0 -1
  202. package/src/assets/web-panel/assets/index-DhpvjpqI.js +0 -1
@@ -94,7 +94,10 @@ export async function runBatch(opts, deps) {
94
94
  try {
95
95
  worktreePath = createWorktree(unit.key, branch);
96
96
  onEvent({ type: "unit:start", key: unit.key, branch });
97
- await runAgent(unit.prompt, worktreePath);
97
+ await runAgent(unit.prompt, worktreePath, {
98
+ key: unit.key,
99
+ branch,
100
+ });
98
101
 
99
102
  const stat = diffStat(worktreePath);
100
103
  record.filesChanged = stat.filesChanged;
@@ -51,6 +51,10 @@ export class TeamRunner {
51
51
  this._executions = 0;
52
52
  this._inFlight = 0;
53
53
  this._maxInFlight = 0;
54
+ // Keys executing in this runner process. An event-loop stall may let a
55
+ // short lease expire before its heartbeat callback runs; peer loops in the
56
+ // same process must not steal and double-run that still-active task.
57
+ this._activeKeys = new Set();
54
58
  this._members = new Map(); // holder → lifecycle record
55
59
  this._budgetStopped = false;
56
60
  }
@@ -208,6 +212,7 @@ export class TeamRunner {
208
212
  let best = null;
209
213
  let bestScore = Infinity;
210
214
  for (const key of claimable) {
215
+ if (this._activeKeys.has(key)) continue;
211
216
  const t = this.registry.getTask(key);
212
217
  const score = rank[t?.priority] ?? 1;
213
218
  if (score < bestScore) {
@@ -222,10 +227,22 @@ export class TeamRunner {
222
227
  const task = this.registry.getTask(key);
223
228
  this._executions++;
224
229
  this._inFlight++;
230
+ this._activeKeys.add(key);
225
231
  this._maxInFlight = Math.max(this._maxInFlight, this._inFlight);
226
232
  this._setState(holder, "running", { key });
227
233
  this._emit("task:claimed", { key, holder, attempts: task.attempts });
228
- const renew = () => this.registry.renew(key, { holder, ttlMs: this.ttlMs });
234
+ const renew = () => {
235
+ const result = this.registry.renew(key, {
236
+ holder,
237
+ ttlMs: this.ttlMs,
238
+ });
239
+ // A delayed timer may wake just after expiry. Reacquire is safe and
240
+ // optimistic: it succeeds only if no external runner has taken the
241
+ // lease; a real takeover remains authoritative and stops the heartbeat.
242
+ return !result?.ok && result?.reason === "not_holder_or_expired"
243
+ ? this.registry.acquire(key, { holder, ttlMs: this.ttlMs })
244
+ : result;
245
+ };
229
246
  // Heartbeat: the built-in executors (shell / agent / worktree) never call
230
247
  // `renew` themselves, so before this ANY task outliving the lease TTL was
231
248
  // indistinguishable from a crash — a peer could steal it and the SAME task
@@ -338,6 +355,7 @@ export class TeamRunner {
338
355
  });
339
356
  } finally {
340
357
  clearInterval(heartbeat);
358
+ this._activeKeys.delete(key);
341
359
  this._inFlight--;
342
360
  }
343
361
  }
@@ -126,6 +126,8 @@ export class TeamWorktreeCoordinator {
126
126
  sparsePaths: options.sparsePaths ?? null,
127
127
  symlinkDirectories: options.symlinkDirectories ?? null,
128
128
  };
129
+ this._onWorktree =
130
+ typeof options.onWorktree === "function" ? options.onWorktree : null;
129
131
  }
130
132
 
131
133
  isGitRepo() {
@@ -173,6 +175,7 @@ export class TeamWorktreeCoordinator {
173
175
  wtOptions,
174
176
  );
175
177
  this._created.set(key, { branch, path: worktreePath, committed: false });
178
+ this._onWorktree?.({ key, branch, path: worktreePath, holder });
176
179
  if (typeof runInWorktree === "function") {
177
180
  await runInWorktree({ key, task, holder, cwd: worktreePath });
178
181
  } else {
@@ -36,6 +36,13 @@ const {
36
36
  tryParseDecision,
37
37
  resolveHookSandboxPolicy,
38
38
  } = require("./hook-runner.cjs");
39
+ const {
40
+ buildExplicitHookShellInvocation,
41
+ issueTrustedHookSandboxContract,
42
+ requireTrustedHookRoot,
43
+ requiresExplicitHookShell,
44
+ sandboxBoundaryError,
45
+ } = require("./hook-shell-command.cjs");
39
46
 
40
47
  const DEFAULT_TIMEOUT_MS = 60000;
41
48
  const MAX_TIMEOUT_MS = 600000;
@@ -117,8 +124,25 @@ class AsyncHookSupervisor {
117
124
  * the process 'exit' reaper, where async work would be cut off. A child with no
118
125
  * real pid (the unit-test fakes) falls back to its own `.kill()`.
119
126
  */
120
- _killChildTree(child, signal = "SIGTERM") {
127
+ _killChildTree(
128
+ child,
129
+ signal = "SIGTERM",
130
+ { sandboxManagedTree = false } = {},
131
+ ) {
121
132
  if (!child) return;
133
+ if (sandboxManagedTree) {
134
+ // The Linux generic backend launches bubblewrap with --die-with-parent,
135
+ // --new-session, and a private PID namespace. Killing the Broker child
136
+ // therefore tears down the complete sandbox tree; a negative host PID
137
+ // would be both unnecessary and invalid because strong launches must not
138
+ // use detached:true.
139
+ try {
140
+ child.kill(signal);
141
+ } catch {
142
+ /* already gone */
143
+ }
144
+ return;
145
+ }
122
146
  const pid = child.pid;
123
147
  if (typeof pid !== "number" || pid <= 0) {
124
148
  try {
@@ -361,25 +385,39 @@ class AsyncHookSupervisor {
361
385
  : DEFAULT_TIMEOUT_MS;
362
386
 
363
387
  let child;
388
+ let sandboxManagedTree = false;
364
389
  try {
365
390
  const sandboxPolicy = resolveHookSandboxPolicy(hook, opts);
391
+ const explicitSandboxShell = requiresExplicitHookShell(sandboxPolicy);
392
+ const trustedRoot = explicitSandboxShell
393
+ ? requireTrustedHookRoot(opts.cwd)
394
+ : null;
395
+ const workingDirectory = trustedRoot || opts.cwd || process.cwd();
396
+ sandboxManagedTree =
397
+ explicitSandboxShell && this._deps.platform === "linux";
366
398
  const spawnOptions = {
367
- cwd: opts.cwd || process.cwd(),
368
- shell: true,
399
+ cwd: workingDirectory,
400
+ shell: explicitSandboxShell ? false : true,
369
401
  // POSIX: own process group so a timeout / stopAll can signal the whole
370
402
  // tree (shell + the real hook command), not just the shell wrapper.
371
403
  // No-op on Windows, where the tree is reaped via `taskkill /T` instead.
372
- detached: this._deps.platform !== "win32",
404
+ detached: this._deps.platform !== "win32" && !sandboxManagedTree,
373
405
  env: {
374
406
  ...process.env,
375
407
  CLAUDE_HOOK_EVENT: hook.event || payload.hook_event_name || "",
376
408
  },
377
409
  ...(sandboxPolicy ? { sandboxPolicy } : {}),
378
410
  };
379
- if (typeof this._deps.run !== "function") {
411
+ const brokerRun =
412
+ explicitSandboxShell &&
413
+ opts.broker &&
414
+ typeof opts.broker.spawn === "function"
415
+ ? opts.broker.spawn.bind(opts.broker)
416
+ : null;
417
+ if (typeof (brokerRun || this._deps.run) !== "function") {
380
418
  throw new Error("async hook process runner unavailable");
381
419
  }
382
- child = this._deps.run(hook.command, {
420
+ const processOptions = {
383
421
  ...spawnOptions,
384
422
  origin: hook.origin || "async-hook:command",
385
423
  policy: "allow",
@@ -387,7 +425,39 @@ class AsyncHookSupervisor {
387
425
  pluginId: hook.pluginId || null,
388
426
  pluginVersion: hook.pluginVersion || null,
389
427
  pluginSource: hook.pluginSource || null,
390
- });
428
+ };
429
+ if (explicitSandboxShell) {
430
+ const invocation = buildExplicitHookShellInvocation(hook.command, {
431
+ platform: this._deps.platform,
432
+ });
433
+ const run = brokerRun || this._deps.run;
434
+ let brokerOptions = processOptions;
435
+ if (brokerRun) {
436
+ const issuer =
437
+ opts.broker.issueLinuxWorkspaceSandboxExecutionContract;
438
+ const sandboxExecutionContract = issueTrustedHookSandboxContract({
439
+ issuer,
440
+ receiver: opts.broker,
441
+ file: invocation.file,
442
+ args: invocation.argv,
443
+ options: processOptions,
444
+ trustedRoot,
445
+ label: "trusted async-hook sandbox contract issuance failed",
446
+ });
447
+ if (this._deps.platform === "linux" && !sandboxExecutionContract) {
448
+ throw sandboxBoundaryError(
449
+ "trusted Linux async-hook sandbox contract issuer is unavailable",
450
+ );
451
+ }
452
+ brokerOptions = {
453
+ ...processOptions,
454
+ ...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
455
+ };
456
+ }
457
+ child = run(invocation.file, invocation.argv, brokerOptions);
458
+ } else {
459
+ child = this._deps.run(hook.command, processOptions);
460
+ }
391
461
  } catch (err) {
392
462
  const sandboxDenied =
393
463
  err?.code === "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED";
@@ -406,7 +476,7 @@ class AsyncHookSupervisor {
406
476
  let out = "";
407
477
  let errOut = "";
408
478
  let killedByTimeout = false;
409
- const rec = { child, timer: null };
479
+ const rec = { child, timer: null, sandboxManagedTree };
410
480
  this._running.set(key, rec);
411
481
 
412
482
  try {
@@ -432,7 +502,7 @@ class AsyncHookSupervisor {
432
502
  killedByTimeout = true;
433
503
  // Tree-kill: a hook that spawned a subprocess (e.g. `npm test`) must not
434
504
  // leave that subprocess orphaned when it exceeds its timeout.
435
- this._killChildTree(child, "SIGTERM");
505
+ this._killChildTree(child, "SIGTERM", { sandboxManagedTree });
436
506
  }, timeout);
437
507
  if (typeof timer.unref === "function") timer.unref();
438
508
  rec.timer = timer;
@@ -594,7 +664,9 @@ class AsyncHookSupervisor {
594
664
  }
595
665
  // Tree-kill (not a bare child.kill) so a session end / process exit never
596
666
  // leaves an orphaned hook subprocess — the guarantee this class documents.
597
- this._killChildTree(rec.child, "SIGTERM");
667
+ this._killChildTree(rec.child, "SIGTERM", {
668
+ sandboxManagedTree: rec.sandboxManagedTree === true,
669
+ });
598
670
  this._running.delete(key);
599
671
  }
600
672
  try {
@@ -1,5 +1,13 @@
1
1
  import supervisor from "./async-hook-supervisor.cjs";
2
2
  import executionBroker from "./process-execution-broker/index.js";
3
+ import hookShellCommand from "./hook-shell-command.cjs";
4
+
5
+ const {
6
+ issueTrustedHookSandboxContract,
7
+ requireTrustedHookRoot,
8
+ requiresExplicitHookShell,
9
+ sandboxBoundaryError,
10
+ } = hookShellCommand;
3
11
 
4
12
  const brokerRunner = executionBroker.spawn.bind(executionBroker);
5
13
  const brokerSyncRunner = executionBroker.spawnSync.bind(executionBroker);
@@ -9,12 +17,50 @@ export const _processDeps = {
9
17
  runSync: brokerSyncRunner,
10
18
  };
11
19
 
12
- function runAsyncHook(command, options = {}) {
13
- return _processDeps.run(command, [], {
14
- ...options,
15
- origin: options.origin || "async-hook:command",
20
+ function normalizeInvocation(argsOrOptions, maybeOptions) {
21
+ return Array.isArray(argsOrOptions)
22
+ ? { args: argsOrOptions, options: maybeOptions || {} }
23
+ : { args: [], options: argsOrOptions || {} };
24
+ }
25
+
26
+ function runAsyncHook(command, argsOrOptions = {}, maybeOptions) {
27
+ const { args, options } = normalizeInvocation(argsOrOptions, maybeOptions);
28
+ const callerOptions = { ...options };
29
+ delete callerOptions.sandboxExecutionContract;
30
+ const processOptions = {
31
+ ...callerOptions,
32
+ origin: callerOptions.origin || "async-hook:command",
16
33
  policy: "allow",
17
34
  scope: "async-hook",
35
+ };
36
+ const requiresContract = requiresExplicitHookShell(
37
+ processOptions.sandboxPolicy,
38
+ );
39
+ const trustedRoot = requiresContract
40
+ ? requireTrustedHookRoot(processOptions.cwd)
41
+ : null;
42
+ const sandboxExecutionContract = requiresContract
43
+ ? issueTrustedHookSandboxContract({
44
+ issuer: executionBroker.issueLinuxWorkspaceSandboxExecutionContract,
45
+ receiver: executionBroker,
46
+ file: command,
47
+ args,
48
+ options: processOptions,
49
+ trustedRoot,
50
+ })
51
+ : null;
52
+ if (
53
+ process.platform === "linux" &&
54
+ requiresContract &&
55
+ !sandboxExecutionContract
56
+ ) {
57
+ throw sandboxBoundaryError(
58
+ "trusted Linux async-hook sandbox contract could not be issued",
59
+ );
60
+ }
61
+ return _processDeps.run(command, args, {
62
+ ...processOptions,
63
+ ...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
18
64
  });
19
65
  }
20
66
 
@@ -762,6 +762,47 @@ export function normalizeBackgroundWorktree(worktree, cwd) {
762
762
  return normalized;
763
763
  }
764
764
 
765
+ /**
766
+ * Persist only the bounded, secret-free governance envelope needed by IDE task
767
+ * rows. The supervisor owns `owner`; callers cannot forge it. Prompt text,
768
+ * argv, credentials and tool arguments are deliberately excluded.
769
+ */
770
+ export function normalizeBackgroundGovernance(
771
+ governance,
772
+ { id, sessionId } = {},
773
+ ) {
774
+ const source = governance && typeof governance === "object" ? governance : {};
775
+ const budget =
776
+ source.resourceBudget && typeof source.resourceBudget === "object"
777
+ ? source.resourceBudget
778
+ : {};
779
+ const positive = (value) => {
780
+ const n = Number(value);
781
+ return Number.isFinite(n) && n > 0 ? n : null;
782
+ };
783
+ const permissionMode = new Set([
784
+ "manual",
785
+ "auto",
786
+ "dontAsk",
787
+ "default",
788
+ "plan",
789
+ "acceptEdits",
790
+ "bypassPermissions",
791
+ ]).has(source.permissionMode)
792
+ ? source.permissionMode
793
+ : "default";
794
+ return {
795
+ version: 1,
796
+ owner: `background:${String(id || "").slice(0, 160)}`,
797
+ sessionId: sessionId ? String(sessionId).slice(0, 256) : null,
798
+ permissionMode,
799
+ resourceBudget: {
800
+ maxTurns: positive(budget.maxTurns),
801
+ maxCostUsd: positive(budget.maxCostUsd),
802
+ },
803
+ };
804
+ }
805
+
765
806
  function persistedWorktree(state) {
766
807
  if (!state?.worktreePath) return null;
767
808
  return {
@@ -817,6 +858,7 @@ export function resumeBackgroundAgent(id, prompt, options = {}) {
817
858
  cliEntry: options.cliEntry,
818
859
  followUpArgv: ["agent", "--session", state.sessionId],
819
860
  worktree,
861
+ governance: state.governance,
820
862
  });
821
863
  }
822
864
 
@@ -855,10 +897,15 @@ export function launchBackgroundAgent({
855
897
  cliEntry,
856
898
  followUpArgv,
857
899
  worktree,
900
+ governance,
858
901
  }) {
859
902
  assertUsableCwd(cwd);
860
903
  const worktreeState = normalizeBackgroundWorktree(worktree, cwd);
861
904
  const id = createBackgroundAgentId();
905
+ const governanceState = normalizeBackgroundGovernance(governance, {
906
+ id,
907
+ sessionId,
908
+ });
862
909
  const dir = backgroundAgentsDir();
863
910
  const jobFile = join(dir, `${id}.job.${process.pid}.json`);
864
911
  const worker = fileURLToPath(
@@ -873,6 +920,7 @@ export function launchBackgroundAgent({
873
920
  cliEntry: cliEntry || process.argv[1],
874
921
  logFile: logPath(id),
875
922
  ...(worktreeState || {}),
923
+ governance: governanceState,
876
924
  // Present = interactive attach can start follow-up turns; absent = the
877
925
  // transport rejects prompts (log-only session).
878
926
  ...(Array.isArray(followUpArgv) ? { followUpArgv } : {}),
@@ -897,6 +945,7 @@ export function launchBackgroundAgent({
897
945
  exitCode: null,
898
946
  logFile: job.logFile,
899
947
  ...(worktreeState || {}),
948
+ governance: governanceState,
900
949
  };
901
950
  writeBackgroundAgentState(state);
902
951
  let child;