@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.
Files changed (279) hide show
  1. package/dist/browser-ext/dist/background.js +184 -2
  2. package/dist/browser-ext/dist/sidepanel.js +9 -0
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{activity-store-CKrylgib.js → activity-store-CvJ9rdwP.js} +1 -1
  6. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Dv5Rda4y.js → agent-avatar-dicebear-CFP6g4w7.js} +1 -1
  7. package/dist/gateway/static/root/assets/{agent-avatar-display-B1ycycx2.js → agent-avatar-display-BHBe88Gu.js} +2 -2
  8. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-D285zy0S.js → agent-defaults-settings-panel-DlGRuKMv.js} +1 -1
  9. package/dist/gateway/static/root/assets/{agent-plugin-dialog-D3yCqVan.js → agent-plugin-dialog-C_Dm-u6_.js} +1 -1
  10. package/dist/gateway/static/root/assets/{agents-DyAuR3rG.js → agents-BdVJHOiK.js} +3 -3
  11. package/dist/gateway/static/root/assets/{agents-admin-api-CASGzvkW.js → agents-admin-api-Do1Pza0R.js} +1 -1
  12. package/dist/gateway/static/root/assets/{api-Uk05lF9i.js → api-BQv5dLcH.js} +1 -1
  13. package/dist/gateway/static/root/assets/{api-C4LxuNzq.js → api-ONg37_fH.js} +1 -1
  14. package/dist/gateway/static/root/assets/app-link-1j40sJ-z.js +1 -0
  15. package/dist/gateway/static/root/assets/{app-management-settings-panel-CDnZNKHt.js → app-management-settings-panel-B4Wqhf4J.js} +1 -1
  16. package/dist/gateway/static/root/assets/{appearance-settings-CmCF7PZ5.js → appearance-settings-Dm1kRzKL.js} +1 -1
  17. package/dist/gateway/static/root/assets/{apps-page-BR9qoA2P.js → apps-page-D1MMk1P7.js} +1 -1
  18. package/dist/gateway/static/root/assets/{archive-plugin-nuHYYVty.js → archive-plugin-p_yLkyaC.js} +1 -1
  19. package/dist/gateway/static/root/assets/{automation-api-DtJfd1QM.js → automation-api-DQaWBR03.js} +1 -1
  20. package/dist/gateway/static/root/assets/automation-result-state-CO_243JM.js +1 -0
  21. package/dist/gateway/static/root/assets/automation-task-card-D2p9qlYE.js +3 -0
  22. package/dist/gateway/static/root/assets/{automations-page-DM4IdUJh.js → automations-page-Ba64H37d.js} +1 -1
  23. package/dist/gateway/static/root/assets/automations-workspace-BkzC3oIM.js +9 -0
  24. package/dist/gateway/static/root/assets/{binary-plugins-CHCAr8jt.js → binary-plugins-B-6kyqtt.js} +1 -1
  25. package/dist/gateway/static/root/assets/{block-editor-B0v8KxsO.js → block-editor-B5ysJ96n.js} +1 -1
  26. package/dist/gateway/static/root/assets/{browser-automation-inputs-DjUW7PEB.js → browser-automation-inputs-C2A3NIt3.js} +1 -1
  27. package/dist/gateway/static/root/assets/{browser-automations-page-Cf8AoQz1.js → browser-automations-page-CnbXFw1_.js} +1 -1
  28. package/dist/gateway/static/root/assets/{browser-control-api-C6NZgaA1.js → browser-control-api-Aj_seKE9.js} +1 -1
  29. package/dist/gateway/static/root/assets/{browser-settings-page-DV2FNEDf.js → browser-settings-page-C7fxPq_z.js} +1 -1
  30. package/dist/gateway/static/root/assets/{capabilities-CwdjWz2r.js → capabilities-BgtCwC_E.js} +1 -1
  31. package/dist/gateway/static/root/assets/{capabilities-D0-HCZ5J.js → capabilities-DOTgaSgA.js} +1 -1
  32. package/dist/gateway/static/root/assets/{capabilities-page-DwDkritH.js → capabilities-page-m3b9sLFG.js} +2 -2
  33. package/dist/gateway/static/root/assets/{capabilities-settings-panel-mme16Fbp.js → capabilities-settings-panel-pYWZtPeU.js} +3 -3
  34. package/dist/gateway/static/root/assets/{channels-lKFLu8p-.js → channels-DGpRM6pj.js} +1 -1
  35. package/dist/gateway/static/root/assets/{chat-agents-api-l80cvSac.js → chat-agents-api-EvJ_Wvuc.js} +1 -1
  36. package/dist/gateway/static/root/assets/{chat-chart-block-BwP5QXEk.js → chat-chart-block-BQDbHLcw.js} +1 -1
  37. package/dist/gateway/static/root/assets/chat-page-GF9NIuZN.js +72 -0
  38. package/dist/gateway/static/root/assets/{chat-terminal-dock-D2bV9YUL.js → chat-terminal-dock-DHFTGoEp.js} +2 -2
  39. package/dist/gateway/static/root/assets/{command-palette-api-CtapG8ck.js → command-palette-api-DPA_yEuy.js} +1 -1
  40. package/dist/gateway/static/root/assets/{composer-payload-handoff-DYuhKZRC.js → composer-payload-handoff-BTjauSNp.js} +1 -1
  41. package/dist/gateway/static/root/assets/{computer-settings-page-DZtMuihL.js → computer-settings-page-BFSSQfcx.js} +1 -1
  42. package/dist/gateway/static/root/assets/{connected-providers-grid-BQH5k3iC.js → connected-providers-grid-DM2glQN3.js} +1 -1
  43. package/dist/gateway/static/root/assets/{connector-service-page-DrvFdP3G.js → connector-service-page-CFvg58qa.js} +1 -1
  44. package/dist/gateway/static/root/assets/{connectors-api-BzkgVBYC.js → connectors-api-CbC6Yht5.js} +1 -1
  45. package/dist/gateway/static/root/assets/{connectors-page-BtRNVCFy.js → connectors-page-D7pipFHA.js} +1 -1
  46. package/dist/gateway/static/root/assets/date-picker-Dd3MbG0x.js +1 -0
  47. package/dist/gateway/static/root/assets/{dependency-picker-Dh8hPmsT.js → dependency-picker-DcsHg2ml.js} +1 -1
  48. package/dist/gateway/static/root/assets/{desktop-pet-CFz8bqyI.js → desktop-pet-DldzQT2h.js} +1 -1
  49. package/dist/gateway/static/root/assets/{desktop-pet-settings-COaboNn1.js → desktop-pet-settings-CYdI4KxB.js} +1 -1
  50. package/dist/gateway/static/root/assets/{dev-view-store-D8XYHVqG.js → dev-view-store-JiJv585f.js} +1 -1
  51. package/dist/gateway/static/root/assets/device-pairing-wizard-D8cLc41G.js +1 -0
  52. package/dist/gateway/static/root/assets/{directory-picker-path-field-Cjv7rdo3.js → directory-picker-path-field-8WOXv8zw.js} +1 -1
  53. package/dist/gateway/static/root/assets/{discussion-annotation-sync-YvlVw17j.js → discussion-annotation-sync-B-grSe0w.js} +1 -1
  54. package/dist/gateway/static/root/assets/{extension-debug-page-CXx3m5-B.js → extension-debug-page-EEQkTNEu.js} +1 -1
  55. package/dist/gateway/static/root/assets/{extension-iframe-host-CnOdNwbo.js → extension-iframe-host-vlZVeMCY.js} +1 -1
  56. package/dist/gateway/static/root/assets/{extension-marketplace-api-BABmUY2s.js → extension-marketplace-api-_o2In_al.js} +1 -1
  57. package/dist/gateway/static/root/assets/{extension-page-C1nGr4DD.js → extension-page-CPrT111s.js} +1 -1
  58. package/dist/gateway/static/root/assets/{extension-provider-BA0Y2Rvs.js → extension-provider-B2Mf3FBu.js} +1 -1
  59. package/dist/gateway/static/root/assets/{extension-settings-page-QIPI8e7k.js → extension-settings-page-CznJEgWV.js} +1 -1
  60. package/dist/gateway/static/root/assets/{file-preview-surface-BsB7bnFH.js → file-preview-surface-7h8_fZul.js} +1 -1
  61. package/dist/gateway/static/root/assets/{gateway-config-swr-Bt8w0Iio.js → gateway-config-swr-DbbNEOKj.js} +1 -1
  62. package/dist/gateway/static/root/assets/{gateway-realtime-CyCCNUxm.js → gateway-realtime-D20ZrAuM.js} +1 -1
  63. package/dist/gateway/static/root/assets/gateway-realtime-store-C6sMzYDT.js +1 -0
  64. package/dist/gateway/static/root/assets/{gateway-restart-Ty4AzDxa.js → gateway-restart-5ut-lhJm.js} +1 -1
  65. package/dist/gateway/static/root/assets/{gateway-settings-BvKq7vuz.js → gateway-settings-BCf_cibp.js} +1 -1
  66. package/dist/gateway/static/root/assets/{gateway-startup-retry-N3k-C7c2.js → gateway-startup-retry-DlHL0DpH.js} +1 -1
  67. package/dist/gateway/static/root/assets/{global-defaults-api-D8EB2vfY.js → global-defaults-api-CmODomcV.js} +1 -1
  68. package/dist/gateway/static/root/assets/{home-page-eRuAzi58.js → home-page-uz22TRB-.js} +2 -2
  69. package/dist/gateway/static/root/assets/{image-generation-api-C99Fsofm.js → image-generation-api-dzPnpUFu.js} +1 -1
  70. package/dist/gateway/static/root/assets/{imports-page-Cp9RquVb.js → imports-page-ChBoNMyE.js} +1 -1
  71. package/dist/gateway/static/root/assets/index-CSIg7V1A.js +11 -0
  72. package/dist/gateway/static/root/assets/index-Dv8mUlNN.css +1 -0
  73. package/dist/gateway/static/root/assets/{install-connector-dialog-CsoAidXp.js → install-connector-dialog-CNcdQDX0.js} +1 -1
  74. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DFhOP2aD.js → keyboard-shortcuts-settings-CRbYq2_g.js} +1 -1
  75. package/dist/gateway/static/root/assets/{local-app-workbench-page-DbtU_A-l.js → local-app-workbench-page-04cX5TJu.js} +1 -1
  76. package/dist/gateway/static/root/assets/{local-apps-page-rjhHfLpe.js → local-apps-page-fWM0FEhN.js} +1 -1
  77. package/dist/gateway/static/root/assets/{local-session-drafts-DHM4vWDO.js → local-session-drafts-ClrSe5mH.js} +1 -1
  78. package/dist/gateway/static/root/assets/{locale-store-5Hf8J8-_.js → locale-store-VNjH18Dj.js} +1 -1
  79. package/dist/gateway/static/root/assets/{logs-page-Dx_JIOpF.js → logs-page-6GanGLrm.js} +1 -1
  80. package/dist/gateway/static/root/assets/management-settings-BxD_lc4E.js +1 -0
  81. package/dist/gateway/static/root/assets/{markdown-view-pzSRu7av.js → markdown-view-DLCBYWtx.js} +2 -2
  82. package/dist/gateway/static/root/assets/{media-plugins-3cJuHbO7.js → media-plugins-BvtNwKFb.js} +1 -1
  83. package/dist/gateway/static/root/assets/{messages-uXxg5p54.js → messages-Btr7C0hF.js} +2 -2
  84. package/dist/gateway/static/root/assets/{model-selector-ksLo-5B_.js → model-selector-DYUnp917.js} +1 -1
  85. package/dist/gateway/static/root/assets/{models-hub-cache-DhZng2nF.js → models-hub-cache-_qE7L-_H.js} +1 -1
  86. package/dist/gateway/static/root/assets/{note-detail-panel-B6RbjwJq.js → note-detail-panel-bjRZesmS.js} +3 -3
  87. package/dist/gateway/static/root/assets/note-image-lightbox-D7Jqb75C.js +1 -0
  88. package/dist/gateway/static/root/assets/{notes-api-DocbsBbT.js → notes-api-Bxa11X3a.js} +1 -1
  89. package/dist/gateway/static/root/assets/{notes-page-B7889s6-.js → notes-page-D2QpJ3WZ.js} +1 -1
  90. package/dist/gateway/static/root/assets/{oauth-api-CCFIPbA0.js → oauth-api-DIE4UpP7.js} +1 -1
  91. package/dist/gateway/static/root/assets/{oauth-provider-connect-GxkF0vWJ.js → oauth-provider-connect-B95tiddN.js} +1 -1
  92. package/dist/gateway/static/root/assets/{page-context-capture-button-CvWLVeRA.js → page-context-capture-button-C-QbbwmN.js} +1 -1
  93. package/dist/gateway/static/root/assets/{page-context-draft-CMRDNv4d.js → page-context-draft-BHmOfksL.js} +1 -1
  94. package/dist/gateway/static/root/assets/page-header-store-bPV8_tKL.js +1 -0
  95. package/dist/gateway/static/root/assets/{personal-avatar-CqNJ48Ge.js → personal-avatar-nXrmMfrV.js} +1 -1
  96. package/dist/gateway/static/root/assets/personal-page-BCwlYT3F.js +1 -0
  97. package/dist/gateway/static/root/assets/{personal-settings-panel-DAcl8DU4.js → personal-settings-panel-vkYmXK8h.js} +1 -1
  98. package/dist/gateway/static/root/assets/{phone-dependencies-card-DLWblGj7.js → phone-dependencies-card-CZDAxTWv.js} +1 -1
  99. package/dist/gateway/static/root/assets/{phone-model-picker-ugrT9GJu.js → phone-model-picker-4Snfs8Hq.js} +1 -1
  100. package/dist/gateway/static/root/assets/{phone-settings-page-DIIt7JOE.js → phone-settings-page-D8HperfA.js} +1 -1
  101. package/dist/gateway/static/root/assets/{phone-workspace-store-mawKT5kV.js → phone-workspace-store-Di9R9Ux5.js} +1 -1
  102. package/dist/gateway/static/root/assets/{plugin-details-dialog-BdueMT3q.js → plugin-details-dialog-DJsDu-_8.js} +1 -1
  103. package/dist/gateway/static/root/assets/{preference-select-fields-CmLZNwbm.js → preference-select-fields-C6abaM3k.js} +1 -1
  104. package/dist/gateway/static/root/assets/{product-automation-feedback-DYwhL37Y.js → product-automation-feedback-C74s9Mjz.js} +1 -1
  105. package/dist/gateway/static/root/assets/product-delivery-DRyD9_Xq.js +1 -0
  106. package/dist/gateway/static/root/assets/{product-open-page-B5G4X71t.js → product-open-page-AbDP5w1c.js} +1 -1
  107. package/dist/gateway/static/root/assets/product-preview-content-CktjvFbc.js +1 -0
  108. package/dist/gateway/static/root/assets/product-preview-modal-CzsqLwB4.js +2 -0
  109. package/dist/gateway/static/root/assets/{project-detail-page-CRJb69vP.js → project-detail-page-Cf3tggGP.js} +2 -2
  110. package/dist/gateway/static/root/assets/{project-understanding-D3w8cZEm.js → project-understanding-jmhFbHch.js} +1 -1
  111. package/dist/gateway/static/root/assets/{projects-page-CkifzKjz.js → projects-page-jMgZiFnO.js} +1 -1
  112. package/dist/gateway/static/root/assets/{providers-api-CKFg2PZQ.js → providers-api-CJBYFKAK.js} +1 -1
  113. package/dist/gateway/static/root/assets/{recording-upload-ZkmRf0GR.js → recording-upload-Dl9a5yOV.js} +1 -1
  114. package/dist/gateway/static/root/assets/{registry-api-B1Mh3bNo.js → registry-api-BwkbPUYu.js} +1 -1
  115. package/dist/gateway/static/root/assets/{remote-access-hub-Dw5rG-aA.js → remote-access-hub-CBQ_bOy9.js} +1 -1
  116. package/dist/gateway/static/root/assets/{resource-unavailable-BUWsH3fp.js → resource-unavailable-35wBZxmz.js} +1 -1
  117. package/dist/gateway/static/root/assets/route-preload-BcA6SPeC.js +2 -0
  118. package/dist/gateway/static/root/assets/{runtime-tools-api-BXvn5ImA.js → runtime-tools-api-BF28JDAO.js} +1 -1
  119. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BALcypHp.js → runtime-tools-settings-panel-Bq2xQ2qd.js} +1 -1
  120. package/dist/gateway/static/root/assets/{service-recommendation-list-CpVUnz89.js → service-recommendation-list-CdaS4MPF.js} +1 -1
  121. package/dist/gateway/static/root/assets/{session-api-VquhIBHI.js → session-api-zJXYlRPt.js} +1 -1
  122. package/dist/gateway/static/root/assets/session-manager-wWxpzSo0.js +23 -0
  123. package/dist/gateway/static/root/assets/{sessions-page-DlvPkE5Y.js → sessions-page-BkjD_dUD.js} +1 -1
  124. package/dist/gateway/static/root/assets/settings-advanced-gate-BUMCEtA_.js +1 -0
  125. package/dist/gateway/static/root/assets/{settings-advanced-section-c9_iZ-46.js → settings-advanced-section-C84IghdL.js} +1 -1
  126. package/dist/gateway/static/root/assets/{settings-mode-store-Dem6-TBe.js → settings-mode-store-CtQBJvbO.js} +1 -1
  127. package/dist/gateway/static/root/assets/{settings-page-B_wNfFQe.js → settings-page-eEIz9InP.js} +1 -1
  128. package/dist/gateway/static/root/assets/{settings-page-header-C8a_oOyw.js → settings-page-header-CsPAYlqV.js} +1 -1
  129. package/dist/gateway/static/root/assets/{settings-page-layout-CTSRWljG.js → settings-page-layout-lPLeJQoV.js} +1 -1
  130. package/dist/gateway/static/root/assets/{setup-status-panel-BNZoqF40.js → setup-status-panel-BSU5WpaE.js} +1 -1
  131. package/dist/gateway/static/root/assets/{share-link-dialog-C5JTTxsy.js → share-link-dialog-NaM6jvIz.js} +1 -1
  132. package/dist/gateway/static/root/assets/{share-preview-page-D-KxzXgm.js → share-preview-page-CHjZn27s.js} +1 -1
  133. package/dist/gateway/static/root/assets/{shares-api-CztKbxdd.js → shares-api-B9rVcV1y.js} +1 -1
  134. package/dist/gateway/static/root/assets/{shares-settings-CzR21j6e.js → shares-settings-5sB15V5v.js} +1 -1
  135. package/dist/gateway/static/root/assets/{skill-api-B7kWl6LK.js → skill-api-D6zVfEuK.js} +1 -1
  136. package/dist/gateway/static/root/assets/{skill-list-api-Cn07P6qZ.js → skill-list-api-SLmTBYF5.js} +1 -1
  137. package/dist/gateway/static/root/assets/{skill-reload-api-Bhl0u16_.js → skill-reload-api-UQkq2orI.js} +1 -1
  138. package/dist/gateway/static/root/assets/{skills-page-BmkuxHZe.js → skills-page-Ca0pYZ4i.js} +1 -1
  139. package/dist/gateway/static/root/assets/{system-settings-panel-Ce6zIcki.js → system-settings-panel-BARWbjRN.js} +1 -1
  140. package/dist/gateway/static/root/assets/{task-detail-page-CysJhG3B.js → task-detail-page-roqAsGue.js} +2 -2
  141. package/dist/gateway/static/root/assets/{task-result-evidence-CGZKMnNo.js → task-result-evidence-Bb1F7nAM.js} +1 -1
  142. package/dist/gateway/static/root/assets/{terminal-panel-store-YwLUkuyV.js → terminal-panel-store-DYDeXXAg.js} +1 -1
  143. package/dist/gateway/static/root/assets/{theme-store-DOJ9tu-j.js → theme-store-DbStcH89.js} +1 -1
  144. package/dist/gateway/static/root/assets/{tracing-settings-page-BplBpTdL.js → tracing-settings-page-YfkNCxbP.js} +1 -1
  145. package/dist/gateway/static/root/assets/{tunnel-api-B5-Ypvqa.js → tunnel-api-BK_ivyIb.js} +1 -1
  146. package/dist/gateway/static/root/assets/{understanding-activity-store-CmwtE8Kd.js → understanding-activity-store-p7Z7oq8z.js} +1 -1
  147. package/dist/gateway/static/root/assets/{understanding-refresh-store-9apSmAVL.js → understanding-refresh-store-D-U6rcwZ.js} +1 -1
  148. package/dist/gateway/static/root/assets/{usage-page-cPbirpXX.js → usage-page-URFSiswS.js} +1 -1
  149. package/dist/gateway/static/root/assets/{use-autosave-B3p2QWaF.js → use-autosave-B7cbumro.js} +1 -1
  150. package/dist/gateway/static/root/assets/{use-composer-attachments-D-AEYC85.js → use-composer-attachments-DAtiwLsE.js} +1 -1
  151. package/dist/gateway/static/root/assets/{use-file-preview-expanded-CO4SC-LX.js → use-file-preview-expanded-B8erpWsX.js} +2 -2
  152. package/dist/gateway/static/root/assets/{use-personal-profile-DocmhC9I.js → use-personal-profile-DDW29nm-.js} +1 -1
  153. package/dist/gateway/static/root/assets/{use-workspace-tree-VraTGKx-.js → use-workspace-tree-DCoTf22f.js} +1 -1
  154. package/dist/gateway/static/root/assets/{user-model-page-DuRALzhe.js → user-model-page-DSV5NkNj.js} +1 -1
  155. package/dist/gateway/static/root/assets/{voice-api-key-field-DbSQHZZW.js → voice-api-key-field-DzYdJZN4.js} +1 -1
  156. package/dist/gateway/static/root/assets/{voice-config-api-D9iNJSvm.js → voice-config-api-wotvT6Hw.js} +1 -1
  157. package/dist/gateway/static/root/assets/{voice-input-shortcut-events-hso9_VvT.js → voice-input-shortcut-events-BI6PuiIB.js} +1 -1
  158. package/dist/gateway/static/root/assets/voice-transcribe-api-DAK9eLRr.js +1 -0
  159. package/dist/gateway/static/root/assets/{work-discovery-overlay-fsL9RJqB.js → work-discovery-overlay-BzmB9Rub.js} +1 -1
  160. package/dist/gateway/static/root/assets/{work-discovery-page-DAQPKVDs.js → work-discovery-page-CmGjxUIT.js} +1 -1
  161. package/dist/gateway/static/root/assets/{working-directory-picker-modal-Ta73QrNw.js → working-directory-picker-modal-B-EHm52-.js} +1 -1
  162. package/dist/gateway/static/root/assets/{workspace-api-CgrcNyBc.js → workspace-api-uhiEMPpK.js} +2 -2
  163. package/dist/gateway/static/root/assets/workspace-editor-agent-store-DrPVyikA.js +1 -0
  164. package/dist/gateway/static/root/assets/{workspace-file-preview-dialog-B7uBuPtA.js → workspace-file-preview-dialog-BDm06MZm.js} +1 -1
  165. package/dist/gateway/static/root/assets/{workspace-open-location-menu-Bi2vRvaQ.js → workspace-open-location-menu-Bv83uoqu.js} +1 -1
  166. package/dist/gateway/static/root/assets/{xopc-cloud-account-card-CCOOBG-7.js → xopc-cloud-account-card-Cd5GdpGL.js} +1 -1
  167. package/dist/gateway/static/root/index.html +84 -84
  168. package/dist/gateway/static/root/notification-sw.js +21 -7
  169. package/dist/package.js +1 -1
  170. package/dist/packages/endpoint-tools-protocol/src/context.js +115 -0
  171. package/dist/packages/endpoint-tools-protocol/src/index.js +4 -0
  172. package/dist/packages/endpoint-tools-protocol/src/location-sharing.js +51 -0
  173. package/dist/packages/gateway-contract/src/gateway-scopes.js +5 -1
  174. package/dist/packages/gateway-contract/src/index.js +2 -0
  175. package/dist/packages/gateway-contract/src/phone-workspace.js +6 -0
  176. package/dist/packages/gateway-contract/src/product-delivery.js +9 -3
  177. package/dist/src/agent/embedded/external-tool-discovery.js +1 -1
  178. package/dist/src/agent/embedded/run-for-session.js +2 -2
  179. package/dist/src/agent/embedded/run-turn.js +72 -9
  180. package/dist/src/agent/embedded/session-runner.js +2 -1
  181. package/dist/src/agent/embedded/types.d.ts +1 -1
  182. package/dist/src/agent/prompt/system-prompt.js +1 -0
  183. package/dist/src/agent/service/direct-turn-helpers.d.ts +0 -7
  184. package/dist/src/agent/service/direct-turn-helpers.js +3 -2
  185. package/dist/src/agent/service/process-direct-streaming.js +2 -1
  186. package/dist/src/agent/source-context/device-environment.d.ts +3 -0
  187. package/dist/src/agent/source-context/device-environment.js +38 -0
  188. package/dist/src/agent/source-context/injector.js +2 -1
  189. package/dist/src/agent/tool-manuals/phone.d.ts +1 -1
  190. package/dist/src/agent/tool-manuals/phone.js +1 -1
  191. package/dist/src/agent/tools/automation-tool.js +3 -2
  192. package/dist/src/agent/tools/factory.js +1 -1
  193. package/dist/src/agent/tools/personal-read-tool.js +1 -1
  194. package/dist/src/agent/tools/phone-use-tool.js +46 -2
  195. package/dist/src/agent/tools/xopc-use-tool.js +1 -0
  196. package/dist/src/computer/runtime.d.ts +1 -1
  197. package/dist/src/config/schema.d.ts +1 -0
  198. package/dist/src/endpoint-tools/context-store.d.ts +29 -0
  199. package/dist/src/endpoint-tools/context-store.js +138 -0
  200. package/dist/src/endpoint-tools/location-sharing.d.ts +42 -0
  201. package/dist/src/endpoint-tools/location-sharing.js +203 -0
  202. package/dist/src/endpoint-tools/runtime.d.ts +4 -0
  203. package/dist/src/endpoint-tools/runtime.js +8 -0
  204. package/dist/src/endpoint-tools/turn-context.d.ts +2 -1
  205. package/dist/src/endpoint-tools/turn-context.js +4 -2
  206. package/dist/src/gateway/hono/middleware/auth.js +1 -1
  207. package/dist/src/gateway/hono/routes/devices.js +1 -0
  208. package/dist/src/gateway/hono/routes/endpoint-compatibility.js +4 -0
  209. package/dist/src/gateway/hono/routes/endpoint-environment.d.ts +3 -0
  210. package/dist/src/gateway/hono/routes/endpoint-environment.js +98 -0
  211. package/dist/src/gateway/hono/routes/endpoint-tools.js +1 -0
  212. package/dist/src/gateway/hono/routes/lazy-bundles.js +16 -0
  213. package/dist/src/gateway/hono/routes/location-sharing.d.ts +3 -0
  214. package/dist/src/gateway/hono/routes/location-sharing.js +164 -0
  215. package/dist/src/gateway/hono/routes/phone.js +246 -34
  216. package/dist/src/gateway/hono/routes/session-command-handler.js +1 -1
  217. package/dist/src/gateway/hono/routes/session-input-handler.js +2 -1
  218. package/dist/src/gateway/hono/routes/tunnel.js +10 -0
  219. package/dist/src/gateway/security/gateway-scopes.js +6 -0
  220. package/dist/src/gateway/security/phone-access.d.ts +4 -0
  221. package/dist/src/gateway/security/phone-access.js +20 -0
  222. package/dist/src/gateway/service/agent-runner.js +3 -1
  223. package/dist/src/gateway/service/config-coordinator.d.ts +1 -0
  224. package/dist/src/gateway/service/run-gateway-agent.js +2 -1
  225. package/dist/src/gateway/service/session-input-coordinator.d.ts +3 -0
  226. package/dist/src/gateway/service/session-input-coordinator.js +50 -2
  227. package/dist/src/gateway/service.d.ts +1 -0
  228. package/dist/src/gateway/service.js +8 -0
  229. package/dist/src/gateway/session-reset-service.js +4 -0
  230. package/dist/src/notifications/planner.js +6 -6
  231. package/dist/src/phone/assistance-service.d.ts +6 -0
  232. package/dist/src/phone/assistance-service.js +32 -0
  233. package/dist/src/phone/assistance.d.ts +40 -0
  234. package/dist/src/phone/assistance.js +273 -0
  235. package/dist/src/phone/cloud-phones.d.ts +5 -0
  236. package/dist/src/phone/cloud-phones.js +14 -0
  237. package/dist/src/phone/contracts.d.ts +7 -1
  238. package/dist/src/phone/contracts.js +5 -0
  239. package/dist/src/phone/environment-adapter.d.ts +3 -0
  240. package/dist/src/phone/environment-adapter.js +11 -0
  241. package/dist/src/phone/location-baseline-store.d.ts +9 -0
  242. package/dist/src/phone/location-baseline-store.js +61 -0
  243. package/dist/src/phone/location-environment.d.ts +61 -0
  244. package/dist/src/phone/location-environment.js +157 -0
  245. package/dist/src/phone/model-adapter.js +16 -5
  246. package/dist/src/phone/runtime.d.ts +17 -2
  247. package/dist/src/phone/runtime.js +203 -20
  248. package/dist/src/phone/stream-gateway.d.ts +5 -0
  249. package/dist/src/phone/stream-gateway.js +85 -51
  250. package/dist/src/phone/wuying-location.d.ts +18 -0
  251. package/dist/src/phone/wuying-location.js +86 -0
  252. package/dist/src/storage/sqlite/device-access-repository.d.ts +4 -0
  253. package/dist/src/storage/sqlite/device-access-repository.js +18 -1
  254. package/dist/src/tunnel/broker-client.d.ts +8 -0
  255. package/dist/src/tunnel/broker-client.js +18 -1
  256. package/dist/src/voice/realtime/runtime.js +1 -1
  257. package/package.json +4 -3
  258. package/dist/gateway/static/root/assets/app-link-CytZ5zkp.js +0 -1
  259. package/dist/gateway/static/root/assets/automation-display-BlGt8MAC.js +0 -2
  260. package/dist/gateway/static/root/assets/automation-task-card-Cp3RIw5d.js +0 -2
  261. package/dist/gateway/static/root/assets/automations-workspace-CXLu04I-.js +0 -9
  262. package/dist/gateway/static/root/assets/chat-page-CT3K7lFT.js +0 -71
  263. package/dist/gateway/static/root/assets/date-picker-ByTasrfl.js +0 -1
  264. package/dist/gateway/static/root/assets/device-pairing-wizard-BKMpLdIA.js +0 -1
  265. package/dist/gateway/static/root/assets/gateway-realtime-store-0gmDfPeP.js +0 -1
  266. package/dist/gateway/static/root/assets/index-6vVrWfwM.js +0 -11
  267. package/dist/gateway/static/root/assets/index-DDGK6klk.css +0 -1
  268. package/dist/gateway/static/root/assets/management-settings-CuSzixOw.js +0 -1
  269. package/dist/gateway/static/root/assets/note-image-lightbox-BCjvBqwy.js +0 -1
  270. package/dist/gateway/static/root/assets/page-header-store-reh1es-3.js +0 -1
  271. package/dist/gateway/static/root/assets/personal-page-D95FHaw9.js +0 -1
  272. package/dist/gateway/static/root/assets/product-delivery-BU_H84jL.js +0 -1
  273. package/dist/gateway/static/root/assets/product-preview-content-BUheyLqt.js +0 -1
  274. package/dist/gateway/static/root/assets/product-preview-modal-Cj_Zxu4G.js +0 -2
  275. package/dist/gateway/static/root/assets/route-preload-CRKbVwQ1.js +0 -2
  276. package/dist/gateway/static/root/assets/session-manager-DNMosCtW.js +0 -23
  277. package/dist/gateway/static/root/assets/settings-advanced-gate-Ckc1aCLG.js +0 -1
  278. package/dist/gateway/static/root/assets/voice-transcribe-api-BqIs0jgv.js +0 -1
  279. 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") session.taskPending = false;
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.publish(session);
1038
- await this.waitForAgent(session, signal);
1039
- observation.frame.fill(0);
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
- this.cloudPhones?.disconnect(id);
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 key = hash(protocols.find((value) => /^[\w-]{43}$/.test(value)) ?? "");
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(() => ws.close(4403, "Phone access changed")).finally(() => {
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
- try {
107
- if (binary) throw new Error("Unexpected binary input");
108
- const input = JSON.parse(bytes.toString());
109
- if (input.type === "ping") {
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
- send({ type: "pong" });
112
- return;
113
- }
114
- if (!stream || disposed) throw new Error("Phone video not ready");
115
- const cancel = releaseSchema.safeParse(input);
116
- if (cancel.success) {
117
- stream.authorizeClient(claim.client, cancel.data.epoch);
118
- stream.revoke().catch(() => ws.close(4411, "Touch release failed"));
119
- return;
120
- }
121
- const pointer = PhonePointerSchema.parse(input);
122
- if (Date.now() - windowAt > 1e3) {
123
- windowAt = Date.now();
124
- events = 0;
125
- }
126
- if (++events > 120) throw new Error("Input rate exceeded");
127
- lastSeen = Date.now();
128
- const startedAt = performance.now();
129
- stream.input(claim.client, pointer).then(() => send({
130
- type: "ack",
131
- seq: pointer.seq,
132
- phase: "transport-written",
133
- durationMs: performance.now() - startedAt
134
- }), (error) => {
135
- const code = error instanceof PhoneError ? error.code : "PHONE_INPUT_FAILED";
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(4409, "Input rejected");
142
- });
143
- } catch (error) {
144
- const code = error instanceof PhoneError ? error.code : "PHONE_INVALID_INPUT";
145
- log.info({
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();