mobile-debug-mcp 0.33.0 → 0.35.0
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/.agents/skills/mcp-builder/SKILL.md +44 -64
- package/.agents/skills/mcp-builder/references/build-flags.md +44 -43
- package/.agents/skills/mcp-builder/references/diagnostics-schema.md +4 -1
- package/.agents/skills/mcp-builder/references/toolchain-details.md +49 -62
- package/.agents/skills/spec-review/references/spec-review-checklist.md +7 -0
- package/.agents/skills/test-authoring/SKILL.md +7 -6
- package/.agents/skills/test-authoring/references/repo-test-layout.md +3 -1
- package/.agents/skills/test-authoring/references/test-authoring-workflow.md +16 -7
- package/.agents/skills/test-authoring/references/test-quality-checklist.md +2 -1
- package/.github/copilot-instructions.md +14 -36
- package/.github/workflows/ci.yml +23 -2
- package/AGENTS.md +12 -3
- package/CONTRIBUTING.md +79 -0
- package/README.md +158 -124
- package/agent-plugin/.claude-plugin/plugin.json +2 -2
- package/agent-plugin/.codex-plugin/plugin.json +1 -1
- package/agent-plugin/.cursor-plugin/plugin.json +2 -2
- package/agent-plugin/.mcp.json +1 -1
- package/agent-plugin/README.md +20 -6
- package/agent-plugin/mcp.json +1 -1
- package/agent-plugin/plugin.json +1 -1
- package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +113 -4
- package/dist/contracts/execution-controls.js +24 -0
- package/dist/contracts/selector.js +8 -0
- package/dist/contracts/target.js +15 -0
- package/dist/contracts/ui.js +1 -0
- package/dist/debugger/android.js +79 -0
- package/dist/debugger/diagnostics.js +54 -0
- package/dist/debugger/guard.js +30 -0
- package/dist/debugger/protocol.js +33 -0
- package/dist/debugger/sessions.js +304 -0
- package/dist/debugger/worker.js +176 -0
- package/dist/interact/adjustment/adjustment-context.js +41 -0
- package/dist/interact/adjustment/adjustment-gestures.js +55 -0
- package/dist/interact/adjustment/adjustment-plan.js +18 -0
- package/dist/interact/adjustment/adjustment-resolution.js +127 -0
- package/dist/interact/adjustment/adjustment-result.js +19 -0
- package/dist/interact/adjustment/adjustment-semantic.js +64 -0
- package/dist/interact/adjustment/adjustment-verification.js +35 -0
- package/dist/interact/adjustment/adjustment.js +52 -0
- package/dist/interact/adjustment/control-geometry.js +87 -0
- package/dist/interact/adjustment/control-values.js +136 -0
- package/dist/interact/adjustment/slider-labels.js +84 -0
- package/dist/interact/assertions/screen-assertion.js +37 -0
- package/dist/interact/assertions/state-assertion-context.js +35 -0
- package/dist/interact/assertions/state-assertion.js +76 -0
- package/dist/interact/assertions/state-comparison.js +141 -0
- package/dist/interact/assertions/visibility-assertion.js +34 -0
- package/dist/interact/conditional-action.js +12 -7
- package/dist/interact/index.js +65 -3074
- package/dist/interact/input-service.js +1 -0
- package/dist/interact/interaction-trace.js +21 -0
- package/dist/interact/model.js +1 -0
- package/dist/interact/observation.js +1 -0
- package/dist/interact/targeting/candidate-ancestor.js +58 -0
- package/dist/interact/targeting/candidate-result.js +23 -0
- package/dist/interact/targeting/candidate-scoring.js +50 -0
- package/dist/interact/targeting/element-helpers.js +216 -0
- package/dist/interact/targeting/find-element.js +65 -0
- package/dist/interact/targeting/nearby-control.js +56 -0
- package/dist/interact/targeting/resolved-elements.js +21 -0
- package/dist/interact/targeting/tap-target.js +17 -0
- package/dist/interact/targeting/target-scope.js +104 -0
- package/dist/interact/waits/legacy-log-wait.js +38 -0
- package/dist/interact/waits/legacy-wait-observations.js +103 -0
- package/dist/interact/waits/legacy-wait-state.js +34 -0
- package/dist/interact/waits/legacy-wait.js +48 -0
- package/dist/interact/waits/screen-wait.js +49 -0
- package/dist/interact/waits/ui-change-helpers.js +157 -0
- package/dist/interact/waits/ui-change-state.js +18 -0
- package/dist/interact/waits/ui-change-wait.js +77 -0
- package/dist/interact/waits/ui-wait-condition.js +56 -0
- package/dist/interact/waits/ui-wait-state.js +38 -0
- package/dist/interact/waits/ui-wait.js +106 -0
- package/dist/manage/android.js +55 -51
- package/dist/manage/index.js +38 -75
- package/dist/manage/ios/build-types.js +1 -0
- package/dist/manage/ios/build.js +210 -0
- package/dist/manage/ios/project-discovery.js +29 -0
- package/dist/manage/ios/scheme.js +18 -0
- package/dist/manage/ios.js +42 -256
- package/dist/observe/android/context.js +1 -0
- package/dist/observe/android/current-screen.js +55 -0
- package/dist/observe/android/fingerprint.js +13 -0
- package/dist/observe/android/log-stream.js +132 -0
- package/dist/observe/android/logs.js +107 -0
- package/dist/observe/android/screenshot.js +70 -0
- package/dist/observe/android/ui-tree.js +107 -0
- package/dist/observe/android.js +16 -520
- package/dist/observe/evidence.js +122 -76
- package/dist/observe/index.js +16 -263
- package/dist/observe/ios/context.js +16 -0
- package/dist/observe/ios/fingerprint.js +12 -0
- package/dist/observe/ios/log-stream.js +105 -0
- package/dist/observe/ios/logs.js +168 -0
- package/dist/observe/ios/screenshot.js +22 -0
- package/dist/observe/ios/ui-elements.js +241 -0
- package/dist/observe/ios/ui-tree.js +137 -0
- package/dist/observe/ios.js +17 -715
- package/dist/observe/logs/model.js +1 -0
- package/dist/{network → observe/network}/index.js +2 -2
- package/dist/observe/readiness-service.js +13 -2
- package/dist/observe/readiness.js +44 -18
- package/dist/observe/screenshot-encoding.js +41 -0
- package/dist/observe/snapshot-collection.js +79 -0
- package/dist/observe/snapshot-logs.js +30 -0
- package/dist/observe/snapshot-metadata.js +43 -31
- package/dist/observe/snapshot-semantic.js +115 -0
- package/dist/observe/system-trace-android.js +135 -0
- package/dist/observe/system-trace.js +294 -0
- package/dist/runtime/action-results.js +270 -0
- package/dist/runtime/clock.js +4 -0
- package/dist/server/common.js +1 -256
- package/dist/server/definitions/assert.js +162 -0
- package/dist/server/definitions/diagnostic.js +118 -0
- package/dist/server/definitions/input.js +274 -0
- package/dist/server/definitions/manage.js +194 -0
- package/dist/server/definitions/observe.js +149 -0
- package/dist/server/definitions/target.js +138 -0
- package/dist/server/definitions/wait.js +105 -0
- package/dist/server/handlers/action-execution.js +163 -0
- package/dist/server/handlers/correlation.js +23 -0
- package/dist/server/handlers/debugger-handlers.js +48 -0
- package/dist/server/handlers/debugger-response.js +9 -0
- package/dist/server/handlers/diagnostic.js +69 -0
- package/dist/server/handlers/handler-types.js +1 -0
- package/dist/server/handlers/input.js +154 -0
- package/dist/server/handlers/interact.js +113 -0
- package/dist/server/handlers/journey-assertions.js +35 -0
- package/dist/server/handlers/journey.js +147 -0
- package/dist/server/handlers/manage.js +119 -0
- package/dist/server/handlers/observe.js +95 -0
- package/dist/server/handlers/payload.js +4 -0
- package/dist/server/handlers/tool-handlers.js +160 -0
- package/dist/server/schemas/condition-schema.js +2 -0
- package/dist/server/schemas/debugger-schema.js +47 -0
- package/dist/server/{journey-schema.js → schemas/journey-schema.js} +8 -22
- package/dist/server/schemas/system-trace-schema.js +17 -0
- package/dist/server/tool-definitions.js +62 -1134
- package/dist/server-core.js +4 -4
- package/dist/server.js +34 -0
- package/dist/system/android-status.js +101 -0
- package/dist/system/android.js +37 -27
- package/dist/system/index.js +13 -438
- package/dist/system/ios-status.js +132 -0
- package/dist/system/ios.js +11 -8
- package/dist/system/model.js +1 -0
- package/dist/system/probes.js +105 -0
- package/dist/system/status-summary.js +43 -0
- package/dist/system/unsupported-status.js +52 -0
- package/dist/utils/android/commands.js +89 -0
- package/dist/utils/android/gradle.js +139 -0
- package/dist/utils/android/logs.js +103 -0
- package/dist/utils/android/ui-tree.js +233 -0
- package/dist/utils/android/utils.js +6 -564
- package/dist/utils/cli/idb/idb-helper.js +4 -6
- package/dist/utils/cli/idb/install-idb.js +13 -9
- package/dist/utils/cli/ios/preflight-ios.js +42 -38
- package/dist/utils/exec.js +2 -0
- package/dist/utils/ios/utils.js +45 -46
- package/dist/utils/java.js +75 -57
- package/dist/utils/operation-budget.js +2 -0
- package/dist/utils/resolve-device.js +6 -2
- package/dist/utils/ui/index.js +26 -26
- package/docs/CHANGELOG.md +48 -0
- package/docs/README.md +38 -0
- package/docs/benchmarks/README.md +18 -0
- package/docs/benchmarks/interaction-latency/README.md +14 -0
- package/docs/benchmarks/interaction-latency/final.md +4 -1
- package/docs/benchmarks/interaction-latency/ios-validation.md +2 -0
- package/docs/benchmarks/ui-tree-performance/README.md +24 -16
- package/docs/benchmarks/ui-tree-performance/comparison.md +11 -10
- package/docs/code-structure.md +118 -0
- package/docs/specs/001-state-verification/spec.md +16 -14
- package/docs/specs/002-richer-element-identity/spec.md +12 -10
- package/docs/specs/003-wait-and-synchronization-reliability/spec.md +12 -10
- package/docs/specs/004-action-verification-routing/spec.md +3 -3
- package/docs/specs/005-unified-action-execution-and-verification-model/spec.md +2 -0
- package/docs/specs/006-runtime-action-instrumentation-and-binding-layer/spec.md +2 -0
- package/docs/specs/007-actionability-resolution-and-executable-target-selection/spec.md +3 -1
- package/docs/specs/008-adjustable-control-support-and-semantic-value-manipulation/spec.md +3 -4
- package/docs/specs/009-semantic-control-modeling-for-custom-and-composite-controls/spec.md +2 -0
- package/docs/specs/010-verification-stabilization-and-temporal-convergence/spec.md +2 -2
- package/docs/specs/011-recovery-and-replanning-for-failed-or-ambiguous-interaction-flows/spec.md +2 -2
- package/docs/specs/011.1-recovery-contract-types-and-runtime-wiring-spec/spec.md +2 -0
- package/docs/specs/012-action-trace-and-xecution-observability/spec.md +2 -3
- package/docs/specs/013-wait-and-synchronization-reliability/spec.md +19 -19
- package/docs/specs/014-actionability-resolution/spec.md +20 -18
- package/docs/specs/015-adjustable-control-support/spec.md +17 -15
- package/docs/specs/016-environment-tools/spec.md +20 -20
- package/docs/specs/018-interaction-latency-and-batched-journeys/spec.md +2 -0
- package/docs/specs/019-ui-tree-observation-performance/spec.md +3 -1
- package/docs/specs/020-conditional-actions/intent.md +5 -3
- package/docs/specs/020-conditional-actions/plan.md +8 -9
- package/docs/specs/020-conditional-actions/spec.md +4 -2
- package/docs/specs/021-app-readiness-signals/intent.md +4 -2
- package/docs/specs/021-app-readiness-signals/plan.md +7 -6
- package/docs/specs/021-app-readiness-signals/spec.md +3 -1
- package/docs/specs/022-correlated-evidence/intent.md +4 -4
- package/docs/specs/022-correlated-evidence/plan.md +10 -11
- package/docs/specs/022-correlated-evidence/spec.md +3 -1
- package/docs/specs/023-android-system-traces/device-evidence.json +38 -0
- package/docs/specs/023-android-system-traces/emulator-recheck.json +38 -0
- package/docs/specs/023-android-system-traces/intent.md +41 -0
- package/docs/specs/023-android-system-traces/plan.md +115 -0
- package/docs/specs/023-android-system-traces/spec.md +122 -0
- package/docs/specs/024-android-heap-dump-capture/intent.md +41 -0
- package/docs/specs/025-android-runtime-debugger/intent.md +41 -0
- package/docs/specs/025-android-runtime-debugger/plan.md +106 -0
- package/docs/specs/025-android-runtime-debugger/spec.md +183 -0
- package/docs/specs/026-code-structure-and-quality-gates/health-plan.md +88 -0
- package/docs/specs/026-code-structure-and-quality-gates/intent.md +47 -0
- package/docs/specs/026-code-structure-and-quality-gates/plan.md +191 -0
- package/docs/specs/026-code-structure-and-quality-gates/spec.md +138 -0
- package/docs/specs/026-code-structure-and-quality-gates/verification-final.json +200 -0
- package/docs/specs/026-code-structure-and-quality-gates/verification-thin-facade.json +154 -0
- package/docs/specs/026-code-structure-and-quality-gates/verification.json +136 -0
- package/docs/specs/027-debugger-failure-diagnostics/intent.md +43 -0
- package/docs/specs/027-debugger-failure-diagnostics/plan.md +86 -0
- package/docs/specs/027-debugger-failure-diagnostics/spec.md +133 -0
- package/docs/specs/028-compact-debugger-responses/intent.md +41 -0
- package/docs/specs/028-compact-debugger-responses/plan.md +112 -0
- package/docs/specs/028-compact-debugger-responses/spec.md +108 -0
- package/docs/specs/029-kotlin-locals-validation/report.md +119 -0
- package/docs/specs/030-android-coroutine-validation/evidence.json +475 -0
- package/docs/specs/030-android-coroutine-validation/intent.md +47 -0
- package/docs/specs/030-android-coroutine-validation/plan.md +147 -0
- package/docs/specs/030-android-coroutine-validation/report.md +240 -0
- package/docs/specs/030-android-coroutine-validation/spec.md +107 -0
- package/docs/tools/TOOLS.md +31 -16
- package/docs/tools/{evidence.md → correlated-evidence.md} +14 -3
- package/docs/tools/debugger.md +180 -0
- package/docs/tools/interact.md +42 -20
- package/docs/tools/manage.md +25 -44
- package/docs/tools/observe.md +64 -25
- package/docs/tools/system-traces.md +134 -0
- package/docs/tools/system.md +12 -5
- package/package.json +11 -8
- package/scripts/architecture-graph.mjs +69 -0
- package/scripts/check-architecture.mjs +47 -0
- package/scripts/code-quality-baseline.json +1 -0
- package/scripts/code-quality.mjs +161 -0
- package/src/contracts/execution-controls.ts +26 -0
- package/src/contracts/selector.ts +17 -0
- package/src/contracts/target.ts +19 -0
- package/src/contracts/ui.ts +94 -0
- package/src/debugger/android.ts +66 -0
- package/src/debugger/diagnostics.ts +43 -0
- package/src/debugger/guard.ts +31 -0
- package/src/debugger/java/DebuggerWorker.java +166 -0
- package/src/debugger/protocol.ts +37 -0
- package/src/debugger/sessions.ts +327 -0
- package/src/debugger/worker.ts +135 -0
- package/src/interact/adjustment/adjustment-context.ts +83 -0
- package/src/interact/adjustment/adjustment-gestures.ts +48 -0
- package/src/interact/adjustment/adjustment-plan.ts +23 -0
- package/src/interact/adjustment/adjustment-resolution.ts +104 -0
- package/src/interact/adjustment/adjustment-result.ts +34 -0
- package/src/interact/adjustment/adjustment-semantic.ts +53 -0
- package/src/interact/adjustment/adjustment-verification.ts +60 -0
- package/src/interact/adjustment/adjustment.ts +49 -0
- package/src/interact/adjustment/control-geometry.ts +120 -0
- package/src/interact/adjustment/control-values.ts +141 -0
- package/src/interact/adjustment/slider-labels.ts +84 -0
- package/src/interact/assertions/screen-assertion.ts +39 -0
- package/src/interact/assertions/state-assertion-context.ts +48 -0
- package/src/interact/assertions/state-assertion.ts +64 -0
- package/src/interact/assertions/state-comparison.ts +168 -0
- package/src/interact/assertions/visibility-assertion.ts +41 -0
- package/src/interact/conditional-action.ts +18 -12
- package/src/interact/index.ts +76 -3491
- package/src/interact/input-service.ts +11 -0
- package/src/interact/interaction-trace.ts +36 -0
- package/src/interact/model.ts +109 -0
- package/src/interact/observation.ts +7 -0
- package/src/interact/targeting/candidate-ancestor.ts +49 -0
- package/src/interact/targeting/candidate-result.ts +24 -0
- package/src/interact/targeting/candidate-scoring.ts +42 -0
- package/src/interact/targeting/element-helpers.ts +243 -0
- package/src/interact/targeting/find-element.ts +47 -0
- package/src/interact/targeting/nearby-control.ts +46 -0
- package/src/interact/targeting/resolved-elements.ts +20 -0
- package/src/interact/targeting/tap-target.ts +22 -0
- package/src/interact/targeting/target-scope.ts +121 -0
- package/src/interact/waits/legacy-log-wait.ts +30 -0
- package/src/interact/waits/legacy-wait-observations.ts +55 -0
- package/src/interact/waits/legacy-wait-state.ts +48 -0
- package/src/interact/waits/legacy-wait.ts +34 -0
- package/src/interact/waits/screen-wait.ts +37 -0
- package/src/interact/waits/ui-change-helpers.ts +184 -0
- package/src/interact/waits/ui-change-state.ts +26 -0
- package/src/interact/waits/ui-change-wait.ts +59 -0
- package/src/interact/waits/ui-wait-condition.ts +52 -0
- package/src/interact/waits/ui-wait-state.ts +55 -0
- package/src/interact/waits/ui-wait.ts +84 -0
- package/src/manage/android.ts +56 -50
- package/src/manage/index.ts +63 -93
- package/src/manage/ios/build-types.ts +14 -0
- package/src/manage/ios/build.ts +230 -0
- package/src/manage/ios/project-discovery.ts +26 -0
- package/src/manage/ios/scheme.ts +16 -0
- package/src/manage/ios.ts +65 -298
- package/src/observe/android/context.ts +1 -0
- package/src/observe/android/current-screen.ts +63 -0
- package/src/observe/android/fingerprint.ts +13 -0
- package/src/observe/android/log-stream.ts +127 -0
- package/src/observe/android/logs.ts +110 -0
- package/src/observe/android/screenshot.ts +79 -0
- package/src/observe/android/ui-tree.ts +113 -0
- package/src/observe/android.ts +17 -531
- package/src/observe/evidence.ts +130 -71
- package/src/observe/index.ts +24 -286
- package/src/observe/ios/context.ts +22 -0
- package/src/observe/ios/fingerprint.ts +12 -0
- package/src/observe/ios/log-stream.ts +103 -0
- package/src/observe/ios/logs.ts +179 -0
- package/src/observe/ios/screenshot.ts +26 -0
- package/src/observe/ios/ui-elements.ts +266 -0
- package/src/observe/ios/ui-tree.ts +143 -0
- package/src/observe/ios.ts +18 -749
- package/src/observe/logs/model.ts +11 -0
- package/src/{network → observe/network}/index.ts +2 -2
- package/src/observe/readiness-service.ts +10 -3
- package/src/observe/readiness.ts +49 -22
- package/src/observe/screenshot-encoding.ts +32 -0
- package/src/observe/snapshot-collection.ts +69 -0
- package/src/observe/snapshot-logs.ts +26 -0
- package/src/observe/snapshot-metadata.ts +51 -31
- package/src/observe/snapshot-semantic.ts +153 -0
- package/src/observe/system-trace-android.ts +133 -0
- package/src/observe/system-trace.ts +227 -0
- package/src/runtime/action-results.ts +376 -0
- package/src/runtime/clock.ts +9 -0
- package/src/server/common.ts +9 -359
- package/src/server/definitions/assert.ts +164 -0
- package/src/server/definitions/diagnostic.ts +120 -0
- package/src/server/definitions/input.ts +276 -0
- package/src/server/definitions/manage.ts +196 -0
- package/src/server/definitions/observe.ts +151 -0
- package/src/server/definitions/target.ts +140 -0
- package/src/server/definitions/wait.ts +107 -0
- package/src/server/handlers/action-execution.ts +164 -0
- package/src/server/handlers/correlation.ts +20 -0
- package/src/server/handlers/debugger-handlers.ts +39 -0
- package/src/server/handlers/debugger-response.ts +7 -0
- package/src/server/handlers/diagnostic.ts +79 -0
- package/src/server/handlers/handler-types.ts +7 -0
- package/src/server/handlers/input.ts +173 -0
- package/src/server/handlers/interact.ts +135 -0
- package/src/server/handlers/journey-assertions.ts +39 -0
- package/src/server/handlers/journey.ts +132 -0
- package/src/server/handlers/manage.ts +141 -0
- package/src/server/handlers/observe.ts +116 -0
- package/src/server/handlers/payload.ts +36 -0
- package/src/server/handlers/tool-handlers.ts +159 -0
- package/src/server/schemas/condition-schema.ts +2 -0
- package/src/server/schemas/debugger-schema.ts +43 -0
- package/src/server/{journey-schema.ts → schemas/journey-schema.ts} +8 -22
- package/src/server/schemas/system-trace-schema.ts +17 -0
- package/src/server/tool-definitions.ts +69 -1140
- package/src/server-core.ts +4 -4
- package/src/server.ts +25 -1
- package/src/system/android-status.ts +124 -0
- package/src/system/android.ts +33 -21
- package/src/system/index.ts +21 -523
- package/src/system/ios-status.ts +147 -0
- package/src/system/ios.ts +14 -10
- package/src/system/model.ts +41 -0
- package/src/system/probes.ts +114 -0
- package/src/system/status-summary.ts +47 -0
- package/src/system/unsupported-status.ts +55 -0
- package/src/types.ts +16 -106
- package/src/utils/android/commands.ts +81 -0
- package/src/utils/android/gradle.ts +132 -0
- package/src/utils/android/logs.ts +103 -0
- package/src/utils/android/ui-tree.ts +239 -0
- package/src/utils/android/utils.ts +16 -556
- package/src/utils/cli/idb/check-idb.ts +4 -4
- package/src/utils/cli/idb/idb-helper.ts +6 -8
- package/src/utils/cli/idb/install-idb.ts +15 -9
- package/src/utils/cli/ios/preflight-ios.ts +39 -31
- package/src/utils/cli/ios/run-ios-ui-tree-tap.ts +4 -4
- package/src/utils/exec.ts +2 -0
- package/src/utils/ios/utils.ts +57 -61
- package/src/utils/java.ts +71 -51
- package/src/utils/operation-budget.ts +2 -0
- package/src/utils/resolve-device.ts +11 -6
- package/src/utils/ui/index.ts +28 -28
- package/test/device/automated/debugger/coroutine-lifecycle.ts +27 -0
- package/test/device/automated/debugger/coroutine-support.ts +140 -0
- package/test/device/automated/debugger/coroutine.android.test.ts +169 -0
- package/test/device/automated/debugger/kotlin.android.test.ts +127 -0
- package/test/device/automated/debugger/runtime.android.test.ts +219 -0
- package/test/device/automated/interact/interaction.android.test.ts +56 -0
- package/test/device/automated/observe/system-trace.smoke.ts +104 -0
- package/test/device/manual/interact/modul8_selector_tap.manual.ts +1 -1
- package/test/fixtures/contracts/tool-definitions.json +3536 -0
- package/test/fixtures/coroutine-app/MainActivity.kt +100 -0
- package/test/fixtures/coroutine-app/README.md +48 -0
- package/test/fixtures/coroutine-app/build.ts +35 -0
- package/test/fixtures/debugger-app/MainActivity.java +67 -0
- package/test/fixtures/debugger-app/MainActivity.kt +32 -0
- package/test/fixtures/debugger-app/NoLocals.java +7 -0
- package/test/fixtures/debugger-app/README.md +65 -0
- package/test/fixtures/debugger-app/build.ts +39 -0
- package/test/fixtures/latency-journey-app/README.md +2 -0
- package/test/fixtures/readiness-app/README.md +2 -0
- package/test/helpers/perfetto.ts +59 -0
- package/test/unit/debugger/coroutine-lifecycle.test.ts +43 -0
- package/test/unit/debugger/diagnostic-sessions.test.ts +67 -0
- package/test/unit/debugger/diagnostics.test.ts +80 -0
- package/test/unit/debugger/guards.test.ts +50 -0
- package/test/unit/debugger/native.test.ts +33 -0
- package/test/unit/debugger/preparation.test.ts +32 -0
- package/test/unit/debugger/protocol.test.ts +15 -0
- package/test/unit/debugger/sessions.test.ts +83 -0
- package/test/unit/interact/adjustment_service.test.ts +42 -0
- package/test/unit/interact/adjustment_verification.test.ts +35 -0
- package/test/unit/interact/clock_dependencies.test.ts +63 -0
- package/test/unit/interact/element_helpers.test.ts +19 -0
- package/test/unit/interact/legacy_wait_service.test.ts +17 -0
- package/test/unit/interact/state_comparison.test.ts +20 -0
- package/test/unit/interact/target_scope.test.ts +35 -0
- package/test/unit/network/get_network_activity.test.ts +1 -1
- package/test/unit/observe/readiness.test.ts +3 -1
- package/test/unit/observe/screenshot-encoding.test.ts +30 -0
- package/test/unit/observe/snapshot-logs.test.ts +14 -0
- package/test/unit/observe/system-trace-android.test.ts +52 -0
- package/test/unit/observe/system-trace.test.ts +166 -0
- package/test/unit/quality/architecture.test.ts +60 -0
- package/test/unit/quality/code-quality.test.ts +58 -0
- package/test/unit/quality/interaction_boundaries.test.ts +24 -0
- package/test/unit/server/conditional_actions.test.ts +1 -1
- package/test/unit/server/conditional_schema.test.ts +2 -2
- package/test/unit/server/debugger-response.test.ts +63 -0
- package/test/unit/server/system-trace.test.ts +64 -0
- package/test/unit/server/tool_definitions_parity.test.ts +8 -0
- package/dist/server/tool-handlers.js +0 -893
- package/docs/ROADMAP.md +0 -646
- package/docs/benchmarks/ui-tree-performance/bridge-probe.json +0 -29
- package/docs/benchmarks/ui-tree-performance/initial-baseline.json +0 -59560
- package/docs/specs/baseline-spec-v0.md +0 -312
- package/docs/specs/mcp-tooling-spec-v1.md +0 -446
- package/src/server/tool-handlers.ts +0 -923
- /package/dist/{server/condition-schema.js → contracts/condition.js} +0 -0
- /package/dist/server/{readiness-schema.js → schemas/readiness-schema.js} +0 -0
- /package/src/{server/condition-schema.ts → contracts/condition.ts} +0 -0
- /package/src/server/{readiness-schema.ts → schemas/readiness-schema.ts} +0 -0
package/src/server/common.ts
CHANGED
|
@@ -1,27 +1,9 @@
|
|
|
1
|
-
import { currentEvidenceActionId } from '../observe/evidence.js'
|
|
2
1
|
import { recordTiming } from '../utils/timing.js'
|
|
3
|
-
import type {
|
|
4
|
-
ActionTrace,
|
|
5
|
-
ActionExecutionResult,
|
|
6
|
-
ActionFailureCode,
|
|
7
|
-
ActionTargetResolved,
|
|
8
|
-
FailureClass,
|
|
9
|
-
RecoveryState,
|
|
10
|
-
TraceResult,
|
|
11
|
-
TraceStage,
|
|
12
|
-
TraceStep
|
|
13
|
-
} from '../types.js'
|
|
14
|
-
import { ToolsObserve } from '../observe/index.js'
|
|
15
|
-
import { isConditionBlocked } from '../interact/conditional-action.js'
|
|
16
|
-
|
|
17
|
-
export const DEFAULT_MAX_RECOVERY_ATTEMPTS = 3
|
|
18
|
-
export const DEFAULT_MAX_RETRY_DEPTH = 3
|
|
19
|
-
|
|
20
2
|
export function wrapResponse<T>(data: T, includeSerializationTiming = true) {
|
|
21
3
|
const start = performance.now()
|
|
22
4
|
let text = JSON.stringify(data, null, 2)
|
|
23
5
|
const elapsed = performance.now() - start
|
|
24
|
-
const value = data as
|
|
6
|
+
const value = data as { timing?: { serialization_ms?: number } } | null
|
|
25
7
|
if (includeSerializationTiming && value && typeof value === 'object' && value.timing && typeof value.timing === 'object') {
|
|
26
8
|
value.timing.serialization_ms = (value.timing.serialization_ms ?? 0) + elapsed
|
|
27
9
|
recordTiming('serialization_ms', elapsed)
|
|
@@ -86,351 +68,17 @@ export function getArrayArg<T>(args: ToolCallArgs, key: string): T[] | undefined
|
|
|
86
68
|
return Array.isArray(value) ? value as T[] : undefined
|
|
87
69
|
}
|
|
88
70
|
|
|
89
|
-
let actionSequence = 0
|
|
90
|
-
|
|
91
|
-
export function nextActionId(actionType: string, timestamp: number) {
|
|
92
|
-
const current = currentEvidenceActionId()
|
|
93
|
-
if (current) return current
|
|
94
|
-
actionSequence += 1
|
|
95
|
-
return `${actionType}_${timestamp}_${actionSequence}`
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export async function captureActionFingerprint(platform?: 'android' | 'ios', deviceId?: string): Promise<string | null> {
|
|
99
|
-
if (isConditionBlocked()) return null
|
|
100
|
-
if (!platform) return null
|
|
101
|
-
try {
|
|
102
|
-
const result = await ToolsObserve.getScreenFingerprintHandler({ platform, deviceId }) as any
|
|
103
|
-
return result?.fingerprint ?? null
|
|
104
|
-
} catch {
|
|
105
|
-
return null
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function createTraceStep({
|
|
110
|
-
stage,
|
|
111
|
-
timestamp,
|
|
112
|
-
result,
|
|
113
|
-
attemptIndex,
|
|
114
|
-
cycleId,
|
|
115
|
-
metadata
|
|
116
|
-
}: {
|
|
117
|
-
stage: TraceStage
|
|
118
|
-
timestamp: number
|
|
119
|
-
result: TraceResult
|
|
120
|
-
attemptIndex: number
|
|
121
|
-
cycleId?: number
|
|
122
|
-
metadata?: Record<string, unknown>
|
|
123
|
-
}): TraceStep {
|
|
124
|
-
return {
|
|
125
|
-
stage,
|
|
126
|
-
timestamp,
|
|
127
|
-
result,
|
|
128
|
-
attempt_index: attemptIndex,
|
|
129
|
-
...(cycleId !== undefined ? { cycle_id: cycleId } : {}),
|
|
130
|
-
...(metadata ? { metadata } : {})
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export function buildActionTrace({
|
|
135
|
-
actionId,
|
|
136
|
-
actionType,
|
|
137
|
-
sourceModule,
|
|
138
|
-
selector,
|
|
139
|
-
resolved,
|
|
140
|
-
success,
|
|
141
|
-
failure,
|
|
142
|
-
details,
|
|
143
|
-
recovery,
|
|
144
|
-
attempts = 1,
|
|
145
|
-
steps
|
|
146
|
-
}: {
|
|
147
|
-
actionId: string
|
|
148
|
-
actionType: string
|
|
149
|
-
sourceModule: 'server' | 'interact'
|
|
150
|
-
selector: Record<string, unknown> | null
|
|
151
|
-
resolved?: Partial<ActionTargetResolved> | null
|
|
152
|
-
success: boolean
|
|
153
|
-
failure?: { failureCode: ActionFailureCode; retryable: boolean }
|
|
154
|
-
details?: Record<string, unknown>
|
|
155
|
-
recovery?: RecoveryState
|
|
156
|
-
attempts?: number
|
|
157
|
-
steps?: TraceStep[]
|
|
158
|
-
}): ActionTrace {
|
|
159
|
-
if (steps && steps.length > 0) {
|
|
160
|
-
return {
|
|
161
|
-
action_id: actionId,
|
|
162
|
-
steps,
|
|
163
|
-
final_outcome: success ? 'success' : 'failure',
|
|
164
|
-
attempts: Math.max(1, Math.floor(attempts || steps.length))
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const start = Date.now()
|
|
169
|
-
const builtSteps: TraceStep[] = []
|
|
170
|
-
let attemptIndex = 0
|
|
171
|
-
const totalAttempts = Math.max(1, Math.floor(attempts || 1))
|
|
172
|
-
|
|
173
|
-
if (selector || resolved) {
|
|
174
|
-
const stageResult: TraceResult = resolved ? 'success' : 'failure'
|
|
175
|
-
builtSteps.push(createTraceStep({
|
|
176
|
-
stage: 'resolve',
|
|
177
|
-
timestamp: start,
|
|
178
|
-
result: stageResult,
|
|
179
|
-
attemptIndex: attemptIndex++,
|
|
180
|
-
metadata: {
|
|
181
|
-
action_type: actionType,
|
|
182
|
-
source_module: sourceModule,
|
|
183
|
-
selector: selector ?? null,
|
|
184
|
-
resolved: resolved ? normalizeResolvedTarget(resolved) : null
|
|
185
|
-
}
|
|
186
|
-
}))
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
builtSteps.push(createTraceStep({
|
|
190
|
-
stage: 'execute',
|
|
191
|
-
timestamp: Date.now(),
|
|
192
|
-
result: success ? 'success' : 'failure',
|
|
193
|
-
attemptIndex: attemptIndex++,
|
|
194
|
-
metadata: {
|
|
195
|
-
action_type: actionType,
|
|
196
|
-
source_module: sourceModule,
|
|
197
|
-
...(failure ? { failure_code: failure.failureCode, retryable: failure.retryable } : {})
|
|
198
|
-
}
|
|
199
|
-
}))
|
|
200
|
-
|
|
201
|
-
const hasStabilizeDetails = Boolean(details && (
|
|
202
|
-
Object.prototype.hasOwnProperty.call(details, 'stabilization_attempts') ||
|
|
203
|
-
Object.prototype.hasOwnProperty.call(details, 'stable_observation_count') ||
|
|
204
|
-
Object.prototype.hasOwnProperty.call(details, 'snapshot_freshness_ms')
|
|
205
|
-
))
|
|
206
|
-
const hasVerifyDetails = Boolean(details && (
|
|
207
|
-
Object.prototype.hasOwnProperty.call(details, 'within_tolerance') ||
|
|
208
|
-
Object.prototype.hasOwnProperty.call(details, 'converged') ||
|
|
209
|
-
Object.prototype.hasOwnProperty.call(details, 'observed_state')
|
|
210
|
-
))
|
|
211
|
-
|
|
212
|
-
if (hasStabilizeDetails) {
|
|
213
|
-
builtSteps.push(createTraceStep({
|
|
214
|
-
stage: 'stabilize',
|
|
215
|
-
timestamp: Date.now(),
|
|
216
|
-
result: success ? 'success' : 'failure',
|
|
217
|
-
attemptIndex: attemptIndex++,
|
|
218
|
-
metadata: {
|
|
219
|
-
stabilization_attempts: (details?.stabilization_attempts as number | undefined) ?? null,
|
|
220
|
-
stable_observation_count: (details?.stable_observation_count as number | undefined) ?? null,
|
|
221
|
-
snapshot_freshness_ms: (details?.snapshot_freshness_ms as number | undefined) ?? null
|
|
222
|
-
}
|
|
223
|
-
}))
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (hasVerifyDetails) {
|
|
227
|
-
builtSteps.push(createTraceStep({
|
|
228
|
-
stage: 'verify',
|
|
229
|
-
timestamp: Date.now(),
|
|
230
|
-
result: success ? 'success' : 'failure',
|
|
231
|
-
attemptIndex: attemptIndex++,
|
|
232
|
-
metadata: {
|
|
233
|
-
within_tolerance: details?.within_tolerance ?? null,
|
|
234
|
-
converged: details?.converged ?? null,
|
|
235
|
-
actual_state: details?.actual_state ?? null,
|
|
236
|
-
reason: details?.reason ?? null
|
|
237
|
-
}
|
|
238
|
-
}))
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (failure) {
|
|
242
|
-
builtSteps.push(createTraceStep({
|
|
243
|
-
stage: 'recover',
|
|
244
|
-
timestamp: Date.now(),
|
|
245
|
-
result: failure.retryable ? 'retry' : 'failure',
|
|
246
|
-
attemptIndex: attemptIndex++,
|
|
247
|
-
metadata: {
|
|
248
|
-
failure_class: recovery?.failure_class ?? mapFailureCodeToFailureClass(failure.failureCode),
|
|
249
|
-
runtime_code: failure.failureCode,
|
|
250
|
-
retry_allowed: failure.retryable,
|
|
251
|
-
recovery_attempts: recovery?.recovery_attempts ?? 0,
|
|
252
|
-
retry_depth: recovery?.retry_depth ?? 0
|
|
253
|
-
}
|
|
254
|
-
}))
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (!builtSteps.length) {
|
|
258
|
-
builtSteps.push(createTraceStep({
|
|
259
|
-
stage: 'execute',
|
|
260
|
-
timestamp: start,
|
|
261
|
-
result: success ? 'success' : 'failure',
|
|
262
|
-
attemptIndex: 0,
|
|
263
|
-
metadata: { action_type: actionType, source_module: sourceModule }
|
|
264
|
-
}))
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return {
|
|
268
|
-
action_id: actionId,
|
|
269
|
-
steps: builtSteps,
|
|
270
|
-
final_outcome: success ? 'success' : 'failure',
|
|
271
|
-
attempts: totalAttempts
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
export function normalizeResolvedTarget(value: Partial<ActionTargetResolved> | null = null): ActionTargetResolved | null {
|
|
276
|
-
if (!value) return null
|
|
277
|
-
return {
|
|
278
|
-
elementId: value.elementId ?? null,
|
|
279
|
-
text: value.text ?? null,
|
|
280
|
-
resource_id: value.resource_id ?? null,
|
|
281
|
-
accessibility_id: value.accessibility_id ?? null,
|
|
282
|
-
class: value.class ?? null,
|
|
283
|
-
bounds: value.bounds ?? null,
|
|
284
|
-
index: value.index ?? null,
|
|
285
|
-
state: value.state ?? null
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export function inferGenericFailure(message: string | undefined): { failureCode: ActionFailureCode; retryable: boolean } {
|
|
290
|
-
if (message && /timeout/i.test(message)) return { failureCode: 'TIMEOUT', retryable: true }
|
|
291
|
-
if (message && /semantic mismatch/i.test(message)) return { failureCode: 'SEMANTIC_MISMATCH', retryable: false }
|
|
292
|
-
return { failureCode: 'UNKNOWN', retryable: false }
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export function inferScrollFailure(message: string | undefined): { failureCode: ActionFailureCode; retryable: boolean } {
|
|
296
|
-
if (message && /unchanged|no change|end of list/i.test(message)) return { failureCode: 'NAVIGATION_NO_CHANGE', retryable: true }
|
|
297
|
-
if (message && /timeout/i.test(message)) return { failureCode: 'TIMEOUT', retryable: true }
|
|
298
|
-
return { failureCode: 'UNKNOWN', retryable: false }
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const ACTION_LIFECYCLE_STATE_BY_OUTCOME = {
|
|
302
|
-
success: 'pending_verification',
|
|
303
|
-
failure: 'failed'
|
|
304
|
-
} as const
|
|
305
|
-
|
|
306
|
-
export function determineActionLifecycleState({
|
|
307
|
-
success,
|
|
308
|
-
failure
|
|
309
|
-
}: {
|
|
310
|
-
success: boolean
|
|
311
|
-
failure?: { failureCode: ActionFailureCode; retryable: boolean }
|
|
312
|
-
}): NonNullable<ActionExecutionResult['lifecycle_state']> {
|
|
313
|
-
if (failure) return ACTION_LIFECYCLE_STATE_BY_OUTCOME.failure
|
|
314
|
-
if (success) return ACTION_LIFECYCLE_STATE_BY_OUTCOME.success
|
|
315
|
-
return ACTION_LIFECYCLE_STATE_BY_OUTCOME.success
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function mapFailureCodeToFailureClass(code: ActionFailureCode): FailureClass {
|
|
319
|
-
switch (code) {
|
|
320
|
-
case 'ELEMENT_NOT_FOUND':
|
|
321
|
-
case 'AMBIGUOUS_TARGET':
|
|
322
|
-
case 'STALE_REFERENCE':
|
|
323
|
-
return 'TargetResolutionFailure'
|
|
324
|
-
case 'ELEMENT_NOT_INTERACTABLE':
|
|
325
|
-
case 'PRECONDITION_FAILED':
|
|
326
|
-
case 'PRECONDITION_UNAVAILABLE':
|
|
327
|
-
case 'ACTION_TIMEOUT':
|
|
328
|
-
return 'ExecutionFailure'
|
|
329
|
-
case 'TIMEOUT':
|
|
330
|
-
case 'ACTION_REJECTED':
|
|
331
|
-
case 'NAVIGATION_NO_CHANGE':
|
|
332
|
-
case 'UNKNOWN':
|
|
333
|
-
return 'ExecutionFailure'
|
|
334
|
-
case 'VERIFICATION_FAILED':
|
|
335
|
-
case 'EXPECT_STATE_MISMATCH':
|
|
336
|
-
case 'ASSERTION_TIMEOUT':
|
|
337
|
-
return 'VerificationFailure'
|
|
338
|
-
case 'CONTROL_CONVERGENCE_FAILED':
|
|
339
|
-
return 'ControlConvergenceFailure'
|
|
340
|
-
case 'SEMANTIC_MISMATCH':
|
|
341
|
-
return 'SemanticMismatchFailure'
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function buildRecoveryState(failureCode: ActionFailureCode, retryable: boolean): RecoveryState {
|
|
346
|
-
return {
|
|
347
|
-
failure_class: mapFailureCodeToFailureClass(failureCode),
|
|
348
|
-
runtime_code: failureCode,
|
|
349
|
-
recovery_attempts: 0,
|
|
350
|
-
max_recovery_attempts: DEFAULT_MAX_RECOVERY_ATTEMPTS,
|
|
351
|
-
retry_depth: 0,
|
|
352
|
-
max_retry_depth: DEFAULT_MAX_RETRY_DEPTH,
|
|
353
|
-
is_terminal: false,
|
|
354
|
-
retry_allowed: retryable
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
export function buildActionExecutionResult({
|
|
359
|
-
actionType,
|
|
360
|
-
device,
|
|
361
|
-
selector,
|
|
362
|
-
resolved,
|
|
363
|
-
success,
|
|
364
|
-
uiFingerprintBefore,
|
|
365
|
-
uiFingerprintAfter,
|
|
366
|
-
failure,
|
|
367
|
-
details,
|
|
368
|
-
sourceModule,
|
|
369
|
-
traceSteps
|
|
370
|
-
}: {
|
|
371
|
-
actionType: string
|
|
372
|
-
device?: ActionExecutionResult['device']
|
|
373
|
-
selector: Record<string, unknown> | null
|
|
374
|
-
resolved?: Partial<ActionTargetResolved> | null
|
|
375
|
-
success: boolean
|
|
376
|
-
uiFingerprintBefore: string | null
|
|
377
|
-
uiFingerprintAfter: string | null
|
|
378
|
-
failure?: { failureCode: ActionFailureCode; retryable: boolean }
|
|
379
|
-
details?: Record<string, unknown>
|
|
380
|
-
sourceModule: 'server' | 'interact'
|
|
381
|
-
traceSteps?: TraceStep[]
|
|
382
|
-
}): ActionExecutionResult {
|
|
383
|
-
const timestampMs = Date.now()
|
|
384
|
-
const timestamp = new Date(timestampMs).toISOString()
|
|
385
|
-
const actionId = nextActionId(actionType, timestampMs)
|
|
386
|
-
const recoveryState = failure ? buildRecoveryState(failure.failureCode, failure.retryable) : undefined
|
|
387
|
-
const attempts = typeof details?.attempts === 'number' && Number.isFinite(details.attempts)
|
|
388
|
-
? Math.max(1, Math.floor(details.attempts))
|
|
389
|
-
: 1
|
|
390
|
-
return {
|
|
391
|
-
action_id: actionId,
|
|
392
|
-
timestamp,
|
|
393
|
-
action_type: actionType,
|
|
394
|
-
lifecycle_state: determineActionLifecycleState({ success, failure }),
|
|
395
|
-
source_module: sourceModule,
|
|
396
|
-
...(device ? { device } : {}),
|
|
397
|
-
target: {
|
|
398
|
-
selector,
|
|
399
|
-
resolved: normalizeResolvedTarget(resolved)
|
|
400
|
-
},
|
|
401
|
-
success,
|
|
402
|
-
...(failure ? { failure_code: failure.failureCode, retryable: failure.retryable } : {}),
|
|
403
|
-
...(recoveryState ? { recovery: recoveryState } : {}),
|
|
404
|
-
trace: buildActionTrace({
|
|
405
|
-
actionId,
|
|
406
|
-
actionType,
|
|
407
|
-
sourceModule,
|
|
408
|
-
selector,
|
|
409
|
-
resolved,
|
|
410
|
-
success,
|
|
411
|
-
failure,
|
|
412
|
-
details,
|
|
413
|
-
recovery: recoveryState,
|
|
414
|
-
attempts,
|
|
415
|
-
steps: traceSteps
|
|
416
|
-
}),
|
|
417
|
-
ui_fingerprint_before: uiFingerprintBefore,
|
|
418
|
-
ui_fingerprint_after: uiFingerprintAfter,
|
|
419
|
-
...(details ? { details } : {})
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
71
|
export function wrapToolError(name: string, error: unknown) {
|
|
424
72
|
const message = error instanceof Error
|
|
425
73
|
? error.message
|
|
426
74
|
: typeof error === 'object' && error !== null
|
|
427
75
|
? (() => {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
76
|
+
try {
|
|
77
|
+
return JSON.stringify(error, null, 2)
|
|
78
|
+
} catch {
|
|
79
|
+
return '[unserializable error object]'
|
|
80
|
+
}
|
|
81
|
+
})()
|
|
434
82
|
: String(error)
|
|
435
83
|
return {
|
|
436
84
|
content: [{
|
|
@@ -444,3 +92,5 @@ export function wrapToolError(name: string, error: unknown) {
|
|
|
444
92
|
}]
|
|
445
93
|
}
|
|
446
94
|
}
|
|
95
|
+
|
|
96
|
+
export { DEFAULT_MAX_RECOVERY_ATTEMPTS, DEFAULT_MAX_RETRY_DEPTH, nextActionId, captureActionFingerprint, createTraceStep, buildActionTrace, normalizeResolvedTarget, inferGenericFailure, inferScrollFailure, determineActionLifecycleState, buildActionExecutionResult } from '../runtime/action-results.js'
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js'
|
|
2
|
+
|
|
3
|
+
export const assertTools: Tool[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'expect_screen',
|
|
6
|
+
description: `Purpose:
|
|
7
|
+
Deterministically verify that the intended navigation outcome of an action has occurred.
|
|
8
|
+
|
|
9
|
+
Inputs:
|
|
10
|
+
- fingerprint: preferred exact-match screen fingerprint
|
|
11
|
+
- screen: exact semantic screen identifier when a fingerprint is not available
|
|
12
|
+
|
|
13
|
+
Output Structure:
|
|
14
|
+
- success: true when the expected screen matches the observed screen
|
|
15
|
+
- observed_screen: current fingerprint and screen identifier
|
|
16
|
+
- expected_screen: the expected fingerprint and/or screen identifier
|
|
17
|
+
- confidence: 1 for an exact match, otherwise 0
|
|
18
|
+
|
|
19
|
+
Recommended Usage:
|
|
20
|
+
1. Define the expected screen before executing the action
|
|
21
|
+
2. Resolve the target element or screen state
|
|
22
|
+
3. Call an action tool such as tap_element
|
|
23
|
+
4. If needed, wait for transition using wait_for_*
|
|
24
|
+
5. Call expect_screen as the final verification step
|
|
25
|
+
6. If success=false, treat the outcome as unverified and follow the action tool retry guidance
|
|
26
|
+
|
|
27
|
+
Verification Guidance:
|
|
28
|
+
- Primary and authoritative verification tool for navigation outcomes
|
|
29
|
+
- Prefer fingerprint whenever you have one
|
|
30
|
+
- Use screen only as a fallback exact match against known identifiers
|
|
31
|
+
- Works best when the expected screen identifier is known ahead of time
|
|
32
|
+
- If the expected screen is not already known, capture or define it before executing the action
|
|
33
|
+
|
|
34
|
+
Constraints:
|
|
35
|
+
- Returns structured binary success/failure only
|
|
36
|
+
- Must not rely on natural-language interpretation or reasoning
|
|
37
|
+
|
|
38
|
+
Failure Handling:
|
|
39
|
+
- success=false means the expected screen was not reached; retry or recover using the action tool's failure strategy`,
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override (android|ios)' },
|
|
44
|
+
fingerprint: { type: 'string', description: 'Expected screen fingerprint. Preferred verification mechanism.' },
|
|
45
|
+
screen: { type: 'string', description: 'Expected exact screen identifier when no fingerprint is available.' },
|
|
46
|
+
deviceId: { type: 'string', description: 'Optional device id/udid to target' }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'expect_element_visible',
|
|
52
|
+
description: `Purpose:
|
|
53
|
+
Deterministically verify that the intended UI outcome of an action has occurred by confirming a target element is visible.
|
|
54
|
+
|
|
55
|
+
Inputs:
|
|
56
|
+
- selector: required selector used to resolve the target element
|
|
57
|
+
- element_id: optional previously resolved element identifier used only as context
|
|
58
|
+
|
|
59
|
+
Output Structure:
|
|
60
|
+
- success: true when the element is visible
|
|
61
|
+
- selector: selector used for verification
|
|
62
|
+
- element_id: resolved element identifier when available
|
|
63
|
+
- element: minimal resolved element info when visible
|
|
64
|
+
- failure_code: TIMEOUT or UNKNOWN when verification fails
|
|
65
|
+
- retryable: true when failure_code=TIMEOUT
|
|
66
|
+
|
|
67
|
+
Recommended Usage:
|
|
68
|
+
1. Define the expected element state before executing the action
|
|
69
|
+
2. Resolve the target element or triggering control
|
|
70
|
+
3. Call an action tool such as tap_element
|
|
71
|
+
4. If needed, wait for UI availability using wait_for_*
|
|
72
|
+
5. Call expect_element_visible as the final verification step
|
|
73
|
+
6. If success=false, follow the action tool retry guidance
|
|
74
|
+
|
|
75
|
+
Verification Guidance:
|
|
76
|
+
- Primary and authoritative verification tool for expected element appearance or visibility
|
|
77
|
+
- Use this when the screen should stay the same but the UI should reveal or update a specific element
|
|
78
|
+
- selector is the primary input; element_id is an optional optimization only
|
|
79
|
+
- The tool resolves the selector internally when needed
|
|
80
|
+
|
|
81
|
+
Constraints:
|
|
82
|
+
- Returns structured binary success/failure only
|
|
83
|
+
- Must not rely on natural-language interpretation or reasoning
|
|
84
|
+
|
|
85
|
+
Failure Handling:
|
|
86
|
+
- TIMEOUT → retry verification once or retry the action after re-resolving
|
|
87
|
+
- UNKNOWN → capture a snapshot and stop`,
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
selector: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
text: { type: 'string' },
|
|
95
|
+
resource_id: { type: 'string' },
|
|
96
|
+
accessibility_id: { type: 'string' },
|
|
97
|
+
contains: { type: 'boolean', default: false }
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
element_id: { type: 'string', description: 'Optional previously resolved element identifier.' },
|
|
101
|
+
timeout_ms: { type: 'number', default: 5000 },
|
|
102
|
+
poll_interval_ms: { type: 'number', default: 300 },
|
|
103
|
+
platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override' },
|
|
104
|
+
deviceId: { type: 'string', description: 'Optional device serial/udid' }
|
|
105
|
+
},
|
|
106
|
+
required: ['selector']
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'expect_state',
|
|
111
|
+
description: `Purpose:
|
|
112
|
+
Verify a readable UI state property on the currently visible element.
|
|
113
|
+
|
|
114
|
+
Inputs:
|
|
115
|
+
- selector or element_id
|
|
116
|
+
- property
|
|
117
|
+
- expected
|
|
118
|
+
- platform/deviceId (optional)
|
|
119
|
+
|
|
120
|
+
Supported properties:
|
|
121
|
+
- checked, selected, focused, expanded, enabled, text_value, value, raw_value
|
|
122
|
+
|
|
123
|
+
Verification Guidance:
|
|
124
|
+
- Use this when the UI element is visible but its state must also be confirmed
|
|
125
|
+
- Prefer the canonical property names above
|
|
126
|
+
- The tool compares the normalized readable state and returns the observed value when available
|
|
127
|
+
|
|
128
|
+
Constraints:
|
|
129
|
+
- Returns structured success/failure only
|
|
130
|
+
- Does not infer a state when the property is unavailable
|
|
131
|
+
|
|
132
|
+
Failure Handling:
|
|
133
|
+
- ELEMENT_NOT_FOUND → re-resolve the element or wait for UI stabilization
|
|
134
|
+
- UNKNOWN → capture a snapshot and stop`,
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
properties: {
|
|
138
|
+
selector: {
|
|
139
|
+
type: 'object',
|
|
140
|
+
properties: {
|
|
141
|
+
text: { type: 'string' },
|
|
142
|
+
resource_id: { type: 'string' },
|
|
143
|
+
accessibility_id: { type: 'string' },
|
|
144
|
+
contains: { type: 'boolean', default: false }
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
element_id: { type: 'string', description: 'Optional previously resolved element identifier.' },
|
|
148
|
+
property: { type: 'string', description: 'Readable state property to verify.' },
|
|
149
|
+
expected: {
|
|
150
|
+
description: 'Expected normalized state value.',
|
|
151
|
+
oneOf: [
|
|
152
|
+
{ type: 'boolean' },
|
|
153
|
+
{ type: 'number' },
|
|
154
|
+
{ type: 'string' },
|
|
155
|
+
{ type: 'object' }
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override' },
|
|
159
|
+
deviceId: { type: 'string', description: 'Optional device serial/udid' }
|
|
160
|
+
},
|
|
161
|
+
required: ['property', 'expected']
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
]
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js'
|
|
2
|
+
|
|
3
|
+
export const diagnosticTools: Tool[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'get_system_status',
|
|
6
|
+
description: 'Quick healthcheck of local mobile debugging environment (adb, devices, logs, env, iOS).',
|
|
7
|
+
inputSchema: { type: 'object', properties: {} }
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
name: 'classify_action_outcome',
|
|
11
|
+
description: `Classify the outcome of the most recent action into exactly one of: success, no_op, backend_failure, ui_failure, unknown.
|
|
12
|
+
|
|
13
|
+
Use the runtime action result's \`action_type\` as \`actionType\` so the classifier can distinguish local-state actions from side-effect actions.
|
|
14
|
+
Use this when the intended outcome is not already fully verified by the UI signal alone.
|
|
15
|
+
For backend/API activity, compare get_screen_fingerprint before and after the action and call get_network_activity immediately after the action if the outcome is still ambiguous.
|
|
16
|
+
|
|
17
|
+
HOW TO GATHER INPUTS before calling:
|
|
18
|
+
1. Call wait_for_screen_change or compare get_screen_fingerprint before/after — set uiChanged accordingly.
|
|
19
|
+
2. If you checked for a specific element with wait_for_ui, set expectedElementVisible.
|
|
20
|
+
3. Pass actionType from the action response when available.
|
|
21
|
+
4. Only provide networkRequests if you already collected them or want to classify a side-effect action with backend evidence.
|
|
22
|
+
|
|
23
|
+
RULES (applied in order — stop at first match):
|
|
24
|
+
1. If uiChanged=true OR expectedElementVisible=true → outcome=success
|
|
25
|
+
2. If actionType is missing → outcome=unknown
|
|
26
|
+
3. If any request has status=failure or retryable → outcome=backend_failure
|
|
27
|
+
4. If actionType maps to a local-state action → outcome=no_op; prefer state-based verification and avoid default network fallback
|
|
28
|
+
5. If actionType maps to a side-effect action and no networkRequests were supplied → outcome=unknown
|
|
29
|
+
6. If no requests returned → outcome=no_op
|
|
30
|
+
7. If all requests succeeded → outcome=ui_failure
|
|
31
|
+
8. Otherwise → outcome=unknown
|
|
32
|
+
|
|
33
|
+
BEHAVIOUR after outcome:
|
|
34
|
+
- success → continue
|
|
35
|
+
- no_op → retry with richer state verification or re-resolve the element
|
|
36
|
+
- backend_failure → stop and report the failing endpoint
|
|
37
|
+
- ui_failure → stop and report failure
|
|
38
|
+
- unknown → take one recovery step (e.g. capture_debug_snapshot), then stop`,
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
uiChanged: {
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
description: 'true if the screen fingerprint or activity changed after the action. Use wait_for_screen_change or compare get_screen_fingerprint before and after.'
|
|
45
|
+
},
|
|
46
|
+
expectedElementVisible: {
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
description: 'true if the element you expected to appear is now visible (from wait_for_ui). Omit if you did not check for a specific element.'
|
|
49
|
+
},
|
|
50
|
+
actionType: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
description: 'The runtime action_type from the action response (for example tap, tap_element, swipe, type_text, press_back, start_app).'
|
|
53
|
+
},
|
|
54
|
+
networkRequests: {
|
|
55
|
+
type: 'array',
|
|
56
|
+
description: 'Optional network evidence collected after the action. Use it when the expected outcome is backend/API activity or when the UI signal is ambiguous.',
|
|
57
|
+
items: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
endpoint: { type: 'string', description: 'Request endpoint or full URL' },
|
|
61
|
+
status: { type: 'string', enum: ['success', 'failure', 'retryable'], description: 'Outcome of the request' }
|
|
62
|
+
},
|
|
63
|
+
required: ['endpoint', 'status']
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
hasLogErrors: {
|
|
67
|
+
type: 'boolean',
|
|
68
|
+
description: 'true if structured log errors were observed (e.g. from read_log_stream). Optional — include if you have already read logs.'
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
required: ['uiChanged']
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'run_journey',
|
|
76
|
+
description: 'Run an ordered, bounded sequence of start_app, tap, wait, and deterministic assertion steps against one device. Stops after the first failed step.',
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
platform: { type: 'string', enum: ['android', 'ios'] },
|
|
81
|
+
deviceId: { type: 'string' },
|
|
82
|
+
responseMode: { type: 'string', enum: ['compact', 'debug'], default: 'compact' },
|
|
83
|
+
steps: { type: 'array', minItems: 1, maxItems: 50, items: { type: 'object', properties: { id: { type: 'string' }, type: { type: 'string', enum: ['start_app', 'tap', 'wait', 'assert'] } }, required: ['id', 'type'] } }
|
|
84
|
+
},
|
|
85
|
+
required: ['platform', 'steps']
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'get_network_activity',
|
|
90
|
+
description: `Returns structured network events captured from platform logs since the last action.
|
|
91
|
+
|
|
92
|
+
Call this immediately after an action when you want backend evidence for a side-effect flow, only if the result is still ambiguous.
|
|
93
|
+
Do not call more than once per action.
|
|
94
|
+
|
|
95
|
+
Events are filtered to significant (non-background) requests only.
|
|
96
|
+
Each event includes endpoint, method, statusCode, networkError, status, and durationMs.
|
|
97
|
+
|
|
98
|
+
status values:
|
|
99
|
+
- success: HTTP 2xx or request detected with no error signal
|
|
100
|
+
- failure: HTTP 4xx
|
|
101
|
+
- retryable: HTTP 5xx, network error (timeout, dns_error, tls_error, etc.)
|
|
102
|
+
|
|
103
|
+
Returns { requests: [], count: 0 } when no credible network signals are found.`,
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
platform: {
|
|
108
|
+
type: 'string',
|
|
109
|
+
enum: ['android', 'ios'],
|
|
110
|
+
description: 'Platform to read network logs from'
|
|
111
|
+
},
|
|
112
|
+
deviceId: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: 'Device Serial (Android) or UDID (iOS). Defaults to connected/booted device.'
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
required: ['platform']
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
]
|