@xopcai/xopc 0.0.413 → 0.0.415
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-BT6oqCMD.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-BdCgtTfT.js → app-management-settings-panel-DgwqNi5i.js} +1 -1
- package/dist/gateway/static/root/assets/app-window-DXFNqt_0.js +1 -0
- package/dist/gateway/static/root/assets/{appearance-settings-C3-hCFlF.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-BviFGCy4.js → archive-plugin-Ch7wolHE.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-CfzazXE5.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-Dp-gtJEk.js → binary-plugins-B4HcHYsC.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-D-DEPdpi.js → block-editor-BQAWEZF-.js} +4 -4
- 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-CiGx8wpQ.js +2 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-a8kqjzQu.js +8 -0
- package/dist/gateway/static/root/assets/{capability-header-actions-CP2fI9Fe.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-BL3tFntQ.js → chat-terminal-dock-zdMySdMm.js} +2 -2
- package/dist/gateway/static/root/assets/chevron-down-ccbiQQz_.js +1 -0
- package/dist/gateway/static/root/assets/{computer-settings-page-DEQ5GYfC.js → computer-settings-page-C5Z6Jr0p.js} +1 -1
- package/dist/gateway/static/root/assets/{connector-service-page-CecwMnC5.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-N5jnlvPs.js → date-picker-DuyyD6jz.js} +1 -1
- package/dist/gateway/static/root/assets/dependency-picker-BZN72rrk.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-BtvTIDCi.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-settings-BcyP7Z6v.js +1 -0
- package/dist/gateway/static/root/assets/device-pairing-wizard-Ctbgr50F.js +1 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-CbMbPimD.js → directory-picker-path-field-DOusKz-O.js} +1 -1
- package/dist/gateway/static/root/assets/ellipsis-0k4rZLoy.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-OE1nJMQ8.js → extension-debug-page-Bt4FwsFu.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BEomiKhy.js → extension-page-0vsxX3FB.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BFOjQeSX.js → extension-settings-page-5Pt5_ELc.js} +1 -1
- package/dist/gateway/static/root/assets/eye-CABVeTq0.js +1 -0
- package/dist/gateway/static/root/assets/folder-open-CyzJ19I9.js +1 -0
- 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-6ONPRH5r.js → highlight-code-C8eS9949.js} +1 -1
- package/dist/gateway/static/root/assets/{highlighted-body-KQOG7T2V-EJ9Kg4iQ.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-D0HNic89.js → image-generation-api-CAS8QVla.js} +1 -1
- package/dist/gateway/static/root/assets/imports-page-BCLBYzV1.js +1 -0
- 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-Cmi-2SIh.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-jgaogH9Z.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-D3gVdm8S.js +2 -0
- package/dist/gateway/static/root/assets/management-settings-BQNYFE4J.js +1 -0
- package/dist/gateway/static/root/assets/{media-plugins-BRS9puQV.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-D_VOB2_C.js → page-context-capture-button-DVn_dU83.js} +1 -1
- 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-DOv38vBn.js +1 -0
- 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-DaTaSKox.js → preview-open-alternatives-RlHk76TI.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-wYlC0IVZ.js → product-open-page-Dh-HqLrE.js} +1 -1
- package/dist/gateway/static/root/assets/{product-preview-content-Bxur-p6S.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-DXNidliV.js → runtime-tools-settings-panel-Dbskk6RG.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-DeSZ7SiO.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-BNYQjZU4.js → settings-form-section-BQxKmbbJ.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-29Ix0jSQ.js → settings-page-DvVMpKiA.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-COQZgKX9.js → setup-status-panel-DKo_VTx-.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-CwMRULXc.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-xfB5fUAh.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-DRJu1ehM.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-C4o_Yue_.js → task-result-evidence-D-gGKNX9.js} +1 -1
- package/dist/gateway/static/root/assets/{tracing-settings-page-CYslh-Pu.js → tracing-settings-page-CQw3G-FB.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-Bi_kw6bv.js → tunnel-api-B2HnCi0U.js} +1 -1
- package/dist/gateway/static/root/assets/usage-page-uUEkO38r.js +1 -0
- package/dist/gateway/static/root/assets/{use-autosave-BkB9uD93.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-bt8_Agih.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-D3NG14dX.js → work-discovery-overlay-H3cdwq3l.js} +1 -1
- package/dist/gateway/static/root/assets/{xopc-cloud-account-card-B2OUKOQi.js → xopc-cloud-account-card-CvT_W6YS.js} +1 -1
- package/dist/gateway/static/root/index.html +19 -14
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +1 -1
- package/dist/src/agent/background-review/run-background-review.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/external-tools/cliProvider.js +2 -2
- package/dist/src/agent/external-tools/composio-provider.js +2 -2
- package/dist/src/agent/external-tools/endpoint-provider.js +2 -2
- package/dist/src/agent/memory/context-recovery.d.ts +1 -1
- package/dist/src/agent/service.js +1 -1
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/installer.js +3 -3
- package/dist/src/agent/skills/managed-store.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/tool-manuals/phone.d.ts +1 -1
- package/dist/src/agent/tool-manuals/phone.js +5 -3
- package/dist/src/agent/tools/personal-read-tool.js +1 -1
- 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 +22 -2
- package/dist/src/agent/tools/xopc-use-tool.js +2 -2
- package/dist/src/agent-catalog/capabilities.js +2 -2
- package/dist/src/agent-catalog/repository.js +1 -1
- package/dist/src/automations/capabilities/drafts.js +1 -1
- package/dist/src/backup/archive.js +2 -1
- package/dist/src/browser/providers/browser-ext-install.js +2 -2
- package/dist/src/capabilities/runtime/dispatcher.js +1 -1
- package/dist/src/capabilities/runtime/product.js +1 -1
- package/dist/src/cli/commands/agents.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard/model.js +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +1 -1
- package/dist/src/config/agent-profile.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/execution-environments/local-worktree-manager.js +1 -1
- package/dist/src/extensions/agent-plugins/skill-catalog.js +1 -1
- package/dist/src/extensions/agent-plugins/validation.js +1 -1
- package/dist/src/extensions/install.js +1 -1
- package/dist/src/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/global-defaults-admin.js +1 -1
- package/dist/src/gateway/hono/routes/agents.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.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/notes.js +2 -2
- package/dist/src/gateway/hono/routes/phone.js +33 -4
- package/dist/src/gateway/hono/routes/projects.js +2 -2
- package/dist/src/gateway/hono/routes/public-gateway.js +1 -1
- package/dist/src/gateway/hono/routes/session-input-handler.js +1 -1
- package/dist/src/gateway/hono/routes/tasks.js +1 -1
- package/dist/src/gateway/image-generation-setup.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/gateway/xopc-cloud-capability-setup.js +1 -1
- package/dist/src/imports/importService.js +1 -1
- package/dist/src/infra/unhandled-rejections.js +2 -2
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/local-apps/service.js +1 -1
- package/dist/src/personal-agent/prompts.js +1 -1
- package/dist/src/personal-agent/service.js +1 -1
- package/dist/src/phone/adb-driver.d.ts +9 -2
- package/dist/src/phone/adb-driver.js +113 -37
- package/dist/src/phone/app-access.d.ts +10 -0
- package/dist/src/phone/app-access.js +21 -0
- package/dist/src/phone/applications.d.ts +4 -0
- package/dist/src/phone/applications.js +49 -0
- package/dist/src/phone/cloud-adb.d.ts +1 -0
- package/dist/src/phone/cloud-adb.js +60 -2
- package/dist/src/phone/cloud-phones.js +2 -1
- package/dist/src/phone/cloud-scrcpy.d.ts +3 -1
- package/dist/src/phone/cloud-scrcpy.js +19 -4
- package/dist/src/phone/contracts.d.ts +54 -1
- package/dist/src/phone/contracts.js +18 -4
- package/dist/src/phone/model-adapter.d.ts +90 -2
- package/dist/src/phone/model-adapter.js +87 -19
- package/dist/src/phone/reliability.d.ts +7 -0
- package/dist/src/phone/reliability.js +66 -0
- package/dist/src/phone/repository.js +4 -3
- package/dist/src/phone/runtime.d.ts +14 -1
- package/dist/src/phone/runtime.js +215 -19
- package/dist/src/phone/scrcpy-assets.d.ts +4 -0
- package/dist/src/phone/scrcpy-assets.js +16 -0
- 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 +6 -5
- package/dist/src/phone/scrcpy.js +51 -14
- package/dist/src/runtime-tools/archive.js +1 -1
- 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/read.js +2 -2
- package/dist/src/tasks/capabilities/relations.d.ts +2 -2
- package/dist/src/tasks/capabilities/write.js +1 -1
- package/dist/src/tui/backends/embedded-backend.js +1 -1
- 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/voice/realtime/persona-context.js +1 -1
- package/dist/src/work-discovery/probe.js +1 -1
- package/dist/src/work-discovery/service.js +2 -2
- package/package.json +6 -6
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-BFmRRD_4.js +0 -1
- package/dist/gateway/static/root/assets/agents-Clwskw2J.js +0 -77
- package/dist/gateway/static/root/assets/apps-page-DJbKg3va.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-D_X0_1X3.js +0 -8
- package/dist/gateway/static/root/assets/browser-automations-page-BI-ep6uM.js +0 -3
- package/dist/gateway/static/root/assets/browser-settings-page-BTyQ4-vN.js +0 -3
- package/dist/gateway/static/root/assets/capabilities-page-GNS95mG9.js +0 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-DBiv1HN-.js +0 -8
- package/dist/gateway/static/root/assets/channels-DWlrUKBr.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-BcpUvrtl.js +0 -1
- package/dist/gateway/static/root/assets/dependency-picker-C4jddxFl.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-BCbiUNQG.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-settings-CkKU0JMY.js +0 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard--E2GQoY1.js +0 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-CoIgC1Al.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-CR4CsfNN.js +0 -1
- package/dist/gateway/static/root/assets/home-page-B9w3a6r2.js +0 -2
- package/dist/gateway/static/root/assets/imports-page-bxTINe-2.js +0 -1
- package/dist/gateway/static/root/assets/index-D1oivYvN.js +0 -710
- package/dist/gateway/static/root/assets/index-D_7q1UkI.css +0 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-NGCmXpa_.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-drrr-c2W.js +0 -2
- package/dist/gateway/static/root/assets/management-settings-Dc1opXVr.js +0 -1
- package/dist/gateway/static/root/assets/mermaid-MCJ5UELQ-B--edI1I.js +0 -1
- package/dist/gateway/static/root/assets/note-detail-panel-C3s4-ytB.js +0 -5
- package/dist/gateway/static/root/assets/note-image-lightbox-D7q5-KuS.js +0 -1
- package/dist/gateway/static/root/assets/notes-page-qVxoErUS.js +0 -8
- package/dist/gateway/static/root/assets/personal-page-B-T8NzQo.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-DhEsfEUN.js +0 -1
- package/dist/gateway/static/root/assets/phone-model-picker-J4mFDk21.js +0 -1
- package/dist/gateway/static/root/assets/phone-settings-page-BLflz8CQ.js +0 -1
- package/dist/gateway/static/root/assets/plugin-details-dialog-1BwvmKZi.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-DwowzxzD.js +0 -7
- package/dist/gateway/static/root/assets/projects-page-Bwvn4i_e.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-DrHisOUe.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-seP7t0kv.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-VqBu7gHu.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-BWaykw9E.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-Bq-3_MCt.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-C3LfWsAZ.js +0 -2
- package/dist/gateway/static/root/assets/task-detail-page-XCJFP-Aa.js +0 -2
- package/dist/gateway/static/root/assets/usage-page-CTTmklCs.js +0 -1
- package/dist/gateway/static/root/assets/user-model-page-BDqguzRE.js +0 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
//#region src/phone/reliability.ts
|
|
3
|
+
function geometryMatches(before, after) {
|
|
4
|
+
return before.package === after.package && before.width === after.width && before.height === after.height && before.rotation === after.rotation;
|
|
5
|
+
}
|
|
6
|
+
function ambientClock(node) {
|
|
7
|
+
const clock = /^(?:[01]?\d|2[0-3]):[0-5]\d(?::[0-5]\d)?(?:\s?[AP]M)?$/i;
|
|
8
|
+
return node.resourceId === "com.android.systemui:id/clock" && !node.clickable && !node.editable && !node.focused && !node.password && clock.test(node.text) && (!node.description || clock.test(node.description));
|
|
9
|
+
}
|
|
10
|
+
function semanticElements(observation) {
|
|
11
|
+
return observation.elements.map((node) => ambientClock(node) ? {
|
|
12
|
+
...node,
|
|
13
|
+
text: "",
|
|
14
|
+
description: ""
|
|
15
|
+
} : node);
|
|
16
|
+
}
|
|
17
|
+
/** Ignore only recognized inert system-clock text, never arbitrary application content. */
|
|
18
|
+
function observationSignature(observation) {
|
|
19
|
+
return createHash("sha256").update(JSON.stringify([
|
|
20
|
+
observation.package,
|
|
21
|
+
observation.width,
|
|
22
|
+
observation.height,
|
|
23
|
+
observation.rotation,
|
|
24
|
+
observation.elements.length ? semanticElements(observation) : createHash("sha256").update(observation.frame).digest("hex")
|
|
25
|
+
])).digest("hex");
|
|
26
|
+
}
|
|
27
|
+
function atPoint(elements, x, y) {
|
|
28
|
+
return elements.filter((node) => node.bounds[0] <= x && x < node.bounds[2] && node.bounds[1] <= y && y < node.bounds[3]);
|
|
29
|
+
}
|
|
30
|
+
/** Preserve full page semantics; only inert system-clock updates may be ignored. */
|
|
31
|
+
function canDispatchPhoneAction(action, before, fresh) {
|
|
32
|
+
if (!geometryMatches(before, fresh)) return false;
|
|
33
|
+
if (JSON.stringify(before.elements) === JSON.stringify(fresh.elements)) return before.elements.length > 0 || fresh.frame.equals(before.frame);
|
|
34
|
+
if (JSON.stringify(semanticElements(before)) !== JSON.stringify(semanticElements(fresh))) return false;
|
|
35
|
+
if (action.type === "tap" || action.type === "doubleTap" || action.type === "longPress") {
|
|
36
|
+
const [x, y] = [action.point[0] * before.width / 1e3, action.point[1] * before.height / 1e3];
|
|
37
|
+
const targets = atPoint(before.elements, x, y);
|
|
38
|
+
const identified = targets.filter((node) => node.resourceId && node.clickable && !node.password);
|
|
39
|
+
if (identified.length !== 1) return false;
|
|
40
|
+
const target = identified[0];
|
|
41
|
+
if (before.elements.filter((node) => node.resourceId === target.resourceId).length !== 1 || fresh.elements.filter((node) => node.resourceId === target.resourceId).length !== 1) return false;
|
|
42
|
+
return JSON.stringify(targets) === JSON.stringify(atPoint(fresh.elements, x, y));
|
|
43
|
+
}
|
|
44
|
+
if (action.type === "typeText") {
|
|
45
|
+
const focused = (screen) => screen.elements.filter((node) => node.focused && node.editable && !node.password);
|
|
46
|
+
const previous = focused(before);
|
|
47
|
+
const current = focused(fresh);
|
|
48
|
+
return previous.length === 1 && current.length === 1 && Boolean(previous[0].resourceId) && JSON.stringify(previous) === JSON.stringify(current);
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
/** Evaluate all native nodes, separately from the truncated display facts. */
|
|
53
|
+
function phoneCompletionSatisfied(condition, observation, changed) {
|
|
54
|
+
if (condition.requireChange && !changed) return false;
|
|
55
|
+
if (condition.appPackage && condition.appPackage !== observation.package) return false;
|
|
56
|
+
const texts = observation.elements.filter((node) => !node.password).flatMap((node) => [node.text, node.description]).filter(Boolean);
|
|
57
|
+
if (condition.textAny && !condition.textAny.some((value) => texts.some((text) => text.includes(value)))) return false;
|
|
58
|
+
if (condition.textAll && !condition.textAll.every((value) => texts.some((text) => text.includes(value)))) return false;
|
|
59
|
+
if (condition.field) {
|
|
60
|
+
const fields = observation.elements.filter((node) => !node.password && node.editable && node.resourceId === condition.field.resourceId);
|
|
61
|
+
if (fields.length !== 1 || fields[0].text !== condition.field.value) return false;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { canDispatchPhoneAction, observationSignature, phoneCompletionSatisfied };
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { PhoneDeviceSchema, PhoneError, init_contracts } from "./contracts.js";
|
|
2
|
+
import { normalizePhoneAppAccess } from "./app-access.js";
|
|
2
3
|
import { DurableState } from "../storage/sqlite/durable-state.js";
|
|
3
4
|
import { randomUUID } from "node:crypto";
|
|
4
5
|
//#region src/phone/repository.ts
|
|
5
6
|
init_contracts();
|
|
6
7
|
function parseDevice(value) {
|
|
7
8
|
const { agentIds: _removed, ...device } = value;
|
|
8
|
-
return PhoneDeviceSchema.parse(device);
|
|
9
|
+
return normalizePhoneAppAccess(PhoneDeviceSchema.parse(device));
|
|
9
10
|
}
|
|
10
11
|
/** Small settings and latest metadata-only reports; pixels and input text are never stored. */
|
|
11
12
|
var PhoneRepository = class {
|
|
@@ -24,12 +25,12 @@ var PhoneRepository = class {
|
|
|
24
25
|
save(input, id) {
|
|
25
26
|
const previous = id ? this.get(id) : void 0;
|
|
26
27
|
if (this.list().some((device) => device.serial === input.serial && device.id !== id)) throw new PhoneError("PHONE_DEVICE_DUPLICATE", "This ADB device is already registered");
|
|
27
|
-
const next = PhoneDeviceSchema.parse({
|
|
28
|
+
const next = normalizePhoneAppAccess(PhoneDeviceSchema.parse({
|
|
28
29
|
...input,
|
|
29
30
|
id: id ?? randomUUID(),
|
|
30
31
|
createdAt: previous?.createdAt ?? Date.now(),
|
|
31
32
|
revision: (previous?.revision ?? 0) + 1
|
|
32
|
-
});
|
|
33
|
+
}));
|
|
33
34
|
this.devices.set(next.id, next);
|
|
34
35
|
return next;
|
|
35
36
|
}
|
|
@@ -5,7 +5,7 @@ import { CloudPhones } from './cloud-phones.js';
|
|
|
5
5
|
import { type PhoneDriver } from './adb-driver.js';
|
|
6
6
|
import { createPhoneModel } from './model-adapter.js';
|
|
7
7
|
import { PhoneRepository } from './repository.js';
|
|
8
|
-
import { type PhoneAction, type PhoneDevice, type PhoneInput, type PhoneObservation, type PhoneReport } from './contracts.js';
|
|
8
|
+
import { type PhoneAction, type PhoneDevice, type PhoneInput, type PhoneApplication, type PhoneObservation, type PhoneReport } from './contracts.js';
|
|
9
9
|
type ExecutionContext = {
|
|
10
10
|
config: Config;
|
|
11
11
|
agentId: string;
|
|
@@ -26,6 +26,17 @@ export declare class PhoneRuntime {
|
|
|
26
26
|
constructor(repository?: PhoneRepository, driverFactory?: (path: string) => PhoneDriver, modelFactory?: typeof createPhoneModel, interactiveFactory?: (adb: string, serial: string, failed: () => void) => PhoneInteractiveProvider);
|
|
27
27
|
driverFor(device: PhoneDevice, config: Config): Promise<PhoneDriver>;
|
|
28
28
|
discover(config: Config, signal?: AbortSignal): Promise<import("./contracts.js").DiscoveredPhone[]>;
|
|
29
|
+
applications(input: {
|
|
30
|
+
deviceId?: string;
|
|
31
|
+
serial?: string;
|
|
32
|
+
}, config: Config, signal: AbortSignal): Promise<PhoneApplication[]>;
|
|
33
|
+
textInputStatus(id: string, config: Config, signal: AbortSignal): Promise<{
|
|
34
|
+
policy: "manual" | "auto";
|
|
35
|
+
androidSdk: number;
|
|
36
|
+
unicode: "available" | "setup_required" | "unsupported";
|
|
37
|
+
method: "scrcpy-clipboard" | "provider-native";
|
|
38
|
+
keyboardRequired: false;
|
|
39
|
+
}>;
|
|
29
40
|
preview(id: string, config: Config, signal: AbortSignal): Promise<PhoneObservation>;
|
|
30
41
|
previewSession(owner: string, context: ExecutionContext, signal: AbortSignal): Promise<PhoneObservation>;
|
|
31
42
|
reports(): PhoneReport[];
|
|
@@ -50,6 +61,8 @@ export declare class PhoneRuntime {
|
|
|
50
61
|
report: PhoneReport;
|
|
51
62
|
ownControl: boolean;
|
|
52
63
|
allowedApps: string[];
|
|
64
|
+
appAccess: "all" | "selected";
|
|
65
|
+
textInput: "manual" | "auto";
|
|
53
66
|
frame: {
|
|
54
67
|
seq: number;
|
|
55
68
|
capturedAt: number;
|
|
@@ -2,6 +2,7 @@ import { createLogger } from "../utils/logger/index.js";
|
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
3
|
import { MOCK_PHONE_MODEL, PhoneError, PhoneInputSchema, init_contracts } from "./contracts.js";
|
|
4
4
|
import { resolveEffectiveAgentConfigForAgent } from "../config/agent-profile.js";
|
|
5
|
+
import { phoneAllowsApp, phoneAppAccess } from "./app-access.js";
|
|
5
6
|
import { PhoneFrameCache, PhoneOperationBudget } from "./live-state.js";
|
|
6
7
|
import { PhoneInteractiveSession } from "./interactive-session.js";
|
|
7
8
|
import { AdbPhoneDriver, resolveAdbPath } from "./adb-driver.js";
|
|
@@ -9,6 +10,7 @@ import { LocalScrcpyProvider } from "./scrcpy.js";
|
|
|
9
10
|
import { CloudPhones } from "./cloud-phones.js";
|
|
10
11
|
import { createPhoneModel, isAboutScreen } from "./model-adapter.js";
|
|
11
12
|
import { PhoneRepository } from "./repository.js";
|
|
13
|
+
import { canDispatchPhoneAction, observationSignature, phoneCompletionSatisfied } from "./reliability.js";
|
|
12
14
|
import { createHash, randomUUID } from "node:crypto";
|
|
13
15
|
//#region src/phone/runtime.ts
|
|
14
16
|
init_logger();
|
|
@@ -40,13 +42,40 @@ var PhoneRuntime = class {
|
|
|
40
42
|
async discover(config, signal) {
|
|
41
43
|
return this.driverFactory(resolveAdbPath(config.phone.adbPath)).discover(signal);
|
|
42
44
|
}
|
|
45
|
+
async applications(input, config, signal) {
|
|
46
|
+
let driver;
|
|
47
|
+
let serial;
|
|
48
|
+
if (input.deviceId) {
|
|
49
|
+
const device = this.repository.get(input.deviceId);
|
|
50
|
+
if (!device.enabled) throw new PhoneError("PHONE_DEVICE_DENIED", "Phone access is disabled", 403);
|
|
51
|
+
driver = await this.driverFor(device, config);
|
|
52
|
+
serial = device.serial;
|
|
53
|
+
} else {
|
|
54
|
+
const found = (await this.discover(config, signal)).find((device) => device.serial === input.serial && device.state === "device");
|
|
55
|
+
if (!found) throw new PhoneError("PHONE_DEVICE_OFFLINE", "Choose a connected and authorized phone");
|
|
56
|
+
driver = this.driverFactory(resolveAdbPath(config.phone.adbPath));
|
|
57
|
+
serial = found.serial;
|
|
58
|
+
}
|
|
59
|
+
if (!driver.applications) throw new PhoneError("PHONE_APPS_UNAVAILABLE", "The phone provider cannot list applications");
|
|
60
|
+
return driver.applications(serial, signal);
|
|
61
|
+
}
|
|
62
|
+
async textInputStatus(id, config, signal) {
|
|
63
|
+
const device = this.repository.get(id);
|
|
64
|
+
if (!device.enabled) throw new PhoneError("PHONE_DEVICE_DENIED", "Phone access is disabled", 403);
|
|
65
|
+
const driver = await this.driverFor(device, config);
|
|
66
|
+
if (!driver.textInputStatus) throw new PhoneError("PHONE_TEXT_INPUT_UNAVAILABLE", "This phone provider does not expose text input diagnostics", 409);
|
|
67
|
+
return {
|
|
68
|
+
...await driver.textInputStatus(device.serial, signal),
|
|
69
|
+
policy: device.textInput ?? "auto"
|
|
70
|
+
};
|
|
71
|
+
}
|
|
43
72
|
async preview(id, config, signal) {
|
|
44
73
|
const device = this.repository.get(id);
|
|
45
74
|
if (!device.enabled) throw new PhoneError("PHONE_DEVICE_DENIED", "Phone access is disabled", 403);
|
|
46
75
|
const lease = this.repository.acquire(device.serial);
|
|
47
76
|
try {
|
|
48
77
|
const observation = await (await this.driverFor(device, config)).observe(device.serial, signal);
|
|
49
|
-
if (!device
|
|
78
|
+
if (!phoneAllowsApp(device, observation.package)) {
|
|
50
79
|
observation.frame.fill(0);
|
|
51
80
|
throw new PhoneError("PHONE_APP_DENIED", "Open an allowed app before previewing the screen");
|
|
52
81
|
}
|
|
@@ -91,7 +120,7 @@ var PhoneRuntime = class {
|
|
|
91
120
|
this.repository.renew(session.device.serial, session.lease);
|
|
92
121
|
}
|
|
93
122
|
allowedScreen(session, observation) {
|
|
94
|
-
if (!session.device
|
|
123
|
+
if (!phoneAllowsApp(session.device, observation.package)) throw new PhoneError("PHONE_APP_DENIED", "The foreground application is outside this phone session allowlist. Open an allowed app or reopen with appPackage.");
|
|
95
124
|
}
|
|
96
125
|
async execute(owner, raw, context, signal = new AbortController().signal) {
|
|
97
126
|
const input = PhoneInputSchema.parse(raw);
|
|
@@ -132,6 +161,12 @@ var PhoneRuntime = class {
|
|
|
132
161
|
try {
|
|
133
162
|
const adapter = await this.modelFactory(modelRef, context.config, context.agentId);
|
|
134
163
|
signal.throwIfAborted();
|
|
164
|
+
let applications = [];
|
|
165
|
+
if (input.mode === "control" && modelRef !== "phone-mock/settings-about" && driver.applications) try {
|
|
166
|
+
applications = await driver.applications(device.serial, signal);
|
|
167
|
+
} catch {
|
|
168
|
+
signal.throwIfAborted();
|
|
169
|
+
}
|
|
135
170
|
const now = Date.now();
|
|
136
171
|
const session = {
|
|
137
172
|
device,
|
|
@@ -143,6 +178,10 @@ var PhoneRuntime = class {
|
|
|
143
178
|
modelRef,
|
|
144
179
|
adbPath,
|
|
145
180
|
context,
|
|
181
|
+
applications,
|
|
182
|
+
trajectory: [],
|
|
183
|
+
trajectoryCount: 0,
|
|
184
|
+
trajectorySummary: [],
|
|
146
185
|
frames: new PhoneFrameCache(),
|
|
147
186
|
io: Promise.resolve(),
|
|
148
187
|
ioBusy: false,
|
|
@@ -169,12 +208,18 @@ var PhoneRuntime = class {
|
|
|
169
208
|
updatedAt: now,
|
|
170
209
|
verified: false,
|
|
171
210
|
verificationLevel: "none",
|
|
172
|
-
steps: []
|
|
211
|
+
steps: [],
|
|
212
|
+
diagnostics: {
|
|
213
|
+
stalePredictions: 0,
|
|
214
|
+
protocolRepairs: 0,
|
|
215
|
+
unchangedActions: 0,
|
|
216
|
+
completionCorrections: 0
|
|
217
|
+
}
|
|
173
218
|
}
|
|
174
219
|
};
|
|
175
220
|
this.sessions.set(owner, session);
|
|
176
221
|
if (input.appPackage) {
|
|
177
|
-
if (input.mode !== "control" || !device
|
|
222
|
+
if (input.mode !== "control" || !phoneAllowsApp(device, input.appPackage)) throw new PhoneError("PHONE_APP_DENIED", "Launching requires control mode and an allowed application");
|
|
178
223
|
const epoch = session.report.controlEpoch;
|
|
179
224
|
const observation = await this.observe(session, signal);
|
|
180
225
|
try {
|
|
@@ -234,6 +279,9 @@ var PhoneRuntime = class {
|
|
|
234
279
|
const startedAt = Date.now();
|
|
235
280
|
session.report.errorCode = void 0;
|
|
236
281
|
session.report.verified = false;
|
|
282
|
+
session.report.verificationLevel = "none";
|
|
283
|
+
session.report.stopReason = void 0;
|
|
284
|
+
session.report.message = void 0;
|
|
237
285
|
try {
|
|
238
286
|
await this.waitForAgent(session, signal);
|
|
239
287
|
session.report.status = "running";
|
|
@@ -257,7 +305,20 @@ var PhoneRuntime = class {
|
|
|
257
305
|
if (session.report.mode !== "control") throw new PhoneError("PHONE_OBSERVE_ONLY", "Reopen the phone in control mode to execute actions");
|
|
258
306
|
if (session.report.mock && input.scenario !== "settings-about") throw new PhoneError("PHONE_MOCK_SCENARIO_REQUIRED", "Mock mode only supports the explicit settings-about scenario");
|
|
259
307
|
const maxActions = input.op === "step" ? 1 : input.maxActions;
|
|
308
|
+
const requiredTexts = [...input.completion?.textAll ?? [], ...input.expectText ? [input.expectText] : []];
|
|
309
|
+
const completion = input.completion || input.expectText ? {
|
|
310
|
+
...input.completion,
|
|
311
|
+
textAll: requiredTexts.length ? requiredTexts : void 0
|
|
312
|
+
} : void 0;
|
|
260
313
|
let actions = 0;
|
|
314
|
+
let protocolFeedback;
|
|
315
|
+
let repairs = 0;
|
|
316
|
+
let completionCorrections = 0;
|
|
317
|
+
let recoveryHint;
|
|
318
|
+
let changed = false;
|
|
319
|
+
let previousSignature = observationSignature(observation);
|
|
320
|
+
let controlEpoch = session.report.controlEpoch;
|
|
321
|
+
const attempts = /* @__PURE__ */ new Map();
|
|
261
322
|
while (true) {
|
|
262
323
|
if (await this.waitForAgent(session, signal)) {
|
|
263
324
|
observation.frame.fill(0);
|
|
@@ -267,13 +328,36 @@ var PhoneRuntime = class {
|
|
|
267
328
|
this.guard(session, context);
|
|
268
329
|
this.allowedScreen(session, observation);
|
|
269
330
|
const epoch = session.report.controlEpoch;
|
|
331
|
+
if (controlEpoch !== epoch) {
|
|
332
|
+
attempts.clear();
|
|
333
|
+
recoveryHint = "The user handed back control. Re-evaluate the fresh screen before continuing.";
|
|
334
|
+
controlEpoch = epoch;
|
|
335
|
+
}
|
|
336
|
+
const signature = observationSignature(observation);
|
|
337
|
+
if (signature !== previousSignature) changed = true;
|
|
338
|
+
previousSignature = signature;
|
|
270
339
|
this.recordObservation(session, observation);
|
|
271
|
-
if (session.report.mock ? isAboutScreen(observation) : Boolean(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
340
|
+
if (session.report.mock ? isAboutScreen(observation) : Boolean(completion && phoneCompletionSatisfied(completion, observation, changed))) {
|
|
341
|
+
const confirmation = await this.observe(session, signal);
|
|
342
|
+
try {
|
|
343
|
+
signal.throwIfAborted();
|
|
344
|
+
this.guard(session, context);
|
|
345
|
+
this.allowedScreen(session, confirmation);
|
|
346
|
+
const confirmed = session.report.mock ? isAboutScreen(confirmation) : Boolean(completion && phoneCompletionSatisfied(completion, confirmation, changed));
|
|
347
|
+
if (epoch !== session.report.controlEpoch || session.report.controlOwner !== "agent" || !confirmed) {
|
|
348
|
+
observation.frame.fill(0);
|
|
349
|
+
observation = confirmation;
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
this.recordObservation(session, confirmation);
|
|
353
|
+
session.report.verified = true;
|
|
354
|
+
session.report.verificationLevel = "ui";
|
|
355
|
+
session.report.status = "completed";
|
|
356
|
+
session.report.stopReason = actions ? "condition_satisfied" : "condition_present";
|
|
357
|
+
return this.publish(session);
|
|
358
|
+
} finally {
|
|
359
|
+
if (observation !== confirmation) confirmation.frame.fill(0);
|
|
360
|
+
}
|
|
277
361
|
}
|
|
278
362
|
if (actions >= maxActions) {
|
|
279
363
|
session.report.status = "ready";
|
|
@@ -290,8 +374,22 @@ var PhoneRuntime = class {
|
|
|
290
374
|
decision = await session.adapter.predict({
|
|
291
375
|
goal: input.goal,
|
|
292
376
|
observation,
|
|
377
|
+
completion,
|
|
293
378
|
allowedApps: session.device.allowedApps,
|
|
379
|
+
appAccess: phoneAppAccess(session.device),
|
|
380
|
+
applications: session.applications.filter((app) => phoneAllowsApp(session.device, app.packageName)),
|
|
294
381
|
history: session.report.steps.map((step) => step.action),
|
|
382
|
+
trajectory: session.trajectory,
|
|
383
|
+
progressSummary: JSON.stringify({
|
|
384
|
+
dispatched: session.trajectoryCount,
|
|
385
|
+
earlier: [...session.trajectorySummary, ...session.trajectory.slice(0, -8).map((entry) => JSON.stringify({
|
|
386
|
+
action: entry.action,
|
|
387
|
+
effect: entry.effect,
|
|
388
|
+
afterApp: entry.afterApp
|
|
389
|
+
}).slice(0, 250))].slice(-12)
|
|
390
|
+
}).slice(-1500),
|
|
391
|
+
recoveryHint,
|
|
392
|
+
protocolFeedback,
|
|
295
393
|
scenario: input.scenario
|
|
296
394
|
}, AbortSignal.any([signal, session.prediction.signal]));
|
|
297
395
|
} catch (error) {
|
|
@@ -301,6 +399,14 @@ var PhoneRuntime = class {
|
|
|
301
399
|
observation = await this.observe(session, signal);
|
|
302
400
|
continue;
|
|
303
401
|
}
|
|
402
|
+
if (!signal.aborted && error instanceof PhoneError && ["PHONE_MODEL_PROTOCOL", "PHONE_MODEL_TRUNCATED"].includes(error.code) && repairs < 1 && session.report.modelRequests < context.config.phone.maxModelRequests) {
|
|
403
|
+
repairs++;
|
|
404
|
+
session.report.diagnostics.protocolRepairs++;
|
|
405
|
+
protocolFeedback = "Your previous output was invalid or truncated. Return exactly one complete action in the specified format with no extra prose.";
|
|
406
|
+
observation.frame.fill(0);
|
|
407
|
+
observation = await this.observe(session, signal);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
304
410
|
throw error;
|
|
305
411
|
} finally {
|
|
306
412
|
session.prediction = void 0;
|
|
@@ -313,9 +419,21 @@ var PhoneRuntime = class {
|
|
|
313
419
|
observation = await this.observe(session, signal);
|
|
314
420
|
continue;
|
|
315
421
|
}
|
|
422
|
+
protocolFeedback = void 0;
|
|
316
423
|
if (decision.kind !== "action") {
|
|
424
|
+
if (decision.kind === "complete" && !session.report.mock && (input.completion || input.expectText) && completionCorrections < 1 && session.report.modelRequests < context.config.phone.maxModelRequests) {
|
|
425
|
+
completionCorrections++;
|
|
426
|
+
session.report.diagnostics.completionCorrections++;
|
|
427
|
+
recoveryHint = `Your completion claim is not verified. These required native UI conditions are not all satisfied: ${JSON.stringify({
|
|
428
|
+
expectText: input.expectText,
|
|
429
|
+
...input.completion
|
|
430
|
+
})}. Recheck the fresh screen, preserve user constraints, and continue if a safe authorized action can satisfy the goal; otherwise report unfinished.`;
|
|
431
|
+
observation.frame.fill(0);
|
|
432
|
+
observation = await this.observe(session, signal);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
317
435
|
session.report.status = decision.kind === "needsUser" ? "needs_user" : "completed";
|
|
318
|
-
session.report.message = decision.kind === "needsUser" ? decision.reason :
|
|
436
|
+
session.report.message = decision.kind === "needsUser" ? decision.reason : decision.summary;
|
|
319
437
|
session.report.stopReason = decision.kind === "needsUser" ? "user_takeover" : "model_finished_unverified";
|
|
320
438
|
if (decision.kind === "needsUser" && !session.report.mock) {
|
|
321
439
|
session.report.controlOwner = "none";
|
|
@@ -332,21 +450,59 @@ var PhoneRuntime = class {
|
|
|
332
450
|
const fresh = await this.observe(session, signal);
|
|
333
451
|
try {
|
|
334
452
|
this.allowedScreen(session, fresh);
|
|
335
|
-
if (
|
|
453
|
+
if (!canDispatchPhoneAction(decision.action, observation, fresh)) {
|
|
454
|
+
session.report.diagnostics.stalePredictions++;
|
|
455
|
+
recoveryHint = "The proposed action was not dispatched because its target changed. Locate it again on the fresh screen.";
|
|
336
456
|
observation.frame.fill(0);
|
|
337
457
|
observation = fresh;
|
|
338
458
|
continue;
|
|
339
459
|
}
|
|
340
460
|
if (epoch !== session.report.controlEpoch || session.report.controlOwner !== "agent") continue;
|
|
461
|
+
const attemptKey = `${observationSignature(fresh)}:${JSON.stringify(decision.action)}`;
|
|
462
|
+
const repetitions = attempts.get(attemptKey) ?? 0;
|
|
463
|
+
if (repetitions >= (decision.action.type === "wait" ? 3 : 2)) {
|
|
464
|
+
session.report.status = "paused";
|
|
465
|
+
session.report.controlOwner = "none";
|
|
466
|
+
session.report.controlEpoch++;
|
|
467
|
+
session.report.stopReason = "no_progress";
|
|
468
|
+
session.report.message = "Repeated actions made no progress. Inspect the phone and hand back before continuing.";
|
|
469
|
+
session.budget?.pause();
|
|
470
|
+
await session.stream?.revoke();
|
|
471
|
+
return this.publish(session);
|
|
472
|
+
}
|
|
473
|
+
attempts.set(attemptKey, repetitions + 1);
|
|
341
474
|
if (await this.dispatch(session, decision.action, fresh, context, signal, epoch)) actions++;
|
|
342
475
|
} finally {
|
|
343
476
|
if (observation !== fresh) fresh.frame.fill(0);
|
|
344
477
|
}
|
|
478
|
+
const beforeSignature = observationSignature(observation);
|
|
345
479
|
observation.frame.fill(0);
|
|
346
480
|
observation = await this.observe(session, signal);
|
|
481
|
+
const entry = session.trajectory.at(-1);
|
|
482
|
+
if (entry && entry.actor === "agent" && epoch === session.report.controlEpoch) {
|
|
483
|
+
const unchanged = beforeSignature === observationSignature(observation);
|
|
484
|
+
entry.afterApp = observation.package;
|
|
485
|
+
entry.effect = unchanged ? "unchanged" : "changed";
|
|
486
|
+
entry.facts = observation.elements.filter((node) => !node.password).flatMap((node) => [node.text, node.description]).filter(Boolean).slice(0, 12).map((text) => text.slice(0, 160));
|
|
487
|
+
if (unchanged) session.report.diagnostics.unchangedActions++;
|
|
488
|
+
recoveryHint = unchanged ? "The last dispatched action produced no observable change. Wait if loading, otherwise try a different target or route; do not repeat an uncertain input." : void 0;
|
|
489
|
+
}
|
|
347
490
|
}
|
|
348
491
|
} catch (error) {
|
|
349
492
|
const failure = signal.aborted && signal.reason instanceof PhoneError ? signal.reason : error;
|
|
493
|
+
if (!signal.aborted && failure instanceof PhoneError && ["PHONE_TEXT_INPUT_UNAVAILABLE", "PHONE_TEXT_INPUT_UNCONFIRMED"].includes(failure.code)) {
|
|
494
|
+
session.report.status = "needs_user";
|
|
495
|
+
session.human = void 0;
|
|
496
|
+
session.report.controlOwner = "none";
|
|
497
|
+
session.report.controlEpoch++;
|
|
498
|
+
session.report.errorCode = failure.code;
|
|
499
|
+
session.report.message = failure.message;
|
|
500
|
+
session.report.currentAction = void 0;
|
|
501
|
+
session.report.stopReason = failure.code === "PHONE_TEXT_INPUT_UNAVAILABLE" ? "text_input_required" : "text_input_unconfirmed";
|
|
502
|
+
session.budget?.pause();
|
|
503
|
+
await session.stream?.revoke();
|
|
504
|
+
return this.publish(session);
|
|
505
|
+
}
|
|
350
506
|
if (failure instanceof PhoneError && failure.code === "PHONE_OPERATION_TIMEOUT" && !terminal.has(session.report.status) && session.report.status !== "paused") {
|
|
351
507
|
session.report.status = "paused";
|
|
352
508
|
session.report.controlOwner = "none";
|
|
@@ -400,7 +556,8 @@ var PhoneRuntime = class {
|
|
|
400
556
|
status: session.report.status,
|
|
401
557
|
stopReason: session.report.stopReason,
|
|
402
558
|
actions: session.report.actions,
|
|
403
|
-
modelRequests: session.report.modelRequests
|
|
559
|
+
modelRequests: session.report.modelRequests,
|
|
560
|
+
diagnostics: session.report.diagnostics
|
|
404
561
|
}, "Phone goal finished");
|
|
405
562
|
}
|
|
406
563
|
}
|
|
@@ -416,8 +573,9 @@ var PhoneRuntime = class {
|
|
|
416
573
|
if (session.human && Date.now() > (session.humanExpiresAt ?? 0)) throw new PhoneError("PHONE_CONTROL_REQUIRED", "Manual control expired", 409);
|
|
417
574
|
if (!session.human && session.report.actions - (session.report.humanActions ?? 0) >= context.config.phone.maxActionsPerSession) throw new PhoneError("PHONE_ACTION_LIMIT", "Phone action limit reached");
|
|
418
575
|
if (session.human && (session.report.humanActions ?? 0) >= 1200) throw new PhoneError("PHONE_ACTION_LIMIT", "Manual input limit reached; reopen the session");
|
|
419
|
-
if (action.type === "launch" && !session.device
|
|
420
|
-
if (action.type === "home") throw new PhoneError("PHONE_APP_DENIED", "Home leaves the app allowlist");
|
|
576
|
+
if (action.type === "launch" && !phoneAllowsApp(session.device, action.package)) throw new PhoneError("PHONE_APP_DENIED", "Application is not authorized");
|
|
577
|
+
if (action.type === "home" && phoneAppAccess(session.device) !== "all") throw new PhoneError("PHONE_APP_DENIED", "Home leaves the app allowlist");
|
|
578
|
+
if (action.type === "typeText" && session.device.textInput === "manual") throw new PhoneError("PHONE_TEXT_INPUT_UNAVAILABLE", "Automatic text input is disabled for this phone. Ask the user to take over and type with the phone keyboard.", 409);
|
|
421
579
|
if (action.type === "typeText" && !observation.elements.some((node) => node.focused && node.editable && !node.password)) throw new PhoneError("PHONE_INPUT_NOT_FOCUSED", "Focus a non-password editable field before typing", 409);
|
|
422
580
|
const step = {
|
|
423
581
|
action: action.type,
|
|
@@ -433,14 +591,48 @@ var PhoneRuntime = class {
|
|
|
433
591
|
session.report.steps = session.report.steps.slice(-200);
|
|
434
592
|
}
|
|
435
593
|
this.publish(session);
|
|
594
|
+
const entry = {
|
|
595
|
+
action: structuredClone(action),
|
|
596
|
+
dispatch: "unknown",
|
|
597
|
+
actor: step.actor,
|
|
598
|
+
beforeApp: observation.package,
|
|
599
|
+
effect: "unknown"
|
|
600
|
+
};
|
|
601
|
+
session.trajectory.push(entry);
|
|
602
|
+
session.trajectoryCount++;
|
|
603
|
+
if (session.trajectory.length > 24) {
|
|
604
|
+
const earlier = session.trajectory.shift();
|
|
605
|
+
session.trajectorySummary.push(JSON.stringify({
|
|
606
|
+
action: earlier.action,
|
|
607
|
+
effect: earlier.effect,
|
|
608
|
+
afterApp: earlier.afterApp
|
|
609
|
+
}).slice(0, 250));
|
|
610
|
+
session.trajectorySummary = session.trajectorySummary.slice(-12);
|
|
611
|
+
}
|
|
436
612
|
try {
|
|
437
613
|
await session.driver.execute(session.device.serial, action, observation, signal, session.human ? {
|
|
438
614
|
settleMs: 0,
|
|
439
615
|
...resetTask ? { resetTask: true } : {}
|
|
440
616
|
} : void 0);
|
|
441
617
|
step.dispatch = "completed";
|
|
618
|
+
entry.dispatch = "completed";
|
|
442
619
|
} catch (error) {
|
|
443
|
-
|
|
620
|
+
const textFailure = error instanceof PhoneError && ["PHONE_TEXT_INPUT_UNAVAILABLE", "PHONE_TEXT_INPUT_UNCONFIRMED"].includes(error.code);
|
|
621
|
+
if (textFailure && error.code === "PHONE_TEXT_INPUT_UNAVAILABLE") {
|
|
622
|
+
session.report.steps.pop();
|
|
623
|
+
session.report.actions--;
|
|
624
|
+
session.trajectory.pop();
|
|
625
|
+
session.trajectoryCount--;
|
|
626
|
+
if (session.human) session.report.humanActions--;
|
|
627
|
+
} else session.report.status = "outcome_unknown";
|
|
628
|
+
if (textFailure && session.human) {
|
|
629
|
+
session.report.status = "paused";
|
|
630
|
+
session.report.errorCode = error.code;
|
|
631
|
+
session.report.message = error.message;
|
|
632
|
+
session.report.stopReason = error.code === "PHONE_TEXT_INPUT_UNAVAILABLE" ? "text_input_required" : "text_input_unconfirmed";
|
|
633
|
+
session.report.currentAction = void 0;
|
|
634
|
+
this.publish(session);
|
|
635
|
+
}
|
|
444
636
|
session.frames.clear();
|
|
445
637
|
throw error;
|
|
446
638
|
}
|
|
@@ -470,6 +662,8 @@ var PhoneRuntime = class {
|
|
|
470
662
|
if (op !== "pause") {
|
|
471
663
|
session.report.facts = void 0;
|
|
472
664
|
session.report.message = void 0;
|
|
665
|
+
session.trajectory = [];
|
|
666
|
+
session.trajectorySummary = [];
|
|
473
667
|
session.frames.clear();
|
|
474
668
|
session.adapter = { predict: async () => {
|
|
475
669
|
throw new PhoneError("PHONE_OPEN_REQUIRED", "Reopen the phone session");
|
|
@@ -497,7 +691,7 @@ var PhoneRuntime = class {
|
|
|
497
691
|
return this.io(session, async () => {
|
|
498
692
|
signal.throwIfAborted();
|
|
499
693
|
const observation = await (forText && session.driver.observeForText ? session.driver.observeForText : session.driver.observe).call(session.driver, session.device.serial, signal);
|
|
500
|
-
if (session.device
|
|
694
|
+
if (phoneAllowsApp(session.device, observation.package)) {
|
|
501
695
|
if (Date.now() - session.viewedAt < 3e4 && !terminal.has(session.report.status)) session.frames.set(observation);
|
|
502
696
|
} else session.frames.clear();
|
|
503
697
|
return observation;
|
|
@@ -597,6 +791,8 @@ var PhoneRuntime = class {
|
|
|
597
791
|
report: structuredClone(session.report),
|
|
598
792
|
ownControl: Boolean(session.human === client),
|
|
599
793
|
allowedApps: [...session.device.allowedApps],
|
|
794
|
+
appAccess: phoneAppAccess(session.device),
|
|
795
|
+
textInput: session.device.textInput ?? "auto",
|
|
600
796
|
frame: session.frames.get() ? {
|
|
601
797
|
seq: session.frames.get().seq,
|
|
602
798
|
capturedAt: session.frames.get().capturedAt
|
|
@@ -631,7 +827,7 @@ var PhoneRuntime = class {
|
|
|
631
827
|
if (terminal.has(session.report.status)) throw new PhoneError("PHONE_VIDEO_ENDED", "Phone session ended", 409);
|
|
632
828
|
this.guard(session, session.context);
|
|
633
829
|
const app = await provider.foreground(AbortSignal.timeout(2e3));
|
|
634
|
-
if (!session.device
|
|
830
|
+
if (!phoneAllowsApp(session.device, app)) throw new PhoneError("PHONE_APP_DENIED", "Current page is outside the authorized applications", 403);
|
|
635
831
|
session.viewedAt = Date.now();
|
|
636
832
|
}, (client, epoch) => {
|
|
637
833
|
if (terminal.has(session.report.status) || session.report.handoff !== "idle" || session.report.controlOwner !== "human" || session.human !== client || session.report.controlEpoch !== epoch || Date.now() > (session.humanExpiresAt ?? 0)) throw new PhoneError("PHONE_CONTROL_CHANGED", "Manual control is no longer available", 409);
|
|
@@ -811,7 +1007,7 @@ var PhoneRuntime = class {
|
|
|
811
1007
|
for (const wake of session.wake) wake();
|
|
812
1008
|
} else if (input.op === "recover") {
|
|
813
1009
|
if (session.human !== client || Date.now() > (session.humanExpiresAt ?? 0)) throw new PhoneError("PHONE_CONTROL_REQUIRED", "Take over the phone first", 409);
|
|
814
|
-
if (input.action?.type !== "launch" || !session.device
|
|
1010
|
+
if (input.action?.type !== "launch" || !phoneAllowsApp(session.device, input.action.package)) throw new PhoneError("PHONE_APP_DENIED", "Choose an authorized recovery application", 403);
|
|
815
1011
|
const observation = await this.io(session, () => session.driver.capture ? session.driver.capture(session.device.serial, session.controller.signal) : session.driver.observe(session.device.serial, session.controller.signal));
|
|
816
1012
|
try {
|
|
817
1013
|
if (!await this.dispatch(session, input.action, observation, context, session.controller.signal, input.epoch, true)) throw new PhoneError("PHONE_CONTROL_CHANGED", "Control changed before recovery", 409);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PhoneError, init_contracts } from "./contracts.js";
|
|
2
|
+
import { SCRCPY_SHA256, SCRCPY_VERSION, managedScrcpyServerPath } from "../runtime-tools/phone-dependency-catalog.js";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
//#region src/phone/scrcpy-assets.ts
|
|
6
|
+
init_contracts();
|
|
7
|
+
const scrcpyServerPath = managedScrcpyServerPath;
|
|
8
|
+
async function readScrcpyServer() {
|
|
9
|
+
const binary = await readFile(scrcpyServerPath()).catch(() => {
|
|
10
|
+
throw new PhoneError("PHONE_VIDEO_SETUP_REQUIRED", "Install scrcpy server in Settings → Phone Use → Dependencies");
|
|
11
|
+
});
|
|
12
|
+
if (createHash("sha256").update(binary).digest("hex") !== "7e70323ba7f259649dd4acce97ac4fefbae8102b2c6d91e2e7be613fd5354be0") throw new PhoneError("PHONE_VIDEO_HASH_MISMATCH", "Phone video server checksum mismatch");
|
|
13
|
+
return binary;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { SCRCPY_SHA256, SCRCPY_VERSION, readScrcpyServer, scrcpyServerPath };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Only supplied text is sent to Android; clipboard autosync and reads stay off. */
|
|
2
|
+
export declare class ScrcpyTextChannel {
|
|
3
|
+
private write;
|
|
4
|
+
private buffer;
|
|
5
|
+
private sequence;
|
|
6
|
+
private pending;
|
|
7
|
+
constructor(write: (bytes: Buffer) => Promise<void>);
|
|
8
|
+
receive(bytes: Buffer): void;
|
|
9
|
+
paste(text: string, signal: AbortSignal): Promise<void>;
|
|
10
|
+
close(): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { PhoneError, init_contracts } from "./contracts.js";
|
|
2
|
+
//#region src/phone/scrcpy-text.ts
|
|
3
|
+
init_contracts();
|
|
4
|
+
/** Only supplied text is sent to Android; clipboard autosync and reads stay off. */
|
|
5
|
+
var ScrcpyTextChannel = class {
|
|
6
|
+
buffer = Buffer.alloc(0);
|
|
7
|
+
sequence = 0n;
|
|
8
|
+
pending = /* @__PURE__ */ new Map();
|
|
9
|
+
constructor(write) {
|
|
10
|
+
this.write = write;
|
|
11
|
+
}
|
|
12
|
+
receive(bytes) {
|
|
13
|
+
this.buffer = Buffer.concat([this.buffer, bytes]);
|
|
14
|
+
while (this.buffer.length) {
|
|
15
|
+
const type = this.buffer[0];
|
|
16
|
+
let length;
|
|
17
|
+
if (type === 1) {
|
|
18
|
+
if (this.buffer.length < 9) return;
|
|
19
|
+
const seq = this.buffer.readBigUInt64BE(1);
|
|
20
|
+
this.pending.get(seq)?.resolve();
|
|
21
|
+
this.pending.delete(seq);
|
|
22
|
+
length = 9;
|
|
23
|
+
} else if (type === 0 || type === 2) {
|
|
24
|
+
if (this.buffer.length < 5) return;
|
|
25
|
+
length = 5 + (type === 0 ? this.buffer.readUInt32BE(1) : this.buffer.readUInt16BE(3));
|
|
26
|
+
if (length > 262144) throw new Error("Invalid device message");
|
|
27
|
+
if (this.buffer.length < length) return;
|
|
28
|
+
} else throw new Error("Unknown device message");
|
|
29
|
+
this.buffer = this.buffer.subarray(length);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async paste(text, signal) {
|
|
33
|
+
signal.throwIfAborted();
|
|
34
|
+
if (!text || text.length > 4e3 || this.pending.size) throw new PhoneError("PHONE_INVALID_INPUT", "Invalid or concurrent phone text input");
|
|
35
|
+
const sequence = ++this.sequence;
|
|
36
|
+
const payload = Buffer.from(text);
|
|
37
|
+
const bytes = Buffer.alloc(14 + payload.length);
|
|
38
|
+
bytes[0] = 9;
|
|
39
|
+
bytes.writeBigUInt64BE(sequence, 1);
|
|
40
|
+
bytes[9] = 1;
|
|
41
|
+
bytes.writeUInt32BE(payload.length, 10);
|
|
42
|
+
payload.copy(bytes, 14);
|
|
43
|
+
payload.fill(0);
|
|
44
|
+
const deadline = AbortSignal.any([signal, AbortSignal.timeout(1e4)]);
|
|
45
|
+
let abort;
|
|
46
|
+
const ack = new Promise((resolve, reject) => {
|
|
47
|
+
abort = () => reject(new PhoneError("PHONE_TEXT_INPUT_UNCONFIRMED", "Text may have been pasted. Take over and inspect the input field before retrying.", 409));
|
|
48
|
+
this.pending.set(sequence, {
|
|
49
|
+
resolve,
|
|
50
|
+
reject
|
|
51
|
+
});
|
|
52
|
+
deadline.addEventListener("abort", abort, { once: true });
|
|
53
|
+
});
|
|
54
|
+
try {
|
|
55
|
+
await Promise.all([this.write(bytes), ack]);
|
|
56
|
+
} catch {
|
|
57
|
+
throw new PhoneError("PHONE_TEXT_INPUT_UNCONFIRMED", "Text may have been pasted. Take over and inspect the input field before retrying.", 409);
|
|
58
|
+
} finally {
|
|
59
|
+
bytes.fill(0);
|
|
60
|
+
deadline.removeEventListener("abort", abort);
|
|
61
|
+
this.pending.delete(sequence);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
close() {
|
|
65
|
+
for (const request of this.pending.values()) request.reject(new PhoneError("PHONE_TEXT_INPUT_UNCONFIRMED", "Phone text channel disconnected; inspect the field before retrying.", 409));
|
|
66
|
+
this.pending.clear();
|
|
67
|
+
this.buffer.fill(0);
|
|
68
|
+
this.buffer = Buffer.alloc(0);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { ScrcpyTextChannel };
|