taphound 0.2.0-dev.1 → 0.2.0-dev.11

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 (447) 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 +500 -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 +561 -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 +349 -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 +34 -0
  66. package/dist/adapters/appium/appium-ui-snapshot-provider.js +240 -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/external-flow-registry.d.ts +17 -0
  73. package/dist/adapters/filesystem/external-flow-registry.js +325 -0
  74. package/dist/adapters/filesystem/generation-session-store.d.ts +17 -11
  75. package/dist/adapters/filesystem/generation-session-store.js +281 -855
  76. package/dist/adapters/filesystem/generation-store/session-lock.d.ts +29 -0
  77. package/dist/adapters/filesystem/generation-store/session-lock.js +202 -0
  78. package/dist/adapters/filesystem/generation-store/session-transitions.d.ts +24 -0
  79. package/dist/adapters/filesystem/generation-store/session-transitions.js +340 -0
  80. package/dist/adapters/filesystem/generation-store/store-files.d.ts +50 -0
  81. package/dist/adapters/filesystem/generation-store/store-files.js +363 -0
  82. package/dist/adapters/filesystem/journey-composition-store.d.ts +23 -0
  83. package/dist/adapters/filesystem/journey-composition-store.js +151 -0
  84. package/dist/adapters/filesystem/knowledge-registry.d.ts +12 -0
  85. package/dist/adapters/filesystem/knowledge-registry.js +223 -0
  86. package/dist/adapters/filesystem/project-bound-file.js +3 -5
  87. package/dist/adapters/filesystem/project-file-inspector.d.ts +2 -0
  88. package/dist/adapters/filesystem/project-file-inspector.js +57 -28
  89. package/dist/adapters/filesystem/project-identity-inspector.d.ts +7 -0
  90. package/dist/adapters/filesystem/project-identity-inspector.js +215 -0
  91. package/dist/adapters/filesystem/project-inventory-inspector.d.ts +8 -0
  92. package/dist/adapters/filesystem/project-inventory-inspector.js +93 -0
  93. package/dist/adapters/filesystem/project-module-discoverer.d.ts +6 -0
  94. package/dist/adapters/filesystem/project-module-discoverer.js +158 -0
  95. package/dist/adapters/filesystem/skill-installer.d.ts +7 -2
  96. package/dist/adapters/filesystem/skill-installer.js +40 -11
  97. package/dist/adapters/filesystem/workspace-layout.d.ts +7 -0
  98. package/dist/adapters/filesystem/workspace-layout.js +57 -0
  99. package/dist/adapters/git/node-git-diff.d.ts +17 -0
  100. package/dist/adapters/git/node-git-diff.js +114 -0
  101. package/dist/adapters/process/node-detached-process-launcher.d.ts +6 -0
  102. package/dist/adapters/process/node-detached-process-launcher.js +96 -0
  103. package/dist/adapters/process/node-process-runner.js +11 -2
  104. package/dist/adapters/prompt/inquirer-align-prompt.d.ts +27 -0
  105. package/dist/adapters/prompt/inquirer-align-prompt.js +65 -0
  106. package/dist/adapters/prompt/inquirer-generation-prompt.js +3 -0
  107. package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +9 -1
  108. package/dist/adapters/prompt/inquirer-recorder-prompt.js +63 -0
  109. package/dist/adapters/runtime/adb-runtime-backend.d.ts +67 -0
  110. package/dist/adapters/runtime/adb-runtime-backend.js +206 -0
  111. package/dist/adapters/runtime/fake-runtime-backend.d.ts +87 -0
  112. package/dist/adapters/runtime/fake-runtime-backend.js +240 -0
  113. package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.d.ts +45 -0
  114. package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.js +153 -0
  115. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.d.ts +23 -0
  116. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.js +39 -0
  117. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.d.ts +20 -0
  118. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.js +180 -0
  119. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.d.ts +78 -0
  120. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.js +379 -0
  121. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-tools.d.ts +46 -0
  122. package/dist/adapters/runtime/session-adb-view.d.ts +53 -0
  123. package/dist/adapters/runtime/session-adb-view.js +193 -0
  124. package/dist/adapters/runtime/session-backed-ports.d.ts +21 -0
  125. package/dist/adapters/runtime/session-backed-ports.js +43 -0
  126. package/dist/adapters/runtime/shared-session-runtime-backend.d.ts +12 -0
  127. package/dist/adapters/runtime/shared-session-runtime-backend.js +64 -0
  128. package/dist/adapters/ui/auto-ui-snapshot-provider.d.ts +8 -0
  129. package/dist/adapters/ui/auto-ui-snapshot-provider.js +48 -0
  130. package/dist/adapters/ui/device-layout-temp.d.ts +6 -0
  131. package/dist/adapters/ui/device-layout-temp.js +18 -0
  132. package/dist/adapters/ui/device-ui-environment.d.ts +13 -0
  133. package/dist/adapters/ui/device-ui-environment.js +81 -0
  134. package/dist/adapters/ui/layout-normalization.d.ts +9 -0
  135. package/dist/adapters/ui/layout-normalization.js +44 -0
  136. package/dist/adapters/ui/ui-snapshot-error.d.ts +11 -0
  137. package/dist/adapters/ui/ui-snapshot-error.js +12 -0
  138. package/dist/adapters/ui/ui-snapshot-support.d.ts +15 -0
  139. package/dist/adapters/ui/ui-snapshot-support.js +12 -0
  140. package/dist/application/align/align-service.d.ts +42 -0
  141. package/dist/application/align/align-service.js +106 -0
  142. package/dist/application/assertion/expectation-evaluator.d.ts +18 -5
  143. package/dist/application/assertion/expectation-evaluator.js +156 -18
  144. package/dist/application/assertion/guarded-expectation.d.ts +45 -0
  145. package/dist/application/assertion/guarded-expectation.js +74 -0
  146. package/dist/application/checkpoint/baseline-capturer.d.ts +26 -0
  147. package/dist/application/checkpoint/baseline-capturer.js +163 -0
  148. package/dist/application/checkpoint/baseline-error.d.ts +5 -0
  149. package/dist/application/checkpoint/baseline-error.js +8 -0
  150. package/dist/application/checkpoint/baseline-service.d.ts +31 -0
  151. package/dist/application/checkpoint/baseline-service.js +147 -0
  152. package/dist/application/checkpoint/checkpoint-evaluator.d.ts +35 -0
  153. package/dist/application/checkpoint/checkpoint-evaluator.js +415 -0
  154. package/dist/application/checkpoint/regression-comparator.d.ts +18 -0
  155. package/dist/application/checkpoint/regression-comparator.js +192 -0
  156. package/dist/application/collector/logcat-collector.d.ts +22 -1
  157. package/dist/application/collector/logcat-collector.js +103 -15
  158. package/dist/application/collector/logcat-event.d.ts +8 -0
  159. package/dist/application/collector/logcat-event.js +79 -0
  160. package/dist/application/collector/logcat-stop.d.ts +2 -0
  161. package/dist/application/collector/logcat-stop.js +11 -0
  162. package/dist/application/context/context-generator.d.ts +45 -0
  163. package/dist/application/context/context-generator.js +228 -0
  164. package/dist/application/context/context-loader.d.ts +40 -0
  165. package/dist/application/context/context-loader.js +223 -0
  166. package/dist/application/context/context-refresher.d.ts +59 -0
  167. package/dist/application/context/context-refresher.js +349 -0
  168. package/dist/application/context/context-rehasher.d.ts +36 -0
  169. package/dist/application/context/context-rehasher.js +110 -0
  170. package/dist/application/context/context-validator.d.ts +23 -1
  171. package/dist/application/context/context-validator.js +116 -9
  172. package/dist/application/context/evidence-hash.d.ts +1 -0
  173. package/dist/application/context/evidence-hash.js +125 -0
  174. package/dist/application/context/evidence-match.d.ts +9 -0
  175. package/dist/application/context/evidence-match.js +8 -0
  176. package/dist/application/context/shard-identity.d.ts +2 -0
  177. package/dist/application/context/shard-identity.js +7 -0
  178. package/dist/application/contract/contract-loader.d.ts +25 -0
  179. package/dist/application/contract/contract-loader.js +68 -0
  180. package/dist/application/contract/contract-review.d.ts +25 -0
  181. package/dist/application/contract/contract-review.js +51 -0
  182. package/dist/application/contract/contract-verifier.d.ts +53 -0
  183. package/dist/application/contract/contract-verifier.js +645 -0
  184. package/dist/application/devices/resolve-device-assignments.d.ts +21 -0
  185. package/dist/application/devices/resolve-device-assignments.js +60 -0
  186. package/dist/application/diagnosis/failure-classifier.d.ts +23 -0
  187. package/dist/application/diagnosis/failure-classifier.js +179 -0
  188. package/dist/application/doctor/doctor-service.d.ts +36 -3
  189. package/dist/application/doctor/doctor-service.js +255 -25
  190. package/dist/application/generation/generation-app-preparer.d.ts +20 -0
  191. package/dist/application/generation/generation-app-preparer.js +28 -0
  192. package/dist/application/generation/generation-config-service.d.ts +21 -0
  193. package/dist/application/generation/generation-config-service.js +53 -0
  194. package/dist/application/generation/generation-confirmation-service.d.ts +14 -3
  195. package/dist/application/generation/generation-confirmation-service.js +109 -24
  196. package/dist/application/generation/generation-context-snapshot.d.ts +6 -0
  197. package/dist/application/generation/generation-context-snapshot.js +29 -0
  198. package/dist/application/generation/generation-finalizer.d.ts +15 -9
  199. package/dist/application/generation/generation-finalizer.js +167 -70
  200. package/dist/application/generation/generation-publisher.js +12 -8
  201. package/dist/application/generation/generation-recovery-service.d.ts +35 -0
  202. package/dist/application/generation/generation-recovery-service.js +144 -0
  203. package/dist/application/generation/generation-reopen-service.d.ts +15 -0
  204. package/dist/application/generation/generation-reopen-service.js +37 -0
  205. package/dist/application/generation/generation-replace-service.d.ts +32 -0
  206. package/dist/application/generation/generation-replace-service.js +116 -0
  207. package/dist/application/generation/generation-starter.d.ts +44 -4
  208. package/dist/application/generation/generation-starter.js +175 -8
  209. package/dist/application/generation/generation-step-executor.d.ts +67 -5
  210. package/dist/application/generation/generation-step-executor.js +471 -108
  211. package/dist/application/generation/proposed-step-validator.js +96 -30
  212. package/dist/application/generation/replay-policy-loader.d.ts +16 -0
  213. package/dist/application/generation/replay-policy-loader.js +29 -0
  214. package/dist/application/generation/risk-evaluator.d.ts +4 -1
  215. package/dist/application/generation/risk-evaluator.js +118 -3
  216. package/dist/application/generation/runtime-observer.d.ts +39 -7
  217. package/dist/application/generation/runtime-observer.js +193 -24
  218. package/dist/application/impact/impact-resolver.d.ts +32 -0
  219. package/dist/application/impact/impact-resolver.js +160 -0
  220. package/dist/application/init/init-service.js +26 -13
  221. package/dist/application/interaction/action-executor.d.ts +3 -2
  222. package/dist/application/interaction/action-executor.js +11 -1
  223. package/dist/application/interaction/action-target.d.ts +17 -0
  224. package/dist/application/interaction/action-target.js +40 -0
  225. package/dist/application/interaction/external-step-runner.d.ts +153 -0
  226. package/dist/application/interaction/external-step-runner.js +332 -0
  227. package/dist/application/interaction/fallback-resolver.d.ts +6 -3
  228. package/dist/application/interaction/fallback-resolver.js +7 -5
  229. package/dist/application/interaction/scroll-to-executor.d.ts +17 -3
  230. package/dist/application/interaction/scroll-to-executor.js +118 -36
  231. package/dist/application/journey/external-flow-resolver.d.ts +25 -0
  232. package/dist/application/journey/external-flow-resolver.js +22 -0
  233. package/dist/application/journey/journey-check-service.d.ts +54 -0
  234. package/dist/application/journey/journey-check-service.js +207 -0
  235. package/dist/application/journey/journey-promoter.d.ts +26 -0
  236. package/dist/application/journey/journey-promoter.js +123 -0
  237. package/dist/application/journey/journey-resolver.d.ts +42 -0
  238. package/dist/application/journey/journey-resolver.js +239 -0
  239. package/dist/application/journey/journey-retirer.d.ts +25 -0
  240. package/dist/application/journey/journey-retirer.js +87 -0
  241. package/dist/application/knowledge/anchor-resolver.d.ts +36 -0
  242. package/dist/application/knowledge/anchor-resolver.js +110 -0
  243. package/dist/application/knowledge/knowledge-loader.d.ts +16 -0
  244. package/dist/application/knowledge/knowledge-loader.js +36 -0
  245. package/dist/application/locator/layout-failure-summary.d.ts +2 -0
  246. package/dist/application/locator/layout-failure-summary.js +67 -0
  247. package/dist/application/locator/layout-traversal.d.ts +11 -0
  248. package/dist/application/locator/layout-traversal.js +18 -0
  249. package/dist/application/locator/locator-resolver.d.ts +13 -1
  250. package/dist/application/locator/locator-resolver.js +182 -18
  251. package/dist/application/observe/observe-service.d.ts +20 -0
  252. package/dist/application/observe/observe-service.js +91 -0
  253. package/dist/application/project/project-describer.d.ts +14 -6
  254. package/dist/application/project/project-describer.js +38 -5
  255. package/dist/application/recognition/screen-detector.d.ts +28 -0
  256. package/dist/application/recognition/screen-detector.js +111 -0
  257. package/dist/application/recorder/locator-selector.js +151 -17
  258. package/dist/application/recorder/recorder-service.d.ts +16 -4
  259. package/dist/application/recorder/recorder-service.js +420 -128
  260. package/dist/application/report/report-writer.js +1 -1
  261. package/dist/application/runtime/cold-launch.d.ts +28 -0
  262. package/dist/application/runtime/cold-launch.js +59 -0
  263. package/dist/application/runtime/step-runner.d.ts +49 -4
  264. package/dist/application/runtime/step-runner.js +721 -130
  265. package/dist/application/runtime/verify-runtime.d.ts +57 -7
  266. package/dist/application/runtime/verify-runtime.js +554 -174
  267. package/dist/application/ui/cached-ui-snapshot-provider.d.ts +45 -0
  268. package/dist/application/ui/cached-ui-snapshot-provider.js +111 -0
  269. package/dist/application/ui/ui-snapshot-lifecycle.d.ts +2 -0
  270. package/dist/application/ui/ui-snapshot-lifecycle.js +8 -0
  271. package/dist/application/ui/ui-stability-probe.d.ts +3 -0
  272. package/dist/application/ui/ui-stability-probe.js +8 -0
  273. package/dist/application/wait/idle-advice.d.ts +6 -0
  274. package/dist/application/wait/idle-advice.js +27 -0
  275. package/dist/application/wait/idle-profiles.d.ts +15 -0
  276. package/dist/application/wait/idle-profiles.js +75 -0
  277. package/dist/application/wait/idle-waiter.d.ts +28 -4
  278. package/dist/application/wait/idle-waiter.js +186 -15
  279. package/dist/cli/commands/align.d.ts +3 -0
  280. package/dist/cli/commands/align.js +124 -0
  281. package/dist/cli/commands/baseline.d.ts +3 -0
  282. package/dist/cli/commands/baseline.js +130 -0
  283. package/dist/cli/commands/context.js +300 -27
  284. package/dist/cli/commands/contract.d.ts +3 -0
  285. package/dist/cli/commands/contract.js +120 -0
  286. package/dist/cli/commands/doctor.js +16 -9
  287. package/dist/cli/commands/failure.d.ts +3 -0
  288. package/dist/cli/commands/failure.js +63 -0
  289. package/dist/cli/commands/generation/lifecycle-commands.d.ts +5 -0
  290. package/dist/cli/commands/generation/lifecycle-commands.js +211 -0
  291. package/dist/cli/commands/generation/session-commands.d.ts +11 -0
  292. package/dist/cli/commands/generation/session-commands.js +472 -0
  293. package/dist/cli/commands/generation/shared.d.ts +1414 -0
  294. package/dist/cli/commands/generation/shared.js +291 -0
  295. package/dist/cli/commands/generation/step-commands.d.ts +8 -0
  296. package/dist/cli/commands/generation/step-commands.js +315 -0
  297. package/dist/cli/commands/generation.js +10 -507
  298. package/dist/cli/commands/impact.d.ts +3 -0
  299. package/dist/cli/commands/impact.js +87 -0
  300. package/dist/cli/commands/init.js +13 -6
  301. package/dist/cli/commands/journey.d.ts +3 -0
  302. package/dist/cli/commands/journey.js +382 -0
  303. package/dist/cli/commands/knowledge.d.ts +3 -0
  304. package/dist/cli/commands/knowledge.js +93 -0
  305. package/dist/cli/commands/observe.d.ts +3 -0
  306. package/dist/cli/commands/observe.js +127 -0
  307. package/dist/cli/commands/project.js +2 -1
  308. package/dist/cli/commands/record.js +18 -5
  309. package/dist/cli/commands/verify.js +273 -63
  310. package/dist/cli/dependencies.d.ts +125 -1
  311. package/dist/cli/dependencies.js +450 -51
  312. package/dist/cli/diff-verification.d.ts +41 -0
  313. package/dist/cli/diff-verification.js +192 -0
  314. package/dist/cli/main.js +49 -1
  315. package/dist/cli/output.d.ts +5 -3
  316. package/dist/cli/output.js +12 -0
  317. package/dist/cli/program.js +19 -1
  318. package/dist/cli/project-root.d.ts +1 -0
  319. package/dist/cli/project-root.js +6 -0
  320. package/dist/cli/runtime-selection.d.ts +22 -0
  321. package/dist/cli/runtime-selection.js +92 -0
  322. package/dist/cli/version.d.ts +1 -0
  323. package/dist/cli/version.js +14 -0
  324. package/dist/cli/workspace-guard.d.ts +2 -0
  325. package/dist/cli/workspace-guard.js +3 -0
  326. package/dist/domain/binding-reference.d.ts +4 -0
  327. package/dist/domain/binding-reference.js +23 -0
  328. package/dist/domain/checkpoint.d.ts +404 -0
  329. package/dist/domain/checkpoint.js +297 -0
  330. package/dist/domain/config.d.ts +107 -1
  331. package/dist/domain/config.js +52 -6
  332. package/dist/domain/contract.d.ts +413 -0
  333. package/dist/domain/contract.js +196 -0
  334. package/dist/domain/external-flow.d.ts +493 -0
  335. package/dist/domain/external-flow.js +15 -0
  336. package/dist/domain/failure-classification.d.ts +176 -0
  337. package/dist/domain/failure-classification.js +169 -0
  338. package/dist/domain/failure.d.ts +2 -1
  339. package/dist/domain/failure.js +123 -0
  340. package/dist/domain/generation.d.ts +1671 -132
  341. package/dist/domain/generation.js +202 -13
  342. package/dist/domain/geometry.d.ts +13 -0
  343. package/dist/domain/geometry.js +16 -0
  344. package/dist/domain/impact.d.ts +71 -0
  345. package/dist/domain/impact.js +76 -0
  346. package/dist/domain/init.d.ts +12 -4
  347. package/dist/domain/init.js +24 -18
  348. package/dist/domain/journey-composition.d.ts +2285 -0
  349. package/dist/domain/journey-composition.js +85 -0
  350. package/dist/domain/journey-lifecycle.d.ts +10 -0
  351. package/dist/domain/journey-lifecycle.js +9 -0
  352. package/dist/domain/journey.d.ts +2419 -212
  353. package/dist/domain/journey.js +384 -14
  354. package/dist/domain/knowledge.d.ts +165 -0
  355. package/dist/domain/knowledge.js +188 -0
  356. package/dist/domain/layout.d.ts +34 -5
  357. package/dist/domain/layout.js +64 -6
  358. package/dist/domain/locator-evidence.d.ts +3 -0
  359. package/dist/domain/locator-evidence.js +36 -0
  360. package/dist/domain/logcat-event.d.ts +54 -0
  361. package/dist/domain/logcat-event.js +40 -0
  362. package/dist/domain/observation.d.ts +33 -0
  363. package/dist/domain/observation.js +18 -0
  364. package/dist/domain/project-context.d.ts +219 -1
  365. package/dist/domain/project-context.js +209 -6
  366. package/dist/domain/proposed-step.d.ts +340 -60
  367. package/dist/domain/proposed-step.js +23 -3
  368. package/dist/domain/report.d.ts +1455 -28
  369. package/dist/domain/report.js +211 -14
  370. package/dist/domain/runtime-snapshot.d.ts +58 -1
  371. package/dist/domain/runtime-snapshot.js +14 -3
  372. package/dist/domain/runtime.d.ts +50 -0
  373. package/dist/domain/runtime.js +36 -0
  374. package/dist/domain/system-app-profiles.d.ts +6 -0
  375. package/dist/domain/system-app-profiles.js +43 -0
  376. package/dist/domain/ui-backend.d.ts +28 -0
  377. package/dist/domain/ui-backend.js +33 -0
  378. package/dist/domain/ui-cache.d.ts +11 -0
  379. package/dist/domain/ui-cache.js +10 -0
  380. package/dist/domain/window-hierarchy.d.ts +84 -0
  381. package/dist/domain/window-hierarchy.js +172 -0
  382. package/dist/domain/workflow-manifest.d.ts +77 -0
  383. package/dist/domain/workflow-manifest.js +136 -0
  384. package/dist/domain/workspace.d.ts +28 -0
  385. package/dist/domain/workspace.js +72 -0
  386. package/dist/ports/adb.d.ts +34 -1
  387. package/dist/ports/align-prompt.d.ts +11 -0
  388. package/dist/ports/align-prompt.js +6 -0
  389. package/dist/ports/anchor-resolver.d.ts +30 -0
  390. package/dist/ports/anchor-resolver.js +1 -0
  391. package/dist/ports/annotated-screen-resolver.d.ts +4 -0
  392. package/dist/ports/annotated-screen-resolver.js +1 -0
  393. package/dist/ports/camera-probe.d.ts +23 -0
  394. package/dist/ports/camera-probe.js +8 -0
  395. package/dist/ports/context-document-writer.d.ts +24 -0
  396. package/dist/ports/context-document-writer.js +1 -0
  397. package/dist/ports/detached-process-launcher.d.ts +12 -0
  398. package/dist/ports/detached-process-launcher.js +1 -0
  399. package/dist/ports/external-flow-registry.d.ts +37 -0
  400. package/dist/ports/external-flow-registry.js +1 -0
  401. package/dist/ports/generation-prompt.d.ts +2 -1
  402. package/dist/ports/generation-session-store.d.ts +12 -2
  403. package/dist/ports/git-diff.d.ts +9 -0
  404. package/dist/ports/git-diff.js +1 -0
  405. package/dist/ports/journey-composition-store.d.ts +17 -0
  406. package/dist/ports/journey-composition-store.js +1 -0
  407. package/dist/ports/knowledge-registry.d.ts +20 -0
  408. package/dist/ports/knowledge-registry.js +1 -0
  409. package/dist/ports/process-runner.d.ts +7 -0
  410. package/dist/ports/project-file-inspector.d.ts +1 -0
  411. package/dist/ports/project-identity-inspector.d.ts +24 -0
  412. package/dist/ports/project-identity-inspector.js +1 -0
  413. package/dist/ports/project-inventory-inspector.d.ts +21 -0
  414. package/dist/ports/project-inventory-inspector.js +1 -0
  415. package/dist/ports/project-module-discoverer.d.ts +26 -0
  416. package/dist/ports/project-module-discoverer.js +1 -0
  417. package/dist/ports/recorder-prompt.d.ts +10 -1
  418. package/dist/ports/runtime-backend.d.ts +89 -0
  419. package/dist/ports/runtime-backend.js +13 -0
  420. package/dist/ports/runtime-capability.d.ts +12 -0
  421. package/dist/ports/runtime-capability.js +20 -0
  422. package/dist/ports/runtime-session-ports.d.ts +25 -0
  423. package/dist/ports/runtime-session-ports.js +1 -0
  424. package/dist/ports/screenshot.d.ts +11 -0
  425. package/dist/ports/screenshot.js +1 -0
  426. package/dist/ports/skill-installer.d.ts +2 -2
  427. package/dist/ports/ui-snapshot.d.ts +35 -0
  428. package/dist/ports/ui-snapshot.js +1 -0
  429. package/dist/ports/ui-stability.d.ts +19 -0
  430. package/dist/ports/ui-stability.js +1 -0
  431. package/dist/ports/workspace-layout.d.ts +4 -0
  432. package/dist/ports/workspace-layout.js +1 -0
  433. package/dist/shared/errors.d.ts +2 -0
  434. package/dist/shared/errors.js +22 -0
  435. package/dist/shared/paths.d.ts +2 -0
  436. package/dist/shared/paths.js +24 -0
  437. package/dist/shared/strings.d.ts +2 -0
  438. package/dist/shared/strings.js +16 -0
  439. package/package.json +6 -1
  440. package/assets/skills/taphound-ai-journey/GUIDE.md +0 -862
  441. package/assets/skills/taphound-ai-journey/SKILL.md +0 -377
  442. package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +0 -526
  443. package/assets/skills/taphound-ai-journey/prompts/generate-step.md +0 -109
  444. package/assets/skills/taphound-ai-journey/schemas/project-context.json +0 -81
  445. package/assets/skills/taphound-ai-journey/templates/project-context.example.json +0 -49
  446. package/dist/ports/android-cli.d.ts +0 -21
  447. /package/dist/{ports/android-cli.js → adapters/runtime/mobile-mcp/mobile-mcp-tools.js} +0 -0
@@ -0,0 +1,60 @@
1
+ function failure(code, message, missingRoles) {
2
+ return {
3
+ ok: false,
4
+ code,
5
+ message,
6
+ ...(missingRoles === undefined ? {} : { missingRoles })
7
+ };
8
+ }
9
+ export function resolveDeviceAssignments(input) {
10
+ const { declaredRoles, explicit, onlineSerials } = input;
11
+ const duplicateRole = explicit.find((mapping, index) => explicit.some((other, otherIndex) => otherIndex > index && other.role === mapping.role));
12
+ if (duplicateRole !== undefined) {
13
+ return failure("CONFIG_INVALID", `Duplicate --device mapping for role: ${duplicateRole.role}`);
14
+ }
15
+ const unknownRole = explicit.find((mapping) => !declaredRoles.includes(mapping.role));
16
+ if (unknownRole !== undefined) {
17
+ return failure("CONFIG_INVALID", `Device role is not declared by the journey: ${unknownRole.role}`);
18
+ }
19
+ const reusedSerial = explicit.find((mapping, index) => explicit.some((other, otherIndex) => otherIndex > index
20
+ && other.deviceSerial === mapping.deviceSerial));
21
+ if (reusedSerial !== undefined) {
22
+ return failure("CONFIG_INVALID", `Device serial is mapped to multiple roles: ${reusedSerial.deviceSerial}`);
23
+ }
24
+ const online = new Set(onlineSerials);
25
+ const offlineSerial = explicit.find((mapping) => !online.has(mapping.deviceSerial));
26
+ if (offlineSerial !== undefined) {
27
+ return failure("DEVICE_UNAVAILABLE", `Requested device is not online: ${offlineSerial.deviceSerial}`);
28
+ }
29
+ if (explicit.length === 0) {
30
+ if (declaredRoles.length > 1) {
31
+ return failure("DEVICE_ROLE_UNMAPPED", `Journey declares multiple devices; map each role with --device <role>=<serial> for: ${declaredRoles.join(", ")}`, [...declaredRoles]);
32
+ }
33
+ if (onlineSerials.length !== 1) {
34
+ return failure("DEVICE_UNAVAILABLE", `Expected exactly one online device, found ${String(onlineSerials.length)}`);
35
+ }
36
+ const autoSerial = onlineSerials[0];
37
+ const autoRole = declaredRoles[0];
38
+ if (autoSerial === undefined || autoRole === undefined) {
39
+ return failure("DEVICE_UNAVAILABLE", "Expected exactly one online device, found 1");
40
+ }
41
+ return {
42
+ ok: true,
43
+ assignments: [{ role: autoRole, deviceSerial: autoSerial }]
44
+ };
45
+ }
46
+ const explicitRoles = new Set(explicit.map((mapping) => mapping.role));
47
+ const missingRoles = declaredRoles.filter((role) => !explicitRoles.has(role));
48
+ if (missingRoles.length > 0) {
49
+ return failure("DEVICE_ROLE_UNMAPPED", `No device mapping for journey role(s): ${missingRoles.join(", ")}`, missingRoles);
50
+ }
51
+ return {
52
+ ok: true,
53
+ assignments: declaredRoles.flatMap((role) => {
54
+ const mapping = explicit.find((entry) => entry.role === role);
55
+ return mapping === undefined
56
+ ? []
57
+ : [{ role, deviceSerial: mapping.deviceSerial }];
58
+ })
59
+ };
60
+ }
@@ -0,0 +1,23 @@
1
+ import { type FailureClassification } from "../../domain/failure-classification.js";
2
+ import type { TapHoundReport } from "../../domain/report.js";
3
+ export interface FailureClassifierInput {
4
+ report: TapHoundReport;
5
+ classificationId?: string | undefined;
6
+ }
7
+ export interface FailureClassifierDependencies {
8
+ now: () => Date;
9
+ }
10
+ /**
11
+ * Deterministic Failure Classifier (architecture doc §20, P1.5).
12
+ *
13
+ * Maps a completed run report into a concise, structured failure contract for
14
+ * a Coding Agent: type, likely stage, expected vs actual, step index, locator,
15
+ * and evidence refs. No model call, no log dump; raw evidence stays on disk
16
+ * and is referenced by path. Classification is pure derivation from the
17
+ * published report.
18
+ */
19
+ export declare class FailureClassifier {
20
+ private readonly dependencies;
21
+ constructor(dependencies: FailureClassifierDependencies);
22
+ readonly classify: (input: FailureClassifierInput) => FailureClassification;
23
+ }
@@ -0,0 +1,179 @@
1
+ import { FAILURE_CODE_TYPES, FAILURE_TYPE_STAGES, FailureClassificationSchema } from "../../domain/failure-classification.js";
2
+ function expectedActualFor(type, report) {
3
+ const primary = report.primaryFailure;
4
+ if (primary === undefined) {
5
+ return {};
6
+ }
7
+ if (type === "activity_mismatch") {
8
+ const step = report.steps.find((candidate) => (candidate.status === "failed"));
9
+ const check = step?.activity?.after.status === "failed"
10
+ ? step.activity.after
11
+ : step?.activity?.before.status === "failed"
12
+ ? step.activity.before
13
+ : undefined;
14
+ if (check !== undefined) {
15
+ return {
16
+ expected: check.expected,
17
+ actual: check.actual ?? "missing"
18
+ };
19
+ }
20
+ }
21
+ if (type === "expect_failed") {
22
+ const step = report.steps.find((candidate) => (candidate.expectation?.status === "failed"));
23
+ const expectation = step?.expectation;
24
+ if (expectation !== undefined) {
25
+ return {
26
+ expected: expectation.type,
27
+ actual: expectation.message ?? "missing"
28
+ };
29
+ }
30
+ }
31
+ if (type === "target_not_found"
32
+ || type === "target_ambiguous"
33
+ || type === "target_unresolved") {
34
+ const step = report.steps.find((candidate) => (candidate.status === "failed"));
35
+ const locator = step?.locator;
36
+ if (locator !== undefined) {
37
+ const source = locator.anchorId ?? locator.message ?? locator.matchedBy;
38
+ if (source === undefined) {
39
+ return {};
40
+ }
41
+ return {
42
+ expected: locator.status === "found"
43
+ ? `found ${source}`
44
+ : source,
45
+ actual: locator.status === "failed"
46
+ ? "not found in layout"
47
+ : locator.status
48
+ };
49
+ }
50
+ }
51
+ if (type === "app_crash") {
52
+ return {
53
+ expected: "process alive",
54
+ actual: "process exited"
55
+ };
56
+ }
57
+ if (type === "contract_invalid" || type === "evidence_failed") {
58
+ return {
59
+ expected: "binding satisfied",
60
+ actual: primary.message
61
+ };
62
+ }
63
+ return {};
64
+ }
65
+ function evidenceRefsFor(report) {
66
+ const refs = [];
67
+ for (const screenshot of report.artifacts.screenshots) {
68
+ refs.push(screenshot.path);
69
+ }
70
+ for (const logcat of report.artifacts.logcats) {
71
+ refs.push(logcat.path);
72
+ }
73
+ for (const stepLog of report.artifacts.stepLogs) {
74
+ refs.push(stepLog);
75
+ }
76
+ const failedStepLog = report.steps.find((step) => (step.status === "failed" && step.logcatPath !== undefined));
77
+ if (failedStepLog?.logcatPath !== undefined) {
78
+ refs.push(failedStepLog.logcatPath);
79
+ }
80
+ return refs;
81
+ }
82
+ function requestOutcomeFor(report) {
83
+ const failedIndex = report.primaryFailure?.stepIndex;
84
+ const events = report.steps.filter((step) => (step.expectation?.type === "logcatEvent"
85
+ && step.expectation.status === "passed"
86
+ && step.expectation.logcatEvent?.matchedCount === 1
87
+ && step.expectation.logcatEvent.matchedLineSha256 !== undefined
88
+ && (failedIndex === undefined || step.index <= failedIndex)));
89
+ if (events.length === 0) {
90
+ return { errorClass: "unknown", evidenceRefs: [] };
91
+ }
92
+ const classes = new Set(events.map((step) => step.expectation?.logcatEvent?.requestErrorClass));
93
+ const errorClass = classes.size === 1
94
+ ? [...classes][0] ?? "unknown"
95
+ : "unknown";
96
+ const refs = new Set([report.artifacts.report]);
97
+ for (const step of events) {
98
+ if (step.logcatPath !== undefined)
99
+ refs.add(step.logcatPath);
100
+ }
101
+ return { errorClass, evidenceRefs: [...refs] };
102
+ }
103
+ function locatorFor(type, report) {
104
+ if (type !== "target_not_found"
105
+ && type !== "target_ambiguous"
106
+ && type !== "target_unresolved") {
107
+ return undefined;
108
+ }
109
+ const locator = report.steps.find((step) => step.status === "failed")?.locator;
110
+ if (locator === undefined) {
111
+ return undefined;
112
+ }
113
+ // Rebuild a canonical Locator from a report locator: report locators carry
114
+ // runtime status fields the domain Locator does not accept.
115
+ if (locator.anchorId !== undefined) {
116
+ return { resourceId: `anchor:${locator.anchorId}` };
117
+ }
118
+ if (locator.matchedBy === "resourceId") {
119
+ return { resourceId: locator.message ?? "resourceId" };
120
+ }
121
+ if (locator.matchedBy === "text") {
122
+ return { text: locator.message ?? "text" };
123
+ }
124
+ if (locator.matchedBy === "contentDescription") {
125
+ return { contentDescription: locator.message ?? "contentDescription" };
126
+ }
127
+ return { resourceId: "unknown" };
128
+ }
129
+ /**
130
+ * Deterministic Failure Classifier (architecture doc §20, P1.5).
131
+ *
132
+ * Maps a completed run report into a concise, structured failure contract for
133
+ * a Coding Agent: type, likely stage, expected vs actual, step index, locator,
134
+ * and evidence refs. No model call, no log dump; raw evidence stays on disk
135
+ * and is referenced by path. Classification is pure derivation from the
136
+ * published report.
137
+ */
138
+ export class FailureClassifier {
139
+ dependencies;
140
+ constructor(dependencies) {
141
+ this.dependencies = dependencies;
142
+ }
143
+ classify = (input) => {
144
+ const { report } = input;
145
+ const primary = report.primaryFailure;
146
+ if (primary === undefined) {
147
+ throw new Error(`Cannot classify run ${report.runId}: the report has no primary failure`);
148
+ }
149
+ const type = FAILURE_CODE_TYPES[primary.code] ?? "internal_error";
150
+ const stage = FAILURE_TYPE_STAGES[type];
151
+ const expectedActual = expectedActualFor(type, report);
152
+ const locator = locatorFor(type, report);
153
+ const classificationId = input.classificationId
154
+ ?? `${report.runId}:${primary.code}:${String(primary.stepIndex ?? "-")}`;
155
+ const classification = FailureClassificationSchema.parse({
156
+ version: 1,
157
+ runId: report.runId,
158
+ classificationId,
159
+ type,
160
+ stage,
161
+ code: primary.code,
162
+ message: primary.message,
163
+ ...(primary.stepIndex === undefined
164
+ ? {}
165
+ : { stepIndex: primary.stepIndex }),
166
+ ...(expectedActual.expected === undefined
167
+ ? {}
168
+ : { expected: expectedActual.expected }),
169
+ ...(expectedActual.actual === undefined
170
+ ? {}
171
+ : { actual: expectedActual.actual }),
172
+ ...(locator === undefined ? {} : { locator }),
173
+ evidenceRefs: evidenceRefsFor(report),
174
+ requestOutcome: requestOutcomeFor(report),
175
+ sourceReportPath: report.artifacts.report
176
+ });
177
+ return classification;
178
+ };
179
+ }
@@ -1,35 +1,68 @@
1
1
  import type { FailureCode } from "../../domain/failure.js";
2
+ import type { RuntimeBackendId } from "../../domain/runtime.js";
3
+ import type { UiBackendSelection } from "../../domain/ui-backend.js";
2
4
  import type { AdbPort } from "../../ports/adb.js";
3
5
  import type { ProcessRunner } from "../../ports/process-runner.js";
4
- export type DoctorCheckName = "node" | "adb" | "android" | "app" | "permissions" | "device";
6
+ import type { DeviceAssignment } from "../devices/resolve-device-assignments.js";
7
+ export type DoctorCheckName = "node" | "adb" | "android" | "app" | "permissions" | "device" | "appium" | "mobile-mcp";
8
+ export type DoctorCheckStatus = "passed" | "failed" | "notRun";
5
9
  export interface DoctorCheck {
6
10
  name: DoctorCheckName;
7
- status: "passed" | "failed" | "notRun";
11
+ status: DoctorCheckStatus;
8
12
  version?: string | undefined;
9
13
  message?: string | undefined;
10
14
  }
15
+ export interface DoctorDeviceCheck {
16
+ role: string;
17
+ deviceSerial: string;
18
+ online: boolean;
19
+ app: DoctorCheckStatus;
20
+ permissions: DoctorCheckStatus;
21
+ message?: string | undefined;
22
+ }
11
23
  export interface DoctorReport {
12
24
  status: "passed" | "failed";
25
+ runtimeBackend: RuntimeBackendId;
13
26
  checks: DoctorCheck[];
14
27
  deviceSerial?: string | undefined;
28
+ devices?: readonly DoctorDeviceCheck[] | undefined;
15
29
  failureCode?: Extract<FailureCode, "ENVIRONMENT_MISSING_TOOL" | "DEVICE_UNAVAILABLE" | "APP_NOT_INSTALLED"> | undefined;
16
30
  }
17
31
  export interface DoctorRunInput {
18
32
  packageName?: string | undefined;
19
33
  requestedDevice?: string | undefined;
34
+ requestedDevices?: readonly DeviceAssignment[] | undefined;
20
35
  signal?: AbortSignal | undefined;
36
+ skipPermissionProbe?: boolean | undefined;
37
+ requestedUiBackend?: UiBackendSelection | undefined;
21
38
  }
22
39
  export interface DoctorDependencies {
23
40
  runner: ProcessRunner;
24
- adb: AdbPort;
41
+ /** Device listing and the install probe; each probe borrows a session. */
42
+ adb: Pick<AdbPort, "devices" | "isInstalled">;
25
43
  nodeVersion: string;
44
+ runtimeBackendId: RuntimeBackendId;
26
45
  checkAndroidPermissions: (deviceSerial: string, signal?: AbortSignal) => Promise<{
27
46
  status: "passed" | "failed";
28
47
  message?: string | undefined;
29
48
  }>;
49
+ checkAppiumUiAutomator2?: (signal?: AbortSignal) => Promise<{
50
+ status: "passed" | "failed";
51
+ version?: string | undefined;
52
+ message?: string | undefined;
53
+ }>;
54
+ checkMobileMcpServer?: (signal?: AbortSignal) => Promise<{
55
+ status: "passed" | "failed";
56
+ version?: string | undefined;
57
+ message?: string | undefined;
58
+ }>;
30
59
  }
31
60
  export declare class DoctorService {
32
61
  private readonly dependencies;
33
62
  constructor(dependencies: DoctorDependencies);
34
63
  run(input?: DoctorRunInput): Promise<DoctorReport>;
64
+ private environmentChecks;
65
+ private conclude;
66
+ private runSingleDevice;
67
+ private runForAssignments;
35
68
  }
@@ -19,7 +19,14 @@ export class DoctorService {
19
19
  this.dependencies = dependencies;
20
20
  }
21
21
  async run(input = {}) {
22
- const { packageName, requestedDevice, signal } = input;
22
+ const assignments = input.requestedDevices;
23
+ if (assignments !== undefined && assignments.length > 0) {
24
+ return this.runForAssignments(input, assignments);
25
+ }
26
+ return this.runSingleDevice(input);
27
+ }
28
+ async environmentChecks(input) {
29
+ const { signal } = input;
23
30
  const checks = [nodeCheck(this.dependencies.nodeVersion)];
24
31
  const tool = async (name, executable, args) => {
25
32
  try {
@@ -54,7 +61,106 @@ export class DoctorService {
54
61
  };
55
62
  }
56
63
  };
57
- checks.push(await tool("adb", "adb", ["version"]), await tool("android", "android", ["--version"]));
64
+ checks.push(await tool("adb", "adb", ["version"]));
65
+ if (this.dependencies.runtimeBackendId === "mobile-mcp") {
66
+ checks.push({
67
+ name: "android",
68
+ status: "notRun",
69
+ message: "Android CLI probe requires the adb runtime backend"
70
+ });
71
+ try {
72
+ const mobileMcp = await this.dependencies.checkMobileMcpServer?.(signal);
73
+ checks.push(mobileMcp === undefined ? {
74
+ name: "mobile-mcp",
75
+ status: "failed",
76
+ message: "Mobile MCP server check is not configured"
77
+ } : {
78
+ name: "mobile-mcp",
79
+ status: mobileMcp.status,
80
+ ...(mobileMcp.version === undefined ? {} : { version: mobileMcp.version }),
81
+ ...(mobileMcp.message === undefined ? {} : { message: mobileMcp.message })
82
+ });
83
+ }
84
+ catch (error) {
85
+ checks.push({
86
+ name: "mobile-mcp",
87
+ status: "failed",
88
+ message: error instanceof Error ? error.message : String(error)
89
+ });
90
+ }
91
+ }
92
+ else {
93
+ checks.push(await tool("android", "android", ["--version"]));
94
+ checks.push({
95
+ name: "mobile-mcp",
96
+ status: "notRun",
97
+ message: "Mobile MCP server probe requires the mobile-mcp runtime backend"
98
+ });
99
+ }
100
+ if (input.requestedUiBackend === "appium-uiautomator2") {
101
+ if (this.dependencies.runtimeBackendId === "mobile-mcp") {
102
+ checks.push({
103
+ name: "appium",
104
+ status: "failed",
105
+ message: "ui.backend=appium-uiautomator2 is not supported by the mobile-mcp runtime backend"
106
+ });
107
+ }
108
+ else {
109
+ try {
110
+ const appium = await this.dependencies.checkAppiumUiAutomator2?.(signal);
111
+ checks.push(appium === undefined ? {
112
+ name: "appium",
113
+ status: "failed",
114
+ message: "Appium UiAutomator2 check is not configured"
115
+ } : {
116
+ name: "appium",
117
+ status: appium.status,
118
+ ...(appium.version === undefined ? {} : { version: appium.version }),
119
+ ...(appium.message === undefined ? {} : { message: appium.message })
120
+ });
121
+ }
122
+ catch (error) {
123
+ checks.push({
124
+ name: "appium",
125
+ status: "failed",
126
+ message: error instanceof Error ? error.message : String(error)
127
+ });
128
+ }
129
+ }
130
+ }
131
+ else {
132
+ checks.push({
133
+ name: "appium",
134
+ status: "notRun",
135
+ message: "Appium probe requires ui.backend=appium-uiautomator2"
136
+ });
137
+ }
138
+ return checks;
139
+ }
140
+ conclude(checks, identity) {
141
+ const failedCheck = (name) => checks.some((check) => check.name === name && check.status === "failed");
142
+ const environmentFailed = ["node", "adb", "android", "appium", "mobile-mcp", "permissions"].some(failedCheck);
143
+ const failureCode = environmentFailed
144
+ ? "ENVIRONMENT_MISSING_TOOL"
145
+ : failedCheck("device")
146
+ ? "DEVICE_UNAVAILABLE"
147
+ : failedCheck("app")
148
+ ? "APP_NOT_INSTALLED"
149
+ : undefined;
150
+ return {
151
+ status: failureCode === undefined ? "passed" : "failed",
152
+ runtimeBackend: this.dependencies.runtimeBackendId,
153
+ checks,
154
+ ...(identity.deviceSerial === undefined
155
+ ? {}
156
+ : { deviceSerial: identity.deviceSerial }),
157
+ ...(identity.devices === undefined ? {} : { devices: identity.devices }),
158
+ ...(failureCode === undefined ? {} : { failureCode })
159
+ };
160
+ }
161
+ async runSingleDevice(input) {
162
+ const { packageName, requestedDevice, signal } = input;
163
+ const checks = await this.environmentChecks(input);
58
164
  let deviceSerial;
59
165
  let deviceCheck;
60
166
  try {
@@ -124,7 +230,14 @@ export class DoctorService {
124
230
  });
125
231
  }
126
232
  }
127
- if (deviceSerial === undefined) {
233
+ if (input.skipPermissionProbe === true) {
234
+ checks.push({
235
+ name: "permissions",
236
+ status: "notRun",
237
+ message: "Permission probe deferred to verification"
238
+ });
239
+ }
240
+ else if (deviceSerial === undefined) {
128
241
  checks.push({
129
242
  name: "permissions",
130
243
  status: "notRun",
@@ -151,28 +264,145 @@ export class DoctorService {
151
264
  }
152
265
  }
153
266
  checks.push(deviceCheck);
154
- const failedCheck = (name) => checks.some((check) => check.name === name && check.status === "failed");
155
- const environmentFailed = ["node", "adb", "android", "permissions"]
156
- .some(failedCheck);
157
- const failureCode = environmentFailed
158
- ? "ENVIRONMENT_MISSING_TOOL"
159
- : failedCheck("device")
160
- ? "DEVICE_UNAVAILABLE"
161
- : failedCheck("app")
162
- ? "APP_NOT_INSTALLED"
163
- : undefined;
164
- if (failureCode !== undefined) {
165
- return {
166
- status: "failed",
167
- checks,
168
- ...(deviceSerial === undefined ? {} : { deviceSerial }),
169
- failureCode
170
- };
171
- }
172
- return {
173
- status: "passed",
174
- checks,
267
+ return this.conclude(checks, {
175
268
  ...(deviceSerial === undefined ? {} : { deviceSerial })
176
- };
269
+ });
270
+ }
271
+ async runForAssignments(input, assignments) {
272
+ const { packageName, signal } = input;
273
+ const checks = await this.environmentChecks(input);
274
+ let onlineSerials;
275
+ let listingError;
276
+ try {
277
+ const devices = await this.dependencies.adb.devices(signal);
278
+ onlineSerials = new Set(devices
279
+ .filter((device) => device.status === "device")
280
+ .map((device) => device.serial));
281
+ }
282
+ catch (error) {
283
+ onlineSerials = new Set();
284
+ listingError = error instanceof Error ? error.message : String(error);
285
+ }
286
+ const deviceChecks = [];
287
+ const appFailures = [];
288
+ const permissionFailures = [];
289
+ for (const assignment of assignments) {
290
+ const online = onlineSerials.has(assignment.deviceSerial);
291
+ const problems = [];
292
+ if (!online) {
293
+ problems.push(listingError !== undefined
294
+ ? `Device listing failed: ${listingError}`
295
+ : `Requested device is not online: ${assignment.deviceSerial}`);
296
+ }
297
+ let app = "notRun";
298
+ if (online && packageName !== undefined) {
299
+ try {
300
+ const installed = await this.dependencies.adb.isInstalled({
301
+ packageName,
302
+ deviceSerial: assignment.deviceSerial,
303
+ ...(signal === undefined ? {} : { signal })
304
+ });
305
+ app = installed ? "passed" : "failed";
306
+ if (!installed) {
307
+ const message = `Package ${packageName} is not installed on ${assignment.deviceSerial}`;
308
+ appFailures.push(message);
309
+ problems.push(message);
310
+ }
311
+ }
312
+ catch (error) {
313
+ app = "failed";
314
+ const message = error instanceof Error
315
+ ? error.message
316
+ : String(error);
317
+ appFailures.push(message);
318
+ problems.push(message);
319
+ }
320
+ }
321
+ let permissions = "notRun";
322
+ if (online && input.skipPermissionProbe !== true) {
323
+ try {
324
+ const permission = await this.dependencies.checkAndroidPermissions(assignment.deviceSerial, signal);
325
+ permissions = permission.status;
326
+ if (permission.status === "failed") {
327
+ if (permission.message !== undefined) {
328
+ permissionFailures.push(permission.message);
329
+ problems.push(permission.message);
330
+ }
331
+ else {
332
+ permissionFailures.push(`Permission probe failed on ${assignment.deviceSerial}`);
333
+ }
334
+ }
335
+ }
336
+ catch (error) {
337
+ permissions = "failed";
338
+ const message = error instanceof Error
339
+ ? error.message
340
+ : String(error);
341
+ permissionFailures.push(message);
342
+ problems.push(message);
343
+ }
344
+ }
345
+ deviceChecks.push({
346
+ role: assignment.role,
347
+ deviceSerial: assignment.deviceSerial,
348
+ online,
349
+ app,
350
+ permissions,
351
+ ...(problems.length === 0 ? {} : { message: problems.join("; ") })
352
+ });
353
+ }
354
+ const offlineDevices = deviceChecks.filter((device) => !device.online);
355
+ const appRan = deviceChecks.some((device) => device.app !== "notRun");
356
+ const permissionsRan = deviceChecks.some((device) => device.permissions !== "notRun");
357
+ const appStatus = appFailures.length > 0
358
+ ? "failed"
359
+ : appRan
360
+ ? "passed"
361
+ : "notRun";
362
+ checks.push({
363
+ name: "app",
364
+ status: appStatus,
365
+ ...(appStatus === "failed"
366
+ ? { message: appFailures.join("; ") }
367
+ : appStatus === "passed"
368
+ ? { message: packageName }
369
+ : {
370
+ message: packageName === undefined
371
+ ? "Installed application probe requires a configured package"
372
+ : "Installed application probe requires an online selected device"
373
+ })
374
+ });
375
+ const permissionStatus = permissionFailures.length > 0
376
+ ? "failed"
377
+ : permissionsRan
378
+ ? "passed"
379
+ : "notRun";
380
+ checks.push({
381
+ name: "permissions",
382
+ status: permissionStatus,
383
+ ...(permissionStatus === "failed"
384
+ ? { message: permissionFailures.join("; ") }
385
+ : permissionStatus === "notRun"
386
+ ? {
387
+ message: input.skipPermissionProbe === true
388
+ ? "Permission probe deferred to verification"
389
+ : "Permission probe requires an online selected device"
390
+ }
391
+ : {})
392
+ });
393
+ checks.push({
394
+ name: "device",
395
+ status: offlineDevices.length === 0 ? "passed" : "failed",
396
+ ...(offlineDevices.length === 0
397
+ ? {
398
+ message: deviceChecks
399
+ .map((device) => `${device.role}=${device.deviceSerial}`)
400
+ .join(", ")
401
+ }
402
+ : {
403
+ message: `Requested devices are not online: ${offlineDevices.map((device) => device.deviceSerial).join(", ")}`
404
+ })
405
+ });
406
+ return this.conclude(checks, { devices: deviceChecks });
177
407
  }
178
408
  }
@@ -0,0 +1,20 @@
1
+ import type { TapHoundConfig } from "../../domain/config.js";
2
+ import type { Clock } from "../../ports/clock.js";
3
+ import { type RuntimeSessionOpener } from "../../ports/runtime-backend.js";
4
+ import type { RuntimeSessionPortViewsFactory } from "../../ports/runtime-session-ports.js";
5
+ export interface GenerationAppPreparationInput {
6
+ config: TapHoundConfig;
7
+ deviceSerial: string;
8
+ signal?: AbortSignal | undefined;
9
+ }
10
+ export interface GenerationAppPreparerDependencies {
11
+ sessions: RuntimeSessionOpener;
12
+ sessionPorts: RuntimeSessionPortViewsFactory;
13
+ clock: Clock;
14
+ }
15
+ export declare class GenerationAppPreparer {
16
+ private readonly dependencies;
17
+ constructor(dependencies: GenerationAppPreparerDependencies);
18
+ prepare(input: GenerationAppPreparationInput): Promise<void>;
19
+ private coldLaunch;
20
+ }