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
@@ -330,14 +330,81 @@ export function registerContextCommand(program) {
330
330
  );
331
331
  }
332
332
  if (sourceReport.skillCache.descriptors.resident > 0) {
333
+ const skillDescriptors = sourceReport.skillCache.descriptors;
334
+ const skillBodies = sourceReport.skillCache.bodies;
333
335
  logger.log("");
334
336
  logger.log(
335
337
  chalk.gray(
336
- ` Skill cache: ${sourceReport.skillCache.descriptors.resident} descriptors, ` +
337
- `${sourceReport.skillCache.bodies.resident} bodies resident, ` +
338
+ ` Skill cache: ${skillDescriptors.resident} descriptors ` +
339
+ `(${skillDescriptors.estimatedTokens} estimated descriptor-token equivalents), ` +
340
+ `${skillBodies.resident} bodies resident ` +
341
+ `(${skillBodies.estimatedTokensResident} estimated body-token equivalents), ` +
338
342
  `${sourceReport.skillCache.savings.estimatedTokensAvoided} estimated tokens kept lazy`,
339
343
  ),
340
344
  );
345
+ logger.log(
346
+ chalk.gray(
347
+ ` Descriptor prompt returns: ${skillDescriptors.contextLoads} / ` +
348
+ `${skillDescriptors.contextTokens} estimated context tokens`,
349
+ ),
350
+ );
351
+ logger.log(
352
+ chalk.gray(
353
+ ` On-demand reads: ${skillBodies.cacheMisses} disk / ` +
354
+ `${skillBodies.cacheHits} cache; cache reuse avoided ` +
355
+ `${skillBodies.estimatedTokensAvoidedByCache} body-token equivalents of disk reads`,
356
+ ),
357
+ );
358
+ logger.log(
359
+ chalk.gray(
360
+ ` Prompt injections: ${skillBodies.contextLoads} loads / ` +
361
+ `${skillBodies.contextTokens} estimated context tokens`,
362
+ ),
363
+ );
364
+ const activeBodies = [...skillBodies.entries]
365
+ .filter((entry) => entry.loads > 0 || entry.contextLoads > 0)
366
+ .sort(
367
+ (a, b) =>
368
+ b.contextTokens - a.contextTokens ||
369
+ b.loads - a.loads ||
370
+ a.id.localeCompare(b.id),
371
+ );
372
+ for (const entry of activeBodies.slice(0, 8)) {
373
+ logger.log(
374
+ chalk.gray(
375
+ ` ${entry.id} [${entry.source}]: ${entry.estimatedTokens} token body; ` +
376
+ `${entry.cacheMisses} disk / ${entry.cacheHits} cache reads; ` +
377
+ `${entry.contextLoads} prompt loads / ${entry.contextTokens} tokens`,
378
+ ),
379
+ );
380
+ }
381
+ if (activeBodies.length > 8) {
382
+ logger.log(
383
+ chalk.gray(` ... ${activeBodies.length - 8} more active bodies`),
384
+ );
385
+ }
386
+ const usedDescriptors = [...skillDescriptors.entries]
387
+ .filter((entry) => entry.contextLoads > 0)
388
+ .sort(
389
+ (a, b) =>
390
+ b.contextTokens - a.contextTokens ||
391
+ a.id.localeCompare(b.id),
392
+ );
393
+ for (const entry of usedDescriptors.slice(0, 8)) {
394
+ logger.log(
395
+ chalk.gray(
396
+ ` descriptor ${entry.id} [${entry.source}]: ` +
397
+ `${entry.contextLoads} prompt returns / ${entry.contextTokens} tokens`,
398
+ ),
399
+ );
400
+ }
401
+ if (usedDescriptors.length > 8) {
402
+ logger.log(
403
+ chalk.gray(
404
+ ` ... ${usedDescriptors.length - 8} more used descriptors`,
405
+ ),
406
+ );
407
+ }
341
408
  }
342
409
  if (sourceReport.optimizations.length > 0) {
343
410
  logger.log("");
@@ -1,6 +1,32 @@
1
1
  {
2
2
  "source": "CHANGELOG.md",
3
3
  "releases": [
4
+ {
5
+ "productVersion": null,
6
+ "cliVersion": "0.162.182",
7
+ "cliVersions": ["0.162.181", "0.162.182"],
8
+ "date": "2026-07-27",
9
+ "title": "",
10
+ "sections": [
11
+ {
12
+ "heading": "Fixed — cc CLI 0.162.182: attested native plugin sandbox closure",
13
+ "body": "> `chainlesschain` **0.162.181 → 0.162.182** (2026-07-27).\n> CLI-only release; `@chainlesschain/personal-data-hub` remains **0.4.57**\n> and `@chainlesschain/agent-sdk` remains **0.1.7**.\n\n- **One-shot native plugin execution contracts**: policy-bearing native plugin\n bins must preserve the resolver-issued target, literal argv, plugin root, and\n executable identity through the process broker. Replayed, copied, stale, or\n asynchronous contracts fail closed before a target process is started.\n- **Linux strong boundary completion**: strict native launches accept only\n attested static ELF executables for the current architecture, reject dynamic\n loaders and malformed program headers, pin the plugin tree through file\n descriptors, and enter the fixed bubblewrap filesystem/network policy\n without exposing a mutable host path.\n- **Cross-platform identity hardening**: Windows AppContainer launches retain\n attested target handles, while platform adapters revalidate executable and\n plugin identities across probe, spawn, IPC, and detached launch boundaries.\n- **Release validation gate**: the exact release commit must pass `CLI CI` and\n `CLI Strict Sandbox` on their configured Ubuntu, Windows, and macOS runners;\n local tests are supplementary and cannot authorize publication."
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "productVersion": null,
19
+ "cliVersion": "0.162.181",
20
+ "cliVersions": ["0.162.181"],
21
+ "date": "2026-07-26",
22
+ "title": "",
23
+ "sections": [
24
+ {
25
+ "heading": "Added — cc CLI 0.162.181 + PDH 0.4.57 + Agent SDK 0.1.7: sandbox contracts and complete collection",
26
+ "body": "> `chainlesschain` **0.162.181** bundles\n> `@chainlesschain/personal-data-hub` **0.4.57** and co-releases\n> `@chainlesschain/agent-sdk` **0.1.7** (2026-07-26).\n\n- **Enforced process boundaries**: the CLI process broker preserves native\n sandbox identity across foreground, detached, IPC, plugin hook, MCP, monitor,\n and LSP launches. Plugin manifests can require explicit sandbox boundaries,\n and Linux launches attest the primitives actually applied.\n- **Runtime and protocol convergence**: restore compatibility-shim exports,\n keep permission-decision fields stable in golden transcripts, and carry the\n current background-session and event-recovery contract through the Agent SDK.\n- **Complete resumable collection**: PDH adapters use explicit cursors and\n bounded page budgets across local databases, desktop clients, mobile APIs,\n maps, social, messaging, education, music, health, shopping, and travel\n sources. Partial or unreadable SQLite results now fail closed instead of\n silently advancing checkpoints.\n- **Release validation**: PDH 282 files / 4,528 tests, Agent SDK build plus\n 50 tests, CLI sandbox/plugin focused regression, Web Panel build, and\n isolated Git-worktree/WebSocket regressions passed before publication."
27
+ }
28
+ ]
29
+ },
4
30
  {
5
31
  "productVersion": null,
6
32
  "cliVersion": "0.162.180",
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * PtyManager — ESM mirror of `desktop-app-vue/src/main/terminal/PtyManager.js`.
3
3
  *
4
- * Keep both copies in sync. The desktop copy targets Electron's CJS main
5
- * process; this one targets `cc ui` (Node ESM) so `cc ui` users also get
6
- * remote-terminal support via the same WS protocol.
4
+ * Keep their protocol and session behavior in sync. The desktop copy targets
5
+ * Electron's CJS main process; this one targets `cc ui` (Node ESM), whose host
6
+ * injects its own trusted workspace-policy resolver.
7
7
  *
8
8
  * Design notes — see docs/design/Android_Remote_Terminal_Plan_A.md §4:
9
9
  * - node-pty loaded lazily so the module loads without the native
@@ -44,6 +44,24 @@ function resolveShellCmd(shell) {
44
44
  return shell;
45
45
  }
46
46
 
47
+ function validatePinnedSandboxPolicy(policy) {
48
+ if (policy == null) return null;
49
+ if (
50
+ typeof policy !== "object" ||
51
+ Array.isArray(policy) ||
52
+ typeof policy.then === "function" ||
53
+ !Array.isArray(policy.requiredBoundaries) ||
54
+ policy.requiredBoundaries.length === 0 ||
55
+ !Object.isFrozen(policy) ||
56
+ !Object.isFrozen(policy.requiredBoundaries)
57
+ ) {
58
+ const error = new TypeError("invalid_pty_sandbox_policy");
59
+ error.code = "ERR_PTY_SANDBOX_POLICY_INVALID";
60
+ throw error;
61
+ }
62
+ return policy;
63
+ }
64
+
47
65
  class PtySession {
48
66
  constructor({ id, shell, cwd, cols, rows, proc, createdAt, ringBuffer }) {
49
67
  this.id = id;
@@ -65,6 +83,16 @@ export class PtyManager extends EventEmitter {
65
83
  constructor(opts = {}) {
66
84
  super();
67
85
  this.config = { ...DEFAULT_CONFIG, ...(opts.config || {}) };
86
+ const resolveSandboxPolicy =
87
+ opts.resolveSandboxPolicy ?? opts._deps?.resolveSandboxPolicy ?? null;
88
+ if (
89
+ resolveSandboxPolicy !== null &&
90
+ typeof resolveSandboxPolicy !== "function"
91
+ ) {
92
+ throw new TypeError("resolveSandboxPolicy must be a function or null");
93
+ }
94
+ this._policyCwd = opts.policyCwd || process.cwd();
95
+ this._resolveSandboxPolicy = resolveSandboxPolicy;
68
96
  this._deps = {
69
97
  loadNodePty: opts._deps?.loadNodePty || (() => require("node-pty")),
70
98
  spawnPty: opts._deps?.spawnPty || null,
@@ -92,6 +120,19 @@ export class PtyManager extends EventEmitter {
92
120
  throw new Error("shell_not_allowed");
93
121
  }
94
122
 
123
+ const cwd = req.cwd || this.config.defaultCwd || process.cwd();
124
+ // A remote cwd may discover a stricter policy, but it must never replace
125
+ // the fixed workspace root supplied by the host. Resolve and validate the
126
+ // pinned policy before loading node-pty or allocating a native terminal.
127
+ const sandboxPolicy = this._resolveSandboxPolicy
128
+ ? validatePinnedSandboxPolicy(
129
+ this._resolveSandboxPolicy({
130
+ workspaceCwd: this._policyCwd,
131
+ executionCwd: cwd,
132
+ }),
133
+ )
134
+ : null;
135
+
95
136
  let pty;
96
137
  try {
97
138
  pty = this._deps.loadNodePty();
@@ -101,7 +142,6 @@ export class PtyManager extends EventEmitter {
101
142
  throw err;
102
143
  }
103
144
 
104
- const cwd = req.cwd || this.config.defaultCwd || process.cwd();
105
145
  const cols = Number.isFinite(req.cols) ? req.cols : 80;
106
146
  const rows = Number.isFinite(req.rows) ? req.rows : 24;
107
147
  const cmd = resolveShellCmd(shell);
@@ -123,6 +163,7 @@ export class PtyManager extends EventEmitter {
123
163
  origin: "terminal:pty",
124
164
  policy: "allow",
125
165
  scope: "terminal",
166
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
126
167
  });
127
168
 
128
169
  const sessionId = randomUUID();
@@ -12,6 +12,8 @@
12
12
  * log, latestSeq, [reattached, replayed,
13
13
  * replayTruncated]}
14
14
  * bg-prompt {bgId, text} → {type:"bg-prompt", bgId, sent}
15
+ * bg-answer {bgId, requestId, binding, answer}
16
+ * → {type:"bg-answer", bgId, sent}
15
17
  * bg-stop-turn {bgId} → {type:"bg-stop-turn", bgId, sent}
16
18
  * bg-detach {bgId} → {type:"bg-detach", bgId}
17
19
  * bg-stop {bgId} → {type:"bg-stop", session} (kill whole session)
@@ -340,6 +342,42 @@ export async function handleBgPrompt(server, clientId, id, ws, message) {
340
342
  server._send(ws, { id, type: "bg-prompt", bgId: message.bgId, sent });
341
343
  }
342
344
 
345
+ export async function handleBgAnswer(server, clientId, id, ws, message) {
346
+ const entry = requireAttachment(server, clientId, id, ws, message);
347
+ if (!entry) return;
348
+ const requestId = String(message.requestId || "");
349
+ if (!requestId) {
350
+ return sendError(server, id, ws, "NO_REQUEST_ID", "requestId required");
351
+ }
352
+ if (
353
+ !message.binding ||
354
+ typeof message.binding !== "object" ||
355
+ Array.isArray(message.binding)
356
+ ) {
357
+ return sendError(
358
+ server,
359
+ id,
360
+ ws,
361
+ "NO_INTERACTION_BINDING",
362
+ "interaction binding required",
363
+ );
364
+ }
365
+ const sent = entry.conn.send({
366
+ type: "interaction_response",
367
+ requestId,
368
+ binding: message.binding,
369
+ answer: message.answer,
370
+ ...(message.error ? { error: message.error } : {}),
371
+ });
372
+ server._send(ws, {
373
+ id,
374
+ type: "bg-answer",
375
+ bgId: message.bgId,
376
+ requestId,
377
+ sent,
378
+ });
379
+ }
380
+
343
381
  export async function handleBgStopTurn(server, clientId, id, ws, message) {
344
382
  const entry = requireAttachment(server, clientId, id, ws, message);
345
383
  if (!entry) return;
@@ -182,6 +182,7 @@ export function createWsMessageDispatcher(server) {
182
182
  "bg-view": () => server._handleBgView(id, ws, message),
183
183
  "bg-attach": () => server._handleBgAttach(clientId, id, ws, message),
184
184
  "bg-prompt": () => server._handleBgPrompt(clientId, id, ws, message),
185
+ "bg-answer": () => server._handleBgAnswer(clientId, id, ws, message),
185
186
  "bg-stop-turn": () =>
186
187
  server._handleBgStopTurn(clientId, id, ws, message),
187
188
  "bg-detach": () => server._handleBgDetach(clientId, id, ws, message),
@@ -12,12 +12,15 @@ import { RemoteCommandLedger } from "../../harness/remote-command-ledger.js";
12
12
  import {
13
13
  ORIGIN,
14
14
  assertCanApprove,
15
+ assertCanAnswerInteraction,
15
16
  describeAuthorityChain,
16
17
  } from "../../lib/agent-authority.js";
18
+ import { hasCompleteInteractionBinding } from "../../lib/interaction-binding.js";
17
19
 
18
20
  const CLIENT_EVENT_SCOPES = Object.freeze({
19
21
  prompt: "prompt",
20
22
  "approval.resolve": "approve",
23
+ "question.answer": "prompt",
21
24
  interrupt: "interrupt",
22
25
  });
23
26
 
@@ -30,7 +33,8 @@ function audit(server, entry) {
30
33
  }
31
34
 
32
35
  /**
33
- * Apply a remote-client control event (prompt / approval.resolve / interrupt)
36
+ * Apply a remote-client control event (prompt / approval.resolve /
37
+ * question.answer / interrupt)
34
38
  * idempotently when it carries a stable `commandId`. This is the ACTUAL takeover
35
39
  * path — a paired mobile/web device driving the host agent — and it does NOT
36
40
  * flow through message-dispatcher's execute/stream ledger, so without this a
@@ -448,7 +452,7 @@ export async function handleRemoteSessionPublish(
448
452
  requiredScope || "observe",
449
453
  );
450
454
  // Runtime/output events are host-only. Remote clients may publish only the
451
- // three explicitly scoped control event types above.
455
+ // explicitly scoped control event types above.
452
456
  if (!requiredScope && session.hostClientId !== clientId) {
453
457
  throw new Error("Only the host can publish runtime events");
454
458
  }
@@ -468,6 +472,24 @@ export async function handleRemoteSessionPublish(
468
472
  ) {
469
473
  throw new Error("prompt content is required");
470
474
  }
475
+ if (message.event.type === "question.answer") {
476
+ if (
477
+ typeof message.event.requestId !== "string" ||
478
+ !message.event.requestId
479
+ ) {
480
+ throw new Error("question requestId is required");
481
+ }
482
+ if (!hasCompleteInteractionBinding(message.event.binding)) {
483
+ throw new Error("complete question binding is required");
484
+ }
485
+ assertCanAnswerInteraction({
486
+ origin: ORIGIN.REMOTE,
487
+ authenticated: true,
488
+ scopes: ["prompt"],
489
+ principalId: clientId,
490
+ sessionId: message.remoteSessionId,
491
+ });
492
+ }
471
493
  // Authority (§"权限来源与跨 Agent 授权边界"): a remote *approve* is honored
472
494
  // only from an authenticated, approve-scoped device — the SINGLE
473
495
  // authoritative rule, not just the per-device scope check above, so the
@@ -531,7 +553,9 @@ export async function handleRemoteSessionPublish(
531
553
  ? "control.prompt"
532
554
  : message.event.type === "approval.resolve"
533
555
  ? "control.approval"
534
- : "control.interrupt";
556
+ : message.event.type === "question.answer"
557
+ ? "control.question"
558
+ : "control.interrupt";
535
559
  audit(server, {
536
560
  sessionId: message.remoteSessionId,
537
561
  actor: clientId,
@@ -548,11 +572,23 @@ export async function handleRemoteSessionPublish(
548
572
  authority: describeAuthorityChain(approvalEnvelope || {}),
549
573
  forwarded: true,
550
574
  }
551
- : { forwarded: true },
575
+ : message.event.type === "question.answer"
576
+ ? {
577
+ requestId: message.event.requestId,
578
+ authority: describeAuthorityChain({
579
+ origin: ORIGIN.REMOTE,
580
+ authenticated: true,
581
+ scopes: ["prompt"],
582
+ principalId: clientId,
583
+ sessionId: message.remoteSessionId,
584
+ }),
585
+ forwarded: true,
586
+ }
587
+ : { forwarded: true },
552
588
  });
553
589
  // Hand the event to the host process. `message.event` carries any
554
- // echoed approval binding, so the host gate can reject a stale /
555
- // tampered verdict.
590
+ // echoed approval or interaction binding, so the host gate can
591
+ // reject a stale / tampered verdict.
556
592
  server._send(hostTarget.ws, {
557
593
  type: "remote-session-control",
558
594
  remoteSessionId: message.remoteSessionId,
@@ -599,6 +635,29 @@ export async function handleRemoteSessionPublish(
599
635
  // a verdict whose binding doesn't match the pending request.
600
636
  binding: message.event.binding ?? null,
601
637
  });
638
+ } else if (message.event.type === "question.answer") {
639
+ const authority = {
640
+ origin: ORIGIN.REMOTE,
641
+ authenticated: true,
642
+ scopes: ["prompt"],
643
+ principalId: clientId,
644
+ sessionId: message.remoteSessionId,
645
+ };
646
+ audit(server, {
647
+ sessionId: message.remoteSessionId,
648
+ actor: clientId,
649
+ action: "control.question",
650
+ detail: {
651
+ requestId: message.event.requestId,
652
+ authority: describeAuthorityChain(authority),
653
+ },
654
+ });
655
+ await handleSessionAnswer(server, message.id, ws, {
656
+ ...controlMessage,
657
+ requestId: message.event.requestId,
658
+ answer: message.event.answer ?? null,
659
+ binding: message.event.binding,
660
+ });
602
661
  } else if (message.event.type === "interrupt") {
603
662
  audit(server, {
604
663
  sessionId: message.remoteSessionId,
@@ -95,6 +95,7 @@ import {
95
95
  import { handleLlmChat } from "./llm-chat-protocol.js";
96
96
  import {
97
97
  cleanupBgAttachments,
98
+ handleBgAnswer,
98
99
  handleBgAttach,
99
100
  handleBgDetach,
100
101
  handleBgList,
@@ -113,6 +114,7 @@ import {
113
114
  handleArtifactShow,
114
115
  } from "./artifact-protocol.js";
115
116
  import { executionBroker } from "../../lib/process-execution-broker/index.js";
117
+ import { collectWorkspacePluginBinSandboxPolicy } from "../../lib/plugin-runtime/bin.js";
116
118
 
117
119
  export const _deps = {
118
120
  spawn: (...args) => executionBroker.spawn(...args),
@@ -569,7 +571,8 @@ export class ChainlessChainWSServer extends EventEmitter {
569
571
  }
570
572
  const rulesMod = await import("../../lib/permission-rules.cjs");
571
573
  const mod = rulesMod.default || rulesMod;
572
- if (!mod.parseRule(message.rule)) throw new Error("invalid permission rule");
574
+ if (!mod.parseRule(message.rule))
575
+ throw new Error("invalid permission rule");
573
576
  const { addRule } = await import("../../lib/settings-loader.cjs");
574
577
  const scope = ["project", "local", "user"].includes(message.scope)
575
578
  ? message.scope
@@ -767,6 +770,11 @@ export class ChainlessChainWSServer extends EventEmitter {
767
770
  return handleBgPrompt(this, clientId, id, ws, message);
768
771
  }
769
772
 
773
+ /** @private — answer a bound interaction in an attached background session */
774
+ async _handleBgAnswer(clientId, id, ws, message) {
775
+ return handleBgAnswer(this, clientId, id, ws, message);
776
+ }
777
+
770
778
  /** @private — stop the current turn of an attached background session */
771
779
  async _handleBgStopTurn(clientId, id, ws, message) {
772
780
  return handleBgStopTurn(this, clientId, id, ws, message);
@@ -1208,7 +1216,15 @@ export class ChainlessChainWSServer extends EventEmitter {
1208
1216
  if (this._taskManager) return this._taskManager;
1209
1217
  const { BackgroundTaskManager } =
1210
1218
  await import("../../harness/background-task-manager.js");
1211
- this._taskManager = new BackgroundTaskManager({ recoverOnStart: true });
1219
+ this._taskManager = new BackgroundTaskManager({
1220
+ recoverOnStart: true,
1221
+ policyCwd: this.projectRoot,
1222
+ resolveSandboxPolicy: ({ workspaceCwd, executionCwd }) =>
1223
+ collectWorkspacePluginBinSandboxPolicy({
1224
+ workspaceCwd,
1225
+ executionCwd,
1226
+ }),
1227
+ });
1212
1228
  this._subscribeTaskNotifications();
1213
1229
  return this._taskManager;
1214
1230
  }
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import { existsSync, mkdirSync, appendFileSync, readFileSync } from "node:fs";
12
- import { join } from "node:path";
12
+ import { join, resolve } from "node:path";
13
13
  import { createHash } from "node:crypto";
14
14
  import { EventEmitter } from "node:events";
15
15
  import { getHomeDir } from "../lib/paths.js";
@@ -42,9 +42,55 @@ const RECOVERABLE_TASK_STATUSES = new Set([
42
42
  TaskStatus.RUNNING,
43
43
  ]);
44
44
 
45
+ function validatePinnedSandboxPolicy(policy) {
46
+ if (policy == null) return null;
47
+ if (
48
+ typeof policy !== "object" ||
49
+ Array.isArray(policy) ||
50
+ typeof policy.then === "function" ||
51
+ !Array.isArray(policy.requiredBoundaries) ||
52
+ policy.requiredBoundaries.length === 0 ||
53
+ policy.requiredBoundaries.some(
54
+ (boundary) => typeof boundary !== "string" || boundary.length === 0,
55
+ ) ||
56
+ !Object.isFrozen(policy) ||
57
+ !Object.isFrozen(policy.requiredBoundaries)
58
+ ) {
59
+ const error = new TypeError("invalid_background_task_sandbox_policy");
60
+ error.code = "ERR_BACKGROUND_TASK_SANDBOX_POLICY_INVALID";
61
+ error.sandboxReason = "invalid_background_task_sandbox_policy";
62
+ error.sandboxFailClosed = true;
63
+ throw error;
64
+ }
65
+ return policy;
66
+ }
67
+
68
+ function createUnsupportedSandboxError(sandboxPolicy) {
69
+ const requiredBoundaries = [...sandboxPolicy.requiredBoundaries];
70
+ const error = new Error(
71
+ `Background tasks cannot run without their required sandbox boundaries: ${requiredBoundaries.join(", ")}`,
72
+ );
73
+ error.code = "ERR_BACKGROUND_TASK_SANDBOX_UNSUPPORTED";
74
+ error.sandboxReason = "background_execution_unsupported";
75
+ error.sandboxFailClosed = true;
76
+ error.requiredBoundaries = requiredBoundaries;
77
+ error.actualGuarantees = [];
78
+ error.missingBoundaries = [...requiredBoundaries];
79
+ error.sandboxBackend = null;
80
+ error.sandboxCandidateBackend = null;
81
+ return error;
82
+ }
83
+
45
84
  export class BackgroundTaskManager extends EventEmitter {
46
85
  constructor(options = {}) {
47
86
  super();
87
+ const resolveSandboxPolicy = options.resolveSandboxPolicy ?? null;
88
+ if (
89
+ resolveSandboxPolicy !== null &&
90
+ typeof resolveSandboxPolicy !== "function"
91
+ ) {
92
+ throw new TypeError("resolveSandboxPolicy must be a function or null");
93
+ }
48
94
  this.maxConcurrent = options.maxConcurrent || 3;
49
95
  this.heartbeatTimeout = options.heartbeatTimeout || 60000;
50
96
  this.historyLimit = options.historyLimit || 50;
@@ -54,6 +100,10 @@ export class BackgroundTaskManager extends EventEmitter {
54
100
  `${process.pid}@${process.platform}`;
55
101
  this.recoveryPolicy = options.recoveryPolicy || "claim-stale";
56
102
  this.staleNodeTimeout = options.staleNodeTimeout || 5 * 60 * 1000;
103
+ this._policyCwd = resolveSandboxPolicy
104
+ ? resolve(options.policyCwd || process.cwd())
105
+ : null;
106
+ this._resolveSandboxPolicy = resolveSandboxPolicy;
57
107
  this.tasks = new Map();
58
108
  this.processes = new Map();
59
109
  this._checkInterval = null;
@@ -72,12 +122,15 @@ export class BackgroundTaskManager extends EventEmitter {
72
122
  );
73
123
  }
74
124
 
125
+ const cwd = spec.cwd || process.cwd();
126
+ this._assertSandboxSupported(cwd);
127
+
75
128
  const id = `task-${Date.now()}-${createHash("sha256").update(Math.random().toString()).digest("hex").slice(0, 6)}`;
76
129
  const task = {
77
130
  id,
78
131
  type: spec.type || "shell",
79
132
  command: spec.command,
80
- cwd: spec.cwd || process.cwd(),
133
+ cwd,
81
134
  description: spec.description || spec.command,
82
135
  status: TaskStatus.PENDING,
83
136
  createdAt: Date.now(),
@@ -111,6 +164,8 @@ export class BackgroundTaskManager extends EventEmitter {
111
164
  throw new Error(`Task ${taskId} is not pending (status: ${task.status})`);
112
165
  }
113
166
 
167
+ this._assertSandboxSupported(task.cwd);
168
+
114
169
  task.status = TaskStatus.RUNNING;
115
170
  task.startedAt = Date.now();
116
171
  task.lastHeartbeat = Date.now();
@@ -185,6 +240,19 @@ export class BackgroundTaskManager extends EventEmitter {
185
240
  return task;
186
241
  }
187
242
 
243
+ _assertSandboxSupported(executionCwd) {
244
+ if (!this._resolveSandboxPolicy) return;
245
+ const sandboxPolicy = validatePinnedSandboxPolicy(
246
+ this._resolveSandboxPolicy({
247
+ workspaceCwd: this._policyCwd,
248
+ executionCwd,
249
+ }),
250
+ );
251
+ if (sandboxPolicy) {
252
+ throw createUnsupportedSandboxError(sandboxPolicy);
253
+ }
254
+ }
255
+
188
256
  get(taskId) {
189
257
  return this.tasks.get(taskId) || null;
190
258
  }
@@ -17,6 +17,8 @@ import { withFileLock } from "../lib/with-file-lock.js";
17
17
  import { getConfigPath } from "../lib/paths.js";
18
18
  import { createHash } from "node:crypto";
19
19
  import { hostname } from "node:os";
20
+ import { mkdirSync } from "node:fs";
21
+ import { dirname } from "node:path";
20
22
 
21
23
  const FLAG_REGISTRY = {
22
24
  BACKGROUND_TASKS: {
@@ -115,14 +117,19 @@ export function listFeatures() {
115
117
  export function setFeature(name, value) {
116
118
  // Serialize the read-modify-write across processes so two concurrent
117
119
  // `cc config features enable/disable` invocations don't clobber each other
118
- // (and to space out writes so the atomic rename doesn't collide). Mirrors
119
- // setConfigValue; the lock is best-effort and never hangs the CLI.
120
- withFileLock(getConfigPath(), () => {
121
- const config = loadConfig();
122
- if (!config.features) config.features = {};
123
- config.features[name] = value;
124
- saveConfig(config);
125
- });
120
+ // (and to space out writes so the atomic rename doesn't collide).
121
+ const configPath = getConfigPath();
122
+ mkdirSync(dirname(configPath), { recursive: true });
123
+ withFileLock(
124
+ configPath,
125
+ () => {
126
+ const config = loadConfig({ failIfUnavailable: true });
127
+ if (!config.features) config.features = {};
128
+ config.features[name] = value;
129
+ saveConfig(config);
130
+ },
131
+ { failIfUnavailable: true },
132
+ );
126
133
  }
127
134
 
128
135
  export function getFlagInfo(name) {