chainlesschain 0.162.179 → 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 (251) 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-BF13YdB-.js → AIOps-cq0dwbnf.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-B9y67AjF.js → ActionButton-CvcgGl3w.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-FcdDHoSC.js → Analytics-hM2-jFqg.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-CRm_zwSp.js → AppLayout-B9_Gsd_g.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-Cbo7-usf.js → Artifacts-B7bu52Og.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-vMaLpkHC.js → Audit-DThfDgqB.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-FiUY692t.js → BackgroundAgents-DjlFiD4v.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-BK80j_GQ.js → Backup-CK9xJKnG.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-ChNMuYR1.js → BaseInput-DEiSU8K8.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-928UREpm.js → Chat-CpT96czj.js} +5 -5
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-BKPPNmZI.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-dFW-zDbe.js → Checkbox-C1GfDnO0.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-BCcNJUj2.js → Codegen-3DAKgiLx.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-DcEkLv70.js → Col-74cOwZU1.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-DihBRRqg.js → Community-tn9Kkz9j.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DGYxwWET.js → Compact-BcoXH7m4.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-OnYe1vS4.js → Compliance-DIPGUBgi.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-CmWB54c2.js → Cowork-doD5_91c.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-BrANb2uw.js → Cron-C-IDvubz.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-2HgWzSxg.js → Crosschain-CdcCoBdj.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-DdDwNI6K.js → DID-DdH5aMMA.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-D6PnE78-.js → Dashboard-DHLA4mm0.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-ClzvSN4O.js → Dropdown-dEeCt7Md.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-BUk9B727.js → EmailListRenderer-BMaecFv9.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-C8hi3v7i.js → FamilyGuardDashboard-DUaUukTN.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-B5SP8xkg.js → Federation-CMBE0_rp.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-DWzJ8AU5.js → FormItemContext-PG-JDbXq.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-3E3Q3rjG.js → GenericCardRenderer-Bcb0p88m.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-z8IdCMId.js → Git-Bgv15myB.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-CgqCF9lX.js → Governance-DB8YTArI.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-zgS-AuH9.js → Inference-BQMwCWaS.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-BdbgmESJ.js → KnowledgeGraph-B1_ZziiB.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-BHpknnjy.js → Logs-DovEEnt_.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-D201OL4T.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-Di9sAR7j.js → Marketplace-BlCXSpyB.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DtnR9Z_A.js → McpTools-CsCR09Wj.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-BaX9uuXD.js → Memory-CBVRLKno.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-xelyKWTY.js → MobileBridge-C9OIafgA.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-BiFTOGBN.js → MobileProjects-D_zoJ8Xs.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-bV3OzTiO.js → Mtc-vE-HdvLj.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-BkwvFTdZ.js → MtcAudit-CPLaekCd.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-_6HQQ-oC.js → Multisig-C_UP9C2o.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-BBYhmWQZ.js → NLProgramming-2nODJcU1.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-Cq-6dQnU.js → Notes-BhE-SJGu.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-ILZ9zkX6.js → NotificationSettings-D21q7BNU.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-DgmTBEvk.js → OrderTableRenderer-0b62GYOD.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-CkLnWwCk.js → Organization-CEqmw_sb.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-CWVHZbhf.js → Overflow-MW-gMDO_.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BXbqETkr.js → P2P-CY0hjI_G.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser--0JR-0ex.js → PdhVaultBrowser-BbWh7Man.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-Cz9_tfSy.js → Permissions-ChqEA_4Q.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-Dq7OWYtx.js → PersonalDataHub-B3YrbYfD.js} +5 -5
  55. package/src/assets/web-panel/assets/{Pipeline-DHPUNcc-.js → Pipeline-B8WBhhlW.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-CEAbrDkS.js → Privacy-Cmb0PXNx.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-CJYfclXE.js → ProjectInit-hF6zW3Nz.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-oCTgPCgp.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-BLTZtzTG.js → Providers-B8yux8Dx.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-C0b0TQB3.js → QrScannerModal-N76cQluO.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-tOl6N_gM.js → QuickAsk-ClvZ0KYR.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-DPzKr8yr.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-Cji1Vl79.js → Reputation-DJIZzQeG.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-D-NERWf2.js → Row-COzxu_bb.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-DzlgmAlB.js → RssFeed-CoYsJQuE.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-Cs0SWSa4.js → Search-DC77Mq9S.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-DL7EzcUO.js → Security-Loy2EdMO.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-CT-PzgTd.js → Services-GUD07Eu0.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-tByJWdJg.js → Skeleton-DWzkptYU.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-Dnr_6TTl.js → Skills-CEDn1r-Z.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-4O-yq0LY.js → Sla-ub_k4pXh.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-BYBVg1oR.js → SpeechSettings-D7HLX9CA.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-CpOYPbE5.js → SyncSettings-DbwOvl6m.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-CGVhOVh6.js → Tasks-CBQzoAu5.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-BuHFxR4X.js → Templates-BNVeIhud.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-DxDxHzgf.js → Tenant-PS9AzhXU.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-B9g_nISg.js → Terminal-Cgb4eLB_.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-DlrBC_4A.js → TimelineRenderer-CrmXMOkg.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-DvBlYZ6i.js → Tokens-DD-VDHw1.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-Dg8G9bhF.js → Trigger-BsoN_n-n.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-COICx4E-.js → Trust-n8LmN4ZV.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-B4pOBv7T.js → UkeySign-C9_7MImA.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-Qt4NMA8Q.js → VideoEditing-DyBteNgn.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-X1EkQc2Z.js → Wallet-BS5ixeXI.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-CyeEFBE5.js → WebAuthn-spc4UImU.js} +3 -3
  88. package/src/assets/web-panel/assets/{WorkflowEditor-nwzbMiVQ.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-5Isz5lhJ.js → colors-BGoCLkxI.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-DsFQS3Oy.js → compact-item-fYZBetMO.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-OcEXp2uz.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-CZvC4p09.js → hasIn-CE7ZuEnV.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BF1sab0Q.js → index--lap6cf7.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-C7yeL3rf.js → index-ATcSr_2O.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-VkFXA9N-.js → index-B2U-ObMq.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DSmGGg5h.js → index-B3CDgvNW.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-jrNIrpq4.js → index-B4wiTEOM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-B8ibl7bc.js → index-B8BsdNV_.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DOLozRk8.js → index-BEVm0lCS.js} +10 -10
  102. package/src/assets/web-panel/assets/{index-BPgc_bdR.js → index-BNZDILcO.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BTZZzlMO.js → index-BU_2qiDk.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Vubi6Tf5.js → index-BV551878.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-nXrnztXp.js → index-BfPL-m46.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CN8lPVpQ.js → index-BowH1eLQ.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BgE2BZl2.js → index-BsMfoaeo.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Cd5b2JHB.js → index-BunaQCa0.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CQlGw0nQ.js → index-C0v6YKKO.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Do6FJK32.js → index-C4SJVjs4.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Bqvo1f9T.js → index-C9Y3k0HI.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-LUdgPZg9.js → index-CIpVrf5P.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BpD2O7_p.js → index-CRLjEP1H.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-r_9NG6P7.js → index-CRnUE8Cb.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-FIgMAfBh.js → index-Cip8emOQ.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-RsQ1lFDV.js → index-CpIGIB85.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DGJupV0S.js → index-D1r9tT3J.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BpglwPWl.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-CG3B2wCF.js → index-De0quFDw.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CxA2PDK1.js → index-DoXk5KJ8.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DaGKOVvQ.js → index-DsvfrESp.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C7CtJYXq.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-Bu9NXvIa.js → index-MtOuq2i6.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BsZkTaa3.js → index-Puq6Luf1.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CPE4zHXF.js → index-QIpnpZgz.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Bsb-iylv.js → index-RMZ8a7Yt.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DMtGRQqw.js → index-WUrJ6S1j.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-C3lWvhk4.js → index-YjcumOZ4.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CmVbCGx_.js → index-lJBjB1QX.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-A5SHQW_M.js → index-ukg99D1V.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-CFJgt6qM.js → index-zb7rVOhV.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-Cdgep9YT.js → initDefaultProps-CYHMs1ag.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-7yn1ACEm.js → motion-DDB2Ffk5.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-D8o133Xc.js → move-Bg3cD-dX.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-CClHIG9V.js → mtc-parser-B5nedjTS.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-CLnrPE2d.js → omit-BHyuUILu.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-1CTWoZWl.js → pickAttrs-C9YQnEN9.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-xNNtS4Sp.js → placementArrow-DBUFYUe7.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-ypzAofs7.js → responsiveObserve-nBs2Obnk.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-QsrY9UwD.js → slide-DkCEaXNU.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-DSdThzOB.js → statusUtils-aqE1Wi6U.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-B_kKM8Ob.js → styleChecker-CN5bhfHB.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-CcdTBNV9.js → useFlexGapSupport-B_oX_kvJ.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-BuAH2XFU.js → useFs-rCuXHeme.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-B3zWiG4A.js → usePersonalDataHub-DZR9Abyd.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-B45Rc-CQ.js → vnode-ConCYQlX.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-DaYZDU5f.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/commands/hub.js +30 -24
  155. package/src/data/changelog.json +27 -1
  156. package/src/gateways/ws/background-agent-protocol.js +38 -0
  157. package/src/gateways/ws/message-dispatcher.js +1 -0
  158. package/src/gateways/ws/remote-session-protocol.js +65 -6
  159. package/src/gateways/ws/ws-server.js +8 -1
  160. package/src/harness/feature-flags.js +15 -8
  161. package/src/harness/mcp-client.js +618 -43
  162. package/src/lazy-dispatch.js +30 -1
  163. package/src/lib/agent-authority.js +22 -0
  164. package/src/lib/agent-core.js +2 -0
  165. package/src/lib/agent-sandbox.js +161 -0
  166. package/src/lib/agent-team/team-runner.js +22 -0
  167. package/src/lib/artifact-store.js +35 -0
  168. package/src/lib/async-hook-supervisor.cjs +13 -2
  169. package/src/lib/audit-mtc.js +3 -2
  170. package/src/lib/background-agent-supervisor.js +104 -3
  171. package/src/lib/background-interaction-journal.js +407 -0
  172. package/src/lib/background-interaction-resolver.js +23 -34
  173. package/src/lib/background-session-transport.js +12 -2
  174. package/src/lib/capability-manifest.js +41 -3
  175. package/src/lib/capability-negotiation.js +1 -1
  176. package/src/lib/channels/channel-manager.js +33 -0
  177. package/src/lib/channels/telegram-channel.js +23 -5
  178. package/src/lib/channels/webhook-channel.js +38 -15
  179. package/src/lib/chrome-connector.js +169 -23
  180. package/src/lib/config-manager.js +23 -9
  181. package/src/lib/context-breakdown.js +31 -0
  182. package/src/lib/cowork-cron.js +274 -32
  183. package/src/lib/cross-chain-mtc.js +3 -2
  184. package/src/lib/durable-security-store.js +124 -0
  185. package/src/lib/event-runtime-host.js +67 -6
  186. package/src/lib/event-runtime-store.js +163 -0
  187. package/src/lib/goal-store.js +32 -12
  188. package/src/lib/headless-manifest.js +2 -1
  189. package/src/lib/hook-environment.cjs +58 -0
  190. package/src/lib/hook-manager.js +12 -5
  191. package/src/lib/hook-runner.cjs +122 -28
  192. package/src/lib/hook-runner.js +1 -0
  193. package/src/lib/hooks-v2-producers.js +36 -0
  194. package/src/lib/hooks-v2-runtime.js +329 -52
  195. package/src/lib/interaction-adapter.js +60 -19
  196. package/src/lib/interaction-binding.js +64 -0
  197. package/src/lib/json-schema-output.js +439 -14
  198. package/src/lib/json-schema-validate.js +514 -883
  199. package/src/lib/lan-pairing-tokens.js +115 -57
  200. package/src/lib/lsp/__tests__/lsp-client.test.js +32 -0
  201. package/src/lib/lsp/__tests__/lsp-manager.test.js +25 -0
  202. package/src/lib/lsp/lsp-client.js +4 -0
  203. package/src/lib/lsp/lsp-manager.js +3 -0
  204. package/src/lib/lsp/lsp-server-registry.js +18 -0
  205. package/src/lib/mcp-client.js +1 -0
  206. package/src/lib/mcp-oauth.js +35 -21
  207. package/src/lib/packer/pkg-config-generator.js +1 -0
  208. package/src/lib/permission-decision.js +99 -0
  209. package/src/lib/plugin-monitor-supervisor.js +3 -0
  210. package/src/lib/plugin-runtime/capabilities.js +50 -0
  211. package/src/lib/plugin-runtime/capability-consent.js +42 -35
  212. package/src/lib/plugin-runtime/hooks.js +36 -9
  213. package/src/lib/plugin-runtime/install.js +23 -7
  214. package/src/lib/plugin-runtime/lsp.js +4 -0
  215. package/src/lib/plugin-runtime/manifest.js +50 -2
  216. package/src/lib/plugin-runtime/mcp.js +30 -5
  217. package/src/lib/plugin-runtime/monitors.js +11 -0
  218. package/src/lib/plugin-runtime/plugin-options.js +94 -38
  219. package/src/lib/plugin-runtime/policy.js +29 -2
  220. package/src/lib/plugin-runtime/sandbox-policy.js +84 -0
  221. package/src/lib/plugin-runtime/scopes.js +7 -3
  222. package/src/lib/plugin-runtime/trust.js +36 -31
  223. package/src/lib/process-execution-broker/credential-agent.js +114 -16
  224. package/src/lib/process-execution-broker/credential-transport-worker.js +312 -0
  225. package/src/lib/process-execution-broker/credential-transport.js +378 -0
  226. package/src/lib/process-execution-broker/index.js +408 -19
  227. package/src/lib/process-execution-broker/platform-sandbox.js +561 -21
  228. package/src/lib/process-execution-broker/windows-sandbox.ps1 +999 -0
  229. package/src/lib/project-mcp-trust.js +21 -26
  230. package/src/lib/protocol-replay.js +19 -12
  231. package/src/lib/settings-hooks.cjs +13 -0
  232. package/src/lib/skill-loader.js +83 -10
  233. package/src/lib/sync-credentials.js +5 -4
  234. package/src/lib/turn-binding.js +8 -6
  235. package/src/repl/agent-repl.js +113 -28
  236. package/src/repl/repl-turn-binding.js +5 -2
  237. package/src/runtime/agent-core.js +331 -26
  238. package/src/runtime/coding-agent-contract-shared.cjs +3 -3
  239. package/src/runtime/headless-runner.js +101 -4
  240. package/src/runtime/headless-stream.js +209 -23
  241. package/src/runtime/mcp-config.js +29 -17
  242. package/src/runtime/policies/agent-policy.js +2 -0
  243. package/src/workers/background-agent-worker.js +128 -13
  244. package/src/assets/web-panel/assets/ChatBubbleRenderer-DvySUzxJ.js +0 -1
  245. package/src/assets/web-panel/assets/MarkdownRenderer-DkxCweb1.js +0 -1
  246. package/src/assets/web-panel/assets/Projects-ClomCrN0.js +0 -1
  247. package/src/assets/web-panel/assets/RemoteSession-pRgaWsUK.js +0 -4
  248. package/src/assets/web-panel/assets/chat-Bvl-ojg2.js +0 -1
  249. package/src/assets/web-panel/assets/devWarning-CSVgo71Z.js +0 -1
  250. package/src/assets/web-panel/assets/index-DMIWh9fu.js +0 -1
  251. package/src/assets/web-panel/assets/index-ssTvV1l6.js +0 -1
@@ -3,9 +3,9 @@
3
3
  *
4
4
  * Platform enforcement currently available:
5
5
  * - macOS: Seatbelt sandbox-exec profiles
6
- * - Linux: prlimit resource-limit wrapper
7
- * - Windows: explicitly unavailable until a native Job Object + restricted
8
- * token adapter is installed
6
+ * - Linux: prlimit resource-limit wrapper, plus a non-promoting bubblewrap
7
+ * runtime probe for explicitly requested filesystem/network boundaries
8
+ * - Windows: Win32 Job Object + restricted-token wrapper
9
9
  *
10
10
  * Security model:
11
11
  * Adapters return a truthful spawn plan. An unavailable primitive is never
@@ -19,6 +19,60 @@ import os from "node:os";
19
19
  import path from "node:path";
20
20
  import * as fs from "node:fs";
21
21
  import crypto from "node:crypto";
22
+ import { spawnSync as nativeSpawnSync } from "node:child_process";
23
+ import { fileURLToPath } from "node:url";
24
+
25
+ /**
26
+ * Stable boundary identifiers shared by broker policies, platform plans, and
27
+ * audit records. A guarantee means that the named restriction is enforced by
28
+ * the selected backend; it does not merely describe a configured intent.
29
+ */
30
+ export const SANDBOX_BOUNDARIES = Object.freeze({
31
+ FILESYSTEM: "filesystem",
32
+ NETWORK: "network",
33
+ PROCESS_EXEC: "process-exec",
34
+ PROCESS_TREE: "process-tree",
35
+ RESOURCE_LIMITS: "resource-limits",
36
+ PRIVILEGE_REDUCTION: "privilege-reduction",
37
+ });
38
+
39
+ /**
40
+ * @typedef {typeof SANDBOX_BOUNDARIES[keyof typeof SANDBOX_BOUNDARIES]} SandboxBoundary
41
+ *
42
+ * @typedef {Object} SandboxGuaranteePlan
43
+ * @property {1} contractVersion
44
+ * @property {boolean} applied
45
+ * @property {string} platform
46
+ * @property {string} profile
47
+ * @property {string} command
48
+ * @property {readonly string[]} args
49
+ * @property {Readonly<Object>} options
50
+ * @property {string|null} enforcement
51
+ * @property {string|null} backend
52
+ * @property {readonly SandboxBoundary[]} guarantees
53
+ * @property {string|null} reason
54
+ * @property {{required: boolean, mode: "none"|"sync"|"async"}} postSpawn
55
+ */
56
+
57
+ const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
58
+ const WINDOWS_IDENTITY_WAIT = new Int32Array(new SharedArrayBuffer(4));
59
+ const LINUX_BWRAP_PATH = "/usr/bin/bwrap";
60
+ const LINUX_BWRAP_RUNTIME_PROBE_ARGS = Object.freeze([
61
+ "--die-with-parent",
62
+ "--new-session",
63
+ "--unshare-all",
64
+ "--ro-bind",
65
+ "/",
66
+ "/",
67
+ "--proc",
68
+ "/proc",
69
+ "--dev",
70
+ "/dev",
71
+ "--tmpfs",
72
+ "/tmp",
73
+ "--",
74
+ "/bin/true",
75
+ ]);
22
76
  const DEFAULT_RUNTIME = Object.freeze({
23
77
  platform: os.platform(),
24
78
  fs,
@@ -27,12 +81,49 @@ const DEFAULT_RUNTIME = Object.freeze({
27
81
  randomBytes: (size) => crypto.randomBytes(size),
28
82
  resolvePath: (value) => path.resolve(value),
29
83
  joinPath: (...parts) => path.join(...parts),
84
+ windowsDir: () => process.env.WINDIR || "C:\\Windows",
85
+ moduleDir: MODULE_DIR,
86
+ now: () => Date.now(),
87
+ sleepSync: (milliseconds) =>
88
+ Atomics.wait(WINDOWS_IDENTITY_WAIT, 0, 0, milliseconds),
89
+ spawnSync: nativeSpawnSync,
30
90
  });
31
91
 
32
92
  function resolveRuntime(overrides = {}) {
33
93
  return { ...DEFAULT_RUNTIME, ...overrides };
34
94
  }
35
95
 
96
+ function normalizeSandboxRequest(profileOrRequest, explicitRequest) {
97
+ const legacyRequest =
98
+ profileOrRequest &&
99
+ typeof profileOrRequest === "object" &&
100
+ !Array.isArray(profileOrRequest)
101
+ ? profileOrRequest
102
+ : { profile: profileOrRequest };
103
+ const request =
104
+ explicitRequest &&
105
+ typeof explicitRequest === "object" &&
106
+ !Array.isArray(explicitRequest)
107
+ ? explicitRequest
108
+ : legacyRequest;
109
+ const profile =
110
+ typeof request.profile === "string"
111
+ ? request.profile
112
+ : typeof legacyRequest.profile === "string"
113
+ ? legacyRequest.profile
114
+ : "default";
115
+ const requiredBoundaries = Array.isArray(request.requiredBoundaries)
116
+ ? [
117
+ ...new Set(
118
+ request.requiredBoundaries.filter(
119
+ (boundary) => typeof boundary === "string",
120
+ ),
121
+ ),
122
+ ]
123
+ : [];
124
+ return { profile, requiredBoundaries };
125
+ }
126
+
36
127
  /**
37
128
  * A native `spawn(..., { shell: true })` asks Node to execute one command
38
129
  * string through the platform shell. Once a sandbox wrapper becomes the
@@ -45,7 +136,7 @@ function resolveRuntime(overrides = {}) {
45
136
  * sandbox executable itself is always spawned directly.
46
137
  */
47
138
  function normalizeWrappedInvocation(command, args, spawnOpts, platform) {
48
- if (!spawnOpts?.shell || platform === "win32") {
139
+ if (!spawnOpts?.shell) {
49
140
  return {
50
141
  command,
51
142
  args: [...(args || [])],
@@ -53,6 +144,19 @@ function normalizeWrappedInvocation(command, args, spawnOpts, platform) {
53
144
  };
54
145
  }
55
146
 
147
+ if (platform === "win32") {
148
+ const shell =
149
+ typeof spawnOpts.shell === "string"
150
+ ? spawnOpts.shell
151
+ : process.env.ComSpec || "C:\\Windows\\System32\\cmd.exe";
152
+ const shellCommand = [command, ...(args || [])].map(String).join(" ");
153
+ return {
154
+ command: shell,
155
+ args: ["/d", "/s", "/c", shellCommand],
156
+ options: { ...spawnOpts, shell: false },
157
+ };
158
+ }
159
+
56
160
  const shell =
57
161
  typeof spawnOpts.shell === "string" ? spawnOpts.shell : "/bin/sh";
58
162
  const shellCommand = [command, ...(args || [])].map(String).join(" ");
@@ -82,6 +186,8 @@ function normalizeWrappedInvocation(command, args, spawnOpts, platform) {
82
186
  * args: string[],
83
187
  * options: Object,
84
188
  * enforcement: string|null,
189
+ * backend: string|null,
190
+ * guarantees: readonly SandboxBoundary[],
85
191
  * reason: string|null,
86
192
  * cleanup?: Function,
87
193
  * postSpawn: {required: boolean, mode: "none"|"sync"|"async"}
@@ -97,10 +203,16 @@ function createSandboxPlan(input) {
97
203
  contractVersion: 1,
98
204
  applied: false,
99
205
  enforcement: null,
206
+ backend: null,
207
+ guarantees: Object.freeze([]),
100
208
  reason: null,
101
209
  ...input,
102
210
  args: Object.freeze([...(input.args || [])]),
103
211
  options: Object.freeze({ ...(input.options || {}) }),
212
+ guarantees: Object.freeze([...(input.guarantees || [])]),
213
+ runtimeProbe: input.runtimeProbe
214
+ ? Object.freeze({ ...input.runtimeProbe })
215
+ : null,
104
216
  postSpawn,
105
217
  });
106
218
  }
@@ -132,6 +244,10 @@ export function generateMacSeatbeltProfile(opts = {}, runtimeOverrides = {}) {
132
244
  const lines = [
133
245
  "(version 1)",
134
246
  "(deny default)",
247
+ // Apple's system baseline supplies the loader/device primitives required
248
+ // for ordinary command startup. Our explicit rules below still own
249
+ // process, filesystem write, and network policy.
250
+ '(import "system.sb")',
135
251
  // Basic system operations always allowed
136
252
  "(allow signal (target self))",
137
253
  '(allow process-exec (literal "/usr/bin/env"))',
@@ -148,6 +264,10 @@ export function generateMacSeatbeltProfile(opts = {}, runtimeOverrides = {}) {
148
264
 
149
265
  if (allowNetwork) {
150
266
  lines.push("(allow network*)");
267
+ } else {
268
+ // Keep the imported system baseline from widening the caller's explicit
269
+ // no-network policy.
270
+ lines.push("(deny network*)");
151
271
  }
152
272
 
153
273
  // Allow read/write to specific paths
@@ -162,9 +282,17 @@ export function generateMacSeatbeltProfile(opts = {}, runtimeOverrides = {}) {
162
282
  }
163
283
 
164
284
  // Always allow read access to system paths needed for basic execution
285
+ lines.push('(allow file-read* (subpath "/bin"))');
286
+ lines.push('(allow file-read* (subpath "/usr/bin"))');
165
287
  lines.push('(allow file-read* (subpath "/usr/lib"))');
288
+ lines.push('(allow file-read* (subpath "/usr/libexec"))');
166
289
  lines.push('(allow file-read* (subpath "/System/Library"))');
290
+ lines.push('(allow file-read* (subpath "/Library/Frameworks"))');
167
291
  lines.push('(allow file-read* (subpath "/usr/local/lib"))');
292
+ lines.push(
293
+ '(allow file-read* file-write* (literal "/dev/null") (literal "/dev/stdin") (literal "/dev/stdout") (literal "/dev/stderr"))',
294
+ );
295
+ lines.push('(allow file-read* (literal "/dev/urandom"))');
168
296
  lines.push('(allow file-read* (literal "/etc/passwd"))');
169
297
 
170
298
  return lines.join("\n");
@@ -199,6 +327,7 @@ export function applyMacSandbox(
199
327
  const base = {
200
328
  platform: runtime.platform,
201
329
  profile: sandboxOpts.profileName || "default",
330
+ backend: "macos-seatbelt",
202
331
  command,
203
332
  args,
204
333
  options: { ...(spawnOpts || {}) },
@@ -250,6 +379,11 @@ export function applyMacSandbox(
250
379
  ...base,
251
380
  applied: true,
252
381
  enforcement: "macos-seatbelt",
382
+ guarantees: [
383
+ SANDBOX_BOUNDARIES.FILESYSTEM,
384
+ ...(sandboxOpts.allowNetwork ? [] : [SANDBOX_BOUNDARIES.NETWORK]),
385
+ ...(sandboxOpts.allowExec ? [] : [SANDBOX_BOUNDARIES.PROCESS_EXEC]),
386
+ ],
253
387
  command: sandboxExecutable,
254
388
  args: newArgs,
255
389
  options: invocation.options,
@@ -264,12 +398,137 @@ export function applyMacSandbox(
264
398
  }
265
399
 
266
400
  // ---------------------------------------------------------------------------
267
- // Windows Job Object availability
401
+ // Windows Job Object + Restricted Token
268
402
  // ---------------------------------------------------------------------------
269
403
 
270
404
  /**
271
- * Report Windows sandbox availability. Node's child_process API cannot create
272
- * and synchronously assign a Job Object or restricted token by itself.
405
+ * Materialize the shipped PowerShell/Win32 adapter into a physical temporary
406
+ * file. This is required for pkg builds where source assets live in a virtual
407
+ * snapshot that an external powershell.exe process cannot open directly.
408
+ */
409
+ function materializeWindowsAdapter(runtime) {
410
+ const sourcePath =
411
+ runtime.windowsAdapterScriptPath ||
412
+ runtime.joinPath(runtime.moduleDir, "windows-sandbox.ps1");
413
+ let content = runtime.windowsAdapterContent;
414
+ if (content === undefined) {
415
+ if (!runtime.fs.existsSync(sourcePath)) {
416
+ return { reason: "windows_native_adapter_missing" };
417
+ }
418
+ try {
419
+ content = runtime.fs.readFileSync(sourcePath, "utf8");
420
+ } catch {
421
+ return { reason: "windows_native_adapter_unreadable" };
422
+ }
423
+ }
424
+ const adapterHash = crypto
425
+ .createHash("sha256")
426
+ .update(String(content))
427
+ .digest("hex")
428
+ .slice(0, 24);
429
+ const cacheExecutable = runtime.joinPath(
430
+ runtime.tmpdir(),
431
+ `chainless-win-sandbox-${adapterHash}.exe`,
432
+ );
433
+ if (runtime.fs.existsSync(cacheExecutable)) {
434
+ return {
435
+ cacheExecutable,
436
+ bootstrapRequired: false,
437
+ };
438
+ }
439
+ const scriptPath = runtime.joinPath(
440
+ runtime.tmpdir(),
441
+ `chainless-win-sandbox-${runtime.randomBytes(12).toString("hex")}.ps1`,
442
+ );
443
+ try {
444
+ runtime.fs.writeFileSync(scriptPath, content, { mode: 0o600 });
445
+ } catch {
446
+ return { reason: "windows_native_adapter_materialize_failed" };
447
+ }
448
+ return {
449
+ scriptPath,
450
+ cacheExecutable,
451
+ bootstrapRequired: true,
452
+ cleanup: () => {
453
+ try {
454
+ runtime.fs.unlinkSync(scriptPath);
455
+ } catch {
456
+ // The temporary adapter may already have been removed.
457
+ }
458
+ },
459
+ };
460
+ }
461
+
462
+ function waitForWindowsTargetIdentity(
463
+ proc,
464
+ identityPath,
465
+ runtime,
466
+ timeoutMs = 10_000,
467
+ ) {
468
+ const deadline = runtime.now() + timeoutMs;
469
+ let lastError;
470
+ while (runtime.now() < deadline) {
471
+ try {
472
+ const identity = JSON.parse(
473
+ runtime.fs.readFileSync(identityPath, "utf8"),
474
+ );
475
+ if (identity?.error) {
476
+ throw new Error(
477
+ `Windows sandbox helper rejected the target: ${identity.error}`,
478
+ );
479
+ }
480
+ const targetPid = Number(identity?.targetPid);
481
+ const helperPid = Number(identity?.helperPid);
482
+ if (!Number.isSafeInteger(targetPid) || targetPid <= 0) {
483
+ throw new Error("Windows sandbox identity omitted a valid target PID");
484
+ }
485
+ if (
486
+ Number.isSafeInteger(helperPid) &&
487
+ helperPid > 0 &&
488
+ helperPid !== proc.pid
489
+ ) {
490
+ throw new Error(
491
+ `Windows sandbox identity named unexpected helper PID ${helperPid}`,
492
+ );
493
+ }
494
+ if (targetPid === proc.pid) {
495
+ throw new Error(
496
+ "Windows sandbox target PID unexpectedly equals its helper PID",
497
+ );
498
+ }
499
+
500
+ const wrapperPid = proc.pid;
501
+ proc.sandboxWrapperPid = wrapperPid;
502
+ proc.sandboxTargetPid = targetPid;
503
+ // ChildProcess.pid is a writable data property. The native ChildProcess
504
+ // handle still points at the helper, so kill()/ref()/unref() retain Job
505
+ // lifetime semantics while callers observe and persist the real target.
506
+ proc.pid = targetPid;
507
+ return { targetPid, wrapperPid };
508
+ } catch (error) {
509
+ lastError = error;
510
+ if (
511
+ error?.code !== "ENOENT" &&
512
+ !(error instanceof SyntaxError) &&
513
+ !String(error?.message || "").includes("omitted a valid target PID")
514
+ ) {
515
+ throw error;
516
+ }
517
+ }
518
+ runtime.sleepSync(10);
519
+ }
520
+ throw new Error(
521
+ `Timed out waiting for Windows sandbox target identity${
522
+ lastError?.message ? `: ${lastError.message}` : ""
523
+ }`,
524
+ );
525
+ }
526
+
527
+ /**
528
+ * Wrap a target with the built-in Windows PowerShell host. The shipped script
529
+ * P/Invokes Win32 to create a restricted primary token, starts the target
530
+ * suspended, assigns it to a kill-on-close Job Object, then resumes it. The
531
+ * target therefore cannot run before both native boundaries are active.
273
532
  *
274
533
  * @param {string} command
275
534
  * @param {string[]} args
@@ -287,6 +546,7 @@ export function applyWindowsSandbox(
287
546
  const base = {
288
547
  platform: runtime.platform,
289
548
  profile: sandboxOpts.profileName || "default",
549
+ backend: "windows-job-restricted-token",
290
550
  command,
291
551
  args,
292
552
  options: spawnOpts,
@@ -298,19 +558,196 @@ export function applyWindowsSandbox(
298
558
  });
299
559
  }
300
560
 
301
- // Node does not expose Job Object or restricted-token primitives. Mark the
302
- // platform unavailable instead of treating CREATE_NO_WINDOW or an
303
- // environment marker as a security boundary. A future native adapter may
304
- // return an applied plan with a synchronous postSpawn step.
561
+ // The hosted Windows restricted-token path cannot reliably preserve file
562
+ // descriptor backed stdout/stderr when its helper is itself detached. Mark
563
+ // that combination unavailable before launch: strict/required callers fail
564
+ // closed, while ordinary trusted control-plane callers retain the broker's
565
+ // explicit audited fallback instead of receiving a phantom target PID.
566
+ if (
567
+ spawnOpts?.detached === true &&
568
+ Array.isArray(spawnOpts?.stdio) &&
569
+ spawnOpts.stdio.slice(0, 3).some((entry) => Number.isInteger(entry))
570
+ ) {
571
+ return createSandboxPlan({
572
+ ...base,
573
+ reason: "windows_detached_file_stdio_unsupported",
574
+ });
575
+ }
576
+
577
+ const nodeIpcFd = Array.isArray(spawnOpts?.stdio)
578
+ ? spawnOpts.stdio.findIndex((entry) => entry === "ipc")
579
+ : -1;
580
+ if (
581
+ Array.isArray(spawnOpts?.stdio) &&
582
+ spawnOpts.stdio
583
+ .slice(3)
584
+ .some(
585
+ (entry, offset) =>
586
+ offset + 3 !== nodeIpcFd &&
587
+ entry !== "ignore" &&
588
+ entry !== null &&
589
+ entry !== undefined,
590
+ )
591
+ ) {
592
+ return createSandboxPlan({
593
+ ...base,
594
+ reason: "windows_extra_descriptor_unsupported",
595
+ });
596
+ }
597
+
598
+ const adapter = materializeWindowsAdapter(runtime);
599
+ if (!adapter.cacheExecutable) {
600
+ return createSandboxPlan({
601
+ ...base,
602
+ reason: adapter.reason,
603
+ });
604
+ }
605
+
606
+ const invocation = normalizeWrappedInvocation(
607
+ command,
608
+ args,
609
+ spawnOpts,
610
+ runtime.platform,
611
+ );
612
+ const profile = sandboxOpts.profileName || "default";
613
+ const limits = sandboxOpts.limits || {};
614
+ const identityPath =
615
+ spawnOpts?.detached === true
616
+ ? runtime.joinPath(
617
+ runtime.tmpdir(),
618
+ `chainless-win-sandbox-identity-${runtime
619
+ .randomBytes(24)
620
+ .toString("hex")}.json`,
621
+ )
622
+ : null;
623
+ const launchSpec = {
624
+ cpuSeconds: Number(limits.cpu || 0),
625
+ processMemoryBytes: Number(
626
+ limits.as || (profile === "strict" ? 256 * 1024 * 1024 : 0),
627
+ ),
628
+ activeProcessLimit: profile === "strict" ? 16 : 64,
629
+ command: invocation.command,
630
+ args: invocation.args,
631
+ nodeIpcFd,
632
+ detached: invocation.options.detached === true,
633
+ windowsHide: invocation.options.windowsHide === true,
634
+ };
635
+ if (identityPath) launchSpec.identityPath = identityPath;
636
+ const payload = Buffer.from(JSON.stringify(launchSpec), "utf8").toString(
637
+ "base64",
638
+ );
639
+ const helperArgs = [payload];
640
+ const options = {
641
+ ...invocation.options,
642
+ shell: false,
643
+ env: {
644
+ ...(invocation.options.env || process.env),
645
+ CC_WINDOWS_SANDBOXED: "1",
646
+ CC_WINDOWS_SANDBOX_PROFILE: profile,
647
+ },
648
+ };
649
+ const cleanupIdentity = () => {
650
+ if (!identityPath) return;
651
+ try {
652
+ runtime.fs.unlinkSync(identityPath);
653
+ } catch {
654
+ // The post-spawn handshake normally removes this file first.
655
+ }
656
+ };
657
+ const identityContract = identityPath
658
+ ? {
659
+ postSpawn: { required: true, mode: "sync" },
660
+ postSpawnWindows: (proc) => {
661
+ try {
662
+ return waitForWindowsTargetIdentity(proc, identityPath, runtime);
663
+ } catch (error) {
664
+ try {
665
+ proc.kill?.();
666
+ } catch {
667
+ // Closing the helper is best-effort; the thrown failure remains
668
+ // authoritative and strict mode will fail closed.
669
+ }
670
+ throw error;
671
+ } finally {
672
+ cleanupIdentity();
673
+ }
674
+ },
675
+ }
676
+ : {};
677
+
678
+ if (adapter.bootstrapRequired) {
679
+ const powershell = runtime.joinPath(
680
+ runtime.windowsDir(),
681
+ "System32",
682
+ "WindowsPowerShell",
683
+ "v1.0",
684
+ "powershell.exe",
685
+ );
686
+ if (!runtime.fs.existsSync(powershell)) {
687
+ adapter.cleanup?.();
688
+ cleanupIdentity();
689
+ return createSandboxPlan({
690
+ ...base,
691
+ reason: "windows_powershell_host_unavailable",
692
+ });
693
+ }
694
+ const compileResult = runtime.spawnSync(
695
+ powershell,
696
+ [
697
+ "-NoLogo",
698
+ "-NoProfile",
699
+ "-NonInteractive",
700
+ "-ExecutionPolicy",
701
+ "Bypass",
702
+ "-File",
703
+ adapter.scriptPath,
704
+ "-CacheExecutable",
705
+ adapter.cacheExecutable,
706
+ "-CompileOnly",
707
+ ],
708
+ {
709
+ shell: false,
710
+ windowsHide: true,
711
+ encoding: "utf8",
712
+ timeout: 30_000,
713
+ env: invocation.options.env || process.env,
714
+ },
715
+ );
716
+ adapter.cleanup?.();
717
+ if (
718
+ compileResult?.error ||
719
+ compileResult?.status !== 0 ||
720
+ !runtime.fs.existsSync(adapter.cacheExecutable)
721
+ ) {
722
+ cleanupIdentity();
723
+ return createSandboxPlan({
724
+ ...base,
725
+ reason: "windows_native_adapter_compile_failed",
726
+ });
727
+ }
728
+ }
729
+
305
730
  return createSandboxPlan({
306
731
  ...base,
307
- reason: "windows_native_job_object_unavailable",
732
+ applied: true,
733
+ enforcement: "windows-job-restricted-token",
734
+ guarantees: [
735
+ SANDBOX_BOUNDARIES.PROCESS_TREE,
736
+ SANDBOX_BOUNDARIES.RESOURCE_LIMITS,
737
+ SANDBOX_BOUNDARIES.PRIVILEGE_REDUCTION,
738
+ ],
739
+ command: adapter.cacheExecutable,
740
+ args: helperArgs,
741
+ options,
742
+ ...identityContract,
743
+ cleanup: cleanupIdentity,
308
744
  });
309
745
  }
310
746
 
311
747
  /**
312
- * Reject an attempted Windows post-spawn association until a native adapter is
313
- * available.
748
+ * Retained for injected adapters that elect to perform synchronous post-spawn
749
+ * enforcement. The built-in wrapper completes enforcement before target resume
750
+ * and therefore does not require this path.
314
751
  *
315
752
  * @param {ChildProcess} proc - The spawned child process
316
753
  * @param {Object} sandboxResult - Result from applyWindowsSandbox
@@ -324,17 +761,83 @@ export function postSpawnWindowsSandbox(
324
761
  if (runtime.platform !== "win32" || !sandboxResult?.postSpawn?.required) {
325
762
  return;
326
763
  }
327
- const error = new Error(
328
- "Windows native Job Object enforcement is unavailable",
329
- );
330
- error.code = "ERR_WINDOWS_SANDBOX_UNAVAILABLE";
331
- throw error;
764
+ if (typeof sandboxResult.postSpawnWindows === "function") {
765
+ return sandboxResult.postSpawnWindows(proc);
766
+ }
332
767
  }
333
768
 
334
769
  // ---------------------------------------------------------------------------
335
770
  // Linux resource-limit enforcement
336
771
  // ---------------------------------------------------------------------------
337
772
 
773
+ /**
774
+ * Prove only that the installed bubblewrap binary can create the namespace and
775
+ * mount primitives needed by a future policy backend. This deliberately does
776
+ * not attest any filesystem/network policy and therefore must never promote a
777
+ * sandbox guarantee.
778
+ */
779
+ function probeLinuxBubblewrapRuntime(runtime) {
780
+ let binaryPresent = false;
781
+ try {
782
+ binaryPresent = runtime.fs.existsSync(LINUX_BWRAP_PATH);
783
+ } catch {
784
+ return {
785
+ kind: "linux-bwrap-runtime-smoke-v1",
786
+ attempted: false,
787
+ runnable: false,
788
+ reason: "binary_probe_failed",
789
+ };
790
+ }
791
+ if (!binaryPresent) {
792
+ return {
793
+ kind: "linux-bwrap-runtime-smoke-v1",
794
+ attempted: false,
795
+ runnable: false,
796
+ reason: "binary_missing",
797
+ };
798
+ }
799
+
800
+ let result;
801
+ try {
802
+ result = runtime.spawnSync(
803
+ LINUX_BWRAP_PATH,
804
+ [...LINUX_BWRAP_RUNTIME_PROBE_ARGS],
805
+ {
806
+ shell: false,
807
+ stdio: "ignore",
808
+ timeout: 5_000,
809
+ env: {
810
+ PATH: "/usr/bin:/bin",
811
+ LANG: "C",
812
+ LC_ALL: "C",
813
+ },
814
+ },
815
+ );
816
+ } catch {
817
+ return {
818
+ kind: "linux-bwrap-runtime-smoke-v1",
819
+ attempted: true,
820
+ runnable: false,
821
+ reason: "probe_spawn_failed",
822
+ };
823
+ }
824
+
825
+ if (result?.error || result?.status !== 0) {
826
+ return {
827
+ kind: "linux-bwrap-runtime-smoke-v1",
828
+ attempted: true,
829
+ runnable: false,
830
+ reason: result?.error ? "probe_spawn_failed" : "probe_failed",
831
+ };
832
+ }
833
+ return {
834
+ kind: "linux-bwrap-runtime-smoke-v1",
835
+ attempted: true,
836
+ runnable: true,
837
+ reason: null,
838
+ };
839
+ }
840
+
338
841
  /**
339
842
  * Apply Linux resource limits through the util-linux `prlimit` wrapper.
340
843
  *
@@ -360,6 +863,7 @@ export function applyLinuxSandbox(
360
863
  const base = {
361
864
  platform: runtime.platform,
362
865
  profile: sandboxOpts.profileName || "default",
866
+ backend: "linux-prlimit",
363
867
  command,
364
868
  args,
365
869
  options: { ...(spawnOpts || {}) },
@@ -372,6 +876,29 @@ export function applyLinuxSandbox(
372
876
  }
373
877
 
374
878
  const { limits = {} } = sandboxOpts;
879
+ const requiredBoundaries = Array.isArray(sandboxOpts.requiredBoundaries)
880
+ ? sandboxOpts.requiredBoundaries
881
+ : [];
882
+ const requiresStrongLinuxBoundary = requiredBoundaries.some(
883
+ (boundary) =>
884
+ boundary === SANDBOX_BOUNDARIES.FILESYSTEM ||
885
+ boundary === SANDBOX_BOUNDARIES.NETWORK,
886
+ );
887
+
888
+ if (requiresStrongLinuxBoundary) {
889
+ const runtimeProbe = probeLinuxBubblewrapRuntime(runtime);
890
+ return createSandboxPlan({
891
+ ...base,
892
+ backend: null,
893
+ candidateBackend: "linux-bwrap",
894
+ policyAttested: false,
895
+ runtimeProbe,
896
+ reason: runtimeProbe.runnable
897
+ ? "linux_bwrap_policy_unattested"
898
+ : "linux_bwrap_unavailable",
899
+ guarantees: [],
900
+ });
901
+ }
375
902
 
376
903
  const prlimitParts = [];
377
904
  if (limits.cpu) prlimitParts.push(`--cpu=${limits.cpu}`);
@@ -404,6 +931,7 @@ export function applyLinuxSandbox(
404
931
  ...base,
405
932
  applied: true,
406
933
  enforcement: "linux-prlimit",
934
+ guarantees: [SANDBOX_BOUNDARIES.RESOURCE_LIMITS],
407
935
  command: "/usr/bin/prlimit",
408
936
  args: [...prlimitParts, "--", invocation.command, ...invocation.args],
409
937
  options,
@@ -420,17 +948,28 @@ export function applyLinuxSandbox(
420
948
  * @param {string} command
421
949
  * @param {string[]} args
422
950
  * @param {Object} spawnOpts - Original spawn options
423
- * @param {"default"|"strict"|"network-only"} profileName
951
+ * @param {"default"|"strict"|"network-only"|{
952
+ * profile?: "default"|"strict"|"network-only",
953
+ * requiredBoundaries?: string[]
954
+ * }} profileOrRequest
955
+ * @param {Object} runtimeOverrides
956
+ * @param {{profile?: string, requiredBoundaries?: string[]}|null} explicitRequest
424
957
  * @returns {ReturnType<typeof createSandboxPlan>}
425
958
  */
426
959
  export function applySandbox(
427
960
  command,
428
961
  args,
429
962
  spawnOpts,
430
- profileName = "default",
963
+ profileOrRequest = "default",
431
964
  runtimeOverrides = {},
965
+ explicitRequest = null,
432
966
  ) {
433
967
  const runtime = resolveRuntime(runtimeOverrides);
968
+ const sandboxRequest = normalizeSandboxRequest(
969
+ profileOrRequest,
970
+ explicitRequest,
971
+ );
972
+ const profileName = sandboxRequest.profile;
434
973
  const profiles = {
435
974
  default: {
436
975
  allowNetwork: false,
@@ -471,6 +1010,7 @@ export function applySandbox(
471
1010
  const profile = {
472
1011
  ...(profiles[profileName] || profiles.default),
473
1012
  profileName: profiles[profileName] ? profileName : "default",
1013
+ requiredBoundaries: sandboxRequest.requiredBoundaries,
474
1014
  };
475
1015
 
476
1016
  // Dispatch to platform handler