@xopcai/xopc 0.0.415 → 0.0.416

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 (251) hide show
  1. package/dist/browser-ext/_locales/en/messages.json +4 -1
  2. package/dist/browser-ext/_locales/zh_CN/messages.json +4 -1
  3. package/dist/browser-ext/dist/assets/{page-context-MuzjMBlJ.js → page-context-CVvmTWZb.js} +33 -9
  4. package/dist/browser-ext/dist/background.js +39 -10
  5. package/dist/browser-ext/dist/sidepanel.html +1 -1
  6. package/dist/browser-ext/dist/sidepanel.js +22 -3
  7. package/dist/browser-ext/manifest.json +1 -1
  8. package/dist/extensions/telegram/xopc.extension.json +1 -1
  9. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CJFwitU2.js → agent-avatar-dicebear-CufU7cqe.js} +1 -1
  10. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DHDAcss8.js +1 -0
  11. package/dist/gateway/static/root/assets/agents-BV3dAoa0.js +77 -0
  12. package/dist/gateway/static/root/assets/{app-management-settings-panel-DgwqNi5i.js → app-management-settings-panel-D4-n1uBg.js} +1 -1
  13. package/dist/gateway/static/root/assets/{appearance-settings-C0wJfFyz.js → appearance-settings-Cgn4eNsX.js} +1 -1
  14. package/dist/gateway/static/root/assets/apps-page-BOYzD7Zz.js +1 -0
  15. package/dist/gateway/static/root/assets/{archive-plugin-Ch7wolHE.js → archive-plugin-B4btCsiV.js} +1 -1
  16. package/dist/gateway/static/root/assets/{automations-page-DK7AU79v.js → automations-page-yb1EL_3N.js} +1 -1
  17. package/dist/gateway/static/root/assets/automations-workspace-J7LJ4RuV.js +8 -0
  18. package/dist/gateway/static/root/assets/{binary-plugins-B4HcHYsC.js → binary-plugins-DQT9e258.js} +1 -1
  19. package/dist/gateway/static/root/assets/{block-editor-BQAWEZF-.js → block-editor-B50rU9tL.js} +4 -4
  20. package/dist/gateway/static/root/assets/browser-automations-page-BiQz6lJA.js +3 -0
  21. package/dist/gateway/static/root/assets/browser-settings-page-W9kuxBbE.js +3 -0
  22. package/dist/gateway/static/root/assets/capabilities-page-iXe87MdG.js +2 -0
  23. package/dist/gateway/static/root/assets/capabilities-settings-panel-CRLSXe7Z.js +8 -0
  24. package/dist/gateway/static/root/assets/{capability-header-actions-D4Yq35fD.js → capability-header-actions-BMnbzHms.js} +1 -1
  25. package/dist/gateway/static/root/assets/channels-CrRJkOKX.js +1 -0
  26. package/dist/gateway/static/root/assets/{chat-terminal-dock-zdMySdMm.js → chat-terminal-dock-Bd3af4gL.js} +2 -2
  27. package/dist/gateway/static/root/assets/{computer-settings-page-C5Z6Jr0p.js → computer-settings-page-DXfqy7xh.js} +1 -1
  28. package/dist/gateway/static/root/assets/{connector-service-page-Cxus0qrZ.js → connector-service-page-B-cw0CPU.js} +1 -1
  29. package/dist/gateway/static/root/assets/connectors-page-DbFxc0Fd.js +1 -0
  30. package/dist/gateway/static/root/assets/{date-picker-DuyyD6jz.js → date-picker-CqzXevH6.js} +1 -1
  31. package/dist/gateway/static/root/assets/{dependency-picker-BZN72rrk.js → dependency-picker-pgT4Gyaf.js} +1 -1
  32. package/dist/gateway/static/root/assets/{desktop-pet-settings-BcyP7Z6v.js → desktop-pet-settings-Dy_-i2L8.js} +1 -1
  33. package/dist/gateway/static/root/assets/{desktop-pet-BtvTIDCi.js → desktop-pet-zICeqw9c.js} +1 -1
  34. package/dist/gateway/static/root/assets/device-pairing-wizard-ByGbxqkg.js +1 -0
  35. package/dist/gateway/static/root/assets/{directory-picker-path-field-DOusKz-O.js → directory-picker-path-field-C-aQ7qoM.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-debug-page-Bt4FwsFu.js → extension-debug-page-2NwpYkS5.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-page-0vsxX3FB.js → extension-page-y9J0z7_Q.js} +1 -1
  38. package/dist/gateway/static/root/assets/{extension-settings-page-5Pt5_ELc.js → extension-settings-page-BtowusUh.js} +1 -1
  39. package/dist/gateway/static/root/assets/gateway-realtime-C52ghnBK.js +1 -0
  40. package/dist/gateway/static/root/assets/gateway-settings-BYSKzUMR.js +1 -0
  41. package/dist/gateway/static/root/assets/highlight-code-Cz6eeDH5.js +1 -0
  42. package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-Cbk7eMlg.js → highlighted-body-KQOG7T2V-BiAa8HKC.js} +1 -1
  43. package/dist/gateway/static/root/assets/home-page-Cd1BjYHj.js +2 -0
  44. package/dist/gateway/static/root/assets/{image-generation-api-CAS8QVla.js → image-generation-api-gBFro48x.js} +1 -1
  45. package/dist/gateway/static/root/assets/{imports-page-BCLBYzV1.js → imports-page-BZQpXgjj.js} +1 -1
  46. package/dist/gateway/static/root/assets/index-Cy16gQST.css +1 -0
  47. package/dist/gateway/static/root/assets/index-DRyGCING.js +710 -0
  48. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CM_kt5XN.js → keyboard-shortcuts-settings-CYunR34k.js} +1 -1
  49. package/dist/gateway/static/root/assets/local-app-workbench-page-B_Ac1lly.js +1 -0
  50. package/dist/gateway/static/root/assets/{local-apps-page-BTSOChhk.js → local-apps-page-w-J5bY02.js} +1 -1
  51. package/dist/gateway/static/root/assets/logs-page-Dnjvvx2i.js +2 -0
  52. package/dist/gateway/static/root/assets/management-settings-w1EDwgLW.js +1 -0
  53. package/dist/gateway/static/root/assets/{media-plugins-DYQDjCVQ.js → media-plugins-CIx1koXw.js} +1 -1
  54. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-DISkHmEC.js +1 -0
  55. package/dist/gateway/static/root/assets/note-detail-panel-CNkxMTFU.js +5 -0
  56. package/dist/gateway/static/root/assets/note-image-lightbox-D5GH2wuC.js +1 -0
  57. package/dist/gateway/static/root/assets/notes-page-DZgKqFBG.js +8 -0
  58. package/dist/gateway/static/root/assets/{page-context-capture-button-DVn_dU83.js → page-context-capture-button-DusR2Itk.js} +1 -1
  59. package/dist/gateway/static/root/assets/personal-page-CR5JFkQ9.js +1 -0
  60. package/dist/gateway/static/root/assets/{personal-settings-panel-C16Iqe7X.js → personal-settings-panel-B7undvlr.js} +1 -1
  61. package/dist/gateway/static/root/assets/phone-model-picker-fWDltRUJ.js +1 -0
  62. package/dist/gateway/static/root/assets/phone-settings-page-CsOirAiy.js +1 -0
  63. package/dist/gateway/static/root/assets/plugin-details-dialog-BOBVIB3j.js +1 -0
  64. package/dist/gateway/static/root/assets/{preview-open-alternatives-RlHk76TI.js → preview-open-alternatives-CMxjsKSw.js} +1 -1
  65. package/dist/gateway/static/root/assets/{product-open-page-Dh-HqLrE.js → product-open-page-CrlyJx5M.js} +1 -1
  66. package/dist/gateway/static/root/assets/{product-preview-content-c3K-4DWc.js → product-preview-content-B-Jhzi7W.js} +1 -1
  67. package/dist/gateway/static/root/assets/project-detail-page-DoYUbtcI.js +7 -0
  68. package/dist/gateway/static/root/assets/{projects-page-B8HAt9sD.js → projects-page-xGQd9oAZ.js} +1 -1
  69. package/dist/gateway/static/root/assets/refresh-cw-BRlGaq2w.js +1 -0
  70. package/dist/gateway/static/root/assets/remote-access-hub-Gs0Xkws9.js +1 -0
  71. package/dist/gateway/static/root/assets/runtime-tools-settings-panel-Bwg7U7TR.js +1 -0
  72. package/dist/gateway/static/root/assets/{schema-form-CXO1B1k4.js → schema-form-ByzTNR_C.js} +1 -1
  73. package/dist/gateway/static/root/assets/{session-manager-CA1NkTR_.js → session-manager-B_h0jlLb.js} +1 -1
  74. package/dist/gateway/static/root/assets/sessions-page-XzK1ftbR.js +1 -0
  75. package/dist/gateway/static/root/assets/settings-advanced-gate-D_8Mvnog.js +1 -0
  76. package/dist/gateway/static/root/assets/{settings-form-section-BQxKmbbJ.js → settings-form-section-Brgd8IOV.js} +1 -1
  77. package/dist/gateway/static/root/assets/{settings-page-DvVMpKiA.js → settings-page-B6Mv02eg.js} +1 -1
  78. package/dist/gateway/static/root/assets/settings-page-header-BWyxJKOG.js +1 -0
  79. package/dist/gateway/static/root/assets/{settings-page-layout-B20AfmFG.js → settings-page-layout-DLHcS-0T.js} +1 -1
  80. package/dist/gateway/static/root/assets/setup-status-panel-DSCj5yis.js +1 -0
  81. package/dist/gateway/static/root/assets/{share-preview-page-XgZk8Oa1.js → share-preview-page-DF8Of0aZ.js} +1 -1
  82. package/dist/gateway/static/root/assets/shares-settings-DXNPe4Tl.js +1 -0
  83. package/dist/gateway/static/root/assets/{skill-api-CV-WT2hj.js → skill-api-CKCDCHLq.js} +1 -1
  84. package/dist/gateway/static/root/assets/skill-reload-api-BH0WF6Br.js +1 -0
  85. package/dist/gateway/static/root/assets/skills-page-DFWMVRW4.js +2 -0
  86. package/dist/gateway/static/root/assets/system-settings-panel-DB-ttubV.js +1 -0
  87. package/dist/gateway/static/root/assets/task-detail-page-BJY7c8wb.js +2 -0
  88. package/dist/gateway/static/root/assets/{task-result-evidence-D-gGKNX9.js → task-result-evidence-B-vt1BgK.js} +1 -1
  89. package/dist/gateway/static/root/assets/{tracing-settings-page-CQw3G-FB.js → tracing-settings-page-Dt51Z_ws.js} +1 -1
  90. package/dist/gateway/static/root/assets/{tunnel-api-B2HnCi0U.js → tunnel-api-Dok3t7Qe.js} +1 -1
  91. package/dist/gateway/static/root/assets/usage-page-lJym5G7F.js +1 -0
  92. package/dist/gateway/static/root/assets/{use-autosave-Dc0nwUpX.js → use-autosave-D4EMIB-V.js} +1 -1
  93. package/dist/gateway/static/root/assets/user-model-page-BXdNkITp.js +2 -0
  94. package/dist/gateway/static/root/assets/{voice-api-key-field-BSWXZkGa.js → voice-api-key-field-CO3KuanA.js} +1 -1
  95. package/dist/gateway/static/root/assets/{work-discovery-overlay-H3cdwq3l.js → work-discovery-overlay-BJRtgQM3.js} +1 -1
  96. package/dist/gateway/static/root/assets/wrench-DFb4r8bH.js +1 -0
  97. package/dist/gateway/static/root/assets/xopc-cloud-account-card-CS-_GxHV.js +1 -0
  98. package/dist/gateway/static/root/browser-setup-test.html +5 -0
  99. package/dist/gateway/static/root/browser-setup-test.js +7 -0
  100. package/dist/gateway/static/root/index.html +7 -4
  101. package/dist/package.js +1 -1
  102. package/dist/packages/gateway-contract/src/browser-setup.js +6 -0
  103. package/dist/packages/gateway-contract/src/index.js +4 -0
  104. package/dist/packages/gateway-contract/src/phone-setup.js +6 -0
  105. package/dist/packages/realtime-client/src/index.js +1 -0
  106. package/dist/packages/realtime-protocol/src/index.js +1 -0
  107. package/dist/src/agent/agent-manager.d.ts +4 -0
  108. package/dist/src/agent/agent-manager.js +12 -1
  109. package/dist/src/agent/embedded/tool-search-extension.js +14 -1
  110. package/dist/src/agent/service.d.ts +1 -0
  111. package/dist/src/agent/service.js +5 -0
  112. package/dist/src/agent/service.types.d.ts +2 -0
  113. package/dist/src/agent/tool-manuals/phone.d.ts +1 -1
  114. package/dist/src/agent/tool-manuals/phone.js +1 -1
  115. package/dist/src/agent/tool-manuals/xopc-configuration.d.ts +2 -0
  116. package/dist/src/agent/tool-manuals/xopc-configuration.js +25 -0
  117. package/dist/src/agent/tools/browser/tool/browser-use-tool.d.ts +2 -0
  118. package/dist/src/agent/tools/browser/tool/browser-use-tool.js +6 -2
  119. package/dist/src/agent/tools/factory.d.ts +3 -0
  120. package/dist/src/agent/tools/factory.js +11 -1
  121. package/dist/src/agent/tools/phone-use-tool.d.ts +1 -0
  122. package/dist/src/agent/tools/phone-use-tool.js +9 -1
  123. package/dist/src/agent/tools/tool-manual-tool.d.ts +1 -1
  124. package/dist/src/agent/tools/tool-manual-tool.js +3 -1
  125. package/dist/src/agent/tools/toolExposure.js +3 -0
  126. package/dist/src/agent/tools/xopc-use-tool.d.ts +4 -2
  127. package/dist/src/agent/tools/xopc-use-tool.js +40 -2
  128. package/dist/src/browser/drivers/browser-driver.d.ts +2 -2
  129. package/dist/src/browser/drivers/extension-driver.d.ts +2 -2
  130. package/dist/src/browser/drivers/extension-driver.js +6 -3
  131. package/dist/src/browser/providers/browser-ext-install.js +2 -1
  132. package/dist/src/browser/runtime/browser-runtime.d.ts +4 -1
  133. package/dist/src/browser/runtime/browser-runtime.js +29 -17
  134. package/dist/src/browser/setup/repository.d.ts +5 -0
  135. package/dist/src/browser/setup/repository.js +42 -0
  136. package/dist/src/browser/setup/service.d.ts +152 -0
  137. package/dist/src/browser/setup/service.js +404 -0
  138. package/dist/src/browser/setup/types.d.ts +38 -0
  139. package/dist/src/browser/setup/types.js +1 -0
  140. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  141. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  142. package/dist/src/config/schema.d.ts +2 -0
  143. package/dist/src/connectors/connected-source-understanding.js +1 -1
  144. package/dist/src/connectors/learning-coordinator.js +1 -1
  145. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  146. package/dist/src/gateway/hono/routes/browser-setup.d.ts +3 -0
  147. package/dist/src/gateway/hono/routes/browser-setup.js +114 -0
  148. package/dist/src/gateway/hono/routes/browser.d.ts +2 -1
  149. package/dist/src/gateway/hono/routes/browser.js +1 -1
  150. package/dist/src/gateway/hono/routes/connectors.js +7 -7
  151. package/dist/src/gateway/hono/routes/home.js +1 -1
  152. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  153. package/dist/src/gateway/hono/routes/phone-dependencies.js +1 -1
  154. package/dist/src/gateway/hono/routes/phone-setup.d.ts +7 -0
  155. package/dist/src/gateway/hono/routes/phone-setup.js +162 -0
  156. package/dist/src/gateway/hono/routes/phone.js +3 -7
  157. package/dist/src/gateway/hono/routes/public-gateway.js +3 -1
  158. package/dist/src/gateway/provider-model-setup.js +1 -1
  159. package/dist/src/gateway/service/config-coordinator.d.ts +2 -0
  160. package/dist/src/gateway/service.d.ts +5 -0
  161. package/dist/src/gateway/service.js +57 -12
  162. package/dist/src/gateway/xopc-cloud-capability-setup.js +7 -1
  163. package/dist/src/home-intelligence/host.js +2 -2
  164. package/dist/src/infra/update-restart.js +1 -1
  165. package/dist/src/phone/adb-driver.d.ts +2 -0
  166. package/dist/src/phone/adb-driver.js +35 -17
  167. package/dist/src/phone/contracts.d.ts +40 -0
  168. package/dist/src/phone/contracts.js +17 -4
  169. package/dist/src/phone/model-adapter.d.ts +59 -2
  170. package/dist/src/phone/model-adapter.js +107 -13
  171. package/dist/src/phone/numeric-entry.d.ts +31 -0
  172. package/dist/src/phone/numeric-entry.js +126 -0
  173. package/dist/src/phone/planning.d.ts +27 -0
  174. package/dist/src/phone/planning.js +79 -0
  175. package/dist/src/phone/recovery.d.ts +37 -0
  176. package/dist/src/phone/recovery.js +81 -0
  177. package/dist/src/phone/repository.d.ts +1 -0
  178. package/dist/src/phone/repository.js +3 -0
  179. package/dist/src/phone/runtime.js +232 -54
  180. package/dist/src/phone/semantic-controls.d.ts +13 -0
  181. package/dist/src/phone/semantic-controls.js +76 -0
  182. package/dist/src/phone/setup/service.d.ts +148 -0
  183. package/dist/src/phone/setup/service.js +251 -0
  184. package/dist/src/providers/presentation.js +2 -2
  185. package/dist/src/providers/xopc-cloud-model-source.js +6 -2
  186. package/dist/src/realtime/runtime.js +1 -1
  187. package/dist/src/storage/sqlite/schema.d.ts +2 -2
  188. package/dist/src/storage/sqlite/schema.js +7 -7
  189. package/dist/src/storage/sqlite/schema.sql +10 -0
  190. package/dist/src/storage/sqlite/upgrades/260_browser_setup.sql +9 -0
  191. package/dist/src/user-context/sources/index.js +1 -1
  192. package/dist/src/work-discovery/service.js +2 -2
  193. package/package.json +6 -6
  194. package/skills/tools/configure-xopc/SKILL.md +47 -0
  195. package/skills/tools/configure-xopc/references/browser/acceptance.md +31 -0
  196. package/skills/tools/configure-xopc/references/browser/chrome-extension.md +19 -0
  197. package/skills/tools/configure-xopc/references/browser/overview.md +22 -0
  198. package/skills/tools/configure-xopc/references/browser/permissions.md +25 -0
  199. package/skills/tools/configure-xopc/references/browser/troubleshooting.md +24 -0
  200. package/skills/tools/configure-xopc/references/phone/acceptance.md +25 -0
  201. package/skills/tools/configure-xopc/references/phone/aliyun.md +29 -0
  202. package/skills/tools/configure-xopc/references/phone/dependencies.md +18 -0
  203. package/skills/tools/configure-xopc/references/phone/local.md +19 -0
  204. package/skills/tools/configure-xopc/references/phone/models.md +18 -0
  205. package/skills/tools/configure-xopc/references/phone/overview.md +30 -0
  206. package/skills/tools/configure-xopc/references/phone/troubleshooting.md +24 -0
  207. package/skills/tools/configure-xopc/references/scene-index.md +12 -0
  208. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DclyJr1N.js +0 -1
  209. package/dist/gateway/static/root/assets/agents-jhErNtzi.js +0 -77
  210. package/dist/gateway/static/root/assets/apps-page-C0LcnI4h.js +0 -1
  211. package/dist/gateway/static/root/assets/automations-workspace-CBxiV34x.js +0 -8
  212. package/dist/gateway/static/root/assets/browser-automations-page-vJIrTxdi.js +0 -3
  213. package/dist/gateway/static/root/assets/browser-settings-page-in5Roh_E.js +0 -3
  214. package/dist/gateway/static/root/assets/capabilities-page-CiGx8wpQ.js +0 -2
  215. package/dist/gateway/static/root/assets/capabilities-settings-panel-a8kqjzQu.js +0 -8
  216. package/dist/gateway/static/root/assets/channels-C_CxcjKT.js +0 -1
  217. package/dist/gateway/static/root/assets/connectors-page-DCBO2CxK.js +0 -1
  218. package/dist/gateway/static/root/assets/device-pairing-wizard-Ctbgr50F.js +0 -1
  219. package/dist/gateway/static/root/assets/gateway-realtime-DkWCHVzI.js +0 -1
  220. package/dist/gateway/static/root/assets/gateway-settings-BsVOcmV5.js +0 -1
  221. package/dist/gateway/static/root/assets/highlight-code-C8eS9949.js +0 -1
  222. package/dist/gateway/static/root/assets/home-page-BZDZnmyc.js +0 -2
  223. package/dist/gateway/static/root/assets/index-Bys6njTY.js +0 -710
  224. package/dist/gateway/static/root/assets/index-Vsf0bj5e.css +0 -1
  225. package/dist/gateway/static/root/assets/local-app-workbench-page-Dk87jtcc.js +0 -1
  226. package/dist/gateway/static/root/assets/logs-page-D3gVdm8S.js +0 -2
  227. package/dist/gateway/static/root/assets/management-settings-BQNYFE4J.js +0 -1
  228. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-ChweJKTv.js +0 -1
  229. package/dist/gateway/static/root/assets/note-detail-panel-CYKwUCte.js +0 -5
  230. package/dist/gateway/static/root/assets/note-image-lightbox-BTSZb9ZT.js +0 -1
  231. package/dist/gateway/static/root/assets/notes-page-CJ5nhWfL.js +0 -8
  232. package/dist/gateway/static/root/assets/personal-page-DcW14lqn.js +0 -1
  233. package/dist/gateway/static/root/assets/phone-dependencies-card-D3l9uPFM.js +0 -1
  234. package/dist/gateway/static/root/assets/phone-model-picker-DOv38vBn.js +0 -1
  235. package/dist/gateway/static/root/assets/phone-settings-page-DUHp1q_9.js +0 -1
  236. package/dist/gateway/static/root/assets/plugin-details-dialog-BpfgDlVa.js +0 -1
  237. package/dist/gateway/static/root/assets/project-detail-page-GD8TxKad.js +0 -7
  238. package/dist/gateway/static/root/assets/remote-access-hub-D3el7lHK.js +0 -1
  239. package/dist/gateway/static/root/assets/runtime-tools-settings-panel-Dbskk6RG.js +0 -1
  240. package/dist/gateway/static/root/assets/sessions-page-07liaSHH.js +0 -1
  241. package/dist/gateway/static/root/assets/settings-advanced-gate-BGjU8rfN.js +0 -1
  242. package/dist/gateway/static/root/assets/settings-page-header-CYpuTXUW.js +0 -1
  243. package/dist/gateway/static/root/assets/setup-status-panel-DKo_VTx-.js +0 -1
  244. package/dist/gateway/static/root/assets/shares-settings-BODFqK8k.js +0 -1
  245. package/dist/gateway/static/root/assets/skill-reload-api-Ce6OUk2C.js +0 -1
  246. package/dist/gateway/static/root/assets/skills-page-B7ehOIML.js +0 -2
  247. package/dist/gateway/static/root/assets/system-settings-panel-CxJkZRT4.js +0 -1
  248. package/dist/gateway/static/root/assets/task-detail-page-CxRR0QuW.js +0 -2
  249. package/dist/gateway/static/root/assets/usage-page-uUEkO38r.js +0 -1
  250. package/dist/gateway/static/root/assets/user-model-page-ICUutbKL.js +0 -2
  251. package/dist/gateway/static/root/assets/xopc-cloud-account-card-CvT_W6YS.js +0 -1
@@ -8,10 +8,15 @@ import { PhoneInteractiveSession } from "./interactive-session.js";
8
8
  import { AdbPhoneDriver, resolveAdbPath } from "./adb-driver.js";
9
9
  import { LocalScrcpyProvider } from "./scrcpy.js";
10
10
  import { CloudPhones } from "./cloud-phones.js";
11
+ import { advancePhonePlan, buildPhonePlan, createPhonePlanState } from "./planning.js";
12
+ import { canDispatchPhoneAction, observationSignature, phoneCompletionSatisfied } from "./reliability.js";
13
+ import { groundPhoneCoordinateAction, resolvePhoneSemanticAction } from "./semantic-controls.js";
11
14
  import { createPhoneModel, isAboutScreen } from "./model-adapter.js";
12
15
  import { PhoneRepository } from "./repository.js";
13
- import { canDispatchPhoneAction, observationSignature, phoneCompletionSatisfied } from "./reliability.js";
16
+ import { confirmPhoneFocusRecovery, phoneBackRecovery, phoneUnfocusedTypingHint, preparePhoneFocusRecovery } from "./recovery.js";
17
+ import { createPhoneNumericEntry, nextPhoneNumericEntry } from "./numeric-entry.js";
14
18
  import { createHash, randomUUID } from "node:crypto";
19
+ import { setTimeout } from "node:timers/promises";
15
20
  //#region src/phone/runtime.ts
16
21
  init_logger();
17
22
  init_contracts();
@@ -226,7 +231,7 @@ var PhoneRuntime = class {
226
231
  await this.dispatch(session, {
227
232
  type: "launch",
228
233
  package: input.appPackage
229
- }, observation, context, signal, epoch);
234
+ }, observation, context, signal, epoch, false, input.settingsPage);
230
235
  } finally {
231
236
  observation.frame.fill(0);
232
237
  }
@@ -315,7 +320,29 @@ var PhoneRuntime = class {
315
320
  let repairs = 0;
316
321
  let completionCorrections = 0;
317
322
  let recoveryHint;
323
+ const enhancements = {
324
+ semanticControls: context.config.phone.semanticControls,
325
+ hierarchicalPlanning: context.config.phone.hierarchicalPlanning
326
+ };
327
+ const derivedPlan = enhancements.hierarchicalPlanning ? buildPhonePlan(input.goal, completion) : void 0;
328
+ let planning = derivedPlan ? createPhonePlanState(derivedPlan) : void 0;
329
+ if (enhancements.hierarchicalPlanning) {
330
+ session.report.diagnostics.plannedSubgoals = planning?.steps.length ?? 0;
331
+ session.report.diagnostics.completedSubgoals = 0;
332
+ }
318
333
  let changed = false;
334
+ const entryObservation = {
335
+ ...observation,
336
+ frame: Buffer.alloc(0),
337
+ elements: structuredClone(observation.elements)
338
+ };
339
+ const trajectoryStart = session.trajectoryCount;
340
+ let pendingFocus;
341
+ let numericIntent;
342
+ let numericPending;
343
+ const focusedIntents = /* @__PURE__ */ new Set();
344
+ let backReplans = 0;
345
+ let focusReplans = 0;
319
346
  let previousSignature = observationSignature(observation);
320
347
  let controlEpoch = session.report.controlEpoch;
321
348
  const attempts = /* @__PURE__ */ new Map();
@@ -331,12 +358,51 @@ var PhoneRuntime = class {
331
358
  if (controlEpoch !== epoch) {
332
359
  attempts.clear();
333
360
  recoveryHint = "The user handed back control. Re-evaluate the fresh screen before continuing.";
361
+ pendingFocus = void 0;
362
+ numericIntent = void 0;
363
+ numericPending = void 0;
334
364
  controlEpoch = epoch;
335
365
  }
336
366
  const signature = observationSignature(observation);
337
367
  if (signature !== previousSignature) changed = true;
338
368
  previousSignature = signature;
339
369
  this.recordObservation(session, observation);
370
+ if (planning) {
371
+ planning = advancePhonePlan(planning, observation);
372
+ session.report.diagnostics.completedSubgoals = planning.verifiedSteps;
373
+ }
374
+ let numericStep;
375
+ if (enhancements.hierarchicalPlanning) {
376
+ numericIntent ??= createPhoneNumericEntry(input.goal, completion, observation);
377
+ if (numericIntent) {
378
+ let next = nextPhoneNumericEntry(numericIntent, observation, numericPending);
379
+ for (let poll = 0; numericPending && next.kind === "blocked" && poll < 2; poll++) {
380
+ await setTimeout(150, void 0, { signal });
381
+ observation.frame.fill(0);
382
+ observation = await this.observe(session, signal);
383
+ this.guard(session, context);
384
+ this.allowedScreen(session, observation);
385
+ if (epoch !== session.report.controlEpoch || session.report.controlOwner !== "agent") break;
386
+ next = nextPhoneNumericEntry(numericIntent, observation, numericPending);
387
+ }
388
+ if (epoch !== session.report.controlEpoch || session.report.controlOwner !== "agent") continue;
389
+ this.recordObservation(session, observation);
390
+ if (next.kind === "blocked") {
391
+ session.report.diagnostics.numericMismatches = (session.report.diagnostics.numericMismatches ?? 0) + 1;
392
+ session.report.status = "paused";
393
+ session.report.controlOwner = "none";
394
+ session.report.controlEpoch++;
395
+ session.report.stopReason = "numeric_state_mismatch";
396
+ session.report.message = "Numeric input was not confirmed. Inspect the displayed value before continuing; no key was retried.";
397
+ session.budget?.pause();
398
+ await session.stream?.revoke();
399
+ return this.publish(session);
400
+ }
401
+ if (numericPending) session.report.diagnostics.numericConfirmedSteps = (session.report.diagnostics.numericConfirmedSteps ?? 0) + 1;
402
+ numericPending = void 0;
403
+ if (next.kind === "action") numericStep = next.step;
404
+ }
405
+ }
340
406
  if (session.report.mock ? isAboutScreen(observation) : Boolean(completion && phoneCompletionSatisfied(completion, observation, changed))) {
341
407
  const confirmation = await this.observe(session, signal);
342
408
  try {
@@ -350,6 +416,10 @@ var PhoneRuntime = class {
350
416
  continue;
351
417
  }
352
418
  this.recordObservation(session, confirmation);
419
+ if (planning) {
420
+ planning = advancePhonePlan(planning, confirmation);
421
+ session.report.diagnostics.completedSubgoals = planning.verifiedSteps;
422
+ }
353
423
  session.report.verified = true;
354
424
  session.report.verificationLevel = "ui";
355
425
  session.report.status = "completed";
@@ -364,52 +434,72 @@ var PhoneRuntime = class {
364
434
  session.report.stopReason = "action_limit";
365
435
  return this.publish(session);
366
436
  }
367
- if (session.report.modelRequests >= context.config.phone.maxModelRequests) throw new PhoneError("PHONE_MODEL_LIMIT", "Phone model request limit reached");
368
- session.report.modelRequests++;
369
- session.report.currentAction = void 0;
370
- this.publish(session);
371
- session.prediction = new AbortController();
372
- let decision;
373
- try {
374
- decision = await session.adapter.predict({
375
- goal: input.goal,
376
- observation,
377
- completion,
378
- allowedApps: session.device.allowedApps,
379
- appAccess: phoneAppAccess(session.device),
380
- applications: session.applications.filter((app) => phoneAllowsApp(session.device, app.packageName)),
381
- history: session.report.steps.map((step) => step.action),
382
- trajectory: session.trajectory,
383
- progressSummary: JSON.stringify({
384
- dispatched: session.trajectoryCount,
385
- earlier: [...session.trajectorySummary, ...session.trajectory.slice(0, -8).map((entry) => JSON.stringify({
386
- action: entry.action,
387
- effect: entry.effect,
388
- afterApp: entry.afterApp
389
- }).slice(0, 250))].slice(-12)
390
- }).slice(-1500),
391
- recoveryHint,
392
- protocolFeedback,
393
- scenario: input.scenario
394
- }, AbortSignal.any([signal, session.prediction.signal]));
395
- } catch (error) {
396
- if (!signal.aborted && epoch !== session.report.controlEpoch) {
397
- await this.waitForAgent(session, signal);
398
- observation.frame.fill(0);
399
- observation = await this.observe(session, signal);
400
- continue;
401
- }
402
- if (!signal.aborted && error instanceof PhoneError && ["PHONE_MODEL_PROTOCOL", "PHONE_MODEL_TRUNCATED"].includes(error.code) && repairs < 1 && session.report.modelRequests < context.config.phone.maxModelRequests) {
403
- repairs++;
404
- session.report.diagnostics.protocolRepairs++;
405
- protocolFeedback = "Your previous output was invalid or truncated. Return exactly one complete action in the specified format with no extra prose.";
406
- observation.frame.fill(0);
407
- observation = await this.observe(session, signal);
408
- continue;
437
+ let decision = numericStep ? {
438
+ kind: "action",
439
+ action: numericStep.action
440
+ } : void 0;
441
+ let resumedText = false;
442
+ if (!decision && pendingFocus) {
443
+ const pending = pendingFocus;
444
+ pendingFocus = void 0;
445
+ const intended = confirmPhoneFocusRecovery(pending, observation);
446
+ if (intended) {
447
+ decision = {
448
+ kind: "action",
449
+ action: intended
450
+ };
451
+ resumedText = true;
452
+ } else recoveryHint = "The focus transition was not confirmed. No text was sent. Reinspect the intended editable field before another action.";
453
+ }
454
+ if (!decision) {
455
+ if (session.report.modelRequests >= context.config.phone.maxModelRequests) throw new PhoneError("PHONE_MODEL_LIMIT", "Phone model request limit reached");
456
+ session.report.modelRequests++;
457
+ session.report.currentAction = void 0;
458
+ this.publish(session);
459
+ session.prediction = new AbortController();
460
+ try {
461
+ decision = await session.adapter.predict({
462
+ goal: input.goal,
463
+ observation,
464
+ completion,
465
+ allowedApps: session.device.allowedApps,
466
+ appAccess: phoneAppAccess(session.device),
467
+ applications: session.applications.filter((app) => phoneAllowsApp(session.device, app.packageName)),
468
+ history: session.report.steps.map((step) => step.action),
469
+ trajectory: session.trajectory,
470
+ progressSummary: JSON.stringify({
471
+ dispatched: session.trajectoryCount,
472
+ earlier: [...session.trajectorySummary, ...session.trajectory.slice(0, -8).map((entry) => JSON.stringify({
473
+ action: entry.action,
474
+ effect: entry.effect,
475
+ afterApp: entry.afterApp
476
+ }).slice(0, 250))].slice(-12)
477
+ }).slice(-1500),
478
+ recoveryHint,
479
+ protocolFeedback,
480
+ scenario: input.scenario,
481
+ enhancements,
482
+ planning
483
+ }, AbortSignal.any([signal, session.prediction.signal]));
484
+ } catch (error) {
485
+ if (!signal.aborted && epoch !== session.report.controlEpoch) {
486
+ await this.waitForAgent(session, signal);
487
+ observation.frame.fill(0);
488
+ observation = await this.observe(session, signal);
489
+ continue;
490
+ }
491
+ if (!signal.aborted && error instanceof PhoneError && ["PHONE_MODEL_PROTOCOL", "PHONE_MODEL_TRUNCATED"].includes(error.code) && repairs < 1 && session.report.modelRequests < context.config.phone.maxModelRequests) {
492
+ repairs++;
493
+ session.report.diagnostics.protocolRepairs++;
494
+ protocolFeedback = "Your previous output was invalid or truncated. Return exactly one complete action in the specified format with no extra prose.";
495
+ observation.frame.fill(0);
496
+ observation = await this.observe(session, signal);
497
+ continue;
498
+ }
499
+ throw error;
500
+ } finally {
501
+ session.prediction = void 0;
409
502
  }
410
- throw error;
411
- } finally {
412
- session.prediction = void 0;
413
503
  }
414
504
  signal.throwIfAborted();
415
505
  this.guard(session, context);
@@ -420,6 +510,11 @@ var PhoneRuntime = class {
420
510
  continue;
421
511
  }
422
512
  protocolFeedback = void 0;
513
+ if (enhancements.hierarchicalPlanning && !planning && decision.plan) {
514
+ planning = advancePhonePlan(createPhonePlanState(decision.plan), observation);
515
+ session.report.diagnostics.plannedSubgoals = planning.steps.length;
516
+ session.report.diagnostics.completedSubgoals = planning.verifiedSteps;
517
+ }
423
518
  if (decision.kind !== "action") {
424
519
  if (decision.kind === "complete" && !session.report.mock && (input.completion || input.expectText) && completionCorrections < 1 && session.report.modelRequests < context.config.phone.maxModelRequests) {
425
520
  completionCorrections++;
@@ -447,10 +542,77 @@ var PhoneRuntime = class {
447
542
  }
448
543
  return this.publish(session);
449
544
  }
545
+ let preparedFocus;
546
+ if (enhancements.hierarchicalPlanning) {
547
+ const operationActions = session.trajectoryCount - trajectoryStart;
548
+ const back = phoneBackRecovery({
549
+ action: decision.action,
550
+ observation,
551
+ entryObservation,
552
+ completion,
553
+ trajectory: operationActions ? session.trajectory.slice(-operationActions) : []
554
+ });
555
+ if (back) {
556
+ session.report.diagnostics.backReplans = (session.report.diagnostics.backReplans ?? 0) + 1;
557
+ if (backReplans++ >= 2) {
558
+ session.report.status = "paused";
559
+ session.report.controlOwner = "none";
560
+ session.report.controlEpoch++;
561
+ session.report.stopReason = "back_boundary";
562
+ session.report.message = back.hint;
563
+ session.budget?.pause();
564
+ await session.stream?.revoke();
565
+ return this.publish(session);
566
+ }
567
+ recoveryHint = back.hint;
568
+ observation.frame.fill(0);
569
+ observation = await this.observe(session, signal);
570
+ continue;
571
+ }
572
+ const focusHint = phoneUnfocusedTypingHint(decision.action, observation, completion);
573
+ if (focusHint) {
574
+ const key = JSON.stringify(decision.action);
575
+ preparedFocus = !focusedIntents.has(key) ? preparePhoneFocusRecovery(decision.action, observation, completion) : void 0;
576
+ if (preparedFocus) decision = {
577
+ kind: "action",
578
+ action: preparedFocus.focusAction
579
+ };
580
+ else {
581
+ session.report.diagnostics.focusReplans = (session.report.diagnostics.focusReplans ?? 0) + 1;
582
+ if (focusReplans++ >= 2) throw new PhoneError("PHONE_INPUT_NOT_FOCUSED", "The requested editable field could not be focused; inspect before typing", 409);
583
+ recoveryHint = focusHint;
584
+ observation.frame.fill(0);
585
+ observation = await this.observe(session, signal);
586
+ continue;
587
+ }
588
+ }
589
+ }
450
590
  const fresh = await this.observe(session, signal);
451
591
  try {
452
592
  this.allowedScreen(session, fresh);
453
- if (!canDispatchPhoneAction(decision.action, observation, fresh)) {
593
+ if (decision.action.type !== "tapElement" && !canDispatchPhoneAction(decision.action, observation, fresh)) {
594
+ session.report.diagnostics.stalePredictions++;
595
+ recoveryHint = "The proposed action was not dispatched because its target changed. Locate it again on the fresh screen.";
596
+ observation.frame.fill(0);
597
+ observation = fresh;
598
+ continue;
599
+ }
600
+ let action = enhancements.semanticControls ? groundPhoneCoordinateAction(decision.action, observation) : decision.action;
601
+ const semanticAction = action.type === "tapElement";
602
+ if (action.type === "tapElement") {
603
+ if (!enhancements.semanticControls && !preparedFocus) throw new PhoneError("PHONE_SEMANTIC_DISABLED", "Semantic controls are disabled");
604
+ try {
605
+ action = resolvePhoneSemanticAction(action, observation, fresh);
606
+ } catch (error) {
607
+ if (!(error instanceof PhoneError) || !error.code.startsWith("PHONE_SEMANTIC_")) throw error;
608
+ session.report.diagnostics.semanticRejections = (session.report.diagnostics.semanticRejections ?? 0) + 1;
609
+ recoveryHint = "The semantic target was not dispatched: select one unique fully visible non-password control from the fresh native UI.";
610
+ observation.frame.fill(0);
611
+ observation = fresh;
612
+ continue;
613
+ }
614
+ }
615
+ if (!canDispatchPhoneAction(action, observation, fresh)) {
454
616
  session.report.diagnostics.stalePredictions++;
455
617
  recoveryHint = "The proposed action was not dispatched because its target changed. Locate it again on the fresh screen.";
456
618
  observation.frame.fill(0);
@@ -458,9 +620,9 @@ var PhoneRuntime = class {
458
620
  continue;
459
621
  }
460
622
  if (epoch !== session.report.controlEpoch || session.report.controlOwner !== "agent") continue;
461
- const attemptKey = `${observationSignature(fresh)}:${JSON.stringify(decision.action)}`;
623
+ const attemptKey = `${observationSignature(fresh)}:${JSON.stringify(action)}`;
462
624
  const repetitions = attempts.get(attemptKey) ?? 0;
463
- if (repetitions >= (decision.action.type === "wait" ? 3 : 2)) {
625
+ if (repetitions >= (action.type === "wait" ? 3 : 2)) {
464
626
  session.report.status = "paused";
465
627
  session.report.controlOwner = "none";
466
628
  session.report.controlEpoch++;
@@ -471,7 +633,20 @@ var PhoneRuntime = class {
471
633
  return this.publish(session);
472
634
  }
473
635
  attempts.set(attemptKey, repetitions + 1);
474
- if (await this.dispatch(session, decision.action, fresh, context, signal, epoch)) actions++;
636
+ if (await this.dispatch(session, action, fresh, context, signal, epoch)) {
637
+ actions++;
638
+ if (semanticAction && enhancements.semanticControls) session.report.diagnostics.semanticActions = (session.report.diagnostics.semanticActions ?? 0) + 1;
639
+ if (preparedFocus) {
640
+ pendingFocus = preparedFocus;
641
+ focusedIntents.add(JSON.stringify(preparedFocus.intendedAction));
642
+ session.report.diagnostics.focusRecoveries = (session.report.diagnostics.focusRecoveries ?? 0) + 1;
643
+ }
644
+ if (resumedText) session.report.diagnostics.resumedTextInputs = (session.report.diagnostics.resumedTextInputs ?? 0) + 1;
645
+ if (numericStep) {
646
+ numericPending = numericStep;
647
+ session.report.diagnostics.numericActions = (session.report.diagnostics.numericActions ?? 0) + 1;
648
+ }
649
+ }
475
650
  } finally {
476
651
  if (observation !== fresh) fresh.frame.fill(0);
477
652
  }
@@ -565,7 +740,7 @@ var PhoneRuntime = class {
565
740
  session.report.foregroundApp = observation.package;
566
741
  session.report.facts = [...new Set(observation.elements.flatMap((node) => node.password ? [] : [node.text, node.description]).filter(Boolean))].slice(0, 60);
567
742
  }
568
- async dispatch(session, action, observation, context, signal, epoch = session.report.controlEpoch, resetTask = false) {
743
+ async dispatch(session, action, observation, context, signal, epoch = session.report.controlEpoch, resetTask = false, settingsPage) {
569
744
  return this.io(session, async () => {
570
745
  signal.throwIfAborted();
571
746
  this.guard(session, context);
@@ -610,7 +785,10 @@ var PhoneRuntime = class {
610
785
  session.trajectorySummary = session.trajectorySummary.slice(-12);
611
786
  }
612
787
  try {
613
- await session.driver.execute(session.device.serial, action, observation, signal, session.human ? {
788
+ await session.driver.execute(session.device.serial, action, observation, signal, settingsPage ? {
789
+ settleMs: session.human ? 0 : 350,
790
+ settingsPage
791
+ } : session.human ? {
614
792
  settleMs: 0,
615
793
  ...resetTask ? { resetTask: true } : {}
616
794
  } : void 0);
@@ -1023,7 +1201,7 @@ var PhoneRuntime = class {
1023
1201
  const video = input.geometry !== void 0 && session.stream?.verifyGeometry(input.geometry);
1024
1202
  if (!video && (!frame || Date.now() - frame.capturedAt > 5e3)) throw new PhoneError("PHONE_FRAME_STALE", "Wait for a fresh phone image before interacting", 409);
1025
1203
  if (video && input.action?.type !== "typeText" && input.action?.type !== "back") throw new PhoneError("PHONE_INVALID_INPUT", "Use the continuous input channel for video gestures", 409);
1026
- if (!input.action || input.action.type === "launch" || input.action.type === "home" || input.action.type === "wait") throw new PhoneError("PHONE_INVALID_INPUT", "Unsupported manual input");
1204
+ if (!input.action || input.action.type === "launch" || input.action.type === "home" || input.action.type === "wait" || input.action.type === "tapElement") throw new PhoneError("PHONE_INVALID_INPUT", "Unsupported manual input");
1027
1205
  const observation = input.action.type === "typeText" ? await this.observe(session, session.controller.signal, true) : await this.capture(session, session.controller.signal);
1028
1206
  try {
1029
1207
  if (input.epoch !== session.report.controlEpoch || session.human !== client || Date.now() > (session.humanExpiresAt ?? 0)) throw new PhoneError("PHONE_CONTROL_CHANGED", "Manual control expired while observing the phone", 409);
@@ -0,0 +1,13 @@
1
+ import { type PhoneAction, type PhoneElementSelector, type PhoneObservation } from './contracts.js';
2
+ export type PhonePrimitiveAction = Exclude<PhoneAction, {
3
+ type: 'tapElement';
4
+ }>;
5
+ /** Offer exact identities only when one fully visible native node matches. */
6
+ export declare function phoneSemanticControls(screen: PhoneObservation): Array<{
7
+ frameId: string;
8
+ selector: PhoneElementSelector;
9
+ }>;
10
+ /** Ground ordinary model taps only when native hit-testing identifies one bounded control. */
11
+ export declare function groundPhoneCoordinateAction(action: PhoneAction, screen: PhoneObservation): PhoneAction;
12
+ /** Resolve against the prediction snapshot and a fresh snapshot before any dispatch. */
13
+ export declare function resolvePhoneSemanticAction(action: PhoneAction, predicted: PhoneObservation, fresh?: PhoneObservation): PhonePrimitiveAction;
@@ -0,0 +1,76 @@
1
+ import { PhoneActionSchema, PhoneError, init_contracts } from "./contracts.js";
2
+ import { canDispatchPhoneAction } from "./reliability.js";
3
+ //#region src/phone/semantic-controls.ts
4
+ init_contracts();
5
+ function visible(node, screen) {
6
+ const [left, top, right, bottom] = node.bounds;
7
+ return !node.password && node.bounds.every(Number.isFinite) && left >= 0 && top >= 0 && right <= screen.width && bottom <= screen.height && right > left && bottom > top;
8
+ }
9
+ function matches(node, selector) {
10
+ return (selector.resourceId === void 0 || node.resourceId === selector.resourceId) && (selector.text === void 0 || node.text === selector.text) && (selector.description === void 0 || node.description === selector.description);
11
+ }
12
+ function candidates(screen, selector) {
13
+ return screen.elements.filter((node) => visible(node, screen) && matches(node, selector));
14
+ }
15
+ function exactSelector(node) {
16
+ const selector = {};
17
+ if (node.resourceId && node.resourceId.length <= 300) selector.resourceId = node.resourceId;
18
+ if (node.text && node.text.length <= 4e3) selector.text = node.text;
19
+ if (node.description && node.description.length <= 1e3) selector.description = node.description;
20
+ return selector;
21
+ }
22
+ /** Offer exact identities only when one fully visible native node matches. */
23
+ function phoneSemanticControls(screen) {
24
+ return screen.elements.filter((node) => visible(node, screen)).flatMap((node) => {
25
+ const selector = exactSelector(node);
26
+ return Object.keys(selector).length && candidates(screen, selector).length === 1 ? [{
27
+ frameId: screen.frameId,
28
+ selector
29
+ }] : [];
30
+ }).slice(0, 40);
31
+ }
32
+ function containsPoint(node, x, y) {
33
+ return x >= node.bounds[0] && y >= node.bounds[1] && x < node.bounds[2] && y < node.bounds[3];
34
+ }
35
+ function containsNode(outer, inner) {
36
+ return outer.bounds[0] <= inner.bounds[0] && outer.bounds[1] <= inner.bounds[1] && outer.bounds[2] >= inner.bounds[2] && outer.bounds[3] >= inner.bounds[3];
37
+ }
38
+ /** Ground ordinary model taps only when native hit-testing identifies one bounded control. */
39
+ function groundPhoneCoordinateAction(action, screen) {
40
+ if (action.type !== "tap") return action;
41
+ const [x, y] = [action.point[0] * screen.width / 1e3, action.point[1] * screen.height / 1e3];
42
+ if (screen.elements.some((node) => node.password && containsPoint(node, x, y))) return action;
43
+ const actionable = screen.elements.filter((node) => visible(node, screen) && (node.clickable || node.editable));
44
+ const hits = actionable.filter((node) => containsPoint(node, x, y));
45
+ const smallest = hits.filter((node) => hits.every((other) => other === node || containsNode(other, node)));
46
+ if (smallest.length !== 1) return action;
47
+ const target = smallest[0];
48
+ if ((target.bounds[2] - target.bounds[0]) * (target.bounds[3] - target.bounds[1]) > screen.width * screen.height / 4) return action;
49
+ const selector = exactSelector(target);
50
+ if (!Object.keys(selector).length || candidates(screen, selector).length !== 1) return action;
51
+ const [centerX, centerY] = [(target.bounds[0] + target.bounds[2]) / 2, (target.bounds[1] + target.bounds[3]) / 2];
52
+ if (screen.elements.some((node) => node.password && containsPoint(node, centerX, centerY)) || actionable.some((node) => node !== target && containsPoint(node, centerX, centerY) && !containsNode(node, target))) return action;
53
+ return {
54
+ type: "tapElement",
55
+ frameId: screen.frameId,
56
+ selector
57
+ };
58
+ }
59
+ /** Resolve against the prediction snapshot and a fresh snapshot before any dispatch. */
60
+ function resolvePhoneSemanticAction(action, predicted, fresh = predicted) {
61
+ if (action.type !== "tapElement") return action;
62
+ if (!PhoneActionSchema.safeParse(action).success) throw new PhoneError("PHONE_SEMANTIC_INVALID", "Invalid exact native element selector", 409);
63
+ if (action.frameId !== predicted.frameId) throw new PhoneError("PHONE_SEMANTIC_STALE", "Native element belongs to a different observation", 409);
64
+ const before = candidates(predicted, action.selector);
65
+ const after = candidates(fresh, action.selector);
66
+ if (before.length !== 1 || after.length !== 1) throw new PhoneError("PHONE_SEMANTIC_TARGET", "Native element selector must match exactly one fully visible non-password node", 409);
67
+ const target = after[0];
68
+ const resolved = {
69
+ type: "tap",
70
+ point: [500 * (target.bounds[0] + target.bounds[2]) / fresh.width, 500 * (target.bounds[1] + target.bounds[3]) / fresh.height]
71
+ };
72
+ if (JSON.stringify(before[0]) !== JSON.stringify(target) || !canDispatchPhoneAction(resolved, predicted, fresh)) throw new PhoneError("PHONE_SEMANTIC_STALE", "Native element or page changed before dispatch", 409);
73
+ return resolved;
74
+ }
75
+ //#endregion
76
+ export { groundPhoneCoordinateAction, phoneSemanticControls, resolvePhoneSemanticAction };
@@ -0,0 +1,148 @@
1
+ import type { PhoneSetupView } from '@xopcai/gateway-contract';
2
+ import type { PhoneInput } from '../contracts.js';
3
+ import type { PhoneRuntime } from '../runtime.js';
4
+ type Context = {
5
+ conversationId: string;
6
+ agentId: string;
7
+ };
8
+ type Setup = Omit<PhoneSetupView, 'readiness' | 'stale' | 'settingsPath' | 'guidanceRefs'> & {
9
+ environmentRevision?: string;
10
+ calls: number;
11
+ owner?: string;
12
+ };
13
+ export interface PhoneSetupDeps {
14
+ runtime: PhoneRuntime;
15
+ inspect(context: Context, source?: 'local' | 'aliyun'): Promise<PhoneSetupView['readiness']>;
16
+ revision(context: Context, deviceId: string): string;
17
+ connected(deviceId: string, signal?: AbortSignal): Promise<boolean>;
18
+ leaseActive(deviceId: string): boolean;
19
+ }
20
+ /** Setup only orchestrates existing settings; secrets and configuration writes stay in product forms. */
21
+ export declare class PhoneSetupService {
22
+ private readonly deps;
23
+ constructor(deps: PhoneSetupDeps);
24
+ private store;
25
+ current(context: Context): Setup;
26
+ private save;
27
+ private owned;
28
+ view(context: Context, setup: Setup): Promise<PhoneSetupView>;
29
+ command(command: string, args: Record<string, unknown>, context: Context, dryRun?: boolean): Promise<PhoneSetupView | {
30
+ dryRun?: boolean;
31
+ sceneId: string;
32
+ skillName: string;
33
+ readiness: {
34
+ enabled: boolean;
35
+ toolAllowed: boolean;
36
+ model: {
37
+ ref?: string;
38
+ chatRef?: string;
39
+ source?: "system" | "global" | "agent";
40
+ supported: boolean;
41
+ configured: boolean;
42
+ };
43
+ dependencies: Array<{
44
+ kind: "adb" | "scrcpy";
45
+ state: string;
46
+ version?: string;
47
+ canInstall: boolean;
48
+ }>;
49
+ devices: Array<{
50
+ id: string;
51
+ name: string;
52
+ source: "local" | "aliyun";
53
+ enabled: boolean;
54
+ settingsAllowed: boolean;
55
+ }>;
56
+ localDiscovery: Array<{
57
+ name: string;
58
+ state: string;
59
+ emulator: boolean;
60
+ }>;
61
+ diagnostic?: string;
62
+ };
63
+ setup: PhoneSetupView;
64
+ settingsPath: string;
65
+ guidanceRefs: {
66
+ sceneId: string;
67
+ path: string;
68
+ resourceVersion: string;
69
+ }[];
70
+ } | {
71
+ message: string;
72
+ id: string;
73
+ conversationId: string;
74
+ agentId: string;
75
+ revision: number;
76
+ state: "configuring" | "verifying" | "completed" | "needs_attention" | "cancelled";
77
+ source?: "local" | "aliyun";
78
+ error?: string;
79
+ stale: boolean;
80
+ settingsPath: string;
81
+ readiness: {
82
+ enabled: boolean;
83
+ toolAllowed: boolean;
84
+ model: {
85
+ ref?: string;
86
+ chatRef?: string;
87
+ source?: "system" | "global" | "agent";
88
+ supported: boolean;
89
+ configured: boolean;
90
+ };
91
+ dependencies: Array<{
92
+ kind: "adb" | "scrcpy";
93
+ state: string;
94
+ version?: string;
95
+ canInstall: boolean;
96
+ }>;
97
+ devices: Array<{
98
+ id: string;
99
+ name: string;
100
+ source: "local" | "aliyun";
101
+ enabled: boolean;
102
+ settingsAllowed: boolean;
103
+ }>;
104
+ localDiscovery: Array<{
105
+ name: string;
106
+ state: string;
107
+ emulator: boolean;
108
+ }>;
109
+ diagnostic?: string;
110
+ };
111
+ checks: {
112
+ configured: boolean;
113
+ read: boolean;
114
+ operated: boolean;
115
+ verified: boolean;
116
+ released: boolean;
117
+ };
118
+ attempt?: {
119
+ id: string;
120
+ deviceId: string;
121
+ modelRef: string;
122
+ sessionId?: string;
123
+ goal: string;
124
+ completion: {
125
+ appPackage: string;
126
+ textAny: string[];
127
+ requireChange: boolean;
128
+ };
129
+ maxActions: number;
130
+ expiresAt: number;
131
+ released: boolean;
132
+ };
133
+ guidanceRefs: Array<{
134
+ sceneId: "phone";
135
+ path: string;
136
+ resourceVersion: string;
137
+ }>;
138
+ }>;
139
+ source(context: Context, id: string, source: 'local' | 'aliyun', revision: number): Promise<PhoneSetupView>;
140
+ begin(context: Context, id: string, deviceId: string, revision: number, signal?: AbortSignal): Promise<PhoneSetupView>;
141
+ beforeInput(context: Context, input: PhoneInput): {
142
+ owner: string;
143
+ input: PhoneInput;
144
+ } | undefined;
145
+ afterInput(context: Context, owner: string, op: PhoneInput['op']): void;
146
+ release(context: Context, id: string): Promise<PhoneSetupView>;
147
+ }
148
+ export {};