@bastani/atomic 0.9.39 → 0.9.40-alpha.2

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 (280) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +14 -0
  3. package/dist/builtin/intercom/broker/broker.ts +523 -72
  4. package/dist/builtin/intercom/broker/client.ts +136 -5
  5. package/dist/builtin/intercom/broker/database-wake-up-bus.ts +274 -0
  6. package/dist/builtin/intercom/broker/group-membership.ts +2 -2
  7. package/dist/builtin/intercom/broker/multinode-router.ts +1500 -0
  8. package/dist/builtin/intercom/broker/multinode-state-store.ts +569 -0
  9. package/dist/builtin/intercom/broker/open-broker-state.ts +160 -0
  10. package/dist/builtin/intercom/broker/paths.ts +6 -0
  11. package/dist/builtin/intercom/broker/pending-send-registry.ts +14 -0
  12. package/dist/builtin/intercom/broker/recipient-receipts.ts +93 -0
  13. package/dist/builtin/intercom/broker/restoring-recipients.ts +263 -0
  14. package/dist/builtin/intercom/broker/send-handler.ts +241 -25
  15. package/dist/builtin/intercom/broker/session-id-reclaim.ts +41 -0
  16. package/dist/builtin/intercom/broker/socket-writes.ts +47 -0
  17. package/dist/builtin/intercom/broker/spawn.ts +10 -2
  18. package/dist/builtin/intercom/broker/sql-held-send-store.ts +96 -0
  19. package/dist/builtin/intercom/broker/sql-known-session-store.ts +129 -0
  20. package/dist/builtin/intercom/broker/sql-lease-store.ts +194 -0
  21. package/dist/builtin/intercom/broker/sql-outbox-store.ts +249 -0
  22. package/dist/builtin/intercom/broker/sql-pending-ask-store.ts +105 -0
  23. package/dist/builtin/intercom/broker/sql-restoring-reservation-store.ts +295 -0
  24. package/dist/builtin/intercom/broker/sql-sent-message-authority.ts +126 -0
  25. package/dist/builtin/intercom/broker/supervisor-channel.ts +10 -0
  26. package/dist/builtin/intercom/broker/wake-up-bus.ts +60 -0
  27. package/dist/builtin/intercom/index.bundle.mjs +793 -91
  28. package/dist/builtin/intercom/package.json +1 -1
  29. package/dist/builtin/intercom/retry-policy.ts +4 -0
  30. package/dist/builtin/intercom/session-target.ts +24 -0
  31. package/dist/builtin/intercom/types.ts +57 -3
  32. package/dist/builtin/mcp/package.json +1 -1
  33. package/dist/builtin/subagents/CHANGELOG.md +31 -0
  34. package/dist/builtin/subagents/README.md +18 -12
  35. package/dist/builtin/subagents/package.json +1 -1
  36. package/dist/builtin/subagents/skills/agent-browser/SKILL.md +1 -1
  37. package/dist/builtin/subagents/skills/cua-driver/BROWSER.md +55 -8
  38. package/dist/builtin/subagents/skills/cua-driver/EMBEDDING.md +7 -2
  39. package/dist/builtin/subagents/skills/cua-driver/LINUX.md +7 -1
  40. package/dist/builtin/subagents/skills/cua-driver/MACOS.md +151 -27
  41. package/dist/builtin/subagents/skills/cua-driver/RUNTIME.md +1 -1
  42. package/dist/builtin/subagents/skills/cua-driver/SKILL.md +33 -17
  43. package/dist/builtin/subagents/skills/cua-driver/WINDOWS.md +5 -0
  44. package/dist/builtin/subagents/skills/cua-driver/WORKFLOW.md +46 -8
  45. package/dist/builtin/subagents/skills/subagent/SKILL.md +18 -20
  46. package/dist/builtin/subagents/src/extension/index.bundle.mjs +8032 -6034
  47. package/dist/builtin/web-access/CHANGELOG.md +6 -0
  48. package/dist/builtin/web-access/README.md +1 -1
  49. package/dist/builtin/web-access/index.bundle.mjs +2 -5
  50. package/dist/builtin/web-access/package.json +1 -1
  51. package/dist/builtin/workflows/CHANGELOG.md +24 -0
  52. package/dist/builtin/workflows/README.md +1 -1
  53. package/dist/builtin/workflows/package.json +1 -1
  54. package/dist/builtin/workflows/skills/impeccable/SKILL.md +8 -8
  55. package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +3 -3
  56. package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +1 -1
  57. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +4 -2
  58. package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +2 -2
  59. package/dist/builtin/workflows/skills/impeccable/reference/craft-floor.md +1 -1
  60. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +22 -9
  61. package/dist/builtin/workflows/skills/impeccable/reference/degraded/finish-reviewer.md +2 -2
  62. package/dist/builtin/workflows/skills/impeccable/reference/distill.md +3 -3
  63. package/dist/builtin/workflows/skills/impeccable/reference/extract.md +3 -3
  64. package/dist/builtin/workflows/skills/impeccable/reference/harden.md +2 -4
  65. package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +4 -4
  66. package/dist/builtin/workflows/skills/impeccable/reference/live.md +1 -1
  67. package/dist/builtin/workflows/skills/impeccable/reference/new-work.md +6 -6
  68. package/dist/builtin/workflows/skills/impeccable/reference/onboard.md +3 -3
  69. package/dist/builtin/workflows/skills/impeccable/reference/optimize.md +3 -3
  70. package/dist/builtin/workflows/skills/impeccable/reference/overdrive.md +3 -3
  71. package/dist/builtin/workflows/skills/impeccable/reference/quieter.md +3 -3
  72. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +10 -0
  73. package/dist/builtin/workflows/skills/impeccable/reference/visualize.md +1 -1
  74. package/dist/builtin/workflows/skills/impeccable/scripts/VERSION +1 -1
  75. package/dist/builtin/workflows/skills/impeccable/scripts/impeccable +6 -0
  76. package/dist/builtin/workflows/skills/impeccable/scripts/impeccable.cmd +47 -22
  77. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser-dom.js +34 -1
  78. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser-session.js +25 -1
  79. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +33 -8
  80. package/dist/builtin/workflows/src/durable/journal/resolution.d.ts +7 -7
  81. package/dist/builtin/workflows/src/extension/index.bundle.mjs +22715 -22159
  82. package/dist/builtin/workflows/src/index.js +106 -35
  83. package/dist/cli/args.d.ts +2 -0
  84. package/dist/cli/args.d.ts.map +1 -1
  85. package/dist/cli/args.js +17 -1
  86. package/dist/cli/args.js.map +1 -1
  87. package/dist/core/agent-session-extension-bindings.d.ts +2 -0
  88. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  89. package/dist/core/agent-session-extension-bindings.js +6 -6
  90. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  91. package/dist/core/agent-session-methods.d.ts +6 -3
  92. package/dist/core/agent-session-methods.d.ts.map +1 -1
  93. package/dist/core/agent-session-methods.js.map +1 -1
  94. package/dist/core/agent-session-persistent-custom-messages.d.ts +7 -2
  95. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  96. package/dist/core/agent-session-persistent-custom-messages.js +29 -12
  97. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  98. package/dist/core/agent-session-recovery.d.ts +14 -0
  99. package/dist/core/agent-session-recovery.d.ts.map +1 -0
  100. package/dist/core/agent-session-recovery.js +83 -0
  101. package/dist/core/agent-session-recovery.js.map +1 -0
  102. package/dist/core/agent-session-tasks.d.ts.map +1 -1
  103. package/dist/core/agent-session-tasks.js +4 -6
  104. package/dist/core/agent-session-tasks.js.map +1 -1
  105. package/dist/core/agent-session-types.d.ts +13 -0
  106. package/dist/core/agent-session-types.d.ts.map +1 -1
  107. package/dist/core/agent-session-types.js.map +1 -1
  108. package/dist/core/agent-session.d.ts +2 -1
  109. package/dist/core/agent-session.d.ts.map +1 -1
  110. package/dist/core/agent-session.js +4 -1
  111. package/dist/core/agent-session.js.map +1 -1
  112. package/dist/core/export-html/index.d.ts.map +1 -1
  113. package/dist/core/export-html/index.js +3 -2
  114. package/dist/core/export-html/index.js.map +1 -1
  115. package/dist/core/export-html/template.html +1 -0
  116. package/dist/core/export-html/template.js +37 -20
  117. package/dist/core/export-html/vendor/marked-cjk-friendly.min.js +6 -0
  118. package/dist/core/export-html/vendor/marked.min.js +76 -3
  119. package/dist/core/extensions/api-types.d.ts +6 -2
  120. package/dist/core/extensions/api-types.d.ts.map +1 -1
  121. package/dist/core/extensions/api-types.js.map +1 -1
  122. package/dist/core/extensions/event-results.d.ts +21 -0
  123. package/dist/core/extensions/event-results.d.ts.map +1 -1
  124. package/dist/core/extensions/event-results.js.map +1 -1
  125. package/dist/core/extensions/index.d.ts +1 -1
  126. package/dist/core/extensions/index.d.ts.map +1 -1
  127. package/dist/core/extensions/index.js.map +1 -1
  128. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  129. package/dist/core/extensions/loader-api.js +4 -0
  130. package/dist/core/extensions/loader-api.js.map +1 -1
  131. package/dist/core/extensions/runner-events.d.ts +9 -2
  132. package/dist/core/extensions/runner-events.d.ts.map +1 -1
  133. package/dist/core/extensions/runner-events.js +29 -0
  134. package/dist/core/extensions/runner-events.js.map +1 -1
  135. package/dist/core/extensions/runner.d.ts +5 -3
  136. package/dist/core/extensions/runner.d.ts.map +1 -1
  137. package/dist/core/extensions/runner.js +7 -2
  138. package/dist/core/extensions/runner.js.map +1 -1
  139. package/dist/core/extensions/runtime-types.d.ts +1 -0
  140. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  141. package/dist/core/extensions/runtime-types.js.map +1 -1
  142. package/dist/core/extensions/session-events.d.ts +30 -2
  143. package/dist/core/extensions/session-events.d.ts.map +1 -1
  144. package/dist/core/extensions/session-events.js.map +1 -1
  145. package/dist/core/sdk-types.d.ts +3 -2
  146. package/dist/core/sdk-types.d.ts.map +1 -1
  147. package/dist/core/sdk-types.js.map +1 -1
  148. package/dist/core/sdk.d.ts.map +1 -1
  149. package/dist/core/sdk.js +3 -0
  150. package/dist/core/sdk.js.map +1 -1
  151. package/dist/core/session-storage.d.ts.map +1 -1
  152. package/dist/core/session-storage.js +15 -9
  153. package/dist/core/session-storage.js.map +1 -1
  154. package/dist/core/storage/process-selection.d.ts +39 -0
  155. package/dist/core/storage/process-selection.d.ts.map +1 -0
  156. package/dist/core/storage/process-selection.js +154 -0
  157. package/dist/core/storage/process-selection.js.map +1 -0
  158. package/dist/core/storage/provider.d.ts +6 -1
  159. package/dist/core/storage/provider.d.ts.map +1 -1
  160. package/dist/core/storage/provider.js +3 -2
  161. package/dist/core/storage/provider.js.map +1 -1
  162. package/dist/core/storage/selection.d.ts +1 -1
  163. package/dist/core/storage/selection.d.ts.map +1 -1
  164. package/dist/core/storage/selection.js.map +1 -1
  165. package/dist/core/storage/session/jsonl-session-store.d.ts +2 -0
  166. package/dist/core/storage/session/jsonl-session-store.d.ts.map +1 -1
  167. package/dist/core/storage/session/jsonl-session-store.js +38 -2
  168. package/dist/core/storage/session/jsonl-session-store.js.map +1 -1
  169. package/dist/core/tasks/completion.d.ts +11 -2
  170. package/dist/core/tasks/completion.d.ts.map +1 -1
  171. package/dist/core/tasks/completion.js +40 -16
  172. package/dist/core/tasks/completion.js.map +1 -1
  173. package/dist/core/tasks/pause.d.ts +4 -1
  174. package/dist/core/tasks/pause.d.ts.map +1 -1
  175. package/dist/core/tasks/pause.js +8 -5
  176. package/dist/core/tasks/pause.js.map +1 -1
  177. package/dist/index-extensions.d.ts +1 -1
  178. package/dist/index-extensions.d.ts.map +1 -1
  179. package/dist/index-extensions.js.map +1 -1
  180. package/dist/index.d.ts +2 -1
  181. package/dist/index.d.ts.map +1 -1
  182. package/dist/index.js +2 -0
  183. package/dist/index.js.map +1 -1
  184. package/dist/main.d.ts.map +1 -1
  185. package/dist/main.js +7 -1
  186. package/dist/main.js.map +1 -1
  187. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
  188. package/dist/modes/interactive/components/chat-session-host-rendering.js +11 -1
  189. package/dist/modes/interactive/components/chat-session-host-rendering.js.map +1 -1
  190. package/dist/modes/interactive/components/chat-session-host-state.d.ts +2 -0
  191. package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
  192. package/dist/modes/interactive/components/chat-session-host-state.js +2 -0
  193. package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
  194. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  195. package/dist/modes/interactive/interactive-deferred-startup.js +4 -0
  196. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  197. package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -1
  198. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  199. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  200. package/dist/modes/interactive/interactive-render-chat.js +3 -0
  201. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  202. package/dist/modes/interactive/interactive-session-routing.js +2 -0
  203. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  204. package/dist/modes/interactive/interactive-session-runtime.d.ts +2 -1
  205. package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
  206. package/dist/modes/interactive/interactive-session-runtime.js +29 -0
  207. package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
  208. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  209. package/dist/modes/interactive/interactive-startup.js +4 -1
  210. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  211. package/dist/modes/interactive-engine/engine-args.d.ts.map +1 -1
  212. package/dist/modes/interactive-engine/engine-args.js +4 -0
  213. package/dist/modes/interactive-engine/engine-args.js.map +1 -1
  214. package/dist/modes/interactive-engine/engine-render-service.d.ts.map +1 -1
  215. package/dist/modes/interactive-engine/engine-render-service.js +4 -4
  216. package/dist/modes/interactive-engine/engine-render-service.js.map +1 -1
  217. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  218. package/dist/modes/interactive-engine/isolated-runtime.js +4 -0
  219. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  220. package/dist/modes/interactive-engine/remote-renderer.js +1 -1
  221. package/dist/modes/interactive-engine/remote-renderer.js.map +1 -1
  222. package/dist/modes/rpc/rpc-client-api.d.ts +4 -1
  223. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  224. package/dist/modes/rpc/rpc-client-api.js +4 -0
  225. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  226. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  227. package/dist/modes/rpc/rpc-command-handler.js +3 -0
  228. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  229. package/dist/modes/rpc/rpc-command-timeouts.d.ts.map +1 -1
  230. package/dist/modes/rpc/rpc-command-timeouts.js +1 -0
  231. package/dist/modes/rpc/rpc-command-timeouts.js.map +1 -1
  232. package/dist/modes/rpc/rpc-input-scheduler.d.ts.map +1 -1
  233. package/dist/modes/rpc/rpc-input-scheduler.js +6 -1
  234. package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
  235. package/dist/modes/rpc/rpc-types.d.ts +12 -1
  236. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  237. package/dist/modes/rpc/rpc-types.js.map +1 -1
  238. package/dist/utils/shell.d.ts.map +1 -1
  239. package/dist/utils/shell.js +44 -11
  240. package/dist/utils/shell.js.map +1 -1
  241. package/dist/utils/tools-manager.js +1 -1
  242. package/dist/utils/tools-manager.js.map +1 -1
  243. package/docs/background-tasks.md +5 -3
  244. package/docs/computer-use.md +2 -2
  245. package/docs/environment-variables.md +4 -1
  246. package/docs/extensions/events.md +29 -2
  247. package/docs/getting-started/first-session.md +1 -1
  248. package/docs/guides/intercom.md +1 -1
  249. package/docs/intercom/operations.md +103 -1
  250. package/docs/intercom/reference.md +10 -3
  251. package/docs/intercom.md +8 -6
  252. package/docs/keybindings.md +1 -1
  253. package/docs/models.md +1 -1
  254. package/docs/providers.md +5 -3
  255. package/docs/reference/cli.md +5 -0
  256. package/docs/rpc/protocol.md +19 -0
  257. package/docs/rpc.md +3 -0
  258. package/docs/sdk/web-server.md +1 -1
  259. package/docs/sdk.md +5 -2
  260. package/docs/sessions.md +17 -1
  261. package/docs/settings.md +6 -4
  262. package/docs/skills.md +1 -1
  263. package/docs/storage.md +54 -3
  264. package/docs/subagents.md +49 -9
  265. package/docs/usage.md +1 -1
  266. package/docs/windows.md +14 -4
  267. package/docs/workflows/api-reference.md +1 -1
  268. package/docs/workflows/authoring.md +1 -1
  269. package/docs/workflows/operations.md +10 -6
  270. package/npm-shrinkwrap.json +40 -40
  271. package/package.json +6 -6
  272. package/dist/core/export-html/template-js/data-tree.js +0 -287
  273. package/dist/core/export-html/template-js/entries-navigation.js +0 -434
  274. package/dist/core/export-html/template-js/initialization.js +0 -335
  275. package/dist/core/export-html/template-js/message-tools.js +0 -428
  276. package/dist/core/export-html/template-js/tree-filter-render.js +0 -419
  277. package/dist/core/export-html/template-script.d.ts +0 -3
  278. package/dist/core/export-html/template-script.d.ts.map +0 -1
  279. package/dist/core/export-html/template-script.js +0 -13
  280. package/dist/core/export-html/template-script.js.map +0 -1
@@ -83,8 +83,20 @@ frontmost is true'`). Mutating it is not.
83
83
  **Corollary: the AXMenuBar rule.** Do not manually drive a background
84
84
  application's `AXMenuBarItem`: the visible macOS menu bar belongs to the
85
85
  frontmost app, and command items may be disabled otherwise. Use `invoke_menu`
86
- for a known application-menu path. It owns the necessary temporary activation,
87
- resolves every AX level live, and restores the prior app on a best-effort basis. Prefer an in-window
86
+ for a known application-menu path. With the app in the background it resolves
87
+ the whole path without opening a menu and never activates the app; if macOS
88
+ keeps the item unavailable to a background app it refuses with
89
+ `background_unavailable` (then, if the user allows it, `bring_to_front` first).
90
+ One exception: a disabled Save (the ⌘S item) of a document window is saved
91
+ through the app's scripting interface instead, still in the background, and the
92
+ result says so; TextEdit disables Save whenever it is behind the user's window.
93
+ A Save is verified on disk. A background `hotkey` whose combo is a menu item's
94
+ key equivalent (⌘S, ⌘W, ⌘C, ...) in a native app takes the same route, because
95
+ background key events never reach an app's menus; no keys are sent. macOS asks
96
+ the user once to allow Cua Driver to control the app (Automation) the first
97
+ time a scripting route runs; when that is off, the call refuses and nothing is
98
+ saved.
99
+ With the app already in front it resolves every AX level live. Prefer an in-window
88
100
  element action when the same command has an ordinary control, and prefer
89
101
  `set_window_frame` for exact geometry. Full rationale is in “Navigating native
90
102
  menu bars” below.
@@ -109,10 +121,13 @@ is safe even for apps that normally foreground on media-load
109
121
  | Enumerate an app's windows | `list_windows({pid})`: or read the `windows` array `launch_app` already returns | `osascript 'every window of app …'` |
110
122
  | Move or resize one exact window | `set_window_frame({pid, window_id, x, y, width, height})` | `osascript` position/size writes or title-bar dragging |
111
123
  | Click / type / scroll / keys | `click`, `type_text`, `scroll`, `press_key`, `hotkey` | `osascript`, `cliclick`, raw `CGEvent`, `open <url>` |
112
- | Drag / drag-and-drop / marquee select | `drag({pid, window_id, from_x, from_y, to_x, to_y, delivery_mode:"foreground"})` (pixel-only and foreground-only: macOS AX has no semantic drag, and there is no background drag) | `cliclick dd:`, `osascript drag` |
124
+ | Loop, branch or retry over app actions | `run_script({script})` (sandboxed JavaScript over the `cua` API, one call); `run_steps` for a fixed list | an AppleScript or shell loop around `osascript` or `cua-driver` calls |
125
+ | Drag / drag-and-drop / marquee select | `drag({pid, window_id, from_x, from_y, to_x, to_y})` in the background (pixel-only: macOS AX has no semantic drag). Verify with a screenshot; `delivery_mode:"foreground"` only if nothing changed. A bound browser tab: `browser_pointer` `action:"drag"` | `cliclick dd:`, `osascript drag`, refusing a drag-only step as "needs the window in front" |
126
+ | Hover (reveal a hover-only control) | Window-targeted `move_cursor`; its result says whether it hovered (AX alternate UI or an Electron page). A web page in Chrome, Edge or Brave: `browser_pointer` hover after `get_browser_state`. If not, look for the same command elsewhere (menu item, context menu, shortcut, help text in `get_window_state`); only `move_cursor({scope:"desktop", delivery_mode:"foreground"})` (moves the user's pointer) or `bring_to_front` reveals it, and only if the user allows it | assuming a window-targeted `move_cursor` hovered when its result says the app sees no hover; a desktop `move_cursor` over a background browser page |
113
127
  | Screenshot | `get_window_state` (window) or authorized `get_desktop_state` (desktop) | `screencapture` |
114
128
  | Quit an app | ask the user first, then `hotkey({pid, keys:["cmd","q"]})` | `kill`, `killall`, `pkill` |
115
129
  | Hand a file/URL to an app | `launch_app({bundle_id, urls:[<path>]})` | `open -a <App> <path>`, `open <url>` |
130
+ | View a local file in the GUI | `launch_app({bundle_id, urls:["file:///…"]})` | typing `file://` into the omnibox |
116
131
 
117
132
  ### The narrow carve-out
118
133
 
@@ -122,6 +137,11 @@ For authorized foreground input, use the Cua action's
122
137
 
123
138
  When a cua-driver call surprises you, diagnose cua-driver first:
124
139
 
140
+ - **Large tree or repeated reads?** `get_window_state` returns one compact
141
+ markdown tree capped at 250 nodes; pass `since:"latest"` for a diff
142
+ (`no change since …; focused element is …` names the focused row on macOS)
143
+ and `tree_format:"elements"` for structured rows. `full_output:true` restores
144
+ the old payload (both forms, `_note`, `background_input`, 2 000 nodes).
125
145
  - **Empty `tree_markdown`?** `get_window_state` returns **both** the
126
146
  AX tree and a screenshot by default: there's nothing to configure and
127
147
  no capture mode to pick. An empty tree means the surface isn't AX (a
@@ -158,8 +178,10 @@ response, and only switch _dispatch rung_ on a real signal:
158
178
  1. **Re-snapshot and read the tree diff**: a changed `AXValue`, a new
159
179
  element, a collapsed menu, a disabled button. If the tree shows the
160
180
  change, you're done. When you only need the tree diff and don't need
161
- fresh pixels, pass `include_screenshot:false` to skip the grab: a
162
- **perf** knob, not a mode flip.
181
+ fresh pixels, pass `since:"latest"` and `include_screenshot:false` to
182
+ skip the grab: a **perf** knob, not a mode flip. The window keeps its
183
+ last screenshot's pixel frame while its size is unchanged, so `x,y`
184
+ from that screenshot still work afterwards.
163
185
  2. **Trust the screenshot and do an element px action** when the tree
164
186
  **lies**: the action response carried `effect:"suspected_noop"`, the
165
187
  re-snapshot came back `degraded` (empty tree), or the tree looks
@@ -279,14 +301,75 @@ permission prompt the agent loop shouldn't be burning on app launch.
279
301
 
280
302
  The pixel click is routed through SkyLight's per-pid event path
281
303
  (`SLEventPostToPid`), not the system HID stream. The dispatch recipe
282
- is the backgrounded "noraise" sequence: focus-without-raise
283
- SLPS event records followed by an off-screen user-activation primer
284
- and the real click. The target app becomes AppKit-active for event
285
- routing but its window does **not** rise to the front of the
286
- z-stack, and macOS's "switch to Space with windows for app" follow
287
- is suppressed. Full mechanics in
288
- `Sources/CuaDriverCore/Input/MouseInput.swift` (`clickViaAuthSignedPost`)
289
- and the companion `FocusWithoutRaise.swift`.
304
+ is the backgrounded "noraise" sequence: a focus-without-raise
305
+ SLPS event record to the target window, followed by an off-screen
306
+ user-activation primer and the real click. The target window becomes
307
+ AppKit-key for event routing, but it does **not** rise to the front of
308
+ the z-stack, macOS's "switch to Space with windows for app" follow is
309
+ suppressed, and the user's front window keeps key status, so their
310
+ typing keeps landing there.
311
+
312
+ ### Menus opened in the background keep the user's keyboard
313
+
314
+ A popup button, a `<select>`, or a context menu opened in a background
315
+ app takes key focus from the user's front window for as long as the menu
316
+ is open (AppKit's menu tracking steals it). While a background action
317
+ runs, Cua Driver watches for that and hands key focus straight back, so
318
+ the user's window never loses it. The menu stays open and its items are
319
+ chosen with `click` on the `AXMenuItem` (or in one call with
320
+ `set_value` on the popup). Keyboard navigation of such a menu with
321
+ `press_key` is not the route; pick the item by element.
322
+
323
+ ### Apps that activate themselves
324
+
325
+ Some apps activate themselves in answer to input, even in the background:
326
+ Chrome when `cmd+L` focuses its address bar, GnuCash on a double-click in
327
+ its account tree. No client can stop that. After a background
328
+ action Cua Driver checks WindowServer's front process and, if the target
329
+ made itself frontmost, re-activates the user's app and hands its window
330
+ key focus back within a few tens of milliseconds. Prefer routes that do
331
+ not ask the app to come forward: `set_value` on the address field or
332
+ `browser_navigate` instead of `cmd+L`, and `AXOpen` through
333
+ `double_click` on an element instead of a pixel double-click.
334
+ ### Hover in the background
335
+
336
+ macOS apps track hover and show tooltips (help tags) from the real
337
+ pointer. Pointer-move and enter events posted to a background app reach
338
+ the app but never its tracking areas, so most hover-only controls and all
339
+ native tooltips cannot be revealed in the background. A window-targeted
340
+ `move_cursor` on a background app tries the two routes that leave the
341
+ real pointer, focus and z-order alone. Its result says whether it hovered
342
+ and how:
343
+
344
+ - `ax_alternate_ui`: the element under the point (or an ancestor) lists
345
+ `showalternateui`, as sidebar section headers in Finder, Mail, Music and
346
+ System Settings do. The hover-only controls appear in the next
347
+ `get_window_state`; hovering elsewhere hides them. The same element can
348
+ be driven directly with `click` and `action:"show_alternate_ui"` /
349
+ `"show_default_ui"`.
350
+ - `electron_input`: an Electron page gets `mouseEnter`/`mouseMove` through
351
+ the app's main process, so page hover effects update. Native tooltips do
352
+ not show.
353
+
354
+ For a web page in Chrome, Edge or Brave use `browser_pointer` hover: a
355
+ window-target `move_cursor` there says the page saw no hover and names
356
+ `get_browser_state` as the next call. Otherwise `move_cursor` moves only the
357
+ agent cursor and says so. Look for the same command elsewhere first: a menu
358
+ item, a context menu, a keyboard shortcut, or the control's help text in
359
+ `get_window_state` (toolbar tooltips are usually there as `help=`).
360
+ Otherwise the step needs the user's pointer (`move_cursor` with
361
+ `scope:"desktop"` and `delivery_mode:"foreground"`) or the app in front
362
+ (`bring_to_front`); use either only if the user allows it, and say which.
363
+
364
+ ### Desktop-target pointer input over a background app
365
+
366
+ `move_cursor`, `click`, `drag` and `scroll` with a desktop target move the
367
+ user's real pointer. When the point lands on a window of an app that is not
368
+ in front, they refuse with `background_unavailable` before sending anything,
369
+ unless the call passes `delivery_mode:"foreground"`. Over a web browser the
370
+ refusal names the browser tools (`get_browser_state`, then
371
+ `browser_pointer`, `browser_click` or `browser_type`) instead of foreground.
372
+ Points over the app in front, or over no window, behave as before.
290
373
 
291
374
  ### `delivery_mode` on the pointer family (macOS)
292
375
 
@@ -300,12 +383,31 @@ surface that only accepts events while frontmost (the canvas/viewport/game
300
383
  case below). Unmodified `element_token` (AX) actions remain background-capable
301
384
  and hold the no-foreground contract without the flag.
302
385
 
303
- `drag` is the exception: macOS has no background drag. A window-scoped
304
- `drag` needs `delivery_mode:"foreground"` and `window_id`; without them it
305
- refuses with `background_unavailable` and sends nothing. The foreground drag
306
- fronts the exact window, moves the physical pointer along the path, and then
307
- restores the prior frontmost app. Use it only when a drag is the task, and
308
- expect the hardware pointer to move.
386
+ `drag` runs in the background too. A window-scoped `drag` (`pid` and
387
+ `window_id`) makes the window key without raising it, posts the press, the
388
+ interpolated drags and the release to that window's process, and hands focus
389
+ back to the user's window. The real pointer does not move. It reaches
390
+ controls that follow the drag events: native sliders and canvases, and
391
+ pointer-event sliders, canvases and boards on web pages in Chrome. The
392
+ result is not driver-verified: check the outcome with a screenshot or the
393
+ tree. Only if nothing changed, retry with `delivery_mode:"foreground"`,
394
+ which fronts the window, moves the physical pointer along the path, and
395
+ restores the prior frontmost app.
396
+
397
+ A native `<input type="range">` in Chrome does not follow a background drag
398
+ (pointer-event widgets do). Use `set_value` on its slider element, or
399
+ `browser_pointer` `action:"drag"` on a bound tab, which sends the drag
400
+ through CDP `Input.dispatchMouseEvent` with the button held.
401
+
402
+ A drag that starts a macOS drag-and-drop session (reordering a list by drag
403
+ and drop, moving a file, HTML5 `draggable` content) cannot drop in the
404
+ background: macOS tracks those sessions with the real pointer. The driver
405
+ sees the session start, releases at once, and returns
406
+ `background_unavailable` with `reason: "drag_and_drop_session"`. Look for a
407
+ non-drag route first (Move Up/Down, a menu or context-menu command, a
408
+ shortcut, cut and paste), then foreground if the user allows it. Toolkits that read the hardware pointer (Tk) or drop routed
409
+ mouse events (LibreOffice) refuse the background drag with
410
+ `background_unavailable` before anything is sent.
309
411
 
310
412
  A foreground window-scoped **pixel** `click`, `double_click`, or
311
413
  `right_click` (`x`/`y` with `window_id`) is delivered like a desktop-scope click, not through the per-pid path: Cua Driver
@@ -390,6 +492,16 @@ There is no backgrounded path that reaches these apps today.
390
492
  AX-addressable elements (links, buttons, toolbar items), use
391
493
  `right_click({pid, element_token})` instead.
392
494
 
495
+ ### A value write the app does not apply (LibreOffice)
496
+
497
+ Some apps accept an accessibility value write and ignore it. LibreOffice
498
+ Calc cells do: `set_value` returns success at the AX level and the cell
499
+ keeps its old content. `set_value` re-reads the value, and when it has
500
+ not moved it fails with `code:"write_not_applied"` and
501
+ `effect:"not_applied"`, so a `run_script` stops there. LibreOffice also
502
+ ignores background pointer input, so address it with `window_id` and type
503
+ instead (see "LibreOffice, Numbers and other editors in the background").
504
+
393
505
  ### Known text-input limits (Catalyst + Electron)
394
506
 
395
507
  On **Catalyst** apps (WhatsApp, Reminders, Notes-via-Catalyst,
@@ -455,6 +567,10 @@ and the refusal says whether any menu window it opened is still on screen. If
455
567
  the refusal says the menu may still be open, press `escape` on the window
456
568
  before other input.
457
569
 
570
+ A `get_window_state` read lists only the menu bar's titles and says
571
+ `menus collapsed`; pass `query` to find an item in any menu, or run it with
572
+ `invoke_menu`. A missed path segment lists the items at that level.
573
+
458
574
  ```bash
459
575
  cua-driver invoke_menu \
460
576
  '{"pid":844,"window_id":10725,"path":["Window","Move & Resize","Left"]}'
@@ -486,11 +602,11 @@ window, and PID-routes its bounded checkbox-pixel fallback. The same control's
486
602
  state must verify after mutation; unsupported appearance, scale, zoom,
487
603
  window-size, or toolbar geometry refuses without a click.
488
604
 
489
- Standalone Chromium activates its window when CDP's trusted pointer route is
490
- used on macOS. The driver therefore returns
491
- `browser_input_trust_unavailable` before dispatch rather than falsely claiming
492
- background delivery. Use `input_route:"dom_event"` only when synthetic click
493
- semantics are acceptable. Embedded Electron has a separately bounded route;
605
+ CDP's trusted pointer route (the `browser_click` and `browser_pointer`
606
+ default) stays in the background on macOS Chrome: it delivers trusted events
607
+ to the bound tab, even an inactive one, without activating or raising the
608
+ browser window. Use `input_route:"dom_event"` only when synthetic events are
609
+ acceptable; controls that check `event.isTrusted` ignore it. Embedded Electron has a separately bounded route;
494
610
  do not infer that route for arbitrary WKWebView or Tauri hosts.
495
611
 
496
612
  For a declared session, typed browser click, type, and pointer mutations animate
@@ -518,8 +634,8 @@ starting point for new browser workflows.
518
634
  | `Accessibility permission not granted` | TCC not granted | Stop; tell user to grant in System Settings |
519
635
  | `Screen Recording permission not granted` | TCC not granted for capture | Screenshots and pixel actions are unavailable. If the task is AX-completable, use `get_window_state({include_screenshot:false})` and `element_token` actions; otherwise stop and ask the user to run `cua-driver permissions grant` |
520
636
  | `AX action failed: ... returned -25200` (or -25205, -25206) | The app may have acted anyway: some AppKit controls return these after a press they performed | Re-read with `get_window_state` before retrying; a second press on a checkbox toggles it back. A radio button or checkbox whose AXValue settles on the pressed state is reported as performed, naming the error |
521
- | `set_value refused (file_name_needs_rename)` | A file's name as Finder lists it: an AXValue write changes only the list, never the file | `click` the item, then with Finder frontmost send `press_key` return, `hotkey` cmd+a, `type_text` the full new name, `press_key` return, each with `scope:"desktop"`; re-read to confirm |
522
- | `set_value refused (file_name_needs_rename)` | A file's name in Finder's Get Info window (AXIdentifier `Name`): an AXValue write changes only the field | foreground `click` at the field's centre in screenshot pixels (with its `capture_id`), then `hotkey` cmd+a, `type_text` the full new name with its extension, `press_key` return, each with `delivery_mode:"foreground"`; a changed extension asks for confirmation first; check the folder listing |
637
+ | `set_value refused (file_name_needs_rename)` | A file's name in Finder's Get Info window (AXIdentifier `Name`): an AXValue write changes only the field | `set_value` the full new name on the file's name cell in a Finder window that lists it (renamed through Finder in the background and read back from disk) |
638
+ | `Background input refused (no_focused_text_input)` | Untargeted background `type_text` while the window's focused element is not a text input or an editing surface (spreadsheet grid, document canvas: `AXLayoutArea`, `AXScrollArea`, `AXTable`, `AXCell`, `AXGroup`; not in Finder/Dock); keys would drive the view (Finder type-to-select) | pass the field's `element_token`, or `x,y` to pixel-focus it first; to rename a Finder file, `set_value` its name cell |
523
639
 
524
640
  ## Example end-to-end task (macOS)
525
641
 
@@ -537,4 +653,12 @@ starting point for new browser workflows.
537
653
 
538
654
  If the user instead asks to navigate _within_ an already-open Finder
539
655
  window, use the menu-bar flow from "Navigating native menu bars"
540
- above (click Go → pick a menu item → re-snapshot → click it).
656
+ above (`invoke_menu` with a path such as `["Go", "Downloads"]`).
657
+
658
+ ### LibreOffice, Numbers and other editors in the background
659
+
660
+ With `window_id`, `type_text`, `press_key` and `hotkey` post keys to that exact window: the driver makes it the app's key window through in-app key-window records (no raise, no activation, real pointer untouched), confirms that the app's focused window and focused element are in that window, posts, and hands key back. This also covers another window or dialog of the app holding focus, and a LibreOffice that was never activated. When the switch cannot be confirmed the call is refused with `window_not_key` and no key is sent. A second window of the same app (Find & Replace, another document) is no longer `same_pid_keyboard_ambiguity`. A command key whose menu route is refused (item disabled while the app is in the background) falls through to key events in that window. Recipe: pass `window_id` on every call; go to the cell with Name Box navigation (`type_text` `A1\n`), type with `\n` and `\t`, read the cell back; run commands (Paste Special, Format, Save) with `invoke_menu({pid, window_id, path})` rather than clicking the menu bar. A `background_unavailable` refusal here means use `get_window_state` and act by element, not stop.
661
+
662
+ ### Preview: export before resizing
663
+
664
+ Duplicate is disabled in a background Preview, so work on an export: File > Export (`invoke_menu`) to the destination, then resize or edit the exported copy. Do not resize the original first.
@@ -98,7 +98,7 @@ Pixel `click` already animates the cursor. Do not precede it with `move_cursor`
98
98
 
99
99
  The default theme provides session-colored pointer/glow, action marks, and activity animations. Named sessions have stable palette colors. Native hosts own delivery/target badges separately from theme artwork; cursor state is scoped to the private lifecycle.
100
100
 
101
- `set_agent_cursor_enabled` controls idle visibility. `set_agent_cursor_motion` takes `style` (`signature_arc` default, `spring_settle`, `magnetic`, `comet_swoop`, `adaptive`, `classic`), `timing` (`native`, `fitts`, `fixed`), `effects` (`trail`, `glow`, `magnet`, `ripple`, `squish` booleans; null restores the style default), and the arc knobs `start_handle`, `end_handle`, `arc_size`, `arc_flow` (bezier styles) plus `spring` and `turn_radius` (`classic`) where advertised. Choose preinstalled artwork with `set_agent_cursor_theme`; `cursor_id` on input tools is not a theme selector. Never supply inline theme code or arbitrary source paths through agent tools. The trusted local `cursor-theme` workflow owns validation, compilation, preview, installation, and removal.
101
+ `set_agent_cursor_enabled` controls idle visibility. `set_agent_cursor_motion` takes `style` (`signature_arc` default, `spring_settle`, `magnetic`, `comet_swoop`, `adaptive`, `classic`), `timing` (`native`, `fitts`, `fixed`), `effects` (`trail`, `glow`, `magnet`, `ripple`, `squish` as `on`, `off`, or `default`; `default` restores the style default, and an omitted effect keeps its current setting), and the arc knobs `start_handle`, `end_handle`, `arc_size`, `arc_flow` (bezier styles) plus `spring` and `turn_radius` (`classic`) where advertised. Choose preinstalled artwork with `set_agent_cursor_theme`; `cursor_id` on input tools is not a theme selector. Never supply inline theme code or arbitrary source paths through agent tools. The trusted local `cursor-theme` workflow owns validation, compilation, preview, installation, and removal.
102
102
 
103
103
  Overlay operations need a suitable UI event loop. macOS direct SDK/MCP runtimes without a certified host main-thread adapter can return `facility_unavailable`; do not report that as a successful move. See [EMBEDDING.md](EMBEDDING.md).
104
104
 
@@ -4,16 +4,26 @@ description: Drive a native GUI app (macOS, Windows, Linux) via the cua-driver C
4
4
  hidden: true
5
5
  metadata:
6
6
  github-path: libs/cua-driver/rust/Skills/cua-driver
7
- github-ref: refs/tags/cua-driver-rs-v0.34.0
7
+ github-ref: refs/tags/cua-driver-rs-v0.36.0
8
8
  github-repo: https://github.com/trycua/cua
9
- github-tree-sha: bc6d2b59a453e10321e30e58203294fee9d4bc28
9
+ github-tree-sha: 229cf71d61810fb3fe25771705478985640d93a0
10
10
  name: cua-driver
11
- version: 0.34.0
11
+ version: 0.36.0
12
12
  ---
13
13
 
14
14
  # cua-driver
15
15
 
16
- Operate one exact target, observe its state, act once, and verify the user's postcondition.
16
+ Operate one exact target, observe it narrowly, act by element, and verify the user's postcondition at checkpoints.
17
+
18
+ ## Efficient loop
19
+
20
+ The MCP server instructions carry the same rules, so they apply even when this file is not opened.
21
+
22
+ 1. Start with a targeted read: `get_window_state({pid, window_id, query:"Save"})` returns about 2K chars where a full snapshot is about 42K. Bound large trees with `max_elements` / `max_depth`. Widen only if the target is missing.
23
+ 2. Act by `element_token`: row `[N]` of a read is `<snapshot_id>:N` (for example `"s0000002a:11"`, not `"11"`). Use pixels only for surfaces missing from the tree.
24
+ 3. Act through `run_steps` with `observe:{}`, even for a single step. It runs the steps, stops at the first failure, and returns only what changed since your last read, so you do not need a separate read after acting; see [Workflow](WORKFLOW.md#batch-known-actions). When the flow needs logic between actions (a loop, a branch on what the app shows, a retry), write it as one `run_script` instead; see [Workflow](WORKFLOW.md#script-a-flow).
25
+ 4. To re-read a window, pass `since:"latest"`: you get the changed rows, not the whole tree. To just look, use `include_accessibility_tree:false` where advertised (it keeps the current tokens valid). Do not use `max_elements:1` or a junk `query` to get a picture.
26
+ 5. Verify at checkpoints (after a meaningful state change, before finishing), not after every action. One `verify_state` or one targeted `get_window_state` is usually enough.
17
27
 
18
28
  ## Act
19
29
 
@@ -21,12 +31,16 @@ Operate one exact target, observe its state, act once, and verify the user's pos
21
31
  | ----------------------------------------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
22
32
  | Check installation and capabilities | `cua-driver --version`, `status`, `doctor`, `describe <tool>`; MCP `tools/list` | [Runtime](RUNTIME.md) |
23
33
  | Find or open the requested app | `list_apps`, `list_windows`, `launch_app` | Current platform guide below |
24
- | Observe one window | `get_window_state({pid, window_id})` | [Workflow](WORKFLOW.md) |
34
+ | View a local file in the GUI | `launch_app({bundle_id, urls:["file:///…"]})` opens it in that app | Current platform guide below |
35
+ | Observe one window | `get_window_state({pid, window_id, query})` | [Workflow](WORKFLOW.md) |
25
36
  | Act on a control | `click` / `type_text` with a fresh `element_token` and exact window target | [Workflow](WORKFLOW.md) |
37
+ | Act and see what changed in one call | `run_steps({steps:[{tool,args},...], observe:{}})`; stops at the first failure | [Workflow](WORKFLOW.md#batch-known-actions) |
38
+ | Loop, branch or retry in one call | `run_script({script})`: JavaScript over the `cua` API, sandboxed, time and call limits | [Workflow](WORKFLOW.md#script-a-flow) |
26
39
  | Use pixels when semantics cannot reach it | Fresh target screenshot, then `x,y` on the same target | [Workflow](WORKFLOW.md) |
27
40
  | Verify the outcome | `verify_state({pid, window_id, expect})` or a fresh snapshot read by the agent | [Workflow](WORKFLOW.md) |
28
41
  | Operate the authorized desktop | `get_desktop_state` → input with `target:{kind:"desktop",display_id:"primary"}` → `get_desktop_state` | [Workflow](WORKFLOW.md), [Linux](LINUX.md) on Wayland |
29
- | Drive supported browser page content | `get_browser_state` → typed browser action → fresh state | [Browser](BROWSER.md) |
42
+ | Drive supported browser page content | `get_browser_state` → `browser_prepare` from `detail.next_call` if refused → typed browser action | [Browser](BROWSER.md) |
43
+ | Browser tools refuse in the background | Not blocked: fill the page through `get_window_state` + `set_value`/click; if the first read shows only browser chrome, read again. BLOCKED only after that fails | [Browser](BROWSER.md) |
30
44
  | Record an explicitly requested run | `start_recording` → actions → `stop_recording`; verify artifacts | [Recording](RECORDING.md) |
31
45
  | Finish | Stop after proof; `end_session` for this run, not `cua-driver stop` on a shared service | [Runtime](RUNTIME.md) |
32
46
 
@@ -39,8 +53,8 @@ Check the installed version and advertised schema before using unfamiliar parame
39
53
  ## Rules
40
54
 
41
55
  1. Select the exact target on each action. A session is lifecycle metadata, not capture scope or permission authority.
42
- 2. Observe before input and verify after it. `effect:"unverifiable"` and a successful exit are not task success; never replay a partial, canceled, or unknown action blindly.
43
- 3. Use returned tokens, never invented indices. A fresh snapshot replaces prior element handles and lists them in `invalidated_snapshot_ids`; act with `element_token`.
56
+ 2. Observe before input and verify the outcome at checkpoints, not after every action. `effect:"unverifiable"` and a successful exit are not task success; never replay a partial, canceled, or unknown action blindly.
57
+ 3. Use returned tokens, never invented indices. A fresh snapshot replaces prior element handles and lists them in `invalidated_snapshot_ids`; act with `element_token`. Reads are bounded and return one tree form by default; follow up with `since:"latest"` instead of re-reading a large window.
44
58
  4. Keep background window actions non-interfering. Foreground delivery and desktop input require authorization for visible control; an unavailable route is not permission to escalate.
45
59
  5. Never infer pixels from a missing image, a different window, or an unaccounted-for resized preview. Capture failure and an empty accessibility tree are different failures.
46
60
  6. Keep one controller for a shared desktop. Distinct sessions/cursors do not isolate focus, keyboard input, application state, or snapshot caches.
@@ -48,15 +62,17 @@ Check the installed version and advertised schema before using unfamiliar parame
48
62
 
49
63
  ## Failure map
50
64
 
51
- | Symptom | Next step |
52
- | --------------------------------------------------------- | ------------------------------------------------------------------------------------- |
53
- | Missing binary, mismatched daemon, unknown tool/field | [Runtime preflight](RUNTIME.md#preflight-and-transport) |
54
- | Stale token or ambiguous window | Refresh `list_windows` / `get_window_state`; choose the intended live target |
55
- | Large or sparse tree | [Bounded observation](WORKFLOW.md#observe) |
56
- | `surface_identity_unproven` or screenshot permission wait | [Wayland capture recovery](LINUX.md#capture-recovery) |
57
- | `background_unavailable` | Verify current state; ask before foreground/desktop control if not already authorized |
58
- | Text did not visibly change | Reobserve before retrying; [text and value semantics](WORKFLOW.md#act-once) |
59
- | Browser setup, binding, or ref refused | [Browser recovery](BROWSER.md#recovery-rules) |
65
+ | Symptom | Next step |
66
+ | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
67
+ | Missing binary, mismatched daemon, unknown tool/field | [Runtime preflight](RUNTIME.md#preflight-and-transport) |
68
+ | Stale token or ambiguous window | Refresh `list_windows` / `get_window_state`; choose the intended live target |
69
+ | Large, truncated, or sparse tree; repeated reads | [Bounded observation and `since` diffs](WORKFLOW.md#observe) |
70
+ | `surface_identity_unproven` or screenshot permission wait | [Wayland capture recovery](LINUX.md#capture-recovery) |
71
+ | `background_unavailable` | Not a stop: that input route is closed, the task is not. Read `get_window_state` and act by element (`element_token`, `invoke_menu`); foreground only if authorized |
72
+ | Web page in a browser | Refs from `get_browser_state` with `browser_click` / `browser_type`; pixel clicks only when no ref exists |
73
+ | LibreOffice, Preview and other macOS editors | [macOS recipes](MACOS.md#libreoffice-numbers-and-other-editors-in-the-background) |
74
+ | Text did not visibly change | Reobserve before retrying; [text and value semantics](WORKFLOW.md#act-once) |
75
+ | Browser setup, binding, or ref refused | [Browser recovery](BROWSER.md#recovery-rules) |
60
76
 
61
77
  ## Consult recent Cua activity only for continuation
62
78
 
@@ -376,6 +376,11 @@ When a cua-driver surprises you, diagnose cua-driver first:
376
376
  click in.
377
377
  - **`Invalid window handle (0x80070578)`?** The HWND you passed is
378
378
  stale (window closed, recreated). Re-resolve via `list_windows`.
379
+ - **Large tree or repeated reads?** `get_window_state` returns one compact
380
+ markdown tree capped at 250 nodes (`full_output:true` restores 5 000 nodes
381
+ and both forms). Pass `since:<snapshot_id>` for added/changed/removed rows;
382
+ the focused element is not reported on Windows yet, so an unchanged read
383
+ says only `no change since …`.
379
384
  - **Empty `tree_markdown` / sparse UIA tree?** Some apps populate
380
385
  their UIA tree lazily on first call; retry `get_window_state`
381
386
  once. If still empty, the app has no UIA provider: fall back to an
@@ -35,14 +35,17 @@ These objects are argument fragments for tools advertising `target`, not standal
35
35
 
36
36
  ## Observe
37
37
 
38
- `get_window_state({pid, window_id})` requests the accessibility tree and a grounding screenshot by default. Check what actually came back: permission, backing-store, or surface-identity failures can leave usable tree data without an image. `screenshot_error` and `screenshot_frame_valid:false` are not empty-tree signals.
38
+ `get_window_state({pid, window_id})` requests the accessibility tree and a grounding screenshot by default. Start with `query` (or `max_elements`) instead of a full read, and verify at checkpoints rather than after every action. Check what actually came back: permission, backing-store, or surface-identity failures can leave usable tree data without an image. `screenshot_error` and `screenshot_frame_valid:false` are not empty-tree signals.
39
39
 
40
- Prefer `structuredContent.elements` in MCP (the CLI prints structured fields directly) over parsing `tree_markdown`. Rows may contain `element_token`, role, label, value, actions, parent, depth, enabled/selected state, `frame` (screen coordinates, the space of `scope:"desktop"` actions), and `screenshot_frame` (pixels of the screenshot in the same response, the space of window-local pointer `x`/`y`). Missing fields are unknown.
40
+ By default the tree comes back **once**, as compact `tree_markdown` (`tree_format:"markdown"`). A row `[N]` is addressed with `element_token` `<snapshot_id>:N`; the `snapshot_id` is in the response header and in `structuredContent`. Use `tree_format:"elements"` for the structured `elements` array when you need explicit tokens, frames, parents, or values, or `"both"` for the old double payload (about twice the size). `full_output:true` restores the previous full response, including the platform's walk limits. In `elements`, rows may contain `element_token`, role, label, value, actions, parent, depth, enabled/selected state, `frame` (screen coordinates, the space of `scope:"desktop"` actions), and `screenshot_frame` (pixels of the screenshot in the same response, the space of window-local pointer `x`/`y`). Missing fields are unknown.
41
41
 
42
42
  - Use `query` to project matching rows plus ancestors without renumbering their indices.
43
- - Use `max_elements` / `max_depth` to bound the walk, and compare returned/total counts. Truncation does not prove absence.
44
- - Use `include_screenshot:false` only when tree-only observation is enough; it cannot ground a pixel action.
45
- - Where advertised, `include_accessibility_tree:false` requests capture without a tree walk. Check the installed schema first.
43
+ - A read walks at most 250 nodes by default. When it stops there the response says `Tree truncated at max_elements=…` (`truncated:true`, `truncation_hint`). Pass a larger `max_elements`, or narrow with `query` / `max_depth`. Truncation does not prove absence.
44
+ - After the first read, pass `since:"latest"` (or `since:<snapshot_id>`) to get only what changed. `latest` is this window's most recent read with the same `query`, `max_elements` and `max_depth`. You get: `+` added, `~` changed, `-` removed rows (removed ids are the old snapshot's), or `no change since …; focused element is …` (focus is reported on macOS only). The response carries a new `snapshot_id`: use it in tokens (`<new id>:N`). Rows not listed keep their `[N]` unless a `reindexed:` line says otherwise. An unknown, expired, other-window, or differently-scoped `since` returns a full read, and `since_status` says why. A diff reads the rendered tree rows only; it does not prove the screenshot is unchanged.
45
+ - `_note`, `background_input`, and (Linux) `frame_note` are omitted unless `verbose:true`; a degraded snapshot keeps `background_input`.
46
+ - `include_screenshot:false` returns the tree only. On macOS the window keeps the pixel frame of its last screenshot while its size is unchanged, so `x,y` read off that screenshot still work; elsewhere, and after a resize, take a new screenshot before a pixel action.
47
+ - Where advertised, `include_accessibility_tree:false` requests capture without a tree walk. It keeps the current snapshot's rows and tokens valid. `display_only:true` with it is for live previews: it also leaves your pixel frame alone, and its image cannot ground actions. Use it when you only need to look; do not use `max_elements:1` or a `query` that matches nothing for that, because those replace the snapshot with an almost empty one.
48
+ - Row numbers belong to one read. A narrower re-read (smaller `max_elements`, `max_depth`, another `query`) can stop before a row you saw earlier, and `<new snapshot_id>:N` for that row is then out of range. Use rows printed in the read whose `snapshot_id` you use.
46
49
  - `capture_mode` is deprecated and ignored. Do not change configuration to repair a sparse tree.
47
50
  - Use `screenshot_out_file` to save a PNG instead of inlining it, then actually read the image. Use an absolute, run-scoped output path.
48
51
 
@@ -50,7 +53,7 @@ The accessibility model may lag or disagree with rendered state: Electron text s
50
53
 
51
54
  ## Act once
52
55
 
53
- Use an opaque `element_token` from the latest snapshot of the intended window. Do not derive or edit tokens. A later snapshot can invalidate a pending action, including when another agent observes the same window.
56
+ Use the `element_token` `<snapshot_id>:N` for row `[N]` of the latest snapshot of the intended window, for example `"s0000002a:11"`. A bare `"11"` is accepted only together with `window_id` and then means row 11 of that window's current snapshot. Do not mix a row number from one read with the `snapshot_id` of another. A later snapshot can invalidate a pending action, including when another agent observes the same window.
54
57
 
55
58
  Example CLI window action, with IDs and token replaced from the preceding response:
56
59
 
@@ -66,7 +69,7 @@ cua-driver click '{"target":{"kind":"window","pid":844,"window_id":10725},"eleme
66
69
  | Focus a visible field and type | `type_text` with `x,y,text`, when that form is advertised |
67
70
  | Replace an exposed native value | `set_value` with `pid,element_token,value` (its own schema, no `target`) |
68
71
  | Send a key or combination | `press_key` with `key`, or `hotkey` with `keys` |
69
- | Scroll / drag | Inspect `describe scroll` / `describe drag`; units and supported shapes matter |
72
+ | Scroll / drag | `scroll` takes `direction` and `amount` (wheel notches); signed `dx`/`dy` is converted (positive `dy` is down; up to 50 is notches, more is pixels at 100 px per notch). Inspect `describe drag` |
70
73
 
71
74
  Text insertion and value replacement are different intents. Setting a field does not prove a form submission, navigation, or rename committed. Open a collapsed search/input control and reobserve before typing into it; one focus-click may not both open and focus it. Inspect the existing value/selection before replacing content.
72
75
 
@@ -74,13 +77,48 @@ If a text action returns `unverifiable`, take a fresh snapshot before retrying.
74
77
 
75
78
  Keep `delivery_mode:"background"` as the default for window input. The route may use accessibility hit-testing even when addressed by pixels: pixel coordinates do not promise physical pointer delivery. Read the returned `route` instead of inferring it from the tool name.
76
79
 
80
+ ## Batch known actions
81
+
82
+ `run_steps` is the default way to act. When the next action or actions are decided, send them as one `run_steps` call with `observe:{}` instead of one call per action followed by a read. Even a single step pays off: the action and the look at its result become one call. The batch runs the same tools in order, stops at the first failure, and returns per-step status plus at most one bounded observation. Typical fit: fill several fields, then press a button, then read the result. Do not batch across a point where the answer decides the next step, or when a step reshuffles the window and invalidates element tokens used by later steps (use pixel targets after it, or split the batch there).
83
+
84
+ ```bash
85
+ cua-driver run_steps '{"session":"run-1","steps":[
86
+ {"tool":"set_value","args":{"pid":844,"element_token":"s0000002a:14","value":"Ada"}},
87
+ {"tool":"set_value","args":{"pid":844,"element_token":"s0000002a:15","value":"Lovelace"}},
88
+ {"tool":"click","args":{"target":{"kind":"window","pid":844,"window_id":10725},"element_token":"s0000002a:21"}},
89
+ {"tool":"press_key","args":{"pid":844,"key":"return"}}
90
+ ],"delay_ms":100,"observe":{}}'
91
+ ```
92
+
93
+ - `tool` is one of `click`, `double_click`, `right_click`, `set_value`, `type_text`, `press_key`, `hotkey`, `scroll`, `drag`, `move_cursor` (`press` and `type` are accepted for `press_key` and `type_text`, `triple_click` is a click with `count:3`, and a key name such as `down` as the tool is a `press_key`); `args` are exactly that tool's arguments. Observation tools do not run inside a batch: a read between actions is skipped (it could not change the later steps), a `get_window_state` as the last step is the observation (merged with `observe`), and a trailing `zoom` runs after it. Run `describe run_steps` and `describe <tool>` for schemas. Up to 64 steps. `delivery_mode` on `run_steps` is the default for steps that set none.
94
+ - Name the window once: `pid` and `window_id` (or `app`/`window`) on `run_steps` itself are the default for every step that names none.
95
+ - A step whose `element_token` went stale because an earlier step re-rendered the window is retried once on the element with the same role and label in a fresh read, when exactly one matches; the step's line says so.
96
+ - Every step is validated before the first runs, so a malformed step changes nothing. Each step then passes the same session, permission, capability-manifest and approval checks as a direct call; a batch grants nothing a single call lacks, and a refused step ends the batch like any other failure.
97
+ - A batch has one session. Set `session` on `run_steps`; a step may repeat it but not name another.
98
+ - `observe` is optional and reads once, after the last executed step (also after a failure). Pass `true` or `get_window_state` arguments; `pid`/`window_id` come from the last step that names both. Defaults: `since:"latest"` (only what changed since your last read of that window with the same view; a full read if there is none), `include_screenshot:false`, `max_elements:250`. Pass `include_screenshot:true` to see the window, or `since:null` for a full read. Omit `observe` to read nothing.
99
+ - Read `steps[].ok` and `failed_step`. Steps before a failure did run and are not rolled back; steps after it did not. Observe before repairing, as for a single `unverifiable` action. When a step fails because another window of the app holds focus (a dialog opened mid-batch), the end read is of that window, even without `observe`; when a pixel step names a window with no screenshot yet, the end read includes its screenshot. A step refused because its app ignores background input (Electron, LibreOffice) says how to re-send it; set `foreground_fallback:true` on `run_steps` to retry such steps once in the foreground (the window is activated and the pointer may move).
100
+
101
+ ## Script a flow
102
+
103
+ Use `run_script` when the flow needs logic between actions: a loop over rows, a branch on what the app shows, a retry, or a value read in one window and typed into another. The script is JavaScript, the body of an async function; it runs in ONE call and only its `return` value, console output and a per-call log come back. Use `run_steps` for a fixed list of steps you have already decided, and plain calls when the next step needs your judgement.
104
+
105
+ ```bash
106
+ cua-driver run_script '{"session":"run-1","script":"const app = await cua.getApp(\"Calculator\"); for (const key of [\"7\", \"multiply\", \"6\", \"equals\"]) await app.click({role: \"button\", name: key}); const [display] = await app.query({role: \"statictext\"}); return display.value;"}'
107
+ ```
108
+
109
+ - The only API is `cua`: `getApp`, `launch`, `listApps`, `listWindows`, `sleep`, and on an app `getState`, `query`, `waitFor`, `verify` and the actions (`click`, `typeText`, `pressKey`, `setValue`, `scroll`, `drag`, ...). A target is `{role, name, nth?}` (found fresh, waits up to 3 s), an `element_token`, or `[x, y]` window pixels. `describe run_script` lists the full API.
110
+ - A failed driver call throws; catch it to recover. An uncaught error names the script line, the call and the reason.
111
+ - Every driver call passes the same session, permission, capability-manifest and approval checks as a direct call, so a script grants nothing the same direct calls lack. It cannot call `run_steps`, `run_script`, `set_config` or tools outside its allowlist.
112
+ - Sandbox: QuickJS with no filesystem, network, process, timers or environment. Limits: `timeout_ms` wall time (default 30000, max 120000), `max_calls` driver calls (default 100, max 500), a 32 MiB heap, a bounded stack, and a 64 KiB script. The script stops when the request is cancelled.
113
+ - `run_script` is on by default. Whoever runs the driver can turn it off with `CUA_DRIVER_DISABLE_RUN_SCRIPT=1` or `"disable_run_script": true` in `~/.cua-driver/config.json` (the older `CUA_DRIVER_EXPERIMENTAL_SCRIPT=0` and `"experimental_script": false` also work). `set_config` cannot change it. When it is off, it is not in `tools/list`.
114
+
77
115
  ## Pixel coordinates
78
116
 
79
117
  Ground window actions on the PNG from that exact `get_window_state`; ground desktop actions on `get_desktop_state`. Origins are top-left, increasing downward. The driver handles its own window capture scaling; do not add window offsets to window-local input.
80
118
 
81
119
  The harness may downsample the displayed preview independently of the returned PNG. Use the returned dimensions and the original file. If measuring on a resized preview, account for its exact scale in both axes; do not assume the preview is native resolution. Do not guess from accessibility frames or another app's geometry.
82
120
 
83
- After movement, resize, navigation, or a competing desktop interaction, reobserve. When using `zoom`, read its schema and preserve the `from_zoom` mapping on the supported follow-up action. A manually cropped image requires its crop offset; an untracked crop is not an action coordinate source.
121
+ After movement, resize, navigation, or a competing desktop interaction, reobserve. `zoom` takes a region as corners, `x1,y1` (top-left) to `x2,y2` (bottom-right); `x,y,width,height` is converted. Preserve the `from_zoom` mapping on the supported follow-up action. A manually cropped image requires its crop offset; an untracked crop is not an action coordinate source.
84
122
 
85
123
  For tiny targets, inspect at full resolution or annotate a copy without changing its dimensions. Keep the raw evidence unmodified. `debug_image_out`, where advertised, captures an action diagnostic; it is not a pre-action approval step.
86
124