chainlesschain 0.162.183 → 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 (196) 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-BapS7lz4.js → AIOps-DzAl5fGx.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CXYdXEUI.js → ActionButton-DAHtMGeC.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-CKR5Lexn.js → Analytics-BHQaexv5.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DATVUOXB.js → AppLayout-Phz_pD0n.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-vb_0ZlV1.js → Artifacts-DHt47kCA.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-BI1l9GWt.js → Audit--Y_8FULl.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-BFXbaUEf.js → BackgroundAgents-N1zchH7D.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-DEYBJJ6H.js → Backup-B_BoyN30.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-COdzeO0M.js → BaseInput-Gl4uQHcT.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-oSN6sfM5.js → Chat-Cg-eIyAZ.js} +6 -6
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-BzeUi3JH.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-BezEa4tm.js → Checkbox-D0MGR2wA.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CNEL0Ife.js → Codegen-BdObJEFn.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-Bf2Y2Bn1.js → Col--ghxXT3z.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-BfUlu4Hc.js → Community-BUFeXOqz.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-BN4xWP9e.js → Compact-DhQO2_Mr.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-Bt6BKmEP.js → Compliance-3HoTqKYd.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DKLp8YxO.js → Cowork-B3zmO4VW.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-o7Hhw6ky.js → Cron-CJDrZOcc.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-DaNXWJIw.js → Crosschain-SMl15obd.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-B-Qa2mha.js → DID-CgTrTWrl.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-CMX0Qnxw.js → Dashboard-dXFKaKol.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-BLuLyRu_.js → Dropdown-qQHEhCvI.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-CFBAeMij.js → EmailListRenderer-C_ej0MWH.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DWzXQ67V.js → FamilyGuardDashboard-D6ajoE7I.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-DB1QSLgr.js → Federation-Cio2y49h.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-C-OKCToP.js → FormItemContext-ClJz1cYp.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-DMJFtFZf.js → GenericCardRenderer-Dr_8Maca.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-DsGSujOV.js → Git-CevLwp5u.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-B0iS9NEW.js → Governance-CW9YyQUY.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CH004H7c.js → Inference-CQqeAyER.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-Q9MTQ83c.js → KnowledgeGraph-cuGVxHrw.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-B_fyjZK9.js → Logs-DIuHri5d.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-C0o4FtpD.js → MarkdownRenderer-R91Qw5JA.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-BIStF9d1.js → Marketplace-CY1VhFGx.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-pGfal768.js → McpTools-BBAbi1vt.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-xbNqpIqB.js → Memory-BWYGHxJA.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-B5EQGrIK.js → MobileBridge-CPIFkW7A.js} +1 -1
  41. package/src/assets/web-panel/assets/MobileProjects-DQyWxCho.js +1 -0
  42. package/src/assets/web-panel/assets/{Mtc-a_rmfxJH.js → Mtc-Bo3GnQbx.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-BmKuofUr.js → MtcAudit-C8-c4rSW.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-BQbJ-CAN.js → Multisig-ChqMTtoq.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-JtPJKX25.js → NLProgramming-C48TyhvI.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-ZFKznTSG.js → Notes-zogUFWxp.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-B6aHRBBR.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-BFDTAUgS.js → Organization-Boi1dQfR.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-0FoTfbWH.js → Overflow-DM5YWwgA.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-CWd90etw.js → P2P-C57LKpKK.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-DtCGC-W-.js → PdhVaultBrowser-BFkPqu6Q.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-CvQ7nr7T.js → Permissions-D6fB9YSp.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-rdNPcR-Z.js → PersonalDataHub-Bp7EefEo.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-CmrfiNOs.js → Pipeline-BVln3hZf.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-D7ep1h_t.js → Privacy-Bf8P3nF6.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-C0xKINHS.js → ProjectInit-DM6wA2E2.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-AOmZFp-F.js → ProjectSettings-CGbc5Iho.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-BWHSkXZP.js → Projects-D0p9EOxm.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-Cb7JCTPL.js → Providers-B2kJcxS6.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-BDMehcXC.js → QrScannerModal-Cxu7dXYy.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-BF6_1nvx.js → QuickAsk-DTlrTPms.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-BHfT-A28.js → Recommend-Cm3X-cCj.js} +1 -1
  64. package/src/assets/web-panel/assets/RemoteSession-DNb2kjvt.js +5 -0
  65. package/src/assets/web-panel/assets/{Reputation-CsWHLEcj.js → Reputation-CZUFFjc1.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-D6srnWhm.js → Row-DAPgloxA.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-Xhr-KyVd.js → RssFeed-dsx9S793.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-vIbIJvwL.js → Search-DnnC8kz-.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-Bzi5Aiko.js → Security-VuZknRMK.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-BHtVX52P.js → Services-B3XNX8Vx.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-C0R8B2jf.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-0B37mxVU.js → Sla-HzU56n1z.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-0V2jE1Bs.js → SpeechSettings-CRle7Jl2.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-CYedRzaj.js → SyncSettings-DWadz7Sl.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-D-R1x8vm.js → Tasks-CNVTDGg8.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-BKoHztGT.js → Templates-BBAgvrlM.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant--17nwVNZ.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-C5jx4AE3.js → TimelineRenderer-mTyjNE6r.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-DYJKYa9t.js → Tokens-Bz97oBgs.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-DChpWiQ9.js → Trigger-BTRKC140.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DfWG52EA.js → Trust-B4A_KuyX.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-CMugYcyA.js → UkeySign-DES9TmLU.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-BOSmrHAd.js → VideoEditing-ClXs7Qxp.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-D5g3k3Sa.js → Wallet-IzZqFz-7.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-D-7u4Mkc.js → WebAuthn-BtOP04g_.js} +3 -3
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CFzZMSUJ.js → WorkflowEditor-_0cqnopY.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-VbR6l6mc.js → chat-ax7193uU.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-BSZn8EDS.js → colors-Bu4WGJah.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-y74obMEq.js → compact-item-BX1dlTW0.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-C326bjex.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-D_INJQT_.js → hasIn-D5wd817l.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CsovTpx9.js → index-5PYYkr8S.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-9PIVuXqo.js → index-A4uBPQCz.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BjcWKg1S.js → index-BAtBNfkr.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CMHqs3Jb.js → index-BLLsUNDc.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-B7SrC-On.js → index-BQtoRyTO.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DUU2606o.js → index-BRfXoRuh.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Iw0FfqUz.js → index-Bad2CYeH.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Dlxh9D8D.js → index-BcGdL3sG.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DuIkfnTF.js → index-C3HmzCLt.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-E_v1ZCTP.js → index-C8DkWyNr.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-4eyH-eC2.js → index-C8tLR2TG.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BnSfnSMP.js → index-CP8ELJBc.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DwHSZLXg.js → index-CUForzsG.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C2NyllnW.js → index-CYRE0NJA.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Bnua8IX3.js → index-CeVZaJSC.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-KF9Zmqln.js → index-CfzxR61U.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Cr3uuz0C.js → index-Ci3y1ZAv.js} +3 -3
  112. package/src/assets/web-panel/assets/{index-DBkzrpiL.js → index-CjEJjl5C.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-abmn_8Yz.js → index-Cv7bVWJ5.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-qMA4GYdQ.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-jYaogFE8.js → index-D9op8poE.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-GA80ir_u.js → index-DF1TysIF.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CzE53HiB.js → index-DYo9S5OT.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-gINyI1rh.js → index-D_cycOET.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-FQBsuExX.js → index-Db3CpWTr.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-IZAWt0H8.js → index-DkD1K-fF.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-C327pLbw.js → index-DmPJ6T6F.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DdyjrCAu.js → index-DmVUeWUk.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CfLJ42kv.js → index-Dzdr0zDf.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DkH1JvEt.js → index-PBLzwYnu.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DNMxIsDg.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-C-ZCaUJc.js → index-h3KJZr95.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Bfw2hf2s.js → index-iHcPtQ2e.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BJi3UqBt.js → index-jzlzm4VK.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BY8-nCgs.js → index-o14NNnxw.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-nSn2-C4N.js → index-tYPSfGbd.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-4PIeTM--.js → index-ytp2H5Zx.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DI0Zaf8-.js → initDefaultProps-D2Iut83B.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BZDKSy3Z.js → motion-BuaJVdK9.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DsIW01Vp.js → move-CaEuM92y.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-CohkDhsf.js → mtc-parser-wuKNBwys.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-DpiN5rzg.js → omit-7a3eklaq.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-qbuqni3y.js → pickAttrs-Dn4z7KvJ.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-mBky6714.js → placementArrow-DVn_qT4-.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-Bas3eQ2K.js → responsiveObserve-Dodz1OMQ.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-CBx_SKoK.js → slide-CxoGskbp.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-BlYguJSV.js → statusUtils-DBh_epQH.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BCYQmRGv.js → styleChecker-RXQ4gG5P.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-Dp5dybLM.js → useFlexGapSupport-1Wxm0HEG.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-DHAMBgA2.js → useFs-CI_ySNxJ.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-W6jsSw76.js → usePersonalDataHub-C6973rZP.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BkI6aayc.js → vnode-CalynwKr.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-C_IMGueq.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/background-session.js +11 -1
  153. package/src/commands/batch.js +160 -5
  154. package/src/commands/plugin.js +90 -27
  155. package/src/commands/session.js +31 -1
  156. package/src/commands/team.js +212 -6
  157. package/src/data/changelog.json +13 -0
  158. package/src/harness/jsonl-session-store.js +57 -6
  159. package/src/harness/mcp-client.js +65 -43
  160. package/src/lib/agent-batch.js +4 -1
  161. package/src/lib/agent-team/team-worktree.js +3 -0
  162. package/src/lib/async-hook-supervisor.cjs +82 -10
  163. package/src/lib/async-hook-supervisor.js +50 -4
  164. package/src/lib/collaboration-run-store.js +362 -0
  165. package/src/lib/hook-runner.cjs +98 -17
  166. package/src/lib/hook-runner.js +77 -6
  167. package/src/lib/hook-shell-command.cjs +148 -0
  168. package/src/lib/hooks-v2-runtime.js +188 -77
  169. package/src/lib/jsonl-session-store.js +2 -0
  170. package/src/lib/lsp/__tests__/lsp-client.test.js +18 -0
  171. package/src/lib/lsp/lsp-client.js +12 -1
  172. package/src/lib/plugin-monitor-supervisor.js +53 -16
  173. package/src/lib/plugin-runtime/bin.js +1 -1
  174. package/src/lib/plugin-runtime/install.js +249 -57
  175. package/src/lib/plugin-runtime/mcp.js +21 -2
  176. package/src/lib/plugin-runtime/monitors.js +19 -3
  177. package/src/lib/plugin-runtime/sandbox-policy.js +99 -0
  178. package/src/lib/process-execution-broker/index.js +453 -12
  179. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1347 -0
  180. package/src/lib/process-execution-broker/linux-generic-bwrap.js +1260 -0
  181. package/src/lib/process-execution-broker/platform-sandbox.js +580 -56
  182. package/src/lib/session-usage.js +202 -2
  183. package/src/lib/stream-retry.js +29 -0
  184. package/src/repl/agent-repl.js +32 -3
  185. package/src/runtime/agent-core.js +32 -4
  186. package/src/runtime/headless-runner.js +37 -0
  187. package/src/runtime/headless-stream.js +34 -2
  188. package/src/assets/web-panel/assets/ChatBubbleRenderer-CI_jT0ru.js +0 -1
  189. package/src/assets/web-panel/assets/MobileProjects-VTc3gT1y.js +0 -1
  190. package/src/assets/web-panel/assets/OrderTableRenderer-DdcsOqEz.js +0 -1
  191. package/src/assets/web-panel/assets/RemoteSession-B6EEf8hh.js +0 -5
  192. package/src/assets/web-panel/assets/Skills-DtT6rWOX.js +0 -1
  193. package/src/assets/web-panel/assets/Terminal-Dnkos7vW.js +0 -3
  194. package/src/assets/web-panel/assets/devWarning-D3Bho_6u.js +0 -1
  195. package/src/assets/web-panel/assets/index-5iAFwYx1.js +0 -1
  196. package/src/assets/web-panel/assets/index-BfnB7LPx.js +0 -1
@@ -24,14 +24,48 @@ import { TeamWorktreeCoordinator } from "../lib/agent-team/team-worktree.js";
24
24
  import { TeamBudget } from "../lib/agent-team/team-budget.js";
25
25
  import { TeamMailbox } from "../lib/agent-team/team-mailbox.js";
26
26
  import { executionBroker } from "../lib/process-execution-broker/index.js";
27
+ import {
28
+ createCollaborationRun,
29
+ createCollaborationRunId,
30
+ createCollaborationSessionId,
31
+ finalizeCollaborationRun,
32
+ readCollaborationRun,
33
+ updateCollaborationUnit,
34
+ } from "../lib/collaboration-run-store.js";
35
+ import { loadSideEffectLedger } from "../lib/side-effect-ledger-store.js";
27
36
 
28
37
  export const _deps = {
29
38
  spawn: (...args) => executionBroker.spawn(...args),
39
+ collaborationStore: {
40
+ createRun: createCollaborationRun,
41
+ createRunId: createCollaborationRunId,
42
+ createSessionId: createCollaborationSessionId,
43
+ readRun: readCollaborationRun,
44
+ updateUnit: updateCollaborationUnit,
45
+ finalizeRun: finalizeCollaborationRun,
46
+ loadSideEffects: (sessionId) =>
47
+ loadSideEffectLedger(sessionId, { failIfUnavailable: false }),
48
+ },
30
49
  };
31
50
 
32
51
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
33
52
  const BIN = path.resolve(__dirname, "..", "..", "bin", "chainlesschain.js");
34
53
 
54
+ function normalizePermissionMode(value) {
55
+ const mode = String(value || "acceptEdits");
56
+ return new Set([
57
+ "manual",
58
+ "auto",
59
+ "dontAsk",
60
+ "default",
61
+ "plan",
62
+ "acceptEdits",
63
+ "bypassPermissions",
64
+ ]).has(mode)
65
+ ? mode
66
+ : "acceptEdits";
67
+ }
68
+
35
69
  /** Load + validate a task-graph file into a registry (throws on bad input). */
36
70
  export function loadRegistry(file, { ttlMs } = {}) {
37
71
  const abs = path.resolve(process.cwd(), file);
@@ -136,6 +170,13 @@ export function spawnAgent(prompt, cwd, opts = {}) {
136
170
  "text",
137
171
  ];
138
172
  if (opts.model) args.push("--model", opts.model);
173
+ if (opts.sessionId) args.push("--session", opts.sessionId);
174
+ if (Number(opts.maxTurns) > 0) {
175
+ args.push("--max-turns", String(opts.maxTurns));
176
+ }
177
+ if (Number(opts.maxBudgetUsd) > 0) {
178
+ args.push("--max-budget-usd", String(opts.maxBudgetUsd));
179
+ }
139
180
  const child = _deps.spawn(process.execPath, args, {
140
181
  cwd,
141
182
  env: { ...process.env, CLAUDECODE: "1" },
@@ -162,12 +203,15 @@ export function spawnAgent(prompt, cwd, opts = {}) {
162
203
 
163
204
  /** Real executor: hand a task's `prompt` to a headless `cc agent -p` in cwd. */
164
205
  function makeAgentRunTask(opts = {}) {
165
- return function runTask({ task }) {
206
+ return function runTask({ key, task }) {
166
207
  const prompt = task.metadata?.prompt || task?.prompt;
167
208
  if (!prompt) {
168
209
  throw new Error(`task "${task.key}" has no \`prompt\` to --agent`);
169
210
  }
170
- return spawnAgent(prompt, process.cwd(), opts);
211
+ return spawnAgent(prompt, process.cwd(), {
212
+ ...opts,
213
+ sessionId: opts.sessionIdForTask?.(key) || null,
214
+ });
171
215
  };
172
216
  }
173
217
 
@@ -220,6 +264,13 @@ export function registerTeamCommand(program, { logger } = {}) {
220
264
  "Hand each task's `prompt` to a headless cc agent (real)",
221
265
  )
222
266
  .option("--model <model>", "Model for --agent runs")
267
+ .option(
268
+ "--permission-mode <mode>",
269
+ "Permission mode for each --agent task",
270
+ "acceptEdits",
271
+ )
272
+ .option("--agent-max-turns <n>", "Per-agent task turn cap")
273
+ .option("--agent-max-budget-usd <amount>", "Per-agent task cost cap")
223
274
  .option(
224
275
  "--worktree",
225
276
  "Run each task's `command` in its own git worktree (parallel isolation)",
@@ -272,6 +323,7 @@ export function registerTeamCommand(program, { logger } = {}) {
272
323
  : null,
273
324
  });
274
325
  let priorMembers = [];
326
+ let priorCollaborationRunId = null;
275
327
  try {
276
328
  // Resume from a prior (possibly crashed) run's state: completed tasks
277
329
  // stay COMPLETED, and any lease left dangling by a crash is reclaimed so
@@ -297,6 +349,7 @@ export function registerTeamCommand(program, { logger } = {}) {
297
349
  },
298
350
  });
299
351
  priorMembers = Array.isArray(snap.members) ? snap.members : [];
352
+ priorCollaborationRunId = snap.collaborationRunId || null;
300
353
  }
301
354
  // A teammate whose lease is still dangling here crashed last run — its
302
355
  // task is about to be reclaimed, so report it LOST before the sweep.
@@ -331,6 +384,86 @@ export function registerTeamCommand(program, { logger } = {}) {
331
384
  return;
332
385
  }
333
386
 
387
+ const permissionMode = normalizePermissionMode(options.permissionMode);
388
+ let priorCollaborationRun = null;
389
+ if (priorCollaborationRunId) {
390
+ try {
391
+ priorCollaborationRun = _deps.collaborationStore.readRun(
392
+ priorCollaborationRunId,
393
+ );
394
+ } catch {
395
+ // A malformed/foreign v3 pointer must not block registry recovery.
396
+ priorCollaborationRun = null;
397
+ }
398
+ }
399
+ const priorUnits = new Map(
400
+ (priorCollaborationRun?.units || []).map((unit) => [unit.key, unit]),
401
+ );
402
+ let collaborationRun;
403
+ try {
404
+ const runId = _deps.collaborationStore.createRunId("team");
405
+ collaborationRun = _deps.collaborationStore.createRun({
406
+ id: runId,
407
+ kind: "team",
408
+ repoRoot: process.cwd(),
409
+ permissionMode: options.agent ? permissionMode : "default",
410
+ resourceBudget: {
411
+ maxTurns: options.agentMaxTurns,
412
+ maxCostUsd: options.agentMaxBudgetUsd,
413
+ maxTasks: options.maxTasks,
414
+ maxTokens: options.maxTokens,
415
+ maxWallMs: options.maxWall
416
+ ? Math.max(1, Number(options.maxWall)) * 1000
417
+ : null,
418
+ },
419
+ units: reg.list().map((task) => ({
420
+ key: task.key,
421
+ branch: options.worktree ? `team/${task.key}` : null,
422
+ sessionId: options.agent
423
+ ? priorUnits.get(task.key)?.sessionId ||
424
+ _deps.collaborationStore.createSessionId(runId, task.key)
425
+ : null,
426
+ status:
427
+ String(task.status || "").toLowerCase() === "completed"
428
+ ? "completed"
429
+ : "pending",
430
+ })),
431
+ });
432
+ } catch (err) {
433
+ (log.error || console.error)(
434
+ `Failed to persist team governance: ${err.message}`,
435
+ );
436
+ process.exitCode = 1;
437
+ return;
438
+ }
439
+ const collaborationUnits = new Map(
440
+ collaborationRun.units.map((unit) => [unit.key, unit]),
441
+ );
442
+ const settleGovernance = (key, status, extra = {}) => {
443
+ const unit = collaborationUnits.get(key);
444
+ let sideEffects = null;
445
+ if (unit?.sessionId) {
446
+ try {
447
+ sideEffects = _deps.collaborationStore.loadSideEffects(
448
+ unit.sessionId,
449
+ );
450
+ } catch {
451
+ /* side-effect summary is additive */
452
+ }
453
+ }
454
+ const updated = _deps.collaborationStore.updateUnit(
455
+ collaborationRun.id,
456
+ key,
457
+ {
458
+ status,
459
+ ...extra,
460
+ ...(sideEffects ? { sideEffects } : {}),
461
+ },
462
+ );
463
+ const next = updated.units.find((candidate) => candidate.key === key);
464
+ if (next) collaborationUnits.set(key, next);
465
+ };
466
+
334
467
  // Persist a snapshot after each task settles so a crash mid-run is
335
468
  // resumable (persist-after-run alone would lose everything on a crash).
336
469
  // v2 bundles registry + mailbox + budget + member lifecycle so a resume
@@ -347,11 +480,12 @@ export function registerTeamCommand(program, { logger } = {}) {
347
480
  tmp,
348
481
  JSON.stringify(
349
482
  {
350
- version: 2,
483
+ version: 3,
351
484
  registry: reg.snapshot(),
352
485
  mailbox: mailbox.snapshot(),
353
486
  budget: budget.snapshot(),
354
487
  members: runnerRef ? runnerRef.members() : priorMembers,
488
+ collaborationRunId: collaborationRun.id,
355
489
  },
356
490
  null,
357
491
  2,
@@ -379,8 +513,20 @@ export function registerTeamCommand(program, { logger } = {}) {
379
513
  coord = new TeamWorktreeCoordinator(process.cwd(), {
380
514
  sparsePaths: splitList(options.sparsePaths),
381
515
  symlinkDirectories: splitList(options.symlinkDirs),
516
+ onWorktree: ({ key, branch, path: worktreePath }) => {
517
+ settleGovernance(key, "running", {
518
+ branch,
519
+ worktreePath,
520
+ startedAt: Date.now(),
521
+ });
522
+ },
382
523
  });
383
524
  if (!coord.isGitRepo()) {
525
+ try {
526
+ _deps.collaborationStore.finalizeRun(collaborationRun.id, "failed");
527
+ } catch {
528
+ /* the original validation error remains primary */
529
+ }
384
530
  (log.error || console.error)(
385
531
  "--worktree requires a git repository (run inside one)",
386
532
  );
@@ -397,7 +543,13 @@ export function registerTeamCommand(program, { logger } = {}) {
397
543
  if (!prompt) {
398
544
  throw new Error(`task "${key}" has no \`prompt\` to --agent`);
399
545
  }
400
- await spawnAgent(prompt, cwd, { model: options.model });
546
+ await spawnAgent(prompt, cwd, {
547
+ model: options.model,
548
+ permissionMode,
549
+ maxTurns: options.agentMaxTurns,
550
+ maxBudgetUsd: options.agentMaxBudgetUsd,
551
+ sessionId: collaborationUnits.get(key)?.sessionId,
552
+ });
401
553
  },
402
554
  });
403
555
  } else {
@@ -412,7 +564,13 @@ export function registerTeamCommand(program, { logger } = {}) {
412
564
  );
413
565
  runTask = makeShellRunTask(log);
414
566
  } else if (options.agent)
415
- runTask = makeAgentRunTask({ model: options.model });
567
+ runTask = makeAgentRunTask({
568
+ model: options.model,
569
+ permissionMode,
570
+ maxTurns: options.agentMaxTurns,
571
+ maxBudgetUsd: options.agentMaxBudgetUsd,
572
+ sessionIdForTask: (key) => collaborationUnits.get(key)?.sessionId,
573
+ });
416
574
  else runTask = async () => ({ dryRun: true });
417
575
 
418
576
  // Optional workflow tracing (Claude-Code 2.1.202 parity): every task
@@ -439,6 +597,19 @@ export function registerTeamCommand(program, { logger } = {}) {
439
597
  mailbox,
440
598
  recorder,
441
599
  onEvent: (e) => {
600
+ if (e.type === "task:claimed") {
601
+ settleGovernance(e.key, "running", {
602
+ startedAt: Date.now(),
603
+ });
604
+ } else if (e.type === "task:completed") {
605
+ settleGovernance(e.key, "completed", {
606
+ endedAt: Date.now(),
607
+ });
608
+ } else if (e.type === "task:failed") {
609
+ settleGovernance(e.key, e.retry ? "pending" : "failed", {
610
+ endedAt: e.retry ? null : Date.now(),
611
+ });
612
+ }
442
613
  if (options.json) console.log(JSON.stringify(e));
443
614
  else if (e.type === "task:claimed")
444
615
  (log.info || console.log)(` → ${e.key} [${e.holder}]`);
@@ -459,8 +630,43 @@ export function registerTeamCommand(program, { logger } = {}) {
459
630
  runner.seedMembers(priorMembers);
460
631
  runnerRef = runner;
461
632
 
462
- const summary = await runner.run();
633
+ let summary;
634
+ try {
635
+ summary = await runner.run();
636
+ } catch (err) {
637
+ try {
638
+ _deps.collaborationStore.finalizeRun(collaborationRun.id, "failed");
639
+ } catch {
640
+ /* retain the original runner failure */
641
+ }
642
+ throw err;
643
+ }
463
644
  persist();
645
+ try {
646
+ // Reconcile from the authoritative registry after all event callbacks.
647
+ // This closes any best-effort onEvent write that failed mid-run.
648
+ for (const task of reg.list()) {
649
+ const state = String(task.status || "").toLowerCase();
650
+ const status =
651
+ state === "completed"
652
+ ? "completed"
653
+ : state === "cancelled" || state === "pending"
654
+ ? "cancelled"
655
+ : "failed";
656
+ settleGovernance(task.key, status, {
657
+ endedAt: Date.now(),
658
+ });
659
+ }
660
+ _deps.collaborationStore.finalizeRun(
661
+ collaborationRun.id,
662
+ summary.done ? "completed" : "failed",
663
+ );
664
+ } catch (err) {
665
+ (log.error || console.error)(
666
+ `Failed to finalize team governance: ${err.message}`,
667
+ );
668
+ process.exitCode = 1;
669
+ }
464
670
 
465
671
  if (recorder && options.otlp) {
466
672
  try {
@@ -1,6 +1,19 @@
1
1
  {
2
2
  "source": "CHANGELOG.md",
3
3
  "releases": [
4
+ {
5
+ "productVersion": null,
6
+ "cliVersion": "0.162.184",
7
+ "cliVersions": ["0.4.75", "0.37.36", "0.162.183", "0.162.184"],
8
+ "date": "2026-07-28",
9
+ "title": "",
10
+ "sections": [
11
+ {
12
+ "heading": "Added — cc CLI 0.162.184 + IDE retry, collaboration, and recovery governance",
13
+ "body": "> `chainlesschain` **0.162.183 → 0.162.184**, VS Code\n> **0.37.35 → 0.37.36**, and JetBrains **0.4.74 → 0.4.75** (2026-07-28).\n> `@chainlesschain/personal-data-hub` remains **0.4.57** and\n> `@chainlesschain/agent-sdk` remains **0.1.7**.\n\n- **Measured retry and tool attribution**: compact transcripts now record\n bounded tool durations, observed same-turn retries, and secret-free streaming\n LLM retry reasons with actual provider/model identity. CLI and both IDE usage\n reports consume the same authoritative aggregates.\n- **Durable team/batch governance**: collaboration runs persist bounded\n owner/session, permission mode, resource budget, lifecycle, and side-effect\n counts without prompts, argv, tool arguments, output, or credentials. IDE\n worktree views project these records as managed tasks without exposing\n background attach/stop controls.\n- **Transactional plugin upgrades**: staged installs revalidate manifests and\n signed SBOMs before atomic activation. Failed copies, load checks,\n post-install handling, or declined capability widening restore the prior\n active version; forced same-version reinstalls restore the exact old bytes.\n- **IDE activation feedback**: VS Code and JetBrains parse controlled\n `activated / rolled_back / unchanged` results, reload live sessions only\n after activation, and require explicit user approval before retrying with a\n widened capability grant.\n- **Process-boundary hardening**: plugin hooks, MCP/LSP servers, monitors, and\n native bins retain sandbox policy through the centralized execution broker;\n Linux bubblewrap setup fails closed with bounded diagnostics.\n- **Release validation**: publication requires this exact release commit to\n pass CLI CI and CLI Strict Sandbox on Linux, Windows, and macOS plus the\n packaged VSIX and JetBrains unit/build/Remote Robot gates."
14
+ }
15
+ ]
16
+ },
4
17
  {
5
18
  "productVersion": null,
6
19
  "cliVersion": "0.162.183",
@@ -164,22 +164,73 @@ export function appendToolCall(sessionId, toolName, args) {
164
164
 
165
165
  /**
166
166
  * Compact tool-call record for usage attribution (用量归因): tool name +
167
- * error flag (+ optional skill and plugin/version hints) deliberately NOT
168
- * the args, which can carry whole file bodies (write_file content) and would
169
- * bloat the transcript. Written at tool-result time by the agent drivers so
170
- * `cc session usage --by tool|mcp|plugin` and `cc insights` can aggregate tool
171
- * use for any persisted session.
167
+ * error flag (+ optional skill, plugin/version, and bounded observed duration)
168
+ * — deliberately NOT the args, which can carry whole file bodies (write_file
169
+ * content) and would bloat the transcript. Written at tool-result time by the
170
+ * agent drivers so `cc session usage --by tool|mcp|plugin` and `cc insights`
171
+ * can aggregate tool use for any persisted session.
172
172
  */
173
173
  export function appendToolCallCompact(
174
174
  sessionId,
175
- { tool, isError, skill, plugin, pluginVersion } = {},
175
+ { tool, isError, skill, plugin, pluginVersion, durationMs } = {},
176
176
  ) {
177
+ const duration = normalizeCompactDuration(durationMs);
177
178
  appendEvent(sessionId, "tool_call", {
178
179
  tool: tool || "?",
179
180
  is_error: Boolean(isError),
180
181
  ...(skill ? { skill: String(skill) } : {}),
181
182
  ...(plugin ? { plugin: String(plugin) } : {}),
182
183
  ...(pluginVersion ? { plugin_version: String(pluginVersion) } : {}),
184
+ ...(duration !== null ? { duration_ms: duration } : {}),
185
+ });
186
+ }
187
+
188
+ /** Hard cap for one observed failed LLM/tool attempt (seven days). */
189
+ export const MAX_COMPACT_DURATION_MS = 7 * 24 * 60 * 60 * 1000;
190
+
191
+ function normalizeCompactDuration(value) {
192
+ if (value === null || value === undefined || value === "") return null;
193
+ const number = Number(value);
194
+ if (!Number.isFinite(number) || number < 0) return null;
195
+ return Math.min(MAX_COMPACT_DURATION_MS, Math.round(number));
196
+ }
197
+
198
+ /**
199
+ * Persist one automatic LLM stream retry without raw errors, prompts, URLs, or
200
+ * credentials. Provider/model are bounded identity labels; reason is a closed
201
+ * vocabulary produced by classifyStreamRetryReason().
202
+ */
203
+ export function appendLlmRetryCompact(
204
+ sessionId,
205
+ { attempt, durationMs, provider, model, reason } = {},
206
+ ) {
207
+ const attemptNumber = Math.max(
208
+ 1,
209
+ Math.min(15, Math.trunc(Number(attempt) || 1)),
210
+ );
211
+ const duration = normalizeCompactDuration(durationMs);
212
+ const cleanLabel = (value, max) => {
213
+ if (typeof value !== "string") return null;
214
+ const clean = value.replace(/\p{Cc}/gu, "").trim();
215
+ return clean ? clean.slice(0, max) : null;
216
+ };
217
+ const allowedReasons = new Set([
218
+ "timeout",
219
+ "dns",
220
+ "connection_refused",
221
+ "network_unreachable",
222
+ "connection_reset",
223
+ "unknown",
224
+ ]);
225
+ const cleanReason = allowedReasons.has(reason) ? reason : "unknown";
226
+ const cleanProvider = cleanLabel(provider, 64);
227
+ const cleanModel = cleanLabel(model, 128);
228
+ appendEvent(sessionId, "llm_retry", {
229
+ attempt: attemptNumber,
230
+ duration_ms: duration ?? 0,
231
+ reason: cleanReason,
232
+ ...(cleanProvider ? { provider: cleanProvider } : {}),
233
+ ...(cleanModel ? { model: cleanModel } : {}),
183
234
  });
184
235
  }
185
236
 
@@ -14,6 +14,7 @@ import { EventEmitter } from "events";
14
14
  import { pathToFileURL } from "url";
15
15
  import { safeJsonParse } from "../lib/safe-json.js";
16
16
  import { EventRuntimeProducer } from "../lib/event-runtime-producer.js";
17
+ import { resolvePluginWorkspaceAuthority } from "../lib/plugin-runtime/sandbox-policy.js";
17
18
 
18
19
  /**
19
20
  * Injectable dependencies — overridable from tests.
@@ -62,8 +63,7 @@ const URL_ELICITATION_REQUIRED = -32042;
62
63
  function supportsUrlElicitationVersion(version) {
63
64
  const normalized = String(version || "");
64
65
  return (
65
- /^\d{4}-\d{2}-\d{2}$/.test(normalized) &&
66
- normalized >= MCP_PROTOCOL_VERSION
66
+ /^\d{4}-\d{2}-\d{2}$/.test(normalized) && normalized >= MCP_PROTOCOL_VERSION
67
67
  );
68
68
  }
69
69
 
@@ -130,9 +130,7 @@ export function normalizeMcpElicitationRequest(
130
130
 
131
131
  const elicitationId = String(params.elicitationId || "").trim();
132
132
  if (!elicitationId) {
133
- const error = new Error(
134
- "URL mode MCP elicitation requires elicitationId",
135
- );
133
+ const error = new Error("URL mode MCP elicitation requires elicitationId");
136
134
  error.code = "CC_MCP_ELICITATION_INVALID";
137
135
  throw error;
138
136
  }
@@ -267,10 +265,14 @@ export class MCPClient extends EventEmitter {
267
265
  Number(options.maxConcurrentElicitations) > 0
268
266
  ? Math.min(128, Number(options.maxConcurrentElicitations))
269
267
  : 32;
270
- this._protocolVersion =
271
- String(options.protocolVersion || MCP_PROTOCOL_VERSION);
268
+ this._protocolVersion = String(
269
+ options.protocolVersion || MCP_PROTOCOL_VERSION,
270
+ );
272
271
  this._runtimeProducer = options.eventRuntimeStore
273
- ? new EventRuntimeProducer({ store: options.eventRuntimeStore, emitter: this })
272
+ ? new EventRuntimeProducer({
273
+ store: options.eventRuntimeStore,
274
+ emitter: this,
275
+ })
274
276
  : null;
275
277
  }
276
278
 
@@ -279,11 +281,11 @@ export class MCPClient extends EventEmitter {
279
281
  const sessionId = options.sessionId;
280
282
  if (sessionId != null && sessionId !== "") {
281
283
  const key = String(sessionId);
282
- if (typeof handler === "function") this._elicitationHandlers.set(key, handler);
284
+ if (typeof handler === "function")
285
+ this._elicitationHandlers.set(key, handler);
283
286
  else this._elicitationHandlers.delete(key);
284
287
  } else {
285
- this._elicitationHandler =
286
- typeof handler === "function" ? handler : null;
288
+ this._elicitationHandler = typeof handler === "function" ? handler : null;
287
289
  }
288
290
  if (Number(options.timeoutMs) > 0) {
289
291
  this._elicitationTimeoutMs = Number(options.timeoutMs);
@@ -389,8 +391,9 @@ export class MCPClient extends EventEmitter {
389
391
  requestId,
390
392
  params,
391
393
  );
392
- const negotiatedVersion =
393
- this.servers.get(String(serverName))?.protocolVersion;
394
+ const negotiatedVersion = this.servers.get(
395
+ String(serverName),
396
+ )?.protocolVersion;
394
397
  if (
395
398
  request.mode === "url" &&
396
399
  negotiatedVersion &&
@@ -431,10 +434,7 @@ export class MCPClient extends EventEmitter {
431
434
  direct !== undefined &&
432
435
  String(direct?.action || "").toLowerCase() !== "defer"
433
436
  ) {
434
- const response = this._normalizeElicitationResponse(
435
- direct,
436
- request,
437
- );
437
+ const response = this._normalizeElicitationResponse(direct, request);
438
438
  this._settleUrlElicitation(request, response);
439
439
  try {
440
440
  this._runtimeProducer?.store.acknowledgeInbox(
@@ -504,7 +504,8 @@ export class MCPClient extends EventEmitter {
504
504
  });
505
505
  this.emit("elicitation-request", {
506
506
  ...request,
507
- respond: (response) => this.respondElicitation(serverName, requestId, response),
507
+ respond: (response) =>
508
+ this.respondElicitation(serverName, requestId, response),
508
509
  cancel: () => this.cancelElicitation(serverName, requestId),
509
510
  });
510
511
  });
@@ -515,9 +516,7 @@ export class MCPClient extends EventEmitter {
515
516
  if (!id) return;
516
517
  const existing = this._urlElicitations.get(id);
517
518
  if (existing && existing.server !== request.server) {
518
- throw new Error(
519
- `MCP URL elicitation id collision across servers: ${id}`,
520
- );
519
+ throw new Error(`MCP URL elicitation id collision across servers: ${id}`);
521
520
  }
522
521
  if (existing && existing.request?.url !== request.url) {
523
522
  throw new Error(
@@ -544,7 +543,9 @@ export class MCPClient extends EventEmitter {
544
543
  const entry = this._urlElicitations.get(request.elicitationId);
545
544
  if (!entry || entry.status === "completed") return;
546
545
  entry.status =
547
- response?.action === "accept" ? "accepted" : response?.action || "decline";
546
+ response?.action === "accept"
547
+ ? "accepted"
548
+ : response?.action || "decline";
548
549
  if (entry.status === "accepted" && entry.completionNotified) {
549
550
  this._completeUrlElicitation(entry);
550
551
  }
@@ -757,7 +758,26 @@ export class MCPClient extends EventEmitter {
757
758
  `stdio transport requires a command (server "${name}")`,
758
759
  );
759
760
  }
760
- const proc = _deps.spawn(config.command, config.args || [], {
761
+ const isPlugin = config.origin === "plugin:mcp";
762
+ const pluginWorkspaceRoot = isPlugin
763
+ ? resolvePluginWorkspaceAuthority(config.pluginWorkspaceAuthority, {
764
+ origin: config.origin,
765
+ pluginId: config.pluginId,
766
+ pluginVersion: config.pluginVersion,
767
+ pluginSource: config.pluginSource,
768
+ })
769
+ : null;
770
+ if (
771
+ isPlugin &&
772
+ config.sandboxPolicy?.requiredBoundaries?.length > 0 &&
773
+ !pluginWorkspaceRoot
774
+ ) {
775
+ throw new Error(
776
+ `plugin MCP server "${name}" is missing its trusted workspace authority`,
777
+ );
778
+ }
779
+ const spawnOptions = {
780
+ cwd: pluginWorkspaceRoot || process.cwd(),
761
781
  stdio: ["pipe", "pipe", "pipe"],
762
782
  // process.env < agent identity (CLAUDECODE / session id) < the
763
783
  // server's own config.env, so an explicit per-server override wins.
@@ -776,6 +796,19 @@ export class MCPClient extends EventEmitter {
776
796
  ...(config.sandboxPolicy
777
797
  ? { sandboxPolicy: config.sandboxPolicy }
778
798
  : {}),
799
+ };
800
+ const sandboxExecutionContract =
801
+ !isPlugin || pluginWorkspaceRoot
802
+ ? executionBroker.issueLinuxWorkspaceSandboxExecutionContract(
803
+ config.command,
804
+ config.args || [],
805
+ spawnOptions,
806
+ pluginWorkspaceRoot || process.cwd(),
807
+ )
808
+ : null;
809
+ const proc = _deps.spawn(config.command, config.args || [], {
810
+ ...spawnOptions,
811
+ ...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
779
812
  });
780
813
 
781
814
  entry.process = proc;
@@ -1113,7 +1146,8 @@ export class MCPClient extends EventEmitter {
1113
1146
  } catch {
1114
1147
  return false;
1115
1148
  }
1116
- if (request.mode !== "url" || ids.has(request.elicitationId)) return false;
1149
+ if (request.mode !== "url" || ids.has(request.elicitationId))
1150
+ return false;
1117
1151
  ids.add(request.elicitationId);
1118
1152
  normalized.push(request);
1119
1153
  }
@@ -1392,10 +1426,7 @@ export class MCPClient extends EventEmitter {
1392
1426
  entry._httpMessageStream = stream;
1393
1427
  stream.promise = (async () => {
1394
1428
  try {
1395
- while (
1396
- !stream.stopped &&
1397
- this.servers.get(serverName) === entry
1398
- ) {
1429
+ while (!stream.stopped && this.servers.get(serverName) === entry) {
1399
1430
  stream.controller =
1400
1431
  typeof AbortController === "function"
1401
1432
  ? new AbortController()
@@ -1427,9 +1458,7 @@ export class MCPClient extends EventEmitter {
1427
1458
  );
1428
1459
  }
1429
1460
  const contentType = response.headers?.get
1430
- ? String(
1431
- response.headers.get("content-type") || "",
1432
- ).toLowerCase()
1461
+ ? String(response.headers.get("content-type") || "").toLowerCase()
1433
1462
  : "";
1434
1463
  if (!contentType.includes("text/event-stream")) return;
1435
1464
  const cap = Number.isFinite(entry.config?.maxBufferChars)
@@ -1438,8 +1467,7 @@ export class MCPClient extends EventEmitter {
1438
1467
  await _consumeSseMessageStream(response, {
1439
1468
  cap,
1440
1469
  stream,
1441
- onMessage: (message) =>
1442
- this._handleMessage(serverName, message),
1470
+ onMessage: (message) => this._handleMessage(serverName, message),
1443
1471
  });
1444
1472
  } catch (error) {
1445
1473
  if (stream.stopped || stream.controller?.signal.aborted) return;
@@ -1564,11 +1592,8 @@ export class MCPClient extends EventEmitter {
1564
1592
 
1565
1593
  let envelope;
1566
1594
  if (contentType.includes("text/event-stream")) {
1567
- envelope = await _extractSseResponse(
1568
- response,
1569
- id,
1570
- cap,
1571
- (message) => this._handleMessage(serverName, message),
1595
+ envelope = await _extractSseResponse(response, id, cap, (message) =>
1596
+ this._handleMessage(serverName, message),
1572
1597
  );
1573
1598
  } else {
1574
1599
  const text = await _readBodyCapped(response, cap);
@@ -1813,8 +1838,7 @@ export class MCPClient extends EventEmitter {
1813
1838
  ...(entry.httpSessionId
1814
1839
  ? { "Mcp-Session-Id": entry.httpSessionId }
1815
1840
  : {}),
1816
- "MCP-Protocol-Version":
1817
- entry.protocolVersion || this._protocolVersion,
1841
+ "MCP-Protocol-Version": entry.protocolVersion || this._protocolVersion,
1818
1842
  };
1819
1843
  try {
1820
1844
  const pending = _deps.fetch(entry.httpUrl, {
@@ -1986,9 +2010,7 @@ async function _consumeSseMessageStream(
1986
2010
  if (done) break;
1987
2011
  buffer += decoder.decode(value, { stream: true });
1988
2012
  if (cap > 0 && buffer.length > cap) {
1989
- throw new Error(
1990
- `MCP HTTP SSE event exceeded the ${cap}-char cap`,
1991
- );
2013
+ throw new Error(`MCP HTTP SSE event exceeded the ${cap}-char cap`);
1992
2014
  }
1993
2015
  let boundary;
1994
2016
  while ((boundary = buffer.search(/\r?\n\r?\n/)) >= 0) {
@@ -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;