testdriver:events

Listen to SDK lifecycle events with wildcard support

SKILL.md

GitHub
<!-- Generated from events.mdx. DO NOT EDIT. -->

Overview

TestDriver uses EventEmitter2 for its event system. Events use a namespace pattern with colons. They permit wildcard listeners.

Use 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 one level. Use ** to match many 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.).

Event Payload
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.

Event Payload
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.

Event Payload
test:start { filePath, timestamp }
test:success Emitted on test completion
test:error Emitted on test failure

Log Events

Emitted for all log output from the SDK.

Event Payload
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.

Event Payload
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.

Event Payload
sandbox:connected No 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:received No 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.

Event Payload
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.

Event Payload
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.

Event Payload
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.

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

Other Events

Event Payload
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:stdout Terminal stdout output
terminal:stderr Terminal 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;
}