The beating heart. The core loop that makes Claude Code an agent, not just a chatbot.
β Back to Main | β Architecture Overview
A regular chatbot sends one message and gets one response. An agentic loop keeps going β it can call tools, get results, reason about them, call more tools, and iterate until the task is complete. Claude Code's conversation loop is what turns a language model into an autonomous coding assistant.
flowchart TD
START(["π’ User sends message"]) --> BUILD["Build message payload<br/>(system prompt + history + tools)"]
BUILD --> STREAM["π‘ Stream request to Anthropic API"]
STREAM --> PARSE["Parse SSE events"]
PARSE --> CHECK{"What type of event?"}
CHECK -->|"text_delta"| DISPLAY["π¬ Display text to terminal"]
DISPLAY --> PARSE
CHECK -->|"tool_use"| COLLECT["π¦ Collect tool call<br/>(id, name, input)"]
COLLECT --> PARSE
CHECK -->|"message_stop"| ASSEMBLE["Assemble AssistantMessage"]
ASSEMBLE --> TOOLS_CHECK{"Any tool calls<br/>in message?"}
TOOLS_CHECK -->|"No"| DONE(["β
Turn complete"])
TOOLS_CHECK -->|"Yes"| EXEC_LOOP["For each tool call:"]
EXEC_LOOP --> PERM{"π Permission<br/>check"}
PERM -->|"Denied"| DENY["Return error result"]
PERM -->|"Allowed"| PRE_HOOK["β‘ Run PreToolUse hook"]
PRE_HOOK -->|"exit 2"| HOOK_DENY["Return hook-denied result"]
PRE_HOOK -->|"exit 0"| EXECUTE["π Execute tool"]
EXECUTE --> POST_HOOK["β‘ Run PostToolUse hook"]
POST_HOOK --> RESULT["π¨ Collect tool result"]
DENY --> RESULT
HOOK_DENY --> RESULT
RESULT --> MORE_TOOLS{"More tools<br/>to execute?"}
MORE_TOOLS -->|"Yes"| EXEC_LOOP
MORE_TOOLS -->|"No"| SEND_RESULTS["Send all tool_results to API"]
SEND_RESULTS --> STREAM
style START fill:#22c55e,color:#fff
style DONE fill:#22c55e,color:#fff
style DENY fill:#ef4444,color:#fff
style HOOK_DENY fill:#ef4444,color:#fff
A real-world example: user asks "Read the config file and fix the bug"
sequenceDiagram
participant U as π€ User
participant RT as π§ Runtime
participant API as π API
participant T as π§ Tools
U->>RT: "Read config.rs and fix the bug on line 42"
RT->>API: messages + tool definitions
Note over API: Decides to read file first
API-->>RT: tool_use: read_file("config.rs")
RT->>T: Execute read_file
T-->>RT: File contents (200 lines)
RT->>API: tool_result: [file contents]
Note over API: Analyzes code, finds bug
API-->>RT: text: "I see the issue..."
API-->>RT: tool_use: edit_file("config.rs", fix)
RT->>T: Execute edit_file
T-->>RT: Edit applied successfully
RT->>API: tool_result: "success"
Note over API: Verifies the fix
API-->>RT: tool_use: bash("cargo check")
RT->>T: Execute bash
T-->>RT: "Compiling... 0 errors"
RT->>API: tool_result: "0 errors"
API-->>RT: text: "Fixed! The bug was..."
API-->>RT: message_stop
RT-->>U: Display complete response
βββββββββββββββββββββββββββββββββββββββββββ
β Assistant Event Types β
βββββββββββββββββββββββββββββββββββββββββββ€
β Text Delta β Incremental text β
β Tool Use β Tool invocation β
β (id, name, input) β
β Usage β Token count update β
β Message Stop β End of response β
βββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββ
β Turn Summary β
βββββββββββββββββββββββββββββββββββββββββββ€
β Assistant messages sent β
β Tool results collected β
β Number of iterations β
β Token usage stats β
β Whether auto-compaction triggered β
βββββββββββββββββββββββββββββββββββββββββββ
The loop terminates when:
| Condition | What Happens |
|---|---|
stop_reason: "end_turn" |
Model is done β natural completion |
| Max iterations reached | Safety limit to prevent infinite loops |
| Token budget exceeded | Triggers compaction, then continues |
| User cancellation | Ctrl+C interrupts the stream |
| API error (non-retryable) | Error displayed, turn ends |
When the cumulative input tokens exceed the budget (default 200K), the loop triggers auto-compaction mid-conversation:
flowchart LR
A["Token count check<br/>after each API call"] --> B{"Over budget?"}
B -->|"No"| C["Continue loop"]
B -->|"Yes"| D["Compact history"]
D --> E["Summarize old turns"]
E --> F["Inject summary"]
F --> G["Continue with<br/>smaller context"]
See Memory & Compaction β for the full deep dive.
flowchart TD
ERR["API returns error"] --> TYPE{"Error type?"}
TYPE -->|"Retryable<br/>(timeout, 500)"| RETRY["Wait (exponential backoff)<br/>200ms β 400ms β 800ms β 2s"]
RETRY --> RESEND["Retry request"]
TYPE -->|"Rate limit<br/>(429)"| WAIT["Wait for retry-after header"]
WAIT --> RESEND
TYPE -->|"Auth error<br/>(401)"| REFRESH["Refresh OAuth token"]
REFRESH --> RESEND
TYPE -->|"Non-retryable"| FAIL["Display error,<br/>end turn"]
RESEND --> CHECK{"Max retries?"}
CHECK -->|"No"| STREAM["Continue streaming"]
CHECK -->|"Yes"| EXHAUST["RetriesExhausted error"]
- Memory & Compaction β β How the loop handles token limits
- Tool System β β What happens inside "Execute tool"
- Streaming & SSE β β How the SSE parser works