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.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-CIpoXyVI.js → AIOps-Cth_zZeA.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Ch_QXIPk.js → ActionButton-zfwA3Oxn.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CvaBYShv.js → Analytics-FeKc4aU2.js} +2 -2
- package/src/assets/web-panel/assets/AppLayout-C6WIrDZk.js +9 -0
- package/src/assets/web-panel/assets/{Audit-Bng8eHCW.js → Audit-C2gRq5Qi.js} +1 -1
- package/src/assets/web-panel/assets/BackgroundAgents-Dq2340Kl.css +1 -0
- package/src/assets/web-panel/assets/BackgroundAgents-TeTIFiW-.js +1 -0
- package/src/assets/web-panel/assets/{Backup-DvBs8-lm.js → Backup-CLJk3k6i.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DKwajm0g.js → BaseInput-5ms537NY.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BoGpZ3uO.js → Chat-4Nr5U9PJ.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BFEnA8LN.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-BMJEmxFQ.js → Checkbox-B2Bca5ev.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-Bsoag5CV.js → Codegen-D6Uysdbv.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CDjH9-sp.js → Col-B3XQ8xc7.js} +1 -1
- package/src/assets/web-panel/assets/{Community-Cg2QHPQ6.js → Community-CqwKtoPz.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BU2BaqP-.js → Compact-BZZINTus.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-D6ZYMCqZ.js → Compliance-Dr3BXkWA.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DaZ8VW-I.js → Cowork-rx_x9Wqc.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-DQY1RH9v.js → Cron-BpZr7rHc.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BVR28vk9.js → Crosschain-CeV5EYVg.js} +1 -1
- package/src/assets/web-panel/assets/{DID-N44KvTP-.js → DID-fe2Qaowz.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Bjhlx6wS.js → Dashboard-Fh_EdWVa.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CpLYxpy1.js → Dropdown-N1PIwWYf.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-srEITSPI.js → EmailListRenderer-B6vK93Ed.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-juOC_fX2.js → FamilyGuardDashboard-e3iUZhAg.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-nRaQtaXW.js → Federation-uInc9YcN.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DflhTj09.js → FormItemContext-DmQZSX0z.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DulI_3NI.js → GenericCardRenderer-CV_Z8OAo.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BxOPefXn.js → Git-jXAhNN8d.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-C6G_Wnwi.js → Governance-Blw1esBB.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BnFYa2BF.js → Inference-CoK8Ia0Z.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-TqQLEabm.js → KnowledgeGraph-5xrglMTK.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BH4AVh7s.js → Logs-BGIG9798.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-DoWLTaEE.js → Marketplace-DFqhJMuY.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-D6yC7OG_.js → McpTools-7U8tEhwK.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-DLCeHj21.js → Memory-BrvboPLE.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BZBE0GRl.js → MobileBridge-nzcd7d1H.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DJA15G1J.js → MobileProjects-DETVNKa7.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CyTKJDHn.js → Mtc-SZcCYec3.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-DZKjgEVc.js → MtcAudit-Cx_d3N17.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-2rMPNhSO.js → Multisig-DtcmSUo-.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-VeMk4Fx-.js → NLProgramming-Dgx2QHl-.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-jr2GHMhh.js → Notes-oHTGEHoJ.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-WN9VZyFb.js → NotificationSettings-qKD-IlOl.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CjNO-e9T.js → OrderTableRenderer-CiU4Sy1J.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DdjVHuzT.js → Organization-BBNXnltP.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-l8vBsGGq.js → Overflow-Df2fDbvT.js} +1 -1
- package/src/assets/web-panel/assets/{OverrideContext-BlgL9440.js → OverrideContext-C0w0P4A3.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CVQDHHwq.js → P2P-DUHj6zjO.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CTsNh8qE.js → PdhVaultBrowser-B2nWgdoV.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-DLW87uyK.js → Permissions-CiW0afny.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-N8OvASTx.js → PersonalDataHub-cc1mHU_D.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-BPZmdlz2.js → Pipeline-B93zTapU.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-MFZw_GNh.js → Privacy-Db1jXWfd.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-zW0pEsfU.js → ProjectInit-DFoZ1h9U.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CppVHr_o.js → ProjectSettings-BkkUnmYs.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BTglkJO2.js → Projects-Xa779XLC.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CPHZ6JKT.js → Providers-DOF01IEN.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DO4klNZ2.js → QrScannerModal-TohRsKd0.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-B37TTqUa.js → QuickAsk-DjLcr1ZU.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-n7KIfK7y.js → Recommend--Ism6bMx.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-SjVoS9mm.js → RemoteSession-B8CL3YHM.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-Dt6RTeHi.js → Reputation-CaRxB3Ep.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Dty6jz5V.js → Row-BioxZBBH.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DDLhqw5j.js → RssFeed-B2LipwH3.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DrYbXi-W.js → Search-BpjLBV8u.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Fc9Z6fbB.js → Security-TibHuJWP.js} +3 -3
- package/src/assets/web-panel/assets/{Services-BAhPgjSM.js → Services-DtkuWseW.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-8JF7EPfE.js → Skeleton-BIcPQae6.js} +1 -1
- package/src/assets/web-panel/assets/Skills-DoTWIQOX.js +1 -0
- package/src/assets/web-panel/assets/{Sla-CSvjcATc.js → Sla-uUSU185m.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-Demx7Gnx.js → SpeechSettings-B7Bgc9mk.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CKLnRWJr.js → SyncSettings-K-XrJ2s9.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-mhITRxjv.js → Tasks-B3hEXNcI.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-AjQI5rxD.js → Templates-CaFnyGDK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-dbtEh1yR.js → Tenant-C2X0PtEC.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DX-9jjfk.js → Terminal-CKU5yVsT.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C11EDZRt.js → TimelineRenderer-8trZeYDC.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-GiA9fiZn.js → Tokens-BUEEe9US.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BPcnclJn.js → Trigger-COU97I7Y.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-C-DVY8nq.js → Trust-DNWQChaP.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DxodSwME.js → UkeySign-DWX_VtCp.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D_WV0O8O.js → VideoEditing-D9eIDhsp.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DSeNlVSU.js → Wallet-BOnCCgIL.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Bwuuvpz1.js → WebAuthn-DN4dD-W8.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-8M96EU1N.js → WorkflowEditor-ClvDb_jX.js} +1 -1
- package/src/assets/web-panel/assets/{chat-D5S6SmLm.js → chat-DIxQ_Eeb.js} +1 -1
- package/src/assets/web-panel/assets/{collapseMotion-DVfhbsdP.js → collapseMotion-CqM1QOwM.js} +1 -1
- package/src/assets/web-panel/assets/{colors-5fbnGkAP.js → colors-BOX3WKcP.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DaSuuMlW.js → compact-item-Bny82VDi.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BNOHcXAF.js → createContext-Cnoy3enS.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Dr9dzlQ5.js +1 -0
- package/src/assets/web-panel/assets/{echarts-CL0F2SnX.js → echarts-C61Vn3mP.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-DONwZUra.js → hasIn-BJ7fkx3F.js} +1 -1
- package/src/assets/web-panel/assets/{icons-BsDmGpcT.js → icons-DEmcnxBf.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_yDD9Lm.js → index--CcGiwAI.js} +1 -1
- package/src/assets/web-panel/assets/{index-5_fj5gVd.js → index--Xb2lq6E.js} +1 -1
- package/src/assets/web-panel/assets/{index-uR5KTNZA.js → index-2N9Y1cLx.js} +1 -1
- package/src/assets/web-panel/assets/{index-MOgO5Yvx.js → index-7GLXvG8p.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLXf-8ek.js → index-B1-h2QrC.js} +1 -1
- package/src/assets/web-panel/assets/{index-D7gs_3Ck.js → index-B6WoW8DJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CztESwB4.js → index-B7Fah_BX.js} +1 -1
- package/src/assets/web-panel/assets/index-B92M3t6B.js +1 -0
- package/src/assets/web-panel/assets/{index-BEO5f7Vf.js → index-BDkN6YOI.js} +1 -1
- package/src/assets/web-panel/assets/{index-_6S41ikV.js → index-BJ8-oLHI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrkCFohi.js → index-BM0mQhVv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BrAJSu-v.js → index-BP7HFsAK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcqOK7Lv.js → index-BWPey0Uf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLzMuz38.js → index-BbZynivw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DbdQBn4n.js → index-BcmXbIwv.js} +1 -1
- package/src/assets/web-panel/assets/{index-KwV9g_cf.js → index-Bk5yST66.js} +1 -1
- package/src/assets/web-panel/assets/{index-ClGqO-2X.js → index-Bvheiep2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dd7xwGcD.js → index-C--PPqwi.js} +1 -1
- package/src/assets/web-panel/assets/{index-DP2-3A_j.js → index-C1nHEOFE.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAYmc5Nh.js → index-CEPTiBWy.js} +1 -1
- package/src/assets/web-panel/assets/{index-LRIt-AFl.js → index-CEq5Vh0i.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dm5A-MN5.js → index-CN6emwbq.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cf2OCa8q.js → index-CQ-xLCVb.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHx2lhHW.js → index-CW6n-y1L.js} +1 -1
- package/src/assets/web-panel/assets/{index-DM3Ug61e.js → index-CZ6CHiCQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJp4kRjw.js → index-CZCTan8Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-PsMErAB1.js → index-CnjtAKdw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEzd5m5a.js → index-D2qNdrNW.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4L3gsNA.js → index-D9QEc7Tq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcWMZqb4.js → index-DBhkJqVF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSfYJqXt.js → index-DSRIjAWO.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmUOjyL6.js → index-DxX8hhmL.js} +1 -1
- package/src/assets/web-panel/assets/{index-RkInlwV0.js → index-Kio1ekYg.js} +1 -1
- package/src/assets/web-panel/assets/{index-CTZeOnzO.js → index-OaNFXV8N.js} +1 -1
- package/src/assets/web-panel/assets/{index-BK2N5yVW.js → index-Ty4w18mR.js} +4 -4
- package/src/assets/web-panel/assets/{index-Cn5LtyIH.js → index-ceH9DsAi.js} +1 -1
- package/src/assets/web-panel/assets/index-d2UcGpK-.js +1 -0
- package/src/assets/web-panel/assets/{index-xo2f4DZR.js → index-qOBKrw-g.js} +1 -1
- package/src/assets/web-panel/assets/{index-vYtprlNs.js → index-rn6xd12s.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CxwLynp9.js → initDefaultProps-BmxMBuUZ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-XbPvGJbW.js → motion-DnXNwo2k.js} +1 -1
- package/src/assets/web-panel/assets/{move-D1u5pcgS.js → move-WnIJwmZN.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BDxhTBMH.js → mtc-parser-DAto-c2w.js} +1 -1
- package/src/assets/web-panel/assets/{omit-EtIFGg5k.js → omit-BtvW9W0H.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D33sWb0l.js → pickAttrs-DFqI9-_C.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BwFRzaGo.js → placementArrow-Dax6wOK3.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CBk2WqvQ.js → responsiveObserve-BUFWWC83.js} +1 -1
- package/src/assets/web-panel/assets/{slide-B52HtrKn.js → slide-DyARyuSz.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-D4qxqWnH.js → statusUtils-BZSDugpi.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BxaCFlOL.js → styleChecker-B7o_K-4R.js} +1 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-Dg7Bg8VG.js +1 -0
- package/src/assets/web-panel/assets/{useFs-C6VAU1_6.js → useFs-DQYXo5Rk.js} +1 -1
- package/src/assets/web-panel/assets/{useMergedState-Dc9sO2d7.js → useMergedState-DXbJa_zE.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-rEG6oXG8.js → usePersonalDataHub-E8NBAnkq.js} +1 -1
- package/src/assets/web-panel/assets/{useRefs-CAeFRzPf.js → useRefs-BrCfMLMc.js} +1 -1
- package/src/assets/web-panel/assets/{useState-LwExUYDO.js → useState-CnxtbLoC.js} +1 -1
- package/src/assets/web-panel/assets/{vendor-BMxUs6UX.js → vendor-Bn1JzzAn.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DOTafkEi.js → vnode-DD71_w1b.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BuwwN5K2.js → zoom-zc9EMd3q.js} +1 -1
- package/src/assets/web-panel/index.html +3 -3
- package/src/command-manifest.json +36 -1
- package/src/commands/agent.js +23 -2
- package/src/commands/agents.js +62 -1
- package/src/commands/auto-mode.js +87 -0
- package/src/commands/background-session.js +499 -0
- package/src/commands/command.js +4 -1
- package/src/commands/permissions.js +49 -0
- package/src/commands/remote-control.js +407 -0
- package/src/data/changelog.json +7 -2
- package/src/gateways/ws/background-agent-protocol.js +337 -0
- package/src/gateways/ws/message-dispatcher.js +10 -0
- package/src/gateways/ws/remote-session-protocol.js +102 -1
- package/src/gateways/ws/ws-server.js +61 -0
- package/src/harness/mcp-client.js +8 -0
- package/src/index.js +10 -0
- package/src/lib/auto-mode-config.js +396 -0
- package/src/lib/background-agent-supervisor.js +202 -17
- package/src/lib/background-session-transport.js +315 -0
- package/src/lib/permission-denial-store.js +159 -0
- package/src/lib/remote-approval-bridge.js +360 -0
- package/src/lib/remote-control.js +270 -0
- package/src/lib/repl-denials.js +26 -0
- package/src/lib/shell-approval.js +4 -0
- package/src/lib/ws-rpc-client.js +150 -0
- package/src/repl/agent-repl.js +208 -16
- package/src/repl/permission-tier.js +31 -0
- package/src/runtime/agent-core.js +57 -0
- package/src/runtime/headless-runner.js +126 -8
- package/src/runtime/headless-stream.js +87 -1
- package/src/runtime/mcp-config.js +11 -0
- package/src/runtime/mcp-tool-search.js +0 -0
- package/src/runtime/policies/agent-policy.js +17 -0
- package/src/workers/background-agent-worker.js +218 -37
- package/src/assets/web-panel/assets/AppLayout-B6Q6olcr.js +0 -9
- package/src/assets/web-panel/assets/ChatBubbleRenderer-mDQl_PXO.js +0 -1
- package/src/assets/web-panel/assets/Skills-DL39dY0t.js +0 -1
- package/src/assets/web-panel/assets/devWarning-K_woVElh.js +0 -1
- package/src/assets/web-panel/assets/index-3ILvaBpo.js +0 -1
- package/src/assets/web-panel/assets/index-JpB-FoZq.js +0 -1
- 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)
|
|
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
|
|