Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sanbuphy/claude-code-source-code/llms.txt

Use this file to discover all available pages before exploring further.

The agent loop is the beating heart of Claude Code. Every interaction — interactive or non-interactive — runs through the same while-loop in src/query.ts. The loop calls the Claude API, inspects the stop_reason, executes any requested tools, appends the results to the message history, and calls the API again. It stops when the model returns a text response with no pending tool calls.

The core loop

                    THE CORE LOOP
                    =============

    User --> messages[] --> Claude API --> response
                                          |
                                stop_reason == "tool_use"?
                               /                          \
                             yes                           no
                              |                             |
                        execute tools                    return text
                        append tool_result
                        loop back -----------------> messages[]
That is the minimal agent loop. Claude Code wraps this loop with a production-grade harness: permissions, streaming, concurrency, compaction, sub-agents, persistence, and MCP.

Single query lifecycle

1

processUserInput()

Parse slash commands (e.g., /compact, /memory), build the UserMessage, and attach any file context. Slash commands that produce an immediate response return early here without touching the API.
2

fetchSystemPromptParts()

Assemble the system prompt from tool descriptions, permission context, and any CLAUDE.md memory files discovered lazily under the current working directory. The result is a SystemPrompt branded string array.
3

recordTranscript()

Persist the user message to disk as an append-only JSONL entry under ~/.claude/projects/<hash>/sessions/<session-id>.jsonl. This write is blocking (await) to protect against crash loss.
4

normalizeMessagesForAPI()

Strip UI-only fields from the messages array and, if the context window is near capacity, trigger autoCompact() to summarize older messages before the API call.
5

Claude API (streaming)

POST to /v1/messages with the full tool list and assembled system prompt. Stream events arrive as message_startcontent_block_deltamessage_stop. Text blocks are yielded immediately to the consumer (SDK or REPL).
6

StreamingToolExecutor

When a tool_use block arrives, the executor partitions tool calls into two buckets: concurrency-safe tools that can run in parallel (isConcurrencySafe() === true), and serial tools that must run one at a time. The partition is determined per-tool per-input.
7

canUseTool() — permission check

Before executing each tool, the permission system runs: pre-tool hooks → allow/deny rules → interactive prompt (if needed). A DENY result appends a tool_result error and continues the loop without executing the tool.
8

tool.call()

Execute the tool (BashTool, FileReadTool, FileEditTool, WebFetchTool, etc.) and collect the result. Long-running tools stream progress events back to the UI via the onProgress callback.
9

append tool_result and loop

Push the tool result onto messages[], fire off a recordTranscript() write (fire-and-forget for assistant messages, order-preserving queue), and loop back to step 4 for another API call with the updated history.
10

yield result message

When stop_reason is not "tool_use", yield the final SDKMessage to the consumer, including the response text, token usage, accumulated cost, and session ID.

Architecture layers

┌─────────────────────────────────────────────────────────────────────┐
│                         ENTRY LAYER                                 │
│  cli.tsx ──> main.tsx ──> REPL.tsx (interactive)                   │
│                     └──> QueryEngine.ts (headless/SDK)              │
└──────────────────────────────┬──────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│                       QUERY ENGINE                                  │
│  submitMessage(prompt) ──> AsyncGenerator<SDKMessage>               │
│    │                                                                │
│    ├── fetchSystemPromptParts()    ──> assemble system prompt       │
│    ├── processUserInput()          ──> handle /commands             │
│    ├── query()                     ──> main agent loop              │
│    │     ├── StreamingToolExecutor ──> parallel tool execution      │
│    │     ├── autoCompact()         ──> context compression          │
│    │     └── runTools()            ──> tool orchestration           │
│    └── yield SDKMessage            ──> stream to consumer           │
└─────────────────────────────────────────────────────────────────────┘

The 12 progressive harness mechanisms

The source code demonstrates 12 layered mechanisms that a production AI agent harness needs beyond the minimal loop. Each builds on the previous.
#MechanismWhat it adds
s01The loopquery.ts: the while-loop that calls the API, checks stop_reason, executes tools, appends results. One loop and Bash is all you need to start.
s02Tool dispatchTool.ts + tools.ts: every tool registers into the dispatch map. The loop stays identical. buildTool() factory provides safe defaults. Adding a tool = adding one handler.
s03PlanningEnterPlanModeTool / ExitPlanModeTool + TodoWriteTool: list steps first, then execute. Tracked in AppState as mode: 'plan'.
s04Sub-agentsAgentTool + forkSubagent.ts: each child gets a fresh messages[], keeping the parent conversation clean. Four spawn modes: default, fork, worktree, remote.
s05Knowledge on demandSkillTool + memdir/: inject knowledge via tool_result, not the system prompt. CLAUDE.md files loaded lazily per directory.
s06Context compressionservices/compact/: three-layer strategy — autoCompact (summarize) + snipCompact (trim) + contextCollapse (restructure).
s07Persistent tasksTaskCreateTool / TaskUpdateTool / TaskGetTool / TaskListTool: file-based task graph with status tracking and persistence across restarts.
s08Background tasksDreamTask + LocalShellTask: daemon threads run slow operations in the background; completions are injected as notifications.
s09Agent teamsTeamCreateTool / TeamDeleteTool + InProcessTeammateTask: persistent teammates with async mailboxes for parallel workstreams.
s10Team protocolsSendMessageTool: one request-response pattern drives all inter-agent negotiation.
s11Autonomous agentscoordinator/coordinatorMode.ts (feature-gated COORDINATOR_MODE): idle-scan-and-claim loop — workers self-assign tasks without the lead assigning each one.
s12Worktree isolationEnterWorktreeTool / ExitWorktreeTool: sub-agents bound to isolated git worktrees; tasks manage goals, worktrees manage directories.

Key source files

FileRole
src/query.tsMain agent loop (~785 KB, the largest file in the repo)
src/QueryEngine.tsSDK/headless query lifecycle, AsyncGenerator<SDKMessage>
src/main.tsxREPL bootstrap, 4,683 lines
src/Tool.tsTool<Input, Output, Progress> interface and buildTool() factory
src/tools.tsTool registry, presets, and feature-gated conditional imports
src/services/tools/StreamingToolExecutor.tsParallel tool runner with concurrency partitioning
src/services/tools/toolOrchestration.tsBatch tool orchestration (runTools())
src/query.ts is ~785 KB — the largest file in the repository. It contains the streaming tool executor, context compaction logic, permission orchestration, and the main agent loop all in one place.