chainlesschain 0.162.147 → 0.162.149
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 +2 -2
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-DywJ7xTv.js → AIOps-BJif14yR.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BTlqcY-q.js → ActionButton-CXek6gJY.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-FdG1Mvwy.js → Analytics-BgQhdAJi.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CVsnbvN1.js → AppLayout-DTYl5NtR.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-ITdpJ7vR.js → Audit-BzATQAeR.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BfXqnXo1.js → Backup-BzdPEQhL.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C09ip3_E.js → BaseInput-BM0KVh8E.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-qdkhB42l.js → Chat-D7fHXHkz.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-D2KfETZD.js → ChatBubbleRenderer-Wm34RR-b.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-j6WZCuff.js → Checkbox-CAiSQ9vO.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CGZ-K3uK.js → Codegen-Df40CrEe.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CIlPOAu6.js → Col-DIT2fNFU.js} +1 -1
- package/src/assets/web-panel/assets/{Community-sY-i-hic.js → Community-CSTIbW2k.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DcoEfyL8.js → Compact-DtqXZZUi.js} +1 -1
- package/src/assets/web-panel/assets/Compliance-CUtzw6o7.js +1 -0
- package/src/assets/web-panel/assets/{Cowork-DLY_fkLv.js → Cowork-CezmlnmU.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-D049pZBC.js → Cron-P4BITarg.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DCXdE8M9.js → Crosschain-BRRiYwvq.js} +1 -1
- package/src/assets/web-panel/assets/{DID-JCvPZtjW.js → DID-L5ijB9i4.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Cg2xu3iE.js → Dashboard-CFxro4ob.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DjJLjbLh.js → Dropdown-BmAFCQ71.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DugNcSLR.js → EmailListRenderer-DJBCEuon.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-J6Nb-E30.js → FamilyGuardDashboard-z4oLq6eT.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CQ3Ttpbl.js → Federation-DsVCpHMF.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cz0NKag4.js → FormItemContext-DP3bP5th.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-NYcMP6iz.js → GenericCardRenderer-BAXP2Gc6.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B4hYN18N.js → Git-I3g_bAw9.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DOQPCcC0.js → Governance-jHS5-ioS.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-HtLF746W.js → Inference-CPL7sfx9.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BXrk8rMm.js → KnowledgeGraph-Jf236h4C.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DS0JnD3-.js → Logs-Cm2tcSKg.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-Cqnjihrz.js → Marketplace-CZK82rhi.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Cx3Psk-U.js → McpTools-CZCeji7a.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-BjyysPtj.js → Memory-3l2KVS9k.js} +2 -2
- package/src/assets/web-panel/assets/MobileBridge-D3Arxfeg.css +1 -0
- package/src/assets/web-panel/assets/MobileBridge-VYdpoLh6.js +1 -0
- package/src/assets/web-panel/assets/{MobileProjects-Xwf-fdOQ.js → MobileProjects-CiB9cmm1.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Djkql5m5.js → Mtc-wCFZbBuL.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-D2BAZlu0.js → MtcAudit-1a3THIyG.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-ByqP1ZzH.js → Multisig-DMzLB2hG.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-C_kn0nE2.js → NLProgramming-D9wZbf6l.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-7l7eXHPq.js → Notes-hrFe2ZM-.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BrCDoitw.js → NotificationSettings-CHGX5Jwm.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CRIFE2Tj.js +1 -0
- package/src/assets/web-panel/assets/{Organization-DdoNAxsM.js → Organization-By7FkhtY.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Bw7R6dE3.js → Overflow--khGSzJn.js} +1 -1
- package/src/assets/web-panel/assets/{OverrideContext-C2r4vyBb.js → OverrideContext-BlgL9440.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DJNtBugA.js → P2P-CAG9dH35.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Dwhd5_ue.js → PdhVaultBrowser-B3SQZmK9.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYoSfhPR.js → Permissions-D35ec6JA.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-aYCYJbnH.js → PersonalDataHub-CxBF7hW_.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-DJp6CprF.js → Pipeline-s6EtOO1s.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Cq9HZweR.js → Privacy-BbJYmWmO.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CmF0HsSt.js → ProjectInit-VGQq1jMT.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-P8V5Aea2.js → ProjectSettings-B1ew3Teh.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CGcNIs_g.js +1 -0
- package/src/assets/web-panel/assets/{Providers-D589v0q2.js → Providers-mrO47FIK.js} +1 -1
- package/src/assets/web-panel/assets/QrScannerModal-IJF2mHyw.js +1 -0
- package/src/assets/web-panel/assets/{MobileBridge-zJpPuf4E.css → QrScannerModal-NwAHNfiJ.css} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-lvRV2osB.js → QuickAsk-CU0wN_Z0.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-sb84MTpY.js → Recommend-DhN37R6G.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-D_xOX_hu.js +4 -0
- package/src/assets/web-panel/assets/{Reputation-d9Y3vy-W.js → Reputation-D_Ssv7uH.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DMdhLUTp.js → Row-Kx5dKxX7.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-OL-SMDkz.js → RssFeed-BlGwqZkY.js} +2 -2
- package/src/assets/web-panel/assets/{Search-BjStLKq1.js → Search-hLFQzxpb.js} +1 -1
- package/src/assets/web-panel/assets/{Security-MeI411qr.js → Security-DMlvsVt4.js} +2 -2
- package/src/assets/web-panel/assets/{Services-UajosuhT.js → Services-Bd0Qsj2m.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CGpG-QJ1.js → Skeleton-DFclq9X3.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-C6P2RPY-.js → Skills-BNtln2qY.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-B_fPprgw.js → Sla-Dch5H19G.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CqxLjSlQ.js → SpeechSettings-2UfQcU1g.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DixSfFOQ.js → SyncSettings-CD9YQr4i.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-PavzPBxc.js → Tasks-BNu-7MzI.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BYdGRnfX.js → Templates-D1n9CaIR.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BQLgnarD.js → Tenant-DenL3iBW.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-ZzU0Io1Y.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-Bv7uZRM7.js → TimelineRenderer-D87IfukO.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DxhgszRJ.js → Tokens-BkiZc8E3.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-nQYHayuc.js → Trigger-BNRSytGN.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CvqZDuZ4.js → Trust-Bwbd4X2m.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DaR8GV2I.js → UkeySign-B8O2bs0o.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C6Mpsokw.js → VideoEditing-C2fL8zmH.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-B57VRrWc.js → Wallet-BORYDdE1.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BbtEEtpq.js → WebAuthn-C0V5S2FM.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-C5LwvKmH.js → WorkflowEditor-2dR0fcHL.js} +1 -1
- package/src/assets/web-panel/assets/{chat-MzuPR5jh.js → chat-BmuAFAn8.js} +1 -1
- package/src/assets/web-panel/assets/{collapseMotion-mxzzcDsg.js → collapseMotion-DVfhbsdP.js} +1 -1
- package/src/assets/web-panel/assets/{colors-HyW8mi_y.js → colors-CN3smMcK.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DvUSzWAp.js → compact-item-DCnxb4kW.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-4MppZl7X.js → createContext-vlR43pHn.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DxwmsL4j.js +1 -0
- package/src/assets/web-panel/assets/{echarts-jxgIOFUZ.js → echarts-CL0F2SnX.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-D9q3CJ-u.js → hasIn-D04tHYFd.js} +1 -1
- package/src/assets/web-panel/assets/{icons-DP3uiYxy.js → icons-BsDmGpcT.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9I5MuJ0.js → index-0DnaZGkC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Caqtu6Et.js → index-430S68MN.js} +1 -1
- package/src/assets/web-panel/assets/{index-eOQO6KnV.js → index-B78xL3s2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ci6adKh8.js → index-BTPEZTk1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CifKUNtV.js → index-BXRg8GFQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-D7TDMMfu.js → index-BasvsWDM.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNYqw0MO.js → index-BeGDEXFs.js} +1 -1
- package/src/assets/web-panel/assets/{index-m0b6Fj9q.js → index-BqhASEL4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BF2JsbiX.js → index-BudvKQfG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFqvWeGc.js → index-C-6NO5il.js} +1 -1
- package/src/assets/web-panel/assets/{index-SDblbKj6.js → index-C5Sxb1sE.js} +1 -1
- package/src/assets/web-panel/assets/{index-UUMCA2Sq.js → index-CP9m9Ggr.js} +4 -4
- package/src/assets/web-panel/assets/{index--Bm6OqmU.js → index-CTOCnIQ7.js} +1 -1
- package/src/assets/web-panel/assets/index-Cg2ldRfU.js +1 -0
- package/src/assets/web-panel/assets/{index-Dv7ffI2g.js → index-ClhAHDK3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcunsBIx.js → index-CnXebZLL.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvAqCAo9.js → index-CngCC8hC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcMkjb92.js → index-Cr-A0FYJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFGfC5RS.js → index-CsAsCPJ6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDZ6fP5Z.js → index-D28DeAAB.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgIfXOD9.js → index-D5LZVZ0L.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmZ6-suL.js → index-D65_XseV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cs-RKRUM.js → index-D8vwp4qp.js} +1 -1
- package/src/assets/web-panel/assets/{index-B994UQfE.js → index-DDwLgQY9.js} +1 -1
- package/src/assets/web-panel/assets/{index-Btz8pU68.js → index-DIpY0qM5.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5E4DhVc.js → index-DTWg-18U.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2jrnaq3.js → index-DTtRscUa.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmCo2Xdp.js → index-DYAtmqiv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Zvtru2oE.js → index-D_n8_vfI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLAZs1Vd.js → index-DbADHwhr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CucRo4Vz.js → index-DbVsQBOH.js} +1 -1
- package/src/assets/web-panel/assets/index-Dwzo-dtJ.js +1 -0
- package/src/assets/web-panel/assets/{index-C0DiL97c.js → index-HhNjnCfe.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bl-E4W4q.js → index-ToUh2b1-.js} +1 -1
- package/src/assets/web-panel/assets/{index-ryFLxB1p.js → index-TslMTwNy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGQtFso0.js → index-_FIkN3jd.js} +1 -1
- package/src/assets/web-panel/assets/{index-zLwYpvX0.js → index-hpvvtAZ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dvg5xYuP.js → index-jzR7Ujuw.js} +1 -1
- package/src/assets/web-panel/assets/{index-xvUVuFnA.js → index-ljuBiQW9.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CC--PMsC.js → initDefaultProps-C3wUb4je.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DdrBjRF1.js → motion-D-jYFUNA.js} +1 -1
- package/src/assets/web-panel/assets/{move-D712Gwl2.js → move-tqb8nwJ2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-39y-keKO.js → mtc-parser-DSOjMJMz.js} +1 -1
- package/src/assets/web-panel/assets/{omit-6Y51gDB9.js → omit-CJ7VimIW.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Cy6EHbq9.js → pickAttrs-qXiG1iT3.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BYxdAm0p.js → placementArrow-CkGBcy1Z.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C9R6q_cr.js → responsiveObserve-D3WvTlLO.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CXUJlilB.js → slide-BEE2ftge.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DOtrOeql.js → statusUtils-CsfBpxiG.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-D5r39o92.js → styleChecker-xPrIiJPI.js} +1 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-C5Yd-SCR.js +1 -0
- package/src/assets/web-panel/assets/{useFs-BkrT-Zbc.js → useFs-D_MDS8HI.js} +1 -1
- package/src/assets/web-panel/assets/{useMergedState-Cl2q1fes.js → useMergedState-Dc9sO2d7.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BwnnnZjU.js → usePersonalDataHub-BqJh-usA.js} +1 -1
- package/src/assets/web-panel/assets/{useRefs-DD9q4mOT.js → useRefs-CAeFRzPf.js} +1 -1
- package/src/assets/web-panel/assets/{useState-BK4hy8Ma.js → useState-LwExUYDO.js} +1 -1
- package/src/assets/web-panel/assets/{vendor-BvqAck49.js → vendor-BMxUs6UX.js} +16 -16
- package/src/assets/web-panel/assets/{vnode-SU-N4pum.js → vnode-chm_p-gz.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DHScfpTP.js → zoom-3-YDNz0Z.js} +1 -1
- package/src/assets/web-panel/index.html +3 -3
- package/src/assets/web-panel/remote-session-sw.js +43 -0
- package/src/command-manifest.json +22 -1
- package/src/commands/agent.js +44 -5
- package/src/commands/codeintel.js +252 -0
- package/src/commands/eval.js +248 -0
- package/src/commands/plugin.js +354 -0
- package/src/commands/serve.js +10 -0
- package/src/commands/team.js +456 -0
- package/src/gateways/remote-session-relay.js +160 -0
- package/src/gateways/ws/message-dispatcher.js +77 -2
- package/src/gateways/ws/remote-session-protocol.js +466 -0
- package/src/gateways/ws/ws-server.js +299 -0
- package/src/harness/golden-transcript.js +65 -0
- package/src/harness/mcp-client.js +6 -2
- package/src/harness/prompt-compressor.js +27 -1
- package/src/harness/remote-command-ledger.js +158 -0
- package/src/harness/remote-session-audit-sink.js +132 -0
- package/src/harness/remote-session-audit.js +110 -0
- package/src/harness/remote-session-crypto.js +217 -0
- package/src/harness/remote-session-push-apns.js +246 -0
- package/src/harness/remote-session-push-errors.js +15 -0
- package/src/harness/remote-session-push-fcm.js +238 -0
- package/src/harness/remote-session-push-huawei.js +174 -0
- package/src/harness/remote-session-push-oppo.js +171 -0
- package/src/harness/remote-session-push-senders.js +42 -0
- package/src/harness/remote-session-push-vivo.js +179 -0
- package/src/harness/remote-session-push-web.js +299 -0
- package/src/harness/remote-session-push-xiaomi.js +99 -0
- package/src/harness/remote-session-push.js +104 -0
- package/src/harness/remote-session-registry.js +420 -0
- package/src/index.js +6 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/agent-sandbox.js +161 -5
- package/src/lib/agent-team/task-lease.js +434 -0
- package/src/lib/agent-team/team-budget.js +165 -0
- package/src/lib/agent-team/team-mailbox.js +106 -0
- package/src/lib/agent-team/team-runner.js +266 -0
- package/src/lib/agent-team/team-worktree.js +204 -0
- package/src/lib/agents.js +18 -0
- package/src/lib/async-hook-supervisor.cjs +305 -0
- package/src/lib/did-manager.js +6 -4
- package/src/lib/eval/runner.js +163 -0
- package/src/lib/eval/tasks.js +456 -0
- package/src/lib/eval/trend.js +185 -0
- package/src/lib/lsp/__tests__/code-intelligence.integration.test.js +129 -0
- package/src/lib/lsp/__tests__/code-intelligence.test.js +228 -0
- package/src/lib/lsp/__tests__/jsonrpc-stream.test.js +104 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +258 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +264 -0
- package/src/lib/lsp/__tests__/lsp-server-registry.test.js +128 -0
- package/src/lib/lsp/code-intelligence.js +356 -0
- package/src/lib/lsp/jsonrpc-stream.js +139 -0
- package/src/lib/lsp/lsp-client.js +318 -0
- package/src/lib/lsp/lsp-manager.js +323 -0
- package/src/lib/lsp/lsp-server-registry.js +196 -0
- package/src/lib/mcp-oauth.js +23 -6
- package/src/lib/plugin-monitor-supervisor.js +238 -0
- package/src/lib/plugin-runtime/agents.js +51 -0
- package/src/lib/plugin-runtime/bin.js +100 -0
- package/src/lib/plugin-runtime/hooks.js +100 -0
- package/src/lib/plugin-runtime/install.js +388 -0
- package/src/lib/plugin-runtime/lsp.js +75 -0
- package/src/lib/plugin-runtime/manifest.js +458 -0
- package/src/lib/plugin-runtime/mcp.js +89 -0
- package/src/lib/plugin-runtime/monitors.js +100 -0
- package/src/lib/plugin-runtime/policy.js +144 -0
- package/src/lib/plugin-runtime/reload.js +79 -0
- package/src/lib/plugin-runtime/scopes.js +197 -0
- package/src/lib/plugin-runtime/settings.js +119 -0
- package/src/lib/plugin-runtime/signature.js +107 -0
- package/src/lib/plugin-runtime/skills.js +43 -0
- package/src/lib/plugin-runtime/trust.js +140 -0
- package/src/lib/sandbox-egress-proxy.js +162 -0
- package/src/lib/sandbox-network-policy.js +134 -0
- package/src/lib/sandbox-v2.js +10 -4
- package/src/lib/session-manager.js +7 -3
- package/src/lib/settings-hook-events.cjs +78 -6
- package/src/lib/settings-hooks.cjs +29 -3
- package/src/lib/settings-loader.cjs +35 -1
- package/src/lib/skill-loader.js +18 -0
- package/src/lib/telemetry/span-recorder.js +237 -0
- package/src/repl/agent-repl.js +400 -4
- package/src/runtime/agent-core.js +546 -52
- package/src/runtime/agent-runtime.js +8 -13
- package/src/runtime/coding-agent-contract-shared.cjs +82 -8
- package/src/runtime/coding-agent-policy.cjs +32 -7
- package/src/runtime/fallback-model.js +134 -7
- package/src/runtime/headless-runner.js +353 -108
- package/src/runtime/mcp-config.js +46 -0
- package/src/runtime/policies/agent-policy.js +2 -0
- package/src/assets/web-panel/assets/Compliance-T8EtTecW.js +0 -1
- package/src/assets/web-panel/assets/MobileBridge-CYaITZ7w.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CmtqIdwr.js +0 -1
- package/src/assets/web-panel/assets/Projects-DPd5-lNS.js +0 -1
- package/src/assets/web-panel/assets/Terminal-BzOHpq-B.js +0 -3
- package/src/assets/web-panel/assets/devWarning-6YJJMjSH.js +0 -1
- package/src/assets/web-panel/assets/index-D_oeGLr6.js +0 -1
- package/src/assets/web-panel/assets/index-Qa57YG19.js +0 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-DfAD5U4Z.js +0 -1
|
@@ -7,6 +7,61 @@ import {
|
|
|
7
7
|
PERSONAL_DATA_HUB_HANDLERS,
|
|
8
8
|
PERSONAL_DATA_HUB_STREAMING_HANDLERS,
|
|
9
9
|
} from "./personal-data-hub-protocol.js";
|
|
10
|
+
import {
|
|
11
|
+
handleRemoteSessionAudit,
|
|
12
|
+
handleRemoteSessionCreate,
|
|
13
|
+
handleRemoteSessionClose,
|
|
14
|
+
handleRemoteSessionDevices,
|
|
15
|
+
handleRemoteSessionJoin,
|
|
16
|
+
handleRemoteSessionPairingToken,
|
|
17
|
+
handleRemoteSessionPolicy,
|
|
18
|
+
handleRemoteSessionPublish,
|
|
19
|
+
handleRemoteSessionPushRegister,
|
|
20
|
+
handleRemoteSessionRevoke,
|
|
21
|
+
} from "./remote-session-protocol.js";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Reconnect-safe command execution. When a message carries a stable
|
|
25
|
+
* `commandId`, route it through the server's RemoteCommandLedger so the side
|
|
26
|
+
* effect runs AT MOST ONCE: a client that re-sends the same command after a
|
|
27
|
+
* dropped ACK / reconnect gets a `replayed` ack instead of a second execution
|
|
28
|
+
* (Phase 5 acceptance "断网恢复后不会重复执行工具调用"). Messages WITHOUT a
|
|
29
|
+
* commandId are unchanged — they call `_executeCommand` directly, so existing
|
|
30
|
+
* clients behave exactly as before. A revoked device / stale-seq command is
|
|
31
|
+
* rejected without executing.
|
|
32
|
+
*/
|
|
33
|
+
async function executeWithIdempotency(server, id, ws, message, stream) {
|
|
34
|
+
const commandId = message.commandId;
|
|
35
|
+
if (!commandId) {
|
|
36
|
+
return server._executeCommand(id, ws, message.command, stream);
|
|
37
|
+
}
|
|
38
|
+
if (!server._commandLedger) {
|
|
39
|
+
const { RemoteCommandLedger } =
|
|
40
|
+
await import("../../harness/remote-command-ledger.js");
|
|
41
|
+
server._commandLedger = new RemoteCommandLedger();
|
|
42
|
+
}
|
|
43
|
+
const outcome = await server._commandLedger.apply(
|
|
44
|
+
{ commandId, deviceId: message.deviceId ?? null, seq: message.seq },
|
|
45
|
+
() => server._executeCommand(id, ws, message.command, stream),
|
|
46
|
+
);
|
|
47
|
+
if (outcome.status === "replayed") {
|
|
48
|
+
server._send(ws, {
|
|
49
|
+
id,
|
|
50
|
+
type: "replayed",
|
|
51
|
+
commandId,
|
|
52
|
+
applyIndex: outcome.applyIndex,
|
|
53
|
+
});
|
|
54
|
+
} else if (outcome.status === "rejected") {
|
|
55
|
+
server._send(ws, {
|
|
56
|
+
id,
|
|
57
|
+
type: "error",
|
|
58
|
+
code: "COMMAND_REJECTED",
|
|
59
|
+
commandId,
|
|
60
|
+
message: outcome.reason,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return outcome;
|
|
64
|
+
}
|
|
10
65
|
|
|
11
66
|
export function createWsMessageDispatcher(server) {
|
|
12
67
|
return {
|
|
@@ -40,8 +95,8 @@ export function createWsMessageDispatcher(server) {
|
|
|
40
95
|
auth: () => server._handleAuth(clientId, ws, message),
|
|
41
96
|
ping: () =>
|
|
42
97
|
server._send(ws, { id, type: "pong", serverTime: Date.now() }),
|
|
43
|
-
execute: () => server
|
|
44
|
-
stream: () => server
|
|
98
|
+
execute: () => executeWithIdempotency(server, id, ws, message, false),
|
|
99
|
+
stream: () => executeWithIdempotency(server, id, ws, message, true),
|
|
45
100
|
cancel: () => server._cancelRequest(id, ws),
|
|
46
101
|
"session-create": () => server._handleSessionCreate(id, ws, message),
|
|
47
102
|
"session-resume": () => server._handleSessionResume(id, ws, message),
|
|
@@ -54,6 +109,26 @@ export function createWsMessageDispatcher(server) {
|
|
|
54
109
|
server._handleSessionInterrupt(id, ws, message),
|
|
55
110
|
"slash-command": () => server._handleSlashCommand(id, ws, message),
|
|
56
111
|
"session-answer": () => server._handleSessionAnswer(id, ws, message),
|
|
112
|
+
"remote-session-create": () =>
|
|
113
|
+
handleRemoteSessionCreate(server, clientId, ws, message),
|
|
114
|
+
"remote-session-close": () =>
|
|
115
|
+
handleRemoteSessionClose(server, clientId, ws, message),
|
|
116
|
+
"remote-session-pairing-token": () =>
|
|
117
|
+
handleRemoteSessionPairingToken(server, clientId, ws, message),
|
|
118
|
+
"remote-session-join": () =>
|
|
119
|
+
handleRemoteSessionJoin(server, clientId, ws, message),
|
|
120
|
+
"remote-session-devices": () =>
|
|
121
|
+
handleRemoteSessionDevices(server, clientId, ws, message),
|
|
122
|
+
"remote-session-audit": () =>
|
|
123
|
+
handleRemoteSessionAudit(server, clientId, ws, message),
|
|
124
|
+
"remote-session-policy": () =>
|
|
125
|
+
handleRemoteSessionPolicy(server, clientId, ws, message),
|
|
126
|
+
"remote-session-push-register": () =>
|
|
127
|
+
handleRemoteSessionPushRegister(server, clientId, ws, message),
|
|
128
|
+
"remote-session-revoke": () =>
|
|
129
|
+
handleRemoteSessionRevoke(server, clientId, ws, message),
|
|
130
|
+
"remote-session-publish": () =>
|
|
131
|
+
handleRemoteSessionPublish(server, clientId, ws, message),
|
|
57
132
|
"host-tool-result": () => server._handleHostToolResult(id, ws, message),
|
|
58
133
|
orchestrate: () => server._handleOrchestrate(id, ws, message),
|
|
59
134
|
"cowork-task": () => server._handleCoworkTask(id, ws, message),
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import {
|
|
2
|
+
handleSessionAnswer,
|
|
3
|
+
handleSessionInterrupt,
|
|
4
|
+
handleSessionMessage,
|
|
5
|
+
} from "./session-protocol.js";
|
|
6
|
+
import {
|
|
7
|
+
RemoteSessionCryptoContext,
|
|
8
|
+
createRemotePairingUri,
|
|
9
|
+
} from "../../harness/remote-session-crypto.js";
|
|
10
|
+
|
|
11
|
+
const CLIENT_EVENT_SCOPES = Object.freeze({
|
|
12
|
+
prompt: "prompt",
|
|
13
|
+
"approval.resolve": "approve",
|
|
14
|
+
interrupt: "interrupt",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function reply(server, ws, id, type, payload = {}) {
|
|
18
|
+
server._send(ws, { id, type, ...payload });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function audit(server, entry) {
|
|
22
|
+
server.remoteSessionAudit?.record(entry);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function attachPairingUri(server, result) {
|
|
26
|
+
const relayUrl = server.remoteSessionRelayUrl;
|
|
27
|
+
const hostPeerId = server.remoteSessionPeerId;
|
|
28
|
+
if (!relayUrl || !hostPeerId) return result;
|
|
29
|
+
server.remoteSessionPairingSecrets.set(
|
|
30
|
+
result.session.sessionId,
|
|
31
|
+
result.pairing.token,
|
|
32
|
+
);
|
|
33
|
+
let crypto = server.remoteSessionCrypto.get(result.session.sessionId);
|
|
34
|
+
if (!crypto) {
|
|
35
|
+
crypto = new RemoteSessionCryptoContext({
|
|
36
|
+
sessionId: result.session.sessionId,
|
|
37
|
+
localPeerId: hostPeerId,
|
|
38
|
+
});
|
|
39
|
+
server.remoteSessionCrypto.set(result.session.sessionId, crypto);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
...result,
|
|
43
|
+
pairing: {
|
|
44
|
+
...result.pairing,
|
|
45
|
+
hostPublicKey: crypto.publicKey,
|
|
46
|
+
uri: createRemotePairingUri({
|
|
47
|
+
relayUrl,
|
|
48
|
+
remoteSessionId: result.session.sessionId,
|
|
49
|
+
hostPeerId,
|
|
50
|
+
hostPublicKey: crypto.publicKey,
|
|
51
|
+
pairingToken: result.pairing.token,
|
|
52
|
+
expiresAt: result.pairing.expiresAt,
|
|
53
|
+
}),
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function handleRemoteSessionCreate(server, clientId, ws, message) {
|
|
59
|
+
try {
|
|
60
|
+
const result = server.remoteSessions.create({
|
|
61
|
+
hostClientId: clientId,
|
|
62
|
+
agentSessionId: message.sessionId,
|
|
63
|
+
name: message.name,
|
|
64
|
+
scopes: message.scopes,
|
|
65
|
+
});
|
|
66
|
+
audit(server, {
|
|
67
|
+
sessionId: result.session.sessionId,
|
|
68
|
+
actor: clientId,
|
|
69
|
+
action: "session.created",
|
|
70
|
+
detail: { agentSessionId: message.sessionId, name: message.name || null },
|
|
71
|
+
});
|
|
72
|
+
reply(
|
|
73
|
+
server,
|
|
74
|
+
ws,
|
|
75
|
+
message.id,
|
|
76
|
+
"remote-session-created",
|
|
77
|
+
attachPairingUri(server, result),
|
|
78
|
+
);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
reply(server, ws, message.id, "error", {
|
|
81
|
+
code: "REMOTE_SESSION_CREATE_ERROR",
|
|
82
|
+
message: error.message,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function handleRemoteSessionPairingToken(server, clientId, ws, message) {
|
|
88
|
+
try {
|
|
89
|
+
const { session } = server.remoteSessions.authorize(
|
|
90
|
+
message.remoteSessionId,
|
|
91
|
+
clientId,
|
|
92
|
+
"observe",
|
|
93
|
+
);
|
|
94
|
+
if (session.hostClientId !== clientId) {
|
|
95
|
+
throw new Error("Only the host can issue pairing tokens");
|
|
96
|
+
}
|
|
97
|
+
const pairing = server.remoteSessions.issuePairingToken(
|
|
98
|
+
message.remoteSessionId,
|
|
99
|
+
{ scopes: message.scopes },
|
|
100
|
+
);
|
|
101
|
+
const result = attachPairingUri(server, {
|
|
102
|
+
session: {
|
|
103
|
+
sessionId: message.remoteSessionId,
|
|
104
|
+
},
|
|
105
|
+
pairing,
|
|
106
|
+
});
|
|
107
|
+
audit(server, {
|
|
108
|
+
sessionId: message.remoteSessionId,
|
|
109
|
+
actor: clientId,
|
|
110
|
+
action: "pairing-token.issued",
|
|
111
|
+
detail: { scopes: message.scopes || null, expiresAt: pairing.expiresAt },
|
|
112
|
+
});
|
|
113
|
+
reply(server, ws, message.id, "remote-session-pairing-token", {
|
|
114
|
+
pairing: result.pairing,
|
|
115
|
+
});
|
|
116
|
+
} catch (error) {
|
|
117
|
+
reply(server, ws, message.id, "error", {
|
|
118
|
+
code: "REMOTE_SESSION_PAIRING_ERROR",
|
|
119
|
+
message: error.message,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function handleRemoteSessionJoin(server, clientId, ws, message) {
|
|
125
|
+
try {
|
|
126
|
+
const result = server.remoteSessions.join({
|
|
127
|
+
sessionId: message.remoteSessionId,
|
|
128
|
+
clientId,
|
|
129
|
+
token: message.token,
|
|
130
|
+
pushToken: message.pushToken,
|
|
131
|
+
pushProvider: message.pushProvider,
|
|
132
|
+
});
|
|
133
|
+
audit(server, {
|
|
134
|
+
sessionId: message.remoteSessionId,
|
|
135
|
+
actor: clientId,
|
|
136
|
+
action: "device.joined",
|
|
137
|
+
detail: {
|
|
138
|
+
scopes: result.member.scopes,
|
|
139
|
+
via: "direct",
|
|
140
|
+
hasPush: result.member.pushToken ? true : false,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
reply(server, ws, message.id, "remote-session-joined", result);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
reply(server, ws, message.id, "error", {
|
|
146
|
+
code: "REMOTE_SESSION_JOIN_ERROR",
|
|
147
|
+
message: error.message,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function handleRemoteSessionPushRegister(server, clientId, ws, message) {
|
|
153
|
+
try {
|
|
154
|
+
// A device may only register its OWN token (clientId is the authenticated
|
|
155
|
+
// caller). Storing no push value clears it.
|
|
156
|
+
const result = server.remoteSessions.registerPush(
|
|
157
|
+
message.remoteSessionId,
|
|
158
|
+
clientId,
|
|
159
|
+
{ token: message.pushToken, provider: message.pushProvider },
|
|
160
|
+
);
|
|
161
|
+
audit(server, {
|
|
162
|
+
sessionId: message.remoteSessionId,
|
|
163
|
+
actor: clientId,
|
|
164
|
+
action: "push.registered",
|
|
165
|
+
detail: { hasPush: result.hasPush, provider: result.provider },
|
|
166
|
+
});
|
|
167
|
+
reply(server, ws, message.id, "remote-session-push-registered", result);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
reply(server, ws, message.id, "error", {
|
|
170
|
+
code: "REMOTE_SESSION_PUSH_REGISTER_ERROR",
|
|
171
|
+
message: error.message,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function handleRemoteSessionDevices(server, clientId, ws, message) {
|
|
177
|
+
try {
|
|
178
|
+
const result = server.remoteSessions.listDevices(
|
|
179
|
+
message.remoteSessionId,
|
|
180
|
+
clientId,
|
|
181
|
+
);
|
|
182
|
+
reply(server, ws, message.id, "remote-session-devices", result);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
reply(server, ws, message.id, "error", {
|
|
185
|
+
code: "REMOTE_SESSION_DEVICES_ERROR",
|
|
186
|
+
message: error.message,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function handleRemoteSessionPolicy(server, clientId, ws, message) {
|
|
192
|
+
try {
|
|
193
|
+
// The active org policy is not sensitive — any authenticated client may
|
|
194
|
+
// read it (e.g. to pre-check allowed scopes before requesting a session).
|
|
195
|
+
const policy = server.remoteSessions?.policy;
|
|
196
|
+
reply(server, ws, message.id, "remote-session-policy", {
|
|
197
|
+
policy: policy ? policy.describe() : null,
|
|
198
|
+
});
|
|
199
|
+
} catch (error) {
|
|
200
|
+
reply(server, ws, message.id, "error", {
|
|
201
|
+
code: "REMOTE_SESSION_POLICY_ERROR",
|
|
202
|
+
message: error.message,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function handleRemoteSessionAudit(server, clientId, ws, message) {
|
|
208
|
+
try {
|
|
209
|
+
// Host-only: authorize proves membership, the host check proves ownership.
|
|
210
|
+
const { session } = server.remoteSessions.authorize(
|
|
211
|
+
message.remoteSessionId,
|
|
212
|
+
clientId,
|
|
213
|
+
"observe",
|
|
214
|
+
);
|
|
215
|
+
if (session.hostClientId !== clientId) {
|
|
216
|
+
throw new Error("Only the host can read the audit log");
|
|
217
|
+
}
|
|
218
|
+
const auditLog = server.remoteSessionAudit;
|
|
219
|
+
const entries = auditLog
|
|
220
|
+
? auditLog.list({
|
|
221
|
+
sessionId: message.remoteSessionId,
|
|
222
|
+
limit: message.limit || 200,
|
|
223
|
+
})
|
|
224
|
+
: [];
|
|
225
|
+
const stats = auditLog
|
|
226
|
+
? auditLog.stats(message.remoteSessionId)
|
|
227
|
+
: { total: 0, byAction: {} };
|
|
228
|
+
reply(server, ws, message.id, "remote-session-audit", {
|
|
229
|
+
remoteSessionId: message.remoteSessionId,
|
|
230
|
+
entries,
|
|
231
|
+
stats,
|
|
232
|
+
});
|
|
233
|
+
} catch (error) {
|
|
234
|
+
reply(server, ws, message.id, "error", {
|
|
235
|
+
code: "REMOTE_SESSION_AUDIT_ERROR",
|
|
236
|
+
message: error.message,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Tell a revoked device it is no longer paired. Locally connected clients get a
|
|
243
|
+
* plaintext `remote-session-revoked` push; relay-paired mobile devices get an
|
|
244
|
+
* encrypted `session.revoked` control event so they stop auto-reconnecting.
|
|
245
|
+
*/
|
|
246
|
+
function notifyRevokedDevice(
|
|
247
|
+
server,
|
|
248
|
+
remoteSessionId,
|
|
249
|
+
agentSessionId,
|
|
250
|
+
clientId,
|
|
251
|
+
) {
|
|
252
|
+
const target = server.clients.get(clientId);
|
|
253
|
+
if (target) {
|
|
254
|
+
server._send(target.ws, {
|
|
255
|
+
type: "remote-session-revoked",
|
|
256
|
+
remoteSessionId,
|
|
257
|
+
agentSessionId,
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (!server.remoteSessionRelay) return;
|
|
262
|
+
const crypto = server.remoteSessionCrypto.get(remoteSessionId);
|
|
263
|
+
if (!crypto) return;
|
|
264
|
+
try {
|
|
265
|
+
server.remoteSessionRelay.sendEncrypted(
|
|
266
|
+
clientId,
|
|
267
|
+
crypto.encrypt(clientId, { type: "session.revoked", remoteSessionId }),
|
|
268
|
+
);
|
|
269
|
+
} catch {
|
|
270
|
+
// Peer key may already be gone; the registry removal is what actually
|
|
271
|
+
// enforces revocation, so a failed courtesy notice is non-fatal.
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function handleRemoteSessionRevoke(server, clientId, ws, message) {
|
|
276
|
+
try {
|
|
277
|
+
const targetClientId = message.clientId || message.deviceId;
|
|
278
|
+
const { session, member } = server.remoteSessions.revokeMember(
|
|
279
|
+
message.remoteSessionId,
|
|
280
|
+
clientId,
|
|
281
|
+
targetClientId,
|
|
282
|
+
);
|
|
283
|
+
notifyRevokedDevice(
|
|
284
|
+
server,
|
|
285
|
+
message.remoteSessionId,
|
|
286
|
+
session.agentSessionId,
|
|
287
|
+
member.clientId,
|
|
288
|
+
);
|
|
289
|
+
audit(server, {
|
|
290
|
+
sessionId: message.remoteSessionId,
|
|
291
|
+
actor: clientId,
|
|
292
|
+
action: "device.revoked",
|
|
293
|
+
detail: { revoked: member.clientId },
|
|
294
|
+
});
|
|
295
|
+
reply(server, ws, message.id, "remote-session-revoked", {
|
|
296
|
+
session,
|
|
297
|
+
revoked: member.clientId,
|
|
298
|
+
devices: server.remoteSessions.listDevices(
|
|
299
|
+
message.remoteSessionId,
|
|
300
|
+
clientId,
|
|
301
|
+
).devices,
|
|
302
|
+
});
|
|
303
|
+
} catch (error) {
|
|
304
|
+
reply(server, ws, message.id, "error", {
|
|
305
|
+
code: "REMOTE_SESSION_REVOKE_ERROR",
|
|
306
|
+
message: error.message,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function handleRemoteSessionClose(server, clientId, ws, message) {
|
|
312
|
+
try {
|
|
313
|
+
const session = server.remoteSessions.close(
|
|
314
|
+
message.remoteSessionId,
|
|
315
|
+
clientId,
|
|
316
|
+
);
|
|
317
|
+
server.remoteSessionCrypto.delete(message.remoteSessionId);
|
|
318
|
+
server.remoteSessionPairingSecrets.delete(message.remoteSessionId);
|
|
319
|
+
audit(server, {
|
|
320
|
+
sessionId: message.remoteSessionId,
|
|
321
|
+
actor: clientId,
|
|
322
|
+
action: "session.closed",
|
|
323
|
+
detail: { reason: "host-closed" },
|
|
324
|
+
});
|
|
325
|
+
reply(server, ws, message.id, "remote-session-closed", { session });
|
|
326
|
+
} catch (error) {
|
|
327
|
+
reply(server, ws, message.id, "error", {
|
|
328
|
+
code: "REMOTE_SESSION_CLOSE_ERROR",
|
|
329
|
+
message: error.message,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export async function handleRemoteSessionPublish(
|
|
335
|
+
server,
|
|
336
|
+
clientId,
|
|
337
|
+
ws,
|
|
338
|
+
message,
|
|
339
|
+
) {
|
|
340
|
+
try {
|
|
341
|
+
if (!message.event || typeof message.event.type !== "string") {
|
|
342
|
+
throw new Error("event.type is required");
|
|
343
|
+
}
|
|
344
|
+
// A paired device (incl. relay-only mobiles) refreshing its own vendor push
|
|
345
|
+
// token — e.g. after FCM onNewToken. Self-scoped: authorize proves
|
|
346
|
+
// membership and registerPush keys off the authenticated clientId, so a
|
|
347
|
+
// device can only ever set its OWN token. Omitting the token clears it.
|
|
348
|
+
if (message.event.type === "push.register") {
|
|
349
|
+
server.remoteSessions.authorize(
|
|
350
|
+
message.remoteSessionId,
|
|
351
|
+
clientId,
|
|
352
|
+
"observe",
|
|
353
|
+
);
|
|
354
|
+
const registered = server.remoteSessions.registerPush(
|
|
355
|
+
message.remoteSessionId,
|
|
356
|
+
clientId,
|
|
357
|
+
{
|
|
358
|
+
token: message.event.pushToken,
|
|
359
|
+
provider: message.event.pushProvider,
|
|
360
|
+
},
|
|
361
|
+
);
|
|
362
|
+
audit(server, {
|
|
363
|
+
sessionId: message.remoteSessionId,
|
|
364
|
+
actor: clientId,
|
|
365
|
+
action: "push.registered",
|
|
366
|
+
detail: {
|
|
367
|
+
hasPush: registered.hasPush,
|
|
368
|
+
provider: registered.provider,
|
|
369
|
+
via: "relay",
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
reply(
|
|
373
|
+
server,
|
|
374
|
+
ws,
|
|
375
|
+
message.id,
|
|
376
|
+
"remote-session-push-registered",
|
|
377
|
+
registered,
|
|
378
|
+
);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const requiredScope = CLIENT_EVENT_SCOPES[message.event.type];
|
|
382
|
+
const { session } = server.remoteSessions.authorize(
|
|
383
|
+
message.remoteSessionId,
|
|
384
|
+
clientId,
|
|
385
|
+
requiredScope || "observe",
|
|
386
|
+
);
|
|
387
|
+
// Runtime/output events are host-only. Remote clients may publish only the
|
|
388
|
+
// three explicitly scoped control event types above.
|
|
389
|
+
if (!requiredScope && session.hostClientId !== clientId) {
|
|
390
|
+
throw new Error("Only the host can publish runtime events");
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (requiredScope && session.hostClientId !== clientId) {
|
|
394
|
+
const controlMessage = {
|
|
395
|
+
id: message.id,
|
|
396
|
+
sessionId: session.agentSessionId,
|
|
397
|
+
};
|
|
398
|
+
if (message.event.type === "prompt") {
|
|
399
|
+
if (
|
|
400
|
+
typeof message.event.content !== "string" ||
|
|
401
|
+
!message.event.content.trim()
|
|
402
|
+
) {
|
|
403
|
+
throw new Error("prompt content is required");
|
|
404
|
+
}
|
|
405
|
+
// Record the shape, not the content — the audit trail must stay useful
|
|
406
|
+
// without hoarding potentially sensitive session prompts.
|
|
407
|
+
audit(server, {
|
|
408
|
+
sessionId: message.remoteSessionId,
|
|
409
|
+
actor: clientId,
|
|
410
|
+
action: "control.prompt",
|
|
411
|
+
detail: { chars: message.event.content.length },
|
|
412
|
+
});
|
|
413
|
+
handleSessionMessage(server, message.id, ws, {
|
|
414
|
+
...controlMessage,
|
|
415
|
+
content: message.event.content,
|
|
416
|
+
});
|
|
417
|
+
} else if (message.event.type === "approval.resolve") {
|
|
418
|
+
audit(server, {
|
|
419
|
+
sessionId: message.remoteSessionId,
|
|
420
|
+
actor: clientId,
|
|
421
|
+
action: "control.approval",
|
|
422
|
+
detail: {
|
|
423
|
+
requestId: message.event.requestId || message.event.approvalId,
|
|
424
|
+
approved: message.event.answer ?? message.event.approved,
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
await handleSessionAnswer(server, message.id, ws, {
|
|
428
|
+
...controlMessage,
|
|
429
|
+
requestId: message.event.requestId || message.event.approvalId,
|
|
430
|
+
answer: message.event.answer ?? message.event.approved,
|
|
431
|
+
});
|
|
432
|
+
} else if (message.event.type === "interrupt") {
|
|
433
|
+
audit(server, {
|
|
434
|
+
sessionId: message.remoteSessionId,
|
|
435
|
+
actor: clientId,
|
|
436
|
+
action: "control.interrupt",
|
|
437
|
+
detail: null,
|
|
438
|
+
});
|
|
439
|
+
await handleSessionInterrupt(server, message.id, ws, controlMessage);
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
let delivered = 0;
|
|
445
|
+
for (const member of server.remoteSessions.members(
|
|
446
|
+
message.remoteSessionId,
|
|
447
|
+
)) {
|
|
448
|
+
if (member.clientId === clientId) continue;
|
|
449
|
+
const target = server.clients.get(member.clientId);
|
|
450
|
+
if (!target) continue;
|
|
451
|
+
server._send(target.ws, {
|
|
452
|
+
type: "remote-session-event",
|
|
453
|
+
remoteSessionId: message.remoteSessionId,
|
|
454
|
+
agentSessionId: session.agentSessionId,
|
|
455
|
+
event: message.event,
|
|
456
|
+
});
|
|
457
|
+
delivered += 1;
|
|
458
|
+
}
|
|
459
|
+
reply(server, ws, message.id, "remote-session-published", { delivered });
|
|
460
|
+
} catch (error) {
|
|
461
|
+
reply(server, ws, message.id, "error", {
|
|
462
|
+
code: "REMOTE_SESSION_PUBLISH_ERROR",
|
|
463
|
+
message: error.message,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|