New:Microsoft Teams Notifications Are Now Available in Socket.Learn more →
Get Started

@serkanalgur/opencode-nexus

Package Overview
Dependencies
Maintainers
1
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@serkanalgur/opencode-nexus

Adaptive Multi-Agent Orchestration with Cost Intelligence for OpenCode V2

Source
npmnpm
Version
2.9.0
Version published
Weekly downloads
1.2K
-86.61%
Maintainers
1
Weekly downloads
 
Created
Source
OpenCode Nexus

npm version npm downloads stars license Socket Badge opencode typescript sponsor

Adaptive Multi-Agent Orchestration with Cost Intelligence

Installation • Quick Start • Features • Agents • Tools • Configuration • Development

What is OpenCode Nexus?

OpenCode Nexus is an agent orchestration plugin for OpenCode V2 that spawns real sub-agent sessions with cost-aware routing, DAG-based task execution, self-healing, and a TUI/web dashboard.

Key Capabilities

CapabilityDescription
Real SessionsEach agent runs in its own OpenCode session. The preferred path dispatches through OpenCode's built-in subagent tool so the child is parent-linked; ctx.session.create() is the fallback when no parent tool context is available, and the spawn is logged as degraded
Role-Based AgentsArchitect, Coder, Reviewer, Tester, Explorer, Documenter — each with specialized prompts
Custom RolesYour own roles, from .opencode/nexus.jsonc or nexus.roles.add for the session
DAG ExecutionTasks are parallelized based on dependency graphs with priority queuing
Cost-Aware RoutingScores models by quality and cost, selects optimal per task complexity. A speed score is computed and reported but does not affect selection
Self-HealingRetries with exponential backoff, context transfer, then an escalation policy. The retry counts are configurable; the fallback model list is a hardcoded default, not file-configurable
Config Hot-ReloadEdits to nexus.jsonc take effect without a restart — a filesystem.changed fast path plus a 2s poll of the two config files, debounced 150ms
Web DashboardA live view of sessions, agents, tasks, costs and config, served over HTTP + WebSocket (default port 4747) — started on request from /nexus-dashboard or the agent, never automatically
TUI DashboardMonitor agents, budget, and config from the terminal
Team ModeLead agent orchestrates specialist agents in parallel
Todo & Goal TrackingEnforce task completion, persist objectives across sessions
Persistent MemorySQLite-backed memory store with per-entry TTL (off by default) and substring search. Reachable from the orchestrator API, not from a registered tool
Learning ModulePattern recognition from failures, confidence scoring
JSONC ConfigRead/write project and global config files with comments
OpenCode LSP opt-inOn startup, inserts "lsp": true into your global opencode.jsonc if it isn't already there. That is the whole of it — Nexus does not read LSP state, manage servers, or report anything about them
AST-GrepPattern-aware code search
Security ScanningRegex detection of hardcoded secrets and known-dangerous constructs
Slash Commands/nexus, /nexus-dashboard, /nexus-web, /nexus-overview, /nexus-config, /nexus-model, /nexus-status, /nexus-reset

Installation

# Install the plugin and add it to your global OpenCode config
opencode plugin add @serkanalgur/opencode-nexus

This package is an OpenCode plugin, not a command-line tool: it declares no bin, so there is nothing for a global install to put on your $PATH and no CLI to invoke afterwards. opencode plugin takes a subcommand (list, add, check, update, remove) and has no --global flag.

Or manually add to ~/.config/opencode/opencode.jsonc:

{
  "plugins": ["@serkanalgur/opencode-nexus"]
}

Auto-Setup

On every plugin load, Nexus:

  • Writes nexus-orchestrator to ~/.config/opencode/agents/nexus-orchestrator.md
  • Writes the six subagent files: nexus-coder, nexus-explorer, nexus-reviewer, nexus-tester, nexus-architect, nexus-documenter
  • Reads role→model mappings from .opencode/nexus.jsonc (and the global config) and resolves them per spawn; the generated agent files carry no model pin
  • Best-effort: if ~/.config/opencode/opencode.jsonc already exists and does not already mention "lsp", inserts "lsp": true; silently does nothing if the file is absent

The agent files are rewritten on every load, not created once. Any hand-edit to them is lost the next time OpenCode starts. Edit nexus.jsonc for models and budget, or nexus.roles.add for an extra role; do not edit the generated agent files.

Quick Start

1. Configure Agent Models

Press Ctrl+N or type /nexus to open the configuration dialog. Select where to save (project or global).

2. Use the Nexus Orchestrator

Select nexus-orchestrator as your primary agent, then:

# Spawn agents for tasks
Use nexus.spawn with role="coder" and task="Implement JWT auth"

# Wait for completion and get results
Use nexus.spawn with role="reviewer" and task="Review the implementation" and wait=true

# Delegate (convenience wrapper)
Use nexus.delegate with role="tester" and task="Write tests for auth module"

# Check progress
Use nexus.sessions

# Track goals
Use nexus.goal.set with description="Build complete auth system"

3. Use Slash Commands

/nexus              # Open full configuration dialog
/nexus dashboard    # Start the web dashboard and open it in your browser
/nexus status       # Show the config summary
/nexus model coder  # Pick the model for a role
/nexus reset        # Reset configuration to defaults

Features

Cost-Aware Model Selection

Models are configured per role in .opencode/nexus.jsonc. Nexus scores models by quality and cost, then picks the optimal one. A speed score is computed and reported alongside, but it is not a term in the overall score, so it never affects which model is selected.

Example (the model ids below are illustrative — use whatever your provider offers):

{
  "models": {
    "architect": "opencode/muse-spark-1.3-contributor-free",
    "coder": "opencode/mimo-v2.6-flash-free",
    "reviewer": "opencode/muse-spark-1.2-contributor-free",
    "tester": "opencode-go/mimo-v2.5",
    "explorer": "opencode/big-pickle",
    "documenter": "opencode/big-pickle"
  }
}

When you call nexus.spawn(role="coder"), the coder model from config is used automatically.

Self-Healing with Escalation

Failed tasks follow a 4-step escalation chain:

  • Retry — Exponential backoff (1s, 2s, 4s...)
  • Respawn — Collect context, spawn new agent with transferred state
  • Fallback Model — Try a cheaper alternative model. The fallback list (google/gemini-2.5-flash, then anthropic/claude-haiku-4-5) is a hardcoded default in src/orchestrator.ts, not something nexus.jsonc can change
  • Alert — Emit escalation event, mark as failed

The retry count, retry delay and context-transfer toggle are configurable under selfHealing; the fallback models are not.

Web Dashboard

A live view of the orchestrator, served by an HTTP + WebSocket server on port 4747 (127.0.0.1).

Nothing is listening until you ask for it. The server is not started at startup, and no command starts it implicitly. The start has to happen in the OpenCode server process, next to the orchestrator that feeds it, and there are two ways to reach it:

/nexus dashboard [port] [host]      # from the TUI — starts it and opens it
Ask the agent: "start the nexus dashboard"

The TUI command submits /nexus dashboard [port] [host] to that server process, whose prompt hook routes it to the orchestrator; the agent's route calls nexus.dashboard.start(port=4747, host="127.0.0.1") directly. Both end at the same start, and both print the URL it bound. If the start fails, nothing is listening and no browser is opened — the reason is reported, and no URL is offered for a server that is not there. The ways it fails are a port already in use (the bind is refused; pass a different port) and dashboard.enabled: false in nexus.jsonc, which is refused by name.

Once it is serving, the same command opens it:

/nexus dashboard [port] [host]      # /nexus web is an alias of this
/nexus web [port] [host]

The TUI cannot start the server itself — its process has no orchestrator, no module registry and no way to invoke a tool — but it can reach the process that has all three, and it can ask whether anything is listening. So the command asks http://host:port/api/health first, and then does one of four things:

What it foundWhat it does
A nexus dashboardOpens your browser at that URL. Nothing is started a second time
A different process on that portSays so, opens nothing, suggests another port
Nothing thereSubmits /nexus dashboard [port] [host] to the server, waits for the port to answer, then opens the browser
Still nothing after ~3sSays the start did not confirm, opens nothing, and points at the command's own reply for the reason

The browser opens only after a confirmed listen. That is the whole point of the wait: a browser pointed at a dead address gives a connection-refused page, which looks like the dashboard failing rather than the dashboard not running.

How it stays current. A WebSocket to /ws/events carries a throttled orchestrator:state push — every state change schedules a full snapshot, at most once a second — plus the thirteen orchestrator events as they happen. On top of that, an Auto-refresh checkbox (on by default) has the page ask the server for a fresh state every 5 seconds and poll /api/health and /api/costs, the two things the socket does not carry. The push is what keeps the page fresh; the interval is a belt-and-braces refresh you can switch off. The page also shows how old the last snapshot is, and labels it stale past 15 seconds.

What it shows:

  • Sessions — one row per session nexus owns, is still collecting cost from, or has abandoned, with each one's state (running / idle / abandoned / settled), age, last read token count, and unbilled spend. Rows with no owning agent are called out in a banner above the table, because a session that is still generating after its agent was terminated keeps spending and nothing is collecting that spend — a case that was invisible on every layer before. The Age column is elapsed time, not a wall clock, and reads — for those orphan rows: spawnedAt comes from the owning agent, so an unowned session has no start time to show. The cell says so in its tooltip, and the — is shown rather than the column dropped, so the gap is visible. This list is nexus's own bookkeeping, not an enumeration of every open session on the server, and the page says so on the section itself.
  • Agents — role, status, model, session id, and metrics
  • Budget — spend against the configured cap, with the alert threshold marked
  • Tasks and DAG — the task list, and a graph drawn from the dependency edges the state actually reports. Edges pointing at tasks that are not in the snapshot, self-edges, and cycles are counted and reported in the section note rather than silently not drawn.
  • Cost breakdown — by agent and by model, read from /api/costs, which covers the full history rather than only the live agents
  • Configuration (read-only) — the resolved config the orchestrator reports as in force, plus a read-only JSON viewer. The write path was deliberately removed rather than left broken: it used to post a config:update message that the server does not handle, so the Apply button reported success and nothing happened. There is no auth story for writes and the socket is a localhost server answering with CORS: *, so no write path was added to replace it — edit nexus.jsonc instead.
  • Activity log — every event the broadcaster forwards, each delivered once. Two of them carry a fact the line used to leave out:
    • cost:delta names where the price came from (settledTier.pricing) and which token tier the amount was priced at. That is deliberately not the same as the measured/estimated split elsewhere on the page: settledTier.pricing is about the price — the model's published list, a fallback table because this model is not in it, or an unknown-model fallback — while measured/estimated is about the token counts, which the orchestrator reads off a real session. A line whose settledTier is missing says so instead of implying a price source.
    • config:reloaded names the cause (trigger) alongside the load number, the raw ISO load time, and both config files' state, so a reload that happened for a reason you did not ask for is visible as one.

Stop the dashboard:

/nexus dashboard stop
Ask the agent to call nexus.dashboard.stop

This stops the HTTP/WebSocket server only. The orchestrator, its agents and its sessions keep running. Both routes say so plainly when there was nothing running, rather than reporting a stop that did not happen.

Team Mode

Create a team of specialist agents working in parallel:

nexus.team.create(name="auth-team", leadRole="architect")
nexus.team.addMember(teamId="...", role="coder", model="opencode/mimo-v2.6-flash-free")
nexus.team.addMember(teamId="...", role="reviewer", model="opencode/muse-spark-1.2-contributor-free")
nexus.team.activate(teamId="...")

Todo & Goal Tracking

Track tasks and persist objectives across sessions:

nexus.todo.add(description="Implement auth middleware")
nexus.todo.list()
nexus.todo.complete(id="...")

nexus.goal.set(description="Build complete auth system")
nexus.goal.status()
nexus.goal.complete()

LSP Integration

None, beyond one line. On plugin load Nexus inserts "lsp": true into ~/.config/opencode/opencode.jsonc — but only if that file already exists and does not already contain the string "lsp". The edit is best-effort and its failure is silent. Nexus has no language list, no LSP state, and no surface that reports anything about LSP.

AST-Grep

Pattern-aware code search, shelling out to sg run:

nexus.astgrep.search(pattern="console.log($$$)", language="typescript", directory="src/")

There is no nexus.astgrep.rewrite tool. The AstGrep class has a rewrite method, but it is not registered — and it builds its shell command by interpolating the pattern into the string, which is presumably why.

Security Scanning

Regex detection of hardcoded secrets and known-dangerous constructs. The scanner is six secret patterns (API key, password, token, private key, -----BEGIN … PRIVATE KEY-----, AWS credentials) and eight dangerous-substring patterns (eval(, exec(, child_process, innerHTML=, document.write(, new Function(, __proto__=, direct process.env). It is per-file and per-line with no dataflow or CVE awareness; secret matches are reported critical and dangerous-pattern matches medium regardless of context.

nexus.security.scan(content="const API_KEY = \"sk-123\"", filename="config.ts")

Persistent Memory

SQLite-backed memory store that survives restarts. Entries never expire unless you pass a per-entry ttl — the store's defaultTTL is 0 — and search is a LIKE '%q%' substring match, not a full-text or ranked search. Reachable through the orchestrator API (orchestrator.memoryStore); no registered tool exposes it.

orchestrator.memoryStore.set({
  key: 'api-pattern',
  value: { endpoint: '/users', method: 'GET' },
  scope: 'project',
  author: 'architect',
  confidence: 0.9,
  tags: ['api', 'design']
})

Learning Module

Records failure patterns and solutions, building confidence over time:

const solutions = orchestrator.learning.findSolutions('TypeScript TS2345 error')
// → [{ entry: { solution: 'Add type cast', confidence: 0.85 }, similarity: 0.7 }]

Preset Configurations

PresetModelsBudgetSelf-Healing
minimalGemini Flash$1Off
balancedClaude/GPT mix$10On (3 retries)
enterpriseTop-tier$50On (5 retries)
cost-optimizedCheapest$3On (2 retries)

Agents

Nexus creates 7 agent files in ~/.config/opencode/agents/:

AgentModePurpose
nexus-orchestratorprimaryMain orchestrator — decompose, dispatch, integrate
nexus-architectsubagentSystem design and architecture
nexus-codersubagentImplement code tasks
nexus-reviewersubagentCode review (read-only)
nexus-testersubagentWrite and run tests
nexus-explorersubagentExplore codebases (read-only)
nexus-documentersubagentWrite documentation

Clarify

nexus.clarify does not ask the user anything and does not wait for a reply. It formats the question — with a numbered option list and a stated default — and returns that text as tool content, so the model is the one that ends up holding the question:

nexus.clarify(question="Should I use JWT or OAuth?", options="JWT, OAuth", assumption="JWT")
→ ❓ Should I use JWT or OAuth?
  Options:
  1. JWT
  2. OAuth
  💡 Default: JWT

The repository also contains a skills/ask-if-clarify/SKILL.md prompt. Nothing in src/ loads it and it is not in package.json's files list, so it is not installed with the package — treat it as a repo document, not a shipped feature.

Tools

ToolDescriptionInput
nexus.spawnSpawn a sub-agent{ role, task, model?, wait?, timeout? }
nexus.delegateSpawn + wait + result{ role, task, model?, timeout? }
nexus.sessionsList active sessions{}
nexus.backgroundMove agents to background{}
nexus.resultGet agent result{ sessionID }
nexus.statusOrchestrator status{ detailed? }
nexus.agentsList active agents, as JSON{ filter? } — filter by agent status
nexus.costsCost report & budget{}
nexus.notifications.testSend one OS notification and report whether it was delivered, and why not{}
nexus.dashboardFull orchestrator state as JSON{}
nexus.queueCurrent task list with priorities, as JSON{}
nexus.forecastPredict costs{ tasks }
nexus.model.costsShow/set model pricing{ model?, setInput?, setOutput? }
nexus.presetApply a preset config, or drop the session override{ mode?: 'apply' | 'clear', name? } — clear (2.6.0+) drops the in-process preset/TUI override so nexus.jsonc is in control again; no file is modified
nexus.templateList or instantiate a task template{ name?, baseDir? } — name: 'list' (or omitted) lists; baseDir resolves the template's file paths, defaulting to cwd
nexus.roles.listList custom agent roles from nexus.jsonc{}
nexus.roles.addRegister a custom role for this session only{ name, displayName, prompt, emoji?, model? } — a config reload replaces the registry from the file, so a role added this way and not written to nexus.jsonc stops resolving on the next reload
nexus.config.saveSave config to disk{ level: 'project' | 'global' }
nexus.config.initInitialize config files{ level }
nexus.dashboard.startStart the web dashboard server (port must be free){ port?, host? }
nexus.dashboard.stopStop the web dashboard server{}
nexus.todo.addAdd a todo item{ description, assignedTo? }
nexus.todo.listList all todos{}
nexus.todo.completeComplete a todo{ id }
nexus.todo.statsTodo statistics{}
nexus.goal.setSet a goal{ description, autoContinue? }
nexus.goal.statusCurrent goal status{}
nexus.goal.completeComplete goal{}
nexus.goal.listList all goals{}
nexus.team.createCreate a team{ name, leadRole }
nexus.team.addMemberAdd team member{ teamId, role, model }
nexus.team.statusTeam status{ teamId? }
nexus.team.activateStart team{ teamId }
nexus.performance.scoresPerformance scores{}
nexus.performance.bestBest model for role{ role }
nexus.history.listExecution history{ count? }
nexus.history.statsExecution statistics{}
nexus.astgrep.searchSearch AST patterns{ pattern, language, directory }
nexus.astgrep.statusCheck ast-grep install{}
nexus.security.scanScan for security issues{ content, filename? }
nexus.clarifyFormat a clarifying question and return it to the model (it does not query the user){ question, options?, assumption? }
nexus.worktree.enableEnable worktree isolation{ repoRoot? }
nexus.worktree.listList worktrees{}
nexus.worktree.disableDisable worktrees{}

TUI Commands

The TUI plugin registers exactly these slash commands. With no argument, /nexus opens the full configuration dialog; with one, it dispatches to a subcommand (config/c, status/s, dashboard/d, web/w, overview, model/m, reset).

CommandAliasDescription
/nexusCtrl+NFull configuration dialog, or a subcommand
/nexus-dashboard/ndStart the web dashboard and open it. If one is already serving, opens that and starts nothing — see Web Dashboard
/nexus-web/nwAlias of /nexus-dashboard
/nexus-overview/noConfig, budget and dashboard-status overview. Prints text; starts nothing
/nexus-config/ncConfigure models & budget
/nexus-model/nmSelect a model for a role
/nexus-status/nsShow the config summary
/nexus-reset—Reset all settings to defaults

A prompt beginning /nexus … typed into the composer is a different thing: it is intercepted by a prompt hook and routed to orchestrator.handleCommand(), which understands status, agents, costs, pause, resume, and dashboard [port] [host] (which starts the server, or says why it did not), dashboard stop, and dashboard state (the state as JSON). Anything else answers Unknown command. This hook cannot cancel the prompt — the plugin API gives it no way to — so it replaces the command text with the command's result rather than leaving the model holding a bare /nexus dashboard next to an answer it has no reason to read. The table above is the TUI palette.

Configuration

Agent Models

Press Ctrl+N or type /nexus to pick a model per role, then choose project or global. That writes the models block of nexus.jsonc; the example in Cost-Aware Model Selection shows its shape.

The resolved map is read at spawn time, so an edit to nexus.jsonc applies to the next spawn without a restart.

Web Dashboard

.opencode/nexus.jsonc (project) and ~/.config/opencode/nexus.jsonc (global) both accept:

{
  "dashboard": {
    "enabled": true,   // false makes every start attempt refuse, and say so
    "port": 4747,      // default port; startDashboard({port}) still wins
    "host": "127.0.0.1"
  }
}

The server has no authentication, which is why host defaults to loopback. Leave it there unless you have put your own authentication in front of it.

Notifications

{
  "notifications": {
    "enabled": true
  }
}

enabled (default true) is the whole block, and it is read once at orchestrator construction — changing it takes effect on the next reload or restart, not on the next event.

One switch governs every notification site: task-complete, task-failed and the budget alert/limit notifications alike. There is no per-site or per-level setting; false silences all of them and each suppressed send is counted rather than attempted.

nexus.status reports the outcome as a notifications object — sent, failed, suppressed, lastError, lastErrorAt, enabled, platform — in both the detailed and the summary branch. nexus.notifications.test sends one probe and reports whether the OS notifier accepted it and, if not, why; the probe is deliberately not counted in sent/failed.

Config Diagnostics

nexus.status carries a config object that answers "why did my spawn use an unexpected model":

FieldMeaning
sessionOverrideAn in-process preset or TUI override is active, so models describes memory rather than disk
diskModelsIgnoredThe same fact, named for the diagnosis: the file's models block is being shadowed right now. Clear it with nexus.preset(mode="clear")
triggerinitial, event (a filesystem.changed) or poll. poll on every reload means the host is not delivering events for these files
loadCount, loadedAtHow many times config has been loaded, and when
project, globalThe two paths consulted, and which existed

Custom Roles

Define your own agent roles:

{
  "customRoles": [
    {
      "name": "security-auditor",
      "displayName": "Security Auditor",
      "emoji": "🔐",
      "prompt": "You are a security auditor...",
      "model": "anthropic/claude-sonnet-4-6"
    }
  ]
}

Task Templates

nexus.template(name="list")      — Show available templates
nexus.template(name="feature")   — Full feature pipeline
nexus.template(name="bugfix")    — Bug investigation and fix
nexus.template(name="refactor")  — Code refactoring pipeline
nexus.template(name="documentation") — Documentation update

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      NEXUS PLUGIN                            │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │              SERVER PLUGIN (index.ts)                │     │
│  │  • 40+ tool registrations                            │     │
│  │  • Auto-creates agent files; opts OpenCode into LSP  │     │
│  │  • Config file loading and creation                 │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │            ORCHESTRATOR (orchestrator.ts)            │     │
│  │  • Real OpenCode session creation                    │     │
│  │  • DAG execution with priority queuing               │     │
│  │  • Cost-aware model routing (scored selection)       │     │
│  │  • Self-healing with 4-step escalation               │     │
│  │  • Context transfer to respawned agents              │     │
│  │  • Deadlock detection (cycle finding)                │     │
│  │  • Todo/Goal tracking                                │     │
│  │  • Team management                                   │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │                    MODULES                          │     │
│  │  Health Monitor │ Learning │ Message Store (JSONL)  │     │
│  │  Persistent Mem │ Fan-Out  │ Notifications (OS)     │     │
│  │  State Broadcaster │ Module Registry │ Security     │     │
│  │  Cost Forecaster │ Performance Tracker │ AST-Grep   │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │              WEB DASHBOARD                          │     │
│  │  • Bun.serve() HTTP + WebSocket (port 4747)         │     │
│  │  • DAG viz, cost chart, read-only config, sessions  │     │
│  │  • Started on request; throttled push + poll        │     │
│  └────────────────────────────────────────────────────┘     │
│                                                              │
│  ┌────────────────────────────────────────────────────┐     │
│  │              AGENTS (auto-created)                   │     │
│  │  nexus-orchestrator (primary)                       │     │
│  │  nexus-architect, nexus-coder, nexus-reviewer        │     │
│  │  nexus-tester, nexus-explorer, nexus-documenter      │     │
│  └────────────────────────────────────────────────────┘     │
└─────────────────────────────────────────────────────────────┘

Development

# Clone the repo
git clone https://github.com/serkanalgur/opencode-nexus.git
cd opencode-nexus

# Install dependencies
bun install

# Build
bun run build

# Run tests
bun test

# Type check
bun run typecheck

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for details.

License

MIT License - see LICENSE for details.

Built with ❤️ by Serkan Algur

GitHub npm

Keywords

opencode

FAQs

Package last updated on 27 Sep 2026

Related posts