@ai-agent-forge/agent-forge 0.88.5 → 0.88.6

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 (198) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/bundled-plugins/agent-loop-policy/agent-forge.json +1 -1
  3. package/bundled-plugins/agent-loop-policy/plugin.json +1 -1
  4. package/bundled-plugins/alignment/agent-forge.json +19 -0
  5. package/bundled-plugins/alignment/dist/baseline.d.ts +12 -0
  6. package/bundled-plugins/alignment/dist/baseline.d.ts.map +1 -0
  7. package/bundled-plugins/alignment/dist/baseline.js +68 -0
  8. package/bundled-plugins/alignment/dist/baseline.js.map +1 -0
  9. package/bundled-plugins/alignment/dist/engine.d.ts +102 -0
  10. package/bundled-plugins/alignment/dist/engine.d.ts.map +1 -0
  11. package/bundled-plugins/alignment/dist/engine.js +654 -0
  12. package/bundled-plugins/alignment/dist/engine.js.map +1 -0
  13. package/bundled-plugins/alignment/dist/entry.d.ts +14 -0
  14. package/bundled-plugins/alignment/dist/entry.d.ts.map +1 -0
  15. package/bundled-plugins/alignment/dist/entry.js +23 -0
  16. package/bundled-plugins/alignment/dist/entry.js.map +1 -0
  17. package/bundled-plugins/alignment/dist/export.d.ts +15 -0
  18. package/bundled-plugins/alignment/dist/export.d.ts.map +1 -0
  19. package/bundled-plugins/alignment/dist/export.js +122 -0
  20. package/bundled-plugins/alignment/dist/export.js.map +1 -0
  21. package/bundled-plugins/alignment/dist/http-server.d.ts +33 -0
  22. package/bundled-plugins/alignment/dist/http-server.d.ts.map +1 -0
  23. package/bundled-plugins/alignment/dist/http-server.js +119 -0
  24. package/bundled-plugins/alignment/dist/http-server.js.map +1 -0
  25. package/bundled-plugins/alignment/dist/index.d.ts +24 -0
  26. package/bundled-plugins/alignment/dist/index.d.ts.map +1 -0
  27. package/bundled-plugins/alignment/dist/index.js +719 -0
  28. package/bundled-plugins/alignment/dist/index.js.map +1 -0
  29. package/bundled-plugins/alignment/dist/page.d.ts +15 -0
  30. package/bundled-plugins/alignment/dist/page.d.ts.map +1 -0
  31. package/bundled-plugins/alignment/dist/page.js +832 -0
  32. package/bundled-plugins/alignment/dist/page.js.map +1 -0
  33. package/bundled-plugins/alignment/dist/render.d.ts +40 -0
  34. package/bundled-plugins/alignment/dist/render.d.ts.map +1 -0
  35. package/bundled-plugins/alignment/dist/render.js +194 -0
  36. package/bundled-plugins/alignment/dist/render.js.map +1 -0
  37. package/bundled-plugins/alignment/dist/types.d.ts +217 -0
  38. package/bundled-plugins/alignment/dist/types.d.ts.map +1 -0
  39. package/bundled-plugins/alignment/dist/types.js +30 -0
  40. package/bundled-plugins/alignment/dist/types.js.map +1 -0
  41. package/bundled-plugins/alignment/plugin.json +12 -0
  42. package/bundled-plugins/browser-playwright/MANUAL.md +60 -0
  43. package/bundled-plugins/browser-playwright/agent-forge.json +25 -0
  44. package/bundled-plugins/browser-playwright/dist/capability.d.ts +25 -0
  45. package/bundled-plugins/browser-playwright/dist/capability.d.ts.map +1 -0
  46. package/bundled-plugins/browser-playwright/dist/capability.js +612 -0
  47. package/bundled-plugins/browser-playwright/dist/capability.js.map +1 -0
  48. package/bundled-plugins/browser-playwright/dist/entry.d.ts +13 -0
  49. package/bundled-plugins/browser-playwright/dist/entry.d.ts.map +1 -0
  50. package/bundled-plugins/browser-playwright/dist/entry.js +14 -0
  51. package/bundled-plugins/browser-playwright/dist/entry.js.map +1 -0
  52. package/bundled-plugins/browser-playwright/dist/index.d.ts +7 -0
  53. package/bundled-plugins/browser-playwright/dist/index.d.ts.map +1 -0
  54. package/bundled-plugins/browser-playwright/dist/index.js +5 -0
  55. package/bundled-plugins/browser-playwright/dist/index.js.map +1 -0
  56. package/bundled-plugins/browser-playwright/dist/operations.d.ts +69 -0
  57. package/bundled-plugins/browser-playwright/dist/operations.d.ts.map +1 -0
  58. package/bundled-plugins/browser-playwright/dist/operations.js +149 -0
  59. package/bundled-plugins/browser-playwright/dist/operations.js.map +1 -0
  60. package/bundled-plugins/browser-playwright/dist/playwright-driver.d.ts +275 -0
  61. package/bundled-plugins/browser-playwright/dist/playwright-driver.d.ts.map +1 -0
  62. package/bundled-plugins/browser-playwright/dist/playwright-driver.js +953 -0
  63. package/bundled-plugins/browser-playwright/dist/playwright-driver.js.map +1 -0
  64. package/bundled-plugins/browser-playwright/dist/setup.d.ts +88 -0
  65. package/bundled-plugins/browser-playwright/dist/setup.d.ts.map +1 -0
  66. package/bundled-plugins/browser-playwright/dist/setup.js +252 -0
  67. package/bundled-plugins/browser-playwright/dist/setup.js.map +1 -0
  68. package/bundled-plugins/browser-playwright/dist/types.d.ts +270 -0
  69. package/bundled-plugins/browser-playwright/dist/types.d.ts.map +1 -0
  70. package/bundled-plugins/browser-playwright/dist/types.js +2 -0
  71. package/bundled-plugins/browser-playwright/dist/types.js.map +1 -0
  72. package/bundled-plugins/browser-playwright/plugin.json +20 -0
  73. package/bundled-plugins/coding-tools/agent-forge.json +1 -1
  74. package/bundled-plugins/coding-tools/dist/bash.d.ts +2 -7
  75. package/bundled-plugins/coding-tools/dist/bash.d.ts.map +1 -1
  76. package/bundled-plugins/coding-tools/dist/bash.js +2 -34
  77. package/bundled-plugins/coding-tools/dist/bash.js.map +1 -1
  78. package/bundled-plugins/coding-tools/plugin.json +1 -1
  79. package/bundled-plugins/compaction-policy/agent-forge.json +1 -1
  80. package/bundled-plugins/compaction-policy/plugin.json +1 -1
  81. package/bundled-plugins/mcp-http/dist/mcp-http.d.ts.map +1 -1
  82. package/bundled-plugins/mcp-http/dist/mcp-http.js +84 -31
  83. package/bundled-plugins/mcp-http/dist/mcp-http.js.map +1 -1
  84. package/bundled-plugins/overflow-policy/agent-forge.json +1 -1
  85. package/bundled-plugins/overflow-policy/plugin.json +1 -1
  86. package/bundled-plugins/provider-failover/agent-forge.json +1 -1
  87. package/bundled-plugins/provider-failover/plugin.json +1 -1
  88. package/bundled-plugins/retry-policy/agent-forge.json +1 -1
  89. package/bundled-plugins/retry-policy/plugin.json +1 -1
  90. package/dist/bundle/.build-manifest.json +60 -58
  91. package/dist/bundle/chunks/{anthropic-messages-3FJHRAJL.js → anthropic-messages-NL4Y6WPB.js} +1 -1
  92. package/dist/bundle/chunks/{azure-openai-responses-GCL47IDS.js → azure-openai-responses-CPXC42HO.js} +1 -1
  93. package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
  94. package/dist/bundle/chunks/chunk-5LRQFZFE.js +2 -0
  95. package/dist/bundle/chunks/{chunk-HTEVUY7F.js → chunk-7HSJPQKN.js} +1 -1
  96. package/dist/bundle/chunks/{chunk-AC2FIS4G.js → chunk-EDO7Q6XR.js} +1 -1
  97. package/dist/bundle/chunks/{chunk-755VNWIF.js → chunk-ISLHFRER.js} +6 -6
  98. package/dist/bundle/chunks/{chunk-HV64ASMT.js → chunk-K3S6GTL7.js} +1 -1
  99. package/dist/bundle/chunks/{chunk-HEL6TAWE.js → chunk-POLDAUYA.js} +37 -36
  100. package/dist/bundle/chunks/{chunk-V3ACW7VD.js → chunk-WJSKB3FC.js} +1 -1
  101. package/dist/bundle/chunks/{chunk-32YDZI4J.js → chunk-ZS5JEI76.js} +1 -1
  102. package/dist/bundle/chunks/{google-generative-ai-6BMXWA44.js → google-generative-ai-UMZ53EFA.js} +1 -1
  103. package/dist/bundle/chunks/{google-vertex-6H7NLRTK.js → google-vertex-ZNR5NS6H.js} +1 -1
  104. package/dist/bundle/chunks/image-resize-worker.js +1 -1
  105. package/dist/bundle/chunks/{mistral-conversations-IXUHSFSJ.js → mistral-conversations-IT66WUNN.js} +1 -1
  106. package/dist/bundle/chunks/{openai-codex-responses-YHUAEVCM.js → openai-codex-responses-FOVCVG6I.js} +1 -1
  107. package/dist/bundle/chunks/{openai-completions-ULQCFC72.js → openai-completions-FLQ7CBOL.js} +1 -1
  108. package/dist/bundle/chunks/{openai-responses-NLLAJDPS.js → openai-responses-ABGYHS2T.js} +1 -1
  109. package/dist/bundle/chunks/{pi-messages-EJTI2EJ3.js → pi-messages-6K5W6LUN.js} +1 -1
  110. package/dist/bundle/chunks/{virtual-modules-TQWEGNNZ.js → virtual-modules-2QGILFV7.js} +1 -1
  111. package/dist/bundle/cli.js +1 -1
  112. package/dist/bundle/client.js +1 -1
  113. package/dist/bundle/index.js +1 -1
  114. package/dist/bundle/rpc-entry.js +1 -1
  115. package/dist/client/remote-session.d.ts.map +1 -1
  116. package/dist/client/remote-session.js +65 -6
  117. package/dist/client/remote-session.js.map +1 -1
  118. package/dist/core/agent-session-store.d.ts.map +1 -1
  119. package/dist/core/agent-session-store.js +20 -3
  120. package/dist/core/agent-session-store.js.map +1 -1
  121. package/dist/core/extensions/agent-composition.d.ts.map +1 -1
  122. package/dist/core/extensions/agent-composition.js +20 -1
  123. package/dist/core/extensions/agent-composition.js.map +1 -1
  124. package/dist/core/i18n/locales/zh-cn/core-errors.d.ts.map +1 -1
  125. package/dist/core/i18n/locales/zh-cn/core-errors.js +1 -0
  126. package/dist/core/i18n/locales/zh-cn/core-errors.js.map +1 -1
  127. package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts.map +1 -1
  128. package/dist/core/i18n/locales/zh-cn/interactive-mode.js +10 -3
  129. package/dist/core/i18n/locales/zh-cn/interactive-mode.js.map +1 -1
  130. package/dist/core/keybindings.d.ts +35 -0
  131. package/dist/core/keybindings.d.ts.map +1 -1
  132. package/dist/core/keybindings.js +34 -0
  133. package/dist/core/keybindings.js.map +1 -1
  134. package/dist/core/sdk.d.ts.map +1 -1
  135. package/dist/core/sdk.js +62 -15
  136. package/dist/core/sdk.js.map +1 -1
  137. package/dist/core/session-manager.d.ts.map +1 -1
  138. package/dist/core/session-manager.js +11 -3
  139. package/dist/core/session-manager.js.map +1 -1
  140. package/dist/core/system-prompt.d.ts.map +1 -1
  141. package/dist/core/system-prompt.js +2 -2
  142. package/dist/core/system-prompt.js.map +1 -1
  143. package/dist/core/tools/capability-mount.d.ts.map +1 -1
  144. package/dist/core/tools/capability-mount.js +4 -21
  145. package/dist/core/tools/capability-mount.js.map +1 -1
  146. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  147. package/dist/modes/interactive/components/assistant-message.js +2 -2
  148. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  149. package/dist/modes/interactive/components/bash-execution.d.ts +2 -0
  150. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  151. package/dist/modes/interactive/components/bash-execution.js +25 -0
  152. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  153. package/dist/modes/interactive/components/changelog-pager.d.ts +53 -0
  154. package/dist/modes/interactive/components/changelog-pager.d.ts.map +1 -0
  155. package/dist/modes/interactive/components/changelog-pager.js +239 -0
  156. package/dist/modes/interactive/components/changelog-pager.js.map +1 -0
  157. package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  158. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/tool-execution.js +12 -0
  160. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  161. package/dist/modes/interactive/components/tool-run-group.d.ts +41 -11
  162. package/dist/modes/interactive/components/tool-run-group.d.ts.map +1 -1
  163. package/dist/modes/interactive/components/tool-run-group.js +126 -14
  164. package/dist/modes/interactive/components/tool-run-group.js.map +1 -1
  165. package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
  166. package/dist/modes/interactive/interactive-commands.js +23 -13
  167. package/dist/modes/interactive/interactive-commands.js.map +1 -1
  168. package/dist/modes/interactive/interactive-events.d.ts +14 -4
  169. package/dist/modes/interactive/interactive-events.d.ts.map +1 -1
  170. package/dist/modes/interactive/interactive-events.js +94 -12
  171. package/dist/modes/interactive/interactive-events.js.map +1 -1
  172. package/dist/modes/interactive/interactive-mode.d.ts +6 -1
  173. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  174. package/dist/modes/interactive/interactive-mode.js +21 -5
  175. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  176. package/dist/plugins/ecosystem/node-local-package-source.d.ts +2 -1
  177. package/dist/plugins/ecosystem/node-local-package-source.d.ts.map +1 -1
  178. package/dist/plugins/ecosystem/node-local-package-source.js +110 -4
  179. package/dist/plugins/ecosystem/node-local-package-source.js.map +1 -1
  180. package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -1
  181. package/dist/plugins/ecosystem/node-package-install-host.js +106 -39
  182. package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -1
  183. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -1
  184. package/dist/plugins/ecosystem/node-plugin-bootstrap.js +59 -2
  185. package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -1
  186. package/dist/suites/builtin-suites.d.ts.map +1 -1
  187. package/dist/suites/builtin-suites.js +2 -2
  188. package/dist/suites/builtin-suites.js.map +1 -1
  189. package/dist/utils/changelog.d.ts +27 -0
  190. package/dist/utils/changelog.d.ts.map +1 -1
  191. package/dist/utils/changelog.js +110 -0
  192. package/dist/utils/changelog.js.map +1 -1
  193. package/docs/keybindings.md +14 -0
  194. package/examples/extensions/with-deps/package-lock.json +2 -2
  195. package/examples/extensions/with-deps/package.json +1 -1
  196. package/examples/extensions/with-deps/plugin.json +1 -1
  197. package/package.json +1 -1
  198. package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
@@ -0,0 +1,275 @@
1
+ import { type PluginResultV1 } from "@agent-forge/plugin-sdk";
2
+ import { type BrowserSetupOptionsV1 } from "./setup.ts";
3
+ import type { BrowserConsoleReadInputV1, BrowserDragInputV1, BrowserDriverV1, BrowserEvaluateInputV1, BrowserEvidenceKindV1, BrowserLocatorActionInputV1, BrowserMouseInputV1, BrowserNavigateInputV1, BrowserNetworkReadInputV1, BrowserOperationOptionsV1, BrowserPostconditionV1, BrowserPressKeyInputV1, BrowserRuntimeV1, BrowserSetupOutcomeV1, BrowserSnapshotInputV1, BrowserStorageInputV1, BrowserTabsInputV1, BrowserViewportInputV1, BrowserWaitInputV1 } from "./types.ts";
4
+ export declare const BROWSER_MAX_SNAPSHOT_CHARS_V1: number;
5
+ export declare const BROWSER_MAX_DOM_CHARS_V1: number;
6
+ export declare const BROWSER_MAX_EVIDENCE_CHARS_V1: number;
7
+ export declare const BROWSER_MAX_SCREENSHOT_BYTES_V1: number;
8
+ export declare const BROWSER_RING_BUFFER_SIZE_V1 = 200;
9
+ export declare const BROWSER_DOWNLOADS_DIR_NAME_V1 = "downloads";
10
+ /** Minimal structural surface of the lazily required `playwright` module. */
11
+ export interface PlaywrightLocatorLike {
12
+ click(options?: {
13
+ readonly button?: "left" | "right" | "middle";
14
+ readonly timeout?: number;
15
+ }): Promise<void>;
16
+ dblclick(options?: {
17
+ readonly timeout?: number;
18
+ }): Promise<void>;
19
+ fill(value: string, options?: {
20
+ readonly timeout?: number;
21
+ }): Promise<void>;
22
+ type(text: string, options?: {
23
+ readonly delay?: number;
24
+ readonly timeout?: number;
25
+ }): Promise<void>;
26
+ press(key: string, options?: {
27
+ readonly timeout?: number;
28
+ }): Promise<void>;
29
+ hover(options?: {
30
+ readonly timeout?: number;
31
+ }): Promise<void>;
32
+ selectOption(value: string, options?: {
33
+ readonly timeout?: number;
34
+ }): Promise<void>;
35
+ check(options?: {
36
+ readonly timeout?: number;
37
+ }): Promise<void>;
38
+ uncheck(options?: {
39
+ readonly timeout?: number;
40
+ }): Promise<void>;
41
+ clear(options?: {
42
+ readonly timeout?: number;
43
+ }): Promise<void>;
44
+ setInputFiles(files: readonly string[], options?: {
45
+ readonly timeout?: number;
46
+ }): Promise<void>;
47
+ scrollIntoViewIfNeeded(options?: {
48
+ readonly timeout?: number;
49
+ }): Promise<void>;
50
+ waitFor(options: {
51
+ readonly state: "visible" | "hidden";
52
+ readonly timeout?: number;
53
+ }): Promise<void>;
54
+ boundingBox(): Promise<{
55
+ readonly x: number;
56
+ readonly y: number;
57
+ readonly width: number;
58
+ readonly height: number;
59
+ } | null>;
60
+ isVisible(): Promise<boolean>;
61
+ isHidden(): Promise<boolean>;
62
+ inputValue(options?: {
63
+ readonly timeout?: number;
64
+ }): Promise<string>;
65
+ textContent(options?: {
66
+ readonly timeout?: number;
67
+ }): Promise<string | null>;
68
+ ariaSnapshot(options?: {
69
+ readonly timeout?: number;
70
+ }): Promise<string>;
71
+ }
72
+ export interface PlaywrightConsoleMessageLike {
73
+ type(): string;
74
+ text(): string;
75
+ location(): {
76
+ readonly url: string;
77
+ readonly lineNumber: number;
78
+ readonly columnNumber: number;
79
+ };
80
+ }
81
+ export interface PlaywrightRequestLike {
82
+ method(): string;
83
+ timing(): {
84
+ readonly startTime: number;
85
+ readonly responseEnd: number;
86
+ };
87
+ }
88
+ export interface PlaywrightResponseLike {
89
+ url(): string;
90
+ status(): number;
91
+ headers(): Record<string, string>;
92
+ request(): PlaywrightRequestLike;
93
+ }
94
+ export interface PlaywrightDialogLike {
95
+ type(): string;
96
+ message(): string;
97
+ accept(text?: string): Promise<void>;
98
+ }
99
+ export interface PlaywrightDownloadLike {
100
+ suggestedFilename(): string;
101
+ saveAs(path: string): Promise<void>;
102
+ }
103
+ export interface PlaywrightMouseLike {
104
+ move(x: number, y: number, options?: {
105
+ readonly steps?: number;
106
+ }): Promise<void>;
107
+ down(options?: {
108
+ readonly button?: "left" | "right" | "middle";
109
+ }): Promise<void>;
110
+ up(options?: {
111
+ readonly button?: "left" | "right" | "middle";
112
+ }): Promise<void>;
113
+ click(x: number, y: number, options?: {
114
+ readonly button?: "left" | "right" | "middle";
115
+ readonly clickCount?: number;
116
+ }): Promise<void>;
117
+ dblclick(x: number, y: number, options?: {
118
+ readonly button?: "left" | "right" | "middle";
119
+ }): Promise<void>;
120
+ wheel(deltaX: number, deltaY: number): Promise<void>;
121
+ }
122
+ export interface PlaywrightKeyboardLike {
123
+ press(key: string, options?: {
124
+ readonly delay?: number;
125
+ }): Promise<void>;
126
+ }
127
+ export interface PlaywrightPageLike {
128
+ url(): string;
129
+ goto(url: string, options?: {
130
+ readonly waitUntil?: "commit" | "domcontentloaded" | "load" | "networkidle";
131
+ readonly timeout?: number;
132
+ }): Promise<unknown>;
133
+ content(): Promise<string>;
134
+ screenshot(options?: {
135
+ readonly type: "png";
136
+ }): Promise<Buffer>;
137
+ title(): Promise<string>;
138
+ close(options?: {
139
+ readonly runBeforeUnload?: boolean;
140
+ }): Promise<void>;
141
+ /**
142
+ * Internal scripts are passed as function objects (real Playwright invokes
143
+ * them with `arg`); plain-string evaluation is only reliable for
144
+ * user-supplied expressions, where a `() => ...` string is NOT invoked.
145
+ */
146
+ evaluate<T = unknown>(script: string | ((arg: never) => unknown), arg?: unknown): Promise<T>;
147
+ waitForLoadState(state: "load" | "domcontentloaded" | "networkidle", options?: {
148
+ readonly timeout?: number;
149
+ }): Promise<void>;
150
+ setViewportSize(viewport: {
151
+ readonly width: number;
152
+ readonly height: number;
153
+ }): Promise<void>;
154
+ viewportSize(): {
155
+ readonly width: number;
156
+ readonly height: number;
157
+ } | null;
158
+ locator(selector: string): PlaywrightLocatorLike;
159
+ getByRole(role: string, options?: {
160
+ readonly name?: string;
161
+ }): PlaywrightLocatorLike;
162
+ getByText(text: string): PlaywrightLocatorLike;
163
+ getByTestId(testId: string): PlaywrightLocatorLike;
164
+ readonly mouse: PlaywrightMouseLike;
165
+ readonly keyboard: PlaywrightKeyboardLike;
166
+ on(event: "console", listener: (message: PlaywrightConsoleMessageLike) => void): void;
167
+ on(event: "response", listener: (response: PlaywrightResponseLike) => void): void;
168
+ on(event: "dialog", listener: (dialog: PlaywrightDialogLike) => void): void;
169
+ on(event: "download", listener: (download: PlaywrightDownloadLike) => void): void;
170
+ }
171
+ export interface PlaywrightCookieLike {
172
+ readonly name: string;
173
+ readonly value: string;
174
+ readonly domain: string;
175
+ readonly path: string;
176
+ readonly expires: number;
177
+ readonly httpOnly: boolean;
178
+ readonly secure: boolean;
179
+ readonly sameSite: string;
180
+ }
181
+ export interface PlaywrightCookieParamV1 {
182
+ readonly name: string;
183
+ readonly value: string;
184
+ readonly domain?: string;
185
+ readonly path?: string;
186
+ readonly expires?: number;
187
+ }
188
+ export interface PlaywrightContextLike {
189
+ newPage(): Promise<PlaywrightPageLike>;
190
+ cookies(urls?: readonly string[]): Promise<readonly PlaywrightCookieLike[]>;
191
+ addCookies(cookies: readonly PlaywrightCookieParamV1[]): Promise<void>;
192
+ clearCookies(): Promise<void>;
193
+ }
194
+ export interface PlaywrightBrowserLike {
195
+ newContext(): Promise<PlaywrightContextLike>;
196
+ close(): Promise<void>;
197
+ }
198
+ export interface PlaywrightModuleLike {
199
+ chromium: {
200
+ launch(options?: {
201
+ readonly headless?: boolean;
202
+ }): Promise<PlaywrightBrowserLike>;
203
+ };
204
+ }
205
+ export interface PlaywrightDriverOptionsV1 {
206
+ /** Host agent directory; the default deps dir is `<agentDir>/browser-deps`. */
207
+ readonly agentDir?: string;
208
+ /** Direct override of the resolution directory; wins over `agentDir`. */
209
+ readonly browserDepsDir?: string;
210
+ readonly headless?: boolean;
211
+ /** Deterministic seam for the module probe (tests; production uses createRequire). */
212
+ readonly resolvePlaywright?: (browserDepsDir: string) => string | undefined;
213
+ /**
214
+ * Deterministic seam replacing the createRequire lazy load (tests and
215
+ * brokered hosts). Production leaves it undefined so the module resolves
216
+ * from `<browser-deps>` at connect time.
217
+ */
218
+ readonly loadPlaywright?: () => PlaywrightModuleLike;
219
+ /** Deterministic seam for download-directory creation (tests inject a no-op). */
220
+ readonly ensureDir?: (dir: string) => void;
221
+ }
222
+ export declare class PlaywrightBrowserDriver implements BrowserDriverV1 {
223
+ private readonly depsDir;
224
+ private readonly headless;
225
+ private readonly resolveModule;
226
+ private readonly loadPlaywright;
227
+ private readonly ensureDir;
228
+ private browser;
229
+ private context;
230
+ private page;
231
+ private pages;
232
+ private pageIndex;
233
+ private consoleRing;
234
+ private networkRing;
235
+ private lastDownload;
236
+ constructor(options?: PlaywrightDriverOptionsV1);
237
+ /** Resolves Playwright, launches Chromium, and opens the context. Idempotent. */
238
+ connect(): Promise<void>;
239
+ /** Idempotent best-effort teardown; never throws. */
240
+ close(): void;
241
+ navigate(input: BrowserNavigateInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
242
+ snapshot(input?: BrowserSnapshotInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
243
+ locatorAction(input: BrowserLocatorActionInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
244
+ assertPostcondition(input: BrowserPostconditionV1 | readonly BrowserPostconditionV1[], options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
245
+ collectEvidence(kind: BrowserEvidenceKindV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
246
+ evaluate(input: BrowserEvaluateInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
247
+ consoleEntries(input: BrowserConsoleReadInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
248
+ networkEntries(input: BrowserNetworkReadInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
249
+ performance(options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
250
+ wait(input: BrowserWaitInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
251
+ drag(input: BrowserDragInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
252
+ mouse(input: BrowserMouseInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
253
+ pressKey(input: BrowserPressKeyInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
254
+ tabs(input: BrowserTabsInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
255
+ storage(input: BrowserStorageInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
256
+ viewport(input: BrowserViewportInputV1, options?: BrowserOperationOptionsV1): Promise<PluginResultV1>;
257
+ private loadModule;
258
+ private downloadsDir;
259
+ private openPage;
260
+ private pushRing;
261
+ private checkCondition;
262
+ }
263
+ /**
264
+ * Production runtime: one driver per session plus the `browser_setup`
265
+ * self-install face. Hosts and tests may inject `spawnSync` and
266
+ * `resolvePlaywright` through the setup options.
267
+ */
268
+ export declare class PlaywrightBrowserRuntimeV1 implements BrowserRuntimeV1 {
269
+ private readonly options;
270
+ private driver;
271
+ constructor(options?: PlaywrightDriverOptionsV1 & Omit<BrowserSetupOptionsV1, "agentDir" | "browserDepsDir">);
272
+ connect(): Promise<BrowserDriverV1>;
273
+ setup(): Promise<BrowserSetupOutcomeV1>;
274
+ }
275
+ //# sourceMappingURL=playwright-driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright-driver.d.ts","sourceRoot":"","sources":["../src/playwright-driver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Playwright-backed driver for the browser capability.\n *\n * Playwright is resolved lazily at connect time through\n * `createRequire(<browser-deps>/package.json)` — the deliberate optional-heavy\n * dependency load point; this package never imports Playwright types, so the\n * surface used here is declared structurally below. A missing module throws\n * the structured guidance message (exact install commands plus the\n * `browser_setup` hint), which the capability layer surfaces verbatim.\n *\n * Bounded results: aria snapshots are capped at 16KiB, DOM captures and all\n * JSON payloads (console/network entries, performance metrics, evaluate\n * results, storage/tab/viewport state) at 64KiB, screenshots at 4MiB of PNG\n * bytes. Styles capture is declared `not_supported_in_this_build` (parameter\n * stays accepted for contract compatibility). Console and network evidence\n * come from 200-entry ring buffers attached to every opened page.\n *\n * Built-in policies (no dedicated tools): `dialog` events are auto-accepted\n * and logged into the console ring as a `[dialog]` entry; `download` events\n * are saved into `<agentDir>/browser-deps/downloads/` and the most recent\n * download filename is attached to snapshot results.\n */\nimport { mkdirSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport {\n\tbuildPluginResultV1,\n\ttype EvidenceKindV1,\n\ttype EvidenceRefV1,\n\ttype PluginResultV1,\n} from \"@agent-forge/plugin-sdk\";\nimport {\n\tBROWSER_DEFAULT_EVALUATE_TIMEOUT_MS_V1,\n\tclampDragStepsV1,\n\tclampViewportDimensionV1,\n\tclampWaitMsV1,\n\tfilterConsoleEntriesV1,\n\tfilterNetworkEntriesV1,\n\tparseScrollDirectiveV1,\n\tparseUploadPathsV1,\n\tscrollWheelDeltaV1,\n\tserializeJsonPayloadV1,\n\tshapePerformanceV1,\n\twaitMissingInput,\n} from \"./operations.ts\";\nimport {\n\tBROWSER_BROWSERS_DIR_NAME_V1,\n\ttype BrowserSetupOptionsV1,\n\tboundedPreviewV1,\n\tbrowserDepsDirFor,\n\tinstallGuidanceMessage,\n\tresolvePlaywrightModule,\n\tsetupBrowserDepsPlaywrightV1,\n} from \"./setup.ts\";\nimport type {\n\tBrowserConsoleEntryV1,\n\tBrowserConsoleReadInputV1,\n\tBrowserDragInputV1,\n\tBrowserDriverV1,\n\tBrowserEvaluateInputV1,\n\tBrowserEvidenceKindV1,\n\tBrowserLocatorActionInputV1,\n\tBrowserLocatorV1,\n\tBrowserMouseInputV1,\n\tBrowserNavigateInputV1,\n\tBrowserNetworkEntryV1,\n\tBrowserNetworkReadInputV1,\n\tBrowserOperationOptionsV1,\n\tBrowserPerformanceRawV1,\n\tBrowserPostconditionV1,\n\tBrowserPressKeyInputV1,\n\tBrowserRuntimeV1,\n\tBrowserSetupOutcomeV1,\n\tBrowserSnapshotInputV1,\n\tBrowserStorageInputV1,\n\tBrowserTabsInputV1,\n\tBrowserViewportInputV1,\n\tBrowserWaitInputV1,\n} from \"./types.ts\";\n\nexport const BROWSER_MAX_SNAPSHOT_CHARS_V1 = 16 * 1024;\nexport const BROWSER_MAX_DOM_CHARS_V1 = 64 * 1024;\nexport const BROWSER_MAX_EVIDENCE_CHARS_V1 = 64 * 1024;\nexport const BROWSER_MAX_SCREENSHOT_BYTES_V1 = 4 * 1024 * 1024;\nexport const BROWSER_RING_BUFFER_SIZE_V1 = 200;\nexport const BROWSER_DOWNLOADS_DIR_NAME_V1 = \"downloads\";\nconst BROWSER_CONDITION_LABEL_CHARS_V1 = 256;\n\n/** Minimal structural surface of the lazily required `playwright` module. */\nexport interface PlaywrightLocatorLike {\n\tclick(options?: { readonly button?: \"left\" | \"right\" | \"middle\"; readonly timeout?: number }): Promise<void>;\n\tdblclick(options?: { readonly timeout?: number }): Promise<void>;\n\tfill(value: string, options?: { readonly timeout?: number }): Promise<void>;\n\ttype(text: string, options?: { readonly delay?: number; readonly timeout?: number }): Promise<void>;\n\tpress(key: string, options?: { readonly timeout?: number }): Promise<void>;\n\thover(options?: { readonly timeout?: number }): Promise<void>;\n\tselectOption(value: string, options?: { readonly timeout?: number }): Promise<void>;\n\tcheck(options?: { readonly timeout?: number }): Promise<void>;\n\tuncheck(options?: { readonly timeout?: number }): Promise<void>;\n\tclear(options?: { readonly timeout?: number }): Promise<void>;\n\tsetInputFiles(files: readonly string[], options?: { readonly timeout?: number }): Promise<void>;\n\tscrollIntoViewIfNeeded(options?: { readonly timeout?: number }): Promise<void>;\n\twaitFor(options: { readonly state: \"visible\" | \"hidden\"; readonly timeout?: number }): Promise<void>;\n\tboundingBox(): Promise<{\n\t\treadonly x: number;\n\t\treadonly y: number;\n\t\treadonly width: number;\n\t\treadonly height: number;\n\t} | null>;\n\tisVisible(): Promise<boolean>;\n\tisHidden(): Promise<boolean>;\n\tinputValue(options?: { readonly timeout?: number }): Promise<string>;\n\ttextContent(options?: { readonly timeout?: number }): Promise<string | null>;\n\tariaSnapshot(options?: { readonly timeout?: number }): Promise<string>;\n}\n\nexport interface PlaywrightConsoleMessageLike {\n\ttype(): string;\n\ttext(): string;\n\tlocation(): { readonly url: string; readonly lineNumber: number; readonly columnNumber: number };\n}\n\nexport interface PlaywrightRequestLike {\n\tmethod(): string;\n\ttiming(): { readonly startTime: number; readonly responseEnd: number };\n}\n\nexport interface PlaywrightResponseLike {\n\turl(): string;\n\tstatus(): number;\n\theaders(): Record<string, string>;\n\trequest(): PlaywrightRequestLike;\n}\n\nexport interface PlaywrightDialogLike {\n\ttype(): string;\n\tmessage(): string;\n\taccept(text?: string): Promise<void>;\n}\n\nexport interface PlaywrightDownloadLike {\n\tsuggestedFilename(): string;\n\tsaveAs(path: string): Promise<void>;\n}\n\nexport interface PlaywrightMouseLike {\n\tmove(x: number, y: number, options?: { readonly steps?: number }): Promise<void>;\n\tdown(options?: { readonly button?: \"left\" | \"right\" | \"middle\" }): Promise<void>;\n\tup(options?: { readonly button?: \"left\" | \"right\" | \"middle\" }): Promise<void>;\n\tclick(\n\t\tx: number,\n\t\ty: number,\n\t\toptions?: { readonly button?: \"left\" | \"right\" | \"middle\"; readonly clickCount?: number },\n\t): Promise<void>;\n\tdblclick(x: number, y: number, options?: { readonly button?: \"left\" | \"right\" | \"middle\" }): Promise<void>;\n\twheel(deltaX: number, deltaY: number): Promise<void>;\n}\n\nexport interface PlaywrightKeyboardLike {\n\tpress(key: string, options?: { readonly delay?: number }): Promise<void>;\n}\n\nexport interface PlaywrightPageLike {\n\turl(): string;\n\tgoto(\n\t\turl: string,\n\t\toptions?: {\n\t\t\treadonly waitUntil?: \"commit\" | \"domcontentloaded\" | \"load\" | \"networkidle\";\n\t\t\treadonly timeout?: number;\n\t\t},\n\t): Promise<unknown>;\n\tcontent(): Promise<string>;\n\tscreenshot(options?: { readonly type: \"png\" }): Promise<Buffer>;\n\ttitle(): Promise<string>;\n\tclose(options?: { readonly runBeforeUnload?: boolean }): Promise<void>;\n\t/**\n\t * Internal scripts are passed as function objects (real Playwright invokes\n\t * them with `arg`); plain-string evaluation is only reliable for\n\t * user-supplied expressions, where a `() => ...` string is NOT invoked.\n\t */\n\tevaluate<T = unknown>(script: string | ((arg: never) => unknown), arg?: unknown): Promise<T>;\n\twaitForLoadState(\n\t\tstate: \"load\" | \"domcontentloaded\" | \"networkidle\",\n\t\toptions?: { readonly timeout?: number },\n\t): Promise<void>;\n\tsetViewportSize(viewport: { readonly width: number; readonly height: number }): Promise<void>;\n\tviewportSize(): { readonly width: number; readonly height: number } | null;\n\tlocator(selector: string): PlaywrightLocatorLike;\n\tgetByRole(role: string, options?: { readonly name?: string }): PlaywrightLocatorLike;\n\tgetByText(text: string): PlaywrightLocatorLike;\n\tgetByTestId(testId: string): PlaywrightLocatorLike;\n\treadonly mouse: PlaywrightMouseLike;\n\treadonly keyboard: PlaywrightKeyboardLike;\n\ton(event: \"console\", listener: (message: PlaywrightConsoleMessageLike) => void): void;\n\ton(event: \"response\", listener: (response: PlaywrightResponseLike) => void): void;\n\ton(event: \"dialog\", listener: (dialog: PlaywrightDialogLike) => void): void;\n\ton(event: \"download\", listener: (download: PlaywrightDownloadLike) => void): void;\n}\n\nexport interface PlaywrightCookieLike {\n\treadonly name: string;\n\treadonly value: string;\n\treadonly domain: string;\n\treadonly path: string;\n\treadonly expires: number;\n\treadonly httpOnly: boolean;\n\treadonly secure: boolean;\n\treadonly sameSite: string;\n}\n\nexport interface PlaywrightCookieParamV1 {\n\treadonly name: string;\n\treadonly value: string;\n\treadonly domain?: string;\n\treadonly path?: string;\n\treadonly expires?: number;\n}\n\nexport interface PlaywrightContextLike {\n\tnewPage(): Promise<PlaywrightPageLike>;\n\tcookies(urls?: readonly string[]): Promise<readonly PlaywrightCookieLike[]>;\n\taddCookies(cookies: readonly PlaywrightCookieParamV1[]): Promise<void>;\n\tclearCookies(): Promise<void>;\n}\n\nexport interface PlaywrightBrowserLike {\n\tnewContext(): Promise<PlaywrightContextLike>;\n\tclose(): Promise<void>;\n}\n\nexport interface PlaywrightModuleLike {\n\tchromium: {\n\t\tlaunch(options?: { readonly headless?: boolean }): Promise<PlaywrightBrowserLike>;\n\t};\n}\n\nexport interface PlaywrightDriverOptionsV1 {\n\t/** Host agent directory; the default deps dir is `<agentDir>/browser-deps`. */\n\treadonly agentDir?: string;\n\t/** Direct override of the resolution directory; wins over `agentDir`. */\n\treadonly browserDepsDir?: string;\n\treadonly headless?: boolean;\n\t/** Deterministic seam for the module probe (tests; production uses createRequire). */\n\treadonly resolvePlaywright?: (browserDepsDir: string) => string | undefined;\n\t/**\n\t * Deterministic seam replacing the createRequire lazy load (tests and\n\t * brokered hosts). Production leaves it undefined so the module resolves\n\t * from `<browser-deps>` at connect time.\n\t */\n\treadonly loadPlaywright?: () => PlaywrightModuleLike;\n\t/** Deterministic seam for download-directory creation (tests inject a no-op). */\n\treadonly ensureDir?: (dir: string) => void;\n}\n\nconst placeholder = (text: string): string => (text.trim() === \"\" ? \"(empty)\" : text);\n\nconst sanitizeUrlV1 = (value: string): string => {\n\ttry {\n\t\tconst parsed = new URL(value);\n\t\treturn `${parsed.origin}${parsed.pathname.slice(0, 512)}`;\n\t} catch {\n\t\treturn value.slice(0, 512);\n\t}\n};\n\nconst conditionLabel = (condition: BrowserPostconditionV1, index: number): string => {\n\tconst raw =\n\t\tcondition.kind === \"url\"\n\t\t\t? `url ${condition.equals !== undefined ? `equals ${JSON.stringify(condition.equals)}` : \"\"}${condition.contains !== undefined ? ` contains ${JSON.stringify(condition.contains)}` : \"\"}`\n\t\t\t: condition.kind === \"element\"\n\t\t\t\t? `element ${condition.locator.kind}=${JSON.stringify(condition.locator.value)} state ${condition.state}`\n\t\t\t\t: condition.kind === \"text\"\n\t\t\t\t\t? `text contains ${JSON.stringify(condition.contains)}`\n\t\t\t\t\t: condition.kind === \"console\"\n\t\t\t\t\t\t? `console contains ${JSON.stringify(condition.contains)}`\n\t\t\t\t\t\t: `network containsUrl ${JSON.stringify(condition.containsUrl)}`;\n\treturn `${index + 1}: ${raw.trim()}`.slice(0, BROWSER_CONDITION_LABEL_CHARS_V1);\n};\n\nconst locatorLabel = (locator: BrowserLocatorV1): string =>\n\t`${locator.kind}=${locator.value.slice(0, 64)}${locator.name === undefined ? \"\" : ` name=${locator.name.slice(0, 64)}`}`;\n\n/**\n * Page-side globals referenced by the in-page evaluate scripts below. These\n * module-scoped declarations exist only so the scripts typecheck (the\n * Node-side tsconfig has no DOM lib); Playwright stringifies the functions\n * and runs them in the browser, where the real globals are present.\n */\ntype PagePerformanceEntryV1 = {\n\treadonly name: string;\n\treadonly startTime: number;\n\treadonly duration: number;\n\treadonly responseStart: number;\n\treadonly domContentLoadedEventEnd: number;\n\treadonly loadEventEnd: number;\n\treadonly transferSize: number;\n};\ntype PageLocalStorageV1 = {\n\tgetItem(key: string): string | null;\n\tsetItem(key: string, value: string): void;\n\tclear(): void;\n};\ndeclare const document: { getElementsByTagName(tag: string): ArrayLike<unknown> };\ndeclare const window: { localStorage: PageLocalStorageV1 };\n\n/**\n * In-page performance collection evaluated through `page.evaluate`. Must stay a\n * closure-free function object: real Playwright stringifies it and runs it in\n * the page. Passing an equivalent `() => {...}` *string* is silently wrong —\n * real Playwright evaluates the string, serializes the resulting function\n * object, and yields `undefined` on the Node side.\n */\nconst collectPerformanceInPage = (): BrowserPerformanceRawV1 => {\n\tconst entries = (type: string): PagePerformanceEntryV1[] =>\n\t\t(performance as unknown as { getEntriesByType(type: string): PagePerformanceEntryV1[] }).getEntriesByType(type);\n\tconst nav = entries(\"navigation\")[0] ?? null;\n\tconst paints = entries(\"paint\").map((entry) => ({ name: String(entry.name), startTimeMs: entry.startTime }));\n\tconst resources = entries(\"resource\").map((entry) => ({\n\t\turl: String(entry.name),\n\t\tdurationMs: entry.duration,\n\t\ttransferBytes: entry.transferSize,\n\t}));\n\treturn {\n\t\tnavigation:\n\t\t\tnav === null\n\t\t\t\t? null\n\t\t\t\t: {\n\t\t\t\t\t\tttfbMs: nav.responseStart - nav.startTime,\n\t\t\t\t\t\tdomContentLoadedMs: nav.domContentLoadedEventEnd - nav.startTime,\n\t\t\t\t\t\tloadMs: nav.loadEventEnd - nav.startTime,\n\t\t\t\t\t},\n\t\tpaints,\n\t\tresources,\n\t\tdomNodes: document.getElementsByTagName(\"*\").length,\n\t};\n};\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * `page.evaluate` with a wall-clock escape: Playwright itself cannot cancel a\n * hung script, so the race rejects after `timeoutMs` and leaves the page-side\n * evaluation to finish on its own.\n */\nasync function evaluateWithTimeout<T>(\n\tpage: PlaywrightPageLike,\n\texpression: string,\n\targ: unknown,\n\ttimeoutMs: number,\n): Promise<T> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst timeout = new Promise<never>((_, reject) => {\n\t\ttimer = setTimeout(() => reject(new Error(`browser_evaluate timed out after ${timeoutMs}ms`)), timeoutMs);\n\t\ttimer.unref?.();\n\t});\n\ttry {\n\t\treturn await Promise.race([page.evaluate<T>(expression, arg), timeout]);\n\t} finally {\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t}\n}\n\nconst isTimeoutError = (error: unknown): error is Error => error instanceof Error && error.name === \"TimeoutError\";\n\nexport class PlaywrightBrowserDriver implements BrowserDriverV1 {\n\tprivate readonly depsDir: string;\n\tprivate readonly headless: boolean;\n\tprivate readonly resolveModule: (browserDepsDir: string) => string | undefined;\n\tprivate readonly loadPlaywright: (() => PlaywrightModuleLike) | undefined;\n\tprivate readonly ensureDir: (dir: string) => void;\n\tprivate browser: PlaywrightBrowserLike | undefined;\n\tprivate context: PlaywrightContextLike | undefined;\n\tprivate page: PlaywrightPageLike | undefined;\n\tprivate pages: PlaywrightPageLike[] = [];\n\tprivate pageIndex = 0;\n\tprivate consoleRing: BrowserConsoleEntryV1[] = [];\n\tprivate networkRing: BrowserNetworkEntryV1[] = [];\n\tprivate lastDownload: string | undefined;\n\n\tconstructor(options: PlaywrightDriverOptionsV1 = {}) {\n\t\tthis.depsDir =\n\t\t\toptions.browserDepsDir ?? (options.agentDir === undefined ? \"\" : browserDepsDirFor(options.agentDir));\n\t\tthis.headless = options.headless ?? true;\n\t\tthis.resolveModule = options.resolvePlaywright ?? resolvePlaywrightModule;\n\t\tthis.loadPlaywright = options.loadPlaywright;\n\t\tthis.ensureDir = options.ensureDir ?? ((dir: string) => mkdirSync(dir, { recursive: true }));\n\t}\n\n\t/** Resolves Playwright, launches Chromium, and opens the context. Idempotent. */\n\tasync connect(): Promise<void> {\n\t\tif (this.browser !== undefined) return;\n\t\tconst playwright = this.loadModule();\n\t\t// Point the Playwright registry at the same isolated directory the setup\n\t\t// flow installs browsers into; never override an explicit user setting.\n\t\t// Without this, `browser_setup` binaries land in <deps>/browsers while\n\t\t// launch silently looks in the shared OS cache and fails on a fresh host.\n\t\tif (this.depsDir !== \"\") {\n\t\t\tprocess.env.PLAYWRIGHT_BROWSERS_PATH ??= join(this.depsDir, BROWSER_BROWSERS_DIR_NAME_V1);\n\t\t}\n\t\tlet browser: PlaywrightBrowserLike;\n\t\ttry {\n\t\t\tbrowser = await playwright.chromium.launch({ headless: this.headless });\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"\";\n\t\t\t// The module resolves but the browser binaries were never installed.\n\t\t\tif (/Executable doesn't exist|playwright install/i.test(message)) {\n\t\t\t\tthrow new Error(installGuidanceMessage(this.depsDir), {\n\t\t\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t\t\t});\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tthis.browser = browser;\n\t\tthis.context = await browser.newContext();\n\t}\n\n\t/** Idempotent best-effort teardown; never throws. */\n\tclose(): void {\n\t\tconst browser = this.browser;\n\t\tthis.browser = undefined;\n\t\tthis.context = undefined;\n\t\tthis.page = undefined;\n\t\tthis.pages = [];\n\t\tthis.pageIndex = 0;\n\t\tthis.consoleRing = [];\n\t\tthis.networkRing = [];\n\t\tthis.lastDownload = undefined;\n\t\tif (browser === undefined) return;\n\t\tvoid browser.close().catch(() => undefined);\n\t}\n\n\tasync navigate(input: BrowserNavigateInputV1, options: BrowserOperationOptionsV1 = {}): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tawait this.connect();\n\t\tconst page = this.page ?? (await this.openPage());\n\t\tawait page.goto(input.url, { waitUntil: input.waitUntil ?? \"load\" });\n\t\treturn buildPluginResultV1({\n\t\t\tversion: 1,\n\t\t\toperationId,\n\t\t\tstatus: \"succeeded\",\n\t\t\tsummary: `Navigated to ${sanitizeUrlV1(page.url())}`,\n\t\t\tartifacts: [],\n\t\t\tevidence: [],\n\t\t});\n\t}\n\n\tasync snapshot(\n\t\tinput: BrowserSnapshotInputV1 = {},\n\t\toptions: BrowserOperationOptionsV1 = {},\n\t): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tconst evidence = evidenceBuilder(operationId);\n\t\tconst parts: string[] = [];\n\t\tif (input.includeStyles === true) parts.push(\"styles: not_supported_in_this_build\");\n\t\tif (input.includeA11y !== false) {\n\t\t\tevidence.add(\n\t\t\t\t\"structure\",\n\t\t\t\tplaceholder(boundedPreviewV1(await page.locator(\"body\").ariaSnapshot(), BROWSER_MAX_SNAPSHOT_CHARS_V1)),\n\t\t\t);\n\t\t\tparts.push(\"a11y\");\n\t\t}\n\t\tif (input.includeDom === true) {\n\t\t\tevidence.add(\"dom\", placeholder(boundedPreviewV1(await page.content(), BROWSER_MAX_DOM_CHARS_V1)));\n\t\t\tparts.push(\"dom\");\n\t\t}\n\t\tif (input.includeScreenshot === true) {\n\t\t\tconst png = await page.screenshot({ type: \"png\" });\n\t\t\tif (png.byteLength > BROWSER_MAX_SCREENSHOT_BYTES_V1) {\n\t\t\t\treturn failed(operationId, \"screenshot_too_large\", \"Screenshot exceeds the 4MiB evidence budget\");\n\t\t\t}\n\t\t\tevidence.add(\"screenshot\", `data:image/png;base64,${png.toString(\"base64\")}`);\n\t\t\tparts.push(\"screenshot\");\n\t\t}\n\t\tif (this.lastDownload !== undefined) {\n\t\t\tconst filename = this.lastDownload.slice(0, 256);\n\t\t\tevidence.add(\"log\", `download: ${filename}`);\n\t\t\tparts.push(`download: ${filename}`);\n\t\t}\n\t\treturn buildPluginResultV1({\n\t\t\tversion: 1,\n\t\t\toperationId,\n\t\t\tstatus: \"succeeded\",\n\t\t\tsummary: `Captured browser snapshot (${parts.length === 0 ? \"none\" : parts.join(\", \")})`,\n\t\t\tartifacts: [],\n\t\t\tevidence: evidence.refs,\n\t\t});\n\t}\n\n\tasync locatorAction(\n\t\tinput: BrowserLocatorActionInputV1,\n\t\toptions: BrowserOperationOptionsV1 = {},\n\t): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tconst missing = actionMissingInput(input);\n\t\tif (missing !== undefined) {\n\t\t\treturn failed(operationId, \"browser_action_invalid\", `The ${input.action} action requires ${missing}`);\n\t\t}\n\t\tconst locator = resolveLocator(page, input.locator);\n\t\tswitch (input.action) {\n\t\t\tcase \"click\":\n\t\t\t\tawait locator.click();\n\t\t\t\tbreak;\n\t\t\tcase \"dblclick\":\n\t\t\t\tawait locator.dblclick();\n\t\t\t\tbreak;\n\t\t\tcase \"rightclick\":\n\t\t\t\tawait locator.click({ button: \"right\" });\n\t\t\t\tbreak;\n\t\t\tcase \"fill\":\n\t\t\t\tawait locator.fill(input.value ?? \"\");\n\t\t\t\tbreak;\n\t\t\tcase \"type\":\n\t\t\t\tawait locator.type(input.value ?? \"\", input.delay === undefined ? {} : { delay: input.delay });\n\t\t\t\tbreak;\n\t\t\tcase \"press\":\n\t\t\t\tawait locator.press(input.key ?? \"\");\n\t\t\t\tbreak;\n\t\t\tcase \"hover\":\n\t\t\t\tawait locator.hover();\n\t\t\t\tbreak;\n\t\t\tcase \"select\":\n\t\t\t\tawait locator.selectOption(input.value ?? \"\");\n\t\t\t\tbreak;\n\t\t\tcase \"check\":\n\t\t\t\tawait locator.check();\n\t\t\t\tbreak;\n\t\t\tcase \"uncheck\":\n\t\t\t\tawait locator.uncheck();\n\t\t\t\tbreak;\n\t\t\tcase \"clear\":\n\t\t\t\tawait locator.clear();\n\t\t\t\tbreak;\n\t\t\tcase \"upload\": {\n\t\t\t\tlet paths: readonly string[];\n\t\t\t\ttry {\n\t\t\t\t\tpaths = parseUploadPathsV1(input.value ?? \"\");\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn failed(\n\t\t\t\t\t\toperationId,\n\t\t\t\t\t\t\"browser_action_invalid\",\n\t\t\t\t\t\terror instanceof Error ? error.message : \"Invalid upload value\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tawait locator.setInputFiles([...paths]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"scroll\": {\n\t\t\t\tlet directive: ReturnType<typeof parseScrollDirectiveV1>;\n\t\t\t\ttry {\n\t\t\t\t\tdirective = parseScrollDirectiveV1(input.value ?? \"\");\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn failed(\n\t\t\t\t\t\toperationId,\n\t\t\t\t\t\t\"browser_action_invalid\",\n\t\t\t\t\t\terror instanceof Error ? error.message : \"Invalid scroll value\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tawait locator.scrollIntoViewIfNeeded();\n\t\t\t\t// Wheel scrolls whatever scrollable container is under the cursor,\n\t\t\t\t// so park the cursor over the target element first; otherwise the\n\t\t\t\t// delta lands on whatever the mouse happened to hover before.\n\t\t\t\tconst scrollBox = await locator.boundingBox();\n\t\t\t\tif (scrollBox !== null) {\n\t\t\t\t\tawait page.mouse.move(scrollBox.x + scrollBox.width / 2, scrollBox.y + scrollBox.height / 2);\n\t\t\t\t}\n\t\t\t\tconst delta = scrollWheelDeltaV1(directive);\n\t\t\t\tawait page.mouse.wheel(delta.deltaX, delta.deltaY);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tconst evidence = evidenceBuilder(operationId);\n\t\tevidence.add(\n\t\t\t\"structure\",\n\t\t\tplaceholder(boundedPreviewV1(await page.locator(\"body\").ariaSnapshot(), BROWSER_MAX_SNAPSHOT_CHARS_V1)),\n\t\t);\n\t\treturn buildPluginResultV1({\n\t\t\tversion: 1,\n\t\t\toperationId,\n\t\t\tstatus: \"succeeded\",\n\t\t\tsummary: `Performed ${input.action}`,\n\t\t\tartifacts: [],\n\t\t\tevidence: evidence.refs,\n\t\t});\n\t}\n\n\tasync assertPostcondition(\n\t\tinput: BrowserPostconditionV1 | readonly BrowserPostconditionV1[],\n\t\toptions: BrowserOperationOptionsV1 = {},\n\t): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tconst conditions = Array.isArray(input) ? input : [input];\n\t\tconst checks: string[] = [];\n\t\tconst failedChecks: string[] = [];\n\t\tfor (const [index, condition] of conditions.entries()) {\n\t\t\tconst label = conditionLabel(condition, index);\n\t\t\tconst passed = await this.checkCondition(page, condition);\n\t\t\tchecks.push(`${label}:${passed ? \"passed\" : \"failed\"}`);\n\t\t\tif (!passed) failedChecks.push(label);\n\t\t}\n\t\tif (failedChecks.length === 0) {\n\t\t\treturn buildPluginResultV1({\n\t\t\t\tversion: 1,\n\t\t\t\toperationId,\n\t\t\t\tstatus: \"succeeded\",\n\t\t\t\tsummary: `All ${checks.length} postcondition(s) passed`,\n\t\t\t\tartifacts: [],\n\t\t\t\tevidence: [],\n\t\t\t\tverification: { version: 1, status: \"passed\", checks },\n\t\t\t});\n\t\t}\n\t\treturn buildPluginResultV1({\n\t\t\tversion: 1,\n\t\t\toperationId,\n\t\t\tstatus: \"failed\",\n\t\t\tsummary: \"One or more postconditions failed\",\n\t\t\tartifacts: [],\n\t\t\tevidence: [],\n\t\t\tverification: { version: 1, status: \"failed\", checks },\n\t\t\terror: {\n\t\t\t\tversion: 1,\n\t\t\t\tcode: \"postcondition_failed\",\n\t\t\t\tmessage: boundedPreviewV1(`Failed postconditions: ${failedChecks.join(\"; \")}`, 2048),\n\t\t\t},\n\t\t});\n\t}\n\n\tasync collectEvidence(\n\t\tkind: BrowserEvidenceKindV1,\n\t\toptions: BrowserOperationOptionsV1 = {},\n\t): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tconst evidence = evidenceBuilder(operationId);\n\t\tif (kind === \"console\") {\n\t\t\tconst text = this.consoleRing.map((entry) => `[${entry.type}] ${entry.text}`).join(\"\\n\");\n\t\t\tevidence.add(\"log\", placeholder(boundedPreviewV1(text, BROWSER_MAX_EVIDENCE_CHARS_V1)));\n\t\t} else if (kind === \"network\") {\n\t\t\tconst text = this.networkRing.map((entry) => `${entry.status} ${entry.url}`).join(\"\\n\");\n\t\t\tevidence.add(\"network\", placeholder(boundedPreviewV1(text, BROWSER_MAX_EVIDENCE_CHARS_V1)));\n\t\t} else {\n\t\t\tevidence.add(\n\t\t\t\t\"structure\",\n\t\t\t\tplaceholder(boundedPreviewV1(await page.locator(\"body\").ariaSnapshot(), BROWSER_MAX_SNAPSHOT_CHARS_V1)),\n\t\t\t);\n\t\t}\n\t\treturn buildPluginResultV1({\n\t\t\tversion: 1,\n\t\t\toperationId,\n\t\t\tstatus: \"succeeded\",\n\t\t\tsummary: `Collected ${kind} evidence`,\n\t\t\tartifacts: [],\n\t\t\tevidence: evidence.refs,\n\t\t});\n\t}\n\n\tasync evaluate(input: BrowserEvaluateInputV1, options: BrowserOperationOptionsV1 = {}): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tconst started = Date.now();\n\t\tconst value = await evaluateWithTimeout(\n\t\t\tpage,\n\t\t\tinput.expression,\n\t\t\tinput.arg,\n\t\t\tBROWSER_DEFAULT_EVALUATE_TIMEOUT_MS_V1,\n\t\t);\n\t\tconst evidence = evidenceBuilder(operationId);\n\t\tevidence.add(\"log\", placeholder(boundedPreviewV1(serializeJsonPayloadV1(value), BROWSER_MAX_EVIDENCE_CHARS_V1)));\n\t\treturn buildPluginResultV1({\n\t\t\tversion: 1,\n\t\t\toperationId,\n\t\t\tstatus: \"succeeded\",\n\t\t\tsummary: `Evaluated expression in ${Date.now() - started}ms`,\n\t\t\tartifacts: [],\n\t\t\tevidence: evidence.refs,\n\t\t});\n\t}\n\n\tasync consoleEntries(\n\t\tinput: BrowserConsoleReadInputV1,\n\t\toptions: BrowserOperationOptionsV1 = {},\n\t): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tlet result: ReturnType<typeof filterConsoleEntriesV1>;\n\t\ttry {\n\t\t\tresult = filterConsoleEntriesV1(this.consoleRing, input);\n\t\t} catch (error) {\n\t\t\treturn failed(\n\t\t\t\toperationId,\n\t\t\t\t\"browser_console_invalid\",\n\t\t\t\terror instanceof Error ? error.message : \"Invalid console filter\",\n\t\t\t);\n\t\t}\n\t\treturn payloadResult(\n\t\t\toperationId,\n\t\t\t`Read console: ${result.total} entry(ies), ${result.returned.length} returned`,\n\t\t\t\"log\",\n\t\t\tresult,\n\t\t);\n\t}\n\n\tasync networkEntries(\n\t\tinput: BrowserNetworkReadInputV1,\n\t\toptions: BrowserOperationOptionsV1 = {},\n\t): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tconst result = filterNetworkEntriesV1(this.networkRing, input);\n\t\treturn payloadResult(\n\t\t\toperationId,\n\t\t\t`Read network: ${result.total} entry(ies), ${result.returned.length} returned`,\n\t\t\t\"network\",\n\t\t\tresult,\n\t\t);\n\t}\n\n\tasync performance(options: BrowserOperationOptionsV1 = {}): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tconst raw = await page.evaluate<BrowserPerformanceRawV1>(collectPerformanceInPage);\n\t\tconst payload = shapePerformanceV1(raw);\n\t\treturn payloadResult(\n\t\t\toperationId,\n\t\t\t`Collected performance metrics (${payload.resources.count} resource(s), ${payload.domNodes} DOM nodes)`,\n\t\t\t\"trace\",\n\t\t\tpayload,\n\t\t);\n\t}\n\n\tasync wait(input: BrowserWaitInputV1, options: BrowserOperationOptionsV1 = {}): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tconst missing = waitMissingInput(input);\n\t\tif (missing !== undefined) {\n\t\t\treturn failed(operationId, \"browser_wait_invalid\", `The ${input.until} wait requires ${missing}`);\n\t\t}\n\t\tconst effectiveMs = clampWaitMsV1(input.ms);\n\t\tconst started = Date.now();\n\t\tlet label: string;\n\t\ttry {\n\t\t\tswitch (input.until) {\n\t\t\t\tcase \"selector\": {\n\t\t\t\t\tconst locator = resolveLocator(page, input.locator!);\n\t\t\t\t\tawait locator.waitFor({ state: \"visible\", timeout: effectiveMs });\n\t\t\t\t\tlabel = `selector ${locatorLabel(input.locator!)}`;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"navigation\":\n\t\t\t\t\tawait page.waitForLoadState(\"load\", { timeout: effectiveMs });\n\t\t\t\t\tlabel = \"navigation\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"networkidle\":\n\t\t\t\t\tawait page.waitForLoadState(\"networkidle\", { timeout: effectiveMs });\n\t\t\t\t\tlabel = \"networkidle\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"time\":\n\t\t\t\t\tawait sleep(effectiveMs);\n\t\t\t\t\tlabel = \"time\";\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (!isTimeoutError(error)) throw error;\n\t\t\treturn failed(\n\t\t\t\toperationId,\n\t\t\t\t\"browser_wait_timeout\",\n\t\t\t\t`Wait for ${input.until} timed out after ${effectiveMs}ms: ${boundedPreviewV1(error.message, 2048)}`,\n\t\t\t);\n\t\t}\n\t\tconst waitedMs = Date.now() - started;\n\t\tconst payload = {\n\t\t\tuntil: input.until,\n\t\t\t...(input.ms === undefined ? {} : { requestedMs: input.ms }),\n\t\t\teffectiveMs,\n\t\t\twaitedMs,\n\t\t};\n\t\treturn payloadResult(operationId, `Waited ${waitedMs}ms for ${label}`, \"log\", payload);\n\t}\n\n\tasync drag(input: BrowserDragInputV1, options: BrowserOperationOptionsV1 = {}): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tconst steps = clampDragStepsV1(input.steps);\n\t\tconst from = resolveLocator(page, input.from);\n\t\tconst to = resolveLocator(page, input.to);\n\t\tawait from.hover();\n\t\tawait page.mouse.down();\n\t\t// Deliberately no `to.hover()` while the button is down: pages that call\n\t\t// setPointerCapture (the standard drag idiom) retarget pointer events to\n\t\t// the from-element, so Playwright's hover hit-target check would retry\n\t\t// until timeout. Move by geometry instead.\n\t\tconst box = await to.boundingBox();\n\t\tif (box !== null) {\n\t\t\tawait page.mouse.move(box.x + box.width / 2, box.y + box.height / 2, { steps });\n\t\t}\n\t\tawait page.mouse.up();\n\t\tconst payload = {\n\t\t\tsteps,\n\t\t\ttoCenter: box === null ? null : { x: box.x + box.width / 2, y: box.y + box.height / 2 },\n\t\t};\n\t\treturn payloadResult(\n\t\t\toperationId,\n\t\t\t`Dragged from ${locatorLabel(input.from)} to ${locatorLabel(input.to)}`,\n\t\t\t\"log\",\n\t\t\tpayload,\n\t\t);\n\t}\n\n\tasync mouse(input: BrowserMouseInputV1, options: BrowserOperationOptionsV1 = {}): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tconst buttonOptions = input.button === undefined ? {} : { button: input.button };\n\t\tlet summary: string;\n\t\tswitch (input.action) {\n\t\t\tcase \"move\":\n\t\t\t\tawait page.mouse.move(input.x, input.y);\n\t\t\t\tsummary = `Mouse moved to (${input.x}, ${input.y})`;\n\t\t\t\tbreak;\n\t\t\tcase \"click\":\n\t\t\t\tawait page.mouse.click(input.x, input.y, buttonOptions);\n\t\t\t\tsummary = `Mouse clicked (${input.button ?? \"left\"}) at (${input.x}, ${input.y})`;\n\t\t\t\tbreak;\n\t\t\tcase \"dblclick\":\n\t\t\t\tawait page.mouse.dblclick(input.x, input.y, buttonOptions);\n\t\t\t\tsummary = `Mouse double-clicked (${input.button ?? \"left\"}) at (${input.x}, ${input.y})`;\n\t\t\t\tbreak;\n\t\t\tcase \"rightclick\":\n\t\t\t\tawait page.mouse.click(input.x, input.y, { ...buttonOptions, button: \"right\" });\n\t\t\t\tsummary = `Mouse right-clicked at (${input.x}, ${input.y})`;\n\t\t\t\tbreak;\n\t\t\tcase \"wheel\": {\n\t\t\t\tconst deltaX = input.deltaX ?? 0;\n\t\t\t\tconst deltaY = input.deltaY ?? 0;\n\t\t\t\tawait page.mouse.wheel(deltaX, deltaY);\n\t\t\t\tsummary = `Mouse wheel (${deltaX}, ${deltaY})`;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn payloadResult(operationId, summary, \"log\", input);\n\t}\n\n\tasync pressKey(input: BrowserPressKeyInputV1, options: BrowserOperationOptionsV1 = {}): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tawait page.keyboard.press(input.key, input.delay === undefined ? {} : { delay: input.delay });\n\t\treturn payloadResult(operationId, `Pressed key ${input.key}`, \"log\", input);\n\t}\n\n\tasync tabs(input: BrowserTabsInputV1, options: BrowserOperationOptionsV1 = {}): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tif (input.action === \"new\") {\n\t\t\tawait this.connect();\n\t\t\tconst page = await this.openPage();\n\t\t\tif (input.url !== undefined) await page.goto(input.url, { waitUntil: \"load\" });\n\t\t\treturn payloadResult(operationId, `Opened tab ${this.pageIndex}`, \"log\", {\n\t\t\t\tindex: this.pageIndex,\n\t\t\t\ttitle: await page.title(),\n\t\t\t\turl: page.url(),\n\t\t\t});\n\t\t}\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tif (input.action === \"list\") {\n\t\t\tconst tabs: { index: number; title: string; url: string }[] = [];\n\t\t\tfor (const [index, entry] of this.pages.entries()) {\n\t\t\t\ttabs.push({ index, title: await entry.title(), url: entry.url() });\n\t\t\t}\n\t\t\treturn payloadResult(operationId, `Listed ${tabs.length} tab(s), current ${this.pageIndex}`, \"log\", {\n\t\t\t\tcurrentIndex: this.pageIndex,\n\t\t\t\ttabs,\n\t\t\t});\n\t\t}\n\t\tif (input.action === \"switch\") {\n\t\t\tif (\n\t\t\t\tinput.index === undefined ||\n\t\t\t\t!Number.isInteger(input.index) ||\n\t\t\t\tinput.index < 0 ||\n\t\t\t\tinput.index >= this.pages.length\n\t\t\t) {\n\t\t\t\treturn failed(\n\t\t\t\t\toperationId,\n\t\t\t\t\t\"browser_tabs_invalid\",\n\t\t\t\t\t`switch requires an index between 0 and ${this.pages.length - 1}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.pageIndex = input.index;\n\t\t\tthis.page = this.pages[this.pageIndex];\n\t\t\treturn payloadResult(operationId, `Switched to tab ${this.pageIndex}`, \"log\", {\n\t\t\t\tindex: this.pageIndex,\n\t\t\t\ttitle: await this.page.title(),\n\t\t\t\turl: this.page.url(),\n\t\t\t});\n\t\t}\n\t\t// close\n\t\tif (\n\t\t\tinput.index === undefined ||\n\t\t\t!Number.isInteger(input.index) ||\n\t\t\tinput.index < 0 ||\n\t\t\tinput.index >= this.pages.length\n\t\t) {\n\t\t\treturn failed(\n\t\t\t\toperationId,\n\t\t\t\t\"browser_tabs_invalid\",\n\t\t\t\t`close requires an index between 0 and ${this.pages.length - 1}`,\n\t\t\t);\n\t\t}\n\t\tconst closing = this.pages[input.index];\n\t\tawait closing.close();\n\t\tthis.pages.splice(input.index, 1);\n\t\tif (this.pageIndex >= input.index) this.pageIndex = Math.max(0, this.pageIndex - 1);\n\t\tthis.page = this.pages[this.pageIndex];\n\t\treturn payloadResult(operationId, `Closed tab ${input.index}; ${this.pages.length} remaining`, \"log\", {\n\t\t\tclosedIndex: input.index,\n\t\t\tremaining: this.pages.length,\n\t\t\t...(this.page === undefined ? {} : { currentIndex: this.pageIndex }),\n\t\t});\n\t}\n\n\tasync storage(input: BrowserStorageInputV1, options: BrowserOperationOptionsV1 = {}): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tswitch (input.action) {\n\t\t\tcase \"get-cookies\": {\n\t\t\t\tconst context = this.context;\n\t\t\t\tif (context === undefined) return sessionRequired(operationId);\n\t\t\t\tconst cookies = await context.cookies();\n\t\t\t\treturn payloadResult(operationId, `Read ${cookies.length} cookie(s)`, \"log\", { cookies });\n\t\t\t}\n\t\t\tcase \"set-cookie\": {\n\t\t\t\tconst context = this.context;\n\t\t\t\tif (context === undefined) return sessionRequired(operationId);\n\t\t\t\tif (input.name === undefined || input.value === undefined || input.domain === undefined) {\n\t\t\t\t\treturn failed(operationId, \"browser_storage_invalid\", \"set-cookie requires name, value, and domain\");\n\t\t\t\t}\n\t\t\t\tawait context.addCookies([\n\t\t\t\t\t{\n\t\t\t\t\t\tname: input.name,\n\t\t\t\t\t\tvalue: input.value,\n\t\t\t\t\t\tdomain: input.domain,\n\t\t\t\t\t\t...(input.path === undefined ? {} : { path: input.path }),\n\t\t\t\t\t\t...(input.expires === undefined ? {} : { expires: input.expires }),\n\t\t\t\t\t},\n\t\t\t\t]);\n\t\t\t\treturn payloadResult(operationId, `Set cookie ${input.name}`, \"log\", {\n\t\t\t\t\tname: input.name,\n\t\t\t\t\tdomain: input.domain,\n\t\t\t\t\t...(input.path === undefined ? {} : { path: input.path }),\n\t\t\t\t\t...(input.expires === undefined ? {} : { expires: input.expires }),\n\t\t\t\t});\n\t\t\t}\n\t\t\tcase \"clear-cookies\": {\n\t\t\t\tconst context = this.context;\n\t\t\t\tif (context === undefined) return sessionRequired(operationId);\n\t\t\t\tawait context.clearCookies();\n\t\t\t\treturn payloadResult(operationId, \"Cleared cookies\", \"log\", { cleared: \"cookies\" });\n\t\t\t}\n\t\t\tcase \"get-local\": {\n\t\t\t\tconst page = this.page;\n\t\t\t\tif (page === undefined) return sessionRequired(operationId);\n\t\t\t\tif (input.key === undefined) {\n\t\t\t\t\treturn failed(operationId, \"browser_storage_invalid\", \"get-local requires a key\");\n\t\t\t\t}\n\t\t\t\tconst value = await page.evaluate<string | null>((key) => window.localStorage.getItem(key), input.key);\n\t\t\t\treturn payloadResult(operationId, `Read localStorage[\"${input.key}\"]`, \"log\", { key: input.key, value });\n\t\t\t}\n\t\t\tcase \"set-local\": {\n\t\t\t\tconst page = this.page;\n\t\t\t\tif (page === undefined) return sessionRequired(operationId);\n\t\t\t\tif (input.key === undefined || input.value === undefined) {\n\t\t\t\t\treturn failed(operationId, \"browser_storage_invalid\", \"set-local requires key and value\");\n\t\t\t\t}\n\t\t\t\tawait page.evaluate(\n\t\t\t\t\t({ key, value }) => {\n\t\t\t\t\t\twindow.localStorage.setItem(key, value);\n\t\t\t\t\t},\n\t\t\t\t\t{ key: input.key, value: input.value },\n\t\t\t\t);\n\t\t\t\treturn payloadResult(operationId, `Set localStorage[\"${input.key}\"]`, \"log\", { key: input.key });\n\t\t\t}\n\t\t\tcase \"clear-local\": {\n\t\t\t\tconst page = this.page;\n\t\t\t\tif (page === undefined) return sessionRequired(operationId);\n\t\t\t\tawait page.evaluate(() => {\n\t\t\t\t\twindow.localStorage.clear();\n\t\t\t\t});\n\t\t\t\treturn payloadResult(operationId, \"Cleared localStorage\", \"log\", { cleared: \"local\" });\n\t\t\t}\n\t\t}\n\t}\n\n\tasync viewport(input: BrowserViewportInputV1, options: BrowserOperationOptionsV1 = {}): Promise<PluginResultV1> {\n\t\tconst operationId = options.operationId ?? \"browser-tool-call\";\n\t\tif (options.signal?.aborted) return cancelled(operationId);\n\t\tconst page = this.page;\n\t\tif (page === undefined) return sessionRequired(operationId);\n\t\tif (input.action === \"get\") {\n\t\t\tconst viewport = page.viewportSize();\n\t\t\treturn payloadResult(operationId, \"Read viewport size\", \"log\", {\n\t\t\t\twidth: viewport?.width ?? null,\n\t\t\t\theight: viewport?.height ?? null,\n\t\t\t});\n\t\t}\n\t\tif (input.width === undefined || input.height === undefined) {\n\t\t\treturn failed(operationId, \"browser_viewport_invalid\", \"set requires width and height\");\n\t\t}\n\t\tconst width = clampViewportDimensionV1(input.width);\n\t\tconst height = clampViewportDimensionV1(input.height);\n\t\tawait page.setViewportSize({ width, height });\n\t\tconst clamped = width !== input.width || height !== input.height;\n\t\treturn payloadResult(operationId, `Viewport set to ${width}x${height}${clamped ? \" (clamped)\" : \"\"}`, \"log\", {\n\t\t\twidth,\n\t\t\theight,\n\t\t\t...(clamped ? { requestedWidth: input.width, requestedHeight: input.height } : {}),\n\t\t});\n\t}\n\n\tprivate loadModule(): PlaywrightModuleLike {\n\t\tif (this.loadPlaywright !== undefined) return this.loadPlaywright();\n\t\tif (this.depsDir === \"\") throw new Error(installGuidanceMessage());\n\t\tif (this.resolveModule(this.depsDir) === undefined) throw new Error(installGuidanceMessage(this.depsDir));\n\t\ttry {\n\t\t\tconst require = createRequire(join(this.depsDir, \"package.json\"));\n\t\t\treturn require(\"playwright\") as PlaywrightModuleLike;\n\t\t} catch (error) {\n\t\t\tthrow new Error(installGuidanceMessage(this.depsDir), {\n\t\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate downloadsDir(): string | undefined {\n\t\treturn this.depsDir === \"\" ? undefined : join(this.depsDir, BROWSER_DOWNLOADS_DIR_NAME_V1);\n\t}\n\n\tprivate async openPage(): Promise<PlaywrightPageLike> {\n\t\tconst context = this.context;\n\t\tif (context === undefined) throw new Error(\"Browser context is not initialized; connect first\");\n\t\tconst page = await context.newPage();\n\t\tpage.on(\"console\", (message) => {\n\t\t\tconst location = message.location();\n\t\t\tthis.pushRing(\n\t\t\t\tthis.consoleRing,\n\t\t\t\t{\n\t\t\t\t\ttype: message.type(),\n\t\t\t\t\ttext: message.text(),\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t...(location.url === \"\" ? {} : { location }),\n\t\t\t\t},\n\t\t\t\tBROWSER_RING_BUFFER_SIZE_V1,\n\t\t\t);\n\t\t});\n\t\tpage.on(\"response\", (response) => {\n\t\t\tthis.pushRing(this.networkRing, networkEntryOf(response), BROWSER_RING_BUFFER_SIZE_V1);\n\t\t});\n\t\t// Built-in dialog policy: auto-accept and keep a bounded trace entry so\n\t\t// assert/evidence tools can observe that a dialog appeared.\n\t\tpage.on(\"dialog\", (dialog) => {\n\t\t\tvoid dialog.accept().catch(() => undefined);\n\t\t\tthis.pushRing(\n\t\t\t\tthis.consoleRing,\n\t\t\t\t{ type: \"dialog\", text: `[dialog] ${dialog.type()}: ${dialog.message()}`, timestamp: Date.now() },\n\t\t\t\tBROWSER_RING_BUFFER_SIZE_V1,\n\t\t\t);\n\t\t});\n\t\t// Built-in download policy: persist into <agentDir>/browser-deps/downloads.\n\t\tpage.on(\"download\", (download) => {\n\t\t\tconst filename = download.suggestedFilename();\n\t\t\tthis.lastDownload = filename;\n\t\t\tconst dir = this.downloadsDir();\n\t\t\tif (dir === undefined) return;\n\t\t\ttry {\n\t\t\t\tthis.ensureDir(dir);\n\t\t\t} catch (error) {\n\t\t\t\tthis.pushRing(\n\t\t\t\t\tthis.consoleRing,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"download\",\n\t\t\t\t\t\ttext: `[download] could not create ${dir}: ${error instanceof Error ? error.message : \"unknown error\"}`,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t},\n\t\t\t\t\tBROWSER_RING_BUFFER_SIZE_V1,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvoid download.saveAs(join(dir, filename)).catch((error: unknown) => {\n\t\t\t\tthis.pushRing(\n\t\t\t\t\tthis.consoleRing,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"download\",\n\t\t\t\t\t\ttext: `[download] could not save ${filename}: ${error instanceof Error ? error.message : \"unknown error\"}`,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t},\n\t\t\t\t\tBROWSER_RING_BUFFER_SIZE_V1,\n\t\t\t\t);\n\t\t\t});\n\t\t});\n\t\tthis.pages.push(page);\n\t\tthis.pageIndex = this.pages.length - 1;\n\t\tthis.page = page;\n\t\treturn page;\n\t}\n\n\tprivate pushRing<T>(ring: T[], entry: T, maximum: number): void {\n\t\tring.push(entry);\n\t\tif (ring.length > maximum) ring.splice(0, ring.length - maximum);\n\t}\n\n\tprivate async checkCondition(page: PlaywrightPageLike, condition: BrowserPostconditionV1): Promise<boolean> {\n\t\tif (condition.kind === \"url\") {\n\t\t\tconst url = page.url();\n\t\t\tif (condition.equals !== undefined && url !== condition.equals) return false;\n\t\t\tif (condition.contains !== undefined && !url.includes(condition.contains)) return false;\n\t\t\treturn true;\n\t\t}\n\t\tif (condition.kind === \"element\") {\n\t\t\tconst locator = resolveLocator(page, condition.locator);\n\t\t\tif (condition.state === \"visible\") return locator.isVisible();\n\t\t\tif (condition.state === \"hidden\") return locator.isHidden();\n\t\t\tconst value = await locator.inputValue();\n\t\t\treturn condition.value === undefined || value === condition.value;\n\t\t}\n\t\tif (condition.kind === \"text\") {\n\t\t\tconst text =\n\t\t\t\tcondition.locator === undefined\n\t\t\t\t\t? ((await page.locator(\"body\").textContent()) ?? \"\")\n\t\t\t\t\t: ((await resolveLocator(page, condition.locator).textContent()) ?? \"\");\n\t\t\treturn text.includes(condition.contains);\n\t\t}\n\t\tif (condition.kind === \"console\") {\n\t\t\treturn this.consoleRing.some(\n\t\t\t\t(entry) =>\n\t\t\t\t\tentry.text.includes(condition.contains) &&\n\t\t\t\t\t(condition.level === undefined || entry.type === condition.level),\n\t\t\t);\n\t\t}\n\t\treturn this.networkRing.some(\n\t\t\t(entry) =>\n\t\t\t\tentry.url.includes(condition.containsUrl) &&\n\t\t\t\t(condition.status === undefined || entry.status === condition.status),\n\t\t);\n\t}\n}\n\n/** Shapes a ring-buffer response event into a bounded network entry. */\nfunction networkEntryOf(response: PlaywrightResponseLike): BrowserNetworkEntryV1 {\n\tconst request = response.request();\n\tlet durationMs: number | undefined;\n\tconst timing = request.timing();\n\tif (timing.responseEnd > 0 && timing.responseEnd >= timing.startTime) {\n\t\tdurationMs = Math.round(timing.responseEnd - timing.startTime);\n\t}\n\tconst contentLength = response.headers()[\"content-length\"];\n\tconst sizeBytes = contentLength === undefined ? undefined : Number.parseInt(contentLength, 10);\n\treturn {\n\t\tmethod: request.method(),\n\t\turl: response.url(),\n\t\tstatus: response.status(),\n\t\ttimestamp: Date.now(),\n\t\t...(durationMs === undefined ? {} : { durationMs }),\n\t\t...(sizeBytes === undefined || Number.isNaN(sizeBytes) ? {} : { sizeBytes }),\n\t};\n}\n\n/** Builds a succeeded result carrying one bounded JSON payload as evidence. */\nfunction payloadResult(operationId: string, summary: string, kind: EvidenceKindV1, payload: unknown): PluginResultV1 {\n\tconst evidence = evidenceBuilder(operationId);\n\tevidence.add(kind, placeholder(boundedPreviewV1(serializeJsonPayloadV1(payload), BROWSER_MAX_EVIDENCE_CHARS_V1)));\n\treturn buildPluginResultV1({\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: \"succeeded\",\n\t\tsummary,\n\t\tartifacts: [],\n\t\tevidence: evidence.refs,\n\t});\n}\n\n/**\n * Production runtime: one driver per session plus the `browser_setup`\n * self-install face. Hosts and tests may inject `spawnSync` and\n * `resolvePlaywright` through the setup options.\n */\nexport class PlaywrightBrowserRuntimeV1 implements BrowserRuntimeV1 {\n\tprivate readonly options: PlaywrightDriverOptionsV1 & BrowserSetupOptionsV1;\n\tprivate driver: PlaywrightBrowserDriver | undefined;\n\n\tconstructor(options: PlaywrightDriverOptionsV1 & Omit<BrowserSetupOptionsV1, \"agentDir\" | \"browserDepsDir\"> = {}) {\n\t\tthis.options = options;\n\t}\n\n\tasync connect(): Promise<BrowserDriverV1> {\n\t\tif (this.driver === undefined) this.driver = new PlaywrightBrowserDriver(this.options);\n\t\tawait this.driver.connect();\n\t\treturn this.driver;\n\t}\n\n\tasync setup(): Promise<BrowserSetupOutcomeV1> {\n\t\treturn setupBrowserDepsPlaywrightV1(this.options);\n\t}\n}\n\nconst actionMissingInput = (input: BrowserLocatorActionInputV1): string | undefined => {\n\tif (\n\t\t(input.action === \"fill\" ||\n\t\t\tinput.action === \"type\" ||\n\t\t\tinput.action === \"select\" ||\n\t\t\tinput.action === \"upload\" ||\n\t\t\tinput.action === \"scroll\") &&\n\t\tinput.value === undefined\n\t)\n\t\treturn \"a value\";\n\tif (input.action === \"press\" && input.key === undefined) return \"a key\";\n\treturn undefined;\n};\n\nconst resolveLocator = (page: PlaywrightPageLike, locator: BrowserLocatorV1): PlaywrightLocatorLike => {\n\tif (locator.kind === \"id\") return page.locator(`#${locator.value}`);\n\tif (locator.kind === \"role\")\n\t\treturn page.getByRole(locator.value, locator.name === undefined ? {} : { name: locator.name });\n\tif (locator.kind === \"text\") return page.getByText(locator.value);\n\tif (locator.kind === \"test-id\") return page.getByTestId(locator.value);\n\treturn page.locator(locator.value);\n};\n\nconst cancelled = (operationId: string): PluginResultV1 =>\n\tfailed(operationId, \"cancelled\", \"Browser operation was cancelled\");\n\nconst sessionRequired = (operationId: string): PluginResultV1 =>\n\tfailed(operationId, \"browser_session_required\", \"No active browser session. Call browser_navigate first.\");\n\nfunction failed(operationId: string, code: string, message: string): PluginResultV1 {\n\treturn buildPluginResultV1({\n\t\tversion: 1,\n\t\toperationId,\n\t\tstatus: code === \"cancelled\" ? \"cancelled\" : \"failed\",\n\t\tsummary: message,\n\t\tartifacts: [],\n\t\tevidence: [],\n\t\terror: { version: 1, code, message },\n\t});\n}\n\nfunction evidenceBuilder(operationId: string): {\n\treadonly refs: EvidenceRefV1[];\n\tadd(kind: EvidenceKindV1, summary: string): void;\n} {\n\tconst refs: EvidenceRefV1[] = [];\n\tlet index = 1;\n\treturn {\n\t\trefs,\n\t\tadd(kind: EvidenceKindV1, summary: string): void {\n\t\t\trefs.push({\n\t\t\t\tversion: 1,\n\t\t\t\tevidenceId: `${operationId}-evidence-${index}`,\n\t\t\t\tkind,\n\t\t\t\toperationId,\n\t\t\t\tsummary,\n\t\t\t});\n\t\t\tindex += 1;\n\t\t},\n\t};\n}\n"]}