taphound 0.2.0-dev.1 → 0.2.0-dev.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -149
- package/README.zh-CN.md +47 -147
- package/assets/skills/taphound-case-suite/SKILL.md +363 -0
- package/assets/skills/taphound-case-suite/schemas/base-flow-record.schema.json +22 -0
- package/assets/skills/taphound-case-suite/schemas/case-catalog.schema.json +26 -0
- package/assets/skills/taphound-case-suite/schemas/case-ledger.schema.json +158 -0
- package/assets/skills/taphound-case-suite/schemas/suite-input.schema.json +41 -0
- package/assets/skills/taphound-case-suite/schemas/transition.schema.json +61 -0
- package/assets/skills/taphound-case-suite/scripts/ledger.mjs +1207 -0
- package/assets/skills/taphound-case-suite/templates/base-flow-record.example.json +20 -0
- package/assets/skills/taphound-case-suite/templates/suite-input.example.json +27 -0
- package/assets/skills/taphound-case-suite/templates/transition.example.json +8 -0
- package/assets/skills/taphound-flash/SKILL.md +115 -0
- package/assets/skills/taphound-flash/scripts/flash.mjs +585 -0
- package/assets/skills/taphound-flash/templates/flash-plan.example.json +15 -0
- package/assets/skills/taphound-journey-brief-author/CONTEXT-GUIDE.md +471 -0
- package/assets/skills/taphound-journey-brief-author/SKILL.md +494 -0
- package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.md +96 -0
- package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.zh-CN.md +80 -0
- package/assets/skills/taphound-journey-brief-author/prompts/context-analyze-project.md +309 -0
- package/assets/skills/taphound-journey-brief-author/schemas/context-refresh-result.json +123 -0
- package/assets/skills/taphound-journey-brief-author/schemas/project-context-module.json +192 -0
- package/assets/skills/taphound-journey-brief-author/schemas/project-context.json +152 -0
- package/assets/skills/taphound-journey-brief-author/templates/project-context-module.example.json +58 -0
- package/assets/skills/taphound-journey-brief-author/templates/project-context.example.json +51 -0
- package/assets/skills/taphound-journey-brief-author/templates/taphound-journey-brief.template.md +63 -0
- package/assets/skills/taphound-journey-generator/SKILL.md +507 -0
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/prompts/check-completion.md +3 -0
- package/assets/skills/taphound-journey-generator/prompts/consume-journey-brief.md +52 -0
- package/assets/skills/taphound-journey-generator/prompts/generate-step.md +211 -0
- package/assets/skills/taphound-journey-generator/prompts/select-flow.md +91 -0
- package/assets/skills/taphound-journey-generator/references/bridge.md +35 -0
- package/assets/skills/taphound-journey-generator/references/mid-session.md +44 -0
- package/assets/skills/taphound-journey-generator/schemas/external-flow.json +37 -0
- package/assets/skills/taphound-journey-generator/schemas/flow.json +28 -0
- package/assets/skills/taphound-journey-generator/schemas/journey-source.json +25 -0
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/observe-output.json +13 -4
- package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/proposed-step-envelope.json +127 -5
- package/assets/skills/taphound-journey-generator/scripts/envelope.mjs +578 -0
- package/assets/skills/taphound-journey-generator/templates/external-flow.example.json +22 -0
- package/assets/skills/taphound-journey-generator/templates/flow.example.json +22 -0
- package/assets/skills/taphound-journey-generator/templates/journey-source.example.json +17 -0
- package/assets/skills/taphound-journey-generator/templates/taphound-journey-brief.example.md +70 -0
- package/assets/skills/taphound-verify-change/SKILL.md +91 -0
- package/assets/skills/taphound-verify-change/references/accept.md +38 -0
- package/assets/skills/taphound-verify-change/references/preserve.md +357 -0
- package/assets/skills/taphound-verify-change/schemas/workflow-manifest.schema.json +295 -0
- package/assets/skills/taphound-verify-change/scripts/handoff.mjs +482 -0
- package/assets/skills/taphound-verify-change/scripts/ui-refactor.mjs +505 -0
- package/dist/adapters/adb/adb-adapter.d.ts +16 -2
- package/dist/adapters/adb/adb-adapter.js +199 -0
- package/dist/adapters/adb/system-uiautomator-snapshot-provider.d.ts +13 -0
- package/dist/adapters/adb/system-uiautomator-snapshot-provider.js +122 -0
- package/dist/adapters/adb/ui-automator-parser.d.ts +3 -0
- package/dist/adapters/adb/ui-automator-parser.js +160 -0
- package/dist/adapters/adb/window-topology-parser.d.ts +2 -0
- package/dist/adapters/adb/window-topology-parser.js +101 -0
- package/dist/adapters/android-cli/android-cli-adapter.d.ts +16 -8
- package/dist/adapters/android-cli/android-cli-adapter.js +137 -15
- package/dist/adapters/android-cli/android-cli-snapshot-provider.d.ts +9 -0
- package/dist/adapters/android-cli/android-cli-snapshot-provider.js +99 -0
- package/dist/adapters/android-cli/layout-parser.js +12 -8
- package/dist/adapters/appium/appium-doctor.d.ts +7 -0
- package/dist/adapters/appium/appium-doctor.js +128 -0
- package/dist/adapters/appium/appium-ui-snapshot-provider.d.ts +42 -0
- package/dist/adapters/appium/appium-ui-snapshot-provider.js +309 -0
- package/dist/adapters/camera/camera-probe-adapter.d.ts +18 -0
- package/dist/adapters/camera/camera-probe-adapter.js +286 -0
- package/dist/adapters/filesystem/artifact-store.js +40 -1
- package/dist/adapters/filesystem/context-document-writer.d.ts +15 -0
- package/dist/adapters/filesystem/context-document-writer.js +112 -0
- package/dist/adapters/filesystem/diagnostics-journal.d.ts +17 -0
- package/dist/adapters/filesystem/diagnostics-journal.js +130 -0
- package/dist/adapters/filesystem/external-flow-registry.d.ts +17 -0
- package/dist/adapters/filesystem/external-flow-registry.js +325 -0
- package/dist/adapters/filesystem/generation-session-store.d.ts +17 -11
- package/dist/adapters/filesystem/generation-session-store.js +281 -855
- package/dist/adapters/filesystem/generation-store/session-lock.d.ts +29 -0
- package/dist/adapters/filesystem/generation-store/session-lock.js +202 -0
- package/dist/adapters/filesystem/generation-store/session-transitions.d.ts +24 -0
- package/dist/adapters/filesystem/generation-store/session-transitions.js +340 -0
- package/dist/adapters/filesystem/generation-store/store-files.d.ts +50 -0
- package/dist/adapters/filesystem/generation-store/store-files.js +363 -0
- package/dist/adapters/filesystem/journey-composition-store.d.ts +23 -0
- package/dist/adapters/filesystem/journey-composition-store.js +151 -0
- package/dist/adapters/filesystem/knowledge-registry.d.ts +12 -0
- package/dist/adapters/filesystem/knowledge-registry.js +223 -0
- package/dist/adapters/filesystem/project-bound-file.js +3 -5
- package/dist/adapters/filesystem/project-file-inspector.d.ts +2 -0
- package/dist/adapters/filesystem/project-file-inspector.js +57 -28
- package/dist/adapters/filesystem/project-identity-inspector.d.ts +7 -0
- package/dist/adapters/filesystem/project-identity-inspector.js +215 -0
- package/dist/adapters/filesystem/project-inventory-inspector.d.ts +8 -0
- package/dist/adapters/filesystem/project-inventory-inspector.js +93 -0
- package/dist/adapters/filesystem/project-module-discoverer.d.ts +6 -0
- package/dist/adapters/filesystem/project-module-discoverer.js +158 -0
- package/dist/adapters/filesystem/skill-installer.d.ts +7 -2
- package/dist/adapters/filesystem/skill-installer.js +40 -11
- package/dist/adapters/filesystem/workspace-layout.d.ts +7 -0
- package/dist/adapters/filesystem/workspace-layout.js +57 -0
- package/dist/adapters/git/node-git-diff.d.ts +17 -0
- package/dist/adapters/git/node-git-diff.js +114 -0
- package/dist/adapters/process/node-detached-process-launcher.d.ts +6 -0
- package/dist/adapters/process/node-detached-process-launcher.js +96 -0
- package/dist/adapters/process/node-process-runner.js +11 -2
- package/dist/adapters/prompt/inquirer-align-prompt.d.ts +27 -0
- package/dist/adapters/prompt/inquirer-align-prompt.js +65 -0
- package/dist/adapters/prompt/inquirer-generation-prompt.js +3 -0
- package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +9 -1
- package/dist/adapters/prompt/inquirer-recorder-prompt.js +63 -0
- package/dist/adapters/runtime/adb-runtime-backend.d.ts +67 -0
- package/dist/adapters/runtime/adb-runtime-backend.js +206 -0
- package/dist/adapters/runtime/fake-runtime-backend.d.ts +87 -0
- package/dist/adapters/runtime/fake-runtime-backend.js +240 -0
- package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.d.ts +45 -0
- package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.js +153 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.d.ts +23 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.js +39 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.d.ts +20 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.js +180 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.d.ts +78 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.js +379 -0
- package/dist/adapters/runtime/mobile-mcp/mobile-mcp-tools.d.ts +46 -0
- package/dist/adapters/runtime/session-adb-view.d.ts +53 -0
- package/dist/adapters/runtime/session-adb-view.js +193 -0
- package/dist/adapters/runtime/session-backed-ports.d.ts +21 -0
- package/dist/adapters/runtime/session-backed-ports.js +43 -0
- package/dist/adapters/runtime/shared-session-runtime-backend.d.ts +12 -0
- package/dist/adapters/runtime/shared-session-runtime-backend.js +64 -0
- package/dist/adapters/ui/auto-ui-snapshot-provider.d.ts +8 -0
- package/dist/adapters/ui/auto-ui-snapshot-provider.js +48 -0
- package/dist/adapters/ui/device-layout-temp.d.ts +6 -0
- package/dist/adapters/ui/device-layout-temp.js +18 -0
- package/dist/adapters/ui/device-ui-environment.d.ts +13 -0
- package/dist/adapters/ui/device-ui-environment.js +81 -0
- package/dist/adapters/ui/layout-normalization.d.ts +9 -0
- package/dist/adapters/ui/layout-normalization.js +44 -0
- package/dist/adapters/ui/ui-snapshot-error.d.ts +11 -0
- package/dist/adapters/ui/ui-snapshot-error.js +12 -0
- package/dist/adapters/ui/ui-snapshot-support.d.ts +15 -0
- package/dist/adapters/ui/ui-snapshot-support.js +12 -0
- package/dist/application/align/align-service.d.ts +42 -0
- package/dist/application/align/align-service.js +106 -0
- package/dist/application/assertion/expectation-evaluator.d.ts +18 -5
- package/dist/application/assertion/expectation-evaluator.js +156 -18
- package/dist/application/assertion/guarded-expectation.d.ts +45 -0
- package/dist/application/assertion/guarded-expectation.js +74 -0
- package/dist/application/checkpoint/baseline-capturer.d.ts +26 -0
- package/dist/application/checkpoint/baseline-capturer.js +163 -0
- package/dist/application/checkpoint/baseline-error.d.ts +5 -0
- package/dist/application/checkpoint/baseline-error.js +8 -0
- package/dist/application/checkpoint/baseline-service.d.ts +31 -0
- package/dist/application/checkpoint/baseline-service.js +147 -0
- package/dist/application/checkpoint/checkpoint-evaluator.d.ts +35 -0
- package/dist/application/checkpoint/checkpoint-evaluator.js +415 -0
- package/dist/application/checkpoint/regression-comparator.d.ts +18 -0
- package/dist/application/checkpoint/regression-comparator.js +192 -0
- package/dist/application/collector/logcat-collector.d.ts +22 -1
- package/dist/application/collector/logcat-collector.js +103 -15
- package/dist/application/collector/logcat-event.d.ts +8 -0
- package/dist/application/collector/logcat-event.js +79 -0
- package/dist/application/collector/logcat-stop.d.ts +2 -0
- package/dist/application/collector/logcat-stop.js +11 -0
- package/dist/application/context/context-generator.d.ts +45 -0
- package/dist/application/context/context-generator.js +228 -0
- package/dist/application/context/context-loader.d.ts +40 -0
- package/dist/application/context/context-loader.js +223 -0
- package/dist/application/context/context-refresher.d.ts +59 -0
- package/dist/application/context/context-refresher.js +349 -0
- package/dist/application/context/context-rehasher.d.ts +36 -0
- package/dist/application/context/context-rehasher.js +110 -0
- package/dist/application/context/context-validator.d.ts +23 -1
- package/dist/application/context/context-validator.js +116 -9
- package/dist/application/context/evidence-hash.d.ts +1 -0
- package/dist/application/context/evidence-hash.js +125 -0
- package/dist/application/context/evidence-match.d.ts +9 -0
- package/dist/application/context/evidence-match.js +8 -0
- package/dist/application/context/shard-identity.d.ts +2 -0
- package/dist/application/context/shard-identity.js +7 -0
- package/dist/application/contract/contract-loader.d.ts +25 -0
- package/dist/application/contract/contract-loader.js +68 -0
- package/dist/application/contract/contract-review.d.ts +25 -0
- package/dist/application/contract/contract-review.js +51 -0
- package/dist/application/contract/contract-verifier.d.ts +53 -0
- package/dist/application/contract/contract-verifier.js +645 -0
- package/dist/application/devices/resolve-device-assignments.d.ts +21 -0
- package/dist/application/devices/resolve-device-assignments.js +60 -0
- package/dist/application/diagnosis/failure-classifier.d.ts +23 -0
- package/dist/application/diagnosis/failure-classifier.js +179 -0
- package/dist/application/diagnostics/diagnostics-exporter.d.ts +29 -0
- package/dist/application/diagnostics/diagnostics-exporter.js +283 -0
- package/dist/application/diagnostics/ui-capture-telemetry.d.ts +16 -0
- package/dist/application/diagnostics/ui-capture-telemetry.js +81 -0
- package/dist/application/doctor/doctor-service.d.ts +36 -3
- package/dist/application/doctor/doctor-service.js +255 -25
- package/dist/application/generation/generation-app-preparer.d.ts +20 -0
- package/dist/application/generation/generation-app-preparer.js +28 -0
- package/dist/application/generation/generation-config-service.d.ts +21 -0
- package/dist/application/generation/generation-config-service.js +53 -0
- package/dist/application/generation/generation-confirmation-service.d.ts +14 -3
- package/dist/application/generation/generation-confirmation-service.js +109 -24
- package/dist/application/generation/generation-context-snapshot.d.ts +6 -0
- package/dist/application/generation/generation-context-snapshot.js +29 -0
- package/dist/application/generation/generation-finalizer.d.ts +15 -9
- package/dist/application/generation/generation-finalizer.js +167 -70
- package/dist/application/generation/generation-publisher.js +12 -8
- package/dist/application/generation/generation-recovery-service.d.ts +35 -0
- package/dist/application/generation/generation-recovery-service.js +144 -0
- package/dist/application/generation/generation-reopen-service.d.ts +15 -0
- package/dist/application/generation/generation-reopen-service.js +37 -0
- package/dist/application/generation/generation-replace-service.d.ts +32 -0
- package/dist/application/generation/generation-replace-service.js +116 -0
- package/dist/application/generation/generation-starter.d.ts +44 -4
- package/dist/application/generation/generation-starter.js +175 -8
- package/dist/application/generation/generation-step-executor.d.ts +67 -5
- package/dist/application/generation/generation-step-executor.js +471 -108
- package/dist/application/generation/proposed-step-validator.js +96 -30
- package/dist/application/generation/replay-policy-loader.d.ts +16 -0
- package/dist/application/generation/replay-policy-loader.js +29 -0
- package/dist/application/generation/risk-evaluator.d.ts +4 -1
- package/dist/application/generation/risk-evaluator.js +118 -3
- package/dist/application/generation/runtime-observer.d.ts +39 -7
- package/dist/application/generation/runtime-observer.js +193 -24
- package/dist/application/impact/impact-resolver.d.ts +32 -0
- package/dist/application/impact/impact-resolver.js +160 -0
- package/dist/application/init/init-service.js +26 -13
- package/dist/application/interaction/action-executor.d.ts +3 -2
- package/dist/application/interaction/action-executor.js +11 -1
- package/dist/application/interaction/action-target.d.ts +17 -0
- package/dist/application/interaction/action-target.js +40 -0
- package/dist/application/interaction/external-step-runner.d.ts +153 -0
- package/dist/application/interaction/external-step-runner.js +332 -0
- package/dist/application/interaction/fallback-resolver.d.ts +6 -3
- package/dist/application/interaction/fallback-resolver.js +7 -5
- package/dist/application/interaction/scroll-to-executor.d.ts +17 -3
- package/dist/application/interaction/scroll-to-executor.js +118 -36
- package/dist/application/journey/external-flow-resolver.d.ts +25 -0
- package/dist/application/journey/external-flow-resolver.js +22 -0
- package/dist/application/journey/journey-check-service.d.ts +54 -0
- package/dist/application/journey/journey-check-service.js +207 -0
- package/dist/application/journey/journey-promoter.d.ts +26 -0
- package/dist/application/journey/journey-promoter.js +123 -0
- package/dist/application/journey/journey-resolver.d.ts +42 -0
- package/dist/application/journey/journey-resolver.js +239 -0
- package/dist/application/journey/journey-retirer.d.ts +25 -0
- package/dist/application/journey/journey-retirer.js +87 -0
- package/dist/application/knowledge/anchor-resolver.d.ts +36 -0
- package/dist/application/knowledge/anchor-resolver.js +110 -0
- package/dist/application/knowledge/knowledge-loader.d.ts +16 -0
- package/dist/application/knowledge/knowledge-loader.js +36 -0
- package/dist/application/locator/layout-failure-summary.d.ts +2 -0
- package/dist/application/locator/layout-failure-summary.js +67 -0
- package/dist/application/locator/layout-traversal.d.ts +11 -0
- package/dist/application/locator/layout-traversal.js +18 -0
- package/dist/application/locator/locator-resolver.d.ts +13 -1
- package/dist/application/locator/locator-resolver.js +182 -18
- package/dist/application/observe/observe-service.d.ts +20 -0
- package/dist/application/observe/observe-service.js +91 -0
- package/dist/application/project/project-describer.d.ts +14 -6
- package/dist/application/project/project-describer.js +38 -5
- package/dist/application/recognition/screen-detector.d.ts +28 -0
- package/dist/application/recognition/screen-detector.js +111 -0
- package/dist/application/recorder/locator-selector.js +151 -17
- package/dist/application/recorder/recorder-service.d.ts +16 -4
- package/dist/application/recorder/recorder-service.js +420 -128
- package/dist/application/report/report-writer.d.ts +5 -0
- package/dist/application/report/report-writer.js +17 -1
- package/dist/application/runtime/cold-launch.d.ts +28 -0
- package/dist/application/runtime/cold-launch.js +59 -0
- package/dist/application/runtime/step-runner.d.ts +49 -4
- package/dist/application/runtime/step-runner.js +721 -130
- package/dist/application/runtime/verify-runtime.d.ts +57 -7
- package/dist/application/runtime/verify-runtime.js +577 -175
- package/dist/application/ui/cached-ui-snapshot-provider.d.ts +45 -0
- package/dist/application/ui/cached-ui-snapshot-provider.js +111 -0
- package/dist/application/ui/observed-ui-snapshot-provider.d.ts +15 -0
- package/dist/application/ui/observed-ui-snapshot-provider.js +66 -0
- package/dist/application/ui/ui-snapshot-lifecycle.d.ts +2 -0
- package/dist/application/ui/ui-snapshot-lifecycle.js +8 -0
- package/dist/application/ui/ui-stability-probe.d.ts +9 -0
- package/dist/application/ui/ui-stability-probe.js +24 -0
- package/dist/application/wait/idle-advice.d.ts +6 -0
- package/dist/application/wait/idle-advice.js +27 -0
- package/dist/application/wait/idle-profiles.d.ts +15 -0
- package/dist/application/wait/idle-profiles.js +75 -0
- package/dist/application/wait/idle-waiter.d.ts +28 -4
- package/dist/application/wait/idle-waiter.js +201 -15
- package/dist/cli/commands/align.d.ts +3 -0
- package/dist/cli/commands/align.js +124 -0
- package/dist/cli/commands/baseline.d.ts +3 -0
- package/dist/cli/commands/baseline.js +130 -0
- package/dist/cli/commands/context.js +300 -27
- package/dist/cli/commands/contract.d.ts +3 -0
- package/dist/cli/commands/contract.js +120 -0
- package/dist/cli/commands/diagnose.d.ts +3 -0
- package/dist/cli/commands/diagnose.js +84 -0
- package/dist/cli/commands/doctor.js +16 -9
- package/dist/cli/commands/failure.d.ts +3 -0
- package/dist/cli/commands/failure.js +63 -0
- package/dist/cli/commands/generation/lifecycle-commands.d.ts +5 -0
- package/dist/cli/commands/generation/lifecycle-commands.js +211 -0
- package/dist/cli/commands/generation/session-commands.d.ts +11 -0
- package/dist/cli/commands/generation/session-commands.js +472 -0
- package/dist/cli/commands/generation/shared.d.ts +1414 -0
- package/dist/cli/commands/generation/shared.js +291 -0
- package/dist/cli/commands/generation/step-commands.d.ts +8 -0
- package/dist/cli/commands/generation/step-commands.js +315 -0
- package/dist/cli/commands/generation.js +10 -507
- package/dist/cli/commands/impact.d.ts +3 -0
- package/dist/cli/commands/impact.js +87 -0
- package/dist/cli/commands/init.js +13 -6
- package/dist/cli/commands/journey.d.ts +3 -0
- package/dist/cli/commands/journey.js +382 -0
- package/dist/cli/commands/knowledge.d.ts +3 -0
- package/dist/cli/commands/knowledge.js +93 -0
- package/dist/cli/commands/observe.d.ts +3 -0
- package/dist/cli/commands/observe.js +127 -0
- package/dist/cli/commands/project.js +2 -1
- package/dist/cli/commands/record.js +18 -5
- package/dist/cli/commands/verify.js +326 -64
- package/dist/cli/dependencies.d.ts +136 -1
- package/dist/cli/dependencies.js +504 -52
- package/dist/cli/diagnostics-recorder.d.ts +31 -0
- package/dist/cli/diagnostics-recorder.js +82 -0
- package/dist/cli/diff-verification.d.ts +41 -0
- package/dist/cli/diff-verification.js +192 -0
- package/dist/cli/main.d.ts +1 -1
- package/dist/cli/main.js +107 -2
- package/dist/cli/output.d.ts +5 -3
- package/dist/cli/output.js +12 -0
- package/dist/cli/program.js +21 -1
- package/dist/cli/project-root.d.ts +1 -0
- package/dist/cli/project-root.js +6 -0
- package/dist/cli/runtime-selection.d.ts +22 -0
- package/dist/cli/runtime-selection.js +92 -0
- package/dist/cli/version.d.ts +1 -0
- package/dist/cli/version.js +14 -0
- package/dist/cli/workspace-guard.d.ts +2 -0
- package/dist/cli/workspace-guard.js +3 -0
- package/dist/domain/binding-reference.d.ts +4 -0
- package/dist/domain/binding-reference.js +23 -0
- package/dist/domain/checkpoint.d.ts +404 -0
- package/dist/domain/checkpoint.js +297 -0
- package/dist/domain/config.d.ts +107 -1
- package/dist/domain/config.js +52 -6
- package/dist/domain/contract.d.ts +413 -0
- package/dist/domain/contract.js +196 -0
- package/dist/domain/diagnostics.d.ts +1396 -0
- package/dist/domain/diagnostics.js +193 -0
- package/dist/domain/external-flow.d.ts +493 -0
- package/dist/domain/external-flow.js +15 -0
- package/dist/domain/failure-classification.d.ts +176 -0
- package/dist/domain/failure-classification.js +169 -0
- package/dist/domain/failure.d.ts +2 -1
- package/dist/domain/failure.js +123 -0
- package/dist/domain/generation.d.ts +1671 -132
- package/dist/domain/generation.js +202 -13
- package/dist/domain/geometry.d.ts +13 -0
- package/dist/domain/geometry.js +16 -0
- package/dist/domain/impact.d.ts +71 -0
- package/dist/domain/impact.js +76 -0
- package/dist/domain/init.d.ts +12 -4
- package/dist/domain/init.js +24 -18
- package/dist/domain/journey-composition.d.ts +2285 -0
- package/dist/domain/journey-composition.js +85 -0
- package/dist/domain/journey-lifecycle.d.ts +10 -0
- package/dist/domain/journey-lifecycle.js +9 -0
- package/dist/domain/journey.d.ts +2419 -212
- package/dist/domain/journey.js +384 -14
- package/dist/domain/knowledge.d.ts +165 -0
- package/dist/domain/knowledge.js +188 -0
- package/dist/domain/layout.d.ts +34 -5
- package/dist/domain/layout.js +64 -6
- package/dist/domain/locator-evidence.d.ts +3 -0
- package/dist/domain/locator-evidence.js +36 -0
- package/dist/domain/logcat-event.d.ts +54 -0
- package/dist/domain/logcat-event.js +40 -0
- package/dist/domain/observation.d.ts +33 -0
- package/dist/domain/observation.js +18 -0
- package/dist/domain/project-context.d.ts +219 -1
- package/dist/domain/project-context.js +209 -6
- package/dist/domain/proposed-step.d.ts +340 -60
- package/dist/domain/proposed-step.js +23 -3
- package/dist/domain/report.d.ts +1455 -28
- package/dist/domain/report.js +211 -14
- package/dist/domain/runtime-snapshot.d.ts +58 -1
- package/dist/domain/runtime-snapshot.js +14 -3
- package/dist/domain/runtime.d.ts +50 -0
- package/dist/domain/runtime.js +36 -0
- package/dist/domain/system-app-profiles.d.ts +6 -0
- package/dist/domain/system-app-profiles.js +43 -0
- package/dist/domain/ui-backend.d.ts +28 -0
- package/dist/domain/ui-backend.js +33 -0
- package/dist/domain/ui-cache.d.ts +11 -0
- package/dist/domain/ui-cache.js +10 -0
- package/dist/domain/verify-receipt.d.ts +17 -0
- package/dist/domain/verify-receipt.js +17 -0
- package/dist/domain/window-hierarchy.d.ts +84 -0
- package/dist/domain/window-hierarchy.js +172 -0
- package/dist/domain/workflow-manifest.d.ts +77 -0
- package/dist/domain/workflow-manifest.js +136 -0
- package/dist/domain/workspace.d.ts +30 -0
- package/dist/domain/workspace.js +74 -0
- package/dist/ports/adb.d.ts +34 -1
- package/dist/ports/align-prompt.d.ts +11 -0
- package/dist/ports/align-prompt.js +6 -0
- package/dist/ports/anchor-resolver.d.ts +30 -0
- package/dist/ports/anchor-resolver.js +1 -0
- package/dist/ports/annotated-screen-resolver.d.ts +4 -0
- package/dist/ports/annotated-screen-resolver.js +1 -0
- package/dist/ports/camera-probe.d.ts +23 -0
- package/dist/ports/camera-probe.js +8 -0
- package/dist/ports/context-document-writer.d.ts +24 -0
- package/dist/ports/context-document-writer.js +1 -0
- package/dist/ports/detached-process-launcher.d.ts +12 -0
- package/dist/ports/detached-process-launcher.js +1 -0
- package/dist/ports/diagnostics.d.ts +15 -0
- package/dist/ports/diagnostics.js +1 -0
- package/dist/ports/external-flow-registry.d.ts +37 -0
- package/dist/ports/external-flow-registry.js +1 -0
- package/dist/ports/generation-prompt.d.ts +2 -1
- package/dist/ports/generation-session-store.d.ts +12 -2
- package/dist/ports/git-diff.d.ts +9 -0
- package/dist/ports/git-diff.js +1 -0
- package/dist/ports/journey-composition-store.d.ts +17 -0
- package/dist/ports/journey-composition-store.js +1 -0
- package/dist/ports/knowledge-registry.d.ts +20 -0
- package/dist/ports/knowledge-registry.js +1 -0
- package/dist/ports/process-runner.d.ts +7 -0
- package/dist/ports/project-file-inspector.d.ts +1 -0
- package/dist/ports/project-identity-inspector.d.ts +24 -0
- package/dist/ports/project-identity-inspector.js +1 -0
- package/dist/ports/project-inventory-inspector.d.ts +21 -0
- package/dist/ports/project-inventory-inspector.js +1 -0
- package/dist/ports/project-module-discoverer.d.ts +26 -0
- package/dist/ports/project-module-discoverer.js +1 -0
- package/dist/ports/recorder-prompt.d.ts +10 -1
- package/dist/ports/runtime-backend.d.ts +89 -0
- package/dist/ports/runtime-backend.js +13 -0
- package/dist/ports/runtime-capability.d.ts +12 -0
- package/dist/ports/runtime-capability.js +20 -0
- package/dist/ports/runtime-session-ports.d.ts +25 -0
- package/dist/ports/runtime-session-ports.js +1 -0
- package/dist/ports/screenshot.d.ts +11 -0
- package/dist/ports/screenshot.js +1 -0
- package/dist/ports/skill-installer.d.ts +2 -2
- package/dist/ports/ui-snapshot.d.ts +35 -0
- package/dist/ports/ui-snapshot.js +1 -0
- package/dist/ports/ui-stability.d.ts +19 -0
- package/dist/ports/ui-stability.js +1 -0
- package/dist/ports/workspace-layout.d.ts +4 -0
- package/dist/ports/workspace-layout.js +1 -0
- package/dist/shared/errors.d.ts +2 -0
- package/dist/shared/errors.js +22 -0
- package/dist/shared/paths.d.ts +2 -0
- package/dist/shared/paths.js +24 -0
- package/dist/shared/strings.d.ts +2 -0
- package/dist/shared/strings.js +16 -0
- package/package.json +7 -2
- package/assets/skills/taphound-ai-journey/GUIDE.md +0 -862
- package/assets/skills/taphound-ai-journey/SKILL.md +0 -377
- package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +0 -526
- package/assets/skills/taphound-ai-journey/prompts/generate-step.md +0 -109
- package/assets/skills/taphound-ai-journey/schemas/project-context.json +0 -81
- package/assets/skills/taphound-ai-journey/templates/project-context.example.json +0 -49
- package/dist/ports/android-cli.d.ts +0 -21
- /package/dist/{ports/android-cli.js → adapters/runtime/mobile-mcp/mobile-mcp-tools.js} +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { GenerationMetaSchema } from "../../domain/generation.js";
|
|
3
|
+
import { ResolvedJourneyPathSchema } from "../../domain/journey-composition.js";
|
|
4
|
+
import { JourneySchema } from "../../domain/journey.js";
|
|
5
|
+
import { TapHoundReportSchema } from "../../domain/report.js";
|
|
6
|
+
import { GENERATIONS_DIR } from "../../domain/workspace.js";
|
|
7
|
+
export class JourneyPromotionError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
name = "JourneyPromotionError";
|
|
10
|
+
constructor(code, message, options) {
|
|
11
|
+
super(message, options);
|
|
12
|
+
this.code = code;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function sha256(bytes) {
|
|
16
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
17
|
+
}
|
|
18
|
+
function parseJson(bytes) {
|
|
19
|
+
return JSON.parse(bytes.toString("utf8"));
|
|
20
|
+
}
|
|
21
|
+
function sameJson(left, right) {
|
|
22
|
+
return (JSON.stringify(parseJson(left)) === JSON.stringify(parseJson(right)));
|
|
23
|
+
}
|
|
24
|
+
function serialize(value) {
|
|
25
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
26
|
+
}
|
|
27
|
+
const VERIFIED_JOURNEY_EVIDENCE = "verified/journey.json";
|
|
28
|
+
export class JourneyPromoter {
|
|
29
|
+
dependencies;
|
|
30
|
+
constructor(dependencies) {
|
|
31
|
+
this.dependencies = dependencies;
|
|
32
|
+
}
|
|
33
|
+
promote = async (input) => {
|
|
34
|
+
const journeyPath = ResolvedJourneyPathSchema.parse(input.journeyPath);
|
|
35
|
+
const metaPath = `${journeyPath.slice(0, -".json".length)}.meta.json`;
|
|
36
|
+
let journeyBytes;
|
|
37
|
+
try {
|
|
38
|
+
journeyBytes = await this.dependencies.store.read({
|
|
39
|
+
projectRoot: input.projectRoot,
|
|
40
|
+
relativePath: journeyPath
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
throw new JourneyPromotionError("JOURNEY_NOT_FOUND", `Journey is not readable: ${journeyPath}`);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
JourneySchema.parse(parseJson(journeyBytes));
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new JourneyPromotionError("JOURNEY_INVALID", `Journey does not match the Journey schema: ${journeyPath}`, { cause: error });
|
|
51
|
+
}
|
|
52
|
+
const metaBytes = await this.dependencies.store.readJourneyMeta({
|
|
53
|
+
projectRoot: input.projectRoot,
|
|
54
|
+
journeyPath
|
|
55
|
+
});
|
|
56
|
+
if (metaBytes === null) {
|
|
57
|
+
throw new JourneyPromotionError("META_MISSING", `Journey has no generation meta sidecar: ${metaPath}`);
|
|
58
|
+
}
|
|
59
|
+
let meta;
|
|
60
|
+
try {
|
|
61
|
+
meta = GenerationMetaSchema.parse(parseJson(metaBytes));
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
throw new JourneyPromotionError("META_INVALID", `Journey meta sidecar is invalid: ${metaPath}`, { cause: error });
|
|
65
|
+
}
|
|
66
|
+
if (meta.status === "promoted") {
|
|
67
|
+
throw new JourneyPromotionError("JOURNEY_ALREADY_PROMOTED", `Journey is already promoted: ${journeyPath}`);
|
|
68
|
+
}
|
|
69
|
+
const bundleRoot = `${GENERATIONS_DIR}/${meta.generationId}`;
|
|
70
|
+
const reportPath = `${bundleRoot}/${meta.verification.reportPath}`;
|
|
71
|
+
let reportBytes;
|
|
72
|
+
try {
|
|
73
|
+
reportBytes = await this.dependencies.store.read({
|
|
74
|
+
projectRoot: input.projectRoot,
|
|
75
|
+
relativePath: reportPath
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw new JourneyPromotionError("EVIDENCE_MISSING", `Verification evidence is missing: ${reportPath}`);
|
|
80
|
+
}
|
|
81
|
+
if (sha256(reportBytes) !== meta.verification.reportSha256) {
|
|
82
|
+
throw new JourneyPromotionError("EVIDENCE_HASH_MISMATCH", `Verification evidence no longer matches the promoted hash: ${reportPath}`);
|
|
83
|
+
}
|
|
84
|
+
const report = TapHoundReportSchema.parse(parseJson(reportBytes));
|
|
85
|
+
if (report.runId !== meta.verification.runId) {
|
|
86
|
+
throw new JourneyPromotionError("EVIDENCE_HASH_MISMATCH", "Verification evidence run does not match the generation meta");
|
|
87
|
+
}
|
|
88
|
+
let verifiedJourneyBytes;
|
|
89
|
+
try {
|
|
90
|
+
verifiedJourneyBytes = await this.dependencies.store.read({
|
|
91
|
+
projectRoot: input.projectRoot,
|
|
92
|
+
relativePath: `${bundleRoot}/${VERIFIED_JOURNEY_EVIDENCE}`
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
throw new JourneyPromotionError("EVIDENCE_MISSING", `Verified Journey evidence is missing: ${bundleRoot}`);
|
|
97
|
+
}
|
|
98
|
+
if (!sameJson(journeyBytes, verifiedJourneyBytes)) {
|
|
99
|
+
throw new JourneyPromotionError("JOURNEY_MODIFIED", `Journey no longer matches its verified evidence: ${journeyPath}`);
|
|
100
|
+
}
|
|
101
|
+
const promotedAt = input.now.toISOString();
|
|
102
|
+
const promoted = GenerationMetaSchema.parse({
|
|
103
|
+
...meta,
|
|
104
|
+
status: "promoted",
|
|
105
|
+
promotion: {
|
|
106
|
+
promotedAt,
|
|
107
|
+
reason: input.reason
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
await this.dependencies.store.writeText({
|
|
111
|
+
projectRoot: input.projectRoot,
|
|
112
|
+
relativePath: metaPath,
|
|
113
|
+
content: serialize(promoted)
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
status: "promoted",
|
|
117
|
+
journeyPath,
|
|
118
|
+
metaPath,
|
|
119
|
+
generationId: meta.generationId,
|
|
120
|
+
promotedAt
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type JourneyResolutionManifest } from "../../domain/journey-composition.js";
|
|
2
|
+
import { type Journey } from "../../domain/journey.js";
|
|
3
|
+
import type { JourneyCompositionStore } from "../../ports/journey-composition-store.js";
|
|
4
|
+
export type JourneyCompositionErrorCode = "SOURCE_INVALID" | "FLOW_INVALID" | "FLOW_NOT_FOUND" | "FLOW_CYCLE" | "FLOW_DUPLICATE" | "ACTIVITY_BOUNDARY_MISMATCH";
|
|
5
|
+
export declare class JourneyCompositionError extends Error {
|
|
6
|
+
readonly code: JourneyCompositionErrorCode;
|
|
7
|
+
readonly name = "JourneyCompositionError";
|
|
8
|
+
constructor(code: JourneyCompositionErrorCode, message: string, options?: ErrorOptions);
|
|
9
|
+
}
|
|
10
|
+
export interface JourneyResolveInput {
|
|
11
|
+
projectRoot: string;
|
|
12
|
+
sourcePath: string;
|
|
13
|
+
}
|
|
14
|
+
export interface JourneyResolution {
|
|
15
|
+
journey: Journey;
|
|
16
|
+
manifest: JourneyResolutionManifest;
|
|
17
|
+
}
|
|
18
|
+
export interface FlowCatalogEntry {
|
|
19
|
+
name: string;
|
|
20
|
+
path: string;
|
|
21
|
+
status: "valid" | "invalid";
|
|
22
|
+
entryActivity?: string | undefined;
|
|
23
|
+
exitActivity?: string | undefined;
|
|
24
|
+
stepCount?: number | undefined;
|
|
25
|
+
resolutionSha256?: string | undefined;
|
|
26
|
+
failure?: {
|
|
27
|
+
code: string;
|
|
28
|
+
message: string;
|
|
29
|
+
} | undefined;
|
|
30
|
+
}
|
|
31
|
+
export declare class JourneyResolver {
|
|
32
|
+
private readonly store;
|
|
33
|
+
constructor(store: Pick<JourneyCompositionStore, "read" | "listFlowPaths">);
|
|
34
|
+
readonly resolve: (input: JourneyResolveInput) => Promise<JourneyResolution>;
|
|
35
|
+
readonly resolveFlow: (input: {
|
|
36
|
+
projectRoot: string;
|
|
37
|
+
name: string;
|
|
38
|
+
}) => Promise<JourneyResolution>;
|
|
39
|
+
readonly listFlows: (projectRoot: string) => Promise<FlowCatalogEntry[]>;
|
|
40
|
+
private readonly resolveDocument;
|
|
41
|
+
private readonly loadFlow;
|
|
42
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { FlowNameSchema, FlowSchema, JourneySourcePathSchema, JourneyResolutionManifestSchema, JourneySourceSchema } from "../../domain/journey-composition.js";
|
|
3
|
+
import { DEFAULT_DEVICE_ROLE, JourneySchema } from "../../domain/journey.js";
|
|
4
|
+
import { FLOWS_DIR } from "../../domain/workspace.js";
|
|
5
|
+
const MAX_COMPOSITION_BYTES = 1024 * 1024;
|
|
6
|
+
export class JourneyCompositionError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
name = "JourneyCompositionError";
|
|
9
|
+
constructor(code, message, options) {
|
|
10
|
+
super(message, options);
|
|
11
|
+
this.code = code;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function canonicalize(value) {
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return value.map(canonicalize);
|
|
17
|
+
}
|
|
18
|
+
if (value !== null && typeof value === "object") {
|
|
19
|
+
return Object.fromEntries(Object.entries(value)
|
|
20
|
+
.filter(([, item]) => item !== undefined)
|
|
21
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
22
|
+
.map(([key, item]) => [key, canonicalize(item)]));
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
function sha256(value) {
|
|
27
|
+
return createHash("sha256").update(value).digest("hex");
|
|
28
|
+
}
|
|
29
|
+
function hashCanonical(value) {
|
|
30
|
+
return sha256(JSON.stringify(canonicalize(value)));
|
|
31
|
+
}
|
|
32
|
+
function parseJson(bytes, label) {
|
|
33
|
+
if (bytes.byteLength > MAX_COMPOSITION_BYTES) {
|
|
34
|
+
throw new JourneyCompositionError(label === "Journey source" ? "SOURCE_INVALID" : "FLOW_INVALID", `${label} exceeds ${String(MAX_COMPOSITION_BYTES)} bytes`);
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(bytes.toString("utf8"));
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
throw new JourneyCompositionError(label === "Journey source" ? "SOURCE_INVALID" : "FLOW_INVALID", `${label} is not valid JSON`, { cause: error });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function flowPath(name) {
|
|
44
|
+
return `${FLOWS_DIR}/${FlowNameSchema.parse(name)}.json`;
|
|
45
|
+
}
|
|
46
|
+
function normalizedSourcePath(path) {
|
|
47
|
+
try {
|
|
48
|
+
return JourneySourcePathSchema.parse(path);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new JourneyCompositionError("SOURCE_INVALID", "Journey source must be a normalized JSON file under .taphound/sources", { cause: error });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function assertActivityBoundaries(steps) {
|
|
55
|
+
for (let index = 1; index < steps.length; index += 1) {
|
|
56
|
+
const previous = steps[index - 1];
|
|
57
|
+
const current = steps[index];
|
|
58
|
+
if (previous !== undefined
|
|
59
|
+
&& current !== undefined
|
|
60
|
+
&& previous.activity.after !== current.activity.before) {
|
|
61
|
+
throw new JourneyCompositionError("ACTIVITY_BOUNDARY_MISMATCH", `Step ${String(index - 1)} ends at ${previous.activity.after}, but step ${String(index)} starts at ${current.activity.before}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function errorDetails(error) {
|
|
66
|
+
if (error instanceof JourneyCompositionError) {
|
|
67
|
+
return { code: error.code, message: error.message };
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
code: "FLOW_INVALID",
|
|
71
|
+
message: error instanceof Error ? error.message : String(error)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export class JourneyResolver {
|
|
75
|
+
store;
|
|
76
|
+
constructor(store) {
|
|
77
|
+
this.store = store;
|
|
78
|
+
}
|
|
79
|
+
resolve = async (input) => {
|
|
80
|
+
const sourcePath = normalizedSourcePath(input.sourcePath);
|
|
81
|
+
let sourceBytes;
|
|
82
|
+
try {
|
|
83
|
+
sourceBytes = await this.store.read({
|
|
84
|
+
projectRoot: input.projectRoot,
|
|
85
|
+
relativePath: sourcePath
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
throw new JourneyCompositionError("SOURCE_INVALID", `Unable to read Journey source ${sourcePath}`, { cause: error });
|
|
90
|
+
}
|
|
91
|
+
let source;
|
|
92
|
+
try {
|
|
93
|
+
source = JourneySourceSchema.parse(parseJson(sourceBytes, "Journey source"));
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
throw error instanceof JourneyCompositionError
|
|
97
|
+
? error
|
|
98
|
+
: new JourneyCompositionError("SOURCE_INVALID", `Journey source ${sourcePath} is invalid`, { cause: error });
|
|
99
|
+
}
|
|
100
|
+
return this.resolveDocument(input.projectRoot, sourcePath, sourceBytes, source);
|
|
101
|
+
};
|
|
102
|
+
resolveFlow = async (input) => {
|
|
103
|
+
const name = FlowNameSchema.parse(input.name);
|
|
104
|
+
const path = flowPath(name);
|
|
105
|
+
const loaded = await this.loadFlow(input.projectRoot, name);
|
|
106
|
+
const source = {
|
|
107
|
+
version: 1,
|
|
108
|
+
kind: "journeySource",
|
|
109
|
+
name,
|
|
110
|
+
includes: loaded.flow.includes,
|
|
111
|
+
steps: loaded.flow.steps
|
|
112
|
+
};
|
|
113
|
+
return this.resolveDocument(input.projectRoot, path, loaded.bytes, source, name);
|
|
114
|
+
};
|
|
115
|
+
listFlows = async (projectRoot) => {
|
|
116
|
+
const paths = [...await this.store.listFlowPaths(projectRoot)].sort();
|
|
117
|
+
const entries = [];
|
|
118
|
+
for (const path of paths) {
|
|
119
|
+
try {
|
|
120
|
+
const bytes = await this.store.read({ projectRoot, relativePath: path });
|
|
121
|
+
const flow = FlowSchema.parse(parseJson(bytes, "Flow"));
|
|
122
|
+
const expectedPath = flowPath(flow.name);
|
|
123
|
+
if (path !== expectedPath) {
|
|
124
|
+
throw new JourneyCompositionError("FLOW_INVALID", `Flow ${flow.name} must be stored at ${expectedPath}`);
|
|
125
|
+
}
|
|
126
|
+
const resolution = await this.resolveFlow({
|
|
127
|
+
projectRoot,
|
|
128
|
+
name: flow.name
|
|
129
|
+
});
|
|
130
|
+
entries.push({
|
|
131
|
+
name: flow.name,
|
|
132
|
+
path,
|
|
133
|
+
status: "valid",
|
|
134
|
+
entryActivity: resolution.journey.steps[0]?.activity.before,
|
|
135
|
+
exitActivity: resolution.journey.steps.at(-1)?.activity.after,
|
|
136
|
+
stepCount: resolution.journey.steps.length,
|
|
137
|
+
resolutionSha256: resolution.manifest.resolutionSha256
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
entries.push({
|
|
142
|
+
name: path.slice(`${FLOWS_DIR}/`.length, -".json".length),
|
|
143
|
+
path,
|
|
144
|
+
status: "invalid",
|
|
145
|
+
failure: errorDetails(error)
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return entries;
|
|
150
|
+
};
|
|
151
|
+
resolveDocument = async (projectRoot, sourcePath, sourceBytes, source, rootFlowName) => {
|
|
152
|
+
const steps = [];
|
|
153
|
+
const dependencies = [];
|
|
154
|
+
const expanded = new Set();
|
|
155
|
+
const stack = [];
|
|
156
|
+
const expand = async (name) => {
|
|
157
|
+
if (stack.includes(name)) {
|
|
158
|
+
throw new JourneyCompositionError("FLOW_CYCLE", `Flow cycle detected: ${[...stack, name].join(" -> ")}`);
|
|
159
|
+
}
|
|
160
|
+
if (expanded.has(name)) {
|
|
161
|
+
throw new JourneyCompositionError("FLOW_DUPLICATE", `Flow ${name} is included more than once`);
|
|
162
|
+
}
|
|
163
|
+
stack.push(name);
|
|
164
|
+
const loaded = await this.loadFlow(projectRoot, name);
|
|
165
|
+
for (const dependency of loaded.flow.includes) {
|
|
166
|
+
await expand(dependency);
|
|
167
|
+
}
|
|
168
|
+
stack.pop();
|
|
169
|
+
expanded.add(name);
|
|
170
|
+
steps.push(...loaded.flow.steps);
|
|
171
|
+
dependencies.push({
|
|
172
|
+
name,
|
|
173
|
+
path: loaded.path,
|
|
174
|
+
sha256: sha256(loaded.bytes),
|
|
175
|
+
stepCount: loaded.flow.steps.length
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
if (rootFlowName === undefined) {
|
|
179
|
+
for (const name of source.includes) {
|
|
180
|
+
await expand(name);
|
|
181
|
+
}
|
|
182
|
+
steps.push(...source.steps);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
await expand(rootFlowName);
|
|
186
|
+
}
|
|
187
|
+
assertActivityBoundaries(steps);
|
|
188
|
+
const journey = JourneySchema.parse({
|
|
189
|
+
version: 2,
|
|
190
|
+
name: source.name,
|
|
191
|
+
devices: [{ role: DEFAULT_DEVICE_ROLE }],
|
|
192
|
+
steps
|
|
193
|
+
});
|
|
194
|
+
const unsignedManifest = {
|
|
195
|
+
version: 1,
|
|
196
|
+
source: {
|
|
197
|
+
path: sourcePath,
|
|
198
|
+
sha256: sha256(sourceBytes)
|
|
199
|
+
},
|
|
200
|
+
flows: dependencies,
|
|
201
|
+
expansion: dependencies.map((dependency) => dependency.name),
|
|
202
|
+
journey: {
|
|
203
|
+
name: journey.name,
|
|
204
|
+
sha256: hashCanonical(journey),
|
|
205
|
+
stepCount: journey.steps.length
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
return {
|
|
209
|
+
journey,
|
|
210
|
+
manifest: JourneyResolutionManifestSchema.parse({
|
|
211
|
+
...unsignedManifest,
|
|
212
|
+
resolutionSha256: hashCanonical(unsignedManifest)
|
|
213
|
+
})
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
loadFlow = async (projectRoot, name) => {
|
|
217
|
+
const path = flowPath(name);
|
|
218
|
+
let bytes;
|
|
219
|
+
try {
|
|
220
|
+
bytes = await this.store.read({ projectRoot, relativePath: path });
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
throw new JourneyCompositionError("FLOW_NOT_FOUND", `Unable to read Flow ${name} at ${path}`, { cause: error });
|
|
224
|
+
}
|
|
225
|
+
let flow;
|
|
226
|
+
try {
|
|
227
|
+
flow = FlowSchema.parse(parseJson(bytes, "Flow"));
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
throw error instanceof JourneyCompositionError
|
|
231
|
+
? error
|
|
232
|
+
: new JourneyCompositionError("FLOW_INVALID", `Flow ${name} is invalid`, { cause: error });
|
|
233
|
+
}
|
|
234
|
+
if (flow.name !== name) {
|
|
235
|
+
throw new JourneyCompositionError("FLOW_INVALID", `Flow at ${path} declares name ${flow.name}`);
|
|
236
|
+
}
|
|
237
|
+
return { path, bytes, flow };
|
|
238
|
+
};
|
|
239
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { JourneyCompositionStore } from "../../ports/journey-composition-store.js";
|
|
2
|
+
export type JourneyRetireErrorCode = "JOURNEY_NOT_FOUND" | "JOURNEY_INVALID" | "META_MISSING" | "META_INVALID" | "JOURNEY_ALREADY_RETIRED";
|
|
3
|
+
export declare class JourneyRetireError extends Error {
|
|
4
|
+
readonly code: JourneyRetireErrorCode;
|
|
5
|
+
readonly name = "JourneyRetireError";
|
|
6
|
+
constructor(code: JourneyRetireErrorCode, message: string, options?: ErrorOptions);
|
|
7
|
+
}
|
|
8
|
+
export interface JourneyRetireResult {
|
|
9
|
+
status: "retired";
|
|
10
|
+
journeyPath: string;
|
|
11
|
+
metaPath: string;
|
|
12
|
+
retiredAt: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class JourneyRetirer {
|
|
15
|
+
private readonly dependencies;
|
|
16
|
+
constructor(dependencies: {
|
|
17
|
+
store: Pick<JourneyCompositionStore, "read" | "readJourneyMeta" | "writeText">;
|
|
18
|
+
});
|
|
19
|
+
readonly retire: (input: {
|
|
20
|
+
projectRoot: string;
|
|
21
|
+
journeyPath: string;
|
|
22
|
+
reason: string;
|
|
23
|
+
now: Date;
|
|
24
|
+
}) => Promise<JourneyRetireResult>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { GenerationMetaSchema } from "../../domain/generation.js";
|
|
2
|
+
import { ResolvedJourneyPathSchema } from "../../domain/journey-composition.js";
|
|
3
|
+
import { JourneySchema } from "../../domain/journey.js";
|
|
4
|
+
export class JourneyRetireError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
name = "JourneyRetireError";
|
|
7
|
+
constructor(code, message, options) {
|
|
8
|
+
super(message, options);
|
|
9
|
+
this.code = code;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function parseJson(bytes) {
|
|
13
|
+
return JSON.parse(bytes.toString("utf8"));
|
|
14
|
+
}
|
|
15
|
+
function serialize(value) {
|
|
16
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
17
|
+
}
|
|
18
|
+
export class JourneyRetirer {
|
|
19
|
+
dependencies;
|
|
20
|
+
constructor(dependencies) {
|
|
21
|
+
this.dependencies = dependencies;
|
|
22
|
+
}
|
|
23
|
+
retire = async (input) => {
|
|
24
|
+
const journeyPath = ResolvedJourneyPathSchema.parse(input.journeyPath);
|
|
25
|
+
const metaPath = `${journeyPath.slice(0, -".json".length)}.meta.json`;
|
|
26
|
+
let journeyBytes;
|
|
27
|
+
try {
|
|
28
|
+
journeyBytes = await this.dependencies.store.read({
|
|
29
|
+
projectRoot: input.projectRoot,
|
|
30
|
+
relativePath: journeyPath
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw new JourneyRetireError("JOURNEY_NOT_FOUND", `Journey is not readable: ${journeyPath}`);
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
JourneySchema.parse(parseJson(journeyBytes));
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
throw new JourneyRetireError("JOURNEY_INVALID", `Journey does not match the Journey schema: ${journeyPath}`, { cause: error });
|
|
41
|
+
}
|
|
42
|
+
const metaBytes = await this.dependencies.store.readJourneyMeta({
|
|
43
|
+
projectRoot: input.projectRoot,
|
|
44
|
+
journeyPath
|
|
45
|
+
});
|
|
46
|
+
if (metaBytes === null) {
|
|
47
|
+
throw new JourneyRetireError("META_MISSING", `Journey has no generation meta sidecar: ${metaPath}`);
|
|
48
|
+
}
|
|
49
|
+
let meta;
|
|
50
|
+
try {
|
|
51
|
+
meta = GenerationMetaSchema.parse(parseJson(metaBytes));
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
throw new JourneyRetireError("META_INVALID", `Journey meta sidecar is invalid: ${metaPath}`, { cause: error });
|
|
55
|
+
}
|
|
56
|
+
if (meta.retired !== undefined) {
|
|
57
|
+
throw new JourneyRetireError("JOURNEY_ALREADY_RETIRED", `Journey is already retired: ${journeyPath}`);
|
|
58
|
+
}
|
|
59
|
+
const retiredAt = input.now.toISOString();
|
|
60
|
+
const updated = {
|
|
61
|
+
...meta,
|
|
62
|
+
retired: {
|
|
63
|
+
retiredAt,
|
|
64
|
+
reason: input.reason.trim()
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
await this.dependencies.store.writeText({
|
|
68
|
+
projectRoot: input.projectRoot,
|
|
69
|
+
relativePath: metaPath,
|
|
70
|
+
content: serialize(updated)
|
|
71
|
+
});
|
|
72
|
+
const written = await this.dependencies.store.readJourneyMeta({
|
|
73
|
+
projectRoot: input.projectRoot,
|
|
74
|
+
journeyPath
|
|
75
|
+
});
|
|
76
|
+
if (written === null) {
|
|
77
|
+
throw new JourneyRetireError("META_INVALID", `Retired meta sidecar is not readable: ${metaPath}`);
|
|
78
|
+
}
|
|
79
|
+
GenerationMetaSchema.parse(parseJson(written));
|
|
80
|
+
return {
|
|
81
|
+
status: "retired",
|
|
82
|
+
journeyPath,
|
|
83
|
+
metaPath,
|
|
84
|
+
retiredAt
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DisplayViewport } from "../../domain/geometry.js";
|
|
2
|
+
import type { LayoutElement } from "../../domain/layout.js";
|
|
3
|
+
import type { AnchorResolution, AnchorResolverPort } from "../../ports/anchor-resolver.js";
|
|
4
|
+
import type { KnowledgeRegistryPort } from "../../ports/knowledge-registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Semantic Anchor resolver.
|
|
7
|
+
*
|
|
8
|
+
* An Anchor is a Semantic UI Reference: an ordered candidate chain
|
|
9
|
+
* (composeSemantics → resourceId → contentDescription → visibleText →
|
|
10
|
+
* visualMatch). Resolution walks the chain deterministically:
|
|
11
|
+
*
|
|
12
|
+
* - the first candidate that resolves **uniquely** wins; the win is recorded
|
|
13
|
+
* as `primary` (first candidate) or `fallback` (a later candidate), so a
|
|
14
|
+
* fallback resolution is visible in the report rather than silent;
|
|
15
|
+
* - an **ambiguous** match at any candidate fails closed (matching the fixed
|
|
16
|
+
* locator-priority rule: never select heuristically);
|
|
17
|
+
* - `visualMatch` is never resolved by Core: when nothing before it matched,
|
|
18
|
+
* the resolution is `visualOnly` and the run fails closed —
|
|
19
|
+
* `RUNTIME_CAPABILITY_MISSING` for the caller — leaving the visual layer to
|
|
20
|
+
* an external multimodal reviewer under the Escalation Policy.
|
|
21
|
+
*
|
|
22
|
+
* The legacy single-locator identity remains supported as the primary
|
|
23
|
+
* candidate when no explicit `candidates` chain is present.
|
|
24
|
+
*/
|
|
25
|
+
export declare class KnowledgeAnchorResolver implements AnchorResolverPort {
|
|
26
|
+
private readonly registry;
|
|
27
|
+
private readonly projectRoot;
|
|
28
|
+
constructor(registry: Pick<KnowledgeRegistryPort, "load">, projectRoot: string);
|
|
29
|
+
readonly resolve: (input: {
|
|
30
|
+
anchorId: string;
|
|
31
|
+
layout: readonly LayoutElement[];
|
|
32
|
+
viewport?: DisplayViewport | undefined;
|
|
33
|
+
signal?: AbortSignal | undefined;
|
|
34
|
+
}) => Promise<AnchorResolution>;
|
|
35
|
+
private readonly found;
|
|
36
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { resolveLocator } from "../locator/locator-resolver.js";
|
|
2
|
+
function candidateKindForLocator(locator) {
|
|
3
|
+
if (locator.resourceId !== undefined) {
|
|
4
|
+
return "resourceId";
|
|
5
|
+
}
|
|
6
|
+
if (locator.contentDescription !== undefined) {
|
|
7
|
+
return "contentDescription";
|
|
8
|
+
}
|
|
9
|
+
if (locator.text !== undefined) {
|
|
10
|
+
return "visibleText";
|
|
11
|
+
}
|
|
12
|
+
return "resourceId";
|
|
13
|
+
}
|
|
14
|
+
function resolveCandidate(layout, candidate, viewport) {
|
|
15
|
+
return resolveLocator(layout, candidate.locator, {
|
|
16
|
+
requireEnabled: true,
|
|
17
|
+
...(viewport === undefined ? {} : { viewport })
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Semantic Anchor resolver.
|
|
22
|
+
*
|
|
23
|
+
* An Anchor is a Semantic UI Reference: an ordered candidate chain
|
|
24
|
+
* (composeSemantics → resourceId → contentDescription → visibleText →
|
|
25
|
+
* visualMatch). Resolution walks the chain deterministically:
|
|
26
|
+
*
|
|
27
|
+
* - the first candidate that resolves **uniquely** wins; the win is recorded
|
|
28
|
+
* as `primary` (first candidate) or `fallback` (a later candidate), so a
|
|
29
|
+
* fallback resolution is visible in the report rather than silent;
|
|
30
|
+
* - an **ambiguous** match at any candidate fails closed (matching the fixed
|
|
31
|
+
* locator-priority rule: never select heuristically);
|
|
32
|
+
* - `visualMatch` is never resolved by Core: when nothing before it matched,
|
|
33
|
+
* the resolution is `visualOnly` and the run fails closed —
|
|
34
|
+
* `RUNTIME_CAPABILITY_MISSING` for the caller — leaving the visual layer to
|
|
35
|
+
* an external multimodal reviewer under the Escalation Policy.
|
|
36
|
+
*
|
|
37
|
+
* The legacy single-locator identity remains supported as the primary
|
|
38
|
+
* candidate when no explicit `candidates` chain is present.
|
|
39
|
+
*/
|
|
40
|
+
export class KnowledgeAnchorResolver {
|
|
41
|
+
registry;
|
|
42
|
+
projectRoot;
|
|
43
|
+
constructor(registry, projectRoot) {
|
|
44
|
+
this.registry = registry;
|
|
45
|
+
this.projectRoot = projectRoot;
|
|
46
|
+
}
|
|
47
|
+
resolve = async (input) => {
|
|
48
|
+
void input.signal;
|
|
49
|
+
const bundle = await this.registry.load(this.projectRoot);
|
|
50
|
+
const anchor = bundle.anchors.find((candidate) => candidate.id === input.anchorId);
|
|
51
|
+
if (anchor === undefined) {
|
|
52
|
+
return {
|
|
53
|
+
status: "notFound",
|
|
54
|
+
message: `Knowledge anchor ${input.anchorId} is not defined in ${this.projectRoot}`
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (anchor.identity.kind !== "element") {
|
|
58
|
+
return {
|
|
59
|
+
status: "notFound",
|
|
60
|
+
message: `Knowledge anchor ${input.anchorId} does not carry an element locator identity`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const candidates = anchor.candidates ?? [];
|
|
64
|
+
const chain = candidates.length > 0
|
|
65
|
+
? candidates.map((candidate) => ({ kind: candidate.kind, candidate }))
|
|
66
|
+
: [{
|
|
67
|
+
kind: candidateKindForLocator(anchor.identity.locator),
|
|
68
|
+
candidate: {
|
|
69
|
+
kind: candidateKindForLocator(anchor.identity.locator),
|
|
70
|
+
locator: anchor.identity.locator
|
|
71
|
+
}
|
|
72
|
+
}];
|
|
73
|
+
for (const [index, entry] of chain.entries()) {
|
|
74
|
+
if (entry.kind === "visualMatch") {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const resolution = resolveCandidate(input.layout, entry.candidate, input.viewport);
|
|
78
|
+
if (resolution.status !== "found") {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
return this.found(entry.kind, index === 0, resolution);
|
|
82
|
+
}
|
|
83
|
+
const hasVisual = chain.some((entry) => entry.kind === "visualMatch");
|
|
84
|
+
if (hasVisual) {
|
|
85
|
+
return {
|
|
86
|
+
status: "visualOnly",
|
|
87
|
+
message: `Knowledge anchor ${input.anchorId} resolves only via visualMatch, which Core never performs`
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
status: "notFound",
|
|
92
|
+
message: `Knowledge anchor ${input.anchorId} did not resolve through any candidate`
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
found = (kind, primary, resolution) => {
|
|
96
|
+
if (resolution.element.bounds === undefined) {
|
|
97
|
+
return {
|
|
98
|
+
status: "ambiguous",
|
|
99
|
+
message: `Knowledge anchor resolved via ${kind} to an element without bounds`
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
status: "found",
|
|
104
|
+
point: resolution.point,
|
|
105
|
+
bounds: resolution.element.bounds,
|
|
106
|
+
element: resolution.element,
|
|
107
|
+
resolvedBy: { kind, confidence: primary ? "primary" : "fallback" }
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { KnowledgeRegistryPort, LoadedKnowledgeBundle } from "../../ports/knowledge-registry.js";
|
|
2
|
+
export type KnowledgeLoadErrorCode = "KNOWLEDGE_INVALID" | "KNOWLEDGE_STALE" | "KNOWLEDGE_NOT_FOUND";
|
|
3
|
+
export declare class KnowledgeLoadError extends Error {
|
|
4
|
+
readonly code: KnowledgeLoadErrorCode;
|
|
5
|
+
readonly name = "KnowledgeLoadError";
|
|
6
|
+
constructor(code: KnowledgeLoadErrorCode, message: string, options?: ErrorOptions);
|
|
7
|
+
}
|
|
8
|
+
export declare class KnowledgeLoader {
|
|
9
|
+
private readonly registry;
|
|
10
|
+
constructor(registry: Pick<KnowledgeRegistryPort, "load">);
|
|
11
|
+
readonly load: (input: {
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
packageName?: string | undefined;
|
|
14
|
+
expectedKnowledgeHash?: string | undefined;
|
|
15
|
+
}) => Promise<LoadedKnowledgeBundle>;
|
|
16
|
+
}
|