Request Flow
Every conversation turn follows a structured pipeline: a message arrives through a gateway, the session handler loads context, the loop orchestrator manages API-tool rounds, hooks fire at defined points, and the cycle repeats until the model produces a final text response or the tool round limit is reached.
Step-by-Step Walkthrough
1. Message Arrives via Gateway
A user message enters the system through a gateway. The two built-in gateways are:
- Telegram (
plugins/gateways/telegram/handler.gleam) -- receives messages from Telegram via long-polling, extracts text and media, and routes them to the session handler - CLI REPL (
agent.gleam) -- reads stdin in a loop and passes each line to the session handler
2. Session Handler Loads Context
The session handler (core/session/handler.gleam) loads the existing session via the SessionStore behaviour, resolves parent lineage (for forked/child sessions), and hands the complete conversation state to the loop orchestrator.
3. Loop Orchestrator Manages the API-Tool Loop
The orchestrator (core/loop/orchestrator.gleam) is a pure function that runs the core conversation loop. It receives a LoopContext (wired at startup by agent_supervisor.gleam) containing all injected behaviours. Each iteration:
- Context Builder (
services/context/context.gleam) assembles the system prompt with persona instructions, active memories, tool usage guidance, and relevant project files - LLM Client (
services/api/openai/completions.gleam) sends the assembled conversation to the model and returns either a text response or a list of tool calls - Tool Executor (
core/tool/executor.gleam) validates tool calls, groups parallel-safe tools, and dispatches them (parallel calls run as separate OTP processes; dependent calls run sequentially) - Tool Registry (
core/registry/registry.gleam) resolves tool names to handler functions for dispatch
4. Hooks Fire at Defined Points
Three hook types intercept the loop at specific moments:
- Context Compression (
plugins/hooks/context_compressor/) -- fires before every API call. If the context window is at 70% or more capacity, it summarizes middle messages in-place using a secondary LLM call, reducing token usage without losing conversation structure - Tool Guardrails (
plugins/hooks/tool_guardrails/) -- fires after each tool execution batch. Detects repetitive tool calls and stuck loops, escalating through warn/block/halt stages. Acts as a circuit breaker for runaway tool chains - Reflection (
plugins/hooks/reflection/) -- fires after each completed turn. Spawns an asynchronous review agent that analyzes the turn and consolidates important information into persistent memory
5. Loop Continues Until Terminal State
The loop repeats from step 3 until one of two conditions is met:
- The model returns a plain text response (no tool calls), indicating it is ready to reply to the user
- The tool round limit is reached (configurable, prevents infinite loops)
When either condition is met, the final response is sent back through the gateway to the user.
Related Pages
- Project Structure -- File tree and layer responsibilities
- Dependency Injection -- How the LoopContext is wired
- Core Modules -- Deep dive into orchestrator, registry, session, and tool modules