Backends¶
A backend is Cortex's adapter for a specific coding-agent CLI. Cortex
does not call LLM APIs directly. It spawns a coding agent (Claude Code,
PI, or Codex) as a child process, sends messages to it, and consumes a
normalized event stream. Each backend implements the AgentAdapter
interface defined in agent-server/src/agent-adapter/types.ts.
Supported backends¶
| Backend | Status | Binary | npm package | Feature level |
|---|---|---|---|---|
| Claude Code | Supported | claude |
@anthropic-ai/claude-code |
Full (8/8 capabilities) |
| PI | Supported | pi |
@mariozechner/pi-coding-agent |
Full (8/8 capabilities) |
| Codex | Planned | codex |
— | Partial (3/8 capabilities) |
How backends work¶
When an agent session starts, Cortex resolves the active profile (from
profiles.json or the --profile flag) to determine which backend to use.
It then calls getAdapter(backend) to get the adapter instance and calls
adapter.spawn(config) to start a session.
The AgentSpawnConfig carries the full session context: system prompt,
plugin directories, tool allowlist, MCP server config, hooks, model name,
and backend-specific passthroughs. The adapter translates this into
backend-native CLI arguments and spawns the coding agent.
From there, Cortex sends user messages and receives a normalized event
stream. The normalization layer (agent-adapter/normalize/) translates
each backend's native event format into a common NormalizedEvent
discriminated union, so the orchestration layer never needs to know which
backend is running.
Feature matrix¶
Cortex defines eight capabilities that a backend may support. The orchestration layer checks capabilities before attempting backend-specific operations.
| Capability | Claude Code | PI | Codex | Description |
|---|---|---|---|---|
hooks |
yes | yes | no | PreToolUse/PostToolUse/Stop hooks via hook-bridge |
plugins |
yes | yes | no | Role-scoped skill plugins via --skill or equivalent |
mcp |
yes | yes | yes | MCP tool server integration |
plan-mode |
yes | yes | no | EnterPlanMode/ExitPlanMode tool support |
ask-user-question |
yes | yes | no | AskUserQuestion tool support |
system-prompt-override |
yes | yes | yes | Custom system prompt injection |
session-resume |
yes | yes | yes | Resume an existing session |
tool-allowlist |
yes | yes | no | Restrict available tools to a subset |
Claude Code¶
The reference backend. Supports all eight capabilities natively. Two adapter modes are available:
Print mode (claudeBackend: "print", default). Uses claude -p
--stream-json for one-shot turns. Each user message spawns a fresh Claude
invocation. Fast, stateless, and the recommended mode for most use cases.
TUI mode (claudeBackend: "tui"). Spawns an interactive Claude session
under tmux and tails the session's JSONL file for events. Supports
multi-turn conversation with session persistence. Heavier resource usage
but allows interactive workflows.
Claude Code adapter session pool is keyed by channel for session reuse.
Cost reporting reverse-derives USD from message.usage token counts using
Anthropic's published pricing.
PI¶
Full feature parity with Claude Code. PI's adapter bridges the gap where PI's native feature set differs:
- MCP — implemented via
mcp-bridge.ts, an extension that connects PI to Cortex's MCP server. Auto-injected via--extensionat spawn time. - PlanMode / AskUserQuestion — implemented via
tool-shims.tspseudo tools that registerask,exit_plan, andtodoas first-class PI tools, routing responses throughextension_ui_response. - Hooks — implemented via
hook-bridge.ts, which translates PI tool events to Cortex hook scripts. - Plugins — PI's native
--skillflag maps to Cortex's plugin system.
PI sessions use --session <path> for resume and --system-prompt for
system prompt override. The adapter handles LF-only NDJSON framing for
PI's event stream.
Codex¶
Codex currently supports three capabilities: MCP, system prompt override, and session resume. The adapter is present in the codebase but the backend is marked as planned rather than supported.
Selecting a backend¶
Backends are selected per profile in $CORTEX_HOME/config/profiles.json
(see configuration.md for the full profiles schema):
{
"defaultProfile": "plan",
"profiles": {
"plan": {
"model": "claude-sonnet-4-20250514",
"backend": "claude"
},
"execute": {
"model": "claude-sonnet-4-20250514",
"backend": "pi"
}
}
}
The backend field accepts "claude", "pi", or "codex". If omitted,
it defaults to "claude".
Thread templates can also specify a profile per agent, allowing different agents in the same pipeline to use different backends. See threads.md for template configuration.
Fallback behavior¶
Each profile entry can specify a fallback array of alternative profiles.
If the primary backend call fails with a transient error (network timeout,
rate limit, authentication), Cortex iterates through the fallback chain in
order. Each fallback entry inherits unspecified fields from the primary.
Example:
{
"plan": {
"model": "claude-sonnet-4-20250514",
"backend": "claude",
"fallback": [
{ "model": "claude-sonnet-4-20250514", "backend": "pi" }
]
}
}
Cost reporting¶
Cost reporting differs by backend:
- Claude Code — reverse-derives USD cost from
message.usagetoken counts (input/output) using Anthropic's published per-model pricing. Costs are written to$CORTEX_HOME/data/costs.jsonl. - PI — cost reporting depends on the PI coding agent's provider configuration. The adapter captures whatever cost metadata PI emits.
- Codex — cost reporting is not yet implemented.
All cost records follow the same JSONL format and are subject to a 90-day
rolling retention window. Cost queries via MCP tools aggregate across all
backends — see mcp.md for the cost_query tool.
Adding a new backend¶
New backends implement the AgentAdapter interface in a new directory
under agent-server/src/agent-adapter/. The required surface:
adapter.ts— implementsAgentAdapterwithspawn(),close(),kill(), andlistSessions(). Returns anAgentProcessfromspawn().AgentProcess— exposessend(message)for user messages andeventsas an async iterable ofNormalizedEvent. Must also supportclose()andkill().event-parser.ts— translates the backend's native event format toNormalizedEventdiscriminated union members.- Registration — add the adapter to the
ADAPTERSmap inagent-adapter/index.ts, add capabilities tocapabilities.ts, and include the backend label in theBackendtype union intypes.ts.
The normalization layer (agent-adapter/normalize/) provides shared
utilities for event stream queuing, tool name translation, and hook
specification that all backends use.