LLM-powered task execution engine with tool calling, checkpointing, and state persistence.

Runtime Engine

The runtime module is the core execution engine for autonomous agent tasks. It provides an LLM-driven loop that processes goals, calls tools, manages state, and supports checkpointing for resumable long-running tasks.

npm install @codmir/sdk

Quick Start

import { DefaultRunnerEngine } from '@codmir/sdk/runtime';

const engine = new DefaultRunnerEngine({
  llm: myLlmAdapter,
  tools: myToolRegistry,
  state: myStateStore,
  maxIterations: 60,
  onEvent: (event) => console.log(event.type, event.payload),
});

const task = {
  id: 'task_001',
  goal: 'Fix the failing login test',
  state: 'queued' as const,
  createdAt: Date.now(),
  updatedAt: Date.now(),
  steps: [],
};

const completed = await engine.run(task);
console.log(completed.state);   // 'completed' | 'failed' | 'paused'
console.log(completed.result);  // Output from the LLM

Architecture

The runtime uses an iterative loop:

  1. LLM decides — The adapter receives the task, history, and available tools, then returns a decision
  2. Engine acts — Based on the decision: call a tool, finish, or pause
  3. State persists — Each step is recorded; checkpoints are saved for resumability
  4. Loop continues — Until the LLM finishes, max iterations are hit, or the task is cancelled

Task Lifecycle

queued → assigned → running → completed
                           → failed
                           → paused (checkpoint)
                           → cancelled (abort signal)
                           → awaiting_approval

Task Structure

interface RunnerTask {
  id: string;
  goal: string;
  projectId?: string;
  workspace?: string;
  state: RunnerTaskState;
  createdAt: number;
  updatedAt: number;
  metadata?: Record<string, unknown>;
  steps: RunnerTaskStep[];
  result?: unknown;
  error?: string;
}

Task Steps

Every action taken by the engine is recorded as a step:

interface RunnerTaskStep {
  id: string;
  type: 'thinking' | 'tool_call' | 'tool_result' | 'step_pending' | 'state_change' | 'result';
  timestamp: number;
  payload: Record<string, unknown>;
}

LLM Adapter

Implement the RunnerLlmAdapter interface to connect any LLM provider.

interface RunnerLlmAdapter {
  nextAction(input: {
    task: RunnerTask;
    history: RunnerTaskStep[];
    availableTools: Array<{ name: string; description?: string }>;
    context: RunnerExecutionContext;
  }): Promise<RunnerDecision>;
}

The adapter returns one of three decisions:

type RunnerDecision =
  | { kind: 'tool_call'; toolName: string; args: Record<string, unknown>; thinking?: string }
  | { kind: 'finish'; output?: unknown; summary?: string }
  | { kind: 'pause'; reason?: string };

Example Adapter

import Anthropic from '@anthropic-ai/sdk';
import type { RunnerLlmAdapter } from '@codmir/sdk/runtime';

const adapter: RunnerLlmAdapter = {
  async nextAction({ task, history, availableTools }) {
    const anthropic = new Anthropic();
    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 4096,
      system: `You are an agent. Your goal: ${task.goal}`,
      messages: historyToMessages(history),
      tools: availableTools.map(t => ({
        name: t.name,
        description: t.description || '',
        input_schema: { type: 'object' },
      })),
    });

    // Parse response into RunnerDecision
    return parseDecision(response);
  },
};

Tool Registry

Register tools that the LLM can call during execution.

interface RunnerToolRegistry {
  listTools(): Array<{ name: string; description?: string }>;
  executeTool(
    name: string,
    args: Record<string, unknown>,
    context: RunnerExecutionContext,
  ): Promise<unknown>;
}

Example Registry

const tools: RunnerToolRegistry = {
  listTools: () => [
    { name: 'read_file', description: 'Read a file from disk' },
    { name: 'write_file', description: 'Write content to a file' },
    { name: 'run_tests', description: 'Run the test suite' },
  ],
  executeTool: async (name, args) => {
    switch (name) {
      case 'read_file': return await fs.readFile(args.path as string, 'utf-8');
      case 'write_file': return await fs.writeFile(args.path as string, args.content as string);
      case 'run_tests': return await exec('npm test');
      default: throw new Error(`Unknown tool: ${name}`);
    }
  },
};

State Store and Checkpointing

Implement RunnerStateStore for persistent checkpointing. Tasks can be resumed from checkpoints after crashes or restarts.

interface RunnerStateStore {
  saveCheckpoint(checkpoint: LocalTaskCheckpoint): Promise<void>;
  loadCheckpoint(taskId: string): Promise<LocalTaskCheckpoint | null>;
  deleteCheckpoint(taskId: string): Promise<void>;
}

Resume from Checkpoint

// Resume a previously paused or crashed task
const resumed = await engine.resume('task_001');

Execution Context

Pass an AbortSignal to cancel tasks, and metadata for runtime context.

const controller = new AbortController();

const task = await engine.run(myTask, {
  signal: controller.signal,
  metadata: { userId: 'u_123', environment: 'staging' },
});

// Cancel from outside
controller.abort();

Events

Subscribe to runtime events for monitoring, logging, or UI updates.

const engine = new DefaultRunnerEngine({
  llm: adapter,
  tools: registry,
  state: store,
  onEvent: async (event) => {
    console.log(`[${event.type}] Task ${event.taskId}: ${JSON.stringify(event.payload)}`);
    // Send to monitoring dashboard, WebSocket, etc.
  },
});

TypeScript Support

import type {
  RunnerTask,
  RunnerTaskState,
  RunnerTaskStep,
  RunnerEvent,
  RunnerDecision,
  RunnerEngine,
  RunnerLlmAdapter,
  RunnerToolRegistry,
  RunnerStateStore,
  RunnerExecutionContext,
} from '@codmir/sdk/runtime';

import { DefaultRunnerEngine } from '@codmir/sdk/runtime';