testdriver:events

SkillDev tools

Listen to SDK lifecycle events with wildcard support

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the testdriver:events skill

What this skill tells your AI

The instructions your AI receives, as published by testdriverai/testdriverai in ai/skills/testdriver-events/SKILL.md and read by ahel’s review.

Overview

TestDriver uses EventEmitter2 for its event system. Events use a colon-delimited namespace pattern and support wildcard listeners.

Access the emitter through testdriver.emitter:

testdriver.emitter.on('command:start', (data) => {
  console.log(`Running: ${data.command}`);
});

Configuration

The internal emitter is created with:

new EventEmitter2({
  wildcard: true,
  delimiter: ':',
  maxListeners: 20,
  verboseMemoryLeak: false,
  ignoreErrors: false,
});

Wildcard Listeners

Use * to match a single level or ** to match multiple levels:

// Match all log events
testdriver.emitter.on('log:*', (message) => {
  console.log(message);
});

// Match all events in any namespace
testdriver.emitter.on('**', (...args) => {
  console.log('Event:', this.event, args);
});

Event Reference

Command Events

Emitted during the execution of SDK commands (click, type, find, etc.).

EventPayload
command:start{ command, depth, data, timestamp, sourcePosition }
command:success{ command, depth, data, duration, response, timestamp, sourcePosition }
command:error{ command, depth, data, error, duration, timestamp, sourcePosition }
command:status{ command, status: "executing", data, depth, timestamp }
command:progress{ command, status: "completed", timing, data, depth, timestamp }
testdriver.emitter.on('command:start', ({ command, data }) => {
  console.log(`Starting ${command}`, data);
});

testdriver.emitter.on('command:error', ({ command, error, duration }) => {
  console.error(`${command} failed after ${duration}ms: ${error}`);
});

Step Events

Emitted for each AI reasoning step within a command.

EventPayload
step:start{ stepIndex, prompt, commandCount, timestamp, sourcePosition }
step:success{ stepIndex, prompt, commandCount, duration, timestamp, sourcePosition }
step:error{ stepIndex, prompt, error, duration?, timestamp, sourcePosition? }
testdriver.emitter.on('step:start', ({ stepIndex, prompt }) => {
  console.log(`Step ${stepIndex}: ${prompt}`);
});

Test Events

Emitted when a test file execution starts.

EventPayload
test:start{ filePath, timestamp }
test:successEmitted on test completion
test:errorEmitted on test failure

Log Events

Emitted for all log output from the SDK.

EventPayload
log:log(message: string) — general log message
log:warn(message: string) — warning message
log:debug(message: string) — debug output (only when VERBOSE/DEBUG/TD_DEBUG env set)
log:info(message: string) — informational message
log:error(message: string) — error message
log:narration(message: string, overwrite?: boolean) — in-place status line
log:markdown(markdown: string) — full static markdown content
log:markdown:start(streamId: string) — begin streaming markdown
log:markdown:chunk(streamId: string, chunk: string) — incremental chunk
log:markdown:end(streamId: string) — end streaming markdown
// Capture all logs
testdriver.emitter.on('log:*', function (message) {
  console.log(`[${this.event}]`, message);
});

Screen Capture Events

Emitted during screenshot capture.

EventPayload
screen-capture:start{ scale, silent, display }
screen-capture:end{ scale, silent, display }
screen-capture:error{ error, scale, silent, display }

Sandbox Events

Emitted for sandbox WebSocket lifecycle and communication.

EventPayload
sandbox:connectedNo payload — WebSocket connection established
sandbox:authenticated{ traceId } — authentication successful
sandbox:error(err: Error | string) — connection or sandbox error
sandbox:sent(message: object) — WebSocket message sent
sandbox:receivedNo payload — successful message reply received
sandbox:progress{ step, message } — sandbox setup progress
testdriver.emitter.on('sandbox:connected', () => {
  console.log('Connected to sandbox');
});

testdriver.emitter.on('sandbox:progress', ({ step, message }) => {
  console.log(`Sandbox: [${step}] ${message}`);
});

Redraw Events

Emitted during screen stability detection. See Redraw for more details.

EventPayload
redraw:status{ redraw: { enabled, settled, hasChangedFromInitial, consecutiveFramesStable, diffFromInitial, diffFromLast, text }, network: { enabled, settled, rxBytes, txBytes, text }, timeout: { isTimeout, elapsed, max, text } }
redraw:complete{ screenSettled, hasChangedFromInitial, consecutiveFramesStable, networkSettled, isTimeout, timeElapsed }
testdriver.emitter.on('redraw:complete', (result) => {
  if (result.isTimeout) {
    console.warn('Redraw timed out after', result.timeElapsed, 'ms');
  }
});

File Events

Emitted during file load/save operations in the agent.

EventPayload
file:start{ operation: "load" | "save" | "run", filePath, timestamp }
file:stop{ operation, filePath, duration, success, sourceMap?, reason?, timestamp }
file:load{ filePath, size, timestamp }
file:save{ filePath, size, timestamp }
file:diff{ filePath, diff: { patches, sourceMaps, summary: { additions, deletions, modifications } }, timestamp }
file:error{ operation, filePath, error, duration?, timestamp }

Error Events

Emitted for errors at various severity levels.

EventPayload
error:fatal(error: string | Error) — terminates the process
error:general(message: string) — non-fatal error
error:sandbox(err: Error | string) — sandbox/WebSocket error
testdriver.emitter.on('error:*', function (err) {
  console.error(`[${this.event}]`, err);
});

SDK Events

Emitted for API request lifecycle.

EventPayload
sdk:request{ path } — outgoing API request
sdk:response{ path } — API response received
sdk:retry{ path, attempt, error, delayMs } — request retry

Other Events

EventPayload
exit(exitCode: number)0 for success, 1 for failure
status(message: string) — general status updates
mouse-click{ x, y, button, click, double } — mouse click performed
terminal:stdoutTerminal stdout output
terminal:stderrTerminal stderr output

Practical Examples

Custom Test Reporter

const results = [];

testdriver.emitter.on('command:success', ({ command, duration }) => {
  results.push({ command, duration, status: 'pass' });
});

testdriver.emitter.on('command:error', ({ command, duration, error }) => {
  results.push({ command, duration, status: 'fail', error });
});

// After test completes
afterAll(() => {
  console.table(results);
});

Progress Monitoring

testdriver.emitter.on('step:start', ({ stepIndex, prompt }) => {
  process.stdout.write(`\r  Step ${stepIndex}: ${prompt}`);
});

testdriver.emitter.on('command:progress', ({ command, timing }) => {
  process.stdout.write(`\r  ${command} completed in ${timing}ms`);
});

Debug Logging

// Log every event (verbose)
testdriver.emitter.on('**', function (...args) {
  console.debug(`[EVENT] ${this.event}`, ...args);
});

Types

interface CommandStartEvent {
  command: string;
  depth: number;
  data: Record<string, any>;
  timestamp: number;
  sourcePosition: SourcePosition;
}

interface CommandSuccessEvent {
  command: string;
  depth: number;
  data: Record<string, any>;
  duration: number;
  response: any;
  timestamp: number;
  sourcePosition: SourcePosition;
}

interface CommandErrorEvent {
  command: string;
  depth: number;
  data: Record<string, any>;
  error: string;
  duration: number;
  timestamp: number;
  sourcePosition: SourcePosition;
}

interface StepStartEvent {
  stepIndex: number;
  prompt: string;
  commandCount: number;
  timestamp: number;
  sourcePosition: SourcePosition;
}

interface StepSuccessEvent {
  stepIndex: number;
  prompt: string;
  commandCount: number;
  duration: number;
  timestamp: number;
  sourcePosition: SourcePosition;
}

interface RedrawStatusEvent {
  redraw: {
    enabled: boolean;
    settled: boolean;
    hasChangedFromInitial: boolean;
    consecutiveFramesStable: number;
    diffFromInitial: number;
    diffFromLast: number;
    text: string;
  };
  network: {
    enabled: boolean;
    settled: boolean;
    rxBytes: number;
    txBytes: number;
    text: string;
  };
  timeout: {
    isTimeout: boolean;
    elapsed: number;
    max: number;
    text: string;
  };
}

interface RedrawCompleteEvent {
  screenSettled: boolean;
  hasChangedFromInitial: boolean;
  consecutiveFramesStable: number;
  networkSettled: boolean;
  isTimeout: boolean;
  timeElapsed: number;
}

interface SandboxProgressEvent {
  step: string;
  message: string;
}

interface SourcePosition {
  file: string;
  line: number;
  column: number;
}

Signals

GitHub stars
242
Forks
35
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
testdriver-events
Source
github.com/testdriverai/testdriverai