Current state
A handful of small bugs, inconsistencies, and brittle bits surfaced during a recent audit of packages/core. Each is small individually; bundling them lets a single PR clear them out.
1. Multiple tool calls in one turn are silently dropped
webAgent.ts:1019:
const toolResult = aiResponse.toolResults[0];
If a provider returns two tool calls in one turn, the second is dropped with no log, no error, no telemetry. The system prompt strongly tells the model not to do this (prompts.ts:213-217), but some providers occasionally do anyway (especially on retries or with certain models). Silent drop makes this hard to debug.
2. isSetupError matches by string
webAgent.ts:1521-1527:
function isSetupError(error: unknown): boolean {
if (!(error instanceof Error)) return false;
return error.message.includes("Failed to generate plan")
|| error.message.includes("No starting URL");
}
String matching for control flow. If anywhere in the planner or navigator path the error text changes (refactor, i18n, etc.), this breaks silently — the setup error path stops rethrowing past execute()'s try/catch and instead surfaces as TASK_FAILED.
3. Image-strip fallback brittleness
webAgent.ts:542-552 catches a specific AI SDK detectMediaType crash:
catch (err: any) {
if (err instanceof TypeError && err.message.includes("substring")) {
// ... strip images from last message, retry
}
}
Tight coupling to the upstream's current error message. If the upstream changes the error type or message text, the fallback silently stops catching the case. A more robust check would also inspect err.name and possibly a cause chain.
4. wait capped at 30s
tools/webActionTools.ts:225-245: seconds: z.number().min(0).max(30).
30 seconds is the upper bound. For genuinely slow sites (some government portals, large file downloads, slow JS-heavy SPAs) this isn't enough. The agent has no wait_for_selector or wait_for_navigation either, so when 30s isn't enough, the only escape is to retry-and-hope.
5. Dead export: actionLoopSystemPrompt
prompts.ts:425:
export const actionLoopSystemPrompt = buildActionLoopSystemPrompt(false, false);
Exported as a constant with all gating flags off. Not referenced from webAgent.ts. Possibly used by downstream consumers, but verify with grep and delete if unused.
6. Misleading scroll guidance in persona prompt
prompts.ts:157-159 tells the agent to "try scrolling or interacting to trigger loading" but the tool set has no scroll action (see separate issue for adding scroll). Either land scroll first and keep this text, or strip the misleading line until scroll lands. This issue covers the strip-if-not-landed-yet path.
The gap
None of these is critical alone. Together they represent technical debt that adds up: silent drops obscure debugging, string-based control flow is fragile, the AI-SDK error-message coupling will eventually break, the 30s wait cap surfaces as opaque agent failures on slow sites.
Proposed scope
A. Log multi-tool-call drops
webAgent.ts:1019:
if (aiResponse.toolResults.length > 1) {
this.logger.warn(
`Provider returned ${aiResponse.toolResults.length} tool calls in one turn; ` +
`only the first ('${aiResponse.toolResults[0].toolName}') will be processed.`,
);
this.eventEmitter.emit(WebAgentEventType.SYSTEM_DEBUG_TOOL_DROP, {
iterationId: this.currentIterationId,
droppedCount: aiResponse.toolResults.length - 1,
droppedTools: aiResponse.toolResults.slice(1).map(t => t.toolName),
});
}
const toolResult = aiResponse.toolResults[0];
(Add a new SYSTEM_DEBUG_TOOL_DROP event type to events.ts.)
B. Replace isSetupError string match with an error subclass
Introduce PlanningError and NoStartingUrlError in errors.ts. Throw those from the relevant code paths in planTask and navigateToStartWithRetry. Replace isSetupError body with error instanceof PlanningError || error instanceof NoStartingUrlError.
C. Harden image-strip detection
Match more conservatively:
const isImageDetectCrash =
err instanceof TypeError &&
(err.message.includes("substring") || err.message.includes("detectMediaType"));
And ideally root-cause-fix the underlying issue: trace why the Uint8Array wrapping at webAgent.ts:838-843 doesn't fully prevent the crash. If it does prevent the crash for all current Buffer states, the fallback becomes truly dead code and can be removed.
D. Bump wait cap to 120s
tools/webActionTools.ts:228: z.number().min(0).max(120). Update the prompt's tool example to mention "up to 120 seconds for slow-loading pages."
If a follow-up issue wants to add wait_for_selector or wait_for_navigation, those are better tools. But bumping the cap is a cheap improvement now.
E. Audit and drop the dead actionLoopSystemPrompt export
rg "actionLoopSystemPrompt" packages/
If only prompts.ts references it, delete the export. If downstream consumers use it, leave it but document why.
F. Decide on persona prompt scroll guidance
If the scroll-action issue lands first, this is moot. Otherwise, strip the misleading line from prompts.ts:157-159.
Implementation notes
- These can be bundled in one PR or split into 6 commits in the same PR.
- Each touches a small, focused area. The harden-image-strip work has the highest investigation cost (verify whether the workaround at line 838-843 is fully effective).
Acceptance criteria
- Multi-tool-call drops emit a
SYSTEM_DEBUG_TOOL_DROP event with the dropped tool names.
isSetupError is removed; error subclasses are used instead.
- Image-strip detection is hardened with broader matching, OR the root cause is verified fixed and the fallback is removed.
wait cap is 120s; prompt example reflects this.
- Dead export is removed (after grep confirms no downstream use).
- Persona prompt scroll guidance is either accurate (scroll exists) or removed.
Effort estimate
1-2 days total for all six.
Related issues
Item F depends on the scroll-action issue. The rest are independent.
Files likely affected
packages/core/src/webAgent.ts
packages/core/src/errors.ts (new subclasses)
packages/core/src/events.ts (new debug event)
packages/core/src/prompts.ts
packages/core/src/tools/webActionTools.ts
packages/core/test/webAgent.test.ts
Current state
A handful of small bugs, inconsistencies, and brittle bits surfaced during a recent audit of
packages/core. Each is small individually; bundling them lets a single PR clear them out.1. Multiple tool calls in one turn are silently dropped
webAgent.ts:1019:If a provider returns two tool calls in one turn, the second is dropped with no log, no error, no telemetry. The system prompt strongly tells the model not to do this (
prompts.ts:213-217), but some providers occasionally do anyway (especially on retries or with certain models). Silent drop makes this hard to debug.2.
isSetupErrormatches by stringwebAgent.ts:1521-1527:String matching for control flow. If anywhere in the planner or navigator path the error text changes (refactor, i18n, etc.), this breaks silently — the setup error path stops rethrowing past
execute()'s try/catch and instead surfaces asTASK_FAILED.3. Image-strip fallback brittleness
webAgent.ts:542-552catches a specific AI SDKdetectMediaTypecrash:Tight coupling to the upstream's current error message. If the upstream changes the error type or message text, the fallback silently stops catching the case. A more robust check would also inspect
err.nameand possibly acausechain.4.
waitcapped at 30stools/webActionTools.ts:225-245:seconds: z.number().min(0).max(30).30 seconds is the upper bound. For genuinely slow sites (some government portals, large file downloads, slow JS-heavy SPAs) this isn't enough. The agent has no
wait_for_selectororwait_for_navigationeither, so when 30s isn't enough, the only escape is to retry-and-hope.5. Dead export:
actionLoopSystemPromptprompts.ts:425:Exported as a constant with all gating flags off. Not referenced from
webAgent.ts. Possibly used by downstream consumers, but verify with grep and delete if unused.6. Misleading scroll guidance in persona prompt
prompts.ts:157-159tells the agent to "try scrolling or interacting to trigger loading" but the tool set has no scroll action (see separate issue for addingscroll). Either land scroll first and keep this text, or strip the misleading line until scroll lands. This issue covers the strip-if-not-landed-yet path.The gap
None of these is critical alone. Together they represent technical debt that adds up: silent drops obscure debugging, string-based control flow is fragile, the AI-SDK error-message coupling will eventually break, the 30s wait cap surfaces as opaque agent failures on slow sites.
Proposed scope
A. Log multi-tool-call drops
webAgent.ts:1019:(Add a new
SYSTEM_DEBUG_TOOL_DROPevent type toevents.ts.)B. Replace
isSetupErrorstring match with an error subclassIntroduce
PlanningErrorandNoStartingUrlErrorinerrors.ts. Throw those from the relevant code paths inplanTaskandnavigateToStartWithRetry. ReplaceisSetupErrorbody witherror instanceof PlanningError || error instanceof NoStartingUrlError.C. Harden image-strip detection
Match more conservatively:
And ideally root-cause-fix the underlying issue: trace why the
Uint8Arraywrapping atwebAgent.ts:838-843doesn't fully prevent the crash. If it does prevent the crash for all current Buffer states, the fallback becomes truly dead code and can be removed.D. Bump
waitcap to 120stools/webActionTools.ts:228:z.number().min(0).max(120). Update the prompt's tool example to mention "up to 120 seconds for slow-loading pages."If a follow-up issue wants to add
wait_for_selectororwait_for_navigation, those are better tools. But bumping the cap is a cheap improvement now.E. Audit and drop the dead
actionLoopSystemPromptexportrg "actionLoopSystemPrompt" packages/If only
prompts.tsreferences it, delete the export. If downstream consumers use it, leave it but document why.F. Decide on persona prompt scroll guidance
If the scroll-action issue lands first, this is moot. Otherwise, strip the misleading line from
prompts.ts:157-159.Implementation notes
Acceptance criteria
SYSTEM_DEBUG_TOOL_DROPevent with the dropped tool names.isSetupErroris removed; error subclasses are used instead.waitcap is 120s; prompt example reflects this.Effort estimate
1-2 days total for all six.
Related issues
Item F depends on the scroll-action issue. The rest are independent.
Files likely affected
packages/core/src/webAgent.tspackages/core/src/errors.ts(new subclasses)packages/core/src/events.ts(new debug event)packages/core/src/prompts.tspackages/core/src/tools/webActionTools.tspackages/core/test/webAgent.test.ts