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.
Files changed (467) hide show
  1. package/README.md +49 -149
  2. package/README.zh-CN.md +47 -147
  3. package/assets/skills/taphound-case-suite/SKILL.md +363 -0
  4. package/assets/skills/taphound-case-suite/schemas/base-flow-record.schema.json +22 -0
  5. package/assets/skills/taphound-case-suite/schemas/case-catalog.schema.json +26 -0
  6. package/assets/skills/taphound-case-suite/schemas/case-ledger.schema.json +158 -0
  7. package/assets/skills/taphound-case-suite/schemas/suite-input.schema.json +41 -0
  8. package/assets/skills/taphound-case-suite/schemas/transition.schema.json +61 -0
  9. package/assets/skills/taphound-case-suite/scripts/ledger.mjs +1207 -0
  10. package/assets/skills/taphound-case-suite/templates/base-flow-record.example.json +20 -0
  11. package/assets/skills/taphound-case-suite/templates/suite-input.example.json +27 -0
  12. package/assets/skills/taphound-case-suite/templates/transition.example.json +8 -0
  13. package/assets/skills/taphound-flash/SKILL.md +115 -0
  14. package/assets/skills/taphound-flash/scripts/flash.mjs +585 -0
  15. package/assets/skills/taphound-flash/templates/flash-plan.example.json +15 -0
  16. package/assets/skills/taphound-journey-brief-author/CONTEXT-GUIDE.md +471 -0
  17. package/assets/skills/taphound-journey-brief-author/SKILL.md +494 -0
  18. package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.md +96 -0
  19. package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.zh-CN.md +80 -0
  20. package/assets/skills/taphound-journey-brief-author/prompts/context-analyze-project.md +309 -0
  21. package/assets/skills/taphound-journey-brief-author/schemas/context-refresh-result.json +123 -0
  22. package/assets/skills/taphound-journey-brief-author/schemas/project-context-module.json +192 -0
  23. package/assets/skills/taphound-journey-brief-author/schemas/project-context.json +152 -0
  24. package/assets/skills/taphound-journey-brief-author/templates/project-context-module.example.json +58 -0
  25. package/assets/skills/taphound-journey-brief-author/templates/project-context.example.json +51 -0
  26. package/assets/skills/taphound-journey-brief-author/templates/taphound-journey-brief.template.md +63 -0
  27. package/assets/skills/taphound-journey-generator/SKILL.md +507 -0
  28. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/prompts/check-completion.md +3 -0
  29. package/assets/skills/taphound-journey-generator/prompts/consume-journey-brief.md +52 -0
  30. package/assets/skills/taphound-journey-generator/prompts/generate-step.md +211 -0
  31. package/assets/skills/taphound-journey-generator/prompts/select-flow.md +91 -0
  32. package/assets/skills/taphound-journey-generator/references/bridge.md +35 -0
  33. package/assets/skills/taphound-journey-generator/references/mid-session.md +44 -0
  34. package/assets/skills/taphound-journey-generator/schemas/external-flow.json +37 -0
  35. package/assets/skills/taphound-journey-generator/schemas/flow.json +28 -0
  36. package/assets/skills/taphound-journey-generator/schemas/journey-source.json +25 -0
  37. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/observe-output.json +13 -4
  38. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/proposed-step-envelope.json +127 -5
  39. package/assets/skills/taphound-journey-generator/scripts/envelope.mjs +578 -0
  40. package/assets/skills/taphound-journey-generator/templates/external-flow.example.json +22 -0
  41. package/assets/skills/taphound-journey-generator/templates/flow.example.json +22 -0
  42. package/assets/skills/taphound-journey-generator/templates/journey-source.example.json +17 -0
  43. package/assets/skills/taphound-journey-generator/templates/taphound-journey-brief.example.md +70 -0
  44. package/assets/skills/taphound-verify-change/SKILL.md +91 -0
  45. package/assets/skills/taphound-verify-change/references/accept.md +38 -0
  46. package/assets/skills/taphound-verify-change/references/preserve.md +357 -0
  47. package/assets/skills/taphound-verify-change/schemas/workflow-manifest.schema.json +295 -0
  48. package/assets/skills/taphound-verify-change/scripts/handoff.mjs +482 -0
  49. package/assets/skills/taphound-verify-change/scripts/ui-refactor.mjs +505 -0
  50. package/dist/adapters/adb/adb-adapter.d.ts +16 -2
  51. package/dist/adapters/adb/adb-adapter.js +199 -0
  52. package/dist/adapters/adb/system-uiautomator-snapshot-provider.d.ts +13 -0
  53. package/dist/adapters/adb/system-uiautomator-snapshot-provider.js +122 -0
  54. package/dist/adapters/adb/ui-automator-parser.d.ts +3 -0
  55. package/dist/adapters/adb/ui-automator-parser.js +160 -0
  56. package/dist/adapters/adb/window-topology-parser.d.ts +2 -0
  57. package/dist/adapters/adb/window-topology-parser.js +101 -0
  58. package/dist/adapters/android-cli/android-cli-adapter.d.ts +16 -8
  59. package/dist/adapters/android-cli/android-cli-adapter.js +137 -15
  60. package/dist/adapters/android-cli/android-cli-snapshot-provider.d.ts +9 -0
  61. package/dist/adapters/android-cli/android-cli-snapshot-provider.js +99 -0
  62. package/dist/adapters/android-cli/layout-parser.js +12 -8
  63. package/dist/adapters/appium/appium-doctor.d.ts +7 -0
  64. package/dist/adapters/appium/appium-doctor.js +128 -0
  65. package/dist/adapters/appium/appium-ui-snapshot-provider.d.ts +42 -0
  66. package/dist/adapters/appium/appium-ui-snapshot-provider.js +309 -0
  67. package/dist/adapters/camera/camera-probe-adapter.d.ts +18 -0
  68. package/dist/adapters/camera/camera-probe-adapter.js +286 -0
  69. package/dist/adapters/filesystem/artifact-store.js +40 -1
  70. package/dist/adapters/filesystem/context-document-writer.d.ts +15 -0
  71. package/dist/adapters/filesystem/context-document-writer.js +112 -0
  72. package/dist/adapters/filesystem/diagnostics-journal.d.ts +17 -0
  73. package/dist/adapters/filesystem/diagnostics-journal.js +130 -0
  74. package/dist/adapters/filesystem/external-flow-registry.d.ts +17 -0
  75. package/dist/adapters/filesystem/external-flow-registry.js +325 -0
  76. package/dist/adapters/filesystem/generation-session-store.d.ts +17 -11
  77. package/dist/adapters/filesystem/generation-session-store.js +281 -855
  78. package/dist/adapters/filesystem/generation-store/session-lock.d.ts +29 -0
  79. package/dist/adapters/filesystem/generation-store/session-lock.js +202 -0
  80. package/dist/adapters/filesystem/generation-store/session-transitions.d.ts +24 -0
  81. package/dist/adapters/filesystem/generation-store/session-transitions.js +340 -0
  82. package/dist/adapters/filesystem/generation-store/store-files.d.ts +50 -0
  83. package/dist/adapters/filesystem/generation-store/store-files.js +363 -0
  84. package/dist/adapters/filesystem/journey-composition-store.d.ts +23 -0
  85. package/dist/adapters/filesystem/journey-composition-store.js +151 -0
  86. package/dist/adapters/filesystem/knowledge-registry.d.ts +12 -0
  87. package/dist/adapters/filesystem/knowledge-registry.js +223 -0
  88. package/dist/adapters/filesystem/project-bound-file.js +3 -5
  89. package/dist/adapters/filesystem/project-file-inspector.d.ts +2 -0
  90. package/dist/adapters/filesystem/project-file-inspector.js +57 -28
  91. package/dist/adapters/filesystem/project-identity-inspector.d.ts +7 -0
  92. package/dist/adapters/filesystem/project-identity-inspector.js +215 -0
  93. package/dist/adapters/filesystem/project-inventory-inspector.d.ts +8 -0
  94. package/dist/adapters/filesystem/project-inventory-inspector.js +93 -0
  95. package/dist/adapters/filesystem/project-module-discoverer.d.ts +6 -0
  96. package/dist/adapters/filesystem/project-module-discoverer.js +158 -0
  97. package/dist/adapters/filesystem/skill-installer.d.ts +7 -2
  98. package/dist/adapters/filesystem/skill-installer.js +40 -11
  99. package/dist/adapters/filesystem/workspace-layout.d.ts +7 -0
  100. package/dist/adapters/filesystem/workspace-layout.js +57 -0
  101. package/dist/adapters/git/node-git-diff.d.ts +17 -0
  102. package/dist/adapters/git/node-git-diff.js +114 -0
  103. package/dist/adapters/process/node-detached-process-launcher.d.ts +6 -0
  104. package/dist/adapters/process/node-detached-process-launcher.js +96 -0
  105. package/dist/adapters/process/node-process-runner.js +11 -2
  106. package/dist/adapters/prompt/inquirer-align-prompt.d.ts +27 -0
  107. package/dist/adapters/prompt/inquirer-align-prompt.js +65 -0
  108. package/dist/adapters/prompt/inquirer-generation-prompt.js +3 -0
  109. package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +9 -1
  110. package/dist/adapters/prompt/inquirer-recorder-prompt.js +63 -0
  111. package/dist/adapters/runtime/adb-runtime-backend.d.ts +67 -0
  112. package/dist/adapters/runtime/adb-runtime-backend.js +206 -0
  113. package/dist/adapters/runtime/fake-runtime-backend.d.ts +87 -0
  114. package/dist/adapters/runtime/fake-runtime-backend.js +240 -0
  115. package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.d.ts +45 -0
  116. package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.js +153 -0
  117. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.d.ts +23 -0
  118. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.js +39 -0
  119. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.d.ts +20 -0
  120. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.js +180 -0
  121. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.d.ts +78 -0
  122. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.js +379 -0
  123. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-tools.d.ts +46 -0
  124. package/dist/adapters/runtime/session-adb-view.d.ts +53 -0
  125. package/dist/adapters/runtime/session-adb-view.js +193 -0
  126. package/dist/adapters/runtime/session-backed-ports.d.ts +21 -0
  127. package/dist/adapters/runtime/session-backed-ports.js +43 -0
  128. package/dist/adapters/runtime/shared-session-runtime-backend.d.ts +12 -0
  129. package/dist/adapters/runtime/shared-session-runtime-backend.js +64 -0
  130. package/dist/adapters/ui/auto-ui-snapshot-provider.d.ts +8 -0
  131. package/dist/adapters/ui/auto-ui-snapshot-provider.js +48 -0
  132. package/dist/adapters/ui/device-layout-temp.d.ts +6 -0
  133. package/dist/adapters/ui/device-layout-temp.js +18 -0
  134. package/dist/adapters/ui/device-ui-environment.d.ts +13 -0
  135. package/dist/adapters/ui/device-ui-environment.js +81 -0
  136. package/dist/adapters/ui/layout-normalization.d.ts +9 -0
  137. package/dist/adapters/ui/layout-normalization.js +44 -0
  138. package/dist/adapters/ui/ui-snapshot-error.d.ts +11 -0
  139. package/dist/adapters/ui/ui-snapshot-error.js +12 -0
  140. package/dist/adapters/ui/ui-snapshot-support.d.ts +15 -0
  141. package/dist/adapters/ui/ui-snapshot-support.js +12 -0
  142. package/dist/application/align/align-service.d.ts +42 -0
  143. package/dist/application/align/align-service.js +106 -0
  144. package/dist/application/assertion/expectation-evaluator.d.ts +18 -5
  145. package/dist/application/assertion/expectation-evaluator.js +156 -18
  146. package/dist/application/assertion/guarded-expectation.d.ts +45 -0
  147. package/dist/application/assertion/guarded-expectation.js +74 -0
  148. package/dist/application/checkpoint/baseline-capturer.d.ts +26 -0
  149. package/dist/application/checkpoint/baseline-capturer.js +163 -0
  150. package/dist/application/checkpoint/baseline-error.d.ts +5 -0
  151. package/dist/application/checkpoint/baseline-error.js +8 -0
  152. package/dist/application/checkpoint/baseline-service.d.ts +31 -0
  153. package/dist/application/checkpoint/baseline-service.js +147 -0
  154. package/dist/application/checkpoint/checkpoint-evaluator.d.ts +35 -0
  155. package/dist/application/checkpoint/checkpoint-evaluator.js +415 -0
  156. package/dist/application/checkpoint/regression-comparator.d.ts +18 -0
  157. package/dist/application/checkpoint/regression-comparator.js +192 -0
  158. package/dist/application/collector/logcat-collector.d.ts +22 -1
  159. package/dist/application/collector/logcat-collector.js +103 -15
  160. package/dist/application/collector/logcat-event.d.ts +8 -0
  161. package/dist/application/collector/logcat-event.js +79 -0
  162. package/dist/application/collector/logcat-stop.d.ts +2 -0
  163. package/dist/application/collector/logcat-stop.js +11 -0
  164. package/dist/application/context/context-generator.d.ts +45 -0
  165. package/dist/application/context/context-generator.js +228 -0
  166. package/dist/application/context/context-loader.d.ts +40 -0
  167. package/dist/application/context/context-loader.js +223 -0
  168. package/dist/application/context/context-refresher.d.ts +59 -0
  169. package/dist/application/context/context-refresher.js +349 -0
  170. package/dist/application/context/context-rehasher.d.ts +36 -0
  171. package/dist/application/context/context-rehasher.js +110 -0
  172. package/dist/application/context/context-validator.d.ts +23 -1
  173. package/dist/application/context/context-validator.js +116 -9
  174. package/dist/application/context/evidence-hash.d.ts +1 -0
  175. package/dist/application/context/evidence-hash.js +125 -0
  176. package/dist/application/context/evidence-match.d.ts +9 -0
  177. package/dist/application/context/evidence-match.js +8 -0
  178. package/dist/application/context/shard-identity.d.ts +2 -0
  179. package/dist/application/context/shard-identity.js +7 -0
  180. package/dist/application/contract/contract-loader.d.ts +25 -0
  181. package/dist/application/contract/contract-loader.js +68 -0
  182. package/dist/application/contract/contract-review.d.ts +25 -0
  183. package/dist/application/contract/contract-review.js +51 -0
  184. package/dist/application/contract/contract-verifier.d.ts +53 -0
  185. package/dist/application/contract/contract-verifier.js +645 -0
  186. package/dist/application/devices/resolve-device-assignments.d.ts +21 -0
  187. package/dist/application/devices/resolve-device-assignments.js +60 -0
  188. package/dist/application/diagnosis/failure-classifier.d.ts +23 -0
  189. package/dist/application/diagnosis/failure-classifier.js +179 -0
  190. package/dist/application/diagnostics/diagnostics-exporter.d.ts +29 -0
  191. package/dist/application/diagnostics/diagnostics-exporter.js +283 -0
  192. package/dist/application/diagnostics/ui-capture-telemetry.d.ts +16 -0
  193. package/dist/application/diagnostics/ui-capture-telemetry.js +81 -0
  194. package/dist/application/doctor/doctor-service.d.ts +36 -3
  195. package/dist/application/doctor/doctor-service.js +255 -25
  196. package/dist/application/generation/generation-app-preparer.d.ts +20 -0
  197. package/dist/application/generation/generation-app-preparer.js +28 -0
  198. package/dist/application/generation/generation-config-service.d.ts +21 -0
  199. package/dist/application/generation/generation-config-service.js +53 -0
  200. package/dist/application/generation/generation-confirmation-service.d.ts +14 -3
  201. package/dist/application/generation/generation-confirmation-service.js +109 -24
  202. package/dist/application/generation/generation-context-snapshot.d.ts +6 -0
  203. package/dist/application/generation/generation-context-snapshot.js +29 -0
  204. package/dist/application/generation/generation-finalizer.d.ts +15 -9
  205. package/dist/application/generation/generation-finalizer.js +167 -70
  206. package/dist/application/generation/generation-publisher.js +12 -8
  207. package/dist/application/generation/generation-recovery-service.d.ts +35 -0
  208. package/dist/application/generation/generation-recovery-service.js +144 -0
  209. package/dist/application/generation/generation-reopen-service.d.ts +15 -0
  210. package/dist/application/generation/generation-reopen-service.js +37 -0
  211. package/dist/application/generation/generation-replace-service.d.ts +32 -0
  212. package/dist/application/generation/generation-replace-service.js +116 -0
  213. package/dist/application/generation/generation-starter.d.ts +44 -4
  214. package/dist/application/generation/generation-starter.js +175 -8
  215. package/dist/application/generation/generation-step-executor.d.ts +67 -5
  216. package/dist/application/generation/generation-step-executor.js +471 -108
  217. package/dist/application/generation/proposed-step-validator.js +96 -30
  218. package/dist/application/generation/replay-policy-loader.d.ts +16 -0
  219. package/dist/application/generation/replay-policy-loader.js +29 -0
  220. package/dist/application/generation/risk-evaluator.d.ts +4 -1
  221. package/dist/application/generation/risk-evaluator.js +118 -3
  222. package/dist/application/generation/runtime-observer.d.ts +39 -7
  223. package/dist/application/generation/runtime-observer.js +193 -24
  224. package/dist/application/impact/impact-resolver.d.ts +32 -0
  225. package/dist/application/impact/impact-resolver.js +160 -0
  226. package/dist/application/init/init-service.js +26 -13
  227. package/dist/application/interaction/action-executor.d.ts +3 -2
  228. package/dist/application/interaction/action-executor.js +11 -1
  229. package/dist/application/interaction/action-target.d.ts +17 -0
  230. package/dist/application/interaction/action-target.js +40 -0
  231. package/dist/application/interaction/external-step-runner.d.ts +153 -0
  232. package/dist/application/interaction/external-step-runner.js +332 -0
  233. package/dist/application/interaction/fallback-resolver.d.ts +6 -3
  234. package/dist/application/interaction/fallback-resolver.js +7 -5
  235. package/dist/application/interaction/scroll-to-executor.d.ts +17 -3
  236. package/dist/application/interaction/scroll-to-executor.js +118 -36
  237. package/dist/application/journey/external-flow-resolver.d.ts +25 -0
  238. package/dist/application/journey/external-flow-resolver.js +22 -0
  239. package/dist/application/journey/journey-check-service.d.ts +54 -0
  240. package/dist/application/journey/journey-check-service.js +207 -0
  241. package/dist/application/journey/journey-promoter.d.ts +26 -0
  242. package/dist/application/journey/journey-promoter.js +123 -0
  243. package/dist/application/journey/journey-resolver.d.ts +42 -0
  244. package/dist/application/journey/journey-resolver.js +239 -0
  245. package/dist/application/journey/journey-retirer.d.ts +25 -0
  246. package/dist/application/journey/journey-retirer.js +87 -0
  247. package/dist/application/knowledge/anchor-resolver.d.ts +36 -0
  248. package/dist/application/knowledge/anchor-resolver.js +110 -0
  249. package/dist/application/knowledge/knowledge-loader.d.ts +16 -0
  250. package/dist/application/knowledge/knowledge-loader.js +36 -0
  251. package/dist/application/locator/layout-failure-summary.d.ts +2 -0
  252. package/dist/application/locator/layout-failure-summary.js +67 -0
  253. package/dist/application/locator/layout-traversal.d.ts +11 -0
  254. package/dist/application/locator/layout-traversal.js +18 -0
  255. package/dist/application/locator/locator-resolver.d.ts +13 -1
  256. package/dist/application/locator/locator-resolver.js +182 -18
  257. package/dist/application/observe/observe-service.d.ts +20 -0
  258. package/dist/application/observe/observe-service.js +91 -0
  259. package/dist/application/project/project-describer.d.ts +14 -6
  260. package/dist/application/project/project-describer.js +38 -5
  261. package/dist/application/recognition/screen-detector.d.ts +28 -0
  262. package/dist/application/recognition/screen-detector.js +111 -0
  263. package/dist/application/recorder/locator-selector.js +151 -17
  264. package/dist/application/recorder/recorder-service.d.ts +16 -4
  265. package/dist/application/recorder/recorder-service.js +420 -128
  266. package/dist/application/report/report-writer.d.ts +5 -0
  267. package/dist/application/report/report-writer.js +17 -1
  268. package/dist/application/runtime/cold-launch.d.ts +28 -0
  269. package/dist/application/runtime/cold-launch.js +59 -0
  270. package/dist/application/runtime/step-runner.d.ts +49 -4
  271. package/dist/application/runtime/step-runner.js +721 -130
  272. package/dist/application/runtime/verify-runtime.d.ts +57 -7
  273. package/dist/application/runtime/verify-runtime.js +577 -175
  274. package/dist/application/ui/cached-ui-snapshot-provider.d.ts +45 -0
  275. package/dist/application/ui/cached-ui-snapshot-provider.js +111 -0
  276. package/dist/application/ui/observed-ui-snapshot-provider.d.ts +15 -0
  277. package/dist/application/ui/observed-ui-snapshot-provider.js +66 -0
  278. package/dist/application/ui/ui-snapshot-lifecycle.d.ts +2 -0
  279. package/dist/application/ui/ui-snapshot-lifecycle.js +8 -0
  280. package/dist/application/ui/ui-stability-probe.d.ts +9 -0
  281. package/dist/application/ui/ui-stability-probe.js +24 -0
  282. package/dist/application/wait/idle-advice.d.ts +6 -0
  283. package/dist/application/wait/idle-advice.js +27 -0
  284. package/dist/application/wait/idle-profiles.d.ts +15 -0
  285. package/dist/application/wait/idle-profiles.js +75 -0
  286. package/dist/application/wait/idle-waiter.d.ts +28 -4
  287. package/dist/application/wait/idle-waiter.js +201 -15
  288. package/dist/cli/commands/align.d.ts +3 -0
  289. package/dist/cli/commands/align.js +124 -0
  290. package/dist/cli/commands/baseline.d.ts +3 -0
  291. package/dist/cli/commands/baseline.js +130 -0
  292. package/dist/cli/commands/context.js +300 -27
  293. package/dist/cli/commands/contract.d.ts +3 -0
  294. package/dist/cli/commands/contract.js +120 -0
  295. package/dist/cli/commands/diagnose.d.ts +3 -0
  296. package/dist/cli/commands/diagnose.js +84 -0
  297. package/dist/cli/commands/doctor.js +16 -9
  298. package/dist/cli/commands/failure.d.ts +3 -0
  299. package/dist/cli/commands/failure.js +63 -0
  300. package/dist/cli/commands/generation/lifecycle-commands.d.ts +5 -0
  301. package/dist/cli/commands/generation/lifecycle-commands.js +211 -0
  302. package/dist/cli/commands/generation/session-commands.d.ts +11 -0
  303. package/dist/cli/commands/generation/session-commands.js +472 -0
  304. package/dist/cli/commands/generation/shared.d.ts +1414 -0
  305. package/dist/cli/commands/generation/shared.js +291 -0
  306. package/dist/cli/commands/generation/step-commands.d.ts +8 -0
  307. package/dist/cli/commands/generation/step-commands.js +315 -0
  308. package/dist/cli/commands/generation.js +10 -507
  309. package/dist/cli/commands/impact.d.ts +3 -0
  310. package/dist/cli/commands/impact.js +87 -0
  311. package/dist/cli/commands/init.js +13 -6
  312. package/dist/cli/commands/journey.d.ts +3 -0
  313. package/dist/cli/commands/journey.js +382 -0
  314. package/dist/cli/commands/knowledge.d.ts +3 -0
  315. package/dist/cli/commands/knowledge.js +93 -0
  316. package/dist/cli/commands/observe.d.ts +3 -0
  317. package/dist/cli/commands/observe.js +127 -0
  318. package/dist/cli/commands/project.js +2 -1
  319. package/dist/cli/commands/record.js +18 -5
  320. package/dist/cli/commands/verify.js +326 -64
  321. package/dist/cli/dependencies.d.ts +136 -1
  322. package/dist/cli/dependencies.js +504 -52
  323. package/dist/cli/diagnostics-recorder.d.ts +31 -0
  324. package/dist/cli/diagnostics-recorder.js +82 -0
  325. package/dist/cli/diff-verification.d.ts +41 -0
  326. package/dist/cli/diff-verification.js +192 -0
  327. package/dist/cli/main.d.ts +1 -1
  328. package/dist/cli/main.js +107 -2
  329. package/dist/cli/output.d.ts +5 -3
  330. package/dist/cli/output.js +12 -0
  331. package/dist/cli/program.js +21 -1
  332. package/dist/cli/project-root.d.ts +1 -0
  333. package/dist/cli/project-root.js +6 -0
  334. package/dist/cli/runtime-selection.d.ts +22 -0
  335. package/dist/cli/runtime-selection.js +92 -0
  336. package/dist/cli/version.d.ts +1 -0
  337. package/dist/cli/version.js +14 -0
  338. package/dist/cli/workspace-guard.d.ts +2 -0
  339. package/dist/cli/workspace-guard.js +3 -0
  340. package/dist/domain/binding-reference.d.ts +4 -0
  341. package/dist/domain/binding-reference.js +23 -0
  342. package/dist/domain/checkpoint.d.ts +404 -0
  343. package/dist/domain/checkpoint.js +297 -0
  344. package/dist/domain/config.d.ts +107 -1
  345. package/dist/domain/config.js +52 -6
  346. package/dist/domain/contract.d.ts +413 -0
  347. package/dist/domain/contract.js +196 -0
  348. package/dist/domain/diagnostics.d.ts +1396 -0
  349. package/dist/domain/diagnostics.js +193 -0
  350. package/dist/domain/external-flow.d.ts +493 -0
  351. package/dist/domain/external-flow.js +15 -0
  352. package/dist/domain/failure-classification.d.ts +176 -0
  353. package/dist/domain/failure-classification.js +169 -0
  354. package/dist/domain/failure.d.ts +2 -1
  355. package/dist/domain/failure.js +123 -0
  356. package/dist/domain/generation.d.ts +1671 -132
  357. package/dist/domain/generation.js +202 -13
  358. package/dist/domain/geometry.d.ts +13 -0
  359. package/dist/domain/geometry.js +16 -0
  360. package/dist/domain/impact.d.ts +71 -0
  361. package/dist/domain/impact.js +76 -0
  362. package/dist/domain/init.d.ts +12 -4
  363. package/dist/domain/init.js +24 -18
  364. package/dist/domain/journey-composition.d.ts +2285 -0
  365. package/dist/domain/journey-composition.js +85 -0
  366. package/dist/domain/journey-lifecycle.d.ts +10 -0
  367. package/dist/domain/journey-lifecycle.js +9 -0
  368. package/dist/domain/journey.d.ts +2419 -212
  369. package/dist/domain/journey.js +384 -14
  370. package/dist/domain/knowledge.d.ts +165 -0
  371. package/dist/domain/knowledge.js +188 -0
  372. package/dist/domain/layout.d.ts +34 -5
  373. package/dist/domain/layout.js +64 -6
  374. package/dist/domain/locator-evidence.d.ts +3 -0
  375. package/dist/domain/locator-evidence.js +36 -0
  376. package/dist/domain/logcat-event.d.ts +54 -0
  377. package/dist/domain/logcat-event.js +40 -0
  378. package/dist/domain/observation.d.ts +33 -0
  379. package/dist/domain/observation.js +18 -0
  380. package/dist/domain/project-context.d.ts +219 -1
  381. package/dist/domain/project-context.js +209 -6
  382. package/dist/domain/proposed-step.d.ts +340 -60
  383. package/dist/domain/proposed-step.js +23 -3
  384. package/dist/domain/report.d.ts +1455 -28
  385. package/dist/domain/report.js +211 -14
  386. package/dist/domain/runtime-snapshot.d.ts +58 -1
  387. package/dist/domain/runtime-snapshot.js +14 -3
  388. package/dist/domain/runtime.d.ts +50 -0
  389. package/dist/domain/runtime.js +36 -0
  390. package/dist/domain/system-app-profiles.d.ts +6 -0
  391. package/dist/domain/system-app-profiles.js +43 -0
  392. package/dist/domain/ui-backend.d.ts +28 -0
  393. package/dist/domain/ui-backend.js +33 -0
  394. package/dist/domain/ui-cache.d.ts +11 -0
  395. package/dist/domain/ui-cache.js +10 -0
  396. package/dist/domain/verify-receipt.d.ts +17 -0
  397. package/dist/domain/verify-receipt.js +17 -0
  398. package/dist/domain/window-hierarchy.d.ts +84 -0
  399. package/dist/domain/window-hierarchy.js +172 -0
  400. package/dist/domain/workflow-manifest.d.ts +77 -0
  401. package/dist/domain/workflow-manifest.js +136 -0
  402. package/dist/domain/workspace.d.ts +30 -0
  403. package/dist/domain/workspace.js +74 -0
  404. package/dist/ports/adb.d.ts +34 -1
  405. package/dist/ports/align-prompt.d.ts +11 -0
  406. package/dist/ports/align-prompt.js +6 -0
  407. package/dist/ports/anchor-resolver.d.ts +30 -0
  408. package/dist/ports/anchor-resolver.js +1 -0
  409. package/dist/ports/annotated-screen-resolver.d.ts +4 -0
  410. package/dist/ports/annotated-screen-resolver.js +1 -0
  411. package/dist/ports/camera-probe.d.ts +23 -0
  412. package/dist/ports/camera-probe.js +8 -0
  413. package/dist/ports/context-document-writer.d.ts +24 -0
  414. package/dist/ports/context-document-writer.js +1 -0
  415. package/dist/ports/detached-process-launcher.d.ts +12 -0
  416. package/dist/ports/detached-process-launcher.js +1 -0
  417. package/dist/ports/diagnostics.d.ts +15 -0
  418. package/dist/ports/diagnostics.js +1 -0
  419. package/dist/ports/external-flow-registry.d.ts +37 -0
  420. package/dist/ports/external-flow-registry.js +1 -0
  421. package/dist/ports/generation-prompt.d.ts +2 -1
  422. package/dist/ports/generation-session-store.d.ts +12 -2
  423. package/dist/ports/git-diff.d.ts +9 -0
  424. package/dist/ports/git-diff.js +1 -0
  425. package/dist/ports/journey-composition-store.d.ts +17 -0
  426. package/dist/ports/journey-composition-store.js +1 -0
  427. package/dist/ports/knowledge-registry.d.ts +20 -0
  428. package/dist/ports/knowledge-registry.js +1 -0
  429. package/dist/ports/process-runner.d.ts +7 -0
  430. package/dist/ports/project-file-inspector.d.ts +1 -0
  431. package/dist/ports/project-identity-inspector.d.ts +24 -0
  432. package/dist/ports/project-identity-inspector.js +1 -0
  433. package/dist/ports/project-inventory-inspector.d.ts +21 -0
  434. package/dist/ports/project-inventory-inspector.js +1 -0
  435. package/dist/ports/project-module-discoverer.d.ts +26 -0
  436. package/dist/ports/project-module-discoverer.js +1 -0
  437. package/dist/ports/recorder-prompt.d.ts +10 -1
  438. package/dist/ports/runtime-backend.d.ts +89 -0
  439. package/dist/ports/runtime-backend.js +13 -0
  440. package/dist/ports/runtime-capability.d.ts +12 -0
  441. package/dist/ports/runtime-capability.js +20 -0
  442. package/dist/ports/runtime-session-ports.d.ts +25 -0
  443. package/dist/ports/runtime-session-ports.js +1 -0
  444. package/dist/ports/screenshot.d.ts +11 -0
  445. package/dist/ports/screenshot.js +1 -0
  446. package/dist/ports/skill-installer.d.ts +2 -2
  447. package/dist/ports/ui-snapshot.d.ts +35 -0
  448. package/dist/ports/ui-snapshot.js +1 -0
  449. package/dist/ports/ui-stability.d.ts +19 -0
  450. package/dist/ports/ui-stability.js +1 -0
  451. package/dist/ports/workspace-layout.d.ts +4 -0
  452. package/dist/ports/workspace-layout.js +1 -0
  453. package/dist/shared/errors.d.ts +2 -0
  454. package/dist/shared/errors.js +22 -0
  455. package/dist/shared/paths.d.ts +2 -0
  456. package/dist/shared/paths.js +24 -0
  457. package/dist/shared/strings.d.ts +2 -0
  458. package/dist/shared/strings.js +16 -0
  459. package/package.json +7 -2
  460. package/assets/skills/taphound-ai-journey/GUIDE.md +0 -862
  461. package/assets/skills/taphound-ai-journey/SKILL.md +0 -377
  462. package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +0 -526
  463. package/assets/skills/taphound-ai-journey/prompts/generate-step.md +0 -109
  464. package/assets/skills/taphound-ai-journey/schemas/project-context.json +0 -81
  465. package/assets/skills/taphound-ai-journey/templates/project-context.example.json +0 -49
  466. package/dist/ports/android-cli.d.ts +0 -21
  467. /package/dist/{ports/android-cli.js → adapters/runtime/mobile-mcp/mobile-mcp-tools.js} +0 -0
@@ -0,0 +1,36 @@
1
+ export class KnowledgeLoadError extends Error {
2
+ code;
3
+ name = "KnowledgeLoadError";
4
+ constructor(code, message, options) {
5
+ super(message, options);
6
+ this.code = code;
7
+ }
8
+ }
9
+ export class KnowledgeLoader {
10
+ registry;
11
+ constructor(registry) {
12
+ this.registry = registry;
13
+ }
14
+ load = async (input) => {
15
+ let bundle;
16
+ try {
17
+ bundle = await this.registry.load(input.projectRoot);
18
+ }
19
+ catch (error) {
20
+ const message = error instanceof Error ? error.message : String(error);
21
+ const notFound = (error instanceof Error
22
+ && "code" in error
23
+ && error.code === "ENOENT");
24
+ throw new KnowledgeLoadError(notFound ? "KNOWLEDGE_NOT_FOUND" : "KNOWLEDGE_INVALID", message, { cause: error });
25
+ }
26
+ if (input.packageName !== undefined
27
+ && bundle.index.packageName !== input.packageName) {
28
+ throw new KnowledgeLoadError("KNOWLEDGE_INVALID", "Knowledge package identity does not match the target project");
29
+ }
30
+ if (input.expectedKnowledgeHash !== undefined
31
+ && bundle.knowledgeHash !== input.expectedKnowledgeHash) {
32
+ throw new KnowledgeLoadError("KNOWLEDGE_STALE", "Knowledge Registry hash changed after it was bound");
33
+ }
34
+ return bundle;
35
+ };
36
+ }
@@ -0,0 +1,2 @@
1
+ import type { LayoutElement } from "../../domain/layout.js";
2
+ export declare function summarizeLayoutElements(elements: readonly LayoutElement[]): string;
@@ -0,0 +1,67 @@
1
+ const MAX_NOTABLE = 8;
2
+ const MAX_LABEL_TEXT = 24;
3
+ /**
4
+ * Bounded, human-scannable description of the screen at the moment a
5
+ * Locator or Expectation failed, so an operator (or agent) can tell an
6
+ * environment blocker (lock screen, permission dialog, empty window)
7
+ * from a genuine regression without re-dumping the UI hierarchy.
8
+ */
9
+ function truncate(value) {
10
+ return value.length <= MAX_LABEL_TEXT
11
+ ? value
12
+ : `${value.slice(0, MAX_LABEL_TEXT - 1)}…`;
13
+ }
14
+ function label(element) {
15
+ const id = element.resourceId;
16
+ const text = element.text === undefined || element.text === ""
17
+ ? undefined
18
+ : truncate(element.text);
19
+ const description = element.contentDescription === undefined
20
+ || element.contentDescription === ""
21
+ ? undefined
22
+ : truncate(element.contentDescription);
23
+ if (id !== undefined && text !== undefined) {
24
+ return `#${id} "${text}"`;
25
+ }
26
+ if (id !== undefined) {
27
+ return `#${id}`;
28
+ }
29
+ if (text !== undefined) {
30
+ return `"${text}"`;
31
+ }
32
+ if (description !== undefined) {
33
+ return `(${description})`;
34
+ }
35
+ return undefined;
36
+ }
37
+ function salience(element) {
38
+ return (element.clickable === true || element.scrollable === true ? 2 : 0)
39
+ + (element.focused === true || element.focusable === true ? 1 : 0);
40
+ }
41
+ export function summarizeLayoutElements(elements) {
42
+ if (elements.length === 0) {
43
+ return "Screen shows no Layout elements";
44
+ }
45
+ const notable = [];
46
+ const seen = new Set();
47
+ const ranked = elements
48
+ .filter((element) => label(element) !== undefined)
49
+ .sort((a, b) => salience(b) - salience(a));
50
+ for (const element of ranked) {
51
+ const text = label(element);
52
+ if (text === undefined || seen.has(text)) {
53
+ continue;
54
+ }
55
+ seen.add(text);
56
+ notable.push(text);
57
+ if (notable.length >= MAX_NOTABLE) {
58
+ break;
59
+ }
60
+ }
61
+ if (notable.length === 0) {
62
+ return `Screen shows ${String(elements.length)} element(s),`
63
+ + " none with resourceId, text, or contentDescription";
64
+ }
65
+ return `Screen shows ${String(elements.length)} element(s);`
66
+ + ` notable: ${notable.join(", ")}`;
67
+ }
@@ -0,0 +1,11 @@
1
+ import type { LayoutElement } from "../../domain/layout.js";
2
+ export interface LayoutEntry {
3
+ element: LayoutElement;
4
+ ancestors: readonly LayoutElement[];
5
+ }
6
+ /**
7
+ * Pre-order (document order) flattening. Siblings share one ancestor array,
8
+ * and entries are appended to a single result instead of re-spreading every
9
+ * subtree at each level.
10
+ */
11
+ export declare function flattenLayout(elements: readonly LayoutElement[], ancestors?: readonly LayoutElement[]): LayoutEntry[];
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Pre-order (document order) flattening. Siblings share one ancestor array,
3
+ * and entries are appended to a single result instead of re-spreading every
4
+ * subtree at each level.
5
+ */
6
+ export function flattenLayout(elements, ancestors = []) {
7
+ const entries = [];
8
+ const visit = (siblings, parents) => {
9
+ for (const element of siblings) {
10
+ entries.push({ element, ancestors: parents });
11
+ if (element.children.length > 0) {
12
+ visit(element.children, [...parents, element]);
13
+ }
14
+ }
15
+ };
16
+ visit(elements, ancestors);
17
+ return entries;
18
+ }
@@ -1,20 +1,32 @@
1
1
  import type { FailureCode } from "../../domain/failure.js";
2
2
  import { type LocatorField } from "../../domain/locator.js";
3
3
  import type { LayoutElement, Locator } from "../../domain/layout.js";
4
- import type { Point } from "../../ports/android-cli.js";
4
+ import type { DisplayViewport, Point } from "../../domain/geometry.js";
5
5
  export interface LocatedTarget {
6
6
  status: "found";
7
7
  element: LayoutElement;
8
8
  point: Point;
9
9
  matchedBy: LocatorField;
10
+ matchedFields?: readonly LocatorField[] | undefined;
11
+ }
12
+ export interface LocatedIdentity {
13
+ status: "found";
14
+ element: LayoutElement;
15
+ matchedBy: LocatorField;
16
+ matchedFields?: readonly LocatorField[] | undefined;
10
17
  }
11
18
  export interface LocatorFailure {
12
19
  status: "failed";
13
20
  code: Extract<FailureCode, "LOCATOR_NOT_FOUND" | "LOCATOR_AMBIGUOUS" | "ACTION_FAILED">;
14
21
  message: string;
22
+ evidenceMismatch?: true | undefined;
15
23
  }
16
24
  export type LocatorResolution = LocatedTarget | LocatorFailure;
25
+ export type LocatorIdentityResolution = LocatedIdentity | LocatorFailure;
17
26
  export interface LocatorResolutionOptions {
18
27
  requireEnabled?: boolean | undefined;
28
+ requiredCapability?: "clickable" | "longClickable" | undefined;
29
+ viewport?: DisplayViewport | undefined;
19
30
  }
31
+ export declare function resolveLocatorIdentity(roots: readonly LayoutElement[], locator: Locator): LocatorIdentityResolution;
20
32
  export declare function resolveLocator(roots: readonly LayoutElement[], locator: Locator, options?: LocatorResolutionOptions): LocatorResolution;
@@ -1,9 +1,16 @@
1
1
  import { LOCATOR_FIELDS } from "../../domain/locator.js";
2
- function flatten(elements) {
3
- return elements.flatMap((element) => [
4
- element,
5
- ...flatten(element.children)
6
- ]);
2
+ import { locatorEvidenceMatches } from "../../domain/locator-evidence.js";
3
+ import { flattenLayout } from "./layout-traversal.js";
4
+ import { summarizeLayoutElements } from "./layout-failure-summary.js";
5
+ function pointWithin(point, bounds) {
6
+ return point !== undefined
7
+ && bounds !== undefined
8
+ && point.x >= bounds.left
9
+ && point.x < bounds.right
10
+ && point.y >= bounds.top
11
+ && point.y < bounds.bottom
12
+ ? point
13
+ : undefined;
7
14
  }
8
15
  function center(element) {
9
16
  if (element.center !== undefined) {
@@ -11,24 +18,79 @@ function center(element) {
11
18
  }
12
19
  const bounds = element.bounds;
13
20
  if (bounds === undefined) {
14
- throw new Error(`Layout element ${element.id} has no center or bounds`);
21
+ return undefined;
15
22
  }
16
23
  return {
17
24
  x: Math.round((bounds.left + bounds.right) / 2),
18
25
  y: Math.round((bounds.top + bounds.bottom) / 2)
19
26
  };
20
27
  }
21
- export function resolveLocator(roots, locator, options = {}) {
22
- const elements = flatten(roots);
28
+ const compiledPatterns = new Map();
29
+ const MAX_COMPILED_PATTERNS = 256;
30
+ /** Locator regexes are non-global, so a cached instance is stateless. */
31
+ function compiledPattern(pattern) {
32
+ let compiled = compiledPatterns.get(pattern);
33
+ if (compiled === undefined) {
34
+ if (compiledPatterns.size >= MAX_COMPILED_PATTERNS) {
35
+ compiledPatterns.clear();
36
+ }
37
+ compiled = new RegExp(pattern);
38
+ compiledPatterns.set(pattern, compiled);
39
+ }
40
+ return compiled;
41
+ }
42
+ function fieldValueMatches(elementValue, locatorValue, match) {
43
+ if (elementValue === undefined) {
44
+ return false;
45
+ }
46
+ if (match === "contains") {
47
+ return elementValue.includes(locatorValue);
48
+ }
49
+ if (match === "startsWith") {
50
+ return elementValue.startsWith(locatorValue);
51
+ }
52
+ if (match === "regex") {
53
+ return compiledPattern(locatorValue).test(elementValue);
54
+ }
55
+ return elementValue === locatorValue;
56
+ }
57
+ function matchForField(locator, field) {
58
+ return locator.matchBy?.[field] ?? locator.match;
59
+ }
60
+ function resolveEntry(allEntries, locator, entries = allEntries) {
61
+ if (locator.within !== undefined) {
62
+ const scope = resolveEntry(allEntries, locator.within);
63
+ if (scope.status === "failed") {
64
+ return {
65
+ ...scope,
66
+ message: `Locator scope failed: ${scope.message}`
67
+ };
68
+ }
69
+ entries = allEntries.flatMap((entry) => {
70
+ const scopeIndex = entry.ancestors.indexOf(scope.entry.element);
71
+ return scopeIndex < 0
72
+ ? []
73
+ : [{
74
+ ...entry,
75
+ ancestors: entry.ancestors.slice(scopeIndex)
76
+ }];
77
+ });
78
+ }
23
79
  let candidates;
24
80
  let matchedBy;
25
- for (const field of LOCATOR_FIELDS) {
81
+ let matchedFields;
82
+ if (locator.combine === "all") {
83
+ matchedFields = LOCATOR_FIELDS.filter((field) => locator[field] !== undefined);
84
+ candidates = entries.filter(({ element }) => matchedFields?.every((field) => fieldValueMatches(element[field], locator[field], matchForField(locator, field))) === true);
85
+ matchedBy = matchedFields[0];
86
+ }
87
+ for (const field of locator.combine === "all" ? [] : LOCATOR_FIELDS) {
26
88
  const value = locator[field];
27
89
  if (value === undefined) {
28
90
  continue;
29
91
  }
30
92
  if (candidates === undefined) {
31
- const matches = elements.filter((element) => element[field] === value);
93
+ const matches = entries.filter(({ element }) => fieldValueMatches(element[field], value, matchForField(locator, field)));
32
94
  if (matches.length === 0) {
33
95
  continue;
34
96
  }
@@ -36,7 +98,7 @@ export function resolveLocator(roots, locator, options = {}) {
36
98
  matchedBy = field;
37
99
  }
38
100
  else if (candidates.length > 1) {
39
- const narrowed = candidates.filter((element) => element[field] === value);
101
+ const narrowed = candidates.filter(({ element }) => fieldValueMatches(element[field], value, matchForField(locator, field)));
40
102
  if (narrowed.length === 0) {
41
103
  return {
42
104
  status: "failed",
@@ -55,24 +117,98 @@ export function resolveLocator(roots, locator, options = {}) {
55
117
  return {
56
118
  status: "failed",
57
119
  code: "LOCATOR_NOT_FOUND",
58
- message: "No Layout element matches the Locator"
120
+ message: "No Layout element matches the Locator."
121
+ + ` ${summarizeLayoutElements(allEntries.map((entry) => entry.element))}`
59
122
  };
60
123
  }
61
- if (candidates.length > 1) {
124
+ if (locator.index !== undefined) {
125
+ const indexed = candidates[locator.index];
126
+ if (indexed === undefined) {
127
+ return {
128
+ status: "failed",
129
+ code: "LOCATOR_NOT_FOUND",
130
+ message: `Locator index ${String(locator.index)} is out of range for ${String(candidates.length)} matches`
131
+ };
132
+ }
133
+ candidates = [indexed];
134
+ }
135
+ else if (candidates.length > 1) {
62
136
  return {
63
137
  status: "failed",
64
138
  code: "LOCATOR_AMBIGUOUS",
65
139
  message: `Locator matches ${String(candidates.length)} Layout elements`
66
140
  };
67
141
  }
68
- const element = candidates[0];
69
- if (element === undefined || matchedBy === undefined) {
142
+ const entry = candidates[0];
143
+ if (entry === undefined || matchedBy === undefined) {
144
+ return {
145
+ status: "failed",
146
+ code: "LOCATOR_NOT_FOUND",
147
+ message: "No Layout element matches the Locator."
148
+ + ` ${summarizeLayoutElements(allEntries.map((entry) => entry.element))}`
149
+ };
150
+ }
151
+ return {
152
+ status: "found",
153
+ entry,
154
+ matchedBy,
155
+ ...(matchedFields === undefined ? {} : { matchedFields })
156
+ };
157
+ }
158
+ export function resolveLocatorIdentity(roots, locator) {
159
+ const resolution = resolveEntry(flattenLayout(roots), locator);
160
+ if (resolution.status === "failed") {
161
+ return resolution;
162
+ }
163
+ const { element } = resolution.entry;
164
+ if (locator.evidence !== undefined
165
+ && !locatorEvidenceMatches(element, locator.evidence)) {
166
+ return {
167
+ status: "failed",
168
+ code: "LOCATOR_NOT_FOUND",
169
+ message: "Indexed Locator element evidence does not match the live Layout",
170
+ evidenceMismatch: true
171
+ };
172
+ }
173
+ return {
174
+ status: "found",
175
+ element,
176
+ matchedBy: resolution.matchedBy,
177
+ ...(resolution.matchedFields === undefined
178
+ ? {}
179
+ : { matchedFields: resolution.matchedFields })
180
+ };
181
+ }
182
+ export function resolveLocator(roots, locator, options = {}) {
183
+ const resolution = resolveEntry(flattenLayout(roots), locator);
184
+ if (resolution.status === "failed") {
185
+ return resolution;
186
+ }
187
+ const { entry, matchedBy } = resolution;
188
+ let element = entry.element;
189
+ if (locator.evidence !== undefined
190
+ && !locatorEvidenceMatches(element, locator.evidence)) {
70
191
  return {
71
192
  status: "failed",
72
193
  code: "LOCATOR_NOT_FOUND",
73
- message: "No Layout element matches the Locator"
194
+ message: "Indexed Locator element evidence does not match the live Layout",
195
+ evidenceMismatch: true
74
196
  };
75
197
  }
198
+ if (options.requiredCapability !== undefined
199
+ && element[options.requiredCapability] !== true) {
200
+ // The nearest capable ancestor receives the touch; a disabled one
201
+ // swallows it, so it is reported instead of skipped.
202
+ const ancestor = [...entry.ancestors].reverse().find((candidate) => candidate[options.requiredCapability] === true);
203
+ if (ancestor === undefined) {
204
+ return {
205
+ status: "failed",
206
+ code: "ACTION_FAILED",
207
+ message: `Layout target lacks required ${options.requiredCapability} capability`
208
+ };
209
+ }
210
+ element = ancestor;
211
+ }
76
212
  if (options.requireEnabled !== false && !element.enabled) {
77
213
  return {
78
214
  status: "failed",
@@ -80,10 +216,38 @@ export function resolveLocator(roots, locator, options = {}) {
80
216
  message: `Layout element ${element.id} is disabled`
81
217
  };
82
218
  }
219
+ // Touch the matched element itself: it dispatches to the capable ancestor,
220
+ // whose own center may be covered by an unrelated child control.
221
+ const point = element === entry.element
222
+ ? center(element)
223
+ : pointWithin(center(entry.element), element.bounds) ?? center(element);
224
+ if (point === undefined) {
225
+ return {
226
+ status: "failed",
227
+ code: "ACTION_FAILED",
228
+ message: `Layout element ${element.id} has no executable geometry`
229
+ };
230
+ }
231
+ const viewport = options.viewport;
232
+ if (viewport !== undefined
233
+ && (point.x >= viewport.width
234
+ || point.y >= viewport.height
235
+ || (element.bounds !== undefined
236
+ && (element.bounds.right > viewport.width
237
+ || element.bounds.bottom > viewport.height)))) {
238
+ return {
239
+ status: "failed",
240
+ code: "ACTION_FAILED",
241
+ message: `Layout element ${element.id} geometry is outside the physical display viewport`
242
+ };
243
+ }
83
244
  return {
84
245
  status: "found",
85
246
  element,
86
- point: center(element),
87
- matchedBy
247
+ point,
248
+ matchedBy,
249
+ ...(resolution.matchedFields === undefined
250
+ ? {}
251
+ : { matchedFields: resolution.matchedFields })
88
252
  };
89
253
  }
@@ -0,0 +1,20 @@
1
+ import { type ObserveReport } from "../../domain/observation.js";
2
+ import type { RuntimeSessionOpener } from "../../ports/runtime-backend.js";
3
+ import type { UiBackendSelection } from "../../domain/ui-backend.js";
4
+ export interface ObserveInput {
5
+ packageName: string;
6
+ deviceSerial: string;
7
+ logcatLines?: number | undefined;
8
+ signal?: AbortSignal | undefined;
9
+ }
10
+ export interface ObserveDependencies {
11
+ sessions: RuntimeSessionOpener;
12
+ layoutTimeoutMs: number;
13
+ backend?: UiBackendSelection | undefined;
14
+ cacheEnabled?: boolean | undefined;
15
+ }
16
+ export declare class ObserveService {
17
+ private readonly dependencies;
18
+ constructor(dependencies: ObserveDependencies);
19
+ observe(input: ObserveInput): Promise<ObserveReport>;
20
+ }
@@ -0,0 +1,91 @@
1
+ import { ObserveReportSchema } from "../../domain/observation.js";
2
+ import { runtimeCapabilityMissing } from "../../ports/runtime-capability.js";
3
+ import { closeUiSnapshotProvider } from "../ui/ui-snapshot-lifecycle.js";
4
+ function failedCommand(result) {
5
+ return result.exitCode !== 0
6
+ || result.spawnError !== undefined
7
+ || result.timedOut
8
+ || result.cancelled;
9
+ }
10
+ function splitLogcat(stdout) {
11
+ const lines = stdout.split("\n");
12
+ while (lines.length > 0 && lines[lines.length - 1] === "") {
13
+ lines.pop();
14
+ }
15
+ return lines;
16
+ }
17
+ export class ObserveService {
18
+ dependencies;
19
+ constructor(dependencies) {
20
+ this.dependencies = dependencies;
21
+ }
22
+ async observe(input) {
23
+ const { packageName, deviceSerial, logcatLines, signal } = input;
24
+ const session = await this.dependencies.sessions.openSession({
25
+ deviceSerial,
26
+ ...(signal === undefined ? {} : { signal })
27
+ });
28
+ const app = {
29
+ packageName,
30
+ ...(signal === undefined ? {} : { signal })
31
+ };
32
+ if (session.foregroundComponent === undefined) {
33
+ throw runtimeCapabilityMissing(session.descriptor.id, "foregroundComponent");
34
+ }
35
+ const foreground = await session.foregroundComponent(app);
36
+ const activity = foreground.packageName === packageName
37
+ ? foreground.activity
38
+ : undefined;
39
+ const uiSnapshotProvider = await session.openUiSnapshots({
40
+ timeoutMs: this.dependencies.layoutTimeoutMs,
41
+ ...(this.dependencies.backend === undefined
42
+ ? {}
43
+ : { backend: this.dependencies.backend }),
44
+ ...(this.dependencies.cacheEnabled === undefined
45
+ ? {}
46
+ : { cacheEnabled: this.dependencies.cacheEnabled }),
47
+ ...(signal === undefined ? {} : { signal })
48
+ });
49
+ let uiCache;
50
+ const uiSnapshot = await (async () => {
51
+ try {
52
+ return await uiSnapshotProvider.capture({
53
+ reason: "observe",
54
+ timeoutMs: this.dependencies.layoutTimeoutMs,
55
+ ...(signal === undefined ? {} : { signal })
56
+ });
57
+ }
58
+ finally {
59
+ uiCache = uiSnapshotProvider.cacheTelemetry?.();
60
+ await closeUiSnapshotProvider(uiSnapshotProvider);
61
+ }
62
+ })();
63
+ let logcat;
64
+ if (logcatLines !== undefined && logcatLines > 0) {
65
+ if (session.dumpLogcat === undefined) {
66
+ throw runtimeCapabilityMissing(session.descriptor.id, "dumpLogcat");
67
+ }
68
+ const result = await session.dumpLogcat({
69
+ maxLines: logcatLines,
70
+ ...(signal === undefined ? {} : { signal })
71
+ });
72
+ if (failedCommand(result)) {
73
+ throw new Error(result.stderr.trim()
74
+ || result.spawnError
75
+ || "Logcat dump failed");
76
+ }
77
+ logcat = splitLogcat(result.stdout);
78
+ }
79
+ return ObserveReportSchema.parse({
80
+ deviceSerial,
81
+ packageName,
82
+ ...(activity === undefined ? {} : { activity }),
83
+ foreground,
84
+ uiBackend: uiSnapshot.backend,
85
+ uiCaptureDurationMs: uiSnapshot.durationMs,
86
+ ...(uiCache === undefined ? {} : { uiCache }),
87
+ layout: uiSnapshot.roots,
88
+ ...(logcat === undefined ? {} : { logcat })
89
+ });
90
+ }
91
+ }
@@ -1,14 +1,22 @@
1
1
  import type { TapHoundConfig } from "../../domain/config.js";
2
+ import type { ProjectIdentityInspector } from "../../ports/project-identity-inspector.js";
3
+ import type { ProjectModuleDiscoverer } from "../../ports/project-module-discoverer.js";
2
4
  export interface ProjectDescribeInput {
3
- projectRoot: string;
4
- config: TapHoundConfig;
5
- signal?: AbortSignal | undefined;
5
+ readonly projectRoot: string;
6
+ readonly config: TapHoundConfig;
7
+ readonly signal?: AbortSignal | undefined;
6
8
  }
7
9
  export interface ProjectDescription {
8
- projectRoot: string;
9
- packageName: string;
10
- launchActivity: string;
10
+ readonly projectRoot: string;
11
+ readonly packageName: string;
12
+ readonly launchActivity: string;
13
+ }
14
+ export interface ProjectDescriberDependencies {
15
+ readonly discoverer?: ProjectModuleDiscoverer | undefined;
16
+ readonly identity?: ProjectIdentityInspector | undefined;
11
17
  }
12
18
  export declare class ProjectDescriber {
19
+ private readonly dependencies;
20
+ constructor(dependencies?: ProjectDescriberDependencies);
13
21
  readonly describe: (input: ProjectDescribeInput) => Promise<ProjectDescription>;
14
22
  }
@@ -1,8 +1,41 @@
1
1
  import { normalizeActivity } from "../../domain/activity.js";
2
2
  export class ProjectDescriber {
3
- describe = async (input) => Promise.resolve({
4
- projectRoot: input.projectRoot,
5
- packageName: input.config.run.packageName,
6
- launchActivity: normalizeActivity(input.config.run.packageName, input.config.run.activity)
7
- });
3
+ dependencies;
4
+ constructor(dependencies = {}) {
5
+ this.dependencies = dependencies;
6
+ }
7
+ describe = async (input) => {
8
+ const { discoverer, identity } = this.dependencies;
9
+ if (discoverer !== undefined && identity !== undefined) {
10
+ try {
11
+ const discovery = await discoverer.discoverModules({
12
+ projectRoot: input.projectRoot
13
+ });
14
+ if (discovery.status === "discovered") {
15
+ const appModule = discovery.modules.find((module) => module.kind === "application");
16
+ if (appModule !== undefined) {
17
+ const inspected = await identity.inspectIdentity({
18
+ projectRoot: input.projectRoot,
19
+ moduleDir: appModule.projectDir
20
+ });
21
+ if (inspected.status === "inspected") {
22
+ return {
23
+ projectRoot: input.projectRoot,
24
+ packageName: inspected.packageName,
25
+ launchActivity: inspected.launchActivity
26
+ };
27
+ }
28
+ }
29
+ }
30
+ }
31
+ catch {
32
+ // fall back to config values
33
+ }
34
+ }
35
+ return {
36
+ projectRoot: input.projectRoot,
37
+ packageName: input.config.run.packageName,
38
+ launchActivity: normalizeActivity(input.config.run.packageName, input.config.run.activity)
39
+ };
40
+ };
8
41
  }
@@ -0,0 +1,28 @@
1
+ import type { AnchorDefinition, ScreenDefinition } from "../../domain/knowledge.js";
2
+ import type { RuntimeSnapshot } from "../../domain/runtime-snapshot.js";
3
+ /** The runtime facts Screen detection reads; any live observation provides them. */
4
+ export type ScreenObservation = Pick<RuntimeSnapshot, "activity" | "layout" | "windowHierarchy">;
5
+ export type AnchorMatchResult = {
6
+ anchorId: string;
7
+ result: "matched" | "missing" | "ambiguous" | "unknown";
8
+ detail?: string | undefined;
9
+ };
10
+ export type ScreenDetectionResult = {
11
+ status: "matched";
12
+ screenId: string;
13
+ evidence: AnchorMatchResult[];
14
+ } | {
15
+ status: "ambiguous";
16
+ screenIds: string[];
17
+ evidence: AnchorMatchResult[];
18
+ } | {
19
+ status: "unknown";
20
+ evidence: AnchorMatchResult[];
21
+ };
22
+ export declare class ScreenDetector {
23
+ readonly detect: (input: {
24
+ snapshot: ScreenObservation;
25
+ anchors: readonly AnchorDefinition[];
26
+ screens: readonly ScreenDefinition[];
27
+ }) => ScreenDetectionResult;
28
+ }