Skip to content

Claude Adapter

Claude Code agent adapter implementation.

Claude Code adapter -- runs AI agent tasks via the Claude Agent SDK.

This implements AgentAdapter by wrapping the Claude Code CLI as a subprocess, communicating through the SDK's streaming protocol. The orchestrator sees only the AgentAdapter interface; all Claude-specific concerns live here.

Key design decisions:

  • System CLI over bundled binary: We use the user's installed claude CLI (found via shutil.which) rather than the SDK's bundled binary. The system CLI carries the user's login credentials (from claude login or ANTHROPIC_API_KEY); the bundled one does not.

  • Environment scrubbing: CLAUDECODE env vars are stripped before launching to prevent the SDK from detecting it's inside an existing Claude session, which would block nested agent invocations.

  • Resilient query (_resilient_query): The SDK's message parser crashes on unknown message types (e.g. rate_limit_event) because its async generator dies on the first MessageParseError. Our wrapper accesses SDK internals to iterate raw JSON messages and parse them ourselves, silently skipping unrecognised types instead of aborting the entire session.

  • Message extraction (_extract_message_text): Translates the SDK's typed message objects (AssistantMessage, ResultMessage, etc.) into human-readable Discord-friendly text with markdown formatting.

See specs/adapters/claude.md for the full behavioral specification.

Classes

ClaudeAdapterConfig dataclass

ClaudeAdapterConfig(model: str = '', permission_mode: str = 'acceptEdits', allowed_tools: list[str] = (lambda: ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep'])())

Configuration for the Claude Code agent adapter.

Attributes:

Name Type Description
model str

Model ID to pass to Claude Code. Empty string means let the CLI pick its default (usually the latest Sonnet).

permission_mode str

Controls which tool calls require human approval. "acceptEdits" auto-approves file edits but prompts for shell commands; other modes are more or less permissive.

allowed_tools list[str]

Whitelist of tool names the agent may use. Defaults to the safe set of file and search tools. The orchestrator may extend this per-task (e.g. adding "WebSearch").

ClaudeAdapter

ClaudeAdapter(config: ClaudeAdapterConfig | None = None, llm_logger=None)

Bases: AgentAdapter

AgentAdapter implementation that runs tasks via the Claude Code CLI.

Each task gets a fresh SDK query (subprocess). The adapter streams messages back through the on_message callback and collects the final result/token counts from the ResultMessage.

Source code in src/adapters/claude.py
def __init__(self, config: ClaudeAdapterConfig | None = None, llm_logger=None):
    self._config = config or ClaudeAdapterConfig()
    self._task: TaskContext | None = None
    self._cancel_event = asyncio.Event()
    self._session_id: str | None = None
    self._llm_logger = llm_logger
    # References to the active SDK transport/query so stop() can force-kill
    # the subprocess instead of just setting a flag.
    self._active_transport = None
    self._active_query = None

Functions