@xopcai/xopc 0.0.415 → 0.0.416

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/dist/browser-ext/_locales/en/messages.json +4 -1
  2. package/dist/browser-ext/_locales/zh_CN/messages.json +4 -1
  3. package/dist/browser-ext/dist/assets/{page-context-MuzjMBlJ.js → page-context-CVvmTWZb.js} +33 -9
  4. package/dist/browser-ext/dist/background.js +39 -10
  5. package/dist/browser-ext/dist/sidepanel.html +1 -1
  6. package/dist/browser-ext/dist/sidepanel.js +22 -3
  7. package/dist/browser-ext/manifest.json +1 -1
  8. package/dist/extensions/telegram/xopc.extension.json +1 -1
  9. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CJFwitU2.js → agent-avatar-dicebear-CufU7cqe.js} +1 -1
  10. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DHDAcss8.js +1 -0
  11. package/dist/gateway/static/root/assets/agents-BV3dAoa0.js +77 -0
  12. package/dist/gateway/static/root/assets/{app-management-settings-panel-DgwqNi5i.js → app-management-settings-panel-D4-n1uBg.js} +1 -1
  13. package/dist/gateway/static/root/assets/{appearance-settings-C0wJfFyz.js → appearance-settings-Cgn4eNsX.js} +1 -1
  14. package/dist/gateway/static/root/assets/apps-page-BOYzD7Zz.js +1 -0
  15. package/dist/gateway/static/root/assets/{archive-plugin-Ch7wolHE.js → archive-plugin-B4btCsiV.js} +1 -1
  16. package/dist/gateway/static/root/assets/{automations-page-DK7AU79v.js → automations-page-yb1EL_3N.js} +1 -1
  17. package/dist/gateway/static/root/assets/automations-workspace-J7LJ4RuV.js +8 -0
  18. package/dist/gateway/static/root/assets/{binary-plugins-B4HcHYsC.js → binary-plugins-DQT9e258.js} +1 -1
  19. package/dist/gateway/static/root/assets/{block-editor-BQAWEZF-.js → block-editor-B50rU9tL.js} +4 -4
  20. package/dist/gateway/static/root/assets/browser-automations-page-BiQz6lJA.js +3 -0
  21. package/dist/gateway/static/root/assets/browser-settings-page-W9kuxBbE.js +3 -0
  22. package/dist/gateway/static/root/assets/capabilities-page-iXe87MdG.js +2 -0
  23. package/dist/gateway/static/root/assets/capabilities-settings-panel-CRLSXe7Z.js +8 -0
  24. package/dist/gateway/static/root/assets/{capability-header-actions-D4Yq35fD.js → capability-header-actions-BMnbzHms.js} +1 -1
  25. package/dist/gateway/static/root/assets/channels-CrRJkOKX.js +1 -0
  26. package/dist/gateway/static/root/assets/{chat-terminal-dock-zdMySdMm.js → chat-terminal-dock-Bd3af4gL.js} +2 -2
  27. package/dist/gateway/static/root/assets/{computer-settings-page-C5Z6Jr0p.js → computer-settings-page-DXfqy7xh.js} +1 -1
  28. package/dist/gateway/static/root/assets/{connector-service-page-Cxus0qrZ.js → connector-service-page-B-cw0CPU.js} +1 -1
  29. package/dist/gateway/static/root/assets/connectors-page-DbFxc0Fd.js +1 -0
  30. package/dist/gateway/static/root/assets/{date-picker-DuyyD6jz.js → date-picker-CqzXevH6.js} +1 -1
  31. package/dist/gateway/static/root/assets/{dependency-picker-BZN72rrk.js → dependency-picker-pgT4Gyaf.js} +1 -1
  32. package/dist/gateway/static/root/assets/{desktop-pet-settings-BcyP7Z6v.js → desktop-pet-settings-Dy_-i2L8.js} +1 -1
  33. package/dist/gateway/static/root/assets/{desktop-pet-BtvTIDCi.js → desktop-pet-zICeqw9c.js} +1 -1
  34. package/dist/gateway/static/root/assets/device-pairing-wizard-ByGbxqkg.js +1 -0
  35. package/dist/gateway/static/root/assets/{directory-picker-path-field-DOusKz-O.js → directory-picker-path-field-C-aQ7qoM.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-debug-page-Bt4FwsFu.js → extension-debug-page-2NwpYkS5.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-page-0vsxX3FB.js → extension-page-y9J0z7_Q.js} +1 -1
  38. package/dist/gateway/static/root/assets/{extension-settings-page-5Pt5_ELc.js → extension-settings-page-BtowusUh.js} +1 -1
  39. package/dist/gateway/static/root/assets/gateway-realtime-C52ghnBK.js +1 -0
  40. package/dist/gateway/static/root/assets/gateway-settings-BYSKzUMR.js +1 -0
  41. package/dist/gateway/static/root/assets/highlight-code-Cz6eeDH5.js +1 -0
  42. package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-Cbk7eMlg.js → highlighted-body-KQOG7T2V-BiAa8HKC.js} +1 -1
  43. package/dist/gateway/static/root/assets/home-page-Cd1BjYHj.js +2 -0
  44. package/dist/gateway/static/root/assets/{image-generation-api-CAS8QVla.js → image-generation-api-gBFro48x.js} +1 -1
  45. package/dist/gateway/static/root/assets/{imports-page-BCLBYzV1.js → imports-page-BZQpXgjj.js} +1 -1
  46. package/dist/gateway/static/root/assets/index-Cy16gQST.css +1 -0
  47. package/dist/gateway/static/root/assets/index-DRyGCING.js +710 -0
  48. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CM_kt5XN.js → keyboard-shortcuts-settings-CYunR34k.js} +1 -1
  49. package/dist/gateway/static/root/assets/local-app-workbench-page-B_Ac1lly.js +1 -0
  50. package/dist/gateway/static/root/assets/{local-apps-page-BTSOChhk.js → local-apps-page-w-J5bY02.js} +1 -1
  51. package/dist/gateway/static/root/assets/logs-page-Dnjvvx2i.js +2 -0
  52. package/dist/gateway/static/root/assets/management-settings-w1EDwgLW.js +1 -0
  53. package/dist/gateway/static/root/assets/{media-plugins-DYQDjCVQ.js → media-plugins-CIx1koXw.js} +1 -1
  54. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-DISkHmEC.js +1 -0
  55. package/dist/gateway/static/root/assets/note-detail-panel-CNkxMTFU.js +5 -0
  56. package/dist/gateway/static/root/assets/note-image-lightbox-D5GH2wuC.js +1 -0
  57. package/dist/gateway/static/root/assets/notes-page-DZgKqFBG.js +8 -0
  58. package/dist/gateway/static/root/assets/{page-context-capture-button-DVn_dU83.js → page-context-capture-button-DusR2Itk.js} +1 -1
  59. package/dist/gateway/static/root/assets/personal-page-CR5JFkQ9.js +1 -0
  60. package/dist/gateway/static/root/assets/{personal-settings-panel-C16Iqe7X.js → personal-settings-panel-B7undvlr.js} +1 -1
  61. package/dist/gateway/static/root/assets/phone-model-picker-fWDltRUJ.js +1 -0
  62. package/dist/gateway/static/root/assets/phone-settings-page-CsOirAiy.js +1 -0
  63. package/dist/gateway/static/root/assets/plugin-details-dialog-BOBVIB3j.js +1 -0
  64. package/dist/gateway/static/root/assets/{preview-open-alternatives-RlHk76TI.js → preview-open-alternatives-CMxjsKSw.js} +1 -1
  65. package/dist/gateway/static/root/assets/{product-open-page-Dh-HqLrE.js → product-open-page-CrlyJx5M.js} +1 -1
  66. package/dist/gateway/static/root/assets/{product-preview-content-c3K-4DWc.js → product-preview-content-B-Jhzi7W.js} +1 -1
  67. package/dist/gateway/static/root/assets/project-detail-page-DoYUbtcI.js +7 -0
  68. package/dist/gateway/static/root/assets/{projects-page-B8HAt9sD.js → projects-page-xGQd9oAZ.js} +1 -1
  69. package/dist/gateway/static/root/assets/refresh-cw-BRlGaq2w.js +1 -0
  70. package/dist/gateway/static/root/assets/remote-access-hub-Gs0Xkws9.js +1 -0
  71. package/dist/gateway/static/root/assets/runtime-tools-settings-panel-Bwg7U7TR.js +1 -0
  72. package/dist/gateway/static/root/assets/{schema-form-CXO1B1k4.js → schema-form-ByzTNR_C.js} +1 -1
  73. package/dist/gateway/static/root/assets/{session-manager-CA1NkTR_.js → session-manager-B_h0jlLb.js} +1 -1
  74. package/dist/gateway/static/root/assets/sessions-page-XzK1ftbR.js +1 -0
  75. package/dist/gateway/static/root/assets/settings-advanced-gate-D_8Mvnog.js +1 -0
  76. package/dist/gateway/static/root/assets/{settings-form-section-BQxKmbbJ.js → settings-form-section-Brgd8IOV.js} +1 -1
  77. package/dist/gateway/static/root/assets/{settings-page-DvVMpKiA.js → settings-page-B6Mv02eg.js} +1 -1
  78. package/dist/gateway/static/root/assets/settings-page-header-BWyxJKOG.js +1 -0
  79. package/dist/gateway/static/root/assets/{settings-page-layout-B20AfmFG.js → settings-page-layout-DLHcS-0T.js} +1 -1
  80. package/dist/gateway/static/root/assets/setup-status-panel-DSCj5yis.js +1 -0
  81. package/dist/gateway/static/root/assets/{share-preview-page-XgZk8Oa1.js → share-preview-page-DF8Of0aZ.js} +1 -1
  82. package/dist/gateway/static/root/assets/shares-settings-DXNPe4Tl.js +1 -0
  83. package/dist/gateway/static/root/assets/{skill-api-CV-WT2hj.js → skill-api-CKCDCHLq.js} +1 -1
  84. package/dist/gateway/static/root/assets/skill-reload-api-BH0WF6Br.js +1 -0
  85. package/dist/gateway/static/root/assets/skills-page-DFWMVRW4.js +2 -0
  86. package/dist/gateway/static/root/assets/system-settings-panel-DB-ttubV.js +1 -0
  87. package/dist/gateway/static/root/assets/task-detail-page-BJY7c8wb.js +2 -0
  88. package/dist/gateway/static/root/assets/{task-result-evidence-D-gGKNX9.js → task-result-evidence-B-vt1BgK.js} +1 -1
  89. package/dist/gateway/static/root/assets/{tracing-settings-page-CQw3G-FB.js → tracing-settings-page-Dt51Z_ws.js} +1 -1
  90. package/dist/gateway/static/root/assets/{tunnel-api-B2HnCi0U.js → tunnel-api-Dok3t7Qe.js} +1 -1
  91. package/dist/gateway/static/root/assets/usage-page-lJym5G7F.js +1 -0
  92. package/dist/gateway/static/root/assets/{use-autosave-Dc0nwUpX.js → use-autosave-D4EMIB-V.js} +1 -1
  93. package/dist/gateway/static/root/assets/user-model-page-BXdNkITp.js +2 -0
  94. package/dist/gateway/static/root/assets/{voice-api-key-field-BSWXZkGa.js → voice-api-key-field-CO3KuanA.js} +1 -1
  95. package/dist/gateway/static/root/assets/{work-discovery-overlay-H3cdwq3l.js → work-discovery-overlay-BJRtgQM3.js} +1 -1
  96. package/dist/gateway/static/root/assets/wrench-DFb4r8bH.js +1 -0
  97. package/dist/gateway/static/root/assets/xopc-cloud-account-card-CS-_GxHV.js +1 -0
  98. package/dist/gateway/static/root/browser-setup-test.html +5 -0
  99. package/dist/gateway/static/root/browser-setup-test.js +7 -0
  100. package/dist/gateway/static/root/index.html +7 -4
  101. package/dist/package.js +1 -1
  102. package/dist/packages/gateway-contract/src/browser-setup.js +6 -0
  103. package/dist/packages/gateway-contract/src/index.js +4 -0
  104. package/dist/packages/gateway-contract/src/phone-setup.js +6 -0
  105. package/dist/packages/realtime-client/src/index.js +1 -0
  106. package/dist/packages/realtime-protocol/src/index.js +1 -0
  107. package/dist/src/agent/agent-manager.d.ts +4 -0
  108. package/dist/src/agent/agent-manager.js +12 -1
  109. package/dist/src/agent/embedded/tool-search-extension.js +14 -1
  110. package/dist/src/agent/service.d.ts +1 -0
  111. package/dist/src/agent/service.js +5 -0
  112. package/dist/src/agent/service.types.d.ts +2 -0
  113. package/dist/src/agent/tool-manuals/phone.d.ts +1 -1
  114. package/dist/src/agent/tool-manuals/phone.js +1 -1
  115. package/dist/src/agent/tool-manuals/xopc-configuration.d.ts +2 -0
  116. package/dist/src/agent/tool-manuals/xopc-configuration.js +25 -0
  117. package/dist/src/agent/tools/browser/tool/browser-use-tool.d.ts +2 -0
  118. package/dist/src/agent/tools/browser/tool/browser-use-tool.js +6 -2
  119. package/dist/src/agent/tools/factory.d.ts +3 -0
  120. package/dist/src/agent/tools/factory.js +11 -1
  121. package/dist/src/agent/tools/phone-use-tool.d.ts +1 -0
  122. package/dist/src/agent/tools/phone-use-tool.js +9 -1
  123. package/dist/src/agent/tools/tool-manual-tool.d.ts +1 -1
  124. package/dist/src/agent/tools/tool-manual-tool.js +3 -1
  125. package/dist/src/agent/tools/toolExposure.js +3 -0
  126. package/dist/src/agent/tools/xopc-use-tool.d.ts +4 -2
  127. package/dist/src/agent/tools/xopc-use-tool.js +40 -2
  128. package/dist/src/browser/drivers/browser-driver.d.ts +2 -2
  129. package/dist/src/browser/drivers/extension-driver.d.ts +2 -2
  130. package/dist/src/browser/drivers/extension-driver.js +6 -3
  131. package/dist/src/browser/providers/browser-ext-install.js +2 -1
  132. package/dist/src/browser/runtime/browser-runtime.d.ts +4 -1
  133. package/dist/src/browser/runtime/browser-runtime.js +29 -17
  134. package/dist/src/browser/setup/repository.d.ts +5 -0
  135. package/dist/src/browser/setup/repository.js +42 -0
  136. package/dist/src/browser/setup/service.d.ts +152 -0
  137. package/dist/src/browser/setup/service.js +404 -0
  138. package/dist/src/browser/setup/types.d.ts +38 -0
  139. package/dist/src/browser/setup/types.js +1 -0
  140. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  141. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  142. package/dist/src/config/schema.d.ts +2 -0
  143. package/dist/src/connectors/connected-source-understanding.js +1 -1
  144. package/dist/src/connectors/learning-coordinator.js +1 -1
  145. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  146. package/dist/src/gateway/hono/routes/browser-setup.d.ts +3 -0
  147. package/dist/src/gateway/hono/routes/browser-setup.js +114 -0
  148. package/dist/src/gateway/hono/routes/browser.d.ts +2 -1
  149. package/dist/src/gateway/hono/routes/browser.js +1 -1
  150. package/dist/src/gateway/hono/routes/connectors.js +7 -7
  151. package/dist/src/gateway/hono/routes/home.js +1 -1
  152. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  153. package/dist/src/gateway/hono/routes/phone-dependencies.js +1 -1
  154. package/dist/src/gateway/hono/routes/phone-setup.d.ts +7 -0
  155. package/dist/src/gateway/hono/routes/phone-setup.js +162 -0
  156. package/dist/src/gateway/hono/routes/phone.js +3 -7
  157. package/dist/src/gateway/hono/routes/public-gateway.js +3 -1
  158. package/dist/src/gateway/provider-model-setup.js +1 -1
  159. package/dist/src/gateway/service/config-coordinator.d.ts +2 -0
  160. package/dist/src/gateway/service.d.ts +5 -0
  161. package/dist/src/gateway/service.js +57 -12
  162. package/dist/src/gateway/xopc-cloud-capability-setup.js +7 -1
  163. package/dist/src/home-intelligence/host.js +2 -2
  164. package/dist/src/infra/update-restart.js +1 -1
  165. package/dist/src/phone/adb-driver.d.ts +2 -0
  166. package/dist/src/phone/adb-driver.js +35 -17
  167. package/dist/src/phone/contracts.d.ts +40 -0
  168. package/dist/src/phone/contracts.js +17 -4
  169. package/dist/src/phone/model-adapter.d.ts +59 -2
  170. package/dist/src/phone/model-adapter.js +107 -13
  171. package/dist/src/phone/numeric-entry.d.ts +31 -0
  172. package/dist/src/phone/numeric-entry.js +126 -0
  173. package/dist/src/phone/planning.d.ts +27 -0
  174. package/dist/src/phone/planning.js +79 -0
  175. package/dist/src/phone/recovery.d.ts +37 -0
  176. package/dist/src/phone/recovery.js +81 -0
  177. package/dist/src/phone/repository.d.ts +1 -0
  178. package/dist/src/phone/repository.js +3 -0
  179. package/dist/src/phone/runtime.js +232 -54
  180. package/dist/src/phone/semantic-controls.d.ts +13 -0
  181. package/dist/src/phone/semantic-controls.js +76 -0
  182. package/dist/src/phone/setup/service.d.ts +148 -0
  183. package/dist/src/phone/setup/service.js +251 -0
  184. package/dist/src/providers/presentation.js +2 -2
  185. package/dist/src/providers/xopc-cloud-model-source.js +6 -2
  186. package/dist/src/realtime/runtime.js +1 -1
  187. package/dist/src/storage/sqlite/schema.d.ts +2 -2
  188. package/dist/src/storage/sqlite/schema.js +7 -7
  189. package/dist/src/storage/sqlite/schema.sql +10 -0
  190. package/dist/src/storage/sqlite/upgrades/260_browser_setup.sql +9 -0
  191. package/dist/src/user-context/sources/index.js +1 -1
  192. package/dist/src/work-discovery/service.js +2 -2
  193. package/package.json +6 -6
  194. package/skills/tools/configure-xopc/SKILL.md +47 -0
  195. package/skills/tools/configure-xopc/references/browser/acceptance.md +31 -0
  196. package/skills/tools/configure-xopc/references/browser/chrome-extension.md +19 -0
  197. package/skills/tools/configure-xopc/references/browser/overview.md +22 -0
  198. package/skills/tools/configure-xopc/references/browser/permissions.md +25 -0
  199. package/skills/tools/configure-xopc/references/browser/troubleshooting.md +24 -0
  200. package/skills/tools/configure-xopc/references/phone/acceptance.md +25 -0
  201. package/skills/tools/configure-xopc/references/phone/aliyun.md +29 -0
  202. package/skills/tools/configure-xopc/references/phone/dependencies.md +18 -0
  203. package/skills/tools/configure-xopc/references/phone/local.md +19 -0
  204. package/skills/tools/configure-xopc/references/phone/models.md +18 -0
  205. package/skills/tools/configure-xopc/references/phone/overview.md +30 -0
  206. package/skills/tools/configure-xopc/references/phone/troubleshooting.md +24 -0
  207. package/skills/tools/configure-xopc/references/scene-index.md +12 -0
  208. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DclyJr1N.js +0 -1
  209. package/dist/gateway/static/root/assets/agents-jhErNtzi.js +0 -77
  210. package/dist/gateway/static/root/assets/apps-page-C0LcnI4h.js +0 -1
  211. package/dist/gateway/static/root/assets/automations-workspace-CBxiV34x.js +0 -8
  212. package/dist/gateway/static/root/assets/browser-automations-page-vJIrTxdi.js +0 -3
  213. package/dist/gateway/static/root/assets/browser-settings-page-in5Roh_E.js +0 -3
  214. package/dist/gateway/static/root/assets/capabilities-page-CiGx8wpQ.js +0 -2
  215. package/dist/gateway/static/root/assets/capabilities-settings-panel-a8kqjzQu.js +0 -8
  216. package/dist/gateway/static/root/assets/channels-C_CxcjKT.js +0 -1
  217. package/dist/gateway/static/root/assets/connectors-page-DCBO2CxK.js +0 -1
  218. package/dist/gateway/static/root/assets/device-pairing-wizard-Ctbgr50F.js +0 -1
  219. package/dist/gateway/static/root/assets/gateway-realtime-DkWCHVzI.js +0 -1
  220. package/dist/gateway/static/root/assets/gateway-settings-BsVOcmV5.js +0 -1
  221. package/dist/gateway/static/root/assets/highlight-code-C8eS9949.js +0 -1
  222. package/dist/gateway/static/root/assets/home-page-BZDZnmyc.js +0 -2
  223. package/dist/gateway/static/root/assets/index-Bys6njTY.js +0 -710
  224. package/dist/gateway/static/root/assets/index-Vsf0bj5e.css +0 -1
  225. package/dist/gateway/static/root/assets/local-app-workbench-page-Dk87jtcc.js +0 -1
  226. package/dist/gateway/static/root/assets/logs-page-D3gVdm8S.js +0 -2
  227. package/dist/gateway/static/root/assets/management-settings-BQNYFE4J.js +0 -1
  228. package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-ChweJKTv.js +0 -1
  229. package/dist/gateway/static/root/assets/note-detail-panel-CYKwUCte.js +0 -5
  230. package/dist/gateway/static/root/assets/note-image-lightbox-BTSZb9ZT.js +0 -1
  231. package/dist/gateway/static/root/assets/notes-page-CJ5nhWfL.js +0 -8
  232. package/dist/gateway/static/root/assets/personal-page-DcW14lqn.js +0 -1
  233. package/dist/gateway/static/root/assets/phone-dependencies-card-D3l9uPFM.js +0 -1
  234. package/dist/gateway/static/root/assets/phone-model-picker-DOv38vBn.js +0 -1
  235. package/dist/gateway/static/root/assets/phone-settings-page-DUHp1q_9.js +0 -1
  236. package/dist/gateway/static/root/assets/plugin-details-dialog-BpfgDlVa.js +0 -1
  237. package/dist/gateway/static/root/assets/project-detail-page-GD8TxKad.js +0 -7
  238. package/dist/gateway/static/root/assets/remote-access-hub-D3el7lHK.js +0 -1
  239. package/dist/gateway/static/root/assets/runtime-tools-settings-panel-Dbskk6RG.js +0 -1
  240. package/dist/gateway/static/root/assets/sessions-page-07liaSHH.js +0 -1
  241. package/dist/gateway/static/root/assets/settings-advanced-gate-BGjU8rfN.js +0 -1
  242. package/dist/gateway/static/root/assets/settings-page-header-CYpuTXUW.js +0 -1
  243. package/dist/gateway/static/root/assets/setup-status-panel-DKo_VTx-.js +0 -1
  244. package/dist/gateway/static/root/assets/shares-settings-BODFqK8k.js +0 -1
  245. package/dist/gateway/static/root/assets/skill-reload-api-Ce6OUk2C.js +0 -1
  246. package/dist/gateway/static/root/assets/skills-page-B7ehOIML.js +0 -2
  247. package/dist/gateway/static/root/assets/system-settings-panel-CxJkZRT4.js +0 -1
  248. package/dist/gateway/static/root/assets/task-detail-page-CxRR0QuW.js +0 -2
  249. package/dist/gateway/static/root/assets/usage-page-uUEkO38r.js +0 -1
  250. package/dist/gateway/static/root/assets/user-model-page-ICUutbKL.js +0 -2
  251. package/dist/gateway/static/root/assets/xopc-cloud-account-card-CvT_W6YS.js +0 -1
@@ -18,6 +18,31 @@ requested change once with the new revision and a new intent key. After an uncer
18
18
  inspect first and retain the original idempotency key. A permission denial is not a conflict:
19
19
  do not switch identity, target or tools to bypass it. Inspect returned product ok and saved
20
20
  fields before reporting success. A saved model reference does not prove it can be called.`,
21
+ phone_setup: `# Phone setup
22
+ Use configure-xopc and references/phone/overview.md. mode phone_setup commands inspect/preview,
23
+ start, get/recheck/verification_report/cancel {setupId}. Only inspect readiness and orchestrate;
24
+ configure models, install managed ADB/scrcpy, and upload cloud adbkey in existing product forms.
25
+ Never paste keys into chat or modify permissions or cloud billing yourself. Phone model is
26
+ independent of chat model. Cloud ADB does not need local adb; scrcpy provides video/Unicode.
27
+ Owner selects local/aliyun and the exact registered device in the same chat panel. Only the
28
+ owner can start a test. Get returns attempt.goal/completion/maxActions. Discover phone_use if
29
+ not declared, open this device in control mode with appPackage com.android.settings, observe,
30
+ run the exact returned goal and completion, then close and read verification_report. Host
31
+ records actual PhoneRuntime evidence; mock or model finish alone cannot pass. On cancellation,
32
+ unknown outcome or restart, release first. Keep the original conversation and never substitute
33
+ another phone, widen app access, replay input or claim control released from cancellation.`,
34
+ browser_setup: `# Browser setup
35
+ Use configure-xopc and its Browser references. Commands on xopc_use mode browser_setup:
36
+ inspect/preview {} read current shared configuration and available endpoints; start {} creates
37
+ or reuses this Agent conversation's setup. get/recheck/advance/request_interaction/
38
+ begin_verification/verification_report/cancel take {setupId}. dryRun never creates or mutates.
39
+ Mutations require local-owner approval in the returned settingsPath. The UI selects the Chrome
40
+ endpoint and begins verification; never bypass that step with file writes or shell commands.
41
+ During verification get returns attempt.fixtureUrl, sample and evidence. Use normal browser_use
42
+ navigate, observe, click, fill, close; the host binds these calls to the approved test endpoint.
43
+ Read guidanceRefs progressively with skill_view(name:"configure-xopc",path:<returned path>).
44
+ Report completed only when all checks and control release pass. No API Key is needed for Chrome.
45
+ settingsPath is navigation; opening it is not proof of installation or connection.`,
21
46
  settings: `# xopc settings and capability checks
22
47
  settings open is navigation only; it does not read, apply or verify configuration.
23
48
  Voice: {section:"capabilities/voice",title:"语音设置"}; models: section:"capabilities/models";
@@ -3,6 +3,8 @@ import type { BrowserNotReadyError } from '../../../../browser/readiness.js';
3
3
  import type { BrowserRuntime } from '../../../../browser/runtime/browser-runtime.js';
4
4
  import { BrowserUseSchema } from './schemas.js';
5
5
  export interface CreateBrowserUseToolDeps {
6
+ getSetupService?: () => import('../../../../browser/setup/service.js').BrowserSetupService | undefined;
7
+ getAgentId?: () => string;
6
8
  getRuntime: () => BrowserRuntime;
7
9
  getTaskId: () => string;
8
10
  getReadiness?: () => Promise<BrowserNotReadyError | null>;
@@ -24,7 +24,7 @@ function createBrowserUseTool(deps) {
24
24
  }
25
25
  };
26
26
  const readiness = await deps.getReadiness?.();
27
- if (readiness) return {
27
+ if (readiness && input.action !== "close") return {
28
28
  content: [{
29
29
  type: "text",
30
30
  text: `Browser is not ready: ${readiness.hint.detail || readiness.hint.reason}. Open Settings → Browser.`
@@ -35,7 +35,11 @@ function createBrowserUseTool(deps) {
35
35
  hint: readiness.hint
36
36
  }
37
37
  };
38
- return presentResult(await deps.getRuntime().execute(deps.getTaskId(), input, signal));
38
+ const setupService = deps.getSetupService?.();
39
+ const verification = setupService?.beforeAction(deps.getTaskId(), deps.getAgentId?.() ?? "", input);
40
+ const result = await deps.getRuntime().execute(verification?.taskKey ?? deps.getTaskId(), verification?.input ?? input, signal);
41
+ if (verification) setupService.afterAction(verification.setupId, verification.attemptId, verification.input, result);
42
+ return presentResult(result);
39
43
  }
40
44
  };
41
45
  }
@@ -57,6 +57,8 @@ export interface ToolFactoryDeps {
57
57
  };
58
58
  /** Gateway: enables the `automation` tool. */
59
59
  getAutomationService?: () => AutomationService | undefined;
60
+ getPhoneSetupService?: () => import('../../phone/setup/service.js').PhoneSetupService | undefined;
61
+ getBrowserSetupService?: () => import('../../browser/setup/service.js').BrowserSetupService | undefined;
60
62
  getBrowserAutomationService?: () => BrowserAutomationService | undefined;
61
63
  emitBrowserEvent?: (type: string, payload: unknown) => void;
62
64
  /** Gateway: enables the `xopc_use` product-object tool. */
@@ -127,6 +129,7 @@ export declare class AgentToolsFactory {
127
129
  private ensureBrowserRuntime;
128
130
  private computerRuntime?;
129
131
  private ensureComputerRuntime;
132
+ hasBrowserSessions(): boolean;
130
133
  /** Close Playwright and all pages (gateway stop, agent manager dispose, or config hot-reload). */
131
134
  shutdownBrowser(): Promise<void>;
132
135
  /** Drop the tab for a session when its agent instance is removed. */
@@ -196,6 +196,8 @@ var AgentToolsFactory = class AgentToolsFactory {
196
196
  return createBrowserDriver(config.browser, this.deps.endpointTools);
197
197
  },
198
198
  allowedUploadRoots: [this.deps.workspace],
199
+ canRestoreSetupSession: (taskKey, sessionId) => this.deps.getBrowserSetupService?.()?.canRestoreSession(taskKey, sessionId) === true,
200
+ isTrustedSetupUrl: (taskKey, url) => this.deps.getBrowserSetupService?.()?.allowsVerificationUrl(taskKey, url) === true,
199
201
  emit: this.deps.emitBrowserEvent,
200
202
  resolveTarget: (conversationId) => {
201
203
  const binding = getBrowserTabBinding(conversationId);
@@ -224,6 +226,9 @@ var AgentToolsFactory = class AgentToolsFactory {
224
226
  return context?.conversationId === owner && context.origin.type === "endpoint" ? context.origin.endpointId : void 0;
225
227
  });
226
228
  }
229
+ hasBrowserSessions() {
230
+ return this.browserRuntime?.hasSessions() ?? false;
231
+ }
227
232
  /** Close Playwright and all pages (gateway stop, agent manager dispose, or config hot-reload). */
228
233
  async shutdownBrowser() {
229
234
  await this.computerRuntime?.shutdown();
@@ -333,6 +338,8 @@ var AgentToolsFactory = class AgentToolsFactory {
333
338
  getWorkDiscovery: this.deps.getWorkDiscovery,
334
339
  getLocalAppService: this.deps.getLocalAppService,
335
340
  getChatPreviewService: this.deps.getChatPreviewService,
341
+ getBrowserSetupService: this.deps.getBrowserSetupService,
342
+ getPhoneSetupService: this.deps.getPhoneSetupService,
336
343
  dispatchTaskEvents: this.deps.dispatchTaskEvents,
337
344
  dispatchTaskRuns: this.deps.dispatchTaskRuns,
338
345
  onAgentCatalogMutate: this.deps.onAgentCatalogMutate
@@ -556,7 +563,7 @@ var AgentToolsFactory = class AgentToolsFactory {
556
563
  getProjectService: this.deps.getProjectService
557
564
  })] : [],
558
565
  ...browserEnabled && this.deps.getBrowserAutomationService ? [createBrowserAutomationTool({ getBrowserAutomationService: this.deps.getBrowserAutomationService })] : [],
559
- ...this.deps.getAutomationService || this.deps.getProjectService || this.deps.getNotesService || this.deps.getLocalAppService || this.deps.getChatPreviewService || this.deps.dispatchTaskEvents || this.deps.dispatchTaskRuns ? [createXopcUseTool(productToolDeps), ...personalToolsConfigured ? [
566
+ ...this.deps.getPhoneSetupService || this.deps.getBrowserSetupService || this.deps.getAutomationService || this.deps.getProjectService || this.deps.getNotesService || this.deps.getLocalAppService || this.deps.getChatPreviewService || this.deps.dispatchTaskEvents || this.deps.dispatchTaskRuns ? [createXopcUseTool(productToolDeps), ...personalToolsConfigured ? [
560
567
  createPersonalTaskTool(productToolDeps),
561
568
  createPersonalReadTool({
562
569
  ...productToolDeps,
@@ -571,6 +578,7 @@ var AgentToolsFactory = class AgentToolsFactory {
571
578
  ] : []] : [],
572
579
  ...cfg?.phone?.enabled ? [createPhoneUseTool({
573
580
  runtime: getPhoneRuntime(),
581
+ getSetupService: this.deps.getPhoneSetupService,
574
582
  context: () => {
575
583
  const config = this.deps.getConfig?.();
576
584
  const conversationId = getEmbeddedExecutionSession() ?? currentConversationId();
@@ -595,6 +603,8 @@ var AgentToolsFactory = class AgentToolsFactory {
595
603
  })] : [],
596
604
  ...browserEnabled ? [createBrowserUseTool({
597
605
  getRuntime: () => this.ensureBrowserRuntime(),
606
+ getSetupService: this.deps.getBrowserSetupService,
607
+ getAgentId: () => resolvedAgentId,
598
608
  getTaskId: () => options?.conversationId ?? this.deps.getCurrentContext()?.conversationId ?? "default",
599
609
  getReadiness: () => this.checkBrowserReadinessCached()
600
610
  })] : [],
@@ -2,6 +2,7 @@ import type { AgentTool } from '@earendil-works/pi-agent-core';
2
2
  import type { PhoneRuntime, phoneExecutionContext } from '../../phone/runtime.js';
3
3
  export declare function createPhoneUseTool(deps: {
4
4
  runtime: PhoneRuntime;
5
+ getSetupService?: () => import('../../phone/setup/service.js').PhoneSetupService | undefined;
5
6
  context(): ReturnType<typeof phoneExecutionContext> & {
6
7
  owner: string;
7
8
  };
@@ -54,7 +54,15 @@ function createPhoneUseTool(deps) {
54
54
  description: "Use an authorized Android phone via local ADB or a configured cloud connection. Load tool_manual for phone_use before first use. Discover registered devices, appAccess and allowedApps first; open with mode observe/control and optional authorized appPackage, observe native UI text, then run one small goal or step once. Device and model configuration live in Phone Use settings. Connections originate from the Gateway host; cloud credentials are configured in phone management. Mock mode only supports the explicitly requested settings-about emulator test. Never silently switch phones, execute shell commands, bypass app restrictions, or repeat a dispatch with unknown outcome. Request user assistance for login, CAPTCHA or an effect not already authorized. Pause before manual takeover; resume re-observes. Inspect verified, verificationLevel and stopReason before claiming success. Close releases device control. Raw screenshots never enter the chat transcript.",
55
55
  async execute(_id, raw, signal) {
56
56
  const { owner, ...context } = deps.context();
57
- const result = await deps.runtime.execute(owner, PhoneInputSchema.parse(raw), context, signal);
57
+ const input = PhoneInputSchema.parse(raw);
58
+ const setup = deps.getSetupService?.();
59
+ const setupContext = {
60
+ conversationId: owner,
61
+ agentId: context.agentId
62
+ };
63
+ const setupOwner = setup?.beforeInput(setupContext, input);
64
+ const result = await deps.runtime.execute(setupOwner?.owner ?? owner, setupOwner?.input ?? input, context, signal);
65
+ if (setupOwner) setup.afterInput(setupContext, setupOwner.owner, input.op);
58
66
  return {
59
67
  content: [{
60
68
  type: "text",
@@ -1,7 +1,7 @@
1
1
  import type { AgentTool } from '@earendil-works/pi-agent-core';
2
2
  declare const ToolManualSchema: import("@sinclair/typebox").TObject<{
3
3
  tool: import("@sinclair/typebox").TString;
4
- topic: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"agent">, import("@sinclair/typebox").TLiteral<"settings">, import("@sinclair/typebox").TLiteral<"automation">, import("@sinclair/typebox").TLiteral<"connections">]>>;
4
+ topic: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"agent">, import("@sinclair/typebox").TLiteral<"settings">, import("@sinclair/typebox").TLiteral<"automation">, import("@sinclair/typebox").TLiteral<"connections">, import("@sinclair/typebox").TLiteral<"browser_setup">, import("@sinclair/typebox").TLiteral<"phone_setup">]>>;
5
5
  }>;
6
6
  export declare function createToolManualTool(): AgentTool<typeof ToolManualSchema, {}>;
7
7
  export {};
@@ -8,7 +8,9 @@ const ToolManualSchema = Type.Object({
8
8
  Type.Literal("agent"),
9
9
  Type.Literal("settings"),
10
10
  Type.Literal("automation"),
11
- Type.Literal("connections")
11
+ Type.Literal("connections"),
12
+ Type.Literal("browser_setup"),
13
+ Type.Literal("phone_setup")
12
14
  ], { description: "Focused xopc_use workflow; omit for the full parameter manual." }))
13
15
  });
14
16
  function createToolManualTool() {
@@ -9,6 +9,9 @@ const EAGER_CORE_TOOLS = new Set([
9
9
  "list_dir",
10
10
  "exec_command",
11
11
  "review_workspace",
12
+ "skill_view",
13
+ "skills_list",
14
+ "tool_manual",
12
15
  "publish_artifacts",
13
16
  "task_collaboration",
14
17
  "publish_task_result"
@@ -9,12 +9,12 @@ import type { LocalAppService } from '../../local-apps/index.js';
9
9
  import type { ChatPreviewService } from '../../chat-previews/index.js';
10
10
  import { TaskApplicationService } from '../../tasks/task-application-service.js';
11
11
  declare const XopcUseToolSchema: import("@sinclair/typebox").TObject<{
12
- mode: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"context">, import("@sinclair/typebox").TLiteral<"project">, import("@sinclair/typebox").TLiteral<"automation">, import("@sinclair/typebox").TLiteral<"note">, import("@sinclair/typebox").TLiteral<"task">, import("@sinclair/typebox").TLiteral<"task_run">, import("@sinclair/typebox").TLiteral<"local_app">, import("@sinclair/typebox").TLiteral<"chat_preview">, import("@sinclair/typebox").TLiteral<"agent">, import("@sinclair/typebox").TLiteral<"settings">]>;
12
+ mode: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"context">, import("@sinclair/typebox").TLiteral<"project">, import("@sinclair/typebox").TLiteral<"automation">, import("@sinclair/typebox").TLiteral<"note">, import("@sinclair/typebox").TLiteral<"task">, import("@sinclair/typebox").TLiteral<"task_run">, import("@sinclair/typebox").TLiteral<"local_app">, import("@sinclair/typebox").TLiteral<"chat_preview">, import("@sinclair/typebox").TLiteral<"agent">, import("@sinclair/typebox").TLiteral<"settings">, import("@sinclair/typebox").TLiteral<"browser_setup">, import("@sinclair/typebox").TLiteral<"phone_setup">]>;
13
13
  command: import("@sinclair/typebox").TString;
14
14
  args: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TAny>>;
15
15
  dryRun: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
16
16
  }>;
17
- export type XopcUseMode = 'context' | 'project' | 'automation' | 'note' | 'task' | 'task_run' | 'chat_preview' | 'local_app' | 'agent' | 'settings';
17
+ export type XopcUseMode = 'context' | 'project' | 'automation' | 'note' | 'task' | 'task_run' | 'chat_preview' | 'local_app' | 'agent' | 'settings' | 'browser_setup' | 'phone_setup';
18
18
  export interface XopcUseToolInput {
19
19
  mode: XopcUseMode;
20
20
  command: string;
@@ -22,6 +22,8 @@ export interface XopcUseToolInput {
22
22
  dryRun?: boolean;
23
23
  }
24
24
  export interface XopcUseToolDeps {
25
+ getPhoneSetupService?: () => import('../../phone/setup/service.js').PhoneSetupService | undefined;
26
+ getBrowserSetupService?: () => import('../../browser/setup/service.js').BrowserSetupService | undefined;
25
27
  authorizeCapability?: CapabilityContext['authorize'];
26
28
  getWorkspace?: () => string;
27
29
  getConfig?: () => Config | undefined;
@@ -50,7 +50,9 @@ const XopcUseToolSchema = Type.Object({
50
50
  Type.Literal("local_app"),
51
51
  Type.Literal("chat_preview"),
52
52
  Type.Literal("agent"),
53
- Type.Literal("settings")
53
+ Type.Literal("settings"),
54
+ Type.Literal("browser_setup"),
55
+ Type.Literal("phone_setup")
54
56
  ]),
55
57
  command: Type.String({ description: "Context resolve takes an explicit AppContextEnvelope; never guess a current page. Automation draft/repair_draft accept prompt/id, agentId?, language?, idempotencyKey?; simulate explains an automation without running it. Local_app record_acceptance records supplied checks and sourceHash, not proof that checks were executed. Agent profile-only changes: patch_profile {id, expectedRevision, patch:{name?,description?,style?,language?,...}, idempotencyKey?} preserves omitted fields. Agent commands: list, get {id}, create {id, profile, workspace?, models?, skills?, tools?, runtime?, idempotencyKey?}, update {id, expectedRevision, patch, idempotencyKey?}, set_default {id, expectedRevision, idempotencyKey?}, disable/delete/purge {id, expectedRevision, idempotencyKey?}. Purge also removes on-disk data. Project create accepts idempotencyKey; update/pin/unpin/delete accepts {projectId, expectedVersion?, idempotencyKey?}; stable retries require the original expectedVersion. Delete preserves workspace files and does not confirm external execution stopped. Project milestone writes support idempotencyKey; update_milestone/delete_milestone require original expectedRevision for stable retries, and create_update requires original expectedVersion. TaskRun cancel accepts idempotencyKey and expectedVersion, and does not confirm external execution stopped. Automation diagnostics: get_run/run_events {runId}, metrics {}, events {eventType?, source?, limit?}, deliveries {runId?, limit?}, product_events {eventType, source?, payloadKey?, payloadValue?, limit?}; payload filters require both key and value. Recovery commands: replay_event {eventId, reason, idempotencyKey}, retry_delivery {runId, destinationKey, reason, idempotencyKey}. Automation also supports cancel/read {runId, idempotencyKey?} and read_all {projectId?, idempotencyKey?}; omitted projectId means all projects. Cancel acceptance is not confirmation of stopping. Object command. Supports project list/get/create/update/resolve_workspace/list_milestones/create_milestone/update_milestone/list_updates/create_update, automation list/get/create/update/delete/run/rerun/pause/resume/history (rerun takes runId; run/rerun accept idempotencyKey), note list/get/project_summaries/history {noteId}/snapshot {noteId, timestamp}/create/append/update/preview_edit/delete, task list/get/create/update_dependencies/add_context/remove_context/command/delete, task_run list/get/cancel, chat_preview create/revise/get, local_app list/get/create/validate, and settings open." }),
56
58
  args: Type.Optional(Type.Record(Type.String(), Type.Any())),
@@ -1915,7 +1917,7 @@ function createXopcUseTool(deps) {
1915
1917
  return {
1916
1918
  name: "xopc_use",
1917
1919
  label: "XOPC Use",
1918
- description: "Operate first-class xopc objects through one safe entry point. To delegate work for immediate execution: mode task, command create, args {title:\"Short task name\", objective:\"One-sentence outcome\", body:\"Full Markdown task brief\", createMode:\"start\", agentId:\"researcher\"}; omitting createMode creates a backlog task only. To start an existing Task: mode task, command command, args {taskId, type:\"start\", expectedVersion, commandArgs:{executor:{kind:\"agent\",agentId:\"main\"}}}. Inspect an existing Task and its run before creating another Task for the same objective. For delegated work, task delegated_tasks lists this conversation's tasks; task collaboration reads the shared board; task collaboration_post writes {taskId, kind, body, causationId?, idempotencyKey?}. Workers report progress or questions; the main Agent writes instructions and answers. Use chat_preview for lightweight UI mockups in the current conversation; do not create a Local App unless the user asks for a durable app. Local App capabilities takes extensionId and discovers already granted bindings. Local App invoke requires extensionId, the discovered manifestDigest, capabilityId and a pinned call {majorVersion, descriptorDigest, input, idempotencyKey for writes}. Never manufacture grants or change a retry key after an uncertain write. Use for Agents, projects, automations, notes, tasks, TaskRuns, chat previews, local apps, and exact settings jump targets instead of editing storage files directly. For non-trivial object changes, load the built-in manual first with tool_manual({ tool: \"xopc_use\" }).",
1920
+ description: "Use mode browser_setup for inspect/preview/start/get/advance/recheck/request_interaction/begin_verification/verification_report/cancel of local Chrome setup. Load configure-xopc for guided setup; writes require product-owner approval. Operate first-class xopc objects through one safe entry point. To delegate work for immediate execution: mode task, command create, args {title:\"Short task name\", objective:\"One-sentence outcome\", body:\"Full Markdown task brief\", createMode:\"start\", agentId:\"researcher\"}; omitting createMode creates a backlog task only. To start an existing Task: mode task, command command, args {taskId, type:\"start\", expectedVersion, commandArgs:{executor:{kind:\"agent\",agentId:\"main\"}}}. Inspect an existing Task and its run before creating another Task for the same objective. For delegated work, task delegated_tasks lists this conversation's tasks; task collaboration reads the shared board; task collaboration_post writes {taskId, kind, body, causationId?, idempotencyKey?}. Workers report progress or questions; the main Agent writes instructions and answers. Use chat_preview for lightweight UI mockups in the current conversation; do not create a Local App unless the user asks for a durable app. Local App capabilities takes extensionId and discovers already granted bindings. Local App invoke requires extensionId, the discovered manifestDigest, capabilityId and a pinned call {majorVersion, descriptorDigest, input, idempotencyKey for writes}. Never manufacture grants or change a retry key after an uncertain write. Use for Agents, projects, automations, notes, tasks, TaskRuns, chat previews, local apps, and exact settings jump targets instead of editing storage files directly. For non-trivial object changes, load the built-in manual first with tool_manual({ tool: \"xopc_use\" }).",
1919
1921
  parameters: XopcUseToolSchema,
1920
1922
  mutatesWorkspace: true,
1921
1923
  mutationScope: "external",
@@ -1932,6 +1934,42 @@ function createXopcUseTool(deps) {
1932
1934
  dryRun
1933
1935
  };
1934
1936
  if (!command) return errorText("command is required", details);
1937
+ if (mode === "phone_setup") {
1938
+ const service = deps.getPhoneSetupService?.();
1939
+ const conversationId = deps.getCurrentConversationId?.();
1940
+ const agentId = deps.getCurrentAgentId?.();
1941
+ if (!service || !conversationId || !agentId) return errorText("Phone setup requires a Gateway conversation. Open Settings → Phone Use.", details);
1942
+ if (deps.authorizeCapability && !await deps.authorizeCapability(`xopc.phone_setup.${command}`, args)) return errorText("Phone setup access denied", details);
1943
+ try {
1944
+ return okText({
1945
+ ...details,
1946
+ result: await service.command(command, args, {
1947
+ conversationId,
1948
+ agentId
1949
+ }, dryRun)
1950
+ });
1951
+ } catch (error) {
1952
+ return errorText(error instanceof Error ? error.message : String(error), details);
1953
+ }
1954
+ }
1955
+ if (mode === "browser_setup") {
1956
+ const service = deps.getBrowserSetupService?.();
1957
+ const conversationId = deps.getCurrentConversationId?.();
1958
+ const agentId = deps.getCurrentAgentId?.();
1959
+ if (!service || !conversationId || !agentId) return errorText("Browser setup requires a Gateway conversation. Open Settings → Browser.", details);
1960
+ if (deps.authorizeCapability && !await deps.authorizeCapability(`xopc.browser_setup.${command}`, args)) return errorText("Browser setup access denied", details);
1961
+ try {
1962
+ return okText({
1963
+ ...details,
1964
+ result: await service.command(command, args, {
1965
+ conversationId,
1966
+ agentId
1967
+ }, dryRun)
1968
+ });
1969
+ } catch (error) {
1970
+ return errorText(error instanceof Error ? error.message : String(error), details);
1971
+ }
1972
+ }
1935
1973
  if (mode === "local_app" && (command === "capabilities" || command === "invoke")) {
1936
1974
  const apps = deps.getLocalAppService?.();
1937
1975
  if (!apps) return errorText("Local app service is unavailable", details);
@@ -1,4 +1,4 @@
1
- import type { BrowserActionInput, BrowserControlResult, BrowserNavigateInput, BrowserObservation, BrowserObserveInput, BrowserTabsInput } from '@xopcai/browser-control-contract';
1
+ import type { BrowserActionInput, BrowserControlResult, BrowserNavigateInput, BrowserObservation, BrowserObserveInput, BrowserTabsInput, BrowserTarget } from '@xopcai/browser-control-contract';
2
2
  export type BrowserPrimitiveInput = Exclude<BrowserActionInput, BrowserNavigateInput | BrowserObserveInput | BrowserTabsInput | {
3
3
  action: 'sequence';
4
4
  } | {
@@ -9,7 +9,7 @@ export interface BrowserDriver {
9
9
  connect(): Promise<void>;
10
10
  disconnect(): Promise<void>;
11
11
  createSession(sessionId: string): Promise<void>;
12
- closeSession(sessionId: string): Promise<void>;
12
+ closeSession(sessionId: string, target?: BrowserTarget): Promise<void>;
13
13
  navigate(sessionId: string, input: BrowserNavigateInput, signal?: AbortSignal): Promise<BrowserControlResult>;
14
14
  observe(sessionId: string, input: BrowserObserveInput, signal?: AbortSignal): Promise<BrowserObservation>;
15
15
  perform(sessionId: string, input: BrowserPrimitiveInput, signal?: AbortSignal): Promise<BrowserControlResult>;
@@ -1,4 +1,4 @@
1
- import type { BrowserControlResult, BrowserNavigateInput, BrowserObservation, BrowserObserveInput, BrowserTabsInput } from '@xopcai/browser-control-contract';
1
+ import type { BrowserControlResult, BrowserNavigateInput, BrowserObservation, BrowserObserveInput, BrowserTabsInput, BrowserTarget } from '@xopcai/browser-control-contract';
2
2
  import type { BrowserDriver, BrowserPrimitiveInput } from './browser-driver.js';
3
3
  type ExtensionProvider = {
4
4
  start(): Promise<void>;
@@ -15,7 +15,7 @@ export declare class ExtensionDriver implements BrowserDriver {
15
15
  connect(): Promise<void>;
16
16
  disconnect(): Promise<void>;
17
17
  createSession(_sessionId: string): Promise<void>;
18
- closeSession(sessionId: string): Promise<void>;
18
+ closeSession(sessionId: string, target?: BrowserTarget): Promise<void>;
19
19
  navigate(sessionId: string, input: BrowserNavigateInput, signal?: AbortSignal): Promise<BrowserControlResult>;
20
20
  observe(sessionId: string, input: BrowserObserveInput, signal?: AbortSignal): Promise<BrowserObservation>;
21
21
  perform(sessionId: string, input: BrowserPrimitiveInput, signal?: AbortSignal): Promise<BrowserControlResult>;
@@ -17,11 +17,14 @@ var ExtensionDriver = class {
17
17
  async createSession(_sessionId) {
18
18
  await this.connect();
19
19
  }
20
- async closeSession(sessionId) {
21
- await this.send({
20
+ async closeSession(sessionId, target) {
21
+ const result = await this.send({
22
22
  action: "close",
23
- sessionId
23
+ sessionId,
24
+ ...target ? { target } : {}
24
25
  });
26
+ if ("error" in result) throw new Error(result.error.message);
27
+ if (!result.receipt.verified) throw new Error("Browser control release was not verified");
25
28
  }
26
29
  navigate(sessionId, input, signal) {
27
30
  return this.send({
@@ -9,7 +9,7 @@ import { BROWSER_EXTENSION_ID, BROWSER_NATIVE_HOST_NAME } from "../extension-ide
9
9
  import "../native-messaging-host.js";
10
10
  import { resolvePackageRoot } from "../../infra/update-check.js";
11
11
  import { dirname, join } from "node:path";
12
- import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
12
+ import { accessSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
13
13
  import { fileURLToPath } from "node:url";
14
14
  import { createHash } from "node:crypto";
15
15
  import { execFileSync, spawn } from "node:child_process";
@@ -384,6 +384,7 @@ function browserNativeHostDoctor(platform = process.platform, home = process.env
384
384
  const manifestPaths = (platform === "win32" ? windowsRegisteredNativeManifests() : directories.map((directory) => join(directory, `${BROWSER_NATIVE_HOST_NAME}.json`))).filter((manifestPath) => {
385
385
  try {
386
386
  const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
387
+ if (typeof manifest.path === "string" && platform !== "win32") accessSync(manifest.path, constants.X_OK);
387
388
  return manifest.name === "ai.xopc.browser" && typeof manifest.path === "string" && existsSync(manifest.path) && Array.isArray(manifest.allowed_origins) && manifest.allowed_origins.includes(`chrome-extension://gopbfhaojnnhiheiikblejnpgmfmkmgd/`);
388
389
  } catch {
389
390
  return false;
@@ -5,6 +5,8 @@ export interface BrowserRuntimeOptions {
5
5
  getConfig: () => Config['browser'];
6
6
  createDriver: () => Promise<BrowserDriver>;
7
7
  allowedUploadRoots?: string[];
8
+ canRestoreSetupSession?: (taskKey: string, sessionId: string) => boolean;
9
+ isTrustedSetupUrl?: (taskKey: string, url: string) => boolean;
8
10
  emit?: (type: string, payload: unknown) => void;
9
11
  resolveTarget?: (taskKey: string) => BrowserTarget | undefined;
10
12
  }
@@ -14,8 +16,9 @@ export declare class BrowserRuntime {
14
16
  private sessionsByTask;
15
17
  constructor(options: BrowserRuntimeOptions);
16
18
  execute(taskKey: string, input: BrowserActionInput, signal?: AbortSignal): Promise<BrowserControlResult>;
19
+ hasSessions(): boolean;
17
20
  shutdown(): Promise<void>;
18
- closeTaskSession(taskKey: string): Promise<void>;
21
+ closeTaskSession(taskKey: string, target?: BrowserTarget): Promise<void>;
19
22
  private dispatch;
20
23
  private executeSequence;
21
24
  private actionDenied;
@@ -11,7 +11,7 @@ var BrowserRuntime = class {
11
11
  }
12
12
  async execute(taskKey, input, signal) {
13
13
  this.evictExpiredSessions();
14
- if (!this.options.getConfig().enabled) return failure("DRIVER_UNAVAILABLE", "Browser Control is disabled.");
14
+ if (!this.options.getConfig().enabled && input.action !== "close") return failure("DRIVER_UNAVAILABLE", "Browser Control is disabled.");
15
15
  if (signal?.aborted) return failure("ABORTED", "Operation was aborted.");
16
16
  const resolvedTarget = input.target ? void 0 : this.options.resolveTarget?.(taskKey);
17
17
  const effectiveInput = input.target || !resolvedTarget ? input : {
@@ -55,21 +55,24 @@ var BrowserRuntime = class {
55
55
  }
56
56
  return result;
57
57
  }
58
+ hasSessions() {
59
+ return this.sessionsByTask.size > 0;
60
+ }
58
61
  async shutdown() {
59
62
  await this.driver?.disconnect();
60
63
  this.driver = null;
61
64
  this.sessionsByTask.clear();
62
65
  }
63
- async closeTaskSession(taskKey) {
66
+ async closeTaskSession(taskKey, target) {
64
67
  const session = this.sessionsByTask.get(taskKey);
65
68
  if (!session) return;
66
- await this.driver?.closeSession(session.id);
69
+ await this.driver?.closeSession(session.id, target);
67
70
  this.sessionsByTask.delete(taskKey);
68
71
  this.options.emit?.("browser.session.closed", { sessionId: session.id });
69
72
  }
70
73
  async dispatch(driver, session, input, signal) {
71
74
  if (input.action === "close") {
72
- await this.closeTaskSession(session.taskKey);
75
+ await this.closeTaskSession(session.taskKey, input.target);
73
76
  return {
74
77
  ok: true,
75
78
  receipt: {
@@ -91,22 +94,22 @@ var BrowserRuntime = class {
91
94
  }
92
95
  };
93
96
  if (input.action === "navigate") {
94
- const blocked = this.validateUrl(input.url);
97
+ const blocked = this.validateUrl(input.url, session.taskKey);
95
98
  if (blocked) return failure("BLOCKED_URL", blocked);
96
99
  if (this.crossDomainPolicy(session.observation?.url, input.url) === "deny") return failure("BLOCKED_URL", "Cross-domain navigation is denied by policy.");
97
100
  const result = await driver.navigate(session.id, input, signal);
98
- return this.validateRedirect(input.url, result);
101
+ return this.validateRedirect(input.url, result, session.taskKey);
99
102
  }
100
103
  if (input.action === "tabs") {
101
104
  if (input.url) {
102
- const blocked = this.validateUrl(input.url);
105
+ const blocked = this.validateUrl(input.url, session.taskKey);
103
106
  if (blocked) return failure("BLOCKED_URL", blocked);
104
107
  if (this.crossDomainPolicy(session.observation?.url, input.url) === "deny") return failure("BLOCKED_URL", "Cross-domain navigation is denied by policy.");
105
108
  }
106
109
  const result = await driver.tabs(session.id, input, signal);
107
110
  if (!input.url || !result.ok) return result;
108
111
  const finalUrl = result.receipt.tabs?.find((tab) => tab.active)?.url;
109
- const blocked = finalUrl ? this.blockedRedirect(finalUrl) : "The created tab did not report its final URL.";
112
+ const blocked = finalUrl ? this.blockedRedirect(finalUrl, session.taskKey) : "The created tab did not report its final URL.";
110
113
  return blocked ? failure("BLOCKED_URL", blocked) : result;
111
114
  }
112
115
  if (input.action === "sequence") return this.executeSequence(driver, session, input, signal);
@@ -119,12 +122,12 @@ var BrowserRuntime = class {
119
122
  }
120
123
  if (this.actionDenied(input, risk)) return failure("INVALID_INPUT", `Browser action is denied by policy (${risk}).`);
121
124
  if (input.action === "click" && target?.href) {
122
- const blocked = this.validateUrl(target.href);
125
+ const blocked = this.validateUrl(target.href, session.taskKey);
123
126
  if (blocked) return failure("BLOCKED_URL", blocked);
124
127
  if (this.crossDomainPolicy(session.observation?.url, target.href) === "deny") return failure("BLOCKED_URL", "Cross-domain navigation is denied by policy.");
125
128
  }
126
129
  const result = await driver.perform(session.id, input, signal);
127
- return withRisk(this.validateRedirect(session.observation?.url ?? "", result), risk);
130
+ return withRisk(this.validateRedirect(session.observation?.url ?? "", result, session.taskKey), risk);
128
131
  }
129
132
  async executeSequence(driver, session, input, signal) {
130
133
  const config = this.options.getConfig();
@@ -171,27 +174,27 @@ var BrowserRuntime = class {
171
174
  "sensitive"
172
175
  ].includes(risk);
173
176
  }
174
- validateUrl(url) {
177
+ validateUrl(url, taskKey) {
175
178
  if (containsApiKeyPattern(url)) return "URL contains a credential-like value.";
176
179
  try {
177
180
  const host = new URL(url).hostname.toLowerCase();
178
- assertBrowserUrlAllowed(url, { privateHostAllowed: this.options.getConfig().security.allowedPrivateHosts.some((candidate) => candidate.toLowerCase() === host) });
181
+ assertBrowserUrlAllowed(url, { privateHostAllowed: this.options.getConfig().security.allowedPrivateHosts.some((candidate) => candidate.toLowerCase() === host) || this.options.isTrustedSetupUrl?.(taskKey, url) === true });
179
182
  return null;
180
183
  } catch (error) {
181
184
  return error instanceof Error ? error.message : String(error);
182
185
  }
183
186
  }
184
- validateRedirect(requestedUrl, result) {
187
+ validateRedirect(requestedUrl, result, taskKey) {
185
188
  if (!result.ok || !result.receipt.observation) return result;
186
189
  const finalUrl = result.receipt.observation.url;
187
190
  if (finalUrl === requestedUrl) return result;
188
- const blocked = this.blockedRedirect(finalUrl);
191
+ const blocked = this.blockedRedirect(finalUrl, taskKey);
189
192
  return blocked ? failure("BLOCKED_URL", blocked, result.receipt.observation) : result;
190
193
  }
191
- blockedRedirect(finalUrl) {
194
+ blockedRedirect(finalUrl, taskKey) {
192
195
  try {
193
196
  const host = new URL(finalUrl).hostname.toLowerCase();
194
- return checkPostRedirectUrl(finalUrl, { privateHostAllowed: this.options.getConfig().security.allowedPrivateHosts.some((candidate) => candidate.toLowerCase() === host) });
197
+ return checkPostRedirectUrl(finalUrl, { privateHostAllowed: this.options.getConfig().security.allowedPrivateHosts.some((candidate) => candidate.toLowerCase() === host) || this.options.isTrustedSetupUrl?.(taskKey, finalUrl) === true });
195
198
  } catch {
196
199
  return "Browser returned an invalid final URL.";
197
200
  }
@@ -222,7 +225,16 @@ var BrowserRuntime = class {
222
225
  async resolveSession(taskKey, requestedId) {
223
226
  const existing = this.sessionsByTask.get(taskKey);
224
227
  if (existing) return !requestedId || requestedId === existing.id ? existing : null;
225
- if (requestedId) return null;
228
+ if (requestedId) {
229
+ if (!this.options.canRestoreSetupSession?.(taskKey, requestedId)) return null;
230
+ const restored = {
231
+ id: requestedId,
232
+ taskKey,
233
+ lastUsedAt: Date.now()
234
+ };
235
+ this.sessionsByTask.set(taskKey, restored);
236
+ return restored;
237
+ }
226
238
  const session = {
227
239
  id: randomUUID(),
228
240
  taskKey,
@@ -0,0 +1,5 @@
1
+ import type { BrowserSetup } from './types.js';
2
+ export declare function readBrowserSetup(id: string): BrowserSetup | undefined;
3
+ export declare function findBrowserSetup(conversationId: string, agentId: string): BrowserSetup | undefined;
4
+ export declare function createBrowserSetup(conversationId: string, agentId: string, configRevision: string): BrowserSetup;
5
+ export declare function saveBrowserSetup(next: BrowserSetup, expectedRevision: number): BrowserSetup;
@@ -0,0 +1,42 @@
1
+ import { getSqliteDatabase, init_transaction, runSqliteWriteTransaction } from "../../storage/sqlite/transaction.js";
2
+ import { randomUUID } from "node:crypto";
3
+ //#region src/browser/setup/repository.ts
4
+ init_transaction();
5
+ function readBrowserSetup(id) {
6
+ const row = getSqliteDatabase().prepare("SELECT payload_json FROM browser_setup_runs WHERE id = ?").get(id);
7
+ return row ? JSON.parse(row.payload_json) : void 0;
8
+ }
9
+ function findBrowserSetup(conversationId, agentId) {
10
+ const row = getSqliteDatabase().prepare("SELECT payload_json FROM browser_setup_runs WHERE conversation_id = ? AND agent_id = ? ORDER BY updated_at DESC, rowid DESC LIMIT 1").get(conversationId, agentId);
11
+ return row ? JSON.parse(row.payload_json) : void 0;
12
+ }
13
+ function createBrowserSetup(conversationId, agentId, configRevision) {
14
+ return runSqliteWriteTransaction((db) => {
15
+ const existing = findBrowserSetup(conversationId, agentId);
16
+ if (existing && existing.state !== "cancelled" && existing.state !== "completed") return existing;
17
+ const now = Date.now();
18
+ const setup = {
19
+ id: randomUUID(),
20
+ conversationId,
21
+ agentId,
22
+ configRevision,
23
+ state: "plan_ready",
24
+ revision: 1,
25
+ createdAt: now,
26
+ updatedAt: now
27
+ };
28
+ db.prepare("INSERT INTO browser_setup_runs (id, conversation_id, agent_id, revision, updated_at, payload_json) VALUES (?, ?, ?, ?, ?, ?)").run(setup.id, conversationId, agentId, setup.revision, now, JSON.stringify(setup));
29
+ return setup;
30
+ });
31
+ }
32
+ function saveBrowserSetup(next, expectedRevision) {
33
+ next = {
34
+ ...next,
35
+ revision: expectedRevision + 1,
36
+ updatedAt: Date.now()
37
+ };
38
+ if (getSqliteDatabase().prepare("UPDATE browser_setup_runs SET revision = ?, updated_at = ?, payload_json = ? WHERE id = ? AND revision = ?").run(next.revision, next.updatedAt, JSON.stringify(next), next.id, expectedRevision).changes !== 1) throw new Error("SETUP_CONFLICT: Configuration progress changed. Read it again.");
39
+ return next;
40
+ }
41
+ //#endregion
42
+ export { createBrowserSetup, findBrowserSetup, readBrowserSetup, saveBrowserSetup };