sandbox-agent/research/agent-json-schemas.md
2026-01-24 22:37:22 -08:00

11 KiB

Type definitions for coding agent CLIs in SDK mode

Four major coding agent CLIs now offer programmatic access through TypeScript SDKs with well-defined type systems. OpenCode provides the most complete formal specification with a published OpenAPI 3.1.1 schema, while Claude Code and Codex offer comprehensive TypeScript types through npm packages. All four tools use similar patterns: streaming events via JSON lines, discriminated union types for messages, and structured configuration schemas.

Codex CLI has TypeScript SDK but no formal schema

OpenAI's Codex CLI provides programmatic control through the @openai/codex-sdk package, which wraps the bundled binary and exchanges JSONL events over stdin/stdout. Types are well-defined in source code but not published as JSON Schema or OpenAPI specifications.

Core SDK types from @openai/codex-sdk:

interface CodexOptions {
  codexPathOverride?: string;
  baseURL?: string;
  apiKey?: string;
  env?: Record<string, string>;
}

interface ThreadOptions {
  model?: string;
  sandboxMode?: "read-only" | "workspace-write" | "danger-full-access";
  workingDirectory?: string;
  skipGitRepoCheck?: boolean;
}

interface TurnOptions {
  outputSchema?: Record<string, unknown>;  // JSON Schema for structured output
}

type Input = string | UserInput[];
interface UserInput {
  type: "text" | "local_image";
  text?: string;
  path?: string;
}

Event types for streaming (runStreamed()):

type EventType = "thread.started" | "turn.started" | "turn.completed" | 
                 "turn.failed" | "item.started" | "item.updated" | 
                 "item.completed" | "error";

interface ThreadEvent {
  type: EventType;
  thread_id?: string;
  usage?: { input_tokens: number; cached_input_tokens: number; output_tokens: number };
  error?: { message: string };
  item?: ThreadItem;
}

type ItemType = "agent_message" | "reasoning" | "command_execution" | 
                "file_change" | "mcp_tool_call" | "web_search" | 
                "todo_list" | "error" | "unknown";

Key source files: sdk/typescript/src/ contains items.ts, events.ts, options.ts, input.ts, and thread.ts. Documentation at https://developers.openai.com/codex/sdk/ and https://developers.openai.com/codex/noninteractive/.


Claude Code SDK offers the most comprehensive TypeScript types

Now rebranded as the Claude Agent SDK, the official package @anthropic-ai/claude-agent-sdk provides production-ready types with full coverage of tools, hooks, permissions, and streaming events.

Core query function and options:

import { query } from "@anthropic-ai/claude-agent-sdk";

interface Options {
  cwd?: string;
  model?: string;
  systemPrompt?: string | { type: 'preset'; preset: 'claude_code'; append?: string };
  tools?: string[] | { type: 'preset'; preset: 'claude_code' };
  allowedTools?: string[];
  disallowedTools?: string[];
  permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan';
  maxTurns?: number;
  maxBudgetUsd?: number;
  outputFormat?: { type: 'json_schema', schema: JSONSchema };
  mcpServers?: Record<string, McpServerConfig>;
  hooks?: Partial<Record<HookEvent, HookCallbackMatcher[]>>;
  sandbox?: SandboxSettings;
  // ... 30+ additional options
}

Message types (SDK output):

type SDKMessage = SDKAssistantMessage | SDKUserMessage | SDKResultMessage | 
                  SDKSystemMessage | SDKPartialAssistantMessage | SDKCompactBoundaryMessage;

type SDKResultMessage = {
  type: 'result';
  subtype: 'success' | 'error_max_turns' | 'error_during_execution' | 
           'error_max_budget_usd' | 'error_max_structured_output_retries';
  uuid: UUID;
  session_id: string;
  duration_ms: number;
  duration_api_ms: number;
  is_error: boolean;
  num_turns: number;
  result?: string;
  total_cost_usd: number;
  usage: { input_tokens: number; output_tokens: number; /* ... */ };
  modelUsage: { [modelName: string]: ModelUsage };
  structured_output?: unknown;
  errors?: string[];
};

Built-in tool input types:

type ToolInput = AgentInput | BashInput | FileEditInput | FileReadInput | 
                 FileWriteInput | GlobInput | GrepInput | WebFetchInput | 
                 WebSearchInput | /* ... 10+ more */;

interface BashInput {
  command: string;
  timeout?: number;  // Max 600000ms
  description?: string;
  run_in_background?: boolean;
}

interface FileEditInput {
  file_path: string;  // Absolute path
  old_string: string;
  new_string: string;
  replace_all?: boolean;
}

Source: https://github.com/anthropics/claude-agent-sdk-typescript and https://platform.claude.com/docs/en/agent-sdk/typescript


OpenCode provides a full OpenAPI specification

The sst/opencode project is the only CLI with a published OpenAPI 3.1.1 specification, enabling automatic client generation. Types are auto-generated from the spec using @hey-api/openapi-ts.

Key resources:

Core types from the SDK:

interface Session {
  id: string;  // Pattern: ^ses.*
  version: string;
  projectID: string;
  directory: string;
  parentID?: string;
  title: string;
  summary?: { additions: number; deletions: number; files: number; diffs?: FileDiff[] };
  time: { created: number; updated: number; compacting?: number; archived?: number };
}

type Message = UserMessage | AssistantMessage;

interface AssistantMessage {
  id: string;
  role: "assistant";
  sessionID: string;
  error?: NamedError;
  tokens: { input: number; output: number; cache: { read: number; write: number } };
  cost: { input: number; output: number };
  modelID: string;
  providerID: string;
}

type Part = TextPart | ReasoningPart | ToolPart | FilePart | AgentPart | 
            StepStartPart | StepFinishPart | SnapshotPart | PatchPart | RetryPart;

SSE event types:

type Event = 
  | { type: "session.created"; properties: { session: Session } }
  | { type: "session.updated"; properties: { session: Session } }
  | { type: "message.updated"; properties: { message: Message } }
  | { type: "message.part.updated"; properties: { part: Part } }
  | { type: "permission.asked"; properties: { permission: Permission } }
  | { type: "pty.created"; properties: { pty: Pty } }
  // 30+ total event types

Plugin system types (@opencode-ai/plugin):

type Plugin = (ctx: PluginInput) => Promise<Hooks>;

interface Hooks {
  event?: (input: { event: Event }) => Promise<void>;
  tool?: { [key: string]: ToolDefinition };
  "tool.execute.before"?: (input, output) => Promise<void>;
  "tool.execute.after"?: (input, output) => Promise<void>;
  stop?: (input: { sessionID: string }) => Promise<{ continue?: boolean }>;
}

Amp CLI documents stream JSON schema in manual

Sourcegraph's Amp provides @sourcegraph/amp-sdk with documented TypeScript types, though the package source is closed. The stream JSON schema is fully documented at https://ampcode.com/manual/appendix.

Complete StreamJSONMessage type:

type StreamJSONMessage =
  | {
      type: "assistant";
      message: {
        type: "message";
        role: "assistant";
        content: Array<
          | { type: "text"; text: string }
          | { type: "tool_use"; id: string; name: string; input: Record<string, unknown> }
          | { type: "thinking"; thinking: string }
          | { type: "redacted_thinking"; data: string }
        >;
        stop_reason: "end_turn" | "tool_use" | "max_tokens" | null;
        usage?: { input_tokens: number; output_tokens: number; /* ... */ };
      };
      parent_tool_use_id: string | null;
      session_id: string;
    }
  | {
      type: "user";
      message: {
        role: "user";
        content: Array<{ type: "tool_result"; tool_use_id: string; content: string; is_error: boolean }>;
      };
      parent_tool_use_id: string | null;
      session_id: string;
    }
  | {
      type: "result";
      subtype: "success";
      duration_ms: number;
      is_error: false;
      num_turns: number;
      result: string;
      session_id: string;
    }
  | {
      type: "system";
      subtype: "init";
      cwd: string;
      session_id: string;
      tools: string[];
      mcp_servers: { name: string; status: "connected" | "connecting" | "connection-failed" | "disabled" }[];
    };

SDK execute function:

import { execute, type AmpOptions, type MCPConfig } from '@sourcegraph/amp-sdk';

interface AmpOptions {
  cwd?: string;
  dangerouslyAllowAll?: boolean;
  toolbox?: string;
  mcpConfig?: MCPConfig;
  permissions?: PermissionRule[];
  continue?: boolean | string;
}

interface PermissionRule {
  tool: string;  // Glob pattern like "Bash", "mcp__playwright__*"
  matches?: { [argumentName: string]: string | string[] | boolean };
  action: "allow" | "reject" | "ask" | "delegate";
  context?: "thread" | "subagent";
}

Documentation at https://ampcode.com/manual/sdk and npm package at https://www.npmjs.com/package/@sourcegraph/amp-sdk.


Comparing schema availability across all four CLIs

CLI TypeScript Types OpenAPI/JSON Schema Source Available
Codex Full SDK types None published GitHub
Claude Code Comprehensive None published GitHub
OpenCode Auto-generated OpenAPI 3.1.1 GitHub
Amp Documented types None published Closed

Common patterns across all SDKs:

All four tools share remarkably similar architectural patterns. They use discriminated union types for messages (user, assistant, system, result), JSONL streaming for real-time output, session/thread-based conversation management, MCP server integration with identical config shapes, and permission systems with allow/deny/ask actions.

For maximum type safety, OpenCode is the only option with a formal OpenAPI specification that enables automatic client generation in any language. Claude Code provides the most comprehensive TypeScript-native types with 35+ option fields and full hook/plugin typing. Codex types are well-structured but require importing from source. Amp types are documented but not directly inspectable in source code.

Conclusion

Developers building integrations should prioritize OpenCode for formal schema needs (OpenAPI enables code generation) and Claude Code for TypeScript-first development (most complete type exports). All four SDKs are actively maintained and converging on similar patterns, suggesting a de facto standard for coding agent interfaces is emerging around discriminated message unions, streaming events, and permission-based tool control.