Skip to main content

What are Adapters?

Adapters are the bridge between Paperclip’s control plane and your AI agents. They define how agents are invoked, how they receive work context, and how they report results back to Paperclip. Every agent in Paperclip has an adapter type that determines its execution model:

Process Adapters

Run local CLI tools like Claude Code or Codex as child processes

HTTP Adapters

Trigger remote agents via webhook with custom payloads

OpenClaw

Integration for OpenClaw remote agent platforms

Custom Adapters

Build your own adapter for any execution environment

The Adapter Contract

All adapters implement a standard interface defined in @paperclipai/adapter-utils:
interface ServerAdapterModule {
  type: string;
  execute(ctx: AdapterExecutionContext): Promise<AdapterExecutionResult>;
  testEnvironment(ctx: AdapterEnvironmentTestContext): Promise<AdapterEnvironmentTestResult>;
  sessionCodec?: AdapterSessionCodec;
  supportsLocalAgentJwt?: boolean;
  models?: AdapterModel[];
  agentConfigurationDoc?: string;
}

Execution Context

When an agent is invoked, the adapter receives:
interface AdapterExecutionContext {
  runId: string;                    // Unique run identifier
  agent: AdapterAgent;              // Agent metadata (id, name, companyId)
  runtime: AdapterRuntime;          // Session state and task context
  config: Record<string, unknown>;  // Adapter-specific configuration
  context: Record<string, unknown>; // Wake context (task, approval, etc.)
  onLog: (stream: "stdout" | "stderr", chunk: string) => Promise<void>;
  onMeta?: (meta: AdapterInvocationMeta) => Promise<void>;
  authToken?: string;               // Agent API key for Paperclip API
}

Execution Result

Adapters must return structured results:
interface AdapterExecutionResult {
  exitCode: number | null;
  signal: string | null;
  timedOut: boolean;
  errorMessage?: string | null;
  errorCode?: string | null;
  usage?: UsageSummary;            // Token usage for cost tracking
  sessionId?: string | null;       // Session continuation support
  sessionParams?: Record<string, unknown>;
  provider?: string | null;        // "anthropic", "openai", etc.
  model?: string | null;
  billingType?: "api" | "subscription";
  costUsd?: number | null;
  resultJson?: Record<string, unknown>;
  summary?: string | null;
  clearSession?: boolean;
}

Heartbeat Invocations

Agents are triggered via heartbeat invocations on a configured schedule:
{
  "adapterType": "claude_local",
  "adapterConfig": {
    "command": "claude",
    "model": "claude-sonnet-4-5-20250929",
    "cwd": "/workspace",
    "heartbeatEnabled": true,
    "intervalSec": 300
  }
}
On each heartbeat:
  1. Paperclip checks if the agent has pending work
  2. The adapter is invoked with current context
  3. The agent executes and reports results
  4. Token usage and costs are recorded
  5. Session state is saved for continuity
Heartbeat intervals must be at least 30 seconds. V1 enforces maxConcurrentRuns: 1 per agent.

Context Modes

Agents can receive context in two modes:
Thin context (default) sends only IDs and pointers. The agent fetches full details via the Paperclip API:
{
  "taskId": "550e8400-e29b-41d4-a716-446655440000",
  "wakeReason": "task_assigned",
  "issueIds": ["..."]
}
Agents use PAPERCLIP_API_KEY to call /api/issues/:id and retrieve task details.

Environment Variables

Paperclip injects standard environment variables for all adapters:
VariableDescription
PAPERCLIP_API_KEYAgent API key for authenticated requests
PAPERCLIP_RUN_IDCurrent heartbeat run ID
PAPERCLIP_AGENT_IDAgent’s unique identifier
PAPERCLIP_COMPANY_IDCompany the agent belongs to
PAPERCLIP_TASK_IDTask ID if woken for a specific task
PAPERCLIP_WAKE_REASONWhy the agent was invoked
PAPERCLIP_WORKSPACE_CWDWorking directory for the agent
Adapters can add custom environment variables via adapterConfig.env.

Session Management

Adapters supporting stateful execution (like Claude Code and Codex) use session codecs to persist state across invocations:
interface AdapterSessionCodec {
  deserialize(raw: unknown): Record<string, unknown> | null;
  serialize(params: Record<string, unknown> | null): Record<string, unknown> | null;
  getDisplayId?: (params: Record<string, unknown> | null) => string | null;
}
Session parameters typically include:
  • sessionId: Session identifier from the agent runtime
  • cwd: Working directory path
  • workspaceId: Optional workspace identifier
  • repoUrl, repoRef: Git repository context
Sessions are automatically resumed when the agent is invoked with the same cwd and sessionId.
Sessions are cleared when clearSession: true is returned, or when max-turns limits are reached.

Cost Tracking

Adapters report token usage for automatic cost calculation:
interface UsageSummary {
  inputTokens: number;
  outputTokens: number;
  cachedInputTokens?: number;
}
Cost events are automatically created and rolled up to:
  • Agent monthly budgets
  • Project budgets
  • Company budgets
Budget enforcement triggers auto-pause when limits are exceeded.

Error Handling

Adapters should return structured errors with actionable codes:
{
  exitCode: 1,
  errorMessage: "Claude authentication required",
  errorCode: "claude_auth_required",
  errorMeta: {
    loginUrl: "https://console.anthropic.com/login"
  }
}
Common error codes:
  • timeout: Execution exceeded configured timeout
  • claude_auth_required, codex_auth_required: Authentication failure
  • unknown_session: Session no longer exists
  • openclaw_http_error: HTTP adapter request failed

Next Steps

Process Adapter Guide

Learn how to configure local CLI-based agents

HTTP Adapter Guide

Trigger remote agents via webhooks

OpenClaw Integration

Connect OpenClaw remote agents

Build Custom Adapters

Create your own adapter implementation