@xopcai/xopc 0.0.421 → 0.0.422
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/dist/browser-ext/dist/background.js +184 -2
- package/dist/browser-ext/dist/sidepanel.js +9 -0
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-CKrylgib.js → activity-store-CvJ9rdwP.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Dv5Rda4y.js → agent-avatar-dicebear-CFP6g4w7.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-display-B1ycycx2.js → agent-avatar-display-BHBe88Gu.js} +2 -2
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-D285zy0S.js → agent-defaults-settings-panel-DlGRuKMv.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-plugin-dialog-D3yCqVan.js → agent-plugin-dialog-C_Dm-u6_.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-DyAuR3rG.js → agents-BdVJHOiK.js} +3 -3
- package/dist/gateway/static/root/assets/{agents-admin-api-CASGzvkW.js → agents-admin-api-Do1Pza0R.js} +1 -1
- package/dist/gateway/static/root/assets/{api-Uk05lF9i.js → api-BQv5dLcH.js} +1 -1
- package/dist/gateway/static/root/assets/{api-C4LxuNzq.js → api-ONg37_fH.js} +1 -1
- package/dist/gateway/static/root/assets/app-link-1j40sJ-z.js +1 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CDnZNKHt.js → app-management-settings-panel-B4Wqhf4J.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-CmCF7PZ5.js → appearance-settings-Dm1kRzKL.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-BR9qoA2P.js → apps-page-D1MMk1P7.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-nuHYYVty.js → archive-plugin-p_yLkyaC.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-api-DtJfd1QM.js → automation-api-DQaWBR03.js} +1 -1
- package/dist/gateway/static/root/assets/automation-result-state-CO_243JM.js +1 -0
- package/dist/gateway/static/root/assets/automation-task-card-D2p9qlYE.js +3 -0
- package/dist/gateway/static/root/assets/{automations-page-DM4IdUJh.js → automations-page-Ba64H37d.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-BkzC3oIM.js +9 -0
- package/dist/gateway/static/root/assets/{binary-plugins-CHCAr8jt.js → binary-plugins-B-6kyqtt.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-B0v8KxsO.js → block-editor-B5ysJ96n.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automation-inputs-DjUW7PEB.js → browser-automation-inputs-C2A3NIt3.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-Cf8AoQz1.js → browser-automations-page-CnbXFw1_.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-control-api-C6NZgaA1.js → browser-control-api-Aj_seKE9.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-DV2FNEDf.js → browser-settings-page-C7fxPq_z.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-CwdjWz2r.js → capabilities-BgtCwC_E.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-D0-HCZ5J.js → capabilities-DOTgaSgA.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-page-DwDkritH.js → capabilities-page-m3b9sLFG.js} +2 -2
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-mme16Fbp.js → capabilities-settings-panel-pYWZtPeU.js} +3 -3
- package/dist/gateway/static/root/assets/{channels-lKFLu8p-.js → channels-DGpRM6pj.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-agents-api-l80cvSac.js → chat-agents-api-EvJ_Wvuc.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-chart-block-BwP5QXEk.js → chat-chart-block-BQDbHLcw.js} +1 -1
- package/dist/gateway/static/root/assets/chat-page-GF9NIuZN.js +72 -0
- package/dist/gateway/static/root/assets/{chat-terminal-dock-D2bV9YUL.js → chat-terminal-dock-DHFTGoEp.js} +2 -2
- package/dist/gateway/static/root/assets/{command-palette-api-CtapG8ck.js → command-palette-api-DPA_yEuy.js} +1 -1
- package/dist/gateway/static/root/assets/{composer-payload-handoff-DYuhKZRC.js → composer-payload-handoff-BTjauSNp.js} +1 -1
- package/dist/gateway/static/root/assets/{computer-settings-page-DZtMuihL.js → computer-settings-page-BFSSQfcx.js} +1 -1
- package/dist/gateway/static/root/assets/{connected-providers-grid-BQH5k3iC.js → connected-providers-grid-DM2glQN3.js} +1 -1
- package/dist/gateway/static/root/assets/{connector-service-page-DrvFdP3G.js → connector-service-page-CFvg58qa.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-api-BzkgVBYC.js → connectors-api-CbC6Yht5.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-BtRNVCFy.js → connectors-page-D7pipFHA.js} +1 -1
- package/dist/gateway/static/root/assets/date-picker-Dd3MbG0x.js +1 -0
- package/dist/gateway/static/root/assets/{dependency-picker-Dh8hPmsT.js → dependency-picker-DcsHg2ml.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-CFz8bqyI.js → desktop-pet-DldzQT2h.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-COaboNn1.js → desktop-pet-settings-CYdI4KxB.js} +1 -1
- package/dist/gateway/static/root/assets/{dev-view-store-D8XYHVqG.js → dev-view-store-JiJv585f.js} +1 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-D8cLc41G.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-Cjv7rdo3.js → directory-picker-path-field-8WOXv8zw.js} +1 -1
- package/dist/gateway/static/root/assets/{discussion-annotation-sync-YvlVw17j.js → discussion-annotation-sync-B-grSe0w.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-CXx3m5-B.js → extension-debug-page-EEQkTNEu.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-iframe-host-CnOdNwbo.js → extension-iframe-host-vlZVeMCY.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-marketplace-api-BABmUY2s.js → extension-marketplace-api-_o2In_al.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-C1nGr4DD.js → extension-page-CPrT111s.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-BA0Y2Rvs.js → extension-provider-B2Mf3FBu.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-QIPI8e7k.js → extension-settings-page-CznJEgWV.js} +1 -1
- package/dist/gateway/static/root/assets/{file-preview-surface-BsB7bnFH.js → file-preview-surface-7h8_fZul.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Bt8w0Iio.js → gateway-config-swr-DbbNEOKj.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-realtime-CyCCNUxm.js → gateway-realtime-D20ZrAuM.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-C6sMzYDT.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-restart-Ty4AzDxa.js → gateway-restart-5ut-lhJm.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-BvKq7vuz.js → gateway-settings-BCf_cibp.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-N3k-C7c2.js → gateway-startup-retry-DlHL0DpH.js} +1 -1
- package/dist/gateway/static/root/assets/{global-defaults-api-D8EB2vfY.js → global-defaults-api-CmODomcV.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-eRuAzi58.js → home-page-uz22TRB-.js} +2 -2
- package/dist/gateway/static/root/assets/{image-generation-api-C99Fsofm.js → image-generation-api-dzPnpUFu.js} +1 -1
- package/dist/gateway/static/root/assets/{imports-page-Cp9RquVb.js → imports-page-ChBoNMyE.js} +1 -1
- package/dist/gateway/static/root/assets/index-CSIg7V1A.js +11 -0
- package/dist/gateway/static/root/assets/index-Dv8mUlNN.css +1 -0
- package/dist/gateway/static/root/assets/{install-connector-dialog-CsoAidXp.js → install-connector-dialog-CNcdQDX0.js} +1 -1
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DFhOP2aD.js → keyboard-shortcuts-settings-CRbYq2_g.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-DbtU_A-l.js → local-app-workbench-page-04cX5TJu.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-rjhHfLpe.js → local-apps-page-fWM0FEhN.js} +1 -1
- package/dist/gateway/static/root/assets/{local-session-drafts-DHM4vWDO.js → local-session-drafts-ClrSe5mH.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-5Hf8J8-_.js → locale-store-VNjH18Dj.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-Dx_JIOpF.js → logs-page-6GanGLrm.js} +1 -1
- package/dist/gateway/static/root/assets/management-settings-BxD_lc4E.js +1 -0
- package/dist/gateway/static/root/assets/{markdown-view-pzSRu7av.js → markdown-view-DLCBYWtx.js} +2 -2
- package/dist/gateway/static/root/assets/{media-plugins-3cJuHbO7.js → media-plugins-BvtNwKFb.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-uXxg5p54.js → messages-Btr7C0hF.js} +2 -2
- package/dist/gateway/static/root/assets/{model-selector-ksLo-5B_.js → model-selector-DYUnp917.js} +1 -1
- package/dist/gateway/static/root/assets/{models-hub-cache-DhZng2nF.js → models-hub-cache-_qE7L-_H.js} +1 -1
- package/dist/gateway/static/root/assets/{note-detail-panel-B6RbjwJq.js → note-detail-panel-bjRZesmS.js} +3 -3
- package/dist/gateway/static/root/assets/note-image-lightbox-D7Jqb75C.js +1 -0
- package/dist/gateway/static/root/assets/{notes-api-DocbsBbT.js → notes-api-Bxa11X3a.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-B7889s6-.js → notes-page-D2QpJ3WZ.js} +1 -1
- package/dist/gateway/static/root/assets/{oauth-api-CCFIPbA0.js → oauth-api-DIE4UpP7.js} +1 -1
- package/dist/gateway/static/root/assets/{oauth-provider-connect-GxkF0vWJ.js → oauth-provider-connect-B95tiddN.js} +1 -1
- package/dist/gateway/static/root/assets/{page-context-capture-button-CvWLVeRA.js → page-context-capture-button-C-QbbwmN.js} +1 -1
- package/dist/gateway/static/root/assets/{page-context-draft-CMRDNv4d.js → page-context-draft-BHmOfksL.js} +1 -1
- package/dist/gateway/static/root/assets/page-header-store-bPV8_tKL.js +1 -0
- package/dist/gateway/static/root/assets/{personal-avatar-CqNJ48Ge.js → personal-avatar-nXrmMfrV.js} +1 -1
- package/dist/gateway/static/root/assets/personal-page-BCwlYT3F.js +1 -0
- package/dist/gateway/static/root/assets/{personal-settings-panel-DAcl8DU4.js → personal-settings-panel-vkYmXK8h.js} +1 -1
- package/dist/gateway/static/root/assets/{phone-dependencies-card-DLWblGj7.js → phone-dependencies-card-CZDAxTWv.js} +1 -1
- package/dist/gateway/static/root/assets/{phone-model-picker-ugrT9GJu.js → phone-model-picker-4Snfs8Hq.js} +1 -1
- package/dist/gateway/static/root/assets/{phone-settings-page-DIIt7JOE.js → phone-settings-page-D8HperfA.js} +1 -1
- package/dist/gateway/static/root/assets/{phone-workspace-store-mawKT5kV.js → phone-workspace-store-Di9R9Ux5.js} +1 -1
- package/dist/gateway/static/root/assets/{plugin-details-dialog-BdueMT3q.js → plugin-details-dialog-DJsDu-_8.js} +1 -1
- package/dist/gateway/static/root/assets/{preference-select-fields-CmLZNwbm.js → preference-select-fields-C6abaM3k.js} +1 -1
- package/dist/gateway/static/root/assets/{product-automation-feedback-DYwhL37Y.js → product-automation-feedback-C74s9Mjz.js} +1 -1
- package/dist/gateway/static/root/assets/product-delivery-DRyD9_Xq.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-B5G4X71t.js → product-open-page-AbDP5w1c.js} +1 -1
- package/dist/gateway/static/root/assets/product-preview-content-CktjvFbc.js +1 -0
- package/dist/gateway/static/root/assets/product-preview-modal-CzsqLwB4.js +2 -0
- package/dist/gateway/static/root/assets/{project-detail-page-CRJb69vP.js → project-detail-page-Cf3tggGP.js} +2 -2
- package/dist/gateway/static/root/assets/{project-understanding-D3w8cZEm.js → project-understanding-jmhFbHch.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-CkifzKjz.js → projects-page-jMgZiFnO.js} +1 -1
- package/dist/gateway/static/root/assets/{providers-api-CKFg2PZQ.js → providers-api-CJBYFKAK.js} +1 -1
- package/dist/gateway/static/root/assets/{recording-upload-ZkmRf0GR.js → recording-upload-Dl9a5yOV.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-B1Mh3bNo.js → registry-api-BwkbPUYu.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-Dw5rG-aA.js → remote-access-hub-CBQ_bOy9.js} +1 -1
- package/dist/gateway/static/root/assets/{resource-unavailable-BUWsH3fp.js → resource-unavailable-35wBZxmz.js} +1 -1
- package/dist/gateway/static/root/assets/route-preload-BcA6SPeC.js +2 -0
- package/dist/gateway/static/root/assets/{runtime-tools-api-BXvn5ImA.js → runtime-tools-api-BF28JDAO.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BALcypHp.js → runtime-tools-settings-panel-Bq2xQ2qd.js} +1 -1
- package/dist/gateway/static/root/assets/{service-recommendation-list-CpVUnz89.js → service-recommendation-list-CdaS4MPF.js} +1 -1
- package/dist/gateway/static/root/assets/{session-api-VquhIBHI.js → session-api-zJXYlRPt.js} +1 -1
- package/dist/gateway/static/root/assets/session-manager-wWxpzSo0.js +23 -0
- package/dist/gateway/static/root/assets/{sessions-page-DlvPkE5Y.js → sessions-page-BkjD_dUD.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BUMCEtA_.js +1 -0
- package/dist/gateway/static/root/assets/{settings-advanced-section-c9_iZ-46.js → settings-advanced-section-C84IghdL.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-mode-store-Dem6-TBe.js → settings-mode-store-CtQBJvbO.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-B_wNfFQe.js → settings-page-eEIz9InP.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-header-C8a_oOyw.js → settings-page-header-CsPAYlqV.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-CTSRWljG.js → settings-page-layout-lPLeJQoV.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-BNZoqF40.js → setup-status-panel-BSU5WpaE.js} +1 -1
- package/dist/gateway/static/root/assets/{share-link-dialog-C5JTTxsy.js → share-link-dialog-NaM6jvIz.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-D-KxzXgm.js → share-preview-page-CHjZn27s.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-api-CztKbxdd.js → shares-api-B9rVcV1y.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-CzR21j6e.js → shares-settings-5sB15V5v.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-api-B7kWl6LK.js → skill-api-D6zVfEuK.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-list-api-Cn07P6qZ.js → skill-list-api-SLmTBYF5.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-Bhl0u16_.js → skill-reload-api-UQkq2orI.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-BmkuxHZe.js → skills-page-Ca0pYZ4i.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-Ce6zIcki.js → system-settings-panel-BARWbjRN.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-CysJhG3B.js → task-detail-page-roqAsGue.js} +2 -2
- package/dist/gateway/static/root/assets/{task-result-evidence-CGZKMnNo.js → task-result-evidence-Bb1F7nAM.js} +1 -1
- package/dist/gateway/static/root/assets/{terminal-panel-store-YwLUkuyV.js → terminal-panel-store-DYDeXXAg.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-DOJ9tu-j.js → theme-store-DbStcH89.js} +1 -1
- package/dist/gateway/static/root/assets/{tracing-settings-page-BplBpTdL.js → tracing-settings-page-YfkNCxbP.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-B5-Ypvqa.js → tunnel-api-BK_ivyIb.js} +1 -1
- package/dist/gateway/static/root/assets/{understanding-activity-store-CmwtE8Kd.js → understanding-activity-store-p7Z7oq8z.js} +1 -1
- package/dist/gateway/static/root/assets/{understanding-refresh-store-9apSmAVL.js → understanding-refresh-store-D-U6rcwZ.js} +1 -1
- package/dist/gateway/static/root/assets/{usage-page-cPbirpXX.js → usage-page-URFSiswS.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-B3p2QWaF.js → use-autosave-B7cbumro.js} +1 -1
- package/dist/gateway/static/root/assets/{use-composer-attachments-D-AEYC85.js → use-composer-attachments-DAtiwLsE.js} +1 -1
- package/dist/gateway/static/root/assets/{use-file-preview-expanded-CO4SC-LX.js → use-file-preview-expanded-B8erpWsX.js} +2 -2
- package/dist/gateway/static/root/assets/{use-personal-profile-DocmhC9I.js → use-personal-profile-DDW29nm-.js} +1 -1
- package/dist/gateway/static/root/assets/{use-workspace-tree-VraTGKx-.js → use-workspace-tree-DCoTf22f.js} +1 -1
- package/dist/gateway/static/root/assets/{user-model-page-DuRALzhe.js → user-model-page-DSV5NkNj.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-DbSQHZZW.js → voice-api-key-field-DzYdJZN4.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-D9iNJSvm.js → voice-config-api-wotvT6Hw.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-input-shortcut-events-hso9_VvT.js → voice-input-shortcut-events-BI6PuiIB.js} +1 -1
- package/dist/gateway/static/root/assets/voice-transcribe-api-DAK9eLRr.js +1 -0
- package/dist/gateway/static/root/assets/{work-discovery-overlay-fsL9RJqB.js → work-discovery-overlay-BzmB9Rub.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-page-DAQPKVDs.js → work-discovery-page-CmGjxUIT.js} +1 -1
- package/dist/gateway/static/root/assets/{working-directory-picker-modal-Ta73QrNw.js → working-directory-picker-modal-B-EHm52-.js} +1 -1
- package/dist/gateway/static/root/assets/{workspace-api-CgrcNyBc.js → workspace-api-uhiEMPpK.js} +2 -2
- package/dist/gateway/static/root/assets/workspace-editor-agent-store-DrPVyikA.js +1 -0
- package/dist/gateway/static/root/assets/{workspace-file-preview-dialog-B7uBuPtA.js → workspace-file-preview-dialog-BDm06MZm.js} +1 -1
- package/dist/gateway/static/root/assets/{workspace-open-location-menu-Bi2vRvaQ.js → workspace-open-location-menu-Bv83uoqu.js} +1 -1
- package/dist/gateway/static/root/assets/{xopc-cloud-account-card-CCOOBG-7.js → xopc-cloud-account-card-Cd5GdpGL.js} +1 -1
- package/dist/gateway/static/root/index.html +84 -84
- package/dist/gateway/static/root/notification-sw.js +21 -7
- package/dist/package.js +1 -1
- package/dist/packages/endpoint-tools-protocol/src/context.js +115 -0
- package/dist/packages/endpoint-tools-protocol/src/index.js +4 -0
- package/dist/packages/endpoint-tools-protocol/src/location-sharing.js +51 -0
- package/dist/packages/gateway-contract/src/gateway-scopes.js +5 -1
- package/dist/packages/gateway-contract/src/index.js +2 -0
- package/dist/packages/gateway-contract/src/phone-workspace.js +6 -0
- package/dist/packages/gateway-contract/src/product-delivery.js +9 -3
- package/dist/src/agent/embedded/external-tool-discovery.js +1 -1
- package/dist/src/agent/embedded/run-for-session.js +2 -2
- package/dist/src/agent/embedded/run-turn.js +72 -9
- package/dist/src/agent/embedded/session-runner.js +2 -1
- package/dist/src/agent/embedded/types.d.ts +1 -1
- package/dist/src/agent/prompt/system-prompt.js +1 -0
- package/dist/src/agent/service/direct-turn-helpers.d.ts +0 -7
- package/dist/src/agent/service/direct-turn-helpers.js +3 -2
- package/dist/src/agent/service/process-direct-streaming.js +2 -1
- package/dist/src/agent/source-context/device-environment.d.ts +3 -0
- package/dist/src/agent/source-context/device-environment.js +38 -0
- package/dist/src/agent/source-context/injector.js +2 -1
- package/dist/src/agent/tool-manuals/phone.d.ts +1 -1
- package/dist/src/agent/tool-manuals/phone.js +1 -1
- package/dist/src/agent/tools/automation-tool.js +3 -2
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/personal-read-tool.js +1 -1
- package/dist/src/agent/tools/phone-use-tool.js +46 -2
- package/dist/src/agent/tools/xopc-use-tool.js +1 -0
- package/dist/src/computer/runtime.d.ts +1 -1
- package/dist/src/config/schema.d.ts +1 -0
- package/dist/src/endpoint-tools/context-store.d.ts +29 -0
- package/dist/src/endpoint-tools/context-store.js +138 -0
- package/dist/src/endpoint-tools/location-sharing.d.ts +42 -0
- package/dist/src/endpoint-tools/location-sharing.js +203 -0
- package/dist/src/endpoint-tools/runtime.d.ts +4 -0
- package/dist/src/endpoint-tools/runtime.js +8 -0
- package/dist/src/endpoint-tools/turn-context.d.ts +2 -1
- package/dist/src/endpoint-tools/turn-context.js +4 -2
- package/dist/src/gateway/hono/middleware/auth.js +1 -1
- package/dist/src/gateway/hono/routes/devices.js +1 -0
- package/dist/src/gateway/hono/routes/endpoint-compatibility.js +4 -0
- package/dist/src/gateway/hono/routes/endpoint-environment.d.ts +3 -0
- package/dist/src/gateway/hono/routes/endpoint-environment.js +98 -0
- package/dist/src/gateway/hono/routes/endpoint-tools.js +1 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js +16 -0
- package/dist/src/gateway/hono/routes/location-sharing.d.ts +3 -0
- package/dist/src/gateway/hono/routes/location-sharing.js +164 -0
- package/dist/src/gateway/hono/routes/phone.js +246 -34
- package/dist/src/gateway/hono/routes/session-command-handler.js +1 -1
- package/dist/src/gateway/hono/routes/session-input-handler.js +2 -1
- package/dist/src/gateway/hono/routes/tunnel.js +10 -0
- package/dist/src/gateway/security/gateway-scopes.js +6 -0
- package/dist/src/gateway/security/phone-access.d.ts +4 -0
- package/dist/src/gateway/security/phone-access.js +20 -0
- package/dist/src/gateway/service/agent-runner.js +3 -1
- package/dist/src/gateway/service/config-coordinator.d.ts +1 -0
- package/dist/src/gateway/service/run-gateway-agent.js +2 -1
- package/dist/src/gateway/service/session-input-coordinator.d.ts +3 -0
- package/dist/src/gateway/service/session-input-coordinator.js +50 -2
- package/dist/src/gateway/service.d.ts +1 -0
- package/dist/src/gateway/service.js +8 -0
- package/dist/src/gateway/session-reset-service.js +4 -0
- package/dist/src/notifications/planner.js +6 -6
- package/dist/src/phone/assistance-service.d.ts +6 -0
- package/dist/src/phone/assistance-service.js +32 -0
- package/dist/src/phone/assistance.d.ts +40 -0
- package/dist/src/phone/assistance.js +273 -0
- package/dist/src/phone/cloud-phones.d.ts +5 -0
- package/dist/src/phone/cloud-phones.js +14 -0
- package/dist/src/phone/contracts.d.ts +7 -1
- package/dist/src/phone/contracts.js +5 -0
- package/dist/src/phone/environment-adapter.d.ts +3 -0
- package/dist/src/phone/environment-adapter.js +11 -0
- package/dist/src/phone/location-baseline-store.d.ts +9 -0
- package/dist/src/phone/location-baseline-store.js +61 -0
- package/dist/src/phone/location-environment.d.ts +61 -0
- package/dist/src/phone/location-environment.js +157 -0
- package/dist/src/phone/model-adapter.js +16 -5
- package/dist/src/phone/runtime.d.ts +17 -2
- package/dist/src/phone/runtime.js +203 -20
- package/dist/src/phone/stream-gateway.d.ts +5 -0
- package/dist/src/phone/stream-gateway.js +85 -51
- package/dist/src/phone/wuying-location.d.ts +18 -0
- package/dist/src/phone/wuying-location.js +86 -0
- package/dist/src/storage/sqlite/device-access-repository.d.ts +4 -0
- package/dist/src/storage/sqlite/device-access-repository.js +18 -1
- package/dist/src/tunnel/broker-client.d.ts +8 -0
- package/dist/src/tunnel/broker-client.js +18 -1
- package/dist/src/voice/realtime/runtime.js +1 -1
- package/package.json +4 -3
- package/dist/gateway/static/root/assets/app-link-CytZ5zkp.js +0 -1
- package/dist/gateway/static/root/assets/automation-display-BlGt8MAC.js +0 -2
- package/dist/gateway/static/root/assets/automation-task-card-Cp3RIw5d.js +0 -2
- package/dist/gateway/static/root/assets/automations-workspace-CXLu04I-.js +0 -9
- package/dist/gateway/static/root/assets/chat-page-CT3K7lFT.js +0 -71
- package/dist/gateway/static/root/assets/date-picker-ByTasrfl.js +0 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-BKMpLdIA.js +0 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-0gmDfPeP.js +0 -1
- package/dist/gateway/static/root/assets/index-6vVrWfwM.js +0 -11
- package/dist/gateway/static/root/assets/index-DDGK6klk.css +0 -1
- package/dist/gateway/static/root/assets/management-settings-CuSzixOw.js +0 -1
- package/dist/gateway/static/root/assets/note-image-lightbox-BCjvBqwy.js +0 -1
- package/dist/gateway/static/root/assets/page-header-store-reh1es-3.js +0 -1
- package/dist/gateway/static/root/assets/personal-page-D95FHaw9.js +0 -1
- package/dist/gateway/static/root/assets/product-delivery-BU_H84jL.js +0 -1
- package/dist/gateway/static/root/assets/product-preview-content-BUheyLqt.js +0 -1
- package/dist/gateway/static/root/assets/product-preview-modal-Cj_Zxu4G.js +0 -2
- package/dist/gateway/static/root/assets/route-preload-CRKbVwQ1.js +0 -2
- package/dist/gateway/static/root/assets/session-manager-DNMosCtW.js +0 -23
- package/dist/gateway/static/root/assets/settings-advanced-gate-Ckc1aCLG.js +0 -1
- package/dist/gateway/static/root/assets/voice-transcribe-api-BqIs0jgv.js +0 -1
- package/dist/gateway/static/root/assets/workspace-editor-agent-store-Dw6wtadX.js +0 -1
|
@@ -7,8 +7,10 @@ import { nativePhoneOutput, updatePhoneFeedback } from "./task-feedback.js";
|
|
|
7
7
|
import { PhoneFrameCache, PhoneOperationBudget } from "./live-state.js";
|
|
8
8
|
import { PhoneInteractiveSession } from "./interactive-session.js";
|
|
9
9
|
import { LocalScrcpyProvider } from "./scrcpy.js";
|
|
10
|
+
import { pendingPhoneAssistance, phoneAssistance, updatePhoneCheckpoint } from "./assistance.js";
|
|
10
11
|
import { AdbPhoneDriver, resolveAdbPath } from "./adb-driver.js";
|
|
11
12
|
import { CloudPhones } from "./cloud-phones.js";
|
|
13
|
+
import { PhoneLocationEnvironment } from "./location-environment.js";
|
|
12
14
|
import { WirelessPhones } from "./wireless.js";
|
|
13
15
|
import { advancePhonePlan, buildPhonePlan, createPhonePlanState } from "./planning.js";
|
|
14
16
|
import { canDispatchPhoneAction, observationSignature, phoneCompletionSatisfied } from "./reliability.js";
|
|
@@ -29,10 +31,47 @@ var PhoneRuntime = class {
|
|
|
29
31
|
reconnecting = /* @__PURE__ */ new Map();
|
|
30
32
|
reconnectAfter = /* @__PURE__ */ new Map();
|
|
31
33
|
cloudPhones;
|
|
34
|
+
locationEnvironment;
|
|
35
|
+
get environment() {
|
|
36
|
+
return this.locationEnvironment ??= new PhoneLocationEnvironment(this.repository, {
|
|
37
|
+
adapter: (device) => this.cloud.locationAdapter(device),
|
|
38
|
+
exclusive: (phoneId, owner, cleanup, action) => this.withEnvironmentLock(phoneId, owner, cleanup, action)
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async withEnvironmentLock(phoneId, owner, cleanup, action) {
|
|
42
|
+
const device = this.repository.get(phoneId);
|
|
43
|
+
if (this.deviceOpenings.has(phoneId)) throw new PhoneError("PHONE_DEVICE_BUSY", "Phone environment is busy", 409);
|
|
44
|
+
const session = [...this.sessions.values()].find((value) => value.device.id === phoneId && value.report.status !== "closed");
|
|
45
|
+
if (session && cleanup && ![
|
|
46
|
+
"closed",
|
|
47
|
+
"cancelled",
|
|
48
|
+
"completed"
|
|
49
|
+
].includes(session.report.status)) await this.stop(session.report.owner, "pause");
|
|
50
|
+
if (session && !cleanup && (session.report.owner !== owner || session.active && !this.locationEnvironment?.hasVerified(phoneId, owner))) throw new PhoneError("PHONE_DEVICE_BUSY", "Pause the phone task before sharing location", 409);
|
|
51
|
+
const opening = new AbortController();
|
|
52
|
+
this.deviceOpenings.set(phoneId, opening);
|
|
53
|
+
let lease;
|
|
54
|
+
try {
|
|
55
|
+
if (session) {
|
|
56
|
+
this.repository.renew(device.serial, session.lease);
|
|
57
|
+
const result = await this.io(session, action);
|
|
58
|
+
session.report.controlEpoch++;
|
|
59
|
+
session.frames.invalidate();
|
|
60
|
+
this.publish(session);
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
lease = this.repository.acquire(device.serial);
|
|
64
|
+
return await action();
|
|
65
|
+
} finally {
|
|
66
|
+
if (lease) this.repository.release(device.serial, lease);
|
|
67
|
+
if (this.deviceOpenings.get(phoneId) === opening) this.deviceOpenings.delete(phoneId);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
32
70
|
get cloud() {
|
|
33
71
|
return this.cloudPhones ??= new CloudPhones(this.repository);
|
|
34
72
|
}
|
|
35
73
|
sessions = /* @__PURE__ */ new Map();
|
|
74
|
+
listeners = /* @__PURE__ */ new Map();
|
|
36
75
|
lastDevices = /* @__PURE__ */ new Map();
|
|
37
76
|
openings = /* @__PURE__ */ new Map();
|
|
38
77
|
deviceOpenings = /* @__PURE__ */ new Map();
|
|
@@ -159,6 +198,75 @@ var PhoneRuntime = class {
|
|
|
159
198
|
const report = this.sessions.get(owner)?.report;
|
|
160
199
|
return report && structuredClone(report);
|
|
161
200
|
}
|
|
201
|
+
checkpoint(session) {
|
|
202
|
+
const { sessionId, deviceId, deviceName, owner, agentId, modelRef, executionMode } = session.report;
|
|
203
|
+
return {
|
|
204
|
+
report: {
|
|
205
|
+
sessionId,
|
|
206
|
+
deviceId,
|
|
207
|
+
deviceName,
|
|
208
|
+
owner,
|
|
209
|
+
agentId,
|
|
210
|
+
modelRef,
|
|
211
|
+
executionMode
|
|
212
|
+
},
|
|
213
|
+
deviceRevision: session.device.revision,
|
|
214
|
+
task: structuredClone(session.report.task),
|
|
215
|
+
usage: {
|
|
216
|
+
actions: session.report.actions - (session.report.humanActions ?? 0) - (session.taskStart?.actions ?? 0),
|
|
217
|
+
modelRequests: session.report.modelRequests - (session.taskStart?.modelRequests ?? 0),
|
|
218
|
+
activeDurationMs: (session.taskActiveMs ?? 0) + (session.budget?.elapsedMs ?? 0)
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
async restoreAssistance(checkpoint, context, signal, assistanceId) {
|
|
223
|
+
const { owner, deviceId, agentId, modelRef } = checkpoint.report;
|
|
224
|
+
if (context.agentId !== agentId || context.modelRef !== modelRef || this.repository.get(deviceId).revision !== checkpoint.deviceRevision) throw new PhoneError("PHONE_BINDING_CHANGED", "Phone or Agent settings changed. Inspect the task before restarting.", 409);
|
|
225
|
+
const existing = this.sessions.get(owner);
|
|
226
|
+
if (existing && existing.report.status !== "closed") {
|
|
227
|
+
this.guard(existing, context);
|
|
228
|
+
if (existing.report.deviceId !== deviceId || existing.report.task && existing.report.task.taskId !== checkpoint.task.taskId) throw new PhoneError("PHONE_ASSISTANCE_CHANGED", "Another phone task is active", 409);
|
|
229
|
+
if (existing.report.task) {
|
|
230
|
+
existing.report.assistanceId = assistanceId ?? existing.report.assistanceId;
|
|
231
|
+
return this.publish(existing);
|
|
232
|
+
}
|
|
233
|
+
if (existing.active || existing.human) throw this.busy(deviceId, existing);
|
|
234
|
+
}
|
|
235
|
+
if (!existing || existing.report.status === "closed") await this.execute(owner, {
|
|
236
|
+
op: "open",
|
|
237
|
+
deviceId,
|
|
238
|
+
mode: "control"
|
|
239
|
+
}, {
|
|
240
|
+
...context,
|
|
241
|
+
assistanceId
|
|
242
|
+
}, signal);
|
|
243
|
+
const session = this.sessions.get(owner);
|
|
244
|
+
session.report.task = structuredClone(checkpoint.task);
|
|
245
|
+
session.taskPending = true;
|
|
246
|
+
session.report.executionMode = checkpoint.task.executionMode;
|
|
247
|
+
session.report.actions = checkpoint.usage.actions;
|
|
248
|
+
session.report.modelRequests = checkpoint.usage.modelRequests;
|
|
249
|
+
session.taskStart = {
|
|
250
|
+
actions: 0,
|
|
251
|
+
modelRequests: 0
|
|
252
|
+
};
|
|
253
|
+
session.taskActiveMs = checkpoint.usage.activeDurationMs;
|
|
254
|
+
session.report.status = "needs_user";
|
|
255
|
+
session.report.stopReason = "user_takeover";
|
|
256
|
+
session.report.assistanceId = assistanceId;
|
|
257
|
+
session.report.controlOwner = "none";
|
|
258
|
+
session.report.controlEpoch++;
|
|
259
|
+
return this.publish(session);
|
|
260
|
+
}
|
|
261
|
+
subscribe(owner, listener) {
|
|
262
|
+
const listeners = this.listeners.get(owner) ?? /* @__PURE__ */ new Set();
|
|
263
|
+
listeners.add(listener);
|
|
264
|
+
this.listeners.set(owner, listeners);
|
|
265
|
+
return () => {
|
|
266
|
+
listeners.delete(listener);
|
|
267
|
+
if (!listeners.size) this.listeners.delete(owner);
|
|
268
|
+
};
|
|
269
|
+
}
|
|
162
270
|
deviceContext(id, config) {
|
|
163
271
|
const session = [...this.sessions.values()].find((session) => session.device.id === id && session.report.status !== "closed");
|
|
164
272
|
if (!session) throw new PhoneError("PHONE_SESSION_NOT_FOUND", "Phone is not connected", 404);
|
|
@@ -195,6 +303,19 @@ var PhoneRuntime = class {
|
|
|
195
303
|
await this.stop(owner, "cancel");
|
|
196
304
|
return;
|
|
197
305
|
}
|
|
306
|
+
const wait = phoneAssistance(owner);
|
|
307
|
+
if (wait && [
|
|
308
|
+
"open",
|
|
309
|
+
"queued",
|
|
310
|
+
"resuming"
|
|
311
|
+
].includes(wait.status) && session.report.task?.taskId === wait.checkpoint.task.taskId && session.report.status !== "completed") {
|
|
312
|
+
if (!session.human) {
|
|
313
|
+
session.report.status = "needs_user";
|
|
314
|
+
session.report.controlOwner = "none";
|
|
315
|
+
this.publish(session);
|
|
316
|
+
}
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
198
319
|
if (!session.human && ![
|
|
199
320
|
"paused",
|
|
200
321
|
"needs_user",
|
|
@@ -210,10 +331,20 @@ var PhoneRuntime = class {
|
|
|
210
331
|
activeDurationMs: session.segmentUsage.activeDurationMs + (session.budget?.elapsedMs ?? 0)
|
|
211
332
|
}, session.report.feedback);
|
|
212
333
|
session.report.updatedAt = Date.now();
|
|
334
|
+
if (session.report.task) updatePhoneCheckpoint(session.report.owner, this.checkpoint(session));
|
|
213
335
|
this.repository.report(session.report);
|
|
336
|
+
for (const listener of this.listeners.get(session.report.owner) ?? []) try {
|
|
337
|
+
listener(structuredClone(session.report));
|
|
338
|
+
} catch {
|
|
339
|
+
log.warn({
|
|
340
|
+
conversationId: session.report.owner,
|
|
341
|
+
phase: "report_listener"
|
|
342
|
+
}, "Phone progress listener failed");
|
|
343
|
+
}
|
|
214
344
|
return structuredClone(session.report);
|
|
215
345
|
}
|
|
216
|
-
guard(session, context) {
|
|
346
|
+
guard(session, context, requireLocation = true) {
|
|
347
|
+
if (requireLocation) this.locationEnvironment?.assertAllowed(session.device.id, session.report.owner);
|
|
217
348
|
if (!context.config.phone.enabled || context.toolDenied) throw new PhoneError("PHONE_DISABLED", "Phone Use is disabled for this agent");
|
|
218
349
|
if (context.modelRef !== session.modelRef || resolveAdbPath(context.config.phone.adbPath) !== session.adbPath) throw new PhoneError("PHONE_BINDING_CHANGED", "Phone model or execution configuration changed; reopen the phone");
|
|
219
350
|
if (context.validateBinding && !context.validateBinding()) throw new PhoneError("PHONE_BINDING_CHANGED", "Agent model or tool policy changed; reopen the phone");
|
|
@@ -226,6 +357,26 @@ var PhoneRuntime = class {
|
|
|
226
357
|
}
|
|
227
358
|
async execute(owner, raw, context, signal = new AbortController().signal) {
|
|
228
359
|
const input = PhoneInputSchema.parse(raw);
|
|
360
|
+
if (input.op === "request_user") {
|
|
361
|
+
const session = this.sessions.get(owner);
|
|
362
|
+
if (!session?.report.task || terminal.has(session.report.status)) throw new PhoneError("PHONE_OPEN_REQUIRED", "Start a phone task before requesting user assistance", 409);
|
|
363
|
+
this.guard(session, context);
|
|
364
|
+
if (session.human || session.active || session.switching) throw new PhoneError("PHONE_SESSION_BUSY", "Wait for the current phone operation before requesting user assistance", 409);
|
|
365
|
+
if (session.report.status === "outcome_unknown") throw new PhoneError("PHONE_DISPATCH_UNKNOWN", "Inspect the previous input before continuing", 409);
|
|
366
|
+
session.controller.abort();
|
|
367
|
+
session.prediction?.abort();
|
|
368
|
+
session.budget?.pause();
|
|
369
|
+
session.report.status = "needs_user";
|
|
370
|
+
session.report.stopReason = "user_takeover";
|
|
371
|
+
session.report.message = input.reason;
|
|
372
|
+
session.report.controlOwner = "none";
|
|
373
|
+
session.report.controlEpoch++;
|
|
374
|
+
session.report.verified = false;
|
|
375
|
+
session.report.assistanceId = context.onNeedsUser?.(this.checkpoint(session));
|
|
376
|
+
this.publish(session);
|
|
377
|
+
await session.stream?.revoke();
|
|
378
|
+
return this.publish(session);
|
|
379
|
+
}
|
|
229
380
|
if (input.op === "close") return this.finishTask(owner);
|
|
230
381
|
if ([
|
|
231
382
|
"cancel",
|
|
@@ -247,6 +398,7 @@ var PhoneRuntime = class {
|
|
|
247
398
|
signal.throwIfAborted();
|
|
248
399
|
if (input.op === "open") {
|
|
249
400
|
const requestedDevice = input.deviceId ?? context.preferredDeviceId ?? this.repository.defaultDevice();
|
|
401
|
+
if (requestedDevice && this.repository.get(requestedDevice).connection) await this.environment.recover(requestedDevice);
|
|
250
402
|
if (requestedDevice && this.deviceOpenings.has(requestedDevice)) throw this.busy(requestedDevice);
|
|
251
403
|
if (this.openings.has(owner)) throw new PhoneError("PHONE_SESSION_BUSY", "A phone is already being opened", 409);
|
|
252
404
|
const opening = new AbortController();
|
|
@@ -258,6 +410,8 @@ var PhoneRuntime = class {
|
|
|
258
410
|
if (this.sessions.size >= 50 && ![...this.sessions.values()].some((session) => session.device.id === requestedDevice)) throw new PhoneError("PHONE_CAPACITY", "Disconnect an idle phone in the global phone panel before opening another", 409);
|
|
259
411
|
const previous = this.sessions.get(owner);
|
|
260
412
|
const deviceId = requestedDevice;
|
|
413
|
+
const waiting = pendingPhoneAssistance().find((wait) => wait.checkpoint.report.deviceId === deviceId);
|
|
414
|
+
if (waiting && waiting.id !== context.assistanceId) throw new PhoneError("PHONE_PAUSED", "This phone has a pending user handoff. Complete or cancel that task before opening another.", 409);
|
|
261
415
|
if (!deviceId) throw new PhoneError("PHONE_DEVICE_REQUIRED", "Choose a registered phone in Phone Use settings");
|
|
262
416
|
if (previous && !terminal.has(previous.report.status) && previous.device.id !== deviceId) {
|
|
263
417
|
if (previous.active || previous.human || previous.report.controlOwner !== "none" || !["ready", "completed"].includes(previous.report.status)) throw this.busy(previous.device.id, previous);
|
|
@@ -483,6 +637,7 @@ var PhoneRuntime = class {
|
|
|
483
637
|
if (session.active) throw new PhoneError("PHONE_SESSION_BUSY", "A phone operation is already running", 409);
|
|
484
638
|
this.guard(session, context);
|
|
485
639
|
if (input.op === "resume") {
|
|
640
|
+
if (session.report.status === "needs_user") throw new PhoneError("PHONE_PAUSED", "User assistance is required. Wait for explicit handback in the phone panel", 409);
|
|
486
641
|
if (session.modeHandbackRequired) throw new PhoneError("PHONE_PAUSED", "Inspect the phone and explicitly hand back in the phone panel after changing execution mode", 409);
|
|
487
642
|
if (session.report.steps.at(-1)?.dispatch === "unknown") throw new PhoneError("PHONE_DISPATCH_UNKNOWN", "Inspect the previous input before continuing; do not retry automatically", 409);
|
|
488
643
|
session.controller = new AbortController();
|
|
@@ -669,7 +824,10 @@ var PhoneRuntime = class {
|
|
|
669
824
|
session.report.status = "ready";
|
|
670
825
|
session.taskPending = false;
|
|
671
826
|
}
|
|
672
|
-
if (session.report.status === "completed")
|
|
827
|
+
if (session.report.status === "completed") {
|
|
828
|
+
session.taskPending = false;
|
|
829
|
+
await this.locationEnvironment?.releaseOwner(owner);
|
|
830
|
+
}
|
|
673
831
|
if (!terminal.has(session.report.status) && !session.human && !["paused", "needs_user"].includes(session.report.status)) {
|
|
674
832
|
session.report.controlOwner = "none";
|
|
675
833
|
session.report.controlEpoch++;
|
|
@@ -745,6 +903,7 @@ var PhoneRuntime = class {
|
|
|
745
903
|
if (this.deviceOpenings.has(session.device.id)) throw this.busy(session.device.id, session);
|
|
746
904
|
if (session.human || session.report.status === "needs_user" || session.report.steps.at(-1)?.dispatch === "unknown") throw this.busy(session.device.id, session);
|
|
747
905
|
if (session.active) await this.stop(owner, "pause");
|
|
906
|
+
await this.locationEnvironment?.releaseOwner(owner);
|
|
748
907
|
session.controller = new AbortController();
|
|
749
908
|
session.report.controlOwner = "none";
|
|
750
909
|
session.report.controlEpoch++;
|
|
@@ -1034,11 +1193,9 @@ var PhoneRuntime = class {
|
|
|
1034
1193
|
session.report.controlOwner = "none";
|
|
1035
1194
|
session.report.controlEpoch++;
|
|
1036
1195
|
session.budget?.pause();
|
|
1037
|
-
this.
|
|
1038
|
-
await
|
|
1039
|
-
|
|
1040
|
-
observation = await this.observe(session, signal);
|
|
1041
|
-
continue;
|
|
1196
|
+
session.report.assistanceId = context.onNeedsUser?.(this.checkpoint(session));
|
|
1197
|
+
await session.stream?.revoke();
|
|
1198
|
+
return this.publish(session);
|
|
1042
1199
|
}
|
|
1043
1200
|
return this.publish(session);
|
|
1044
1201
|
}
|
|
@@ -1175,6 +1332,7 @@ var PhoneRuntime = class {
|
|
|
1175
1332
|
session.report.currentAction = void 0;
|
|
1176
1333
|
session.report.stopReason = failure.code === "PHONE_TEXT_INPUT_UNAVAILABLE" ? "text_input_required" : "text_input_unconfirmed";
|
|
1177
1334
|
session.budget?.pause();
|
|
1335
|
+
session.report.assistanceId = context.onNeedsUser?.(this.checkpoint(session));
|
|
1178
1336
|
await session.stream?.revoke();
|
|
1179
1337
|
return this.publish(session);
|
|
1180
1338
|
}
|
|
@@ -1299,8 +1457,9 @@ var PhoneRuntime = class {
|
|
|
1299
1457
|
}
|
|
1300
1458
|
async dispatch(session, action, observation, context, signal, epoch = session.report.controlEpoch, resetTask = false, settingsPage) {
|
|
1301
1459
|
return this.io(session, async () => {
|
|
1460
|
+
if (session.human) session.authorizeHuman?.();
|
|
1302
1461
|
signal.throwIfAborted();
|
|
1303
|
-
this.guard(session, context);
|
|
1462
|
+
this.guard(session, context, !session.human);
|
|
1304
1463
|
if (epoch !== session.report.controlEpoch || session.report.controlOwner === "none" || session.report.handoff !== "idle") return false;
|
|
1305
1464
|
if (session.human && Date.now() > (session.humanExpiresAt ?? 0)) throw new PhoneError("PHONE_CONTROL_REQUIRED", "Manual control expired", 409);
|
|
1306
1465
|
if (!session.human && session.report.actions - (session.report.humanActions ?? 0) >= context.config.phone.maxActionsPerSession) throw new PhoneError("PHONE_ACTION_LIMIT", "Phone action limit reached");
|
|
@@ -1410,6 +1569,7 @@ var PhoneRuntime = class {
|
|
|
1410
1569
|
await session.active?.catch(() => {});
|
|
1411
1570
|
await session.io.catch(() => {});
|
|
1412
1571
|
if (op === "close") this.repository.release(session.device.serial, session.lease);
|
|
1572
|
+
if (op !== "pause") await this.locationEnvironment?.releaseOwner(owner);
|
|
1413
1573
|
if (op === "cancel") {
|
|
1414
1574
|
session.controller = new AbortController();
|
|
1415
1575
|
session.report.status = "ready";
|
|
@@ -1447,10 +1607,10 @@ var PhoneRuntime = class {
|
|
|
1447
1607
|
async capture(session, signal) {
|
|
1448
1608
|
return this.io(session, async () => {
|
|
1449
1609
|
signal.throwIfAborted();
|
|
1450
|
-
this.guard(session, session.context);
|
|
1610
|
+
this.guard(session, session.context, false);
|
|
1451
1611
|
const observation = await (session.driver.capture ?? session.driver.observe).call(session.driver, session.device.serial, signal);
|
|
1452
1612
|
try {
|
|
1453
|
-
this.guard(session, session.context);
|
|
1613
|
+
this.guard(session, session.context, false);
|
|
1454
1614
|
this.allowedScreen(session, observation);
|
|
1455
1615
|
if (!terminal.has(session.report.status)) session.frames.set(observation);
|
|
1456
1616
|
return observation;
|
|
@@ -1499,7 +1659,7 @@ var PhoneRuntime = class {
|
|
|
1499
1659
|
continue;
|
|
1500
1660
|
}
|
|
1501
1661
|
try {
|
|
1502
|
-
this.guard(session, session.context);
|
|
1662
|
+
this.guard(session, session.context, false);
|
|
1503
1663
|
const idleLimit = session.context.config.phone.idleDisconnectMs;
|
|
1504
1664
|
if (idleLimit && !session.active && !session.human && session.report.controlOwner === "none" && Date.now() - Math.max(session.viewedAt, session.report.updatedAt) >= idleLimit) {
|
|
1505
1665
|
this.stopDevice(session.device.id);
|
|
@@ -1528,7 +1688,7 @@ var PhoneRuntime = class {
|
|
|
1528
1688
|
if (!session || session.report.sessionId !== id) throw new PhoneError("PHONE_SESSION_NOT_FOUND", "Phone session not found", 404);
|
|
1529
1689
|
if (context.agentId !== session.report.agentId) throw new PhoneError("PHONE_ACCESS_CHANGED", "Agent binding changed", 403);
|
|
1530
1690
|
if (!terminal.has(session.report.status)) try {
|
|
1531
|
-
this.guard(session, context);
|
|
1691
|
+
this.guard(session, context, false);
|
|
1532
1692
|
} catch (error) {
|
|
1533
1693
|
session.frames.clear();
|
|
1534
1694
|
throw error;
|
|
@@ -1584,11 +1744,12 @@ var PhoneRuntime = class {
|
|
|
1584
1744
|
}
|
|
1585
1745
|
session.stream = new PhoneInteractiveSession(provider, async () => {
|
|
1586
1746
|
if (terminal.has(session.report.status)) throw new PhoneError("PHONE_VIDEO_ENDED", "Phone session ended", 409);
|
|
1587
|
-
this.guard(session, session.context);
|
|
1747
|
+
this.guard(session, session.context, false);
|
|
1588
1748
|
const app = await provider.foreground(AbortSignal.timeout(2e3));
|
|
1589
1749
|
if (!phoneAllowsApp(session.device, app)) throw new PhoneError("PHONE_APP_DENIED", "Current page is outside the authorized applications", 403);
|
|
1590
1750
|
session.viewedAt = Date.now();
|
|
1591
1751
|
}, (client, epoch) => {
|
|
1752
|
+
session.authorizeHuman?.();
|
|
1592
1753
|
if (terminal.has(session.report.status) || session.report.handoff !== "idle" || session.report.controlOwner !== "human" || session.human !== client || session.report.controlEpoch !== epoch || Date.now() > (session.humanExpiresAt ?? 0)) throw new PhoneError("PHONE_CONTROL_CHANGED", "Manual control is no longer available", 409);
|
|
1593
1754
|
session.humanExpiresAt = Date.now() + 15e3;
|
|
1594
1755
|
if ((session.report.humanActions ?? 0) >= 1200) throw new PhoneError("PHONE_ACTION_LIMIT", "Manual input limit reached; reopen the session");
|
|
@@ -1615,10 +1776,10 @@ var PhoneRuntime = class {
|
|
|
1615
1776
|
throw error;
|
|
1616
1777
|
}
|
|
1617
1778
|
}
|
|
1618
|
-
async disconnectVideo(owner, id, client) {
|
|
1779
|
+
async disconnectVideo(owner, id, client, epoch) {
|
|
1619
1780
|
const session = this.sessions.get(owner);
|
|
1620
1781
|
if (!session || session.report.sessionId !== id) return;
|
|
1621
|
-
if (session.human === client) {
|
|
1782
|
+
if (session.human === client && (epoch === void 0 || session.report.controlEpoch === epoch)) {
|
|
1622
1783
|
session.human = void 0;
|
|
1623
1784
|
session.report.controlOwner = "none";
|
|
1624
1785
|
session.report.controlEpoch++;
|
|
@@ -1648,7 +1809,7 @@ var PhoneRuntime = class {
|
|
|
1648
1809
|
session.preview ??= (async () => {
|
|
1649
1810
|
const observation = await this.capture(session, signal);
|
|
1650
1811
|
try {
|
|
1651
|
-
this.guard(session, context);
|
|
1812
|
+
this.guard(session, context, false);
|
|
1652
1813
|
this.allowedScreen(session, observation);
|
|
1653
1814
|
} catch (error) {
|
|
1654
1815
|
session.frames.clear();
|
|
@@ -1661,7 +1822,7 @@ var PhoneRuntime = class {
|
|
|
1661
1822
|
});
|
|
1662
1823
|
await session.preview;
|
|
1663
1824
|
}
|
|
1664
|
-
this.guard(session, context);
|
|
1825
|
+
this.guard(session, context, false);
|
|
1665
1826
|
const frame = session.frames.get();
|
|
1666
1827
|
if (!frame) throw new PhoneError("PHONE_OPEN_REQUIRED", "Phone session ended", 409);
|
|
1667
1828
|
return {
|
|
@@ -1669,7 +1830,8 @@ var PhoneRuntime = class {
|
|
|
1669
1830
|
frame: Buffer.from(frame.frame)
|
|
1670
1831
|
};
|
|
1671
1832
|
}
|
|
1672
|
-
async liveControl(owner, id, context, client, input) {
|
|
1833
|
+
async liveControl(owner, id, context, client, input, authorize) {
|
|
1834
|
+
authorize?.();
|
|
1673
1835
|
const session = this.liveSession(owner, id, context);
|
|
1674
1836
|
if (session.switching) throw new PhoneError("PHONE_SESSION_BUSY", "Phone execution mode is changing", 409);
|
|
1675
1837
|
if (this.deviceOpenings.has(session.device.id)) throw this.busy(session.device.id, session);
|
|
@@ -1691,8 +1853,9 @@ var PhoneRuntime = class {
|
|
|
1691
1853
|
"heartbeat"
|
|
1692
1854
|
].includes(input.op)) throw new PhoneError("PHONE_SESSION_LIMIT", "Phone control request limit reached; reopen the session");
|
|
1693
1855
|
const result = session.commands.catch(() => {}).then(async () => {
|
|
1856
|
+
authorize?.();
|
|
1694
1857
|
if (terminal.has(session.report.status)) throw new PhoneError("PHONE_OPEN_REQUIRED", "Phone session ended", 409);
|
|
1695
|
-
this.guard(session, context);
|
|
1858
|
+
this.guard(session, context, input.op === "handback");
|
|
1696
1859
|
if (session.switching) throw new PhoneError("PHONE_SESSION_BUSY", "Phone execution mode is changing", 409);
|
|
1697
1860
|
if (input.epoch !== session.report.controlEpoch) throw new PhoneError("PHONE_CONTROL_CHANGED", "Control changed. Refresh before trying again.", 409);
|
|
1698
1861
|
if (session.human && session.human !== client) throw new PhoneError("PHONE_CONTROL_BUSY", "Another browser window controls this phone", 409);
|
|
@@ -1719,6 +1882,8 @@ var PhoneRuntime = class {
|
|
|
1719
1882
|
if (session.controller.signal.aborted && !session.active) session.controller = new AbortController();
|
|
1720
1883
|
session.human = void 0;
|
|
1721
1884
|
if (input.op === "takeover") {
|
|
1885
|
+
authorize?.();
|
|
1886
|
+
session.authorizeHuman = authorize;
|
|
1722
1887
|
session.human = client;
|
|
1723
1888
|
session.humanExpiresAt = Date.now() + 15e3;
|
|
1724
1889
|
session.report.controlOwner = "human";
|
|
@@ -1759,6 +1924,7 @@ var PhoneRuntime = class {
|
|
|
1759
1924
|
} finally {
|
|
1760
1925
|
observation?.frame.fill(0);
|
|
1761
1926
|
}
|
|
1927
|
+
authorize?.();
|
|
1762
1928
|
session.report.controlOwner = session.active || session.taskPending ? "agent" : "none";
|
|
1763
1929
|
session.report.status = session.active ? "running" : "ready";
|
|
1764
1930
|
session.report.handoff = "idle";
|
|
@@ -1820,10 +1986,26 @@ var PhoneRuntime = class {
|
|
|
1820
1986
|
});
|
|
1821
1987
|
return result;
|
|
1822
1988
|
}
|
|
1989
|
+
/** Called only for an explicit completion message from the controlling conversation's user. */
|
|
1990
|
+
async completeAssistanceForUser(owner, context) {
|
|
1991
|
+
const session = this.sessions.get(owner);
|
|
1992
|
+
if (!session) throw new PhoneError("PHONE_OPEN_REQUIRED", "Reconnect the phone before continuing", 409);
|
|
1993
|
+
this.guard(session, context);
|
|
1994
|
+
if (session.report.controlOwner === "agent" && session.report.status === "ready") return;
|
|
1995
|
+
await this.liveControl(owner, session.report.sessionId, context, session.human ?? `phone-user:${owner}`, {
|
|
1996
|
+
op: "handback",
|
|
1997
|
+
epoch: session.report.controlEpoch,
|
|
1998
|
+
operationId: randomUUID()
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
1823
2001
|
async stopDevice(id) {
|
|
1824
2002
|
this.deviceOpenings.get(id)?.abort();
|
|
1825
2003
|
await Promise.all(this.reports().filter((report) => report.deviceId === id).map((report) => this.stop(report.owner, "close")));
|
|
1826
|
-
|
|
2004
|
+
try {
|
|
2005
|
+
await this.locationEnvironment?.releaseDevice(id);
|
|
2006
|
+
} finally {
|
|
2007
|
+
this.cloudPhones?.disconnect(id);
|
|
2008
|
+
}
|
|
1827
2009
|
}
|
|
1828
2010
|
async shutdown() {
|
|
1829
2011
|
this.wireless.close();
|
|
@@ -1831,6 +2013,7 @@ var PhoneRuntime = class {
|
|
|
1831
2013
|
await Promise.allSettled(this.reconnecting.values());
|
|
1832
2014
|
for (const controller of this.openings.values()) controller.abort();
|
|
1833
2015
|
await Promise.all([...this.sessions.keys()].map((owner) => this.stop(owner, "close")));
|
|
2016
|
+
await this.locationEnvironment?.close();
|
|
1834
2017
|
this.cloudPhones?.close();
|
|
1835
2018
|
this.sessions.clear();
|
|
1836
2019
|
this.lastDevices.clear();
|
|
@@ -2,6 +2,9 @@ import type { IncomingMessage } from 'node:http';
|
|
|
2
2
|
import type { Socket } from 'node:net';
|
|
3
3
|
import type { PhoneInteractiveSession } from './interactive-session.js';
|
|
4
4
|
type Claim = {
|
|
5
|
+
deviceId?: string;
|
|
6
|
+
readOnly?: boolean;
|
|
7
|
+
inputEpoch?: number;
|
|
5
8
|
origin: string;
|
|
6
9
|
principal: string;
|
|
7
10
|
expiresAt: number;
|
|
@@ -12,6 +15,7 @@ type Claim = {
|
|
|
12
15
|
};
|
|
13
16
|
export declare class PhoneStreamGateway {
|
|
14
17
|
private principalActive;
|
|
18
|
+
private connections;
|
|
15
19
|
private tickets;
|
|
16
20
|
private wss;
|
|
17
21
|
constructor(principalActive?: (principal: string) => boolean);
|
|
@@ -21,6 +25,7 @@ export declare class PhoneStreamGateway {
|
|
|
21
25
|
};
|
|
22
26
|
handleUpgrade(req: IncomingMessage, socket: Socket, head: Buffer): boolean;
|
|
23
27
|
private connection;
|
|
28
|
+
disconnectPrincipal(principal: string): void;
|
|
24
29
|
close(): void;
|
|
25
30
|
}
|
|
26
31
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
3
|
import { PhoneError, init_contracts } from "./contracts.js";
|
|
4
|
+
import { verifyDeviceConnectionProof } from "../gateway/security/device-request-proof.js";
|
|
4
5
|
import { PhonePointerSchema } from "./live-provider.js";
|
|
5
6
|
import { createRequire } from "node:module";
|
|
6
7
|
import { createHash, randomBytes } from "node:crypto";
|
|
@@ -16,6 +17,7 @@ const releaseSchema = z.object({
|
|
|
16
17
|
epoch: z.number().int().positive()
|
|
17
18
|
}).strict();
|
|
18
19
|
var PhoneStreamGateway = class {
|
|
20
|
+
connections = /* @__PURE__ */ new Map();
|
|
19
21
|
tickets = /* @__PURE__ */ new Map();
|
|
20
22
|
wss = new WebSocketServer({
|
|
21
23
|
noServer: true,
|
|
@@ -42,10 +44,11 @@ var PhoneStreamGateway = class {
|
|
|
42
44
|
handleUpgrade(req, socket, head) {
|
|
43
45
|
if (new URL(req.url ?? "/", "http://gateway.local").pathname !== "/api/phone/live/v1/ws") return false;
|
|
44
46
|
const protocols = (req.headers["sec-websocket-protocol"] ?? "").split(",").map((value) => value.trim());
|
|
45
|
-
const
|
|
47
|
+
const ticket = protocols.find((value) => /^[\w-]{43}$/.test(value)) ?? "";
|
|
48
|
+
const key = hash(ticket);
|
|
46
49
|
const claim = this.tickets.get(key);
|
|
47
50
|
this.tickets.delete(key);
|
|
48
|
-
if (!protocols.includes("xopc-phone-v1") || !claim || claim.expiresAt < Date.now() || req.headers.origin !== claim.origin || !this.principalActive(claim.principal) || this.wss.clients.size >= 16) {
|
|
51
|
+
if (!protocols.includes("xopc-phone-v1") || !claim || claim.expiresAt < Date.now() || (claim.deviceId ? !verifyDeviceConnectionProof(claim.deviceId, ticket, "/api/phone/live/v1/ws", typeof req.headers["x-xopc-proof"] === "string" ? req.headers["x-xopc-proof"] : void 0) : req.headers.origin !== claim.origin) || !this.principalActive(claim.principal) || this.wss.clients.size >= 16) {
|
|
49
52
|
socket.destroy();
|
|
50
53
|
return true;
|
|
51
54
|
}
|
|
@@ -55,6 +58,7 @@ var PhoneStreamGateway = class {
|
|
|
55
58
|
return true;
|
|
56
59
|
}
|
|
57
60
|
async connection(ws, claim) {
|
|
61
|
+
this.connections.set(ws, claim);
|
|
58
62
|
let stream;
|
|
59
63
|
let unsubscribe;
|
|
60
64
|
let disposed = false;
|
|
@@ -83,7 +87,13 @@ var PhoneStreamGateway = class {
|
|
|
83
87
|
if (ws.readyState === ws.OPEN) ws.ping();
|
|
84
88
|
if (!validating) {
|
|
85
89
|
validating = true;
|
|
86
|
-
claim.validate().catch(() =>
|
|
90
|
+
claim.validate().catch((error) => {
|
|
91
|
+
log.warn({
|
|
92
|
+
phase: "stream_revalidate",
|
|
93
|
+
errorCode: error instanceof PhoneError ? error.code : "PHONE_ACCESS_CHANGED"
|
|
94
|
+
}, "Phone video authorization changed");
|
|
95
|
+
ws.close(4403, "Phone access changed");
|
|
96
|
+
}).finally(() => {
|
|
87
97
|
validating = false;
|
|
88
98
|
});
|
|
89
99
|
}
|
|
@@ -91,6 +101,7 @@ var PhoneStreamGateway = class {
|
|
|
91
101
|
const cleanup = async () => {
|
|
92
102
|
if (disposed) return;
|
|
93
103
|
disposed = true;
|
|
104
|
+
this.connections.delete(ws);
|
|
94
105
|
clearInterval(timer);
|
|
95
106
|
unsubscribe?.();
|
|
96
107
|
await claim.disconnect().catch(() => {});
|
|
@@ -102,57 +113,76 @@ var PhoneStreamGateway = class {
|
|
|
102
113
|
ws.on("pong", () => {
|
|
103
114
|
lastSeen = Date.now();
|
|
104
115
|
});
|
|
116
|
+
let inputs = Promise.resolve();
|
|
117
|
+
let queuedInputs = 0;
|
|
105
118
|
ws.on("message", (bytes, binary) => {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
119
|
+
if (disposed || ws.readyState !== ws.OPEN) return;
|
|
120
|
+
if (queuedInputs >= 32) {
|
|
121
|
+
ws.close(4408, "Input queue full");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
queuedInputs++;
|
|
125
|
+
inputs = inputs.then(async () => {
|
|
126
|
+
try {
|
|
127
|
+
if (disposed || ws.readyState !== ws.OPEN) return;
|
|
128
|
+
if (binary) throw new Error("Unexpected binary input");
|
|
129
|
+
const input = JSON.parse(bytes.toString());
|
|
130
|
+
if (input.type === "ping") {
|
|
131
|
+
lastSeen = Date.now();
|
|
132
|
+
send({ type: "pong" });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (!stream || disposed) throw new Error("Phone video not ready");
|
|
136
|
+
if (!this.principalActive(claim.principal)) throw new PhoneError("PHONE_ACCESS_REVOKED", "Phone authorization expired", 403);
|
|
137
|
+
await claim.validate();
|
|
138
|
+
if (disposed || ws.readyState !== ws.OPEN) return;
|
|
139
|
+
if (claim.readOnly) throw new PhoneError("PHONE_OBSERVE_ONLY", "This stream is read only", 403);
|
|
140
|
+
const epoch = input.epoch;
|
|
141
|
+
if (claim.inputEpoch !== void 0 && epoch !== claim.inputEpoch) throw new PhoneError("PHONE_CONTROL_CHANGED", "Control changed", 409);
|
|
142
|
+
const cancel = releaseSchema.safeParse(input);
|
|
143
|
+
if (cancel.success) {
|
|
144
|
+
stream.authorizeClient(claim.client, cancel.data.epoch);
|
|
145
|
+
stream.revoke().catch(() => ws.close(4411, "Touch release failed"));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const pointer = PhonePointerSchema.parse(input);
|
|
149
|
+
if (Date.now() - windowAt > 1e3) {
|
|
150
|
+
windowAt = Date.now();
|
|
151
|
+
events = 0;
|
|
152
|
+
}
|
|
153
|
+
if (++events > 120) throw new Error("Input rate exceeded");
|
|
110
154
|
lastSeen = Date.now();
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
log.warn({
|
|
137
|
-
phase: "input_dispatch",
|
|
138
|
-
errorCode: code
|
|
139
|
-
}, "Phone input channel closed after refused input");
|
|
155
|
+
const startedAt = performance.now();
|
|
156
|
+
await stream.input(claim.client, pointer).then(() => send({
|
|
157
|
+
type: "ack",
|
|
158
|
+
seq: pointer.seq,
|
|
159
|
+
phase: "transport-written",
|
|
160
|
+
durationMs: performance.now() - startedAt
|
|
161
|
+
}), (error) => {
|
|
162
|
+
const code = error instanceof PhoneError ? error.code : "PHONE_INPUT_FAILED";
|
|
163
|
+
log.warn({
|
|
164
|
+
phase: "input_dispatch",
|
|
165
|
+
errorCode: code
|
|
166
|
+
}, "Phone input channel closed after refused input");
|
|
167
|
+
fail(code);
|
|
168
|
+
ws.close(4409, "Input rejected");
|
|
169
|
+
});
|
|
170
|
+
} catch (error) {
|
|
171
|
+
const code = error instanceof PhoneError ? error.code : "PHONE_INVALID_INPUT";
|
|
172
|
+
log.info({
|
|
173
|
+
phase: "input_validation",
|
|
174
|
+
errorCode: code,
|
|
175
|
+
issues: error instanceof z.ZodError ? error.issues.map((issue) => ({
|
|
176
|
+
path: issue.path.join("."),
|
|
177
|
+
code: issue.code
|
|
178
|
+
})) : void 0
|
|
179
|
+
}, "Phone input refused");
|
|
140
180
|
fail(code);
|
|
141
|
-
ws.close(
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
phase: "input_validation",
|
|
147
|
-
errorCode: code,
|
|
148
|
-
issues: error instanceof z.ZodError ? error.issues.map((issue) => ({
|
|
149
|
-
path: issue.path.join("."),
|
|
150
|
-
code: issue.code
|
|
151
|
-
})) : void 0
|
|
152
|
-
}, "Phone input refused");
|
|
153
|
-
fail(code);
|
|
154
|
-
ws.close(4400, "Invalid input");
|
|
155
|
-
}
|
|
181
|
+
ws.close(4400, "Invalid input");
|
|
182
|
+
}
|
|
183
|
+
}).finally(() => {
|
|
184
|
+
queuedInputs--;
|
|
185
|
+
});
|
|
156
186
|
});
|
|
157
187
|
try {
|
|
158
188
|
await claim.validate();
|
|
@@ -200,6 +230,10 @@ var PhoneStreamGateway = class {
|
|
|
200
230
|
ws.close(4411, "Phone video unavailable");
|
|
201
231
|
}
|
|
202
232
|
}
|
|
233
|
+
disconnectPrincipal(principal) {
|
|
234
|
+
for (const [key, claim] of this.tickets) if (claim.principal === principal) this.tickets.delete(key);
|
|
235
|
+
for (const [ws, claim] of this.connections) if (claim.principal === principal) ws.close(4403, "Phone authorization changed");
|
|
236
|
+
}
|
|
203
237
|
close() {
|
|
204
238
|
this.tickets.clear();
|
|
205
239
|
for (const ws of this.wss.clients) ws.terminate();
|