Merge pull request #545 from alibaba/feat/execute-js-abort-signal

feat(core): make execute_javascript honor AbortSignal
This commit is contained in:
Simon
2026-06-11 20:45:53 +08:00
committed by GitHub
4 changed files with 16 additions and 11 deletions

View File

@@ -181,12 +181,15 @@ tools.set(
'execute_javascript', 'execute_javascript',
tool({ tool({
description: description:
'Execute JavaScript code on the current page. Supports async/await syntax. Use with caution!', 'Execute JavaScript code on the current page. Supports async/await syntax. Use with caution! ' +
'An `AbortSignal` named `signal` is available in scope: long-running async code MUST honor it ' +
'(e.g. `await fetch(url, { signal })`, or `signal.throwIfAborted()` in loops)',
inputSchema: z.object({ inputSchema: z.object({
script: z.string(), script: z.string(),
}), }),
execute: async function (this: PageAgentCore, input) { execute: async function (this: PageAgentCore, input, { signal }) {
const result = await this.pageController.executeJavascript(input.script) const result = await this.pageController.executeJavascript(input.script, signal)
signal.throwIfAborted()
return result.message return result.message
}, },
}) })

View File

@@ -53,6 +53,8 @@ export class MultiPageAgent extends PageAgentCore {
super({ super({
...config, ...config,
// Disabled: AbortSignal cannot cross contexts
experimentalScriptExecutionTool: false,
pageController: pageController as any, pageController: pageController as any,
customTools: customTools, customTools: customTools,
customSystemPrompt: systemPrompt, customSystemPrompt: systemPrompt,

View File

@@ -133,9 +133,7 @@ export class RemotePageController {
return this.remoteCallDomAction('scroll_horizontally', args) return this.remoteCallDomAction('scroll_horizontally', args)
} }
async executeJavascript(...args: any[]): Promise<DomActionReturn> { // `execute_javascript` is intentionally not implemented: AbortSignal cannot cross context
return this.remoteCallDomAction('execute_javascript', args)
}
/** @note Managed by content script via storage polling. */ /** @note Managed by content script via storage polling. */
async showMask(): Promise<void> {} async showMask(): Promise<void> {}

View File

@@ -376,13 +376,15 @@ export class PageController extends EventTarget {
} }
/** /**
* Execute arbitrary JavaScript on the page * Execute arbitrary JavaScript on the page.
* The optional `signal` is exposed to the script scope so cooperative code
* can abort promptly when the task is stopped.
*/ */
async executeJavascript(script: string): Promise<ActionResult> { async executeJavascript(script: string, signal?: AbortSignal): Promise<ActionResult> {
try { try {
// Wrap script in async function to support await // Wrap script in async function to support await, exposing `signal`.
const asyncFunction = eval(`(async () => { ${script} })`) const asyncFunction = eval(`(async (signal) => { ${script} })`)
const result = await asyncFunction() const result = await asyncFunction(signal)
return { return {
success: true, success: true,
message: `✅ Executed JavaScript. Result: ${result}`, message: `✅ Executed JavaScript. Result: ${result}`,