What Happens to a Message Inside an AI Agent Loop?

This article walks through the full lifecycle of a user message in Claude Code's Agent SDK, explaining each processing stage, the crucial tool‑use decision, parallel tool execution, error handling, and how Hooks let developers extend the loop without modifying core logic.

Qborfy AI
Qborfy AI
Qborfy AI
What Happens to a Message Inside an AI Agent Loop?

When you press Enter after typing a prompt for Claude or ChatGPT, the message passes through a dozen internal steps defined by the Claude Code Agent SDK.

Message lifecycle

The SDK models the loop as eight ordered phases:

Receive the prompt (SystemMessage with session metadata).

Pre‑process and create a SystemMessage.

Send the SystemMessage to the LLM.

Parse the LLM response.

Check whether the response contains a tool_use request.

If a tool is requested, execute it.

Collect the tool result, wrap it in a UserMessage, and feed it back to the LLM.

When no tool is requested, generate the final AssistantMessage, then a ResultMessage that includes token usage and the session ID.

The author likens this to a parcel delivery process: "pickup" (receiving the prompt), "sorting" (pre‑processing), "transport" (LLM thinking, which causes the noticeable pause), "delivery" (tool execution and result collection), and finally "sign‑off" (returning the ResultMessage).

Key decision point

The most critical branch occurs at step 5 – the loop asks, "Does the response contain a tool call?" The answer determines whether the loop continues or terminates:

Tool call present (tool_use) : the Agent believes more work is needed and the loop repeats.

No tool call (pure text response) : the Agent considers the task finished and exits.

Concrete example

For the task "fix failing tests in auth.ts", the author shows four rounds:

Round 1 – Run npm test (fails) → tool call returned.

Round 2 – Read auth.ts and auth.test.ts → tool call returned.

Round 3 – Edit auth.ts and re‑run tests → tool call returned.

Round 4 – Tests pass, pure text response → loop ends.

This illustrates why the Agent sometimes calls several tools in a row and why a brief pause appears before each LLM thinking step.

Tool execution pattern

Tool calls follow a "question‑answer" exchange: the Agent asks the SDK to run a tool, the SDK returns a UserMessage with the result, and the LLM immediately re‑evaluates the next step.

ToolSDK → LLM → ToolSDK → LLM → …
User: "I want to call Read('auth.ts')"
SDK: returns file content as UserMessage
LLM: decides next action based on that content

Parallel tool execution

Production‑grade loops allow read‑only tools (e.g., Read, Glob, Grep) to run concurrently, while write‑or‑modify tools (e.g., Edit, Write, Bash) must execute sequentially to avoid state conflicts.

# Pseudo‑code for parallel execution of read‑only tools
readonly = [tc for tc in tool_calls if is_readonly(tc)]
writeops = [tc for tc in tool_calls if not is_readonly(tc)]
results = parallel_map(execute, readonly)
for tc in writeops:
    results.append(execute(tc))
return results

This design can make the Agent up to five times faster when reading multiple files.

Error handling

Tool failures are never raised as exceptions. Instead, the SDK returns an error string as the tool result, allowing the LLM to reason about the failure and possibly retry.

# Correct error‑handling pattern
def execute_tool_safely(tool_call):
    try:
        return tools[tool_call.name](**tool_call.args)
    except Exception as e:
        return f"Tool execution failed: {type(e).__name__}: {str(e)}"

For example, if Bash('npm test') reports "command not found", the LLM may subsequently call Bash('npm install') and retry.

Hooks – extending the loop without touching core code

Hooks are callbacks triggered at specific loop stages. They let developers inject validation, auditing, cost alerts, or custom approval logic without modifying the SDK itself. UserPromptSubmit – before the prompt is sent. PreToolUse – right before a tool runs (e.g., reject dangerous commands). PostToolUse – after a tool returns (e.g., log the outcome). Stop – when the Agent finishes (e.g., persist session state). SubagentStart / SubagentStop – for nested agents. PreCompact – before context compression, useful for archiving the full message history.

Example: a PreToolUse hook that blocks commands like rm -rf or drop table and returns {"allow": False, "reason": "dangerous command detected"} to short‑circuit the loop.

# Pseudo‑code for automatic tool approval
DANGEROUS_COMMANDS = ["rm -rf", "drop table", "format"]
def pre_tool_use_hook(tool_name, tool_input):
    log(f"Agent wants to call {tool_name} with {tool_input}")
    if tool_name == "Bash":
        cmd = tool_input.get("command", "")
        for dangerous in DANGEROUS_COMMANDS:
            if dangerous in cmd.lower():
                return {"allow": False, "reason": f"Detected dangerous command: {dangerous}"}
    return {"allow": True}
agent.register_hook("PreToolUse", pre_tool_use_hook)

Simple loop vs. SDK implementation

The author contrasts the earlier 50‑line minimal loop with the full SDK version. Key differences include:

Message types – simple loop only supports user/assistant; SDK adds System, Assistant, User, Stream, Result.

Termination – simple loop uses a fixed max‑iterations; SDK adds budget limits.

Tool execution – simple loop runs tools serially; SDK runs read‑only tools in parallel and enforces write ordering.

Hooks – absent in the minimal version, plentiful in the SDK.

Context management – SDK automatically compresses prompts, caches, and supports session‑ID continuation.

Observability – SDK emits a full event stream; the minimal version only prints.

When the 50‑line loop is enough

Personal scripts or internal tools.

Few iterations (< 10).

No need for parallel tool calls.

No audit or security requirements.

One‑off tasks that can be discarded after completion.

When you should upgrade

Production use by other users.

Complex or many‑round tasks (dozens of steps).

Need for safety checks (prevent destructive commands).

Requirement for observability and logging.

Need for session recovery after a browser refresh.

Budget or cost control.

If the loop misbehaves enough to keep you up at night, it’s time to switch to the SDK version.

Key takeaways

The Agent’s internal message lifecycle follows eight distinct stages from prompt receipt to final result.

The presence of a tool_use in the LLM response decides whether the loop continues.

Tool execution follows a one‑question‑one‑answer pattern; read‑only tools can run in parallel, write tools must be sequential.

Error handling should return error information to the LLM instead of raising exceptions.

Hooks provide a non‑intrusive way to add approval, auditing, rate‑limiting, and other cross‑cutting concerns.

The SDK adds richer message types, parallelism, hooks, context compression, session persistence, and full observability compared with the minimal loop.

Next article preview

The upcoming piece will explore the "memory" problem: why an Agent can start forgetting earlier steps once the token window fills up.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

HooksAI AgentError HandlingClaudeParallelismAgent LoopTool Execution
Qborfy AI
Written by

Qborfy AI

A knowledge base that logs daily experiences and learning journeys, sharing them with you to grow together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.