README.md

July 29, 2026 · View on GitHub

OpenCode Orchestrator logo

OpenCode Orchestrator

Multi-agent mission control for OpenCode.

MIT License npm

Version: 1.7.10


Highlights

  • A mission loop, not a single prompt. Commander, Planner, Worker, and Reviewer agents share one mission. The runtime — not the model — decides when work is done: it adjudicates at the idle boundary, nudges for missing verification, and on stagnation escalates DECOMPOSE → RE-PLAN → ASK instead of looping blindly.
  • Pluggable agent profiles. Built-in roles plus your own agents defined in .opencode/agents.json, with system prompts composed from modular fragments. Retrieval re-weights itself per active role, so a Planner, a Worker, and a Reviewer search the same vault through different lenses.
  • Local-first, human-like memory. An on-disk Ebbinghaus model with no external vector DB: notes gain strength when recalled and fade when unused, are de-referenced rather than deleted (recoverable), and separate when a fact was true (event_time) from when it was learned (ingestion_time).
  • Memory organized the way yours is. Notes carry a kindepisodic (what happened on a mission), semantic (facts that outlive it), procedural (reusable playbooks with failure pivots). Recurring episodes are promoted into facts and procedures, so the agent recognizes what it already learned instead of re-deriving it.
  • Retrieval by complementary senses. BM25 lexical + tag + wiki-link graph, fused with Reciprocal Rank Fusion and biased by the active role — and a faded memory naturally sinks while a frequently recalled one rises, so search and memory are one model.
  • Auditable by design. A bounded mission ledger, a live scratchpad, and an Obsidian-compatible knowledge-map canvas make every decision and piece of evidence inspectable.
  • Safe by default. Disk writes and destructive maintenance are opt-in; sensitive or malicious memories never enter the prompt.

1. Install

npm install -g opencode-orchestrator

The install hook merges OpenCode config instead of replacing it, prefers opencode.jsonc when present, preserves existing plugin tuple options, and skips automatic config mutation in CI.

To remove the plugin from OpenCode config:

npm explore -g opencode-orchestrator -- npm run cleanup:plugin
npm uninstall -g opencode-orchestrator

Manual fallback: remove "opencode-orchestrator" or ["opencode-orchestrator", {...}] from the plugin array in opencode.json or opencode.jsonc.

2. Configure

Tested compatibility:

  1. Node.js 24+
  2. @opencode-ai/plugin 1.17.18
  3. @opencode-ai/sdk 1.17.18

OpenCode plugin options belong inside the plugin array as ["plugin-name", {...}] tuples. Configure agentConcurrency and missionLoop there:

{
  "$schema": "https://opencode.ai/config.json",
  "model": "opencode/gpt-5.1-codex",
  "permission": {
    "question": "allow"
  },
  "agent": {
    "commander": {
      "model": "opencode/gpt-5.1-codex"
    },
    "worker": {
      "model": "anthropic/claude-opus-4-5-20251101"
    }
  },
  "plugin": [
    [
      "opencode-orchestrator",
      {
        "agentConcurrency": {
          "commander": 1,
          "planner": 10,
          "worker": 10,
          "reviewer": 10
        },
        "missionLoop": {
          "ledger": true,
          "markdownMemory": true,
          "maxEvidenceEvents": 20
        }
      }
    ]
  ]
}

Model selection follows normal OpenCode inheritance. The plugin does not force a model:

  1. Commander uses the global model unless agent.commander.model is set.
  2. Planner, Worker, and Reviewer inherit the invoking primary agent model unless agent.<name>.model is set.
  3. Generated Commander, Planner, Worker, and Reviewer agents inherit global permissions.
  4. Same-name user agent config can still override model, temperature, and specific permission keys.

Plugin options are schema-described in opencode-orchestrator.schema.json (generated from the Zod source, shipped in the package) for editor autocomplete and validation. Invalid or missing option fields fall back to defaults rather than failing.

3. Run

Inside OpenCode:

/task "Implement the requested change and verify it"

Mission controls:

  1. /task ... starts a persisted mission loop under .opencode/.
  2. Esc/OpenCode interrupt is respected by idle guards so the plugin does not immediately re-continue an interrupted turn.
  3. /cancel and /stop deactivate the current mission loop.
  4. The default mission iteration ceiling is 1,000,000,000.

Role-aware agent sessions are created or reused on demand: Commander delegates Planner, Worker, and Reviewer tasks into a per-role session pool, then releases or cleans those sessions automatically after completion.

Authorized Shell Listener TUI

For owned lab machines or explicitly authorized test environments, the bundled Rust CLI can run a multi-session TCP shell listener:

orchestrator shell-listener --bind 127.0.0.1 --port 4444

The listener is intentionally outside the OpenCode JSON-RPC tool surface. It is an operator-driven terminal workflow, not an LLM-callable tool.

Safety defaults:

  1. Loopback-only bind by default.
  2. Non-loopback bind addresses require --allow-remote.
  3. Raw stream logs are stored under .opencode-orchestrator/shell-listener/.
  4. The CLI does not generate payloads, exploit targets, or bypass authentication.

TUI commands:

CommandPurpose
sessionsShow connected sessions, peer addresses, status, and raw log paths.
use <id>Select the active session.
send <text>Send one input line to the active session. Use this for login, registry, CDK, or reverse-proxy prompts that need human input.
run <cmd>Send a command followed by a unique sentinel marker so completion can be recognized in output.
ptySend a manual PTY helper to the active session when the remote environment supports Python.
close [id]Close a session.
quitStop the listener UI.

4. How It Works

                    /task input
                         |
                         v
                  +-------------+
           +----->|  Commander  |
           |      +------+------+
           |             | delegates
           |       +-----+------+
           |       v            v
           |  +---------+  +-------------+
           |  | Planner |  | Worker pool |
           |  +----+----+  +--+-------+--+
           |       | writes   | impl  |
           |       v          v       v
           |  +------------------+  +----------+
           |  |  Mission state   |  | Reviewer |
           |  |   (.opencode/)   |  +----+-----+
           |  +------------------+       |
           |                             v
           |  no (keep working)    +-----------+
           +-----------------------+ Verified? |
                                   +-----+-----+
                                         | yes
                                         v
                                       Done
AgentPurpose
CommanderInterprets the mission, coordinates agents, and keeps the loop aligned.
PlannerBreaks work into ordered steps and tracks dependencies.
WorkerImplements scoped file changes with isolated context.
ReviewerChecks completion evidence, tests, and integration risk.

The mission loop adjudicates continuation at the idle boundary rather than trusting a model's "done":

  1. Tool calls are observed to record changed files and verification runs; if files changed with no verification, the continuation prompt emphatically asks the model to run tests/build/lint and cite results (a nudge, never a hard block).
  2. Before declaring done the model is asked for a short self-account (scope fit, verification, residual risk).
  3. After sustained stagnation the loop stops blind retries and escalates: DECOMPOSE → RE-PLAN → ASK the user.

Retrieval — finding by complementary senses

A single ranking misses things, so retrieval runs several complementary channels in parallel and fuses them rather than trusting one score:

  • Lexical (BM25) — exact word matches; strong on code, identifiers, and proper nouns.
  • Tag — explicit frontmatter classification.
  • Graph (wiki-link BFS) — pulls in notes that are contextually close through [[links]] and backlinks, even when the wording differs.

These are merged with Reciprocal Rank Fusion (RRF), which combines by rank position instead of raw score, so notes that several channels agree on rise to the top without hand-tuned scales. It all runs locally — no GPU, no external model, no API call.

Whoever is searching changes the weighting. Retrieval is role-aware: a Planner leans on graph/tag structure, a Worker on exact lexical hits, a Reviewer on tag breadth (src/core/knowledge/retrieval-weights.ts). Swap the active agent profile and the same query is searched through a different blend of senses; custom profiles can be added via .opencode/agents.json. Each memory note also carries a relevance horizon (strategic / execution / closure) that tunes how long it stays in play.

Forgetting then feeds back into retrieval — the memory-strength multiplier (below) makes a faded memory sink in the ranking while a frequently recalled one rises, so search and memory behave as one retrieval model rather than two systems.

Three Kinds of Memory — Episodic, Semantic, Procedural

Not all knowledge is the same kind of knowledge, so the store doesn't treat it as one undifferentiated pile. Following how human memory is organized, every note carries a kind:

  • Episodicwhat happened on a mission: the objective, the sequence of tool actions, and the outcome. Captured automatically at mission completion from the mission ledger, keyed by objective and the files it touched.
  • Semanticfacts that outlive any one mission: where this repo's auth flow lives, which build step fails without which dependency. Time- and mission-independent.
  • Proceduralhow to get something done: a reusable playbook with prerequisites, commands, verification steps, and — the part that matters most — failure pivots (what to try when a step fails).

Kinds are promoted, not merely stored. When the same episode recurs, it condenses into a semantic fact; when a sequence of steps succeeds repeatedly, it graduates into a procedure. The agent stops re-deriving what it has already learned and starts recognizing it — the way expertise forms. Promotion runs inside the opt-in maintenance pass and strips mission-specific detail (paths, secrets) first, so what gets promoted actually generalizes and no secret is embedded in shared knowledge.

Kind also steers retrieval and forgetting: a Worker reaches for procedures, a Planner for semantic facts, and episodic detail fades fast while procedures stay sticky — which is exactly what the strength model below does.

Ebbinghaus-Inspired Memory

Memory here is not an append-only log: memories carry a strength that fades the way human memory does, so the store stays useful instead of drowning in stale notes. The model comes straight from the Ebbinghaus forgetting curve:

  • It decays naturally over time. The longer a note goes unused, the lower its retrieval strength, so it quietly recedes instead of crowding out fresher knowledge.
  • Different kinds of memory fade at different rates. Procedural knowledge (sop, workflow) is sticky; one-off episodes fade fast — the same asymmetry that lets people keep a well-worn procedure long after the surrounding details are gone.
  • Recall strengthens memory. Every retrieval reinforces a note, so frequently used knowledge climbs back up the curve (the spacing / retrieval-practice effect).
  • Nothing is truly deleted — it just stops being referenced. Strength has a floor and never reaches zero; a faded memory drops out of search surfacing (archived or tombstoned) but the file stays on disk, fully recoverable. This mirrors human forgetting as retrieval failure, not erasure.

Concretely, local memory follows this Ebbinghaus-style lifecycle entirely on disk with no external vector DB. The scoring model (single source of truth in src/core/knowledge/memory-scoring.ts) applies a per-kind exponential decay so unused notes lose retrieval strength over time (sop/workflow decay slowly, episode fast), while reused notes are reinforced through access counters / access_ema and last_accessed. Memory is bi-temporal: event_time records when a fact was true and ingestion_time records when it was learned, so newer facts can supersede older ones by closing their validity window (valid_to). Notes without memory metadata keep a neutral score, so ordinary docs rank unchanged. Archived, sensitive, malicious, or tombstoned memories stay on disk for auditability but are excluded from prompt injection.

Both destructive and disk-mutating behaviors are off by default and gated behind explicit opt-in flags:

FlagEffect
OPENCODE_MEMORY_WRITEBACK ("1"/"true"), or new KnowledgeContextProvider({ enableAccessWriteback: true })Persist access reinforcement (count/EMA/last_accessed) to note frontmatter on search. Off by default — plain search never writes to disk.
runMemoryMaintenance({ ..., dryRun: false }) (optionally gated by OPENCODE_MEMORY_MAINTENANCE)Manually apply tier moves, archiving, and tombstone supersession. Defaults to dryRun: true (plan only); never runs automatically on search or index.

Runtime evidence is written only when enabled:

ArtifactPurpose
.opencode/mission-ledger.jsonlBounded event trail for mission decisions.
.opencode/docs/brain/scratchpad.mdGenerated Markdown memory surface for active missions.
.opencode/docs/brain/knowledge-map.canvasObsidian-compatible visual map of objective, evidence, and verification nodes.
.opencode/docs/brain/memories/*.mdGenerated mission-relevant memory notes indexed by the knowledge retriever.

5. Developer Notes

Local checks that mirror CI (.github/workflows/ci.yml), which gates every push and PR:

# TypeScript
npm run build
npx tsc --noEmit
npm test

# Rust (same gates CI enforces)
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace

Regenerate the plugin-options JSON Schema after changing option types: npm run gen:schema.

Useful references:

  1. OpenCode plugins: https://opencode.ai/docs/plugins/
  2. OpenCode config: https://opencode.ai/docs/config/
  3. OpenCode keybinds: https://opencode.ai/docs/keybinds/
  4. Project issues: https://github.com/agnusdei1207/opencode-orchestrator/issues

Contributions are welcome: open an issue or pull request when you find a bug, compatibility gap, or focused improvement.

Config logs:

PlatformPath
Unix/tmp/opencode-orchestrator.log
Windows%TEMP%\opencode-orchestrator.log

6. License

MIT License. See LICENSE.