@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.
- package/dist/browser-ext/_locales/en/messages.json +4 -1
- package/dist/browser-ext/_locales/zh_CN/messages.json +4 -1
- package/dist/browser-ext/dist/assets/{page-context-MuzjMBlJ.js → page-context-CVvmTWZb.js} +33 -9
- package/dist/browser-ext/dist/background.js +39 -10
- package/dist/browser-ext/dist/sidepanel.html +1 -1
- package/dist/browser-ext/dist/sidepanel.js +22 -3
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CJFwitU2.js → agent-avatar-dicebear-CufU7cqe.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DHDAcss8.js +1 -0
- package/dist/gateway/static/root/assets/agents-BV3dAoa0.js +77 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-DgwqNi5i.js → app-management-settings-panel-D4-n1uBg.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-C0wJfFyz.js → appearance-settings-Cgn4eNsX.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-BOYzD7Zz.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-Ch7wolHE.js → archive-plugin-B4btCsiV.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-DK7AU79v.js → automations-page-yb1EL_3N.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace-J7LJ4RuV.js +8 -0
- package/dist/gateway/static/root/assets/{binary-plugins-B4HcHYsC.js → binary-plugins-DQT9e258.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BQAWEZF-.js → block-editor-B50rU9tL.js} +4 -4
- package/dist/gateway/static/root/assets/browser-automations-page-BiQz6lJA.js +3 -0
- package/dist/gateway/static/root/assets/browser-settings-page-W9kuxBbE.js +3 -0
- package/dist/gateway/static/root/assets/capabilities-page-iXe87MdG.js +2 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-CRLSXe7Z.js +8 -0
- package/dist/gateway/static/root/assets/{capability-header-actions-D4Yq35fD.js → capability-header-actions-BMnbzHms.js} +1 -1
- package/dist/gateway/static/root/assets/channels-CrRJkOKX.js +1 -0
- package/dist/gateway/static/root/assets/{chat-terminal-dock-zdMySdMm.js → chat-terminal-dock-Bd3af4gL.js} +2 -2
- package/dist/gateway/static/root/assets/{computer-settings-page-C5Z6Jr0p.js → computer-settings-page-DXfqy7xh.js} +1 -1
- package/dist/gateway/static/root/assets/{connector-service-page-Cxus0qrZ.js → connector-service-page-B-cw0CPU.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-DbFxc0Fd.js +1 -0
- package/dist/gateway/static/root/assets/{date-picker-DuyyD6jz.js → date-picker-CqzXevH6.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-BZN72rrk.js → dependency-picker-pgT4Gyaf.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BcyP7Z6v.js → desktop-pet-settings-Dy_-i2L8.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-BtvTIDCi.js → desktop-pet-zICeqw9c.js} +1 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-ByGbxqkg.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-DOusKz-O.js → directory-picker-path-field-C-aQ7qoM.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-Bt4FwsFu.js → extension-debug-page-2NwpYkS5.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-0vsxX3FB.js → extension-page-y9J0z7_Q.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-5Pt5_ELc.js → extension-settings-page-BtowusUh.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-realtime-C52ghnBK.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-BYSKzUMR.js +1 -0
- package/dist/gateway/static/root/assets/highlight-code-Cz6eeDH5.js +1 -0
- package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-Cbk7eMlg.js → highlighted-body-KQOG7T2V-BiAa8HKC.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-Cd1BjYHj.js +2 -0
- package/dist/gateway/static/root/assets/{image-generation-api-CAS8QVla.js → image-generation-api-gBFro48x.js} +1 -1
- package/dist/gateway/static/root/assets/{imports-page-BCLBYzV1.js → imports-page-BZQpXgjj.js} +1 -1
- package/dist/gateway/static/root/assets/index-Cy16gQST.css +1 -0
- package/dist/gateway/static/root/assets/index-DRyGCING.js +710 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CM_kt5XN.js → keyboard-shortcuts-settings-CYunR34k.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-B_Ac1lly.js +1 -0
- package/dist/gateway/static/root/assets/{local-apps-page-BTSOChhk.js → local-apps-page-w-J5bY02.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-Dnjvvx2i.js +2 -0
- package/dist/gateway/static/root/assets/management-settings-w1EDwgLW.js +1 -0
- package/dist/gateway/static/root/assets/{media-plugins-DYQDjCVQ.js → media-plugins-CIx1koXw.js} +1 -1
- package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-DISkHmEC.js +1 -0
- package/dist/gateway/static/root/assets/note-detail-panel-CNkxMTFU.js +5 -0
- package/dist/gateway/static/root/assets/note-image-lightbox-D5GH2wuC.js +1 -0
- package/dist/gateway/static/root/assets/notes-page-DZgKqFBG.js +8 -0
- package/dist/gateway/static/root/assets/{page-context-capture-button-DVn_dU83.js → page-context-capture-button-DusR2Itk.js} +1 -1
- package/dist/gateway/static/root/assets/personal-page-CR5JFkQ9.js +1 -0
- package/dist/gateway/static/root/assets/{personal-settings-panel-C16Iqe7X.js → personal-settings-panel-B7undvlr.js} +1 -1
- package/dist/gateway/static/root/assets/phone-model-picker-fWDltRUJ.js +1 -0
- package/dist/gateway/static/root/assets/phone-settings-page-CsOirAiy.js +1 -0
- package/dist/gateway/static/root/assets/plugin-details-dialog-BOBVIB3j.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-RlHk76TI.js → preview-open-alternatives-CMxjsKSw.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-Dh-HqLrE.js → product-open-page-CrlyJx5M.js} +1 -1
- package/dist/gateway/static/root/assets/{product-preview-content-c3K-4DWc.js → product-preview-content-B-Jhzi7W.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-DoYUbtcI.js +7 -0
- package/dist/gateway/static/root/assets/{projects-page-B8HAt9sD.js → projects-page-xGQd9oAZ.js} +1 -1
- package/dist/gateway/static/root/assets/refresh-cw-BRlGaq2w.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-Gs0Xkws9.js +1 -0
- package/dist/gateway/static/root/assets/runtime-tools-settings-panel-Bwg7U7TR.js +1 -0
- package/dist/gateway/static/root/assets/{schema-form-CXO1B1k4.js → schema-form-ByzTNR_C.js} +1 -1
- package/dist/gateway/static/root/assets/{session-manager-CA1NkTR_.js → session-manager-B_h0jlLb.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-XzK1ftbR.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-D_8Mvnog.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-BQxKmbbJ.js → settings-form-section-Brgd8IOV.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-DvVMpKiA.js → settings-page-B6Mv02eg.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-header-BWyxJKOG.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-layout-B20AfmFG.js → settings-page-layout-DLHcS-0T.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-DSCj5yis.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-XgZk8Oa1.js → share-preview-page-DF8Of0aZ.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-DXNPe4Tl.js +1 -0
- package/dist/gateway/static/root/assets/{skill-api-CV-WT2hj.js → skill-api-CKCDCHLq.js} +1 -1
- package/dist/gateway/static/root/assets/skill-reload-api-BH0WF6Br.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-DFWMVRW4.js +2 -0
- package/dist/gateway/static/root/assets/system-settings-panel-DB-ttubV.js +1 -0
- package/dist/gateway/static/root/assets/task-detail-page-BJY7c8wb.js +2 -0
- package/dist/gateway/static/root/assets/{task-result-evidence-D-gGKNX9.js → task-result-evidence-B-vt1BgK.js} +1 -1
- package/dist/gateway/static/root/assets/{tracing-settings-page-CQw3G-FB.js → tracing-settings-page-Dt51Z_ws.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-B2HnCi0U.js → tunnel-api-Dok3t7Qe.js} +1 -1
- package/dist/gateway/static/root/assets/usage-page-lJym5G7F.js +1 -0
- package/dist/gateway/static/root/assets/{use-autosave-Dc0nwUpX.js → use-autosave-D4EMIB-V.js} +1 -1
- package/dist/gateway/static/root/assets/user-model-page-BXdNkITp.js +2 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-BSWXZkGa.js → voice-api-key-field-CO3KuanA.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-H3cdwq3l.js → work-discovery-overlay-BJRtgQM3.js} +1 -1
- package/dist/gateway/static/root/assets/wrench-DFb4r8bH.js +1 -0
- package/dist/gateway/static/root/assets/xopc-cloud-account-card-CS-_GxHV.js +1 -0
- package/dist/gateway/static/root/browser-setup-test.html +5 -0
- package/dist/gateway/static/root/browser-setup-test.js +7 -0
- package/dist/gateway/static/root/index.html +7 -4
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/browser-setup.js +6 -0
- package/dist/packages/gateway-contract/src/index.js +4 -0
- package/dist/packages/gateway-contract/src/phone-setup.js +6 -0
- package/dist/packages/realtime-client/src/index.js +1 -0
- package/dist/packages/realtime-protocol/src/index.js +1 -0
- package/dist/src/agent/agent-manager.d.ts +4 -0
- package/dist/src/agent/agent-manager.js +12 -1
- package/dist/src/agent/embedded/tool-search-extension.js +14 -1
- package/dist/src/agent/service.d.ts +1 -0
- package/dist/src/agent/service.js +5 -0
- package/dist/src/agent/service.types.d.ts +2 -0
- package/dist/src/agent/tool-manuals/phone.d.ts +1 -1
- package/dist/src/agent/tool-manuals/phone.js +1 -1
- package/dist/src/agent/tool-manuals/xopc-configuration.d.ts +2 -0
- package/dist/src/agent/tool-manuals/xopc-configuration.js +25 -0
- package/dist/src/agent/tools/browser/tool/browser-use-tool.d.ts +2 -0
- package/dist/src/agent/tools/browser/tool/browser-use-tool.js +6 -2
- package/dist/src/agent/tools/factory.d.ts +3 -0
- package/dist/src/agent/tools/factory.js +11 -1
- package/dist/src/agent/tools/phone-use-tool.d.ts +1 -0
- package/dist/src/agent/tools/phone-use-tool.js +9 -1
- package/dist/src/agent/tools/tool-manual-tool.d.ts +1 -1
- package/dist/src/agent/tools/tool-manual-tool.js +3 -1
- package/dist/src/agent/tools/toolExposure.js +3 -0
- package/dist/src/agent/tools/xopc-use-tool.d.ts +4 -2
- package/dist/src/agent/tools/xopc-use-tool.js +40 -2
- package/dist/src/browser/drivers/browser-driver.d.ts +2 -2
- package/dist/src/browser/drivers/extension-driver.d.ts +2 -2
- package/dist/src/browser/drivers/extension-driver.js +6 -3
- package/dist/src/browser/providers/browser-ext-install.js +2 -1
- package/dist/src/browser/runtime/browser-runtime.d.ts +4 -1
- package/dist/src/browser/runtime/browser-runtime.js +29 -17
- package/dist/src/browser/setup/repository.d.ts +5 -0
- package/dist/src/browser/setup/repository.js +42 -0
- package/dist/src/browser/setup/service.d.ts +152 -0
- package/dist/src/browser/setup/service.js +404 -0
- package/dist/src/browser/setup/types.d.ts +38 -0
- package/dist/src/browser/setup/types.js +1 -0
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/config/schema.d.ts +2 -0
- package/dist/src/connectors/connected-source-understanding.js +1 -1
- package/dist/src/connectors/learning-coordinator.js +1 -1
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/hono/routes/browser-setup.d.ts +3 -0
- package/dist/src/gateway/hono/routes/browser-setup.js +114 -0
- package/dist/src/gateway/hono/routes/browser.d.ts +2 -1
- package/dist/src/gateway/hono/routes/browser.js +1 -1
- package/dist/src/gateway/hono/routes/connectors.js +7 -7
- package/dist/src/gateway/hono/routes/home.js +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/phone-dependencies.js +1 -1
- package/dist/src/gateway/hono/routes/phone-setup.d.ts +7 -0
- package/dist/src/gateway/hono/routes/phone-setup.js +162 -0
- package/dist/src/gateway/hono/routes/phone.js +3 -7
- package/dist/src/gateway/hono/routes/public-gateway.js +3 -1
- package/dist/src/gateway/provider-model-setup.js +1 -1
- package/dist/src/gateway/service/config-coordinator.d.ts +2 -0
- package/dist/src/gateway/service.d.ts +5 -0
- package/dist/src/gateway/service.js +57 -12
- package/dist/src/gateway/xopc-cloud-capability-setup.js +7 -1
- package/dist/src/home-intelligence/host.js +2 -2
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/phone/adb-driver.d.ts +2 -0
- package/dist/src/phone/adb-driver.js +35 -17
- package/dist/src/phone/contracts.d.ts +40 -0
- package/dist/src/phone/contracts.js +17 -4
- package/dist/src/phone/model-adapter.d.ts +59 -2
- package/dist/src/phone/model-adapter.js +107 -13
- package/dist/src/phone/numeric-entry.d.ts +31 -0
- package/dist/src/phone/numeric-entry.js +126 -0
- package/dist/src/phone/planning.d.ts +27 -0
- package/dist/src/phone/planning.js +79 -0
- package/dist/src/phone/recovery.d.ts +37 -0
- package/dist/src/phone/recovery.js +81 -0
- package/dist/src/phone/repository.d.ts +1 -0
- package/dist/src/phone/repository.js +3 -0
- package/dist/src/phone/runtime.js +232 -54
- package/dist/src/phone/semantic-controls.d.ts +13 -0
- package/dist/src/phone/semantic-controls.js +76 -0
- package/dist/src/phone/setup/service.d.ts +148 -0
- package/dist/src/phone/setup/service.js +251 -0
- package/dist/src/providers/presentation.js +2 -2
- package/dist/src/providers/xopc-cloud-model-source.js +6 -2
- package/dist/src/realtime/runtime.js +1 -1
- package/dist/src/storage/sqlite/schema.d.ts +2 -2
- package/dist/src/storage/sqlite/schema.js +7 -7
- package/dist/src/storage/sqlite/schema.sql +10 -0
- package/dist/src/storage/sqlite/upgrades/260_browser_setup.sql +9 -0
- package/dist/src/user-context/sources/index.js +1 -1
- package/dist/src/work-discovery/service.js +2 -2
- package/package.json +6 -6
- package/skills/tools/configure-xopc/SKILL.md +47 -0
- package/skills/tools/configure-xopc/references/browser/acceptance.md +31 -0
- package/skills/tools/configure-xopc/references/browser/chrome-extension.md +19 -0
- package/skills/tools/configure-xopc/references/browser/overview.md +22 -0
- package/skills/tools/configure-xopc/references/browser/permissions.md +25 -0
- package/skills/tools/configure-xopc/references/browser/troubleshooting.md +24 -0
- package/skills/tools/configure-xopc/references/phone/acceptance.md +25 -0
- package/skills/tools/configure-xopc/references/phone/aliyun.md +29 -0
- package/skills/tools/configure-xopc/references/phone/dependencies.md +18 -0
- package/skills/tools/configure-xopc/references/phone/local.md +19 -0
- package/skills/tools/configure-xopc/references/phone/models.md +18 -0
- package/skills/tools/configure-xopc/references/phone/overview.md +30 -0
- package/skills/tools/configure-xopc/references/phone/troubleshooting.md +24 -0
- package/skills/tools/configure-xopc/references/scene-index.md +12 -0
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DclyJr1N.js +0 -1
- package/dist/gateway/static/root/assets/agents-jhErNtzi.js +0 -77
- package/dist/gateway/static/root/assets/apps-page-C0LcnI4h.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-CBxiV34x.js +0 -8
- package/dist/gateway/static/root/assets/browser-automations-page-vJIrTxdi.js +0 -3
- package/dist/gateway/static/root/assets/browser-settings-page-in5Roh_E.js +0 -3
- package/dist/gateway/static/root/assets/capabilities-page-CiGx8wpQ.js +0 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-a8kqjzQu.js +0 -8
- package/dist/gateway/static/root/assets/channels-C_CxcjKT.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-DCBO2CxK.js +0 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-Ctbgr50F.js +0 -1
- package/dist/gateway/static/root/assets/gateway-realtime-DkWCHVzI.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-BsVOcmV5.js +0 -1
- package/dist/gateway/static/root/assets/highlight-code-C8eS9949.js +0 -1
- package/dist/gateway/static/root/assets/home-page-BZDZnmyc.js +0 -2
- package/dist/gateway/static/root/assets/index-Bys6njTY.js +0 -710
- package/dist/gateway/static/root/assets/index-Vsf0bj5e.css +0 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-Dk87jtcc.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-D3gVdm8S.js +0 -2
- package/dist/gateway/static/root/assets/management-settings-BQNYFE4J.js +0 -1
- package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-ChweJKTv.js +0 -1
- package/dist/gateway/static/root/assets/note-detail-panel-CYKwUCte.js +0 -5
- package/dist/gateway/static/root/assets/note-image-lightbox-BTSZb9ZT.js +0 -1
- package/dist/gateway/static/root/assets/notes-page-CJ5nhWfL.js +0 -8
- package/dist/gateway/static/root/assets/personal-page-DcW14lqn.js +0 -1
- package/dist/gateway/static/root/assets/phone-dependencies-card-D3l9uPFM.js +0 -1
- package/dist/gateway/static/root/assets/phone-model-picker-DOv38vBn.js +0 -1
- package/dist/gateway/static/root/assets/phone-settings-page-DUHp1q_9.js +0 -1
- package/dist/gateway/static/root/assets/plugin-details-dialog-BpfgDlVa.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-GD8TxKad.js +0 -7
- package/dist/gateway/static/root/assets/remote-access-hub-D3el7lHK.js +0 -1
- package/dist/gateway/static/root/assets/runtime-tools-settings-panel-Dbskk6RG.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-07liaSHH.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BGjU8rfN.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-header-CYpuTXUW.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-DKo_VTx-.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-BODFqK8k.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-Ce6OUk2C.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-B7ehOIML.js +0 -2
- package/dist/gateway/static/root/assets/system-settings-panel-CxJkZRT4.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-CxRR0QuW.js +0 -2
- package/dist/gateway/static/root/assets/usage-page-uUEkO38r.js +0 -1
- package/dist/gateway/static/root/assets/user-model-page-ICUutbKL.js +0 -2
- 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
|
-
|
|
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
|
|
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,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)
|
|
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 };
|