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
|
@@ -2,10 +2,12 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { realpath } from "node:fs/promises";
|
|
3
3
|
import { basename, extname } from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { UiBackendDescriptorSchema } from "../../domain/ui-backend.js";
|
|
5
6
|
import { normalizeActivity } from "../../domain/activity.js";
|
|
6
7
|
import { GenerationMetaSchema, GenerationReportSchema, GenerationSessionSchema } from "../../domain/generation.js";
|
|
7
|
-
import { JourneySchema } from "../../domain/journey.js";
|
|
8
|
-
import {
|
|
8
|
+
import { DEFAULT_DEVICE_ROLE, JourneySchema } from "../../domain/journey.js";
|
|
9
|
+
import { ResolvedProjectContextSchema } from "../../domain/project-context.js";
|
|
10
|
+
import { JourneyOutputPathSchema } from "../../domain/journey-composition.js";
|
|
9
11
|
import { hashJourney, TapHoundReportSchema } from "../../domain/report.js";
|
|
10
12
|
import { TapHoundConfigSchema } from "../../domain/config.js";
|
|
11
13
|
import { GenerationSessionStoreError } from "../../ports/generation-session-store.js";
|
|
@@ -35,7 +37,8 @@ const VerificationReceiptSchema = z.strictObject({
|
|
|
35
37
|
projectHash: Sha256Schema,
|
|
36
38
|
configHash: Sha256Schema,
|
|
37
39
|
contextHash: Sha256Schema,
|
|
38
|
-
snapshotHash: Sha256Schema.nullable()
|
|
40
|
+
snapshotHash: Sha256Schema.nullable(),
|
|
41
|
+
uiBackend: UiBackendDescriptorSchema
|
|
39
42
|
}),
|
|
40
43
|
tools: z.record(z.string(), z.string()),
|
|
41
44
|
report: z.strictObject({
|
|
@@ -44,18 +47,19 @@ const VerificationReceiptSchema = z.strictObject({
|
|
|
44
47
|
runId: z.string().min(1)
|
|
45
48
|
})
|
|
46
49
|
});
|
|
47
|
-
export const GenerationOutputPathSchema =
|
|
48
|
-
&& !path.startsWith(".taphound/generations/")), "Generation output cannot overlap the authoritative bundle");
|
|
50
|
+
export const GenerationOutputPathSchema = JourneyOutputPathSchema;
|
|
49
51
|
export class GenerationFinalizationError extends Error {
|
|
50
52
|
code;
|
|
51
53
|
stage;
|
|
52
54
|
recoverable;
|
|
55
|
+
details;
|
|
53
56
|
name = "GenerationFinalizationError";
|
|
54
|
-
constructor(code, stage, recoverable, message, options) {
|
|
57
|
+
constructor(code, stage, recoverable, message, options, details) {
|
|
55
58
|
super(message, options);
|
|
56
59
|
this.code = code;
|
|
57
60
|
this.stage = stage;
|
|
58
61
|
this.recoverable = recoverable;
|
|
62
|
+
this.details = details;
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
function serializeJson(value) {
|
|
@@ -64,12 +68,6 @@ function serializeJson(value) {
|
|
|
64
68
|
function sha256(value) {
|
|
65
69
|
return createHash("sha256").update(value).digest("hex");
|
|
66
70
|
}
|
|
67
|
-
function commandFailed(result) {
|
|
68
|
-
return result.exitCode !== 0
|
|
69
|
-
|| result.timedOut
|
|
70
|
-
|| result.cancelled
|
|
71
|
-
|| result.spawnError !== undefined;
|
|
72
|
-
}
|
|
73
71
|
function derivedJourneyName(outputPath) {
|
|
74
72
|
const file = basename(outputPath);
|
|
75
73
|
const extension = extname(file);
|
|
@@ -89,13 +87,23 @@ function sameJson(left, right) {
|
|
|
89
87
|
function locatorMatchIsEligible(matchedBy, step) {
|
|
90
88
|
if (step.action !== "click"
|
|
91
89
|
&& step.action !== "longClick"
|
|
92
|
-
&& step.action !== "swipe"
|
|
90
|
+
&& step.action !== "swipe"
|
|
91
|
+
&& step.action !== "bridge") {
|
|
93
92
|
return matchedBy === undefined;
|
|
94
93
|
}
|
|
95
|
-
|
|
94
|
+
if (matchedBy === undefined) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const locator = step.action === "bridge"
|
|
98
|
+
? step.triggerLocator
|
|
99
|
+
: step.locator;
|
|
100
|
+
if (locator === undefined || matchedBy === "anchor") {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return locator[matchedBy] !== undefined;
|
|
96
104
|
}
|
|
97
|
-
function failure(code, stage, message, recoverable = false, cause) {
|
|
98
|
-
return new GenerationFinalizationError(code, stage, recoverable, message, cause === undefined ? undefined : { cause });
|
|
105
|
+
function failure(code, stage, message, recoverable = false, cause, details) {
|
|
106
|
+
return new GenerationFinalizationError(code, stage, recoverable, message, cause === undefined ? undefined : { cause }, details);
|
|
99
107
|
}
|
|
100
108
|
export class GenerationFinalizer {
|
|
101
109
|
dependencies;
|
|
@@ -104,7 +112,7 @@ export class GenerationFinalizer {
|
|
|
104
112
|
}
|
|
105
113
|
finalize = async (input) => {
|
|
106
114
|
const config = TapHoundConfigSchema.parse(input.config);
|
|
107
|
-
const context =
|
|
115
|
+
const context = ResolvedProjectContextSchema.parse(input.context);
|
|
108
116
|
const project = ProjectDescriptionSchema.parse(input.project);
|
|
109
117
|
const canonicalProjectRoot = await realpath(input.projectRoot);
|
|
110
118
|
const outputPath = GenerationOutputPathSchema.parse(input.outputPath);
|
|
@@ -114,13 +122,20 @@ export class GenerationFinalizer {
|
|
|
114
122
|
let session = GenerationSessionSchema.parse(await this.dependencies.store.read(input.generationId));
|
|
115
123
|
this.assertBindings(session, input, config, context, project);
|
|
116
124
|
const journey = JourneySchema.parse({
|
|
117
|
-
version:
|
|
125
|
+
version: 2,
|
|
118
126
|
name,
|
|
127
|
+
devices: [{ role: DEFAULT_DEVICE_ROLE }],
|
|
119
128
|
steps: session.candidateSteps
|
|
120
129
|
});
|
|
130
|
+
if (input.manualReplay === false
|
|
131
|
+
&& journey.steps.some((step) => step.replayMode === "manual")) {
|
|
132
|
+
throw failure("VERIFICATION_FAILED", "verification", "Journey contains manual steps; cannot finalize in non-interactive mode. Re-run finalize from an interactive terminal so manual steps can be replayed with human operation of the external application", false);
|
|
133
|
+
}
|
|
121
134
|
let replayed = false;
|
|
122
135
|
let verificationReport;
|
|
123
136
|
if (session.verification.status === "notRun") {
|
|
137
|
+
this.revalidate(input, config, context, project, session);
|
|
138
|
+
this.dependencies.progress?.("verification");
|
|
124
139
|
const begun = await this.beginVerification(session);
|
|
125
140
|
session = begun.session;
|
|
126
141
|
if (!begun.owned
|
|
@@ -136,7 +151,9 @@ export class GenerationFinalizer {
|
|
|
136
151
|
let reconciled;
|
|
137
152
|
try {
|
|
138
153
|
reconciled = session.verification.status === "running"
|
|
139
|
-
? await this.reconcilePassedVerification(session, expected, () =>
|
|
154
|
+
? await this.reconcilePassedVerification(session, expected, () => {
|
|
155
|
+
this.revalidate(input, config, context, project, session);
|
|
156
|
+
})
|
|
140
157
|
: undefined;
|
|
141
158
|
}
|
|
142
159
|
catch (error) {
|
|
@@ -154,33 +171,33 @@ export class GenerationFinalizer {
|
|
|
154
171
|
}
|
|
155
172
|
if (begun.owned) {
|
|
156
173
|
try {
|
|
157
|
-
await this.revalidate(input, config, context, project, session);
|
|
158
174
|
if (input.signal?.aborted === true) {
|
|
159
175
|
throw failure("VERIFICATION_FAILED", "verification", "Generation finalization was cancelled");
|
|
160
176
|
}
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
timeoutMs: config.idle.timeoutMs
|
|
166
|
-
});
|
|
167
|
-
if (commandFailed(stopped)) {
|
|
168
|
-
throw failure("VERIFICATION_FAILED", "verification", stopped.stderr.trim()
|
|
169
|
-
|| stopped.spawnError
|
|
170
|
-
|| "Unable to reset the generated Journey process");
|
|
171
|
-
}
|
|
177
|
+
const replayProgress = this.bridgeReplayProgress(session.id, expected.attemptId);
|
|
178
|
+
const replayConfig = session.idlePolicy === undefined
|
|
179
|
+
? config
|
|
180
|
+
: { ...config, idle: session.idlePolicy };
|
|
172
181
|
const result = await this.dependencies.verifyRuntime.verify({
|
|
173
|
-
config,
|
|
182
|
+
config: replayConfig,
|
|
174
183
|
journey,
|
|
175
184
|
projectRoot: canonicalProjectRoot,
|
|
176
|
-
|
|
185
|
+
devices: [{
|
|
186
|
+
role: journey.devices[0]?.role ?? DEFAULT_DEVICE_ROLE,
|
|
187
|
+
deviceSerial: input.deviceSerial
|
|
188
|
+
}],
|
|
177
189
|
toolVersions: input.toolVersions,
|
|
178
190
|
requireFocusedInput: true,
|
|
179
191
|
generatedReplayPolicy: true,
|
|
180
|
-
...(input.
|
|
192
|
+
...(input.manualReplay === undefined
|
|
193
|
+
? {}
|
|
194
|
+
: { manualReplay: input.manualReplay }),
|
|
195
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
196
|
+
progress: replayProgress.onEvent
|
|
181
197
|
});
|
|
198
|
+
await replayProgress.settled();
|
|
182
199
|
replayed = true;
|
|
183
|
-
|
|
200
|
+
this.revalidate(input, config, context, project, session);
|
|
184
201
|
this.assertEligible(result, journey, expected);
|
|
185
202
|
verificationReport = TapHoundReportSchema.parse(result.report);
|
|
186
203
|
session = await this.persistPassedVerification(session, verificationReport, expected);
|
|
@@ -189,7 +206,12 @@ export class GenerationFinalizer {
|
|
|
189
206
|
if (session.verification.status === "running"
|
|
190
207
|
&& !(error instanceof GenerationFinalizationError
|
|
191
208
|
&& error.code === "FINALIZATION_IN_PROGRESS")) {
|
|
192
|
-
|
|
209
|
+
const aborted = error instanceof GenerationFinalizationError
|
|
210
|
+
&& error.stage === "precondition"
|
|
211
|
+
&& await this.tryAbortVerification(session);
|
|
212
|
+
if (!aborted) {
|
|
213
|
+
await this.persistFailedVerification(session, error);
|
|
214
|
+
}
|
|
193
215
|
}
|
|
194
216
|
throw error instanceof GenerationFinalizationError
|
|
195
217
|
? error
|
|
@@ -203,7 +225,9 @@ export class GenerationFinalizer {
|
|
|
203
225
|
const expected = this.expectedVerification(session, journey, config, canonicalProjectRoot, input.toolVersions);
|
|
204
226
|
let reconciled;
|
|
205
227
|
try {
|
|
206
|
-
reconciled = await this.reconcilePassedVerification(session, expected, () =>
|
|
228
|
+
reconciled = await this.reconcilePassedVerification(session, expected, () => {
|
|
229
|
+
this.revalidate(input, config, context, project, session);
|
|
230
|
+
});
|
|
207
231
|
}
|
|
208
232
|
catch (error) {
|
|
209
233
|
await this.persistFailedVerification(session, error);
|
|
@@ -228,7 +252,7 @@ export class GenerationFinalizer {
|
|
|
228
252
|
}
|
|
229
253
|
this.assertReport(verificationReport, journey, this.expectedVerification(session, journey, config, canonicalProjectRoot, input.toolVersions), "RECOVERY_REQUIRED");
|
|
230
254
|
this.assertBindings(session, input, config, context, project);
|
|
231
|
-
const meta = this.buildMeta(session, outputPath, verificationReport);
|
|
255
|
+
const meta = this.buildMeta(session, outputPath, verificationReport, config);
|
|
232
256
|
const generationReport = GenerationReportSchema.parse({
|
|
233
257
|
version: 1,
|
|
234
258
|
generationId: session.id,
|
|
@@ -239,6 +263,7 @@ export class GenerationFinalizer {
|
|
|
239
263
|
}))
|
|
240
264
|
});
|
|
241
265
|
if (session.publication.status === "notRun") {
|
|
266
|
+
this.dependencies.progress?.("publication");
|
|
242
267
|
try {
|
|
243
268
|
await this.dependencies.publisher.stage({
|
|
244
269
|
generationId: session.id,
|
|
@@ -287,6 +312,7 @@ export class GenerationFinalizer {
|
|
|
287
312
|
}
|
|
288
313
|
}
|
|
289
314
|
try {
|
|
315
|
+
this.dependencies.progress?.("export");
|
|
290
316
|
const exported = await this.dependencies.publisher.export({
|
|
291
317
|
generationId: session.id,
|
|
292
318
|
projectRoot: input.projectRoot,
|
|
@@ -308,44 +334,75 @@ export class GenerationFinalizer {
|
|
|
308
334
|
throw failure("EXPORT_FAILED", "export", "Authoritative bundle was published but Journey export failed", true, error);
|
|
309
335
|
}
|
|
310
336
|
};
|
|
337
|
+
bridgeReplayProgress(generationId, attemptId) {
|
|
338
|
+
let pending = Promise.resolve();
|
|
339
|
+
const onEvent = (event) => {
|
|
340
|
+
const phase = event;
|
|
341
|
+
pending = pending
|
|
342
|
+
.then(async () => {
|
|
343
|
+
await this.dependencies.store.updateVerificationPhase(generationId, attemptId, phase);
|
|
344
|
+
this.dependencies.replayProgress?.(phase);
|
|
345
|
+
})
|
|
346
|
+
.catch(() => undefined);
|
|
347
|
+
};
|
|
348
|
+
return {
|
|
349
|
+
onEvent,
|
|
350
|
+
settled: () => pending
|
|
351
|
+
};
|
|
352
|
+
}
|
|
311
353
|
assertBindings(session, input, config, context, project) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
354
|
+
const hashSummary = (value) => value.slice(0, 8);
|
|
355
|
+
const mismatches = [];
|
|
356
|
+
const compare = (binding, expected, actual) => {
|
|
357
|
+
if (expected !== actual) {
|
|
358
|
+
mismatches.push({ binding, expected, actual });
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
compare("projectRoot", input.projectRoot, project.projectRoot);
|
|
362
|
+
compare("deviceSerial", session.target.deviceSerial, input.deviceSerial);
|
|
363
|
+
compare("project.packageName", session.target.packageName, project.packageName);
|
|
364
|
+
compare("config.packageName", session.target.packageName, config.run.packageName);
|
|
365
|
+
compare("launchActivity", normalizeActivity(config.run.packageName, config.run.activity), normalizeActivity(project.packageName, project.launchActivity));
|
|
366
|
+
compare("inFlight", "none", session.inFlight === null ? "none" : "present");
|
|
367
|
+
compare("pendingConfirmation", "none", session.pendingConfirmation === null ? "none" : "present");
|
|
368
|
+
compare("session.state", "active", session.state);
|
|
369
|
+
compare("projectHash", hashSummary(session.bindings.projectHash), hashSummary(hashGenerationBinding(project)));
|
|
370
|
+
compare("configHash", hashSummary(session.bindings.configHash), hashSummary(hashGenerationBinding(config)));
|
|
371
|
+
compare("contextHash", hashSummary(session.bindings.contextHash), hashSummary(hashGenerationBinding(context)));
|
|
372
|
+
compare("contextSelection", hashSummary(hashGenerationBinding(session.contextSelection)), hashSummary(hashGenerationBinding(context.selection)));
|
|
373
|
+
compare("interactionPolicy", hashSummary(hashGenerationBinding(session.target.interactionPolicy)), hashSummary(hashGenerationBinding(context.interactionPolicy)));
|
|
374
|
+
if (mismatches.length > 0) {
|
|
375
|
+
const summary = mismatches.map((entry) => `${entry.binding} expected=${entry.expected} actual=${entry.actual}`).join("; ");
|
|
376
|
+
throw failure("CONTEXT_STALE", "precondition", `Generation binding changed: ${summary}`, false, undefined, { mismatches });
|
|
326
377
|
}
|
|
327
378
|
}
|
|
328
|
-
|
|
379
|
+
revalidate(input, config, context, project, session) {
|
|
380
|
+
// The session's stored context snapshot is authoritative; the CLI only
|
|
381
|
+
// warns when the live project context has drifted from it.
|
|
329
382
|
this.assertBindings(session, input, config, context, project);
|
|
330
|
-
const result = await this.dependencies.contextValidator.validate({
|
|
331
|
-
context,
|
|
332
|
-
projectRoot: input.projectRoot,
|
|
333
|
-
config
|
|
334
|
-
});
|
|
335
|
-
if (result.status !== "valid") {
|
|
336
|
-
throw failure(result.status === "stale" ? "CONTEXT_STALE" : "CONTEXT_INVALID", "precondition", result.reason.message);
|
|
337
|
-
}
|
|
338
383
|
}
|
|
339
384
|
async beginVerification(session) {
|
|
340
385
|
const attemptId = this.dependencies.generateAttemptId();
|
|
386
|
+
const owner = this.dependencies.owner === undefined
|
|
387
|
+
? undefined
|
|
388
|
+
: {
|
|
389
|
+
pid: this.dependencies.owner.pid,
|
|
390
|
+
startedAt: this.dependencies.owner.now().toISOString()
|
|
391
|
+
};
|
|
341
392
|
const expected = GenerationSessionSchema.parse({
|
|
342
393
|
...session,
|
|
343
394
|
revision: session.revision + 1,
|
|
344
|
-
verification: {
|
|
395
|
+
verification: {
|
|
396
|
+
status: "running",
|
|
397
|
+
attemptId,
|
|
398
|
+
...(owner === undefined
|
|
399
|
+
? {}
|
|
400
|
+
: { ownerPid: owner.pid, startedAt: owner.startedAt })
|
|
401
|
+
}
|
|
345
402
|
});
|
|
346
403
|
try {
|
|
347
404
|
return {
|
|
348
|
-
session: await this.dependencies.store.beginVerification(session.id, session.revision, attemptId),
|
|
405
|
+
session: await this.dependencies.store.beginVerification(session.id, session.revision, attemptId, owner),
|
|
349
406
|
owned: true
|
|
350
407
|
};
|
|
351
408
|
}
|
|
@@ -390,6 +447,11 @@ export class GenerationFinalizer {
|
|
|
390
447
|
this.assertReport(result.report, journey, expected, "VERIFICATION_FAILED");
|
|
391
448
|
}
|
|
392
449
|
assertReport(report, journey, expected, code) {
|
|
450
|
+
const reportDevice = report.environment.devices[0];
|
|
451
|
+
const singleDefaultDevice = report.environment.devices.length === 1
|
|
452
|
+
&& reportDevice?.role === DEFAULT_DEVICE_ROLE
|
|
453
|
+
&& reportDevice.deviceSerial === expected.deviceSerial;
|
|
454
|
+
const reportUiBackend = reportDevice?.uiBackend;
|
|
393
455
|
if (report.status !== "passed"
|
|
394
456
|
|| report.fallbackUsed
|
|
395
457
|
|| report.primaryFailure !== undefined
|
|
@@ -400,7 +462,8 @@ export class GenerationFinalizer {
|
|
|
400
462
|
|| report.project.root !== expected.project.root
|
|
401
463
|
|| report.project.packageName !== expected.project.packageName
|
|
402
464
|
|| report.project.launchActivity !== expected.project.launchActivity
|
|
403
|
-
||
|
|
465
|
+
|| !singleDefaultDevice
|
|
466
|
+
|| !sameJson(reportUiBackend, expected.bindings.uiBackend)
|
|
404
467
|
|| !sameJson(canonicalTools(report.environment.tools), expected.tools)
|
|
405
468
|
|| report.steps.length !== journey.steps.length) {
|
|
406
469
|
throw failure(code, "verification", "Verification report does not match the exact generated replay");
|
|
@@ -410,7 +473,8 @@ export class GenerationFinalizer {
|
|
|
410
473
|
const expectationType = candidate.expect?.type;
|
|
411
474
|
const hasLocator = candidate.action === "click"
|
|
412
475
|
|| candidate.action === "longClick"
|
|
413
|
-
|| candidate.action === "swipe"
|
|
476
|
+
|| candidate.action === "swipe"
|
|
477
|
+
|| candidate.action === "bridge";
|
|
414
478
|
const isScrollTo = candidate.action === "scrollTo";
|
|
415
479
|
if (step === undefined
|
|
416
480
|
|| step.index !== index
|
|
@@ -529,8 +593,9 @@ export class GenerationFinalizer {
|
|
|
529
593
|
throw new Error("Verification Report run ID does not match receipt");
|
|
530
594
|
}
|
|
531
595
|
this.assertReport(report, {
|
|
532
|
-
version:
|
|
596
|
+
version: 2,
|
|
533
597
|
name: expected.journey.name,
|
|
598
|
+
devices: [{ role: DEFAULT_DEVICE_ROLE }],
|
|
534
599
|
steps: running.candidateSteps
|
|
535
600
|
}, expected, "VERIFICATION_FAILED");
|
|
536
601
|
}
|
|
@@ -539,12 +604,28 @@ export class GenerationFinalizer {
|
|
|
539
604
|
? error
|
|
540
605
|
: failure("VERIFICATION_FAILED", "verification", "Immutable verification Report does not match the receipt", false, error);
|
|
541
606
|
}
|
|
542
|
-
|
|
607
|
+
revalidateCurrent();
|
|
543
608
|
return {
|
|
544
609
|
session: await this.persistPassedVerification(running, report, expected),
|
|
545
610
|
report
|
|
546
611
|
};
|
|
547
612
|
}
|
|
613
|
+
async tryAbortVerification(running) {
|
|
614
|
+
const latest = GenerationSessionSchema.parse(await this.dependencies.store.read(running.id));
|
|
615
|
+
if (running.verification.status !== "running"
|
|
616
|
+
|| latest.verification.status !== "running"
|
|
617
|
+
|| latest.revision !== running.revision
|
|
618
|
+
|| latest.verification.attemptId !== running.verification.attemptId) {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
try {
|
|
622
|
+
await this.dependencies.store.abortVerification(latest.id, latest.revision);
|
|
623
|
+
return true;
|
|
624
|
+
}
|
|
625
|
+
catch {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
548
629
|
async persistFailedVerification(running, error) {
|
|
549
630
|
const latest = GenerationSessionSchema.parse(await this.dependencies.store.read(running.id));
|
|
550
631
|
if (running.verification.status !== "running"
|
|
@@ -603,13 +684,14 @@ export class GenerationFinalizer {
|
|
|
603
684
|
throw failure("RECOVERY_REQUIRED", "verification", "Immutable verification report is not a clean passed replay");
|
|
604
685
|
}
|
|
605
686
|
this.assertReport(report, {
|
|
606
|
-
version:
|
|
687
|
+
version: 2,
|
|
607
688
|
name: expected.journey.name,
|
|
689
|
+
devices: [{ role: DEFAULT_DEVICE_ROLE }],
|
|
608
690
|
steps: session.candidateSteps
|
|
609
691
|
}, expected, "RECOVERY_REQUIRED");
|
|
610
692
|
return report;
|
|
611
693
|
}
|
|
612
|
-
buildMeta(session, outputPath, report) {
|
|
694
|
+
buildMeta(session, outputPath, report, config) {
|
|
613
695
|
if (session.verification.status !== "passed") {
|
|
614
696
|
throw failure("RECOVERY_REQUIRED", "verification", "Generation verification is not durably passed");
|
|
615
697
|
}
|
|
@@ -618,17 +700,32 @@ export class GenerationFinalizer {
|
|
|
618
700
|
status: "verified",
|
|
619
701
|
generationId: session.id,
|
|
620
702
|
journeyPath: outputPath,
|
|
703
|
+
journeySha256: report.journey.sha256,
|
|
621
704
|
bindings: {
|
|
622
705
|
projectHash: session.bindings.projectHash,
|
|
623
706
|
configHash: session.bindings.configHash,
|
|
624
|
-
contextHash: session.bindings.contextHash
|
|
707
|
+
contextHash: session.bindings.contextHash,
|
|
708
|
+
uiBackend: session.bindings.uiBackend
|
|
709
|
+
},
|
|
710
|
+
replayPolicy: {
|
|
711
|
+
generatedReplayPolicy: true,
|
|
712
|
+
requireFocusedInput: true,
|
|
713
|
+
idle: session.idlePolicy ?? config.idle
|
|
625
714
|
},
|
|
715
|
+
contextSelection: session.contextSelection,
|
|
626
716
|
verification: {
|
|
627
717
|
reportPath: GENERATION_BUNDLE_PATHS.verificationReport,
|
|
628
718
|
reportSha256: session.verification.reportSha256,
|
|
629
719
|
runId: report.runId,
|
|
630
720
|
runs: 1
|
|
631
721
|
},
|
|
722
|
+
...(session.baseFlow === undefined
|
|
723
|
+
? {}
|
|
724
|
+
: { baseFlow: session.baseFlow }),
|
|
725
|
+
...(session.sourceBrief === undefined
|
|
726
|
+
? {}
|
|
727
|
+
: { sourceBrief: session.sourceBrief }),
|
|
728
|
+
externalFlows: session.externalFlows,
|
|
632
729
|
manualOverrideStepIndexes: session.candidateSources.flatMap((source, index) => source === "manualOverride" ? [index] : [])
|
|
633
730
|
});
|
|
634
731
|
}
|
|
@@ -2,7 +2,8 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { extname, resolve } from "node:path";
|
|
3
3
|
import { GenerationBundleManifestSchema, GenerationMetaSchema, GenerationReportSchema } from "../../domain/generation.js";
|
|
4
4
|
import { JourneySchema } from "../../domain/journey.js";
|
|
5
|
-
import { TapHoundReportSchema } from "../../domain/report.js";
|
|
5
|
+
import { hashJourney, TapHoundReportSchema } from "../../domain/report.js";
|
|
6
|
+
import { BUILD_DIR } from "../../domain/workspace.js";
|
|
6
7
|
import { GenerationSessionStoreError } from "../../ports/generation-session-store.js";
|
|
7
8
|
export const GENERATION_BUNDLE_PATHS = {
|
|
8
9
|
candidateJourney: "candidate/journey.json",
|
|
@@ -59,7 +60,9 @@ export class GenerationPublisher {
|
|
|
59
60
|
const meta = GenerationMetaSchema.parse(input.meta);
|
|
60
61
|
if (input.generationId !== report.generationId
|
|
61
62
|
|| input.generationId !== meta.generationId
|
|
62
|
-
|| report.steps.length !== journey.steps.length
|
|
63
|
+
|| report.steps.length !== journey.steps.length
|
|
64
|
+
|| meta.journeySha256 !== verificationReport.journey.sha256
|
|
65
|
+
|| meta.journeySha256 !== hashJourney(journey)) {
|
|
63
66
|
throw new Error("Generation bundle identities do not align");
|
|
64
67
|
}
|
|
65
68
|
const journeyBytes = serializeJson(journey);
|
|
@@ -97,12 +100,13 @@ export class GenerationPublisher {
|
|
|
97
100
|
export = async (input) => {
|
|
98
101
|
const journey = JourneySchema.parse(input.journey);
|
|
99
102
|
const meta = GenerationMetaSchema.parse(input.meta);
|
|
100
|
-
const
|
|
103
|
+
const exportRoot = input.projectRoot;
|
|
104
|
+
const journeyPath = resolve(exportRoot, input.journeyPath);
|
|
101
105
|
const metaPath = generationMetaOutputPath(journeyPath);
|
|
102
|
-
const authorityRoot = resolve(
|
|
106
|
+
const authorityRoot = resolve(exportRoot, BUILD_DIR);
|
|
103
107
|
try {
|
|
104
108
|
await this.dependencies.journeyWriter.writeProjectBound({
|
|
105
|
-
projectRoot:
|
|
109
|
+
projectRoot: exportRoot,
|
|
106
110
|
authorityRoot,
|
|
107
111
|
outputPath: journeyPath,
|
|
108
112
|
journey
|
|
@@ -110,7 +114,7 @@ export class GenerationPublisher {
|
|
|
110
114
|
const expectedJourney = Buffer.from(serializeJson(journey));
|
|
111
115
|
const actualJourney = await this.dependencies.journeyWriter
|
|
112
116
|
.readProjectBound({
|
|
113
|
-
projectRoot:
|
|
117
|
+
projectRoot: exportRoot,
|
|
114
118
|
authorityRoot,
|
|
115
119
|
outputPath: journeyPath
|
|
116
120
|
});
|
|
@@ -125,14 +129,14 @@ export class GenerationPublisher {
|
|
|
125
129
|
}
|
|
126
130
|
try {
|
|
127
131
|
await this.dependencies.metaWriter.writeProjectBound({
|
|
128
|
-
projectRoot:
|
|
132
|
+
projectRoot: exportRoot,
|
|
129
133
|
authorityRoot,
|
|
130
134
|
outputPath: metaPath,
|
|
131
135
|
meta
|
|
132
136
|
});
|
|
133
137
|
const expectedMeta = Buffer.from(serializeJson(meta));
|
|
134
138
|
const actualMeta = await this.dependencies.metaWriter.readProjectBound({
|
|
135
|
-
projectRoot:
|
|
139
|
+
projectRoot: exportRoot,
|
|
136
140
|
authorityRoot,
|
|
137
141
|
outputPath: metaPath
|
|
138
142
|
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type GenerationSession } from "../../domain/generation.js";
|
|
2
|
+
import { type GenerationSessionStore } from "../../ports/generation-session-store.js";
|
|
3
|
+
export interface GenerationRecoveryStatus {
|
|
4
|
+
generationId: string;
|
|
5
|
+
revision: number;
|
|
6
|
+
state: GenerationSession["state"];
|
|
7
|
+
candidateStepCount: number;
|
|
8
|
+
idlePolicy?: GenerationSession["idlePolicy"];
|
|
9
|
+
inFlight: GenerationSession["inFlight"];
|
|
10
|
+
pendingConfirmation: (NonNullable<GenerationSession["pendingConfirmation"]> & {
|
|
11
|
+
expired: boolean;
|
|
12
|
+
}) | null;
|
|
13
|
+
verification: GenerationSession["verification"];
|
|
14
|
+
publication: GenerationSession["publication"];
|
|
15
|
+
recovery: {
|
|
16
|
+
available: boolean;
|
|
17
|
+
kind: "step" | "verification" | null;
|
|
18
|
+
actionMayHaveExecuted: boolean;
|
|
19
|
+
attemptOutcome: "succeeded" | "failed" | "cancelled" | "unknown" | null;
|
|
20
|
+
requiredDecision: "retry" | null;
|
|
21
|
+
ownerAlive: boolean | null;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface GenerationRecoveryDependencies {
|
|
25
|
+
store: Pick<GenerationSessionStore, "read" | "readEvidence" | "recover" | "recoverVerification">;
|
|
26
|
+
ownerAlive?: ((pid: number) => boolean) | undefined;
|
|
27
|
+
now: () => Date;
|
|
28
|
+
}
|
|
29
|
+
export declare class GenerationRecoveryService {
|
|
30
|
+
private readonly dependencies;
|
|
31
|
+
constructor(dependencies: GenerationRecoveryDependencies);
|
|
32
|
+
readonly status: (generationId: string) => Promise<GenerationRecoveryStatus>;
|
|
33
|
+
readonly retry: (generationId: string) => Promise<GenerationSession>;
|
|
34
|
+
private evidenceExists;
|
|
35
|
+
}
|