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