@serkanalgur/opencode-nexus 1.4.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +182 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8846,7 +8846,7 @@ class DashboardModule {
8846
8846
  this.server = Bun.serve({
8847
8847
  port,
8848
8848
  hostname: host,
8849
- fetch(req, server) {
8849
+ async fetch(req, server) {
8850
8850
  const url = new URL(req.url);
8851
8851
  if (req.method === "OPTIONS") {
8852
8852
  return new Response(null, { headers: CORS_HEADERS });
@@ -8874,14 +8874,20 @@ class DashboardModule {
8874
8874
  }
8875
8875
  try {
8876
8876
  const spaPath = join2(process.cwd(), "dashboard", "index.html");
8877
- const spaFile = Bun.file(spaPath);
8878
- const exists = spaFile.size > 0;
8879
- if (exists) {
8880
- return new Response(spaFile, {
8877
+ const spaContent = await Bun.file(spaPath).text();
8878
+ if (spaContent.length > 100) {
8879
+ return new Response(spaContent, {
8881
8880
  headers: { "Content-Type": "text/html; charset=utf-8", ...CORS_HEADERS }
8882
8881
  });
8883
8882
  }
8884
- } catch {}
8883
+ return new Response(`SPA not found at ${spaPath} (content: ${spaContent.length} bytes)`, {
8884
+ headers: { "Content-Type": "text/plain", ...CORS_HEADERS }
8885
+ });
8886
+ } catch (e) {
8887
+ return new Response(`SPA error: ${e}`, {
8888
+ headers: { "Content-Type": "text/plain", ...CORS_HEADERS }
8889
+ });
8890
+ }
8885
8891
  return new Response("Nexus Dashboard API", {
8886
8892
  headers: {
8887
8893
  "Content-Type": "text/plain",
@@ -10006,6 +10012,7 @@ class NexusOrchestrator {
10006
10012
  }
10007
10013
  notifications = null;
10008
10014
  modelCosts = new Map;
10015
+ parentSessionID = null;
10009
10016
  constructor(config, messageStoreConfig, memoryStoreConfig) {
10010
10017
  this.config = this.mergeConfig(config);
10011
10018
  this.budget = this.config.budget;
@@ -10644,7 +10651,8 @@ Please continue from where the previous agent left off.`;
10644
10651
  const session = await this.ctx.session.create({
10645
10652
  title,
10646
10653
  agent: agentType,
10647
- model: modelName ? { providerID: provider, id: modelName } : undefined
10654
+ model: modelName ? { providerID: provider, id: modelName } : undefined,
10655
+ parentID: this.parentSessionID || undefined
10648
10656
  });
10649
10657
  const agent = {
10650
10658
  id: agentId,
@@ -11094,9 +11102,84 @@ function getTemplate(name) {
11094
11102
  }
11095
11103
 
11096
11104
  // src/index.ts
11105
+ import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync4, existsSync as existsSync3 } from "node:fs";
11106
+ import { join as join6 } from "node:path";
11107
+ import { homedir as homedir2 } from "node:os";
11108
+ var NEXUS_AGENT_CONTENT = `---
11109
+ description: Nexus multi-agent orchestrator — manages parallel sub-agents with cost-aware routing, self-healing, and intelligent task decomposition.
11110
+ mode: primary
11111
+ ---
11112
+
11113
+ # Nexus Orchestrator Agent
11114
+
11115
+ You are the Nexus Orchestrator — an intelligent multi-agent coordinator. You manage parallel sub-agents with cost-aware model selection, self-healing on failures, and DAG-based task execution.
11116
+
11117
+ ## Core Capabilities
11118
+
11119
+ - **Spawn specialized sub-agents** for each task (architect, coder, reviewer, tester, explorer, documenter)
11120
+ - **Cost-aware routing** — automatically selects optimal models based on task complexity and budget
11121
+ - **Self-healing** — retries failed tasks, transfers context to new agents, escalates if needed
11122
+ - **Performance tracking** — learns which model/role combinations work best
11123
+ - **Execution history** — tracks all past executions for analysis
11124
+
11125
+ ## How to Use Nexus Tools
11126
+
11127
+ ### Spawning Agents
11128
+ Use the \`spawn\` tool to create specialized agents:
11129
+ \`\`\`
11130
+ nexus.spawn(role="coder", task="Implement JWT authentication middleware")
11131
+ nexus.spawn(role="reviewer", task="Review the auth implementation for security")
11132
+ \`\`\`
11133
+
11134
+ Available roles: architect, coder, reviewer, tester, explorer, documenter
11135
+
11136
+ ### Checking Status
11137
+ \`\`\`
11138
+ nexus.status(detailed=true) — Full orchestrator state
11139
+ nexus.agents(filter="working") — List active agents
11140
+ nexus.costs() — Cost report and budget status
11141
+ \`\`\`
11142
+
11143
+ ### Cost Management
11144
+ \`\`\`
11145
+ nexus.forecast(tasks='[...]') — Predict costs
11146
+ nexus.model.costs() — Show real model pricing
11147
+ nexus.preset(name="balanced") — Apply a cost preset
11148
+ \`\`\`
11149
+
11150
+ ### Performance & History
11151
+ \`\`\`
11152
+ nexus.performance.scores() — Model/role performance scores
11153
+ nexus.history.list(count=10) — Recent execution history
11154
+ \`\`\`
11155
+
11156
+ ## Task Decomposition Strategy
11157
+
11158
+ When given a development request:
11159
+ 1. **Analyze** — Break the request into discrete tasks
11160
+ 2. **Estimate** — Use nexus.forecast to predict costs
11161
+ 3. **Execute** — Spawn agents in parallel where possible
11162
+ 4. **Review** — Use nexus.spawn(role="reviewer") for each delivery
11163
+ 5. **Report** — Summarize what was done, costs, and outcomes
11164
+
11165
+ ## Quality Gates
11166
+
11167
+ Never skip these steps:
11168
+ - Every code change must be reviewed by nexus.spawn(role="reviewer")
11169
+ - Run nexus.security.scan() on new code
11170
+ - Verify with nexus.spawn(role="tester") before marking complete
11171
+ `;
11097
11172
  var src_default = define({
11098
11173
  id: "nexus",
11099
11174
  async setup(ctx) {
11175
+ try {
11176
+ const agentDir = join6(homedir2(), ".config", "opencode", "agents");
11177
+ const agentFile = join6(agentDir, "nexus-orchestrator.md");
11178
+ if (!existsSync3(agentFile)) {
11179
+ mkdirSync4(agentDir, { recursive: true });
11180
+ writeFileSync2(agentFile, NEXUS_AGENT_CONTENT, "utf-8");
11181
+ }
11182
+ } catch {}
11100
11183
  const orchestrator = new NexusOrchestrator;
11101
11184
  await orchestrator.initialize(ctx, () => {
11102
11185
  const state = orchestrator.getState();
@@ -11316,25 +11399,54 @@ var src_default = define({
11316
11399
  });
11317
11400
  editor.add({
11318
11401
  name: "spawn",
11319
- description: "Spawn a sub-agent for a task",
11402
+ description: "Spawn a sub-agent for a task. Use wait=true to wait for completion.",
11320
11403
  input: {
11321
11404
  type: "object",
11322
11405
  properties: {
11323
11406
  role: { type: "string", description: "Agent role (architect, coder, reviewer, tester, explorer, documenter)" },
11324
11407
  task: { type: "string", description: "Task description" },
11325
- model: { type: "string", description: "Model override (optional)" }
11408
+ model: { type: "string", description: "Model override (optional)" },
11409
+ wait: { type: "boolean", description: "Wait for completion (default: false)" },
11410
+ timeout: { type: "number", description: "Timeout in ms when waiting (default: 120000)" }
11326
11411
  },
11327
11412
  required: ["role", "task"],
11328
11413
  additionalProperties: false
11329
11414
  },
11330
11415
  execute: async (input) => {
11331
- const { role, task, model } = input;
11416
+ const { role, task, model, wait, timeout } = input;
11332
11417
  try {
11418
+ orchestrator.parentSessionID = null;
11333
11419
  const agent = await orchestrator.spawnAgent({ role, model });
11334
11420
  await orchestrator.ctx.session.prompt({
11335
11421
  sessionID: agent.sessionID,
11336
11422
  text: task
11337
11423
  });
11424
+ if (wait) {
11425
+ const waitTimeout = timeout || 120000;
11426
+ try {
11427
+ await orchestrator.ctx.session.wait({ sessionID: agent.sessionID });
11428
+ } catch {}
11429
+ try {
11430
+ const messages = await orchestrator.ctx.session.context({ sessionID: agent.sessionID });
11431
+ const lastMsg = messages.filter((m) => m.role === "assistant").pop();
11432
+ const result = lastMsg?.content || "Task completed (no output captured)";
11433
+ agent.status = "completed";
11434
+ await ctx.storage.set("orchestrator-state", JSON.parse(JSON.stringify(orchestrator.getState())));
11435
+ const taskPreview = task.length > 80 ? task.substring(0, 77) + "..." : task;
11436
+ return {
11437
+ content: [
11438
+ `${agent.name}`,
11439
+ `\uD83D\uDCCB Task: ${taskPreview}`,
11440
+ `✅ Status: completed`,
11441
+ `\uD83D\uDCCE Session: ${agent.sessionID}`,
11442
+ `
11443
+ --- Result ---`,
11444
+ result
11445
+ ].join(`
11446
+ `)
11447
+ };
11448
+ } catch {}
11449
+ }
11338
11450
  const complexity = orchestrator.analyzeComplexity({
11339
11451
  id: `spawn-${Date.now()}`,
11340
11452
  name: task,
@@ -11355,7 +11467,8 @@ var src_default = define({
11355
11467
  `\uD83D\uDCCB Task: ${taskPreview}`,
11356
11468
  `\uD83D\uDCCA Complexity: ${complexity.overall}/100 (${complexity.factors.riskLevel} risk)`,
11357
11469
  `\uD83E\uDD16 Model reasoning: ${modelSelection.reasoning}`,
11358
- `\uD83D\uDCCE Session: ${agent.sessionID}`
11470
+ `\uD83D\uDCCE Session: ${agent.sessionID}`,
11471
+ `\uD83D\uDCA1 Use wait=true to wait for completion`
11359
11472
  ].join(`
11360
11473
  `);
11361
11474
  return { content: output };
@@ -11576,6 +11689,64 @@ Total: ~$${result.totalEstimatedCost.toFixed(4)}`);
11576
11689
  return { content: "Worktree isolation disabled." };
11577
11690
  }
11578
11691
  });
11692
+ editor.add({
11693
+ name: "sessions",
11694
+ description: "List all active Nexus agent sessions",
11695
+ input: { type: "object", properties: {}, additionalProperties: false },
11696
+ execute: async () => {
11697
+ const agents = orchestrator.getState().agents;
11698
+ if (agents.length === 0)
11699
+ return { content: "No active agent sessions." };
11700
+ const lines = agents.map((a) => {
11701
+ const statusIcon = a.status === "working" ? "\uD83D\uDD04" : a.status === "idle" ? "⏸️" : a.status === "completed" ? "✅" : "❌";
11702
+ return `${statusIcon} ${a.name} (${a.role}) — Session: ${a.sessionID}`;
11703
+ });
11704
+ return { content: `Active Sessions (${agents.length}):
11705
+ ${lines.join(`
11706
+ `)}` };
11707
+ }
11708
+ });
11709
+ editor.add({
11710
+ name: "background",
11711
+ description: "Move running agents to background (detach from current session)",
11712
+ input: { type: "object", properties: {}, additionalProperties: false },
11713
+ execute: async () => {
11714
+ const agents = orchestrator.getState().agents.filter((a) => a.status === "working" || a.status === "idle");
11715
+ if (agents.length === 0)
11716
+ return { content: "No running agents to move to background." };
11717
+ for (const agent of agents) {
11718
+ try {
11719
+ await orchestrator.ctx.session.background({ sessionID: agent.sessionID });
11720
+ } catch {}
11721
+ }
11722
+ return { content: `${agents.length} agent(s) moved to background. You can continue working while they run.` };
11723
+ }
11724
+ });
11725
+ editor.add({
11726
+ name: "result",
11727
+ description: "Get the result of a completed agent session",
11728
+ input: {
11729
+ type: "object",
11730
+ properties: {
11731
+ sessionID: { type: "string", description: "Session ID of the agent" }
11732
+ },
11733
+ required: ["sessionID"]
11734
+ },
11735
+ execute: async (input) => {
11736
+ const { sessionID } = input;
11737
+ try {
11738
+ const messages = await orchestrator.ctx.session.context({ sessionID });
11739
+ const lastMsg = messages.filter((m) => m.role === "assistant").pop();
11740
+ if (lastMsg) {
11741
+ return { content: `Session ${sessionID} result:
11742
+ ${lastMsg.content}` };
11743
+ }
11744
+ return { content: `Session ${sessionID} has no assistant messages yet.` };
11745
+ } catch (error) {
11746
+ return { content: `Failed to get result: ${error.message}` };
11747
+ }
11748
+ }
11749
+ });
11579
11750
  });
11580
11751
  await ctx.session.hook("prompt", (event) => {
11581
11752
  if (event.prompt.text.startsWith("/nexus")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serkanalgur/opencode-nexus",
3
- "version": "1.4.1",
3
+ "version": "1.6.0",
4
4
  "description": "Adaptive Multi-Agent Orchestration with Cost Intelligence for OpenCode V2",
5
5
  "keywords": [
6
6
  "opencode",