@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
|
@@ -8,10 +8,15 @@ import { PhoneInteractiveSession } from "./interactive-session.js";
|
|
|
8
8
|
import { AdbPhoneDriver, resolveAdbPath } from "./adb-driver.js";
|
|
9
9
|
import { LocalScrcpyProvider } from "./scrcpy.js";
|
|
10
10
|
import { CloudPhones } from "./cloud-phones.js";
|
|
11
|
+
import { advancePhonePlan, buildPhonePlan, createPhonePlanState } from "./planning.js";
|
|
12
|
+
import { canDispatchPhoneAction, observationSignature, phoneCompletionSatisfied } from "./reliability.js";
|
|
13
|
+
import { groundPhoneCoordinateAction, resolvePhoneSemanticAction } from "./semantic-controls.js";
|
|
11
14
|
import { createPhoneModel, isAboutScreen } from "./model-adapter.js";
|
|
12
15
|
import { PhoneRepository } from "./repository.js";
|
|
13
|
-
import {
|
|
16
|
+
import { confirmPhoneFocusRecovery, phoneBackRecovery, phoneUnfocusedTypingHint, preparePhoneFocusRecovery } from "./recovery.js";
|
|
17
|
+
import { createPhoneNumericEntry, nextPhoneNumericEntry } from "./numeric-entry.js";
|
|
14
18
|
import { createHash, randomUUID } from "node:crypto";
|
|
19
|
+
import { setTimeout } from "node:timers/promises";
|
|
15
20
|
//#region src/phone/runtime.ts
|
|
16
21
|
init_logger();
|
|
17
22
|
init_contracts();
|
|
@@ -226,7 +231,7 @@ var PhoneRuntime = class {
|
|
|
226
231
|
await this.dispatch(session, {
|
|
227
232
|
type: "launch",
|
|
228
233
|
package: input.appPackage
|
|
229
|
-
}, observation, context, signal, epoch);
|
|
234
|
+
}, observation, context, signal, epoch, false, input.settingsPage);
|
|
230
235
|
} finally {
|
|
231
236
|
observation.frame.fill(0);
|
|
232
237
|
}
|
|
@@ -315,7 +320,29 @@ var PhoneRuntime = class {
|
|
|
315
320
|
let repairs = 0;
|
|
316
321
|
let completionCorrections = 0;
|
|
317
322
|
let recoveryHint;
|
|
323
|
+
const enhancements = {
|
|
324
|
+
semanticControls: context.config.phone.semanticControls,
|
|
325
|
+
hierarchicalPlanning: context.config.phone.hierarchicalPlanning
|
|
326
|
+
};
|
|
327
|
+
const derivedPlan = enhancements.hierarchicalPlanning ? buildPhonePlan(input.goal, completion) : void 0;
|
|
328
|
+
let planning = derivedPlan ? createPhonePlanState(derivedPlan) : void 0;
|
|
329
|
+
if (enhancements.hierarchicalPlanning) {
|
|
330
|
+
session.report.diagnostics.plannedSubgoals = planning?.steps.length ?? 0;
|
|
331
|
+
session.report.diagnostics.completedSubgoals = 0;
|
|
332
|
+
}
|
|
318
333
|
let changed = false;
|
|
334
|
+
const entryObservation = {
|
|
335
|
+
...observation,
|
|
336
|
+
frame: Buffer.alloc(0),
|
|
337
|
+
elements: structuredClone(observation.elements)
|
|
338
|
+
};
|
|
339
|
+
const trajectoryStart = session.trajectoryCount;
|
|
340
|
+
let pendingFocus;
|
|
341
|
+
let numericIntent;
|
|
342
|
+
let numericPending;
|
|
343
|
+
const focusedIntents = /* @__PURE__ */ new Set();
|
|
344
|
+
let backReplans = 0;
|
|
345
|
+
let focusReplans = 0;
|
|
319
346
|
let previousSignature = observationSignature(observation);
|
|
320
347
|
let controlEpoch = session.report.controlEpoch;
|
|
321
348
|
const attempts = /* @__PURE__ */ new Map();
|
|
@@ -331,12 +358,51 @@ var PhoneRuntime = class {
|
|
|
331
358
|
if (controlEpoch !== epoch) {
|
|
332
359
|
attempts.clear();
|
|
333
360
|
recoveryHint = "The user handed back control. Re-evaluate the fresh screen before continuing.";
|
|
361
|
+
pendingFocus = void 0;
|
|
362
|
+
numericIntent = void 0;
|
|
363
|
+
numericPending = void 0;
|
|
334
364
|
controlEpoch = epoch;
|
|
335
365
|
}
|
|
336
366
|
const signature = observationSignature(observation);
|
|
337
367
|
if (signature !== previousSignature) changed = true;
|
|
338
368
|
previousSignature = signature;
|
|
339
369
|
this.recordObservation(session, observation);
|
|
370
|
+
if (planning) {
|
|
371
|
+
planning = advancePhonePlan(planning, observation);
|
|
372
|
+
session.report.diagnostics.completedSubgoals = planning.verifiedSteps;
|
|
373
|
+
}
|
|
374
|
+
let numericStep;
|
|
375
|
+
if (enhancements.hierarchicalPlanning) {
|
|
376
|
+
numericIntent ??= createPhoneNumericEntry(input.goal, completion, observation);
|
|
377
|
+
if (numericIntent) {
|
|
378
|
+
let next = nextPhoneNumericEntry(numericIntent, observation, numericPending);
|
|
379
|
+
for (let poll = 0; numericPending && next.kind === "blocked" && poll < 2; poll++) {
|
|
380
|
+
await setTimeout(150, void 0, { signal });
|
|
381
|
+
observation.frame.fill(0);
|
|
382
|
+
observation = await this.observe(session, signal);
|
|
383
|
+
this.guard(session, context);
|
|
384
|
+
this.allowedScreen(session, observation);
|
|
385
|
+
if (epoch !== session.report.controlEpoch || session.report.controlOwner !== "agent") break;
|
|
386
|
+
next = nextPhoneNumericEntry(numericIntent, observation, numericPending);
|
|
387
|
+
}
|
|
388
|
+
if (epoch !== session.report.controlEpoch || session.report.controlOwner !== "agent") continue;
|
|
389
|
+
this.recordObservation(session, observation);
|
|
390
|
+
if (next.kind === "blocked") {
|
|
391
|
+
session.report.diagnostics.numericMismatches = (session.report.diagnostics.numericMismatches ?? 0) + 1;
|
|
392
|
+
session.report.status = "paused";
|
|
393
|
+
session.report.controlOwner = "none";
|
|
394
|
+
session.report.controlEpoch++;
|
|
395
|
+
session.report.stopReason = "numeric_state_mismatch";
|
|
396
|
+
session.report.message = "Numeric input was not confirmed. Inspect the displayed value before continuing; no key was retried.";
|
|
397
|
+
session.budget?.pause();
|
|
398
|
+
await session.stream?.revoke();
|
|
399
|
+
return this.publish(session);
|
|
400
|
+
}
|
|
401
|
+
if (numericPending) session.report.diagnostics.numericConfirmedSteps = (session.report.diagnostics.numericConfirmedSteps ?? 0) + 1;
|
|
402
|
+
numericPending = void 0;
|
|
403
|
+
if (next.kind === "action") numericStep = next.step;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
340
406
|
if (session.report.mock ? isAboutScreen(observation) : Boolean(completion && phoneCompletionSatisfied(completion, observation, changed))) {
|
|
341
407
|
const confirmation = await this.observe(session, signal);
|
|
342
408
|
try {
|
|
@@ -350,6 +416,10 @@ var PhoneRuntime = class {
|
|
|
350
416
|
continue;
|
|
351
417
|
}
|
|
352
418
|
this.recordObservation(session, confirmation);
|
|
419
|
+
if (planning) {
|
|
420
|
+
planning = advancePhonePlan(planning, confirmation);
|
|
421
|
+
session.report.diagnostics.completedSubgoals = planning.verifiedSteps;
|
|
422
|
+
}
|
|
353
423
|
session.report.verified = true;
|
|
354
424
|
session.report.verificationLevel = "ui";
|
|
355
425
|
session.report.status = "completed";
|
|
@@ -364,52 +434,72 @@ var PhoneRuntime = class {
|
|
|
364
434
|
session.report.stopReason = "action_limit";
|
|
365
435
|
return this.publish(session);
|
|
366
436
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
437
|
+
let decision = numericStep ? {
|
|
438
|
+
kind: "action",
|
|
439
|
+
action: numericStep.action
|
|
440
|
+
} : void 0;
|
|
441
|
+
let resumedText = false;
|
|
442
|
+
if (!decision && pendingFocus) {
|
|
443
|
+
const pending = pendingFocus;
|
|
444
|
+
pendingFocus = void 0;
|
|
445
|
+
const intended = confirmPhoneFocusRecovery(pending, observation);
|
|
446
|
+
if (intended) {
|
|
447
|
+
decision = {
|
|
448
|
+
kind: "action",
|
|
449
|
+
action: intended
|
|
450
|
+
};
|
|
451
|
+
resumedText = true;
|
|
452
|
+
} else recoveryHint = "The focus transition was not confirmed. No text was sent. Reinspect the intended editable field before another action.";
|
|
453
|
+
}
|
|
454
|
+
if (!decision) {
|
|
455
|
+
if (session.report.modelRequests >= context.config.phone.maxModelRequests) throw new PhoneError("PHONE_MODEL_LIMIT", "Phone model request limit reached");
|
|
456
|
+
session.report.modelRequests++;
|
|
457
|
+
session.report.currentAction = void 0;
|
|
458
|
+
this.publish(session);
|
|
459
|
+
session.prediction = new AbortController();
|
|
460
|
+
try {
|
|
461
|
+
decision = await session.adapter.predict({
|
|
462
|
+
goal: input.goal,
|
|
463
|
+
observation,
|
|
464
|
+
completion,
|
|
465
|
+
allowedApps: session.device.allowedApps,
|
|
466
|
+
appAccess: phoneAppAccess(session.device),
|
|
467
|
+
applications: session.applications.filter((app) => phoneAllowsApp(session.device, app.packageName)),
|
|
468
|
+
history: session.report.steps.map((step) => step.action),
|
|
469
|
+
trajectory: session.trajectory,
|
|
470
|
+
progressSummary: JSON.stringify({
|
|
471
|
+
dispatched: session.trajectoryCount,
|
|
472
|
+
earlier: [...session.trajectorySummary, ...session.trajectory.slice(0, -8).map((entry) => JSON.stringify({
|
|
473
|
+
action: entry.action,
|
|
474
|
+
effect: entry.effect,
|
|
475
|
+
afterApp: entry.afterApp
|
|
476
|
+
}).slice(0, 250))].slice(-12)
|
|
477
|
+
}).slice(-1500),
|
|
478
|
+
recoveryHint,
|
|
479
|
+
protocolFeedback,
|
|
480
|
+
scenario: input.scenario,
|
|
481
|
+
enhancements,
|
|
482
|
+
planning
|
|
483
|
+
}, AbortSignal.any([signal, session.prediction.signal]));
|
|
484
|
+
} catch (error) {
|
|
485
|
+
if (!signal.aborted && epoch !== session.report.controlEpoch) {
|
|
486
|
+
await this.waitForAgent(session, signal);
|
|
487
|
+
observation.frame.fill(0);
|
|
488
|
+
observation = await this.observe(session, signal);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
if (!signal.aborted && error instanceof PhoneError && ["PHONE_MODEL_PROTOCOL", "PHONE_MODEL_TRUNCATED"].includes(error.code) && repairs < 1 && session.report.modelRequests < context.config.phone.maxModelRequests) {
|
|
492
|
+
repairs++;
|
|
493
|
+
session.report.diagnostics.protocolRepairs++;
|
|
494
|
+
protocolFeedback = "Your previous output was invalid or truncated. Return exactly one complete action in the specified format with no extra prose.";
|
|
495
|
+
observation.frame.fill(0);
|
|
496
|
+
observation = await this.observe(session, signal);
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
throw error;
|
|
500
|
+
} finally {
|
|
501
|
+
session.prediction = void 0;
|
|
409
502
|
}
|
|
410
|
-
throw error;
|
|
411
|
-
} finally {
|
|
412
|
-
session.prediction = void 0;
|
|
413
503
|
}
|
|
414
504
|
signal.throwIfAborted();
|
|
415
505
|
this.guard(session, context);
|
|
@@ -420,6 +510,11 @@ var PhoneRuntime = class {
|
|
|
420
510
|
continue;
|
|
421
511
|
}
|
|
422
512
|
protocolFeedback = void 0;
|
|
513
|
+
if (enhancements.hierarchicalPlanning && !planning && decision.plan) {
|
|
514
|
+
planning = advancePhonePlan(createPhonePlanState(decision.plan), observation);
|
|
515
|
+
session.report.diagnostics.plannedSubgoals = planning.steps.length;
|
|
516
|
+
session.report.diagnostics.completedSubgoals = planning.verifiedSteps;
|
|
517
|
+
}
|
|
423
518
|
if (decision.kind !== "action") {
|
|
424
519
|
if (decision.kind === "complete" && !session.report.mock && (input.completion || input.expectText) && completionCorrections < 1 && session.report.modelRequests < context.config.phone.maxModelRequests) {
|
|
425
520
|
completionCorrections++;
|
|
@@ -447,10 +542,77 @@ var PhoneRuntime = class {
|
|
|
447
542
|
}
|
|
448
543
|
return this.publish(session);
|
|
449
544
|
}
|
|
545
|
+
let preparedFocus;
|
|
546
|
+
if (enhancements.hierarchicalPlanning) {
|
|
547
|
+
const operationActions = session.trajectoryCount - trajectoryStart;
|
|
548
|
+
const back = phoneBackRecovery({
|
|
549
|
+
action: decision.action,
|
|
550
|
+
observation,
|
|
551
|
+
entryObservation,
|
|
552
|
+
completion,
|
|
553
|
+
trajectory: operationActions ? session.trajectory.slice(-operationActions) : []
|
|
554
|
+
});
|
|
555
|
+
if (back) {
|
|
556
|
+
session.report.diagnostics.backReplans = (session.report.diagnostics.backReplans ?? 0) + 1;
|
|
557
|
+
if (backReplans++ >= 2) {
|
|
558
|
+
session.report.status = "paused";
|
|
559
|
+
session.report.controlOwner = "none";
|
|
560
|
+
session.report.controlEpoch++;
|
|
561
|
+
session.report.stopReason = "back_boundary";
|
|
562
|
+
session.report.message = back.hint;
|
|
563
|
+
session.budget?.pause();
|
|
564
|
+
await session.stream?.revoke();
|
|
565
|
+
return this.publish(session);
|
|
566
|
+
}
|
|
567
|
+
recoveryHint = back.hint;
|
|
568
|
+
observation.frame.fill(0);
|
|
569
|
+
observation = await this.observe(session, signal);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const focusHint = phoneUnfocusedTypingHint(decision.action, observation, completion);
|
|
573
|
+
if (focusHint) {
|
|
574
|
+
const key = JSON.stringify(decision.action);
|
|
575
|
+
preparedFocus = !focusedIntents.has(key) ? preparePhoneFocusRecovery(decision.action, observation, completion) : void 0;
|
|
576
|
+
if (preparedFocus) decision = {
|
|
577
|
+
kind: "action",
|
|
578
|
+
action: preparedFocus.focusAction
|
|
579
|
+
};
|
|
580
|
+
else {
|
|
581
|
+
session.report.diagnostics.focusReplans = (session.report.diagnostics.focusReplans ?? 0) + 1;
|
|
582
|
+
if (focusReplans++ >= 2) throw new PhoneError("PHONE_INPUT_NOT_FOCUSED", "The requested editable field could not be focused; inspect before typing", 409);
|
|
583
|
+
recoveryHint = focusHint;
|
|
584
|
+
observation.frame.fill(0);
|
|
585
|
+
observation = await this.observe(session, signal);
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
450
590
|
const fresh = await this.observe(session, signal);
|
|
451
591
|
try {
|
|
452
592
|
this.allowedScreen(session, fresh);
|
|
453
|
-
if (!canDispatchPhoneAction(decision.action, observation, fresh)) {
|
|
593
|
+
if (decision.action.type !== "tapElement" && !canDispatchPhoneAction(decision.action, observation, fresh)) {
|
|
594
|
+
session.report.diagnostics.stalePredictions++;
|
|
595
|
+
recoveryHint = "The proposed action was not dispatched because its target changed. Locate it again on the fresh screen.";
|
|
596
|
+
observation.frame.fill(0);
|
|
597
|
+
observation = fresh;
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
let action = enhancements.semanticControls ? groundPhoneCoordinateAction(decision.action, observation) : decision.action;
|
|
601
|
+
const semanticAction = action.type === "tapElement";
|
|
602
|
+
if (action.type === "tapElement") {
|
|
603
|
+
if (!enhancements.semanticControls && !preparedFocus) throw new PhoneError("PHONE_SEMANTIC_DISABLED", "Semantic controls are disabled");
|
|
604
|
+
try {
|
|
605
|
+
action = resolvePhoneSemanticAction(action, observation, fresh);
|
|
606
|
+
} catch (error) {
|
|
607
|
+
if (!(error instanceof PhoneError) || !error.code.startsWith("PHONE_SEMANTIC_")) throw error;
|
|
608
|
+
session.report.diagnostics.semanticRejections = (session.report.diagnostics.semanticRejections ?? 0) + 1;
|
|
609
|
+
recoveryHint = "The semantic target was not dispatched: select one unique fully visible non-password control from the fresh native UI.";
|
|
610
|
+
observation.frame.fill(0);
|
|
611
|
+
observation = fresh;
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (!canDispatchPhoneAction(action, observation, fresh)) {
|
|
454
616
|
session.report.diagnostics.stalePredictions++;
|
|
455
617
|
recoveryHint = "The proposed action was not dispatched because its target changed. Locate it again on the fresh screen.";
|
|
456
618
|
observation.frame.fill(0);
|
|
@@ -458,9 +620,9 @@ var PhoneRuntime = class {
|
|
|
458
620
|
continue;
|
|
459
621
|
}
|
|
460
622
|
if (epoch !== session.report.controlEpoch || session.report.controlOwner !== "agent") continue;
|
|
461
|
-
const attemptKey = `${observationSignature(fresh)}:${JSON.stringify(
|
|
623
|
+
const attemptKey = `${observationSignature(fresh)}:${JSON.stringify(action)}`;
|
|
462
624
|
const repetitions = attempts.get(attemptKey) ?? 0;
|
|
463
|
-
if (repetitions >= (
|
|
625
|
+
if (repetitions >= (action.type === "wait" ? 3 : 2)) {
|
|
464
626
|
session.report.status = "paused";
|
|
465
627
|
session.report.controlOwner = "none";
|
|
466
628
|
session.report.controlEpoch++;
|
|
@@ -471,7 +633,20 @@ var PhoneRuntime = class {
|
|
|
471
633
|
return this.publish(session);
|
|
472
634
|
}
|
|
473
635
|
attempts.set(attemptKey, repetitions + 1);
|
|
474
|
-
if (await this.dispatch(session,
|
|
636
|
+
if (await this.dispatch(session, action, fresh, context, signal, epoch)) {
|
|
637
|
+
actions++;
|
|
638
|
+
if (semanticAction && enhancements.semanticControls) session.report.diagnostics.semanticActions = (session.report.diagnostics.semanticActions ?? 0) + 1;
|
|
639
|
+
if (preparedFocus) {
|
|
640
|
+
pendingFocus = preparedFocus;
|
|
641
|
+
focusedIntents.add(JSON.stringify(preparedFocus.intendedAction));
|
|
642
|
+
session.report.diagnostics.focusRecoveries = (session.report.diagnostics.focusRecoveries ?? 0) + 1;
|
|
643
|
+
}
|
|
644
|
+
if (resumedText) session.report.diagnostics.resumedTextInputs = (session.report.diagnostics.resumedTextInputs ?? 0) + 1;
|
|
645
|
+
if (numericStep) {
|
|
646
|
+
numericPending = numericStep;
|
|
647
|
+
session.report.diagnostics.numericActions = (session.report.diagnostics.numericActions ?? 0) + 1;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
475
650
|
} finally {
|
|
476
651
|
if (observation !== fresh) fresh.frame.fill(0);
|
|
477
652
|
}
|
|
@@ -565,7 +740,7 @@ var PhoneRuntime = class {
|
|
|
565
740
|
session.report.foregroundApp = observation.package;
|
|
566
741
|
session.report.facts = [...new Set(observation.elements.flatMap((node) => node.password ? [] : [node.text, node.description]).filter(Boolean))].slice(0, 60);
|
|
567
742
|
}
|
|
568
|
-
async dispatch(session, action, observation, context, signal, epoch = session.report.controlEpoch, resetTask = false) {
|
|
743
|
+
async dispatch(session, action, observation, context, signal, epoch = session.report.controlEpoch, resetTask = false, settingsPage) {
|
|
569
744
|
return this.io(session, async () => {
|
|
570
745
|
signal.throwIfAborted();
|
|
571
746
|
this.guard(session, context);
|
|
@@ -610,7 +785,10 @@ var PhoneRuntime = class {
|
|
|
610
785
|
session.trajectorySummary = session.trajectorySummary.slice(-12);
|
|
611
786
|
}
|
|
612
787
|
try {
|
|
613
|
-
await session.driver.execute(session.device.serial, action, observation, signal,
|
|
788
|
+
await session.driver.execute(session.device.serial, action, observation, signal, settingsPage ? {
|
|
789
|
+
settleMs: session.human ? 0 : 350,
|
|
790
|
+
settingsPage
|
|
791
|
+
} : session.human ? {
|
|
614
792
|
settleMs: 0,
|
|
615
793
|
...resetTask ? { resetTask: true } : {}
|
|
616
794
|
} : void 0);
|
|
@@ -1023,7 +1201,7 @@ var PhoneRuntime = class {
|
|
|
1023
1201
|
const video = input.geometry !== void 0 && session.stream?.verifyGeometry(input.geometry);
|
|
1024
1202
|
if (!video && (!frame || Date.now() - frame.capturedAt > 5e3)) throw new PhoneError("PHONE_FRAME_STALE", "Wait for a fresh phone image before interacting", 409);
|
|
1025
1203
|
if (video && input.action?.type !== "typeText" && input.action?.type !== "back") throw new PhoneError("PHONE_INVALID_INPUT", "Use the continuous input channel for video gestures", 409);
|
|
1026
|
-
if (!input.action || input.action.type === "launch" || input.action.type === "home" || input.action.type === "wait") throw new PhoneError("PHONE_INVALID_INPUT", "Unsupported manual input");
|
|
1204
|
+
if (!input.action || input.action.type === "launch" || input.action.type === "home" || input.action.type === "wait" || input.action.type === "tapElement") throw new PhoneError("PHONE_INVALID_INPUT", "Unsupported manual input");
|
|
1027
1205
|
const observation = input.action.type === "typeText" ? await this.observe(session, session.controller.signal, true) : await this.capture(session, session.controller.signal);
|
|
1028
1206
|
try {
|
|
1029
1207
|
if (input.epoch !== session.report.controlEpoch || session.human !== client || Date.now() > (session.humanExpiresAt ?? 0)) throw new PhoneError("PHONE_CONTROL_CHANGED", "Manual control expired while observing the phone", 409);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type PhoneAction, type PhoneElementSelector, type PhoneObservation } from './contracts.js';
|
|
2
|
+
export type PhonePrimitiveAction = Exclude<PhoneAction, {
|
|
3
|
+
type: 'tapElement';
|
|
4
|
+
}>;
|
|
5
|
+
/** Offer exact identities only when one fully visible native node matches. */
|
|
6
|
+
export declare function phoneSemanticControls(screen: PhoneObservation): Array<{
|
|
7
|
+
frameId: string;
|
|
8
|
+
selector: PhoneElementSelector;
|
|
9
|
+
}>;
|
|
10
|
+
/** Ground ordinary model taps only when native hit-testing identifies one bounded control. */
|
|
11
|
+
export declare function groundPhoneCoordinateAction(action: PhoneAction, screen: PhoneObservation): PhoneAction;
|
|
12
|
+
/** Resolve against the prediction snapshot and a fresh snapshot before any dispatch. */
|
|
13
|
+
export declare function resolvePhoneSemanticAction(action: PhoneAction, predicted: PhoneObservation, fresh?: PhoneObservation): PhonePrimitiveAction;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { PhoneActionSchema, PhoneError, init_contracts } from "./contracts.js";
|
|
2
|
+
import { canDispatchPhoneAction } from "./reliability.js";
|
|
3
|
+
//#region src/phone/semantic-controls.ts
|
|
4
|
+
init_contracts();
|
|
5
|
+
function visible(node, screen) {
|
|
6
|
+
const [left, top, right, bottom] = node.bounds;
|
|
7
|
+
return !node.password && node.bounds.every(Number.isFinite) && left >= 0 && top >= 0 && right <= screen.width && bottom <= screen.height && right > left && bottom > top;
|
|
8
|
+
}
|
|
9
|
+
function matches(node, selector) {
|
|
10
|
+
return (selector.resourceId === void 0 || node.resourceId === selector.resourceId) && (selector.text === void 0 || node.text === selector.text) && (selector.description === void 0 || node.description === selector.description);
|
|
11
|
+
}
|
|
12
|
+
function candidates(screen, selector) {
|
|
13
|
+
return screen.elements.filter((node) => visible(node, screen) && matches(node, selector));
|
|
14
|
+
}
|
|
15
|
+
function exactSelector(node) {
|
|
16
|
+
const selector = {};
|
|
17
|
+
if (node.resourceId && node.resourceId.length <= 300) selector.resourceId = node.resourceId;
|
|
18
|
+
if (node.text && node.text.length <= 4e3) selector.text = node.text;
|
|
19
|
+
if (node.description && node.description.length <= 1e3) selector.description = node.description;
|
|
20
|
+
return selector;
|
|
21
|
+
}
|
|
22
|
+
/** Offer exact identities only when one fully visible native node matches. */
|
|
23
|
+
function phoneSemanticControls(screen) {
|
|
24
|
+
return screen.elements.filter((node) => visible(node, screen)).flatMap((node) => {
|
|
25
|
+
const selector = exactSelector(node);
|
|
26
|
+
return Object.keys(selector).length && candidates(screen, selector).length === 1 ? [{
|
|
27
|
+
frameId: screen.frameId,
|
|
28
|
+
selector
|
|
29
|
+
}] : [];
|
|
30
|
+
}).slice(0, 40);
|
|
31
|
+
}
|
|
32
|
+
function containsPoint(node, x, y) {
|
|
33
|
+
return x >= node.bounds[0] && y >= node.bounds[1] && x < node.bounds[2] && y < node.bounds[3];
|
|
34
|
+
}
|
|
35
|
+
function containsNode(outer, inner) {
|
|
36
|
+
return outer.bounds[0] <= inner.bounds[0] && outer.bounds[1] <= inner.bounds[1] && outer.bounds[2] >= inner.bounds[2] && outer.bounds[3] >= inner.bounds[3];
|
|
37
|
+
}
|
|
38
|
+
/** Ground ordinary model taps only when native hit-testing identifies one bounded control. */
|
|
39
|
+
function groundPhoneCoordinateAction(action, screen) {
|
|
40
|
+
if (action.type !== "tap") return action;
|
|
41
|
+
const [x, y] = [action.point[0] * screen.width / 1e3, action.point[1] * screen.height / 1e3];
|
|
42
|
+
if (screen.elements.some((node) => node.password && containsPoint(node, x, y))) return action;
|
|
43
|
+
const actionable = screen.elements.filter((node) => visible(node, screen) && (node.clickable || node.editable));
|
|
44
|
+
const hits = actionable.filter((node) => containsPoint(node, x, y));
|
|
45
|
+
const smallest = hits.filter((node) => hits.every((other) => other === node || containsNode(other, node)));
|
|
46
|
+
if (smallest.length !== 1) return action;
|
|
47
|
+
const target = smallest[0];
|
|
48
|
+
if ((target.bounds[2] - target.bounds[0]) * (target.bounds[3] - target.bounds[1]) > screen.width * screen.height / 4) return action;
|
|
49
|
+
const selector = exactSelector(target);
|
|
50
|
+
if (!Object.keys(selector).length || candidates(screen, selector).length !== 1) return action;
|
|
51
|
+
const [centerX, centerY] = [(target.bounds[0] + target.bounds[2]) / 2, (target.bounds[1] + target.bounds[3]) / 2];
|
|
52
|
+
if (screen.elements.some((node) => node.password && containsPoint(node, centerX, centerY)) || actionable.some((node) => node !== target && containsPoint(node, centerX, centerY) && !containsNode(node, target))) return action;
|
|
53
|
+
return {
|
|
54
|
+
type: "tapElement",
|
|
55
|
+
frameId: screen.frameId,
|
|
56
|
+
selector
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Resolve against the prediction snapshot and a fresh snapshot before any dispatch. */
|
|
60
|
+
function resolvePhoneSemanticAction(action, predicted, fresh = predicted) {
|
|
61
|
+
if (action.type !== "tapElement") return action;
|
|
62
|
+
if (!PhoneActionSchema.safeParse(action).success) throw new PhoneError("PHONE_SEMANTIC_INVALID", "Invalid exact native element selector", 409);
|
|
63
|
+
if (action.frameId !== predicted.frameId) throw new PhoneError("PHONE_SEMANTIC_STALE", "Native element belongs to a different observation", 409);
|
|
64
|
+
const before = candidates(predicted, action.selector);
|
|
65
|
+
const after = candidates(fresh, action.selector);
|
|
66
|
+
if (before.length !== 1 || after.length !== 1) throw new PhoneError("PHONE_SEMANTIC_TARGET", "Native element selector must match exactly one fully visible non-password node", 409);
|
|
67
|
+
const target = after[0];
|
|
68
|
+
const resolved = {
|
|
69
|
+
type: "tap",
|
|
70
|
+
point: [500 * (target.bounds[0] + target.bounds[2]) / fresh.width, 500 * (target.bounds[1] + target.bounds[3]) / fresh.height]
|
|
71
|
+
};
|
|
72
|
+
if (JSON.stringify(before[0]) !== JSON.stringify(target) || !canDispatchPhoneAction(resolved, predicted, fresh)) throw new PhoneError("PHONE_SEMANTIC_STALE", "Native element or page changed before dispatch", 409);
|
|
73
|
+
return resolved;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { groundPhoneCoordinateAction, phoneSemanticControls, resolvePhoneSemanticAction };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { PhoneSetupView } from '@xopcai/gateway-contract';
|
|
2
|
+
import type { PhoneInput } from '../contracts.js';
|
|
3
|
+
import type { PhoneRuntime } from '../runtime.js';
|
|
4
|
+
type Context = {
|
|
5
|
+
conversationId: string;
|
|
6
|
+
agentId: string;
|
|
7
|
+
};
|
|
8
|
+
type Setup = Omit<PhoneSetupView, 'readiness' | 'stale' | 'settingsPath' | 'guidanceRefs'> & {
|
|
9
|
+
environmentRevision?: string;
|
|
10
|
+
calls: number;
|
|
11
|
+
owner?: string;
|
|
12
|
+
};
|
|
13
|
+
export interface PhoneSetupDeps {
|
|
14
|
+
runtime: PhoneRuntime;
|
|
15
|
+
inspect(context: Context, source?: 'local' | 'aliyun'): Promise<PhoneSetupView['readiness']>;
|
|
16
|
+
revision(context: Context, deviceId: string): string;
|
|
17
|
+
connected(deviceId: string, signal?: AbortSignal): Promise<boolean>;
|
|
18
|
+
leaseActive(deviceId: string): boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Setup only orchestrates existing settings; secrets and configuration writes stay in product forms. */
|
|
21
|
+
export declare class PhoneSetupService {
|
|
22
|
+
private readonly deps;
|
|
23
|
+
constructor(deps: PhoneSetupDeps);
|
|
24
|
+
private store;
|
|
25
|
+
current(context: Context): Setup;
|
|
26
|
+
private save;
|
|
27
|
+
private owned;
|
|
28
|
+
view(context: Context, setup: Setup): Promise<PhoneSetupView>;
|
|
29
|
+
command(command: string, args: Record<string, unknown>, context: Context, dryRun?: boolean): Promise<PhoneSetupView | {
|
|
30
|
+
dryRun?: boolean;
|
|
31
|
+
sceneId: string;
|
|
32
|
+
skillName: string;
|
|
33
|
+
readiness: {
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
toolAllowed: boolean;
|
|
36
|
+
model: {
|
|
37
|
+
ref?: string;
|
|
38
|
+
chatRef?: string;
|
|
39
|
+
source?: "system" | "global" | "agent";
|
|
40
|
+
supported: boolean;
|
|
41
|
+
configured: boolean;
|
|
42
|
+
};
|
|
43
|
+
dependencies: Array<{
|
|
44
|
+
kind: "adb" | "scrcpy";
|
|
45
|
+
state: string;
|
|
46
|
+
version?: string;
|
|
47
|
+
canInstall: boolean;
|
|
48
|
+
}>;
|
|
49
|
+
devices: Array<{
|
|
50
|
+
id: string;
|
|
51
|
+
name: string;
|
|
52
|
+
source: "local" | "aliyun";
|
|
53
|
+
enabled: boolean;
|
|
54
|
+
settingsAllowed: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
localDiscovery: Array<{
|
|
57
|
+
name: string;
|
|
58
|
+
state: string;
|
|
59
|
+
emulator: boolean;
|
|
60
|
+
}>;
|
|
61
|
+
diagnostic?: string;
|
|
62
|
+
};
|
|
63
|
+
setup: PhoneSetupView;
|
|
64
|
+
settingsPath: string;
|
|
65
|
+
guidanceRefs: {
|
|
66
|
+
sceneId: string;
|
|
67
|
+
path: string;
|
|
68
|
+
resourceVersion: string;
|
|
69
|
+
}[];
|
|
70
|
+
} | {
|
|
71
|
+
message: string;
|
|
72
|
+
id: string;
|
|
73
|
+
conversationId: string;
|
|
74
|
+
agentId: string;
|
|
75
|
+
revision: number;
|
|
76
|
+
state: "configuring" | "verifying" | "completed" | "needs_attention" | "cancelled";
|
|
77
|
+
source?: "local" | "aliyun";
|
|
78
|
+
error?: string;
|
|
79
|
+
stale: boolean;
|
|
80
|
+
settingsPath: string;
|
|
81
|
+
readiness: {
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
toolAllowed: boolean;
|
|
84
|
+
model: {
|
|
85
|
+
ref?: string;
|
|
86
|
+
chatRef?: string;
|
|
87
|
+
source?: "system" | "global" | "agent";
|
|
88
|
+
supported: boolean;
|
|
89
|
+
configured: boolean;
|
|
90
|
+
};
|
|
91
|
+
dependencies: Array<{
|
|
92
|
+
kind: "adb" | "scrcpy";
|
|
93
|
+
state: string;
|
|
94
|
+
version?: string;
|
|
95
|
+
canInstall: boolean;
|
|
96
|
+
}>;
|
|
97
|
+
devices: Array<{
|
|
98
|
+
id: string;
|
|
99
|
+
name: string;
|
|
100
|
+
source: "local" | "aliyun";
|
|
101
|
+
enabled: boolean;
|
|
102
|
+
settingsAllowed: boolean;
|
|
103
|
+
}>;
|
|
104
|
+
localDiscovery: Array<{
|
|
105
|
+
name: string;
|
|
106
|
+
state: string;
|
|
107
|
+
emulator: boolean;
|
|
108
|
+
}>;
|
|
109
|
+
diagnostic?: string;
|
|
110
|
+
};
|
|
111
|
+
checks: {
|
|
112
|
+
configured: boolean;
|
|
113
|
+
read: boolean;
|
|
114
|
+
operated: boolean;
|
|
115
|
+
verified: boolean;
|
|
116
|
+
released: boolean;
|
|
117
|
+
};
|
|
118
|
+
attempt?: {
|
|
119
|
+
id: string;
|
|
120
|
+
deviceId: string;
|
|
121
|
+
modelRef: string;
|
|
122
|
+
sessionId?: string;
|
|
123
|
+
goal: string;
|
|
124
|
+
completion: {
|
|
125
|
+
appPackage: string;
|
|
126
|
+
textAny: string[];
|
|
127
|
+
requireChange: boolean;
|
|
128
|
+
};
|
|
129
|
+
maxActions: number;
|
|
130
|
+
expiresAt: number;
|
|
131
|
+
released: boolean;
|
|
132
|
+
};
|
|
133
|
+
guidanceRefs: Array<{
|
|
134
|
+
sceneId: "phone";
|
|
135
|
+
path: string;
|
|
136
|
+
resourceVersion: string;
|
|
137
|
+
}>;
|
|
138
|
+
}>;
|
|
139
|
+
source(context: Context, id: string, source: 'local' | 'aliyun', revision: number): Promise<PhoneSetupView>;
|
|
140
|
+
begin(context: Context, id: string, deviceId: string, revision: number, signal?: AbortSignal): Promise<PhoneSetupView>;
|
|
141
|
+
beforeInput(context: Context, input: PhoneInput): {
|
|
142
|
+
owner: string;
|
|
143
|
+
input: PhoneInput;
|
|
144
|
+
} | undefined;
|
|
145
|
+
afterInput(context: Context, owner: string, op: PhoneInput['op']): void;
|
|
146
|
+
release(context: Context, id: string): Promise<PhoneSetupView>;
|
|
147
|
+
}
|
|
148
|
+
export {};
|