> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeus.agentspro.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Loop

> Zeus Agent Execution Loop — Complete Data Flow from User Message to Streaming Response

The Agent Loop is a complete Agent run: message reception → context assembly → model reasoning → tool execution → streaming response → state persistence. It is the core path that transforms a user message into actions and a final reply.

In Zeus, each Loop is a serialized run per Session, emitting lifecycle events and stream events during model reasoning, tool calls, and streaming output.

***

## Entry Points

| Entry         | Route                    | Description                                  |
| ------------- | ------------------------ | -------------------------------------------- |
| Web frontend  | `POST /api/agent/invoke` | Next.js API Route, proxied to Python backend |
| Python API    | `POST /api/agent/invoke` | FastAPI route, directly calls AgentService   |
| Resume (HITL) | `POST /api/agent/resume` | Resume execution after user approval         |

***

## How It Works (High-level)

```mermaid theme={null}
sequenceDiagram
    participant User
    participant Web as Frontend
    participant API as Next.js → FastAPI
    participant Agent as DeepAgent
    participant LLM
    participant Tools

    User->>Web: Send message
    Web->>API: POST /api/agent/invoke

    rect rgb(248, 255, 240)
        Note over API,Agent: Context Assembly
        API->>Agent: Initialize LLM + Load tools + Build Prompt
    end

    rect rgb(255, 248, 240)
        Note over Agent,Tools: Execution Loop
        loop Agent Loop
            Agent->>LLM: Reasoning request
            LLM-->>Web: SSE: TextMessage (token stream)

            opt Tool call
                alt Requires approval
                    Agent-->>Web: SSE: ToolCallMessage (pending)
                    Note over User,Web: Waiting for user approval...
                else Auto-execute
                    Agent->>Tools: Execute tool
                    Tools-->>Web: SSE: ToolCallResultMessage
                end
            end
        end
    end

    Agent-->>Web: SSE: CompleteMessage
```

1. **Request Reception** — Next.js API validates identity, checks credit balance, loads LLM and tool configuration, asynchronously saves user message, forwards to Python backend
2. **Context Assembly** — `_init_context()` sequentially loads tools (MCP + OAuth + Built-in), initializes LLM, retrieves Memory/Profile, activates Skills, builds System Prompt, caches to `context_cache`
3. **Agent Creation** — Creates a LangGraph graph via DeepAgents, assembling LLM, tools, middleware pipeline, Checkpointer, and HITL interrupt configuration
4. **Message Construction** — Frontend chat\_history is converted to LangChain message types (max 30), with current user message appended
5. **Streaming Execution** — Enters `_astream_events()` core loop; framework events are converted to SSE messages and streamed
6. **Completion** — Sends `CompleteMessage`; Checkpointer automatically saves state

***

## Context Assembly

```mermaid theme={null}
flowchart TD
    start["_init_context()"]
    validate["Parameter validation"]
    session["Session ID generation"]

    subgraph ToolLoading["Tool Loading"]
        mcp["MCP Tools"]
        oauth["OAuth Tools"]
        browser["Browser Tools"]
        desktop["Desktop Tools"]
        sandbox["Sandbox Tools"]
    end

    llm["LLM initialization"]
    memory["Memory retrieval"]
    profile["Profile fetching"]
    skills["Skills activation"]
    prompt["System Prompt construction"]

    start --> validate --> session --> ToolLoading --> llm --> memory --> profile --> skills --> prompt
```

After context assembly completes, it is cached in `_context_cache[session_id]` for reuse during HITL `resume()`.

<CardGroup cols={2}>
  <Card title="Context Details" icon="brain" href="/en/documentation/core-capabilities/context">
    System Prompt assembly, Token management & optimization strategies
  </Card>

  <Card title="System Prompt" icon="file-lines" href="/en/documentation/core-capabilities/system-prompt">
    System prompt — CORE, SOUL, TOOLS, WORKFLOW, MEMORY, dynamic injection
  </Card>
</CardGroup>

***

## Event Streaming

`_astream_events()` listens to DeepAgents framework internal events and converts them to standard SSE messages for the frontend:

```mermaid theme={null}
graph LR
    subgraph DeepAgentsEvents["DeepAgents Internal Events"]
        e1["on_chat_model_stream"]
        e2["on_chat_model_end"]
        e3["on_tool_end"]
        e4["on_interrupt"]
    end

    subgraph SSEMessages["SSE Messages"]
        m1["TextMessage"]
        m2["ToolCallMessage"]
        m3["ToolCallResultMessage"]
        m4["CompleteMessage"]
        m5["ErrorMessage"]
        m6["TokenUsageMessage"]
    end

    e1 -->|"token stream"| m1
    e2 -->|"tool_calls detection"| m2
    e2 -->|"usage_metadata"| m6
    e3 -->|"tool result"| m3
    e4 -->|"HITL interrupt"| m2
```

### SSE Event Types

| SSE Event          | Trigger Timing             | Key Fields                                     |
| ------------------ | -------------------------- | ---------------------------------------------- |
| `text`             | LLM outputs each token     | `content`, `role`                              |
| `tool_call`        | LLM decides to call a tool | `tool_name`, `parameters`, `requires_approval` |
| `tool_call_result` | Tool execution complete    | `tool_name`, `result`, `is_error`              |
| `complete`         | Agent execution finished   | `content`, `summary`                           |
| `error`            | Exception occurred         | `error`, `error_code`, `details`               |
| `token_usage`      | After LLM call ends        | `prompt_tokens`, `completion_tokens`           |

<Card title="Messages" icon="comments" href="/en/documentation/core-capabilities/messages/messages">
  Learn about the complete message flow, state management, and persistence
</Card>

***

## Tool Execution

### Execution Decision

```mermaid theme={null}
flowchart TD
    llm_end["LLM returns tool_calls"]
    check{"Requires approval?"}

    auto["Auto-execute tool"]
    tool_end["Return ToolCallResultMessage"]

    hitl["Save Checkpoint"]
    send_pending["Send ToolCallMessage<br/>(requires_approval=true)"]
    wait["Wait for user decision"]
    resume["resume() recovery"]

    llm_end --> check
    check -->|"No"| auto --> tool_end
    check -->|"Yes"| hitl --> send_pending --> wait --> resume
```

Approval decisions are based on Auto-Run mode (Run Everything / Use Allowlist / Ask Everytime). Tool Call IDs are matched via a FIFO queue, stored grouped by tool name; enqueued during `on_chat_model_end`, dequeued during `on_tool_end`.

### HITL Interrupt & Recovery

When a tool requires approval, the Agent Loop is suspended and state is persisted via Checkpointer. Recovery flow:

```mermaid theme={null}
flowchart TD
    resume_start["resume(session_id, tool_call_results)"]
    init["Re-initialize LLM + restore context cache"]
    process["Process approval results"]

    subgraph Results["Approval Decisions"]
        approved["approved → Execute tool"]
        rejected["rejected → Rejection message + prevent retry"]
        timeout_result["timeout → Skip on timeout"]
    end

    checkpoint["Get Checkpoint state"]
    inject["Inject ToolMessage"]
    continue["Continue Agent Loop"]

    resume_start --> init --> process --> Results --> checkpoint --> inject --> continue
```

Rejected tools have a SystemMessage appended, explicitly telling the Agent not to retry.

<Card title="HITL Details" icon="user-check" href="/en/documentation/core-capabilities/hitl">
  Complete description of Auto-Run modes, approval UI, and interrupt recovery mechanism
</Card>

***

## Frontend Processing

The frontend `handleStreamMessage()` consumes the SSE stream, routing events to corresponding state management:

```mermaid theme={null}
flowchart TD
    fetch["fetch POST /api/agent/invoke"]
    reader["ReadableStream Reader"]
    parse["Parse SSE data lines"]

    subgraph Handlers["Event Routing"]
        h_text["TextMessage → Append token"]
        h_tool["ToolCallMessage → Add to trajectory"]
        h_result["ToolCallResultMessage → Update state"]
        h_complete["CompleteMessage → Mark complete"]
        h_error["ErrorMessage → Toast notification"]
    end

    subgraph Stores["State Updates"]
        chat["chatStore — Message list"]
        trajectory["trajectoryStore — Tool calls + Todos"]
        approval["pendingApprovals — Pending approval queue"]
    end

    saver["RealtimeEventSaver — Batch persistence"]

    fetch --> reader --> parse --> Handlers
    Handlers --> Stores
    Handlers --> saver
```

### Event Persistence

`RealtimeEventSaver` batch-persists real-time events:

| Configuration  | Value                              |
| -------------- | ---------------------------------- |
| Batch size     | 3 events                           |
| Batch interval | 100ms                              |
| Retry strategy | Exponential backoff, max 3 retries |
| Fallback       | LocalStorage backup                |

***

## Error Handling

### Backend Errors

| Error Type              | Detection Condition                      | User Message                   |
| ----------------------- | ---------------------------------------- | ------------------------------ |
| Input length exceeded   | Range of input length / InvalidParameter | Suggest shortening input       |
| Context window overflow | context length / token limit             | Suggest starting a new session |
| General exception       | All other Exceptions                     | Includes traceback details     |

Errors are sent via `ErrorMessage` SSE events, containing `error_code` and `details`.

### Frontend Errors

| HTTP Status Code | Meaning              | Handling                 |
| ---------------- | -------------------- | ------------------------ |
| 401              | Unauthorized         | Redirect to login        |
| 403              | Insufficient credits | Toast notification       |
| 503              | Backend not started  | Connection error message |
| 504              | Request timeout      | Timeout message          |

Stream errors (AbortError, network disconnect, parse errors) all have corresponding exception handling and user notifications.

***

## Timeouts

| Timeout Item              | Default       | Description                            |
| ------------------------- | ------------- | -------------------------------------- |
| Agent max execution time  | 7200s (2h)    | FastAPI single call limit              |
| MCP server                | 1800s (30min) | Connection timeout per MCP server      |
| HITL tool approval        | Configurable  | Independently set per tool             |
| LangGraph recursion limit | 999           | Maximum iteration count for Agent loop |

***

## Concurrency & Isolation

* Each Session has independent Checkpointer state (`thread_id` isolation)
* Context Cache is isolated by `session_id`; resume can only recover the corresponding session
* Tool execution is serialized (LangGraph guarantees no concurrent tool execution within the same Session)
* User workspaces are fully isolated by `user_id`

***

## Where Things Can End Early

* **Agent timeout** — Exceeds 7200s maximum execution time
* **HITL approval timeout** — User does not respond within the specified time
* **Frontend disconnect** — Network interruption or user closes the page
* **Credit exhaustion** — Pre-call check fails
* **Model error** — Context window overflow or API exception
