mobile-debug-mcp 0.34.0 → 0.35.0

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 (432) hide show
  1. package/.agents/skills/mcp-builder/SKILL.md +44 -64
  2. package/.agents/skills/mcp-builder/references/build-flags.md +44 -43
  3. package/.agents/skills/mcp-builder/references/diagnostics-schema.md +4 -1
  4. package/.agents/skills/mcp-builder/references/toolchain-details.md +49 -62
  5. package/.agents/skills/spec-review/references/spec-review-checklist.md +7 -0
  6. package/.agents/skills/test-authoring/SKILL.md +7 -6
  7. package/.agents/skills/test-authoring/references/repo-test-layout.md +3 -1
  8. package/.agents/skills/test-authoring/references/test-authoring-workflow.md +16 -7
  9. package/.agents/skills/test-authoring/references/test-quality-checklist.md +2 -1
  10. package/.github/copilot-instructions.md +14 -36
  11. package/.github/workflows/ci.yml +4 -4
  12. package/AGENTS.md +12 -3
  13. package/CONTRIBUTING.md +79 -0
  14. package/README.md +156 -129
  15. package/agent-plugin/.claude-plugin/plugin.json +2 -2
  16. package/agent-plugin/.codex-plugin/plugin.json +1 -1
  17. package/agent-plugin/.cursor-plugin/plugin.json +2 -2
  18. package/agent-plugin/.mcp.json +1 -1
  19. package/agent-plugin/README.md +16 -6
  20. package/agent-plugin/mcp.json +1 -1
  21. package/agent-plugin/plugin.json +1 -1
  22. package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +27 -2
  23. package/dist/contracts/execution-controls.js +24 -0
  24. package/dist/contracts/selector.js +8 -0
  25. package/dist/contracts/target.js +15 -0
  26. package/dist/contracts/ui.js +1 -0
  27. package/dist/debugger/android.js +2 -1
  28. package/dist/debugger/diagnostics.js +54 -0
  29. package/dist/debugger/protocol.js +33 -0
  30. package/dist/debugger/sessions.js +104 -62
  31. package/dist/debugger/worker.js +53 -42
  32. package/dist/interact/adjustment/adjustment-context.js +41 -0
  33. package/dist/interact/adjustment/adjustment-gestures.js +55 -0
  34. package/dist/interact/adjustment/adjustment-plan.js +18 -0
  35. package/dist/interact/adjustment/adjustment-resolution.js +127 -0
  36. package/dist/interact/adjustment/adjustment-result.js +19 -0
  37. package/dist/interact/adjustment/adjustment-semantic.js +64 -0
  38. package/dist/interact/adjustment/adjustment-verification.js +35 -0
  39. package/dist/interact/adjustment/adjustment.js +52 -0
  40. package/dist/interact/adjustment/control-geometry.js +87 -0
  41. package/dist/interact/adjustment/control-values.js +136 -0
  42. package/dist/interact/adjustment/slider-labels.js +84 -0
  43. package/dist/interact/assertions/screen-assertion.js +37 -0
  44. package/dist/interact/assertions/state-assertion-context.js +35 -0
  45. package/dist/interact/assertions/state-assertion.js +76 -0
  46. package/dist/interact/assertions/state-comparison.js +141 -0
  47. package/dist/interact/assertions/visibility-assertion.js +34 -0
  48. package/dist/interact/conditional-action.js +12 -7
  49. package/dist/interact/index.js +65 -3090
  50. package/dist/interact/input-service.js +1 -0
  51. package/dist/interact/interaction-trace.js +21 -0
  52. package/dist/interact/model.js +1 -0
  53. package/dist/interact/observation.js +1 -0
  54. package/dist/interact/targeting/candidate-ancestor.js +58 -0
  55. package/dist/interact/targeting/candidate-result.js +23 -0
  56. package/dist/interact/targeting/candidate-scoring.js +50 -0
  57. package/dist/interact/targeting/element-helpers.js +216 -0
  58. package/dist/interact/targeting/find-element.js +65 -0
  59. package/dist/interact/targeting/nearby-control.js +56 -0
  60. package/dist/interact/targeting/resolved-elements.js +21 -0
  61. package/dist/interact/targeting/tap-target.js +17 -0
  62. package/dist/interact/targeting/target-scope.js +104 -0
  63. package/dist/interact/waits/legacy-log-wait.js +38 -0
  64. package/dist/interact/waits/legacy-wait-observations.js +103 -0
  65. package/dist/interact/waits/legacy-wait-state.js +34 -0
  66. package/dist/interact/waits/legacy-wait.js +48 -0
  67. package/dist/interact/waits/screen-wait.js +49 -0
  68. package/dist/interact/waits/ui-change-helpers.js +157 -0
  69. package/dist/interact/waits/ui-change-state.js +18 -0
  70. package/dist/interact/waits/ui-change-wait.js +77 -0
  71. package/dist/interact/waits/ui-wait-condition.js +56 -0
  72. package/dist/interact/waits/ui-wait-state.js +38 -0
  73. package/dist/interact/waits/ui-wait.js +106 -0
  74. package/dist/manage/android.js +55 -51
  75. package/dist/manage/index.js +38 -75
  76. package/dist/manage/ios/build-types.js +1 -0
  77. package/dist/manage/ios/build.js +210 -0
  78. package/dist/manage/ios/project-discovery.js +29 -0
  79. package/dist/manage/ios/scheme.js +18 -0
  80. package/dist/manage/ios.js +42 -256
  81. package/dist/observe/android/context.js +1 -0
  82. package/dist/observe/android/current-screen.js +55 -0
  83. package/dist/observe/android/fingerprint.js +13 -0
  84. package/dist/observe/android/log-stream.js +132 -0
  85. package/dist/observe/android/logs.js +107 -0
  86. package/dist/observe/android/screenshot.js +70 -0
  87. package/dist/observe/android/ui-tree.js +107 -0
  88. package/dist/observe/android.js +16 -520
  89. package/dist/observe/evidence.js +114 -75
  90. package/dist/observe/index.js +16 -263
  91. package/dist/observe/ios/context.js +16 -0
  92. package/dist/observe/ios/fingerprint.js +12 -0
  93. package/dist/observe/ios/log-stream.js +105 -0
  94. package/dist/observe/ios/logs.js +168 -0
  95. package/dist/observe/ios/screenshot.js +22 -0
  96. package/dist/observe/ios/ui-elements.js +241 -0
  97. package/dist/observe/ios/ui-tree.js +137 -0
  98. package/dist/observe/ios.js +17 -715
  99. package/dist/observe/logs/model.js +1 -0
  100. package/dist/{network → observe/network}/index.js +2 -2
  101. package/dist/observe/readiness-service.js +4 -2
  102. package/dist/observe/readiness.js +29 -10
  103. package/dist/observe/screenshot-encoding.js +41 -0
  104. package/dist/observe/snapshot-collection.js +79 -0
  105. package/dist/observe/snapshot-logs.js +30 -0
  106. package/dist/observe/snapshot-metadata.js +43 -31
  107. package/dist/observe/snapshot-semantic.js +115 -0
  108. package/dist/observe/system-trace.js +22 -13
  109. package/dist/runtime/action-results.js +270 -0
  110. package/dist/runtime/clock.js +4 -0
  111. package/dist/server/common.js +1 -256
  112. package/dist/server/definitions/assert.js +162 -0
  113. package/dist/server/definitions/diagnostic.js +118 -0
  114. package/dist/server/definitions/input.js +274 -0
  115. package/dist/server/definitions/manage.js +194 -0
  116. package/dist/server/definitions/observe.js +149 -0
  117. package/dist/server/definitions/target.js +138 -0
  118. package/dist/server/definitions/wait.js +105 -0
  119. package/dist/server/handlers/action-execution.js +163 -0
  120. package/dist/server/handlers/correlation.js +23 -0
  121. package/dist/server/{debugger-handlers.js → handlers/debugger-handlers.js} +22 -12
  122. package/dist/server/handlers/debugger-response.js +9 -0
  123. package/dist/server/handlers/diagnostic.js +69 -0
  124. package/dist/server/handlers/handler-types.js +1 -0
  125. package/dist/server/handlers/input.js +154 -0
  126. package/dist/server/handlers/interact.js +113 -0
  127. package/dist/server/handlers/journey-assertions.js +35 -0
  128. package/dist/server/handlers/journey.js +147 -0
  129. package/dist/server/handlers/manage.js +119 -0
  130. package/dist/server/handlers/observe.js +95 -0
  131. package/dist/server/handlers/payload.js +4 -0
  132. package/dist/server/handlers/tool-handlers.js +160 -0
  133. package/dist/server/schemas/condition-schema.js +2 -0
  134. package/dist/server/{debugger-schema.js → schemas/debugger-schema.js} +11 -6
  135. package/dist/server/{journey-schema.js → schemas/journey-schema.js} +7 -22
  136. package/dist/server/tool-definitions.js +58 -1135
  137. package/dist/server-core.js +2 -2
  138. package/dist/system/android-status.js +101 -0
  139. package/dist/system/android.js +37 -27
  140. package/dist/system/index.js +13 -439
  141. package/dist/system/ios-status.js +132 -0
  142. package/dist/system/ios.js +11 -8
  143. package/dist/system/model.js +1 -0
  144. package/dist/system/probes.js +105 -0
  145. package/dist/system/status-summary.js +43 -0
  146. package/dist/system/unsupported-status.js +52 -0
  147. package/dist/utils/android/commands.js +89 -0
  148. package/dist/utils/android/gradle.js +139 -0
  149. package/dist/utils/android/logs.js +103 -0
  150. package/dist/utils/android/ui-tree.js +233 -0
  151. package/dist/utils/android/utils.js +6 -564
  152. package/dist/utils/cli/idb/idb-helper.js +4 -6
  153. package/dist/utils/cli/idb/install-idb.js +13 -9
  154. package/dist/utils/cli/ios/preflight-ios.js +42 -38
  155. package/dist/utils/ios/utils.js +45 -46
  156. package/dist/utils/java.js +65 -49
  157. package/dist/utils/resolve-device.js +4 -2
  158. package/dist/utils/ui/index.js +26 -26
  159. package/docs/CHANGELOG.md +44 -0
  160. package/docs/README.md +38 -0
  161. package/docs/benchmarks/README.md +18 -0
  162. package/docs/benchmarks/interaction-latency/README.md +14 -0
  163. package/docs/benchmarks/interaction-latency/final.md +4 -1
  164. package/docs/benchmarks/interaction-latency/ios-validation.md +2 -0
  165. package/docs/benchmarks/ui-tree-performance/README.md +24 -16
  166. package/docs/benchmarks/ui-tree-performance/comparison.md +11 -10
  167. package/docs/code-structure.md +118 -0
  168. package/docs/specs/001-state-verification/spec.md +16 -14
  169. package/docs/specs/002-richer-element-identity/spec.md +12 -10
  170. package/docs/specs/003-wait-and-synchronization-reliability/spec.md +12 -10
  171. package/docs/specs/004-action-verification-routing/spec.md +3 -3
  172. package/docs/specs/005-unified-action-execution-and-verification-model/spec.md +2 -0
  173. package/docs/specs/006-runtime-action-instrumentation-and-binding-layer/spec.md +2 -0
  174. package/docs/specs/007-actionability-resolution-and-executable-target-selection/spec.md +3 -1
  175. package/docs/specs/008-adjustable-control-support-and-semantic-value-manipulation/spec.md +3 -4
  176. package/docs/specs/009-semantic-control-modeling-for-custom-and-composite-controls/spec.md +2 -0
  177. package/docs/specs/010-verification-stabilization-and-temporal-convergence/spec.md +2 -2
  178. package/docs/specs/011-recovery-and-replanning-for-failed-or-ambiguous-interaction-flows/spec.md +2 -2
  179. package/docs/specs/011.1-recovery-contract-types-and-runtime-wiring-spec/spec.md +2 -0
  180. package/docs/specs/012-action-trace-and-xecution-observability/spec.md +2 -3
  181. package/docs/specs/013-wait-and-synchronization-reliability/spec.md +19 -19
  182. package/docs/specs/014-actionability-resolution/spec.md +20 -18
  183. package/docs/specs/015-adjustable-control-support/spec.md +17 -15
  184. package/docs/specs/016-environment-tools/spec.md +20 -20
  185. package/docs/specs/018-interaction-latency-and-batched-journeys/spec.md +2 -0
  186. package/docs/specs/019-ui-tree-observation-performance/spec.md +3 -1
  187. package/docs/specs/020-conditional-actions/intent.md +5 -3
  188. package/docs/specs/020-conditional-actions/plan.md +8 -9
  189. package/docs/specs/020-conditional-actions/spec.md +4 -2
  190. package/docs/specs/021-app-readiness-signals/intent.md +4 -2
  191. package/docs/specs/021-app-readiness-signals/plan.md +7 -6
  192. package/docs/specs/021-app-readiness-signals/spec.md +3 -1
  193. package/docs/specs/022-correlated-evidence/intent.md +4 -4
  194. package/docs/specs/022-correlated-evidence/plan.md +10 -11
  195. package/docs/specs/022-correlated-evidence/spec.md +3 -1
  196. package/docs/specs/023-android-system-traces/intent.md +3 -2
  197. package/docs/specs/023-android-system-traces/plan.md +5 -7
  198. package/docs/specs/023-android-system-traces/spec.md +3 -1
  199. package/docs/specs/024-android-heap-dump-capture/intent.md +3 -1
  200. package/docs/specs/025-android-runtime-debugger/intent.md +4 -3
  201. package/docs/specs/025-android-runtime-debugger/plan.md +3 -1
  202. package/docs/specs/025-android-runtime-debugger/spec.md +4 -3
  203. package/docs/specs/026-code-structure-and-quality-gates/health-plan.md +88 -0
  204. package/docs/specs/026-code-structure-and-quality-gates/intent.md +47 -0
  205. package/docs/specs/026-code-structure-and-quality-gates/plan.md +191 -0
  206. package/docs/specs/026-code-structure-and-quality-gates/spec.md +138 -0
  207. package/docs/specs/026-code-structure-and-quality-gates/verification-final.json +200 -0
  208. package/docs/specs/026-code-structure-and-quality-gates/verification-thin-facade.json +154 -0
  209. package/docs/specs/026-code-structure-and-quality-gates/verification.json +136 -0
  210. package/docs/specs/027-debugger-failure-diagnostics/intent.md +43 -0
  211. package/docs/specs/027-debugger-failure-diagnostics/plan.md +86 -0
  212. package/docs/specs/027-debugger-failure-diagnostics/spec.md +133 -0
  213. package/docs/specs/028-compact-debugger-responses/intent.md +41 -0
  214. package/docs/specs/028-compact-debugger-responses/plan.md +112 -0
  215. package/docs/specs/028-compact-debugger-responses/spec.md +108 -0
  216. package/docs/specs/029-kotlin-locals-validation/report.md +119 -0
  217. package/docs/specs/030-android-coroutine-validation/evidence.json +475 -0
  218. package/docs/specs/030-android-coroutine-validation/intent.md +47 -0
  219. package/docs/specs/030-android-coroutine-validation/plan.md +147 -0
  220. package/docs/specs/030-android-coroutine-validation/report.md +240 -0
  221. package/docs/specs/030-android-coroutine-validation/spec.md +107 -0
  222. package/docs/tools/TOOLS.md +31 -18
  223. package/docs/tools/{evidence.md → correlated-evidence.md} +9 -1
  224. package/docs/tools/debugger.md +76 -0
  225. package/docs/tools/interact.md +42 -20
  226. package/docs/tools/manage.md +25 -44
  227. package/docs/tools/observe.md +55 -23
  228. package/docs/tools/system-traces.md +9 -1
  229. package/docs/tools/system.md +12 -5
  230. package/package.json +11 -8
  231. package/scripts/architecture-graph.mjs +69 -0
  232. package/scripts/check-architecture.mjs +47 -0
  233. package/scripts/code-quality-baseline.json +1 -0
  234. package/scripts/code-quality.mjs +161 -0
  235. package/src/contracts/execution-controls.ts +26 -0
  236. package/src/contracts/selector.ts +17 -0
  237. package/src/contracts/target.ts +19 -0
  238. package/src/contracts/ui.ts +94 -0
  239. package/src/debugger/android.ts +2 -1
  240. package/src/debugger/diagnostics.ts +43 -0
  241. package/src/debugger/guard.ts +6 -1
  242. package/{worker → src/debugger/java}/DebuggerWorker.java +9 -3
  243. package/src/debugger/protocol.ts +37 -0
  244. package/src/debugger/sessions.ts +314 -155
  245. package/src/debugger/worker.ts +51 -38
  246. package/src/interact/adjustment/adjustment-context.ts +83 -0
  247. package/src/interact/adjustment/adjustment-gestures.ts +48 -0
  248. package/src/interact/adjustment/adjustment-plan.ts +23 -0
  249. package/src/interact/adjustment/adjustment-resolution.ts +104 -0
  250. package/src/interact/adjustment/adjustment-result.ts +34 -0
  251. package/src/interact/adjustment/adjustment-semantic.ts +53 -0
  252. package/src/interact/adjustment/adjustment-verification.ts +60 -0
  253. package/src/interact/adjustment/adjustment.ts +49 -0
  254. package/src/interact/adjustment/control-geometry.ts +120 -0
  255. package/src/interact/adjustment/control-values.ts +141 -0
  256. package/src/interact/adjustment/slider-labels.ts +84 -0
  257. package/src/interact/assertions/screen-assertion.ts +39 -0
  258. package/src/interact/assertions/state-assertion-context.ts +48 -0
  259. package/src/interact/assertions/state-assertion.ts +64 -0
  260. package/src/interact/assertions/state-comparison.ts +168 -0
  261. package/src/interact/assertions/visibility-assertion.ts +41 -0
  262. package/src/interact/conditional-action.ts +18 -12
  263. package/src/interact/index.ts +76 -3492
  264. package/src/interact/input-service.ts +11 -0
  265. package/src/interact/interaction-trace.ts +36 -0
  266. package/src/interact/model.ts +109 -0
  267. package/src/interact/observation.ts +7 -0
  268. package/src/interact/targeting/candidate-ancestor.ts +49 -0
  269. package/src/interact/targeting/candidate-result.ts +24 -0
  270. package/src/interact/targeting/candidate-scoring.ts +42 -0
  271. package/src/interact/targeting/element-helpers.ts +243 -0
  272. package/src/interact/targeting/find-element.ts +47 -0
  273. package/src/interact/targeting/nearby-control.ts +46 -0
  274. package/src/interact/targeting/resolved-elements.ts +20 -0
  275. package/src/interact/targeting/tap-target.ts +22 -0
  276. package/src/interact/targeting/target-scope.ts +121 -0
  277. package/src/interact/waits/legacy-log-wait.ts +30 -0
  278. package/src/interact/waits/legacy-wait-observations.ts +55 -0
  279. package/src/interact/waits/legacy-wait-state.ts +48 -0
  280. package/src/interact/waits/legacy-wait.ts +34 -0
  281. package/src/interact/waits/screen-wait.ts +37 -0
  282. package/src/interact/waits/ui-change-helpers.ts +184 -0
  283. package/src/interact/waits/ui-change-state.ts +26 -0
  284. package/src/interact/waits/ui-change-wait.ts +59 -0
  285. package/src/interact/waits/ui-wait-condition.ts +52 -0
  286. package/src/interact/waits/ui-wait-state.ts +55 -0
  287. package/src/interact/waits/ui-wait.ts +84 -0
  288. package/src/manage/android.ts +56 -50
  289. package/src/manage/index.ts +63 -93
  290. package/src/manage/ios/build-types.ts +14 -0
  291. package/src/manage/ios/build.ts +230 -0
  292. package/src/manage/ios/project-discovery.ts +26 -0
  293. package/src/manage/ios/scheme.ts +16 -0
  294. package/src/manage/ios.ts +65 -298
  295. package/src/observe/android/context.ts +1 -0
  296. package/src/observe/android/current-screen.ts +63 -0
  297. package/src/observe/android/fingerprint.ts +13 -0
  298. package/src/observe/android/log-stream.ts +127 -0
  299. package/src/observe/android/logs.ts +110 -0
  300. package/src/observe/android/screenshot.ts +79 -0
  301. package/src/observe/android/ui-tree.ts +113 -0
  302. package/src/observe/android.ts +17 -531
  303. package/src/observe/evidence.ts +122 -70
  304. package/src/observe/index.ts +24 -286
  305. package/src/observe/ios/context.ts +22 -0
  306. package/src/observe/ios/fingerprint.ts +12 -0
  307. package/src/observe/ios/log-stream.ts +103 -0
  308. package/src/observe/ios/logs.ts +179 -0
  309. package/src/observe/ios/screenshot.ts +26 -0
  310. package/src/observe/ios/ui-elements.ts +266 -0
  311. package/src/observe/ios/ui-tree.ts +143 -0
  312. package/src/observe/ios.ts +18 -749
  313. package/src/observe/logs/model.ts +11 -0
  314. package/src/{network → observe/network}/index.ts +2 -2
  315. package/src/observe/readiness-service.ts +5 -3
  316. package/src/observe/readiness.ts +39 -18
  317. package/src/observe/screenshot-encoding.ts +32 -0
  318. package/src/observe/snapshot-collection.ts +69 -0
  319. package/src/observe/snapshot-logs.ts +26 -0
  320. package/src/observe/snapshot-metadata.ts +51 -31
  321. package/src/observe/snapshot-semantic.ts +153 -0
  322. package/src/observe/system-trace.ts +24 -15
  323. package/src/runtime/action-results.ts +376 -0
  324. package/src/runtime/clock.ts +9 -0
  325. package/src/server/common.ts +9 -359
  326. package/src/server/definitions/assert.ts +164 -0
  327. package/src/server/definitions/diagnostic.ts +120 -0
  328. package/src/server/definitions/input.ts +276 -0
  329. package/src/server/definitions/manage.ts +196 -0
  330. package/src/server/definitions/observe.ts +151 -0
  331. package/src/server/definitions/target.ts +140 -0
  332. package/src/server/definitions/wait.ts +107 -0
  333. package/src/server/handlers/action-execution.ts +164 -0
  334. package/src/server/handlers/correlation.ts +20 -0
  335. package/src/server/handlers/debugger-handlers.ts +39 -0
  336. package/src/server/handlers/debugger-response.ts +7 -0
  337. package/src/server/handlers/diagnostic.ts +79 -0
  338. package/src/server/handlers/handler-types.ts +7 -0
  339. package/src/server/handlers/input.ts +173 -0
  340. package/src/server/handlers/interact.ts +135 -0
  341. package/src/server/handlers/journey-assertions.ts +39 -0
  342. package/src/server/handlers/journey.ts +132 -0
  343. package/src/server/handlers/manage.ts +141 -0
  344. package/src/server/handlers/observe.ts +116 -0
  345. package/src/server/handlers/payload.ts +36 -0
  346. package/src/server/handlers/tool-handlers.ts +159 -0
  347. package/src/server/schemas/condition-schema.ts +2 -0
  348. package/src/server/{debugger-schema.ts → schemas/debugger-schema.ts} +13 -7
  349. package/src/server/{journey-schema.ts → schemas/journey-schema.ts} +7 -22
  350. package/src/server/{system-trace-schema.ts → schemas/system-trace-schema.ts} +3 -3
  351. package/src/server/tool-definitions.ts +67 -1143
  352. package/src/server-core.ts +2 -2
  353. package/src/server.ts +1 -1
  354. package/src/system/android-status.ts +124 -0
  355. package/src/system/android.ts +33 -21
  356. package/src/system/index.ts +21 -525
  357. package/src/system/ios-status.ts +147 -0
  358. package/src/system/ios.ts +14 -10
  359. package/src/system/model.ts +41 -0
  360. package/src/system/probes.ts +114 -0
  361. package/src/system/status-summary.ts +47 -0
  362. package/src/system/unsupported-status.ts +55 -0
  363. package/src/types.ts +16 -106
  364. package/src/utils/android/commands.ts +81 -0
  365. package/src/utils/android/gradle.ts +132 -0
  366. package/src/utils/android/logs.ts +103 -0
  367. package/src/utils/android/ui-tree.ts +239 -0
  368. package/src/utils/android/utils.ts +16 -556
  369. package/src/utils/cli/idb/check-idb.ts +4 -4
  370. package/src/utils/cli/idb/idb-helper.ts +6 -8
  371. package/src/utils/cli/idb/install-idb.ts +15 -9
  372. package/src/utils/cli/ios/preflight-ios.ts +39 -31
  373. package/src/utils/cli/ios/run-ios-ui-tree-tap.ts +4 -4
  374. package/src/utils/ios/utils.ts +57 -61
  375. package/src/utils/java.ts +62 -43
  376. package/src/utils/resolve-device.ts +9 -6
  377. package/src/utils/ui/index.ts +28 -28
  378. package/test/device/automated/debugger/coroutine-lifecycle.ts +27 -0
  379. package/test/device/automated/debugger/coroutine-support.ts +140 -0
  380. package/test/device/automated/debugger/coroutine.android.test.ts +169 -0
  381. package/test/device/automated/debugger/kotlin.android.test.ts +127 -0
  382. package/test/device/automated/debugger/runtime.android.test.ts +19 -3
  383. package/test/device/automated/interact/interaction.android.test.ts +56 -0
  384. package/test/device/automated/observe/system-trace.smoke.ts +1 -1
  385. package/test/device/manual/interact/modul8_selector_tap.manual.ts +1 -1
  386. package/test/fixtures/contracts/tool-definitions.json +3536 -0
  387. package/test/fixtures/coroutine-app/MainActivity.kt +100 -0
  388. package/test/fixtures/coroutine-app/README.md +48 -0
  389. package/test/fixtures/coroutine-app/build.ts +35 -0
  390. package/test/fixtures/debugger-app/MainActivity.java +21 -0
  391. package/test/fixtures/debugger-app/MainActivity.kt +32 -0
  392. package/test/fixtures/debugger-app/README.md +52 -0
  393. package/test/fixtures/debugger-app/build.ts +25 -15
  394. package/test/fixtures/latency-journey-app/README.md +2 -0
  395. package/test/fixtures/readiness-app/README.md +2 -0
  396. package/test/unit/debugger/coroutine-lifecycle.test.ts +43 -0
  397. package/test/unit/debugger/diagnostic-sessions.test.ts +67 -0
  398. package/test/unit/debugger/diagnostics.test.ts +80 -0
  399. package/test/unit/debugger/guards.test.ts +4 -0
  400. package/test/unit/debugger/preparation.test.ts +32 -0
  401. package/test/unit/debugger/protocol.test.ts +15 -0
  402. package/test/unit/debugger/sessions.test.ts +1 -1
  403. package/test/unit/interact/adjustment_service.test.ts +42 -0
  404. package/test/unit/interact/adjustment_verification.test.ts +35 -0
  405. package/test/unit/interact/clock_dependencies.test.ts +63 -0
  406. package/test/unit/interact/element_helpers.test.ts +19 -0
  407. package/test/unit/interact/legacy_wait_service.test.ts +17 -0
  408. package/test/unit/interact/state_comparison.test.ts +20 -0
  409. package/test/unit/interact/target_scope.test.ts +35 -0
  410. package/test/unit/network/get_network_activity.test.ts +1 -1
  411. package/test/unit/observe/screenshot-encoding.test.ts +30 -0
  412. package/test/unit/observe/snapshot-logs.test.ts +14 -0
  413. package/test/unit/quality/architecture.test.ts +60 -0
  414. package/test/unit/quality/code-quality.test.ts +58 -0
  415. package/test/unit/quality/interaction_boundaries.test.ts +24 -0
  416. package/test/unit/server/conditional_actions.test.ts +1 -1
  417. package/test/unit/server/conditional_schema.test.ts +2 -2
  418. package/test/unit/server/debugger-response.test.ts +63 -0
  419. package/test/unit/server/tool_definitions_parity.test.ts +8 -0
  420. package/dist/server/tool-handlers.js +0 -1000
  421. package/docs/ROADMAP.md +0 -646
  422. package/docs/benchmarks/ui-tree-performance/bridge-probe.json +0 -29
  423. package/docs/benchmarks/ui-tree-performance/initial-baseline.json +0 -59560
  424. package/docs/specs/baseline-spec-v0.md +0 -312
  425. package/docs/specs/mcp-tooling-spec-v1.md +0 -446
  426. package/src/server/debugger-handlers.ts +0 -29
  427. package/src/server/tool-handlers.ts +0 -1008
  428. /package/dist/{server/condition-schema.js → contracts/condition.js} +0 -0
  429. /package/dist/server/{readiness-schema.js → schemas/readiness-schema.js} +0 -0
  430. /package/dist/server/{system-trace-schema.js → schemas/system-trace-schema.js} +0 -0
  431. /package/src/{server/condition-schema.ts → contracts/condition.ts} +0 -0
  432. /package/src/server/{readiness-schema.ts → schemas/readiness-schema.ts} +0 -0
@@ -0,0 +1,116 @@
1
+
2
+
3
+ import { ToolsObserve } from '../../observe/index.js'
4
+ import { withObservationContext } from '../../observe/performance-metrics.js'
5
+ import {
6
+ getBooleanArg,
7
+ getNumberArg,
8
+ getStringArg,
9
+ requireStringArg,
10
+ ToolCallArgs,
11
+ wrapResponse
12
+ } from '../common.js'
13
+ import type { PlatformArg, ResponseMode } from './handler-types.js'
14
+
15
+ export async function handleGetLogs(args: ToolCallArgs) {
16
+ const platform = requireStringArg(args, 'platform') as PlatformArg
17
+ const appId = getStringArg(args, 'appId')
18
+ const deviceId = getStringArg(args, 'deviceId')
19
+ const pid = getNumberArg(args, 'pid')
20
+ const tag = getStringArg(args, 'tag')
21
+ const level = getStringArg(args, 'level')
22
+ const contains = getStringArg(args, 'contains')
23
+ const since_seconds = getNumberArg(args, 'since_seconds')
24
+ const limit = getNumberArg(args, 'limit')
25
+ const lines = getNumberArg(args, 'lines')
26
+ const res = await ToolsObserve.getLogsHandler({ platform, appId, deviceId, pid, tag, level, contains, since_seconds, limit, lines })
27
+ const filtered = !!(pid || tag || level || contains || since_seconds || appId)
28
+ return {
29
+ content: [
30
+ { type: 'text' as const, text: JSON.stringify({ device: res.device, result: { count: res.logCount, filtered, crashLines: (res.crashLines || []), source: res.source, meta: res.meta || {} } }, null, 2) },
31
+ { type: 'text' as const, text: JSON.stringify({ logs: res.logs }, null, 2) }
32
+ ]
33
+ }
34
+ }
35
+
36
+ export async function handleCaptureScreenshot(args: ToolCallArgs) {
37
+ const platform = requireStringArg(args, 'platform') as PlatformArg
38
+ const deviceId = getStringArg(args, 'deviceId')
39
+ const started = performance.now()
40
+ const res = await ToolsObserve.captureScreenshotHandler({ platform, deviceId })
41
+ const screenshotMs = performance.now() - started
42
+ const mime = res.screenshot_mime || 'image/png'
43
+ const content: Array<{ type: 'text' | 'image'; text?: string; data?: string; mimeType?: string }> = [
44
+ { type: 'text', text: JSON.stringify({ device: res.device, result: { resolution: res.resolution, mimeType: mime }, timing: { total_ms: screenshotMs, screenshot_ms: screenshotMs } }, null, 2) },
45
+ { type: 'image', data: res.screenshot, mimeType: mime }
46
+ ]
47
+ if (res.screenshot_fallback) {
48
+ content.push({ type: 'text', text: JSON.stringify({ note: 'JPEG fallback included for compatibility', mimeType: res.screenshot_fallback_mime || 'image/jpeg' }) })
49
+ content.push({ type: 'image', data: res.screenshot_fallback, mimeType: res.screenshot_fallback_mime || 'image/jpeg' })
50
+ }
51
+ return { content }
52
+ }
53
+
54
+ export async function handleCaptureDebugSnapshot(args: ToolCallArgs) {
55
+ const reason = getStringArg(args, 'reason')
56
+ const includeLogs = getBooleanArg(args, 'includeLogs')
57
+ const logLines = getNumberArg(args, 'logLines')
58
+ const platform = getStringArg(args, 'platform') as PlatformArg | undefined
59
+ const appId = getStringArg(args, 'appId')
60
+ const deviceId = getStringArg(args, 'deviceId')
61
+ const sessionId = getStringArg(args, 'sessionId')
62
+ const includeScreenshot = getBooleanArg(args, 'includeScreenshot')
63
+ const res = await ToolsObserve.captureDebugSnapshotHandler({ reason, includeLogs, includeScreenshot, logLines, platform, appId, deviceId, sessionId })
64
+ return wrapResponse(res)
65
+ }
66
+
67
+ export async function handleGetUITree(args: ToolCallArgs) {
68
+ const platform = requireStringArg(args, 'platform') as PlatformArg
69
+ const deviceId = getStringArg(args, 'deviceId')
70
+ const responseMode = (getStringArg(args, 'responseMode') as ResponseMode | undefined) ?? 'debug'
71
+ const sinceSnapshotRevision = getNumberArg(args, 'sinceSnapshotRevision')
72
+ const metricsStepId = process.env.MOBILE_DEBUG_MCP_UI_TREE_METRICS === '1' ? getStringArg(args, '_metricsStepId') : undefined
73
+ const res = await withObservationContext({ stepId: metricsStepId, purpose: metricsStepId?.includes('post-response') ? 'assertion' : 'debug' }, () =>
74
+ ToolsObserve.getUITreeHandler({ platform, deviceId, responseMode, sinceSnapshotRevision }))
75
+ return wrapResponse(res)
76
+ }
77
+
78
+ export async function handleGetCurrentScreen(args: ToolCallArgs) {
79
+ const deviceId = getStringArg(args, 'deviceId')
80
+ const res = await ToolsObserve.getCurrentScreenHandler({ deviceId })
81
+ return wrapResponse(res)
82
+ }
83
+
84
+ export async function handleGetScreenFingerprint(args: ToolCallArgs) {
85
+ const platform = getStringArg(args, 'platform') as PlatformArg | undefined
86
+ const deviceId = getStringArg(args, 'deviceId')
87
+ const res = await ToolsObserve.getScreenFingerprintHandler({ platform, deviceId })
88
+ return wrapResponse(res)
89
+ }
90
+
91
+ export async function handleStartLogStream(args: ToolCallArgs) {
92
+ const platform = (getStringArg(args, 'platform') as PlatformArg | undefined) ?? 'android'
93
+ const packageName = requireStringArg(args, 'packageName')
94
+ const level = (getStringArg(args, 'level') as 'error' | 'warn' | 'info' | 'debug' | undefined) ?? 'error'
95
+ const sessionId = getStringArg(args, 'sessionId')
96
+ const deviceId = getStringArg(args, 'deviceId')
97
+ const res = await ToolsObserve.startLogStreamHandler({ platform, packageName, level, sessionId, deviceId })
98
+ return wrapResponse(res)
99
+ }
100
+
101
+ export async function handleReadLogStream(args: ToolCallArgs) {
102
+ const platform = getStringArg(args, 'platform') as PlatformArg | undefined
103
+ const sessionId = getStringArg(args, 'sessionId')
104
+ const limit = getNumberArg(args, 'limit')
105
+ const since = getStringArg(args, 'since')
106
+ const res = await ToolsObserve.readLogStreamHandler({ platform, sessionId, limit, since })
107
+ return wrapResponse(res)
108
+ }
109
+
110
+ export async function handleStopLogStream(args: ToolCallArgs) {
111
+ const platform = getStringArg(args, 'platform') as PlatformArg | undefined
112
+ const sessionId = getStringArg(args, 'sessionId')
113
+ const res = await ToolsObserve.stopLogStreamHandler({ platform, sessionId })
114
+ return wrapResponse(res)
115
+ }
116
+
@@ -0,0 +1,36 @@
1
+ import type { ActionExecutionResult, DeviceInfo, ActionConditionResult, TimingBreakdown } from '../../types.js'
2
+ import type { ToolCallResult } from '../common.js'
3
+
4
+ // Internal decoded MCP payloads vary by capability. Keep the fields inspected by
5
+ // orchestration explicit while preserving untouched capability-specific fields.
6
+ export interface ToolPayload {
7
+ success?: boolean
8
+ action_id?: string
9
+ action_type?: string
10
+ lifecycle_state?: string
11
+ failure_code?: string
12
+ retryable?: boolean
13
+ device?: DeviceInfo
14
+ target?: ActionExecutionResult['target']
15
+ ui_fingerprint_before?: string | null
16
+ error?: string | { code?: string; message?: string }
17
+ reason?: string
18
+ details?: { error?: unknown }
19
+ dispatch_started?: boolean
20
+ delivery_status?: string
21
+ state_delta?: unknown
22
+ state_delta_available?: boolean
23
+ actual_state?: unknown
24
+ observed_fingerprint?: string | null
25
+ status?: string
26
+ condition?: ActionConditionResult
27
+ verification?: { mode?: string; status: string }
28
+ evidence?: { action_id?: string }
29
+ verification_diagnostic?: { code: string; retryable: boolean }
30
+ observed?: { matched_count?: number | null }
31
+ timing?: TimingBreakdown
32
+ }
33
+ export function payloadOf(response: ToolCallResult): ToolPayload {
34
+ const text = response.content.find(item => item.type === 'text')?.text
35
+ return text ? JSON.parse(text) as ToolPayload : null as unknown as ToolPayload
36
+ }
@@ -0,0 +1,159 @@
1
+ import type { DeviceInfo } from '../../types.js'
2
+ import { handleDebugger } from './debugger-handlers.js'
3
+ import { debuggerContracts } from '../schemas/debugger-schema.js'
4
+ import { debuggerSessions } from '../../debugger/sessions.js'
5
+ import { withDebuggerGuard, guardDispatched } from '../../debugger/guard.js'
6
+ import { TraceError } from '../../observe/system-trace.js'
7
+ import { systemTraceTools } from '../schemas/system-trace-schema.js'
8
+ import { withTiming } from '../../utils/timing.js'
9
+ import { readinessSchema, readinessWaitSchema } from '../schemas/readiness-schema.js'
10
+ import { getAppReadiness } from '../../observe/readiness-service.js'
11
+ import { validateExpectedState } from '../schemas/condition-schema.js'
12
+ import { resolveTargetDevice, withResolvedDevice } from '../../utils/resolve-device.js'
13
+
14
+ import {
15
+ ToolCallArgs,
16
+ ToolCallResult,
17
+ ToolHandler,
18
+ wrapResponse,
19
+ wrapToolError
20
+ } from '../common.js'
21
+ import { handleTerminateApp, handleResetAppData, handleInstallApp, handleBuildApp, handleBuildAndInstall, handleStartApp, handleRestartApp } from './manage.js'
22
+ import { handleGetLogs, handleCaptureScreenshot, handleCaptureDebugSnapshot, handleGetUITree, handleGetCurrentScreen, handleGetScreenFingerprint, handleStartLogStream, handleReadLogStream, handleStopLogStream } from './observe.js'
23
+ import { handleWaitForScreenChange, handleExpectScreen, handleExpectElementVisible, handleExpectState, handleAdjustControl, handleWaitForUI, handleWaitForUIChange, handleFindElement } from './interact.js'
24
+ import { handleTap, handleTapElement, handleSwipe, handleScrollToElement, handleTypeText, handlePressBack } from './input.js'
25
+ import { handleRunJourney } from './journey.js'
26
+ import { handleListDevices, handleGetSystemStatus, handleClassifyActionOutcome, handleGetNetworkActivity, handleSystemTrace } from './diagnostic.js'
27
+ import { correlatedHandler } from './action-execution.js'
28
+
29
+ const systemTraceToolNames = new Set(systemTraceTools.map(tool => tool.name))
30
+
31
+ export const toolHandlers: Record<string, ToolHandler> = {
32
+ ...Object.fromEntries(systemTraceTools.map(({ name }) => [name, (args: ToolCallArgs) => handleSystemTrace(name, args)])),
33
+ get_app_readiness: async args => {
34
+ const parsed = readinessSchema.safeParse(args)
35
+ if (!parsed.success) throw new Error('INVALID_ARGUMENT: invalid readiness arguments')
36
+ return wrapResponse(await getAppReadiness(parsed.data))
37
+ },
38
+ wait_for_app_ready: async args => {
39
+ const parsed = readinessWaitSchema.safeParse(args)
40
+ if (!parsed.success) throw new Error('INVALID_ARGUMENT: invalid readiness arguments')
41
+ return wrapResponse(await getAppReadiness(parsed.data, true))
42
+ },
43
+ start_app: handleStartApp,
44
+ terminate_app: handleTerminateApp,
45
+ restart_app: handleRestartApp,
46
+ reset_app_data: handleResetAppData,
47
+ install_app: handleInstallApp,
48
+ build_app: handleBuildApp,
49
+ build_and_install: handleBuildAndInstall,
50
+ get_logs: handleGetLogs,
51
+ list_devices: handleListDevices,
52
+ get_system_status: handleGetSystemStatus,
53
+ capture_screenshot: handleCaptureScreenshot,
54
+ capture_debug_snapshot: handleCaptureDebugSnapshot,
55
+ get_ui_tree: handleGetUITree,
56
+ get_current_screen: handleGetCurrentScreen,
57
+ get_screen_fingerprint: handleGetScreenFingerprint,
58
+ wait_for_screen_change: handleWaitForScreenChange,
59
+ wait_for_ui_change: handleWaitForUIChange,
60
+ expect_screen: handleExpectScreen,
61
+ expect_element_visible: handleExpectElementVisible,
62
+ expect_state: handleExpectState,
63
+ adjust_control: handleAdjustControl,
64
+ wait_for_ui: handleWaitForUI,
65
+ find_element: handleFindElement,
66
+ tap: handleTap,
67
+ tap_element: handleTapElement,
68
+ run_journey: handleRunJourney,
69
+ swipe: handleSwipe,
70
+ scroll_to_element: handleScrollToElement,
71
+ type_text: handleTypeText,
72
+ press_back: handlePressBack,
73
+ start_log_stream: handleStartLogStream,
74
+ read_log_stream: handleReadLogStream,
75
+ stop_log_stream: handleStopLogStream,
76
+ classify_action_outcome: handleClassifyActionOutcome,
77
+ get_network_activity: handleGetNetworkActivity
78
+ }
79
+
80
+ for (const name of ['capture_screenshot', 'get_ui_tree', 'get_screen_fingerprint', 'capture_debug_snapshot', 'get_logs', 'read_log_stream', 'run_journey', 'adjust_control']) {
81
+ toolHandlers[name] = correlatedHandler(name, toolHandlers[name], name === 'adjust_control', name === 'run_journey')
82
+ }
83
+
84
+ async function dispatchGuarded(device: DeviceInfo, args: ToolCallArgs, handler: ToolHandler, addTiming: (response: ToolCallResult) => ToolCallResult) {
85
+ return withResolvedDevice(device, () => withDebuggerGuard('android', device.id, async () => {
86
+ const before = debuggerSessions.paused(device.id)
87
+ if (before) return wrapResponse({ success: false, failure_code: 'DEBUGGER_PAUSED', delivery_status: 'not_delivered', dispatch_started: false, debugger: before })
88
+ let response: ToolCallResult
89
+ try { response = await handler(args) } catch (error) {
90
+ if (!debuggerSessions.paused(device.id)) throw error
91
+ response = wrapResponse({ success: false })
92
+ }
93
+ const pause = debuggerSessions.paused(device.id)
94
+ if (pause) {
95
+ const item = response.content.find(item => item.type === 'text')
96
+ if (item?.text) {
97
+ const payload = JSON.parse(item.text)
98
+ Object.assign(payload, { success: false, failure_code: 'DEBUGGER_PAUSED', retryable: false,
99
+ dispatch_started: guardDispatched(), delivery_status: payload.success ? 'delivered' : guardDispatched() ? 'unknown' : 'not_delivered',
100
+ verification: { status: 'blocked', reason: 'DEBUGGER_PAUSED' }, debugger: pause })
101
+ item.text = JSON.stringify(payload)
102
+ }
103
+ }
104
+ return addTiming(response)
105
+ }))
106
+ }
107
+
108
+ function attachPausedObservation(name: string, args: ToolCallArgs, response: ToolCallResult) {
109
+ if (['capture_screenshot', 'get_ui_tree', 'get_screen_fingerprint', 'capture_debug_snapshot', 'get_logs', 'get_current_screen', 'read_log_stream'].includes(name) && args.platform !== 'ios') {
110
+ const item = response.content.find(item => item.type === 'text')
111
+ if (item?.text) {
112
+ try {
113
+ const payload = JSON.parse(item.text)
114
+ const device = payload.device ?? payload.evidence?.device
115
+ const id = device?.id ?? args.deviceId
116
+ const pause = id && device?.platform !== 'ios' ? debuggerSessions.paused(String(id)) : null
117
+ if (pause) item.text = JSON.stringify({ ...payload, debugger: pause })
118
+ } catch { /* Non-JSON observation */ }
119
+ }
120
+ }
121
+ }
122
+
123
+ export async function handleToolCall(name: string, args: ToolCallArgs = {}, signal?: AbortSignal) {
124
+ if (Object.hasOwn(debuggerContracts, name)) return handleDebugger(name, args, signal)
125
+ const handler = toolHandlers[name]
126
+ if (!handler) throw new Error(`Unknown tool: ${name}`)
127
+
128
+ const started = performance.now()
129
+ const measured: Record<string, number> = {}
130
+ const addTiming = (response: ToolCallResult) => {
131
+ if (!['start_app', 'restart_app', 'tap', 'tap_element', 'swipe', 'scroll_to_element', 'type_text', 'press_back', 'wait_for_ui', 'get_ui_tree', 'capture_screenshot', 'capture_debug_snapshot'].includes(name)) return response
132
+ const item = response.content.find(item => item.type === 'text')
133
+ if (item?.text) {
134
+ try {
135
+ const value = JSON.parse(item.text)
136
+ value.timing = { ...measured, ...value.timing, total_ms: performance.now() - started }
137
+ item.text = JSON.stringify(value)
138
+ } catch { /* Non-JSON text responses retain their original content. */ }
139
+ }
140
+ return response
141
+ }
142
+ return withTiming(measured, async () => {
143
+ try {
144
+ if (!systemTraceToolNames.has(name)) validateExpectedState(name, args)
145
+ const guarded = ['start_app', 'terminate_app', 'restart_app', 'reset_app_data', 'install_app', 'build_and_install', 'tap', 'tap_element', 'swipe', 'scroll_to_element', 'type_text', 'press_back', 'adjust_control', 'run_journey', 'wait_for_ui', 'wait_for_ui_change', 'wait_for_screen_change', 'expect_screen', 'expect_element_visible', 'expect_state'].includes(name)
146
+ if (guarded && args.platform !== 'ios' && debuggerSessions.hasActiveSessions()) {
147
+ const device = await resolveTargetDevice({ platform: 'android', deviceId: args.deviceId as string | undefined })
148
+ return dispatchGuarded(device, args, handler, addTiming)
149
+ }
150
+ const response = await handler(args)
151
+ attachPausedObservation(name, args, response)
152
+ return addTiming(response)
153
+ } catch (error) {
154
+ if (error instanceof TraceError) return { ...wrapResponse({ error: { tool: name, code: error.code, message: error.message } }), isError: true }
155
+ console.error(`Error executing tool ${name}:`, error)
156
+ return addTiming(wrapToolError(name, error))
157
+ }
158
+ })
159
+ }
@@ -0,0 +1,2 @@
1
+ // Compatibility export for existing server consumers.
2
+ export * from '../../contracts/condition.js'
@@ -19,19 +19,25 @@ const specs = {
19
19
  detach_debugger: { fields: session, command: 'detach', description: 'Dispose the owned debugger connection, release owned suspension and remove its exact forward. Idempotent; retries incomplete cleanup.' }
20
20
  }
21
21
  // Derive JSON schemas and runtime validation from the same small Zod field vocabulary.
22
- function jsonField(schema: z.ZodTypeAny): any {
23
- if (schema instanceof z.ZodOptional) return jsonField(schema.unwrap())
22
+ function jsonField(schema: z.ZodType): Record<string, unknown> {
23
+ if (schema instanceof z.ZodOptional) return jsonField(schema.unwrap() as z.ZodType)
24
24
  if (schema instanceof z.ZodLiteral) return { type: 'string', enum: [schema.value] }
25
25
  if (schema instanceof z.ZodEnum) return { type: 'string', enum: schema.options }
26
- if (schema instanceof z.ZodString) return { type: 'string', minLength: 1, maxLength: 512, ...Object.assign({}, ...schema._def.checks.filter(c => c.kind === 'regex').map(c => ({ pattern: (c as any).regex.source }))) }
26
+ if (schema instanceof z.ZodString) {
27
+ const json = z.toJSONSchema(schema)
28
+ return { type: 'string', minLength: 1, maxLength: 512, ...(json.pattern ? { pattern: json.pattern } : {}) }
29
+ }
27
30
  if (schema instanceof z.ZodNumber) return { type: 'integer', minimum: schema.minValue, maximum: schema.maxValue }
28
31
  throw new Error('Unsupported debugger field schema')
29
32
  }
30
33
  export const debuggerContracts = Object.fromEntries(Object.entries(specs).map(([name, spec]) => [name, {
31
- ...spec, schema: z.object({ ...spec.fields, timeoutMs: timeout.default(name === 'wait_for_debugger_event' ? 1000 : 10000) }).strict()
34
+ ...spec, schema: z.object({ ...spec.fields, responseMode: z.enum(['compact', 'debug']).default('debug'), timeoutMs: timeout.default(name === 'wait_for_debugger_event' ? 1000 : 10000) }).strict()
32
35
  }]))
33
- export const debuggerTools = Object.entries(debuggerContracts).map(([name, spec]) => ({ name, description: spec.description,
34
- inputSchema: { type: 'object', additionalProperties: false,
36
+ export const debuggerTools = Object.entries(debuggerContracts).map(([name, spec]) => ({
37
+ name, description: spec.description,
38
+ inputSchema: {
39
+ type: 'object' as const, additionalProperties: false,
35
40
  required: Object.entries(spec.fields).filter(([, field]) => !field.isOptional()).map(([key]) => key),
36
- properties: { ...Object.fromEntries(Object.entries(spec.fields).map(([key, field]) => [key, jsonField(field)])), timeoutMs: { type: 'integer', minimum: 100, maximum: 30000, default: name === 'wait_for_debugger_event' ? 1000 : 10000 } } }
41
+ properties: { responseMode: { type: 'string', enum: ['compact', 'debug'], default: 'debug' }, ...Object.fromEntries(Object.entries(spec.fields).map(([key, field]) => [key, jsonField(field)])), timeoutMs: { type: 'integer', minimum: 100, maximum: 30000, default: name === 'wait_for_debugger_event' ? 1000 : 10000 } }
42
+ }
37
43
  }))
@@ -1,20 +1,13 @@
1
+ import { verificationModeSchema, responseModeSchema, verificationModeJson } from '../../contracts/execution-controls.js'
2
+ import { waitForSchema as waitFor, targetFields as target, exactlyOneTarget, targetJson, oneTargetJson } from '../../contracts/target.js'
3
+ export { tapElementControlsSchema } from '../../contracts/target.js'
4
+ import { selectorSchema as selector, selectorJson } from '../../contracts/selector.js'
1
5
  import { z } from 'zod'
2
6
  import { expectedStateSchema, expectedStateJson } from './condition-schema.js'
3
7
 
4
- const selector = z.object({
5
- text: z.string().min(1).optional(), resource_id: z.string().min(1).optional(),
6
- accessibility_id: z.string().min(1).optional(), contains: z.boolean().optional()
7
- }).strict().refine(value => !!(value.text || value.resource_id || value.accessibility_id), 'Selector requires a target')
8
- const verificationMode = z.enum(['none', 'light', 'full'])
8
+ const verificationMode = verificationModeSchema
9
9
  const timeoutMs = z.number().int().min(100).max(60000).optional()
10
10
  const pollIntervalMs = z.number().int().min(50).max(1000).optional()
11
- const waitFor = z.object({
12
- condition: z.enum(['exists', 'visible', 'clickable']).optional(),
13
- timeoutMs: z.number().int().min(100).max(10000).optional(), pollIntervalMs,
14
- match: z.object({ index: z.number().int().nonnegative().optional() }).strict().optional()
15
- }).strict()
16
- const target = { selector: selector.optional(), elementId: z.string().min(1).optional() }
17
- const exactlyOneTarget = (value: { selector?: unknown, elementId?: string }) => Number(!!value.selector) + Number(!!value.elementId) === 1
18
11
  const assertion = z.union([
19
12
  z.object({ kind: z.literal('element_visible'), selector }).strict(),
20
13
  z.object({ kind: z.literal('element_absent'), selector }).strict(),
@@ -28,25 +21,17 @@ const step = z.union([
28
21
  z.object({ id, type: z.literal('wait'), selector, condition: z.enum(['exists', 'not_exists', 'visible', 'clickable']).optional(), timeoutMs, pollIntervalMs }).strict(),
29
22
  z.object({ id, type: z.literal('assert'), assertion, timeoutMs, pollIntervalMs }).strict()
30
23
  ])
31
- export const tapElementControlsSchema = z.object({
32
- elementId: z.string().min(1).optional(), selector: selector.optional(), waitFor: waitFor.optional()
33
- }).strict().refine(value => Number(!!value.elementId) + Number(!!value.selector) === 1 && (!value.elementId || !value.waitFor))
34
24
 
35
25
  export const journeySchema = z.object({
36
26
  platform: z.enum(['android', 'ios']), deviceId: z.string().min(1).optional(),
37
27
  systemTraceId: z.string().uuid().optional(),
38
- responseMode: z.enum(['compact', 'debug']).default('compact'), captureOnFailure: z.boolean().default(false),
28
+ responseMode: responseModeSchema.default('compact'), captureOnFailure: z.boolean().default(false),
39
29
  defaults: z.object({ verificationMode: verificationMode.optional(), actionTimeoutMs: z.number().int().finite().optional(), verificationTimeoutMs: z.number().int().finite().optional() }).strict().optional(),
40
30
  steps: z.array(step).min(1).max(50)
41
31
  }).strict().refine(value => new Set(value.steps.map(step => step.id)).size === value.steps.length, 'Step IDs must be unique')
42
32
 
43
- const selectorJson = { type: 'object', additionalProperties: false, properties: {
44
- text: { type: 'string', minLength: 1 }, resource_id: { type: 'string', minLength: 1 }, accessibility_id: { type: 'string', minLength: 1 }, contains: { type: 'boolean' }
45
- }, anyOf: ['text', 'resource_id', 'accessibility_id'].map(key => ({ required: [key] })) }
46
- const targetJson = { selector: selectorJson, elementId: { type: 'string', minLength: 1 } }
47
- const oneTargetJson = [{ required: ['selector'], not: { required: ['elementId'] } }, { required: ['elementId'], not: { required: ['selector'] } }]
48
33
  const intervalJson = { timeoutMs: { type: 'integer', minimum: 100, maximum: 60000 }, pollIntervalMs: { type: 'integer', minimum: 50, maximum: 1000 } }
49
- const modeJson = { type: 'string', enum: ['none', 'light', 'full'] }
34
+ const modeJson = verificationModeJson
50
35
  const objectJson = (properties: Record<string, unknown>, required: string[], extra = {}) => ({ type: 'object', additionalProperties: false, properties, required, ...extra })
51
36
  export const journeyStepsJson = {
52
37
  type: 'array', minItems: 1, maxItems: 50, items: { oneOf: [
@@ -6,12 +6,12 @@ export const traceControlSchema = z.object({ traceId: z.string().uuid() }).stric
6
6
  const properties = { platform: { type: 'string', enum: ['android', 'ios'] }, deviceId: { type: 'string', minLength: 1 } }
7
7
  export const systemTraceTools = [
8
8
  { name: 'get_system_trace_capabilities', description: 'Probe Android Perfetto prerequisites without recording. Device-wide system tracing is optional; iOS is unsupported.',
9
- inputSchema: { type: 'object', additionalProperties: false, required: ['platform'], properties } },
9
+ inputSchema: { type: 'object' as const, additionalProperties: false, required: ['platform'], properties } },
10
10
  { name: 'start_system_trace', description: 'Start bounded Android Perfetto recording, returning an owned trace_id. Run an existing journey with systemTraceId, then stop to retrieve a local artifact or cancel to discard. Device-wide traces may contain sensitive system annotations; no upload.',
11
- inputSchema: { type: 'object', additionalProperties: false, required: ['platform'], properties: { ...properties, durationMs: { type: 'integer', minimum: 1000, maximum: 120000, default: 30000 } } } },
11
+ inputSchema: { type: 'object' as const, additionalProperties: false, required: ['platform'], properties: { ...properties, durationMs: { type: 'integer', minimum: 1000, maximum: 120000, default: 30000 } } } },
12
12
  ...['get_system_trace_status', 'stop_system_trace', 'cancel_system_trace'].map(name => ({ name,
13
13
  description: name === 'get_system_trace_status' ? 'Reconcile an owned trace and retrieve its state; finalise duration-completed capture.' :
14
14
  name === 'stop_system_trace' ? 'Stop owned capture gracefully and retrieve a local Perfetto artifact. Retry after a transfer/cleanup failure.' : 'Stop owned capture and discard partial files. Already completed local artifacts are retained.',
15
- inputSchema: { type: 'object', additionalProperties: false, required: ['traceId'], properties: { traceId: { type: 'string', format: 'uuid' } } }
15
+ inputSchema: { type: 'object' as const, additionalProperties: false, required: ['traceId'], properties: { traceId: { type: 'string', format: 'uuid' } } }
16
16
  }))
17
17
  ]