mobile-debug-mcp 0.34.0 → 0.36.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 (500) 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/.cursor-plugin/marketplace.json +1 -1
  11. package/.github/copilot-instructions.md +14 -36
  12. package/.github/workflows/ci.yml +4 -4
  13. package/AGENTS.md +12 -3
  14. package/CONTRIBUTING.md +79 -0
  15. package/README.md +218 -129
  16. package/agent-plugin/.antigravity-plugin/README.md +48 -0
  17. package/agent-plugin/.antigravity-plugin/plugin.json +18 -0
  18. package/agent-plugin/.claude-plugin/plugin.json +2 -2
  19. package/agent-plugin/.codex-plugin/plugin.json +1 -1
  20. package/agent-plugin/.cursor-plugin/plugin.json +2 -2
  21. package/agent-plugin/.mcp.json +1 -1
  22. package/agent-plugin/README.md +50 -11
  23. package/agent-plugin/cursor/.cursor-plugin/plugin.json +23 -0
  24. package/agent-plugin/cursor/LICENSE +201 -0
  25. package/agent-plugin/cursor/README.md +9 -0
  26. package/agent-plugin/cursor/skills/mobile-debug-mcp/SKILL.md +253 -0
  27. package/agent-plugin/mcp.json +1 -1
  28. package/agent-plugin/mcp_config.json +13 -0
  29. package/agent-plugin/plugin.json +1 -1
  30. package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +62 -17
  31. package/dist/contracts/execution-controls.js +24 -0
  32. package/dist/contracts/selector.js +8 -0
  33. package/dist/contracts/target.js +15 -0
  34. package/dist/contracts/ui.js +1 -0
  35. package/dist/debugger/android.js +2 -1
  36. package/dist/debugger/diagnostics.js +54 -0
  37. package/dist/debugger/guard.js +4 -4
  38. package/dist/debugger/ios-worker.js +62 -0
  39. package/dist/debugger/ios.js +57 -0
  40. package/dist/debugger/protocol.js +33 -0
  41. package/dist/debugger/sessions.js +133 -74
  42. package/dist/debugger/worker.js +59 -46
  43. package/dist/interact/adjustment/adjustment-context.js +41 -0
  44. package/dist/interact/adjustment/adjustment-gestures.js +55 -0
  45. package/dist/interact/adjustment/adjustment-plan.js +18 -0
  46. package/dist/interact/adjustment/adjustment-resolution.js +127 -0
  47. package/dist/interact/adjustment/adjustment-result.js +19 -0
  48. package/dist/interact/adjustment/adjustment-semantic.js +64 -0
  49. package/dist/interact/adjustment/adjustment-verification.js +35 -0
  50. package/dist/interact/adjustment/adjustment.js +52 -0
  51. package/dist/interact/adjustment/control-geometry.js +87 -0
  52. package/dist/interact/adjustment/control-values.js +136 -0
  53. package/dist/interact/adjustment/slider-labels.js +84 -0
  54. package/dist/interact/assertions/screen-assertion.js +37 -0
  55. package/dist/interact/assertions/state-assertion-context.js +35 -0
  56. package/dist/interact/assertions/state-assertion.js +76 -0
  57. package/dist/interact/assertions/state-comparison.js +141 -0
  58. package/dist/interact/assertions/visibility-assertion.js +34 -0
  59. package/dist/interact/conditional-action.js +12 -7
  60. package/dist/interact/index.js +77 -3092
  61. package/dist/interact/input-service.js +1 -0
  62. package/dist/interact/interaction-trace.js +21 -0
  63. package/dist/interact/model.js +1 -0
  64. package/dist/interact/observation.js +1 -0
  65. package/dist/interact/targeting/candidate-ancestor.js +58 -0
  66. package/dist/interact/targeting/candidate-result.js +23 -0
  67. package/dist/interact/targeting/candidate-scoring.js +50 -0
  68. package/dist/interact/targeting/element-helpers.js +216 -0
  69. package/dist/interact/targeting/find-element.js +65 -0
  70. package/dist/interact/targeting/nearby-control.js +56 -0
  71. package/dist/interact/targeting/resolved-elements.js +21 -0
  72. package/dist/interact/targeting/tap-target.js +17 -0
  73. package/dist/interact/targeting/target-scope.js +104 -0
  74. package/dist/interact/waits/legacy-log-wait.js +38 -0
  75. package/dist/interact/waits/legacy-wait-observations.js +103 -0
  76. package/dist/interact/waits/legacy-wait-state.js +34 -0
  77. package/dist/interact/waits/legacy-wait.js +48 -0
  78. package/dist/interact/waits/screen-wait.js +49 -0
  79. package/dist/interact/waits/ui-change-helpers.js +157 -0
  80. package/dist/interact/waits/ui-change-state.js +18 -0
  81. package/dist/interact/waits/ui-change-wait.js +77 -0
  82. package/dist/interact/waits/ui-wait-condition.js +56 -0
  83. package/dist/interact/waits/ui-wait-state.js +38 -0
  84. package/dist/interact/waits/ui-wait.js +106 -0
  85. package/dist/manage/android.js +55 -51
  86. package/dist/manage/index.js +38 -75
  87. package/dist/manage/ios/build-types.js +1 -0
  88. package/dist/manage/ios/build.js +210 -0
  89. package/dist/manage/ios/project-discovery.js +29 -0
  90. package/dist/manage/ios/scheme.js +18 -0
  91. package/dist/manage/ios.js +44 -256
  92. package/dist/observe/android/context.js +1 -0
  93. package/dist/observe/android/current-screen.js +55 -0
  94. package/dist/observe/android/fingerprint.js +13 -0
  95. package/dist/observe/android/log-stream.js +132 -0
  96. package/dist/observe/android/logs.js +107 -0
  97. package/dist/observe/android/screenshot.js +70 -0
  98. package/dist/observe/android/ui-tree.js +107 -0
  99. package/dist/observe/android.js +16 -520
  100. package/dist/observe/evidence.js +114 -75
  101. package/dist/observe/index.js +16 -263
  102. package/dist/observe/ios/context.js +16 -0
  103. package/dist/observe/ios/fingerprint.js +12 -0
  104. package/dist/observe/ios/log-stream.js +105 -0
  105. package/dist/observe/ios/logs.js +168 -0
  106. package/dist/observe/ios/screenshot.js +22 -0
  107. package/dist/observe/ios/ui-elements.js +241 -0
  108. package/dist/observe/ios/ui-tree.js +137 -0
  109. package/dist/observe/ios-trace/artifact.js +63 -0
  110. package/dist/observe/ios-trace/capabilities.js +86 -0
  111. package/dist/observe/ios-trace/native.js +93 -0
  112. package/dist/observe/ios-trace/notification.js +35 -0
  113. package/dist/observe/ios-trace/process.js +98 -0
  114. package/dist/observe/ios-trace/sessions.js +244 -0
  115. package/dist/observe/ios.js +17 -715
  116. package/dist/observe/logs/model.js +1 -0
  117. package/dist/{network → observe/network}/index.js +2 -2
  118. package/dist/observe/readiness-service.js +5 -3
  119. package/dist/observe/readiness.js +33 -14
  120. package/dist/observe/screenshot-encoding.js +41 -0
  121. package/dist/observe/snapshot-collection.js +79 -0
  122. package/dist/observe/snapshot-logs.js +30 -0
  123. package/dist/observe/snapshot-metadata.js +43 -31
  124. package/dist/observe/snapshot-semantic.js +115 -0
  125. package/dist/observe/system-trace-error.js +7 -0
  126. package/dist/observe/system-trace-router.js +81 -0
  127. package/dist/observe/system-trace.js +26 -21
  128. package/dist/runtime/action-results.js +270 -0
  129. package/dist/runtime/clock.js +4 -0
  130. package/dist/server/common.js +1 -256
  131. package/dist/server/definitions/assert.js +162 -0
  132. package/dist/server/definitions/diagnostic.js +118 -0
  133. package/dist/server/definitions/input.js +274 -0
  134. package/dist/server/definitions/manage.js +194 -0
  135. package/dist/server/definitions/observe.js +149 -0
  136. package/dist/server/definitions/target.js +138 -0
  137. package/dist/server/definitions/wait.js +105 -0
  138. package/dist/server/handlers/action-execution.js +163 -0
  139. package/dist/server/handlers/correlation.js +23 -0
  140. package/dist/server/handlers/debugger-handlers.js +67 -0
  141. package/dist/server/handlers/debugger-response.js +9 -0
  142. package/dist/server/handlers/diagnostic.js +77 -0
  143. package/dist/server/handlers/handler-types.js +1 -0
  144. package/dist/server/handlers/input.js +154 -0
  145. package/dist/server/handlers/interact.js +113 -0
  146. package/dist/server/handlers/journey-assertions.js +35 -0
  147. package/dist/server/handlers/journey.js +147 -0
  148. package/dist/server/handlers/manage.js +119 -0
  149. package/dist/server/handlers/observe.js +95 -0
  150. package/dist/server/handlers/payload.js +4 -0
  151. package/dist/server/handlers/tool-handlers.js +170 -0
  152. package/dist/server/schemas/condition-schema.js +2 -0
  153. package/dist/server/{debugger-schema.js → schemas/debugger-schema.js} +20 -12
  154. package/dist/server/{journey-schema.js → schemas/journey-schema.js} +7 -22
  155. package/dist/server/schemas/system-trace-schema.js +22 -0
  156. package/dist/server/tool-definitions.js +58 -1135
  157. package/dist/server-core.js +2 -2
  158. package/dist/server.js +4 -4
  159. package/dist/system/android-status.js +101 -0
  160. package/dist/system/android.js +37 -27
  161. package/dist/system/index.js +13 -439
  162. package/dist/system/ios-status.js +132 -0
  163. package/dist/system/ios.js +11 -8
  164. package/dist/system/model.js +1 -0
  165. package/dist/system/probes.js +105 -0
  166. package/dist/system/status-summary.js +43 -0
  167. package/dist/system/unsupported-status.js +52 -0
  168. package/dist/utils/android/commands.js +89 -0
  169. package/dist/utils/android/gradle.js +139 -0
  170. package/dist/utils/android/logs.js +103 -0
  171. package/dist/utils/android/ui-tree.js +233 -0
  172. package/dist/utils/android/utils.js +6 -564
  173. package/dist/utils/cli/idb/idb-helper.js +4 -6
  174. package/dist/utils/cli/idb/install-idb.js +13 -9
  175. package/dist/utils/cli/ios/preflight-ios.js +42 -38
  176. package/dist/utils/ios/utils.js +48 -46
  177. package/dist/utils/java.js +65 -49
  178. package/dist/utils/resolve-device.js +4 -2
  179. package/dist/utils/ui/index.js +26 -26
  180. package/docs/CHANGELOG.md +67 -1
  181. package/docs/README.md +38 -0
  182. package/docs/agent-plugin-installation-verification.md +39 -0
  183. package/docs/benchmarks/README.md +18 -0
  184. package/docs/benchmarks/interaction-latency/README.md +14 -0
  185. package/docs/benchmarks/interaction-latency/final.md +4 -1
  186. package/docs/benchmarks/interaction-latency/ios-validation.md +2 -0
  187. package/docs/benchmarks/ui-tree-performance/README.md +24 -16
  188. package/docs/benchmarks/ui-tree-performance/comparison.md +11 -10
  189. package/docs/code-structure.md +120 -0
  190. package/docs/specs/001-state-verification/spec.md +16 -14
  191. package/docs/specs/002-richer-element-identity/spec.md +12 -10
  192. package/docs/specs/003-wait-and-synchronization-reliability/spec.md +12 -10
  193. package/docs/specs/004-action-verification-routing/spec.md +3 -3
  194. package/docs/specs/005-unified-action-execution-and-verification-model/spec.md +2 -0
  195. package/docs/specs/006-runtime-action-instrumentation-and-binding-layer/spec.md +2 -0
  196. package/docs/specs/007-actionability-resolution-and-executable-target-selection/spec.md +3 -1
  197. package/docs/specs/008-adjustable-control-support-and-semantic-value-manipulation/spec.md +3 -4
  198. package/docs/specs/009-semantic-control-modeling-for-custom-and-composite-controls/spec.md +2 -0
  199. package/docs/specs/010-verification-stabilization-and-temporal-convergence/spec.md +2 -2
  200. package/docs/specs/011-recovery-and-replanning-for-failed-or-ambiguous-interaction-flows/spec.md +2 -2
  201. package/docs/specs/011.1-recovery-contract-types-and-runtime-wiring-spec/spec.md +2 -0
  202. package/docs/specs/012-action-trace-and-xecution-observability/spec.md +2 -3
  203. package/docs/specs/013-wait-and-synchronization-reliability/spec.md +19 -19
  204. package/docs/specs/014-actionability-resolution/spec.md +20 -18
  205. package/docs/specs/015-adjustable-control-support/spec.md +17 -15
  206. package/docs/specs/016-environment-tools/spec.md +20 -20
  207. package/docs/specs/018-interaction-latency-and-batched-journeys/spec.md +2 -0
  208. package/docs/specs/019-ui-tree-observation-performance/spec.md +3 -1
  209. package/docs/specs/020-conditional-actions/intent.md +5 -3
  210. package/docs/specs/020-conditional-actions/plan.md +8 -9
  211. package/docs/specs/020-conditional-actions/spec.md +4 -2
  212. package/docs/specs/021-app-readiness-signals/intent.md +4 -2
  213. package/docs/specs/021-app-readiness-signals/plan.md +7 -6
  214. package/docs/specs/021-app-readiness-signals/spec.md +3 -1
  215. package/docs/specs/022-correlated-evidence/intent.md +4 -4
  216. package/docs/specs/022-correlated-evidence/plan.md +10 -11
  217. package/docs/specs/022-correlated-evidence/spec.md +3 -1
  218. package/docs/specs/023-android-system-traces/intent.md +3 -2
  219. package/docs/specs/023-android-system-traces/plan.md +5 -7
  220. package/docs/specs/023-android-system-traces/spec.md +3 -1
  221. package/docs/specs/024-android-heap-dump-capture/intent.md +3 -1
  222. package/docs/specs/025-android-runtime-debugger/intent.md +4 -3
  223. package/docs/specs/025-android-runtime-debugger/plan.md +3 -1
  224. package/docs/specs/025-android-runtime-debugger/spec.md +4 -3
  225. package/docs/specs/026-code-structure-and-quality-gates/health-plan.md +88 -0
  226. package/docs/specs/026-code-structure-and-quality-gates/intent.md +47 -0
  227. package/docs/specs/026-code-structure-and-quality-gates/plan.md +191 -0
  228. package/docs/specs/026-code-structure-and-quality-gates/spec.md +138 -0
  229. package/docs/specs/026-code-structure-and-quality-gates/verification-final.json +200 -0
  230. package/docs/specs/026-code-structure-and-quality-gates/verification-thin-facade.json +154 -0
  231. package/docs/specs/026-code-structure-and-quality-gates/verification.json +136 -0
  232. package/docs/specs/027-debugger-failure-diagnostics/intent.md +43 -0
  233. package/docs/specs/027-debugger-failure-diagnostics/plan.md +86 -0
  234. package/docs/specs/027-debugger-failure-diagnostics/spec.md +133 -0
  235. package/docs/specs/028-compact-debugger-responses/intent.md +41 -0
  236. package/docs/specs/028-compact-debugger-responses/plan.md +112 -0
  237. package/docs/specs/028-compact-debugger-responses/spec.md +108 -0
  238. package/docs/specs/029-kotlin-locals-validation/report.md +119 -0
  239. package/docs/specs/030-android-coroutine-validation/evidence.json +475 -0
  240. package/docs/specs/030-android-coroutine-validation/intent.md +47 -0
  241. package/docs/specs/030-android-coroutine-validation/plan.md +147 -0
  242. package/docs/specs/030-android-coroutine-validation/report.md +240 -0
  243. package/docs/specs/030-android-coroutine-validation/spec.md +107 -0
  244. package/docs/specs/031-ios-system-traces/intent.md +38 -0
  245. package/docs/specs/031-ios-system-traces/investigation.md +92 -0
  246. package/docs/specs/031-ios-system-traces/late-run-failure-evidence.json +32 -0
  247. package/docs/specs/031-ios-system-traces/plan.md +138 -0
  248. package/docs/specs/031-ios-system-traces/pr102-monitor-evidence.json +22 -0
  249. package/docs/specs/031-ios-system-traces/simulator-evidence.json +47 -0
  250. package/docs/specs/031-ios-system-traces/spec.md +135 -0
  251. package/docs/specs/032-ios-runtime-debugger/evidence.json +176 -0
  252. package/docs/specs/032-ios-runtime-debugger/intent.md +38 -0
  253. package/docs/specs/032-ios-runtime-debugger/plan.md +95 -0
  254. package/docs/specs/032-ios-runtime-debugger/report.md +205 -0
  255. package/docs/specs/032-ios-runtime-debugger/spec.md +153 -0
  256. package/docs/tools/TOOLS.md +31 -18
  257. package/docs/tools/{evidence.md → correlated-evidence.md} +9 -1
  258. package/docs/tools/debugger.md +120 -4
  259. package/docs/tools/interact.md +42 -20
  260. package/docs/tools/manage.md +25 -44
  261. package/docs/tools/observe.md +55 -23
  262. package/docs/tools/system-traces.md +89 -14
  263. package/docs/tools/system.md +12 -5
  264. package/package.json +12 -8
  265. package/scripts/agent-plugin.mjs +55 -5
  266. package/scripts/architecture-graph.mjs +69 -0
  267. package/scripts/check-architecture.mjs +47 -0
  268. package/scripts/code-quality-baseline.json +1 -0
  269. package/scripts/code-quality.mjs +161 -0
  270. package/src/contracts/execution-controls.ts +26 -0
  271. package/src/contracts/selector.ts +17 -0
  272. package/src/contracts/target.ts +19 -0
  273. package/src/contracts/ui.ts +94 -0
  274. package/src/debugger/android.ts +3 -2
  275. package/src/debugger/diagnostics.ts +43 -0
  276. package/src/debugger/guard.ts +10 -5
  277. package/src/debugger/ios-worker.ts +49 -0
  278. package/src/debugger/ios.ts +45 -0
  279. package/{worker → src/debugger/java}/DebuggerWorker.java +9 -3
  280. package/src/debugger/lldb/breakpoints.py +51 -0
  281. package/src/debugger/lldb/inspection.py +109 -0
  282. package/src/debugger/lldb/worker.py +203 -0
  283. package/src/debugger/protocol.ts +37 -0
  284. package/src/debugger/sessions.ts +326 -155
  285. package/src/debugger/worker.ts +57 -41
  286. package/src/interact/adjustment/adjustment-context.ts +83 -0
  287. package/src/interact/adjustment/adjustment-gestures.ts +48 -0
  288. package/src/interact/adjustment/adjustment-plan.ts +23 -0
  289. package/src/interact/adjustment/adjustment-resolution.ts +104 -0
  290. package/src/interact/adjustment/adjustment-result.ts +34 -0
  291. package/src/interact/adjustment/adjustment-semantic.ts +53 -0
  292. package/src/interact/adjustment/adjustment-verification.ts +60 -0
  293. package/src/interact/adjustment/adjustment.ts +49 -0
  294. package/src/interact/adjustment/control-geometry.ts +120 -0
  295. package/src/interact/adjustment/control-values.ts +141 -0
  296. package/src/interact/adjustment/slider-labels.ts +84 -0
  297. package/src/interact/assertions/screen-assertion.ts +39 -0
  298. package/src/interact/assertions/state-assertion-context.ts +48 -0
  299. package/src/interact/assertions/state-assertion.ts +64 -0
  300. package/src/interact/assertions/state-comparison.ts +168 -0
  301. package/src/interact/assertions/visibility-assertion.ts +41 -0
  302. package/src/interact/conditional-action.ts +18 -12
  303. package/src/interact/index.ts +89 -3494
  304. package/src/interact/input-service.ts +11 -0
  305. package/src/interact/interaction-trace.ts +36 -0
  306. package/src/interact/model.ts +109 -0
  307. package/src/interact/observation.ts +7 -0
  308. package/src/interact/targeting/candidate-ancestor.ts +49 -0
  309. package/src/interact/targeting/candidate-result.ts +24 -0
  310. package/src/interact/targeting/candidate-scoring.ts +42 -0
  311. package/src/interact/targeting/element-helpers.ts +243 -0
  312. package/src/interact/targeting/find-element.ts +47 -0
  313. package/src/interact/targeting/nearby-control.ts +46 -0
  314. package/src/interact/targeting/resolved-elements.ts +20 -0
  315. package/src/interact/targeting/tap-target.ts +22 -0
  316. package/src/interact/targeting/target-scope.ts +121 -0
  317. package/src/interact/waits/legacy-log-wait.ts +30 -0
  318. package/src/interact/waits/legacy-wait-observations.ts +55 -0
  319. package/src/interact/waits/legacy-wait-state.ts +48 -0
  320. package/src/interact/waits/legacy-wait.ts +34 -0
  321. package/src/interact/waits/screen-wait.ts +37 -0
  322. package/src/interact/waits/ui-change-helpers.ts +184 -0
  323. package/src/interact/waits/ui-change-state.ts +26 -0
  324. package/src/interact/waits/ui-change-wait.ts +59 -0
  325. package/src/interact/waits/ui-wait-condition.ts +52 -0
  326. package/src/interact/waits/ui-wait-state.ts +55 -0
  327. package/src/interact/waits/ui-wait.ts +84 -0
  328. package/src/manage/android.ts +56 -50
  329. package/src/manage/index.ts +63 -93
  330. package/src/manage/ios/build-types.ts +14 -0
  331. package/src/manage/ios/build.ts +230 -0
  332. package/src/manage/ios/project-discovery.ts +26 -0
  333. package/src/manage/ios/scheme.ts +16 -0
  334. package/src/manage/ios.ts +67 -298
  335. package/src/observe/android/context.ts +1 -0
  336. package/src/observe/android/current-screen.ts +63 -0
  337. package/src/observe/android/fingerprint.ts +13 -0
  338. package/src/observe/android/log-stream.ts +127 -0
  339. package/src/observe/android/logs.ts +110 -0
  340. package/src/observe/android/screenshot.ts +79 -0
  341. package/src/observe/android/ui-tree.ts +113 -0
  342. package/src/observe/android.ts +17 -531
  343. package/src/observe/evidence.ts +122 -70
  344. package/src/observe/index.ts +24 -286
  345. package/src/observe/ios/context.ts +22 -0
  346. package/src/observe/ios/fingerprint.ts +12 -0
  347. package/src/observe/ios/log-stream.ts +103 -0
  348. package/src/observe/ios/logs.ts +179 -0
  349. package/src/observe/ios/screenshot.ts +26 -0
  350. package/src/observe/ios/ui-elements.ts +266 -0
  351. package/src/observe/ios/ui-tree.ts +143 -0
  352. package/src/observe/ios-trace/artifact.ts +55 -0
  353. package/src/observe/ios-trace/capabilities.ts +74 -0
  354. package/src/observe/ios-trace/native.ts +72 -0
  355. package/src/observe/ios-trace/notification.ts +30 -0
  356. package/src/observe/ios-trace/process.ts +82 -0
  357. package/src/observe/ios-trace/sessions.ts +204 -0
  358. package/src/observe/ios.ts +18 -749
  359. package/src/observe/logs/model.ts +11 -0
  360. package/src/{network → observe/network}/index.ts +2 -2
  361. package/src/observe/readiness-service.ts +6 -4
  362. package/src/observe/readiness.ts +42 -21
  363. package/src/observe/screenshot-encoding.ts +32 -0
  364. package/src/observe/snapshot-collection.ts +69 -0
  365. package/src/observe/snapshot-logs.ts +26 -0
  366. package/src/observe/snapshot-metadata.ts +51 -31
  367. package/src/observe/snapshot-semantic.ts +153 -0
  368. package/src/observe/system-trace-error.ts +3 -0
  369. package/src/observe/system-trace-router.ts +61 -0
  370. package/src/observe/system-trace.ts +28 -19
  371. package/src/runtime/action-results.ts +376 -0
  372. package/src/runtime/clock.ts +9 -0
  373. package/src/server/common.ts +9 -359
  374. package/src/server/definitions/assert.ts +164 -0
  375. package/src/server/definitions/diagnostic.ts +120 -0
  376. package/src/server/definitions/input.ts +276 -0
  377. package/src/server/definitions/manage.ts +196 -0
  378. package/src/server/definitions/observe.ts +151 -0
  379. package/src/server/definitions/target.ts +140 -0
  380. package/src/server/definitions/wait.ts +107 -0
  381. package/src/server/handlers/action-execution.ts +164 -0
  382. package/src/server/handlers/correlation.ts +20 -0
  383. package/src/server/handlers/debugger-handlers.ts +58 -0
  384. package/src/server/handlers/debugger-response.ts +7 -0
  385. package/src/server/handlers/diagnostic.ts +86 -0
  386. package/src/server/handlers/handler-types.ts +7 -0
  387. package/src/server/handlers/input.ts +173 -0
  388. package/src/server/handlers/interact.ts +135 -0
  389. package/src/server/handlers/journey-assertions.ts +39 -0
  390. package/src/server/handlers/journey.ts +132 -0
  391. package/src/server/handlers/manage.ts +141 -0
  392. package/src/server/handlers/observe.ts +116 -0
  393. package/src/server/handlers/payload.ts +36 -0
  394. package/src/server/handlers/tool-handlers.ts +170 -0
  395. package/src/server/schemas/condition-schema.ts +2 -0
  396. package/src/server/schemas/debugger-schema.ts +46 -0
  397. package/src/server/{journey-schema.ts → schemas/journey-schema.ts} +7 -22
  398. package/src/server/schemas/system-trace-schema.ts +23 -0
  399. package/src/server/tool-definitions.ts +67 -1143
  400. package/src/server-core.ts +2 -2
  401. package/src/server.ts +4 -4
  402. package/src/system/android-status.ts +124 -0
  403. package/src/system/android.ts +33 -21
  404. package/src/system/index.ts +21 -525
  405. package/src/system/ios-status.ts +147 -0
  406. package/src/system/ios.ts +14 -10
  407. package/src/system/model.ts +41 -0
  408. package/src/system/probes.ts +114 -0
  409. package/src/system/status-summary.ts +47 -0
  410. package/src/system/unsupported-status.ts +55 -0
  411. package/src/types.ts +16 -106
  412. package/src/utils/android/commands.ts +81 -0
  413. package/src/utils/android/gradle.ts +132 -0
  414. package/src/utils/android/logs.ts +103 -0
  415. package/src/utils/android/ui-tree.ts +239 -0
  416. package/src/utils/android/utils.ts +16 -556
  417. package/src/utils/cli/idb/check-idb.ts +4 -4
  418. package/src/utils/cli/idb/idb-helper.ts +6 -8
  419. package/src/utils/cli/idb/install-idb.ts +15 -9
  420. package/src/utils/cli/ios/preflight-ios.ts +39 -31
  421. package/src/utils/cli/ios/run-ios-ui-tree-tap.ts +4 -4
  422. package/src/utils/ios/utils.ts +60 -61
  423. package/src/utils/java.ts +62 -43
  424. package/src/utils/resolve-device.ts +9 -6
  425. package/src/utils/ui/index.ts +28 -28
  426. package/test/device/automated/debugger/coroutine-lifecycle.ts +27 -0
  427. package/test/device/automated/debugger/coroutine-support.ts +140 -0
  428. package/test/device/automated/debugger/coroutine.android.test.ts +169 -0
  429. package/test/device/automated/debugger/kotlin.android.test.ts +127 -0
  430. package/test/device/automated/debugger/runtime.android.test.ts +19 -3
  431. package/test/device/automated/debugger/runtime.ios.test.ts +221 -0
  432. package/test/device/automated/interact/interaction.android.test.ts +56 -0
  433. package/test/device/automated/observe/ios-system-trace.smoke.ts +119 -0
  434. package/test/device/automated/observe/system-trace.smoke.ts +1 -1
  435. package/test/device/manual/interact/modul8_selector_tap.manual.ts +1 -1
  436. package/test/fixtures/contracts/tool-definitions.json +3696 -0
  437. package/test/fixtures/coroutine-app/MainActivity.kt +100 -0
  438. package/test/fixtures/coroutine-app/README.md +48 -0
  439. package/test/fixtures/coroutine-app/build.ts +35 -0
  440. package/test/fixtures/debugger-app/MainActivity.java +21 -0
  441. package/test/fixtures/debugger-app/MainActivity.kt +32 -0
  442. package/test/fixtures/debugger-app/README.md +52 -0
  443. package/test/fixtures/debugger-app/build.ts +25 -15
  444. package/test/fixtures/ios-debugger-app/Main.swift +95 -0
  445. package/test/fixtures/ios-debugger-app/README.md +27 -0
  446. package/test/fixtures/ios-debugger-app/build.ts +21 -0
  447. package/test/fixtures/latency-journey-app/README.md +2 -0
  448. package/test/fixtures/readiness-app/README.md +2 -0
  449. package/test/helpers/ios-trace-shutdown.ts +29 -0
  450. package/test/unit/debugger/coroutine-lifecycle.test.ts +43 -0
  451. package/test/unit/debugger/diagnostic-sessions.test.ts +67 -0
  452. package/test/unit/debugger/diagnostics.test.ts +80 -0
  453. package/test/unit/debugger/guards.test.ts +4 -0
  454. package/test/unit/debugger/handle-guards.test.ts +88 -0
  455. package/test/unit/debugger/ios-preparation.test.ts +57 -0
  456. package/test/unit/debugger/ios.test.ts +104 -0
  457. package/test/unit/debugger/lldb-worker.py +166 -0
  458. package/test/unit/debugger/lldb-worker.test.ts +10 -0
  459. package/test/unit/debugger/preparation.test.ts +32 -0
  460. package/test/unit/debugger/protocol.test.ts +15 -0
  461. package/test/unit/debugger/sessions.test.ts +1 -1
  462. package/test/unit/interact/adjustment_service.test.ts +42 -0
  463. package/test/unit/interact/adjustment_verification.test.ts +35 -0
  464. package/test/unit/interact/clock_dependencies.test.ts +63 -0
  465. package/test/unit/interact/element_helpers.test.ts +19 -0
  466. package/test/unit/interact/legacy_wait_service.test.ts +17 -0
  467. package/test/unit/interact/state_comparison.test.ts +20 -0
  468. package/test/unit/interact/target_scope.test.ts +35 -0
  469. package/test/unit/network/get_network_activity.test.ts +1 -1
  470. package/test/unit/observe/ios-trace-native.test.ts +90 -0
  471. package/test/unit/observe/ios-trace-sessions.test.ts +131 -0
  472. package/test/unit/observe/readiness.test.ts +1 -1
  473. package/test/unit/observe/screenshot-encoding.test.ts +30 -0
  474. package/test/unit/observe/snapshot-logs.test.ts +14 -0
  475. package/test/unit/observe/system-trace-router.test.ts +49 -0
  476. package/test/unit/quality/agent-plugin-export.test.ts +38 -0
  477. package/test/unit/quality/architecture.test.ts +60 -0
  478. package/test/unit/quality/code-quality.test.ts +58 -0
  479. package/test/unit/quality/interaction_boundaries.test.ts +24 -0
  480. package/test/unit/server/conditional_actions.test.ts +1 -1
  481. package/test/unit/server/conditional_schema.test.ts +2 -2
  482. package/test/unit/server/debugger-response.test.ts +63 -0
  483. package/test/unit/server/system-trace.test.ts +12 -3
  484. package/test/unit/server/tool_definitions_parity.test.ts +8 -0
  485. package/dist/server/debugger-handlers.js +0 -38
  486. package/dist/server/system-trace-schema.js +0 -17
  487. package/dist/server/tool-handlers.js +0 -1000
  488. package/docs/ROADMAP.md +0 -646
  489. package/docs/benchmarks/ui-tree-performance/bridge-probe.json +0 -29
  490. package/docs/benchmarks/ui-tree-performance/initial-baseline.json +0 -59560
  491. package/docs/specs/baseline-spec-v0.md +0 -312
  492. package/docs/specs/mcp-tooling-spec-v1.md +0 -446
  493. package/src/server/debugger-handlers.ts +0 -29
  494. package/src/server/debugger-schema.ts +0 -37
  495. package/src/server/system-trace-schema.ts +0 -17
  496. package/src/server/tool-handlers.ts +0 -1008
  497. /package/dist/{server/condition-schema.js → contracts/condition.js} +0 -0
  498. /package/dist/server/{readiness-schema.js → schemas/readiness-schema.js} +0 -0
  499. /package/src/{server/condition-schema.ts → contracts/condition.ts} +0 -0
  500. /package/src/server/{readiness-schema.ts → schemas/readiness-schema.ts} +0 -0
@@ -0,0 +1,140 @@
1
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js'
2
+
3
+ export const targetTools: Tool[] = [
4
+ {
5
+ name: 'adjust_control',
6
+ description: `Purpose:
7
+ Adjust a numeric control value with verification.
8
+
9
+ This is the initial adjustable-control surface for slider-like controls and other controls that expose a numeric value or value_range.
10
+
11
+ Inputs:
12
+ - exactly one of selector or element_id
13
+ - property (defaults to "value")
14
+ - targetValue
15
+ - tolerance (optional; discrete default is 0, continuous default is max(step / 2, 1% of range) when omitted)
16
+ - maxAttempts (optional; defaults to 5)
17
+ - platform/deviceId (optional)
18
+
19
+ Output Structure:
20
+ - action_id, timestamp (ISO 8601), action_type
21
+ - lifecycle_state: post-dispatch lifecycle state (pending_verification or failed)
22
+ - source_module: runtime source of the action envelope
23
+ - target_state / actual_state / within_tolerance / converged / attempts / adjustment_mode
24
+ - target.selector = original selector or element handle
25
+ - success = true when the control converges within tolerance
26
+
27
+ Verification Guidance:
28
+ - Prefer direct platform adjustment when the runtime exposes it; then verified increment/decrement actions; use coordinate fallback only as degraded mode
29
+ - Use expect_state for the control value readback
30
+ - Do not use derived semantic metadata as proof of success
31
+
32
+ Failure Handling:
33
+ - ELEMENT_NOT_FOUND → re-resolve the control
34
+ - ELEMENT_NOT_INTERACTABLE → the control cannot be adjusted through the current runtime
35
+ - CONTROL_CONVERGENCE_FAILED → the control did not converge within bounded retries or the requested value is out of range
36
+ - UNKNOWN → capture a snapshot and stop`,
37
+ inputSchema: {
38
+ type: 'object',
39
+ properties: {
40
+ selector: {
41
+ type: 'object',
42
+ properties: {
43
+ text: { type: 'string' },
44
+ resource_id: { type: 'string' },
45
+ accessibility_id: { type: 'string' },
46
+ contains: { type: 'boolean', default: false }
47
+ }
48
+ },
49
+ element_id: { type: 'string', description: 'Optional previously resolved element identifier.' },
50
+ property: { type: 'string', description: 'Readable numeric state property to adjust.', default: 'value' },
51
+ targetValue: { type: 'number', description: 'Target numeric value.' },
52
+ tolerance: { type: 'number', description: 'Accepted numeric tolerance around the target value. Defaults are derived from the readable control domain.' },
53
+ maxAttempts: { type: 'number', description: 'Maximum adjustment attempts.', default: 5 },
54
+ platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override' },
55
+ deviceId: { type: 'string', description: 'Optional device serial/udid' }
56
+ },
57
+ required: ['targetValue'],
58
+ oneOf: [
59
+ { required: ['selector'] },
60
+ { required: ['element_id'] }
61
+ ]
62
+ }
63
+ },
64
+ {
65
+ name: 'find_element',
66
+ description: `Find a UI element by semantic query (text, content-desc, resource-id, class).
67
+
68
+ Returns the best match plus resolution metadata when available, including confidence, selection reason, and fallback alternates.`,
69
+ inputSchema: {
70
+ type: 'object',
71
+ properties: {
72
+ query: { type: 'string', description: 'Search query (text or label)' },
73
+ exact: { type: 'boolean', description: 'Require exact match (true/false)', default: false },
74
+ timeoutMs: { type: 'number', description: 'Timeout in ms to keep searching', default: 3000 },
75
+ platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override' },
76
+ deviceId: { type: 'string', description: 'Optional device serial/udid' }
77
+ },
78
+ required: ['query']
79
+ }
80
+ },
81
+ {
82
+ name: 'tap_element',
83
+ description: `Purpose:
84
+ Tap a previously resolved UI element using its elementId.
85
+
86
+ Inputs:
87
+ - elementId: a resolved UI element identifier returned by wait_for_ui
88
+
89
+ Output Structure:
90
+ - action_id: unique timestamp-based action identifier
91
+ - timestamp: ISO 8601 timestamp for the action attempt
92
+ - action_type: "tap_element"
93
+ - lifecycle_state: post-dispatch lifecycle state (pending_verification or failed)
94
+ - source_module: runtime source of the action envelope
95
+ - target.selector: original target handle ({ elementId })
96
+ - target.resolved: minimal resolved element info used for the tap
97
+ - success: true when the tap was dispatched
98
+ - failure_code: present when success=false
99
+ - retryable: present when failure_code exists
100
+ - ui_fingerprint_before/ui_fingerprint_after: optional fingerprints captured around the action
101
+
102
+ Recommended Usage:
103
+ 1. Resolve the target with wait_for_ui or another deterministic resolver
104
+ 2. Call tap_element
105
+ 3. If needed, wait for transition using wait_for_*
106
+ 4. Verify outcome using expect_*
107
+ - use expect_screen when navigation is expected
108
+ - use expect_element_visible when the UI change is local
109
+ 5. If verification fails, inspect failure_code and follow the retry strategy below
110
+
111
+ Verification Guidance:
112
+ - Follow RESOLVE → ACT → WAIT (if needed) → EXPECT
113
+ - Prefer expect_screen for navigation or modal transitions
114
+ - Prefer expect_element_visible when the tap should reveal or update a specific element
115
+ - wait_for_* may be used for resolution and synchronization, but not as the final verification step when an applicable expect_* tool exists
116
+ - Do not treat tap_element.success as outcome success; it only means the tap was executed
117
+
118
+ Failure Handling:
119
+ - STALE_REFERENCE → re-resolve the element, then retry
120
+ - ELEMENT_NOT_INTERACTABLE → wait or refine the target, then retry
121
+ - UNKNOWN → capture a snapshot and stop
122
+
123
+ This tool reports execution success only. Verification must be done with a separate expect_* tool.`,
124
+ inputSchema: {
125
+ type: 'object',
126
+ properties: {
127
+ elementId: {
128
+ type: 'string',
129
+ description: 'A unique element identifier returned by wait_for_ui'
130
+ },
131
+ selector: { type: 'object', properties: { text: { type: 'string' }, resource_id: { type: 'string' }, accessibility_id: { type: 'string' }, contains: { type: 'boolean' } } },
132
+ waitFor: { type: 'object', properties: { condition: { type: 'string', enum: ['exists', 'visible', 'clickable'] }, timeoutMs: { type: 'number', minimum: 100, maximum: 10000 }, pollIntervalMs: { type: 'number', minimum: 50, maximum: 1000 }, match: { type: 'object', properties: { index: { type: 'number' } } } } },
133
+ platform: { type: 'string', enum: ['android', 'ios'] },
134
+ deviceId: { type: 'string' },
135
+ verificationMode: { type: 'string', enum: ['none', 'light', 'full'], default: 'full' },
136
+ responseMode: { type: 'string', enum: ['compact', 'debug'], default: 'debug' }
137
+ }
138
+ }
139
+ }
140
+ ]
@@ -0,0 +1,107 @@
1
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js'
2
+
3
+ export const waitTools: Tool[] = [
4
+ {
5
+ name: 'wait_for_screen_change',
6
+ description: `Purpose:
7
+ Detect that a screen transition has occurred by waiting for the current fingerprint to differ from a previous fingerprint.
8
+
9
+ Capabilities:
10
+ - Synchronization for uncertain navigation timing
11
+ - Detection that something changed on screen
12
+
13
+ Constraints:
14
+ - Does not verify correctness of the resulting state
15
+ - Must not be used alone to confirm action success when an applicable expect_* tool exists
16
+ - For backend/API activity without a visible UI change, pass the runtime action_type into classify_action_outcome and collect network evidence only if the result remains ambiguous
17
+
18
+ Recommended Usage:
19
+ 1. Capture or define the expected outcome
20
+ 2. Call an action tool
21
+ 3. Use wait_for_screen_change when transition timing is uncertain
22
+ 4. Follow with expect_screen when the expected destination is known`,
23
+ inputSchema: {
24
+ type: 'object',
25
+ properties: {
26
+ platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override (android|ios)' },
27
+ previousFingerprint: { type: 'string', description: 'The fingerprint to compare against (required)' },
28
+ timeoutMs: { type: 'number', description: 'Timeout in ms to wait for change (default 5000)', default: 5000 },
29
+ pollIntervalMs: { type: 'number', description: 'Polling interval in ms (default 300)', default: 300 },
30
+ deviceId: { type: 'string', description: 'Optional device id/udid to target' }
31
+ },
32
+ required: ['previousFingerprint']
33
+ }
34
+ },
35
+ {
36
+ name: 'wait_for_ui_change',
37
+ description: `Purpose:
38
+ Wait for a non-navigation UI mutation or in-place update to become stable.
39
+
40
+ Inputs:
41
+ - expected_change (optional): hierarchy_diff, text_change, or state_change
42
+ - timeout_ms (optional)
43
+ - stability_window_ms (optional)
44
+ - scope (optional): screen or subtree
45
+ - target (optional): element_id when scope=subtree
46
+
47
+ Guidance:
48
+ - Prefer wait_for_screen_change for navigation transitions.
49
+ - Prefer wait_for_ui_change for in-place mutations and non-navigation updates.
50
+ - Use the returned snapshot_revision as the observed synchronization point when available.
51
+ - Scoped waits return scope-aware stability metadata and a lightweight change summary.
52
+
53
+ Failure Handling:
54
+ - TIMEOUT means the UI did not change in a stable way within the allotted time.`,
55
+ inputSchema: {
56
+ type: 'object',
57
+ properties: {
58
+ platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override (android|ios)' },
59
+ deviceId: { type: 'string', description: 'Optional device id/udid to target' },
60
+ expected_change: { type: 'string', enum: ['hierarchy_diff', 'text_change', 'state_change'], description: 'Optional type of UI change to wait for' },
61
+ scope: { type: 'string', enum: ['screen', 'subtree'], default: 'screen', description: 'Synchronization scope for the wait' },
62
+ target: { type: 'string', description: 'Target element_id when scope is subtree' },
63
+ timeout_ms: { type: 'number', description: 'Timeout in ms to wait for change (default 60000)', default: 60000 },
64
+ stability_window_ms: { type: 'number', description: 'How long the change must remain stable before success (default 300)', default: 300 }
65
+ }
66
+ }
67
+ },
68
+ {
69
+ name: 'wait_for_ui',
70
+ description: `Purpose:
71
+ Resolve elements and/or detect that a UI transition or availability condition has occurred.
72
+
73
+ Capabilities:
74
+ - Deterministic element resolution
75
+ - Synchronization when element timing or availability is uncertain
76
+
77
+ Constraints:
78
+ - Does not verify correctness of the resulting state
79
+ - Must not be used alone to confirm action success when an applicable expect_* tool exists
80
+
81
+ Recommended Usage:
82
+ 1. Use wait_for_ui to resolve an element before acting or to wait for UI readiness
83
+ 2. Call the action tool
84
+ 3. If the expected outcome is known, follow with expect_* as final verification`,
85
+ inputSchema: {
86
+ type: 'object',
87
+ properties: {
88
+ selector: {
89
+ type: 'object',
90
+ properties: {
91
+ text: { type: 'string' },
92
+ resource_id: { type: 'string' },
93
+ accessibility_id: { type: 'string' },
94
+ contains: { type: 'boolean', description: 'When true, perform substring matching', default: false }
95
+ }
96
+ },
97
+ condition: { type: 'string', enum: ['exists', 'not_exists', 'visible', 'clickable'], default: 'exists' },
98
+ timeout_ms: { type: 'number', default: 60000 },
99
+ poll_interval_ms: { type: 'number', default: 300 },
100
+ match: { type: 'object', properties: { index: { type: 'number' } } },
101
+ retry: { type: 'object', properties: { max_attempts: { type: 'number', default: 1 }, backoff_ms: { type: 'number', default: 0 } } },
102
+ platform: { type: 'string', enum: ['android', 'ios'], description: 'Optional platform override' },
103
+ deviceId: { type: 'string', description: 'Optional device serial/udid' }
104
+ }
105
+ }
106
+ }
107
+ ]
@@ -0,0 +1,164 @@
1
+ import type { ToolPayload } from './payload.js'
2
+ import { payloadOf } from './payload.js'
3
+ import { correlatedHandler } from './correlation.js'
4
+ import { interactionControls } from '../../contracts/execution-controls.js'
5
+ import { currentEvidenceActionId, markEvidenceVerification } from '../../observe/evidence.js'
6
+ import { measure, withTiming } from '../../utils/timing.js'
7
+ import { randomUUID } from 'node:crypto'
8
+ import { runWithBudget } from '../../utils/operation-budget.js'
9
+ import { validateExpectedState } from '../schemas/condition-schema.js'
10
+ import { conditionFailureCode, createConditionalAction, withConditionalAction } from '../../interact/conditional-action.js'
11
+ import { resolveTargetDevice, withResolvedDevice } from '../../utils/resolve-device.js'
12
+
13
+ import { ToolsObserve } from '../../observe/index.js'
14
+ import { recordObservationEvent, withObservationContext } from '../../observe/performance-metrics.js'
15
+ import {
16
+ buildActionExecutionResult,
17
+ ToolCallArgs,
18
+ ToolHandler,
19
+ wrapResponse,
20
+ wrapToolError
21
+ } from '../common.js'
22
+ import type { PlatformArg, VerificationMode } from './handler-types.js'
23
+
24
+ export function compactAction(result: ToolPayload, verificationMode: VerificationMode, started = true, stateDelta?: unknown, totalMs = 0) {
25
+ return {
26
+ action_id: result.action_id,
27
+ action_type: result.action_type,
28
+ success: !!result.success,
29
+ lifecycle_state: result.success ? (verificationMode === 'full' ? result.lifecycle_state : 'pending_verification') : 'failed',
30
+ ...(result.failure_code ? { failure_code: result.failure_code } : {}),
31
+ ...(typeof result.retryable === 'boolean' ? { retryable: result.retryable } : {}),
32
+ delivery_status: result.success ? 'delivered' : (started ? 'unknown' : 'not_delivered'),
33
+ dispatch_started: started,
34
+ verification: { mode: verificationMode, status: result.success ? (verificationMode === 'none' ? 'not_requested' : verificationMode === 'light' ? 'observed' : (result.lifecycle_state === 'verified' ? 'verified' : 'observed')) : 'failed' },
35
+ ...(stateDelta ? { state_delta: stateDelta } : {}),
36
+ timing: { total_ms: totalMs }
37
+ }
38
+ }
39
+
40
+ function createAction(type: string, args: ToolCallArgs, timing: Record<string, number>) {
41
+ const started = performance.now()
42
+ const controls = interactionControls(args)
43
+ const expected = validateExpectedState(type, args)
44
+ const condition = expected ? createConditionalAction(args.platform as PlatformArg, args.deviceId as string, expected) : undefined
45
+ const budgetMs = controls.actionTimeoutMs ?? ({ start_app: 10000, restart_app: 15000, scroll_to_element: 10000 }[type] ?? 5000)
46
+ const unbounded = args._journey !== true && controls.verificationMode === 'full' && controls.responseMode === 'debug' && controls.actionTimeoutMs === undefined
47
+ return {
48
+ type, args, timing, started, controls, condition, budgetMs, unbounded,
49
+ result: {} as ToolPayload, dispatchStarted: false, blocked: false,
50
+ delta: undefined as unknown, observationAvailable: false
51
+ }
52
+ }
53
+ type Action = ReturnType<typeof createAction>
54
+ async function dispatchAction(s: Action, execute: ToolHandler) {
55
+ const { type, args, controls, condition, budgetMs } = s
56
+ const dispatchWithBudget = (timeoutMs: number) => runWithBudget(timeoutMs, async (budget) => {
57
+ try {
58
+ if (!condition) return payloadOf(await execute(args))
59
+ return await withConditionalAction(condition, async () => {
60
+ const device = await resolveTargetDevice({ platform: condition.platform, deviceId: condition.deviceId })
61
+ return withResolvedDevice(device, async () => payloadOf(await execute(args)))
62
+ })
63
+ }
64
+ finally { s.dispatchStarted = budget.dispatchStarted }
65
+ })
66
+ try {
67
+ // Omitted controls retain the existing adapter budgets.
68
+ s.result = s.unbounded
69
+ ? await dispatchWithBudget(Infinity) : await dispatchWithBudget(budgetMs)
70
+ if (s.result.success) s.dispatchStarted = true
71
+ } catch (error) {
72
+ if (!condition && controls.verificationMode === 'full' && controls.responseMode === 'debug' && controls.actionTimeoutMs === undefined && !/TIMEOUT|timed out/i.test(String(error))) return wrapToolError(type, error)
73
+ s.result = { success: false, failure_code: /TIMEOUT|timed out/i.test(String(error)) ? 'ACTION_TIMEOUT' : 'UNKNOWN', error: String(error) }
74
+ }
75
+ }
76
+ function rejectCondition(s: Action) {
77
+ const { condition, type } = s
78
+ s.blocked = !!condition && ['mismatch', 'unavailable'].includes(condition.result.status)
79
+ if (s.blocked && condition) {
80
+ s.dispatchStarted = false
81
+ s.result = buildActionExecutionResult({
82
+ actionType: type, sourceModule: 'server', device: condition.result.observed?.device,
83
+ selector: s.result?.target?.selector ?? null, resolved: s.result?.target?.resolved,
84
+ success: false, uiFingerprintBefore: s.result?.ui_fingerprint_before ?? null, uiFingerprintAfter: null,
85
+ failure: { failureCode: conditionFailureCode(condition), retryable: false },
86
+ traceSteps: [{ stage: 'verify', timestamp: Date.now(), result: 'failure', attempt_index: 0, metadata: { phase: 'precondition', dispatch_started: false } }]
87
+ })
88
+ }
89
+ }
90
+ function normalizeAction(s: Action) {
91
+ const { type, args, timing, controls } = s
92
+ rejectCondition(s)
93
+ if (s.result.failure_code === 'TIMEOUT' || /ACTION_TIMEOUT|timeout/i.test(String(s.result.error ?? s.result.reason ?? s.result.details?.error ?? ''))) s.result.failure_code = 'ACTION_TIMEOUT'
94
+ if (s.result.failure_code === 'ACTION_TIMEOUT') s.result.retryable = !s.dispatchStarted
95
+ if (!s.result.success && typeof s.result.retryable !== 'boolean') s.result.retryable = !s.dispatchStarted
96
+ s.result.action_id ??= currentEvidenceActionId() ?? randomUUID()
97
+ s.result.action_type ??= type
98
+ recordObservationEvent({
99
+ step_id: args._stepId as string | undefined,
100
+ action_type: type,
101
+ dispatch_ms: timing.dispatch_ms,
102
+ verification_mode: controls.verificationMode,
103
+ dispatch_started: s.dispatchStarted
104
+ })
105
+ }
106
+ async function verifyAction(s: Action) {
107
+ const { args, controls } = s
108
+ if (s.result.success && controls.verificationMode === 'light') {
109
+ const verificationStarted = performance.now()
110
+ const tree = await withObservationContext({ stepId: args._stepId as string | undefined, purpose: 'verification' }, () =>
111
+ measure('post_observation_ms', () => runWithBudget(controls.verificationTimeoutMs, () =>
112
+ ToolsObserve.getUITreeHandler({ platform: (args.platform as PlatformArg | undefined) ?? (s.result.device?.platform as PlatformArg | undefined), deviceId: (args.deviceId as string | undefined) ?? s.result.device?.id })))).catch(() => null)
113
+ markEvidenceVerification()
114
+ s.observationAvailable = !!tree && !tree.error
115
+ recordObservationEvent({
116
+ verification_budget_ms: controls.verificationTimeoutMs,
117
+ verification_elapsed_ms: performance.now() - verificationStarted,
118
+ verification_outcome: s.observationAvailable ? 'observed' : 'unavailable',
119
+ verification_budget_expired: !s.observationAvailable && performance.now() - verificationStarted >= controls.verificationTimeoutMs
120
+ })
121
+ if (s.observationAvailable && tree?.device) s.delta = ToolsObserve.getLatestStateDelta(tree.device.platform as PlatformArg, tree.device.id) ?? undefined
122
+ }
123
+ if (!s.blocked && controls.verificationMode === 'full' && s.dispatchStarted) markEvidenceVerification()
124
+ }
125
+ function actionResponse(s: Action) {
126
+ const { timing, started, controls, condition, budgetMs } = s
127
+ const compact = compactAction(s.result, controls.verificationMode, s.dispatchStarted, s.delta, performance.now() - started)
128
+ if (s.blocked) compact.verification.status = 'not_requested'
129
+ if (s.result.success && controls.verificationMode === 'light' && !s.observationAvailable) compact.verification.status = 'unavailable'
130
+ const evidence = {
131
+ ...compact,
132
+ ...(condition ? {
133
+ condition: controls.responseMode === 'compact' && condition.result.status === 'matched'
134
+ ? {
135
+ ...condition.result, observed: condition.result.observed ? {
136
+ snapshot_session_id: condition.result.observed.snapshot_session_id,
137
+ snapshot_revision: condition.result.observed.snapshot_revision,
138
+ captured_at_ms: condition.result.observed.captured_at_ms
139
+ } : null
140
+ } : condition.result
141
+ } : {}),
142
+ ...(controls.verificationMode === 'light' ? { state_delta_available: !!s.delta } : {}),
143
+ ...(s.result.success && controls.verificationMode === 'light' && !s.observationAvailable ? { verification_diagnostic: { code: 'VERIFICATION_UNAVAILABLE', retryable: true } } : {})
144
+ }
145
+ return wrapResponse(controls.responseMode === 'compact' ? evidence : {
146
+ ...s.result, ...evidence, timing: { ...timing, total_ms: performance.now() - started },
147
+ effective_controls: { ...controls, actionTimeoutMs: s.unbounded ? null : budgetMs, timeout_bound: !(s.unbounded) }
148
+ }, controls.responseMode === 'debug')
149
+ }
150
+
151
+ export function actionHandler(type: string, execute: ToolHandler): ToolHandler {
152
+ return correlatedHandler(type, async args => {
153
+ const timing: Record<string, number> = {}
154
+ return withTiming(timing, async () => {
155
+ const state = createAction(type, args, timing)
156
+ const error = await dispatchAction(state, execute)
157
+ if (error) return error
158
+ normalizeAction(state)
159
+ await verifyAction(state)
160
+ return actionResponse(state)
161
+ })
162
+ }, true)
163
+ }
164
+ export { interactionControls, correlatedHandler, payloadOf }
@@ -0,0 +1,20 @@
1
+ import { TraceError } from '../../observe/system-trace.js'
2
+ import { withEvidence, validateActionRef } from '../../observe/evidence.js'
3
+ import { wrapResponse, wrapToolError, type ToolHandler } from '../common.js'
4
+ export function correlatedHandler(name: string, handler: ToolHandler, action = false, journey = false): ToolHandler {
5
+ return async args => {
6
+ const ref = validateActionRef(args.actionRef)
7
+ const { result, evidence } = await withEvidence({ action, journey, ref }, async () => {
8
+ try { return await handler(args) }
9
+ catch (error) { return error instanceof TraceError ? { ...wrapResponse({ error: { tool: name, code: error.code, message: error.message } }), isError: true } : wrapToolError(name, error) }
10
+ })
11
+ const item = result.content.find(item => item.type === 'text')
12
+ if (item?.text) {
13
+ const payload = JSON.parse(item.text)
14
+ payload.evidence = evidence
15
+ item.text = JSON.stringify(payload)
16
+ }
17
+ return result
18
+ }
19
+ }
20
+
@@ -0,0 +1,58 @@
1
+ import { iosCapabilities } from '../../debugger/ios-worker.js'
2
+ import { debuggerResponse } from './debugger-response.js'
3
+ import { classify, type DebuggerDiagnostic } from '../../debugger/diagnostics.js'
4
+ import { debuggerContracts } from '../schemas/debugger-schema.js'
5
+ import { debuggerSessions, iosDebuggerSessions, debuggerCapabilities } from '../../debugger/sessions.js'
6
+ import { DebuggerError, withDebuggerBudget } from '../../debugger/worker.js'
7
+ import { wrapResponse, type ToolCallArgs } from '../common.js'
8
+ function failure(code: string, summary: Record<string, unknown> = {}, diagnostic?: DebuggerDiagnostic, command = '', mode: 'compact' | 'debug' = 'debug') {
9
+ return {
10
+ ...wrapResponse(debuggerResponse(command, mode, {
11
+ debug_session_id: null, device: null, target: null, state: null,
12
+ captured_at_ms: Date.now(), capabilities: debuggerCapabilities, limitations: [], cleanup: null,
13
+ ...summary, success: false, error: { code, message: code, retryable: false, ...(diagnostic ? { diagnostic } : {}) }
14
+ })), isError: true
15
+ }
16
+ }
17
+ export async function handleDebugger(name: string, args: ToolCallArgs, signal?: AbortSignal) {
18
+ const parsed = debuggerContracts[name].schema.safeParse(args)
19
+ if (!parsed.success) return failure('INVALID_ARGUMENT')
20
+ const { responseMode, ...input } = parsed.data
21
+ const a = input as { platform?: 'android' | 'ios'; timeoutMs: number; deviceId?: string; appId?: string; processId?: number; debugSessionId?: string }
22
+ const command = debuggerContracts[name].command
23
+ const sessions = selectSessions(a)
24
+ try {
25
+ if (signal?.aborted) throw new DebuggerError('DEBUGGER_CANCELLED')
26
+ const result = await withDebuggerBudget(a.timeoutMs, signal, async () => command === 'capabilities' ? await sessions.capabilities(a.deviceId!, a.appId!, a.processId) :
27
+ command === 'attach' ? await sessions.attach(a.deviceId!, a.appId!, a.processId, a.timeoutMs) :
28
+ await sessions.command(a.debugSessionId!, command, { ...a, signal: command === 'events' ? signal : undefined }, a.timeoutMs))
29
+ if (signal?.aborted && command === 'capabilities') throw new DebuggerError('DEBUGGER_CANCELLED')
30
+ const payload = sessions === iosDebuggerSessions ? iosDiagnostics(result) : result
31
+ return { ...wrapResponse(debuggerResponse(command, responseMode, payload)), ...(!result.success ? { isError: true } : {}) }
32
+ } catch (e) {
33
+ const error = classify(e, 'unknown', 'DEBUGGER_WORKER_FAILED')
34
+ const code = error.code
35
+ let summary: Partial<ReturnType<typeof debuggerSessions.summary>> = {}
36
+ if (a.debugSessionId) { try { summary = sessions.summary(a.debugSessionId) } catch { /* Unknown session */ } }
37
+ const response = failure(code, {
38
+ ...(sessions === iosDebuggerSessions ? { capabilities: iosCapabilities } : {}),
39
+ debug_session_id: a.debugSessionId ?? null,
40
+ device: a.deviceId ? { platform: a.platform ?? 'android', id: a.deviceId } : null, ...summary
41
+ }, code === 'DEBUGGER_CLEANUP_FAILED' ? summary.error?.diagnostic : error.diagnostic, command, responseMode)
42
+ if (sessions === iosDebuggerSessions) response.content[0].text = JSON.stringify(iosDiagnostics(JSON.parse(response.content[0].text)))
43
+ return response
44
+ }
45
+ }
46
+
47
+ function selectSessions(a: { platform?: string; debugSessionId?: string }) {
48
+ return a.platform === 'ios' || (a.debugSessionId && iosDebuggerSessions.owns(a.debugSessionId)) ? iosDebuggerSessions : debuggerSessions
49
+ }
50
+ function iosRecovery(d: DebuggerDiagnostic) {
51
+ return { ...d, recovery: d.recovery.replace('JDK 17+ availability, including java, javac and jdk.jdi.', 'Xcode and its Swift-enabled LLDB/Python availability.').replace('JDK compiler and worker source/cache', 'LLDB worker source').replace('Java worker', 'LLDB worker').replace('ADB server', 'simulator') }
52
+ }
53
+ function iosDiagnostics<T extends Record<string, unknown>>(value: T) {
54
+ const result = { ...value }
55
+ const error = value.error as { diagnostic?: DebuggerDiagnostic } | undefined
56
+ return { ...result, ...(error?.diagnostic ? { error: { ...error, diagnostic: iosRecovery(error.diagnostic) } } : {}),
57
+ ...(Array.isArray(value.diagnostics) ? { diagnostics: (value.diagnostics as DebuggerDiagnostic[]).map(iosRecovery) } : {}) }
58
+ }
@@ -0,0 +1,7 @@
1
+ /** Omit only repeated target metadata; preserve all execution and failure evidence. */
2
+ export function debuggerResponse(command: string, mode: 'compact' | 'debug', payload: Record<string, unknown>) {
3
+ if (mode !== 'compact' || ['capabilities', 'attach', 'state'].includes(command) || payload.debug_session_id == null) return payload
4
+ const result = { ...payload }
5
+ for (const field of ['device', 'target', 'capabilities']) delete result[field]
6
+ return result
7
+ }
@@ -0,0 +1,86 @@
1
+ import type { IosTemplate } from '../../observe/ios-trace/capabilities.js'
2
+ import { systemTraces, TraceError } from '../../observe/system-trace.js'
3
+ import { traceTargetSchema, traceStartSchema, traceControlSchema } from '../schemas/system-trace-schema.js'
4
+ import { resolveTargetDevice } from '../../utils/resolve-device.js'
5
+
6
+ import { ToolsManage } from '../../manage/index.js'
7
+ import { classifyActionOutcome } from '../../interact/classify.js'
8
+ import { ToolsNetwork } from '../../observe/network/index.js'
9
+ import { getSystemStatus } from '../../system/index.js'
10
+ import {
11
+ getArrayArg,
12
+ getBooleanArg,
13
+ getStringArg,
14
+ requireBooleanArg,
15
+ requireStringArg,
16
+ ToolCallArgs,
17
+ wrapResponse
18
+ } from '../common.js'
19
+ import type { PlatformArg, ClassifyNetworkRequestArg } from './handler-types.js'
20
+
21
+ export async function handleListDevices(args: ToolCallArgs) {
22
+ const platform = getStringArg(args, 'platform') as PlatformArg | undefined
23
+ const appId = getStringArg(args, 'appId')
24
+ const res = await ToolsManage.listDevicesHandler({ platform, appId })
25
+ return wrapResponse(res)
26
+ }
27
+
28
+ export async function handleGetSystemStatus() {
29
+ const result = await getSystemStatus()
30
+ return wrapResponse(result)
31
+ }
32
+
33
+ export function handleClassifyActionOutcome(args: ToolCallArgs) {
34
+ const uiChanged = requireBooleanArg(args, 'uiChanged')
35
+ const expectedElementVisible = getBooleanArg(args, 'expectedElementVisible')
36
+ const actionType = getStringArg(args, 'actionType')
37
+ const networkRequests = getArrayArg<ClassifyNetworkRequestArg>(args, 'networkRequests')
38
+ const hasLogErrors = getBooleanArg(args, 'hasLogErrors')
39
+ const result = classifyActionOutcome({
40
+ uiChanged,
41
+ expectedElementVisible: expectedElementVisible ?? null,
42
+ actionType: actionType ?? null,
43
+ networkRequests: networkRequests ?? null,
44
+ hasLogErrors: hasLogErrors ?? null
45
+ })
46
+ return Promise.resolve(wrapResponse(result))
47
+ }
48
+
49
+ export async function handleGetNetworkActivity(args: ToolCallArgs) {
50
+ const platform = requireStringArg(args, 'platform') as PlatformArg
51
+ const deviceId = getStringArg(args, 'deviceId')
52
+ const result = await ToolsNetwork.getNetworkActivity({ platform, deviceId })
53
+ return wrapResponse(result)
54
+ }
55
+
56
+ export async function handleSystemTrace(name: string, args: ToolCallArgs) {
57
+ const schema = name === 'start_system_trace' ? traceStartSchema : name === 'get_system_trace_capabilities' ? traceTargetSchema : traceControlSchema
58
+ const parsed = schema.safeParse(args)
59
+ if (!parsed.success) throw new TraceError('INVALID_ARGUMENT')
60
+ if (name === 'get_system_trace_capabilities' || name === 'start_system_trace') {
61
+ const target = parsed.data as { platform: PlatformArg; deviceId?: string; durationMs?: number; appId?: string; template?: IosTemplate }
62
+ if (target.platform === 'android' && (target.appId !== undefined || target.template !== undefined)) throw new TraceError('INVALID_ARGUMENT')
63
+ if (target.platform === 'ios') {
64
+ return handleIosTrace(name, target)
65
+ }
66
+ let device
67
+ try { device = await resolveTargetDevice(target) }
68
+ catch {
69
+ if (name === 'start_system_trace') throw new TraceError('TRACE_UNSUPPORTED')
70
+ return wrapResponse({ supported: false, backend: 'perfetto', device: null, prerequisites: { device: false }, reasons: ['DEVICE_UNAVAILABLE'], enabled_sources: [], unavailable_categories: [], limitations: ['An authorised Android device is required'] })
71
+ }
72
+ if (name === 'get_system_trace_capabilities') return wrapResponse(await systemTraces.capabilities(device.id))
73
+ const result = await systemTraces.start(device.id, target.durationMs)
74
+ return { ...wrapResponse(result), ...(result.state === 'failed' ? { isError: true } : {}) }
75
+ }
76
+ const operation = name === 'stop_system_trace' ? 'stop' : name === 'cancel_system_trace' ? 'cancel' : 'status'
77
+ const result = await systemTraces.control((parsed.data as { traceId: string }).traceId, operation)
78
+ return { ...wrapResponse(result), ...(['failed', 'cancelled'].includes(result.state) ? { isError: true } : {}) }
79
+ }
80
+
81
+ async function handleIosTrace(name: string, target: { deviceId?: string; appId?: string; durationMs?: number; template?: IosTemplate }) {
82
+ if (!target.deviceId || (name === 'start_system_trace' && !target.appId)) throw new TraceError('INVALID_ARGUMENT')
83
+ if (name === 'get_system_trace_capabilities') return wrapResponse(await systemTraces.iosCapabilities(target.deviceId, target.appId, target.template))
84
+ const result = await systemTraces.start(target.deviceId, target.durationMs, { appId: target.appId!, template: target.template })
85
+ return { ...wrapResponse(result), ...(result.state === 'failed' ? { isError: true } : {}) }
86
+ }
@@ -0,0 +1,7 @@
1
+ export type PlatformArg = 'android' | 'ios'
2
+ export type ProjectTypeArg = 'native' | 'kmp' | 'react-native' | 'flutter'
3
+ export type WaitForUiMatchArg = { index?: number }
4
+ export type WaitForUiRetryArg = { max_attempts?: number, backoff_ms?: number }
5
+ export type { ScrollSelector as ScrollSelectorArg } from '../../contracts/target.js'
6
+ export type ClassifyNetworkRequestArg = { endpoint: string, status: 'success' | 'failure' | 'retryable' }
7
+ export type { VerificationMode, ResponseMode } from '../../contracts/execution-controls.js'