Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 2026-08-21

### Changes

- A browser command that fails now reports a short error instead of Playwright's full retry log.
Click, hover, key press, form and back/reset failures used to carry every wait and retry line,
the same reason repeated once per attempt, and terminal color codes — around 850 characters for
one disabled button. What is left is the element the locator resolved to and the reason it could
not be acted on, so the AI reads the blocker instead of scrolling past bookkeeping.
## 2026-08-20

### Changes
Expand Down
3 changes: 2 additions & 1 deletion src/ai/tester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { detectFocusArea } from '../utils/aria.ts';
import { ErrorPageError, isErrorPage } from '../utils/error-page.ts';
import { createDebug, tag } from '../utils/logger.ts';
import { loop } from '../utils/loop.ts';
import { compactErrorMessage } from '../utils/strings.ts';
import type { Agent, AgentDeps } from './agent.ts';
import type { Captain } from './captain.ts';
import type { Conversation } from './conversation.ts';
Expand Down Expand Up @@ -941,7 +942,7 @@ export class Tester extends TaskAgent implements Agent {
};

if (resetAction.lastError) {
result.error = resetAction.lastError.toString();
result.error = compactErrorMessage(resetAction.lastError);
}

return result;
Expand Down
17 changes: 9 additions & 8 deletions src/ai/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { type Task, TestResult } from '../test-plan.js';
import { LARGE_ARIA_CHANGE_THRESHOLD } from '../utils/aria.ts';
import { isFatalBrowserError } from '../utils/browser-errors.ts';
import { createDebug, tag } from '../utils/logger.js';
import { compactErrorMessage } from '../utils/strings.ts';
import { pause } from '../utils/loop.js';
import { WebElement } from '../utils/web-element.ts';
import type { ToolDeps } from './agent.ts';
Expand Down Expand Up @@ -94,7 +95,7 @@ export function createCodeceptJSTools({ explorer, stateManager, ai }: ToolDeps,
const success = await action.attempt(command, explanation);

const attempt: { command: string; success: boolean; error?: string } = { command, success };
if (action.lastError) attempt.error = action.lastError.toString();
if (action.lastError) attempt.error = errorText(action.lastError);
attempts.push(attempt);

if (success) {
Expand All @@ -120,7 +121,7 @@ export function createCodeceptJSTools({ explorer, stateManager, ai }: ToolDeps,

for (const retryCmd of retryCommands) {
if (!(await action.attempt(retryCmd, explanation))) {
attempts.push({ command: retryCmd, success: false, error: action.lastError?.toString() });
attempts.push({ command: retryCmd, success: false, error: errorText(action.lastError) });
continue;
}
const toolResult = await ActionResult.fromState(stateManager.getCurrentState()!).toToolResult(previousState, retryCmd);
Expand Down Expand Up @@ -198,7 +199,7 @@ export function createCodeceptJSTools({ explorer, stateManager, ai }: ToolDeps,
for (const command of commands) {
const success = await action.attempt(command, explanation);
const attempt: { command: string; success: boolean; error?: string } = { command, success };
if (action.lastError) attempt.error = action.lastError.toString();
if (action.lastError) attempt.error = errorText(action.lastError);
attempts.push(attempt);

if (!success) continue;
Expand Down Expand Up @@ -281,7 +282,7 @@ export function createCodeceptJSTools({ explorer, stateManager, ai }: ToolDeps,
);
}

const errorMsg = `pressKey fallback to type() failed: ${action.lastError?.toString()}`;
const errorMsg = `pressKey fallback to type() failed: ${errorText(action.lastError)}`;
await commitNote(activeNote, TestResult.FAILED, toolResult, action);
return failedToolResult('pressKey', errorMsg, {
...toolResult,
Expand Down Expand Up @@ -327,7 +328,7 @@ export function createCodeceptJSTools({ explorer, stateManager, ai }: ToolDeps,
);
}

const errorMsg = `pressKey() failed: ${action.lastError?.toString()}`;
const errorMsg = `pressKey() failed: ${errorText(action.lastError)}`;
await commitNote(activeNote, TestResult.FAILED, toolResult, action);
return failedToolResult('pressKey', errorMsg, {
...toolResult,
Expand Down Expand Up @@ -407,7 +408,7 @@ export function createCodeceptJSTools({ explorer, stateManager, ai }: ToolDeps,
const toolResult = await ActionResult.fromState(stateManager.getCurrentState()!).toToolResult(previousState, formLocator);

if (action.lastError) {
const message = action.lastError ? String(action.lastError) : 'Unknown error';
const message = errorText(action.lastError);
await commitNote(activeNote, TestResult.FAILED, toolResult, action);

let formSuggestion = 'Look into error message and identify which commands passed and which failed. Continue execution using step-by-step approach using click() and form() tools.';
Expand Down Expand Up @@ -950,7 +951,7 @@ export function createAgentTools({ explorer, stateManager, ai, researcher, navig
}

const failData: Record<string, any> = { suggestion: 'Try reset() to return to the starting page.' };
if (action.lastError) failData.error = action.lastError.toString();
if (action.lastError) failData.error = errorText(action.lastError);
return failedToolResult('back', `Failed to navigate back to ${targetUrl}`, failData);
},
}),
Expand Down Expand Up @@ -1152,7 +1153,7 @@ function transformContainsCommand(command: string): string {
}

function errorText(error: unknown): string {
if (error instanceof Error) return error.toString();
if (error instanceof Error) return compactErrorMessage(error);
return 'Unknown error occurred';
}

Expand Down
36 changes: 36 additions & 0 deletions src/utils/strings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createHash } from 'node:crypto';
import stripAnsi from 'strip-ansi';

export function truncateJson(input: any): string {
if (!input) return '';
Expand Down Expand Up @@ -40,3 +41,38 @@ export function safeFilename(name: string, ext = '', maxBytes = 240): string {
}
return truncated + suffix + ext;
}

export function truncate(text: string, max: number): string {
if (text.length <= max) return text;
return `${text.slice(0, max - 3)}...`;
}

const MAX_COMPACT_ERROR = 400;

export function compactErrorMessage(error: unknown): string {
let text = stripAnsi(String(error));
for (const strip of STRIP_STRATEGIES) {
text = strip(text);
}
return truncate(text, MAX_COMPACT_ERROR);
}

function stripCallLog(text: string): string {
const CALL_LOG = 'Call log:';
const NOISE = ['attempting', 'retrying', 'waiting'];

const [headline, ...log] = text.split(CALL_LOG);
if (!log.length) return text;

const lines = new Set<string>();
for (const line of log.join(CALL_LOG).split('\n')) {
const cleaned = normalizeInlineText(line);
if (!cleaned) continue;
if (NOISE.some((noise) => cleaned.includes(noise))) continue;
lines.add(cleaned);
}

return [headline.trim(), ...lines].join(' ');
}

const STRIP_STRATEGIES = [stripCallLog];
Loading