# Agent Loop Sequence Diagram (ASCII) ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ ┌───────────┐ │ User │─────>│ Agent │─────>│ AgentLoop │─────>│ LLM │ └─────────────┘ └─────────────┘ └─────────────────┘ └───────────┘ │ ▼ ┌─────────────┐ ┌─────────────────┐ ┌───────────┐ ┌─────────────┐ │ User │<─────│ AgentLoop │─────>│ Agent │─────>│ Session │ └─────────────┘ └─────────────────┘ └───────────┘ └─────────────┘ │ ▼ ┌─────────────┐ │ Session │ └─────────────┘ ┌─────────────────────────────────────────────────────────────────────────────────────────┐ │ AGENT LOOP MAIN FLOW │ └─────────────────────────────────────────────────────────────────────────────────────────┘ User │ │ prompt(messages) ▼ Agent │ │ normalizePromptInput() │ runPromptMessages() │ ├─ alt steering_queue has messages │ │ drain(steering_queue) │ │ runPromptMessages() │ ├─ alt follow_up_queue has messages │ │ drain(follow_up_queue) │ │ runPromptMessages() │ └─ else (normal prompt) │ createLoopConfig() │ createActiveRun() │ ▼ AgentLoop │ │ agentLoop(messages, context, config) │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ MAIN LOOP │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. Handle queued messages │ │ ├─ drain(steering_queue) │ │ ├─ emit(MessageStartEvent) │ │ ├─ push to context.messages │ │ └─ emit(MessageEndEvent) │ │ │ │ 2. Get LLM Response │ │ ├─ transform_context?(messages) │ │ ├─ convert_to_llm(messages) │ │ └─ stream_function(model, context, config) │ │ │ │ LLM │ │ │ response stream │ │ └─ emit(MessageStartEvent) │ │ └─ emit(MessageUpdateEvent) (streaming) │ │ └─ emit(MessageEndEvent(final_message)) │ │ │ │ 3. Check for Tool Calls │ │ └─ extract_tool_calls(message.content) │ │ │ │ 4. Execute Tools (if any) │ │ ├─ emit(ToolExecutionStartEvent) │ │ ├─ prepareToolCall() │ │ │ ├─ before_tool_call? (block if needed) │ │ │ └─ validateToolArguments() │ │ ├─ execute(tool_call_id, args) │ │ │ Tool │ │ │ │ execute() │ │ │ └─ on_update(partial_result) │ │ ├─ emit(ToolExecutionEndEvent) │ │ └─ createToolResultMessage() │ │ │ │ 5. Emit Turn End Event │ │ └─ emit(TurnEndEvent(message, tool_results)) │ │ │ │ 6. Prepare Next Turn │ │ └─ prepare_next_turn(context) │ │ └─ AgentLoopTurnUpdate │ │ │ │ 7. Check Stop Condition │ │ └─ should_stop_after_turn? (break if true) │ │ │ │ 8. Get Steering/Follow-up Messages │ │ ├─ drain(steering_queue) │ │ └─ drain(follow_up_queue) │ │ │ │ 9. Continue or Break │ │ ├─ alt pending messages exist → continue loop │ │ └─ else → break │ │ │ └─────────────────────────────────────────────────────────────┘ │ ▼ AgentLoop │ │ emit(AgentEndEvent(messages)) │ ▼ Agent │ │ appendMessage() → Session │ ▼ User (via promise) ``` ## KEY COMPONENTS ### Agent Layer ``` ┌──────────────────────────────────────────────────────────────┐ │ Agent │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ state: AgentState │ │ │ │ - system_prompt │ │ │ │ - model │ │ │ │ - messages[] │ │ │ │ - tools[] │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Queues: │ │ │ │ - steering_queue: inject after assistant turn │ │ │ │ - follow_up_queue: run when agent stops │ │ │ └──────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────┘ ``` ### AgentLoop Layer ``` ┌──────────────────────────────────────────────────────────────┐ │ AgentLoop │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ agentLoop() │ │ │ │ - Main loop execution │ │ │ │ - Tool orchestration │ │ │ │ - Event emission │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ executeToolCallsSequential() │ │ │ │ executeToolCallsParallel() │ │ │ │ - Tool execution coordination │ │ │ │ - Hook invocation │ │ │ │ - Result collection │ │ │ └──────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────┘ ``` ### Lifecycle Events Flow ``` AgentStartEvent │ ├─ TurnStartEvent │ ├─ MessageStartEvent (user/assistant) │ ├─ MessageEndEvent │ ├─ ToolExecutionStartEvent │ │ ├─ ToolExecutionUpdateEvent (streaming) │ │ └─ ToolExecutionEndEvent │ └─ TurnEndEvent │ └─ AgentEndEvent (with final messages) ``` ## QUEUE PROCESSING ORDER 1. **Initial steering messages** (if any) 2. **Main loop**: - Steering/follow-up messages (if any) - LLM call - Tool execution (if any) - Turn end event - Next turn preparation - Check stop condition 3. **Follow-up messages** (after loop exits, only if no steering) ## DATA FLOW ``` Input: User messages → Agent.normalizePromptInput() → AgentLoop LLM Call: AgentMessage[] → transform_context() → convert_to_llm() → LLM.stream() → AssistantMessage Tool Execution: ToolCall[] → prepareToolCall() → before_tool_call? → execute() → after_tool_call? → ToolResultMessage[] Output: AgentEndEvent(messages) → Session.appendMessage() → User promise ``` ## EXECUTION MODES ### Sequential (EXECUTION_SEQUENTIAL) ``` ToolCall1 → ToolCall2 → ToolCall3 │ │ │ ▼ ▼ ▼ Result1 Result2 Result3 ``` ### Parallel (EXECUTION_PARALLEL) ``` ToolCall1 ─┐ ToolCall2──┼→ Execute all → Wait for all → Results ToolCall3 ─┘ ```