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,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
+ }
@@ -0,0 +1,29 @@
1
+ import { type DiagnosticsBundle } from "../../domain/diagnostics.js";
2
+ import type { DiagnosticsJournal } from "../../ports/diagnostics.js";
3
+ export interface DiagnosticsExporterDependencies {
4
+ journal: DiagnosticsJournal;
5
+ readJson: (path: string) => Promise<unknown>;
6
+ now: () => Date;
7
+ taphoundVersion: string;
8
+ host: DiagnosticsBundle["host"];
9
+ }
10
+ export interface DiagnosticsExportInput {
11
+ projectRoot: string;
12
+ /** Most recent journal events to include. */
13
+ eventLimit: number;
14
+ /** Most recent referenced runs to summarize. */
15
+ runLimit: number;
16
+ }
17
+ /**
18
+ * Builds a feedback bundle from the local journal and the reports it
19
+ * references. Only allowlisted structural facts survive: identifying strings
20
+ * become ordinal aliases or salted digests, and everything else is dropped.
21
+ * The result is validated against the strict bundle schema before return.
22
+ */
23
+ export declare class DiagnosticsExporter {
24
+ private readonly dependencies;
25
+ constructor(dependencies: DiagnosticsExporterDependencies);
26
+ export(input: DiagnosticsExportInput): Promise<DiagnosticsBundle>;
27
+ private readConfig;
28
+ private readReport;
29
+ }
@@ -0,0 +1,283 @@
1
+ import { createHmac } from "node:crypto";
2
+ import { join, resolve } from "node:path";
3
+ import { TapHoundConfigSchema } from "../../domain/config.js";
4
+ import { CommandEventSchema, DiagnosticsBundleSchema } from "../../domain/diagnostics.js";
5
+ import { TapHoundReportSchema } from "../../domain/report.js";
6
+ import { CONFIG_PATH, DEFAULT_ARTIFACTS_DIR } from "../../domain/workspace.js";
7
+ const TOKEN = /^[a-zA-Z][a-zA-Z\d]{0,31}$/;
8
+ const VERSION = /^[\w.+-]{1,64}$/;
9
+ const TOOL_NAMES = ["node", "adb", "android"];
10
+ const LOCATOR_FIELDS = ["resourceId", "text", "contentDescription", "index", "within"];
11
+ /** Stable ordinal names for identifying strings, shared across one bundle. */
12
+ class Aliases {
13
+ prefix;
14
+ values = new Map();
15
+ constructor(prefix) {
16
+ this.prefix = prefix;
17
+ }
18
+ of(value) {
19
+ let alias = this.values.get(value);
20
+ if (alias === undefined) {
21
+ alias = `${this.prefix}#${String(this.values.size + 1)}`;
22
+ this.values.set(value, alias);
23
+ }
24
+ return alias;
25
+ }
26
+ }
27
+ function canonical(value) {
28
+ if (Array.isArray(value))
29
+ return value.map(canonical);
30
+ if (value !== null && typeof value === "object") {
31
+ return Object.fromEntries(Object.entries(value)
32
+ .sort(([left], [right]) => left.localeCompare(right))
33
+ .map(([key, entry]) => [key, canonical(entry)]));
34
+ }
35
+ return value;
36
+ }
37
+ function parseEvents(lines) {
38
+ const events = [];
39
+ let skipped = 0;
40
+ for (const line of lines) {
41
+ try {
42
+ const parsed = CommandEventSchema.safeParse(JSON.parse(line));
43
+ if (parsed.success) {
44
+ events.push(parsed.data);
45
+ continue;
46
+ }
47
+ }
48
+ catch {
49
+ // A torn or foreign line is counted, never exported.
50
+ }
51
+ skipped += 1;
52
+ }
53
+ return { events, skipped };
54
+ }
55
+ function configSummary(config) {
56
+ return {
57
+ idle: {
58
+ strategy: config.idle.strategy,
59
+ pollIntervalMs: config.idle.pollIntervalMs,
60
+ stablePolls: config.idle.stablePolls,
61
+ timeoutMs: config.idle.timeoutMs,
62
+ ...(config.idle.ignoreCursorBlink === undefined
63
+ ? {}
64
+ : { ignoreCursorBlink: config.idle.ignoreCursorBlink }),
65
+ ...(config.idle.ignoreLayoutDrift === undefined
66
+ ? {}
67
+ : { ignoreLayoutDrift: config.idle.ignoreLayoutDrift }),
68
+ deviceProfiles: config.idle.deviceProfiles?.length ?? 0
69
+ },
70
+ ...(config.ui === undefined
71
+ ? {}
72
+ : {
73
+ ui: {
74
+ backend: config.ui.backend,
75
+ ...(config.ui.snapshotTimeoutMs === undefined
76
+ ? {}
77
+ : { snapshotTimeoutMs: config.ui.snapshotTimeoutMs }),
78
+ ...(config.ui.cacheEnabled === undefined
79
+ ? {}
80
+ : { cacheEnabled: config.ui.cacheEnabled })
81
+ }
82
+ }),
83
+ ...(config.runtime === undefined ? {} : { runtime: { backend: config.runtime.backend } })
84
+ };
85
+ }
86
+ /**
87
+ * Builds a feedback bundle from the local journal and the reports it
88
+ * references. Only allowlisted structural facts survive: identifying strings
89
+ * become ordinal aliases or salted digests, and everything else is dropped.
90
+ * The result is validated against the strict bundle schema before return.
91
+ */
92
+ export class DiagnosticsExporter {
93
+ dependencies;
94
+ constructor(dependencies) {
95
+ this.dependencies = dependencies;
96
+ }
97
+ async export(input) {
98
+ const { events: allEvents, skipped } = parseEvents(await this.dependencies.journal.readLines(input.projectRoot));
99
+ const events = allEvents.slice(-Math.max(0, input.eventLimit));
100
+ const config = await this.readConfig(input.projectRoot);
101
+ const salt = await this.dependencies.journal.salt(input.projectRoot);
102
+ const runs = new Aliases("run");
103
+ const activities = new Aliases("activity");
104
+ const journeys = new Aliases("journey");
105
+ const devices = new Aliases("device");
106
+ const locatorId = (locator) => createHmac("sha256", salt)
107
+ .update(JSON.stringify(canonical(locator)))
108
+ .digest("hex")
109
+ .slice(0, 16);
110
+ const runIds = [...new Set(events.flatMap((event) => (event.runId === undefined ? [] : [event.runId])))];
111
+ // Aliases follow journal order, whichever runs end up summarized.
112
+ for (const runId of runIds)
113
+ runs.of(runId);
114
+ const artifactsDir = resolve(input.projectRoot, config?.artifactsDir ?? DEFAULT_ARTIFACTS_DIR);
115
+ const summaries = [];
116
+ for (const runId of runIds.slice(-Math.max(0, input.runLimit))) {
117
+ const report = await this.readReport(join(artifactsDir, runId, "report.json"));
118
+ if (report === undefined)
119
+ continue;
120
+ summaries.push(summarizeRun(report, runs.of(runId), {
121
+ activity: (value) => activities.of(value),
122
+ journey: (value) => journeys.of(value),
123
+ device: (value) => devices.of(value),
124
+ locatorId
125
+ }));
126
+ }
127
+ return DiagnosticsBundleSchema.parse({
128
+ version: 1,
129
+ generatedAt: this.dependencies.now().toISOString(),
130
+ taphoundVersion: this.dependencies.taphoundVersion,
131
+ host: this.dependencies.host,
132
+ ...(config === undefined ? {} : { config: configSummary(config) }),
133
+ journal: {
134
+ events: events.map((event) => ({
135
+ ...event,
136
+ ...(event.runId === undefined ? {} : { runId: runs.of(event.runId) })
137
+ })),
138
+ skippedLines: skipped
139
+ },
140
+ runs: summaries
141
+ });
142
+ }
143
+ async readConfig(projectRoot) {
144
+ try {
145
+ const parsed = TapHoundConfigSchema.safeParse(await this.dependencies.readJson(join(projectRoot, CONFIG_PATH)));
146
+ return parsed.success ? parsed.data : undefined;
147
+ }
148
+ catch {
149
+ return undefined;
150
+ }
151
+ }
152
+ async readReport(path) {
153
+ try {
154
+ const parsed = TapHoundReportSchema.safeParse(await this.dependencies.readJson(path));
155
+ return parsed.success ? parsed.data : undefined;
156
+ }
157
+ catch {
158
+ return undefined;
159
+ }
160
+ }
161
+ }
162
+ function summarizeStep(step, redact) {
163
+ const locator = step.locator;
164
+ const requested = locator?.requested;
165
+ return {
166
+ index: step.index,
167
+ action: step.action,
168
+ status: step.status,
169
+ ...(step.device === undefined ? {} : { device: redact.device(step.device) }),
170
+ durationMs: step.durationMs,
171
+ ...(locator === undefined
172
+ ? {}
173
+ : {
174
+ locator: {
175
+ status: locator.status,
176
+ ...(locator.matchedBy === undefined ? {} : { matchedBy: locator.matchedBy }),
177
+ requestedFields: requested === undefined
178
+ ? []
179
+ : LOCATOR_FIELDS.filter((field) => requested[field] !== undefined),
180
+ ...(requested === undefined ? {} : { locatorId: redact.locatorId(requested) }),
181
+ ...(locator.anchor === undefined ? {} : { anchorStatus: locator.anchor.status }),
182
+ fallbackUsed: locator.fallbackUsed
183
+ }
184
+ }),
185
+ ...(step.idle === undefined
186
+ ? {}
187
+ : {
188
+ idle: {
189
+ status: step.idle.status,
190
+ polls: step.idle.polls,
191
+ ...(step.idle.durationMs === undefined ? {} : { durationMs: step.idle.durationMs }),
192
+ ...(step.idle.samplingDurationMs === undefined
193
+ ? {}
194
+ : { samplingDurationMs: step.idle.samplingDurationMs }),
195
+ ...(step.idle.strategy === undefined ? {} : { strategy: step.idle.strategy }),
196
+ ...(step.idle.backend === undefined ? {} : { backend: step.idle.backend }),
197
+ ...(step.idle.fallbackUsed === undefined
198
+ ? {}
199
+ : { fallbackUsed: step.idle.fallbackUsed }),
200
+ ...(step.idle.frameActivityDetected === undefined
201
+ ? {}
202
+ : { frameActivityDetected: step.idle.frameActivityDetected }),
203
+ ...(step.idle.lastDiff === undefined
204
+ ? {}
205
+ : { lastDiffCount: step.idle.lastDiff.length })
206
+ }
207
+ }),
208
+ ...(step.activity === undefined
209
+ ? {}
210
+ : {
211
+ activity: {
212
+ before: activityCheck(step.activity.before, redact),
213
+ after: activityCheck(step.activity.after, redact)
214
+ }
215
+ }),
216
+ ...(step.expectation === undefined
217
+ ? {}
218
+ : {
219
+ expectation: {
220
+ type: step.expectation.type,
221
+ status: step.expectation.status,
222
+ ...(step.expectation.code === undefined ? {} : { code: step.expectation.code })
223
+ }
224
+ }),
225
+ ...(step.scroll === undefined ? {} : { scroll: { ...step.scroll } })
226
+ };
227
+ }
228
+ function activityCheck(check, redact) {
229
+ return {
230
+ status: check.status,
231
+ expected: redact.activity(check.expected),
232
+ ...(check.actual === undefined ? {} : { actual: redact.activity(check.actual) })
233
+ };
234
+ }
235
+ function summarizeRun(report, run, redact) {
236
+ const failure = report.primaryFailure;
237
+ return {
238
+ run,
239
+ status: report.status,
240
+ durationMs: report.durationMs,
241
+ journey: redact.journey(report.journey.name),
242
+ devices: report.environment.devices.map((device) => ({
243
+ device: redact.device(device.role),
244
+ ...(device.uiBackend === undefined
245
+ ? {}
246
+ : {
247
+ uiBackend: {
248
+ id: device.uiBackend.id,
249
+ adapterVersion: VERSION.test(device.uiBackend.adapterVersion)
250
+ ? device.uiBackend.adapterVersion
251
+ : "unknown",
252
+ ...(device.uiBackend.engineVersion !== undefined
253
+ && VERSION.test(device.uiBackend.engineVersion)
254
+ ? { engineVersion: device.uiBackend.engineVersion }
255
+ : {})
256
+ }
257
+ }),
258
+ ...(device.uiCache === undefined ? {} : { uiCache: { ...device.uiCache } })
259
+ })),
260
+ tools: Object.fromEntries(TOOL_NAMES.flatMap((name) => {
261
+ const version = report.environment.tools[name];
262
+ return version !== undefined && VERSION.test(version) ? [[name, version]] : [];
263
+ })),
264
+ layers: { ...report.layers },
265
+ ...(failure === undefined
266
+ ? {}
267
+ : {
268
+ primaryFailure: {
269
+ code: failure.code,
270
+ phase: TOKEN.test(failure.phase) ? failure.phase : "other",
271
+ ...(failure.stepIndex === undefined ? {} : { stepIndex: failure.stepIndex })
272
+ }
273
+ }),
274
+ secondaryErrorCodes: report.secondaryErrors.map((error) => error.code),
275
+ fallbackUsed: report.fallbackUsed,
276
+ logcatEvidence: (report.logcatEvidence ?? []).map((entry) => ({
277
+ device: redact.device(entry.role),
278
+ droppedLines: entry.droppedLines,
279
+ droppedBytes: entry.droppedBytes
280
+ })),
281
+ steps: report.steps.map((step) => summarizeStep(step, redact))
282
+ };
283
+ }
@@ -0,0 +1,16 @@
1
+ import { type UiBackendTelemetry, type UiCaptureFailureKind } from "../../domain/diagnostics.js";
2
+ import type { UiBackendId } from "../../domain/ui-backend.js";
3
+ import type { UiCaptureObserver } from "../../ports/diagnostics.js";
4
+ /**
5
+ * Classifies a capture failure from its type and status only, walking the
6
+ * cause chain. The message is read solely to spot a timeout and is never kept.
7
+ */
8
+ export declare function classifyCaptureFailure(error: unknown): UiCaptureFailureKind;
9
+ /** Aggregates UI backend captures for one CLI invocation. */
10
+ export declare class UiCaptureTelemetry implements UiCaptureObserver {
11
+ private readonly backends;
12
+ captured(backend: UiBackendId, durationMs: number, error?: unknown): void;
13
+ sessionRecovered(backend: UiBackendId, succeeded: boolean): void;
14
+ summary(): UiBackendTelemetry[];
15
+ private entry;
16
+ }
@@ -0,0 +1,81 @@
1
+ import { UI_CAPTURE_LATENCY_BUCKETS_MS } from "../../domain/diagnostics.js";
2
+ /**
3
+ * Classifies a capture failure from its type and status only, walking the
4
+ * cause chain. The message is read solely to spot a timeout and is never kept.
5
+ */
6
+ export function classifyCaptureFailure(error) {
7
+ let current = error;
8
+ for (let depth = 0; depth < 8 && current !== null && typeof current === "object"; depth += 1) {
9
+ const record = current;
10
+ if (record.name === "TimeoutError")
11
+ return "timeout";
12
+ if (record.name === "AbortError")
13
+ return "cancelled";
14
+ if (typeof record.status === "number") {
15
+ if (record.status >= 500)
16
+ return "http5xx";
17
+ if (record.status >= 400)
18
+ return "http4xx";
19
+ }
20
+ if (typeof record.message === "string" && /timed? ?out/i.test(record.message)) {
21
+ return "timeout";
22
+ }
23
+ current = record.cause;
24
+ }
25
+ return "error";
26
+ }
27
+ function emptyTelemetry(backend) {
28
+ return {
29
+ backend,
30
+ captures: 0,
31
+ failures: { timeout: 0, cancelled: 0, http4xx: 0, http5xx: 0, error: 0 },
32
+ totalMs: 0,
33
+ maxMs: 0,
34
+ latencyBuckets: new Array(UI_CAPTURE_LATENCY_BUCKETS_MS.length + 1).fill(0),
35
+ sessionRecoveries: 0,
36
+ sessionRecoveryFailures: 0
37
+ };
38
+ }
39
+ /** Aggregates UI backend captures for one CLI invocation. */
40
+ export class UiCaptureTelemetry {
41
+ backends = new Map();
42
+ captured(backend, durationMs, error) {
43
+ const entry = this.entry(backend);
44
+ const duration = Math.max(0, durationMs);
45
+ entry.captures += 1;
46
+ entry.totalMs += duration;
47
+ entry.maxMs = Math.max(entry.maxMs, duration);
48
+ const bucket = UI_CAPTURE_LATENCY_BUCKETS_MS.findIndex((limit) => duration < limit);
49
+ const index = bucket === -1 ? UI_CAPTURE_LATENCY_BUCKETS_MS.length : bucket;
50
+ entry.latencyBuckets[index] = (entry.latencyBuckets[index] ?? 0) + 1;
51
+ if (error !== undefined) {
52
+ entry.failures[classifyCaptureFailure(error)] += 1;
53
+ }
54
+ }
55
+ sessionRecovered(backend, succeeded) {
56
+ const entry = this.entry(backend);
57
+ if (succeeded) {
58
+ entry.sessionRecoveries += 1;
59
+ }
60
+ else {
61
+ entry.sessionRecoveryFailures += 1;
62
+ }
63
+ }
64
+ summary() {
65
+ return [...this.backends.values()].map((entry) => ({
66
+ ...entry,
67
+ totalMs: Math.round(entry.totalMs),
68
+ maxMs: Math.round(entry.maxMs),
69
+ failures: { ...entry.failures },
70
+ latencyBuckets: [...entry.latencyBuckets]
71
+ }));
72
+ }
73
+ entry(backend) {
74
+ let entry = this.backends.get(backend);
75
+ if (entry === undefined) {
76
+ entry = emptyTelemetry(backend);
77
+ this.backends.set(backend, entry);
78
+ }
79
+ return entry;
80
+ }
81
+ }