taphound 0.2.0-dev.1 → 0.2.0-dev.12
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/README.md +49 -149
- package/README.zh-CN.md +47 -147
- package/assets/skills/taphound-case-suite/SKILL.md +363 -0
- package/assets/skills/taphound-case-suite/schemas/base-flow-record.schema.json +22 -0
- package/assets/skills/taphound-case-suite/schemas/case-catalog.schema.json +26 -0
- package/assets/skills/taphound-case-suite/schemas/case-ledger.schema.json +158 -0
- package/assets/skills/taphound-case-suite/schemas/suite-input.schema.json +41 -0
- package/assets/skills/taphound-case-suite/schemas/transition.schema.json +61 -0
- package/assets/skills/taphound-case-suite/scripts/ledger.mjs +1207 -0
- package/assets/skills/taphound-case-suite/templates/base-flow-record.example.json +20 -0
- package/assets/skills/taphound-case-suite/templates/suite-input.example.json +27 -0
- package/assets/skills/taphound-case-suite/templates/transition.example.json +8 -0
- package/assets/skills/taphound-flash/SKILL.md +115 -0
- package/assets/skills/taphound-flash/scripts/flash.mjs +585 -0
- package/assets/skills/taphound-flash/templates/flash-plan.example.json +15 -0
- package/assets/skills/taphound-journey-brief-author/CONTEXT-GUIDE.md +471 -0
- package/assets/skills/taphound-journey-brief-author/SKILL.md +494 -0
- package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.md +96 -0
- package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.zh-CN.md +80 -0
- package/assets/skills/taphound-journey-brief-author/prompts/context-analyze-project.md +309 -0
- package/assets/skills/taphound-journey-brief-author/schemas/context-refresh-result.json +123 -0
- package/assets/skills/taphound-journey-brief-author/schemas/project-context-module.json +192 -0
- package/assets/skills/taphound-journey-brief-author/schemas/project-context.json +152 -0
- package/assets/skills/taphound-journey-brief-author/templates/project-context-module.example.json +58 -0
- package/assets/skills/taphound-journey-brief-author/templates/project-context.example.json +51 -0
- package/assets/skills/taphound-journey-brief-author/templates/taphound-journey-brief.template.md +63 -0
- package/assets/skills/taphound-journey-generator/SKILL.md +507 -0
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/prompts/check-completion.md +3 -0
- package/assets/skills/taphound-journey-generator/prompts/consume-journey-brief.md +52 -0
- package/assets/skills/taphound-journey-generator/prompts/generate-step.md +211 -0
- package/assets/skills/taphound-journey-generator/prompts/select-flow.md +91 -0
- package/assets/skills/taphound-journey-generator/references/bridge.md +35 -0
- package/assets/skills/taphound-journey-generator/references/mid-session.md +44 -0
- package/assets/skills/taphound-journey-generator/schemas/external-flow.json +37 -0
- package/assets/skills/taphound-journey-generator/schemas/flow.json +28 -0
- package/assets/skills/taphound-journey-generator/schemas/journey-source.json +25 -0
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/observe-output.json +13 -4
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/proposed-step-envelope.json +127 -5
- package/assets/skills/taphound-journey-generator/scripts/envelope.mjs +578 -0
- package/assets/skills/taphound-journey-generator/templates/external-flow.example.json +22 -0
- package/assets/skills/taphound-journey-generator/templates/flow.example.json +22 -0
- package/assets/skills/taphound-journey-generator/templates/journey-source.example.json +17 -0
- package/assets/skills/taphound-journey-generator/templates/taphound-journey-brief.example.md +70 -0
- package/assets/skills/taphound-verify-change/SKILL.md +91 -0
- package/assets/skills/taphound-verify-change/references/accept.md +38 -0
- package/assets/skills/taphound-verify-change/references/preserve.md +357 -0
- package/assets/skills/taphound-verify-change/schemas/workflow-manifest.schema.json +295 -0
- package/assets/skills/taphound-verify-change/scripts/handoff.mjs +482 -0
- package/assets/skills/taphound-verify-change/scripts/ui-refactor.mjs +505 -0
- package/dist/adapters/adb/adb-adapter.d.ts +16 -2
- package/dist/adapters/adb/adb-adapter.js +199 -0
- package/dist/adapters/adb/system-uiautomator-snapshot-provider.d.ts +13 -0
- package/dist/adapters/adb/system-uiautomator-snapshot-provider.js +122 -0
- package/dist/adapters/adb/ui-automator-parser.d.ts +3 -0
- package/dist/adapters/adb/ui-automator-parser.js +160 -0
- package/dist/adapters/adb/window-topology-parser.d.ts +2 -0
- package/dist/adapters/adb/window-topology-parser.js +101 -0
- package/dist/adapters/android-cli/android-cli-adapter.d.ts +16 -8
- package/dist/adapters/android-cli/android-cli-adapter.js +137 -15
- package/dist/adapters/android-cli/android-cli-snapshot-provider.d.ts +9 -0
- package/dist/adapters/android-cli/android-cli-snapshot-provider.js +99 -0
- package/dist/adapters/android-cli/layout-parser.js +12 -8
- package/dist/adapters/appium/appium-doctor.d.ts +7 -0
- package/dist/adapters/appium/appium-doctor.js +128 -0
- package/dist/adapters/appium/appium-ui-snapshot-provider.d.ts +42 -0
- package/dist/adapters/appium/appium-ui-snapshot-provider.js +309 -0
- package/dist/adapters/camera/camera-probe-adapter.d.ts +18 -0
- package/dist/adapters/camera/camera-probe-adapter.js +286 -0
- package/dist/adapters/filesystem/artifact-store.js +40 -1
- package/dist/adapters/filesystem/context-document-writer.d.ts +15 -0
- package/dist/adapters/filesystem/context-document-writer.js +112 -0
- package/dist/adapters/filesystem/diagnostics-journal.d.ts +17 -0
- package/dist/adapters/filesystem/diagnostics-journal.js +130 -0
- package/dist/adapters/filesystem/external-flow-registry.d.ts +17 -0
- package/dist/adapters/filesystem/external-flow-registry.js +325 -0
- package/dist/adapters/filesystem/generation-session-store.d.ts +17 -11
- package/dist/adapters/filesystem/generation-session-store.js +281 -855
- package/dist/adapters/filesystem/generation-store/session-lock.d.ts +29 -0
- package/dist/adapters/filesystem/generation-store/session-lock.js +202 -0
- package/dist/adapters/filesystem/generation-store/session-transitions.d.ts +24 -0
- package/dist/adapters/filesystem/generation-store/session-transitions.js +340 -0
- package/dist/adapters/filesystem/generation-store/store-files.d.ts +50 -0
- package/dist/adapters/filesystem/generation-store/store-files.js +363 -0
- package/dist/adapters/filesystem/journey-composition-store.d.ts +23 -0
- package/dist/adapters/filesystem/journey-composition-store.js +151 -0
- package/dist/adapters/filesystem/knowledge-registry.d.ts +12 -0
- package/dist/adapters/filesystem/knowledge-registry.js +223 -0
- package/dist/adapters/filesystem/project-bound-file.js +3 -5
- package/dist/adapters/filesystem/project-file-inspector.d.ts +2 -0
- package/dist/adapters/filesystem/project-file-inspector.js +57 -28
- package/dist/adapters/filesystem/project-identity-inspector.d.ts +7 -0
- package/dist/adapters/filesystem/project-identity-inspector.js +215 -0
- package/dist/adapters/filesystem/project-inventory-inspector.d.ts +8 -0
- package/dist/adapters/filesystem/project-inventory-inspector.js +93 -0
- package/dist/adapters/filesystem/project-module-discoverer.d.ts +6 -0
- package/dist/adapters/filesystem/project-module-discoverer.js +158 -0
- package/dist/adapters/filesystem/skill-installer.d.ts +7 -2
- package/dist/adapters/filesystem/skill-installer.js +40 -11
- package/dist/adapters/filesystem/workspace-layout.d.ts +7 -0
- package/dist/adapters/filesystem/workspace-layout.js +57 -0
- package/dist/adapters/git/node-git-diff.d.ts +17 -0
- package/dist/adapters/git/node-git-diff.js +114 -0
- package/dist/adapters/process/node-detached-process-launcher.d.ts +6 -0
- package/dist/adapters/process/node-detached-process-launcher.js +96 -0
- package/dist/adapters/process/node-process-runner.js +11 -2
- package/dist/adapters/prompt/inquirer-align-prompt.d.ts +27 -0
- package/dist/adapters/prompt/inquirer-align-prompt.js +65 -0
- package/dist/adapters/prompt/inquirer-generation-prompt.js +3 -0
- package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +9 -1
- package/dist/adapters/prompt/inquirer-recorder-prompt.js +63 -0
- package/dist/adapters/runtime/adb-runtime-backend.d.ts +67 -0
- package/dist/adapters/runtime/adb-runtime-backend.js +206 -0
- package/dist/adapters/runtime/fake-runtime-backend.d.ts +87 -0
- package/dist/adapters/runtime/fake-runtime-backend.js +240 -0
- package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.d.ts +45 -0
- package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.js +153 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.d.ts +23 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.js +39 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.d.ts +20 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.js +180 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.d.ts +78 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.js +379 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-tools.d.ts +46 -0
- package/dist/adapters/runtime/session-adb-view.d.ts +53 -0
- package/dist/adapters/runtime/session-adb-view.js +193 -0
- package/dist/adapters/runtime/session-backed-ports.d.ts +21 -0
- package/dist/adapters/runtime/session-backed-ports.js +43 -0
- package/dist/adapters/runtime/shared-session-runtime-backend.d.ts +12 -0
- package/dist/adapters/runtime/shared-session-runtime-backend.js +64 -0
- package/dist/adapters/ui/auto-ui-snapshot-provider.d.ts +8 -0
- package/dist/adapters/ui/auto-ui-snapshot-provider.js +48 -0
- package/dist/adapters/ui/device-layout-temp.d.ts +6 -0
- package/dist/adapters/ui/device-layout-temp.js +18 -0
- package/dist/adapters/ui/device-ui-environment.d.ts +13 -0
- package/dist/adapters/ui/device-ui-environment.js +81 -0
- package/dist/adapters/ui/layout-normalization.d.ts +9 -0
- package/dist/adapters/ui/layout-normalization.js +44 -0
- package/dist/adapters/ui/ui-snapshot-error.d.ts +11 -0
- package/dist/adapters/ui/ui-snapshot-error.js +12 -0
- package/dist/adapters/ui/ui-snapshot-support.d.ts +15 -0
- package/dist/adapters/ui/ui-snapshot-support.js +12 -0
- package/dist/application/align/align-service.d.ts +42 -0
- package/dist/application/align/align-service.js +106 -0
- package/dist/application/assertion/expectation-evaluator.d.ts +18 -5
- package/dist/application/assertion/expectation-evaluator.js +156 -18
- package/dist/application/assertion/guarded-expectation.d.ts +45 -0
- package/dist/application/assertion/guarded-expectation.js +74 -0
- package/dist/application/checkpoint/baseline-capturer.d.ts +26 -0
- package/dist/application/checkpoint/baseline-capturer.js +163 -0
- package/dist/application/checkpoint/baseline-error.d.ts +5 -0
- package/dist/application/checkpoint/baseline-error.js +8 -0
- package/dist/application/checkpoint/baseline-service.d.ts +31 -0
- package/dist/application/checkpoint/baseline-service.js +147 -0
- package/dist/application/checkpoint/checkpoint-evaluator.d.ts +35 -0
- package/dist/application/checkpoint/checkpoint-evaluator.js +415 -0
- package/dist/application/checkpoint/regression-comparator.d.ts +18 -0
- package/dist/application/checkpoint/regression-comparator.js +192 -0
- package/dist/application/collector/logcat-collector.d.ts +22 -1
- package/dist/application/collector/logcat-collector.js +103 -15
- package/dist/application/collector/logcat-event.d.ts +8 -0
- package/dist/application/collector/logcat-event.js +79 -0
- package/dist/application/collector/logcat-stop.d.ts +2 -0
- package/dist/application/collector/logcat-stop.js +11 -0
- package/dist/application/context/context-generator.d.ts +45 -0
- package/dist/application/context/context-generator.js +228 -0
- package/dist/application/context/context-loader.d.ts +40 -0
- package/dist/application/context/context-loader.js +223 -0
- package/dist/application/context/context-refresher.d.ts +59 -0
- package/dist/application/context/context-refresher.js +349 -0
- package/dist/application/context/context-rehasher.d.ts +36 -0
- package/dist/application/context/context-rehasher.js +110 -0
- package/dist/application/context/context-validator.d.ts +23 -1
- package/dist/application/context/context-validator.js +116 -9
- package/dist/application/context/evidence-hash.d.ts +1 -0
- package/dist/application/context/evidence-hash.js +125 -0
- package/dist/application/context/evidence-match.d.ts +9 -0
- package/dist/application/context/evidence-match.js +8 -0
- package/dist/application/context/shard-identity.d.ts +2 -0
- package/dist/application/context/shard-identity.js +7 -0
- package/dist/application/contract/contract-loader.d.ts +25 -0
- package/dist/application/contract/contract-loader.js +68 -0
- package/dist/application/contract/contract-review.d.ts +25 -0
- package/dist/application/contract/contract-review.js +51 -0
- package/dist/application/contract/contract-verifier.d.ts +53 -0
- package/dist/application/contract/contract-verifier.js +645 -0
- package/dist/application/devices/resolve-device-assignments.d.ts +21 -0
- package/dist/application/devices/resolve-device-assignments.js +60 -0
- package/dist/application/diagnosis/failure-classifier.d.ts +23 -0
- package/dist/application/diagnosis/failure-classifier.js +179 -0
- package/dist/application/diagnostics/diagnostics-exporter.d.ts +29 -0
- package/dist/application/diagnostics/diagnostics-exporter.js +283 -0
- package/dist/application/diagnostics/ui-capture-telemetry.d.ts +16 -0
- package/dist/application/diagnostics/ui-capture-telemetry.js +81 -0
- package/dist/application/doctor/doctor-service.d.ts +36 -3
- package/dist/application/doctor/doctor-service.js +255 -25
- package/dist/application/generation/generation-app-preparer.d.ts +20 -0
- package/dist/application/generation/generation-app-preparer.js +28 -0
- package/dist/application/generation/generation-config-service.d.ts +21 -0
- package/dist/application/generation/generation-config-service.js +53 -0
- package/dist/application/generation/generation-confirmation-service.d.ts +14 -3
- package/dist/application/generation/generation-confirmation-service.js +109 -24
- package/dist/application/generation/generation-context-snapshot.d.ts +6 -0
- package/dist/application/generation/generation-context-snapshot.js +29 -0
- package/dist/application/generation/generation-finalizer.d.ts +15 -9
- package/dist/application/generation/generation-finalizer.js +167 -70
- package/dist/application/generation/generation-publisher.js +12 -8
- package/dist/application/generation/generation-recovery-service.d.ts +35 -0
- package/dist/application/generation/generation-recovery-service.js +144 -0
- package/dist/application/generation/generation-reopen-service.d.ts +15 -0
- package/dist/application/generation/generation-reopen-service.js +37 -0
- package/dist/application/generation/generation-replace-service.d.ts +32 -0
- package/dist/application/generation/generation-replace-service.js +116 -0
- package/dist/application/generation/generation-starter.d.ts +44 -4
- package/dist/application/generation/generation-starter.js +175 -8
- package/dist/application/generation/generation-step-executor.d.ts +67 -5
- package/dist/application/generation/generation-step-executor.js +471 -108
- package/dist/application/generation/proposed-step-validator.js +96 -30
- package/dist/application/generation/replay-policy-loader.d.ts +16 -0
- package/dist/application/generation/replay-policy-loader.js +29 -0
- package/dist/application/generation/risk-evaluator.d.ts +4 -1
- package/dist/application/generation/risk-evaluator.js +118 -3
- package/dist/application/generation/runtime-observer.d.ts +39 -7
- package/dist/application/generation/runtime-observer.js +193 -24
- package/dist/application/impact/impact-resolver.d.ts +32 -0
- package/dist/application/impact/impact-resolver.js +160 -0
- package/dist/application/init/init-service.js +26 -13
- package/dist/application/interaction/action-executor.d.ts +3 -2
- package/dist/application/interaction/action-executor.js +11 -1
- package/dist/application/interaction/action-target.d.ts +17 -0
- package/dist/application/interaction/action-target.js +40 -0
- package/dist/application/interaction/external-step-runner.d.ts +153 -0
- package/dist/application/interaction/external-step-runner.js +332 -0
- package/dist/application/interaction/fallback-resolver.d.ts +6 -3
- package/dist/application/interaction/fallback-resolver.js +7 -5
- package/dist/application/interaction/scroll-to-executor.d.ts +17 -3
- package/dist/application/interaction/scroll-to-executor.js +118 -36
- package/dist/application/journey/external-flow-resolver.d.ts +25 -0
- package/dist/application/journey/external-flow-resolver.js +22 -0
- package/dist/application/journey/journey-check-service.d.ts +54 -0
- package/dist/application/journey/journey-check-service.js +207 -0
- package/dist/application/journey/journey-promoter.d.ts +26 -0
- package/dist/application/journey/journey-promoter.js +123 -0
- package/dist/application/journey/journey-resolver.d.ts +42 -0
- package/dist/application/journey/journey-resolver.js +239 -0
- package/dist/application/journey/journey-retirer.d.ts +25 -0
- package/dist/application/journey/journey-retirer.js +87 -0
- package/dist/application/knowledge/anchor-resolver.d.ts +36 -0
- package/dist/application/knowledge/anchor-resolver.js +110 -0
- package/dist/application/knowledge/knowledge-loader.d.ts +16 -0
- package/dist/application/knowledge/knowledge-loader.js +36 -0
- package/dist/application/locator/layout-failure-summary.d.ts +2 -0
- package/dist/application/locator/layout-failure-summary.js +67 -0
- package/dist/application/locator/layout-traversal.d.ts +11 -0
- package/dist/application/locator/layout-traversal.js +18 -0
- package/dist/application/locator/locator-resolver.d.ts +13 -1
- package/dist/application/locator/locator-resolver.js +182 -18
- package/dist/application/observe/observe-service.d.ts +20 -0
- package/dist/application/observe/observe-service.js +91 -0
- package/dist/application/project/project-describer.d.ts +14 -6
- package/dist/application/project/project-describer.js +38 -5
- package/dist/application/recognition/screen-detector.d.ts +28 -0
- package/dist/application/recognition/screen-detector.js +111 -0
- package/dist/application/recorder/locator-selector.js +151 -17
- package/dist/application/recorder/recorder-service.d.ts +16 -4
- package/dist/application/recorder/recorder-service.js +420 -128
- package/dist/application/report/report-writer.d.ts +5 -0
- package/dist/application/report/report-writer.js +17 -1
- package/dist/application/runtime/cold-launch.d.ts +28 -0
- package/dist/application/runtime/cold-launch.js +59 -0
- package/dist/application/runtime/step-runner.d.ts +49 -4
- package/dist/application/runtime/step-runner.js +721 -130
- package/dist/application/runtime/verify-runtime.d.ts +57 -7
- package/dist/application/runtime/verify-runtime.js +577 -175
- package/dist/application/ui/cached-ui-snapshot-provider.d.ts +45 -0
- package/dist/application/ui/cached-ui-snapshot-provider.js +111 -0
- package/dist/application/ui/observed-ui-snapshot-provider.d.ts +15 -0
- package/dist/application/ui/observed-ui-snapshot-provider.js +66 -0
- package/dist/application/ui/ui-snapshot-lifecycle.d.ts +2 -0
- package/dist/application/ui/ui-snapshot-lifecycle.js +8 -0
- package/dist/application/ui/ui-stability-probe.d.ts +9 -0
- package/dist/application/ui/ui-stability-probe.js +24 -0
- package/dist/application/wait/idle-advice.d.ts +6 -0
- package/dist/application/wait/idle-advice.js +27 -0
- package/dist/application/wait/idle-profiles.d.ts +15 -0
- package/dist/application/wait/idle-profiles.js +75 -0
- package/dist/application/wait/idle-waiter.d.ts +28 -4
- package/dist/application/wait/idle-waiter.js +201 -15
- package/dist/cli/commands/align.d.ts +3 -0
- package/dist/cli/commands/align.js +124 -0
- package/dist/cli/commands/baseline.d.ts +3 -0
- package/dist/cli/commands/baseline.js +130 -0
- package/dist/cli/commands/context.js +300 -27
- package/dist/cli/commands/contract.d.ts +3 -0
- package/dist/cli/commands/contract.js +120 -0
- package/dist/cli/commands/diagnose.d.ts +3 -0
- package/dist/cli/commands/diagnose.js +84 -0
- package/dist/cli/commands/doctor.js +16 -9
- package/dist/cli/commands/failure.d.ts +3 -0
- package/dist/cli/commands/failure.js +63 -0
- package/dist/cli/commands/generation/lifecycle-commands.d.ts +5 -0
- package/dist/cli/commands/generation/lifecycle-commands.js +211 -0
- package/dist/cli/commands/generation/session-commands.d.ts +11 -0
- package/dist/cli/commands/generation/session-commands.js +472 -0
- package/dist/cli/commands/generation/shared.d.ts +1414 -0
- package/dist/cli/commands/generation/shared.js +291 -0
- package/dist/cli/commands/generation/step-commands.d.ts +8 -0
- package/dist/cli/commands/generation/step-commands.js +315 -0
- package/dist/cli/commands/generation.js +10 -507
- package/dist/cli/commands/impact.d.ts +3 -0
- package/dist/cli/commands/impact.js +87 -0
- package/dist/cli/commands/init.js +13 -6
- package/dist/cli/commands/journey.d.ts +3 -0
- package/dist/cli/commands/journey.js +382 -0
- package/dist/cli/commands/knowledge.d.ts +3 -0
- package/dist/cli/commands/knowledge.js +93 -0
- package/dist/cli/commands/observe.d.ts +3 -0
- package/dist/cli/commands/observe.js +127 -0
- package/dist/cli/commands/project.js +2 -1
- package/dist/cli/commands/record.js +18 -5
- package/dist/cli/commands/verify.js +326 -64
- package/dist/cli/dependencies.d.ts +136 -1
- package/dist/cli/dependencies.js +504 -52
- package/dist/cli/diagnostics-recorder.d.ts +31 -0
- package/dist/cli/diagnostics-recorder.js +82 -0
- package/dist/cli/diff-verification.d.ts +41 -0
- package/dist/cli/diff-verification.js +192 -0
- package/dist/cli/main.d.ts +1 -1
- package/dist/cli/main.js +107 -2
- package/dist/cli/output.d.ts +5 -3
- package/dist/cli/output.js +12 -0
- package/dist/cli/program.js +21 -1
- package/dist/cli/project-root.d.ts +1 -0
- package/dist/cli/project-root.js +6 -0
- package/dist/cli/runtime-selection.d.ts +22 -0
- package/dist/cli/runtime-selection.js +92 -0
- package/dist/cli/version.d.ts +1 -0
- package/dist/cli/version.js +14 -0
- package/dist/cli/workspace-guard.d.ts +2 -0
- package/dist/cli/workspace-guard.js +3 -0
- package/dist/domain/binding-reference.d.ts +4 -0
- package/dist/domain/binding-reference.js +23 -0
- package/dist/domain/checkpoint.d.ts +404 -0
- package/dist/domain/checkpoint.js +297 -0
- package/dist/domain/config.d.ts +107 -1
- package/dist/domain/config.js +52 -6
- package/dist/domain/contract.d.ts +413 -0
- package/dist/domain/contract.js +196 -0
- package/dist/domain/diagnostics.d.ts +1396 -0
- package/dist/domain/diagnostics.js +193 -0
- package/dist/domain/external-flow.d.ts +493 -0
- package/dist/domain/external-flow.js +15 -0
- package/dist/domain/failure-classification.d.ts +176 -0
- package/dist/domain/failure-classification.js +169 -0
- package/dist/domain/failure.d.ts +2 -1
- package/dist/domain/failure.js +123 -0
- package/dist/domain/generation.d.ts +1671 -132
- package/dist/domain/generation.js +202 -13
- package/dist/domain/geometry.d.ts +13 -0
- package/dist/domain/geometry.js +16 -0
- package/dist/domain/impact.d.ts +71 -0
- package/dist/domain/impact.js +76 -0
- package/dist/domain/init.d.ts +12 -4
- package/dist/domain/init.js +24 -18
- package/dist/domain/journey-composition.d.ts +2285 -0
- package/dist/domain/journey-composition.js +85 -0
- package/dist/domain/journey-lifecycle.d.ts +10 -0
- package/dist/domain/journey-lifecycle.js +9 -0
- package/dist/domain/journey.d.ts +2419 -212
- package/dist/domain/journey.js +384 -14
- package/dist/domain/knowledge.d.ts +165 -0
- package/dist/domain/knowledge.js +188 -0
- package/dist/domain/layout.d.ts +34 -5
- package/dist/domain/layout.js +64 -6
- package/dist/domain/locator-evidence.d.ts +3 -0
- package/dist/domain/locator-evidence.js +36 -0
- package/dist/domain/logcat-event.d.ts +54 -0
- package/dist/domain/logcat-event.js +40 -0
- package/dist/domain/observation.d.ts +33 -0
- package/dist/domain/observation.js +18 -0
- package/dist/domain/project-context.d.ts +219 -1
- package/dist/domain/project-context.js +209 -6
- package/dist/domain/proposed-step.d.ts +340 -60
- package/dist/domain/proposed-step.js +23 -3
- package/dist/domain/report.d.ts +1455 -28
- package/dist/domain/report.js +211 -14
- package/dist/domain/runtime-snapshot.d.ts +58 -1
- package/dist/domain/runtime-snapshot.js +14 -3
- package/dist/domain/runtime.d.ts +50 -0
- package/dist/domain/runtime.js +36 -0
- package/dist/domain/system-app-profiles.d.ts +6 -0
- package/dist/domain/system-app-profiles.js +43 -0
- package/dist/domain/ui-backend.d.ts +28 -0
- package/dist/domain/ui-backend.js +33 -0
- package/dist/domain/ui-cache.d.ts +11 -0
- package/dist/domain/ui-cache.js +10 -0
- package/dist/domain/verify-receipt.d.ts +17 -0
- package/dist/domain/verify-receipt.js +17 -0
- package/dist/domain/window-hierarchy.d.ts +84 -0
- package/dist/domain/window-hierarchy.js +172 -0
- package/dist/domain/workflow-manifest.d.ts +77 -0
- package/dist/domain/workflow-manifest.js +136 -0
- package/dist/domain/workspace.d.ts +30 -0
- package/dist/domain/workspace.js +74 -0
- package/dist/ports/adb.d.ts +34 -1
- package/dist/ports/align-prompt.d.ts +11 -0
- package/dist/ports/align-prompt.js +6 -0
- package/dist/ports/anchor-resolver.d.ts +30 -0
- package/dist/ports/anchor-resolver.js +1 -0
- package/dist/ports/annotated-screen-resolver.d.ts +4 -0
- package/dist/ports/annotated-screen-resolver.js +1 -0
- package/dist/ports/camera-probe.d.ts +23 -0
- package/dist/ports/camera-probe.js +8 -0
- package/dist/ports/context-document-writer.d.ts +24 -0
- package/dist/ports/context-document-writer.js +1 -0
- package/dist/ports/detached-process-launcher.d.ts +12 -0
- package/dist/ports/detached-process-launcher.js +1 -0
- package/dist/ports/diagnostics.d.ts +15 -0
- package/dist/ports/diagnostics.js +1 -0
- package/dist/ports/external-flow-registry.d.ts +37 -0
- package/dist/ports/external-flow-registry.js +1 -0
- package/dist/ports/generation-prompt.d.ts +2 -1
- package/dist/ports/generation-session-store.d.ts +12 -2
- package/dist/ports/git-diff.d.ts +9 -0
- package/dist/ports/git-diff.js +1 -0
- package/dist/ports/journey-composition-store.d.ts +17 -0
- package/dist/ports/journey-composition-store.js +1 -0
- package/dist/ports/knowledge-registry.d.ts +20 -0
- package/dist/ports/knowledge-registry.js +1 -0
- package/dist/ports/process-runner.d.ts +7 -0
- package/dist/ports/project-file-inspector.d.ts +1 -0
- package/dist/ports/project-identity-inspector.d.ts +24 -0
- package/dist/ports/project-identity-inspector.js +1 -0
- package/dist/ports/project-inventory-inspector.d.ts +21 -0
- package/dist/ports/project-inventory-inspector.js +1 -0
- package/dist/ports/project-module-discoverer.d.ts +26 -0
- package/dist/ports/project-module-discoverer.js +1 -0
- package/dist/ports/recorder-prompt.d.ts +10 -1
- package/dist/ports/runtime-backend.d.ts +89 -0
- package/dist/ports/runtime-backend.js +13 -0
- package/dist/ports/runtime-capability.d.ts +12 -0
- package/dist/ports/runtime-capability.js +20 -0
- package/dist/ports/runtime-session-ports.d.ts +25 -0
- package/dist/ports/runtime-session-ports.js +1 -0
- package/dist/ports/screenshot.d.ts +11 -0
- package/dist/ports/screenshot.js +1 -0
- package/dist/ports/skill-installer.d.ts +2 -2
- package/dist/ports/ui-snapshot.d.ts +35 -0
- package/dist/ports/ui-snapshot.js +1 -0
- package/dist/ports/ui-stability.d.ts +19 -0
- package/dist/ports/ui-stability.js +1 -0
- package/dist/ports/workspace-layout.d.ts +4 -0
- package/dist/ports/workspace-layout.js +1 -0
- package/dist/shared/errors.d.ts +2 -0
- package/dist/shared/errors.js +22 -0
- package/dist/shared/paths.d.ts +2 -0
- package/dist/shared/paths.js +24 -0
- package/dist/shared/strings.d.ts +2 -0
- package/dist/shared/strings.js +16 -0
- package/package.json +7 -2
- package/assets/skills/taphound-ai-journey/GUIDE.md +0 -862
- package/assets/skills/taphound-ai-journey/SKILL.md +0 -377
- package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +0 -526
- package/assets/skills/taphound-ai-journey/prompts/generate-step.md +0 -109
- package/assets/skills/taphound-ai-journey/schemas/project-context.json +0 -81
- package/assets/skills/taphound-ai-journey/templates/project-context.example.json +0 -49
- package/dist/ports/android-cli.d.ts +0 -21
- /package/dist/{ports/android-cli.js → adapters/runtime/mobile-mcp/mobile-mcp-tools.js} +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { resolveLocator } from "../locator/locator-resolver.js";
|
|
2
|
+
/**
|
|
3
|
+
* The element an action acts on, with the capability semantics Generation,
|
|
4
|
+
* generated Replay, and External Flow steps share: a click or longClick
|
|
5
|
+
* reaches the nearest ancestor with that capability (touching the matched
|
|
6
|
+
* element itself), and a swipe needs a scrollable element with bounds.
|
|
7
|
+
*/
|
|
8
|
+
export function resolveActionTarget(layout, action, locator, viewport) {
|
|
9
|
+
const located = resolveLocator(layout, locator, {
|
|
10
|
+
viewport,
|
|
11
|
+
...(action === "swipe" ? {} : {
|
|
12
|
+
requiredCapability: action === "click" ? "clickable" : "longClickable"
|
|
13
|
+
})
|
|
14
|
+
});
|
|
15
|
+
if (located.status !== "found") {
|
|
16
|
+
return located;
|
|
17
|
+
}
|
|
18
|
+
const element = located.element;
|
|
19
|
+
if (action === "click" && element.clickable !== true) {
|
|
20
|
+
return actionFailure("click target is not clickable");
|
|
21
|
+
}
|
|
22
|
+
if (action === "longClick" && element.longClickable !== true) {
|
|
23
|
+
return actionFailure("longClick target is not longClickable");
|
|
24
|
+
}
|
|
25
|
+
if (action === "swipe"
|
|
26
|
+
&& (element.scrollable !== true || element.bounds === undefined)) {
|
|
27
|
+
return actionFailure("swipe target lacks scrollable bounds");
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
status: "found",
|
|
31
|
+
located,
|
|
32
|
+
target: {
|
|
33
|
+
point: located.point,
|
|
34
|
+
...(element.bounds === undefined ? {} : { bounds: element.bounds })
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function actionFailure(message) {
|
|
39
|
+
return { status: "failed", code: "ACTION_FAILED", message };
|
|
40
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { ExternalStep } from "../../domain/external-flow.js";
|
|
2
|
+
import type { FailureCode } from "../../domain/failure.js";
|
|
3
|
+
import type { DisplayViewport } from "../../domain/geometry.js";
|
|
4
|
+
import { type JourneyStep } from "../../domain/journey.js";
|
|
5
|
+
import type { LayoutElement } from "../../domain/layout.js";
|
|
6
|
+
import type { AdbPort } from "../../ports/adb.js";
|
|
7
|
+
import type { Clock } from "../../ports/clock.js";
|
|
8
|
+
import type { UiSnapshotProvider } from "../../ports/ui-snapshot.js";
|
|
9
|
+
import type { IdleConfig, IdleResult, IdleWaiter } from "../wait/idle-waiter.js";
|
|
10
|
+
import type { ActionExecutor, ActionTarget } from "./action-executor.js";
|
|
11
|
+
/**
|
|
12
|
+
* The single implementation of External Flow steps and bridge foreground
|
|
13
|
+
* polling shared by Replay and Generation, so a flow that generates also
|
|
14
|
+
* replays under exactly the same rules.
|
|
15
|
+
*/
|
|
16
|
+
/** Foreground/layout observation budget inside an escaped app. */
|
|
17
|
+
export declare const EXTERNAL_OBSERVATION_TIMEOUT_MS = 5000;
|
|
18
|
+
export interface ExternalStepFailure {
|
|
19
|
+
code: FailureCode;
|
|
20
|
+
message: string;
|
|
21
|
+
details?: unknown;
|
|
22
|
+
}
|
|
23
|
+
export type ExternalFlowOutcome = {
|
|
24
|
+
status: "passed";
|
|
25
|
+
} | {
|
|
26
|
+
status: "cancelled";
|
|
27
|
+
} | ({
|
|
28
|
+
status: "failed";
|
|
29
|
+
stepIndex: number;
|
|
30
|
+
} & ExternalStepFailure);
|
|
31
|
+
export interface ExternalStepRunnerDependencies {
|
|
32
|
+
adb: Pick<AdbPort, "foregroundComponent">;
|
|
33
|
+
actionExecutor: ActionExecutor;
|
|
34
|
+
uiSnapshotProvider: UiSnapshotProvider;
|
|
35
|
+
captureLayout: (reason: "locate" | "expect", timeoutMs: number, signal?: AbortSignal) => Promise<readonly LayoutElement[]>;
|
|
36
|
+
createIdleWaiter: (packageName: string) => IdleWaiter;
|
|
37
|
+
idle: IdleConfig;
|
|
38
|
+
viewport: () => DisplayViewport | undefined;
|
|
39
|
+
deviceSerial: string;
|
|
40
|
+
/** Records idle-timeout evidence before the failure is reported. */
|
|
41
|
+
onIdleTimeout?: ((stepIndex: number, idle: Extract<IdleResult, {
|
|
42
|
+
status: "timeout";
|
|
43
|
+
}>) => Promise<void>) | undefined;
|
|
44
|
+
}
|
|
45
|
+
export declare function idleTimeoutDetails(idle: Extract<IdleResult, {
|
|
46
|
+
status: "timeout";
|
|
47
|
+
}>): Record<string, unknown>;
|
|
48
|
+
export declare function externalStepToJourneyStep(step: ExternalStep): JourneyStep;
|
|
49
|
+
export declare class ExternalStepRunner {
|
|
50
|
+
private readonly dependencies;
|
|
51
|
+
constructor(dependencies: ExternalStepRunnerDependencies);
|
|
52
|
+
run(steps: readonly ExternalStep[], escapedPackageName: string, signal?: AbortSignal): Promise<ExternalFlowOutcome>;
|
|
53
|
+
private assertForeground;
|
|
54
|
+
private runStep;
|
|
55
|
+
private evaluateExpect;
|
|
56
|
+
}
|
|
57
|
+
export type ForegroundPollOutcome = {
|
|
58
|
+
status: "matched";
|
|
59
|
+
packageName: string;
|
|
60
|
+
} | {
|
|
61
|
+
status: "timeout";
|
|
62
|
+
} | {
|
|
63
|
+
status: "cancelled";
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Polls the foreground package until `until` accepts it: bridge escape
|
|
67
|
+
* detection (`packageName !== target`) and return detection (`=== target`).
|
|
68
|
+
*/
|
|
69
|
+
export declare function pollForegroundPackage(input: {
|
|
70
|
+
adb: Pick<AdbPort, "foregroundComponent">;
|
|
71
|
+
clock: Clock;
|
|
72
|
+
packageName: string;
|
|
73
|
+
deviceSerial: string;
|
|
74
|
+
until: (packageName: string) => boolean;
|
|
75
|
+
timeoutMs: number;
|
|
76
|
+
signal?: AbortSignal | undefined;
|
|
77
|
+
}): Promise<ForegroundPollOutcome>;
|
|
78
|
+
export interface BridgeRunDependencies {
|
|
79
|
+
adb: Pick<AdbPort, "foregroundComponent">;
|
|
80
|
+
clock: Clock;
|
|
81
|
+
actionExecutor: ActionExecutor;
|
|
82
|
+
externalSteps: ExternalStepRunner;
|
|
83
|
+
idleWaiter: IdleWaiter;
|
|
84
|
+
idle: IdleConfig;
|
|
85
|
+
packageName: string;
|
|
86
|
+
deviceSerial: string;
|
|
87
|
+
}
|
|
88
|
+
export interface BridgeRunInput {
|
|
89
|
+
trigger: {
|
|
90
|
+
step: JourneyStep;
|
|
91
|
+
target: ActionTarget;
|
|
92
|
+
};
|
|
93
|
+
escapeTimeoutMs: number;
|
|
94
|
+
returnTimeoutMs: number;
|
|
95
|
+
/** Engine policy for the escaped package; a failure stops the bridge. */
|
|
96
|
+
acceptEscape: (escapedPackageName: string) => ExternalStepFailure | undefined;
|
|
97
|
+
external: ExternalPhase;
|
|
98
|
+
signal?: AbortSignal | undefined;
|
|
99
|
+
}
|
|
100
|
+
/** What happens inside the escaped app between escape and return. */
|
|
101
|
+
export type ExternalPhase =
|
|
102
|
+
/** Replay and Generation run a known step list, if any. */
|
|
103
|
+
{
|
|
104
|
+
kind: "steps";
|
|
105
|
+
steps: (escapedPackageName: string) => Promise<readonly ExternalStep[] | undefined>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The Recorder drives the escaped app one chosen step at a time (each
|
|
109
|
+
* through the same ExternalStepRunner) and reports the steps it ran.
|
|
110
|
+
*/
|
|
111
|
+
| {
|
|
112
|
+
kind: "drive";
|
|
113
|
+
drive: (escapedPackageName: string) => Promise<ExternalDriveOutcome>;
|
|
114
|
+
};
|
|
115
|
+
export type ExternalDriveOutcome = {
|
|
116
|
+
status: "driven";
|
|
117
|
+
steps: readonly ExternalStep[];
|
|
118
|
+
} | {
|
|
119
|
+
status: "cancelled";
|
|
120
|
+
} | ({
|
|
121
|
+
status: "failed";
|
|
122
|
+
} & ExternalStepFailure);
|
|
123
|
+
export interface BridgeTiming {
|
|
124
|
+
actionMs: number;
|
|
125
|
+
waitMs: number;
|
|
126
|
+
}
|
|
127
|
+
export type BridgeOutcome = {
|
|
128
|
+
status: "returned";
|
|
129
|
+
escapedPackageName: string;
|
|
130
|
+
externalSteps?: readonly ExternalStep[] | undefined;
|
|
131
|
+
idle: Extract<IdleResult, {
|
|
132
|
+
status: "stable";
|
|
133
|
+
}>;
|
|
134
|
+
timing: BridgeTiming;
|
|
135
|
+
} | ({
|
|
136
|
+
status: "failed";
|
|
137
|
+
idle?: Extract<IdleResult, {
|
|
138
|
+
status: "timeout";
|
|
139
|
+
}> | undefined;
|
|
140
|
+
timing: BridgeTiming;
|
|
141
|
+
} & ExternalStepFailure) | {
|
|
142
|
+
status: "cancelled";
|
|
143
|
+
timing: BridgeTiming;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* The single bridge step flow for Replay and Generation: trigger, escape,
|
|
147
|
+
* policy check, external steps, return, and settle.
|
|
148
|
+
*/
|
|
149
|
+
export declare class BridgeRunner {
|
|
150
|
+
private readonly dependencies;
|
|
151
|
+
constructor(dependencies: BridgeRunDependencies);
|
|
152
|
+
run(input: BridgeRunInput): Promise<BridgeOutcome>;
|
|
153
|
+
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { JourneyStepSchema } from "../../domain/journey.js";
|
|
2
|
+
import { elementPredicateMismatch } from "../assertion/expectation-evaluator.js";
|
|
3
|
+
import { resolveLocator } from "../locator/locator-resolver.js";
|
|
4
|
+
import { withIdleAdvice } from "../wait/idle-advice.js";
|
|
5
|
+
import { resolveActionTarget } from "./action-target.js";
|
|
6
|
+
import { ScrollToExecutor } from "./scroll-to-executor.js";
|
|
7
|
+
/**
|
|
8
|
+
* The single implementation of External Flow steps and bridge foreground
|
|
9
|
+
* polling shared by Replay and Generation, so a flow that generates also
|
|
10
|
+
* replays under exactly the same rules.
|
|
11
|
+
*/
|
|
12
|
+
/** Foreground/layout observation budget inside an escaped app. */
|
|
13
|
+
export const EXTERNAL_OBSERVATION_TIMEOUT_MS = 5000;
|
|
14
|
+
const BRIDGE_POLL_INTERVAL_MS = 500;
|
|
15
|
+
class ExternalStepStop extends Error {
|
|
16
|
+
failure;
|
|
17
|
+
constructor(failure) {
|
|
18
|
+
super(failure === "cancelled" ? "cancelled" : failure.message);
|
|
19
|
+
this.failure = failure;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function stop(code, message, details) {
|
|
23
|
+
throw new ExternalStepStop({
|
|
24
|
+
code,
|
|
25
|
+
message,
|
|
26
|
+
...(details === undefined ? {} : { details })
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function cancelled() {
|
|
30
|
+
throw new ExternalStepStop("cancelled");
|
|
31
|
+
}
|
|
32
|
+
function checkCancelled(signal) {
|
|
33
|
+
if (signal?.aborted === true)
|
|
34
|
+
cancelled();
|
|
35
|
+
}
|
|
36
|
+
export function idleTimeoutDetails(idle) {
|
|
37
|
+
return {
|
|
38
|
+
idle: {
|
|
39
|
+
strategy: idle.strategy,
|
|
40
|
+
...(idle.backend === undefined ? {} : { backend: idle.backend }),
|
|
41
|
+
polls: idle.polls,
|
|
42
|
+
durationMs: idle.durationMs,
|
|
43
|
+
samplingDurationMs: idle.samplingDurationMs,
|
|
44
|
+
fallbackUsed: idle.fallbackUsed,
|
|
45
|
+
frameActivityDetected: idle.frameActivityDetected,
|
|
46
|
+
lastDiff: idle.lastDiff
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function externalStepToJourneyStep(step) {
|
|
51
|
+
const { expectedActivity, ...rest } = step;
|
|
52
|
+
return JourneyStepSchema.parse({
|
|
53
|
+
...rest,
|
|
54
|
+
activity: { before: expectedActivity, after: expectedActivity }
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function requireExternalTarget(layout, step, viewport) {
|
|
58
|
+
if (step.action !== "click"
|
|
59
|
+
&& step.action !== "longClick"
|
|
60
|
+
&& step.action !== "swipe") {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
const resolution = resolveActionTarget(layout, step.action, step.locator, viewport);
|
|
64
|
+
if (resolution.status !== "found") {
|
|
65
|
+
stop(resolution.code, resolution.message);
|
|
66
|
+
}
|
|
67
|
+
return resolution.target;
|
|
68
|
+
}
|
|
69
|
+
export class ExternalStepRunner {
|
|
70
|
+
dependencies;
|
|
71
|
+
constructor(dependencies) {
|
|
72
|
+
this.dependencies = dependencies;
|
|
73
|
+
}
|
|
74
|
+
async run(steps, escapedPackageName, signal) {
|
|
75
|
+
for (const [stepIndex, step] of steps.entries()) {
|
|
76
|
+
try {
|
|
77
|
+
checkCancelled(signal);
|
|
78
|
+
await this.runStep(stepIndex, step, escapedPackageName, signal);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (!(error instanceof ExternalStepStop))
|
|
82
|
+
throw error;
|
|
83
|
+
return error.failure === "cancelled"
|
|
84
|
+
? { status: "cancelled" }
|
|
85
|
+
: { status: "failed", stepIndex, ...error.failure };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { status: "passed" };
|
|
89
|
+
}
|
|
90
|
+
async assertForeground(escapedPackageName, expectedActivity, phase, signal) {
|
|
91
|
+
const foreground = await this.dependencies.adb.foregroundComponent({
|
|
92
|
+
packageName: escapedPackageName,
|
|
93
|
+
deviceSerial: this.dependencies.deviceSerial,
|
|
94
|
+
...(signal === undefined ? {} : { signal }),
|
|
95
|
+
timeoutMs: EXTERNAL_OBSERVATION_TIMEOUT_MS
|
|
96
|
+
});
|
|
97
|
+
checkCancelled(signal);
|
|
98
|
+
if (foreground.packageName !== escapedPackageName) {
|
|
99
|
+
stop("EXTERNAL_PACKAGE_MISMATCH", `External app foreground changed ${phase}: expected "${escapedPackageName}", got "${foreground.packageName}"`);
|
|
100
|
+
}
|
|
101
|
+
if (foreground.activity !== expectedActivity) {
|
|
102
|
+
stop("EXTERNAL_ACTIVITY_MISMATCH", `External Activity mismatch ${phase}: expected "${expectedActivity}", got "${foreground.activity}"`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async runStep(stepIndex, step, escapedPackageName, signal) {
|
|
106
|
+
const { dependencies } = this;
|
|
107
|
+
await this.assertForeground(escapedPackageName, step.expectedActivity, "before the step", signal);
|
|
108
|
+
const layout = await dependencies.captureLayout("locate", EXTERNAL_OBSERVATION_TIMEOUT_MS, signal);
|
|
109
|
+
checkCancelled(signal);
|
|
110
|
+
if (step.action === "scrollTo") {
|
|
111
|
+
const scroll = await new ScrollToExecutor({
|
|
112
|
+
uiSnapshotProvider: dependencies.uiSnapshotProvider,
|
|
113
|
+
actionExecutor: dependencies.actionExecutor,
|
|
114
|
+
idleWaiter: dependencies.createIdleWaiter(escapedPackageName),
|
|
115
|
+
deviceSerial: dependencies.deviceSerial,
|
|
116
|
+
idle: dependencies.idle,
|
|
117
|
+
viewport: dependencies.viewport,
|
|
118
|
+
beforeSwipe: async () => {
|
|
119
|
+
await this.assertForeground(escapedPackageName, step.expectedActivity, "during scrolling", signal);
|
|
120
|
+
return dependencies.captureLayout("locate", EXTERNAL_OBSERVATION_TIMEOUT_MS, signal);
|
|
121
|
+
},
|
|
122
|
+
beforeMutation: () => this.assertForeground(escapedPackageName, step.expectedActivity, "before mutation", signal),
|
|
123
|
+
requireLiveContainerCapability: true
|
|
124
|
+
}).execute(externalStepToJourneyStep(step), signal, layout);
|
|
125
|
+
if (scroll.status === "cancelled")
|
|
126
|
+
cancelled();
|
|
127
|
+
if (scroll.status === "failed") {
|
|
128
|
+
stop(scroll.code, scroll.message, scroll.idle === undefined ? undefined : { idle: scroll.idle });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
const target = requireExternalTarget(layout, step, dependencies.viewport());
|
|
133
|
+
const action = await dependencies.actionExecutor.execute(externalStepToJourneyStep(step), target, signal);
|
|
134
|
+
if (action.status === "failed")
|
|
135
|
+
stop(action.code, action.message);
|
|
136
|
+
}
|
|
137
|
+
const idle = await dependencies
|
|
138
|
+
.createIdleWaiter(escapedPackageName)
|
|
139
|
+
.waitUntilIdle(dependencies.idle, signal);
|
|
140
|
+
if (idle.status === "cancelled")
|
|
141
|
+
cancelled();
|
|
142
|
+
if (idle.status === "timeout") {
|
|
143
|
+
await dependencies.onIdleTimeout?.(stepIndex, idle);
|
|
144
|
+
stop(idle.code, withIdleAdvice("External app layout did not become stable after step", idle), idleTimeoutDetails(idle));
|
|
145
|
+
}
|
|
146
|
+
checkCancelled(signal);
|
|
147
|
+
await this.evaluateExpect(step, escapedPackageName, signal);
|
|
148
|
+
}
|
|
149
|
+
async evaluateExpect(step, escapedPackageName, signal) {
|
|
150
|
+
const expect = step.expect;
|
|
151
|
+
if (expect === undefined)
|
|
152
|
+
return;
|
|
153
|
+
if (expect.type === "activity") {
|
|
154
|
+
const foreground = await this.dependencies.adb.foregroundComponent({
|
|
155
|
+
packageName: expect.packageName ?? escapedPackageName,
|
|
156
|
+
deviceSerial: this.dependencies.deviceSerial,
|
|
157
|
+
...(signal === undefined ? {} : { signal }),
|
|
158
|
+
timeoutMs: expect.timeoutMs
|
|
159
|
+
});
|
|
160
|
+
checkCancelled(signal);
|
|
161
|
+
if (foreground.activity !== expect.value) {
|
|
162
|
+
stop("EXTERNAL_STEP_FAILED", `External expect Activity mismatch: expected "${expect.value}", got "${foreground.activity}"`);
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (expect.type !== "element") {
|
|
167
|
+
stop("EXTERNAL_STEP_FAILED", "Logcat expectations are not supported for external steps");
|
|
168
|
+
}
|
|
169
|
+
const layout = await this.dependencies.captureLayout("expect", expect.timeoutMs, signal);
|
|
170
|
+
checkCancelled(signal);
|
|
171
|
+
const resolution = resolveLocator(layout, expect.locator, {
|
|
172
|
+
requireEnabled: false
|
|
173
|
+
});
|
|
174
|
+
if (expect.absent === true) {
|
|
175
|
+
if (resolution.status !== "failed"
|
|
176
|
+
|| resolution.code !== "LOCATOR_NOT_FOUND"
|
|
177
|
+
|| resolution.evidenceMismatch === true) {
|
|
178
|
+
stop("EXTERNAL_STEP_FAILED", resolution.status === "found"
|
|
179
|
+
? "External expect expected an absent element but it was found"
|
|
180
|
+
: `External expect expected an absent element: ${resolution.message}`);
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (resolution.status !== "found") {
|
|
185
|
+
stop("EXTERNAL_STEP_FAILED", `External expect element not found: ${resolution.message}`);
|
|
186
|
+
}
|
|
187
|
+
const mismatch = elementPredicateMismatch(resolution.element, expect);
|
|
188
|
+
if (mismatch !== undefined) {
|
|
189
|
+
stop("EXTERNAL_STEP_FAILED", `External expect element predicate mismatch: ${mismatch}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Polls the foreground package until `until` accepts it: bridge escape
|
|
195
|
+
* detection (`packageName !== target`) and return detection (`=== target`).
|
|
196
|
+
*/
|
|
197
|
+
export async function pollForegroundPackage(input) {
|
|
198
|
+
const deadline = input.clock.now() + input.timeoutMs;
|
|
199
|
+
while (input.clock.now() < deadline) {
|
|
200
|
+
if (input.signal?.aborted === true)
|
|
201
|
+
return { status: "cancelled" };
|
|
202
|
+
const foreground = await input.adb.foregroundComponent({
|
|
203
|
+
packageName: input.packageName,
|
|
204
|
+
deviceSerial: input.deviceSerial,
|
|
205
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
206
|
+
timeoutMs: EXTERNAL_OBSERVATION_TIMEOUT_MS
|
|
207
|
+
});
|
|
208
|
+
if (input.until(foreground.packageName)) {
|
|
209
|
+
return { status: "matched", packageName: foreground.packageName };
|
|
210
|
+
}
|
|
211
|
+
await input.clock.sleep(Math.min(BRIDGE_POLL_INTERVAL_MS, Math.max(0, deadline - input.clock.now())), input.signal);
|
|
212
|
+
}
|
|
213
|
+
return { status: "timeout" };
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* The single bridge step flow for Replay and Generation: trigger, escape,
|
|
217
|
+
* policy check, external steps, return, and settle.
|
|
218
|
+
*/
|
|
219
|
+
export class BridgeRunner {
|
|
220
|
+
dependencies;
|
|
221
|
+
constructor(dependencies) {
|
|
222
|
+
this.dependencies = dependencies;
|
|
223
|
+
}
|
|
224
|
+
async run(input) {
|
|
225
|
+
const { clock } = this.dependencies;
|
|
226
|
+
const timing = { actionMs: 0, waitMs: 0 };
|
|
227
|
+
const failed = (failure, idle) => ({
|
|
228
|
+
status: "failed",
|
|
229
|
+
...failure,
|
|
230
|
+
...(idle === undefined ? {} : { idle }),
|
|
231
|
+
timing
|
|
232
|
+
});
|
|
233
|
+
const cancelledOutcome = () => ({ status: "cancelled", timing });
|
|
234
|
+
const actionStartedAt = clock.now();
|
|
235
|
+
const action = await this.dependencies.actionExecutor.execute(input.trigger.step, input.trigger.target, input.signal);
|
|
236
|
+
timing.actionMs = clock.now() - actionStartedAt;
|
|
237
|
+
if (input.signal?.aborted === true)
|
|
238
|
+
return cancelledOutcome();
|
|
239
|
+
if (action.status === "failed") {
|
|
240
|
+
return failed({ code: action.code, message: action.message });
|
|
241
|
+
}
|
|
242
|
+
const waitStartedAt = clock.now();
|
|
243
|
+
const escaped = await pollForegroundPackage({
|
|
244
|
+
adb: this.dependencies.adb,
|
|
245
|
+
clock,
|
|
246
|
+
packageName: this.dependencies.packageName,
|
|
247
|
+
deviceSerial: this.dependencies.deviceSerial,
|
|
248
|
+
until: (packageName) => packageName !== this.dependencies.packageName,
|
|
249
|
+
timeoutMs: input.escapeTimeoutMs,
|
|
250
|
+
signal: input.signal
|
|
251
|
+
});
|
|
252
|
+
timing.waitMs += clock.now() - waitStartedAt;
|
|
253
|
+
if (escaped.status === "cancelled")
|
|
254
|
+
return cancelledOutcome();
|
|
255
|
+
if (escaped.status === "timeout") {
|
|
256
|
+
return failed({
|
|
257
|
+
code: "BRIDGE_NO_ESCAPE",
|
|
258
|
+
message: "Bridge trigger did not cause a package escape"
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
const rejected = input.acceptEscape(escaped.packageName);
|
|
262
|
+
if (rejected !== undefined)
|
|
263
|
+
return failed(rejected);
|
|
264
|
+
let externalSteps;
|
|
265
|
+
if (input.external.kind === "drive") {
|
|
266
|
+
const driven = await input.external.drive(escaped.packageName);
|
|
267
|
+
if (driven.status === "cancelled")
|
|
268
|
+
return cancelledOutcome();
|
|
269
|
+
if (driven.status === "failed") {
|
|
270
|
+
return failed({
|
|
271
|
+
code: driven.code,
|
|
272
|
+
message: driven.message,
|
|
273
|
+
...(driven.details === undefined ? {} : { details: driven.details })
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
externalSteps = driven.steps;
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
externalSteps = await input.external.steps(escaped.packageName);
|
|
280
|
+
if (externalSteps !== undefined) {
|
|
281
|
+
const external = await this.dependencies.externalSteps.run(externalSteps, escaped.packageName, input.signal);
|
|
282
|
+
if (external.status === "cancelled")
|
|
283
|
+
return cancelledOutcome();
|
|
284
|
+
if (external.status === "failed") {
|
|
285
|
+
return failed({
|
|
286
|
+
code: external.code,
|
|
287
|
+
message: external.message,
|
|
288
|
+
...(external.details === undefined ? {} : { details: external.details })
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const returnStartedAt = clock.now();
|
|
294
|
+
const returned = await pollForegroundPackage({
|
|
295
|
+
adb: this.dependencies.adb,
|
|
296
|
+
clock,
|
|
297
|
+
packageName: this.dependencies.packageName,
|
|
298
|
+
deviceSerial: this.dependencies.deviceSerial,
|
|
299
|
+
until: (packageName) => packageName === this.dependencies.packageName,
|
|
300
|
+
timeoutMs: input.returnTimeoutMs,
|
|
301
|
+
signal: input.signal
|
|
302
|
+
});
|
|
303
|
+
timing.waitMs += clock.now() - returnStartedAt;
|
|
304
|
+
if (returned.status === "cancelled")
|
|
305
|
+
return cancelledOutcome();
|
|
306
|
+
if (returned.status === "timeout") {
|
|
307
|
+
return failed({
|
|
308
|
+
code: "BRIDGE_NOT_RETURNED",
|
|
309
|
+
message: "Foreground did not return to target package within the timeout"
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
const idleStartedAt = clock.now();
|
|
313
|
+
const idle = await this.dependencies.idleWaiter.waitUntilIdle(this.dependencies.idle, input.signal);
|
|
314
|
+
timing.waitMs += clock.now() - idleStartedAt;
|
|
315
|
+
if (idle.status === "cancelled")
|
|
316
|
+
return cancelledOutcome();
|
|
317
|
+
if (idle.status === "timeout") {
|
|
318
|
+
return failed({
|
|
319
|
+
code: idle.code,
|
|
320
|
+
message: withIdleAdvice("Layout did not become stable after bridge return", idle),
|
|
321
|
+
details: idleTimeoutDetails(idle)
|
|
322
|
+
}, idle);
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
status: "returned",
|
|
326
|
+
escapedPackageName: escaped.packageName,
|
|
327
|
+
...(externalSteps === undefined ? {} : { externalSteps }),
|
|
328
|
+
idle,
|
|
329
|
+
timing
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { FailureCode } from "../../domain/failure.js";
|
|
2
2
|
import type { JourneyStep } from "../../domain/journey.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { Point } from "../../domain/geometry.js";
|
|
4
|
+
import type { AnnotatedScreenResolverPort } from "../../ports/annotated-screen-resolver.js";
|
|
5
|
+
import type { ScreenshotPort } from "../../ports/screenshot.js";
|
|
4
6
|
export type FallbackResolution = {
|
|
5
7
|
status: "unavailable";
|
|
6
8
|
} | {
|
|
@@ -17,8 +19,9 @@ export type FallbackResolution = {
|
|
|
17
19
|
annotatedScreenshotPath?: string | undefined;
|
|
18
20
|
};
|
|
19
21
|
export declare class FallbackResolver {
|
|
20
|
-
private readonly
|
|
22
|
+
private readonly screenshots;
|
|
23
|
+
private readonly annotatedScreens;
|
|
21
24
|
private readonly deviceSerial;
|
|
22
|
-
constructor(
|
|
25
|
+
constructor(screenshots: ScreenshotPort, annotatedScreens: AnnotatedScreenResolverPort, deviceSerial: string);
|
|
23
26
|
resolve(step: JourneyStep, annotatedScreenshotPath: string, signal?: AbortSignal): Promise<FallbackResolution>;
|
|
24
27
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export class FallbackResolver {
|
|
2
|
-
|
|
2
|
+
screenshots;
|
|
3
|
+
annotatedScreens;
|
|
3
4
|
deviceSerial;
|
|
4
|
-
constructor(
|
|
5
|
-
this.
|
|
5
|
+
constructor(screenshots, annotatedScreens, deviceSerial) {
|
|
6
|
+
this.screenshots = screenshots;
|
|
7
|
+
this.annotatedScreens = annotatedScreens;
|
|
6
8
|
this.deviceSerial = deviceSerial;
|
|
7
9
|
}
|
|
8
10
|
async resolve(step, annotatedScreenshotPath, signal) {
|
|
@@ -10,7 +12,7 @@ export class FallbackResolver {
|
|
|
10
12
|
|| step.fallback === undefined) {
|
|
11
13
|
return { status: "unavailable" };
|
|
12
14
|
}
|
|
13
|
-
const capture = await this.
|
|
15
|
+
const capture = await this.screenshots.capture({
|
|
14
16
|
outputPath: annotatedScreenshotPath,
|
|
15
17
|
annotate: true,
|
|
16
18
|
deviceSerial: this.deviceSerial,
|
|
@@ -24,7 +26,7 @@ export class FallbackResolver {
|
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
try {
|
|
27
|
-
const point = await this.
|
|
29
|
+
const point = await this.annotatedScreens.resolve(annotatedScreenshotPath, step.fallback.label, signal);
|
|
28
30
|
return {
|
|
29
31
|
status: "found",
|
|
30
32
|
point,
|
|
@@ -1,39 +1,53 @@
|
|
|
1
1
|
import type { FailureCode } from "../../domain/failure.js";
|
|
2
2
|
import type { JourneyStep } from "../../domain/journey.js";
|
|
3
3
|
import type { LayoutElement } from "../../domain/layout.js";
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
4
|
+
import type { DisplayViewport } from "../../domain/geometry.js";
|
|
5
|
+
import type { AnchorResolverPort } from "../../ports/anchor-resolver.js";
|
|
6
|
+
import type { UiSnapshotProvider } from "../../ports/ui-snapshot.js";
|
|
7
|
+
import type { IdleBackend, IdleConfig, IdleStrategy, IdleWaiter } from "../wait/idle-waiter.js";
|
|
6
8
|
import type { ActionExecutor } from "./action-executor.js";
|
|
7
9
|
export type ScrollToExecutionResult = {
|
|
8
10
|
status: "found";
|
|
9
11
|
swipesUsed: number;
|
|
12
|
+
idleDurationMs: number;
|
|
10
13
|
} | {
|
|
11
14
|
status: "failed";
|
|
12
15
|
code: FailureCode;
|
|
13
16
|
message: string;
|
|
14
17
|
swipesUsed: number;
|
|
18
|
+
idleDurationMs: number;
|
|
15
19
|
idle?: {
|
|
16
20
|
polls: number;
|
|
21
|
+
durationMs: number;
|
|
22
|
+
samplingDurationMs: number;
|
|
23
|
+
strategy: IdleStrategy;
|
|
24
|
+
backend?: IdleBackend | undefined;
|
|
25
|
+
fallbackUsed: boolean;
|
|
26
|
+
frameActivityDetected: boolean;
|
|
17
27
|
lastDiff: readonly unknown[];
|
|
18
28
|
};
|
|
19
29
|
} | {
|
|
20
30
|
status: "cancelled";
|
|
21
31
|
swipesUsed: number;
|
|
32
|
+
idleDurationMs: number;
|
|
22
33
|
};
|
|
23
34
|
export interface ScrollToExecutorOptions {
|
|
24
|
-
|
|
35
|
+
uiSnapshotProvider: UiSnapshotProvider;
|
|
25
36
|
actionExecutor: Pick<ActionExecutor, "swipeBounds">;
|
|
26
37
|
idleWaiter: Pick<IdleWaiter, "waitUntilIdle">;
|
|
27
38
|
deviceSerial: string;
|
|
28
39
|
idle: IdleConfig;
|
|
40
|
+
anchorResolver?: AnchorResolverPort | undefined;
|
|
29
41
|
readLayout?: (() => Promise<readonly LayoutElement[]>) | undefined;
|
|
30
42
|
beforeSwipe?: (() => Promise<readonly LayoutElement[]>) | undefined;
|
|
31
43
|
beforeMutation?: (() => Promise<void>) | undefined;
|
|
32
44
|
requireLiveContainerCapability?: boolean | undefined;
|
|
45
|
+
viewport?: (() => DisplayViewport | undefined) | undefined;
|
|
33
46
|
}
|
|
34
47
|
export declare class ScrollToExecutor {
|
|
35
48
|
private readonly options;
|
|
36
49
|
constructor(options: ScrollToExecutorOptions);
|
|
50
|
+
private resolveTarget;
|
|
37
51
|
execute(step: Extract<JourneyStep, {
|
|
38
52
|
action: "scrollTo";
|
|
39
53
|
}>, signal?: AbortSignal, initialLayout?: readonly LayoutElement[]): Promise<ScrollToExecutionResult>;
|