condensate

How it works

Models and other harnesses

The three ways an agent loop runs: native, wrapped, or through middleware.

On this page

A model generates a response. A harness turns that response into work: it supplies context, offers tools, executes tool calls, and sends the results back to the model. Condensate runs that loop itself, wraps Claude Code and Codex sessions, and connects to other harnesses through middleware. This page explains the three paths and how to choose one.

Explore the three integration paths
CondensateOwns the agent loop
Model APIReturns text or tool calls
ToolsResults return to the loop
Loop owner: CondensateProvider transport: pi-ai. Runtime: agents-host.
Switch paths to see which component owns execution. Connecting MCP tools alone does not turn an external session into a durable Condensate thread.

The agent loop

Suppose you ask an agent to explain a function. The model requests a file read. The harness executes that read, returns the contents, and calls the model again. The model can now explain the function or request another tool.

Your request: “Explain how this function works.”
    ↓
Model requests a file read
    ↓
Harness executes the tool and returns the file contents
    ↓
Model reads the result and responds

This is an illustrative turn, not an API payload. The model proposes tool calls, and the harness runs them under its configured policy.

The three engines

The engine setting picks who runs the agent loop for a session. Condensate ships three engines:

EngineEngine idWho runs the loop
Condensate's own agent (shown as API in the app, engine id pi)piCondensate, inside the runtime process
Claude CodeclaudeThe Claude CLI, through the Claude Agent SDK
CodexcodexThe Codex CLI's app-server

These ids come from packages/engine/src/model/engine-id.ts. The session factory, createSession in packages/engine/src/index.ts, builds one adapter for each:

// @noErrors: condensed from createSession in packages/engine/src/index.ts; it names helpers the engine package does not export
if (full.model && !enginesForModel(full.model).includes(full.engine)) throw ModelEngineMismatch
if (full.engine === "codex") return createCodexSession(full, DEFAULT_CODEX_SESSION_OPTIONS) // refused first if the pinned CLI is missing
if (full.engine === "claude") return createProcessClaudeSession(full)
return createPiLoop(full) // wrapped in Codex auth self-healing

Before it builds a session, the factory checks that the model you asked for can run on that engine. Claude model ids run only on the Claude engine. OpenAI model ids run on Condensate's own agent or on Codex. Every other model id runs on Condensate's own agent. A mismatch is refused at the spawn, before any turn is sent.

Path 1: Condensate runs the loop

Condensate's own agent owns the model-and-tool cycle, transcripts, and context-window policy. It runs no CLI. It runs inside the runtime process and uses @earendil-works/pi-ai to talk to provider APIs, plus selected utilities from pi-coding-agent. Its loop lives in packages/engine/src/pi/loop.ts.

// @noErrors: quoted from packages/engine/src/index.ts; the relative path resolves only inside that package
import { createPiLoop } from "./pi/loop.js";

For application code, use the runtime or SDK. The import above identifies the internal implementation; it is not an application setup example.

Path 2: Condensate wraps a Claude Code or Codex session

The Claude Code engine runs a session through the Claude Agent SDK, which runs the Claude CLI it bundles. The Codex engine runs the Codex CLI's app-server. In both, the CLI runs the loop. Condensate coordinates the surrounding thread and records the session's turns.

The Codex engine needs one pinned release of the Codex CLI, codex-cli 0.160.0 in this build. It runs the binary that CONDENSATE_CODEX_BIN names, else the CLI inside the ChatGPT desktop app, and refuses any other version. The check runs when a session is created, so a missing or mismatched binary fails at spawn, with the path and both versions in the message, rather than on the first turn. When the ChatGPT app updates its CLI, the Codex engine is unavailable until the versions match again.

Credential proxy

The credential proxy is on by default. The runtime refuses to boot when a proxy setting is empty, the proxy does not answer, or its CA file or management key file cannot be read. CONDENSATE_PROXY=off turns it off. With the proxy on, every session holds only placeholder tokens: Claude Code, Codex and Condensate's own agent alike. The proxy swaps in the real credential on the way out. A spawn that would hand a session a real model credential is refused. Accounts covers the setup.

Path 3: Another harness connects to middleware

An existing harness can connect to Condensate's MCP endpoint for tools and wire its lifecycle hooks to middleware. Its own loop continues to run. Condensate can then evaluate the events and tool calls that flow through those connections.

For example, a tool request can pass through middleware policy before reaching an upstream server. A lifecycle hook can return a verdict through the client's hook shim. The client's supported hooks determine what it can apply.

Condensate's plugin does this for Claude Code and Codex. It registers the UserPromptSubmit, PreToolUse, PostToolUse and Stop hooks, and sends SessionStart, SessionEnd, PreCompact, PostCompact, SubagentStart, SubagentStop and Notification where the host supports them. Middleware returns a verdict on the four main hooks. It records the other events as observations. The plugin does not carry model traffic, so the host's own model loop is unchanged.

Connect tools and hooks shows the endpoint configuration and a catalog request.

Choose a path

You want to…Use…Who runs the session?
Build an app around durable agent tasksRuntime + Condensate's own agentCondensate
Use Claude Code sessions inside that runtimeRuntime + Claude Code engineThe Claude CLI, through the Claude Agent SDK
Use Codex sessions inside that runtimeRuntime + Codex engineThe Codex CLI's app-server
Add tools and policy to an existing agentMCP + supported hooksThe external harness

Provider access is a separate choice. A model id alone never picks the Codex engine: an OpenAI model id runs on Condensate's own agent unless your spawn or your routing document names codex. Selecting an OpenCode model catalog entry does not launch the OpenCode CLI. The engine setting tells you which execution path is active.

Where the runtime fits

The harness handles the conversation with the model. The runtime keeps the durable thread and exposes controls to applications. Together they let a UI follow a task, recover its recorded history, and request further work through a consistent interface.

Continue with the lifecycle of a thread or context windows.