OpenCode Is an Agent Runtime, Not Just a Chat Interface

This series examines OpenCode as a local, open-source coding agent and asks a practical question: how does it turn a model into a participant in real repository work without making the model the whole system? The answer matters because reading files, changing code, running commands, preserving a long task, and connecting external capabilities are different responsibilities. OpenCode coordinates them through project-scoped context, agents, models, tools, permissions, and durable sessions. Understanding those boundaries gives the reader a way to judge both what OpenCode can do and what they still have to control.

The real unit is a working repository task

A chat interface is organized around an exchange of messages. A coding agent is organized around a task that may require investigation, decisions, actions, feedback, and several more exchanges before it is complete. The distinction is not that one system uses a larger language model. It is that the surrounding runtime can expose a project to the model, let the model request capabilities, record what happened, and continue from the resulting state.

OpenCode describes itself simply as “The open source AI coding agent.” Its README presents a terminal interface, installation through several package managers, a beta desktop application, model selection, and two built-in user-facing agents. The build agent is the default full-access development mode; plan is read-only and intended for analysis and exploration. That summary is deliberately approachable. The implementation contains additional hidden and subagent-oriented roles for work such as exploration, title generation, summaries, and compaction. The product’s simple front door therefore sits over a more substantial orchestration layer.

That layer changes the meaning of a request such as “update the authentication flow.” A model endpoint can generate a plausible patch, but it does not by itself know which directory is the active project, which instructions apply, whether a file should be read, whether a command needs approval, what happened in the previous turn, or whether the response must continue after a tool call. OpenCode supplies those missing decisions around the model.

The central idea is controlled orchestration. “Controlled” does not mean that the agent is fully contained or incapable of surprise; the repository explicitly says that OpenCode does not sandbox the agent. It means that the runtime gives separate names and decision points to things that are often blurred together in a demo: selecting a model is not executing a command, describing a tool is not authorizing it, and displaying a response is not the same as preserving a session.

This is also why OpenCode should not be understood as merely an alternative model client. A model client primarily solves transport and provider compatibility. OpenCode has to coordinate a changing project environment, a conversation that may outlive one model request, and actions whose consequences occur on the host machine. Its distinctive subject is the relationship among those concerns.

A map of the runtime

The easiest way to orient yourself is to follow the boundaries in the order a task crosses them. A user may enter through the terminal UI or an HTTP client. The request is associated with a project-scoped Location: the directory, worktree, project identity, and optional workspace context that define where the work takes place. A Session then gives the task continuity. It holds the selected agent and model, the conversation, tool activity, and the durable state needed to continue.

The agent is a policy-bearing role, not simply a personality preset. OpenCode’s built-in modes illustrate the difference: build and plan can expose different capabilities even when they operate on the same project. A selected model is similarly more than a string in a dropdown. It is resolved through provider and model metadata, credentials or integrations, capability information, and an adapter that knows how to speak the provider’s protocol. Those choices shape the request before it reaches a model.

Tools form the next boundary. A tool has an input and output contract, an executor, and a model-facing representation. In the V2 core, input is decoded before execution and output is validated before a successful result is published. The registry decides which named tools are visible for a turn and checks that a call still refers to the registration that was advertised. A read, edit, shell, or search operation is therefore not an unstructured escape from the model into the host. It is a runtime operation with its own identity, validation, authority, and result path.

The provider turn is the point at which OpenCode asks a model what to do next. The model may return text, reasoning, or a tool call. If it returns a local tool call, the runtime can authorize and execute it, record the result, and make another provider turn with the new information. The model supplies judgment inside that loop; OpenCode owns the loop’s surrounding mechanics.

The same map can be read in reverse. A tool result becomes model-facing content, the provider’s response becomes a durable session event or projected message, and the client observes the updated state. This two-way movement is important: the runtime is not only sending prompts outward. It is also deciding what becomes part of the record that future turns and clients can rely on.

flowchart LR Client["TUI or HTTP client"] --> Location["Project-scoped Location"] Location --> Session["Durable Session"] Session --> Selection["Agent and Model selection"] Selection --> Tools["Typed Tools and Permission boundary"] Selection --> Turn["Provider turn"] Tools --> Durable["Durable result and event state"] Turn --> Durable Durable --> Session

The repository’s client and server structure reinforces this model. The TUI can use an embedded worker and an in-memory fetch bridge, or it can communicate through an HTTP server. Both paths use the same route and handler concepts rather than defining unrelated local and remote products. The server assembles API groups for sessions, messages, models, providers, agents, permissions, files, events, and other capabilities. The transport can change while the domain boundary remains recognizable.

That design has a practical consequence for readers who are not studying the source code. When OpenCode appears to “know” the project, there is no single magic context switch to look for. Project awareness is assembled from several layers: the active Location establishes scope; the Session provides history; the agent determines role and permissions; the model/provider layer determines what the selected deployment can support; and tools provide controlled access to information or actions. The result is useful precisely because the responsibilities remain distinguishable.

Why the boundaries matter

The boundaries are not academic labels. They determine what can go wrong and what a user can inspect.

If the Location is wrong, a perfectly capable agent may inspect or change the wrong project. If the model is unavailable or its route is unsupported, OpenCode needs to report that rather than silently choosing a different protocol. If a tool call is malformed or stale, the runtime should reject it without invoking the handler. If a permission rule denies an action, the request should stop or ask for approval according to the policy instead of being treated as ordinary model text. If the session is long, the system needs a way to preserve useful state without sending an ever-growing transcript to the provider.

OpenCode’s source shows explicit mechanisms for several of these cases. The session runner records local tool calls before side effects, persists tool outcomes, and continues after settlement. The event and projection layers give clients a durable history as well as live updates. Compaction summarizes older material and keeps a bounded recent context, rather than pretending that an unlimited context window exists. Oversized tool output can be retained separately while the provider receives a bounded preview. These mechanisms do not make every failure disappear; they make failure and continuation different states that the runtime can represent.

The security boundary is especially important. OpenCode’s permission system can evaluate ordered rules, ask for approval, remember project-scoped approvals, and distinguish actions such as reading, editing, and using an external directory. Those controls help a user understand and govern the agent’s actions. They do not isolate the process. The repository’s security policy states that shell execution retains the host user’s filesystem, process, and network authority, and recommends a container or virtual machine when true isolation is required. A permission prompt is therefore a consent and awareness mechanism, not a substitute for deployment security.

The same discipline applies to extensions. An in-process plugin can add hooks or tools within OpenCode’s runtime and lifecycle. An MCP server is an external protocol peer that may run locally or remotely, expose tools and resources, and require its own credentials or OAuth flow. The fact that both can contribute capabilities to a model does not make them equivalent in trust or failure behavior. Later in this series, that difference will matter more than the length of an integration list.

There is also a maturity boundary in the repository itself. The current code contains a broad legacy application path alongside an Effect-native V2 core that is being built around smaller typed services, scoped registrations, durable events, and explicit errors. The V2 session runner documents completed slices beside missing work, including some provider routes, plugin and MCP materialization, clustered ownership, post-crash recovery, and operational backpressure. A useful explanation must preserve that distinction. A design document can reveal the intended architecture, but it is not automatically evidence that every intended behavior is already the default runtime.

This gives us a concrete standard for evaluating OpenCode. Do not ask only whether the model produces a good answer in a short demonstration. Ask whether the system can perform a repository task while keeping four things legible: what context it used, what authority it exercised, what state it preserved, and which component owns the next decision. The next step is to follow one request through those boundaries in detail, from prompt admission to provider turn, tool execution, and continuation.

References