The Agent Loop
“One Loop Is All You Need”
The smallest useful agent is a loop that calls the model, runs tools, and feeds results back.
The core agent loop follows a clear pattern: a while loop calls the LLM, inspects the response for tool calls, executes them, and feeds the results back into the conversation. This pattern forms the foundation of every AI coding agent. The loop itself is framework-agnostic, working with Anthropic's SDK, OpenAI, or any other provider.
Architecture Flow
The Problem: How Does an AI Agent Take Actions?
An LLM alone cannot execute code, read files, or interact with the environment. It can only generate text. To become an agent, the model needs a mechanism to request actions and receive results. The simplest approach is to let the model output structured responses, known as tool calls, intercept them in a loop, execute the requested action, and feed the result back into the conversation. This creates a closed feedback loop that lets the model work iteratively toward a goal.
Why a Simple Loop Wins
The while-true loop represents the minimal viable architecture for an AI agent. There is no state machine, no planner, no task queue. The system prompt tells the model how to behave, and the model's own reasoning determines which tools to call and in what order. This simplicity carries significant advantages: the loop is provably correct, always running until the model signals completion; it is trivially debuggable, with every iteration forming a complete request-response cycle; and it is infinitely extensible, as new capabilities enter through tools rather than modifications to loop logic.
Real Implementation: Effect-Based Concurrency
OpenCode's V2 agent loop (packages/core/src/session/runner/llm.ts) replaces the basic while-true pattern with the Effect TypeScript library. Each turn executes through runTurnAttempt(), which streams the LLM call through the provider SDK, detects tool calls, and settles them in parallel using FiberSet, Effect's structured concurrency primitive. The loop handles multiple exit paths: tool_use continuation, step limits where agent.info.steps bounds the iteration count, context overflow with auto-compaction mid-stream, and interruption signals. Tool calls are not sequential. FiberSet runs all tools concurrently and awaits their collective completion before proceeding to the next LLM call.
Design Decisions
The loop checks `stop_reason != 'tool_use'` instead of checking for an explicit 'done' signal. This means the model naturally signals completion when it has nothing left to execute, with no extra protocol required.
Model selection is a configuration parameter rather than a hardcoded value. The same loop works with any OpenAI-compatible API, making it provider-agnostic from the start.
Comparison: Claude Code
Claude Code uses the same tool-use loop architecture of send, tool_use, execute, append, and repeat. OpenCode distinguishes itself through async Effect and Stream primitives for LLM calls and a formal ToolID schema registry. Where Claude Code uses a flat tool list, OpenCode organizes tools by category (read, write, shell, skill) with structured input and output schemas.
Deep Dive: Design Decisions
Why Bash Alone Is Enough
Bash can read files, write files, run arbitrary programs, pipe data between processes, and manage the filesystem. Any additional tool would be a strict subset of what bash already provides. Adding more tools doesn't unlock new capabilities — it just adds surface area for confusion.
No Planning Framework — The Model Decides
There is no planner, no task queue, no state machine. The system prompt tells the model how to approach problems, and the model decides what tool to call next based on the conversation so far.