@xopcai/xopc 0.0.413 → 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 (323) 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-BT6oqCMD.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-BdCgtTfT.js → app-management-settings-panel-DgwqNi5i.js} +1 -1
  8. package/dist/gateway/static/root/assets/app-window-DXFNqt_0.js +1 -0
  9. package/dist/gateway/static/root/assets/{appearance-settings-C3-hCFlF.js → appearance-settings-C0wJfFyz.js} +1 -1
  10. package/dist/gateway/static/root/assets/apps-page-C0LcnI4h.js +1 -0
  11. package/dist/gateway/static/root/assets/{archive-plugin-BviFGCy4.js → archive-plugin-Ch7wolHE.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automations-page-CfzazXE5.js → automations-page-DK7AU79v.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-workspace-CBxiV34x.js +8 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-Dp-gtJEk.js → binary-plugins-B4HcHYsC.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-D-DEPdpi.js → block-editor-BQAWEZF-.js} +4 -4
  16. package/dist/gateway/static/root/assets/{browser-automation-inputs-D1pDgMJu.js → browser-automation-inputs-D6AccXaA.js} +1 -1
  17. package/dist/gateway/static/root/assets/browser-automations-page-vJIrTxdi.js +3 -0
  18. package/dist/gateway/static/root/assets/browser-settings-page-in5Roh_E.js +3 -0
  19. package/dist/gateway/static/root/assets/{capabilities-Bpeh6E22.js → capabilities-BlxF36t-.js} +1 -1
  20. package/dist/gateway/static/root/assets/capabilities-page-CiGx8wpQ.js +2 -0
  21. package/dist/gateway/static/root/assets/capabilities-settings-panel-a8kqjzQu.js +8 -0
  22. package/dist/gateway/static/root/assets/{capability-header-actions-CP2fI9Fe.js → capability-header-actions-D4Yq35fD.js} +1 -1
  23. package/dist/gateway/static/root/assets/channels-C_CxcjKT.js +1 -0
  24. package/dist/gateway/static/root/assets/{chat-terminal-dock-BL3tFntQ.js → chat-terminal-dock-zdMySdMm.js} +2 -2
  25. package/dist/gateway/static/root/assets/chevron-down-ccbiQQz_.js +1 -0
  26. package/dist/gateway/static/root/assets/{computer-settings-page-DEQ5GYfC.js → computer-settings-page-C5Z6Jr0p.js} +1 -1
  27. package/dist/gateway/static/root/assets/{connector-service-page-CecwMnC5.js → connector-service-page-Cxus0qrZ.js} +1 -1
  28. package/dist/gateway/static/root/assets/connectors-page-DCBO2CxK.js +1 -0
  29. package/dist/gateway/static/root/assets/{date-picker-N5jnlvPs.js → date-picker-DuyyD6jz.js} +1 -1
  30. package/dist/gateway/static/root/assets/dependency-picker-BZN72rrk.js +1 -0
  31. package/dist/gateway/static/root/assets/desktop-pet-BtvTIDCi.js +1 -0
  32. package/dist/gateway/static/root/assets/desktop-pet-settings-BcyP7Z6v.js +1 -0
  33. package/dist/gateway/static/root/assets/device-pairing-wizard-Ctbgr50F.js +1 -0
  34. package/dist/gateway/static/root/assets/{directory-picker-path-field-CbMbPimD.js → directory-picker-path-field-DOusKz-O.js} +1 -1
  35. package/dist/gateway/static/root/assets/ellipsis-0k4rZLoy.js +1 -0
  36. package/dist/gateway/static/root/assets/{extension-debug-page-OE1nJMQ8.js → extension-debug-page-Bt4FwsFu.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-page-BEomiKhy.js → extension-page-0vsxX3FB.js} +1 -1
  38. package/dist/gateway/static/root/assets/{extension-settings-page-BFOjQeSX.js → extension-settings-page-5Pt5_ELc.js} +1 -1
  39. package/dist/gateway/static/root/assets/eye-CABVeTq0.js +1 -0
  40. package/dist/gateway/static/root/assets/folder-open-CyzJ19I9.js +1 -0
  41. package/dist/gateway/static/root/assets/{gateway-config-swr-DF_HV6NP.js → gateway-config-swr-fQKBHBi1.js} +1 -1
  42. package/dist/gateway/static/root/assets/{gateway-realtime-DMAzNlm-.js → gateway-realtime-DkWCHVzI.js} +1 -1
  43. package/dist/gateway/static/root/assets/gateway-realtime-store-Bx9KCID-.js +1 -0
  44. package/dist/gateway/static/root/assets/gateway-settings-BsVOcmV5.js +1 -0
  45. package/dist/gateway/static/root/assets/{gateway-startup-retry-B91CBTzO.js → gateway-startup-retry-Cfwxof4k.js} +1 -1
  46. package/dist/gateway/static/root/assets/{highlight-code-6ONPRH5r.js → highlight-code-C8eS9949.js} +1 -1
  47. package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-EJ9Kg4iQ.js → highlighted-body-KQOG7T2V-Cbk7eMlg.js} +1 -1
  48. package/dist/gateway/static/root/assets/home-page-BZDZnmyc.js +2 -0
  49. package/dist/gateway/static/root/assets/{image-generation-api-D0HNic89.js → image-generation-api-CAS8QVla.js} +1 -1
  50. package/dist/gateway/static/root/assets/imports-page-BCLBYzV1.js +1 -0
  51. package/dist/gateway/static/root/assets/index-Bys6njTY.js +710 -0
  52. package/dist/gateway/static/root/assets/index-Vsf0bj5e.css +1 -0
  53. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Cmi-2SIh.js → keyboard-shortcuts-settings-CM_kt5XN.js} +1 -1
  54. package/dist/gateway/static/root/assets/local-app-workbench-page-Dk87jtcc.js +1 -0
  55. package/dist/gateway/static/root/assets/{local-apps-page-jgaogH9Z.js → local-apps-page-BTSOChhk.js} +1 -1
  56. package/dist/gateway/static/root/assets/{local-session-drafts-DvmwiLUx.js → local-session-drafts-iGX_6O0O.js} +1 -1
  57. package/dist/gateway/static/root/assets/{locale-store-JogcXC4J.js → locale-store-B7s2Hb35.js} +1 -1
  58. package/dist/gateway/static/root/assets/logs-page-D3gVdm8S.js +2 -0
  59. package/dist/gateway/static/root/assets/management-settings-BQNYFE4J.js +1 -0
  60. package/dist/gateway/static/root/assets/{media-plugins-BRS9puQV.js → media-plugins-DYQDjCVQ.js} +1 -1
  61. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-ChweJKTv.js +1 -0
  62. package/dist/gateway/static/root/assets/{messages-RzwPiQNL.js → messages-BYq0cPbn.js} +2 -2
  63. package/dist/gateway/static/root/assets/note-detail-panel-CYKwUCte.js +5 -0
  64. package/dist/gateway/static/root/assets/note-image-lightbox-BTSZb9ZT.js +1 -0
  65. package/dist/gateway/static/root/assets/notes-page-CJ5nhWfL.js +8 -0
  66. package/dist/gateway/static/root/assets/{page-context-capture-button-D_VOB2_C.js → page-context-capture-button-DVn_dU83.js} +1 -1
  67. package/dist/gateway/static/root/assets/personal-page-DcW14lqn.js +1 -0
  68. package/dist/gateway/static/root/assets/personal-settings-panel-C16Iqe7X.js +1 -0
  69. package/dist/gateway/static/root/assets/phone-dependencies-card-D3l9uPFM.js +1 -0
  70. package/dist/gateway/static/root/assets/phone-model-picker-DOv38vBn.js +1 -0
  71. package/dist/gateway/static/root/assets/phone-settings-page-DUHp1q_9.js +1 -0
  72. package/dist/gateway/static/root/assets/plugin-details-dialog-BpfgDlVa.js +1 -0
  73. package/dist/gateway/static/root/assets/{preview-open-alternatives-DaTaSKox.js → preview-open-alternatives-RlHk76TI.js} +1 -1
  74. package/dist/gateway/static/root/assets/{product-open-page-wYlC0IVZ.js → product-open-page-Dh-HqLrE.js} +1 -1
  75. package/dist/gateway/static/root/assets/{product-preview-content-Bxur-p6S.js → product-preview-content-c3K-4DWc.js} +1 -1
  76. package/dist/gateway/static/root/assets/project-detail-page-GD8TxKad.js +7 -0
  77. package/dist/gateway/static/root/assets/projects-page-B8HAt9sD.js +1 -0
  78. package/dist/gateway/static/root/assets/{registry-api-BGqSwITi.js → registry-api-BcfoLEna.js} +1 -1
  79. package/dist/gateway/static/root/assets/remote-access-hub-D3el7lHK.js +1 -0
  80. package/dist/gateway/static/root/assets/{runtime-tools-api-CSm5SpnR.js → runtime-tools-api-DesqGCnE.js} +1 -1
  81. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DXNidliV.js → runtime-tools-settings-panel-Dbskk6RG.js} +1 -1
  82. package/dist/gateway/static/root/assets/{schema-form-DeSZ7SiO.js → schema-form-CXO1B1k4.js} +1 -1
  83. package/dist/gateway/static/root/assets/{session-api-DTxZyMta.js → session-api-DdQnMwkl.js} +1 -1
  84. package/dist/gateway/static/root/assets/{session-manager-B2HDomj4.js → session-manager-CA1NkTR_.js} +1 -1
  85. package/dist/gateway/static/root/assets/sessions-page-07liaSHH.js +1 -0
  86. package/dist/gateway/static/root/assets/settings-advanced-gate-BGjU8rfN.js +1 -0
  87. package/dist/gateway/static/root/assets/{settings-form-section-BNYQjZU4.js → settings-form-section-BQxKmbbJ.js} +1 -1
  88. package/dist/gateway/static/root/assets/{settings-page-29Ix0jSQ.js → settings-page-DvVMpKiA.js} +1 -1
  89. package/dist/gateway/static/root/assets/{setup-status-panel-COQZgKX9.js → setup-status-panel-DKo_VTx-.js} +1 -1
  90. package/dist/gateway/static/root/assets/{share-preview-page-CwMRULXc.js → share-preview-page-XgZk8Oa1.js} +1 -1
  91. package/dist/gateway/static/root/assets/shares-settings-BODFqK8k.js +1 -0
  92. package/dist/gateway/static/root/assets/{skill-api-xfB5fUAh.js → skill-api-CV-WT2hj.js} +1 -1
  93. package/dist/gateway/static/root/assets/skill-reload-api-Ce6OUk2C.js +1 -0
  94. package/dist/gateway/static/root/assets/skills-page-B7ehOIML.js +2 -0
  95. package/dist/gateway/static/root/assets/{system-settings-panel-DRJu1ehM.js → system-settings-panel-CxJkZRT4.js} +1 -1
  96. package/dist/gateway/static/root/assets/task-detail-page-CxRR0QuW.js +2 -0
  97. package/dist/gateway/static/root/assets/{task-result-evidence-C4o_Yue_.js → task-result-evidence-D-gGKNX9.js} +1 -1
  98. package/dist/gateway/static/root/assets/{tracing-settings-page-CYslh-Pu.js → tracing-settings-page-CQw3G-FB.js} +1 -1
  99. package/dist/gateway/static/root/assets/{tunnel-api-Bi_kw6bv.js → tunnel-api-B2HnCi0U.js} +1 -1
  100. package/dist/gateway/static/root/assets/usage-page-uUEkO38r.js +1 -0
  101. package/dist/gateway/static/root/assets/{use-autosave-BkB9uD93.js → use-autosave-Dc0nwUpX.js} +1 -1
  102. package/dist/gateway/static/root/assets/user-model-page-ICUutbKL.js +2 -0
  103. package/dist/gateway/static/root/assets/{voice-api-key-field-bt8_Agih.js → voice-api-key-field-BSWXZkGa.js} +1 -1
  104. package/dist/gateway/static/root/assets/{voice-config-api-D7xsWrdh.js → voice-config-api-BkUlN7GH.js} +1 -1
  105. package/dist/gateway/static/root/assets/{work-discovery-overlay-D3NG14dX.js → work-discovery-overlay-H3cdwq3l.js} +1 -1
  106. package/dist/gateway/static/root/assets/{xopc-cloud-account-card-B2OUKOQi.js → xopc-cloud-account-card-CvT_W6YS.js} +1 -1
  107. package/dist/gateway/static/root/index.html +19 -14
  108. package/dist/package.js +1 -1
  109. package/dist/src/agent/agent-manager.js +1 -1
  110. package/dist/src/agent/background-review/run-background-review.js +1 -1
  111. package/dist/src/agent/coding/workspace-revision.js +1 -1
  112. package/dist/src/agent/commands/command-registry.js +1 -1
  113. package/dist/src/agent/external-tools/cliProvider.js +2 -2
  114. package/dist/src/agent/external-tools/composio-provider.js +2 -2
  115. package/dist/src/agent/external-tools/endpoint-provider.js +2 -2
  116. package/dist/src/agent/memory/context-recovery.d.ts +1 -1
  117. package/dist/src/agent/service.js +1 -1
  118. package/dist/src/agent/skills/index.js +1 -1
  119. package/dist/src/agent/skills/installer.js +3 -3
  120. package/dist/src/agent/skills/managed-store.js +1 -1
  121. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  122. package/dist/src/agent/tool-manuals/phone.d.ts +1 -1
  123. package/dist/src/agent/tool-manuals/phone.js +5 -3
  124. package/dist/src/agent/tools/personal-read-tool.js +1 -1
  125. package/dist/src/agent/tools/personal-task-tool.d.ts +1 -1
  126. package/dist/src/agent/tools/personal-task-tool.js +23 -21
  127. package/dist/src/agent/tools/phone-use-tool.js +22 -2
  128. package/dist/src/agent/tools/xopc-use-tool.js +2 -2
  129. package/dist/src/agent-catalog/capabilities.js +2 -2
  130. package/dist/src/agent-catalog/repository.js +1 -1
  131. package/dist/src/automations/capabilities/drafts.js +1 -1
  132. package/dist/src/backup/archive.js +2 -1
  133. package/dist/src/browser/providers/browser-ext-install.js +2 -2
  134. package/dist/src/capabilities/runtime/dispatcher.js +1 -1
  135. package/dist/src/capabilities/runtime/product.js +1 -1
  136. package/dist/src/cli/commands/agents.js +1 -1
  137. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  138. package/dist/src/cli/commands/extension-pack.js +1 -1
  139. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  140. package/dist/src/cli/commands/models.js +1 -1
  141. package/dist/src/cli/commands/onboard/model.js +1 -1
  142. package/dist/src/cli/commands/onboard.js +1 -1
  143. package/dist/src/cli/utils/init-workspace-core.js +1 -1
  144. package/dist/src/config/agent-profile.js +1 -1
  145. package/dist/src/connectors/connected-source-understanding.js +1 -1
  146. package/dist/src/connectors/learning-coordinator.js +1 -1
  147. package/dist/src/execution-environments/local-worktree-manager.js +1 -1
  148. package/dist/src/extensions/agent-plugins/skill-catalog.js +1 -1
  149. package/dist/src/extensions/agent-plugins/validation.js +1 -1
  150. package/dist/src/extensions/install.js +1 -1
  151. package/dist/src/gateway/agents-admin.js +2 -2
  152. package/dist/src/gateway/global-defaults-admin.js +1 -1
  153. package/dist/src/gateway/hono/routes/agents.js +1 -1
  154. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  155. package/dist/src/gateway/hono/routes/connectors.js +7 -7
  156. package/dist/src/gateway/hono/routes/home.js +1 -1
  157. package/dist/src/gateway/hono/routes/notes.js +2 -2
  158. package/dist/src/gateway/hono/routes/phone.js +33 -4
  159. package/dist/src/gateway/hono/routes/projects.js +2 -2
  160. package/dist/src/gateway/hono/routes/public-gateway.js +1 -1
  161. package/dist/src/gateway/hono/routes/session-input-handler.js +1 -1
  162. package/dist/src/gateway/hono/routes/tasks.js +1 -1
  163. package/dist/src/gateway/image-generation-setup.js +1 -1
  164. package/dist/src/gateway/service/session-input-coordinator.js +1 -1
  165. package/dist/src/gateway/service.js +7 -7
  166. package/dist/src/gateway/xopc-cloud-capability-setup.js +1 -1
  167. package/dist/src/imports/importService.js +1 -1
  168. package/dist/src/infra/unhandled-rejections.js +2 -2
  169. package/dist/src/infra/update-restart.js +1 -1
  170. package/dist/src/local-apps/service.js +1 -1
  171. package/dist/src/personal-agent/prompts.js +1 -1
  172. package/dist/src/personal-agent/service.js +1 -1
  173. package/dist/src/phone/adb-driver.d.ts +9 -2
  174. package/dist/src/phone/adb-driver.js +113 -37
  175. package/dist/src/phone/app-access.d.ts +10 -0
  176. package/dist/src/phone/app-access.js +21 -0
  177. package/dist/src/phone/applications.d.ts +4 -0
  178. package/dist/src/phone/applications.js +49 -0
  179. package/dist/src/phone/cloud-adb.d.ts +1 -0
  180. package/dist/src/phone/cloud-adb.js +60 -2
  181. package/dist/src/phone/cloud-phones.js +2 -1
  182. package/dist/src/phone/cloud-scrcpy.d.ts +3 -1
  183. package/dist/src/phone/cloud-scrcpy.js +19 -4
  184. package/dist/src/phone/contracts.d.ts +54 -1
  185. package/dist/src/phone/contracts.js +18 -4
  186. package/dist/src/phone/model-adapter.d.ts +90 -2
  187. package/dist/src/phone/model-adapter.js +87 -19
  188. package/dist/src/phone/reliability.d.ts +7 -0
  189. package/dist/src/phone/reliability.js +66 -0
  190. package/dist/src/phone/repository.js +4 -3
  191. package/dist/src/phone/runtime.d.ts +14 -1
  192. package/dist/src/phone/runtime.js +215 -19
  193. package/dist/src/phone/scrcpy-assets.d.ts +4 -0
  194. package/dist/src/phone/scrcpy-assets.js +16 -0
  195. package/dist/src/phone/scrcpy-text.d.ts +11 -0
  196. package/dist/src/phone/scrcpy-text.js +72 -0
  197. package/dist/src/phone/scrcpy.d.ts +6 -5
  198. package/dist/src/phone/scrcpy.js +51 -14
  199. package/dist/src/runtime-tools/archive.js +1 -1
  200. package/dist/src/storage/sqlite/connection.js +2 -2
  201. package/dist/src/storage/sqlite/offline-access.js +13 -7
  202. package/dist/src/storage/sqlite/schema-errors.d.ts +12 -0
  203. package/dist/src/storage/sqlite/schema-errors.js +21 -3
  204. package/dist/src/storage/sqlite/schema-upgrade.d.ts +4 -0
  205. package/dist/src/storage/sqlite/schema-upgrade.js +78 -0
  206. package/dist/src/storage/sqlite/schema.d.ts +2 -1
  207. package/dist/src/storage/sqlite/schema.js +9 -3
  208. package/dist/src/storage/sqlite/upgrades/203_remove_retired_proactive_contracts.sql +70 -0
  209. package/dist/src/storage/sqlite/upgrades/204_home_intelligence.sql +78 -0
  210. package/dist/src/storage/sqlite/upgrades/205_agent_catalog.sql +66 -0
  211. package/dist/src/storage/sqlite/upgrades/206_home_advice_strategy_version.sql +2 -0
  212. package/dist/src/storage/sqlite/upgrades/207_user_model_policy_contract.sql +121 -0
  213. package/dist/src/storage/sqlite/upgrades/208_automation_management.sql +2 -0
  214. package/dist/src/storage/sqlite/upgrades/209_automation_event_hub.sql +48 -0
  215. package/dist/src/storage/sqlite/upgrades/210_remove_legacy_automation_timeout.sql +14 -0
  216. package/dist/src/storage/sqlite/upgrades/211_automation_result_deliveries.sql +16 -0
  217. package/dist/src/storage/sqlite/upgrades/212_automation_delivery_policy.sql +30 -0
  218. package/dist/src/storage/sqlite/upgrades/213_automation_reliability_contract.sql +188 -0
  219. package/dist/src/storage/sqlite/upgrades/214_ai_usage_ledger.sql +41 -0
  220. package/dist/src/storage/sqlite/upgrades/215_scene_execution_adapters.sql +6 -0
  221. package/dist/src/storage/sqlite/upgrades/216_remove_scene_model_usage.sql +1 -0
  222. package/dist/src/storage/sqlite/upgrades/217_remove_legacy_memory_tables.sql +4 -0
  223. package/dist/src/storage/sqlite/upgrades/218_core_relation_indexes.sql +26 -0
  224. package/dist/src/storage/sqlite/upgrades/219_json_contracts.sql +123 -0
  225. package/dist/src/storage/sqlite/upgrades/220_normalize_timestamps.sql +339 -0
  226. package/dist/src/storage/sqlite/upgrades/221_ai_usage_retention_index.sql +2 -0
  227. package/dist/src/storage/sqlite/upgrades/222_session_config_authority.sql +13 -0
  228. package/dist/src/storage/sqlite/upgrades/223_session_creation_receipts.sql +59 -0
  229. package/dist/src/storage/sqlite/upgrades/224_task_origin_links.sql +8 -0
  230. package/dist/src/storage/sqlite/upgrades/225_task_collaboration.sql +32 -0
  231. package/dist/src/storage/sqlite/upgrades/226_task_main_updates.sql +9 -0
  232. package/dist/src/storage/sqlite/upgrades/227_task_main_update_decisions.sql +8 -0
  233. package/dist/src/storage/sqlite/upgrades/228_task_main_agent_links.sql +14 -0
  234. package/dist/src/storage/sqlite/upgrades/229_task_criterion_reviews.sql +12 -0
  235. package/dist/src/storage/sqlite/upgrades/230_personal_agent_onboarding.sql +8 -0
  236. package/dist/src/storage/sqlite/upgrades/231_task_result_deliveries.sql +32 -0
  237. package/dist/src/storage/sqlite/upgrades/232_personal_requests.sql +28 -0
  238. package/dist/src/storage/sqlite/upgrades/233_observability_usage_links.sql +3 -0
  239. package/dist/src/storage/sqlite/upgrades/234_task_result_reply.sql +14 -0
  240. package/dist/src/storage/sqlite/upgrades/235_personal_conversation_priority.sql +3 -0
  241. package/dist/src/storage/sqlite/upgrades/236_personal_proactivity.sql +53 -0
  242. package/dist/src/storage/sqlite/upgrades/237_personal_strategy_versions.sql +18 -0
  243. package/dist/src/storage/sqlite/upgrades/238_browser_record_replay.sql +45 -0
  244. package/dist/src/storage/sqlite/upgrades/239_clarification_notifications.sql +26 -0
  245. package/dist/src/storage/sqlite/upgrades/240_device_settings.sql +5 -0
  246. package/dist/src/storage/sqlite/upgrades/241_device_grant_audit.sql +13 -0
  247. package/dist/src/storage/sqlite/upgrades/242_cloud_usage_receipts.sql +5 -0
  248. package/dist/src/storage/sqlite/upgrades/243_voice_opening_greetings.sql +12 -0
  249. package/dist/src/storage/sqlite/upgrades/244_device_request_proofs.sql +8 -0
  250. package/dist/src/storage/sqlite/upgrades/245_memory_learning.sql +125 -0
  251. package/dist/src/storage/sqlite/upgrades/246_personal_session_thinking.sql +22 -0
  252. package/dist/src/storage/sqlite/upgrades/247_agent_selection_records.sql +32 -0
  253. package/dist/src/storage/sqlite/upgrades/248_next_turn_suggestions.sql +20 -0
  254. package/dist/src/storage/sqlite/upgrades/249_task_allocations.sql +17 -0
  255. package/dist/src/storage/sqlite/upgrades/250_research_checkpoints.sql +15 -0
  256. package/dist/src/storage/sqlite/upgrades/251_personal_result_reads.sql +40 -0
  257. package/dist/src/storage/sqlite/upgrades/252_task_work_revisions.sql +15 -0
  258. package/dist/src/storage/sqlite/upgrades/253_discussion_processing_recovery.sql +20 -0
  259. package/dist/src/storage/sqlite/upgrades/254_discussion_annotations.sql +17 -0
  260. package/dist/src/storage/sqlite/upgrades/255_discussion_chapter_progress.sql +10 -0
  261. package/dist/src/storage/sqlite/upgrades/256_task_main_update_retries.sql +21 -0
  262. package/dist/src/storage/sqlite/upgrades/257_session_composer_history.sql +11 -0
  263. package/dist/src/storage/sqlite/upgrades/258_task_phase_changes.sql +20 -0
  264. package/dist/src/storage/sqlite/upgrades/259_personal_default_style.sql +10 -0
  265. package/dist/src/storage/sqlite/windows-database-owners.js +33 -28
  266. package/dist/src/tasks/capabilities/read.js +2 -2
  267. package/dist/src/tasks/capabilities/relations.d.ts +2 -2
  268. package/dist/src/tasks/capabilities/write.js +1 -1
  269. package/dist/src/tui/backends/embedded-backend.js +1 -1
  270. package/dist/src/tui/clipboard-image.js +1 -1
  271. package/dist/src/tui/tui.js +1 -1
  272. package/dist/src/user-context/sources/index.js +1 -1
  273. package/dist/src/utils/index.js +1 -1
  274. package/dist/src/utils/logger/index.js +1 -1
  275. package/dist/src/utils/logger.js +1 -1
  276. package/dist/src/voice/realtime/persona-context.js +1 -1
  277. package/dist/src/work-discovery/probe.js +1 -1
  278. package/dist/src/work-discovery/service.js +2 -2
  279. package/package.json +6 -6
  280. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-BFmRRD_4.js +0 -1
  281. package/dist/gateway/static/root/assets/agents-Clwskw2J.js +0 -77
  282. package/dist/gateway/static/root/assets/apps-page-DJbKg3va.js +0 -1
  283. package/dist/gateway/static/root/assets/automations-workspace-D_X0_1X3.js +0 -8
  284. package/dist/gateway/static/root/assets/browser-automations-page-BI-ep6uM.js +0 -3
  285. package/dist/gateway/static/root/assets/browser-settings-page-BTyQ4-vN.js +0 -3
  286. package/dist/gateway/static/root/assets/capabilities-page-GNS95mG9.js +0 -2
  287. package/dist/gateway/static/root/assets/capabilities-settings-panel-DBiv1HN-.js +0 -8
  288. package/dist/gateway/static/root/assets/channels-DWlrUKBr.js +0 -1
  289. package/dist/gateway/static/root/assets/connectors-page-BcpUvrtl.js +0 -1
  290. package/dist/gateway/static/root/assets/dependency-picker-C4jddxFl.js +0 -1
  291. package/dist/gateway/static/root/assets/desktop-pet-BCbiUNQG.js +0 -1
  292. package/dist/gateway/static/root/assets/desktop-pet-settings-CkKU0JMY.js +0 -1
  293. package/dist/gateway/static/root/assets/device-pairing-wizard--E2GQoY1.js +0 -1
  294. package/dist/gateway/static/root/assets/gateway-realtime-store-CoIgC1Al.js +0 -1
  295. package/dist/gateway/static/root/assets/gateway-settings-CR4CsfNN.js +0 -1
  296. package/dist/gateway/static/root/assets/home-page-B9w3a6r2.js +0 -2
  297. package/dist/gateway/static/root/assets/imports-page-bxTINe-2.js +0 -1
  298. package/dist/gateway/static/root/assets/index-D1oivYvN.js +0 -710
  299. package/dist/gateway/static/root/assets/index-D_7q1UkI.css +0 -1
  300. package/dist/gateway/static/root/assets/local-app-workbench-page-NGCmXpa_.js +0 -1
  301. package/dist/gateway/static/root/assets/logs-page-drrr-c2W.js +0 -2
  302. package/dist/gateway/static/root/assets/management-settings-Dc1opXVr.js +0 -1
  303. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-B--edI1I.js +0 -1
  304. package/dist/gateway/static/root/assets/note-detail-panel-C3s4-ytB.js +0 -5
  305. package/dist/gateway/static/root/assets/note-image-lightbox-D7q5-KuS.js +0 -1
  306. package/dist/gateway/static/root/assets/notes-page-qVxoErUS.js +0 -8
  307. package/dist/gateway/static/root/assets/personal-page-B-T8NzQo.js +0 -1
  308. package/dist/gateway/static/root/assets/personal-settings-panel-BKA4fbYH.js +0 -1
  309. package/dist/gateway/static/root/assets/phone-dependencies-card-DhEsfEUN.js +0 -1
  310. package/dist/gateway/static/root/assets/phone-model-picker-J4mFDk21.js +0 -1
  311. package/dist/gateway/static/root/assets/phone-settings-page-BLflz8CQ.js +0 -1
  312. package/dist/gateway/static/root/assets/plugin-details-dialog-1BwvmKZi.js +0 -1
  313. package/dist/gateway/static/root/assets/project-detail-page-DwowzxzD.js +0 -7
  314. package/dist/gateway/static/root/assets/projects-page-Bwvn4i_e.js +0 -1
  315. package/dist/gateway/static/root/assets/remote-access-hub-DrHisOUe.js +0 -1
  316. package/dist/gateway/static/root/assets/sessions-page-seP7t0kv.js +0 -1
  317. package/dist/gateway/static/root/assets/settings-advanced-gate-VqBu7gHu.js +0 -1
  318. package/dist/gateway/static/root/assets/shares-settings-BWaykw9E.js +0 -1
  319. package/dist/gateway/static/root/assets/skill-reload-api-Bq-3_MCt.js +0 -1
  320. package/dist/gateway/static/root/assets/skills-page-C3LfWsAZ.js +0 -2
  321. package/dist/gateway/static/root/assets/task-detail-page-XCJFP-Aa.js +0 -2
  322. package/dist/gateway/static/root/assets/usage-page-CTTmklCs.js +0 -1
  323. package/dist/gateway/static/root/assets/user-model-page-BDqguzRE.js +0 -2
@@ -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 };
@@ -1,11 +1,12 @@
1
1
  import { PhoneDeviceSchema, PhoneError, init_contracts } from "./contracts.js";
2
+ import { normalizePhoneAppAccess } from "./app-access.js";
2
3
  import { DurableState } from "../storage/sqlite/durable-state.js";
3
4
  import { randomUUID } from "node:crypto";
4
5
  //#region src/phone/repository.ts
5
6
  init_contracts();
6
7
  function parseDevice(value) {
7
8
  const { agentIds: _removed, ...device } = value;
8
- return PhoneDeviceSchema.parse(device);
9
+ return normalizePhoneAppAccess(PhoneDeviceSchema.parse(device));
9
10
  }
10
11
  /** Small settings and latest metadata-only reports; pixels and input text are never stored. */
11
12
  var PhoneRepository = class {
@@ -24,12 +25,12 @@ var PhoneRepository = class {
24
25
  save(input, id) {
25
26
  const previous = id ? this.get(id) : void 0;
26
27
  if (this.list().some((device) => device.serial === input.serial && device.id !== id)) throw new PhoneError("PHONE_DEVICE_DUPLICATE", "This ADB device is already registered");
27
- const next = PhoneDeviceSchema.parse({
28
+ const next = normalizePhoneAppAccess(PhoneDeviceSchema.parse({
28
29
  ...input,
29
30
  id: id ?? randomUUID(),
30
31
  createdAt: previous?.createdAt ?? Date.now(),
31
32
  revision: (previous?.revision ?? 0) + 1
32
- });
33
+ }));
33
34
  this.devices.set(next.id, next);
34
35
  return next;
35
36
  }
@@ -5,7 +5,7 @@ import { CloudPhones } from './cloud-phones.js';
5
5
  import { type PhoneDriver } from './adb-driver.js';
6
6
  import { createPhoneModel } from './model-adapter.js';
7
7
  import { PhoneRepository } from './repository.js';
8
- import { type PhoneAction, type PhoneDevice, type PhoneInput, type PhoneObservation, type PhoneReport } from './contracts.js';
8
+ import { type PhoneAction, type PhoneDevice, type PhoneInput, type PhoneApplication, type PhoneObservation, type PhoneReport } from './contracts.js';
9
9
  type ExecutionContext = {
10
10
  config: Config;
11
11
  agentId: string;
@@ -26,6 +26,17 @@ export declare class PhoneRuntime {
26
26
  constructor(repository?: PhoneRepository, driverFactory?: (path: string) => PhoneDriver, modelFactory?: typeof createPhoneModel, interactiveFactory?: (adb: string, serial: string, failed: () => void) => PhoneInteractiveProvider);
27
27
  driverFor(device: PhoneDevice, config: Config): Promise<PhoneDriver>;
28
28
  discover(config: Config, signal?: AbortSignal): Promise<import("./contracts.js").DiscoveredPhone[]>;
29
+ applications(input: {
30
+ deviceId?: string;
31
+ serial?: string;
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
+ }>;
29
40
  preview(id: string, config: Config, signal: AbortSignal): Promise<PhoneObservation>;
30
41
  previewSession(owner: string, context: ExecutionContext, signal: AbortSignal): Promise<PhoneObservation>;
31
42
  reports(): PhoneReport[];
@@ -50,6 +61,8 @@ export declare class PhoneRuntime {
50
61
  report: PhoneReport;
51
62
  ownControl: boolean;
52
63
  allowedApps: string[];
64
+ appAccess: "all" | "selected";
65
+ textInput: "manual" | "auto";
53
66
  frame: {
54
67
  seq: number;
55
68
  capturedAt: number;
@@ -2,6 +2,7 @@ import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
3
  import { MOCK_PHONE_MODEL, PhoneError, PhoneInputSchema, init_contracts } from "./contracts.js";
4
4
  import { resolveEffectiveAgentConfigForAgent } from "../config/agent-profile.js";
5
+ import { phoneAllowsApp, phoneAppAccess } from "./app-access.js";
5
6
  import { PhoneFrameCache, PhoneOperationBudget } from "./live-state.js";
6
7
  import { PhoneInteractiveSession } from "./interactive-session.js";
7
8
  import { AdbPhoneDriver, resolveAdbPath } from "./adb-driver.js";
@@ -9,6 +10,7 @@ import { LocalScrcpyProvider } from "./scrcpy.js";
9
10
  import { CloudPhones } from "./cloud-phones.js";
10
11
  import { createPhoneModel, isAboutScreen } from "./model-adapter.js";
11
12
  import { PhoneRepository } from "./repository.js";
13
+ import { canDispatchPhoneAction, observationSignature, phoneCompletionSatisfied } from "./reliability.js";
12
14
  import { createHash, randomUUID } from "node:crypto";
13
15
  //#region src/phone/runtime.ts
14
16
  init_logger();
@@ -40,13 +42,40 @@ var PhoneRuntime = class {
40
42
  async discover(config, signal) {
41
43
  return this.driverFactory(resolveAdbPath(config.phone.adbPath)).discover(signal);
42
44
  }
45
+ async applications(input, config, signal) {
46
+ let driver;
47
+ let serial;
48
+ if (input.deviceId) {
49
+ const device = this.repository.get(input.deviceId);
50
+ if (!device.enabled) throw new PhoneError("PHONE_DEVICE_DENIED", "Phone access is disabled", 403);
51
+ driver = await this.driverFor(device, config);
52
+ serial = device.serial;
53
+ } else {
54
+ const found = (await this.discover(config, signal)).find((device) => device.serial === input.serial && device.state === "device");
55
+ if (!found) throw new PhoneError("PHONE_DEVICE_OFFLINE", "Choose a connected and authorized phone");
56
+ driver = this.driverFactory(resolveAdbPath(config.phone.adbPath));
57
+ serial = found.serial;
58
+ }
59
+ if (!driver.applications) throw new PhoneError("PHONE_APPS_UNAVAILABLE", "The phone provider cannot list applications");
60
+ return driver.applications(serial, signal);
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
+ }
43
72
  async preview(id, config, signal) {
44
73
  const device = this.repository.get(id);
45
74
  if (!device.enabled) throw new PhoneError("PHONE_DEVICE_DENIED", "Phone access is disabled", 403);
46
75
  const lease = this.repository.acquire(device.serial);
47
76
  try {
48
77
  const observation = await (await this.driverFor(device, config)).observe(device.serial, signal);
49
- if (!device.allowedApps.includes(observation.package)) {
78
+ if (!phoneAllowsApp(device, observation.package)) {
50
79
  observation.frame.fill(0);
51
80
  throw new PhoneError("PHONE_APP_DENIED", "Open an allowed app before previewing the screen");
52
81
  }
@@ -91,7 +120,7 @@ var PhoneRuntime = class {
91
120
  this.repository.renew(session.device.serial, session.lease);
92
121
  }
93
122
  allowedScreen(session, observation) {
94
- if (!session.device.allowedApps.includes(observation.package)) throw new PhoneError("PHONE_APP_DENIED", "The foreground application is outside this phone session allowlist. Open an allowed app or reopen with appPackage.");
123
+ if (!phoneAllowsApp(session.device, observation.package)) throw new PhoneError("PHONE_APP_DENIED", "The foreground application is outside this phone session allowlist. Open an allowed app or reopen with appPackage.");
95
124
  }
96
125
  async execute(owner, raw, context, signal = new AbortController().signal) {
97
126
  const input = PhoneInputSchema.parse(raw);
@@ -132,6 +161,12 @@ var PhoneRuntime = class {
132
161
  try {
133
162
  const adapter = await this.modelFactory(modelRef, context.config, context.agentId);
134
163
  signal.throwIfAborted();
164
+ let applications = [];
165
+ if (input.mode === "control" && modelRef !== "phone-mock/settings-about" && driver.applications) try {
166
+ applications = await driver.applications(device.serial, signal);
167
+ } catch {
168
+ signal.throwIfAborted();
169
+ }
135
170
  const now = Date.now();
136
171
  const session = {
137
172
  device,
@@ -143,6 +178,10 @@ var PhoneRuntime = class {
143
178
  modelRef,
144
179
  adbPath,
145
180
  context,
181
+ applications,
182
+ trajectory: [],
183
+ trajectoryCount: 0,
184
+ trajectorySummary: [],
146
185
  frames: new PhoneFrameCache(),
147
186
  io: Promise.resolve(),
148
187
  ioBusy: false,
@@ -169,12 +208,18 @@ var PhoneRuntime = class {
169
208
  updatedAt: now,
170
209
  verified: false,
171
210
  verificationLevel: "none",
172
- steps: []
211
+ steps: [],
212
+ diagnostics: {
213
+ stalePredictions: 0,
214
+ protocolRepairs: 0,
215
+ unchangedActions: 0,
216
+ completionCorrections: 0
217
+ }
173
218
  }
174
219
  };
175
220
  this.sessions.set(owner, session);
176
221
  if (input.appPackage) {
177
- if (input.mode !== "control" || !device.allowedApps.includes(input.appPackage)) throw new PhoneError("PHONE_APP_DENIED", "Launching requires control mode and an allowed application");
222
+ if (input.mode !== "control" || !phoneAllowsApp(device, input.appPackage)) throw new PhoneError("PHONE_APP_DENIED", "Launching requires control mode and an allowed application");
178
223
  const epoch = session.report.controlEpoch;
179
224
  const observation = await this.observe(session, signal);
180
225
  try {
@@ -234,6 +279,9 @@ var PhoneRuntime = class {
234
279
  const startedAt = Date.now();
235
280
  session.report.errorCode = void 0;
236
281
  session.report.verified = false;
282
+ session.report.verificationLevel = "none";
283
+ session.report.stopReason = void 0;
284
+ session.report.message = void 0;
237
285
  try {
238
286
  await this.waitForAgent(session, signal);
239
287
  session.report.status = "running";
@@ -257,7 +305,20 @@ var PhoneRuntime = class {
257
305
  if (session.report.mode !== "control") throw new PhoneError("PHONE_OBSERVE_ONLY", "Reopen the phone in control mode to execute actions");
258
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");
259
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;
260
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();
261
322
  while (true) {
262
323
  if (await this.waitForAgent(session, signal)) {
263
324
  observation.frame.fill(0);
@@ -267,13 +328,36 @@ var PhoneRuntime = class {
267
328
  this.guard(session, context);
268
329
  this.allowedScreen(session, observation);
269
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;
270
339
  this.recordObservation(session, observation);
271
- if (session.report.mock ? isAboutScreen(observation) : Boolean(input.expectText && session.report.facts?.some((text) => text.includes(input.expectText)))) {
272
- session.report.verified = true;
273
- session.report.verificationLevel = "ui";
274
- session.report.status = "completed";
275
- session.report.stopReason = actions ? "condition_satisfied" : "condition_present";
276
- 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
+ }
277
361
  }
278
362
  if (actions >= maxActions) {
279
363
  session.report.status = "ready";
@@ -290,8 +374,22 @@ var PhoneRuntime = class {
290
374
  decision = await session.adapter.predict({
291
375
  goal: input.goal,
292
376
  observation,
377
+ completion,
293
378
  allowedApps: session.device.allowedApps,
379
+ appAccess: phoneAppAccess(session.device),
380
+ applications: session.applications.filter((app) => phoneAllowsApp(session.device, app.packageName)),
294
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,
295
393
  scenario: input.scenario
296
394
  }, AbortSignal.any([signal, session.prediction.signal]));
297
395
  } catch (error) {
@@ -301,6 +399,14 @@ var PhoneRuntime = class {
301
399
  observation = await this.observe(session, signal);
302
400
  continue;
303
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
+ }
304
410
  throw error;
305
411
  } finally {
306
412
  session.prediction = void 0;
@@ -313,9 +419,21 @@ var PhoneRuntime = class {
313
419
  observation = await this.observe(session, signal);
314
420
  continue;
315
421
  }
422
+ protocolFeedback = void 0;
316
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
+ }
317
435
  session.report.status = decision.kind === "needsUser" ? "needs_user" : "completed";
318
- session.report.message = decision.kind === "needsUser" ? decision.reason : void 0;
436
+ session.report.message = decision.kind === "needsUser" ? decision.reason : decision.summary;
319
437
  session.report.stopReason = decision.kind === "needsUser" ? "user_takeover" : "model_finished_unverified";
320
438
  if (decision.kind === "needsUser" && !session.report.mock) {
321
439
  session.report.controlOwner = "none";
@@ -332,21 +450,59 @@ var PhoneRuntime = class {
332
450
  const fresh = await this.observe(session, signal);
333
451
  try {
334
452
  this.allowedScreen(session, fresh);
335
- 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.";
336
456
  observation.frame.fill(0);
337
457
  observation = fresh;
338
458
  continue;
339
459
  }
340
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);
341
474
  if (await this.dispatch(session, decision.action, fresh, context, signal, epoch)) actions++;
342
475
  } finally {
343
476
  if (observation !== fresh) fresh.frame.fill(0);
344
477
  }
478
+ const beforeSignature = observationSignature(observation);
345
479
  observation.frame.fill(0);
346
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
+ }
347
490
  }
348
491
  } catch (error) {
349
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
+ }
350
506
  if (failure instanceof PhoneError && failure.code === "PHONE_OPERATION_TIMEOUT" && !terminal.has(session.report.status) && session.report.status !== "paused") {
351
507
  session.report.status = "paused";
352
508
  session.report.controlOwner = "none";
@@ -400,7 +556,8 @@ var PhoneRuntime = class {
400
556
  status: session.report.status,
401
557
  stopReason: session.report.stopReason,
402
558
  actions: session.report.actions,
403
- modelRequests: session.report.modelRequests
559
+ modelRequests: session.report.modelRequests,
560
+ diagnostics: session.report.diagnostics
404
561
  }, "Phone goal finished");
405
562
  }
406
563
  }
@@ -416,8 +573,9 @@ var PhoneRuntime = class {
416
573
  if (session.human && Date.now() > (session.humanExpiresAt ?? 0)) throw new PhoneError("PHONE_CONTROL_REQUIRED", "Manual control expired", 409);
417
574
  if (!session.human && session.report.actions - (session.report.humanActions ?? 0) >= context.config.phone.maxActionsPerSession) throw new PhoneError("PHONE_ACTION_LIMIT", "Phone action limit reached");
418
575
  if (session.human && (session.report.humanActions ?? 0) >= 1200) throw new PhoneError("PHONE_ACTION_LIMIT", "Manual input limit reached; reopen the session");
419
- if (action.type === "launch" && !session.device.allowedApps.includes(action.package)) throw new PhoneError("PHONE_APP_DENIED", "Application is not authorized");
420
- if (action.type === "home") throw new PhoneError("PHONE_APP_DENIED", "Home leaves the app allowlist");
576
+ if (action.type === "launch" && !phoneAllowsApp(session.device, action.package)) throw new PhoneError("PHONE_APP_DENIED", "Application is not authorized");
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);
421
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);
422
580
  const step = {
423
581
  action: action.type,
@@ -433,14 +591,48 @@ var PhoneRuntime = class {
433
591
  session.report.steps = session.report.steps.slice(-200);
434
592
  }
435
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
+ }
436
612
  try {
437
613
  await session.driver.execute(session.device.serial, action, observation, signal, session.human ? {
438
614
  settleMs: 0,
439
615
  ...resetTask ? { resetTask: true } : {}
440
616
  } : void 0);
441
617
  step.dispatch = "completed";
618
+ entry.dispatch = "completed";
442
619
  } catch (error) {
443
- 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
+ }
444
636
  session.frames.clear();
445
637
  throw error;
446
638
  }
@@ -470,6 +662,8 @@ var PhoneRuntime = class {
470
662
  if (op !== "pause") {
471
663
  session.report.facts = void 0;
472
664
  session.report.message = void 0;
665
+ session.trajectory = [];
666
+ session.trajectorySummary = [];
473
667
  session.frames.clear();
474
668
  session.adapter = { predict: async () => {
475
669
  throw new PhoneError("PHONE_OPEN_REQUIRED", "Reopen the phone session");
@@ -497,7 +691,7 @@ var PhoneRuntime = class {
497
691
  return this.io(session, async () => {
498
692
  signal.throwIfAborted();
499
693
  const observation = await (forText && session.driver.observeForText ? session.driver.observeForText : session.driver.observe).call(session.driver, session.device.serial, signal);
500
- if (session.device.allowedApps.includes(observation.package)) {
694
+ if (phoneAllowsApp(session.device, observation.package)) {
501
695
  if (Date.now() - session.viewedAt < 3e4 && !terminal.has(session.report.status)) session.frames.set(observation);
502
696
  } else session.frames.clear();
503
697
  return observation;
@@ -597,6 +791,8 @@ var PhoneRuntime = class {
597
791
  report: structuredClone(session.report),
598
792
  ownControl: Boolean(session.human === client),
599
793
  allowedApps: [...session.device.allowedApps],
794
+ appAccess: phoneAppAccess(session.device),
795
+ textInput: session.device.textInput ?? "auto",
600
796
  frame: session.frames.get() ? {
601
797
  seq: session.frames.get().seq,
602
798
  capturedAt: session.frames.get().capturedAt
@@ -631,7 +827,7 @@ var PhoneRuntime = class {
631
827
  if (terminal.has(session.report.status)) throw new PhoneError("PHONE_VIDEO_ENDED", "Phone session ended", 409);
632
828
  this.guard(session, session.context);
633
829
  const app = await provider.foreground(AbortSignal.timeout(2e3));
634
- if (!session.device.allowedApps.includes(app)) throw new PhoneError("PHONE_APP_DENIED", "Current page is outside the authorized applications", 403);
830
+ if (!phoneAllowsApp(session.device, app)) throw new PhoneError("PHONE_APP_DENIED", "Current page is outside the authorized applications", 403);
635
831
  session.viewedAt = Date.now();
636
832
  }, (client, epoch) => {
637
833
  if (terminal.has(session.report.status) || session.report.handoff !== "idle" || session.report.controlOwner !== "human" || session.human !== client || session.report.controlEpoch !== epoch || Date.now() > (session.humanExpiresAt ?? 0)) throw new PhoneError("PHONE_CONTROL_CHANGED", "Manual control is no longer available", 409);
@@ -811,7 +1007,7 @@ var PhoneRuntime = class {
811
1007
  for (const wake of session.wake) wake();
812
1008
  } else if (input.op === "recover") {
813
1009
  if (session.human !== client || Date.now() > (session.humanExpiresAt ?? 0)) throw new PhoneError("PHONE_CONTROL_REQUIRED", "Take over the phone first", 409);
814
- if (input.action?.type !== "launch" || !session.device.allowedApps.includes(input.action.package)) throw new PhoneError("PHONE_APP_DENIED", "Choose an authorized recovery application", 403);
1010
+ if (input.action?.type !== "launch" || !phoneAllowsApp(session.device, input.action.package)) throw new PhoneError("PHONE_APP_DENIED", "Choose an authorized recovery application", 403);
815
1011
  const observation = await this.io(session, () => session.driver.capture ? session.driver.capture(session.device.serial, session.controller.signal) : session.driver.observe(session.device.serial, session.controller.signal));
816
1012
  try {
817
1013
  if (!await this.dispatch(session, input.action, observation, context, session.controller.signal, input.epoch, true)) throw new PhoneError("PHONE_CONTROL_CHANGED", "Control changed before recovery", 409);
@@ -0,0 +1,4 @@
1
+ import { SCRCPY_SHA256, SCRCPY_VERSION } from '../runtime-tools/phone-dependency-catalog.js';
2
+ export { SCRCPY_SHA256, SCRCPY_VERSION };
3
+ export declare const scrcpyServerPath: (stateDir?: string) => string;
4
+ export declare function readScrcpyServer(): Promise<Buffer>;
@@ -0,0 +1,16 @@
1
+ import { PhoneError, init_contracts } from "./contracts.js";
2
+ import { SCRCPY_SHA256, SCRCPY_VERSION, managedScrcpyServerPath } from "../runtime-tools/phone-dependency-catalog.js";
3
+ import { createHash } from "node:crypto";
4
+ import { readFile } from "node:fs/promises";
5
+ //#region src/phone/scrcpy-assets.ts
6
+ init_contracts();
7
+ const scrcpyServerPath = managedScrcpyServerPath;
8
+ async function readScrcpyServer() {
9
+ const binary = await readFile(scrcpyServerPath()).catch(() => {
10
+ throw new PhoneError("PHONE_VIDEO_SETUP_REQUIRED", "Install scrcpy server in Settings → Phone Use → Dependencies");
11
+ });
12
+ if (createHash("sha256").update(binary).digest("hex") !== "7e70323ba7f259649dd4acce97ac4fefbae8102b2c6d91e2e7be613fd5354be0") throw new PhoneError("PHONE_VIDEO_HASH_MISMATCH", "Phone video server checksum mismatch");
13
+ return binary;
14
+ }
15
+ //#endregion
16
+ export { SCRCPY_SHA256, SCRCPY_VERSION, readScrcpyServer, scrcpyServerPath };
@@ -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 };