@mirasim/computer-use 0.0.0-stage → 0.1.0-rc.3

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 (341) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +16 -0
  3. package/README.md +154 -2
  4. package/THIRD_PARTY_NOTICES +488 -0
  5. package/bin/mirasim-cu.mjs +30 -0
  6. package/dist/entries/cli.d.ts +2 -0
  7. package/dist/entries/cli.js +32 -0
  8. package/dist/entries/commands/approvals.d.ts +1 -0
  9. package/dist/entries/commands/approvals.js +17 -0
  10. package/dist/entries/commands/bridge-host.d.ts +1 -0
  11. package/dist/entries/commands/bridge-host.js +7 -0
  12. package/dist/entries/commands/bridge.d.ts +1 -0
  13. package/dist/entries/commands/bridge.js +16 -0
  14. package/dist/entries/commands/browser-state.d.ts +1 -0
  15. package/dist/entries/commands/browser-state.js +8 -0
  16. package/dist/entries/commands/capture-server.d.ts +1 -0
  17. package/dist/entries/commands/capture-server.js +5 -0
  18. package/dist/entries/commands/help.d.ts +2 -0
  19. package/dist/entries/commands/help.js +1 -0
  20. package/dist/entries/commands/native-host-options.d.ts +32 -0
  21. package/dist/entries/commands/native-host-options.js +7 -0
  22. package/dist/entries/commands/setup-macos.d.ts +1 -0
  23. package/dist/entries/commands/setup-macos.js +4 -0
  24. package/dist/entries/commands/setup.d.ts +1 -0
  25. package/dist/entries/commands/setup.js +15 -0
  26. package/dist/entries/mcp.d.ts +1 -0
  27. package/dist/entries/mcp.js +6 -0
  28. package/dist/entries/version.d.ts +1 -0
  29. package/dist/entries/version.js +1 -0
  30. package/dist/index.d.ts +11 -0
  31. package/dist/index.js +1 -0
  32. package/dist/platform/browser/acquire.d.ts +15 -0
  33. package/dist/platform/browser/acquire.js +1 -0
  34. package/dist/platform/browser/bridge/acquire-bridge.d.ts +7 -0
  35. package/dist/platform/browser/bridge/acquire-bridge.js +3 -0
  36. package/dist/platform/browser/bridge/client.d.ts +69 -0
  37. package/dist/platform/browser/bridge/client.js +1 -0
  38. package/dist/platform/browser/bridge/extension-channel.d.ts +12 -0
  39. package/dist/platform/browser/bridge/extension-channel.js +1 -0
  40. package/dist/platform/browser/bridge/extension-id.d.ts +8 -0
  41. package/dist/platform/browser/bridge/extension-id.js +1 -0
  42. package/dist/platform/browser/bridge/extension-updates.d.ts +37 -0
  43. package/dist/platform/browser/bridge/extension-updates.js +1 -0
  44. package/dist/platform/browser/bridge/model-commands.d.ts +20 -0
  45. package/dist/platform/browser/bridge/model-commands.js +1 -0
  46. package/dist/platform/browser/bridge/model-core.d.ts +59 -0
  47. package/dist/platform/browser/bridge/model-core.js +1 -0
  48. package/dist/platform/browser/bridge/model.d.ts +4 -0
  49. package/dist/platform/browser/bridge/model.js +1 -0
  50. package/dist/platform/browser/bridge/native-frames.d.ts +9 -0
  51. package/dist/platform/browser/bridge/native-frames.js +1 -0
  52. package/dist/platform/browser/bridge/native-host.d.ts +24 -0
  53. package/dist/platform/browser/bridge/native-host.js +5 -0
  54. package/dist/platform/browser/bridge/runtime-contexts.d.ts +12 -0
  55. package/dist/platform/browser/bridge/runtime-contexts.js +1 -0
  56. package/dist/platform/browser/bridge/server-core.d.ts +66 -0
  57. package/dist/platform/browser/bridge/server-core.js +1 -0
  58. package/dist/platform/browser/bridge/server.d.ts +13 -0
  59. package/dist/platform/browser/bridge/server.js +1 -0
  60. package/dist/platform/browser/bridge-tabs.d.ts +22 -0
  61. package/dist/platform/browser/bridge-tabs.js +1 -0
  62. package/dist/platform/browser/cdp-connection.d.ts +3 -0
  63. package/dist/platform/browser/cdp-connection.js +1 -0
  64. package/dist/platform/browser/chrome-binary.d.ts +6 -0
  65. package/dist/platform/browser/chrome-binary.js +1 -0
  66. package/dist/platform/browser/config.d.ts +29 -0
  67. package/dist/platform/browser/config.js +1 -0
  68. package/dist/platform/browser/devtools-port.d.ts +3 -0
  69. package/dist/platform/browser/devtools-port.js +2 -0
  70. package/dist/platform/browser/downloads.d.ts +22 -0
  71. package/dist/platform/browser/downloads.js +1 -0
  72. package/dist/platform/browser/driver-connect.d.ts +13 -0
  73. package/dist/platform/browser/driver-connect.js +1 -0
  74. package/dist/platform/browser/driver-core.d.ts +50 -0
  75. package/dist/platform/browser/driver-core.js +1 -0
  76. package/dist/platform/browser/driver-tabs.d.ts +16 -0
  77. package/dist/platform/browser/driver-tabs.js +1 -0
  78. package/dist/platform/browser/driver-verbs.d.ts +13 -0
  79. package/dist/platform/browser/driver-verbs.js +2 -0
  80. package/dist/platform/browser/driver.d.ts +38 -0
  81. package/dist/platform/browser/driver.js +3 -0
  82. package/dist/platform/browser/focus-guard.d.ts +2 -0
  83. package/dist/platform/browser/focus-guard.js +1 -0
  84. package/dist/platform/browser/journal.d.ts +21 -0
  85. package/dist/platform/browser/journal.js +2 -0
  86. package/dist/platform/browser/keep-in-tab.d.ts +1 -0
  87. package/dist/platform/browser/keep-in-tab.js +1 -0
  88. package/dist/platform/browser/messages.d.ts +25 -0
  89. package/dist/platform/browser/messages.js +8 -0
  90. package/dist/platform/browser/navigate.d.ts +4 -0
  91. package/dist/platform/browser/navigate.js +2 -0
  92. package/dist/platform/browser/occupancy.d.ts +2 -0
  93. package/dist/platform/browser/occupancy.js +2 -0
  94. package/dist/platform/browser/overlay-guard.d.ts +3 -0
  95. package/dist/platform/browser/overlay-guard.js +1 -0
  96. package/dist/platform/browser/save-picker.d.ts +2 -0
  97. package/dist/platform/browser/save-picker.js +1 -0
  98. package/dist/platform/browser/session-files.d.ts +11 -0
  99. package/dist/platform/browser/session-files.js +1 -0
  100. package/dist/platform/browser/state.d.ts +25 -0
  101. package/dist/platform/browser/state.js +2 -0
  102. package/dist/platform/browser/tabs.d.ts +15 -0
  103. package/dist/platform/browser/tabs.js +2 -0
  104. package/dist/platform/browser/window-placement.d.ts +8 -0
  105. package/dist/platform/browser/window-placement.js +1 -0
  106. package/dist/platform/chromium/actions-value.d.ts +11 -0
  107. package/dist/platform/chromium/actions-value.js +5 -0
  108. package/dist/platform/chromium/actions.d.ts +20 -0
  109. package/dist/platform/chromium/actions.js +2 -0
  110. package/dist/platform/chromium/checkable-target.d.ts +1 -0
  111. package/dist/platform/chromium/checkable-target.js +1 -0
  112. package/dist/platform/chromium/collect-viewport.d.ts +1 -0
  113. package/dist/platform/chromium/collect-viewport.js +1 -0
  114. package/dist/platform/chromium/collect.d.ts +11 -0
  115. package/dist/platform/chromium/collect.js +1 -0
  116. package/dist/platform/chromium/coverer-label.d.ts +8 -0
  117. package/dist/platform/chromium/coverer-label.js +1 -0
  118. package/dist/platform/chromium/frames.d.ts +2 -0
  119. package/dist/platform/chromium/frames.js +1 -0
  120. package/dist/platform/chromium/headings.d.ts +1 -0
  121. package/dist/platform/chromium/headings.js +1 -0
  122. package/dist/platform/chromium/locate.d.ts +2 -0
  123. package/dist/platform/chromium/locate.js +1 -0
  124. package/dist/platform/chromium/page-acts.d.ts +9 -0
  125. package/dist/platform/chromium/page-acts.js +1 -0
  126. package/dist/platform/chromium/page-base.d.ts +33 -0
  127. package/dist/platform/chromium/page-base.js +1 -0
  128. package/dist/platform/chromium/page-driver.d.ts +18 -0
  129. package/dist/platform/chromium/page-driver.js +2 -0
  130. package/dist/platform/chromium/page-refs.d.ts +4 -0
  131. package/dist/platform/chromium/page-refs.js +1 -0
  132. package/dist/platform/chromium/page-text.d.ts +15 -0
  133. package/dist/platform/chromium/page-text.js +1 -0
  134. package/dist/platform/chromium/page-waits.d.ts +7 -0
  135. package/dist/platform/chromium/page-waits.js +1 -0
  136. package/dist/platform/chromium/preview-stream.d.ts +37 -0
  137. package/dist/platform/chromium/preview-stream.js +1 -0
  138. package/dist/platform/chromium/preview.d.ts +3 -0
  139. package/dist/platform/chromium/preview.js +1 -0
  140. package/dist/platform/chromium/read-page-script.d.ts +1 -0
  141. package/dist/platform/chromium/read-page-script.js +1 -0
  142. package/dist/platform/chromium/read-page.d.ts +11 -0
  143. package/dist/platform/chromium/read-page.js +6 -0
  144. package/dist/platform/chromium/run-js.d.ts +4 -0
  145. package/dist/platform/chromium/run-js.js +2 -0
  146. package/dist/platform/chromium/scroll-regions.d.ts +1 -0
  147. package/dist/platform/chromium/scroll-regions.js +1 -0
  148. package/dist/platform/chromium/scroll-scripts.d.ts +3 -0
  149. package/dist/platform/chromium/scroll-scripts.js +1 -0
  150. package/dist/platform/chromium/scroll.d.ts +18 -0
  151. package/dist/platform/chromium/scroll.js +5 -0
  152. package/dist/platform/chromium/select-text.d.ts +4 -0
  153. package/dist/platform/chromium/select-text.js +2 -0
  154. package/dist/platform/chromium/shot.d.ts +10 -0
  155. package/dist/platform/chromium/shot.js +2 -0
  156. package/dist/platform/chromium/unreached.d.ts +8 -0
  157. package/dist/platform/chromium/unreached.js +1 -0
  158. package/dist/platform/cli-command.d.ts +1 -0
  159. package/dist/platform/cli-command.js +1 -0
  160. package/dist/platform/driver-base.d.ts +83 -0
  161. package/dist/platform/driver-base.js +1 -0
  162. package/dist/platform/driver-diagnostics.d.ts +30 -0
  163. package/dist/platform/driver-diagnostics.js +4 -0
  164. package/dist/platform/driver-lines.d.ts +15 -0
  165. package/dist/platform/driver-lines.js +2 -0
  166. package/dist/platform/driver-narrate.d.ts +26 -0
  167. package/dist/platform/driver-narrate.js +3 -0
  168. package/dist/platform/driver-refs.d.ts +16 -0
  169. package/dist/platform/driver-refs.js +1 -0
  170. package/dist/platform/driver-render.d.ts +26 -0
  171. package/dist/platform/driver-render.js +4 -0
  172. package/dist/platform/driver-select.d.ts +31 -0
  173. package/dist/platform/driver-select.js +2 -0
  174. package/dist/platform/driver-settle.d.ts +22 -0
  175. package/dist/platform/driver-settle.js +4 -0
  176. package/dist/platform/driver-verdict.d.ts +11 -0
  177. package/dist/platform/driver-verdict.js +5 -0
  178. package/dist/platform/driver-wait.d.ts +9 -0
  179. package/dist/platform/driver-wait.js +1 -0
  180. package/dist/platform/driver-waits.d.ts +10 -0
  181. package/dist/platform/driver-waits.js +1 -0
  182. package/dist/platform/driver.d.ts +39 -0
  183. package/dist/platform/driver.js +15 -0
  184. package/dist/platform/electron/driver.d.ts +30 -0
  185. package/dist/platform/electron/driver.js +7 -0
  186. package/dist/platform/electron/preflight.d.ts +9 -0
  187. package/dist/platform/electron/preflight.js +1 -0
  188. package/dist/platform/electron/verbs.d.ts +3 -0
  189. package/dist/platform/electron/verbs.js +1 -0
  190. package/dist/platform/home.d.ts +2 -0
  191. package/dist/platform/home.js +1 -0
  192. package/dist/platform/macos/act-element.d.ts +27 -0
  193. package/dist/platform/macos/act-element.js +1 -0
  194. package/dist/platform/macos/app-names.d.ts +2 -0
  195. package/dist/platform/macos/app-names.js +2 -0
  196. package/dist/platform/macos/app-notes.d.ts +2 -0
  197. package/dist/platform/macos/app-notes.js +86 -0
  198. package/dist/platform/macos/approvals.d.ts +10 -0
  199. package/dist/platform/macos/approvals.js +2 -0
  200. package/dist/platform/macos/ax-client.d.ts +53 -0
  201. package/dist/platform/macos/ax-client.js +3 -0
  202. package/dist/platform/macos/ax-errors.d.ts +16 -0
  203. package/dist/platform/macos/ax-errors.js +1 -0
  204. package/dist/platform/macos/ax-shapes.d.ts +53 -0
  205. package/dist/platform/macos/ax-shapes.js +1 -0
  206. package/dist/platform/macos/capture-client.d.ts +11 -0
  207. package/dist/platform/macos/capture-client.js +2 -0
  208. package/dist/platform/macos/click-verb.d.ts +4 -0
  209. package/dist/platform/macos/click-verb.js +1 -0
  210. package/dist/platform/macos/driver-apps.d.ts +59 -0
  211. package/dist/platform/macos/driver-apps.js +2 -0
  212. package/dist/platform/macos/driver-read.d.ts +47 -0
  213. package/dist/platform/macos/driver-read.js +1 -0
  214. package/dist/platform/macos/driver-targets.d.ts +9 -0
  215. package/dist/platform/macos/driver-targets.js +1 -0
  216. package/dist/platform/macos/driver.d.ts +65 -0
  217. package/dist/platform/macos/driver.js +2 -0
  218. package/dist/platform/macos/element-lines.d.ts +12 -0
  219. package/dist/platform/macos/element-lines.js +1 -0
  220. package/dist/platform/macos/element-shapes.d.ts +54 -0
  221. package/dist/platform/macos/element-shapes.js +0 -0
  222. package/dist/platform/macos/element-text.d.ts +3 -0
  223. package/dist/platform/macos/element-text.js +2 -0
  224. package/dist/platform/macos/elements.d.ts +26 -0
  225. package/dist/platform/macos/elements.js +1 -0
  226. package/dist/platform/macos/extra-verbs.d.ts +2 -0
  227. package/dist/platform/macos/extra-verbs.js +1 -0
  228. package/dist/platform/macos/interaction-notes.d.ts +2 -0
  229. package/dist/platform/macos/interaction-notes.js +1 -0
  230. package/dist/platform/macos/interaction-policy.d.ts +5 -0
  231. package/dist/platform/macos/interaction-policy.js +1 -0
  232. package/dist/platform/macos/menu-commands.d.ts +22 -0
  233. package/dist/platform/macos/menu-commands.js +5 -0
  234. package/dist/platform/macos/mouse.d.ts +23 -0
  235. package/dist/platform/macos/mouse.js +1 -0
  236. package/dist/platform/macos/narrate.d.ts +20 -0
  237. package/dist/platform/macos/narrate.js +1 -0
  238. package/dist/platform/macos/permissions.d.ts +5 -0
  239. package/dist/platform/macos/permissions.js +1 -0
  240. package/dist/platform/macos/press-rungs.d.ts +49 -0
  241. package/dist/platform/macos/press-rungs.js +5 -0
  242. package/dist/platform/macos/preview-stream.d.ts +15 -0
  243. package/dist/platform/macos/preview-stream.js +1 -0
  244. package/dist/platform/macos/prompt-fragment.d.ts +1 -0
  245. package/dist/platform/macos/prompt-fragment.js +1 -0
  246. package/dist/platform/macos/scroll-pages.d.ts +1 -0
  247. package/dist/platform/macos/scroll-pages.js +1 -0
  248. package/dist/platform/macos/scroll-verb.d.ts +5 -0
  249. package/dist/platform/macos/scroll-verb.js +1 -0
  250. package/dist/platform/macos/select-text-verb.d.ts +8 -0
  251. package/dist/platform/macos/select-text-verb.js +1 -0
  252. package/dist/platform/macos/type-verb.d.ts +5 -0
  253. package/dist/platform/macos/type-verb.js +1 -0
  254. package/dist/platform/ownership.d.ts +20 -0
  255. package/dist/platform/ownership.js +1 -0
  256. package/dist/platform/platform-package.d.ts +1 -0
  257. package/dist/platform/platform-package.js +1 -0
  258. package/dist/platform/preview.d.ts +39 -0
  259. package/dist/platform/preview.js +1 -0
  260. package/dist/platform/profile.d.ts +20 -0
  261. package/dist/platform/profile.js +1 -0
  262. package/dist/platform/registry.d.ts +12 -0
  263. package/dist/platform/registry.js +1 -0
  264. package/dist/platform/tools/api-contract.d.ts +33 -0
  265. package/dist/platform/tools/api-contract.js +3 -0
  266. package/dist/platform/tools/api-expect.d.ts +3 -0
  267. package/dist/platform/tools/api-expect.js +1 -0
  268. package/dist/platform/tools/api-run.d.ts +6 -0
  269. package/dist/platform/tools/api-run.js +1 -0
  270. package/dist/platform/tools/api-target.d.ts +22 -0
  271. package/dist/platform/tools/api-target.js +1 -0
  272. package/dist/platform/tools/api-wait.d.ts +3 -0
  273. package/dist/platform/tools/api-wait.js +1 -0
  274. package/dist/platform/tools/api.d.ts +7 -0
  275. package/dist/platform/tools/api.js +1 -0
  276. package/dist/platform/tools/find-target.d.ts +54 -0
  277. package/dist/platform/tools/find-target.js +1 -0
  278. package/dist/platform/tools/index.d.ts +8 -0
  279. package/dist/platform/tools/index.js +1 -0
  280. package/dist/platform/tools/normalize.d.ts +2 -0
  281. package/dist/platform/tools/normalize.js +1 -0
  282. package/dist/platform/tools/repl-budget.d.ts +8 -0
  283. package/dist/platform/tools/repl-budget.js +4 -0
  284. package/dist/platform/tools/repl-code.d.ts +1 -0
  285. package/dist/platform/tools/repl-code.js +4 -0
  286. package/dist/platform/tools/repl-docs.d.ts +5 -0
  287. package/dist/platform/tools/repl-docs.js +4 -0
  288. package/dist/platform/tools/repl-state.d.ts +21 -0
  289. package/dist/platform/tools/repl-state.js +0 -0
  290. package/dist/platform/tools/repl.d.ts +8 -0
  291. package/dist/platform/tools/repl.js +19 -0
  292. package/dist/platform/tools/shot-file.d.ts +7 -0
  293. package/dist/platform/tools/shot-file.js +1 -0
  294. package/dist/platform/tools/validate.d.ts +2 -0
  295. package/dist/platform/tools/validate.js +1 -0
  296. package/dist/platform/types.d.ts +220 -0
  297. package/dist/platform/types.js +1 -0
  298. package/dist/platform/windows/app-gate.d.ts +24 -0
  299. package/dist/platform/windows/app-gate.js +2 -0
  300. package/dist/platform/windows/background-verb.d.ts +13 -0
  301. package/dist/platform/windows/background-verb.js +1 -0
  302. package/dist/platform/windows/dates.d.ts +2 -0
  303. package/dist/platform/windows/dates.js +1 -0
  304. package/dist/platform/windows/driver-apps.d.ts +9 -0
  305. package/dist/platform/windows/driver-apps.js +1 -0
  306. package/dist/platform/windows/driver-read.d.ts +12 -0
  307. package/dist/platform/windows/driver-read.js +3 -0
  308. package/dist/platform/windows/driver-targets.d.ts +58 -0
  309. package/dist/platform/windows/driver-targets.js +1 -0
  310. package/dist/platform/windows/driver-yield.d.ts +10 -0
  311. package/dist/platform/windows/driver-yield.js +1 -0
  312. package/dist/platform/windows/driver.d.ts +39 -0
  313. package/dist/platform/windows/driver.js +2 -0
  314. package/dist/platform/windows/elements.d.ts +66 -0
  315. package/dist/platform/windows/elements.js +2 -0
  316. package/dist/platform/windows/launch.d.ts +23 -0
  317. package/dist/platform/windows/launch.js +1 -0
  318. package/dist/platform/windows/menu-verb.d.ts +7 -0
  319. package/dist/platform/windows/menu-verb.js +6 -0
  320. package/dist/platform/windows/prompt-fragment.d.ts +2 -0
  321. package/dist/platform/windows/prompt-fragment.js +1 -0
  322. package/dist/platform/windows/text-verbs.d.ts +10 -0
  323. package/dist/platform/windows/text-verbs.js +5 -0
  324. package/dist/platform/windows/uia-client.d.ts +32 -0
  325. package/dist/platform/windows/uia-client.js +3 -0
  326. package/dist/platform/windows/verbs.d.ts +2 -0
  327. package/dist/platform/windows/verbs.js +1 -0
  328. package/extension/background.js +280 -0
  329. package/extension/connect.html +27 -0
  330. package/extension/connect.js +28 -0
  331. package/extension/group.js +283 -0
  332. package/extension/icons/icon-128.png +0 -0
  333. package/extension/icons/icon-16.png +0 -0
  334. package/extension/icons/icon-32.png +0 -0
  335. package/extension/icons/icon-48.png +0 -0
  336. package/extension/manifest.json +36 -0
  337. package/extension/relay.js +361 -0
  338. package/extension/runtime-info.js +9 -0
  339. package/extension/transport.js +81 -0
  340. package/package.json +76 -4
  341. package/scripts/extension.mjs +111 -0
@@ -0,0 +1,11 @@
1
+ import type { ToolResult } from "../types.ts";
2
+ import type { Page } from "playwright-core";
3
+ export interface PageReadState {
4
+ lastFullUrl?: string;
5
+ lastFullCanon?: string;
6
+ }
7
+ export declare function readPage(page: Page, opts: {
8
+ ref?: string;
9
+ depth?: number;
10
+ node?: string;
11
+ }, state: PageReadState, markRefs: () => Promise<void>): Promise<ToolResult>;
@@ -0,0 +1,6 @@
1
+ import{text as u}from"../types.js";import{pageFn as R}from"./collect.js";import{unreachedFacts as C}from"./unreached.js";import{TREE_JS as j}from"./read-page-script.js";const $=8e3,y=3e3,A=[6,4,3,2,1],m=2e3;function T(e){const{vp:s,covered:t,...i}=e;return{...i,kids:e.kids?.map(T)}}function k(e,s,t){const i=" ".repeat(s);e.fold&&(t.folds++,t.lines.push(`${i}\u22EF folded ~${e.fold.items} items (~${e.fold.chars} chars) (node ${e.fold.id})`));const r=[];e.covered&&r.push("(covered)"),e.vp==="out"&&r.push("(off-screen)"),e.vp==="part"&&r.push("(spans viewport edge)"),e.clipped&&(t.clips++,r.push("(clipped by the page \u2014 starts mid-sentence)")),e.cut&&(t.cuts++,r.push(`(cut at ${y} chars \u2014 the page has the full text)`));const n=[];if(e.ref?n.push(["button","link","menuitem","tab"].includes(e.role??"")?`[${e.ref}]`:`[${e.ref} ${e.role}]`):e.tag&&n.push(e.tag),e.name&&n.push(e.ref||e.tag?`"${e.name}"`:`\u300C${e.name}\u300D`),e.text&&n.push(e.text),e.state&&n.push(`(${e.state})`),n.push(...r),n.length&&!e.fold){const c=i+n.join(" ");t.lines.push(c)}t.chars=t.lines.reduce((c,f)=>c+f.length+1,0);for(const c of e.kids??[])k(c,s+(n.length&&!e.fold?1:0),t)}function E(e){const s={lines:[],folds:0,cuts:0,clips:0,chars:0};return k(e,0,s),s.chars=s.lines.reduce((t,i)=>t+i.length+1,0),s}function F(e,s,t,i){const r=[];t.folds&&r.push(`${t.folds} folded branch${t.folds===1?"":"es"} \u2014 pass node:'nK' or a larger depth to go deeper`),t.cuts&&r.push(`${t.cuts} text block${t.cuts===1?"":"s"} cut by the reader (the page has the full text)`),t.clips&&r.push(`${t.clips} snippet${t.clips===1?"":"s"} clipped by the page itself \u2014 the full text only exists behind the item`),r.push(..._(i,t.lines.length>0));const n=r.length?r.join("; "):"complete: nothing folded, nothing cut, nothing left below";return`page read: ${e} \xB7 depth ${s>0?s:"full"} \u2014 ${n}`}function _(e,s){const t=[],i=e.moreScreens??0;return i>.5&&t.push(`~${i} more screens below have not been scrolled into view \u2014 content there may not have loaded yet, so do not conclude something is absent`),t.concat(C(e.un??{canvases:0,frames:0,folded:0,viewerType:""},s))}async function O(e,s,t,i){await i();const r=s.node?`drill ${s.node}`:s.ref?`subtree ${s.ref}`:"whole page",n=async a=>await e.evaluate(R(j),{ref:s.ref,node:s.node,depth:a,cap:y}),c=typeof s.depth=="number"&&s.depth>0;let f=c?s.depth:0,o=await n(f);if(o.error||!o.tree)return u(`page read failed: ${o.error??"empty tree"}${s.node?" \u2014 drill handles die on re-render; re-read with ref or the whole page":""}`);let l=E(o.tree);if(l.chars>$){let a=0,d=null,p=null;for(const w of A.filter(h=>!c||h<f)){const h=await n(w);if(h.error||!h.tree)continue;const x=E(h.tree);if(x.chars<=$){a=w,d=x,p=h;break}}const g=_(o,l.lines.length>0),b=g.length?`
2
+ and either way: ${g.join("; ")}`:"";if(c)return u(`the tree is ~${l.chars} chars at depth ${f} (limit ${$}); `+(a?`depth ${a} fits \u2014 or pass ref to read one block`:"no depth fits this scope \u2014 pass ref to read one section")+b);if(!a||!d||!p)return u(`the page is too wide to read whole (~${l.chars} chars even shallow) \u2014 pass ref to read one section`+b);f=a,l=d,o=p}if(!s.ref&&!s.node){const a=JSON.stringify(T(o.tree)),d=t.lastFullUrl===e.url()&&t.lastFullCanon===a;if(t.lastFullUrl=e.url(),t.lastFullCanon=a,d&&!c)return u(`${F(r,f,l,o)}
3
+ identical to your previous page read \u2014 operate the page, read a subtree (ref:), or name a depth to have it rendered again`)}const S=F(r,f,l,o);let v=l.lines.join(`
4
+ `);if(typeof o.exact=="string"){const a=o.exact.length>m,d=a?o.exact.slice(0,m):o.exact,p=a?` (first ${m} of ${o.exact.length} chars)`:"",g=o.exact!==o.exact.trim()?" \u2014 note the leading/trailing whitespace inside the quotes; it is part of the value":"";v+=`
5
+ exact value${p} (JSON string \u2014 copy the contents verbatim): ${JSON.stringify(d)}${g}`}return u(`${S}
6
+ ${v}`,{scope:r,depth:f,chars:l.chars,folds:l.folds,moreScreens:o.moreScreens??0})}export{O as readPage};
@@ -0,0 +1,4 @@
1
+ import type { Page } from "playwright-core";
2
+ import type { ActResult } from "../types.ts";
3
+ export declare const RUN_JS_CODE_DESC: string;
4
+ export declare function performRunJs(page: () => Page, code: unknown): Promise<ActResult>;
@@ -0,0 +1,2 @@
1
+ const p=1e4;function c(t,n=2e3){return t.length<=n?t:`${t.slice(0,n)}\u2026 (+${t.length-n} chars)`}const u=200,f="JavaScript evaluated in the page, like typing into the DevTools console \u2014 an expression, or a function body using `return` (either form works). An expression returning a Promise is awaited. Only a JSON-safe value comes back \u2014 a string, number, array or plain object; a DOM node, a function or a circular structure does not survive, so map what you want out of it in the expression. READ, COMPUTE, DIAGNOSE with it: a whole table or list in one call, localStorage, the scroll position, a page variable, an API the page already exposes. To ACT \u2014 click, type, select \u2014 use the interaction verbs: element.click() from here skips the visibility and real-input checks and leaves no record that the page was changed. If you do change state with it, say so in your report.",h=/Illegal return statement/;async function g(t,n){const r=typeof n=="string"?n.trim():"";if(!r)return{ok:!1,note:"run_js needs code \u2014 the JavaScript to evaluate"};const i=Symbol("run_js-timeout");let o;const s=e=>Promise.race([t().evaluate(e),new Promise(a=>{o=setTimeout(()=>a(i),1e4)})]);try{let e;try{e=await s(r)}catch(l){if(!h.test(l.message||""))throw l;e=await s(("((async()=>{("+(r)+")})())"))}if(e===i)return{ok:!1,note:`run_js gave no result within ${1e4/1e3}s \u2014 the code may still be running in the page; observe to see its effect`};const a=e===void 0?"undefined":JSON.stringify(e)??String(e);return{ok:!0,value:e,note:`result: ${c(a,u)}`}}catch(e){return{ok:!0,note:`threw: ${c((e.message||String(e)).split(`
2
+ `)[0]??"")}`}}finally{o&&clearTimeout(o)}}export{f as RUN_JS_CODE_DESC,g as performRunJs};
@@ -0,0 +1 @@
1
+ export declare const SCROLL_REGION_JS: string;
@@ -0,0 +1 @@
1
+ const e=("((t=>{if(t===document.body||t===document.documentElement)return null;const e=getComputedStyle(t),l=t.getBoundingClientRect();if(e.visibility===\"hidden\"||e.display===\"none\"||l.width<80||l.height<80)return null;const n=/auto|scroll|overlay/.test(e.overflowY)&&t.scrollHeight>t.clientHeight+4,i=/auto|scroll|overlay/.test(e.overflowX)&&t.scrollWidth>t.clientWidth+4;if(!n&&!i)return null;const o=(t.getAttribute(\"aria-labelledby\")||\"\").split(/\\s+/).map(r=>document.getElementById(r)?.textContent||\"\").join(\" \").trim();return{name:(t.getAttribute(\"aria-label\")||o||t.id||\"Scrollable area\").trim().replace(/\\s+/g,\" \").slice(0,80),vertical:n,horizontal:i}}))");export{e as SCROLL_REGION_JS};
@@ -0,0 +1,3 @@
1
+ export declare const SCROLL_TARGET_JS: string;
2
+ export declare const SCROLL_MEASURE_JS: string;
3
+ export declare const SCROLL_JUMP_JS: string;
@@ -0,0 +1 @@
1
+ import{SCROLL_REGION_JS as t}from"./scroll-regions.js";import{PAGE_REFS_JS as o}from"./page-refs.js";const e=("((function r(t=document){const e=t.querySelector(\"[data-mfscroll]\");if(e)return e;for(const o of t.querySelectorAll(\"*\"))if(o.shadowRoot){const n=r(o.shadowRoot);if(n)return n}return null}))"),r=("((([axisH,refSel])=>{const refs=(__PAGE_REFS__)();const nodes=[];const visit=root=>{for(const el of root.querySelectorAll(\"*\")){nodes.push(el);if(el.shadowRoot)visit(el.shadowRoot)}};visit(document);for(const el of nodes)if(el.hasAttribute(\"data-mfscroll\"))el.removeAttribute(\"data-mfscroll\");const vw=innerWidth,vh=innerHeight;const de=document.scrollingElement||document.documentElement;const scrollRegion=(__SCROLL_REGION__);const modals=nodes.filter(el=>el.matches('[aria-modal=\"true\"], dialog:modal')&&el.getBoundingClientRect().width>0);const modal=modals[modals.length-1];const scrollable=el=>{const s=getComputedStyle(el);if(!/auto|scroll|overlay/.test(axisH?s.overflowX:s.overflowY))return false;return axisH?el.scrollWidth>el.clientWidth+4:el.scrollHeight>el.clientHeight+4};const docScrollable=axisH?de.scrollWidth>(de.clientWidth||vw)+4:de.scrollHeight>(de.clientHeight||vh)+4;const doc=()=>({kind:\"doc\",id:\"window\",name:\"window\",x:Math.round(vw/2),y:Math.round(vh/2),before:axisH?window.scrollX||0:window.scrollY||0,client:axisH?de.clientWidth||vw:de.clientHeight||vh,total:axisH?de.scrollWidth:de.scrollHeight});const mark=el=>{el.setAttribute(\"data-mfscroll\",\"1\");let id=el.getAttribute(\"data-mfsc\");if(!id){id=\"s\"+(window.__mfNextSc=(window.__mfNextSc||0)+1);el.setAttribute(\"data-mfsc\",id)}const r=el.getBoundingClientRect();const name=scrollRegion(el)?.name||el.getAttribute(\"aria-label\")||el.id||\"Scrollable area\";return{kind:\"el\",id,name,x:Math.round(Math.max(r.left,0)+(Math.min(r.right,vw)-Math.max(r.left,0))/2),y:Math.round(Math.max(r.top,0)+(Math.min(r.bottom,vh)-Math.max(r.top,0))/2),before:axisH?el.scrollLeft:el.scrollTop,client:axisH?el.clientWidth:el.clientHeight,total:axisH?el.scrollWidth:el.scrollHeight}};if(refSel===\"window\")return modal?{kind:\"none\",reason:\"modal\"}:doc();if(refSel){const start=refs.elements.get(refSel)?.deref();if(!start?.isConnected)return{kind:\"none\",reason:\"gone\"};if(modal&&!modal.contains(start))return{kind:\"none\",reason:\"modal\"};for(let n=start;n&&n!==document.body&&n!==de;n=n.parentElement||n.getRootNode().host){if(scrollable(n))return mark(n)}return docScrollable&&!modal?doc():{kind:\"none\",reason:modal?\"modal\":\"noscroll\"}}const visibleArea=el=>{const r=el.getBoundingClientRect();return{w:Math.min(r.right,vw)-Math.max(r.left,0),h:Math.min(r.bottom,vh)-Math.max(r.top,0)}};const inner=[];for(const el of nodes){if(!scrollable(el)||!scrollRegion(el))continue;if(modal&&!modal.contains(el))continue;const b=visibleArea(el);if(b.w<80||b.h<80)continue;const r=el.getBoundingClientRect();const root=el.getRootNode();const hit=(root.elementFromPoint?root:document).elementFromPoint(Math.max(0,r.left)+b.w/2,Math.max(0,r.top)+b.h/2);if(!hit||hit!==el&&!el.contains(hit))continue;inner.push(el)}if(inner.length+(docScrollable&&!modal?1:0)>1){const candidates=inner.map(el=>{const r=el.getBoundingClientRect();return{ref:refs.byElement.get(el)||null,name:scrollRegion(el).name,position:Math.round(r.left)+\",\"+Math.round(r.top)+\" \"+Math.round(r.width)+\"\\xD7\"+Math.round(r.height)}});if(docScrollable&&!modal)candidates.push({ref:\"window\",name:\"page\"});return{kind:\"ambiguous\",candidates}}return inner.length?mark(inner[0]):modal?{kind:\"none\",reason:\"modal\"}:doc()}))").replace("__SCROLL_REGION__",t).replace("__PAGE_REFS__",o),i=("((([axisH,isDoc])=>{const de=document.scrollingElement||document.documentElement;if(isDoc)return{lost:false,pos:axisH?window.scrollX||0:window.scrollY||0,client:axisH?de.clientWidth||innerWidth:de.clientHeight||innerHeight,total:axisH?de.scrollWidth:de.scrollHeight};const el=(__FIND_MARK__)();if(!el)return{lost:true,pos:0,client:1,total:0};return{lost:false,pos:axisH?el.scrollLeft:el.scrollTop,client:axisH?el.clientWidth:el.clientHeight,total:axisH?el.scrollWidth:el.scrollHeight}}))").replace("__FIND_MARK__",e),s=("((([toTop,isDoc])=>{const de=document.scrollingElement||document.documentElement;if(!isDoc){const el=(__FIND_MARK__)();if(!el)return{lost:true,before:0};const before2=el.scrollTop;el.scrollTop=toTop?0:el.scrollHeight;return{lost:false,before:before2}}const before=window.scrollY||0;window.scrollTo(0,toTop?0:de.scrollHeight);return{lost:false,before}}))").replace("__FIND_MARK__",e);export{s as SCROLL_JUMP_JS,i as SCROLL_MEASURE_JS,r as SCROLL_TARGET_JS};
@@ -0,0 +1,18 @@
1
+ import type { Page } from "playwright-core";
2
+ import type { ActResult } from "../types.ts";
3
+ export interface ScrollLedgerEntry {
4
+ maxScreen: number;
5
+ lastTotal: number;
6
+ scrollCount: number;
7
+ growCount: number;
8
+ }
9
+ export interface ScrollOutcome {
10
+ res: ActResult;
11
+ point: {
12
+ x: number;
13
+ y: number;
14
+ } | null;
15
+ moved: number | null;
16
+ }
17
+ export declare function performScroll(page: Page, a: Record<string, any>, ledger: Map<string, ScrollLedgerEntry>, contentRead?: boolean): Promise<ScrollOutcome>;
18
+ export declare function performScrollInto(page: Page, ref: string): Promise<ScrollOutcome>;
@@ -0,0 +1,5 @@
1
+ import{SCROLL_JUMP_JS as R,SCROLL_MEASURE_JS as N,SCROLL_TARGET_JS as V}from"./scroll-scripts.js";import{pageFn as M}from"./collect.js";import{readableFrames as j}from"./frames.js";import{refSelector as L}from"./page-refs.js";import{coverOf as D}from"./coverer-label.js";const C=350,A=1500,F=20,x=8,J=n=>`did not scroll \u2014 ${k(n)} is already at the end, or is not scrollable. Do not just repeat it: try ref-targeted scroll / to:'top'|'bottom' / a different container \u2014 or, if this is a list that loads more on reaching its end, wait({}) once and scroll again`,S=n=>new Promise(e=>setTimeout(e,n)),p=n=>({res:{ok:!1,note:n},point:null,moved:null}),k=n=>n.kind==="doc"?"the page":`\xAB${n.name}\xBB`;async function q(n,e,c,u=!1){const a=e.to==="top"||e.to==="bottom"?e.to:void 0,i=typeof e.direction=="string"?e.direction:"down",w=Math.min(Math.max(Math.trunc(Number(e.count))||1,1),F);if(a&&Number(e.count)>1)return p(`scroll takes to:'${a}' or count, not both \u2014 to:'${a}' already goes as far as the container scrolls; drop one`);const l=!a&&(i==="left"||i==="right"),d=a?a==="top":i==="up"||i==="left";let r=n.mainFrame(),f=null;if(typeof e.ref=="string"&&e.ref)if(e.ref==="window")f="window";else{f=String(e.ref);const o=await L(n,f);let h=null;for(const $ of j(n))try{if(await $.locator(o).count()){h=$;break}}catch{}if(!h)return p(`no element for ref ${e.ref} on the page any more \u2014 re-observe and use a current ref`);r=h}let t;try{t=await r.evaluate(M(V),[l,f])}catch(o){return p(`scroll probe failed: ${(o instanceof Error?o.message:String(o)).split(`
2
+ `)[0]}`)}if(t.kind==="ambiguous"){const o=(t.candidates??[]).map(h=>`${h.ref??"(observe for a ref)"}: ${h.name}${h.position?` (${h.position})`:""}`).join("; ");return p(`multiple scrollable regions \u2014 nothing scrolled. Pass scroll({ref:'\u2026', ...}) using a region or a control inside it: ${o}`)}if(t.kind==="none")return t.reason==="modal"?p("an open modal dialog blocks scrolling the background \u2014 choose a scrollable region inside the dialog; nothing scrolled"):p(t.reason==="gone"?`no element for ref ${e.ref} on the page any more \u2014 re-observe and use a current ref`:`ref ${e.ref} has no scrollable ancestor \u2014 that part of the page does not scroll; pick a ref inside the list you want to scroll, or scroll without ref`);let s=0,b=0;if(r.parentFrame()){const o=await(await r.frameElement()).boundingBox().catch(()=>null);o&&(s=o.x,b=o.y)}const m={x:t.x+s,y:t.y+b},v=t.kind==="doc";if(a){let o;try{o=await r.evaluate(M(R),[a==="top",v])}catch(E){return p(`scroll jump failed: ${(E instanceof Error?E.message:String(E)).split(`
3
+ `)[0]}`)}if(o.lost)return _(t,m);await S(C);const h=await T(r,l,v);if(!h)return _(t,m);const $=Math.abs(h.pos-t.before);return $<x?{res:{ok:!0,note:`did not scroll \u2014 ${k(t)} is already at the ${a}; do not repeat`,journal:{moved:0,container:t.name}},point:m,moved:$}:{res:{ok:!0,note:`scrolled to the ${a} of ${k(t)} (moved ${Math.round($)}px)`+O(r.url(),t,h,$,c,u,a==="top"),journal:{moved:Math.round($),container:t.name}},point:m,moved:$}}try{await n.mouse.move(m.x,m.y);const o=Math.max(40,Math.round(t.client*.85))*w;await n.mouse.wheel(l?d?-o:o:0,l?0:d?-o:o)}catch(o){return p(`wheel failed: ${(o instanceof Error?o.message:String(o)).split(`
4
+ `)[0]}`)}await S(C);let y=await T(r,l,v);for(const o=Date.now()+A;y&&Math.abs(y.pos-t.before)<x&&Date.now()<o;)await S(100),y=await T(r,l,v);if(!y)return _(t,m);const g=Math.abs(y.pos-t.before);return g<x?{res:{ok:!0,note:J(t),journal:{moved:Math.round(g),container:t.name}},point:m,moved:g}:{res:{ok:!0,note:`scrolled ${i} ${Math.round(g)}px in ${k(t)}`+(w>1?` (${w} screens asked for)`:"")+O(r.url(),t,y,g,c,u,d),journal:{moved:Math.round(g),container:t.name}},point:m,moved:g}}async function T(n,e,c){try{const u=await n.evaluate(M(N),[e,c]);return u.lost?null:u}catch{return null}}function _(n,e){return{res:{ok:!0,note:"the scrolled container was re-rendered; effect could not be measured \u2014 check the next observation before scrolling again",journal:{container:n.name,containerLost:!0}},point:e,moved:null}}function O(n,e,c,u,a,i,w){const l=Math.max(1,c.client||e.client),d=Math.max(1,Math.ceil(c.total/l));if(d<=1)return"";const r=c.pos+l>=c.total-8,f=r?d:Math.max(1,Math.min(d,Math.floor(c.pos/l)+1)),t=`${n.split("#")[0]}::${e.id}`,s=a.get(t)??{maxScreen:0,lastTotal:0,scrollCount:0,growCount:0},b=[],m=Math.max(1,u),v=Math.max(0,Math.ceil(Math.max(0,c.total-c.pos-l)/m)),y=r?" (reached the end)":w?"":` (~${v} more scrolls to the end)`;if(b.push(`screen ${f}/${d} of this container${y}`),s.lastTotal&&d>s.lastTotal+1){s.growCount++;let g=`note: this list grows as you scroll (was ${s.lastTotal} screens, now ${d})`;s.growCount>=2&&(g+="; scrolling to the end may be impossible \u2014 consider reporting what you have"),b.push(g)}return s.maxScreen>=f+2&&b.push(`you already viewed up to screen ${s.maxScreen} of this container`),s.scrollCount++,s.scrollCount>=5&&!i&&b.push(`this is scroll #${s.scrollCount} in this container \u2014 if what you need has not appeared, try to:'top'|'bottom', the page's own search/filter, or report what you have`),s.maxScreen=Math.max(s.maxScreen,f),s.lastTotal=d,a.set(t,s),`; ${b.join("; ")}`}async function z(n,e){const c=await L(n,e);let u=null;for(const r of j(n))try{const f=r.locator(c);if(await f.count()){u=f.first();break}}catch{}if(!u)return p(`no element for ref ${e} on the page any more \u2014 re-observe and use a current ref`);let a;try{a=await u.evaluate(M(("((n=>{const e=n.getBoundingClientRect().top;n.scrollIntoView({block:\"center\",inline:\"nearest\",behavior:\"instant\"});const t=n.getBoundingClientRect(),i=n.ownerDocument.defaultView;return{moved:Math.abs(t.top-e),inView:t.width>0&&t.height>0&&t.bottom>0&&t.top<i.innerHeight}}))")),void 0,{timeout:2e3})}catch(r){return p(`scroll into ${e} failed: ${(r instanceof Error?r.message:String(r)).split(`
5
+ `)[0]}`)}if(!a.inView)return p(`${e} is not rendered (no box on screen after scrolling to it) \u2014 re-observe`);await S(C);const i=await D(u),w=Math.round(a.moved),l=w>=x?`scrolled ${e} to the middle of the viewport (moved ${w}px)`:`${e} is already as near the middle as the page scrolls`;return{res:{ok:!0,note:i?i.pseudo?`${l}, but the ${i.pseudo} layer of \xAB${i.who}\xBB still covers it: page styling, nothing to close or scroll past \u2014 the keyboard still reaches it (Tab to it, then Enter or Space)`:`${l}, but \xAB${i.who}\xBB still covers it: dismiss that (find('close') reaches its close control when the page marks one; an unmarked one shows only in a screenshot)`:`${l}; nothing covers it now \u2014 act on it`,journal:{moved:w,into:e,...i?{covered:i.who}:{}}},point:null,moved:w}}export{q as performScroll,z as performScrollInto};
@@ -0,0 +1,4 @@
1
+ import type { Locator, Page } from "playwright-core";
2
+ import type { ActResult, VerbSpec } from "../types.ts";
3
+ export declare const SELECT_TEXT_VERB: VerbSpec;
4
+ export declare function performSelectText(page: Page, a: Record<string, any>, locate: () => Promise<Locator | null>): Promise<ActResult>;
@@ -0,0 +1,2 @@
1
+ import{Type as t}from"typebox";import{pageFn as f}from"./collect.js";const x={kind:"select_text",schema:t.Object({kind:t.Literal("select_text"),text:t.String({description:"The exact text to select (copy it verbatim from what you read)"}),ref:t.Optional(t.String({description:"Search inside this element only \u2014 a field, an editor, a section. Omit to search the whole page."})),name:t.Optional(t.String({description:"Visible name of that element \u2014 identity check + fallback locator"})),near:t.Optional(t.String({description:"Nearby text that tells one occurrence from another; the match closest to it wins"})),caret:t.Optional(t.Union([t.Literal("select"),t.Literal("before"),t.Literal("after")],{description:"select (default) leaves it selected \u2014 paste replaces it, Delete removes it; before / after place the caret there instead and select nothing"}))},{description:"Select text for paste({text}) to replace, or place the caret before/after it for paste to insert; setText(ref, text) still replaces the whole field."})},p=("((T=>{function b(e,n,o,i){const u=Math.max(o-(e+n),e-(o+i));return u>0?u:0}const I=T[0],t=T[1],s=I||document.body,h=t.caret;if(typeof s.value==\"string\"&&typeof s.setSelectionRange==\"function\"){const e=s.value,n=[];let o=e.indexOf(t.want);for(;o!==-1;)n.push(o),o=e.indexOf(t.want,o+1);if(!n.length)return{found:0};let i=n[0],u=0;if(t.near){const k=e.indexOf(t.near);if(k===-1)return{found:n.length,nearMissing:!0};let v=1/0;for(let g=0;g<n.length;g++){const C=b(n[g],t.want.length,k,t.near.length);C<v&&(v=C,i=n[g],u=g)}}s.focus();const R=h===\"after\"?i+t.want.length:i,_=h===\"select\"?i+t.want.length:R;return s.setSelectionRange(R,_),{found:n.length,index:u,field:!0,selected:e.slice(R,_)}}const p=[],c=[];let d=\"\";const y=document.createTreeWalker(s,NodeFilter.SHOW_TEXT);let r=y.nextNode();for(;r;){const e=r.parentElement?r.parentElement.tagName:\"\";e!==\"SCRIPT\"&&e!==\"STYLE\"&&e!==\"TEXTAREA\"&&r.data&&(p.push(r),c.push(d.length),d+=r.data),r=y.nextNode()}const l=[];let w=d.indexOf(t.want);for(;w!==-1;)l.push(w),w=d.indexOf(t.want,w+1);if(!l.length)return{found:0};let x=l[0],E=0;if(t.near){const e=d.indexOf(t.near);if(e===-1)return{found:l.length,nearMissing:!0};let n=1/0;for(let o=0;o<l.length;o++){const i=b(l[o],t.want.length,e,t.near.length);i<n&&(n=i,x=l[o],E=o)}}const N=x+t.want.length;let S=0,O=0;for(let e=0;e<p.length;e++)c[e]<=x&&(S=e),c[e]<N&&(O=e);const f=document.createRange();f.setStart(p[S],x-c[S]),f.setEnd(p[O],N-c[O]),h===\"before\"&&f.collapse(!0),h===\"after\"&&f.collapse(!1);let a=f.startContainer.parentElement;for(;a&&!a.isContentEditable;)a=a.parentElement;a&&typeof a.focus==\"function\"&&a.focus();const m=window.getSelection();return m?(m.removeAllRanges(),m.addRange(f),{found:l.length,index:E,selected:m.toString(),editable:!!a}):{found:l.length,index:E,noSelection:!0}}))");async function w(h,e,d){const o=String(e.text??"");if(!o)return{ok:!1,note:"select_text needs the text to select"};let a=null;if(e.ref){const i=await d();if(!i)return{ok:!1,note:`no element for ref ${e.ref}`};if(a=await i.elementHandle().catch(()=>null),!a)return{ok:!1,note:`${e.ref} is no longer in the page \u2014 re-observe`}}const s={want:o,near:typeof e.near=="string"?e.near:"",caret:String(e.caret??"select")},n=await h.evaluate(f(p),[a,s]).catch(i=>({error:i instanceof Error?i.message:String(i)})),r=e.ref?` in ${e.ref}`:"";if(n.error)return{ok:!1,note:`select_text failed: ${String(n.error).split(`
2
+ `)[0]}`};if(n.found===0)return{ok:!1,note:`"${o}" is not${r?` in ${e.ref}`:" on this page"} \u2014 read it back with text() and copy the text exactly, whitespace included`+(e.ref?"":"; text inside an input or textarea is only reachable with that field's ref")};if(n.nearMissing)return{ok:!1,note:`"${e.near}" is not${r||" on this page"}, so it cannot pick between the ${n.found} matches for "${o}" \u2014 drop near, or give text that really sits beside the one you mean`};if(n.noSelection)return{ok:!1,note:"this page has no selection to set"};const c=n.found>1?` (match ${(n.index??0)+1} of ${n.found}${e.near?`, the one nearest "${e.near}"`:", the first"})`:"";if(s.caret!=="select")return{ok:!0,note:`caret placed ${s.caret} "${o}"${r}${c} \u2014 nothing is selected; paste({text}) will INSERT there. setText(ref, text) replaces the whole field`};const l=String(n.selected??"");return l!==o?{ok:!0,note:`WARNING: the selection reads back as \xAB${l.slice(0,60)}\xBB, not the text you asked for \u2014 check the page before typing over it`}:{ok:!0,note:`selected "${o}"${r}${c} \u2014 paste({text}) replaces only this selection. setText(ref, text) replaces the whole field`}}export{x as SELECT_TEXT_VERB,w as performSelectText};
@@ -0,0 +1,10 @@
1
+ import type { Locator, Page } from "playwright-core";
2
+ import type { ScopedShot, ShotScope } from "../types.ts";
3
+ export declare const JPEG_SHOT: {
4
+ readonly type: "jpeg";
5
+ readonly quality: 60;
6
+ readonly scale: "css";
7
+ };
8
+ export declare function withPageCapture<T>(page: Page, task: () => Promise<T>): Promise<T>;
9
+ export declare function captureEvidenceShot(getPage: () => Page): Promise<Buffer | null>;
10
+ export declare function scopedShot(page: Page, scope: ShotScope, locate: (ref: string) => Promise<Locator | null>): Promise<ScopedShot>;
@@ -0,0 +1,2 @@
1
+ const w={type:"jpeg",quality:60,scale:"css"},l=new WeakMap;async function M(n,t){const h=(l.get(n)??Promise.resolve()).catch(()=>{}).then(t);l.set(n,h);try{return await h}finally{l.get(n)===h&&l.delete(n)}}const b=4e3;let $=!1;async function H(n){try{const t=n();return await M(t,()=>t.screenshot({...w,timeout:b}))}catch(t){if(!$){$=!0;const c=t instanceof Error?t.message.split(`
2
+ `)[0]:String(t);console.error(`mirasim-cu: screenshot failed, journal lines will carry no shot \u2014 ${c}`)}return null}}const i=n=>Math.round(n);async function P(n,t,c){if(t.padding!==void 0&&!t.ref)throw new Error("padding widens an element shot \u2014 pass it with ref");if(t.padding!==void 0&&!(t.padding>=0))throw new Error("padding needs a number of px, 0 or more");const h=[t.ref&&"ref",t.region&&"region",t.fullPage&&"fullPage"].filter(Boolean);if(h.length!==1)throw new Error(`pick exactly one of ref / region / fullPage (got ${h.join(" + ")||"none"})`);if(t.ref){const e=await c(t.ref);if(!e)throw new Error(`no element for ref ${t.ref} on this page \u2014 re-observe and use a current ref`);if(t.padding){await e.scrollIntoViewIfNeeded({timeout:5e3});const r=await e.boundingBox();if(!r)throw new Error(`ref ${t.ref} has no box on screen \u2014 it is not rendered`);const u=await n.evaluate("window.innerWidth"),f=await n.evaluate("window.innerHeight"),g=Math.max(0,r.x-t.padding),m=Math.max(0,r.y-t.padding),y=Math.min(u,r.x+r.width+t.padding)-g,x=Math.min(f,r.y+r.height+t.padding)-m;return{bytes:await n.screenshot({...w,clip:{x:g,y:m,width:y,height:x},timeout:1e4}),mimeType:"image/jpeg",note:`element ${t.ref} with ${i(t.padding)}px around it, ${i(y)}\xD7${i(x)} at (${i(g)},${i(m)})`}}const d=await e.screenshot({...w,timeout:1e4}),a=await e.boundingBox().catch(()=>null),s=a?`${i(a.width)}\xD7${i(a.height)} at (${i(a.x)},${i(a.y)})`:"size unknown";return{bytes:d,mimeType:"image/jpeg",note:`element ${t.ref}, ${s}`}}const o=await n.evaluate("({ w: window.innerWidth, h: window.innerHeight, docW: document.documentElement.scrollWidth, docH: document.documentElement.scrollHeight })");if(t.region){const e=t.region;if(!(e.width>0)||!(e.height>0))throw new Error("region needs a positive width and height");const d=Math.max(0,Math.min(e.x,o.w-1)),a=Math.max(0,Math.min(e.y,o.h-1)),s=Math.max(1,Math.min(e.x+e.width,o.w)-d),r=Math.max(1,Math.min(e.y+e.height,o.h)-a),u=d!==e.x||a!==e.y||s!==e.width||r!==e.height;return{bytes:await n.screenshot({...w,clip:{x:d,y:a,width:s,height:r},timeout:1e4}),mimeType:"image/jpeg",note:`region ${i(s)}\xD7${i(r)} at (${i(d)},${i(a)}) of the ${o.w}\xD7${o.h} viewport`+(u?" \u2014 trimmed to the viewport":"")}}const p=await n.screenshot({...w,fullPage:!0,timeout:12e3}),v=o.docH>o.h*3;return{bytes:p,mimeType:"image/jpeg",note:`full page ${o.docW}\xD7${o.docH}, viewport ${o.w}\xD7${o.h}`+(v?" \u2014 very tall: vision APIs downscale long images, so read text via what:'text'/'page' and shoot regions for detail":"")}}export{w as JPEG_SHOT,H as captureEvidenceShot,P as scopedShot,M as withPageCapture};
@@ -0,0 +1,8 @@
1
+ export interface Unreached {
2
+ canvases: number;
3
+ frames: number;
4
+ folded: number;
5
+ viewerType: string;
6
+ }
7
+ export declare function unreachedJs(rootExpr: string): string;
8
+ export declare function unreachedFacts(u: Unreached, rendered: boolean): string[];
@@ -0,0 +1 @@
1
+ const r=100;function a(e){return ("((()=>{const __root=("+(e)+");const __floor=("+(100)+");const __big=el=>{const r=el.getBoundingClientRect();return r.width>=__floor&&r.height>=__floor};const __all=tag=>__root.tagName===tag.toUpperCase()?[__root]:Array.from(__root.querySelectorAll(tag));let canvases=0,frames=0,folded=0;for(const c of __all(\"canvas\"))if(__big(c))canvases++;for(const f of __all(\"iframe\"))if(__big(f))frames++;for(const d of __all(\"details\")){if(d.open)continue;const r=d.getBoundingClientRect();if(r.width>0&&r.height>0)folded++}return{canvases,frames,folded,viewerType:String(document.contentType||\"\")}})())")}const n=/^(text\/|application\/(xhtml\+xml|xml|json)$)/;function i(e,s){const o=[];if(e.canvases&&o.push(`${e.canvases} canvas${e.canvases===1?"":"es"} draw${e.canvases===1?"s":""} content no text read can reach \u2014 screenshot() is the only way to see it`),e.folded){const t=e.folded===1;o.push(`${e.folded} collapsed section${t?"":"s"} hold${t?"s":""} text this read does not reach \u2014 ${t?"its summary is":"their summaries are"} in the element observation marked [collapsed]; click to open`)}if(e.frames){const t=e.frames===1;o.push(`${e.frames} embedded page${t?"":"s"} (iframe) hold${t?"s":""} content no page or text read reaches \u2014 ${t?"its":"their"} controls ARE in the element observation (find/observe); for ${t?"its":"their"} text, screenshot()`)}return e.viewerType&&!n.test(e.viewerType)&&!s&&o.push(`this document is ${e.viewerType}, which the browser renders in a viewer of its own \u2014 NONE of its content is in this read (any controls listed are the viewer's); screenshot() is the only way to see it`),o}export{i as unreachedFacts,a as unreachedJs};
@@ -0,0 +1 @@
1
+ export declare function approvalsAddCommand(app: string, argv1?: string): string;
@@ -0,0 +1 @@
1
+ function s(a,p=process.argv[1]??""){return`${/[\\/]_npx[\\/]/.test(p)?"npx -y @mirasim/computer-use":"mirasim-cu"} approvals add ${a}`}export{s as approvalsAddCommand};
@@ -0,0 +1,83 @@
1
+ import type { ActResult, OwnershipResource, ScopedShot, ShotScope, Snapshot, SnapshotCapture, TargetInfo, ToolResult, VerbSpec, Verdict } from "./types.ts";
2
+ import { DiagnosticsBuffer, type DiagnosticsAbout } from "./driver-diagnostics.ts";
3
+ import { ComputerUseDriverRender } from "./driver-render.ts";
4
+ export declare abstract class ComputerUseDriverBase extends ComputerUseDriverRender {
5
+ interactionMode: "background" | "desktop";
6
+ abstract readonly platform: string;
7
+ protected abstract snapshot(): Promise<Snapshot>;
8
+ abstract screenshot(): Promise<Buffer>;
9
+ screenshotScoped(_scope: ShotScope): Promise<ScopedShot>;
10
+ abstract readText(): Promise<string>;
11
+ abstract listTargets(): Promise<TargetInfo[]>;
12
+ abstract focusTarget(id: string): Promise<ActResult>;
13
+ abstract perform(kind: string, args: Record<string, unknown>): Promise<ActResult>;
14
+ abstract close(): Promise<void>;
15
+ onExecutorStart?: (pid: number) => Promise<void>;
16
+ private readonly disconnectListeners;
17
+ onDisconnect(listener: () => void | Promise<void>): void;
18
+ protected disconnected(): Promise<void>;
19
+ ownershipResources(): Promise<OwnershipResource[]>;
20
+ prepareTarget(): Promise<void>;
21
+ actionResources(_kind: string, _args: Record<string, unknown>): Promise<OwnershipResource[]>;
22
+ invalidateObservation(): void;
23
+ releaseInputResources(): Promise<void>;
24
+ extraVerbs(): VerbSpec[];
25
+ abstract promptFragment(): string;
26
+ interactionNotes(): readonly string[];
27
+ readonly screenshotMimeType: string;
28
+ protected captureSnapshot(): Promise<SnapshotCapture>;
29
+ protected bounded<T>(what: string, run: Promise<T>): Promise<T>;
30
+ protected prevSig: string | null;
31
+ protected prevElSig: string | null;
32
+ protected prevElSigSubstantive: string | null;
33
+ protected lastActionSig: string | null;
34
+ protected lastActionRef: string | null;
35
+ protected prevActionSig: string | null;
36
+ protected actionsInRound: number;
37
+ protected pendingSurfaceChange: boolean;
38
+ protected lastScrollMoved: number | null;
39
+ protected writeRound: {
40
+ fields: number;
41
+ verified: number;
42
+ ended: string | null;
43
+ } | null;
44
+ protected lastRects: Map<string, {
45
+ x: number;
46
+ y: number;
47
+ w: number;
48
+ h: number;
49
+ }> | null;
50
+ protected lastScrollY: number | null;
51
+ noteAction(kind: string, args: Record<string, unknown>): void;
52
+ noteWriteRound(round: {
53
+ fields: number;
54
+ verified: number;
55
+ ended: string | null;
56
+ }): void;
57
+ protected resetObservationState(): void;
58
+ protected journalVerdict?(verdict: Verdict): void;
59
+ readPage?(opts: {
60
+ ref?: string;
61
+ depth?: number;
62
+ node?: string;
63
+ }): Promise<ToolResult>;
64
+ readValue?(ref: string): Promise<string>;
65
+ valueMatches(actual: string, expected: string): boolean;
66
+ readonly supportsDiagnostics: boolean;
67
+ readonly readsOffscreen: boolean;
68
+ protected diag: DiagnosticsBuffer | null;
69
+ enableDiagnostics(): void;
70
+ get diagnosticsAbout(): DiagnosticsAbout;
71
+ protected get diagnosticsSink(): DiagnosticsBuffer | null;
72
+ currentUrl(): Promise<string>;
73
+ readDiagnostics(opts?: {
74
+ id?: string;
75
+ }): Promise<ToolResult>;
76
+ protected trimSurvivors(): string[];
77
+ protected takePendingNote(): string | null;
78
+ takeNoteForResult(): string | null;
79
+ protected takeSurfaceChange(): boolean;
80
+ activeTarget(): Promise<string | null>;
81
+ isActiveTarget(id: string): Promise<boolean>;
82
+ targetsNote(): string | null;
83
+ }
@@ -0,0 +1 @@
1
+ import{text as o}from"./types.js";import{CHROMIUM_DIAGNOSTICS as c,DiagnosticsBuffer as l}from"./driver-diagnostics.js";import{ComputerUseDriverRender as u}from"./driver-render.js";import"./driver-settle.js";const a=15e3;class m extends u{interactionMode="background";screenshotScoped(t){return Promise.reject(new Error(`scoped screenshots are not supported on ${this.platform}`))}onExecutorStart;disconnectListeners=new Set;onDisconnect(t){this.disconnectListeners.add(t)}async disconnected(){for(const t of this.disconnectListeners)await t()}async ownershipResources(){const t=await this.activeTarget();if(!t)throw new Error("No active target to claim; list and bind a surface first.");return[{key:`${this.platform}:${t}`,mode:"exclusive"}]}async prepareTarget(){}async actionResources(t,e){return[]}invalidateObservation(){this.resetObservationState()}async releaseInputResources(){}extraVerbs(){return[]}interactionNotes(){return[]}screenshotMimeType="image/jpeg";async captureSnapshot(){return{snapshot:await this.snapshot()}}async bounded(t,e){let n;try{return await Promise.race([e,new Promise((s,r)=>{n=setTimeout(()=>r(new Error(`${t} timed out after ${a/1e3}s \u2014 the result is unknown. Re-observe before continuing; do not replay a preceding mutation.`)),a),n.unref()})])}finally{clearTimeout(n)}}prevSig=null;prevElSig=null;prevElSigSubstantive=null;lastActionSig=null;lastActionRef=null;prevActionSig=null;actionsInRound=0;pendingSurfaceChange=!1;lastScrollMoved=null;writeRound=null;lastRects=null;lastScrollY=null;noteAction(t,e){if(t==="wait"){this.observesSinceAction=0;return}this.prevActionSig=this.lastActionSig,this.lastActionRef=typeof e.ref=="string"?e.ref:null;const n=["ref","name","x","y","text","chord","direction","to","url","option","target","ms","package"].map(r=>`${e[r]??""}`).join("|"),s=Array.isArray(e.then)?e.then.map(r=>{const i=r;return`${i.do??""}:${i.ref??""}=${i.text??i.option??i.chord??""}`}).join(","):"";this.lastActionSig=`${t}|${n}|${s}`,this.actionsInRound++,this.observesSinceAction=0,t!=="wait"&&this.diag?.markAction()}noteWriteRound(t){this.writeRound=t}resetObservationState(){this.prevSig=null,this.prevElSig=null,this.prevElSigSubstantive=null,this.forgetRefs(),this.lastRects=null,this.lastScrollY=null,this.pendingSurfaceChange=!1,this.resetRenderState()}valueMatches(t,e){return t===e}supportsDiagnostics=!1;readsOffscreen=!0;diag=null;enableDiagnostics(){this.supportsDiagnostics&&!this.diag&&(this.diag=new l(this.diagnosticsAbout))}get diagnosticsAbout(){return c}get diagnosticsSink(){return this.diag}async currentUrl(){return""}async readDiagnostics(t={}){return this.diag?this.diag.render(t.id,await this.currentUrl().catch(()=>"")):o("diagnostics are not enabled for this session",{error:"diagnostics_off"})}trimSurvivors(){return[]}takePendingNote(){return null}takeNoteForResult(){return this.takePendingNote()}takeSurfaceChange(){const t=this.pendingSurfaceChange;return this.pendingSurfaceChange=!1,t}async activeTarget(){return(await this.listTargets()).find(t=>t.active)?.id??null}async isActiveTarget(t){return await this.activeTarget()===t}targetsNote(){return null}}export{m as ComputerUseDriverBase};
@@ -0,0 +1,30 @@
1
+ import { type ToolResult } from "./types.ts";
2
+ export type DiagType = "console.error" | "console.warn" | "console.log" | "console.info" | "console.debug" | "pageerror" | "network" | "helper";
3
+ export interface DiagnosticsAbout {
4
+ empty: string;
5
+ header: string;
6
+ }
7
+ export declare const CHROMIUM_DIAGNOSTICS: DiagnosticsAbout;
8
+ export declare class DiagnosticsBuffer {
9
+ private readonly about;
10
+ constructor(about?: DiagnosticsAbout);
11
+ private entries;
12
+ private seq;
13
+ private dropped;
14
+ private readonly started;
15
+ private counts;
16
+ private notedErrors;
17
+ private lastNoteUrl;
18
+ private blamableActionAt;
19
+ private notedSeq;
20
+ private readSeq;
21
+ push(type: DiagType, message: string, opts?: {
22
+ detail?: string;
23
+ url?: string;
24
+ }): void;
25
+ markAction(): void;
26
+ private errorTotal;
27
+ takeNote(currentUrl?: string): string | null;
28
+ render(id?: string, currentUrl?: string): ToolResult;
29
+ private renderOne;
30
+ }
@@ -0,0 +1,4 @@
1
+ import{text as l}from"./types.js";const m=300,b=2e3,A=160,q=20,g=["pageerror","console.error"],E={empty:"console messages, page errors, or failed network requests",header:"console, page errors, failed network (request failures and HTTP \u2265 400; aborted requests excluded)"};function p(r,e){return r.length<=e?r:`${r.slice(0,e)}\u2026 (+${r.length-e} chars)`}function w(r){const e=r.indexOf("#");return e<0?r:r.slice(0,e)}class S{about;constructor(e=E){this.about=e}entries=[];seq=0;dropped=0;started=Date.now();counts=new Map;notedErrors=0;lastNoteUrl="";blamableActionAt=0;notedSeq=0;readSeq=0;push(e,t,n={}){this.counts.set(e,(this.counts.get(e)??0)+1),this.entries.push({id:`d${++this.seq}`,ts:Date.now()-this.started,type:e,text:p(t.replace(/\s*\n\s*/g," \u23CE "),b),...n.detail?{detail:p(n.detail,b)}:{},url:n.url??""});const o=this.entries.length-m;o>0&&(this.entries.splice(0,o),this.dropped+=o)}markAction(){this.blamableActionAt=Date.now()}errorTotal(){return g.reduce((e,t)=>e+(this.counts.get(t)??0),0)}takeNote(e=""){const t=!!this.lastNoteUrl&&!!e&&w(this.lastNoteUrl)!==w(e);e&&(this.lastNoteUrl=e),t&&(this.blamableActionAt=0);const n=this.blamableActionAt>0&&this.entries.some(a=>Number(a.id.slice(1))>this.notedSeq&&g.includes(a.type)&&this.started+a.ts>=this.blamableActionAt);this.notedSeq=this.seq;const o=this.errorTotal()-this.notedErrors;if(this.notedErrors=this.errorTotal(),o<=0)return null;const i=[...this.entries].reverse().find(a=>g.includes(a.type)),c=i?` (latest: ${i.id} "${p(i.text,120)}")`:"",h=o>1?"s":"";return n?`NOTE: ${o} new page/console error${h} since the last observation${c} \u2014 if your action seemed to have no effect, the page's own code may have failed; read diagnostics() before concluding the element was wrong.`:`NOTE: ${o} page/console error${h} ${t?"while this page loaded":"since the last observation"}${c} \u2014 the page's own; read diagnostics() if something you do next has no effect.`}render(e,t=""){if(e!==void 0)return this.renderOne(e);if(this.seq===0)return l(`No diagnostics \u2014 no ${this.about.empty} since collection started (at this session's first look at the surface).`,{channel:"diagnostics",kept:0,dropped:0});const o=["pageerror","console.error","console.warn","console.log","console.info","console.debug","network","helper"].filter(s=>this.counts.get(s)).map(s=>`${s} ${this.counts.get(s)}`).join(", "),i=this.entries.slice(-q),c=s=>{try{return new URL(s).host}catch{return""}},h=c(t),u=s=>{const f=c(s.url);return f&&h&&f!==h?` \u2039on ${f}\u203A`:""},a=i.filter(s=>u(s)).length,d=this.readSeq;this.readSeq=this.seq;const $=this.seq-d,y=s=>d>0&&Number(s.id.slice(1))>d,N=[`Diagnostics \u2014 ${this.about.header}.`,`Counts since collection started: ${o}.`+(this.dropped?` ${this.dropped} older entr${this.dropped>1?"ies were":"y was"} dropped at the ${m}-entry cap.`:""),`Most recent ${i.length}${this.entries.length>i.length?` of ${this.entries.length} kept`:""} (oldest first; one full entry: diagnostics({id:'dN'}))`+(a?`; ${a} of them happened on a page this surface has since left, marked \u2039on host\u203A`:"")+(d===0?":":$===0?" \u2014 NONE is new since your last read:":`; ${$} new since your last read, marked new:`),...i.map(s=>`${s.id}${y(s)?" new":""} [+${(s.ts/1e3).toFixed(1)}s]${u(s)} ${s.type}: ${p(s.text,A)}`)];return l(N.join(`
2
+ `),{channel:"diagnostics",kept:this.entries.length,dropped:this.dropped,counts:Object.fromEntries(this.counts)})}renderOne(e){const t=this.entries.find(n=>n.id===e);if(!t){const n=this.entries.length?`kept entries currently span ${this.entries[0].id}\u2013${this.entries[this.entries.length-1].id}`:"the buffer is empty";return l(`no diagnostics entry '${e}' \u2014 it may have been dropped at the ring cap; ${n}.`,{error:"bad_id"})}return l(`${t.id} ${t.type} at +${(t.ts/1e3).toFixed(1)}s${t.url?` on ${t.url}`:""}:
3
+ ${t.text}${t.detail?`
4
+ ${t.detail}`:""}`,{channel:"diagnostics",id:t.id,type:t.type})}}export{E as CHROMIUM_DIAGNOSTICS,S as DiagnosticsBuffer};
@@ -0,0 +1,15 @@
1
+ export declare const OBSERVE_FROZEN_NOTE: string;
2
+ export declare function stripFocus(l: string): string;
3
+ export declare function controlRef(line: string): string | undefined;
4
+ export declare function stripRow(l: string): string;
5
+ export declare function reattachRows(stripped: string[], full: string[]): string[];
6
+ export declare function diffLines(prev: string[], next: string[]): {
7
+ added: string[];
8
+ removed: string[];
9
+ };
10
+ export declare function looksUnmarked(controls: number, excerpt: string): boolean;
11
+ export declare function splitTextChurn(lines: string[]): {
12
+ controls: string[];
13
+ text: string[];
14
+ };
15
+ export declare const TEXT_CHURN_MAX = 8;
@@ -0,0 +1,2 @@
1
+ const u="NOTE: unchanged since your last observation (that was a look, not an action \u2014 nothing failed). If you are waiting for something to appear, use wait({text:'\u2026'}) instead of observing again.";function p(e){return e.replace(/ \[focused\]$/,"").replace("[focused, ","[")}function l(e){return/^(e\d+): /.exec(e)?.[1]}function i(e){return e.replace(/ ⟨row: .*⟩(?= \[|$)/,"")}function h(e,r){const o=new Map;for(const t of r){const s=i(t),n=o.get(s);n?n.push(t):o.set(s,[t])}return e.map(t=>o.get(t)?.shift()??t)}function d(e,r){const o=new Map;for(const n of e)o.set(n,(o.get(n)??0)+1);const t=[];for(const n of r){const c=o.get(n)??0;c>0?o.set(n,c-1):t.push(n)}const s=[];for(const[n,c]of o)for(let f=0;f<c;f++)s.push(n);return{added:t,removed:s}}function x(e,r){return e>2?!1:r.split(`
2
+ `).map(t=>t.trim()).filter(t=>t&&!t.startsWith("(cut at")&&t.length<a).length>=(e===0?2:8)}const a=120;function g(e){const r=[],o=[];for(const t of e)(/^e\d+: text /.test(t)||!/^e\d+: /.test(t)?o:r).push(t);return{controls:r,text:o}}const R=8;export{u as OBSERVE_FROZEN_NOTE,R as TEXT_CHURN_MAX,l as controlRef,d as diffLines,x as looksUnmarked,h as reattachRows,g as splitTextChurn,p as stripFocus,i as stripRow};
@@ -0,0 +1,26 @@
1
+ import type { Element, HeaderChanges, ObservationCoverage, Snapshot, TargetIdentity, TargetInfo, ViewportPosition } from "./types.ts";
2
+ export declare function targetsLine(targets: TargetInfo[]): string | null;
3
+ export interface FrozenFacts {
4
+ scrollMoved: number | null;
5
+ writeRound: {
6
+ fields: number;
7
+ verified: number;
8
+ ended: string | null;
9
+ } | null;
10
+ geomChange: string | null;
11
+ repeated: boolean;
12
+ actionsInRound: number;
13
+ treeBlind: boolean;
14
+ }
15
+ export declare abstract class ComputerUseDriverNarrate {
16
+ protected observationCoverage(s: Snapshot): ObservationCoverage;
17
+ protected targetLine(t: TargetIdentity): string;
18
+ protected notListedNote(m: Snapshot["meta"], positionStated: boolean): string | null;
19
+ protected offscreenNote(_count: number): string;
20
+ protected viewportPosition(m: Snapshot["meta"]): ViewportPosition;
21
+ protected renderHeadLines(s: Snapshot, targets: TargetInfo[], targetsUnchanged?: boolean, changes?: HeaderChanges): string[];
22
+ protected elementLine(e: Element): string;
23
+ protected changedElementLine(before: Element, after: Element): string;
24
+ protected coveredBaselineLines(s: Snapshot): string[];
25
+ protected renderBodyLines(s: Snapshot): string[];
26
+ }
@@ -0,0 +1,3 @@
1
+ const L=2e3,v={target:!0,coverage:!0,omissions:!0,groups:!0,ambiguity:!0};function w(c){if(c.length<=1)return null;const e=c.map(t=>`${t.active?"*":""}${t.id}:${t.title.slice(0,40)}${t.own===!1?"(user's)":""}`).join(" ");return c.some(t=>t.own===!1)?`${e}
2
+ (user's) = a tab that was already open \u2014 work in it if the task is about it, but it is not yours to close`:e}const b=6;class E{observationCoverage(e){return{offscreen:e.meta.offscreen??e.offscreenElements?.length??null,covered:e.covered?.count??e.coveredElements?.length??null,capped:e.meta.capped??null,...e.covered?.modal?{modal:e.covered.modal}:{}}}targetLine(e){const t=e.application;return`Target: ${t?`${t.name} ${t.version}`:"Electron app (identity/version not reported)"} | window ${e.windowId} | CDP ${e.endpoint}`}notListedNote(e,t){const r=[];return e.offscreen&&!t&&r.push(this.offscreenNote(e.offscreen)),e.capped&&r.push("some controls exceed this observation's element cap (narrow your target, or read the page)"),r.length?`NOTE: not in the list below \u2014 ${r.join("; ")}.`:null}offscreenNote(e){return"offscreen controls (find() searches them and actions scroll them into view)"}viewportPosition(e){const t=Math.max(1,e.viewportH),r=Math.max(0,e.docH-t),s=e.scrollY>=r-1,n=Math.max(1,Math.ceil(e.docH/t));return{scrollY:Math.round(e.scrollY),viewportH:Math.round(t),docH:Math.round(e.docH),maxScrollY:Math.round(r),belowFold:Math.round(Math.max(0,r-e.scrollY)),atBottom:s,screen:s?n:Math.min(n,Math.floor(e.scrollY/t)+1),total:n}}renderHeadLines(e,t,r=!1,s=v){const n=[];e.target&&s.target&&n.push(this.targetLine(e.target)),n.push(`Title: ${e.title} | ${e.url}`),w(t)&&n.push(r?`Targets: ${t.length} surfaces, the same list as the last observation \u2014 computer.surfaces() lists them again`:`Targets: ${w(t)} (switch with focus(id), or act through the surface's handle)`);const l=this.viewportPosition(e.meta),p=l.maxScrollY>0;if(p)if(e.meta.docH>l.viewportH*1.5)n.push(`Screen ${l.screen}/${l.total} (elements below are current viewport only${l.atBottom?"":"; scroll to see more"})`);else{const o=l.belowFold>0?`; ${l.belowFold}px below the fold \u2014 scroll to see the rest`:"";n.push(`Partial view (elements below are current viewport only${o})`)}s.coverage&&e.covered?.count&&e.covered.modal?n.push(`Scope: \xAB${e.covered.modal}\xBB dialog; covered background controls are omitted.`):s.coverage&&n.push("Scope: window's visible controls.");const d=this.notListedNote(e.meta,p);if(d&&s.omissions&&n.push(d),e.meta.rowsCut&&s.ambiguity){const o=e.meta.rowsCut===1;n.push(`NOTE: ${e.meta.rowsCut} same-named or unnamed element${o?"":"s"} below ${o?"carries":"carry"} no \u27E8row\u27E9 context (this observation's row budget ran out) \u2014 tell ${o?"it":"them"} apart with page() before acting on ${o?"it":"one"}.`)}const g=new Set(e.elements.map(o=>o.ref)),u=[];let m=0,h=0,$=!1;for(const o of e.groups??[]){const i=o.members.filter(f=>g.has(f));if(i.length){if($||m+o.label.length>2e3){h++,$=!0;continue}m+=o.label.length,u.push({label:o.label,members:i})}}if(u.length&&s.groups){n.push(`GROUPS (${u.length}):`);for(const o of u)n.push(` \u27E8${o.label}\u27E9 ${o.members.join(" ")}`)}if(h&&s.groups){const o=h===1;n.push(`NOTE: ${h} more labelled group${o?"":"s"} ${o?"was":"were"} dropped at this observation's group budget \u2014 page() reads one region's structure in full.`)}const a=e.elements.filter(o=>o.rowBlank).map(o=>o.ref);if(a.length&&s.ambiguity){const o=a.length<=b?a.join(", "):`${a.length} same-named or unnamed elements`,i=a.length===1;n.push(`NOTE: ${o} ${i?"carries":"carry"} no \u27E8row\u27E9 context \u2014 the page has no text that sets ${i?"it":"them"} apart, so a page read will not add any; tell ${i?"it":"them"} apart by position (screenshot()), or by ${i?"its place":"their order"} in the list below.`)}return n}elementLine(e){return`${e.ref}: ${e.role}${e.name?` "${e.name}"`:""}${e.id&&e.id.toLowerCase()!==e.name.trim().toLowerCase()?` \u27E8id: ${e.id}\u27E9`:""}${e.help?` \u27E8help: ${e.help}\u27E9`:""}${e.row?` \u27E8row: ${e.row}\u27E9`:""}${e.state?` [${e.state}]`:""}`}changedElementLine(e,t){return e.role===t.role&&e.name===t.name&&e.id===t.id&&e.help===t.help?`~ ${this.elementLine({...t,state:""})}: [${e.state||"none"}] \u2192 [${t.state||"none"}]`:`~ ${this.elementLine(e)} \u2192 ${this.elementLine(t).slice(t.ref.length+2)}`}coveredBaselineLines(e){return e.coveredElements?.length?e.coveredElements.map(t=>this.elementLine(t)):[]}renderBodyLines(e){const t=e.elements.map(r=>this.elementLine(r));return t.push(...this.coveredBaselineLines(e)),e.text&&t.push(...e.text.split(`
3
+ `)),t}}export{E as ComputerUseDriverNarrate,w as targetsLine};
@@ -0,0 +1,16 @@
1
+ import type { Snapshot } from "./types.ts";
2
+ import { ComputerUseDriverVerdict } from "./driver-verdict.ts";
3
+ export declare abstract class ComputerUseDriverRefs extends ComputerUseDriverVerdict {
4
+ private refs;
5
+ private everObserved;
6
+ private lastObserveFailure;
7
+ private coveredRefs;
8
+ readonly scrollsInto: boolean;
9
+ knownRef(ref: string): boolean;
10
+ staleRefNote(ref: string): string;
11
+ protected staleRefCause(): string;
12
+ protected rememberRefs(snap: Snapshot): void;
13
+ protected forgetRefs(): void;
14
+ protected noteObserveFailure(message: string): void;
15
+ }
16
+ export declare function nameAgrees(actual: string, want: string): boolean;
@@ -0,0 +1 @@
1
+ import{ComputerUseDriverVerdict as a}from"./driver-verdict.js";const n=2e3;class f extends a{refs=new Set;everObserved=!1;lastObserveFailure=null;coveredRefs=new Map;scrollsInto=!1;knownRef(r){return this.refs.has(r)}staleRefNote(r){if(!this.everObserved){const e=this.lastObserveFailure;return`ref ${r} is not usable \u2014 this surface has not been read yet, so it has never handed out a ref. `+(e?`The last read failed, and the next one fails the same way until this is dealt with \u2014 ${e}`:"Call observe() first.")}const t=this.coveredRefs.get(r);return t?`ref ${r} was collected but not listed \u2014 \xAB${t}\xBB covers it, so it is not actionable and re-observing returns the same list. Deal with that overlay first`+(this.scrollsInto?`, or scroll({into:'${r}'}) to bring it clear`:"")+", or act on a listed ref.":`ref ${r} is not among the refs currently in play \u2014 either ${this.staleRefCause()}, or it was never handed out (refs come from observe() and find(), never from the surface's own markup). Re-observe (w.observe()) and use a ref from that list.`}staleRefCause(){return"the surface has changed since it was handed out"}rememberRefs(r){this.everObserved=!0,this.lastObserveFailure=null,this.coveredRefs.clear();const t=r.covered?.top||"an overlay";for(const e of r.coveredElements??[])this.coveredRefs.set(e.ref,e.coveredBy||t);for(const e of r.elements)this.refs.delete(e.ref),this.refs.add(e.ref);for(const e of this.refs){if(this.refs.size<=n)break;this.refs.delete(e)}}forgetRefs(){this.refs.clear(),this.coveredRefs.clear()}noteObserveFailure(r){this.lastObserveFailure=r}}function c(o,r){const t=i=>(i||"").trim().toLowerCase().replace(/\s+/g," "),e=t(o),s=t(r);return!s||!e?!0:e.length<2||s.length<2||/^\d+$/.test(e)&&/^\d+$/.test(s)?e===s:e.includes(s)||s.includes(e)||e.slice(0,6)===s.slice(0,6)}export{f as ComputerUseDriverRefs,c as nameAgrees};
@@ -0,0 +1,26 @@
1
+ import type { Snapshot, TargetInfo, Verdict } from "./types.ts";
2
+ import { ComputerUseDriverSelect } from "./driver-select.ts";
3
+ export declare abstract class ComputerUseDriverRender extends ComputerUseDriverSelect {
4
+ private lastBody;
5
+ private lastElements;
6
+ private lastPrintableLen;
7
+ private lastBodyUrl;
8
+ private lastTargetsLine;
9
+ private deltaRounds;
10
+ private lastHeadContext;
11
+ protected contextKeep: string[];
12
+ protected lastGeomChange: string | null;
13
+ protected forgetDeltaBase(): void;
14
+ protected resetRenderState(): void;
15
+ private headerChanges;
16
+ protected renderObservation(snap: Snapshot, targets: TargetInfo[], verdict: Verdict | null, opts: {
17
+ forceImage?: boolean;
18
+ afterAction?: string;
19
+ surfaceChanged?: boolean;
20
+ }): {
21
+ body: string;
22
+ obsKind: "full" | "delta";
23
+ headLines: number;
24
+ effectNote: string | null;
25
+ };
26
+ }
@@ -0,0 +1,4 @@
1
+ import{ComputerUseDriverSelect as R}from"./driver-select.js";import{targetsLine as S}from"./driver-narrate.js";import{controlRef as B,diffLines as C,looksUnmarked as k,reattachRows as T,stripFocus as U,stripRow as w}from"./driver-lines.js";const D=6,A="NOTE: this drag changed the page's text but no interactive element \u2014 a reorder looks exactly like this; read the order (text()) before dragging again.";class H extends R{lastBody=null;lastElements=new Map;lastPrintableLen=0;lastBodyUrl=null;lastTargetsLine=null;deltaRounds=0;lastHeadContext=null;contextKeep=[];lastGeomChange=null;forgetDeltaBase(){this.lastBody=null,this.lastPrintableLen=0,this.lastHeadContext=null,this.lastElements.clear()}resetRenderState(){this.lastBody=null,this.lastPrintableLen=0,this.lastBodyUrl=null,this.lastTargetsLine=null,this.deltaRounds=0,this.lastGeomChange=null,this.lastHeadContext=null,this.contextKeep=[],this.lastElements.clear()}headerChanges(e){const m=new Set(e.elements.map(t=>t.ref)),f=JSON.stringify(e.covered?.modal?["modal",e.covered.modal]:["window"]),n={target:JSON.stringify(e.target??null),coverage:f,omissions:JSON.stringify([f,!!e.meta.offscreen,!!e.meta.capped,this.viewportPosition(e.meta).maxScrollY>0]),groups:JSON.stringify((e.groups??[]).map(t=>({label:t.label,members:t.members.filter(p=>m.has(p))})).filter(t=>t.members.length)),ambiguity:JSON.stringify([e.meta.rowsCut??0,e.elements.filter(t=>t.rowBlank).map(t=>t.ref)])},o=e.url===this.lastBodyUrl?this.lastHeadContext:null,a=t=>o?.[t]!==n[t],u={target:a("target"),coverage:a("coverage"),omissions:a("omissions"),groups:a("groups"),ambiguity:a("ambiguity")};return this.lastHeadContext=n,this.contextKeep=u.target&&e.target?[this.targetLine(e.target)]:[],u}renderObservation(e,m,f,n){const o=this.renderBodyLines(e),a=S(m),u=a!==null&&a===this.lastTargetsLine;this.lastTargetsLine=a;const t=this.renderHeadLines(e,m,u,this.headerChanges(e)),p=t.join(`
2
+ `).split(`
3
+ `).length,x=this.lastBody,E=new Map([...e.elements,...e.coveredElements??[]].map(r=>[r.ref,r]));let v="full",g=null;if(x!==null&&f!==null&&e.url===this.lastBodyUrl){const r=C(x.map(w),o.map(w)),l=T(r.added,o),d=T(r.removed,x),O=new Set(d.map(B).filter(Boolean)),N=new Set(d),b=new Map;for(const c of l){const s=B(c),h=s&&this.lastElements.get(s),i=s&&E.get(s);s&&O.has(s)&&h&&i&&h.role!=="text"&&i.role!=="text"&&c===this.elementLine(i)&&N.has(this.elementLine(h))&&b.set(s,this.changedElementLine(h,i))}if(!n.surfaceChanged&&(n.afterAction==="click"||n.afterAction==="drag"||n.afterAction==="keys")&&l.length+d.length>0){const c=i=>i.filter(y=>/^e\d+: /.test(y)),s=c(l),h=c(d);if(s.length+h.length===0)g=this.lastGeomChange?`NOTE: no element appeared or changed state after this ${n.afterAction}, but geometry changed: ${this.lastGeomChange} \u2014 the action DID take effect; judge progress by the new size/position.`:n.afterAction==="drag"?A:`NOTE: no interactive element changed after this ${n.afterAction} \u2014 only page text moved (e.g. a timer). If you expected an effect, it was likely swallowed (failed validation, wrong element) \u2014 re-check what you entered or take a different path instead of repeating it.`;else{const i=L=>U(w(L)),y=C(h.map(i),s.map(i));if(y.added.length===0&&y.removed.length===0){const L=l.length>s.length||d.length>h.length;g=this.lastGeomChange?`NOTE: this ${n.afterAction} moved the focus, and geometry changed: ${this.lastGeomChange}.`:L?n.afterAction==="drag"?A:`NOTE: this ${n.afterAction} moved the focus; no interactive element changed. Page text moved too (e.g. a timer), which on its own is not evidence your action landed.`:`NOTE: this ${n.afterAction} moved the focus but changed nothing else on the page.`}}}process.env.COMPUTER_USE_OBS_DELTA!=="0"&&!n.forceImage&&this.deltaRounds<D&&(n.afterAction==="scroll"?l.length:l.length+d.length-b.size)<=this.lastPrintableLen*.5&&(v="delta",t.push(...this.renderDelta(l,d,n.afterAction==="scroll",b)))}if(v==="full"){const r=o.slice(0,e.elements.length),l=o.slice(e.elements.length+this.coveredBaselineLines(e).length);t.push("Interactive elements (act by ref):"),t.push(...r.length?r:["(none)"]),l.length&&(t.push("Body excerpt (full text via text()):"),t.push(...l)),k(e.elements.length,e.text??"")&&t.push("NOTE: almost no interactive elements were detected on a text-rich page \u2014 its controls are likely unmarked (bare spans/divs). Take a screenshot() and act by x/y coordinates.")}return g&&t.push(g),this.lastBody=o,this.lastElements=E,this.lastPrintableLen=o.length-this.coveredBaselineLines(e).length,this.lastBodyUrl=e.url,this.deltaRounds=v==="delta"?this.deltaRounds+1:0,{body:t.join(`
4
+ `),obsKind:v,headLines:p,effectNote:g}}}export{H as ComputerUseDriverRender};
@@ -0,0 +1,31 @@
1
+ import type { Element, Heading, ObservationCoverage, SnapshotCapture, TargetIdentity, ToolResult } from "./types.ts";
2
+ import { ComputerUseDriverRefs } from "./driver-refs.ts";
3
+ export declare abstract class ComputerUseDriverSelect extends ComputerUseDriverRefs {
4
+ abstract readonly platform: string;
5
+ protected abstract captureSnapshot(): Promise<SnapshotCapture>;
6
+ protected abstract bounded<T>(what: string, run: Promise<T>): Promise<T>;
7
+ protected observesSinceAction: number;
8
+ protected contentRead: boolean;
9
+ noteContentRead(): void;
10
+ protected takeContentRead(): boolean;
11
+ readElements(): Promise<{
12
+ url: string;
13
+ title: string;
14
+ elements: Array<Element & {
15
+ offscreen?: boolean;
16
+ }>;
17
+ coverage: ObservationCoverage;
18
+ target?: TargetIdentity;
19
+ groups?: {
20
+ label: string;
21
+ members: string[];
22
+ }[];
23
+ headings?: Heading[];
24
+ partial?: string;
25
+ covered?: {
26
+ by: string;
27
+ elements: Element[];
28
+ };
29
+ }>;
30
+ observeMatches(query: string): Promise<ToolResult>;
31
+ }
@@ -0,0 +1,2 @@
1
+ import{SurfaceGoneError as v,text as m}from"./types.js";import{ComputerUseDriverRefs as w}from"./driver-refs.js";const g=25;class C extends w{observesSinceAction=0;contentRead=!1;noteContentRead(){this.contentRead=!0}takeContentRead(){const e=this.contentRead;return this.contentRead=!1,e}async readElements(){const{snapshot:e}=await this.bounded("observe",this.captureSnapshot()),n=(e.offscreenElements??[]).map(s=>({...s,offscreen:!0})),t=[...e.elements,...n];return this.rememberRefs({...e,elements:t}),{url:e.url,title:e.title,elements:t,coverage:this.observationCoverage(e),...e.target?{target:e.target}:{},...e.groups?.length?{groups:e.groups}:{},...e.headings?.length?{headings:e.headings}:{},...e.meta.partial?{partial:e.meta.partial}:{},...e.coveredElements?.length?{covered:{by:e.covered?.top||"an overlay",elements:e.coveredElements}}:{}}}async observeMatches(e){let n;try{n=await this.bounded("observe",this.captureSnapshot())}catch(h){if(h instanceof v)throw h;return m(`(query failed: ${h.message})`,{verdict:"unknown"})}const t=n.snapshot,s=t.offscreenElements??[],p=b(e),a=t.elements.filter(p),o=s.filter(p),r=o.slice(0,g),i=a.slice(0,Math.max(0,g-r.length)),c=o.length-r.length,l=a.length-i.length,u=[...i,...r];this.rememberRefs({...t,elements:u});const d=this.renderMatches(t,e,i,r,t.elements.length+s.length,s.length,{inViewport:l,offscreen:c});return++this.observesSinceAction>=3&&d.push(`NOTE: ${this.observesSinceAction} observations in a row without acting \u2014 more looking will not reveal more; act (click / set_text / drag / scroll) before looking again.`),m(d.join(`
2
+ `),{url:t.url,query:e,matches:a.length+o.length,searched:t.elements.length+s.length,offscreenMatches:o.length,dropped:l+c,droppedInViewport:l,droppedOffscreen:c,obsKind:"find",viewport:this.viewportPosition(t.meta)})}}function b(f){const e=t=>t.trim().toLowerCase().replace(/\s+/g," "),n=e(f);return n?t=>e(t.name).includes(n)||!!t.row&&e(t.row).includes(n):()=>!1}export{C as ComputerUseDriverSelect};
@@ -0,0 +1,22 @@
1
+ import type { Element } from "./types.ts";
2
+ export declare const STATE_CHANGING_ACTIONS: Set<string>;
3
+ export declare function elementSignature(elements: readonly Element[]): string;
4
+ export declare function substantiveElementSignature(elements: readonly Element[]): string;
5
+ export declare function effectAlreadyShown(opts: {
6
+ action: string;
7
+ target: Element | undefined;
8
+ scrolled: boolean;
9
+ focusMoved: boolean;
10
+ geometryMoved: boolean;
11
+ }): boolean;
12
+ export declare function onlyRefsLost(previous: ReadonlySet<string>, current: readonly Element[]): boolean;
13
+ export declare function disclosureClosed(previousFull: string | null, current: readonly Element[]): boolean;
14
+ export declare function settleAfterStateAction<T>(opts: {
15
+ capture: T;
16
+ previous: string | null;
17
+ signature: (capture: T) => string;
18
+ recapture: () => Promise<T>;
19
+ unsettled?: (previous: string, capture: T) => boolean;
20
+ now?: () => number;
21
+ sleep?: (ms: number) => Promise<void>;
22
+ }): Promise<T>;
@@ -0,0 +1,4 @@
1
+ const m=200,S=1200,h=new Set(["click","drag","keys"]);function x(e){return e.map(t=>`${t.role}|${t.name}|${t.state}`).join(`
2
+ `)}function w(e){return e.map(t=>`${t.role}|${t.name}|${i(t.state)}`).join(`
3
+ `)}const f=new Set(["checkbox","switch","radio","tab","option","menuitemcheckbox","menuitemradio","treeitem"]),E=/\b(checked|unchecked|mixed|pressed|expanded|collapsed|selected)\b/;function _(e){if(e.scrolled||e.action==="keys"&&e.focusMoved)return!0;const t=e.target!==void 0&&(f.has(e.target.role)||E.test(i(e.target.state)));return e.geometryMoved&&!t}function g(e,t){if(t.length>=e.size)return!1;for(const r of t)if(!e.has(r.ref))return!1;return!0}function p(e,t){if(e===null)return!1;const r=new Set;for(const n of e.split(`
4
+ `)){const o=n.lastIndexOf("|");o>0&&/\bexpanded\b/.test(n.slice(o+1))&&r.add(n.slice(0,o))}return t.some(n=>/\bcollapsed\b/.test(n.state)&&r.has(`${n.role}|${n.name}`))}function i(e){return e==="focused"?"":e.startsWith("focused, ")?e.slice(9):e}async function T(e){const{previous:t,recapture:r,signature:n}=e;if(t===null)return e.capture;const o=e.now??Date.now,u=e.sleep??(s=>new Promise(a=>setTimeout(a,s))),l=e.unsettled??((s,a)=>s===n(a));let c=e.capture;const d=o()+1200;for(;l(t,c)&&o()<d;){await u(200);try{c=await r()}catch{return c}}return c}export{h as STATE_CHANGING_ACTIONS,p as disclosureClosed,_ as effectAlreadyShown,x as elementSignature,g as onlyRefsLost,T as settleAfterStateAction,w as substantiveElementSignature};
@@ -0,0 +1,11 @@
1
+ import type { Element, Snapshot } from "./types.ts";
2
+ import { ComputerUseDriverNarrate, type FrozenFacts } from "./driver-narrate.ts";
3
+ export declare abstract class ComputerUseDriverVerdict extends ComputerUseDriverNarrate {
4
+ protected renderMatches(s: Snapshot, query: string, inView: Element[], offView: Element[], searched: number, offscreenSearched: number, dropped: {
5
+ inViewport: number;
6
+ offscreen: number;
7
+ }): string[];
8
+ protected renderDelta(added: string[], removed: string[], afterScroll: boolean, modified?: Map<string, string>): string[];
9
+ protected frozenNote(afterAction: string, f: FrozenFacts): string;
10
+ protected signature(s: Snapshot): string;
11
+ }
@@ -0,0 +1,5 @@
1
+ import{ComputerUseDriverNarrate as g}from"./driver-narrate.js";import{controlRef as d,splitTextChurn as f,TEXT_CHURN_MAX as w}from"./driver-lines.js";const p="NOTE: the screen did NOT change after your last action \u2014 it likely had no effect. Do not repeat the same action; try a different element or approach.",m="NOTE: the screen ends this run where it started. That is what a round trip looks like (opened then closed, scrolled away then back), and also what redoing work that produces the same result looks like \u2014 it is NOT by itself evidence that an action failed. Intermediate states are not in this observation: read the value you expected to change, or return your own observe() results, before deciding.",$="NOTE: the element tree is unchanged, but on this surface the tree cannot see the app's own drawing \u2014 so this is NOT evidence that your action failed. Take a screenshot (observe({screenshot: true})) and judge from the picture.",b="NOTE: the surface is unchanged, which is what a wait whose condition has not arrived looks like \u2014 the wait's own line says what it was waiting for and how long it spent. Nothing acted here, so this is not a dead action: wait again if it is still coming.",v="NOTE: You already tried this exact action and the screen did not change. Repeating it a third time will not help \u2014 change target or approach.";class E extends g{renderMatches(t,e,s,l,i,a,o){const h=s.length+l.length,n=o.inViewport+o.offscreen,r=[`Title: ${t.title} | ${t.url}`],u=`${i} control${i===1?"":"s"} searched`+(a?`, ${a} of them outside the viewport`:"");if(n){const c=o.offscreen&&o.inViewport?`${o.inViewport} of them in the viewport, ${o.offscreen} outside it`:o.offscreen?"all of them outside the viewport \u2014 narrow the query to reach those":"all of them in the viewport \u2014 a plain observe() lists those";r.push(`find "${e}" \u2014 ${h} of ${h+n} matches shown, ${n} not listed (${c}). ${u}. If what you want is below, act on it; narrow the query only if it is not.`)}else r.push(`find "${e}" \u2014 ${h} match${h===1?"":"es"} (${u}).`);return h===0?(r.push("Searched every control's name, and the \u27E8row\u27E9 context of controls that have one (a control whose name already identifies it carries none). Page text outside those is a different channel \u2014 read it with page()."),r):(s.length&&(r.push("In the viewport (act by ref):"),r.push(...s.map(c=>this.elementLine(c)))),l.length&&(r.push("Outside the viewport (act by ref \u2014 the click scrolls it into view, no need to scroll first):"),r.push(...l.map(c=>this.elementLine(c)))),r)}renderDelta(t,e,s,l=new Map){const i=[];if(s)t.length&&i.push("Newly visible after scrolling:",...t.map(a=>`+ ${a}`)),e.length&&i.push(`(${e.length} line${e.length===1?"":"s"} left the viewport \u2014 scrolled out, or dismissed (hover menus close on scroll); re-observe before acting on their refs if unsure)`),!t.length&&!e.length&&i.push("(nothing new is visible)");else if(i.push("Changes since last observation:"),t.length||e.length){const a=f(t),o=f(e),h=a.text.length+o.text.length;i.push(...o.controls.filter(n=>!l.has(d(n)??"")).map(n=>`- ${n}`),...l.values(),...a.controls.filter(n=>!l.has(d(n)??"")).map(n=>`+ ${n}`)),h>w?i.push(`(${h} text lines changed too \u2014 this surface re-renders or streams its text; read it with text() rather than from this diff)`):i.push(...o.text.map(n=>`- ${n}`),...a.text.map(n=>`+ ${n}`))}else i.push("(none)");return i.push("(unchanged elements omitted \u2014 refs from earlier observations remain valid; full list re-sent periodically)"),i}frozenNote(t,e){if(t==="scroll"&&e.scrollMoved!==null&&e.scrollMoved>=8)return`NOTE: scrolled ${Math.round(e.scrollMoved)}px but the visible elements look identical (uniform list?).`;if(t==="set_text"&&e.writeRound?.ended){const s=e.writeRound.ended;return`NOTE: the fields are filled and ${s} was dispatched, but nothing on screen changed \u2014 `+(s==="keys Enter"?"this form does not submit on Enter. Click its submit button.":"re-observe before assuming the sequence worked.")}return t==="set_text"&&e.writeRound&&e.writeRound.fields>0&&e.writeRound.verified===e.writeRound.fields?e.writeRound.fields>1?`NOTE: all ${e.writeRound.fields} fields hold the text you sent (verified by read-back); nothing else on screen changed.`:"NOTE: the text is in the field (verified by read-back); nothing else on screen changed.":(t==="click"||t==="drag"||t==="keys")&&e.geomChange?`NOTE: the element list and text are identical, but geometry changed: ${e.geomChange} \u2014 the ${t} DID take effect; judge progress by the new size/position.`:t==="wait"&&e.actionsInRound===0?b:e.treeBlind?$:e.repeated?v:e.actionsInRound>1?m:p}signature(t){const e=t.elements.map(s=>`${s.role}|${s.name}|${s.row??""}|${s.state}`).join(`
2
+ `);return`${t.url}
3
+ ${t.title}
4
+ ${e}
5
+ ${t.text}`}}export{E as ComputerUseDriverVerdict};
@@ -0,0 +1,9 @@
1
+ import type { ActResult } from "./types.ts";
2
+ export declare function waitForControlImpl(list: () => Promise<Array<{
3
+ role: string;
4
+ name: string;
5
+ }>>, name: string, gone: boolean, capMs: number): Promise<ActResult>;
6
+ export declare function waitForTextImpl(read: () => Promise<string>, needle: string, gone: boolean, capMs: number): Promise<ActResult>;
7
+ export declare function waitForUrlImpl(read: () => Promise<string>, want: string, gone: boolean, capMs: number): Promise<ActResult>;
8
+ export declare function waitForLoadImpl(read: () => Promise<string>, capMs: number): Promise<ActResult>;
9
+ export declare function waitSettledOverText(readText: () => Promise<string>, capMs: number, pollMs?: number): Promise<ActResult>;
@@ -0,0 +1 @@
1
+ const f=400,m=2e3;function u(i){return Math.min(2e3,400*2**Math.max(0,i-2))}async function h(i,t,e){const r=Date.now(),s=()=>((Date.now()-r)/1e3).toFixed(1);let a=0;for(let o=1;;o++){const n=Date.now(),l=await i();if(a=Date.now()-n,l!==null&&l===!t)return{met:!0,elapsed:s(),overran:""};const c=e-(Date.now()-r);if(c<=a){const d=Date.now()-r>e+250?` (the ${e}ms budget was spent inside a single read of this surface, which takes ~${(a/1e3).toFixed(1)}s here \u2014 the wait cost that one read)`:"";return{met:!1,elapsed:s(),overran:d}}await new Promise(d=>setTimeout(d,Math.min(u(o),c)))}}async function p(i,t,e,r){const s=t.trim().toLowerCase(),a=async()=>{const c=await i().catch(()=>null);if(c===null)return null;const d=c.filter(w=>w.role!=="text");return d.some(w=>w.name.trim().toLowerCase()===s)||d.some(w=>w.name.toLowerCase().includes(s))},{met:o,elapsed:n,overran:l}=await h(a,e,r);return o?{ok:!0,note:e?`the control "${t}" is gone after ${n}s`:`the control "${t}" is there after ${n}s`}:{ok:!1,note:e?`waited ${n}s and a control named "${t}" is still listed \u2014 it did not go away on its own, so observe and act on what is holding it there rather than waiting again`+l:`waited ${n}s and no control named "${t}" appeared \u2014 observe to see what the surface lists now; the name may differ, or the step before this did not take`+l}}async function $(i,t,e,r){const s=t.toLowerCase(),a=async()=>{const c=await i().catch(()=>null);return c===null?null:c.toLowerCase().includes(s)},{met:o,elapsed:n,overran:l}=await h(a,e,r);return o?{ok:!0,note:e?`"${t}" is gone after ${n}s`:`"${t}" appeared after ${n}s`}:{ok:!1,note:e?`waited ${n}s and "${t}" is still in the readable text \u2014 it did not go away on its own, so observe and act on what is holding it there rather than waiting again`+l:`waited ${n}s and "${t}" never appeared in the readable text \u2014 observe to see what the surface shows now; the wording may differ, or the step before this did not take`}}async function g(i,t,e,r){const s=t.toLowerCase();let a="";const o=async()=>{const d=await i().catch(()=>null);return d===null?null:(a=d,d.toLowerCase().includes(s))},{met:n,elapsed:l,overran:c}=await h(o,e,r);return n?{ok:!0,note:e?`left "${t}" after ${l}s`:`at ${a} after ${l}s`}:{ok:!1,note:e?`waited ${l}s and the address still contains "${t}" (${a}) \u2014 nothing moved away from it, so observe and act rather than waiting again`+c:`waited ${l}s and the address never contained "${t}" \u2014 it is ${a}. Observe what the surface shows now: the step before this may not have gone anywhere`+c}}async function y(i,t){let e="";const r=async()=>{const n=await i().catch(()=>null);return n===null?null:(e=n,n==="complete")},{met:s,elapsed:a,overran:o}=await h(r,!1,t);return s?{ok:!0,note:`loaded after ${a}s`}:{ok:!1,note:`waited ${a}s and the document is still "${e}", not "complete" \u2014 it is STILL LOADING, so an empty read means not-yet, never nothing-there. Wait again with a longer ms, or report that the page has not finished loading`+o}}async function v(i,t,e=600){const r=Date.now(),s=()=>i().then(o=>o.slice(0,2e4),()=>null);let a=await s();for(;Date.now()-r<t;){await new Promise(n=>setTimeout(n,e));const o=await s();if(o!==null&&o===a)return{ok:!0,note:`the readable text settled after ${((Date.now()-r)/1e3).toFixed(1)}s`};a=o??a}return{ok:!0,note:`the readable text was still changing when the ${Math.round(t/1e3)}s budget ran out \u2014 it may still be working, or it may be a surface that never goes quiet (a clock, a progress line). Read it as it is, or wait for something specific: wait({text:'\u2026'}) or wait({control_gone:'\u2026'})`}}export{p as waitForControlImpl,y as waitForLoadImpl,$ as waitForTextImpl,g as waitForUrlImpl,v as waitSettledOverText};
@@ -0,0 +1,10 @@
1
+ import type { ActResult } from "./types.ts";
2
+ import { ComputerUseDriverBase } from "./driver-base.ts";
3
+ export declare abstract class ComputerUseDriverWaits extends ComputerUseDriverBase {
4
+ waitSettled(capMs?: number): Promise<ActResult>;
5
+ waitForText(needle: string, gone: boolean, capMs?: number): Promise<ActResult>;
6
+ readTextForWait(): Promise<string>;
7
+ waitForControl(name: string, gone: boolean, capMs?: number): Promise<ActResult>;
8
+ waitForUrl(want: string, gone: boolean, capMs?: number): Promise<ActResult>;
9
+ waitForLoad(_capMs?: number): Promise<ActResult>;
10
+ }
@@ -0,0 +1 @@
1
+ import{waitForControlImpl as a,waitForTextImpl as o,waitForUrlImpl as s,waitSettledOverText as i}from"./driver-wait.js";import{ComputerUseDriverBase as n}from"./driver-base.js";class m extends n{async waitSettled(t=16e3){return i(()=>this.readText(),t)}async waitForText(t,e,r=16e3){return o(()=>this.readTextForWait(),t,e,r)}async readTextForWait(){return this.readText()}async waitForControl(t,e,r=16e3){return a(async()=>(await this.readElements()).elements,t,e,r)}async waitForUrl(t,e,r=16e3){return s(async()=>(await this.readElements()).url,t,e,r)}async waitForLoad(t){return{ok:!1,note:"this surface has no load state \u2014 it is not a document. Wait for a control or some text instead (w.wait({control}) / w.wait({text}))"}}}export{m as ComputerUseDriverWaits};