@ddwang/magnitude-core 0.3.1-ddwang.1

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 (142) hide show
  1. package/dist/actions/desktopActions.d.ts +58 -0
  2. package/dist/actions/desktopActions.js +154 -0
  3. package/dist/actions/index.d.ts +26 -0
  4. package/dist/actions/index.js +11 -0
  5. package/dist/actions/taskActions.d.ts +8 -0
  6. package/dist/actions/taskActions.js +27 -0
  7. package/dist/actions/types.d.ts +31 -0
  8. package/dist/actions/types.js +5 -0
  9. package/dist/actions/util.d.ts +20 -0
  10. package/dist/actions/util.js +297 -0
  11. package/dist/actions/webActions.d.ts +79 -0
  12. package/dist/actions/webActions.js +190 -0
  13. package/dist/agent/browserAgent.d.ts +29 -0
  14. package/dist/agent/browserAgent.js +122 -0
  15. package/dist/agent/errors.d.ts +8 -0
  16. package/dist/agent/errors.js +68 -0
  17. package/dist/agent/index.d.ts +53 -0
  18. package/dist/agent/index.js +377 -0
  19. package/dist/agent/narrator.d.ts +4 -0
  20. package/dist/agent/narrator.js +63 -0
  21. package/dist/agent/pause.test.d.ts +1 -0
  22. package/dist/agent/pause.test.js +194 -0
  23. package/dist/ai/baml_client/async_client.d.ts +73 -0
  24. package/dist/ai/baml_client/async_client.js +152 -0
  25. package/dist/ai/baml_client/async_request.d.ts +35 -0
  26. package/dist/ai/baml_client/async_request.js +98 -0
  27. package/dist/ai/baml_client/config.d.ts +11 -0
  28. package/dist/ai/baml_client/config.js +20 -0
  29. package/dist/ai/baml_client/globals.d.ts +16 -0
  30. package/dist/ai/baml_client/globals.js +37 -0
  31. package/dist/ai/baml_client/index.d.ts +16 -0
  32. package/dist/ai/baml_client/index.js +41 -0
  33. package/dist/ai/baml_client/inlinedbaml.d.ts +19 -0
  34. package/dist/ai/baml_client/inlinedbaml.js +31 -0
  35. package/dist/ai/baml_client/parser.d.ts +53 -0
  36. package/dist/ai/baml_client/parser.js +86 -0
  37. package/dist/ai/baml_client/partial_types.d.ts +61 -0
  38. package/dist/ai/baml_client/partial_types.js +10 -0
  39. package/dist/ai/baml_client/sync_client.d.ts +50 -0
  40. package/dist/ai/baml_client/sync_client.js +100 -0
  41. package/dist/ai/baml_client/sync_request.d.ts +35 -0
  42. package/dist/ai/baml_client/sync_request.js +98 -0
  43. package/dist/ai/baml_client/tracing.d.ts +16 -0
  44. package/dist/ai/baml_client/tracing.js +18 -0
  45. package/dist/ai/baml_client/type_builder.d.ts +38 -0
  46. package/dist/ai/baml_client/type_builder.js +96 -0
  47. package/dist/ai/baml_client/types.d.ts +69 -0
  48. package/dist/ai/baml_client/types.js +15 -0
  49. package/dist/ai/claudeCode.d.ts +1 -0
  50. package/dist/ai/claudeCode.js +186 -0
  51. package/dist/ai/modelHarness.d.ts +39 -0
  52. package/dist/ai/modelHarness.js +189 -0
  53. package/dist/ai/multiModelHarness.d.ts +27 -0
  54. package/dist/ai/multiModelHarness.js +52 -0
  55. package/dist/ai/types.d.ts +90 -0
  56. package/dist/ai/types.js +5 -0
  57. package/dist/ai/util.d.ts +13 -0
  58. package/dist/ai/util.js +165 -0
  59. package/dist/cli.d.ts +0 -0
  60. package/dist/cli.js +3 -0
  61. package/dist/common/actions.d.ts +0 -0
  62. package/dist/common/actions.js +14 -0
  63. package/dist/common/events.d.ts +15 -0
  64. package/dist/common/events.js +3 -0
  65. package/dist/common/failure.d.ts +68 -0
  66. package/dist/common/failure.js +1 -0
  67. package/dist/common/index.d.ts +3 -0
  68. package/dist/common/index.js +3 -0
  69. package/dist/common/retry.d.ts +16 -0
  70. package/dist/common/retry.js +30 -0
  71. package/dist/common/util.d.ts +23 -0
  72. package/dist/common/util.js +132 -0
  73. package/dist/connectors/browserConnector.d.ts +40 -0
  74. package/dist/connectors/browserConnector.js +127 -0
  75. package/dist/connectors/desktopConnector.d.ts +58 -0
  76. package/dist/connectors/desktopConnector.js +68 -0
  77. package/dist/connectors/index.d.ts +10 -0
  78. package/dist/connectors/index.js +2 -0
  79. package/dist/index.cjs +6143 -0
  80. package/dist/index.d.cts +825 -0
  81. package/dist/index.d.ts +20 -0
  82. package/dist/index.js +26 -0
  83. package/dist/index.mjs +6097 -0
  84. package/dist/logger.d.ts +3 -0
  85. package/dist/logger.js +15 -0
  86. package/dist/memory/agentMemory.d.ts +37 -0
  87. package/dist/memory/agentMemory.js +121 -0
  88. package/dist/memory/image.d.ts +26 -0
  89. package/dist/memory/image.js +106 -0
  90. package/dist/memory/index.d.ts +3 -0
  91. package/dist/memory/index.js +3 -0
  92. package/dist/memory/masking.d.ts +7 -0
  93. package/dist/memory/masking.js +141 -0
  94. package/dist/memory/masking.test.d.ts +1 -0
  95. package/dist/memory/masking.test.js +117 -0
  96. package/dist/memory/observation.d.ts +38 -0
  97. package/dist/memory/observation.js +79 -0
  98. package/dist/memory/rendering/index.d.ts +10 -0
  99. package/dist/memory/rendering/index.js +13 -0
  100. package/dist/memory/rendering/renderJsonParts.d.ts +3 -0
  101. package/dist/memory/rendering/renderJsonParts.js +115 -0
  102. package/dist/memory/rendering/renderJsonParts.test.d.ts +1 -0
  103. package/dist/memory/rendering/renderJsonParts.test.js +217 -0
  104. package/dist/memory/rendering/renderXmlParts.d.ts +7 -0
  105. package/dist/memory/rendering/renderXmlParts.js +127 -0
  106. package/dist/memory/serde.d.ts +20 -0
  107. package/dist/memory/serde.js +81 -0
  108. package/dist/memory/util.d.ts +2 -0
  109. package/dist/memory/util.js +17 -0
  110. package/dist/telemetry/events.d.ts +9 -0
  111. package/dist/telemetry/events.js +80 -0
  112. package/dist/telemetry/index.d.ts +6 -0
  113. package/dist/telemetry/index.js +91 -0
  114. package/dist/types.d.ts +30 -0
  115. package/dist/types.js +1 -0
  116. package/dist/util.d.ts +1 -0
  117. package/dist/util.js +12 -0
  118. package/dist/version.d.ts +1 -0
  119. package/dist/version.js +3 -0
  120. package/dist/web/browserProvider.d.ts +23 -0
  121. package/dist/web/browserProvider.js +153 -0
  122. package/dist/web/harness.d.ts +102 -0
  123. package/dist/web/harness.js +340 -0
  124. package/dist/web/stability.d.ts +57 -0
  125. package/dist/web/stability.js +316 -0
  126. package/dist/web/tabs.d.ts +58 -0
  127. package/dist/web/tabs.js +293 -0
  128. package/dist/web/transformer.d.ts +7 -0
  129. package/dist/web/transformer.js +41 -0
  130. package/dist/web/types.d.ts +32 -0
  131. package/dist/web/types.js +2 -0
  132. package/dist/web/util.d.ts +3 -0
  133. package/dist/web/util.js +81 -0
  134. package/dist/web/visualizer/cursor.d.ts +16 -0
  135. package/dist/web/visualizer/cursor.js +152 -0
  136. package/dist/web/visualizer/index.d.ts +22 -0
  137. package/dist/web/visualizer/index.js +47 -0
  138. package/dist/web/visualizer/mouseEffects.d.ts +18 -0
  139. package/dist/web/visualizer/mouseEffects.js +287 -0
  140. package/dist/web/visualizer/typeEffects.d.ts +10 -0
  141. package/dist/web/visualizer/typeEffects.js +190 -0
  142. package/package.json +71 -0
@@ -0,0 +1,340 @@
1
+ import { PageStabilityAnalyzer } from "./stability";
2
+ import { parseTypeContent } from "./util";
3
+ import { ActionVisualizer } from "./visualizer";
4
+ import logger from "@/logger";
5
+ import { TabManager } from "./tabs";
6
+ import { DOMTransformer } from "./transformer";
7
+ import { Image } from '@/memory/image';
8
+ import EventEmitter from "eventemitter3";
9
+ export class WebHarness {
10
+ /**
11
+ * Executes web actions on a page
12
+ * Not responsible for browser lifecycle
13
+ */
14
+ context;
15
+ options;
16
+ stability;
17
+ visualizer;
18
+ transformer;
19
+ tabs;
20
+ events = new EventEmitter();
21
+ constructor(context, options = {}) {
22
+ //this.page = page;
23
+ this.context = context;
24
+ this.options = options;
25
+ this.stability = new PageStabilityAnalyzer({ disableVisualStability: true });
26
+ this.visualizer = new ActionVisualizer(this.context, this.options.visuals ?? {});
27
+ this.transformer = new DOMTransformer();
28
+ this.tabs = new TabManager(context, {
29
+ switchOnActivity: options.switchTabsOnActivity ?? true
30
+ });
31
+ // this.context.on('page', (page: Page) => {
32
+ // this.setActivePage(page);
33
+ // //logger.info('ayo we got a new page');
34
+ // });
35
+ this.tabs.events.on('tabChanged', async (page) => {
36
+ await this.setActivePage(page);
37
+ // need to wait for page to load before evaluating a script
38
+ //page.on('load', () => { this.transformer.setActivePage(page); });
39
+ //console.log('tabs:', await this.tabs.getState())
40
+ }, this);
41
+ }
42
+ async setActivePage(page) {
43
+ logger.trace(`WebHarness active page: ${page.url()}`);
44
+ this.stability.setActivePage(page);
45
+ await this.visualizer.setActivePage(page);
46
+ this.transformer.setActivePage(page);
47
+ this.events.emit('activePageChanged', page);
48
+ }
49
+ async retrieveTabState() {
50
+ return this.tabs.retrieveState();
51
+ }
52
+ // setActivePage(page: Page) {
53
+ // this.page = page;
54
+ // this.stability.setActivePage(this.page);
55
+ // this.visualizer.setActivePage(this.page);
56
+ // }
57
+ async start() {
58
+ // Initialize tab manager first
59
+ await this.tabs.initialize();
60
+ if (this.context.pages().length > 0) {
61
+ // If context already contains a page, set it as active
62
+ this.tabs.setActivePage(this.context.pages()[0]);
63
+ }
64
+ else {
65
+ const page = await this.context.newPage();
66
+ // Force the initial page to be set as active and emit tabChanged
67
+ this.tabs.setActivePage(page);
68
+ }
69
+ await this.visualizer.setup();
70
+ }
71
+ async stop() {
72
+ // Clean up tab manager resources
73
+ this.tabs.destroy();
74
+ }
75
+ get page() {
76
+ return this.tabs.getActivePage();
77
+ }
78
+ async screenshot(options = {}) {
79
+ /**
80
+ * Get b64 encoded string of screenshot (PNG) with screen dimensions
81
+ */
82
+ // Target page, context or browser has been closed
83
+ let dpr;
84
+ let buffer;
85
+ const retries = 3;
86
+ for (let attempt = 0; attempt <= retries; attempt++) {
87
+ try {
88
+ dpr = await this.page.evaluate(() => window.devicePixelRatio);
89
+ buffer = await this.page.screenshot({ type: 'png', ...options });
90
+ break; // Success! Exit the retry loop
91
+ }
92
+ catch (err) {
93
+ // A few possibilities:
94
+ // 1. Target page, context or browser has been closed
95
+ // 2. Page navigation in progress
96
+ // In theory 2 shouldn't shouldn't happen during typical execution as we wait for page load - unless screenshot is triggered at an usual time.
97
+ const error = err;
98
+ if (error.message.includes('Target page, context or browser has been closed')) {
99
+ // Irrecoverable, no point in retrying
100
+ throw new Error("Attempted to take screenshot but page, context or browser is closed");
101
+ }
102
+ if (attempt >= retries) {
103
+ throw new Error(`Unable to capture screenshot after retries, error: ${error.message}`);
104
+ }
105
+ }
106
+ }
107
+ const base64data = buffer.toString('base64');
108
+ const image = Image.fromBase64(base64data);
109
+ // Now, need to rescale the image based on DPR. This is so that:
110
+ // (1) Save on tokens, dont need huge high res images
111
+ // (2) More importantly, clicks happen in the standard resolution space, so need to do this for coordinates to be correct
112
+ // for any agent not using a virtual screen space (e.g. those that aren't Claude)
113
+ const { width, height } = await image.getDimensions();
114
+ const rescaledImage = await image.resize(width / dpr, height / dpr);
115
+ return rescaledImage;
116
+ }
117
+ // async goto(url: string) {
118
+ // // No need to redraw here anymore, the 'load' event listener handles it
119
+ // await this.page.goto(url);
120
+ // }
121
+ async _type(content) {
122
+ /** Util for typing + keypresses */
123
+ const chunks = parseTypeContent(content);
124
+ // Total typing period to make typing more natural, in ms
125
+ const totalTextDelay = 500;
126
+ let totalTextLength = 0;
127
+ for (const chunk of chunks) {
128
+ if (chunk != '<enter>' && chunk != '<tab>') {
129
+ totalTextLength += chunk.length;
130
+ }
131
+ }
132
+ for (const chunk of chunks) {
133
+ if (chunk == '<enter>') {
134
+ await this.page.keyboard.press('Enter');
135
+ }
136
+ else if (chunk == '<tab>') {
137
+ await this.page.keyboard.press('Tab');
138
+ }
139
+ else {
140
+ const chunkProportion = chunk.length / totalTextLength;
141
+ const chunkDelay = totalTextDelay * chunkProportion;
142
+ const chunkCharDelay = chunkDelay / chunk.length;
143
+ await this.page.keyboard.type(chunk, { delay: chunkCharDelay });
144
+ }
145
+ }
146
+ }
147
+ // safer might be Coordinate interface/obj tied to certain screen space dims
148
+ async transformCoordinates({ x, y }) {
149
+ const virtual = this.options.virtualScreenDimensions;
150
+ if (!virtual) {
151
+ return { x, y };
152
+ }
153
+ let vp = this.page.viewportSize();
154
+ if (!vp) {
155
+ vp = await this.page.evaluate(() => ({
156
+ width: window.innerWidth,
157
+ height: window.innerHeight
158
+ }));
159
+ }
160
+ if (!vp)
161
+ throw new Error("Could not get viewport dimensions to transform coordinates");
162
+ return {
163
+ x: x * (vp.width / virtual.width),
164
+ y: y * (vp.height / virtual.height),
165
+ };
166
+ }
167
+ async click({ x, y }, options) {
168
+ if (options?.transform ?? true)
169
+ ({ x, y } = await this.transformCoordinates({ x, y }));
170
+ // console.log("x:", x);
171
+ // console.log("y:", y);
172
+ //await this.visualizer.visualizeAction(x, y);
173
+ //await this.page.mouse.click(x, y);
174
+ //await this.page.mouse.move(x, y, { steps: 20 });
175
+ //console.log('clicking:', x, y);
176
+ // const loc = this.page.getByText('Where are you going?');
177
+ // console.log('found:', loc);
178
+ // await loc.click();
179
+ await this._click(x, y);
180
+ // await this.page.waitForTimeout(1000);
181
+ // await this.page.mouse.click(x, y);
182
+ // await this.page.waitForTimeout(1000);
183
+ // await this.page.mouse.click(x, y);
184
+ // await this.page.waitForTimeout(1000);
185
+ // await this.page.mouse.click(x, y);
186
+ // await this.page.waitForTimeout(1000);
187
+ // await this.page.mouse.click(x, y);
188
+ // await this.page.waitForTimeout(1000);
189
+ // await Promise.all([
190
+ // this.page.mouse.move(x, y, { steps: 20 }),
191
+ // this.visualizer.visualizeAction(x, y),
192
+ // ]);
193
+ // await this.page.mouse.down();
194
+ // await this.page.waitForTimeout(200);
195
+ // await this.page.mouse.up();
196
+ // await this.page.evaluate(({ x, y }) => {
197
+ // // Find the topmost element at the given coordinates
198
+ // const targetElement = document.elementFromPoint(x, y);
199
+ // if (!targetElement) {
200
+ // console.error('No element found at coordinates:', x, y);
201
+ // return;
202
+ // }
203
+ // // Create and dispatch the events with properties that mimic a real click
204
+ // const options = {
205
+ // bubbles: true,
206
+ // cancelable: true,
207
+ // composed: true,
208
+ // // We can't set isTrusted, the browser forces it to false
209
+ // };
210
+ // targetElement.dispatchEvent(new MouseEvent('mouseover', options));
211
+ // targetElement.dispatchEvent(new MouseEvent('mousedown', options));
212
+ // targetElement.dispatchEvent(new MouseEvent('mouseup', options));
213
+ // targetElement.dispatchEvent(new MouseEvent('click', options));
214
+ // }, { x, y });
215
+ await this.waitForStability();
216
+ //await this.visualizer.removeActionVisuals();
217
+ }
218
+ async _click(x, y, options) {
219
+ await Promise.all([
220
+ this.visualizer.moveVirtualCursor(x, y),
221
+ this.page.mouse.move(x, y, { steps: 20 })
222
+ ]);
223
+ // await this.visualizer.moveVirtualCursor(x, y);
224
+ // await this.page.mouse.move(x, y, { steps: 20 });
225
+ await this.visualizer.hideAll(); // hide / show pointer because no-pointer is not always consistent and visualizer can block click
226
+ await this.page.mouse.click(x, y);
227
+ await this.visualizer.showAll();
228
+ }
229
+ async rightClick({ x, y }, options) {
230
+ if (options?.transform ?? true)
231
+ ({ x, y } = await this.transformCoordinates({ x, y }));
232
+ await this._click(x, y, { button: "right" });
233
+ await this.waitForStability();
234
+ }
235
+ async doubleClick({ x, y }, options) {
236
+ if (options?.transform ?? true)
237
+ ({ x, y } = await this.transformCoordinates({ x, y }));
238
+ await this.visualizer.moveVirtualCursor(x, y);
239
+ await this.visualizer.hideAll();
240
+ await this.page.mouse.dblclick(x, y);
241
+ await this.visualizer.showAll();
242
+ await this.waitForStability();
243
+ }
244
+ async drag({ x1, y1, x2, y2 }, options) {
245
+ if (options?.transform ?? true)
246
+ ({ x: x1, y: y1 } = await this.transformCoordinates({ x: x1, y: y1 }));
247
+ if (options?.transform ?? true)
248
+ ({ x: x2, y: y2 } = await this.transformCoordinates({ x: x2, y: y2 }));
249
+ //console.log(`Dragging: (${x1}, ${y1}) -> (${x2}, ${y2})`);
250
+ await this.page.mouse.move(x1, y1, { steps: 1 });
251
+ await this.page.mouse.down();
252
+ await this.visualizer.moveVirtualCursor(x1, y1);
253
+ await this.page.waitForTimeout(500);
254
+ await Promise.all([
255
+ this.page.mouse.move(x2, y2, { steps: 20 }),
256
+ this.visualizer.moveVirtualCursor(x2, y2)
257
+ ]);
258
+ // await this.page.mouse.move(x2, y2, { steps: 100 });
259
+ // await this.visualizer.visualizeAction(x2, y2);
260
+ await this.page.mouse.up();
261
+ await this.waitForStability();
262
+ //await this.visualizer.removeActionVisuals();
263
+ }
264
+ async type({ content }) {
265
+ await this._type(content);
266
+ await this.waitForStability();
267
+ }
268
+ async clickAndType({ x, y, content }, options) {
269
+ // TODO: transforms incorrect for moondream grounding with virtual screen dims (claude) - unsure why
270
+ //console.log(`Pre transform: ${x}, ${y}`);
271
+ if (options?.transform ?? true)
272
+ ({ x, y } = await this.transformCoordinates({ x, y }));
273
+ //console.log(`Post transform: ${x}, ${y}`);
274
+ await this.visualizer.moveVirtualCursor(x, y);
275
+ this._click(x, y);
276
+ await this._type(content);
277
+ await this.waitForStability();
278
+ }
279
+ async scroll({ x, y, deltaX, deltaY }, options) {
280
+ if (options?.transform ?? true)
281
+ ({ x, y } = await this.transformCoordinates({ x, y }));
282
+ await this.visualizer.moveVirtualCursor(x, y);
283
+ await this.page.mouse.move(x, y);
284
+ await this.page.mouse.wheel(deltaX, deltaY);
285
+ await this.waitForStability();
286
+ }
287
+ async switchTab({ index }) {
288
+ await this.tabs.switchTab(index);
289
+ await this.waitForStability();
290
+ }
291
+ async newTab() {
292
+ await this.context.newPage();
293
+ // Reasonable default and less confusing than white about:blank page
294
+ await this.navigate("https://google.com");
295
+ }
296
+ async navigate(url) {
297
+ // Only wait for DOM content on goto since we handle waiting for network idle etc ourselves
298
+ await this.page.goto(url, { waitUntil: 'domcontentloaded' });
299
+ await this.waitForStability();
300
+ }
301
+ async selectAll() {
302
+ await this.page.keyboard.down('ControlOrMeta');
303
+ await this.page.keyboard.press('KeyA');
304
+ await this.page.keyboard.up('ControlOrMeta');
305
+ }
306
+ async enter() {
307
+ await this.page.keyboard.press('Enter');
308
+ }
309
+ async backspace() {
310
+ await this.page.keyboard.press('Backspace');
311
+ }
312
+ async tab() {
313
+ await this.page.keyboard.press('Tab');
314
+ }
315
+ async goBack() {
316
+ await this.page.goBack();
317
+ }
318
+ async executeAction(action) {
319
+ if (action.variant === 'click') {
320
+ await this.click(action);
321
+ }
322
+ else if (action.variant === 'type') {
323
+ await this.clickAndType(action);
324
+ }
325
+ else if (action.variant === 'scroll') {
326
+ await this.scroll(action);
327
+ }
328
+ else if (action.variant === 'tab') {
329
+ await this.switchTab(action);
330
+ }
331
+ else {
332
+ throw Error(`Unhandled web action variant: ${action.variant}`);
333
+ }
334
+ //await this.stability.waitForStability();
335
+ //await this.visualizer.redrawLastPosition();
336
+ }
337
+ async waitForStability(timeout) {
338
+ await this.stability.waitForStability(timeout);
339
+ }
340
+ }
@@ -0,0 +1,57 @@
1
+ import { Page } from 'playwright';
2
+ export interface ImageDiff {
3
+ /** Difference score between 0-1, where 0 means identical */
4
+ difference: number;
5
+ /** Optional error message if comparison failed */
6
+ error?: string;
7
+ }
8
+ export interface PageStabilityOptions {
9
+ /** Threshold below which screenshots are considered similar (0-1) */
10
+ differenceThreshold?: number;
11
+ /** Number of consecutive stable checks required to confirm stability */
12
+ requiredStableChecks?: number;
13
+ /** Interval between stability checks in ms */
14
+ checkInterval?: number;
15
+ /** Whether to log detailed stability information */
16
+ /** Minimum time to wait before getting page state */
17
+ minimumWaitPageLoadTime?: number;
18
+ /** Time to wait for network requests to finish before getting page state */
19
+ waitForNetworkIdleTime?: number;
20
+ /** Maximum time to wait for page load before proceeding anyway */
21
+ maximumWaitPageLoadTime?: number;
22
+ /** Whether to disable visual stability checking */
23
+ disableVisualStability?: boolean;
24
+ }
25
+ /**
26
+ * Utility class to check for visual stability of a page
27
+ */
28
+ export declare class PageStabilityAnalyzer {
29
+ private page;
30
+ private options;
31
+ private lastStart;
32
+ private logger;
33
+ constructor(options?: PageStabilityOptions);
34
+ setActivePage(page: Page): void;
35
+ private log;
36
+ /**
37
+ * Compare two screenshots and return their difference score
38
+ * @returns Difference as float between 0-1, where 0 means identical
39
+ */
40
+ private compareScreenshots;
41
+ /**
42
+ * Wait for the network to become stable
43
+ * This monitors network requests and waits until there's a period of inactivity
44
+ * @param timeout Maximum time to wait for network stability
45
+ */
46
+ waitForNetworkStability(timeout?: number): Promise<void>;
47
+ /**
48
+ * Wait for the page to become visually stable
49
+ * @param timeout Maximum time to wait for stability in ms
50
+ */
51
+ waitForVisualStability(timeout?: number): Promise<void>;
52
+ /**
53
+ * Wait for both network and visual stability
54
+ * @param timeout Maximum time to wait for page load
55
+ */
56
+ waitForStability(timeout?: number): Promise<void>;
57
+ }