Runtime: The Machine Loops Until the Task Is Done
The Agent Loop goes through 5 steps each revolution. Here is a step-by-step breakdown.
Step 1: User Speaks
You type a message. The system bundles it with the complete conversation history and prepares to send it to the Claude API.
Notice "complete conversation history" — every turn carries all prior history along. The longer the conversation, the larger this bundle. This creates a core problem: Claude's context window is finite, and on a long-running session, the history can fill it up. The solution is covered in Step 4: Context Compression: How the Loop Handles 'Memory Explosion'.
This bundling is the job of the query() function (src/query.ts). Each revolution of the loop carries a notebook tracking the current state:
// Mutable state for a single loop (passed between iterations)
type State = {
messages: Message[] // Conversation history
toolUseContext: ToolUseContext // Tool execution context
autoCompactTracking: AutoCompactTrackingState // Compression state tracking
maxOutputTokensRecoveryCount: number // Output truncation recovery count
hasAttemptedReactiveCompact: boolean // Whether reactive compact was already attempted
turnCount: number // How many iterations this turn
transition: Continue | undefined // Why the last iteration didn't stop
}
Field-by-field explanation:
| Field | Plain-language meaning |
|---|---|
messages | All messages so far — yours, Claude's, and tool results. New content is appended every revolution. |
toolUseContext | Which tools are available, what permissions exist, which directory to operate in. Think of it as "the keys and instructions for the toolbox." |
autoCompactTracking | Records whether history has already been compacted, and how many consecutive compaction failures have occurred. Prevents repeated failed compactions from wasting resources. |
maxOutputTokensRecoveryCount | If Claude's reply was truncated, the system attempts continuation. This tracks how many continuation attempts have been made; max 3. |
hasAttemptedReactiveCompact | Prevents triggering reactiveCompact more than once per turn. Set on first trigger; not repeated. |
turnCount | How many iterations Claude has completed in this turn. Can be used to enforce a maximum loop count and prevent infinite running. |
transition | Why the last iteration didn't stop and continued looping (e.g., "there are still unprocessed tool results," "output was truncated and needs continuation"). Primarily for debugging and tracking abnormal paths. |
Step 2: Claude Thinks
After the messages are sent to the Claude API, Claude first performs internal reasoning (Thinking), then decides what to say or do.
This "thinking" process has strict rules in the code. The developer wrote a famous comment in a magic-spell style:
"The rules of thinking are lengthy and fortuitous... If ye does not heed these rules, ye will be punished with an entire day of debugging"
Specific rules:
- Messages containing thinking content must be sent in a request that allows thinking (
max_thinking_length > 0) - Thinking content cannot be the last block in a message — actual output must follow it
- Thinking content must remain complete throughout the conversation history — it cannot be partially dropped
These rules are so strict because a broken chain-of-thought in the conversation history will corrupt Claude's subsequent reasoning, and such errors are extremely difficult to trace.
Step 3: Decide Which Tool to Use
After thinking, Claude produces a response. One of two outcomes:
- Replies with text directly → streamed to the user; the Loop may end here
- Decides to call a tool → proceeds to the next step
The tools Claude can use are registered and managed in src/tools.ts, organized into three categories:
Standard Tools (available to all users)
| Tool | Purpose |
|---|---|
BashTool | Execute terminal commands |
FileReadTool | Read files |
FileEditTool | Edit files (precise replacement) |
FileWriteTool | Write new files |
GlobTool | Find files by pattern |
GrepTool | Search file contents |
WebFetchTool | Fetch web pages |
WebSearchTool | Search the internet |
AgentTool | Spawn sub-agents |
TodoWriteTool | Maintain task lists |
EnterPlanModeTool / ExitPlanModeTool | Enter/exit plan mode |
AskUserQuestionTool | Ask the user a question |
LSPTool | Language server (code completion/navigation) |
NotebookEditTool | Edit Jupyter Notebooks |
TaskOutputTool | View background task output |
Internal Employee Tools (USER_TYPE === 'ant')
| Tool | Purpose |
|---|---|
REPLTool | Changes Claude's operating mode: hides all base tools and forces interaction through the REPL interface (used to study Claude's behavior under different constraints) |
SuggestBackgroundPRTool | Claude proactively suggests creating a background PR (tests "proactive assistant" interaction patterns) |
Tools hidden in REPL mode: FileRead / FileWrite / FileEdit / Glob / Grep / Bash / NotebookEdit / Agent
Experimental Tools Controlled by Feature Flags
| Feature Area | Tool | Feature Flag |
|---|---|---|
| Scheduled tasks | CronCreate / CronDelete / CronList | AGENT_TRIGGERS |
| Remote triggers | RemoteTriggerTool | AGENT_TRIGGERS_REMOTE |
| Monitoring | MonitorTool | MONITOR_TOOL |
| Proactive push | PushNotificationTool, SendUserFileTool, SubscribePRTool | KAIROS |
| Wait/schedule | SleepTool | PROACTIVE / KAIROS |
| Browser control | WebBrowserTool | WEB_BROWSER_TOOL |
| Terminal screenshot | TerminalCaptureTool | TERMINAL_PANEL |
| Plan verification | VerifyPlanExecutionTool | env CLAUDE_CODE_VERIFY_PLAN=true |
KAIROS is a recurring codename linking push notifications, GitHub PR monitoring, and file delivery — pointing to a "proactive agent" form in development: Claude monitors events and notifies users without being called.
Step 4: Execute the Tool
The system calls the tool Claude specified, performing the actual operation (reading files, running commands, searching…).
AgentTool is the most special — Claude can use it to spawn sub-agents and delegate subtasks. How agents are launched and routed to task types is covered in Step 5: AgentTool: How Subtasks Are Launched and Routed.
Step 5: See Result → Think Again → Until Complete
After the tool executes, the result is appended to the conversation history as a tool_result, then the loop returns to Step 1 and calls the Claude API again.
Claude sees the tool result and re-evaluates: is the task complete? Does it need to do more? This cycle continues until Claude stops calling tools and outputs a final reply, ending the Loop.
Step 1: User speaks
↓
Step 2: Claude thinks
↓
Step 3: Decide which tool to use
↓
Step 4: Execute tool
↓
Step 5: Result appended → back to Step 2
↓
(Until Claude no longer calls tools)
↓
Output final reply, turn ends