chainlesschain 0.162.180 → 0.162.181

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 (249) hide show
  1. package/package.json +6 -3
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-D_TIL54O.js → AIOps-cq0dwbnf.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-eQwy9r-f.js → ActionButton-CvcgGl3w.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-D2eNuDgT.js → Analytics-hM2-jFqg.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-CFhwOFsf.js → AppLayout-B9_Gsd_g.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-CJUMw6PB.js → Artifacts-B7bu52Og.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-CDB9V58o.js → Audit-DThfDgqB.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-TBppYFjN.js → BackgroundAgents-DjlFiD4v.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-CYEOUpzJ.js → Backup-CK9xJKnG.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-C88fFT2u.js → BaseInput-DEiSU8K8.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-mkr1vC3N.js → Chat-CpT96czj.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-C7bofJuA.js → ChatBubbleRenderer-BKPPNmZI.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-CVU4xqti.js → Checkbox-C1GfDnO0.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-rrNcByz3.js → Codegen-3DAKgiLx.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-Cqu-zCvh.js → Col-74cOwZU1.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-RGRlSgXV.js → Community-tn9Kkz9j.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DBm6epOK.js → Compact-BcoXH7m4.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-BgKw5iaW.js → Compliance-DIPGUBgi.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-BqQcNO3Z.js → Cowork-doD5_91c.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-DO5gTFNb.js → Cron-C-IDvubz.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-B8hbJNuN.js → Crosschain-CdcCoBdj.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-B08bFJ2C.js → DID-DdH5aMMA.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-xNdy_ifo.js → Dashboard-DHLA4mm0.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-D-KIZgWL.js → Dropdown-dEeCt7Md.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-DKCvsaGo.js → EmailListRenderer-BMaecFv9.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D_cfqama.js → FamilyGuardDashboard-DUaUukTN.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-B-UqQD68.js → Federation-CMBE0_rp.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BWWvWuJW.js → FormItemContext-PG-JDbXq.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-CPchx6N7.js → GenericCardRenderer-Bcb0p88m.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Vx4co9Ff.js → Git-Bgv15myB.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-D4x0pp_m.js → Governance-DB8YTArI.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-T3KvwPYR.js → Inference-BQMwCWaS.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-Ddv3KUv3.js → KnowledgeGraph-B1_ZziiB.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-DfH-Dkae.js → Logs-DovEEnt_.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-DHW3PbCq.js → MarkdownRenderer-D201OL4T.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-tjgb0R3D.js → Marketplace-BlCXSpyB.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DHAk5nYX.js → McpTools-CsCR09Wj.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-A2Yx7thM.js → Memory-CBVRLKno.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-BEmTVfN5.js → MobileBridge-C9OIafgA.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-DuFHUgE4.js → MobileProjects-D_zoJ8Xs.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-BDSll0OZ.js → Mtc-vE-HdvLj.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-CuBvluaS.js → MtcAudit-CPLaekCd.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-BmXAA-Sb.js → Multisig-C_UP9C2o.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-ci2stZAe.js → NLProgramming-2nODJcU1.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-D-1RpqnS.js → Notes-BhE-SJGu.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-C0GyitIT.js → NotificationSettings-D21q7BNU.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-U2bN-hj5.js → OrderTableRenderer-0b62GYOD.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BNZgpPci.js → Organization-CEqmw_sb.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-CDidKmUN.js → Overflow-MW-gMDO_.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BnreBaXq.js → P2P-CY0hjI_G.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-DwJUxO9j.js → PdhVaultBrowser-BbWh7Man.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-BQGgJPH0.js → Permissions-ChqEA_4Q.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-k_fSkCwL.js → PersonalDataHub-B3YrbYfD.js} +5 -5
  55. package/src/assets/web-panel/assets/{Pipeline-WGqKMqau.js → Pipeline-B8WBhhlW.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-DtHj9Ymq.js → Privacy-Cmb0PXNx.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-B3wH2SFC.js → ProjectInit-hF6zW3Nz.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-Cd0cgcA1.js → ProjectSettings-BFSVfpW0.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-DAWkVFkc.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-DTpxEXur.js → Providers-B8yux8Dx.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-B2v6gR-C.js → QrScannerModal-N76cQluO.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-D3Nr9xXR.js → QuickAsk-ClvZ0KYR.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-C9inoMky.js → Recommend-B-GfgBpT.js} +1 -1
  64. package/src/assets/web-panel/assets/RemoteSession-mlglCdQi.js +5 -0
  65. package/src/assets/web-panel/assets/{Reputation-41KFG646.js → Reputation-DJIZzQeG.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DqAaqqCv.js → Row-COzxu_bb.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-CMDpCDKc.js → RssFeed-CoYsJQuE.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-B6mtegNL.js → Search-DC77Mq9S.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-BaePFVZ8.js → Security-Loy2EdMO.js} +3 -3
  70. package/src/assets/web-panel/assets/{Services-DeG3Qscl.js → Services-GUD07Eu0.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-D_QP7F90.js → Skeleton-DWzkptYU.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-DLGyUgIA.js → Skills-CEDn1r-Z.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-BN2pxW1Q.js → Sla-ub_k4pXh.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-BWsZPd8B.js → SpeechSettings-D7HLX9CA.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-VOZRPODW.js → SyncSettings-DbwOvl6m.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-CBQzoAu5.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-eJSob-q0.js → Templates-BNVeIhud.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-Bdwk8Rwy.js → Tenant-PS9AzhXU.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-CsMLKIoV.js → Terminal-Cgb4eLB_.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-D5qkbPKT.js → TimelineRenderer-CrmXMOkg.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-DKgwn8hI.js → Tokens-DD-VDHw1.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-HlEHniGM.js → Trigger-BsoN_n-n.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-CdPcsBpN.js → Trust-n8LmN4ZV.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-vAdXd-vG.js → UkeySign-C9_7MImA.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-9hHBYFnH.js → VideoEditing-DyBteNgn.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-Dxh5d1yR.js → Wallet-BS5ixeXI.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-BsFmLHkp.js → WebAuthn-spc4UImU.js} +3 -3
  88. package/src/assets/web-panel/assets/{WorkflowEditor-Cn0OIwLn.js → WorkflowEditor-D7iVvaBO.js} +1 -1
  89. package/src/assets/web-panel/assets/chat-6CmBkHrL.js +1 -0
  90. package/src/assets/web-panel/assets/{colors-Du9jzLN0.js → colors-BGoCLkxI.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-DhEscfzw.js → compact-item-fYZBetMO.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-CwzEfF3i.js → createContext-BeM-1UVj.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-BzPwHnw8.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DT_GSE6J.js → hasIn-CE7ZuEnV.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-53z1YUxX.js → index--lap6cf7.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-NA2oBLSB.js → index-ATcSr_2O.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DWL3SYN3.js → index-B2U-ObMq.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-D-QkW1fp.js → index-B3CDgvNW.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BF-ZCVGE.js → index-B4wiTEOM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-D4kZoXNr.js → index-B8BsdNV_.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DnVVYKCE.js → index-BEVm0lCS.js} +10 -10
  102. package/src/assets/web-panel/assets/{index-DfRDyY1q.js → index-BNZDILcO.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-B8s5FxJY.js → index-BU_2qiDk.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BnHhFnwt.js → index-BV551878.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DhXay17G.js → index-BfPL-m46.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-B6QDWRLk.js → index-BowH1eLQ.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DN60VBn5.js → index-BsMfoaeo.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Cnok8mhZ.js → index-BunaQCa0.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Dny_ZnAO.js → index-C0v6YKKO.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-D8gt0j7r.js → index-C4SJVjs4.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Mobw3PHs.js → index-C9Y3k0HI.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CAVW0cWM.js → index-CIpVrf5P.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CyEhUJcu.js → index-CRLjEP1H.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DzjzrOli.js → index-CRnUE8Cb.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-D5IytTND.js → index-Cip8emOQ.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BQWENaZf.js → index-CpIGIB85.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CQWxDOvM.js → index-D1r9tT3J.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BrkJ7JPm.js → index-DT3zPSVX.js} +1 -1
  119. package/src/assets/web-panel/assets/index-DacrAdc-.js +1 -0
  120. package/src/assets/web-panel/assets/{index-BFD0cEna.js → index-De0quFDw.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-v5HLYCVa.js → index-DoXk5KJ8.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DtRwLhee.js → index-DsvfrESp.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BgSR2WMG.js → index-Dw1PyYPm.js} +1 -1
  124. package/src/assets/web-panel/assets/index-EHzNJozo.js +1 -0
  125. package/src/assets/web-panel/assets/{index-CGvgnfl9.js → index-MtOuq2i6.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-1LoutPJd.js → index-Puq6Luf1.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-tND8-TDU.js → index-QIpnpZgz.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-D3llLkYM.js → index-RMZ8a7Yt.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-C4SN3by5.js → index-WUrJ6S1j.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-VeHs6qH0.js → index-YjcumOZ4.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Cc9aPkv-.js → index-lJBjB1QX.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BIYTjbv_.js → index-ukg99D1V.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-gFjYdstW.js → index-zb7rVOhV.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-Cvt59c10.js → initDefaultProps-CYHMs1ag.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-D15pPEzl.js → motion-DDB2Ffk5.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-N55KeXJ7.js → move-Bg3cD-dX.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-CyFO1bYP.js → mtc-parser-B5nedjTS.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-B5IXfxU2.js → omit-BHyuUILu.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-CpVqsulV.js → pickAttrs-C9YQnEN9.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-ME36sMF3.js → placementArrow-DBUFYUe7.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-BXAIR1r-.js → responsiveObserve-nBs2Obnk.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-BkOX3-Dp.js → slide-DkCEaXNU.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-Cy4Aklpt.js → statusUtils-aqE1Wi6U.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-gUd2T206.js → styleChecker-CN5bhfHB.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-CaVD4o99.js → useFlexGapSupport-B_oX_kvJ.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs--pFGl0KD.js → useFs-rCuXHeme.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-D45iP86i.js → usePersonalDataHub-DZR9Abyd.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BayoChH_.js → vnode-ConCYQlX.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-CpP38poi.js → zoom-CUdDxG5M.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/agent.js +4 -1
  152. package/src/commands/background-session.js +2 -1
  153. package/src/commands/context.js +69 -2
  154. package/src/data/changelog.json +13 -0
  155. package/src/gateways/ws/background-agent-protocol.js +38 -0
  156. package/src/gateways/ws/message-dispatcher.js +1 -0
  157. package/src/gateways/ws/remote-session-protocol.js +65 -6
  158. package/src/gateways/ws/ws-server.js +8 -1
  159. package/src/harness/feature-flags.js +15 -8
  160. package/src/harness/mcp-client.js +618 -43
  161. package/src/lazy-dispatch.js +30 -1
  162. package/src/lib/agent-authority.js +22 -0
  163. package/src/lib/agent-core.js +2 -0
  164. package/src/lib/agent-sandbox.js +161 -0
  165. package/src/lib/agent-team/team-runner.js +22 -0
  166. package/src/lib/artifact-store.js +35 -0
  167. package/src/lib/async-hook-supervisor.cjs +13 -2
  168. package/src/lib/audit-mtc.js +3 -2
  169. package/src/lib/background-agent-supervisor.js +104 -3
  170. package/src/lib/background-interaction-journal.js +407 -0
  171. package/src/lib/background-interaction-resolver.js +23 -34
  172. package/src/lib/background-session-transport.js +12 -2
  173. package/src/lib/capability-manifest.js +41 -3
  174. package/src/lib/capability-negotiation.js +1 -1
  175. package/src/lib/channels/channel-manager.js +33 -0
  176. package/src/lib/channels/telegram-channel.js +23 -5
  177. package/src/lib/channels/webhook-channel.js +38 -15
  178. package/src/lib/chrome-connector.js +169 -23
  179. package/src/lib/config-manager.js +23 -9
  180. package/src/lib/context-breakdown.js +31 -0
  181. package/src/lib/cowork-cron.js +274 -32
  182. package/src/lib/cross-chain-mtc.js +3 -2
  183. package/src/lib/durable-security-store.js +124 -0
  184. package/src/lib/event-runtime-host.js +67 -6
  185. package/src/lib/event-runtime-store.js +163 -0
  186. package/src/lib/goal-store.js +32 -12
  187. package/src/lib/headless-manifest.js +2 -1
  188. package/src/lib/hook-environment.cjs +58 -0
  189. package/src/lib/hook-manager.js +12 -5
  190. package/src/lib/hook-runner.cjs +122 -28
  191. package/src/lib/hook-runner.js +1 -0
  192. package/src/lib/hooks-v2-producers.js +36 -0
  193. package/src/lib/hooks-v2-runtime.js +329 -52
  194. package/src/lib/interaction-adapter.js +60 -19
  195. package/src/lib/interaction-binding.js +64 -0
  196. package/src/lib/json-schema-output.js +439 -14
  197. package/src/lib/json-schema-validate.js +514 -883
  198. package/src/lib/lan-pairing-tokens.js +115 -57
  199. package/src/lib/lsp/__tests__/lsp-client.test.js +32 -0
  200. package/src/lib/lsp/__tests__/lsp-manager.test.js +25 -0
  201. package/src/lib/lsp/lsp-client.js +4 -0
  202. package/src/lib/lsp/lsp-manager.js +3 -0
  203. package/src/lib/lsp/lsp-server-registry.js +18 -0
  204. package/src/lib/mcp-client.js +1 -0
  205. package/src/lib/mcp-oauth.js +35 -21
  206. package/src/lib/packer/pkg-config-generator.js +1 -0
  207. package/src/lib/permission-decision.js +99 -0
  208. package/src/lib/plugin-monitor-supervisor.js +3 -0
  209. package/src/lib/plugin-runtime/capabilities.js +50 -0
  210. package/src/lib/plugin-runtime/capability-consent.js +42 -35
  211. package/src/lib/plugin-runtime/hooks.js +36 -9
  212. package/src/lib/plugin-runtime/install.js +23 -7
  213. package/src/lib/plugin-runtime/lsp.js +4 -0
  214. package/src/lib/plugin-runtime/manifest.js +50 -2
  215. package/src/lib/plugin-runtime/mcp.js +30 -5
  216. package/src/lib/plugin-runtime/monitors.js +11 -0
  217. package/src/lib/plugin-runtime/plugin-options.js +94 -38
  218. package/src/lib/plugin-runtime/policy.js +29 -2
  219. package/src/lib/plugin-runtime/sandbox-policy.js +84 -0
  220. package/src/lib/plugin-runtime/scopes.js +7 -3
  221. package/src/lib/plugin-runtime/trust.js +36 -31
  222. package/src/lib/process-execution-broker/credential-agent.js +114 -16
  223. package/src/lib/process-execution-broker/credential-transport-worker.js +312 -0
  224. package/src/lib/process-execution-broker/credential-transport.js +378 -0
  225. package/src/lib/process-execution-broker/index.js +408 -19
  226. package/src/lib/process-execution-broker/platform-sandbox.js +561 -21
  227. package/src/lib/process-execution-broker/windows-sandbox.ps1 +999 -0
  228. package/src/lib/project-mcp-trust.js +21 -26
  229. package/src/lib/protocol-replay.js +19 -12
  230. package/src/lib/settings-hooks.cjs +13 -0
  231. package/src/lib/skill-loader.js +83 -10
  232. package/src/lib/sync-credentials.js +5 -4
  233. package/src/lib/turn-binding.js +8 -6
  234. package/src/repl/agent-repl.js +113 -28
  235. package/src/repl/repl-turn-binding.js +5 -2
  236. package/src/runtime/agent-core.js +331 -26
  237. package/src/runtime/coding-agent-contract-shared.cjs +3 -3
  238. package/src/runtime/headless-runner.js +101 -4
  239. package/src/runtime/headless-stream.js +209 -23
  240. package/src/runtime/mcp-config.js +29 -17
  241. package/src/runtime/policies/agent-policy.js +2 -0
  242. package/src/workers/background-agent-worker.js +128 -13
  243. package/src/assets/web-panel/assets/Projects-BbgmyQnS.js +0 -1
  244. package/src/assets/web-panel/assets/RemoteSession-Bqk3EYSu.js +0 -4
  245. package/src/assets/web-panel/assets/Tasks-3bDAUz8K.js +0 -1
  246. package/src/assets/web-panel/assets/chat-CJ19O6Dh.js +0 -1
  247. package/src/assets/web-panel/assets/devWarning-BBMFnsja.js +0 -1
  248. package/src/assets/web-panel/assets/index-Do8JMV4N.js +0 -1
  249. package/src/assets/web-panel/assets/index-UYAJ6iNM.js +0 -1
@@ -45,6 +45,8 @@ export class EventRuntimeStore {
45
45
  leaseMs = 120000,
46
46
  lockTimeoutMs = 2000,
47
47
  maxQueueLength = 10000,
48
+ hostStaleMs = 15000,
49
+ maxHostRecords = 100,
48
50
  } = {}) {
49
51
  this.dir = dir;
50
52
  this._now = typeof now === "function" ? now : () => Number(now);
@@ -53,6 +55,8 @@ export class EventRuntimeStore {
53
55
  this.leaseMs = Math.max(1, Number(leaseMs) || 120000);
54
56
  this.lockTimeoutMs = Math.max(0, Number(lockTimeoutMs) || 2000);
55
57
  this.maxQueueLength = Math.max(1, Number(maxQueueLength) || 10000);
58
+ this.hostStaleMs = Math.max(1, Number(hostStaleMs) || 15000);
59
+ this.maxHostRecords = Math.max(1, Number(maxHostRecords) || 100);
56
60
  }
57
61
 
58
62
  _assertQueue(queue) {
@@ -64,6 +68,10 @@ export class EventRuntimeStore {
64
68
  return path.join(this.dir, `${queue}.json`);
65
69
  }
66
70
 
71
+ _hostsFile() {
72
+ return path.join(this.dir, "hosts.json");
73
+ }
74
+
67
75
  _ensureDir() {
68
76
  fs.mkdirSync(this.dir, { recursive: true, mode: 0o700 });
69
77
  }
@@ -86,6 +94,23 @@ export class EventRuntimeStore {
86
94
  fs.renameSync(tmp, file);
87
95
  }
88
96
 
97
+ _readHosts() {
98
+ try {
99
+ const value = JSON.parse(fs.readFileSync(this._hostsFile(), "utf8"));
100
+ return Array.isArray(value) ? value : [];
101
+ } catch {
102
+ return [];
103
+ }
104
+ }
105
+
106
+ _writeHosts(records) {
107
+ this._ensureDir();
108
+ const file = this._hostsFile();
109
+ const tmp = `${file}.${process.pid}.${randomUUID()}.tmp`;
110
+ fs.writeFileSync(tmp, JSON.stringify(records, null, 2), "utf8");
111
+ fs.renameSync(tmp, file);
112
+ }
113
+
89
114
  _mutate(queue, fn) {
90
115
  this._ensureDir();
91
116
  return withFileLock(
@@ -100,6 +125,121 @@ export class EventRuntimeStore {
100
125
  );
101
126
  }
102
127
 
128
+ _mutateHosts(fn) {
129
+ this._ensureDir();
130
+ return withFileLock(
131
+ path.join(this.dir, ".event-runtime"),
132
+ () => {
133
+ const records = this._readHosts();
134
+ const result = fn(records);
135
+ this._writeHosts(records);
136
+ return result;
137
+ },
138
+ { timeoutMs: this.lockTimeoutMs, failIfUnavailable: true },
139
+ );
140
+ }
141
+
142
+ /**
143
+ * Publish a process-host heartbeat in the same lock domain as the queues.
144
+ * The registry is bounded and additive: it is safe for an older observer to
145
+ * ignore hosts.json while newer `cc status --json` consumers can distinguish
146
+ * a live worker from queued work with no owner.
147
+ */
148
+ reportHost(
149
+ {
150
+ id = this.owner,
151
+ pid = process.pid,
152
+ role = "cli",
153
+ state = "running",
154
+ startedAt = null,
155
+ heartbeatAt = this._now(),
156
+ staleAfterMs = this.hostStaleMs,
157
+ lastStats = null,
158
+ lastError = null,
159
+ } = {},
160
+ ) {
161
+ const hostId = String(id || "").trim();
162
+ if (!hostId) throw new Error("Event Runtime host id is required");
163
+ const observedAt = Number(heartbeatAt);
164
+ const timestamp = Number.isFinite(observedAt) ? observedAt : this._now();
165
+ return this._mutateHosts((records) => {
166
+ const existing = records.find((record) => record.id === hostId);
167
+ const record =
168
+ existing ||
169
+ (() => {
170
+ const created = { id: hostId };
171
+ records.push(created);
172
+ return created;
173
+ })();
174
+ record.pid =
175
+ pid != null && Number.isFinite(Number(pid)) ? Number(pid) : null;
176
+ record.role = String(role || "cli");
177
+ record.state = state === "stopped" ? "stopped" : "running";
178
+ record.startedAt =
179
+ existing?.startedAt != null &&
180
+ Number.isFinite(Number(existing.startedAt))
181
+ ? Number(existing.startedAt)
182
+ : startedAt != null && Number.isFinite(Number(startedAt))
183
+ ? Number(startedAt)
184
+ : timestamp;
185
+ record.heartbeatAt = timestamp;
186
+ record.staleAfterMs = Math.max(
187
+ 1,
188
+ Number(staleAfterMs) || this.hostStaleMs,
189
+ );
190
+ record.lastStats = clone(lastStats);
191
+ record.lastError =
192
+ lastError == null ? null : String(lastError).slice(0, 1000);
193
+ if (record.state === "stopped") record.stoppedAt = timestamp;
194
+ else delete record.stoppedAt;
195
+
196
+ if (records.length > this.maxHostRecords) {
197
+ records.sort((a, b) => {
198
+ const aRunning = a.state === "running" ? 1 : 0;
199
+ const bRunning = b.state === "running" ? 1 : 0;
200
+ return (
201
+ bRunning - aRunning ||
202
+ Number(b.heartbeatAt || 0) - Number(a.heartbeatAt || 0)
203
+ );
204
+ });
205
+ records.splice(this.maxHostRecords);
206
+ }
207
+ return clone(record);
208
+ });
209
+ }
210
+
211
+ stopHost(id = this.owner, options = {}) {
212
+ return this.reportHost({
213
+ ...options,
214
+ id,
215
+ state: "stopped",
216
+ heartbeatAt: options.heartbeatAt ?? this._now(),
217
+ });
218
+ }
219
+
220
+ listHosts({ now = this._now(), includeStopped = true } = {}) {
221
+ const t = Number(now);
222
+ this._ensureDir();
223
+ return withFileLock(
224
+ path.join(this.dir, ".event-runtime"),
225
+ () =>
226
+ this._readHosts()
227
+ .filter((record) => includeStopped || record.state !== "stopped")
228
+ .map((record) => ({
229
+ ...clone(record),
230
+ stale:
231
+ record.state === "running" &&
232
+ Number(record.heartbeatAt || 0) +
233
+ Math.max(
234
+ 1,
235
+ Number(record.staleAfterMs) || this.hostStaleMs,
236
+ ) <=
237
+ t,
238
+ })),
239
+ { timeoutMs: this.lockTimeoutMs, failIfUnavailable: true },
240
+ );
241
+ }
242
+
103
243
  enqueue(
104
244
  queue,
105
245
  event,
@@ -418,6 +558,28 @@ export class EventRuntimeStore {
418
558
  : Object.values(queues).some((queue) => queue.pressure === "high")
419
559
  ? "high"
420
560
  : "normal";
561
+ const hostEntries = this._readHosts().map((record) => ({
562
+ ...clone(record),
563
+ stale:
564
+ record.state === "running" &&
565
+ Number(record.heartbeatAt || 0) +
566
+ Math.max(
567
+ 1,
568
+ Number(record.staleAfterMs) || this.hostStaleMs,
569
+ ) <=
570
+ t,
571
+ }));
572
+ const hosts = {
573
+ total: hostEntries.length,
574
+ running: hostEntries.filter(
575
+ (record) => record.state === "running" && !record.stale,
576
+ ).length,
577
+ stale: hostEntries.filter((record) => record.stale).length,
578
+ stopped: hostEntries.filter(
579
+ (record) => record.state === "stopped",
580
+ ).length,
581
+ entries: hostEntries,
582
+ };
421
583
  return {
422
584
  schema: "chainlesschain.event-runtime-health.v1",
423
585
  observedAt: new Date(t).toISOString(),
@@ -425,6 +587,7 @@ export class EventRuntimeStore {
425
587
  highWatermarkRatio: highWatermark,
426
588
  pressure,
427
589
  queues,
590
+ hosts,
428
591
  };
429
592
  },
430
593
  { timeoutMs: this.lockTimeoutMs, failIfUnavailable: true },
@@ -146,8 +146,18 @@ export function createGoal(input = {}, opts = {}) {
146
146
  updatedAt: nowIso(),
147
147
  };
148
148
  ensureDir(root);
149
- atomicWriteFileSync(goalFile(root, id), JSON.stringify(goal, null, 2));
150
- return goal;
149
+ const file = goalFile(root, id);
150
+ return withFileLock(
151
+ file,
152
+ () => {
153
+ if (fs.existsSync(file)) {
154
+ throw new Error(`goal already exists: ${id}`);
155
+ }
156
+ atomicWriteFileSync(file, JSON.stringify(goal, null, 2));
157
+ return goal;
158
+ },
159
+ { failIfUnavailable: true },
160
+ );
151
161
  }
152
162
 
153
163
  function normalizeKr(kr) {
@@ -212,19 +222,22 @@ export function listGoals(opts = {}) {
212
222
  function mutate(id, fn, opts = {}) {
213
223
  // Serialize the read-modify-write across processes (cc+cc, or cc+desktop
214
224
  // sharing the goals dir) so concurrent goal edits don't lose an update.
215
- // Best-effort lock (with-file-lock): on contention timeout it proceeds anyway,
216
- // so the CLI never hangs.
225
+ // Durable goal state never proceeds without exclusion after bounded retry.
217
226
  // Guard before deriving the lock path: an unsafe id would otherwise place the
218
227
  // lock file at a traversal path (`<root>/../../x.json.lock`) before getGoal
219
228
  // even runs. getGoal/saveGoal also guard, but keep the lock inside the dir.
220
229
  if (isUnsafeGoalId(id)) throw new Error(`no such goal: ${id}`);
221
230
  const root = opts.root || defaultRoot();
222
- return withFileLock(goalFile(root, id), () => {
223
- const goal = getGoal(id, opts);
224
- if (!goal) throw new Error(`no such goal: ${id}`);
225
- fn(goal);
226
- return saveGoal(goal, opts);
227
- });
231
+ return withFileLock(
232
+ goalFile(root, id),
233
+ () => {
234
+ const goal = getGoal(id, opts);
235
+ if (!goal) throw new Error(`no such goal: ${id}`);
236
+ fn(goal);
237
+ return saveGoal(goal, opts);
238
+ },
239
+ { failIfUnavailable: true },
240
+ );
228
241
  }
229
242
 
230
243
  /** Add a key result to a goal. */
@@ -368,8 +381,15 @@ export function deleteGoal(id, opts = {}) {
368
381
  const root = opts.root || defaultRoot();
369
382
  const file = goalFile(root, id);
370
383
  if (!fs.existsSync(file)) return false;
371
- fs.rmSync(file);
372
- return true;
384
+ return withFileLock(
385
+ file,
386
+ () => {
387
+ if (!fs.existsSync(file)) return false;
388
+ fs.rmSync(file);
389
+ return true;
390
+ },
391
+ { failIfUnavailable: true },
392
+ );
373
393
  }
374
394
 
375
395
  /**
@@ -150,7 +150,8 @@ export function buildAgentCapabilities() {
150
150
  // P1-9: permission modes, formats and the feature flags all project from the
151
151
  // single canonical [[capability-manifest.js]] so `cc agent --capabilities`,
152
152
  // the negotiator and the docs can no longer drift. Additive protocol-v1
153
- // stream fields (event_seq / tool_use_id / trace_id) come in via the wire
153
+ // stream fields (event_seq / tool_use_id / permission_decision / trace_id)
154
+ // come in via the wire
154
155
  // features; the mcp object and sandbox_engines come from the manifest too.
155
156
  permission_modes: [...CAPABILITY_MANIFEST.permissionModes],
156
157
  output_formats: [...CAPABILITY_MANIFEST.outputFormats],
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Environment boundary shared by every CLI hook surface.
5
+ *
6
+ * Hooks receive only the small set needed to locate executables and temporary
7
+ * files. Extra inherited variables require both an administrator-managed
8
+ * allowlist and an explicit request from the hook definition.
9
+ */
10
+ const SAFE_HOOK_ENV_KEYS = Object.freeze([
11
+ "PATH",
12
+ "PATHEXT",
13
+ "SystemRoot",
14
+ "WINDIR",
15
+ "COMSPEC",
16
+ "TMP",
17
+ "TEMP",
18
+ "TMPDIR",
19
+ "LANG",
20
+ "LC_ALL",
21
+ "HOME",
22
+ "USERPROFILE",
23
+ ]);
24
+
25
+ function normalizeNames(values) {
26
+ return new Set(
27
+ (Array.isArray(values) ? values : [])
28
+ .map((value) => String(value || "").trim())
29
+ .filter(Boolean),
30
+ );
31
+ }
32
+
33
+ function buildManagedHookEnvironment({
34
+ source = process.env,
35
+ managedAllowlist = [],
36
+ requestedAllowlist = [],
37
+ values = {},
38
+ } = {}) {
39
+ const env = {};
40
+ for (const key of SAFE_HOOK_ENV_KEYS) {
41
+ if (source[key] != null) env[key] = source[key];
42
+ }
43
+
44
+ const managed = normalizeNames(managedAllowlist);
45
+ for (const key of normalizeNames(requestedAllowlist)) {
46
+ if (managed.has(key) && source[key] != null) env[key] = source[key];
47
+ }
48
+
49
+ for (const [key, value] of Object.entries(values || {})) {
50
+ if (value != null) env[key] = String(value);
51
+ }
52
+ return env;
53
+ }
54
+
55
+ module.exports = {
56
+ SAFE_HOOK_ENV_KEYS,
57
+ buildManagedHookEnvironment,
58
+ };
@@ -5,6 +5,9 @@
5
5
 
6
6
  import crypto from "crypto";
7
7
  import broker from "./process-execution-broker/index.js";
8
+ import hookEnvironment from "./hook-environment.cjs";
9
+
10
+ const { buildManagedHookEnvironment } = hookEnvironment;
8
11
 
9
12
  /**
10
13
  * Hook priority levels — lower values run first.
@@ -285,11 +288,15 @@ export async function executeHook(hook, context = {}) {
285
288
  executionTime: 0,
286
289
  };
287
290
  }
288
- const env = {
289
- ...process.env,
290
- HOOK_EVENT: hook.event,
291
- HOOK_CONTEXT: JSON.stringify(context),
292
- };
291
+ const env = buildManagedHookEnvironment({
292
+ managedAllowlist: hook.managedEnvironmentAllowlist,
293
+ requestedAllowlist:
294
+ hook.environmentAllowlist || hook.envAllowlist,
295
+ values: {
296
+ HOOK_EVENT: hook.event,
297
+ HOOK_CONTEXT: JSON.stringify(context),
298
+ },
299
+ });
293
300
  const output = broker.execSync(cmd, {
294
301
  origin: "hook-manager:command",
295
302
  scope: "hook",
@@ -26,6 +26,9 @@ const {
26
26
  loadShellConfig,
27
27
  } = require("./shell-selector.cjs");
28
28
  const { mergeHookDecisions } = require("./hook-event-bus.cjs");
29
+ const {
30
+ buildManagedHookEnvironment,
31
+ } = require("./hook-environment.cjs");
29
32
 
30
33
  const _deps = { runSync: null, run: null };
31
34
  Object.defineProperties(_deps, {
@@ -56,6 +59,86 @@ const HOOK_DECISIONS = Object.freeze({
56
59
  CONTINUE: "continue",
57
60
  });
58
61
 
62
+ /**
63
+ * Resolve the sandbox contract for one legacy command hook. Requirements from
64
+ * the caller (for example managed settings) and from the hook entry are
65
+ * additive; the caller's profile wins so a hook cannot weaken managed policy.
66
+ */
67
+ function resolveHookSandboxPolicy(hook = {}, opts = {}) {
68
+ const hookPolicy = hook.sandboxPolicy;
69
+ const callerPolicy = opts.sandboxPolicy;
70
+ for (const [label, value] of [
71
+ ["hook sandboxPolicy", hookPolicy],
72
+ ["managed hook sandboxPolicy", callerPolicy],
73
+ ]) {
74
+ if (
75
+ value !== undefined &&
76
+ (value === null || typeof value !== "object" || Array.isArray(value))
77
+ ) {
78
+ const error = new Error(`${label} must be an object`);
79
+ error.code = "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED";
80
+ throw error;
81
+ }
82
+ }
83
+
84
+ const requiredBoundaries = [];
85
+ for (const [label, value] of [
86
+ ["managed requiredBoundaries", opts.requiredBoundaries],
87
+ [
88
+ "managed sandboxPolicy.requiredBoundaries",
89
+ callerPolicy?.requiredBoundaries,
90
+ ],
91
+ ["hook requiredBoundaries", hook.requiredBoundaries],
92
+ ["hook sandboxPolicy.requiredBoundaries", hookPolicy?.requiredBoundaries],
93
+ ]) {
94
+ if (value === undefined) continue;
95
+ if (!Array.isArray(value)) {
96
+ const error = new Error(`${label} must be an array`);
97
+ error.code = "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED";
98
+ throw error;
99
+ }
100
+ for (const boundary of value) {
101
+ if (!requiredBoundaries.includes(boundary)) {
102
+ requiredBoundaries.push(boundary);
103
+ }
104
+ }
105
+ }
106
+
107
+ const configured =
108
+ hookPolicy !== undefined ||
109
+ callerPolicy !== undefined ||
110
+ hook.requiredBoundaries !== undefined ||
111
+ opts.requiredBoundaries !== undefined;
112
+ if (!configured) return null;
113
+ const resolved = {
114
+ ...(hookPolicy || {}),
115
+ ...(callerPolicy || {}),
116
+ };
117
+ delete resolved.requiredBoundaries;
118
+ if (requiredBoundaries.length > 0) {
119
+ resolved.requiredBoundaries = requiredBoundaries;
120
+ }
121
+ return resolved;
122
+ }
123
+
124
+ function hookSpawnFailure(error) {
125
+ const sandboxDenied =
126
+ error?.code === "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED";
127
+ return {
128
+ decision: sandboxDenied
129
+ ? HOOK_DECISIONS.BLOCK
130
+ : HOOK_DECISIONS.CONTINUE,
131
+ reason: sandboxDenied
132
+ ? `hook sandbox denied: ${error.message}`
133
+ : `hook spawn failed: ${error.message}`,
134
+ exitCode: null,
135
+ error: error.message,
136
+ ...(sandboxDenied
137
+ ? { sandboxDenied: true }
138
+ : { nonBlockingError: true }),
139
+ };
140
+ }
141
+
59
142
  /** JSON.parse that returns undefined (not throws) on failure. */
60
143
  function tryJson(text) {
61
144
  try {
@@ -237,6 +320,7 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
237
320
  const usePowershell = shellKind === "powershell" || shellKind === "pwsh";
238
321
  let res;
239
322
  try {
323
+ const sandboxPolicy = resolveHookSandboxPolicy({}, opts);
240
324
  const common = {
241
325
  input: JSON.stringify({
242
326
  ...input,
@@ -246,10 +330,13 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
246
330
  encoding: "utf-8",
247
331
  timeout,
248
332
  maxBuffer: 8 * 1024 * 1024,
249
- env: {
250
- ...process.env,
251
- CLAUDE_HOOK_EVENT: event || input.hook_event_name || "",
252
- },
333
+ env: buildManagedHookEnvironment({
334
+ managedAllowlist: opts.managedEnvironmentAllowlist,
335
+ requestedAllowlist: opts.environmentAllowlist,
336
+ values: {
337
+ CLAUDE_HOOK_EVENT: event || input.hook_event_name || "",
338
+ },
339
+ }),
253
340
  };
254
341
  const processOptions = {
255
342
  ...common,
@@ -259,6 +346,7 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
259
346
  pluginId: opts.pluginId || null,
260
347
  pluginVersion: opts.pluginVersion || null,
261
348
  pluginSource: opts.pluginSource || null,
349
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
262
350
  };
263
351
  const brokerRunSync =
264
352
  opts.broker &&
@@ -293,13 +381,7 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
293
381
  res = _deps.runSync(command, { ...processOptions, shell: true });
294
382
  }
295
383
  } catch (err) {
296
- return {
297
- decision: HOOK_DECISIONS.CONTINUE,
298
- reason: `hook spawn failed: ${err.message}`,
299
- exitCode: null,
300
- error: err.message,
301
- nonBlockingError: true,
302
- };
384
+ return hookSpawnFailure(err);
303
385
  }
304
386
  return interpretHookOutcome(res);
305
387
  }
@@ -390,10 +472,19 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
390
472
  ...input,
391
473
  schema_version: input.schema_version ?? HOOK_PAYLOAD_SCHEMA_VERSION,
392
474
  });
393
- const spawnEnv = {
394
- ...process.env,
395
- CLAUDE_HOOK_EVENT: event || input.hook_event_name || "",
396
- };
475
+ const spawnEnv = buildManagedHookEnvironment({
476
+ managedAllowlist: opts.managedEnvironmentAllowlist,
477
+ requestedAllowlist: opts.environmentAllowlist,
478
+ values: {
479
+ CLAUDE_HOOK_EVENT: event || input.hook_event_name || "",
480
+ },
481
+ });
482
+ let sandboxPolicy;
483
+ try {
484
+ sandboxPolicy = resolveHookSandboxPolicy({}, opts);
485
+ } catch (error) {
486
+ return hookSpawnFailure(error);
487
+ }
397
488
  const processOptions = {
398
489
  cwd: wd,
399
490
  env: spawnEnv,
@@ -403,6 +494,7 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
403
494
  pluginId: opts.pluginId || null,
404
495
  pluginVersion: opts.pluginVersion || null,
405
496
  pluginSource: opts.pluginSource || null,
497
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
406
498
  };
407
499
 
408
500
  let child;
@@ -427,13 +519,7 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
427
519
  : run(command, { ...processOptions, shell: true });
428
520
  }
429
521
  } catch (err) {
430
- return {
431
- decision: HOOK_DECISIONS.CONTINUE,
432
- reason: `hook spawn failed: ${err.message}`,
433
- exitCode: null,
434
- error: err.message,
435
- nonBlockingError: true,
436
- };
522
+ return hookSpawnFailure(err);
437
523
  }
438
524
 
439
525
  return await new Promise((resolve) => {
@@ -511,19 +597,23 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
511
597
  function runHooks(commandHooks, input = {}, opts = {}) {
512
598
  const results = [];
513
599
  const hooks = commandHooks || [];
514
- const runOne = (h) =>
515
- runCommandHook(h.command, input, {
600
+ const runOne = (h) => {
601
+ const sandboxPolicy = resolveHookSandboxPolicy(h, opts);
602
+ return runCommandHook(h.command, input, {
516
603
  ...opts,
604
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
517
605
  broker: opts.broker,
518
606
  origin: h.origin,
519
607
  pluginId: h.pluginId,
520
608
  pluginVersion: h.pluginVersion,
521
609
  pluginSource: h.pluginSource,
522
610
  timeout: h.timeout != null ? h.timeout * 1000 : opts.timeout,
611
+ environmentAllowlist: h.environmentAllowlist || h.envAllowlist,
523
612
  // per-hook shell selection (P1 #8): `{ "type":"command", "command":…,
524
613
  // "shell":"powershell" }` in the settings hook entry
525
614
  shell: h.shell != null ? h.shell : opts.shell,
526
615
  });
616
+ };
527
617
 
528
618
  if (opts.mergeStrict) {
529
619
  const decisions = [];
@@ -576,18 +666,21 @@ function runHooks(commandHooks, input = {}, opts = {}) {
576
666
  async function runHooksParallel(commandHooks, input = {}, opts = {}) {
577
667
  const hooks = commandHooks || [];
578
668
  const settled = await Promise.all(
579
- hooks.map((h) =>
580
- runCommandHookAsync(h.command, input, {
669
+ hooks.map((h) => {
670
+ const sandboxPolicy = resolveHookSandboxPolicy(h, opts);
671
+ return runCommandHookAsync(h.command, input, {
581
672
  ...opts,
673
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
582
674
  broker: opts.broker,
583
675
  origin: h.origin,
584
676
  pluginId: h.pluginId,
585
677
  pluginVersion: h.pluginVersion,
586
678
  pluginSource: h.pluginSource,
587
679
  timeout: h.timeout != null ? h.timeout * 1000 : opts.timeout,
680
+ environmentAllowlist: h.environmentAllowlist || h.envAllowlist,
588
681
  shell: h.shell != null ? h.shell : opts.shell,
589
- }).then((r) => ({ h, r })),
590
- ),
682
+ }).then((r) => ({ h, r }));
683
+ }),
591
684
  );
592
685
  const results = settled.map(({ h, r }) => ({ command: h.command, ...r }));
593
686
  const decisions = settled.map(({ h, r }) => ({
@@ -615,6 +708,7 @@ module.exports = {
615
708
  HOOK_DECISIONS,
616
709
  HOOK_PAYLOAD_SCHEMA_VERSION,
617
710
  hookBreakerConfig,
711
+ resolveHookSandboxPolicy,
618
712
  _resetHookBreaker,
619
713
  _deps,
620
714
  };
@@ -48,6 +48,7 @@ export const {
48
48
  HOOK_DECISIONS,
49
49
  HOOK_PAYLOAD_SCHEMA_VERSION,
50
50
  hookBreakerConfig,
51
+ resolveHookSandboxPolicy,
51
52
  _resetHookBreaker,
52
53
  _deps,
53
54
  } = runner;
@@ -38,3 +38,39 @@ export async function executeHooksV2Event(
38
38
  };
39
39
  }
40
40
  }
41
+
42
+ export function resolvePromptExpansion(prompt, outcome = {}) {
43
+ const base = String(prompt ?? "");
44
+ const updates = [];
45
+ const context = [];
46
+ for (const record of Array.isArray(outcome.results) ? outcome.results : []) {
47
+ if (record?.status !== "success" || !record.result) continue;
48
+ if (typeof record.result.updatedPrompt === "string") {
49
+ updates.push(record.result.updatedPrompt);
50
+ }
51
+ if (typeof record.result.additionalContext === "string") {
52
+ const value = record.result.additionalContext.trim();
53
+ if (value) context.push(value);
54
+ }
55
+ }
56
+ const distinctUpdates = [...new Set(updates)];
57
+ if (distinctUpdates.length > 1) {
58
+ return {
59
+ prompt: base,
60
+ blocked: true,
61
+ reason: "conflicting UserPromptExpansion updatedPrompt results",
62
+ };
63
+ }
64
+ let resolved = distinctUpdates[0] ?? base;
65
+ if (context.length > 0) {
66
+ resolved += `\n\n[hook context]\n${context.join("\n")}`;
67
+ }
68
+ return {
69
+ prompt: resolved,
70
+ blocked: outcome.blocked === true || outcome.decision === "block",
71
+ reason:
72
+ outcome.blockingResult?.reason ||
73
+ outcome.blockingResult?.message ||
74
+ null,
75
+ };
76
+ }