Skip to content
Development
Agent

opencode

Research notes on OpenCode's configuration, credential discovery, and runtime behavior based on agent-jj implementation.

From plugin
sandbox-agent
1.5k4 skills4 agents2 commands1 MCP

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

Research notes on OpenCode's configuration, credential discovery, and runtime behavior based on agent-jj implementation.

Agent definition

opencode.md

OpenCode Research

Research notes on OpenCode's configuration, credential discovery, and runtime behavior based on agent-jj implementation.

Overview

  • **Provider**: Multi-provider (OpenAI, Anthropic, others)
  • **Execution Method**: Embedded server via SDK, or CLI binary
  • **Session Persistence**: Session ID (string)
  • **SDK**: `@opencode-ai/sdk` (server + client)
  • **Binary Location**: `~/.opencode/bin/opencode`
  • **Written in**: Go (with Bubble Tea TUI)

CLI Usage (Alternative to SDK)

OpenCode can be used as a standalone binary instead of embedding the SDK:

Interactive TUI Mode

opencode                      # Start TUI in current directory
opencode /path/to/project     # Start in specific directory
opencode -c                   # Continue last session
opencode -s SESSION_ID        # Continue specific session

Non-Interactive Mode (`opencode run`)

opencode run "your prompt here"
opencode run --format json "prompt"   # Raw JSON events output
opencode run -m anthropic/claude-sonnet-4-20250514 "prompt"
opencode run --agent plan "analyze this code"
opencode run -c "follow up question"  # Continue last session
opencode run -s SESSION_ID "prompt"   # Continue specific session
opencode run -f file1.ts -f file2.ts "review these files"

Key CLI Flags

| Flag | Description | |------|-------------| | `--format json` | Output raw JSON events (for parsing) | | `-m, --model PROVIDER/MODEL` | Model in format `provider/model` | | `--agent AGENT` | Agent to use (`build`, `plan`) | | `-c, --continue` | Continue last session | | `-s, --session ID` | Continue specific session | | `-f, --file FILE` | Attach file(s) to message | | `--attach URL` | Attach to running server | | `--port PORT` | Local server port | | `--variant VARIANT` | Reasoning effort (e.g., `high`, `max`) |

Headless Server Mode

opencode serve                        # Start headless server
opencode serve --port 4096            # Specific port
opencode attach http://localhost:4096 # Attach to running server

Other Commands

opencode models                 # List available models
opencode models anthropic       # List models for provider
opencode auth                   # Manage credentials
opencode session                # Manage sessions
opencode export SESSION_ID      # Export session as JSON
opencode stats                  # Token usage statistics

Sources: [OpenCode GitHub](https://github.com/opencode-ai/opencode), [OpenCode Docs](https://opencode.ai/docs/cli/)

Architecture

OpenCode runs as an embedded HTTP server per workspace/change:

┌─────────────────────┐
│   agent-jj backend  │
│                     │
│  ┌───────────────┐  │
│  │ OpenCode      │  │
│  │ Server        │◄─┼── HTTP API
│  │ (per change)  │  │
│  └───────────────┘  │
└─────────────────────┘
  • One server per `changeId` (workspace+repo+change combination)
  • Multiple sessions can share a server
  • Server runs on dynamic port (4200-4300 range)

Credential Discovery

Priority Order

1. Environment variables: `ANTHROPIC_API_KEY`, `CLAUDE_API_KEY` 2. Environment variables: `OPENAI_API_KEY`, `CODEX_API_KEY` 3. Claude Code config files 4. Codex config files 5. OpenCode config files

Config File Location

| Path | Description | |------|-------------| | `~/.local/share/opencode/auth.json` | Primary auth config |

Auth File Structure

{
  "anthropic": {
    "type": "api",
    "key": "sk-ant-..."
  },
  "openai": {
    "type": "api",
    "key": "sk-..."
  },
  "custom-provider": {
    "type": "oauth",
    "access": "token...",
    "refresh": "refresh-token...",
    "expires": 1704067200000
  }
}

Provider Config Types

interface OpenCodeProviderConfig {
  type: "api" | "oauth";
  key?: string;      // For API type
  access?: string;   // For OAuth type
  refresh?: string;  // For OAuth type
  expires?: number;  // Unix timestamp (ms)
}

OAuth tokens are validated for expiry before use.

Server Management

Starting a Server

import { createOpencodeServer } from "@opencode-ai/sdk/server";
import { createOpencodeClient } from "@opencode-ai/sdk";

const server = await createOpencodeServer({
  hostname: "127.0.0.1",
  port: 4200,
  config: { logLevel: "DEBUG" }
});

const client = createOpencodeClient({
  baseUrl: `http://127.0.0.1:${port}`
});

Server Configuration

// From config.json
{
  "opencode": {
    "host": "127.0.0.1",        // Bind address
    "advertisedHost": "127.0.0.1" // External address (for tunnels)
  }
}

Port Selection

Uses `get-port` package to find available port in range 4200-4300.

Client API

Session Management

// Create session
const response = await client.session.create({});
const sessionId = response.data.id;

// Get session info
const session = await client.session.get({ path: { id: sessionId } });

// Get session messages
const messages = await client.session.messages({ path: { id: sessionId } });

// Get session todos
const todos = await client.session.todo({ path: { id: sessionId } });

Sending Prompts

Synchronous

const response = await client.session.prompt({
  path: { id: sessionId },
  body: {
    model: { providerID: "openai", modelID: "gpt-4o" },
    agent: "build",
    parts: [{ type: "text", text: "prompt text" }]
  }
});

Asynchronous (Streaming)

// Start prompt asynchronously
await client.session.promptAsync({
  path: { id: sessionId },
  body: {
    model: { providerID: "openai", modelID: "gpt-4o" },
    agent: "build",
    parts: [{ type: "text", text: "prompt text" }]
  }
});

// Subscribe to events
const eventStream = await client.event.subscribe({});

for await (const event of eventStream.stream) {
  // Process events
}

Event Types

| Event Type | Description | |------------|-------------| | `message.part.updated` | Message part streamed/updated | | `session.status` | Ses

Read more
Ships withsandbox-agent

Run Coding Agents in Sandboxes. Control Them Over HTTP. Supports Claude Code, Codex, OpenCode, and Amp.

Get the whole plugin
Stats
1,528
Stars
119
Forks
Maintained
Maintenance
TypeScript
Language
Apache-2.0
License
1mo ago
Last commit
6mo ago
Created

Repo: rivet-dev/sandbox-agent