Bridge Module
12 files · ~13K linesThe SDK Bridge — Claude Code as a Library. The same QueryEngine that powers the terminal REPL can run headlessly as a programmatic API. This is how subagents spawn, and how Claude Code scales to remote workers.
One Engine, Two Faces
Terminal Mode vs SDK Mode
The same core loop runs in both modes. The Bridge is the diff — it swaps output and permissions handling.
entrypoints/cli.tsxQueryEngine (shared)sharedInk REPL.tsx UITerminal permission dialogANSI → stdoutentrypoints/sdk/ → BridgeQueryEngine (shared)sharedNo UIProgrammatic callbackSDKMessage async iteratorThis is the core architectural fact about Bridge. It's adapters all the way down.
| Aspect | Terminal | SDK / Headless |
|---|---|---|
| Entry point | entrypoints/cli.tsx → main.tsx | entrypoints/sdk/ → Bridge |
| QueryEngine | Same QueryEngine class | Same QueryEngine class |
| query() loop | Identical 7-phase loop | Identical 7-phase loop |
| Output | ANSI escape codes → terminal stdout | SDKMessage objects → async iterator |
| UI | Ink component tree (REPL.tsx) | No UI — caller processes messages |
| Permissions | User dialog in terminal | Programmatic approval callback |
SDK Usage — 3 Entry Points
Real code showing how to use Claude Code as a library. Three patterns for three use cases.
import { runClaude } from "@anthropic-ai/claude-code";
// Minimal SDK usage — async iterator of events
for await (const message of runClaude({
messages: [{ role: "user", content: "List the files in /tmp" }],
tools: ["BashTool", "FileReadTool"],
})) {
if (message.type === "text") process.stdout.write(message.text);
if (message.type === "tool_result") console.log(message.tool, message.output);
}import { createSession } from "@anthropic-ai/claude-code";
// Stateful session — continue a conversation
const session = await createSession({
tools: ["BashTool", "FileReadTool", "FileEditTool"],
cwd: process.cwd(),
});
// First turn
await session.send("Read package.json and summarize the project");
// Follow-up in same context
await session.send("Now update the description field");// AgentTool internally does this:
import { streamQuery } from "@anthropic-ai/claude-code/internal";
// Fork parent state for subagent
const subagentEngine = new QueryEngine({
...parentEngine.getForkedState(),
messages: [], // fresh history
});
// Run headlessly — same 7-phase loop, no terminal output
for await (const event of subagentEngine.query(subagentTask)) {
results.push(event);
}
// Results injected back as tool_result in parent conversationHow AgentTool Uses the Bridge — Subagent Spawning
When Claude decides to spawn a subagent, the Bridge is the mechanism. The subagent is a full Claude Code instance running headlessly.
SDK API Surface — Key Entry Points
The three functions external callers use to drive Claude Code programmatically.
runClaude()Main SDK entry point. Takes messages and options, returns an async iterator of SDKMessage objects. Same interface whether running local or remote.
createSession()Creates a new QueryEngine instance with the given config. The session is stateful — call .send() repeatedly to continue a conversation.
streamQuery()Low-level function. Wraps query() and yields SDKMessage events as they arrive. Used internally by runClaude() and createSession().
Key Files
entrypoints/sdk/~400 linesHeadless SDK entrypoint — exposes QueryEngine as a programmatic API without terminal UI
bridge/~20KBRemote session bridge — polls for work, spawns sessions, manages remote capacity
remote/~30KBWebSocket adapters and remote session manager for distributed execution
coordinator/~15KBMulti-worker orchestration — coordinates parallel subagent execution
Bridge wraps QueryEngine. The same 7-phase loop runs headlessly when Bridge is the caller.
AgentTool uses Bridge to spawn subagents — it's the connection between the tool system and the SDK.
The main site's analysis of how multi-agent spawning works end to end.