opencode-orchestrator is an enterprise-ready, multi-tenant, ephemeral orchestration engine and CLI for OpenCode. It provides stateless task execution inside isolated tmpfs sandboxes, persists session history and conversation memory in PostgreSQL, translates events in real time to the AG-UI Protocol (SSE), and supports human-in-the-loop approvals via a bidirectional stdio RPC bridge.
- Stateless Compute (
tmpfsIsolation): Every execution turn provisions a fresh directory at/tmp/sandboxes/{sessionId}. OpenCode writes its temporary SQLite database here; upon turn conclusion, the sandbox is purged (rm -rf). - Context Rehydration via PostgreSQL: Before launching the process, the orchestrator queries PostgreSQL for the latest session summary and prior conversation turns, constructing structured context prefixes (
=== PREVIOUS SESSION SUMMARY ===,=== RECENT CONVERSATION HISTORY ===,=== CURRENT TASK ===). - Declarative Skills & MCP Integration: Dynamic agent skills (
SKILL.md) and Model Context Protocol (taskConfig.mcp) servers are automatically provisioned in the sandbox environment immediately prior to process spawning. - AG-UI Protocol Streaming: OpenCode stdout JSON events are mapped to AG-UI SSE events in real time.
- Bidirectional Interaction (Stdio Bridge): Permission requests trigger an
INTERACTION_REQUESTevent over SSE and suspend the sub-process. User decisions viaPOST /api/v1/sessions/:id/interactionsare written directly intostdinto resume execution.
| OpenCode Raw Event | AG-UI SSE Event | Description |
|---|---|---|
token / text |
MESSAGE_START / TEXT_MESSAGE_CONTENT |
Assistant response token stream |
plan_update |
STATE_DELTA (path: "/todos") |
Progress plan and checklist updates |
step_finish |
STATE_DELTA (path: "/metrics") |
Token usage and cost metrics |
tool_start / tool_use |
TOOL_CALL_START |
Tool execution triggered |
tool_finish / tool_use |
TOOL_CALL_RESULT |
Tool completion output & status |
permission_request / permission |
INTERACTION_REQUEST |
Approval request halting execution |
session_compacted |
STATE_DELTA (path: "/summary") |
Updated compacted memory summary |
done |
MESSAGE_END + RUN_FINISHED |
Completion status and stream termination |
# Clone repository
git clone https://github.com/even-wei/opencode-orchestrator.git
cd opencode-orchestrator
# Install dependencies
npm install
# Build TypeScript
npm run buildCopy .env.example to .env and set your PostgreSQL and timeout configurations:
cp .env.example .env| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Port for the HTTP & SSE server |
HOST |
0.0.0.0 |
Host interface to bind |
DATABASE_URL |
postgresql://... |
PostgreSQL connection string |
SANDBOX_BASE_DIR |
/tmp/sandboxes |
Base directory for ephemeral execution |
OPENCODE_BIN_PATH |
opencode |
Path to the OpenCode CLI executable |
DEFAULT_CONTEXT_TURNS |
10 |
Number of previous turns to rehydrate |
PROCESS_TIMEOUT_MS |
300000 |
Process execution timeout (ms) |
INTERACTION_TIMEOUT_MS |
300000 |
Pending approval timeout (ms) |
Spin up PostgreSQL (or use Docker) and apply schema.sql:
# Spin up PostgreSQL in Docker
docker run -d --name opencode-pg -p 5432:5432 -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=opencode postgres:16
# Apply migrations using CLI
npm run cli -- migrate# Production server
npm start
# Development mode with tsx
npm run devopencode-orchestrator can also be used as a standalone CLI tool:
# Show CLI Help
npx opencode-orchestrator --help
# 1. Execute a one-off ephemeral turn directly from terminal
npx opencode-orchestrator run "Write a quicksort in Python" -m openrouter/deepseek/deepseek-v4-flash
# 2. Run with a published agent from the team registry
npx opencode-orchestrator run --agent db-analyzer "Inspect active table indexes"
# 3. Run with AG-UI SSE stream output
npx opencode-orchestrator run "Say hello" -m openrouter/deepseek/deepseek-v4-flash --format sse
# 4. Interactive Agent Genesis Wizard (Create, Test, Iterate, Publish)
npx opencode-orchestrator agent init
npx opencode-orchestrator agent test my-agent.agent.json
npx opencode-orchestrator agent iterate my-agent.agent.json --feedback "Add LIMIT 20 to queries"
npx opencode-orchestrator agent publish my-agent.agent.json
npx opencode-orchestrator agent list
# 5. Start the HTTP & SSE Server
npx opencode-orchestrator serve -p 8080
# 6. Run automated system verification (DB, CLI, Sandbox, Skills, OTel)
npx opencode-orchestrator verify
# 7. Run live end-to-end turn verification with LLM
npx opencode-orchestrator verify --live -m openrouter/deepseek/deepseek-v4-flash
# 8. Apply PostgreSQL migrations
npx opencode-orchestrator migrate
# 9. Check database & binary health
npx opencode-orchestrator healthPOST /api/v1/sessions/:id/stream or POST /api/v1/sessions/:id/turn (also supports GET /api/v1/sessions/:id/stream)
Request Body:
{
"tenantId": "tenant_101",
"model": "openrouter/deepseek/deepseek-v4-flash",
"prompt": "Create a database migration for the users table",
"taskConfig": {
"model": "openrouter/deepseek/deepseek-v4-flash"
},
"skills": [
{
"name": "db-migrate",
"content": "# Database Migration Guide\nUse knex migrations."
}
]
}Response: text/event-stream (AG-UI SSE stream)
event: MESSAGE_START
data: {"messageId":"msg_101","role":"assistant","runId":"run_1786789041"}
event: TEXT_MESSAGE_CONTENT
data: {"messageId":"msg_101","delta":"I will create the migration file."}
event: TOOL_CALL_START
data: {"callId":"call_1","tool":"bash","params":{"command":"touch migration.sql"}}
event: TOOL_CALL_RESULT
data: {"callId":"call_1","result":"File created","isError":false}
event: STATE_DELTA
data: {"path":"/metrics","op":"replace","value":{"tokens":{"total":1200,"input":1100,"output":100},"cost":0.0001}}
event: MESSAGE_END
data: {"messageId":"msg_101"}
event: RUN_FINISHED
data: {"runId":"run_1786789041","status":"completed","exitCode":0}POST /api/v1/sessions/:id/interactions
Request Body:
{
"interactionId": "perm_101",
"resolution": "approved",
"data": {
"selectedOption": "approve",
"feedback": "Deployment approved for staging"
}
}Response:
{
"status": "acknowledged",
"interactionId": "perm_101"
}POST /api/v1/tenants: Create/ensure tenantPOST /api/v1/sessions: Create new sessionGET /api/v1/sessions/:id: Get session metadata & statusGET /api/v1/sessions/:id/events: Get session event historyGET /healthorGET /api/v1/health: Health check endpointGET /livez: Kubernetes liveness probeGET /readyz: Kubernetes readiness probe (PostgreSQL verified)
opencode-orchestrator features a built-in Meta-Agent Factory that allows any team member to design, test, iterate, and publish production-grade AI agents in under 3 minutes.
npx opencode-orchestrator agent init- Scans local repository schemas (
package.json,schema.sql,Dockerfile). - Selects matching Verified MCPs (
postgres,github,slack,fetch) and Curated Skills (db-analyzer,git-release,pr-reviewer). - Synthesizes and lints
SKILL.mdrules and generates benchmark test cases in./<agent-name>.agent.json.
npx opencode-orchestrator agent test my-agent.agent.jsonRuns the agent's evalSuite in disposable ephemeral sandboxes and asserts deterministic post-conditions (file_exists, output_contains, expected_tool_called).
npx opencode-orchestrator agent iterate my-agent.agent.json --feedback "Never drop tables without confirmation"Proposes a visual Git-style Red/Green diff for SKILL.md, bumps the semantic patch version, and prompts for confirmation.
npx opencode-orchestrator agent publish my-agent.agent.json# Any teammate can now invoke the published agent:
npx opencode-orchestrator run --agent my-agent "Triage open customer issues"GET /api/v1/catalog/mcp: List verified Model Context Protocol tools.GET /api/v1/catalog/skills: List curated operational skills library.POST /api/v1/agents/synthesize: SynthesizeAgentBundlefrom description/repo scan.POST /api/v1/agents/refine: Diff-driven iterative steering.POST /api/v1/agents/publish: Save agent bundle to PostgreSQL registry.GET /api/v1/agents: List team agent catalog with trust telemetry.GET /api/v1/agents/:name: Fetch specific agent bundle.
opencode-orchestrator allows injecting declarative skills (SKILL.md) and Model Context Protocol (MCP) servers dynamically into each ephemeral execution turn.
Skills are markdown instructions with YAML frontmatter placed in .opencode/skills/<skill-name>/SKILL.md:
---
name: db-analyzer
description: Inspects database schemas, indexes, and queries using PostgreSQL MCP tools
---
# PostgreSQL Database Analyzer Skill
1. Use `postgres-mcp` tools to inspect `information_schema.tables`.
2. Check foreign key relationships and index coverage.
3. Recommend composite indexes for slow queries.MCP servers are configured in taskConfig.mcp (injected into $HOME/.config/opencode/opencode.json):
{
"taskConfig": {
"model": "openrouter/deepseek/deepseek-v4-flash",
"mcp": {
"postgres": {
"type": "local",
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://postgres:postgres@localhost:5432/opencode"
],
"enabled": true
},
"filesystem": {
"type": "local",
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/tmp/sandboxes"
],
"enabled": true
},
"remote-mcp": {
"type": "remote",
"url": "https://mcp.internal.company.com/sse",
"enabled": true
}
}
}
}Note: If
typeorenabledare omitted,opencode-orchestratorautomatically normalizestypeto"local"(or"remote") and setsenabled: true.
Run a complete turn with skills and MCP using TypeScript:
npx tsx examples/run_turn.tsopencode-orchestrator features native, zero-overhead OpenTelemetry (OTel) tracing compliant with the OpenInference standard. You can visualize tool execution Gantt charts, model latencies, token consumption, and human-in-the-loop pauses using self-hosted Arize Phoenix.
Spin up PostgreSQL, Arize Phoenix, and OpenCode Orchestrator in a single command:
docker compose up -d- Orchestrator API:
http://localhost:8080 - Arize Phoenix Web UI:
http://localhost:6006 - Phoenix OTLP Collector:
http://localhost:6006/v1/traces - PostgreSQL Database:
localhost:5432
Add the following to your .env:
PHOENIX_ENABLED=true
PHOENIX_COLLECTOR_URL=http://localhost:6006/v1/traces
OTEL_SERVICE_NAME=opencode-orchestrator- Root Turn Span (
CHAIN): Model, session ID, tenant ID (user.id), prompt, total duration, and exit status. - Tool Execution Spans (
TOOL): Tool name (bash,read,glob, MCP database tools), inputs, outputs, execution duration, and error states. - Token & Cost Metrics: Input tokens, output tokens, reasoning tokens, cache hit rate, and total USD cost per turn.
- Human-in-the-Loop Spans (
APPROVAL): Time spent waiting for user permission and the final decision (approved/rejected).
opencode-orchestrator natively emits operational and economic telemetry at both the Prometheus infrastructure level and the PostgreSQL relational level:
The server exposes a standard Prometheus exposition endpoint at GET /metrics for scraping by Prometheus, VictoriaMetrics, or Kubernetes Prometheus Operator:
curl http://localhost:8080/metricsExported Metrics:
| Metric Name | Type | Description |
|---|---|---|
orchestrator_turns_total |
Counter | Total turns executed labeled by tenant_id, model, and status (completed|failed) |
orchestrator_turn_duration_seconds |
Histogram | Turn execution latency percentiles (P50, P90, P99) |
orchestrator_active_sessions |
Gauge | Number of in-flight turns currently running |
orchestrator_sandboxes_provisioned_total |
Counter | Total ephemeral TMPFS sandboxes created |
orchestrator_sandboxes_cleaned_total |
Counter | Total sandboxes purged on turn completion |
orchestrator_tokens_total |
Counter | Tokens consumed by tenant_id, model, and type (input|output|reasoning) |
orchestrator_cost_usd_total |
Counter | Estimated LLM spend in USD |
orchestrator_interactions_total |
Counter | Permission requests emitted by agents |
orchestrator_interactions_resolved_total |
Counter | Human approvals resolved (approved|rejected) |
orchestrator_node_* |
Gauges/Counters | Node.js process CPU, RSS memory, event loop lag, and GC metrics |
Add to your Kubernetes Deployment for automatic discovery:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"Operational metrics are automatically recorded in the orchestrator_telemetry table in PostgreSQL for historical auditing and SQL reporting:
SELECT
tenant_id,
metric_name,
count(*) as total_events,
sum(metric_value) as aggregate_value
FROM orchestrator_telemetry
GROUP BY tenant_id, metric_name;You can also fetch recent events via REST API: GET /api/v1/telemetry?limit=50.
A plug-and-play Grafana dashboard is provided in dashboards/grafana-orchestrator.json. Simply import this JSON into Grafana to monitor active sessions, turn latencies, token consumption, and Human-in-the-Loop approval metrics.
For an in-depth guide on how to prepare requests, structure payloads, inject dynamic MCP servers, declare agent skills, and handle streaming SSE events in TypeScript, Python, or cURL, consult the dedicated manual:
π Complete Turn Preparation & Configuration Manual (docs/CALL_PREPARATION_MANUAL.md)
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
prompt |
string |
Yes | The natural language task or prompt for this turn | "List all database tables" |
tenantId / userId |
string |
No | Tenant or user ID for isolation and trace filtering | "tenant_dev_101" |
model |
string |
No | Model identifier override | "openrouter/deepseek/deepseek-v4-flash" |
taskConfig |
object |
No | Injected into $HOME/.config/opencode/opencode.json |
{"model": "...", "mcp": {...}} |
taskConfig.mcp |
object |
No | Model Context Protocol servers (local stdio or remote SSE) | See MCP guide below |
skills |
array |
No | Declarative skills ({ name, content }) injected as SKILL.md |
[{"name": "db-analyzer", "content": "..."}] |
auth |
object |
No | Injected into $HOME/.local/share/opencode/auth.json |
{"openrouter": {"apiKey": "..."}} |
binaryPath |
string |
No | Custom path to the opencode binary if overriding default |
"/usr/local/bin/opencode" |
# Run all test suites (unit, integration, and live OpenCode execution)
npm testtests/unit/agentFactory.test.ts: Verifies verified MCPs, curated skills, skill linter, synthesizer, and refiner diffs.tests/unit/metrics.test.ts: Tests Prometheus counters/gauges and PostgreSQL telemetry persistence.tests/unit/cli.test.ts: Validates CLI commands, flags, and options.tests/unit/sandbox.test.ts: Verifies ephemeral sandbox provisioning, skill injection, and purge lifecycle.tests/unit/skillsAndMcp.test.ts: Verifies MCP configuration normalization and skill injections.tests/unit/aguiAdapter.test.ts: Tests protocol translation for tokens, plans, tool calls, and permissions.tests/unit/sessionStore.test.ts: Tests context rehydration and summary formatting.tests/integration/agentApi.test.ts: Validates Agent Genesis REST endpoints (synthesize, refine, publish, list).tests/integration/api.test.ts: Validates Express API endpoints and Kubernetes probes (/livez,/readyz).tests/integration/interactionFlow.test.ts: Tests stdio pause, resume, and human-in-the-loop approvals.tests/integration/turnStreamE2E.test.ts: Full simulated E2E turn streaming and HTTP interaction resolution.tests/integration/realOpenCode.test.ts: Live execution with real OpenCode CLI and OpenRouter DeepSeek.
In Kubernetes, mount /tmp/sandboxes to a RAM-backed emptyDir (medium: Memory) for zero-disk-I/O execution:
apiVersion: apps/v1
kind: Deployment
metadata:
name: opencode-orchestrator
spec:
replicas: 3
template:
spec:
containers:
- name: orchestrator
image: ghcr.io/opencode-ai/opencode-orchestrator:latest
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: database-url
- name: SANDBOX_BASE_DIR
value: /tmp/sandboxes
volumeMounts:
- mountPath: /tmp/sandboxes
name: sandbox-storage
volumes:
- name: sandbox-storage
emptyDir:
medium: Memory
sizeLimit: 4GiMIT License. See LICENSE for details.

