@e2edev/e2e 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/README.md +4 -4
  2. package/dist/agent/act.d.ts +13 -7
  3. package/dist/agent/act.d.ts.map +1 -1
  4. package/dist/agent/act.js +125 -730
  5. package/dist/agent/act.js.map +1 -1
  6. package/dist/agent/action-dispatcher.d.ts +98 -0
  7. package/dist/agent/action-dispatcher.d.ts.map +1 -0
  8. package/dist/agent/action-dispatcher.js +319 -0
  9. package/dist/agent/action-dispatcher.js.map +1 -0
  10. package/dist/agent/actions.d.ts +16 -1
  11. package/dist/agent/actions.d.ts.map +1 -1
  12. package/dist/agent/actions.js +5 -1
  13. package/dist/agent/actions.js.map +1 -1
  14. package/dist/agent/default-agent.d.ts +1 -1
  15. package/dist/agent/default-agent.d.ts.map +1 -1
  16. package/dist/agent/default-agent.js +30 -9
  17. package/dist/agent/default-agent.js.map +1 -1
  18. package/dist/agent/error.d.ts.map +1 -1
  19. package/dist/agent/error.js +1 -0
  20. package/dist/agent/error.js.map +1 -1
  21. package/dist/agent/executor.d.ts +33 -5
  22. package/dist/agent/executor.d.ts.map +1 -1
  23. package/dist/agent/executor.js.map +1 -1
  24. package/dist/agent/index.js +1 -1
  25. package/dist/agent/index.js.map +1 -1
  26. package/dist/agent/invocation.d.ts +4 -9
  27. package/dist/agent/invocation.d.ts.map +1 -1
  28. package/dist/agent/invocation.js +1 -6
  29. package/dist/agent/invocation.js.map +1 -1
  30. package/dist/agent/model/sdk.d.ts +1 -1
  31. package/dist/agent/model/sdk.js +1 -1
  32. package/dist/agent/observation-feed.d.ts +123 -0
  33. package/dist/agent/observation-feed.d.ts.map +1 -0
  34. package/dist/agent/observation-feed.js +281 -0
  35. package/dist/agent/observation-feed.js.map +1 -0
  36. package/dist/agent/observation.d.ts +14 -1
  37. package/dist/agent/observation.d.ts.map +1 -1
  38. package/dist/agent/observation.js +58 -2
  39. package/dist/agent/observation.js.map +1 -1
  40. package/dist/agent/operation-queue.d.ts +21 -0
  41. package/dist/agent/operation-queue.d.ts.map +1 -0
  42. package/dist/agent/operation-queue.js +27 -0
  43. package/dist/agent/operation-queue.js.map +1 -0
  44. package/dist/agent/phases.js +1 -1
  45. package/dist/agent/phases.js.map +1 -1
  46. package/dist/agent/pixels.d.ts +21 -0
  47. package/dist/agent/pixels.d.ts.map +1 -0
  48. package/dist/agent/pixels.js +85 -0
  49. package/dist/agent/pixels.js.map +1 -0
  50. package/dist/agent/point-tap.d.ts +52 -0
  51. package/dist/agent/point-tap.d.ts.map +1 -0
  52. package/dist/agent/point-tap.js +88 -0
  53. package/dist/agent/point-tap.js.map +1 -0
  54. package/dist/agent/primitives.d.ts +7 -1
  55. package/dist/agent/primitives.d.ts.map +1 -1
  56. package/dist/agent/primitives.js +99 -27
  57. package/dist/agent/primitives.js.map +1 -1
  58. package/dist/agent/replay.d.ts +8 -3
  59. package/dist/agent/replay.d.ts.map +1 -1
  60. package/dist/agent/replay.js +44 -15
  61. package/dist/agent/replay.js.map +1 -1
  62. package/dist/agent/screen-update.d.ts +50 -18
  63. package/dist/agent/screen-update.d.ts.map +1 -1
  64. package/dist/agent/screen-update.js +76 -94
  65. package/dist/agent/screen-update.js.map +1 -1
  66. package/dist/agent/secrets.d.ts.map +1 -1
  67. package/dist/agent/secrets.js +13 -9
  68. package/dist/agent/secrets.js.map +1 -1
  69. package/dist/agent/step-accounting.d.ts +106 -0
  70. package/dist/agent/step-accounting.d.ts.map +1 -0
  71. package/dist/agent/step-accounting.js +216 -0
  72. package/dist/agent/step-accounting.js.map +1 -0
  73. package/dist/agent/step-cache.d.ts.map +1 -1
  74. package/dist/agent/step-cache.js +2 -2
  75. package/dist/agent/step-cache.js.map +1 -1
  76. package/dist/agent/tool-loop.d.ts.map +1 -1
  77. package/dist/agent/tool-loop.js +9 -2
  78. package/dist/agent/tool-loop.js.map +1 -1
  79. package/dist/agent/transcript-compaction.d.ts +38 -0
  80. package/dist/agent/transcript-compaction.d.ts.map +1 -0
  81. package/dist/agent/transcript-compaction.js +165 -0
  82. package/dist/agent/transcript-compaction.js.map +1 -0
  83. package/dist/cache/recorder.d.ts.map +1 -1
  84. package/dist/cache/recorder.js +20 -0
  85. package/dist/cache/recorder.js.map +1 -1
  86. package/dist/cache/trace.d.ts +28 -1
  87. package/dist/cache/trace.d.ts.map +1 -1
  88. package/dist/cache/trace.js +44 -0
  89. package/dist/cache/trace.js.map +1 -1
  90. package/dist/cli/index.d.ts.map +1 -1
  91. package/dist/cli/index.js +55 -32
  92. package/dist/cli/index.js.map +1 -1
  93. package/dist/cli/init/engine-versions.json +1 -1
  94. package/dist/cli/init/engines.d.ts +29 -10
  95. package/dist/cli/init/engines.d.ts.map +1 -1
  96. package/dist/cli/init/engines.js +34 -12
  97. package/dist/cli/init/engines.js.map +1 -1
  98. package/dist/cli/init/gateways.js +3 -3
  99. package/dist/cli/init/mcp-config.js +1 -1
  100. package/dist/cli/init/mcp-config.js.map +1 -1
  101. package/dist/cli/init/scaffold.d.ts.map +1 -1
  102. package/dist/cli/init/scaffold.js +1 -3
  103. package/dist/cli/init/scaffold.js.map +1 -1
  104. package/dist/cli/init.d.ts.map +1 -1
  105. package/dist/cli/init.js +3 -2
  106. package/dist/cli/init.js.map +1 -1
  107. package/dist/collect/collect.d.ts +8 -3
  108. package/dist/collect/collect.d.ts.map +1 -1
  109. package/dist/collect/collect.js +29 -5
  110. package/dist/collect/collect.js.map +1 -1
  111. package/dist/collect/registry.d.ts +20 -5
  112. package/dist/collect/registry.d.ts.map +1 -1
  113. package/dist/collect/registry.js +139 -54
  114. package/dist/collect/registry.js.map +1 -1
  115. package/dist/collect/select.js +3 -1
  116. package/dist/collect/select.js.map +1 -1
  117. package/dist/config/agent.d.ts +1 -7
  118. package/dist/config/agent.d.ts.map +1 -1
  119. package/dist/config/agent.js +5 -8
  120. package/dist/config/agent.js.map +1 -1
  121. package/dist/config/app.d.ts +24 -2
  122. package/dist/config/app.d.ts.map +1 -1
  123. package/dist/config/app.js +82 -15
  124. package/dist/config/app.js.map +1 -1
  125. package/dist/config/diagnose.d.ts.map +1 -1
  126. package/dist/config/diagnose.js +2 -0
  127. package/dist/config/diagnose.js.map +1 -1
  128. package/dist/config/resolve.d.ts +26 -3
  129. package/dist/config/resolve.d.ts.map +1 -1
  130. package/dist/config/resolve.js +66 -23
  131. package/dist/config/resolve.js.map +1 -1
  132. package/dist/config/secrets.d.ts +89 -0
  133. package/dist/config/secrets.d.ts.map +1 -0
  134. package/dist/config/secrets.js +67 -0
  135. package/dist/config/secrets.js.map +1 -0
  136. package/dist/engine/contract.d.ts +17 -2
  137. package/dist/engine/contract.d.ts.map +1 -1
  138. package/dist/engine/contract.js.map +1 -1
  139. package/dist/engine/index.d.ts +18 -4
  140. package/dist/engine/index.d.ts.map +1 -1
  141. package/dist/engine/index.js +8 -0
  142. package/dist/engine/index.js.map +1 -1
  143. package/dist/engine/session.d.ts.map +1 -1
  144. package/dist/engine/session.js +3 -0
  145. package/dist/engine/session.js.map +1 -1
  146. package/dist/engine/surface.d.ts +6 -2
  147. package/dist/engine/surface.d.ts.map +1 -1
  148. package/dist/engine/surface.js.map +1 -1
  149. package/dist/expect/attempt.d.ts +15 -0
  150. package/dist/expect/attempt.d.ts.map +1 -0
  151. package/dist/expect/attempt.js +33 -0
  152. package/dist/expect/attempt.js.map +1 -0
  153. package/dist/expect/index.d.ts +7 -5
  154. package/dist/expect/index.d.ts.map +1 -1
  155. package/dist/expect/index.js +8 -1
  156. package/dist/expect/index.js.map +1 -1
  157. package/dist/expect/poll.d.ts +5 -0
  158. package/dist/expect/poll.d.ts.map +1 -0
  159. package/dist/expect/poll.js +118 -0
  160. package/dist/expect/poll.js.map +1 -0
  161. package/dist/expect/values.d.ts.map +1 -1
  162. package/dist/expect/values.js +6 -1
  163. package/dist/expect/values.js.map +1 -1
  164. package/dist/explore/body.d.ts.map +1 -1
  165. package/dist/explore/body.js +3 -1
  166. package/dist/explore/body.js.map +1 -1
  167. package/dist/explore/executor.js +16 -11
  168. package/dist/explore/executor.js.map +1 -1
  169. package/dist/explore/index.d.ts +3 -2
  170. package/dist/explore/index.d.ts.map +1 -1
  171. package/dist/explore/index.js +5 -5
  172. package/dist/explore/index.js.map +1 -1
  173. package/dist/explore/plan.d.ts.map +1 -1
  174. package/dist/explore/plan.js +16 -5
  175. package/dist/explore/plan.js.map +1 -1
  176. package/dist/explore/progress.d.ts +43 -0
  177. package/dist/explore/progress.d.ts.map +1 -0
  178. package/dist/explore/progress.js +9 -0
  179. package/dist/explore/progress.js.map +1 -0
  180. package/dist/explore/state.d.ts +13 -12
  181. package/dist/explore/state.d.ts.map +1 -1
  182. package/dist/explore/state.js +28 -11
  183. package/dist/explore/state.js.map +1 -1
  184. package/dist/index.d.ts +3 -1
  185. package/dist/index.d.ts.map +1 -1
  186. package/dist/index.js +4 -1
  187. package/dist/index.js.map +1 -1
  188. package/dist/internal/brands.d.ts +2 -0
  189. package/dist/internal/brands.d.ts.map +1 -1
  190. package/dist/internal/brands.js +2 -0
  191. package/dist/internal/brands.js.map +1 -1
  192. package/dist/internal/geometry.d.ts +13 -0
  193. package/dist/internal/geometry.d.ts.map +1 -0
  194. package/dist/internal/geometry.js +16 -0
  195. package/dist/internal/geometry.js.map +1 -0
  196. package/dist/internal/urls.d.ts +6 -0
  197. package/dist/internal/urls.d.ts.map +1 -1
  198. package/dist/internal/urls.js +28 -0
  199. package/dist/internal/urls.js.map +1 -1
  200. package/dist/locator/engine.d.ts.map +1 -1
  201. package/dist/locator/engine.js +1 -0
  202. package/dist/locator/engine.js.map +1 -1
  203. package/dist/mcp/catalog.d.ts +5 -4
  204. package/dist/mcp/catalog.d.ts.map +1 -1
  205. package/dist/mcp/catalog.js +5 -36
  206. package/dist/mcp/catalog.js.map +1 -1
  207. package/dist/mcp/server.js +1 -1
  208. package/dist/mcp/server.js.map +1 -1
  209. package/dist/mcp/session.d.ts +1 -1
  210. package/dist/mcp/session.d.ts.map +1 -1
  211. package/dist/mcp/session.js +12 -6
  212. package/dist/mcp/session.js.map +1 -1
  213. package/dist/report/builtin.d.ts +14 -0
  214. package/dist/report/builtin.d.ts.map +1 -0
  215. package/dist/report/builtin.js +22 -0
  216. package/dist/report/builtin.js.map +1 -0
  217. package/dist/report/explore-text.d.ts +17 -0
  218. package/dist/report/explore-text.d.ts.map +1 -0
  219. package/dist/report/explore-text.js +37 -0
  220. package/dist/report/explore-text.js.map +1 -0
  221. package/dist/report/format.d.ts +2 -0
  222. package/dist/report/format.d.ts.map +1 -1
  223. package/dist/report/format.js +1 -1
  224. package/dist/report/format.js.map +1 -1
  225. package/dist/report/junit.d.ts +2 -4
  226. package/dist/report/junit.d.ts.map +1 -1
  227. package/dist/report/junit.js +7 -40
  228. package/dist/report/junit.js.map +1 -1
  229. package/dist/report/list-explore.d.ts +109 -0
  230. package/dist/report/list-explore.d.ts.map +1 -0
  231. package/dist/report/list-explore.js +368 -0
  232. package/dist/report/list-explore.js.map +1 -0
  233. package/dist/report/list.d.ts +21 -0
  234. package/dist/report/list.d.ts.map +1 -1
  235. package/dist/report/list.js +79 -3
  236. package/dist/report/list.js.map +1 -1
  237. package/dist/report/markdown.d.ts +39 -0
  238. package/dist/report/markdown.d.ts.map +1 -0
  239. package/dist/report/markdown.js +426 -0
  240. package/dist/report/markdown.js.map +1 -0
  241. package/dist/report/outcome.d.ts +29 -0
  242. package/dist/report/outcome.d.ts.map +1 -0
  243. package/dist/report/outcome.js +36 -0
  244. package/dist/report/outcome.js.map +1 -0
  245. package/dist/report/running-tree.d.ts +7 -7
  246. package/dist/report/running-tree.d.ts.map +1 -1
  247. package/dist/report/running-tree.js +12 -13
  248. package/dist/report/running-tree.js.map +1 -1
  249. package/dist/report/write.d.ts +10 -1
  250. package/dist/report/write.d.ts.map +1 -1
  251. package/dist/report/write.js +22 -1
  252. package/dist/report/write.js.map +1 -1
  253. package/dist/run/app-ports.d.ts +17 -0
  254. package/dist/run/app-ports.d.ts.map +1 -0
  255. package/dist/run/app-ports.js +74 -0
  256. package/dist/run/app-ports.js.map +1 -0
  257. package/dist/run/events.d.ts +10 -5
  258. package/dist/run/events.d.ts.map +1 -1
  259. package/dist/run/events.js.map +1 -1
  260. package/dist/run/execute.d.ts.map +1 -1
  261. package/dist/run/execute.js +24 -7
  262. package/dist/run/execute.js.map +1 -1
  263. package/dist/run/extended-fixtures.d.ts +33 -0
  264. package/dist/run/extended-fixtures.d.ts.map +1 -0
  265. package/dist/run/extended-fixtures.js +91 -0
  266. package/dist/run/extended-fixtures.js.map +1 -0
  267. package/dist/run/fixtures.d.ts.map +1 -1
  268. package/dist/run/fixtures.js +39 -11
  269. package/dist/run/fixtures.js.map +1 -1
  270. package/dist/run/managed-process.d.ts +18 -0
  271. package/dist/run/managed-process.d.ts.map +1 -1
  272. package/dist/run/managed-process.js +101 -6
  273. package/dist/run/managed-process.js.map +1 -1
  274. package/dist/run/realm.d.ts +5 -4
  275. package/dist/run/realm.d.ts.map +1 -1
  276. package/dist/run/realm.js +8 -7
  277. package/dist/run/realm.js.map +1 -1
  278. package/dist/run/runner.d.ts +9 -3
  279. package/dist/run/runner.d.ts.map +1 -1
  280. package/dist/run/runner.js +15 -12
  281. package/dist/run/runner.js.map +1 -1
  282. package/dist/run/secrecy.d.ts +3 -3
  283. package/dist/run/secrecy.d.ts.map +1 -1
  284. package/dist/run/secrecy.js +4 -4
  285. package/dist/run/secrecy.js.map +1 -1
  286. package/dist/run/standalone.js +6 -6
  287. package/dist/run/standalone.js.map +1 -1
  288. package/dist/run/worker/entry.js +4 -3
  289. package/dist/run/worker/entry.js.map +1 -1
  290. package/dist/run/worker/protocol.d.ts +7 -1
  291. package/dist/run/worker/protocol.d.ts.map +1 -1
  292. package/dist/run/worker/protocol.js.map +1 -1
  293. package/dist/secrets.d.ts +21 -0
  294. package/dist/secrets.d.ts.map +1 -0
  295. package/dist/secrets.js +66 -0
  296. package/dist/secrets.js.map +1 -0
  297. package/dist/types.d.ts +90 -50
  298. package/dist/types.d.ts.map +1 -1
  299. package/package.json +6 -3
  300. package/skills/e2e/SKILL.md +42 -25
  301. package/skills/e2e/references/agent.md +80 -24
  302. package/skills/e2e/references/debugging.md +6 -5
  303. package/skills/e2e/references/explore.md +13 -7
  304. package/skills/e2e/references/mcp.md +4 -4
  305. package/skills/e2e/references/running.md +32 -21
  306. package/skills/e2e/references/setup.md +42 -16
  307. package/skills/e2e/references/writing-tests.md +32 -16
  308. package/dist/agent/model/router.d.ts +0 -36
  309. package/dist/agent/model/router.d.ts.map +0 -1
  310. package/dist/agent/model/router.js +0 -45
  311. package/dist/agent/model/router.js.map +0 -1
  312. package/dist/credentials.d.ts +0 -13
  313. package/dist/credentials.d.ts.map +0 -1
  314. package/dist/credentials.js +0 -51
  315. package/dist/credentials.js.map +0 -1
  316. package/dist/explore/reporter.d.ts +0 -11
  317. package/dist/explore/reporter.d.ts.map +0 -1
  318. package/dist/explore/reporter.js +0 -54
  319. package/dist/explore/reporter.js.map +0 -1
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Call-order serialization for one step's observations and actions.
3
+ *
4
+ * An executor (or an AI SDK loop running parallel tool calls) that issues a
5
+ * second action before the first settles would otherwise resolve both
6
+ * targets against the same pre-action observation — exactly the wrong-node
7
+ * hazard the staleness rule exists to prevent. Queued, the second call sees
8
+ * the newest observation and a stale id fails loud instead of acting on the
9
+ * wrong node.
10
+ *
11
+ * Invariant: a queued body must never enqueue another operation itself — the
12
+ * inner call would wait behind its own caller and deadlock. Action bodies
13
+ * call the engine session directly, secret authorization never observes, and
14
+ * a relocation captures raw rather than through the queued observe.
15
+ */
16
+ export class OperationQueue {
17
+ chain = Promise.resolve();
18
+ /** Runs `body` after every operation queued before it, in call order. */
19
+ run(body) {
20
+ // The chain is always already settled-to-undefined, so failures propagate
21
+ // to their own caller and never poison the queue.
22
+ const run = this.chain.then(body);
23
+ this.chain = run.then(() => undefined, () => undefined);
24
+ return run;
25
+ }
26
+ }
27
+ //# sourceMappingURL=operation-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-queue.js","sourceRoot":"","sources":["../../src/agent/operation-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,cAAc;IACjB,KAAK,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEpD,yEAAyE;IACzE,GAAG,CAAI,IAAsB;QAC3B,0EAA0E;QAC1E,kDAAkD;QAClD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,IAAI,CACnB,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;QACF,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
@@ -15,7 +15,7 @@ import { AgentError, toAgentError } from './error.js';
15
15
  * a clearly attributed failure, not the test budget.
16
16
  */
17
17
  export function boundedOperation(engine, actionTimeout, deadline) {
18
- return engine.operation(Math.max(1, Math.min(actionTimeout, deadline.remaining())));
18
+ return { ...engine.operation(Math.max(1, Math.min(actionTimeout, deadline.remaining()))), origin: 'agent' };
19
19
  }
20
20
  /** Records one policy decision as a child event of the current step. */
21
21
  export function recordPolicyEvent(steps, name, decision, code) {
@@ -1 +1 @@
1
- {"version":3,"file":"phases.js","sourceRoot":"","sources":["../../src/agent/phases.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"phases.js","sourceRoot":"","sources":["../../src/agent/phases.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The screenshot a model receives, sized for it. Engines capture at CSS or
3
+ * device scale; a 1280 by 720 web viewport or a 1170 by 2532 phone is more
4
+ * image than a model needs to place a tap, and every image pixel is paid
5
+ * for on every turn it stays in the conversation. The long side is capped
6
+ * and the image resampled, and `scale` (image pixels per CSS pixel) is
7
+ * reduced with it, so every coordinate a model reads off the image still
8
+ * maps onto the viewport through the same arithmetic. Masked regions stay
9
+ * masked: a box filter over black stays black.
10
+ */
11
+ import type { ObservationPixels } from '../engine/surface.ts';
12
+ /** Sizes a captured screenshot for the model; the input is returned as is when it already fits. */
13
+ export declare function sizeForModel<P extends ObservationPixels>(pixels: P): P;
14
+ /**
15
+ * Resamples the image so its longer side is at most `maxLongSide`, with a
16
+ * box filter over the source pixels each target pixel covers. Returns the
17
+ * input untouched when it already fits or cannot be decoded; whatever else
18
+ * the caller's pixels carry (a masked-region count) rides along unchanged.
19
+ */
20
+ export declare function downscalePixels<P extends ObservationPixels>(pixels: P, maxLongSide: number): P;
21
+ //# sourceMappingURL=pixels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pixels.d.ts","sourceRoot":"","sources":["../../src/agent/pixels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAa9D,mGAAmG;AACnG,wBAAgB,YAAY,CAAC,CAAC,SAAS,iBAAiB,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEtE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,iBAAiB,EAAE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,CAAC,CAgB9F"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * The screenshot a model receives, sized for it. Engines capture at CSS or
3
+ * device scale; a 1280 by 720 web viewport or a 1170 by 2532 phone is more
4
+ * image than a model needs to place a tap, and every image pixel is paid
5
+ * for on every turn it stays in the conversation. The long side is capped
6
+ * and the image resampled, and `scale` (image pixels per CSS pixel) is
7
+ * reduced with it, so every coordinate a model reads off the image still
8
+ * maps onto the viewport through the same arithmetic. Masked regions stay
9
+ * masked: a box filter over black stays black.
10
+ */
11
+ import { PNG } from 'pngjs';
12
+ /**
13
+ * Longest side a model-bound screenshot keeps, in image pixels. Measured on
14
+ * gpt-5.6 over a ten-key drawn keypad and an eight-screen drawn wizard, four
15
+ * runs per size: a 1280 by 720 capture costs about 1390 input tokens per
16
+ * turn, 1024 about 960, 768 about 680, 640 about 580, 512 about 500. Every
17
+ * size down to 640 passed every run; at 512 the model misread the keypad's
18
+ * display, needed retries, and once accepted a wrong code. 768 keeps a full
19
+ * step of margin above that cliff for 35% less per run than full size.
20
+ */
21
+ const MAX_SCREENSHOT_LONG_SIDE = 768;
22
+ /** Sizes a captured screenshot for the model; the input is returned as is when it already fits. */
23
+ export function sizeForModel(pixels) {
24
+ return downscalePixels(pixels, MAX_SCREENSHOT_LONG_SIDE);
25
+ }
26
+ /**
27
+ * Resamples the image so its longer side is at most `maxLongSide`, with a
28
+ * box filter over the source pixels each target pixel covers. Returns the
29
+ * input untouched when it already fits or cannot be decoded; whatever else
30
+ * the caller's pixels carry (a masked-region count) rides along unchanged.
31
+ */
32
+ export function downscalePixels(pixels, maxLongSide) {
33
+ const longSide = Math.max(pixels.width, pixels.height);
34
+ if (longSide <= maxLongSide || maxLongSide <= 0)
35
+ return pixels;
36
+ const factor = maxLongSide / longSide;
37
+ const width = Math.max(1, Math.round(pixels.width * factor));
38
+ const height = Math.max(1, Math.round(pixels.height * factor));
39
+ let source;
40
+ try {
41
+ source = PNG.sync.read(Buffer.from(pixels.data));
42
+ }
43
+ catch {
44
+ return pixels;
45
+ }
46
+ const target = new PNG({ width, height });
47
+ boxResample(source.data, source.width, source.height, target.data, width, height);
48
+ const data = new Uint8Array(PNG.sync.write(target));
49
+ return { ...pixels, data, width, height, scale: pixels.scale * (width / pixels.width) };
50
+ }
51
+ /** Averages, per channel, every source pixel a target pixel covers. RGBA, 8 bits per channel. */
52
+ function boxResample(source, sourceWidth, sourceHeight, target, targetWidth, targetHeight) {
53
+ const xRatio = sourceWidth / targetWidth;
54
+ const yRatio = sourceHeight / targetHeight;
55
+ for (let ty = 0; ty < targetHeight; ty += 1) {
56
+ const y0 = Math.floor(ty * yRatio);
57
+ const y1 = Math.min(sourceHeight, Math.max(y0 + 1, Math.floor((ty + 1) * yRatio)));
58
+ for (let tx = 0; tx < targetWidth; tx += 1) {
59
+ const x0 = Math.floor(tx * xRatio);
60
+ const x1 = Math.min(sourceWidth, Math.max(x0 + 1, Math.floor((tx + 1) * xRatio)));
61
+ let r = 0;
62
+ let g = 0;
63
+ let b = 0;
64
+ let a = 0;
65
+ let count = 0;
66
+ for (let y = y0; y < y1; y += 1) {
67
+ let offset = (y * sourceWidth + x0) * 4;
68
+ for (let x = x0; x < x1; x += 1) {
69
+ r += source[offset];
70
+ g += source[offset + 1];
71
+ b += source[offset + 2];
72
+ a += source[offset + 3];
73
+ offset += 4;
74
+ count += 1;
75
+ }
76
+ }
77
+ const at = (ty * targetWidth + tx) * 4;
78
+ target[at] = Math.round(r / count);
79
+ target[at + 1] = Math.round(g / count);
80
+ target[at + 2] = Math.round(b / count);
81
+ target[at + 3] = Math.round(a / count);
82
+ }
83
+ }
84
+ }
85
+ //# sourceMappingURL=pixels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pixels.js","sourceRoot":"","sources":["../../src/agent/pixels.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Point taps: how a viewport point an executor names is routed onto the
3
+ * tree, and how the result reads back. A point that a listed, enabled
4
+ * control contains is tapped by that control's id, so policy, stale
5
+ * relocation, and the trace cache see an ordinary tap; a point on nothing
6
+ * listed goes to the engine as a bare point. Coordinates read off a
7
+ * screenshot are scaled into the observation's CSS pixels first.
8
+ */
9
+ import type { SemanticNode, ViewportPoint } from '../engine/surface.ts';
10
+ import type { ExecutorPixels } from './executor.ts';
11
+ import { type AgentObservation } from './observation.ts';
12
+ /**
13
+ * Scales a point read off a screenshot into the observation's CSS pixels and
14
+ * clamps it to the viewport. `pixels.scale` is image pixels per CSS pixel
15
+ * (3 on a device screenshot, 1 on a CSS-scale capture); the observation's
16
+ * own `viewport.scale` is not consulted, because a device engine reports its
17
+ * geometry in logical points regardless of the image.
18
+ */
19
+ export declare function imagePointToViewport(point: {
20
+ readonly x: number;
21
+ readonly y: number;
22
+ }, pixels: Pick<ExecutorPixels, 'width' | 'height' | 'scale'>, viewport: {
23
+ readonly width: number;
24
+ readonly height: number;
25
+ }): ViewportPoint;
26
+ export interface HitTest {
27
+ /** The innermost enabled control whose box contains the point, if any. */
28
+ readonly control: SemanticNode | undefined;
29
+ /** The innermost listed node of any role whose box contains the point, if any. */
30
+ readonly under: SemanticNode | undefined;
31
+ }
32
+ /**
33
+ * Finds what the newest observation lists at a viewport point. Innermost
34
+ * wins (deepest in the tree, then the smallest box); hidden nodes and nodes
35
+ * without a box are skipped. Every box is in the top-level viewport's CSS
36
+ * pixels, nested documents included (`SemanticNode.rect`), so a control
37
+ * inside an iframe is found like any other. Only an enabled interactive
38
+ * role counts as the control: tapping a node taps its center, which is what
39
+ * the caller asked for only when the node is the control itself, not a
40
+ * paragraph or region that happens to contain the point.
41
+ */
42
+ export declare function hitTest(observation: AgentObservation, point: ViewportPoint): HitTest;
43
+ /** The observation's own line for a node, as the model already reads it; the id alone when the line was cut. */
44
+ export declare function nodeLine(observation: Pick<AgentObservation, 'text'>, id: string): string;
45
+ /** What one point tap did, for the model that named the point. */
46
+ export declare function describePointTap(input: {
47
+ readonly point: ViewportPoint;
48
+ readonly control: SemanticNode | undefined;
49
+ readonly under: SemanticNode | undefined;
50
+ readonly observation: Pick<AgentObservation, 'text'>;
51
+ }): string;
52
+ //# sourceMappingURL=point-tap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"point-tap.d.ts","sourceRoot":"","sources":["../../src/agent/point-tap.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAE5E;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE;IAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACjD,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC,EAC1D,QAAQ,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5D,aAAa,CASf;AAED,MAAM,WAAW,OAAO;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAC3C,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC;CAC1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,WAAW,EAAE,gBAAgB,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAgBpF;AAiBD,gHAAgH;AAChH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAMxF;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;CACtD,GAAG,MAAM,CAOT"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Point taps: how a viewport point an executor names is routed onto the
3
+ * tree, and how the result reads back. A point that a listed, enabled
4
+ * control contains is tapped by that control's id, so policy, stale
5
+ * relocation, and the trace cache see an ordinary tap; a point on nothing
6
+ * listed goes to the engine as a bare point. Coordinates read off a
7
+ * screenshot are scaled into the observation's CSS pixels first.
8
+ */
9
+ import { clamp, clampToViewport } from '../internal/geometry.js';
10
+ import { INTERACTIVE_ROLES } from './observation.js';
11
+ /**
12
+ * Scales a point read off a screenshot into the observation's CSS pixels and
13
+ * clamps it to the viewport. `pixels.scale` is image pixels per CSS pixel
14
+ * (3 on a device screenshot, 1 on a CSS-scale capture); the observation's
15
+ * own `viewport.scale` is not consulted, because a device engine reports its
16
+ * geometry in logical points regardless of the image.
17
+ */
18
+ export function imagePointToViewport(point, pixels, viewport) {
19
+ const scale = pixels.scale > 0 ? pixels.scale : 1;
20
+ return clampToViewport({
21
+ x: clamp(point.x, 0, Math.max(0, pixels.width - 1)) / scale,
22
+ y: clamp(point.y, 0, Math.max(0, pixels.height - 1)) / scale,
23
+ }, viewport);
24
+ }
25
+ /**
26
+ * Finds what the newest observation lists at a viewport point. Innermost
27
+ * wins (deepest in the tree, then the smallest box); hidden nodes and nodes
28
+ * without a box are skipped. Every box is in the top-level viewport's CSS
29
+ * pixels, nested documents included (`SemanticNode.rect`), so a control
30
+ * inside an iframe is found like any other. Only an enabled interactive
31
+ * role counts as the control: tapping a node taps its center, which is what
32
+ * the caller asked for only when the node is the control itself, not a
33
+ * paragraph or region that happens to contain the point.
34
+ */
35
+ export function hitTest(observation, point) {
36
+ let control;
37
+ let under;
38
+ for (const node of observation.nodes.values()) {
39
+ const rect = node.rect;
40
+ if (rect === undefined || rect.width <= 0 || rect.height <= 0)
41
+ continue;
42
+ if (node.states?.hidden === true)
43
+ continue;
44
+ if (point.x < rect.x || point.x >= rect.x + rect.width)
45
+ continue;
46
+ if (point.y < rect.y || point.y >= rect.y + rect.height)
47
+ continue;
48
+ const candidate = { node, depth: depthOf(node.ref.id, observation.parents), area: rect.width * rect.height };
49
+ if (inner(candidate, under))
50
+ under = candidate;
51
+ if (INTERACTIVE_ROLES.has(node.role ?? '') && node.states?.disabled !== true && inner(candidate, control)) {
52
+ control = candidate;
53
+ }
54
+ }
55
+ return { control: control?.node, under: under?.node };
56
+ }
57
+ function inner(candidate, best) {
58
+ if (best === undefined)
59
+ return true;
60
+ if (candidate.depth !== best.depth)
61
+ return candidate.depth > best.depth;
62
+ return candidate.area < best.area;
63
+ }
64
+ function depthOf(id, parents) {
65
+ let depth = 0;
66
+ for (let cursor = parents.get(id); cursor !== undefined; cursor = parents.get(cursor))
67
+ depth += 1;
68
+ return depth;
69
+ }
70
+ /** The observation's own line for a node, as the model already reads it; the id alone when the line was cut. */
71
+ export function nodeLine(observation, id) {
72
+ for (const line of observation.text.split('\n')) {
73
+ const trimmed = line.trimStart();
74
+ if (trimmed.startsWith(`#${id} `) || trimmed === `#${id}`)
75
+ return trimmed;
76
+ }
77
+ return `#${id}`;
78
+ }
79
+ /** What one point tap did, for the model that named the point. */
80
+ export function describePointTap(input) {
81
+ const at = `(${String(input.point.x)}, ${String(input.point.y)})`;
82
+ if (input.control !== undefined) {
83
+ return `Tapped ${nodeLine(input.observation, input.control.ref.id)}, the control at ${at}.`;
84
+ }
85
+ const under = input.under === undefined ? '' : ` (under it: ${nodeLine(input.observation, input.under.ref.id)})`;
86
+ return `Tapped the point ${at}; no listed control is there${under}.`;
87
+ }
88
+ //# sourceMappingURL=point-tap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"point-tap.js","sourceRoot":"","sources":["../../src/agent/point-tap.ts"],"names":[],"mappings":"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"}
@@ -35,7 +35,7 @@ export interface GrammarToolOptions {
35
35
  * (budget, timeout, cancel) propagates out of the tool; the chassis's guard
36
36
  * instead turns it into text and ends the loop.
37
37
  */
38
- readonly guard?: (body: () => Promise<string>) => Promise<string>;
38
+ readonly guard?: <T>(body: () => Promise<T>) => Promise<T | string>;
39
39
  /**
40
40
  * Renders screens for the model and remembers what it has seen, so every
41
41
  * result after the first reports the changes rather than the whole tree.
@@ -44,6 +44,12 @@ export interface GrammarToolOptions {
44
44
  */
45
45
  readonly screen?: ScreenPresenter;
46
46
  }
47
+ /**
48
+ * Every name `createGrammarTools` may hand out. The grammar owns these in the
49
+ * model's vocabulary whatever the engine declares, so a project tool cannot
50
+ * take one: it would be silently shadowed on one engine and live on another.
51
+ */
52
+ export declare const GRAMMAR_TOOL_NAMES: ReadonlySet<string>;
47
53
  /**
48
54
  * AI SDK tools over the harness action grammar, limited to the verbs the
49
55
  * target's engine declared. A verb the surface cannot honor is not offered
@@ -1 +1 @@
1
- {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../src/agent/primitives.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAGpD,OAAO,EAAsC,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAE/G,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AA2CrD,wEAAwE;AACxE,eAAO,MAAM,aAAa,umBAG4N,CAAC;AAcvP,wDAAwD;AACxD,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,uEAAuE;IACvE,OAAO,IAAI,WAAW,GAAG,SAAS,CAAC;IACnC,yEAAyE;IACzE,SAAS,IAAI,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CA4C/C;AAED,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClE;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;CACnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAuJT;AAED,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,WAAW,IAAI,IAAI,CAAC;IACpB;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAAC,GAAG,IAAI,CAAC;CAChF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,mBAAmB,EAC5B,KAAK,CAAC,EAAE;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,gBAAgB,CAoBlB"}
1
+ {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../src/agent/primitives.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAGpD,OAAO,EAAsC,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAI/G,OAAO,EAAE,eAAe,EAAqB,MAAM,oBAAoB,CAAC;AA4CxE,wEAAwE;AACxE,eAAO,MAAM,aAAa,umBAG4N,CAAC;AAqDvP,wDAAwD;AACxD,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,uEAAuE;IACvE,OAAO,IAAI,WAAW,GAAG,SAAS,CAAC;IACnC,yEAAyE;IACzE,SAAS,IAAI,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CA4C/C;AAED,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;IACpE;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;CACnC;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAWjD,CAAC;AAEH;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAsLT;AAED,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,WAAW,IAAI,IAAI,CAAC;IACpB;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAAC,GAAG,IAAI,CAAC;CAChF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,mBAAmB,EAC5B,KAAK,CAAC,EAAE;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,gBAAgB,CAoBlB"}
@@ -11,6 +11,8 @@
11
11
  import { z } from 'zod';
12
12
  import { isRuntimeHardStop, BLOCKABLE_CODES } from './executor.js';
13
13
  import { cacheTokenFields, readCost } from './model/sdk.js';
14
+ import { OperationQueue } from './operation-queue.js';
15
+ import { imagePointToViewport } from './point-tap.js';
14
16
  import { ScreenPresenter } from './screen-update.js';
15
17
  /** Codes the model may pick when concluding; runtime codes are runtime-assigned. */
16
18
  const MODEL_ERROR_CODES = [
@@ -20,6 +22,7 @@ const MODEL_ERROR_CODES = [
20
22
  'AUTHENTICATION_FAILED',
21
23
  'AUTH_CREDENTIAL_UNAVAILABLE',
22
24
  'AUTH_CREDENTIAL_INVALID',
25
+ 'SECRET_UNAVAILABLE',
23
26
  'ENVIRONMENT_UNAVAILABLE',
24
27
  'SEED_DATA_MISSING',
25
28
  'TEST_SETUP_FAILED',
@@ -59,6 +62,30 @@ export const VERDICT_RULES = `Verdict rules:
59
62
  function schemaTool(definition) {
60
63
  return definition;
61
64
  }
65
+ /**
66
+ * A grammar tool: its result is a rendered screen, text or text with the
67
+ * screenshot attached as a file part, so every one encodes its output the
68
+ * same way. A tool that skipped the encoder would hand the model the image
69
+ * bytes as JSON the first time an action ran in pixel mode.
70
+ */
71
+ function screenTool(definition) {
72
+ return schemaTool({ ...definition, toModelOutput: screenModelOutput });
73
+ }
74
+ function screenModelOutput({ output }) {
75
+ if (typeof output === 'string')
76
+ return { type: 'text', value: output };
77
+ return {
78
+ type: 'content',
79
+ value: [
80
+ { type: 'text', text: output.text },
81
+ {
82
+ type: 'file',
83
+ data: { type: 'data', data: Buffer.from(output.pixels.data).toString('base64') },
84
+ mediaType: output.pixels.mediaType,
85
+ },
86
+ ],
87
+ };
88
+ }
62
89
  /**
63
90
  * The `complete_step` verdict tool. A blocked verdict without a blockable
64
91
  * code is rejected back to the model, a passed verdict never carries a code,
@@ -103,6 +130,23 @@ export function createVerdictTool() {
103
130
  concluded: () => verdict !== undefined,
104
131
  };
105
132
  }
133
+ /**
134
+ * Every name `createGrammarTools` may hand out. The grammar owns these in the
135
+ * model's vocabulary whatever the engine declares, so a project tool cannot
136
+ * take one: it would be silently shadowed on one engine and live on another.
137
+ */
138
+ export const GRAMMAR_TOOL_NAMES = new Set([
139
+ 'observe',
140
+ 'tap',
141
+ 'type',
142
+ 'type_secret',
143
+ 'press',
144
+ 'select',
145
+ 'scroll',
146
+ 'navigate',
147
+ 'screenshot',
148
+ 'tap_at',
149
+ ]);
106
150
  /**
107
151
  * AI SDK tools over the harness action grammar, limited to the verbs the
108
152
  * target's engine declared. A verb the surface cannot honor is not offered
@@ -117,71 +161,71 @@ export function createGrammarTools(context, options = {}) {
117
161
  const guard = options.guard ?? ((body) => body());
118
162
  const screen = options.screen ?? new ScreenPresenter();
119
163
  const { verbs } = context.target;
120
- // The chain is always already settled-to-undefined, so a failed body
121
- // reaches its own caller and never poisons the queue (same idiom as the
122
- // dispatch's own serialization).
123
- let chain = Promise.resolve();
124
- const inOrder = (body) => {
125
- const run = chain.then(body);
126
- chain = run.then(() => undefined, () => undefined);
127
- return run;
128
- };
164
+ // One queue for the tool bodies: an action and its look at the result run
165
+ // together, so a batched turn gets one coherent result per action.
166
+ const queue = new OperationQueue();
167
+ const inOrder = (body) => queue.run(body);
168
+ /**
169
+ * The screen after an action, as the model reads it: the changes since the
170
+ * screen it holds and, once the step is showing pixels, a fresh screenshot.
171
+ */
172
+ const present = async (lead, expectChange) => screen.present(await context.observe({ pixels: screen.showingPixels }), { lead, expectChange });
129
173
  /**
130
174
  * Performs one action and reads its result. A failed action still returns
131
175
  * the screen, so the model can act on a stale-id or not-found failure at
132
176
  * once instead of spending a turn to observe; runtime hard stops propagate
133
177
  * to the guard, which ends the loop. The guard runs inside the queue, so a
134
178
  * batched call that queued behind a hard stop or a verdict is skipped when
135
- * its turn comes rather than acted on because it was queued in time.
179
+ * its turn comes rather than acted on because it was queued in time. The
180
+ * action may return its own lead line, for a result only it can describe.
136
181
  */
137
182
  const acting = (description, action, expectChange = true) => inOrder(() => guard(async () => {
183
+ let lead = description;
138
184
  try {
139
- await action();
185
+ lead = (await action()) ?? description;
140
186
  }
141
187
  catch (cause) {
142
188
  if (isRuntimeHardStop(cause))
143
189
  throw cause;
144
190
  const message = cause instanceof Error ? cause.message : String(cause);
145
- const observation = await context.observe();
146
- return screen.update(observation, { lead: `${description} failed: ${message}` });
191
+ return present(`${description} failed: ${message}`);
147
192
  }
148
- const observation = await context.observe();
149
- return screen.update(observation, { lead: description, expectChange });
193
+ return present(lead, expectChange);
150
194
  }));
151
195
  const target = z
152
196
  .string()
153
197
  .min(1)
154
198
  .describe('Node id from any screen in this conversation that is still present, e.g. "n42"');
155
199
  const tools = {
156
- observe: schemaTool({
200
+ observe: screenTool({
157
201
  description: 'Look at the screen again and get what changed since the screen you last received. Action results already include their changes, so call this only after waiting for something in progress, never right after an action.',
158
202
  inputSchema: z.object({}),
159
- execute: () => inOrder(() => guard(async () => screen.update(await context.observe()))),
203
+ execute: () => inOrder(() => guard(() => present('Observed.', false))),
160
204
  }),
161
205
  };
162
206
  if (verbs.has('tap')) {
163
- tools['tap'] = schemaTool({
207
+ tools['tap'] = screenTool({
164
208
  description: 'Tap or click one node. The result waits for the effect (a navigation, a route change, a submit) and reports what changed.',
165
209
  inputSchema: z.object({ target }),
166
210
  execute: ({ target: id }) => acting(`Tapped #${id}.`, () => context.actions.tap({ id })),
167
211
  });
168
212
  }
169
213
  if (verbs.has('type')) {
170
- tools['type'] = schemaTool({
214
+ tools['type'] = screenTool({
171
215
  description: 'Type a plain-text value into one input node, replacing its current value. Several fields can be typed in one turn.',
172
216
  inputSchema: z.object({ target, value: z.string() }),
173
217
  execute: ({ target: id, value }) => acting(`Typed into #${id}.`, () => context.actions.type({ id }, value)),
174
218
  });
175
219
  }
176
220
  if (verbs.has('press')) {
177
- tools['press'] = schemaTool({
221
+ tools['press'] = screenTool({
178
222
  description: 'Send one key (e.g. "Enter", "Escape", "Tab") to one node.',
179
223
  inputSchema: z.object({ target, key: z.string().min(1).max(64) }),
180
224
  execute: ({ target: id, key }) => acting(`Pressed ${key} on #${id}.`, () => context.actions.press({ id }, key), !movesFocusOnly(key)),
181
225
  });
182
226
  }
183
227
  if (verbs.has('select')) {
184
- tools['select'] = schemaTool({
228
+ tools['select'] = screenTool({
185
229
  description: 'Pick one option from a select-like control by its visible label.',
186
230
  inputSchema: z.object({ target, value: z.string().min(1) }),
187
231
  execute: ({ target: id, value }) => acting(`Selected "${value}" in #${id}.`, () => context.actions.select({ id }, value)),
@@ -208,33 +252,61 @@ export function createGrammarTools(context, options = {}) {
208
252
  const scrolled = (way, count) => count === 1 ? `Scrolled ${way}.` : `Scrolled ${way} ${String(count)} screens.`;
209
253
  // Node-targeted scrolling rides `perform`; without it only the viewport scrolls.
210
254
  tools['scroll'] = verbs.has('tap')
211
- ? schemaTool({
255
+ ? screenTool({
212
256
  description: 'Scroll the viewport, or one scrollable node when target is given. The result reports the rows that came into or left the tree.',
213
257
  inputSchema: z.object({ direction, target: target.optional(), times }),
214
258
  execute: ({ direction: way, target: id, times: count }) => acting(scrolled(way, count ?? 1), () => scrolling(way, id, count ?? 1), false),
215
259
  })
216
- : schemaTool({
260
+ : screenTool({
217
261
  description: 'Scroll the viewport. The result reports the rows that came into or left the tree.',
218
262
  inputSchema: z.object({ direction, times }),
219
263
  execute: ({ direction: way, times: count }) => acting(scrolled(way, count ?? 1), () => scrolling(way, undefined, count ?? 1), false),
220
264
  });
221
265
  }
222
266
  if (verbs.has('navigate')) {
223
- tools['navigate'] = schemaTool({
267
+ tools['navigate'] = screenTool({
224
268
  description: 'Navigate to a URL or app-relative path within the allowed origins.',
225
269
  inputSchema: z.object({ url: z.string().min(1) }),
226
270
  execute: ({ url }) => acting(`Navigated to ${url}.`, () => context.actions.navigate(url)),
227
271
  });
228
272
  }
273
+ // The pixel verbs are offered while pixels can still leave the runner. Once
274
+ // a secret was filled in the attempt they could only decline, and a verb
275
+ // that is absent costs the model nothing where one that declines costs a
276
+ // turn. tap_at lands either as a tap by id or as a bare point, so it needs
277
+ // one of the two; screenshot needs only the observation every step has.
278
+ if (!context.pixelsTainted) {
279
+ tools['screenshot'] = screenTool({
280
+ description: 'Attach a screenshot of the current viewport. Use it when the screen lists too little to act on (a canvas, a map, an image, a game, a system sheet) or contradicts what you expect. From then on every action result carries a fresh screenshot too, so you can see what each action did.',
281
+ inputSchema: z.object({}),
282
+ execute: () => inOrder(() => guard(async () => screen.present(await context.observe({ pixels: true })))),
283
+ });
284
+ if (verbs.has('tap') || verbs.has('tapAt')) {
285
+ tools['tap_at'] = screenTool({
286
+ description: 'Tap a point in the latest screenshot, given as pixel coordinates in that image (x from the left edge, y from the top edge). Aim for the center of the target. A listed control under the point is tapped by its id; otherwise the bare point is tapped' +
287
+ (verbs.has('tapAt') ? '.' : ', which this engine cannot do: the point must land on a listed control.') +
288
+ ' Last resort: when the screen lists the target, tap it by id.',
289
+ inputSchema: z.object({ x: z.number(), y: z.number() }),
290
+ execute: ({ x, y }) => {
291
+ const shot = screen.latestScreenshot;
292
+ if (shot === undefined) {
293
+ return Promise.resolve('No screenshot has been taken in this step: tap_at coordinates are pixels of the latest screenshot. Call screenshot first, or tap a listed node by id.');
294
+ }
295
+ const point = imagePointToViewport({ x, y }, shot.pixels, shot.viewport);
296
+ return acting(`tap_at (${String(x)}, ${String(y)})`, async () => (await context.actions.tapAt(point)).summary);
297
+ },
298
+ });
299
+ }
300
+ }
229
301
  // Offered only when the step declared secrets and the surface can fill: an
230
302
  // empty vocabulary is better than a tool the model can only be rejected on.
231
303
  if (verbs.has('typeSecret') && context.step.secrets.length > 0) {
232
- tools['type_secret'] = schemaTool({
233
- description: 'Fill one declared secret credential into a secure input field; the plaintext never passes through you and never shows on screen. Available: ' +
304
+ tools['type_secret'] = screenTool({
305
+ description: 'Fill one declared secret into an input by its name; the plaintext never passes through you. A password fills only a password field; a generic-secret fills any editable input. Available: ' +
234
306
  context.step.secrets.map((secret) => `"${secret.name}" (${secret.purpose})`).join(', ') +
235
307
  '.',
236
308
  inputSchema: z.object({ target, name: z.string().min(1) }),
237
- execute: ({ target: id, name }) => acting(`Filled secret "${name}" into #${id}; secure values never show on screen.`, () => context.actions.typeSecret({ id }, name), false),
309
+ execute: ({ target: id, name }) => acting(`Filled secret "${name}" into #${id}; its value is masked in every observation.`, () => context.actions.typeSecret({ id }, name), false),
238
310
  });
239
311
  }
240
312
  return tools;