@xopcai/xopc 0.0.414 → 0.0.415

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 (264) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-ClobpdW3.js → agent-avatar-dicebear-CJFwitU2.js} +1 -1
  4. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DclyJr1N.js +1 -0
  5. package/dist/gateway/static/root/assets/{agents-admin-api-CeBkHnl8.js → agents-admin-api-B1p-ghg9.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-jhErNtzi.js +77 -0
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-CLunLNGp.js → app-management-settings-panel-DgwqNi5i.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-DBEgoE9g.js → appearance-settings-C0wJfFyz.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-C0LcnI4h.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-JElK4Goq.js → archive-plugin-Ch7wolHE.js} +1 -1
  11. package/dist/gateway/static/root/assets/{automations-page-CZ0UqwyU.js → automations-page-DK7AU79v.js} +1 -1
  12. package/dist/gateway/static/root/assets/automations-workspace-CBxiV34x.js +8 -0
  13. package/dist/gateway/static/root/assets/{binary-plugins-CXFawow6.js → binary-plugins-B4HcHYsC.js} +1 -1
  14. package/dist/gateway/static/root/assets/{block-editor-BoduwSYI.js → block-editor-BQAWEZF-.js} +30 -30
  15. package/dist/gateway/static/root/assets/{browser-automation-inputs-D1pDgMJu.js → browser-automation-inputs-D6AccXaA.js} +1 -1
  16. package/dist/gateway/static/root/assets/browser-automations-page-vJIrTxdi.js +3 -0
  17. package/dist/gateway/static/root/assets/browser-settings-page-in5Roh_E.js +3 -0
  18. package/dist/gateway/static/root/assets/{capabilities-Bpeh6E22.js → capabilities-BlxF36t-.js} +1 -1
  19. package/dist/gateway/static/root/assets/{capabilities-page-C-EecWbS.js → capabilities-page-CiGx8wpQ.js} +2 -2
  20. package/dist/gateway/static/root/assets/capabilities-settings-panel-a8kqjzQu.js +8 -0
  21. package/dist/gateway/static/root/assets/{capability-header-actions-BasrAWA0.js → capability-header-actions-D4Yq35fD.js} +1 -1
  22. package/dist/gateway/static/root/assets/channels-C_CxcjKT.js +1 -0
  23. package/dist/gateway/static/root/assets/{chat-terminal-dock-CwQbzcZ_.js → chat-terminal-dock-zdMySdMm.js} +2 -2
  24. package/dist/gateway/static/root/assets/{computer-settings-page-DNWeJKfF.js → computer-settings-page-C5Z6Jr0p.js} +1 -1
  25. package/dist/gateway/static/root/assets/{connector-service-page-CxHgC5RM.js → connector-service-page-Cxus0qrZ.js} +1 -1
  26. package/dist/gateway/static/root/assets/connectors-page-DCBO2CxK.js +1 -0
  27. package/dist/gateway/static/root/assets/{date-picker-CXxLbLJb.js → date-picker-DuyyD6jz.js} +1 -1
  28. package/dist/gateway/static/root/assets/{dependency-picker-DVuWMrSj.js → dependency-picker-BZN72rrk.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-KbVLtAlO.js → desktop-pet-BtvTIDCi.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-settings-BDlg9g4s.js → desktop-pet-settings-BcyP7Z6v.js} +1 -1
  31. package/dist/gateway/static/root/assets/device-pairing-wizard-Ctbgr50F.js +1 -0
  32. package/dist/gateway/static/root/assets/{directory-picker-path-field-a7xEodzo.js → directory-picker-path-field-DOusKz-O.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-debug-page-CHelH5u_.js → extension-debug-page-Bt4FwsFu.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-CrVHmS9-.js → extension-page-0vsxX3FB.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-settings-page-CiMybi0r.js → extension-settings-page-5Pt5_ELc.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-swr-DF_HV6NP.js → gateway-config-swr-fQKBHBi1.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-realtime-DMAzNlm-.js → gateway-realtime-DkWCHVzI.js} +1 -1
  38. package/dist/gateway/static/root/assets/gateway-realtime-store-Bx9KCID-.js +1 -0
  39. package/dist/gateway/static/root/assets/gateway-settings-BsVOcmV5.js +1 -0
  40. package/dist/gateway/static/root/assets/{gateway-startup-retry-B91CBTzO.js → gateway-startup-retry-Cfwxof4k.js} +1 -1
  41. package/dist/gateway/static/root/assets/{highlight-code-AMjU04kR.js → highlight-code-C8eS9949.js} +1 -1
  42. package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-8ACAVktj.js → highlighted-body-KQOG7T2V-Cbk7eMlg.js} +1 -1
  43. package/dist/gateway/static/root/assets/home-page-BZDZnmyc.js +2 -0
  44. package/dist/gateway/static/root/assets/{image-generation-api-CVBaWmrV.js → image-generation-api-CAS8QVla.js} +1 -1
  45. package/dist/gateway/static/root/assets/{imports-page-C5IP3e6d.js → imports-page-BCLBYzV1.js} +1 -1
  46. package/dist/gateway/static/root/assets/index-Bys6njTY.js +710 -0
  47. package/dist/gateway/static/root/assets/index-Vsf0bj5e.css +1 -0
  48. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BhU6VYvd.js → keyboard-shortcuts-settings-CM_kt5XN.js} +1 -1
  49. package/dist/gateway/static/root/assets/local-app-workbench-page-Dk87jtcc.js +1 -0
  50. package/dist/gateway/static/root/assets/{local-apps-page-DcP1Bdlk.js → local-apps-page-BTSOChhk.js} +1 -1
  51. package/dist/gateway/static/root/assets/{local-session-drafts-DvmwiLUx.js → local-session-drafts-iGX_6O0O.js} +1 -1
  52. package/dist/gateway/static/root/assets/{locale-store-JogcXC4J.js → locale-store-B7s2Hb35.js} +1 -1
  53. package/dist/gateway/static/root/assets/{logs-page-CS3hcTOq.js → logs-page-D3gVdm8S.js} +2 -2
  54. package/dist/gateway/static/root/assets/management-settings-BQNYFE4J.js +1 -0
  55. package/dist/gateway/static/root/assets/{media-plugins-BnPpcJ2J.js → media-plugins-DYQDjCVQ.js} +1 -1
  56. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-ChweJKTv.js +1 -0
  57. package/dist/gateway/static/root/assets/{messages-RzwPiQNL.js → messages-BYq0cPbn.js} +2 -2
  58. package/dist/gateway/static/root/assets/note-detail-panel-CYKwUCte.js +5 -0
  59. package/dist/gateway/static/root/assets/note-image-lightbox-BTSZb9ZT.js +1 -0
  60. package/dist/gateway/static/root/assets/notes-page-CJ5nhWfL.js +8 -0
  61. package/dist/gateway/static/root/assets/page-context-capture-button-DVn_dU83.js +1 -0
  62. package/dist/gateway/static/root/assets/personal-page-DcW14lqn.js +1 -0
  63. package/dist/gateway/static/root/assets/personal-settings-panel-C16Iqe7X.js +1 -0
  64. package/dist/gateway/static/root/assets/phone-dependencies-card-D3l9uPFM.js +1 -0
  65. package/dist/gateway/static/root/assets/{phone-model-picker-CZoIAQ1j.js → phone-model-picker-DOv38vBn.js} +1 -1
  66. package/dist/gateway/static/root/assets/phone-settings-page-DUHp1q_9.js +1 -0
  67. package/dist/gateway/static/root/assets/plugin-details-dialog-BpfgDlVa.js +1 -0
  68. package/dist/gateway/static/root/assets/{preview-open-alternatives-FGqN-mQx.js → preview-open-alternatives-RlHk76TI.js} +1 -1
  69. package/dist/gateway/static/root/assets/{product-open-page-CwnHLfdL.js → product-open-page-Dh-HqLrE.js} +1 -1
  70. package/dist/gateway/static/root/assets/{product-preview-content-BD6k5hF9.js → product-preview-content-c3K-4DWc.js} +1 -1
  71. package/dist/gateway/static/root/assets/project-detail-page-GD8TxKad.js +7 -0
  72. package/dist/gateway/static/root/assets/projects-page-B8HAt9sD.js +1 -0
  73. package/dist/gateway/static/root/assets/{registry-api-BGqSwITi.js → registry-api-BcfoLEna.js} +1 -1
  74. package/dist/gateway/static/root/assets/remote-access-hub-D3el7lHK.js +1 -0
  75. package/dist/gateway/static/root/assets/{runtime-tools-api-CSm5SpnR.js → runtime-tools-api-DesqGCnE.js} +1 -1
  76. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-q_oswAdV.js → runtime-tools-settings-panel-Dbskk6RG.js} +1 -1
  77. package/dist/gateway/static/root/assets/{schema-form-DoVn5jJK.js → schema-form-CXO1B1k4.js} +1 -1
  78. package/dist/gateway/static/root/assets/{session-api-DTxZyMta.js → session-api-DdQnMwkl.js} +1 -1
  79. package/dist/gateway/static/root/assets/{session-manager-B2HDomj4.js → session-manager-CA1NkTR_.js} +1 -1
  80. package/dist/gateway/static/root/assets/sessions-page-07liaSHH.js +1 -0
  81. package/dist/gateway/static/root/assets/settings-advanced-gate-BGjU8rfN.js +1 -0
  82. package/dist/gateway/static/root/assets/{settings-form-section-Ce8vVv-9.js → settings-form-section-BQxKmbbJ.js} +1 -1
  83. package/dist/gateway/static/root/assets/{settings-page-DP2w3ejC.js → settings-page-DvVMpKiA.js} +1 -1
  84. package/dist/gateway/static/root/assets/{setup-status-panel-ZmlE8jIE.js → setup-status-panel-DKo_VTx-.js} +1 -1
  85. package/dist/gateway/static/root/assets/{share-preview-page-DV4dKrmG.js → share-preview-page-XgZk8Oa1.js} +1 -1
  86. package/dist/gateway/static/root/assets/shares-settings-BODFqK8k.js +1 -0
  87. package/dist/gateway/static/root/assets/{skill-api-DTdejInH.js → skill-api-CV-WT2hj.js} +1 -1
  88. package/dist/gateway/static/root/assets/skill-reload-api-Ce6OUk2C.js +1 -0
  89. package/dist/gateway/static/root/assets/skills-page-B7ehOIML.js +2 -0
  90. package/dist/gateway/static/root/assets/{system-settings-panel-Baxkhov7.js → system-settings-panel-CxJkZRT4.js} +1 -1
  91. package/dist/gateway/static/root/assets/task-detail-page-CxRR0QuW.js +2 -0
  92. package/dist/gateway/static/root/assets/{task-result-evidence-DIGSC4qF.js → task-result-evidence-D-gGKNX9.js} +1 -1
  93. package/dist/gateway/static/root/assets/{tracing-settings-page-BaU8frtc.js → tracing-settings-page-CQw3G-FB.js} +1 -1
  94. package/dist/gateway/static/root/assets/{tunnel-api-BCcOUsqu.js → tunnel-api-B2HnCi0U.js} +1 -1
  95. package/dist/gateway/static/root/assets/{usage-page-BFeIbpbi.js → usage-page-uUEkO38r.js} +1 -1
  96. package/dist/gateway/static/root/assets/{use-autosave-C1X47WVb.js → use-autosave-Dc0nwUpX.js} +1 -1
  97. package/dist/gateway/static/root/assets/user-model-page-ICUutbKL.js +2 -0
  98. package/dist/gateway/static/root/assets/{voice-api-key-field-Bk4DQMTw.js → voice-api-key-field-BSWXZkGa.js} +1 -1
  99. package/dist/gateway/static/root/assets/{voice-config-api-D7xsWrdh.js → voice-config-api-BkUlN7GH.js} +1 -1
  100. package/dist/gateway/static/root/assets/{work-discovery-overlay-B7_NmTB5.js → work-discovery-overlay-H3cdwq3l.js} +1 -1
  101. package/dist/gateway/static/root/assets/{xopc-cloud-account-card-CJ_akAvc.js → xopc-cloud-account-card-CvT_W6YS.js} +1 -1
  102. package/dist/gateway/static/root/index.html +14 -14
  103. package/dist/package.js +1 -1
  104. package/dist/src/agent/coding/workspace-revision.js +1 -1
  105. package/dist/src/agent/commands/command-registry.js +1 -1
  106. package/dist/src/agent/memory/context-recovery.d.ts +1 -1
  107. package/dist/src/agent/skills/installer.js +3 -3
  108. package/dist/src/agent/tool-manuals/phone.d.ts +1 -1
  109. package/dist/src/agent/tool-manuals/phone.js +4 -2
  110. package/dist/src/agent/tools/personal-task-tool.d.ts +1 -1
  111. package/dist/src/agent/tools/personal-task-tool.js +23 -21
  112. package/dist/src/agent/tools/phone-use-tool.js +20 -0
  113. package/dist/src/backup/archive.js +2 -1
  114. package/dist/src/browser/providers/browser-ext-install.js +1 -1
  115. package/dist/src/cli/commands/extension-marketplace.js +3 -3
  116. package/dist/src/cli/commands/extension-pack.js +1 -1
  117. package/dist/src/cli/commands/extension.js +2 -2
  118. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  119. package/dist/src/connectors/connected-source-understanding.js +1 -1
  120. package/dist/src/connectors/learning-coordinator.js +1 -1
  121. package/dist/src/extensions/install.js +1 -1
  122. package/dist/src/extensions/update.js +1 -1
  123. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  124. package/dist/src/gateway/hono/routes/connectors.js +7 -7
  125. package/dist/src/gateway/hono/routes/home.js +1 -1
  126. package/dist/src/gateway/hono/routes/phone.js +8 -0
  127. package/dist/src/gateway/hono/routes/public-gateway.js +1 -1
  128. package/dist/src/gateway/service/config-coordinator.js +1 -1
  129. package/dist/src/gateway/service/session-input-coordinator.js +1 -1
  130. package/dist/src/gateway/service.js +7 -7
  131. package/dist/src/infra/unhandled-rejections.js +2 -2
  132. package/dist/src/infra/update-restart.js +1 -1
  133. package/dist/src/infra/update-runner.js +1 -1
  134. package/dist/src/personal-agent/prompts.js +1 -1
  135. package/dist/src/phone/adb-driver.d.ts +6 -2
  136. package/dist/src/phone/adb-driver.js +89 -37
  137. package/dist/src/phone/cloud-adb.js +8 -0
  138. package/dist/src/phone/cloud-scrcpy.d.ts +3 -1
  139. package/dist/src/phone/cloud-scrcpy.js +17 -3
  140. package/dist/src/phone/contracts.d.ts +45 -0
  141. package/dist/src/phone/contracts.js +16 -3
  142. package/dist/src/phone/model-adapter.d.ts +83 -1
  143. package/dist/src/phone/model-adapter.js +75 -17
  144. package/dist/src/phone/reliability.d.ts +7 -0
  145. package/dist/src/phone/reliability.js +66 -0
  146. package/dist/src/phone/runtime.d.ts +8 -0
  147. package/dist/src/phone/runtime.js +179 -11
  148. package/dist/src/phone/scrcpy-text.d.ts +11 -0
  149. package/dist/src/phone/scrcpy-text.js +72 -0
  150. package/dist/src/phone/scrcpy.d.ts +5 -1
  151. package/dist/src/phone/scrcpy.js +48 -3
  152. package/dist/src/storage/sqlite/connection.js +2 -2
  153. package/dist/src/storage/sqlite/offline-access.js +13 -7
  154. package/dist/src/storage/sqlite/schema-errors.d.ts +12 -0
  155. package/dist/src/storage/sqlite/schema-errors.js +21 -3
  156. package/dist/src/storage/sqlite/schema-upgrade.d.ts +4 -0
  157. package/dist/src/storage/sqlite/schema-upgrade.js +78 -0
  158. package/dist/src/storage/sqlite/schema.d.ts +2 -1
  159. package/dist/src/storage/sqlite/schema.js +9 -3
  160. package/dist/src/storage/sqlite/upgrades/203_remove_retired_proactive_contracts.sql +70 -0
  161. package/dist/src/storage/sqlite/upgrades/204_home_intelligence.sql +78 -0
  162. package/dist/src/storage/sqlite/upgrades/205_agent_catalog.sql +66 -0
  163. package/dist/src/storage/sqlite/upgrades/206_home_advice_strategy_version.sql +2 -0
  164. package/dist/src/storage/sqlite/upgrades/207_user_model_policy_contract.sql +121 -0
  165. package/dist/src/storage/sqlite/upgrades/208_automation_management.sql +2 -0
  166. package/dist/src/storage/sqlite/upgrades/209_automation_event_hub.sql +48 -0
  167. package/dist/src/storage/sqlite/upgrades/210_remove_legacy_automation_timeout.sql +14 -0
  168. package/dist/src/storage/sqlite/upgrades/211_automation_result_deliveries.sql +16 -0
  169. package/dist/src/storage/sqlite/upgrades/212_automation_delivery_policy.sql +30 -0
  170. package/dist/src/storage/sqlite/upgrades/213_automation_reliability_contract.sql +188 -0
  171. package/dist/src/storage/sqlite/upgrades/214_ai_usage_ledger.sql +41 -0
  172. package/dist/src/storage/sqlite/upgrades/215_scene_execution_adapters.sql +6 -0
  173. package/dist/src/storage/sqlite/upgrades/216_remove_scene_model_usage.sql +1 -0
  174. package/dist/src/storage/sqlite/upgrades/217_remove_legacy_memory_tables.sql +4 -0
  175. package/dist/src/storage/sqlite/upgrades/218_core_relation_indexes.sql +26 -0
  176. package/dist/src/storage/sqlite/upgrades/219_json_contracts.sql +123 -0
  177. package/dist/src/storage/sqlite/upgrades/220_normalize_timestamps.sql +339 -0
  178. package/dist/src/storage/sqlite/upgrades/221_ai_usage_retention_index.sql +2 -0
  179. package/dist/src/storage/sqlite/upgrades/222_session_config_authority.sql +13 -0
  180. package/dist/src/storage/sqlite/upgrades/223_session_creation_receipts.sql +59 -0
  181. package/dist/src/storage/sqlite/upgrades/224_task_origin_links.sql +8 -0
  182. package/dist/src/storage/sqlite/upgrades/225_task_collaboration.sql +32 -0
  183. package/dist/src/storage/sqlite/upgrades/226_task_main_updates.sql +9 -0
  184. package/dist/src/storage/sqlite/upgrades/227_task_main_update_decisions.sql +8 -0
  185. package/dist/src/storage/sqlite/upgrades/228_task_main_agent_links.sql +14 -0
  186. package/dist/src/storage/sqlite/upgrades/229_task_criterion_reviews.sql +12 -0
  187. package/dist/src/storage/sqlite/upgrades/230_personal_agent_onboarding.sql +8 -0
  188. package/dist/src/storage/sqlite/upgrades/231_task_result_deliveries.sql +32 -0
  189. package/dist/src/storage/sqlite/upgrades/232_personal_requests.sql +28 -0
  190. package/dist/src/storage/sqlite/upgrades/233_observability_usage_links.sql +3 -0
  191. package/dist/src/storage/sqlite/upgrades/234_task_result_reply.sql +14 -0
  192. package/dist/src/storage/sqlite/upgrades/235_personal_conversation_priority.sql +3 -0
  193. package/dist/src/storage/sqlite/upgrades/236_personal_proactivity.sql +53 -0
  194. package/dist/src/storage/sqlite/upgrades/237_personal_strategy_versions.sql +18 -0
  195. package/dist/src/storage/sqlite/upgrades/238_browser_record_replay.sql +45 -0
  196. package/dist/src/storage/sqlite/upgrades/239_clarification_notifications.sql +26 -0
  197. package/dist/src/storage/sqlite/upgrades/240_device_settings.sql +5 -0
  198. package/dist/src/storage/sqlite/upgrades/241_device_grant_audit.sql +13 -0
  199. package/dist/src/storage/sqlite/upgrades/242_cloud_usage_receipts.sql +5 -0
  200. package/dist/src/storage/sqlite/upgrades/243_voice_opening_greetings.sql +12 -0
  201. package/dist/src/storage/sqlite/upgrades/244_device_request_proofs.sql +8 -0
  202. package/dist/src/storage/sqlite/upgrades/245_memory_learning.sql +125 -0
  203. package/dist/src/storage/sqlite/upgrades/246_personal_session_thinking.sql +22 -0
  204. package/dist/src/storage/sqlite/upgrades/247_agent_selection_records.sql +32 -0
  205. package/dist/src/storage/sqlite/upgrades/248_next_turn_suggestions.sql +20 -0
  206. package/dist/src/storage/sqlite/upgrades/249_task_allocations.sql +17 -0
  207. package/dist/src/storage/sqlite/upgrades/250_research_checkpoints.sql +15 -0
  208. package/dist/src/storage/sqlite/upgrades/251_personal_result_reads.sql +40 -0
  209. package/dist/src/storage/sqlite/upgrades/252_task_work_revisions.sql +15 -0
  210. package/dist/src/storage/sqlite/upgrades/253_discussion_processing_recovery.sql +20 -0
  211. package/dist/src/storage/sqlite/upgrades/254_discussion_annotations.sql +17 -0
  212. package/dist/src/storage/sqlite/upgrades/255_discussion_chapter_progress.sql +10 -0
  213. package/dist/src/storage/sqlite/upgrades/256_task_main_update_retries.sql +21 -0
  214. package/dist/src/storage/sqlite/upgrades/257_session_composer_history.sql +11 -0
  215. package/dist/src/storage/sqlite/upgrades/258_task_phase_changes.sql +20 -0
  216. package/dist/src/storage/sqlite/upgrades/259_personal_default_style.sql +10 -0
  217. package/dist/src/storage/sqlite/windows-database-owners.js +33 -28
  218. package/dist/src/tasks/capabilities/relations.d.ts +2 -2
  219. package/dist/src/tui/clipboard-image.js +1 -1
  220. package/dist/src/tui/tui.js +1 -1
  221. package/dist/src/user-context/sources/index.js +1 -1
  222. package/dist/src/utils/index.js +1 -1
  223. package/dist/src/utils/logger/index.js +1 -1
  224. package/dist/src/utils/logger.js +1 -1
  225. package/dist/src/work-discovery/probe.js +1 -1
  226. package/dist/src/work-discovery/service.js +2 -2
  227. package/package.json +5 -5
  228. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-mf0jgEPE.js +0 -1
  229. package/dist/gateway/static/root/assets/agents-CKhZ4UYQ.js +0 -77
  230. package/dist/gateway/static/root/assets/apps-page-BZ_MaQJf.js +0 -1
  231. package/dist/gateway/static/root/assets/automations-workspace-CHUDHNVL.js +0 -8
  232. package/dist/gateway/static/root/assets/browser-automations-page-DStDm9y1.js +0 -3
  233. package/dist/gateway/static/root/assets/browser-settings-page-6I4oetKt.js +0 -3
  234. package/dist/gateway/static/root/assets/capabilities-settings-panel-BQoCPk3x.js +0 -8
  235. package/dist/gateway/static/root/assets/channels-BKf_-Mdt.js +0 -1
  236. package/dist/gateway/static/root/assets/connectors-page-CUBg8spT.js +0 -1
  237. package/dist/gateway/static/root/assets/device-pairing-wizard-PmP_vGem.js +0 -1
  238. package/dist/gateway/static/root/assets/gateway-realtime-store-CoIgC1Al.js +0 -1
  239. package/dist/gateway/static/root/assets/gateway-settings-DnrQ28E2.js +0 -1
  240. package/dist/gateway/static/root/assets/home-page-pTwVME-g.js +0 -2
  241. package/dist/gateway/static/root/assets/index-BLL0wKRZ.css +0 -1
  242. package/dist/gateway/static/root/assets/index-ygkHNYyo.js +0 -710
  243. package/dist/gateway/static/root/assets/local-app-workbench-page-Cwl6EvuX.js +0 -1
  244. package/dist/gateway/static/root/assets/management-settings-DC-Y_-GP.js +0 -1
  245. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-vckgHpON.js +0 -1
  246. package/dist/gateway/static/root/assets/note-detail-panel-C7u4dxEk.js +0 -5
  247. package/dist/gateway/static/root/assets/note-image-lightbox-jvqYtySx.js +0 -1
  248. package/dist/gateway/static/root/assets/notes-page-Bs7F5Hpf.js +0 -8
  249. package/dist/gateway/static/root/assets/page-context-capture-button-DO_yhXS1.js +0 -1
  250. package/dist/gateway/static/root/assets/personal-page-BexL2-PQ.js +0 -1
  251. package/dist/gateway/static/root/assets/personal-settings-panel-BKA4fbYH.js +0 -1
  252. package/dist/gateway/static/root/assets/phone-dependencies-card-ChFtdFhA.js +0 -1
  253. package/dist/gateway/static/root/assets/phone-settings-page-DRyK0nHH.js +0 -1
  254. package/dist/gateway/static/root/assets/plugin-details-dialog-B-N61jJH.js +0 -1
  255. package/dist/gateway/static/root/assets/project-detail-page-DtKmFBml.js +0 -7
  256. package/dist/gateway/static/root/assets/projects-page-CFZIz9mc.js +0 -1
  257. package/dist/gateway/static/root/assets/remote-access-hub-CWEc7W0m.js +0 -1
  258. package/dist/gateway/static/root/assets/sessions-page-BuPqNxpd.js +0 -1
  259. package/dist/gateway/static/root/assets/settings-advanced-gate-QTVDCGJG.js +0 -1
  260. package/dist/gateway/static/root/assets/shares-settings-CtNxWqW-.js +0 -1
  261. package/dist/gateway/static/root/assets/skill-reload-api-Egaf8PqZ.js +0 -1
  262. package/dist/gateway/static/root/assets/skills-page-C6FKxf4J.js +0 -2
  263. package/dist/gateway/static/root/assets/task-detail-page-C77-rpkt.js +0 -2
  264. package/dist/gateway/static/root/assets/user-model-page-u8zY5jea.js +0 -2
@@ -0,0 +1,7 @@
1
+ import type { PhoneAction, PhoneCompletion, PhoneObservation } from './contracts.js';
2
+ /** Ignore only recognized inert system-clock text, never arbitrary application content. */
3
+ export declare function observationSignature(observation: PhoneObservation): string;
4
+ /** Preserve full page semantics; only inert system-clock updates may be ignored. */
5
+ export declare function canDispatchPhoneAction(action: PhoneAction, before: PhoneObservation, fresh: PhoneObservation): boolean;
6
+ /** Evaluate all native nodes, separately from the truncated display facts. */
7
+ export declare function phoneCompletionSatisfied(condition: PhoneCompletion, observation: PhoneObservation, changed: boolean): boolean;
@@ -0,0 +1,66 @@
1
+ import { createHash } from "node:crypto";
2
+ //#region src/phone/reliability.ts
3
+ function geometryMatches(before, after) {
4
+ return before.package === after.package && before.width === after.width && before.height === after.height && before.rotation === after.rotation;
5
+ }
6
+ function ambientClock(node) {
7
+ const clock = /^(?:[01]?\d|2[0-3]):[0-5]\d(?::[0-5]\d)?(?:\s?[AP]M)?$/i;
8
+ return node.resourceId === "com.android.systemui:id/clock" && !node.clickable && !node.editable && !node.focused && !node.password && clock.test(node.text) && (!node.description || clock.test(node.description));
9
+ }
10
+ function semanticElements(observation) {
11
+ return observation.elements.map((node) => ambientClock(node) ? {
12
+ ...node,
13
+ text: "",
14
+ description: ""
15
+ } : node);
16
+ }
17
+ /** Ignore only recognized inert system-clock text, never arbitrary application content. */
18
+ function observationSignature(observation) {
19
+ return createHash("sha256").update(JSON.stringify([
20
+ observation.package,
21
+ observation.width,
22
+ observation.height,
23
+ observation.rotation,
24
+ observation.elements.length ? semanticElements(observation) : createHash("sha256").update(observation.frame).digest("hex")
25
+ ])).digest("hex");
26
+ }
27
+ function atPoint(elements, x, y) {
28
+ return elements.filter((node) => node.bounds[0] <= x && x < node.bounds[2] && node.bounds[1] <= y && y < node.bounds[3]);
29
+ }
30
+ /** Preserve full page semantics; only inert system-clock updates may be ignored. */
31
+ function canDispatchPhoneAction(action, before, fresh) {
32
+ if (!geometryMatches(before, fresh)) return false;
33
+ if (JSON.stringify(before.elements) === JSON.stringify(fresh.elements)) return before.elements.length > 0 || fresh.frame.equals(before.frame);
34
+ if (JSON.stringify(semanticElements(before)) !== JSON.stringify(semanticElements(fresh))) return false;
35
+ if (action.type === "tap" || action.type === "doubleTap" || action.type === "longPress") {
36
+ const [x, y] = [action.point[0] * before.width / 1e3, action.point[1] * before.height / 1e3];
37
+ const targets = atPoint(before.elements, x, y);
38
+ const identified = targets.filter((node) => node.resourceId && node.clickable && !node.password);
39
+ if (identified.length !== 1) return false;
40
+ const target = identified[0];
41
+ if (before.elements.filter((node) => node.resourceId === target.resourceId).length !== 1 || fresh.elements.filter((node) => node.resourceId === target.resourceId).length !== 1) return false;
42
+ return JSON.stringify(targets) === JSON.stringify(atPoint(fresh.elements, x, y));
43
+ }
44
+ if (action.type === "typeText") {
45
+ const focused = (screen) => screen.elements.filter((node) => node.focused && node.editable && !node.password);
46
+ const previous = focused(before);
47
+ const current = focused(fresh);
48
+ return previous.length === 1 && current.length === 1 && Boolean(previous[0].resourceId) && JSON.stringify(previous) === JSON.stringify(current);
49
+ }
50
+ return false;
51
+ }
52
+ /** Evaluate all native nodes, separately from the truncated display facts. */
53
+ function phoneCompletionSatisfied(condition, observation, changed) {
54
+ if (condition.requireChange && !changed) return false;
55
+ if (condition.appPackage && condition.appPackage !== observation.package) return false;
56
+ const texts = observation.elements.filter((node) => !node.password).flatMap((node) => [node.text, node.description]).filter(Boolean);
57
+ if (condition.textAny && !condition.textAny.some((value) => texts.some((text) => text.includes(value)))) return false;
58
+ if (condition.textAll && !condition.textAll.every((value) => texts.some((text) => text.includes(value)))) return false;
59
+ if (condition.field) {
60
+ const fields = observation.elements.filter((node) => !node.password && node.editable && node.resourceId === condition.field.resourceId);
61
+ if (fields.length !== 1 || fields[0].text !== condition.field.value) return false;
62
+ }
63
+ return true;
64
+ }
65
+ //#endregion
66
+ export { canDispatchPhoneAction, observationSignature, phoneCompletionSatisfied };
@@ -30,6 +30,13 @@ export declare class PhoneRuntime {
30
30
  deviceId?: string;
31
31
  serial?: string;
32
32
  }, config: Config, signal: AbortSignal): Promise<PhoneApplication[]>;
33
+ textInputStatus(id: string, config: Config, signal: AbortSignal): Promise<{
34
+ policy: "manual" | "auto";
35
+ androidSdk: number;
36
+ unicode: "available" | "setup_required" | "unsupported";
37
+ method: "scrcpy-clipboard" | "provider-native";
38
+ keyboardRequired: false;
39
+ }>;
33
40
  preview(id: string, config: Config, signal: AbortSignal): Promise<PhoneObservation>;
34
41
  previewSession(owner: string, context: ExecutionContext, signal: AbortSignal): Promise<PhoneObservation>;
35
42
  reports(): PhoneReport[];
@@ -55,6 +62,7 @@ export declare class PhoneRuntime {
55
62
  ownControl: boolean;
56
63
  allowedApps: string[];
57
64
  appAccess: "all" | "selected";
65
+ textInput: "manual" | "auto";
58
66
  frame: {
59
67
  seq: number;
60
68
  capturedAt: number;
@@ -10,6 +10,7 @@ import { LocalScrcpyProvider } from "./scrcpy.js";
10
10
  import { CloudPhones } from "./cloud-phones.js";
11
11
  import { createPhoneModel, isAboutScreen } from "./model-adapter.js";
12
12
  import { PhoneRepository } from "./repository.js";
13
+ import { canDispatchPhoneAction, observationSignature, phoneCompletionSatisfied } from "./reliability.js";
13
14
  import { createHash, randomUUID } from "node:crypto";
14
15
  //#region src/phone/runtime.ts
15
16
  init_logger();
@@ -58,6 +59,16 @@ var PhoneRuntime = class {
58
59
  if (!driver.applications) throw new PhoneError("PHONE_APPS_UNAVAILABLE", "The phone provider cannot list applications");
59
60
  return driver.applications(serial, signal);
60
61
  }
62
+ async textInputStatus(id, config, signal) {
63
+ const device = this.repository.get(id);
64
+ if (!device.enabled) throw new PhoneError("PHONE_DEVICE_DENIED", "Phone access is disabled", 403);
65
+ const driver = await this.driverFor(device, config);
66
+ if (!driver.textInputStatus) throw new PhoneError("PHONE_TEXT_INPUT_UNAVAILABLE", "This phone provider does not expose text input diagnostics", 409);
67
+ return {
68
+ ...await driver.textInputStatus(device.serial, signal),
69
+ policy: device.textInput ?? "auto"
70
+ };
71
+ }
61
72
  async preview(id, config, signal) {
62
73
  const device = this.repository.get(id);
63
74
  if (!device.enabled) throw new PhoneError("PHONE_DEVICE_DENIED", "Phone access is disabled", 403);
@@ -168,6 +179,9 @@ var PhoneRuntime = class {
168
179
  adbPath,
169
180
  context,
170
181
  applications,
182
+ trajectory: [],
183
+ trajectoryCount: 0,
184
+ trajectorySummary: [],
171
185
  frames: new PhoneFrameCache(),
172
186
  io: Promise.resolve(),
173
187
  ioBusy: false,
@@ -194,7 +208,13 @@ var PhoneRuntime = class {
194
208
  updatedAt: now,
195
209
  verified: false,
196
210
  verificationLevel: "none",
197
- steps: []
211
+ steps: [],
212
+ diagnostics: {
213
+ stalePredictions: 0,
214
+ protocolRepairs: 0,
215
+ unchangedActions: 0,
216
+ completionCorrections: 0
217
+ }
198
218
  }
199
219
  };
200
220
  this.sessions.set(owner, session);
@@ -259,6 +279,9 @@ var PhoneRuntime = class {
259
279
  const startedAt = Date.now();
260
280
  session.report.errorCode = void 0;
261
281
  session.report.verified = false;
282
+ session.report.verificationLevel = "none";
283
+ session.report.stopReason = void 0;
284
+ session.report.message = void 0;
262
285
  try {
263
286
  await this.waitForAgent(session, signal);
264
287
  session.report.status = "running";
@@ -282,7 +305,20 @@ var PhoneRuntime = class {
282
305
  if (session.report.mode !== "control") throw new PhoneError("PHONE_OBSERVE_ONLY", "Reopen the phone in control mode to execute actions");
283
306
  if (session.report.mock && input.scenario !== "settings-about") throw new PhoneError("PHONE_MOCK_SCENARIO_REQUIRED", "Mock mode only supports the explicit settings-about scenario");
284
307
  const maxActions = input.op === "step" ? 1 : input.maxActions;
308
+ const requiredTexts = [...input.completion?.textAll ?? [], ...input.expectText ? [input.expectText] : []];
309
+ const completion = input.completion || input.expectText ? {
310
+ ...input.completion,
311
+ textAll: requiredTexts.length ? requiredTexts : void 0
312
+ } : void 0;
285
313
  let actions = 0;
314
+ let protocolFeedback;
315
+ let repairs = 0;
316
+ let completionCorrections = 0;
317
+ let recoveryHint;
318
+ let changed = false;
319
+ let previousSignature = observationSignature(observation);
320
+ let controlEpoch = session.report.controlEpoch;
321
+ const attempts = /* @__PURE__ */ new Map();
286
322
  while (true) {
287
323
  if (await this.waitForAgent(session, signal)) {
288
324
  observation.frame.fill(0);
@@ -292,13 +328,36 @@ var PhoneRuntime = class {
292
328
  this.guard(session, context);
293
329
  this.allowedScreen(session, observation);
294
330
  const epoch = session.report.controlEpoch;
331
+ if (controlEpoch !== epoch) {
332
+ attempts.clear();
333
+ recoveryHint = "The user handed back control. Re-evaluate the fresh screen before continuing.";
334
+ controlEpoch = epoch;
335
+ }
336
+ const signature = observationSignature(observation);
337
+ if (signature !== previousSignature) changed = true;
338
+ previousSignature = signature;
295
339
  this.recordObservation(session, observation);
296
- if (session.report.mock ? isAboutScreen(observation) : Boolean(input.expectText && session.report.facts?.some((text) => text.includes(input.expectText)))) {
297
- session.report.verified = true;
298
- session.report.verificationLevel = "ui";
299
- session.report.status = "completed";
300
- session.report.stopReason = actions ? "condition_satisfied" : "condition_present";
301
- return this.publish(session);
340
+ if (session.report.mock ? isAboutScreen(observation) : Boolean(completion && phoneCompletionSatisfied(completion, observation, changed))) {
341
+ const confirmation = await this.observe(session, signal);
342
+ try {
343
+ signal.throwIfAborted();
344
+ this.guard(session, context);
345
+ this.allowedScreen(session, confirmation);
346
+ const confirmed = session.report.mock ? isAboutScreen(confirmation) : Boolean(completion && phoneCompletionSatisfied(completion, confirmation, changed));
347
+ if (epoch !== session.report.controlEpoch || session.report.controlOwner !== "agent" || !confirmed) {
348
+ observation.frame.fill(0);
349
+ observation = confirmation;
350
+ continue;
351
+ }
352
+ this.recordObservation(session, confirmation);
353
+ session.report.verified = true;
354
+ session.report.verificationLevel = "ui";
355
+ session.report.status = "completed";
356
+ session.report.stopReason = actions ? "condition_satisfied" : "condition_present";
357
+ return this.publish(session);
358
+ } finally {
359
+ if (observation !== confirmation) confirmation.frame.fill(0);
360
+ }
302
361
  }
303
362
  if (actions >= maxActions) {
304
363
  session.report.status = "ready";
@@ -315,10 +374,22 @@ var PhoneRuntime = class {
315
374
  decision = await session.adapter.predict({
316
375
  goal: input.goal,
317
376
  observation,
377
+ completion,
318
378
  allowedApps: session.device.allowedApps,
319
379
  appAccess: phoneAppAccess(session.device),
320
380
  applications: session.applications.filter((app) => phoneAllowsApp(session.device, app.packageName)),
321
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,
322
393
  scenario: input.scenario
323
394
  }, AbortSignal.any([signal, session.prediction.signal]));
324
395
  } catch (error) {
@@ -328,6 +399,14 @@ var PhoneRuntime = class {
328
399
  observation = await this.observe(session, signal);
329
400
  continue;
330
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;
409
+ }
331
410
  throw error;
332
411
  } finally {
333
412
  session.prediction = void 0;
@@ -340,9 +419,21 @@ var PhoneRuntime = class {
340
419
  observation = await this.observe(session, signal);
341
420
  continue;
342
421
  }
422
+ protocolFeedback = void 0;
343
423
  if (decision.kind !== "action") {
424
+ if (decision.kind === "complete" && !session.report.mock && (input.completion || input.expectText) && completionCorrections < 1 && session.report.modelRequests < context.config.phone.maxModelRequests) {
425
+ completionCorrections++;
426
+ session.report.diagnostics.completionCorrections++;
427
+ recoveryHint = `Your completion claim is not verified. These required native UI conditions are not all satisfied: ${JSON.stringify({
428
+ expectText: input.expectText,
429
+ ...input.completion
430
+ })}. Recheck the fresh screen, preserve user constraints, and continue if a safe authorized action can satisfy the goal; otherwise report unfinished.`;
431
+ observation.frame.fill(0);
432
+ observation = await this.observe(session, signal);
433
+ continue;
434
+ }
344
435
  session.report.status = decision.kind === "needsUser" ? "needs_user" : "completed";
345
- session.report.message = decision.kind === "needsUser" ? decision.reason : void 0;
436
+ session.report.message = decision.kind === "needsUser" ? decision.reason : decision.summary;
346
437
  session.report.stopReason = decision.kind === "needsUser" ? "user_takeover" : "model_finished_unverified";
347
438
  if (decision.kind === "needsUser" && !session.report.mock) {
348
439
  session.report.controlOwner = "none";
@@ -359,21 +450,59 @@ var PhoneRuntime = class {
359
450
  const fresh = await this.observe(session, signal);
360
451
  try {
361
452
  this.allowedScreen(session, fresh);
362
- if (fresh.package !== observation.package || fresh.width !== observation.width || fresh.height !== observation.height || fresh.rotation !== observation.rotation || JSON.stringify(fresh.elements) !== JSON.stringify(observation.elements) || !fresh.elements.length && !fresh.frame.equals(observation.frame)) {
453
+ if (!canDispatchPhoneAction(decision.action, observation, fresh)) {
454
+ session.report.diagnostics.stalePredictions++;
455
+ recoveryHint = "The proposed action was not dispatched because its target changed. Locate it again on the fresh screen.";
363
456
  observation.frame.fill(0);
364
457
  observation = fresh;
365
458
  continue;
366
459
  }
367
460
  if (epoch !== session.report.controlEpoch || session.report.controlOwner !== "agent") continue;
461
+ const attemptKey = `${observationSignature(fresh)}:${JSON.stringify(decision.action)}`;
462
+ const repetitions = attempts.get(attemptKey) ?? 0;
463
+ if (repetitions >= (decision.action.type === "wait" ? 3 : 2)) {
464
+ session.report.status = "paused";
465
+ session.report.controlOwner = "none";
466
+ session.report.controlEpoch++;
467
+ session.report.stopReason = "no_progress";
468
+ session.report.message = "Repeated actions made no progress. Inspect the phone and hand back before continuing.";
469
+ session.budget?.pause();
470
+ await session.stream?.revoke();
471
+ return this.publish(session);
472
+ }
473
+ attempts.set(attemptKey, repetitions + 1);
368
474
  if (await this.dispatch(session, decision.action, fresh, context, signal, epoch)) actions++;
369
475
  } finally {
370
476
  if (observation !== fresh) fresh.frame.fill(0);
371
477
  }
478
+ const beforeSignature = observationSignature(observation);
372
479
  observation.frame.fill(0);
373
480
  observation = await this.observe(session, signal);
481
+ const entry = session.trajectory.at(-1);
482
+ if (entry && entry.actor === "agent" && epoch === session.report.controlEpoch) {
483
+ const unchanged = beforeSignature === observationSignature(observation);
484
+ entry.afterApp = observation.package;
485
+ entry.effect = unchanged ? "unchanged" : "changed";
486
+ entry.facts = observation.elements.filter((node) => !node.password).flatMap((node) => [node.text, node.description]).filter(Boolean).slice(0, 12).map((text) => text.slice(0, 160));
487
+ if (unchanged) session.report.diagnostics.unchangedActions++;
488
+ recoveryHint = unchanged ? "The last dispatched action produced no observable change. Wait if loading, otherwise try a different target or route; do not repeat an uncertain input." : void 0;
489
+ }
374
490
  }
375
491
  } catch (error) {
376
492
  const failure = signal.aborted && signal.reason instanceof PhoneError ? signal.reason : error;
493
+ if (!signal.aborted && failure instanceof PhoneError && ["PHONE_TEXT_INPUT_UNAVAILABLE", "PHONE_TEXT_INPUT_UNCONFIRMED"].includes(failure.code)) {
494
+ session.report.status = "needs_user";
495
+ session.human = void 0;
496
+ session.report.controlOwner = "none";
497
+ session.report.controlEpoch++;
498
+ session.report.errorCode = failure.code;
499
+ session.report.message = failure.message;
500
+ session.report.currentAction = void 0;
501
+ session.report.stopReason = failure.code === "PHONE_TEXT_INPUT_UNAVAILABLE" ? "text_input_required" : "text_input_unconfirmed";
502
+ session.budget?.pause();
503
+ await session.stream?.revoke();
504
+ return this.publish(session);
505
+ }
377
506
  if (failure instanceof PhoneError && failure.code === "PHONE_OPERATION_TIMEOUT" && !terminal.has(session.report.status) && session.report.status !== "paused") {
378
507
  session.report.status = "paused";
379
508
  session.report.controlOwner = "none";
@@ -427,7 +556,8 @@ var PhoneRuntime = class {
427
556
  status: session.report.status,
428
557
  stopReason: session.report.stopReason,
429
558
  actions: session.report.actions,
430
- modelRequests: session.report.modelRequests
559
+ modelRequests: session.report.modelRequests,
560
+ diagnostics: session.report.diagnostics
431
561
  }, "Phone goal finished");
432
562
  }
433
563
  }
@@ -445,6 +575,7 @@ var PhoneRuntime = class {
445
575
  if (session.human && (session.report.humanActions ?? 0) >= 1200) throw new PhoneError("PHONE_ACTION_LIMIT", "Manual input limit reached; reopen the session");
446
576
  if (action.type === "launch" && !phoneAllowsApp(session.device, action.package)) throw new PhoneError("PHONE_APP_DENIED", "Application is not authorized");
447
577
  if (action.type === "home" && phoneAppAccess(session.device) !== "all") throw new PhoneError("PHONE_APP_DENIED", "Home leaves the app allowlist");
578
+ if (action.type === "typeText" && session.device.textInput === "manual") throw new PhoneError("PHONE_TEXT_INPUT_UNAVAILABLE", "Automatic text input is disabled for this phone. Ask the user to take over and type with the phone keyboard.", 409);
448
579
  if (action.type === "typeText" && !observation.elements.some((node) => node.focused && node.editable && !node.password)) throw new PhoneError("PHONE_INPUT_NOT_FOCUSED", "Focus a non-password editable field before typing", 409);
449
580
  const step = {
450
581
  action: action.type,
@@ -460,14 +591,48 @@ var PhoneRuntime = class {
460
591
  session.report.steps = session.report.steps.slice(-200);
461
592
  }
462
593
  this.publish(session);
594
+ const entry = {
595
+ action: structuredClone(action),
596
+ dispatch: "unknown",
597
+ actor: step.actor,
598
+ beforeApp: observation.package,
599
+ effect: "unknown"
600
+ };
601
+ session.trajectory.push(entry);
602
+ session.trajectoryCount++;
603
+ if (session.trajectory.length > 24) {
604
+ const earlier = session.trajectory.shift();
605
+ session.trajectorySummary.push(JSON.stringify({
606
+ action: earlier.action,
607
+ effect: earlier.effect,
608
+ afterApp: earlier.afterApp
609
+ }).slice(0, 250));
610
+ session.trajectorySummary = session.trajectorySummary.slice(-12);
611
+ }
463
612
  try {
464
613
  await session.driver.execute(session.device.serial, action, observation, signal, session.human ? {
465
614
  settleMs: 0,
466
615
  ...resetTask ? { resetTask: true } : {}
467
616
  } : void 0);
468
617
  step.dispatch = "completed";
618
+ entry.dispatch = "completed";
469
619
  } catch (error) {
470
- session.report.status = "outcome_unknown";
620
+ const textFailure = error instanceof PhoneError && ["PHONE_TEXT_INPUT_UNAVAILABLE", "PHONE_TEXT_INPUT_UNCONFIRMED"].includes(error.code);
621
+ if (textFailure && error.code === "PHONE_TEXT_INPUT_UNAVAILABLE") {
622
+ session.report.steps.pop();
623
+ session.report.actions--;
624
+ session.trajectory.pop();
625
+ session.trajectoryCount--;
626
+ if (session.human) session.report.humanActions--;
627
+ } else session.report.status = "outcome_unknown";
628
+ if (textFailure && session.human) {
629
+ session.report.status = "paused";
630
+ session.report.errorCode = error.code;
631
+ session.report.message = error.message;
632
+ session.report.stopReason = error.code === "PHONE_TEXT_INPUT_UNAVAILABLE" ? "text_input_required" : "text_input_unconfirmed";
633
+ session.report.currentAction = void 0;
634
+ this.publish(session);
635
+ }
471
636
  session.frames.clear();
472
637
  throw error;
473
638
  }
@@ -497,6 +662,8 @@ var PhoneRuntime = class {
497
662
  if (op !== "pause") {
498
663
  session.report.facts = void 0;
499
664
  session.report.message = void 0;
665
+ session.trajectory = [];
666
+ session.trajectorySummary = [];
500
667
  session.frames.clear();
501
668
  session.adapter = { predict: async () => {
502
669
  throw new PhoneError("PHONE_OPEN_REQUIRED", "Reopen the phone session");
@@ -625,6 +792,7 @@ var PhoneRuntime = class {
625
792
  ownControl: Boolean(session.human === client),
626
793
  allowedApps: [...session.device.allowedApps],
627
794
  appAccess: phoneAppAccess(session.device),
795
+ textInput: session.device.textInput ?? "auto",
628
796
  frame: session.frames.get() ? {
629
797
  seq: session.frames.get().seq,
630
798
  capturedAt: session.frames.get().capturedAt
@@ -0,0 +1,11 @@
1
+ /** Only supplied text is sent to Android; clipboard autosync and reads stay off. */
2
+ export declare class ScrcpyTextChannel {
3
+ private write;
4
+ private buffer;
5
+ private sequence;
6
+ private pending;
7
+ constructor(write: (bytes: Buffer) => Promise<void>);
8
+ receive(bytes: Buffer): void;
9
+ paste(text: string, signal: AbortSignal): Promise<void>;
10
+ close(): void;
11
+ }
@@ -0,0 +1,72 @@
1
+ import { PhoneError, init_contracts } from "./contracts.js";
2
+ //#region src/phone/scrcpy-text.ts
3
+ init_contracts();
4
+ /** Only supplied text is sent to Android; clipboard autosync and reads stay off. */
5
+ var ScrcpyTextChannel = class {
6
+ buffer = Buffer.alloc(0);
7
+ sequence = 0n;
8
+ pending = /* @__PURE__ */ new Map();
9
+ constructor(write) {
10
+ this.write = write;
11
+ }
12
+ receive(bytes) {
13
+ this.buffer = Buffer.concat([this.buffer, bytes]);
14
+ while (this.buffer.length) {
15
+ const type = this.buffer[0];
16
+ let length;
17
+ if (type === 1) {
18
+ if (this.buffer.length < 9) return;
19
+ const seq = this.buffer.readBigUInt64BE(1);
20
+ this.pending.get(seq)?.resolve();
21
+ this.pending.delete(seq);
22
+ length = 9;
23
+ } else if (type === 0 || type === 2) {
24
+ if (this.buffer.length < 5) return;
25
+ length = 5 + (type === 0 ? this.buffer.readUInt32BE(1) : this.buffer.readUInt16BE(3));
26
+ if (length > 262144) throw new Error("Invalid device message");
27
+ if (this.buffer.length < length) return;
28
+ } else throw new Error("Unknown device message");
29
+ this.buffer = this.buffer.subarray(length);
30
+ }
31
+ }
32
+ async paste(text, signal) {
33
+ signal.throwIfAborted();
34
+ if (!text || text.length > 4e3 || this.pending.size) throw new PhoneError("PHONE_INVALID_INPUT", "Invalid or concurrent phone text input");
35
+ const sequence = ++this.sequence;
36
+ const payload = Buffer.from(text);
37
+ const bytes = Buffer.alloc(14 + payload.length);
38
+ bytes[0] = 9;
39
+ bytes.writeBigUInt64BE(sequence, 1);
40
+ bytes[9] = 1;
41
+ bytes.writeUInt32BE(payload.length, 10);
42
+ payload.copy(bytes, 14);
43
+ payload.fill(0);
44
+ const deadline = AbortSignal.any([signal, AbortSignal.timeout(1e4)]);
45
+ let abort;
46
+ const ack = new Promise((resolve, reject) => {
47
+ abort = () => reject(new PhoneError("PHONE_TEXT_INPUT_UNCONFIRMED", "Text may have been pasted. Take over and inspect the input field before retrying.", 409));
48
+ this.pending.set(sequence, {
49
+ resolve,
50
+ reject
51
+ });
52
+ deadline.addEventListener("abort", abort, { once: true });
53
+ });
54
+ try {
55
+ await Promise.all([this.write(bytes), ack]);
56
+ } catch {
57
+ throw new PhoneError("PHONE_TEXT_INPUT_UNCONFIRMED", "Text may have been pasted. Take over and inspect the input field before retrying.", 409);
58
+ } finally {
59
+ bytes.fill(0);
60
+ deadline.removeEventListener("abort", abort);
61
+ this.pending.delete(sequence);
62
+ }
63
+ }
64
+ close() {
65
+ for (const request of this.pending.values()) request.reject(new PhoneError("PHONE_TEXT_INPUT_UNCONFIRMED", "Phone text channel disconnected; inspect the field before retrying.", 409));
66
+ this.pending.clear();
67
+ this.buffer.fill(0);
68
+ this.buffer = Buffer.alloc(0);
69
+ }
70
+ };
71
+ //#endregion
72
+ export { ScrcpyTextChannel };
@@ -1,3 +1,4 @@
1
+ import { ScrcpyTextChannel } from './scrcpy-text.js';
1
2
  import type { PhoneInteractiveProvider, PhonePointer, PhoneVideoGeometry, PhoneVideoPacket } from './live-provider.js';
2
3
  export { readScrcpyServer, scrcpyServerPath, SCRCPY_VERSION, SCRCPY_SHA256 } from './scrcpy-assets.js';
3
4
  /** The pinned server sends codec metadata followed by bounded, length-prefixed packets. */
@@ -18,6 +19,7 @@ export declare abstract class ScrcpyProvider implements PhoneInteractiveProvider
18
19
  };
19
20
  geometry: PhoneVideoGeometry;
20
21
  protected closing: boolean;
22
+ protected textChannel: ScrcpyTextChannel;
21
23
  private touch?;
22
24
  private listeners;
23
25
  private config?;
@@ -29,6 +31,7 @@ export declare abstract class ScrcpyProvider implements PhoneInteractiveProvider
29
31
  protected abstract startTransport(): Promise<void>;
30
32
  protected abstract write(buffer: Buffer): Promise<void>;
31
33
  protected abstract cleanup(): Promise<void>;
34
+ pasteText(text: string, signal: AbortSignal): Promise<void>;
32
35
  start(): Promise<void>;
33
36
  protected packet(packet: PhoneVideoPacket): void;
34
37
  subscribe(listener: (packet: PhoneVideoPacket) => void): () => void;
@@ -42,6 +45,7 @@ export declare class LocalScrcpyProvider extends ScrcpyProvider {
42
45
  private adb;
43
46
  private serial;
44
47
  private tuning;
48
+ private textOnly;
45
49
  private process?;
46
50
  private video?;
47
51
  private control?;
@@ -51,7 +55,7 @@ export declare class LocalScrcpyProvider extends ScrcpyProvider {
51
55
  constructor(adb: string, serial: string, onFailure: () => void, tuning?: {
52
56
  maxSize: number;
53
57
  bitRate: number;
54
- });
58
+ }, textOnly?: boolean);
55
59
  foreground(signal: AbortSignal): Promise<string>;
56
60
  protected startTransport(): Promise<void>;
57
61
  protected write(buffer: Buffer): Promise<void>;
@@ -2,6 +2,7 @@ import { PhoneError, init_contracts } from "./contracts.js";
2
2
  import { spawnPhoneCommand } from "../process/phone-process.js";
3
3
  import { SCRCPY_SHA256, SCRCPY_VERSION } from "../runtime-tools/phone-dependency-catalog.js";
4
4
  import { readScrcpyServer, scrcpyServerPath } from "./scrcpy-assets.js";
5
+ import { ScrcpyTextChannel } from "./scrcpy-text.js";
5
6
  import { AdbPhoneDriver, runAdb } from "./adb-driver.js";
6
7
  import { randomBytes } from "node:crypto";
7
8
  import { connect } from "node:net";
@@ -69,6 +70,7 @@ var ScrcpyProvider = class {
69
70
  codec: ""
70
71
  };
71
72
  closing = false;
73
+ textChannel = new ScrcpyTextChannel((bytes) => this.write(bytes));
72
74
  touch;
73
75
  listeners = /* @__PURE__ */ new Set();
74
76
  config;
@@ -78,6 +80,25 @@ var ScrcpyProvider = class {
78
80
  constructor(onFailure) {
79
81
  this.onFailure = onFailure;
80
82
  }
83
+ async pasteText(text, signal) {
84
+ signal.throwIfAborted();
85
+ const deadline = AbortSignal.any([signal, AbortSignal.timeout(25e3)]);
86
+ const abort = () => {
87
+ this.close();
88
+ };
89
+ deadline.addEventListener("abort", abort, { once: true });
90
+ try {
91
+ try {
92
+ await this.start();
93
+ deadline.throwIfAborted();
94
+ } catch {
95
+ throw new PhoneError("PHONE_TEXT_INPUT_UNAVAILABLE", "Automatic text input is unavailable. Check Phone settings or take over and type with the phone keyboard.", 409);
96
+ }
97
+ await this.textChannel.paste(text, deadline);
98
+ } finally {
99
+ deadline.removeEventListener("abort", abort);
100
+ }
101
+ }
81
102
  start() {
82
103
  if (this.closing) return Promise.reject(new PhoneError("PHONE_VIDEO_ENDED", "Phone video ended", 409));
83
104
  if (!this.startResult) {
@@ -144,6 +165,7 @@ var ScrcpyProvider = class {
144
165
  }
145
166
  close() {
146
167
  this.closing = true;
168
+ this.textChannel.close();
147
169
  return this.closeResult ??= (async () => {
148
170
  await this.opening?.catch(() => {});
149
171
  try {
@@ -165,11 +187,12 @@ var LocalScrcpyProvider = class extends ScrcpyProvider {
165
187
  constructor(adb, serial, onFailure, tuning = {
166
188
  maxSize: 960,
167
189
  bitRate: 2e6
168
- }) {
190
+ }, textOnly = false) {
169
191
  super(onFailure);
170
192
  this.adb = adb;
171
193
  this.serial = serial;
172
194
  this.tuning = tuning;
195
+ this.textOnly = textOnly;
173
196
  }
174
197
  foreground(signal) {
175
198
  return new AdbPhoneDriver(this.adb).foreground(this.serial, signal);
@@ -221,7 +244,8 @@ var LocalScrcpyProvider = class extends ScrcpyProvider {
221
244
  "send_device_meta=false",
222
245
  "send_codec_meta=true",
223
246
  "send_frame_meta=true",
224
- "clipboard_autosync=false"
247
+ "clipboard_autosync=false",
248
+ ...this.textOnly ? ["video=false", "power_on=false"] : []
225
249
  ]);
226
250
  this.process.once("error", () => this.fail());
227
251
  this.process.once("exit", () => this.fail());
@@ -259,6 +283,21 @@ var LocalScrcpyProvider = class extends ScrcpyProvider {
259
283
  await setTimeout$1(50);
260
284
  }
261
285
  if (!this.video) throw new Error("Scrcpy connection timed out");
286
+ if (this.textOnly) {
287
+ this.control = this.video;
288
+ this.video = void 0;
289
+ this.control.setNoDelay(true);
290
+ this.control.on("error", () => this.fail());
291
+ this.control.on("end", () => this.fail());
292
+ this.control.on("data", (data) => {
293
+ try {
294
+ this.textChannel.receive(Buffer.from(data));
295
+ } catch {
296
+ this.fail();
297
+ }
298
+ });
299
+ return;
300
+ }
262
301
  const parser = new ScrcpyVideoParser();
263
302
  this.video.on("data", (data) => {
264
303
  try {
@@ -276,7 +315,13 @@ var LocalScrcpyProvider = class extends ScrcpyProvider {
276
315
  this.control.setNoDelay(true);
277
316
  this.control.on("error", () => this.fail());
278
317
  this.control.on("end", () => this.fail());
279
- this.control.on("data", () => {});
318
+ this.control.on("data", (data) => {
319
+ try {
320
+ this.textChannel.receive(Buffer.from(data));
321
+ } catch {
322
+ this.fail();
323
+ }
324
+ });
280
325
  await new Promise((resolve, reject) => {
281
326
  this.control.once("connect", resolve);
282
327
  this.control.once("error", reject);
@@ -1,10 +1,10 @@
1
1
  import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
2
2
  import { resolveXopcDatabasePath } from "../../config/paths-state.js";
3
3
  import { init_paths } from "./paths.js";
4
- import { ensureXopcDatabaseSchema, init_schema } from "./schema.js";
5
- import { init_node_sqlite, requireNodeSqlite } from "../../infra/node-sqlite.js";
6
4
  import { createLogger } from "../../utils/logger/index.js";
7
5
  import { init_logger } from "../../utils/logger.js";
6
+ import { ensureXopcDatabaseSchema, init_schema } from "./schema.js";
7
+ import { init_node_sqlite, requireNodeSqlite } from "../../infra/node-sqlite.js";
8
8
  import { init_unhandled_rejections, installSqliteTransientRejectionHandler } from "../../infra/unhandled-rejections.js";
9
9
  import path from "node:path";
10
10
  import fs from "node:fs";