chainlesschain 0.162.180 → 0.162.182

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 (263) hide show
  1. package/package.json +7 -3
  2. package/scripts/build-windows-sandbox-helper.ps1 +163 -0
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-D_TIL54O.js → AIOps-DzZ5_xoj.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-eQwy9r-f.js → ActionButton-DeLXddIg.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-D2eNuDgT.js → Analytics-rvDCmet1.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CFhwOFsf.js → AppLayout-Yt6QVDnp.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-CJUMw6PB.js → Artifacts-BS112K5L.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-CDB9V58o.js → Audit-0wJuQrNu.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-TBppYFjN.js → BackgroundAgents-B90PMgbn.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-CYEOUpzJ.js → Backup-TkgMglQQ.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-C88fFT2u.js → BaseInput-CQVS9p1l.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-mkr1vC3N.js → Chat-YTjQHoC7.js} +6 -6
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-PR074hUN.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-CVU4xqti.js → Checkbox-5bwaOabq.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-rrNcByz3.js → Codegen-BFmNQ_Xe.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-Cqu-zCvh.js → Col-BT_u4Dzp.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-RGRlSgXV.js → Community-CJP52u5b.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-DBm6epOK.js → Compact-BpzhJE9i.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-BgKw5iaW.js → Compliance-Cg7rfslp.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-BqQcNO3Z.js → Cowork-JisxL6Yy.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-DO5gTFNb.js → Cron-Bu7Ug_MC.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-B8hbJNuN.js → Crosschain-CvuqoLdj.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-B08bFJ2C.js → DID-C2u9G8Pu.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-xNdy_ifo.js → Dashboard-BFt5Yv3d.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-D-KIZgWL.js → Dropdown-DIaT9Wxq.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-DKCvsaGo.js → EmailListRenderer-D_A601Y4.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D_cfqama.js → FamilyGuardDashboard--IyQ8I8w.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-B-UqQD68.js → Federation-BihWHxuY.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-BWWvWuJW.js → FormItemContext-BB8dOXc_.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-CPchx6N7.js → GenericCardRenderer-DQYhj1HH.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-Vx4co9Ff.js → Git-B-2QZxGf.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-D4x0pp_m.js → Governance-CusSb-nG.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-T3KvwPYR.js → Inference-BAjmvPPL.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-Ddv3KUv3.js → KnowledgeGraph-DoNYWKYq.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-DfH-Dkae.js → Logs-B8fKWD-n.js} +2 -2
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-DHW3PbCq.js → MarkdownRenderer-BlJm1J7E.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-tjgb0R3D.js → Marketplace-CyOscbSL.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-DHAk5nYX.js → McpTools-Ex6Om5da.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-A2Yx7thM.js → Memory-CDcD8l8r.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-BEmTVfN5.js → MobileBridge-xfBqoFDg.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-DuFHUgE4.js → MobileProjects-BwObMMA2.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-BDSll0OZ.js → Mtc-CIWd36OV.js} +4 -4
  44. package/src/assets/web-panel/assets/{MtcAudit-CuBvluaS.js → MtcAudit-CPX0ouuA.js} +4 -4
  45. package/src/assets/web-panel/assets/{Multisig-BmXAA-Sb.js → Multisig-BDJsIUuW.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-ci2stZAe.js → NLProgramming-DEkbuD3N.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-D-1RpqnS.js → Notes-DJpHRhH1.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-C0GyitIT.js → NotificationSettings-C1UZ-Qd5.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-U2bN-hj5.js → OrderTableRenderer-GlJtqLjT.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-BNZgpPci.js → Organization-Bbhj0fac.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-CDidKmUN.js → Overflow-1H04x3kx.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-BnreBaXq.js → P2P-CDpPe3et.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-DwJUxO9j.js → PdhVaultBrowser-MUX0Bpvw.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-BQGgJPH0.js → Permissions-A07-0Ci1.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-k_fSkCwL.js → PersonalDataHub-DhhDWxAZ.js} +5 -5
  56. package/src/assets/web-panel/assets/{Pipeline-WGqKMqau.js → Pipeline-CyfbJfe-.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-DtHj9Ymq.js → Privacy-CNFps3ED.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-B3wH2SFC.js → ProjectInit--T-u3Dr5.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-Cd0cgcA1.js → ProjectSettings-DJmrK2fC.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-Qx_MdX9k.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-DTpxEXur.js → Providers-DLzzbOZd.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-B2v6gR-C.js → QrScannerModal-CexOomKS.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-D3Nr9xXR.js → QuickAsk-QklGi19_.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-C9inoMky.js → Recommend-Bb9kwSw8.js} +1 -1
  65. package/src/assets/web-panel/assets/RemoteSession-DZuVNNN5.js +5 -0
  66. package/src/assets/web-panel/assets/{Reputation-41KFG646.js → Reputation-Dr5cLP0N.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-DqAaqqCv.js → Row-oCQHYv5t.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-CMDpCDKc.js → RssFeed-IFDEykIR.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-B6mtegNL.js → Search-CspVIxDc.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-BaePFVZ8.js → Security-Bm66o_DH.js} +3 -3
  71. package/src/assets/web-panel/assets/{Services-DeG3Qscl.js → Services-DvanNqXl.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-D_QP7F90.js → Skeleton-CHjEe9_O.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-DLGyUgIA.js → Skills-Cb8jWjMX.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-BN2pxW1Q.js → Sla-DxZm1491.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-BWsZPd8B.js → SpeechSettings-DWxEEclC.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-VOZRPODW.js → SyncSettings-FIvOFw5p.js} +2 -2
  77. package/src/assets/web-panel/assets/Tasks-CyuLnDFR.js +1 -0
  78. package/src/assets/web-panel/assets/{Templates-eJSob-q0.js → Templates-CaNjzV_t.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-Bdwk8Rwy.js → Tenant-DjlP6TmH.js} +1 -1
  80. package/src/assets/web-panel/assets/Terminal-DwzGMniO.js +3 -0
  81. package/src/assets/web-panel/assets/{TimelineRenderer-D5qkbPKT.js → TimelineRenderer-DoZ8y7y8.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-DKgwn8hI.js → Tokens-390cg2TC.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-HlEHniGM.js → Trigger-CLrtCj5_.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-CdPcsBpN.js → Trust-DpESfSPn.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-vAdXd-vG.js → UkeySign-CN6279O0.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-9hHBYFnH.js → VideoEditing-DPtAUlSe.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-Dxh5d1yR.js → Wallet-nKOMV_1n.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-BsFmLHkp.js → WebAuthn-HuXlSOc4.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-Cn0OIwLn.js → WorkflowEditor-CUfFjARi.js} +1 -1
  90. package/src/assets/web-panel/assets/chat-6Eq0PcZJ.js +1 -0
  91. package/src/assets/web-panel/assets/{colors-Du9jzLN0.js → colors-BP_Sf6Iy.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-DhEscfzw.js → compact-item-BufMKIh5.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-CwzEfF3i.js → createContext-BBMXxWSW.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-DgVKduH2.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DT_GSE6J.js → hasIn-fUK_tRO2.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-tND8-TDU.js → index-2A5ph8b2.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-B8s5FxJY.js → index-3FuwBewR.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-53z1YUxX.js → index-6GmBa4Kg.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BFD0cEna.js → index-B2vS66eI.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CQWxDOvM.js → index-B7Etk0Te.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-VeHs6qH0.js → index-B9WYSn4m.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CGvgnfl9.js → index-BHy6yNiF.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-D3llLkYM.js → index-BMLHv389.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dny_ZnAO.js → index-BWKoBU8R.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DzjzrOli.js → index-Bkg4ZePE.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DN60VBn5.js → index-C44atv4k.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-v5HLYCVa.js → index-CE2DdTP2.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BF-ZCVGE.js → index-CGjmILDm.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-gFjYdstW.js → index-CW_G-MXw.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-NA2oBLSB.js → index-CZ6Yt17L.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Mobw3PHs.js → index-CeASi4fr.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DWL3SYN3.js → index-CgiECHu2.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-1LoutPJd.js → index-Cp8vhyZy.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BnHhFnwt.js → index-CwSf5pnf.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B6QDWRLk.js → index-D4_MuGkd.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-D-QkW1fp.js → index-D9vU25ij.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C4SN3by5.js → index-DASluSqY.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CyEhUJcu.js → index-DLpIs_P_.js} +1 -1
  119. package/src/assets/web-panel/assets/index-DOZTSoS7.js +1 -0
  120. package/src/assets/web-panel/assets/{index-BrkJ7JPm.js → index-DPdUNk1i.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Cnok8mhZ.js → index-DVsqvI4M.js} +1 -1
  122. package/src/assets/web-panel/assets/index-DhpvjpqI.js +1 -0
  123. package/src/assets/web-panel/assets/{index-CAVW0cWM.js → index-DmkMXwnR.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Cc9aPkv-.js → index-DocdRcbd.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DnVVYKCE.js → index-Dyngbd29.js} +20 -20
  126. package/src/assets/web-panel/assets/{index-D8gt0j7r.js → index-GwdRUZ-x.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BIYTjbv_.js → index-LvCBfTLo.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DhXay17G.js → index-cpEli44X.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BQWENaZf.js → index-fzaMtSJ8.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DfRDyY1q.js → index-pum7HYBA.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BgSR2WMG.js → index-t8N1tSeX.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D5IytTND.js → index-uCbEN7cY.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DtRwLhee.js → index-wKTLbKyc.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-D4kZoXNr.js → index-wV6iGdpY.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-Cvt59c10.js → initDefaultProps-V5Fo95FM.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-D15pPEzl.js → motion-BYWU0hUT.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-N55KeXJ7.js → move-Cr1M-J2h.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-CyFO1bYP.js → mtc-parser-BysQKi2N.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-B5IXfxU2.js → omit-D1zHU8GC.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-CpVqsulV.js → pickAttrs-CUArxtfi.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-ME36sMF3.js → placementArrow-ChoYEys0.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-BXAIR1r-.js → responsiveObserve-BQRs3Mo7.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-BkOX3-Dp.js → slide-B8zbsCkg.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-Cy4Aklpt.js → statusUtils-C2U8Gs6h.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-gUd2T206.js → styleChecker-Dd8MgL_V.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-CaVD4o99.js → useFlexGapSupport-hknqY5NH.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs--pFGl0KD.js → useFs-CLTAmcfw.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-D45iP86i.js → usePersonalDataHub-ByegHbiB.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-BayoChH_.js → vnode-DqlAy5DP.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-CpP38poi.js → zoom-DiXiztQn.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/commands/agenda.js +115 -3
  153. package/src/commands/agent.js +10 -1
  154. package/src/commands/background-session.js +2 -1
  155. package/src/commands/context.js +69 -2
  156. package/src/data/changelog.json +26 -0
  157. package/src/gateways/terminal/PtyManager.js +45 -4
  158. package/src/gateways/ws/background-agent-protocol.js +38 -0
  159. package/src/gateways/ws/message-dispatcher.js +1 -0
  160. package/src/gateways/ws/remote-session-protocol.js +65 -6
  161. package/src/gateways/ws/ws-server.js +18 -2
  162. package/src/harness/background-task-manager.js +70 -2
  163. package/src/harness/feature-flags.js +15 -8
  164. package/src/harness/mcp-client.js +618 -43
  165. package/src/harness/worktree-isolator.js +14 -3
  166. package/src/lazy-dispatch.js +30 -1
  167. package/src/lib/agent-authority.js +22 -0
  168. package/src/lib/agent-core.js +3 -0
  169. package/src/lib/agent-sandbox.js +161 -0
  170. package/src/lib/agent-schedule-store.js +45 -22
  171. package/src/lib/agent-team/team-runner.js +22 -0
  172. package/src/lib/artifact-store.js +35 -0
  173. package/src/lib/async-hook-supervisor.cjs +13 -2
  174. package/src/lib/audit-mtc.js +3 -2
  175. package/src/lib/background-agent-supervisor.js +104 -3
  176. package/src/lib/background-interaction-journal.js +407 -0
  177. package/src/lib/background-interaction-resolver.js +23 -34
  178. package/src/lib/background-session-transport.js +12 -2
  179. package/src/lib/capability-manifest.js +41 -3
  180. package/src/lib/capability-negotiation.js +1 -1
  181. package/src/lib/channels/channel-manager.js +33 -0
  182. package/src/lib/channels/telegram-channel.js +23 -5
  183. package/src/lib/channels/webhook-channel.js +38 -15
  184. package/src/lib/chrome-connector.js +169 -23
  185. package/src/lib/config-manager.js +23 -9
  186. package/src/lib/context-breakdown.js +31 -0
  187. package/src/lib/cowork-cron.js +274 -32
  188. package/src/lib/cross-chain-mtc.js +3 -2
  189. package/src/lib/durable-security-store.js +124 -0
  190. package/src/lib/event-runtime-host.js +67 -6
  191. package/src/lib/event-runtime-store.js +163 -0
  192. package/src/lib/goal-store.js +32 -12
  193. package/src/lib/headless-manifest.js +2 -1
  194. package/src/lib/hook-environment.cjs +58 -0
  195. package/src/lib/hook-manager.js +12 -5
  196. package/src/lib/hook-runner.cjs +122 -28
  197. package/src/lib/hook-runner.js +7 -2
  198. package/src/lib/hooks-v2-producers.js +36 -0
  199. package/src/lib/hooks-v2-runtime.js +329 -52
  200. package/src/lib/interaction-adapter.js +60 -19
  201. package/src/lib/interaction-binding.js +64 -0
  202. package/src/lib/json-schema-output.js +439 -14
  203. package/src/lib/json-schema-validate.js +514 -883
  204. package/src/lib/lan-pairing-tokens.js +115 -57
  205. package/src/lib/lsp/__tests__/lsp-client.test.js +32 -0
  206. package/src/lib/lsp/__tests__/lsp-manager.test.js +25 -0
  207. package/src/lib/lsp/lsp-client.js +4 -0
  208. package/src/lib/lsp/lsp-manager.js +3 -0
  209. package/src/lib/lsp/lsp-server-registry.js +18 -0
  210. package/src/lib/mcp-client.js +1 -0
  211. package/src/lib/mcp-oauth.js +35 -21
  212. package/src/lib/packer/pkg-config-generator.js +3 -0
  213. package/src/lib/permission-decision.js +99 -0
  214. package/src/lib/plugin-monitor-supervisor.js +3 -0
  215. package/src/lib/plugin-runtime/bin.js +1148 -70
  216. package/src/lib/plugin-runtime/capabilities.js +50 -0
  217. package/src/lib/plugin-runtime/capability-consent.js +42 -35
  218. package/src/lib/plugin-runtime/hooks.js +36 -9
  219. package/src/lib/plugin-runtime/install.js +23 -7
  220. package/src/lib/plugin-runtime/lsp.js +4 -0
  221. package/src/lib/plugin-runtime/manifest.js +112 -8
  222. package/src/lib/plugin-runtime/mcp.js +30 -5
  223. package/src/lib/plugin-runtime/monitors.js +11 -0
  224. package/src/lib/plugin-runtime/plugin-options.js +94 -38
  225. package/src/lib/plugin-runtime/policy.js +29 -2
  226. package/src/lib/plugin-runtime/sandbox-policy.js +84 -0
  227. package/src/lib/plugin-runtime/scopes.js +27 -13
  228. package/src/lib/plugin-runtime/trust.js +36 -31
  229. package/src/lib/process-execution-broker/credential-agent.js +114 -16
  230. package/src/lib/process-execution-broker/credential-transport-worker.js +312 -0
  231. package/src/lib/process-execution-broker/credential-transport.js +378 -0
  232. package/src/lib/process-execution-broker/index.js +765 -49
  233. package/src/lib/process-execution-broker/platform-sandbox.js +4233 -138
  234. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  235. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  236. package/src/lib/process-execution-broker/windows-sandbox.cs +3856 -0
  237. package/src/lib/project-mcp-trust.js +21 -26
  238. package/src/lib/protocol-replay.js +19 -12
  239. package/src/lib/repl-bang-memorize.js +67 -10
  240. package/src/lib/settings-hooks.cjs +13 -0
  241. package/src/lib/skill-loader.js +83 -10
  242. package/src/lib/slash-commands.js +27 -4
  243. package/src/lib/sync-credentials.js +5 -4
  244. package/src/lib/turn-binding.js +8 -6
  245. package/src/repl/agent-repl.js +117 -31
  246. package/src/repl/repl-turn-binding.js +5 -2
  247. package/src/runtime/agent-core.js +699 -70
  248. package/src/runtime/agent-runtime.js +13 -1
  249. package/src/runtime/coding-agent-contract-shared.cjs +3 -3
  250. package/src/runtime/headless-runner.js +105 -7
  251. package/src/runtime/headless-stream.js +209 -23
  252. package/src/runtime/mcp-config.js +29 -17
  253. package/src/runtime/policies/agent-policy.js +2 -0
  254. package/src/workers/background-agent-worker.js +128 -13
  255. package/src/assets/web-panel/assets/ChatBubbleRenderer-C7bofJuA.js +0 -1
  256. package/src/assets/web-panel/assets/Projects-BbgmyQnS.js +0 -1
  257. package/src/assets/web-panel/assets/RemoteSession-Bqk3EYSu.js +0 -4
  258. package/src/assets/web-panel/assets/Tasks-3bDAUz8K.js +0 -1
  259. package/src/assets/web-panel/assets/Terminal-CsMLKIoV.js +0 -3
  260. package/src/assets/web-panel/assets/chat-CJ19O6Dh.js +0 -1
  261. package/src/assets/web-panel/assets/devWarning-BBMFnsja.js +0 -1
  262. package/src/assets/web-panel/assets/index-Do8JMV4N.js +0 -1
  263. package/src/assets/web-panel/assets/index-UYAJ6iNM.js +0 -1
@@ -33,6 +33,7 @@ import path from "node:path";
33
33
  import os from "node:os";
34
34
  import crypto from "node:crypto";
35
35
  import { getHomeDir } from "./paths.js";
36
+ import { withFileLock } from "./with-file-lock.js";
36
37
 
37
38
  /** Token lifetime — mirrors device-pairing-handler.js pairingTimeout (5 min). */
38
39
  const DEFAULT_TTL_MS = 5 * 60 * 1000;
@@ -108,7 +109,10 @@ export function buildToken({ did, deviceInfo } = {}) {
108
109
  * @param {string} [filePath]
109
110
  * @returns {{ schema: string, tokens: Array }}
110
111
  */
111
- export function readTokens(filePath = defaultTokenStorePath()) {
112
+ export function readTokens(
113
+ filePath = defaultTokenStorePath(),
114
+ { failIfUnavailable = false } = {},
115
+ ) {
112
116
  if (!fs.existsSync(filePath)) {
113
117
  return { schema: "cc-pairing-tokens/v1", tokens: [] };
114
118
  }
@@ -121,9 +125,28 @@ export function readTokens(filePath = defaultTokenStorePath()) {
121
125
  tokens: parsed.tokens,
122
126
  };
123
127
  }
128
+ if (failIfUnavailable) {
129
+ const error = new Error(
130
+ `Pairing token store must contain a tokens array: ${filePath}`,
131
+ );
132
+ error.code = "PAIRING_TOKEN_STORE_CORRUPT";
133
+ throw error;
134
+ }
124
135
  return { schema: "cc-pairing-tokens/v1", tokens: [] };
125
- } catch (_err) {
126
- // Malformed → return empty (don't crash CLI; user can `revoke --all`).
136
+ } catch (cause) {
137
+ if (
138
+ failIfUnavailable ||
139
+ cause?.code === "PAIRING_TOKEN_STORE_CORRUPT"
140
+ ) {
141
+ if (cause?.code === "PAIRING_TOKEN_STORE_CORRUPT") throw cause;
142
+ const error = new Error(
143
+ `Could not read pairing token store: ${filePath}`,
144
+ { cause },
145
+ );
146
+ error.code = "PAIRING_TOKEN_STORE_UNAVAILABLE";
147
+ throw error;
148
+ }
149
+ // Advisory listing keeps its legacy empty fallback.
127
150
  return { schema: "cc-pairing-tokens/v1", tokens: [] };
128
151
  }
129
152
  }
@@ -134,19 +157,46 @@ export function readTokens(filePath = defaultTokenStorePath()) {
134
157
  * @param {{ schema?: string, tokens: Array }} state
135
158
  * @param {string} [filePath]
136
159
  */
137
- export function writeTokens(state, filePath = defaultTokenStorePath()) {
160
+ function _prepareTokenStore(filePath) {
138
161
  const dir = path.dirname(filePath);
139
162
  if (!fs.existsSync(dir)) {
140
- fs.mkdirSync(dir, { recursive: true });
163
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
141
164
  }
165
+ }
166
+
167
+ function _writeTokensUnlocked(state, filePath) {
168
+ _prepareTokenStore(filePath);
142
169
  const toWrite = {
143
170
  schema: state.schema || "cc-pairing-tokens/v1",
144
171
  tokens: state.tokens || [],
145
172
  };
146
173
  // Write to temp + rename for atomicity (avoid partial reads).
147
- const tmp = filePath + ".tmp";
148
- fs.writeFileSync(tmp, JSON.stringify(toWrite, null, 2), "utf-8");
149
- fs.renameSync(tmp, filePath);
174
+ const tmp = `${filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
175
+ try {
176
+ fs.writeFileSync(tmp, JSON.stringify(toWrite, null, 2), {
177
+ encoding: "utf-8",
178
+ mode: 0o600,
179
+ });
180
+ fs.renameSync(tmp, filePath);
181
+ } catch (error) {
182
+ try {
183
+ fs.rmSync(tmp, { force: true });
184
+ } catch {
185
+ // Preserve the original persistence error.
186
+ }
187
+ throw error;
188
+ }
189
+ }
190
+
191
+ function _withTokenStoreLock(filePath, body) {
192
+ _prepareTokenStore(filePath);
193
+ return withFileLock(filePath, body, { failIfUnavailable: true });
194
+ }
195
+
196
+ export function writeTokens(state, filePath = defaultTokenStorePath()) {
197
+ return _withTokenStoreLock(filePath, () =>
198
+ _writeTokensUnlocked(state, filePath),
199
+ );
150
200
  }
151
201
 
152
202
  /**
@@ -160,17 +210,19 @@ export function writeTokens(state, filePath = defaultTokenStorePath()) {
160
210
  */
161
211
  export function addToken(input, filePath = defaultTokenStorePath()) {
162
212
  const token = buildToken(input);
163
- const state = readTokens(filePath);
164
- // Invalidate prior pending tokens for the same DID.
165
- state.tokens = state.tokens.map((t) => {
166
- if (t.qrData?.did === input.did && t.status === STATUS.PENDING) {
167
- return { ...t, status: STATUS.REVOKED, revokedAtMs: Date.now() };
168
- }
169
- return t;
213
+ return _withTokenStoreLock(filePath, () => {
214
+ const state = readTokens(filePath, { failIfUnavailable: true });
215
+ // Invalidate prior pending tokens for the same DID.
216
+ state.tokens = state.tokens.map((t) => {
217
+ if (t.qrData?.did === input.did && t.status === STATUS.PENDING) {
218
+ return { ...t, status: STATUS.REVOKED, revokedAtMs: Date.now() };
219
+ }
220
+ return t;
221
+ });
222
+ state.tokens.push(token);
223
+ _writeTokensUnlocked(state, filePath);
224
+ return token;
170
225
  });
171
- state.tokens.push(token);
172
- writeTokens(state, filePath);
173
- return token;
174
226
  }
175
227
 
176
228
  /**
@@ -178,18 +230,20 @@ export function addToken(input, filePath = defaultTokenStorePath()) {
178
230
  * before `list` to keep status accurate.
179
231
  */
180
232
  export function sweepExpired(filePath = defaultTokenStorePath()) {
181
- const state = readTokens(filePath);
182
- const now = Date.now();
183
- let touched = 0;
184
- state.tokens = state.tokens.map((t) => {
185
- if (t.status === STATUS.PENDING && t.expiresAtMs <= now) {
186
- touched += 1;
187
- return { ...t, status: STATUS.EXPIRED, expiredAtMs: now };
188
- }
189
- return t;
233
+ return _withTokenStoreLock(filePath, () => {
234
+ const state = readTokens(filePath, { failIfUnavailable: true });
235
+ const now = Date.now();
236
+ let touched = 0;
237
+ state.tokens = state.tokens.map((t) => {
238
+ if (t.status === STATUS.PENDING && t.expiresAtMs <= now) {
239
+ touched += 1;
240
+ return { ...t, status: STATUS.EXPIRED, expiredAtMs: now };
241
+ }
242
+ return t;
243
+ });
244
+ if (touched > 0) _writeTokensUnlocked(state, filePath);
245
+ return touched;
190
246
  });
191
- if (touched > 0) writeTokens(state, filePath);
192
- return touched;
193
247
  }
194
248
 
195
249
  /**
@@ -224,20 +278,22 @@ export function findToken(code, filePath = defaultTokenStorePath()) {
224
278
  * not found or already non-pending.
225
279
  */
226
280
  export function revokeToken(code, filePath = defaultTokenStorePath()) {
227
- const state = readTokens(filePath);
228
- const i = state.tokens.findIndex((t) => t.code === code);
229
- if (i < 0) return { revoked: false, reason: "not_found" };
230
- const t = state.tokens[i];
231
- if (t.status !== STATUS.PENDING) {
232
- return { revoked: false, reason: `not_pending(${t.status})` };
233
- }
234
- state.tokens[i] = {
235
- ...t,
236
- status: STATUS.REVOKED,
237
- revokedAtMs: Date.now(),
238
- };
239
- writeTokens(state, filePath);
240
- return { revoked: true, token: state.tokens[i] };
281
+ return _withTokenStoreLock(filePath, () => {
282
+ const state = readTokens(filePath, { failIfUnavailable: true });
283
+ const i = state.tokens.findIndex((t) => t.code === code);
284
+ if (i < 0) return { revoked: false, reason: "not_found" };
285
+ const t = state.tokens[i];
286
+ if (t.status !== STATUS.PENDING) {
287
+ return { revoked: false, reason: `not_pending(${t.status})` };
288
+ }
289
+ state.tokens[i] = {
290
+ ...t,
291
+ status: STATUS.REVOKED,
292
+ revokedAtMs: Date.now(),
293
+ };
294
+ _writeTokensUnlocked(state, filePath);
295
+ return { revoked: true, token: state.tokens[i] };
296
+ });
241
297
  }
242
298
 
243
299
  /**
@@ -245,20 +301,22 @@ export function revokeToken(code, filePath = defaultTokenStorePath()) {
245
301
  * this after the mobile successfully connects.
246
302
  */
247
303
  export function markConsumed(code, filePath = defaultTokenStorePath()) {
248
- const state = readTokens(filePath);
249
- const i = state.tokens.findIndex((t) => t.code === code);
250
- if (i < 0) return { consumed: false, reason: "not_found" };
251
- const t = state.tokens[i];
252
- if (t.status !== STATUS.PENDING) {
253
- return { consumed: false, reason: `not_pending(${t.status})` };
254
- }
255
- state.tokens[i] = {
256
- ...t,
257
- status: STATUS.CONSUMED,
258
- consumedAtMs: Date.now(),
259
- };
260
- writeTokens(state, filePath);
261
- return { consumed: true, token: state.tokens[i] };
304
+ return _withTokenStoreLock(filePath, () => {
305
+ const state = readTokens(filePath, { failIfUnavailable: true });
306
+ const i = state.tokens.findIndex((t) => t.code === code);
307
+ if (i < 0) return { consumed: false, reason: "not_found" };
308
+ const t = state.tokens[i];
309
+ if (t.status !== STATUS.PENDING) {
310
+ return { consumed: false, reason: `not_pending(${t.status})` };
311
+ }
312
+ state.tokens[i] = {
313
+ ...t,
314
+ status: STATUS.CONSUMED,
315
+ consumedAtMs: Date.now(),
316
+ };
317
+ _writeTokensUnlocked(state, filePath);
318
+ return { consumed: true, token: state.tokens[i] };
319
+ });
262
320
  }
263
321
 
264
322
  export { STATUS, QR_TYPE, DEFAULT_TTL_MS };
@@ -15,6 +15,7 @@ import {
15
15
  buildSpawnCommand,
16
16
  } from "../lsp-client.js";
17
17
  import { encodeMessage, MessageBuffer } from "../jsonrpc-stream.js";
18
+ import { executionBroker } from "../../process-execution-broker/index.js";
18
19
 
19
20
  /** A fake language server process. Auto-replies to `initialize`; test controls the rest. */
20
21
  class FakeChild extends EventEmitter {
@@ -94,6 +95,37 @@ describe("LSPClient handshake", () => {
94
95
  shell: false,
95
96
  }),
96
97
  );
98
+ expect(_deps.spawn.mock.calls[0][2]).not.toHaveProperty("sandboxPolicy");
99
+ });
100
+
101
+ it("passes plugin sandbox requirements to ProcessExecutionBroker", async () => {
102
+ const child = new FakeChild();
103
+ const brokerSpawn = vi
104
+ .spyOn(executionBroker, "spawn")
105
+ .mockReturnValue(child);
106
+ const client = makeClient(child, {
107
+ origin: "plugin:lsp",
108
+ pluginId: "strict-lsp",
109
+ pluginVersion: "1.0.0",
110
+ pluginSource: "/plugins/strict-lsp/.lsp.json",
111
+ sandboxPolicy: {
112
+ requiredBoundaries: ["filesystem", "network"],
113
+ },
114
+ });
115
+
116
+ await client.start();
117
+
118
+ expect(brokerSpawn).toHaveBeenCalledWith(
119
+ "fake-lsp",
120
+ ["--stdio"],
121
+ expect.objectContaining({
122
+ origin: "plugin:lsp",
123
+ sandboxPolicy: {
124
+ requiredBoundaries: ["filesystem", "network"],
125
+ },
126
+ }),
127
+ );
128
+ expect(_deps.spawn).not.toHaveBeenCalled();
97
129
  });
98
130
 
99
131
  it("kills the spawned server when the initialize handshake times out (no orphan)", async () => {
@@ -19,6 +19,7 @@ import { _deps as regDeps } from "../lsp-server-registry.js";
19
19
  class FakeClient extends EventEmitter {
20
20
  constructor(opts) {
21
21
  super();
22
+ this.opts = opts;
22
23
  this.rootPath = opts.rootPath;
23
24
  this.running = true;
24
25
  this.notifications = [];
@@ -102,6 +103,30 @@ describe("position helpers", () => {
102
103
  });
103
104
 
104
105
  describe("ensureFor", () => {
106
+ it("passes resolved plugin sandbox requirements into LSPClient", async () => {
107
+ const mgr = new LSPManager();
108
+
109
+ await mgr._startServer(
110
+ "/proj::strict-lsp",
111
+ {
112
+ id: "strict-lsp",
113
+ command: "strict-lsp",
114
+ args: ["--stdio"],
115
+ origin: "plugin:lsp",
116
+ pluginId: "strict-plugin",
117
+ sandboxPolicy: {
118
+ requiredBoundaries: ["filesystem", "network"],
119
+ },
120
+ },
121
+ "/proj",
122
+ );
123
+
124
+ expect(lastClient.opts.sandboxPolicy).toEqual({
125
+ requiredBoundaries: ["filesystem", "network"],
126
+ });
127
+ await mgr.disposeAll();
128
+ });
129
+
105
130
  it("starts a server and sends didOpen the first time", async () => {
106
131
  const mgr = new LSPManager();
107
132
  const ready = await mgr.ensureFor("/proj/src/a.ts");
@@ -47,6 +47,7 @@ export class LSPClient extends EventEmitter {
47
47
  this.pluginId = opts.pluginId || null;
48
48
  this.pluginVersion = opts.pluginVersion || null;
49
49
  this.pluginSource = opts.pluginSource || null;
50
+ this.sandboxPolicy = opts.sandboxPolicy || null;
50
51
 
51
52
  this._child = null;
52
53
  this._buffer = new MessageBuffer();
@@ -97,6 +98,9 @@ export class LSPClient extends EventEmitter {
97
98
  pluginId: this.pluginId,
98
99
  pluginVersion: this.pluginVersion,
99
100
  pluginSource: this.pluginSource,
101
+ ...(this.sandboxPolicy
102
+ ? { sandboxPolicy: this.sandboxPolicy }
103
+ : {}),
100
104
  }
101
105
  : {}),
102
106
  });
@@ -243,6 +243,9 @@ export class LSPManager {
243
243
  pluginId: resolved.pluginId,
244
244
  pluginVersion: resolved.pluginVersion,
245
245
  pluginSource: resolved.pluginSource,
246
+ ...(resolved.sandboxPolicy
247
+ ? { sandboxPolicy: resolved.sandboxPolicy }
248
+ : {}),
246
249
  });
247
250
  // Collect pushed diagnostics keyed by document, tracking publish count/time
248
251
  // so callers can wait for the project to load (first publish) and settle
@@ -96,6 +96,15 @@ export function registerLanguageServer(def) {
96
96
  pluginId: def.pluginId || null,
97
97
  pluginVersion: def.pluginVersion || null,
98
98
  pluginSource: def.pluginSource || null,
99
+ ...(def.sandboxPolicy
100
+ ? {
101
+ sandboxPolicy: {
102
+ requiredBoundaries: [
103
+ ...(def.sandboxPolicy.requiredBoundaries || []),
104
+ ],
105
+ },
106
+ }
107
+ : {}),
99
108
  };
100
109
  const existing = pluginServers.get(def.languageId) || [];
101
110
  // De-dupe by server id so re-registering the same server (e.g. a reload)
@@ -154,6 +163,15 @@ export function resolveServer(languageId, projectRoot) {
154
163
  pluginId: def.pluginId,
155
164
  pluginVersion: def.pluginVersion,
156
165
  pluginSource: def.pluginSource,
166
+ ...(def.sandboxPolicy
167
+ ? {
168
+ sandboxPolicy: {
169
+ requiredBoundaries: [
170
+ ...(def.sandboxPolicy.requiredBoundaries || []),
171
+ ],
172
+ },
173
+ }
174
+ : {}),
157
175
  }
158
176
  : {}),
159
177
  };
@@ -13,6 +13,7 @@ export {
13
13
  MCPClient,
14
14
  MCPServerConfig,
15
15
  inferTransport,
16
+ normalizeMcpElicitationRequest,
16
17
  isHttpTransport,
17
18
  isLikelyConnectionError,
18
19
  isTransientMcpError,
@@ -49,6 +49,7 @@ export const _deps = {
49
49
  warn: (msg) => {
50
50
  if (!process.env.VITEST) process.stderr.write(msg);
51
51
  },
52
+ withStoreLock,
52
53
  };
53
54
 
54
55
  /** Discovery/token requests retry ONCE after a transient error (CC 2.1.191). */
@@ -500,29 +501,36 @@ function _sleepSyncMs(ms) {
500
501
  * lost update. An O_EXCL lockfile makes the whole RMW mutually exclusive across
501
502
  * processes, so each writer re-reads the latest store inside the lock.
502
503
  *
503
- * Robustness: a stale lock (holder crashed) older than STALE_MS is stolen; if
504
- * the lock can't be acquired within MAX_WAIT_MS we proceed anyway (degrading to
505
- * the pre-lock last-writer-wins rather than failing the login). When the
506
- * injected fs lacks the lock primitives (test fs / exotic fs) we run `fn`
507
- * directly — single-process correctness is unchanged, only cross-process mutual
508
- * exclusion is lost. Lock timing uses the wall clock (not the `now` test seam).
504
+ * Robustness: a stale lock (holder crashed) older than STALE_MS is stolen.
505
+ * Credential metadata is critical, so missing lock primitives, unexpected lock
506
+ * errors, and bounded contention all fail closed instead of running `fn`
507
+ * without exclusion. Lock timing uses the wall clock (not the `now` test seam).
509
508
  */
510
- function withStoreLock(file, fn) {
509
+ export function withStoreLock(file, fn) {
511
510
  const fs = _deps.fs;
512
511
  if (
513
512
  typeof fs.openSync !== "function" ||
514
513
  typeof fs.closeSync !== "function" ||
515
514
  typeof fs.unlinkSync !== "function"
516
515
  ) {
517
- return fn(); // no lock primitives → run unlocked (back-compat)
516
+ const error = new Error(
517
+ `MCP OAuth token store lock primitives unavailable: ${file}`,
518
+ );
519
+ error.code = "MCP_TOKEN_STORE_LOCK_UNAVAILABLE";
520
+ throw error;
518
521
  }
519
522
  const STALE_MS = 10_000;
520
523
  const MAX_WAIT_MS = 5_000;
521
524
  const lockPath = `${file}.lock`;
522
525
  try {
523
526
  fs.mkdirSync(pathDefault.dirname(file), { recursive: true, mode: 0o700 });
524
- } catch {
525
- /* dir may already exist — openSync below reports any real problem */
527
+ } catch (cause) {
528
+ const error = new Error(
529
+ `Could not prepare MCP OAuth token store lock: ${file}`,
530
+ { cause },
531
+ );
532
+ error.code = "MCP_TOKEN_STORE_LOCK_UNAVAILABLE";
533
+ throw error;
526
534
  }
527
535
  const start = Date.now();
528
536
  let fd = null;
@@ -532,9 +540,12 @@ function withStoreLock(file, fn) {
532
540
  break;
533
541
  } catch (err) {
534
542
  if (!err || err.code !== "EEXIST") {
535
- // Unexpected (e.g. ENOENT/EACCES): don't fail the login over the lock —
536
- // proceed unlocked, same as a missing-primitives fs.
537
- return fn();
543
+ const error = new Error(
544
+ `Could not acquire MCP OAuth token store lock: ${file}`,
545
+ { cause: err },
546
+ );
547
+ error.code = "MCP_TOKEN_STORE_LOCK_UNAVAILABLE";
548
+ throw error;
538
549
  }
539
550
  let stale = false;
540
551
  try {
@@ -552,7 +563,14 @@ function withStoreLock(file, fn) {
552
563
  }
553
564
  continue;
554
565
  }
555
- if (Date.now() - start > MAX_WAIT_MS) break; // give up waiting; proceed
566
+ if (Date.now() - start > MAX_WAIT_MS) {
567
+ const error = new Error(
568
+ `Timed out acquiring MCP OAuth token store lock: ${file}`,
569
+ { cause: err },
570
+ );
571
+ error.code = "MCP_TOKEN_STORE_LOCK_UNAVAILABLE";
572
+ throw error;
573
+ }
556
574
  _sleepSyncMs(50);
557
575
  }
558
576
  }
@@ -576,7 +594,7 @@ function withStoreLock(file, fn) {
576
594
 
577
595
  export function saveStoredToken(serverUrl, record) {
578
596
  const file = tokenStorePath();
579
- return withStoreLock(file, () => {
597
+ return _deps.withStoreLock(file, () => {
580
598
  const r = _readStore(file); // re-read inside the lock (latest state)
581
599
  if (!r.ok) {
582
600
  // The existing store is unreadable. Writing a fresh single-entry store
@@ -594,16 +612,12 @@ export function saveStoredToken(serverUrl, record) {
594
612
 
595
613
  export function deleteStoredToken(serverUrl) {
596
614
  const file = tokenStorePath();
597
- return withStoreLock(file, () => {
615
+ return _deps.withStoreLock(file, () => {
598
616
  const store = loadTokenStore(); // re-read inside the lock (latest state)
599
617
  const key = serverKey(serverUrl);
600
618
  if (!(key in store)) return false;
601
619
  delete store[key];
602
- try {
603
- _writeStoreSecure(file, store);
604
- } catch {
605
- /* best-effort */
606
- }
620
+ _writeStoreSecure(file, store);
607
621
  return true;
608
622
  });
609
623
  }
@@ -385,6 +385,9 @@ export function generatePkgConfig(ctx) {
385
385
  const assets = [
386
386
  `${posixify(distDir)}/**/*`,
387
387
  `${posixify(templatesDir)}/**/*`,
388
+ `${posixify(cliRoot)}/src/lib/process-execution-broker/windows-sandbox-helper.dll`,
389
+ `${posixify(cliRoot)}/src/lib/process-execution-broker/windows-sandbox-helper.exe`,
390
+ `${posixify(cliRoot)}/src/lib/process-execution-broker/windows-sandbox.cs`,
388
391
  ];
389
392
  if (prebuildsDir) assets.push(`${posixify(prebuildsDir)}/**/*`);
390
393
  // Project mode: bundle the collected .chainlesschain/ snapshot as an asset.
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Build the additive, protocol-safe PermissionDecision attached to a gated
3
+ * tool result. The runtime remains the authority; IDEs and SDKs only render
4
+ * this explanation and must never re-evaluate it.
5
+ *
6
+ * The source result can contain user-controlled rules/reasons. Keep the wire
7
+ * object bounded and run every text field through the export-grade secret
8
+ * redactor before it reaches transcripts, ledgers, or IDE logs.
9
+ */
10
+
11
+ import { redactSecrets } from "./secret-scan.js";
12
+
13
+ export const PERMISSION_DECISION_VERSION = 1;
14
+
15
+ const MAX_TEXT = 512;
16
+ const MAX_CHAIN = 8;
17
+
18
+ function boundedText(value, max = MAX_TEXT) {
19
+ if (value == null) return null;
20
+ let text;
21
+ if (typeof value === "string") {
22
+ text = value;
23
+ } else {
24
+ try {
25
+ text = JSON.stringify(value);
26
+ } catch {
27
+ text = String(value);
28
+ }
29
+ }
30
+ if (typeof text !== "string") text = String(value);
31
+ text = redactSecrets(text);
32
+ return text.length > max ? `${text.slice(0, max)}…` : text;
33
+ }
34
+
35
+ function normalizeChain(value) {
36
+ if (!Array.isArray(value)) return [];
37
+ return value.slice(0, MAX_CHAIN).map((entry) => {
38
+ const row = entry && typeof entry === "object" ? entry : {};
39
+ return {
40
+ layer: boundedText(row.layer, 80),
41
+ outcome: boundedText(row.outcome, 80),
42
+ via: boundedText(row.via || row.source, 120),
43
+ rule: boundedText(row.rule, 256),
44
+ reason: boundedText(row.reason),
45
+ };
46
+ });
47
+ }
48
+
49
+ /**
50
+ * @param {{
51
+ * toolUseId?: string,
52
+ * tool?: string,
53
+ * result?: object,
54
+ * }} input
55
+ * @returns {object|null}
56
+ */
57
+ export function buildPermissionDecision(input = {}) {
58
+ const result =
59
+ input.result && typeof input.result === "object" ? input.result : {};
60
+ const policy =
61
+ result.policy && typeof result.policy === "object" ? result.policy : {};
62
+ const approval =
63
+ result.approval && typeof result.approval === "object"
64
+ ? result.approval
65
+ : {};
66
+ const chain = normalizeChain(result.permissionChain);
67
+ const terminal = chain.length > 0 ? chain[chain.length - 1] : {};
68
+
69
+ const decision = boundedText(
70
+ policy.decision || approval.decision || terminal.outcome,
71
+ 32,
72
+ );
73
+ if (!decision && chain.length === 0) return null;
74
+
75
+ const via = boundedText(
76
+ policy.via || approval.via || terminal.via || terminal.layer,
77
+ 120,
78
+ );
79
+ const rule = boundedText(policy.rule || approval.rule || terminal.rule, 256);
80
+ const reason = boundedText(
81
+ policy.reason ||
82
+ approval.reason ||
83
+ terminal.reason ||
84
+ result.shellCommandPolicy?.reason,
85
+ );
86
+ const toolUseId = boundedText(input.toolUseId, 160);
87
+ const gate = via || decision || "policy";
88
+
89
+ return {
90
+ version: PERMISSION_DECISION_VERSION,
91
+ id: toolUseId ? `${toolUseId}:perm:${gate}` : null,
92
+ tool: boundedText(input.tool, 120),
93
+ decision,
94
+ via,
95
+ rule,
96
+ reason,
97
+ chain,
98
+ };
99
+ }
@@ -224,6 +224,9 @@ export class PluginMonitorSupervisor {
224
224
  pluginId: desc.pluginId,
225
225
  pluginVersion: desc.pluginVersion,
226
226
  pluginSource: desc.pluginSource,
227
+ ...(desc.sandboxPolicy
228
+ ? { sandboxPolicy: desc.sandboxPolicy }
229
+ : {}),
227
230
  }
228
231
  : {}),
229
232
  },