feat(coding-agent-tools): implemented browser execution safety controls

- Implemented cell budget clamping for timeouts to prevent stalled browser operations from exceeding execution limits.
- Added `recover` capabilities for tab workers to clear blocking dialogs and safely terminate hung navigations.
- Introduced `//!world=main` support for `tab.evaluate` via Puppeteer patch to allow execution in main execution contexts.
- Improved failure attribution for tab terminations by tracking dialogs, stalled operations, and specific termination reasons.
This commit is contained in:
can1357
2026-07-12 16:27:12 +02:00
parent 69865b609e
commit 0d07da529a
9 changed files with 262 additions and 47 deletions
+30 -17
View File
@@ -1,16 +1,24 @@
diff --git a/node_modules/puppeteer-core/.bun-tag-2e714b457f0bd8e8 b/.bun-tag-2e714b457f0bd8e8
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/node_modules/puppeteer-core/.bun-tag-a797aeb3ca2bd69f b/.bun-tag-a797aeb3ca2bd69f
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lib/puppeteer/api/ElementHandle.js b/lib/puppeteer/api/ElementHandle.js
index 81454fe2af73518f7e76f04cbe3a7f170d735fa5..6f9eedb9fc1344901705bc83cb3cf415e6271e28 100644
index 81454fe2af73518f7e76f04cbe3a7f170d735fa5..ee6b042c87861f00d7fcbb6e3eb6e87af232ebbd 100644
--- a/lib/puppeteer/api/ElementHandle.js
+++ b/lib/puppeteer/api/ElementHandle.js
@@ -102,6 +102,31 @@ import { throwIfDisposed } from '../util/decorators.js';
@@ -102,6 +102,36 @@ import { throwIfDisposed } from '../util/decorators.js';
import { _isElementHandle } from './ElementHandleSymbol.js';
import { JSHandle } from './JSHandle.js';
import { NodeLocator } from './locators/locators.js';
+const MAIN_WORLD_DIRECTIVE = /^\s*(?:(?:\/\/!world=main(?=$|\s))|(?:\/\*!world=main\s*\*\/))/;
+const shouldEvaluateInMainWorld = (pageFunction) => {
+ if (pageFunction instanceof String) {
+ // withSourcePuppeteerURLIfNone tags string page functions via Object.assign,
+ // which boxes the primitive; unbox so the directive check still sees a string.
+ pageFunction = pageFunction.toString();
+ }
+ if (typeof pageFunction !== 'function' && typeof pageFunction !== 'string') {
+ return false;
+ }
@@ -33,11 +41,11 @@ index 81454fe2af73518f7e76f04cbe3a7f170d735fa5..6f9eedb9fc1344901705bc83cb3cf415
+ const arrowIndex = source.indexOf('=>');
+ const bodyStart = source.indexOf('{', arrowIndex >= 0 ? arrowIndex : 0);
+ return bodyStart >= 0 && MAIN_WORLD_DIRECTIVE.test(source.slice(bodyStart + 1));
+};
+}
/**
* A given method will have it's `this` replaced with an isolated version of
* `this` when decorated with this decorator.
@@ -299,6 +324,7 @@ let ElementHandle = (() => {
@@ -299,6 +329,7 @@ let ElementHandle = (() => {
* trying to adopt it multiple times
*/
isolatedHandle = __runInitializers(this, _instanceExtraInitializers);
@@ -45,7 +53,7 @@ index 81454fe2af73518f7e76f04cbe3a7f170d735fa5..6f9eedb9fc1344901705bc83cb3cf415
/**
* @internal
*/
@@ -335,19 +361,37 @@ let ElementHandle = (() => {
@@ -335,19 +366,37 @@ let ElementHandle = (() => {
async getProperties() {
return await this.handle.getProperties();
}
@@ -85,7 +93,7 @@ index 81454fe2af73518f7e76f04cbe3a7f170d735fa5..6f9eedb9fc1344901705bc83cb3cf415
}
/**
* @internal
@@ -371,7 +415,7 @@ let ElementHandle = (() => {
@@ -371,7 +420,7 @@ let ElementHandle = (() => {
* @internal
*/
async dispose() {
@@ -94,7 +102,7 @@ index 81454fe2af73518f7e76f04cbe3a7f170d735fa5..6f9eedb9fc1344901705bc83cb3cf415
}
/**
* @internal
@@ -555,15 +599,27 @@ let ElementHandle = (() => {
@@ -555,15 +604,27 @@ let ElementHandle = (() => {
async $$eval(selector, pageFunction, ...args) {
const env_2 = { stack: [], error: void 0, hasError: false };
try {
@@ -127,15 +135,20 @@ index 81454fe2af73518f7e76f04cbe3a7f170d735fa5..6f9eedb9fc1344901705bc83cb3cf415
]);
return result;
diff --git a/lib/puppeteer/api/Frame.js b/lib/puppeteer/api/Frame.js
index 8698d2a5a976b277344fe42e90ffe985bdf0fbd7..01614143cba96055d0f0c22a1eb903d4265ca15f 100644
index 8698d2a5a976b277344fe42e90ffe985bdf0fbd7..62c3b7ca8ec987cc62ceacec69acf6545bb59b86 100644
--- a/lib/puppeteer/api/Frame.js
+++ b/lib/puppeteer/api/Frame.js
@@ -119,6 +119,31 @@ export var FrameEvent;
@@ -119,6 +119,36 @@ export var FrameEvent;
export const throwIfDetached = throwIfDisposed(frame => {
return `Attempted to use detached Frame '${frame._id}'.`;
});
+const MAIN_WORLD_DIRECTIVE = /^\s*(?:(?:\/\/!world=main(?=$|\s))|(?:\/\*!world=main\s*\*\/))/;
+const shouldEvaluateInMainWorld = (pageFunction) => {
+ if (pageFunction instanceof String) {
+ // withSourcePuppeteerURLIfNone tags string page functions via Object.assign,
+ // which boxes the primitive; unbox so the directive check still sees a string.
+ pageFunction = pageFunction.toString();
+ }
+ if (typeof pageFunction !== 'function' && typeof pageFunction !== 'string') {
+ return false;
+ }
@@ -158,11 +171,11 @@ index 8698d2a5a976b277344fe42e90ffe985bdf0fbd7..01614143cba96055d0f0c22a1eb903d4
+ const arrowIndex = source.indexOf('=>');
+ const bodyStart = source.indexOf('{', arrowIndex >= 0 ? arrowIndex : 0);
+ return bodyStart >= 0 && MAIN_WORLD_DIRECTIVE.test(source.slice(bodyStart + 1));
+};
+}
/**
* Represents a DOM frame.
*
@@ -277,12 +302,21 @@ let Frame = (() => {
@@ -277,12 +307,21 @@ let Frame = (() => {
super();
}
#_document;
@@ -186,7 +199,7 @@ index 8698d2a5a976b277344fe42e90ffe985bdf0fbd7..01614143cba96055d0f0c22a1eb903d4
return document;
});
}
@@ -295,6 +329,7 @@ let Frame = (() => {
@@ -295,6 +334,7 @@ let Frame = (() => {
*/
clearDocumentHandle() {
this.#_document = undefined;
@@ -194,7 +207,7 @@ index 8698d2a5a976b277344fe42e90ffe985bdf0fbd7..01614143cba96055d0f0c22a1eb903d4
}
/**
* @returns The frame element associated with this frame (if any).
@@ -345,8 +380,9 @@ let Frame = (() => {
@@ -345,8 +385,9 @@ let Frame = (() => {
* See {@link Page.evaluateHandle} for details.
*/
async evaluateHandle(pageFunction, ...args) {
@@ -205,7 +218,7 @@ index 8698d2a5a976b277344fe42e90ffe985bdf0fbd7..01614143cba96055d0f0c22a1eb903d4
}
/**
* Behaves identically to {@link Page.evaluate} except it's run within
@@ -355,8 +391,9 @@ let Frame = (() => {
@@ -355,8 +396,9 @@ let Frame = (() => {
* See {@link Page.evaluate} for details.
*/
async evaluate(pageFunction, ...args) {
@@ -216,7 +229,7 @@ index 8698d2a5a976b277344fe42e90ffe985bdf0fbd7..01614143cba96055d0f0c22a1eb903d4
}
/**
* @internal
@@ -458,9 +495,10 @@ let Frame = (() => {
@@ -458,9 +500,10 @@ let Frame = (() => {
* @returns A promise to the result of the function.
*/
async $eval(selector, pageFunction, ...args) {
@@ -228,7 +241,7 @@ index 8698d2a5a976b277344fe42e90ffe985bdf0fbd7..01614143cba96055d0f0c22a1eb903d4
return await document.$eval(selector, pageFunction, ...args);
}
/**
@@ -498,9 +536,10 @@ let Frame = (() => {
@@ -498,9 +541,10 @@ let Frame = (() => {
* @returns A promise to the result of the function.
*/
async $$eval(selector, pageFunction, ...args) {
@@ -240,7 +253,7 @@ index 8698d2a5a976b277344fe42e90ffe985bdf0fbd7..01614143cba96055d0f0c22a1eb903d4
return await document.$$eval(selector, pageFunction, ...args);
}
/**
@@ -577,7 +616,8 @@ let Frame = (() => {
@@ -577,7 +621,8 @@ let Frame = (() => {
* @returns the promise which resolve when the `pageFunction` returns a truthy value.
*/
async waitForFunction(pageFunction, options = {}, ...args) {