Execution Flow
This page traces a complete request through the Flux-CLI system, from user input to final response.
Complete Request Lifecycle
sequenceDiagram
participant User
participant CLI as CLI Layer
participant Agent as Agent Engine
participant Context as Context Manager
participant LLM as LLM Client
participant Registry as Tool Registry
participant Safety as Approval Manager
participant Tool
User->>CLI: Type prompt or run command
CLI->>Agent: run(message)
activate Agent
Agent->>Agent: yield AGENT_START
Agent->>Context: add_user_message(message)
Agent->>Agent: Check context compression
Note over Agent,LLM: Agentic Loop Start
loop For each turn (max_turns)
Agent->>Context: get_messages()
Context-->>Agent: messages[]
Agent->>Registry: get_schemas()
Registry-->>Agent: tool_schemas[]
Agent->>LLM: chat_completion(messages, tools)
activate LLM
loop Stream events
LLM-->>Agent: TEXT_DELTA (token by token)
Agent-->>CLI: TEXT_DELTA (streamed to UI)
end
LLM-->>Agent: TOOL_CALL_COMPLETE[]
LLM-->>Agent: MESSAGE_COMPLETE (usage)
deactivate LLM
Agent->>Context: add_assistant_message(...)
alt Tool calls received
loop For each tool call
Agent->>Safety: check_approval(context)
Safety-->>Agent: APPROVED / REJECTED / NEEDS_CONFIRMATION
alt Approved
Agent->>Registry: invoke(name, params)
Registry->>Tool: execute()
Tool-->>Registry: ToolResult
Registry-->>Agent: ToolResult
else Rejected
Agent->>Agent: Create error result
end
Agent->>Context: add_tool_result(...)
Agent-->>CLI: TOOL_CALL_COMPLETE
end
Agent->>Agent: Check loop detection
alt Loop detected
Agent->>Context: add_user_message(loop_breaker)
end
else No tool calls (final turn)
Agent->>Context: prune_tool_outputs()
Agent-->>CLI: TEXT_COMPLETE
Agent-->>CLI: AGENT_END
end
end
Note over Agent,LLM: Max turns reached
Agent-->>CLI: AGENT_ERROR (max turns)
deactivate Agent
CLI->>CLI: Render final response
CLI-->>User: Display resultsStep-by-Step Flow
Phase 1: Startup
- CLI Initialization —
main.pyloads configuration, validates the API key, and creates aCLIinstance - Session Creation — A
Sessionobject is created with the LLM client, tool registry, context manager, MCP manager, and approval manager - Agent Initialization — The
Agentis created with the session and optional confirmation callback
Phase 2: Message Processing
- User Input — The user types a prompt (or provides it as a command-line argument)
- Agent Start — The agent yields
AGENT_STARTand triggers thebefore_agenthook - Context Addition — The user message is added to the context manager
- Agentic Loop — The agent enters the multi-turn loop
Phase 3: The Agentic Loop
Each turn of the loop:
- Context Check — If context exceeds 80% of the window, compression is triggered
- Tool Schema Retrieval — The tool registry provides OpenAI-compatible function schemas
- LLM Call — The context and tool schemas are sent to the LLM
- Stream Processing — Text deltas are streamed to the UI, tool calls are collected
- Tool Execution — Each tool call is validated, approved, and executed
- Result Processing — Tool results are added to context and loop detection is checked
Phase 4: Completion
- Final Response — When no more tool calls are needed, the final response is emitted
- Success Hook — The
after_agenthook is triggered - Agent End — The
AGENT_ENDevent is yielded with the final response - Cleanup — The LLM client and MCP connections are closed
Key Design Decisions
Why Async Generators?
The agent uses async for ... yield pattern instead of returning a complete response. This allows:
- Real-time streaming — Users see responses as they're generated
- Incremental UI updates — The TUI can render tool calls as they happen
- Cancellation — The loop can be interrupted gracefully
Why Multi-Turn?
The agent can make multiple tool calls per turn and multiple turns per request. This enables:
- Complex reasoning — The agent can gather information, analyze, take action, and verify
- Iterative refinement — Results from one tool call inform the next
- Autonomous problem-solving — The agent can work through multi-step problems
Why Max Turns?
The max_turns configuration prevents runaway agents. When the limit is reached:
- An
AGENT_ERRORis emitted - The
on_errorhook is triggered - The loop terminates gracefully