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,45 @@
1
+ import type { CaptureUiSnapshotOptions, UiSnapshot, UiSnapshotProvider, UiSnapshotProviderFactory, OpenUiSnapshotProviderOptions } from "../../ports/ui-snapshot.js";
2
+ import type { UiStabilityProbe, UiStabilitySampleOptions, UiStabilitySampleResult } from "../../ports/ui-stability.js";
3
+ type InvalidationReason = Parameters<NonNullable<UiSnapshotProvider["invalidate"]>>[0];
4
+ export declare class CachedUiSnapshotProviderFactory implements UiSnapshotProviderFactory {
5
+ private readonly source;
6
+ private readonly now;
7
+ private readonly ttlMs;
8
+ constructor(source: UiSnapshotProviderFactory, now?: () => number, ttlMs?: number);
9
+ open(options: OpenUiSnapshotProviderOptions): Promise<UiSnapshotProvider>;
10
+ }
11
+ export declare class CachedUiSnapshotProvider implements UiSnapshotProvider, Partial<UiStabilityProbe> {
12
+ private readonly source;
13
+ private readonly now;
14
+ private readonly ttlMs;
15
+ readonly descriptor: {
16
+ id: "system-uiautomator" | "android-cli" | "appium-uiautomator2" | "mobile-mcp";
17
+ adapterVersion: string;
18
+ configSha256: string;
19
+ engineVersion?: string | undefined;
20
+ };
21
+ readonly supportsStability: boolean;
22
+ private mutationEpoch;
23
+ private cached;
24
+ private inFlight;
25
+ private closed;
26
+ private closePromise;
27
+ private hits;
28
+ private misses;
29
+ private capturesSaved;
30
+ constructor(source: UiSnapshotProvider, now?: () => number, ttlMs?: number);
31
+ reset(): void;
32
+ sample(options: UiStabilitySampleOptions): Promise<UiStabilitySampleResult>;
33
+ capture(options: CaptureUiSnapshotOptions): Promise<UiSnapshot>;
34
+ invalidate(reason: InvalidationReason): void;
35
+ close(): Promise<void>;
36
+ cacheTelemetry(): {
37
+ hits: number;
38
+ misses: number;
39
+ stale: number;
40
+ relearns: number;
41
+ capturesSaved: number;
42
+ validationDurationMs: number;
43
+ };
44
+ }
45
+ export {};
@@ -0,0 +1,111 @@
1
+ export class CachedUiSnapshotProviderFactory {
2
+ source;
3
+ now;
4
+ ttlMs;
5
+ constructor(source, now = () => Date.now(), ttlMs = 300) {
6
+ this.source = source;
7
+ this.now = now;
8
+ this.ttlMs = ttlMs;
9
+ }
10
+ async open(options) {
11
+ const provider = await this.source.open(options);
12
+ return options.cacheEnabled === false
13
+ ? provider
14
+ : new CachedUiSnapshotProvider(provider, this.now, this.ttlMs);
15
+ }
16
+ }
17
+ export class CachedUiSnapshotProvider {
18
+ source;
19
+ now;
20
+ ttlMs;
21
+ descriptor;
22
+ supportsStability;
23
+ mutationEpoch = 0;
24
+ cached;
25
+ inFlight;
26
+ closed = false;
27
+ closePromise;
28
+ hits = 0;
29
+ misses = 0;
30
+ capturesSaved = 0;
31
+ constructor(source, now = () => Date.now(), ttlMs = 300) {
32
+ this.source = source;
33
+ this.now = now;
34
+ this.ttlMs = ttlMs;
35
+ this.descriptor = source.descriptor;
36
+ const sourceProbe = source;
37
+ this.supportsStability = typeof sourceProbe.sample === "function"
38
+ && typeof sourceProbe.reset === "function";
39
+ }
40
+ reset() {
41
+ const sourceProbe = this.source;
42
+ sourceProbe.reset?.();
43
+ }
44
+ sample(options) {
45
+ const sourceProbe = this.source;
46
+ const sample = sourceProbe.sample;
47
+ if (sample === undefined) {
48
+ return Promise.reject(new Error("Underlying UI snapshot provider does not support stability sampling"));
49
+ }
50
+ return sample.call(sourceProbe, options);
51
+ }
52
+ capture(options) {
53
+ if (this.closed) {
54
+ return Promise.reject(new Error("UI snapshot provider is closed"));
55
+ }
56
+ const freshness = options.freshness ?? "forceFresh";
57
+ const epoch = this.mutationEpoch;
58
+ if (freshness === "sameMutationEpoch") {
59
+ if (this.cached?.epoch === epoch
60
+ && this.now() - this.cached.storedAtMs <= this.ttlMs) {
61
+ this.hits += 1;
62
+ this.capturesSaved += 1;
63
+ return Promise.resolve(this.cached.snapshot);
64
+ }
65
+ if (this.inFlight?.epoch === epoch) {
66
+ this.hits += 1;
67
+ this.capturesSaved += 1;
68
+ return this.inFlight.promise;
69
+ }
70
+ }
71
+ this.misses += 1;
72
+ const promise = this.source.capture(options).then((snapshot) => {
73
+ if (!this.closed && this.mutationEpoch === epoch) {
74
+ this.cached = { epoch, storedAtMs: this.now(), snapshot };
75
+ }
76
+ return snapshot;
77
+ }).finally(() => {
78
+ if (this.inFlight?.promise === promise) {
79
+ this.inFlight = undefined;
80
+ }
81
+ });
82
+ if (freshness === "sameMutationEpoch") {
83
+ this.inFlight = { epoch, promise };
84
+ }
85
+ return promise;
86
+ }
87
+ invalidate(reason) {
88
+ void reason;
89
+ this.mutationEpoch += 1;
90
+ this.cached = undefined;
91
+ this.inFlight = undefined;
92
+ }
93
+ close() {
94
+ if (this.closePromise !== undefined)
95
+ return this.closePromise;
96
+ this.closed = true;
97
+ this.invalidate("providerClosed");
98
+ this.closePromise = this.source.close();
99
+ return this.closePromise;
100
+ }
101
+ cacheTelemetry() {
102
+ return {
103
+ hits: this.hits,
104
+ misses: this.misses,
105
+ stale: 0,
106
+ relearns: 0,
107
+ capturesSaved: this.capturesSaved,
108
+ validationDurationMs: 0
109
+ };
110
+ }
111
+ }
@@ -0,0 +1,2 @@
1
+ import type { UiSnapshotProvider } from "../../ports/ui-snapshot.js";
2
+ export declare function closeUiSnapshotProvider(provider: UiSnapshotProvider, onFailure?: (error: unknown) => void): Promise<void>;
@@ -0,0 +1,8 @@
1
+ export async function closeUiSnapshotProvider(provider, onFailure) {
2
+ try {
3
+ await provider.close();
4
+ }
5
+ catch (error) {
6
+ onFailure?.(error);
7
+ }
8
+ }
@@ -0,0 +1,3 @@
1
+ import type { UiSnapshotProvider } from "../../ports/ui-snapshot.js";
2
+ import type { UiStabilityProbe } from "../../ports/ui-stability.js";
3
+ export declare function uiStabilityProbe(provider: UiSnapshotProvider, fallback: UiStabilityProbe): UiStabilityProbe;
@@ -0,0 +1,8 @@
1
+ export function uiStabilityProbe(provider, fallback) {
2
+ const withCapability = provider;
3
+ return withCapability.supportsStability !== false
4
+ && typeof withCapability.sample === "function"
5
+ && typeof withCapability.reset === "function"
6
+ ? withCapability
7
+ : fallback;
8
+ }
@@ -0,0 +1,6 @@
1
+ import type { IdleResult } from "./idle-waiter.js";
2
+ export type IdleTimeoutResult = Extract<IdleResult, {
3
+ status: "timeout";
4
+ }>;
5
+ export declare function idleTimeoutAdvice(idle: IdleTimeoutResult): string;
6
+ export declare function withIdleAdvice(message: string, idle: IdleTimeoutResult): string;
@@ -0,0 +1,27 @@
1
+ const SLOW_CAPTURE_BUDGET_RATIO = 0.8;
2
+ export function idleTimeoutAdvice(idle) {
3
+ if (idle.frameActivityDetected) {
4
+ return `frame activity never settled under idle.strategy "${idle.strategy}"; `
5
+ + "if the screen animates continuously, set idle.strategy to "
6
+ + "\"layoutDiff\" in .taphound/config.json";
7
+ }
8
+ if (idle.lastDiff.length > 0) {
9
+ return "layout differences kept appearing (see lastDiff in the step "
10
+ + "report); raise idle.timeoutMs in .taphound/config.json if the "
11
+ + "change is expected to settle, or set idle.strategy to "
12
+ + "\"layoutDiff\" if the screen animates continuously";
13
+ }
14
+ if (idle.durationMs > 0
15
+ && idle.samplingDurationMs >= idle.durationMs * SLOW_CAPTURE_BUDGET_RATIO) {
16
+ return "UI snapshot captures consumed most of the idle budget; raise "
17
+ + "idle.timeoutMs in .taphound/config.json so idle.stablePolls "
18
+ + "consecutive polls fit, and raise ui.snapshotTimeoutMs if "
19
+ + "individual dumps are slow";
20
+ }
21
+ return "no layout change was detected but the idle budget ended before "
22
+ + "idle.stablePolls consecutive stable polls; raise idle.timeoutMs or "
23
+ + "lower idle.stablePolls/pollIntervalMs in .taphound/config.json";
24
+ }
25
+ export function withIdleAdvice(message, idle) {
26
+ return `${message} (${idleTimeoutAdvice(idle)})`;
27
+ }
@@ -0,0 +1,15 @@
1
+ import type { DeviceIdleProfile } from "../../domain/config.js";
2
+ import type { AdbPort, AppIdentity, DeviceIdentity } from "../../ports/adb.js";
3
+ export type IdleStrategyName = "hybrid" | "layoutDiff" | "frameStats" | "structural";
4
+ export interface IdleSettings {
5
+ strategy?: IdleStrategyName | undefined;
6
+ pollIntervalMs: number;
7
+ stablePolls: number;
8
+ timeoutMs: number;
9
+ ignoreCursorBlink?: boolean | undefined;
10
+ ignoreLayoutDrift?: boolean | undefined;
11
+ }
12
+ export declare function resolveIdlePolicy(idle: IdleSettings & {
13
+ deviceProfiles?: readonly DeviceIdleProfile[] | undefined;
14
+ }, identity: DeviceIdentity | undefined): IdleSettings;
15
+ export declare function deviceIdentityResolver(adb: Pick<AdbPort, "deviceIdentity">, identity: Omit<AppIdentity, "signal">): () => Promise<DeviceIdentity | undefined>;
@@ -0,0 +1,75 @@
1
+ function matchesDevice(profile, identity) {
2
+ const match = profile.match;
3
+ if (match.manufacturer !== undefined
4
+ && match.manufacturer.toLowerCase() !== identity.manufacturer.toLowerCase()) {
5
+ return false;
6
+ }
7
+ if (match.model !== undefined
8
+ && match.model.toLowerCase() !== identity.model.toLowerCase()) {
9
+ return false;
10
+ }
11
+ if (match.sdkLevel !== undefined && match.sdkLevel !== identity.sdkLevel) {
12
+ return false;
13
+ }
14
+ return true;
15
+ }
16
+ export function resolveIdlePolicy(idle, identity) {
17
+ if (identity === undefined || idle.deviceProfiles === undefined) {
18
+ return idle;
19
+ }
20
+ const matches = idle.deviceProfiles.filter((profile) => matchesDevice(profile, identity));
21
+ if (matches.length === 0) {
22
+ return idle;
23
+ }
24
+ const merged = {
25
+ strategy: idle.strategy,
26
+ pollIntervalMs: idle.pollIntervalMs,
27
+ stablePolls: idle.stablePolls,
28
+ timeoutMs: idle.timeoutMs,
29
+ ...(idle.ignoreCursorBlink === undefined
30
+ ? {}
31
+ : { ignoreCursorBlink: idle.ignoreCursorBlink }),
32
+ ...(idle.ignoreLayoutDrift === undefined
33
+ ? {}
34
+ : { ignoreLayoutDrift: idle.ignoreLayoutDrift })
35
+ };
36
+ for (const profile of matches) {
37
+ if (profile.ignoreCursorBlink !== undefined) {
38
+ merged.ignoreCursorBlink = profile.ignoreCursorBlink;
39
+ }
40
+ if (profile.ignoreLayoutDrift !== undefined) {
41
+ merged.ignoreLayoutDrift = profile.ignoreLayoutDrift;
42
+ }
43
+ if (profile.strategy !== undefined) {
44
+ merged.strategy = profile.strategy;
45
+ }
46
+ if (profile.pollIntervalMs !== undefined) {
47
+ merged.pollIntervalMs = profile.pollIntervalMs;
48
+ }
49
+ if (profile.stablePolls !== undefined) {
50
+ merged.stablePolls = profile.stablePolls;
51
+ }
52
+ if (profile.timeoutMs !== undefined) {
53
+ merged.timeoutMs = profile.timeoutMs;
54
+ }
55
+ }
56
+ return merged;
57
+ }
58
+ export function deviceIdentityResolver(adb, identity) {
59
+ // Device identity is immutable for a run; only a successful lookup is
60
+ // memoized so a transient getprop failure is retried on the next wait.
61
+ let resolved;
62
+ return async () => {
63
+ if (resolved !== undefined) {
64
+ return resolved;
65
+ }
66
+ const current = adb.deviceIdentity;
67
+ if (current === undefined) {
68
+ return undefined;
69
+ }
70
+ const requested = { ...identity };
71
+ resolved = await current.call(adb, requested)
72
+ .catch(() => undefined);
73
+ return resolved;
74
+ };
75
+ }
@@ -1,30 +1,54 @@
1
1
  import type { FailureCode } from "../../domain/failure.js";
2
- import type { AndroidCliPort } from "../../ports/android-cli.js";
2
+ import type { LayoutElement } from "../../domain/layout.js";
3
+ import type { DeviceIdleProfile } from "../../domain/config.js";
4
+ import type { DeviceIdentity } from "../../ports/adb.js";
5
+ import type { UiStabilityProbe } from "../../ports/ui-stability.js";
3
6
  import type { Clock } from "../../ports/clock.js";
7
+ export type IdleStrategy = "hybrid" | "layoutDiff" | "frameStats" | "structural";
8
+ export type IdleBackend = "uiautomator" | "androidCli" | "gfxFrameStats" | "mobileMcp";
4
9
  export interface IdleConfig {
10
+ strategy?: IdleStrategy | undefined;
5
11
  pollIntervalMs: number;
6
12
  stablePolls: number;
7
13
  timeoutMs: number;
14
+ ignoreCursorBlink?: boolean | undefined;
15
+ ignoreLayoutDrift?: boolean | undefined;
16
+ deviceProfiles?: readonly DeviceIdleProfile[] | undefined;
8
17
  }
9
18
  export type IdleResult = {
10
19
  status: "stable";
11
20
  polls: number;
12
21
  durationMs: number;
22
+ layout?: readonly LayoutElement[] | undefined;
23
+ backend?: IdleBackend | undefined;
24
+ strategy: IdleStrategy;
25
+ fallbackUsed: boolean;
26
+ frameActivityDetected: boolean;
27
+ samplingDurationMs: number;
13
28
  } | {
14
29
  status: "timeout";
15
30
  code: Extract<FailureCode, "IDLE_TIMEOUT">;
16
31
  polls: number;
17
32
  durationMs: number;
18
33
  lastDiff: readonly unknown[];
34
+ backend?: IdleBackend | undefined;
35
+ strategy: IdleStrategy;
36
+ fallbackUsed: boolean;
37
+ frameActivityDetected: boolean;
38
+ samplingDurationMs: number;
19
39
  } | {
20
40
  status: "cancelled";
21
41
  polls: number;
22
42
  durationMs: number;
23
43
  };
24
44
  export declare class IdleWaiter {
25
- private readonly androidCli;
45
+ private readonly stability;
26
46
  private readonly clock;
27
47
  private readonly deviceSerial;
28
- constructor(androidCli: AndroidCliPort, clock: Clock, deviceSerial: string);
29
- waitUntilIdle(config: IdleConfig, signal?: AbortSignal): Promise<IdleResult>;
48
+ private readonly packageName?;
49
+ private readonly resolveDeviceIdentity?;
50
+ private transientKeys;
51
+ constructor(stability: UiStabilityProbe, clock: Clock, deviceSerial: string, packageName?: string | undefined, resolveDeviceIdentity?: (() => Promise<DeviceIdentity | undefined>) | undefined);
52
+ waitUntilIdle(input: IdleConfig, signal?: AbortSignal): Promise<IdleResult>;
53
+ private effectiveConfig;
30
54
  }
@@ -1,20 +1,102 @@
1
+ import { resolveIdlePolicy } from "./idle-profiles.js";
2
+ const EARLY_BAIL_FRAME_CHANGES = 2;
3
+ const POST_FALLBACK_MIN_STABLE = 2;
4
+ function isEditableWidgetChange(change) {
5
+ if (typeof change !== "object" || change === null) {
6
+ return false;
7
+ }
8
+ const record = change;
9
+ const interactions = record.interactions;
10
+ if (Array.isArray(interactions) && interactions.includes("EDITABLE")) {
11
+ return true;
12
+ }
13
+ const cls = record.class;
14
+ return typeof cls === "string" && cls.endsWith("EditText");
15
+ }
16
+ function meaningfulChanges(changes, ignoreCursorBlink) {
17
+ if (!ignoreCursorBlink) {
18
+ return changes;
19
+ }
20
+ return changes.filter((change) => !isEditableWidgetChange(change));
21
+ }
22
+ const EMPTY_SEMANTIC_KEY = "||";
23
+ function semanticKeys(change) {
24
+ if (typeof change !== "object" || change === null) {
25
+ return JSON.stringify(change);
26
+ }
27
+ const record = change;
28
+ const cls = typeof record.class === "string" ? record.class : "";
29
+ const resourceId = typeof record["resource-id"] === "string"
30
+ ? record["resource-id"]
31
+ : "";
32
+ const text = typeof record.text === "string" ? record.text : "";
33
+ return `${cls}|${resourceId}|${text}`;
34
+ }
35
+ /**
36
+ * A structural sample from an opaque backend is one `{layoutSha256}` change
37
+ * that names no element, so every such change collapses to the same empty
38
+ * key. Suppressing a repeated empty key set would report a screen that keeps
39
+ * changing as idle, so drift suppression only applies to identified changes.
40
+ */
41
+ function identifiesElements(keys) {
42
+ return keys.every((key) => key !== EMPTY_SEMANTIC_KEY);
43
+ }
44
+ function sameKeySet(left, right) {
45
+ if (left.length !== right.length)
46
+ return false;
47
+ const sortedLeft = [...left].sort();
48
+ const sortedRight = [...right].sort();
49
+ return sortedLeft.every((key, index) => key === sortedRight[index]);
50
+ }
1
51
  function isAborted(signal) {
2
52
  return signal?.aborted === true;
3
53
  }
54
+ function normalizeObservation(result) {
55
+ return Array.isArray(result)
56
+ ? { changes: result }
57
+ : result;
58
+ }
59
+ function telemetry(strategy, backend, fallbackUsed, frameActivityDetected, samplingDurationMs) {
60
+ return {
61
+ strategy,
62
+ ...(backend === undefined ? {} : { backend }),
63
+ fallbackUsed,
64
+ frameActivityDetected,
65
+ samplingDurationMs
66
+ };
67
+ }
4
68
  export class IdleWaiter {
5
- androidCli;
69
+ stability;
6
70
  clock;
7
71
  deviceSerial;
8
- constructor(androidCli, clock, deviceSerial) {
9
- this.androidCli = androidCli;
72
+ packageName;
73
+ resolveDeviceIdentity;
74
+ transientKeys;
75
+ constructor(stability, clock, deviceSerial, packageName, resolveDeviceIdentity) {
76
+ this.stability = stability;
10
77
  this.clock = clock;
11
78
  this.deviceSerial = deviceSerial;
79
+ this.packageName = packageName;
80
+ this.resolveDeviceIdentity = resolveDeviceIdentity;
12
81
  }
13
- async waitUntilIdle(config, signal) {
82
+ async waitUntilIdle(input, signal) {
83
+ this.stability.reset();
84
+ this.transientKeys = undefined;
85
+ const config = await this.effectiveConfig(input);
86
+ const strategy = config.strategy ?? "hybrid";
14
87
  const startedAt = this.clock.now();
15
88
  let polls = 0;
16
89
  let consecutiveEmpty = 0;
90
+ let consecutiveFrameChanges = 0;
17
91
  let lastDiff = [];
92
+ let lastLayout;
93
+ let backend;
94
+ let fallbackUsed = false;
95
+ let frameActivityDetected = false;
96
+ let samplingDurationMs = 0;
97
+ let useStructuralBackend = strategy === "layoutDiff"
98
+ || strategy === "structural"
99
+ || (strategy === "hybrid" && this.packageName === undefined);
18
100
  for (;;) {
19
101
  if (isAborted(signal)) {
20
102
  return {
@@ -25,13 +107,21 @@ export class IdleWaiter {
25
107
  }
26
108
  const elapsedBeforePoll = this.clock.now() - startedAt;
27
109
  polls += 1;
28
- let diff;
110
+ let observation;
29
111
  try {
30
- diff = await this.androidCli.layoutDiff({
112
+ observation = normalizeObservation(await this.stability.sample({
31
113
  deviceSerial: this.deviceSerial,
114
+ ...(useStructuralBackend
115
+ ? { stabilityBackend: "uiautomator" }
116
+ : this.packageName === undefined
117
+ ? {}
118
+ : {
119
+ packageName: this.packageName,
120
+ stabilityBackend: "frameStats"
121
+ }),
32
122
  ...(signal === undefined ? {} : { signal }),
33
123
  timeoutMs: Math.max(1, config.timeoutMs - elapsedBeforePoll)
34
- });
124
+ }));
35
125
  }
36
126
  catch (error) {
37
127
  const elapsed = this.clock.now() - startedAt;
@@ -44,36 +134,105 @@ export class IdleWaiter {
44
134
  code: "IDLE_TIMEOUT",
45
135
  polls,
46
136
  durationMs: elapsed,
47
- lastDiff
137
+ lastDiff,
138
+ ...telemetry(strategy, backend, fallbackUsed, frameActivityDetected, samplingDurationMs)
48
139
  };
49
140
  }
50
141
  throw error;
51
142
  }
52
- if (diff.length === 0) {
143
+ const diff = observation.changes;
144
+ samplingDurationMs += observation.durationMs ?? 0;
145
+ lastLayout = observation.layout ?? lastLayout;
146
+ backend = observation.backend ?? backend;
147
+ if (observation.backend === "gfxFrameStats" && diff.length > 0) {
148
+ frameActivityDetected = true;
149
+ consecutiveFrameChanges += 1;
150
+ }
151
+ else {
152
+ consecutiveFrameChanges = 0;
153
+ }
154
+ if (strategy === "hybrid"
155
+ && !useStructuralBackend
156
+ && consecutiveFrameChanges >= EARLY_BAIL_FRAME_CHANGES) {
157
+ useStructuralBackend = true;
158
+ fallbackUsed = true;
159
+ consecutiveEmpty = 0;
160
+ }
161
+ else if (useStructuralBackend) {
162
+ let visibleChanges = meaningfulChanges(diff, config.ignoreCursorBlink === true);
163
+ if (config.ignoreLayoutDrift === true && visibleChanges.length > 0) {
164
+ const keys = visibleChanges.map(semanticKeys);
165
+ const identified = identifiesElements(keys);
166
+ if (identified
167
+ && this.transientKeys !== undefined
168
+ && sameKeySet(this.transientKeys, keys)) {
169
+ visibleChanges = [];
170
+ }
171
+ else {
172
+ this.transientKeys = identified ? keys : undefined;
173
+ }
174
+ }
175
+ else if (config.ignoreLayoutDrift === true) {
176
+ this.transientKeys = undefined;
177
+ }
178
+ consecutiveEmpty = visibleChanges.length === 0
179
+ ? consecutiveEmpty + 1
180
+ : 0;
181
+ if (visibleChanges.length > 0)
182
+ lastDiff = visibleChanges;
183
+ }
184
+ else if (diff.length === 0) {
53
185
  consecutiveEmpty += 1;
54
186
  }
55
187
  else {
56
188
  consecutiveEmpty = 0;
57
189
  lastDiff = diff;
58
190
  }
59
- if (consecutiveEmpty >= config.stablePolls) {
191
+ const isPostFallback = fallbackUsed && strategy === "hybrid";
192
+ const requiredStableObservations = useStructuralBackend
193
+ ? (isPostFallback
194
+ ? Math.max(POST_FALLBACK_MIN_STABLE, config.stablePolls - 1)
195
+ : Math.max(2, config.stablePolls))
196
+ : config.stablePolls;
197
+ if (consecutiveEmpty >= requiredStableObservations) {
198
+ if (strategy === "hybrid" && !useStructuralBackend) {
199
+ useStructuralBackend = true;
200
+ consecutiveEmpty = 0;
201
+ }
202
+ else {
203
+ return {
204
+ status: "stable",
205
+ polls,
206
+ durationMs: this.clock.now() - startedAt,
207
+ ...(lastLayout === undefined ? {} : { layout: lastLayout }),
208
+ ...telemetry(strategy, backend, fallbackUsed, frameActivityDetected, samplingDurationMs)
209
+ };
210
+ }
211
+ }
212
+ if (this.clock.now() - startedAt >= config.timeoutMs) {
60
213
  return {
61
- status: "stable",
214
+ status: "timeout",
215
+ code: "IDLE_TIMEOUT",
62
216
  polls,
63
- durationMs: this.clock.now() - startedAt
217
+ durationMs: this.clock.now() - startedAt,
218
+ lastDiff,
219
+ ...telemetry(strategy, backend, fallbackUsed, frameActivityDetected, samplingDurationMs)
64
220
  };
65
221
  }
66
- if (this.clock.now() - startedAt >= config.timeoutMs) {
222
+ const remainingMs = config.timeoutMs
223
+ - (this.clock.now() - startedAt);
224
+ if (remainingMs <= 0) {
67
225
  return {
68
226
  status: "timeout",
69
227
  code: "IDLE_TIMEOUT",
70
228
  polls,
71
229
  durationMs: this.clock.now() - startedAt,
72
- lastDiff
230
+ lastDiff,
231
+ ...telemetry(strategy, backend, fallbackUsed, frameActivityDetected, samplingDurationMs)
73
232
  };
74
233
  }
75
234
  try {
76
- await this.clock.sleep(config.pollIntervalMs, signal);
235
+ await this.clock.sleep(Math.min(config.pollIntervalMs, remainingMs), signal);
77
236
  }
78
237
  catch (error) {
79
238
  if (isAborted(signal)) {
@@ -87,4 +246,16 @@ export class IdleWaiter {
87
246
  }
88
247
  }
89
248
  }
249
+ async effectiveConfig(input) {
250
+ if (this.resolveDeviceIdentity === undefined
251
+ || input.deviceProfiles === undefined
252
+ || input.deviceProfiles.length === 0) {
253
+ return input;
254
+ }
255
+ const identity = await this.resolveDeviceIdentity();
256
+ if (identity === undefined) {
257
+ return input;
258
+ }
259
+ return resolveIdlePolicy(input, identity);
260
+ }
90
261
  }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ import type { CliDependencies } from "../dependencies.js";
3
+ export declare function createAlignCommand(dependencies: CliDependencies): Command;