Skip to content

About

Multi-pane, multi-model dashboard for OpenCode. Per-model subagents so the LLM can actually route to @minimax / @flash / @Pro.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

OpenCode Orchestrator

A multi-pane, multi-model dashboard for OpenCode. Open the app instead of a terminal: manage many agent sessions concurrently, watch them stream side-by-side, and review every file change inline.

What this is

A standalone Next.js app that talks to a running opencode serve instance. Three big capabilities work together:

  1. Multi-pane session manager — every active session is its own live chat pane in a responsive grid. Run several agents at once and watch them all.
  2. In-pane model orchestration — the router classifies each task and picks a model tier (heavy / mid / light / free), with automatic fallback through only the providers you actually have configured.
  3. Live file diff viewer — every write / edit / patch tool call from any pane streams into a right-hand diff panel with accept / reject controls.
  4. Per-model subagents — .opencode/agents/<name>.md files (and a /api/agents/sync route that materializes more from whatever models the server has) so the primary agent can actually route to a specific model. See AGENTS.md for the contract.

Run it

# 1. Start an OpenCode server in one terminal
opencode serve --port 4096

# 2. Install + run the orchestrator in another
npm install
npm run dev        # → http://localhost:3737

If your server requires a password, set:

export OPENCODE_URL=http://127.0.0.1:4096
export OPENCODE_SERVER_PASSWORD=...
npm run dev

The app polls /api/ping every 2s while the server is down and auto-connects the moment it comes up. The global SSE stream (/api/stream → upstream /event) handles live updates for all sessions at once.

Architecture decisions worth knowing

  • One global SSE, fanned out client-side. OpenCode's /event is a single firehose, not per-session. The store demuxes events into per-session state.
  • Next.js API routes proxy everything to the OpenCode HTTP API. This keeps the server password out of the browser bundle and lets us add a single auth header in one place.
  • Send is fire-and-forget. POST /session/:id/message blocks until the assistant finishes; we kick it off in the route handler and let the response stream back through SSE.
  • File diffs are reconstructed client-side from message.part.updated tool events (read → cache, write/edit/patch → diff against cache). OpenCode also emits a session.diff event which is ingested when present.

File map

.opencode/agents/          per-model subagent files (minimax, flash, pro, …)
AGENTS.md                  primary-agent delegation contract
app/                       Next.js App Router
  api/                     HTTP proxy to opencode
    ping/                  liveness check
    sessions/[id]/         list + create + delete
    message/               fire-and-forget user message
    abort/                 interrupt a session (or all)
    stream/                SSE proxy → opencode /event
    models/                flatten /config/providers
    agents/                list subagents (with bound model)
    agents/sync/           write .opencode/agents/*.md from live models
  layout.tsx, page.tsx     root UI
components/
  layout/                  Sidebar, StatusBar, MainArea, ConnectionBanner, NewSessionModal
  panes/                   PaneGrid, AgentPane, PaneHeader, MessageBubble, ToolCallBlock
  diff/                    DiffPanel, DiffCard, DiffViewer
  models/                  ModelBadge, ModelSelector, RouterConfigPanel
  shared/                  StatusDot, TokenCounter
hooks/
  useBootstrap.ts          ping/load on connect
  useGlobalEventStream.ts  one SSE → store
lib/
  opencode.ts              typed HTTP client (server-only)
  router.ts                tier classifier + fallback chains
  modelAgents.ts           model→named-subagent specs (single source of truth)
  diff.ts                  unified-patch / line-count helpers
  store.ts                 Zustand store (the brain)
types/index.ts

"Use minimax to give me a joke" — how it actually works

OpenCode's Task tool spawns subagents by name, not by model id. Passing agent in POST /session/:id/message makes opencode run on that agent's bound model, overriding any provider/modelID in the body. So to route a specific task to a specific model, the model has to be a named subagent.

.opencode/agents/minimax.md binds @minimax to opencode/minimax-m2.5-free. The primary build agent sees @minimax in its Task tool listing and can spawn it directly. The user can also type @minimax <task> in the composer or pin it from the agent picker (Pin to model group). All three paths end up on minimax.

/api/agents/sync runs at orchestrator startup and writes a markdown file for every available model (curated short names first, derived names after). Add a provider via opencode auth and the next reconnect picks it up. The sync is idempotent — it only rewrites files when frontmatter actually drifts.

Limitations & honest caveats

  • Subagent spawn detection is heuristic. OpenCode doesn't emit a subagent_spawned event; we detect new sessions whose parentID matches an open one. If your subagents don't carry that link, panes won't auto-open (you can open them from the sidebar).
  • The model fallback chain includes speculative IDs (e.g. various Claude 4.x slugs). The availability check filters them out at runtime, so unknown IDs simply won't be picked.
  • Cost numbers are estimates computed from the per-model rates that OpenCode reports. Models with no rate show ~$0.

About

Multi-pane, multi-model dashboard for OpenCode. Per-model subagents so the LLM can actually route to @minimax / @flash / @Pro.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages