Skip to main content

Observability

4 min read

Breakpoints & Checkpoints

Pause agent execution at key points for inspection, and save/restore full orchestrator state with checkpoints.


Breakpoints

Breakpoints pause execution at specific lifecycle points so you can inspect state, modify input, or skip steps entirely.

Breakpoint Types

Single-agent types:

TypeWhen it fires
pre_input_guardrailsBefore input guardrails run
pre_agent_runBefore the agent executes
pre_output_guardrailsBefore output guardrails run
post_runAfter the agent completes

Multi-agent types (additional):

TypeWhen it fires
pre_handoffBefore an agent-to-agent handoff
pre_pattern_stepBefore each step in a pattern execution

Configuration

import { createAgentOrchestrator } from '@directive-run/ai';
import type { BreakpointConfig } from '@directive-run/ai/multi-agent';

const orchestrator = createAgentOrchestrator({
  runner,
  debug: true,

  breakpoints: [
    { type: 'pre_agent_run', label: 'Before agent runs' },
    {
      type: 'pre_output_guardrails',
      when: (context) => context.input.includes('sensitive'),
      label: 'Sensitive content check',
    },
  ],

  onBreakpoint: (request) => {
    console.log(`Breakpoint hit: ${request.type} for ${request.agentId}`);
    console.log(`Input: ${request.input}`);
  },

  breakpointTimeoutMs: 300000,  // 5 minutes (default)
});

Resuming and Cancelling

// List pending breakpoints
const pending = orchestrator.getPendingBreakpoints();

for (const bp of pending) {
  console.log(`${bp.id}: ${bp.type}${bp.label}`);
}

// Resume with optional modifications
orchestrator.resumeBreakpoint(bp.id, {
  input: 'Modified input',  // Override the input
  skip: false,               // Set to true to skip the step entirely
});

// Cancel a breakpoint (aborts the operation)
orchestrator.cancelBreakpoint(bp.id, 'User cancelled');

BreakpointConfig

interface BreakpointConfig<T extends BreakpointType = BreakpointType> {
  type: T;
  when?: (context: BreakpointContext) => boolean;  // Conditional – fires only when true
  label?: string;                                   // Human-readable label
}

BreakpointContext

interface BreakpointContext {
  agentId: string;
  agentName: string;
  input: string;
  state: Record<string, unknown>;
  breakpointType: BreakpointType;
  patternId?: string;     // Set during pattern execution
  handoff?: HandoffRequest;  // Set during handoffs
}

BreakpointRequest

interface BreakpointRequest {
  id: string;
  type: BreakpointType;
  agentId: string;
  input: string;
  label?: string;
  requestedAt: number;
}

BreakpointModifications

interface BreakpointModifications {
  input?: string;   // Override the agent input
  skip?: boolean;   // Skip this step entirely
}

Multi-Agent Breakpoints

The multi-agent orchestrator supports two additional breakpoint types (pre_handoff and pre_pattern_step) and can filter by agent or pattern ID:

import { createMultiAgentOrchestrator } from '@directive-run/ai/multi-agent';

const orchestrator = createMultiAgentOrchestrator({
  runner,
  agents: {
    researcher: { agent: researcher },
    writer: { agent: writer },
  },
  breakpoints: [
    { type: 'pre_handoff', agentIds: ['researcher'] },
    { type: 'pre_pattern_step', patternIds: ['pipeline'] },
  ],
  onBreakpoint: (req) => console.log('Breakpoint:', req.type, req.id),
});

Configuration Options

OptionTypeDefaultDescription
breakpointsBreakpointConfig[][]Breakpoint definitions
onBreakpoint(request) => voidCallback when breakpoint fires
breakpointTimeoutMsnumber300000Timeout before breakpoint auto-cancels (ms)

Breakpoint history is capped at 200 entries (MAX_BREAKPOINT_HISTORY).

Matching

import { matchBreakpoint } from '@directive-run/ai/multi-agent';

const match = matchBreakpoint(breakpoints, 'pre_agent_run', context);
if (match) {
  console.log(`Matched: ${match.label}`);
}

Checkpoints

Save and restore full orchestrator state – system facts, timeline events, memory, and orchestrator-specific local state.

Creating Checkpoints

const checkpoint = await orchestrator.checkpoint({ label: 'Before experiment' });

console.log(checkpoint.id);         // Unique ID
console.log(checkpoint.createdAt);  // Timestamp
console.log(checkpoint.label);      // 'Before experiment'

Restoring Checkpoints

orchestrator.restore(checkpoint, {
  restoreTimeline: true,  // Also restore timeline events (default: true)
});

Checkpoint Shape

interface Checkpoint {
  version: 1;
  id: string;
  createdAt: string;
  label?: string;
  systemExport: string;        // Serialized Directive System state
  timelineExport: string;      // Serialized timeline events
  localState: CheckpointLocalState;
  memoryExport: string | null; // Serialized memory state
  orchestratorType: 'single' | 'multi';
}

Multi-Agent Local State

For multi-agent orchestrators, localState includes:

interface MultiAgentCheckpointLocalState {
  type: 'multi';
  globalTokenCount: number;
  globalStatus: string;
  agentStates: Record<string, unknown>;
  handoffCounter: number;
  pendingHandoffs: HandoffRequest[];
  handoffResults: HandoffResult[];
  roundRobinCounters: Record<string, number>;
}

Checkpoint Store

Persist checkpoints with the CheckpointStore interface:

import { InMemoryCheckpointStore } from '@directive-run/ai/multi-agent';
import type { CheckpointStore } from '@directive-run/ai/multi-agent';

const store = new InMemoryCheckpointStore({ maxCheckpoints: 50 });

// Save
await store.save(checkpoint);

// Load
const loaded = await store.load(checkpoint.id);

// List all (returns { id, label, createdAt } summaries)
const all = await store.list();

// Delete
await store.delete(checkpoint.id);

// Clear all
await store.clear();

Custom Store

Implement the CheckpointStore interface for persistent backends:

const store: CheckpointStore = {
  save: async (checkpoint) => {
    await db.insert('checkpoints', checkpoint);

    return checkpoint.id;
  },
  load: async (id) => {
    return await db.findOne('checkpoints', { id });
  },
  list: async () => {
    return await db.find('checkpoints', {}, { select: ['id', 'label', 'createdAt'] });
  },
  delete: async (id) => {
    const deleted = await db.delete('checkpoints', { id });

    return deleted > 0;
  },
  clear: async () => {
    await db.deleteAll('checkpoints');
  },
};

Validation

import { validateCheckpoint, createCheckpointId } from '@directive-run/ai/multi-agent';

// Type guard with prototype pollution protection
if (validateCheckpoint(data)) {
  orchestrator.restore(data);
}

// Generate a unique checkpoint ID
const id = createCheckpointId();

Timeline Integration

Breakpoints emit breakpoint_hit and breakpoint_resumed events on the debug timeline:

const breakpointEvents = timeline.getEventsByType('breakpoint_hit');
const resumeEvents = timeline.getEventsByType('breakpoint_resumed');

Next Steps

  • Debug Timeline – Event recording and time-travel
  • DevTools – Visual debugging UI
  • Testing – Breakpoint simulation in tests
Previous
Pattern Checkpoints

Stay in the loop. Sign up for our newsletter.

We care about your data. We'll never share your email.

Powered by Directive. This signup uses a Directive module with facts, derivations, constraints, and resolvers – zero useState, zero useEffect. Read how it works

Directive - Constraint-Driven Runtime for TypeScript | AI Guardrails & State Management