From 0438bf62652a9de13fd9df9a80ae2c6841e58318 Mon Sep 17 00:00:00 2001 From: Simon <10131203+gaomeng1900@users.noreply.github.com> Date: Thu, 11 Jun 2026 19:15:27 +0800 Subject: [PATCH] fix(core): harden run settlement edge cases from review - install #running before the `running` statuschange fires, so a listener calling stop() immediately awaits the current run - await async mask/highlight cleanup before settling: once settled, the agent must be safely reusable - make the inter-step delay abortable so stop() settles promptly; abort during the delay is classified as `stopped` --- packages/core/src/PageAgentCore.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/core/src/PageAgentCore.ts b/packages/core/src/PageAgentCore.ts index 1294140..9438498 100644 --- a/packages/core/src/PageAgentCore.ts +++ b/packages/core/src/PageAgentCore.ts @@ -220,13 +220,14 @@ export class PageAgentCore extends EventTarget { this.#observations = [] this.#states = { totalWaitTime: 0, lastURL: '', browserState: null } this.#abortController = new AbortController() - - this.#setStatus('running') - this.#emitHistoryChange() + const signal = this.#abortController.signal let resolveRunning!: () => void this.#running = new Promise((r) => (resolveRunning = r)) + this.#setStatus('running') + this.#emitHistoryChange() + // Disable ask_user tool if onAskUser is not set if (!this.onAskUser) this.tools.delete('ask_user') @@ -234,6 +235,8 @@ export class PageAgentCore extends EventTarget { const onAfterStep = this.config.onAfterStep const onBeforeTask = this.config.onBeforeTask const onAfterTask = this.config.onAfterTask + const stepDelay = this.config.stepDelay ?? 0.4 + const maxSteps = this.config.maxSteps let step = 0 let taskResult: ExecutionResult @@ -252,8 +255,9 @@ export class PageAgentCore extends EventTarget { try { console.group(`step: ${step}`) - // inside the try: abort between steps must settle as `stopped` - this.#abortController.signal.throwIfAborted() + if (step > 0) await waitFor(stepDelay, signal) + + signal.throwIfAborted() // observe @@ -276,7 +280,7 @@ export class PageAgentCore extends EventTarget { console.log(chalk.blue.bold('🧠 Thinking...')) this.#emitActivity({ type: 'thinking' }) - const result = await this.#llm.invoke(messages, macroTool, this.#abortController.signal, { + const result = await this.#llm.invoke(messages, macroTool, signal, { toolChoiceName: 'AgentOutput', normalizeResponse: (res) => normalizeResponse(res, this.tools), }) @@ -340,7 +344,7 @@ export class PageAgentCore extends EventTarget { } step++ - if (step > this.config.maxSteps) { + if (step > maxSteps) { const message = 'Step count exceeded maximum limit' console.error(message) this.#emitActivity({ type: 'error', message: message }) @@ -350,8 +354,6 @@ export class PageAgentCore extends EventTarget { finalStatus = 'error' break } - - await waitFor(this.config.stepDelay ?? 0.4) } // while await onAfterTask?.(this, taskResult) @@ -362,8 +364,8 @@ export class PageAgentCore extends EventTarget { finalStatus = 'error' throw error } finally { - suppress(() => this.pageController.cleanUpHighlights()) - suppress(() => this.pageController.hideMask()) + await suppress(() => this.pageController.cleanUpHighlights()) + await suppress(() => this.pageController.hideMask()) this.#abortController.abort() resolveRunning() this.#setStatus(finalStatus)