chainlesschain 0.165.4 → 0.165.6

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 (214) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-C__nnraW.js → AIOps-vfoDb6HZ.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CsKWHEZ-.js → ActionButton-Cm5f7p93.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BhIjfjWO.js → Analytics-yRuvgWxa.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-BjuXFzWZ.js → AppLayout-BROsC4h1.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-k6whDkZJ.js → Artifacts-CimHApb1.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-C4FBMYRh.js → Audit-DI8kr8zI.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-C_vRrSXk.js → BackgroundAgents-DSLzKy-n.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-CngFZeVp.js → Backup-DkH_n2r1.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-B_H9EBRm.js → BaseInput-CKSrUoRA.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-DLEC5kt7.js → Chat-ClwfMXUS.js} +3 -3
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-CjMaqfL7.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-m3ClR4xh.js → Checkbox-BYTehcQG.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CmLSFrgp.js → Codegen-B8AMcp6J.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-DFGQteHN.js → Col-BNSogPo3.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-EUUVORHE.js → Community-JxPvGb8O.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-2PSg0Yr7.js → Compact-DF7bFAgE.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-CcKur7gB.js → Compliance-Bvhqnwrq.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DK_Fezfa.js → Cowork-BDS0CJvM.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-Dbw89xl7.js → Cron-DqvRfxoQ.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CH66h_Ec.js → Crosschain-CDcIvnqS.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-D1igRb17.js → DID-D7jaUxU3.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-DVAsrobk.js → Dashboard-B5A18wEV.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-Dl7W8AQs.js → Dropdown-_6IVkGag.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-D5tpaEQC.js → EmailListRenderer-C9PeiKCG.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-O_VQf8gq.js → FamilyGuardDashboard-DXjuEHrX.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-BTVcgS9n.js → Federation-B4IAR9zv.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BRnkJAIz.js → FormItemContext-CxQeSNx1.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-8OWSCkDj.js → GenericCardRenderer-B8GS-Y3N.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-WXLYTQj2.js → Git-BS8SouUX.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-BezaUhtc.js → Governance-Dq9aYb5Q.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CuRay3Gu.js → Inference-BrCEe9AJ.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-DfWLDW1m.js → KnowledgeGraph-DybOpiY2.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-C55y2pPY.js → Logs-DBpzbRfx.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-Dk08Uycg.js → Marketplace-Cc7CCSCu.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-C11fSB5V.js → McpTools-DFaIhOfu.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-mr0eZc97.js → Memory-KCqlp9ol.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-C0iCSRbb.js → MobileBridge-Bys84ySN.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-CY-xMG8Z.js → MobileProjects-B-6qU-je.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-Bplqc_Wc.js → Mtc-BgY-fvCq.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-BlzwsoSM.js → MtcAudit-B8pCmqQJ.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-BBLmTty6.js → Multisig-BbOmYutA.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-DtT1uf1W.js → NLProgramming-BocLQz_M.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-DcqQAxTK.js → Notes-CSwbxEop.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-cb0kWoZe.js → NotificationSettings-CqFNSYG3.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-kw9HZQNN.js → OrderTableRenderer-C6gMfAk7.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-DlTlbkUN.js → Organization-QrDXVdMJ.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-pp1qs-oY.js → Overflow-Cxntotjt.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BESLnyYz.js → P2P-CIdBOwNq.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-B-J1mGuf.js → PdhVaultBrowser-CT5aP8rD.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-B36PeT1_.js → Permissions-DH7OM8XU.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-CTK2ad-3.js → PersonalDataHub-CnJqbWce.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-zJNGXsac.js → Pipeline-5C3HG1kH.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-b9fz0Qf0.js → Privacy-B4yIp7xK.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-DjZbZgiB.js → ProjectInit-BMvt-2M0.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-C-hHJXlD.js → ProjectSettings-CvSTEMRG.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-BKBJxEek.js +1 -0
  60. package/src/assets/web-panel/assets/Providers-BdwUqFWN.js +1 -0
  61. package/src/assets/web-panel/assets/{QrScannerModal-A0yoQ3-3.js → QrScannerModal-DtnCKlTm.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-DrFgFJNC.js → QuickAsk-sTbgwyNr.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-KC8hF1g4.js → Recommend-Bzr14gSb.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DGTPIk6u.js → RemoteSession-C_TF5IaJ.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-QmbwM7Yk.js → Reputation-GCAM4H2K.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-BxjJylia.js → Row-Dir0r6OQ.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-B7Wda1J6.js → RssFeed-CpGnlecX.js} +2 -2
  68. package/src/assets/web-panel/assets/{Search-_plQ_YSA.js → Search-DMMamC6u.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-CD2YJZfx.js → Security-yqay5yG-.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-qQE7NmwE.js → Services-C5i2wh9G.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-DgQqkFf7.js → Skeleton-vUqk35yj.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-C4C0oFFt.js → Skills-DzcSkhot.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-Px2N2B5c.js → Sla-CB6qbYNr.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-DN6C3lQd.js → SpeechSettings-BHUI7cox.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-jm5aH_m7.js → SyncSettings-D-RrsxTJ.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-DlkJcv67.js → Tasks-DFxdOJiq.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-DyrTHUBs.js → Templates-SKAob_Aq.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-mC434PtL.js → Tenant-BPfsfHQ-.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-o0_s3NgK.js → Terminal-8iw7FpUf.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-DAGlAXOw.js → TimelineRenderer-C4Gjj5Su.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BnulS_bs.js → Tokens-COYhN18e.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-suQbVXYM.js → Trigger-B-JzXcf6.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DbIJAPo5.js → Trust-BJ5bg0nI.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-CQNpbIHi.js → UkeySign-BG_ByRMf.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-D8aWCERk.js → VideoEditing-B3kOnSiU.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-CuLpYfZc.js → Wallet-CcoGFnK7.js} +2 -2
  87. package/src/assets/web-panel/assets/{WebAuthn-CsfG8ZMc.js → WebAuthn-DdT8vRC_.js} +4 -4
  88. package/src/assets/web-panel/assets/{WorkflowEditor-BYsEAZbd.js → WorkflowEditor-CZu3M9N8.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-FT4Um3-I.js → chat-DxJt3Qba.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-CKbS_Wlt.js → colors-DdYfwJaB.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-CFJtzuJM.js → compact-item-BG3SUzwj.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-B9kSRW6K.js → createContext-By51z2-1.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DR-x0Xpi.js → hasIn-B0apZ3KJ.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DjEP7WkH.js → index-3jWkIu5j.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-7jHr_A_k.js → index-B3rWuPMW.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DcfxhKXR.js → index-B5rLiEo0.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-tqSEHPRT.js → index-BBPf3TpM.js} +1 -1
  99. package/src/assets/web-panel/assets/index-BETiL0a3.js +1 -0
  100. package/src/assets/web-panel/assets/{index-Cr2FLUsD.js → index-BIrJfqO4.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Bt4fS2eL.js → index-BLIbGOLX.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BwR7yc2h.js → index-BP94CrBV.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BVPU43b1.js → index-BWF8_mkx.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DShSkww-.js → index-BYINoRLK.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Bzz0C1BZ.js → index-BZ_0qm4g.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-KqDaCApw.js → index-BewKYrX9.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BW-t9n-q.js → index-BfWYsB9t.js} +1 -1
  108. package/src/assets/web-panel/assets/index-BhAVHuW8.js +1 -0
  109. package/src/assets/web-panel/assets/{index-B7osrXqy.js → index-Bissefox.js} +3 -3
  110. package/src/assets/web-panel/assets/{index-DW4BWjTk.js → index-BkvcEo4p.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DdkIhfi-.js → index-Bos5_aMV.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DTxNy0ZU.js → index-C1C9i79A.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-QBSeNVO3.js → index-CAhHirVH.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-D4LfPvWA.js → index-CNIxXMmN.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C_enk9I_.js → index-CW93tZDv.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-WE08gMJP.js → index-CWEuZWCK.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-r2VI1Ikk.js → index-Cp6e-hGw.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Dap9c3eD.js → index-CpjZhgNd.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DWdqx7zI.js → index-CzmQElZy.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BGbPnC5x.js → index-DCn8IcxV.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DiNUEpSQ.js → index-DE76D-kA.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DtkFqPVA.js → index-DaIz0NAW.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CGaXUsIs.js → index-Da_WBdLX.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B3ZX81mK.js → index-Dz49VOyQ.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BK3TiKVq.js → index-Q2SCX-MI.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CJmIQ9XR.js → index-RD6O-0C6.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CvrCCqJt.js → index-SQqOlR3d.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BI5hP_qy.js → index-ZFbps9po.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DeSgw-WV.js → index-ZxtP8jG7.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DMGmg497.js → index-bP_VlDb4.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BfFCQHL3.js → index-kf57LJ93.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-OSzjTzGM.js → index-oxRrmFO9.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-C2JvmYRc.js → index-p1_8SbI1.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-Dd7n7VgI.js → initDefaultProps-BWJktcg8.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BwxouRw1.js → motion-BEXBzUFV.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DObN0PG9.js → move-BUKwn2sx.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-B3nOUQc7.js → mtc-parser-FTI7btqQ.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-o9WU2Nbl.js → omit-CsLtSPH3.js} +1 -1
  139. package/src/assets/web-panel/assets/parsers-YNLm7sQ0.js +4 -0
  140. package/src/assets/web-panel/assets/{pickAttrs-CL_vvfQ_.js → pickAttrs-DWZxsT_S.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-CYrM2tyK.js → placementArrow-Bcfs43CX.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-k_06cQ96.js → responsiveObserve-a9bcC1p-.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-CPKG0pxS.js → slide-BDTzgOXN.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-BDDEeLTw.js → statusUtils-Iq1j_HUg.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-Cmb7RTh7.js → styleChecker-DAZEYxeh.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-e2IedC4N.js → useFlexGapSupport-DVkjyDrh.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-CE_gxVnm.js → useFs-wXTpNTHt.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-BkMIIKMz.js → usePersonalDataHub-CtQM_aZr.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-DW886XV1.js → vnode-hjEoG85v.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-Dwo_adaf.js → zoom-BHVpyKg_.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +3 -3
  153. package/src/commands/agent.js +14 -2
  154. package/src/commands/artifacts.js +235 -0
  155. package/src/commands/background-session.js +100 -10
  156. package/src/commands/cowork.js +86 -57
  157. package/src/commands/mcp.js +5 -0
  158. package/src/commands/plugin.js +145 -29
  159. package/src/commands/remote-control.js +295 -110
  160. package/src/commands/session-location.js +20 -1
  161. package/src/commands/session-message.js +171 -0
  162. package/src/commands/session.js +75 -69
  163. package/src/data/changelog.json +29 -3
  164. package/src/gateways/ws/message-dispatcher.js +57 -0
  165. package/src/harness/mcp-client.js +141 -10
  166. package/src/lib/artifact-access-ledger.js +1 -0
  167. package/src/lib/artifact-deletion-ledger.js +1 -0
  168. package/src/lib/artifact-recovery.js +970 -0
  169. package/src/lib/artifact-workbench-projection.js +171 -0
  170. package/src/lib/background-session-transport.js +153 -27
  171. package/src/lib/dynamic-workflow-runtime.js +408 -1
  172. package/src/lib/execution-location-target.js +4 -2
  173. package/src/lib/mcp-client.js +2 -0
  174. package/src/lib/mcp-headers-helper.js +47 -5
  175. package/src/lib/mcp-oauth.js +55 -6
  176. package/src/lib/mcp-stdio-executable-identity.js +16 -0
  177. package/src/lib/mcp-stdio-native-code-policy.js +74 -0
  178. package/src/lib/mcp-stdio-package-materialization.js +12 -3
  179. package/src/lib/mcp-tls.js +230 -0
  180. package/src/lib/output-styles.js +12 -0
  181. package/src/lib/plugin-runtime/install.js +614 -71
  182. package/src/lib/plugin-runtime/marketplace-archive-source.js +331 -0
  183. package/src/lib/plugin-runtime/marketplace-catalog.js +52 -6
  184. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
  185. package/src/lib/plugin-runtime/marketplace-source-adapter.js +163 -0
  186. package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
  187. package/src/lib/plugin-runtime/remote-source.js +435 -55
  188. package/src/lib/plugin-security.js +393 -41
  189. package/src/lib/plugin-source-identity.js +850 -0
  190. package/src/lib/process-execution-broker/index.js +112 -0
  191. package/src/lib/process-execution-broker/platform-sandbox.js +57 -3
  192. package/src/lib/process-execution-broker/workspace-transaction.js +26 -0
  193. package/src/lib/remote-approval-bridge.js +136 -46
  194. package/src/lib/remote-control.js +217 -31
  195. package/src/lib/session-message-fabric.js +1105 -0
  196. package/src/lib/session-projection.js +171 -12
  197. package/src/lib/session-runtime-retention.js +155 -0
  198. package/src/lib/settings-loader.cjs +29 -0
  199. package/src/repl/agent-repl.js +51 -14
  200. package/src/repl/permission-prompt.js +31 -8
  201. package/src/repl/repl-keybindings.js +55 -0
  202. package/src/runtime/agent-core.js +63 -2
  203. package/src/runtime/headless-runner.js +47 -66
  204. package/src/runtime/headless-stream.js +18 -24
  205. package/src/runtime/mcp-config.js +2 -0
  206. package/src/runtime/policies/agent-policy.js +2 -0
  207. package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +0 -1
  208. package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +0 -1
  209. package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +0 -1
  210. package/src/assets/web-panel/assets/Providers-Dz-CAYYU.js +0 -1
  211. package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +0 -1
  212. package/src/assets/web-panel/assets/index-Bizb4hkw.js +0 -1
  213. package/src/assets/web-panel/assets/index-OHSl-xCh.js +0 -1
  214. package/src/assets/web-panel/assets/parsers-UEvh_ShA.js +0 -4
@@ -71,6 +71,10 @@ import {
71
71
  consumeMcpStdioExecutableIdentityAuthority,
72
72
  MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND,
73
73
  } from "../mcp-stdio-executable-identity.js";
74
+ import {
75
+ isMcpStdioCapsuleNativeCodePolicy,
76
+ mcpStdioCapsuleNativeCodePolicyDigest,
77
+ } from "../mcp-stdio-native-code-policy.js";
74
78
 
75
79
  const SUPPORTED_SANDBOX_BOUNDARIES = new Set(Object.values(SANDBOX_BOUNDARIES));
76
80
  const SUPPORTED_SANDBOX_PROFILES = new Set([
@@ -1193,6 +1197,7 @@ class ProcessExecutionBroker extends EventEmitter {
1193
1197
  "rootIdentity",
1194
1198
  "entryIdentity",
1195
1199
  "runtimeIdentity",
1200
+ "nativeCodePolicy",
1196
1201
  ]);
1197
1202
  const unsupportedKey = Object.keys(raw).find(
1198
1203
  (key) => !supportedKeys.has(key),
@@ -1213,6 +1218,15 @@ class ProcessExecutionBroker extends EventEmitter {
1213
1218
  ) {
1214
1219
  return invalid("unsupported sandboxExecutionContract kind or version");
1215
1220
  }
1221
+ if (
1222
+ (mcpCapsule &&
1223
+ !isMcpStdioCapsuleNativeCodePolicy(raw.nativeCodePolicy)) ||
1224
+ (!mcpCapsule && raw.nativeCodePolicy !== undefined)
1225
+ ) {
1226
+ return invalid(
1227
+ "sandboxExecutionContract native-code policy is invalid for this contract kind",
1228
+ );
1229
+ }
1216
1230
  if (
1217
1231
  options.shell !== false ||
1218
1232
  (!mcpCapsule &&
@@ -1303,6 +1317,11 @@ class ProcessExecutionBroker extends EventEmitter {
1303
1317
  rootIdentity,
1304
1318
  entryIdentity: this._freezeExecutableIdentity(entryIdentity),
1305
1319
  runtimeIdentity: this._freezeExecutableIdentity(runtimeIdentity),
1320
+ ...(mcpCapsule
1321
+ ? {
1322
+ nativeCodePolicy: Object.freeze({ ...raw.nativeCodePolicy }),
1323
+ }
1324
+ : {}),
1306
1325
  });
1307
1326
  }
1308
1327
 
@@ -1789,6 +1808,13 @@ class ProcessExecutionBroker extends EventEmitter {
1789
1808
  "helperTeamIdentifier",
1790
1809
  "helperPackageIdentifier",
1791
1810
  "helperPackageVersion",
1811
+ "nativeCodePolicySchema",
1812
+ "nativeCodePolicyMode",
1813
+ "capsuleNativeCodeFormat",
1814
+ "nativeAddonLoading",
1815
+ "nativeAddonDenialMechanism",
1816
+ "hostRuntimeSharedLibraries",
1817
+ "anonymousExecutableMemory",
1792
1818
  ]) {
1793
1819
  if (
1794
1820
  plan.runtimeProbe[field] !== undefined &&
@@ -1842,6 +1868,7 @@ class ProcessExecutionBroker extends EventEmitter {
1842
1868
  "processForkExplicitlyDenied",
1843
1869
  "sandboxExecLiveGateContract",
1844
1870
  "globalLaunchSerialization",
1871
+ "nativeCodePolicyBound",
1845
1872
  ]) {
1846
1873
  if (
1847
1874
  plan.runtimeProbe[field] !== undefined &&
@@ -1890,6 +1917,7 @@ class ProcessExecutionBroker extends EventEmitter {
1890
1917
  "helperDesignatedRequirementSha256",
1891
1918
  "installAttestationDigest",
1892
1919
  "planBindingDigest",
1920
+ "nativeCodePolicyDigest",
1893
1921
  ]) {
1894
1922
  if (
1895
1923
  plan.runtimeProbe[field] !== undefined &&
@@ -2347,6 +2375,75 @@ class ProcessExecutionBroker extends EventEmitter {
2347
2375
  "Successful dynamic ELF evidence requires a runtime pathname shared-library closure",
2348
2376
  );
2349
2377
  }
2378
+ const nativeCodeEvidenceFields = [
2379
+ "nativeCodePolicyBound",
2380
+ "nativeCodePolicySchema",
2381
+ "nativeCodePolicyDigest",
2382
+ "nativeCodePolicyMode",
2383
+ "capsuleNativeCodeFormat",
2384
+ "nativeAddonLoading",
2385
+ "nativeAddonDenialMechanism",
2386
+ "hostRuntimeSharedLibraries",
2387
+ "anonymousExecutableMemory",
2388
+ ];
2389
+ const nativeAddonLoadingGuaranteed = guarantees.includes(
2390
+ SANDBOX_BOUNDARIES.NATIVE_ADDON_LOADING,
2391
+ );
2392
+ const capsuleExecutionContract = launchContext.executionContract;
2393
+ const expectedNativeCodePolicy =
2394
+ capsuleExecutionContract?.nativeCodePolicy;
2395
+ const nativeCodePolicyEvidenceValid =
2396
+ nativeAddonLoadingGuaranteed &&
2397
+ guarantees.includes(SANDBOX_BOUNDARIES.CODE_SNAPSHOT) &&
2398
+ capsuleExecutionContract?.kind ===
2399
+ MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND &&
2400
+ isMcpStdioCapsuleNativeCodePolicy(expectedNativeCodePolicy) &&
2401
+ plan.runtimeProbe.nativeCodePolicyBound === true &&
2402
+ plan.runtimeProbe.nativeCodePolicySchema ===
2403
+ expectedNativeCodePolicy.schema &&
2404
+ plan.runtimeProbe.nativeCodePolicyDigest ===
2405
+ mcpStdioCapsuleNativeCodePolicyDigest(expectedNativeCodePolicy) &&
2406
+ plan.runtimeProbe.nativeCodePolicyMode ===
2407
+ expectedNativeCodePolicy.mode &&
2408
+ plan.runtimeProbe.capsuleNativeCodeFormat ===
2409
+ expectedNativeCodePolicy.capsuleFormat &&
2410
+ plan.runtimeProbe.nativeAddonLoading ===
2411
+ expectedNativeCodePolicy.nativeAddonLoading &&
2412
+ plan.runtimeProbe.nativeAddonDenialMechanism ===
2413
+ expectedNativeCodePolicy.nativeAddonDenialMechanism &&
2414
+ plan.runtimeProbe.hostRuntimeSharedLibraries ===
2415
+ expectedNativeCodePolicy.hostRuntimeSharedLibraries &&
2416
+ plan.runtimeProbe.anonymousExecutableMemory ===
2417
+ expectedNativeCodePolicy.anonymousExecutableMemory &&
2418
+ plan.runtimeProbe.sharedLibraryClosure === false;
2419
+ if (nativeAddonLoadingGuaranteed && !nativeCodePolicyEvidenceValid) {
2420
+ throw this._sandboxError(
2421
+ "invalid_sandbox_plan",
2422
+ "Native-addon loading guarantee requires the exact deny-native-addons capsule policy",
2423
+ );
2424
+ }
2425
+ if (
2426
+ !nativeAddonLoadingGuaranteed &&
2427
+ nativeCodeEvidenceFields.some(
2428
+ (field) => plan.runtimeProbe[field] !== undefined,
2429
+ )
2430
+ ) {
2431
+ throw this._sandboxError(
2432
+ "invalid_sandbox_plan",
2433
+ "Native-addon policy evidence requires its sandbox guarantee",
2434
+ );
2435
+ }
2436
+ if (
2437
+ capsuleExecutionContract?.kind ===
2438
+ MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND &&
2439
+ guarantees.includes(SANDBOX_BOUNDARIES.CODE_SNAPSHOT) &&
2440
+ !nativeCodePolicyEvidenceValid
2441
+ ) {
2442
+ throw this._sandboxError(
2443
+ "invalid_sandbox_plan",
2444
+ "MCP capsule code snapshots require a bound native-addon denial policy",
2445
+ );
2446
+ }
2350
2447
  const codeSnapshotGuaranteed = guarantees.includes(
2351
2448
  SANDBOX_BOUNDARIES.CODE_SNAPSHOT,
2352
2449
  );
@@ -3099,6 +3196,21 @@ class ProcessExecutionBroker extends EventEmitter {
3099
3196
  }
3100
3197
  : {}),
3101
3198
  };
3199
+ for (const field of [
3200
+ "nativeCodePolicyBound",
3201
+ "nativeCodePolicySchema",
3202
+ "nativeCodePolicyDigest",
3203
+ "nativeCodePolicyMode",
3204
+ "capsuleNativeCodeFormat",
3205
+ "nativeAddonLoading",
3206
+ "nativeAddonDenialMechanism",
3207
+ "hostRuntimeSharedLibraries",
3208
+ "anonymousExecutableMemory",
3209
+ ]) {
3210
+ if (plan.runtimeProbe[field] !== undefined) {
3211
+ runtimeProbe[field] = plan.runtimeProbe[field];
3212
+ }
3213
+ }
3102
3214
  for (const field of [
3103
3215
  "pluginTreeContentSnapshot",
3104
3216
  ...pluginTreeEvidenceFields,
@@ -48,6 +48,11 @@ import {
48
48
  macosMcpLauncherPolicyDigest,
49
49
  verifyMacosMcpLauncherInstallContract,
50
50
  } from "./macos-mcp-launcher-contract.js";
51
+ import {
52
+ isMcpStdioCapsuleNativeCodePolicy,
53
+ mcpStdioCapsuleNativeCodeEvidence,
54
+ mcpStdioCapsuleNativeCodePolicyDigest,
55
+ } from "../mcp-stdio-native-code-policy.js";
51
56
 
52
57
  export {
53
58
  MCP_STDIO_FD_ENTRY_BOOTSTRAP,
@@ -68,6 +73,7 @@ export const SANDBOX_BOUNDARIES = Object.freeze({
68
73
  PROCESS_TREE: "process-tree",
69
74
  RESOURCE_LIMITS: "resource-limits",
70
75
  PRIVILEGE_REDUCTION: "privilege-reduction",
76
+ NATIVE_ADDON_LOADING: "native-addon-loading",
71
77
  });
72
78
 
73
79
  /**
@@ -1663,6 +1669,7 @@ function validateMacMcpCapsuleContract(
1663
1669
  if (
1664
1670
  contract?.kind !== MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND ||
1665
1671
  contract.contractVersion !== 1 ||
1672
+ !isMcpStdioCapsuleNativeCodePolicy(contract.nativeCodePolicy) ||
1666
1673
  (sync !== true && sync !== false) ||
1667
1674
  spawnOpts?.shell !== false ||
1668
1675
  !linuxStdioIsNarrow(spawnOpts?.stdio) ||
@@ -1774,6 +1781,9 @@ function applyMacMcpCapsuleCodeSnapshot(
1774
1781
  });
1775
1782
  }
1776
1783
 
1784
+ const nativeCodeEvidence = mcpStdioCapsuleNativeCodeEvidence(
1785
+ contract.nativeCodePolicy,
1786
+ );
1777
1787
  const protocol = MACOS_MCP_LAUNCHER_INPUTS.protocol;
1778
1788
  const legacyUnavailable = () =>
1779
1789
  unavailable("macos_atomic_runtime_exec_unavailable", {
@@ -1894,6 +1904,7 @@ function applyMacMcpCapsuleCodeSnapshot(
1894
1904
  entryFd: entryPin.fd,
1895
1905
  capsuleRootFd: capsulePin.fd,
1896
1906
  callerLifelineFd: protocol.callerLifelineFd,
1907
+ nativeCodePolicyDigest: nativeCodeEvidence.nativeCodePolicyDigest,
1897
1908
  }),
1898
1909
  )
1899
1910
  .digest("hex");
@@ -1940,6 +1951,7 @@ function applyMacMcpCapsuleCodeSnapshot(
1940
1951
  entrySnapshotBootstrapSha256:
1941
1952
  MCP_STDIO_MACOS_GATED_ENTRY_BOOTSTRAP_SHA256,
1942
1953
  sharedLibraryClosure: false,
1954
+ ...nativeCodeEvidence,
1943
1955
  rootProtectedRuntimeSnapshot: true,
1944
1956
  entryRootOwnedAnonymousSnapshot: true,
1945
1957
  entrySourcePrePostStat: true,
@@ -1996,6 +2008,7 @@ function applyMacMcpCapsuleCodeSnapshot(
1996
2008
  SANDBOX_BOUNDARIES.PROCESS_EXEC,
1997
2009
  SANDBOX_BOUNDARIES.PROCESS_TREE,
1998
2010
  SANDBOX_BOUNDARIES.PRIVILEGE_REDUCTION,
2011
+ SANDBOX_BOUNDARIES.NATIVE_ADDON_LOADING,
1999
2012
  ],
2000
2013
  cleanup,
2001
2014
  });
@@ -4039,6 +4052,15 @@ function windowsPluginNodeEntrySnapshot(invocation, sandboxOpts, spawnOpts) {
4039
4052
  reason: "windows_plugin_execution_contract_unsupported",
4040
4053
  };
4041
4054
  }
4055
+ if (
4056
+ capsuleContract &&
4057
+ !isMcpStdioCapsuleNativeCodePolicy(contract.nativeCodePolicy)
4058
+ ) {
4059
+ return {
4060
+ locks: null,
4061
+ reason: "windows_mcp_native_code_policy_invalid",
4062
+ };
4063
+ }
4042
4064
  if (
4043
4065
  (!capsuleContract && sandboxOpts.sync !== true) ||
4044
4066
  spawnOpts?.detached === true
@@ -4189,6 +4211,9 @@ function windowsAppContainerPolicyDigest({
4189
4211
  runtimeLaunchMechanism:
4190
4212
  "filter-oplock-locked-createprocess-suspended-image-v1",
4191
4213
  sharedLibraryClosure: false,
4214
+ nativeCodePolicyDigest: mcpStdioCapsuleNativeCodePolicyDigest(
4215
+ executionContract.nativeCodePolicy,
4216
+ ),
4192
4217
  }
4193
4218
  : {}),
4194
4219
  launchPathLocks: snapshotLocks,
@@ -4241,6 +4266,7 @@ export function applyWindowsSandbox(
4241
4266
  const appContainerSupportedBoundaries = [
4242
4267
  ...appContainerGuarantees,
4243
4268
  SANDBOX_BOUNDARIES.CODE_SNAPSHOT,
4269
+ SANDBOX_BOUNDARIES.NATIVE_ADDON_LOADING,
4244
4270
  ];
4245
4271
  const base = {
4246
4272
  platform: runtime.platform,
@@ -4859,6 +4885,9 @@ export function applyWindowsSandbox(
4859
4885
  runtimeLaunchMechanism:
4860
4886
  "filter-oplock-locked-createprocess-suspended-image-v1",
4861
4887
  sharedLibraryClosure: false,
4888
+ ...mcpStdioCapsuleNativeCodeEvidence(
4889
+ sandboxOpts.executionContract.nativeCodePolicy,
4890
+ ),
4862
4891
  planBindingMechanism: "windows-mcp-code-snapshot-plan-binding-v1",
4863
4892
  planBindingDigest: windowsMcpPlanBinding.planBindingDigest,
4864
4893
  }
@@ -4884,7 +4913,10 @@ export function applyWindowsSandbox(
4884
4913
  ...(entrySnapshot.locks &&
4885
4914
  sandboxOpts.executionContract?.kind ===
4886
4915
  MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND
4887
- ? [SANDBOX_BOUNDARIES.CODE_SNAPSHOT]
4916
+ ? [
4917
+ SANDBOX_BOUNDARIES.CODE_SNAPSHOT,
4918
+ SANDBOX_BOUNDARIES.NATIVE_ADDON_LOADING,
4919
+ ]
4888
4920
  : []),
4889
4921
  ],
4890
4922
  command: helperInvocation.command,
@@ -5402,6 +5434,12 @@ function validateLinuxPluginContract(
5402
5434
  ) {
5403
5435
  return { ok: false, reason: "execution_contract_missing" };
5404
5436
  }
5437
+ if (
5438
+ capsuleContract &&
5439
+ !isMcpStdioCapsuleNativeCodePolicy(contract.nativeCodePolicy)
5440
+ ) {
5441
+ return { ok: false, reason: "mcp_native_code_policy_invalid" };
5442
+ }
5405
5443
  if (
5406
5444
  (sync !== true && sync !== false) ||
5407
5445
  spawnOpts?.shell !== false ||
@@ -6173,6 +6211,9 @@ function applyLinuxMcpCapsuleCodeSnapshot(
6173
6211
  entrySnapshotSha256: entrySnapshot.attestation.sha256,
6174
6212
  entrySnapshotBytes: entrySnapshot.attestation.bytes,
6175
6213
  });
6214
+ const nativeCodeEvidence = mcpStdioCapsuleNativeCodeEvidence(
6215
+ contract.nativeCodePolicy,
6216
+ );
6176
6217
  const policyDigest = sha256(
6177
6218
  JSON.stringify({
6178
6219
  version: 1,
@@ -6186,6 +6227,7 @@ function applyLinuxMcpCapsuleCodeSnapshot(
6186
6227
  entrySnapshotBootstrapSha256: MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256,
6187
6228
  runtimeLaunchMechanism: "inherited-executable-fd-v1",
6188
6229
  passthroughArgsDigest: sha256(JSON.stringify(invocation.args.slice(1))),
6230
+ nativeCodePolicyDigest: nativeCodeEvidence.nativeCodePolicyDigest,
6189
6231
  }),
6190
6232
  );
6191
6233
  let closed = false;
@@ -6218,10 +6260,14 @@ function applyLinuxMcpCapsuleCodeSnapshot(
6218
6260
  runtimeLaunchMechanism: "inherited-executable-fd-v1",
6219
6261
  entrySnapshotBootstrapSha256: MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256,
6220
6262
  sharedLibraryClosure: false,
6263
+ ...nativeCodeEvidence,
6221
6264
  ...snapshotIdentity,
6222
6265
  },
6223
6266
  reason: null,
6224
- guarantees: [SANDBOX_BOUNDARIES.CODE_SNAPSHOT],
6267
+ guarantees: [
6268
+ SANDBOX_BOUNDARIES.CODE_SNAPSHOT,
6269
+ SANDBOX_BOUNDARIES.NATIVE_ADDON_LOADING,
6270
+ ],
6225
6271
  command: `/proc/self/fd/${runtimeFd}`,
6226
6272
  args: [
6227
6273
  "-e",
@@ -9694,6 +9740,9 @@ export function applyLinuxSandbox(
9694
9740
  runtimeLaunchMechanism:
9695
9741
  "bwrap-descriptor-mount-node-runtime-exec-v1",
9696
9742
  sharedLibraryClosure: false,
9743
+ ...mcpStdioCapsuleNativeCodeEvidence(
9744
+ validation.contract.nativeCodePolicy,
9745
+ ),
9697
9746
  runtimeSnapshotSha256: validation.contract.runtimeIdentity.sha256,
9698
9747
  runtimeSnapshotBytes: validation.contract.runtimeIdentity.bytes,
9699
9748
  entrySnapshotSha256: entrySnapshot.attestation.sha256,
@@ -9749,7 +9798,12 @@ export function applyLinuxSandbox(
9749
9798
  SANDBOX_BOUNDARIES.FILESYSTEM,
9750
9799
  SANDBOX_BOUNDARIES.NETWORK,
9751
9800
  SANDBOX_BOUNDARIES.PROCESS_TREE,
9752
- ...(capsuleContract ? [SANDBOX_BOUNDARIES.CODE_SNAPSHOT] : []),
9801
+ ...(capsuleContract
9802
+ ? [
9803
+ SANDBOX_BOUNDARIES.CODE_SNAPSHOT,
9804
+ SANDBOX_BOUNDARIES.NATIVE_ADDON_LOADING,
9805
+ ]
9806
+ : []),
9753
9807
  ],
9754
9808
  command: invocation.command,
9755
9809
  args: invocation.args,
@@ -102,6 +102,8 @@ const WORKSPACE_LOCK_OWNER_KEYS = Object.freeze([
102
102
  const WORKSPACE_LOCK_RETENTION_REQUEST = Symbol(
103
103
  "workspace-lock-retention-request",
104
104
  );
105
+ const WORKSPACE_COORDINATION_TOKEN_PATTERN = /^[A-Za-z0-9-]{16,128}$/;
106
+ const WORKSPACE_COORDINATION_RECLAIM_TOKENS_PATTERN = /^[A-Za-z0-9-]{33,257}$/;
105
107
  const TERMINAL_STATES = new Set([
106
108
  WORKSPACE_TRANSACTION_STATE.COMMITTED,
107
109
  WORKSPACE_TRANSACTION_STATE.ROLLED_BACK,
@@ -1669,6 +1671,24 @@ function workspacesOverlap(left, right) {
1669
1671
  return isPathInside(left, right) || isPathInside(right, left);
1670
1672
  }
1671
1673
 
1674
+ function isWorkspaceCoordinationDebris(name) {
1675
+ for (const lifecycle of ["acquire", "release"]) {
1676
+ const prefix = `coordination.lock.${lifecycle}-`;
1677
+ if (name.startsWith(prefix)) {
1678
+ return WORKSPACE_COORDINATION_TOKEN_PATTERN.test(
1679
+ name.slice(prefix.length),
1680
+ );
1681
+ }
1682
+ }
1683
+ const reclaimedPrefix = "coordination.lock.reclaimed-";
1684
+ return (
1685
+ name.startsWith(reclaimedPrefix) &&
1686
+ WORKSPACE_COORDINATION_RECLAIM_TOKENS_PATTERN.test(
1687
+ name.slice(reclaimedPrefix.length),
1688
+ )
1689
+ );
1690
+ }
1691
+
1672
1692
  function assertNoOverlappingWorkspaceLock(
1673
1693
  lockDir,
1674
1694
  workspaceRoot,
@@ -1695,6 +1715,12 @@ function assertNoOverlappingWorkspaceLock(
1695
1715
  { path: candidate },
1696
1716
  );
1697
1717
  }
1718
+ // withFileLock atomically detaches its private coordination directory
1719
+ // before recursive cleanup. Windows can temporarily retain that uniquely
1720
+ // tokened directory after the coordination lock itself is already free.
1721
+ // It contains state-lock ownership, not workspace ownership, and therefore
1722
+ // must not be interpreted as an overlapping workspace lease.
1723
+ if (isWorkspaceCoordinationDebris(name)) continue;
1698
1724
  const owner = readLockOwner(candidate);
1699
1725
  if (!owner) {
1700
1726
  throw codedError(
@@ -43,11 +43,15 @@ import {
43
43
  REMOTE_MEMBERSHIP_HOST_UNAVAILABLE_CODE,
44
44
  } from "./remote-membership-host-store.js";
45
45
  import {
46
+ assertDirectWsUrlAllowed,
46
47
  buildDirectPairingUri,
48
+ isLoopbackBindHost,
47
49
  pickLanAddress,
48
50
  renderQrCode,
49
51
  resolveRemoteControlOptions,
52
+ resolveRemoteControlWsUrl,
50
53
  } from "./remote-control.js";
54
+ import { logger } from "./logger.js";
51
55
 
52
56
  const DEFAULT_DECISION_TIMEOUT_MS = 5 * 60 * 1000;
53
57
  const REMOTE_LEASE_AUTHORIZATION_KIND =
@@ -345,7 +349,8 @@ export class RemoteApprovalBridge {
345
349
 
346
350
  /**
347
351
  * Pairing descriptor for devices. Relay-configured servers return a ready
348
- * E2EE URI; otherwise build the direct-LAN URI from the caller's endpoint.
352
+ * E2EE URI; otherwise build a direct URI from the explicitly selected
353
+ * loopback/LAN endpoint.
349
354
  */
350
355
  pairingInfo({ lanWsUrl = null } = {}) {
351
356
  if (!this.pairing) return null;
@@ -363,6 +368,7 @@ export class RemoteApprovalBridge {
363
368
  });
364
369
  return {
365
370
  uri,
371
+ mode: this.pairing.uri ? "relay" : "direct",
366
372
  remoteSessionId: this.remoteSessionId,
367
373
  scopes: this.pairing.scopes,
368
374
  expiresAt: this.pairing.expiresAt,
@@ -1297,21 +1303,60 @@ export class RemoteApprovalBridge {
1297
1303
  * self-hosts a lightweight WS server on an OS-assigned port (no sessionManager
1298
1304
  * needed — the session stays client-hosted), starts a bridge for this run's
1299
1305
  * session, prints the pairing URI (+ optional QR) to stderr, and returns the
1300
- * gate confirmer + teardown. Relay settings (env/config) flow through, so a
1301
- * configured relay yields an E2EE pairing URI reaching devices off-LAN.
1306
+ * gate confirmer + teardown. Relay settings flow through the startup
1307
+ * environment; LAN exposure remains a separate explicit caller authority.
1302
1308
  */
1303
1309
  export async function startHeadlessRemoteApproval({
1304
1310
  agentSessionId,
1311
+ allowLan = false,
1305
1312
  env = process.env,
1306
- config = {},
1313
+ config = undefined,
1307
1314
  writeErr = () => {},
1308
1315
  isText = false,
1309
1316
  decisionTimeoutMs = undefined,
1310
1317
  deps = {},
1311
1318
  } = {}) {
1319
+ let effectiveConfig = config;
1320
+ if (effectiveConfig === undefined) {
1321
+ try {
1322
+ const configModule =
1323
+ deps.configModule || (await import("./config-manager.js"));
1324
+ effectiveConfig = configModule.loadConfig();
1325
+ } catch {
1326
+ effectiveConfig = {};
1327
+ }
1328
+ }
1329
+ const resolved = resolveRemoteControlOptions({
1330
+ flags: { allowApprove: true, allowLan: allowLan === true },
1331
+ env,
1332
+ config: effectiveConfig || {},
1333
+ });
1334
+ if (!resolved.relayUrl && !resolved.lanAccessible) {
1335
+ throw new Error(
1336
+ "remote approval is loopback-only by default; configure a relay, or enable " +
1337
+ "allowLan with a non-loopback host (omit host to use 0.0.0.0) on a trusted network",
1338
+ );
1339
+ }
1340
+ const lanAddress = resolved.lanAccessible
1341
+ ? deps.lanAddress ||
1342
+ (deps.pickLanAddress ? deps.pickLanAddress() : pickLanAddress())
1343
+ : null;
1344
+ const preflightLanWsUrl = resolveRemoteControlWsUrl(
1345
+ { ...resolved, port: 1 },
1346
+ { lanAddress },
1347
+ );
1348
+ if (
1349
+ !resolved.relayUrl &&
1350
+ resolved.lanAccessible &&
1351
+ isLoopbackBindHost(new URL(preflightLanWsUrl).hostname)
1352
+ ) {
1353
+ throw new Error(
1354
+ "LAN binding is enabled but no private LAN address can be advertised; " +
1355
+ "configure a private host or a relay",
1356
+ );
1357
+ }
1312
1358
  const { ChainlessChainWSServer } =
1313
1359
  deps.serverModule || (await import("../gateways/ws/ws-server.js"));
1314
- const resolved = resolveRemoteControlOptions({ flags: {}, env, config });
1315
1360
  const server = new ChainlessChainWSServer({
1316
1361
  port: 0,
1317
1362
  host: resolved.host,
@@ -1319,50 +1364,95 @@ export async function startHeadlessRemoteApproval({
1319
1364
  remoteSessionRelayUrl: resolved.relayUrl,
1320
1365
  remoteSessionPeerId: resolved.peerId,
1321
1366
  });
1322
- await server.start();
1323
- const bridge = new RemoteApprovalBridge({
1324
- wsUrl: `ws://127.0.0.1:${server.port}`,
1325
- token: resolved.token,
1326
- agentSessionId,
1327
- scopes: ["observe", "approve"],
1328
- decisionTimeoutMs,
1329
- });
1367
+ let bridge;
1330
1368
  try {
1331
- await bridge.start();
1369
+ await server.start();
1370
+ const createBridge =
1371
+ deps.createBridge || ((options) => new RemoteApprovalBridge(options));
1372
+ bridge = createBridge({
1373
+ wsUrl: resolveRemoteControlWsUrl(
1374
+ { ...resolved, port: server.port, allowLan: resolved.lanAccessible },
1375
+ { lanAddress: null },
1376
+ ),
1377
+ token: resolved.token,
1378
+ agentSessionId,
1379
+ scopes: ["observe", "approve"],
1380
+ decisionTimeoutMs,
1381
+ });
1382
+ try {
1383
+ await bridge.start();
1384
+ } catch (err) {
1385
+ await server.stop().catch(() => undefined);
1386
+ throw err;
1387
+ }
1388
+ const lanWsUrl = resolveRemoteControlWsUrl(
1389
+ { ...resolved, port: server.port },
1390
+ { lanAddress },
1391
+ );
1392
+ const pairing = bridge.pairingInfo({ lanWsUrl });
1393
+ if (resolved.relayUrl && pairing?.mode !== "relay") {
1394
+ throw new Error(
1395
+ "the configured relay did not provide a pairing URI; refusing to fall back to direct pairing",
1396
+ );
1397
+ }
1398
+ if (
1399
+ !resolved.relayUrl &&
1400
+ pairing?.mode !== "relay" &&
1401
+ !resolved.lanAccessible
1402
+ ) {
1403
+ throw new Error(
1404
+ "direct remote approval requires explicit allowLan on a trusted network",
1405
+ );
1406
+ }
1407
+ if (pairing?.mode === "direct") {
1408
+ if (isLoopbackBindHost(new URL(lanWsUrl).hostname)) {
1409
+ throw new Error(
1410
+ "LAN binding is enabled but no private LAN address can be advertised",
1411
+ );
1412
+ }
1413
+ assertDirectWsUrlAllowed(lanWsUrl);
1414
+ const warn = deps.warn || ((message) => logger.warn(message));
1415
+ warn(
1416
+ `LAN remote approval enabled on ${resolved.host}:${server.port}. ` +
1417
+ "Direct pairing uses plaintext ws:// with bearer credentials; use only on a trusted network.",
1418
+ );
1419
+ }
1420
+ if (!pairing?.uri) {
1421
+ throw new Error("remote approval did not produce a pairing URI");
1422
+ }
1423
+ if (isText) {
1424
+ writeErr(
1425
+ " remote-control: approvals can be answered from a paired device\n",
1426
+ );
1427
+ writeErr(` pairing: ${pairing.uri}\n`);
1428
+ const qr = await renderQrCode(pairing.uri, deps);
1429
+ if (qr) writeErr(qr + "\n");
1430
+ }
1431
+ return {
1432
+ pairing,
1433
+ server,
1434
+ bridge,
1435
+ confirmer: bridge.makeConfirmer({
1436
+ onAsk: (ask) => {
1437
+ if (isText) {
1438
+ writeErr(
1439
+ ` permission(${ask.tool || "?"}): waiting for remote approval…\n`,
1440
+ );
1441
+ }
1442
+ },
1443
+ }),
1444
+ consumeAuthorization: (authorization, context) =>
1445
+ bridge.consumeAuthorization(authorization, context),
1446
+ close: async () => {
1447
+ await bridge.close().catch(() => undefined);
1448
+ await server.stop().catch(() => undefined);
1449
+ },
1450
+ };
1332
1451
  } catch (err) {
1452
+ if (bridge) {
1453
+ await bridge.close().catch(() => undefined);
1454
+ }
1333
1455
  await server.stop().catch(() => undefined);
1334
1456
  throw err;
1335
1457
  }
1336
- const lanAddress = deps.lanAddress || pickLanAddress() || "127.0.0.1";
1337
- const pairing = bridge.pairingInfo({
1338
- lanWsUrl: `ws://${lanAddress}:${server.port}`,
1339
- });
1340
- if (isText) {
1341
- writeErr(
1342
- " remote-control: approvals can be answered from a paired device\n",
1343
- );
1344
- writeErr(` pairing: ${pairing.uri}\n`);
1345
- const qr = await renderQrCode(pairing.uri, deps);
1346
- if (qr) writeErr(qr + "\n");
1347
- }
1348
- return {
1349
- pairing,
1350
- server,
1351
- bridge,
1352
- confirmer: bridge.makeConfirmer({
1353
- onAsk: (ask) => {
1354
- if (isText) {
1355
- writeErr(
1356
- ` permission(${ask.tool || "?"}): waiting for remote approval…\n`,
1357
- );
1358
- }
1359
- },
1360
- }),
1361
- consumeAuthorization: (authorization, context) =>
1362
- bridge.consumeAuthorization(authorization, context),
1363
- close: async () => {
1364
- await bridge.close().catch(() => undefined);
1365
- await server.stop().catch(() => undefined);
1366
- },
1367
- };
1368
1458
  }