chainlesschain 0.162.154 → 0.162.155

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 (198) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-CIpoXyVI.js → AIOps-Cth_zZeA.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Ch_QXIPk.js → ActionButton-zfwA3Oxn.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-CvaBYShv.js → Analytics-FeKc4aU2.js} +2 -2
  7. package/src/assets/web-panel/assets/AppLayout-C6WIrDZk.js +9 -0
  8. package/src/assets/web-panel/assets/{Audit-Bng8eHCW.js → Audit-C2gRq5Qi.js} +1 -1
  9. package/src/assets/web-panel/assets/BackgroundAgents-Dq2340Kl.css +1 -0
  10. package/src/assets/web-panel/assets/BackgroundAgents-TeTIFiW-.js +1 -0
  11. package/src/assets/web-panel/assets/{Backup-DvBs8-lm.js → Backup-CLJk3k6i.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-DKwajm0g.js → BaseInput-5ms537NY.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-BoGpZ3uO.js → Chat-4Nr5U9PJ.js} +5 -5
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-BFEnA8LN.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-BMJEmxFQ.js → Checkbox-B2Bca5ev.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-Bsoag5CV.js → Codegen-D6Uysdbv.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-CDjH9-sp.js → Col-B3XQ8xc7.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-Cg2QHPQ6.js → Community-CqwKtoPz.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-BU2BaqP-.js → Compact-BZZINTus.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-D6ZYMCqZ.js → Compliance-Dr3BXkWA.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DaZ8VW-I.js → Cowork-rx_x9Wqc.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-DQY1RH9v.js → Cron-BpZr7rHc.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BVR28vk9.js → Crosschain-CeV5EYVg.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-N44KvTP-.js → DID-fe2Qaowz.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-Bjhlx6wS.js → Dashboard-Fh_EdWVa.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-CpLYxpy1.js → Dropdown-N1PIwWYf.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-srEITSPI.js → EmailListRenderer-B6vK93Ed.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-juOC_fX2.js → FamilyGuardDashboard-e3iUZhAg.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-nRaQtaXW.js → Federation-uInc9YcN.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-DflhTj09.js → FormItemContext-DmQZSX0z.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-DulI_3NI.js → GenericCardRenderer-CV_Z8OAo.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-BxOPefXn.js → Git-jXAhNN8d.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-C6G_Wnwi.js → Governance-Blw1esBB.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-BnFYa2BF.js → Inference-CoK8Ia0Z.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-TqQLEabm.js → KnowledgeGraph-5xrglMTK.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BH4AVh7s.js → Logs-BGIG9798.js} +2 -2
  37. package/src/assets/web-panel/assets/{Marketplace-DoWLTaEE.js → Marketplace-DFqhJMuY.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-D6yC7OG_.js → McpTools-7U8tEhwK.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-DLCeHj21.js → Memory-BrvboPLE.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-BZBE0GRl.js → MobileBridge-nzcd7d1H.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-DJA15G1J.js → MobileProjects-DETVNKa7.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-CyTKJDHn.js → Mtc-SZcCYec3.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-DZKjgEVc.js → MtcAudit-Cx_d3N17.js} +5 -5
  44. package/src/assets/web-panel/assets/{Multisig-2rMPNhSO.js → Multisig-DtcmSUo-.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-VeMk4Fx-.js → NLProgramming-Dgx2QHl-.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-jr2GHMhh.js → Notes-oHTGEHoJ.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-WN9VZyFb.js → NotificationSettings-qKD-IlOl.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-CjNO-e9T.js → OrderTableRenderer-CiU4Sy1J.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-DdjVHuzT.js → Organization-BBNXnltP.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-l8vBsGGq.js → Overflow-Df2fDbvT.js} +1 -1
  51. package/src/assets/web-panel/assets/{OverrideContext-BlgL9440.js → OverrideContext-C0w0P4A3.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-CVQDHHwq.js → P2P-DUHj6zjO.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-CTsNh8qE.js → PdhVaultBrowser-B2nWgdoV.js} +4 -4
  54. package/src/assets/web-panel/assets/{Permissions-DLW87uyK.js → Permissions-CiW0afny.js} +3 -3
  55. package/src/assets/web-panel/assets/{PersonalDataHub-N8OvASTx.js → PersonalDataHub-cc1mHU_D.js} +2 -2
  56. package/src/assets/web-panel/assets/{Pipeline-BPZmdlz2.js → Pipeline-B93zTapU.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-MFZw_GNh.js → Privacy-Db1jXWfd.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-zW0pEsfU.js → ProjectInit-DFoZ1h9U.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-CppVHr_o.js → ProjectSettings-BkkUnmYs.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-BTglkJO2.js → Projects-Xa779XLC.js} +1 -1
  61. package/src/assets/web-panel/assets/{Providers-CPHZ6JKT.js → Providers-DOF01IEN.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-DO4klNZ2.js → QrScannerModal-TohRsKd0.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-B37TTqUa.js → QuickAsk-DjLcr1ZU.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-n7KIfK7y.js → Recommend--Ism6bMx.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-SjVoS9mm.js → RemoteSession-B8CL3YHM.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-Dt6RTeHi.js → Reputation-CaRxB3Ep.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-Dty6jz5V.js → Row-BioxZBBH.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-DDLhqw5j.js → RssFeed-B2LipwH3.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-DrYbXi-W.js → Search-BpjLBV8u.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-Fc9Z6fbB.js → Security-TibHuJWP.js} +3 -3
  71. package/src/assets/web-panel/assets/{Services-BAhPgjSM.js → Services-DtkuWseW.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-8JF7EPfE.js → Skeleton-BIcPQae6.js} +1 -1
  73. package/src/assets/web-panel/assets/Skills-DoTWIQOX.js +1 -0
  74. package/src/assets/web-panel/assets/{Sla-CSvjcATc.js → Sla-uUSU185m.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-Demx7Gnx.js → SpeechSettings-B7Bgc9mk.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-CKLnRWJr.js → SyncSettings-K-XrJ2s9.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-mhITRxjv.js → Tasks-B3hEXNcI.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-AjQI5rxD.js → Templates-CaFnyGDK.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-dbtEh1yR.js → Tenant-C2X0PtEC.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-DX-9jjfk.js → Terminal-CKU5yVsT.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-C11EDZRt.js → TimelineRenderer-8trZeYDC.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-GiA9fiZn.js → Tokens-BUEEe9US.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BPcnclJn.js → Trigger-COU97I7Y.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-C-DVY8nq.js → Trust-DNWQChaP.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-DxodSwME.js → UkeySign-DWX_VtCp.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-D_WV0O8O.js → VideoEditing-D9eIDhsp.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-DSeNlVSU.js → Wallet-BOnCCgIL.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-Bwuuvpz1.js → WebAuthn-DN4dD-W8.js} +4 -4
  89. package/src/assets/web-panel/assets/{WorkflowEditor-8M96EU1N.js → WorkflowEditor-ClvDb_jX.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-D5S6SmLm.js → chat-DIxQ_Eeb.js} +1 -1
  91. package/src/assets/web-panel/assets/{collapseMotion-DVfhbsdP.js → collapseMotion-CqM1QOwM.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-5fbnGkAP.js → colors-BOX3WKcP.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-DaSuuMlW.js → compact-item-Bny82VDi.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-BNOHcXAF.js → createContext-Cnoy3enS.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-Dr9dzlQ5.js +1 -0
  96. package/src/assets/web-panel/assets/{echarts-CL0F2SnX.js → echarts-C61Vn3mP.js} +1 -1
  97. package/src/assets/web-panel/assets/{hasIn-DONwZUra.js → hasIn-BJ7fkx3F.js} +1 -1
  98. package/src/assets/web-panel/assets/{icons-BsDmGpcT.js → icons-DEmcnxBf.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-B_yDD9Lm.js → index--CcGiwAI.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-5_fj5gVd.js → index--Xb2lq6E.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-uR5KTNZA.js → index-2N9Y1cLx.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-MOgO5Yvx.js → index-7GLXvG8p.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CLXf-8ek.js → index-B1-h2QrC.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-D7gs_3Ck.js → index-B6WoW8DJ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CztESwB4.js → index-B7Fah_BX.js} +1 -1
  106. package/src/assets/web-panel/assets/index-B92M3t6B.js +1 -0
  107. package/src/assets/web-panel/assets/{index-BEO5f7Vf.js → index-BDkN6YOI.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-_6S41ikV.js → index-BJ8-oLHI.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CrkCFohi.js → index-BM0mQhVv.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BrAJSu-v.js → index-BP7HFsAK.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BcqOK7Lv.js → index-BWPey0Uf.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BLzMuz38.js → index-BbZynivw.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DbdQBn4n.js → index-BcmXbIwv.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-KwV9g_cf.js → index-Bk5yST66.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-ClGqO-2X.js → index-Bvheiep2.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Dd7xwGcD.js → index-C--PPqwi.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DP2-3A_j.js → index-C1nHEOFE.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CAYmc5Nh.js → index-CEPTiBWy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-LRIt-AFl.js → index-CEq5Vh0i.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Dm5A-MN5.js → index-CN6emwbq.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Cf2OCa8q.js → index-CQ-xLCVb.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DHx2lhHW.js → index-CW6n-y1L.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DM3Ug61e.js → index-CZ6CHiCQ.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BJp4kRjw.js → index-CZCTan8Q.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-PsMErAB1.js → index-CnjtAKdw.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CEzd5m5a.js → index-D2qNdrNW.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-B4L3gsNA.js → index-D9QEc7Tq.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BcWMZqb4.js → index-DBhkJqVF.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CSfYJqXt.js → index-DSRIjAWO.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CmUOjyL6.js → index-DxX8hhmL.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-RkInlwV0.js → index-Kio1ekYg.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-CTZeOnzO.js → index-OaNFXV8N.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BK2N5yVW.js → index-Ty4w18mR.js} +4 -4
  134. package/src/assets/web-panel/assets/{index-Cn5LtyIH.js → index-ceH9DsAi.js} +1 -1
  135. package/src/assets/web-panel/assets/index-d2UcGpK-.js +1 -0
  136. package/src/assets/web-panel/assets/{index-xo2f4DZR.js → index-qOBKrw-g.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-vYtprlNs.js → index-rn6xd12s.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-CxwLynp9.js → initDefaultProps-BmxMBuUZ.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-XbPvGJbW.js → motion-DnXNwo2k.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-D1u5pcgS.js → move-WnIJwmZN.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-BDxhTBMH.js → mtc-parser-DAto-c2w.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-EtIFGg5k.js → omit-BtvW9W0H.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-D33sWb0l.js → pickAttrs-DFqI9-_C.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-BwFRzaGo.js → placementArrow-Dax6wOK3.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-CBk2WqvQ.js → responsiveObserve-BUFWWC83.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-B52HtrKn.js → slide-DyARyuSz.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-D4qxqWnH.js → statusUtils-BZSDugpi.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-BxaCFlOL.js → styleChecker-B7o_K-4R.js} +1 -1
  149. package/src/assets/web-panel/assets/useFlexGapSupport-Dg7Bg8VG.js +1 -0
  150. package/src/assets/web-panel/assets/{useFs-C6VAU1_6.js → useFs-DQYXo5Rk.js} +1 -1
  151. package/src/assets/web-panel/assets/{useMergedState-Dc9sO2d7.js → useMergedState-DXbJa_zE.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-rEG6oXG8.js → usePersonalDataHub-E8NBAnkq.js} +1 -1
  153. package/src/assets/web-panel/assets/{useRefs-CAeFRzPf.js → useRefs-BrCfMLMc.js} +1 -1
  154. package/src/assets/web-panel/assets/{useState-LwExUYDO.js → useState-CnxtbLoC.js} +1 -1
  155. package/src/assets/web-panel/assets/{vendor-BMxUs6UX.js → vendor-Bn1JzzAn.js} +1 -1
  156. package/src/assets/web-panel/assets/{vnode-DOTafkEi.js → vnode-DD71_w1b.js} +1 -1
  157. package/src/assets/web-panel/assets/{zoom-BuwwN5K2.js → zoom-zc9EMd3q.js} +1 -1
  158. package/src/assets/web-panel/index.html +3 -3
  159. package/src/command-manifest.json +36 -1
  160. package/src/commands/agent.js +23 -2
  161. package/src/commands/agents.js +62 -1
  162. package/src/commands/auto-mode.js +87 -0
  163. package/src/commands/background-session.js +499 -0
  164. package/src/commands/command.js +4 -1
  165. package/src/commands/permissions.js +49 -0
  166. package/src/commands/remote-control.js +407 -0
  167. package/src/data/changelog.json +7 -2
  168. package/src/gateways/ws/background-agent-protocol.js +337 -0
  169. package/src/gateways/ws/message-dispatcher.js +10 -0
  170. package/src/gateways/ws/remote-session-protocol.js +102 -1
  171. package/src/gateways/ws/ws-server.js +61 -0
  172. package/src/harness/mcp-client.js +8 -0
  173. package/src/index.js +10 -0
  174. package/src/lib/auto-mode-config.js +396 -0
  175. package/src/lib/background-agent-supervisor.js +202 -17
  176. package/src/lib/background-session-transport.js +315 -0
  177. package/src/lib/permission-denial-store.js +159 -0
  178. package/src/lib/remote-approval-bridge.js +360 -0
  179. package/src/lib/remote-control.js +270 -0
  180. package/src/lib/repl-denials.js +26 -0
  181. package/src/lib/shell-approval.js +4 -0
  182. package/src/lib/ws-rpc-client.js +150 -0
  183. package/src/repl/agent-repl.js +208 -16
  184. package/src/repl/permission-tier.js +31 -0
  185. package/src/runtime/agent-core.js +57 -0
  186. package/src/runtime/headless-runner.js +126 -8
  187. package/src/runtime/headless-stream.js +87 -1
  188. package/src/runtime/mcp-config.js +11 -0
  189. package/src/runtime/mcp-tool-search.js +0 -0
  190. package/src/runtime/policies/agent-policy.js +17 -0
  191. package/src/workers/background-agent-worker.js +218 -37
  192. package/src/assets/web-panel/assets/AppLayout-B6Q6olcr.js +0 -9
  193. package/src/assets/web-panel/assets/ChatBubbleRenderer-mDQl_PXO.js +0 -1
  194. package/src/assets/web-panel/assets/Skills-DL39dY0t.js +0 -1
  195. package/src/assets/web-panel/assets/devWarning-K_woVElh.js +0 -1
  196. package/src/assets/web-panel/assets/index-3ILvaBpo.js +0 -1
  197. package/src/assets/web-panel/assets/index-JpB-FoZq.js +0 -1
  198. package/src/assets/web-panel/assets/useFlexGapSupport-B1lWlqXN.js +0 -1
@@ -0,0 +1,337 @@
1
+ /**
2
+ * background-agent WS protocol — exposes the background-session transport to
3
+ * web-panel / IDE clients so a "background agents" panel can list sessions,
4
+ * live-follow their logs, and interactively take a session over (follow-up
5
+ * prompts / stop-turn), reusing the exact same NDJSON pipe protocol that
6
+ * `cc attach` speaks.
7
+ *
8
+ * Routes (request → reply):
9
+ * bg-list {all?} → {type:"bg-list", sessions}
10
+ * bg-view {bgId, lines?} → {type:"bg-view", session, log}
11
+ * bg-attach {bgId, lines?} → {type:"bg-attach", bgId, hello, log}
12
+ * bg-prompt {bgId, text} → {type:"bg-prompt", bgId, sent}
13
+ * bg-stop-turn {bgId} → {type:"bg-stop-turn", bgId, sent}
14
+ * bg-detach {bgId} → {type:"bg-detach", bgId}
15
+ * bg-stop {bgId} → {type:"bg-stop", session} (kill whole session)
16
+ *
17
+ * While attached, the server pushes unsolicited frames to that client:
18
+ * {type:"bg-event", bgId, event} worker lifecycle events (turn-started /
19
+ * turn-ended / idle / accepted / error /
20
+ * transport-closed)
21
+ * {type:"bg-log", bgId, chunk} log-file delta (500ms poll)
22
+ *
23
+ * The state file's `transport.token` is the local takeover capability — it is
24
+ * NEVER sent over WS. Sessions are serialized with `interactive: true/false`
25
+ * instead; the server process (same user) performs the pipe handshake itself.
26
+ */
27
+
28
+ import { existsSync, readFileSync } from "node:fs";
29
+
30
+ const LOG_POLL_MS = 500;
31
+
32
+ async function loadSupervisor() {
33
+ return import("../../lib/background-agent-supervisor.js");
34
+ }
35
+
36
+ /** Strip the takeover token before a session state crosses the WS boundary. */
37
+ export function sanitizeBackgroundSession(state) {
38
+ if (!state) return state;
39
+ const { transport, ...rest } = state;
40
+ return { ...rest, interactive: Boolean(transport?.pipe) };
41
+ }
42
+
43
+ function sendError(server, id, ws, code, message) {
44
+ server._send(ws, { id, type: "error", code, message });
45
+ }
46
+
47
+ function attachments(client) {
48
+ if (!client._bgAttachments) client._bgAttachments = new Map();
49
+ return client._bgAttachments;
50
+ }
51
+
52
+ function dropAttachment(client, bgId) {
53
+ const map = client?._bgAttachments;
54
+ const entry = map?.get(bgId);
55
+ if (!entry) return false;
56
+ map.delete(bgId);
57
+ clearInterval(entry.poll);
58
+ try {
59
+ entry.conn.close();
60
+ } catch {
61
+ /* worker side already gone */
62
+ }
63
+ return true;
64
+ }
65
+
66
+ /** Tear down every transport relay a client holds (called on ws close). */
67
+ export function cleanupBgAttachments(client) {
68
+ const map = client?._bgAttachments;
69
+ if (!map) return;
70
+ for (const bgId of [...map.keys()]) dropAttachment(client, bgId);
71
+ }
72
+
73
+ export async function handleBgList(server, id, ws, message) {
74
+ try {
75
+ const { listBackgroundAgents } = await loadSupervisor();
76
+ const sessions = listBackgroundAgents({ all: message.all === true }).map(
77
+ sanitizeBackgroundSession,
78
+ );
79
+ server._send(ws, { id, type: "bg-list", sessions });
80
+ } catch (err) {
81
+ sendError(server, id, ws, "BG_LIST_FAILED", err.message);
82
+ }
83
+ }
84
+
85
+ export async function handleBgView(server, id, ws, message) {
86
+ try {
87
+ const {
88
+ readBackgroundAgentState,
89
+ effectiveBackgroundAgentState,
90
+ readBackgroundAgentLog,
91
+ } = await loadSupervisor();
92
+ if (!message.bgId) {
93
+ return sendError(server, id, ws, "NO_BG_ID", "bgId required");
94
+ }
95
+ const session = effectiveBackgroundAgentState(
96
+ readBackgroundAgentState(message.bgId),
97
+ );
98
+ if (!session) {
99
+ return sendError(
100
+ server,
101
+ id,
102
+ ws,
103
+ "BG_NOT_FOUND",
104
+ `Background agent not found: ${message.bgId}`,
105
+ );
106
+ }
107
+ const log = readBackgroundAgentLog(message.bgId, {
108
+ lines: Number(message.lines) || 100,
109
+ });
110
+ server._send(ws, {
111
+ id,
112
+ type: "bg-view",
113
+ session: sanitizeBackgroundSession(session),
114
+ log,
115
+ });
116
+ } catch (err) {
117
+ sendError(server, id, ws, "BG_VIEW_FAILED", err.message);
118
+ }
119
+ }
120
+
121
+ export async function handleBgAttach(server, clientId, id, ws, message) {
122
+ try {
123
+ const client = server.clients.get(clientId);
124
+ if (!client) {
125
+ return sendError(server, id, ws, "NO_CLIENT", "client not registered");
126
+ }
127
+ if (!message.bgId) {
128
+ return sendError(server, id, ws, "NO_BG_ID", "bgId required");
129
+ }
130
+ const bgId = message.bgId;
131
+ const {
132
+ readBackgroundAgentState,
133
+ effectiveBackgroundAgentState,
134
+ readBackgroundAgentLog,
135
+ logPath,
136
+ } = await loadSupervisor();
137
+
138
+ const existing = attachments(client).get(bgId);
139
+ if (existing) {
140
+ // Idempotent re-attach: reply with the live handshake instead of
141
+ // stacking a second relay (a reconnecting panel re-sends bg-attach).
142
+ server._send(ws, {
143
+ id,
144
+ type: "bg-attach",
145
+ bgId,
146
+ hello: existing.hello,
147
+ log: "",
148
+ reattached: true,
149
+ });
150
+ return;
151
+ }
152
+
153
+ const session = effectiveBackgroundAgentState(
154
+ readBackgroundAgentState(bgId),
155
+ );
156
+ if (!session) {
157
+ return sendError(
158
+ server,
159
+ id,
160
+ ws,
161
+ "BG_NOT_FOUND",
162
+ `Background agent not found: ${bgId}`,
163
+ );
164
+ }
165
+ if (session.status !== "running" || !session.transport?.pipe) {
166
+ return sendError(
167
+ server,
168
+ id,
169
+ ws,
170
+ "BG_NOT_INTERACTIVE",
171
+ `Background agent ${bgId} is ${session.status} and has no live session transport`,
172
+ );
173
+ }
174
+
175
+ const { connectBackgroundSession } =
176
+ await import("../../lib/background-session-transport.js");
177
+ let conn;
178
+ try {
179
+ conn = await connectBackgroundSession({
180
+ pipePath: session.transport.pipe,
181
+ token: session.transport.token,
182
+ onEvent: (event) => {
183
+ server._send(ws, { type: "bg-event", bgId, event });
184
+ },
185
+ onClose: () => {
186
+ if (dropAttachment(client, bgId)) {
187
+ server._send(ws, {
188
+ type: "bg-event",
189
+ bgId,
190
+ event: { type: "transport-closed" },
191
+ });
192
+ }
193
+ },
194
+ });
195
+ } catch (err) {
196
+ return sendError(
197
+ server,
198
+ id,
199
+ ws,
200
+ "BG_ATTACH_FAILED",
201
+ `session transport unavailable: ${err.message}`,
202
+ );
203
+ }
204
+
205
+ const logFile = logPath(bgId);
206
+ let offset = existsSync(logFile) ? readFileSync(logFile, "utf8").length : 0;
207
+ const poll = setInterval(() => {
208
+ try {
209
+ if (!existsSync(logFile)) return;
210
+ const text = readFileSync(logFile, "utf8");
211
+ if (offset > text.length) offset = 0; // truncated/rotated
212
+ if (text.length > offset) {
213
+ const chunk = text.slice(offset);
214
+ offset = text.length;
215
+ server._send(ws, { type: "bg-log", bgId, chunk });
216
+ }
217
+ } catch {
218
+ /* transient read failures skip one tick */
219
+ }
220
+ }, LOG_POLL_MS);
221
+ poll.unref?.();
222
+
223
+ attachments(client).set(bgId, { conn, poll, hello: conn.hello });
224
+ server._send(ws, {
225
+ id,
226
+ type: "bg-attach",
227
+ bgId,
228
+ hello: conn.hello,
229
+ log: readBackgroundAgentLog(bgId, {
230
+ lines: Number(message.lines) || 100,
231
+ }),
232
+ });
233
+ } catch (err) {
234
+ sendError(server, id, ws, "BG_ATTACH_FAILED", err.message);
235
+ }
236
+ }
237
+
238
+ function requireAttachment(server, clientId, id, ws, message) {
239
+ const client = server.clients.get(clientId);
240
+ const entry = client?._bgAttachments?.get(message.bgId);
241
+ if (!message.bgId) {
242
+ sendError(server, id, ws, "NO_BG_ID", "bgId required");
243
+ return null;
244
+ }
245
+ if (!entry) {
246
+ sendError(
247
+ server,
248
+ id,
249
+ ws,
250
+ "BG_NOT_ATTACHED",
251
+ `not attached to ${message.bgId} — send bg-attach first`,
252
+ );
253
+ return null;
254
+ }
255
+ return entry;
256
+ }
257
+
258
+ export async function handleBgPrompt(server, clientId, id, ws, message) {
259
+ const entry = requireAttachment(server, clientId, id, ws, message);
260
+ if (!entry) return;
261
+ const text = String(message.text || "").trim();
262
+ if (!text) {
263
+ return sendError(server, id, ws, "NO_TEXT", "prompt text required");
264
+ }
265
+ const sent = entry.conn.send({ type: "prompt", text });
266
+ server._send(ws, { id, type: "bg-prompt", bgId: message.bgId, sent });
267
+ }
268
+
269
+ export async function handleBgStopTurn(server, clientId, id, ws, message) {
270
+ const entry = requireAttachment(server, clientId, id, ws, message);
271
+ if (!entry) return;
272
+ const sent = entry.conn.send({ type: "stop" });
273
+ server._send(ws, { id, type: "bg-stop-turn", bgId: message.bgId, sent });
274
+ }
275
+
276
+ export async function handleBgDetach(server, clientId, id, ws, message) {
277
+ if (!message.bgId) {
278
+ return sendError(server, id, ws, "NO_BG_ID", "bgId required");
279
+ }
280
+ const client = server.clients.get(clientId);
281
+ const dropped = dropAttachment(client, message.bgId);
282
+ server._send(ws, { id, type: "bg-detach", bgId: message.bgId, dropped });
283
+ }
284
+
285
+ export async function handleBgRename(server, id, ws, message) {
286
+ try {
287
+ if (!message.bgId) {
288
+ return sendError(server, id, ws, "NO_BG_ID", "bgId required");
289
+ }
290
+ const { renameBackgroundAgent } = await loadSupervisor();
291
+ const session = renameBackgroundAgent(message.bgId, message.title);
292
+ server._send(ws, {
293
+ id,
294
+ type: "bg-rename",
295
+ session: sanitizeBackgroundSession(session),
296
+ });
297
+ } catch (err) {
298
+ sendError(server, id, ws, "BG_RENAME_FAILED", err.message);
299
+ }
300
+ }
301
+
302
+ export async function handleBgResume(server, id, ws, message) {
303
+ try {
304
+ if (!message.bgId) {
305
+ return sendError(server, id, ws, "NO_BG_ID", "bgId required");
306
+ }
307
+ const { resumeBackgroundAgent } = await loadSupervisor();
308
+ const session = resumeBackgroundAgent(message.bgId, message.text);
309
+ server._send(ws, {
310
+ id,
311
+ type: "bg-resume",
312
+ session: sanitizeBackgroundSession(session),
313
+ });
314
+ } catch (err) {
315
+ sendError(server, id, ws, "BG_RESUME_FAILED", err.message);
316
+ }
317
+ }
318
+
319
+ export async function handleBgStop(server, clientId, id, ws, message) {
320
+ try {
321
+ if (!message.bgId) {
322
+ return sendError(server, id, ws, "NO_BG_ID", "bgId required");
323
+ }
324
+ // Kill the whole session: drop our relay first so the worker's
325
+ // "no clients" finalize path doesn't race the stop.
326
+ dropAttachment(server.clients.get(clientId), message.bgId);
327
+ const { stopBackgroundAgent } = await loadSupervisor();
328
+ const session = stopBackgroundAgent(message.bgId);
329
+ server._send(ws, {
330
+ id,
331
+ type: "bg-stop",
332
+ session: sanitizeBackgroundSession(session),
333
+ });
334
+ } catch (err) {
335
+ sendError(server, id, ws, "BG_STOP_FAILED", err.message);
336
+ }
337
+ }
@@ -175,6 +175,16 @@ export function createWsMessageDispatcher(server) {
175
175
  "task-graph-advance": () =>
176
176
  server._handleTaskGraphAdvance(id, ws, message),
177
177
  "task-graph-state": () => server._handleTaskGraphState(id, ws, message),
178
+ "bg-list": () => server._handleBgList(id, ws, message),
179
+ "bg-view": () => server._handleBgView(id, ws, message),
180
+ "bg-attach": () => server._handleBgAttach(clientId, id, ws, message),
181
+ "bg-prompt": () => server._handleBgPrompt(clientId, id, ws, message),
182
+ "bg-stop-turn": () =>
183
+ server._handleBgStopTurn(clientId, id, ws, message),
184
+ "bg-detach": () => server._handleBgDetach(clientId, id, ws, message),
185
+ "bg-stop": () => server._handleBgStop(clientId, id, ws, message),
186
+ "bg-rename": () => server._handleBgRename(id, ws, message),
187
+ "bg-resume": () => server._handleBgResume(id, ws, message),
178
188
  "chat.intent.understand": () =>
179
189
  server._handleChatIntentUnderstand(id, ws, message),
180
190
  "chat.intent.understand-stream": () =>
@@ -7,6 +7,7 @@ import {
7
7
  RemoteSessionCryptoContext,
8
8
  createRemotePairingUri,
9
9
  } from "../../harness/remote-session-crypto.js";
10
+ import { isApprovalRequestEvent } from "../../harness/remote-session-push.js";
10
11
  import { RemoteCommandLedger } from "../../harness/remote-command-ledger.js";
11
12
 
12
13
  const CLIENT_EVENT_SCOPES = Object.freeze({
@@ -462,12 +463,75 @@ export async function handleRemoteSessionPublish(
462
463
  ) {
463
464
  throw new Error("prompt content is required");
464
465
  }
466
+ // CLIENT-HOSTED sessions (第四阶段 #2): when the agent session does not
467
+ // live in THIS server's sessionManager — e.g. a REPL/headless process
468
+ // that connected as a WS client and registered its own local session id —
469
+ // the server cannot dispatch the control event itself. Forward it to the
470
+ // HOST connection instead; the host's approval bridge resolves its local
471
+ // permission gate / prompt queue. Checked BEFORE the idempotency ledger
472
+ // so "host unreachable" never consumes a commandId slot (stays retryable
473
+ // after the host reconnects). Server-hosted sessions are byte-identical.
474
+ let serverHosted = false;
475
+ try {
476
+ serverHosted = Boolean(
477
+ server.sessionManager?.getSession?.(session.agentSessionId),
478
+ );
479
+ } catch {
480
+ serverHosted = false;
481
+ }
482
+ let hostTarget = null;
483
+ if (!serverHosted) {
484
+ hostTarget = server.clients.get(session.hostClientId);
485
+ if (!hostTarget) {
486
+ throw new Error(
487
+ "Remote session host is not reachable for this control event",
488
+ );
489
+ }
490
+ }
465
491
  // Idempotent forward: a control event with a stable commandId runs its
466
492
  // side effect (audit + dispatch into the host agent) AT MOST ONCE, so a
467
493
  // reconnecting device re-sending the same prompt/approval/interrupt gets a
468
494
  // `replayed` ack instead of a second agent turn. No commandId → forwarded
469
495
  // directly, byte-identical to before.
470
496
  await applyControlIdempotent(server, clientId, ws, message, async () => {
497
+ if (!serverHosted) {
498
+ // Audit the control action with the same taxonomy as server-hosted
499
+ // dispatch, then hand the event to the host process.
500
+ const auditAction =
501
+ message.event.type === "prompt"
502
+ ? "control.prompt"
503
+ : message.event.type === "approval.resolve"
504
+ ? "control.approval"
505
+ : "control.interrupt";
506
+ audit(server, {
507
+ sessionId: message.remoteSessionId,
508
+ actor: clientId,
509
+ action: auditAction,
510
+ detail:
511
+ message.event.type === "prompt"
512
+ ? { chars: message.event.content.length, forwarded: true }
513
+ : message.event.type === "approval.resolve"
514
+ ? {
515
+ requestId:
516
+ message.event.requestId || message.event.approvalId,
517
+ approved: message.event.answer ?? message.event.approved,
518
+ forwarded: true,
519
+ }
520
+ : { forwarded: true },
521
+ });
522
+ server._send(hostTarget.ws, {
523
+ type: "remote-session-control",
524
+ remoteSessionId: message.remoteSessionId,
525
+ agentSessionId: session.agentSessionId,
526
+ from: clientId,
527
+ event: message.event,
528
+ });
529
+ reply(server, ws, message.id, "remote-session-published", {
530
+ delivered: 1,
531
+ forwardedToHost: true,
532
+ });
533
+ return;
534
+ }
471
535
  if (message.event.type === "prompt") {
472
536
  // Record the shape, not the content — the audit trail must stay
473
537
  // useful without hoarding potentially sensitive session prompts.
@@ -514,8 +578,45 @@ export async function handleRemoteSessionPublish(
514
578
  message.remoteSessionId,
515
579
  )) {
516
580
  if (member.clientId === clientId) continue;
581
+ // Wake a backgrounded device for approval/permission requests — parity
582
+ // with the server-hosted mirror path (_mirrorRemoteSessionEvent). A
583
+ // client-hosted session publishing `permission.request` must reach a
584
+ // suspended phone the same way (第四阶段 #2).
585
+ if (
586
+ member.pushToken &&
587
+ isApprovalRequestEvent(message.event.type) &&
588
+ typeof server._dispatchApprovalPush === "function"
589
+ ) {
590
+ server._dispatchApprovalPush(session, member, message.event);
591
+ }
517
592
  const target = server.clients.get(member.clientId);
518
- if (!target) continue;
593
+ if (!target) {
594
+ // Relay-paired mobiles have no local socket — deliver over the E2EE
595
+ // relay like the mirror path does; without this, host-published
596
+ // events silently skipped every relay member.
597
+ if (server.remoteSessionRelay) {
598
+ const crypto = server.remoteSessionCrypto?.get(
599
+ message.remoteSessionId,
600
+ );
601
+ if (crypto) {
602
+ try {
603
+ server.remoteSessionRelay.sendEncrypted(
604
+ member.clientId,
605
+ crypto.encrypt(member.clientId, {
606
+ type: "remote-session-event",
607
+ remoteSessionId: message.remoteSessionId,
608
+ agentSessionId: session.agentSessionId,
609
+ event: message.event,
610
+ }),
611
+ );
612
+ delivered += 1;
613
+ } catch {
614
+ // Peer key may be gone — best-effort, same as revocation notice.
615
+ }
616
+ }
617
+ }
618
+ continue;
619
+ }
519
620
  server._send(target.ws, {
520
621
  type: "remote-session-event",
521
622
  remoteSessionId: message.remoteSessionId,
@@ -94,6 +94,18 @@ import {
94
94
  handleChatIntentClassifyFollowup,
95
95
  } from "./chat-intent-protocol.js";
96
96
  import { handleLlmChat } from "./llm-chat-protocol.js";
97
+ import {
98
+ cleanupBgAttachments,
99
+ handleBgAttach,
100
+ handleBgDetach,
101
+ handleBgList,
102
+ handleBgPrompt,
103
+ handleBgRename,
104
+ handleBgResume,
105
+ handleBgStop,
106
+ handleBgStopTurn,
107
+ handleBgView,
108
+ } from "./background-agent-protocol.js";
97
109
 
98
110
  const __filename = fileURLToPath(import.meta.url);
99
111
  const __dirname = dirname(__filename);
@@ -469,6 +481,10 @@ export class ChainlessChainWSServer extends EventEmitter {
469
481
  clearTimeout(client.authTimer);
470
482
  client.authTimer = null;
471
483
  }
484
+ // Close any background-session transport relays this client held —
485
+ // mirrors the CLI attach detach semantics (an idle worker with no
486
+ // remaining clients finalizes itself).
487
+ cleanupBgAttachments(client);
472
488
  this.clients.delete(clientId);
473
489
  for (const affected of this.remoteSessions.removeClient(clientId)) {
474
490
  if (affected.closed)
@@ -656,6 +672,51 @@ export class ChainlessChainWSServer extends EventEmitter {
656
672
  return handleWorktreeList(this, id, ws);
657
673
  }
658
674
 
675
+ /** @private — list background agent sessions (token-stripped) */
676
+ async _handleBgList(id, ws, message) {
677
+ return handleBgList(this, id, ws, message);
678
+ }
679
+
680
+ /** @private — background agent detail view (state + log tail) */
681
+ async _handleBgView(id, ws, message) {
682
+ return handleBgView(this, id, ws, message);
683
+ }
684
+
685
+ /** @private — attach to a background session transport (event/log relay) */
686
+ async _handleBgAttach(clientId, id, ws, message) {
687
+ return handleBgAttach(this, clientId, id, ws, message);
688
+ }
689
+
690
+ /** @private — send a follow-up prompt into an attached background session */
691
+ async _handleBgPrompt(clientId, id, ws, message) {
692
+ return handleBgPrompt(this, clientId, id, ws, message);
693
+ }
694
+
695
+ /** @private — stop the current turn of an attached background session */
696
+ async _handleBgStopTurn(clientId, id, ws, message) {
697
+ return handleBgStopTurn(this, clientId, id, ws, message);
698
+ }
699
+
700
+ /** @private — drop this client's background session relay */
701
+ async _handleBgDetach(clientId, id, ws, message) {
702
+ return handleBgDetach(this, clientId, id, ws, message);
703
+ }
704
+
705
+ /** @private — kill a whole background session */
706
+ async _handleBgStop(clientId, id, ws, message) {
707
+ return handleBgStop(this, clientId, id, ws, message);
708
+ }
709
+
710
+ /** @private — rename a background session's title */
711
+ async _handleBgRename(id, ws, message) {
712
+ return handleBgRename(this, id, ws, message);
713
+ }
714
+
715
+ /** @private — continue a finished session as a new background run */
716
+ async _handleBgResume(id, ws, message) {
717
+ return handleBgResume(this, id, ws, message);
718
+ }
719
+
659
720
  /** @private */
660
721
  async _handleCompressionStats(id, ws, message) {
661
722
  return handleCompressionStats(this, id, ws, message);
@@ -411,6 +411,13 @@ export class MCPClient extends EventEmitter {
411
411
  entry.state = ServerState.CONNECTED;
412
412
  entry.serverInfo = initResult?.serverInfo || {};
413
413
  entry.capabilities = initResult?.capabilities || {};
414
+ // Optional top-level usage instructions from the server's initialize
415
+ // response (previously discarded). Surfaced by tool search so a deferred
416
+ // server's guidance still reaches the model on demand.
417
+ entry.instructions =
418
+ typeof initResult?.instructions === "string"
419
+ ? initResult.instructions
420
+ : null;
414
421
 
415
422
  // Fetch available tools. Per MCP a server advertises a `tools` capability
416
423
  // in its initialize response; if it does and tools/list then fails, that
@@ -469,6 +476,7 @@ export class MCPClient extends EventEmitter {
469
476
  resources: entry.resources,
470
477
  prompts: entry.prompts,
471
478
  serverInfo: entry.serverInfo,
479
+ instructions: entry.instructions,
472
480
  };
473
481
  } catch (err) {
474
482
  entry.state = ServerState.ERROR;
package/src/index.js CHANGED
@@ -21,6 +21,7 @@ import { registerHubCommand } from "./commands/hub.js";
21
21
  import { registerAndroidCommand } from "./commands/android.js";
22
22
  import { registerEvalCommand } from "./commands/eval.js";
23
23
  import { registerTeamCommand } from "./commands/team.js";
24
+ import { registerAutoModeCommand } from "./commands/auto-mode.js";
24
25
  import {
25
26
  registerAgentCommand,
26
27
  registerSubAgentV2Command,
@@ -67,6 +68,7 @@ import { registerCheckpointCommand } from "./commands/checkpoint.js";
67
68
  import { registerGoalCommand } from "./commands/goal.js";
68
69
  import { registerCommandCommand } from "./commands/command.js";
69
70
  import { registerAgentsCommand } from "./commands/agents.js";
71
+ import { registerBackgroundSessionCommands } from "./commands/background-session.js";
70
72
  import { registerCompactCommand } from "./commands/compact.js";
71
73
  import { registerLoopCommand } from "./commands/loop.js";
72
74
  import { registerReviewCommand } from "./commands/review.js";
@@ -151,6 +153,9 @@ import { registerCliAnythingCommand } from "./commands/cli-anything.js";
151
153
  // WebSocket Server Interface
152
154
  import { registerServeCommand } from "./commands/serve.js";
153
155
 
156
+ // Remote Control unified entry (第四阶段 #1)
157
+ import { registerRemoteControlCommand } from "./commands/remote-control.js";
158
+
154
159
  // Web UI
155
160
  import { registerUiCommand } from "./commands/ui.js";
156
161
  import { registerPackCommand } from "./commands/pack.js";
@@ -434,6 +439,7 @@ export function createProgram(opts = {}) {
434
439
  registerLlmCommand(program);
435
440
  registerHubCommand(program);
436
441
  registerAndroidCommand(program);
442
+ registerAutoModeCommand(program);
437
443
  registerAgentCommand(program);
438
444
  registerEvalCommand(program);
439
445
  registerTeamCommand(program);
@@ -482,6 +488,7 @@ export function createProgram(opts = {}) {
482
488
  registerGoalCommand(program);
483
489
  registerCommandCommand(program);
484
490
  registerAgentsCommand(program);
491
+ registerBackgroundSessionCommands(program);
485
492
  registerCompactCommand(program);
486
493
  registerLoopCommand(program);
487
494
  registerReviewCommand(program);
@@ -576,6 +583,9 @@ export function createProgram(opts = {}) {
576
583
  // WebSocket Server Interface
577
584
  registerServeCommand(program);
578
585
 
586
+ // Remote Control unified entry
587
+ registerRemoteControlCommand(program);
588
+
579
589
  // Web UI
580
590
  registerUiCommand(program);
581
591