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:

FieldPlain-language meaning
messagesAll messages so far — yours, Claude's, and tool results. New content is appended every revolution.
toolUseContextWhich tools are available, what permissions exist, which directory to operate in. Think of it as "the keys and instructions for the toolbox."
autoCompactTrackingRecords whether history has already been compacted, and how many consecutive compaction failures have occurred. Prevents repeated failed compactions from wasting resources.
maxOutputTokensRecoveryCountIf Claude's reply was truncated, the system attempts continuation. This tracks how many continuation attempts have been made; max 3.
hasAttemptedReactiveCompactPrevents triggering reactiveCompact more than once per turn. Set on first trigger; not repeated.
turnCountHow many iterations Claude has completed in this turn. Can be used to enforce a maximum loop count and prevent infinite running.
transitionWhy 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:

  1. Messages containing thinking content must be sent in a request that allows thinking (max_thinking_length > 0)
  2. Thinking content cannot be the last block in a message — actual output must follow it
  3. 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)

ToolPurpose
BashToolExecute terminal commands
FileReadToolRead files
FileEditToolEdit files (precise replacement)
FileWriteToolWrite new files
GlobToolFind files by pattern
GrepToolSearch file contents
WebFetchToolFetch web pages
WebSearchToolSearch the internet
AgentToolSpawn sub-agents
TodoWriteToolMaintain task lists
EnterPlanModeTool / ExitPlanModeToolEnter/exit plan mode
AskUserQuestionToolAsk the user a question
LSPToolLanguage server (code completion/navigation)
NotebookEditToolEdit Jupyter Notebooks
TaskOutputToolView background task output

Internal Employee Tools (USER_TYPE === 'ant')

ToolPurpose
REPLToolChanges Claude's operating mode: hides all base tools and forces interaction through the REPL interface (used to study Claude's behavior under different constraints)
SuggestBackgroundPRToolClaude 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 AreaToolFeature Flag
Scheduled tasksCronCreate / CronDelete / CronListAGENT_TRIGGERS
Remote triggersRemoteTriggerToolAGENT_TRIGGERS_REMOTE
MonitoringMonitorToolMONITOR_TOOL
Proactive pushPushNotificationTool, SendUserFileTool, SubscribePRToolKAIROS
Wait/scheduleSleepToolPROACTIVE / KAIROS
Browser controlWebBrowserToolWEB_BROWSER_TOOL
Terminal screenshotTerminalCaptureToolTERMINAL_PANEL
Plan verificationVerifyPlanExecutionToolenv 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