opencode-orchestrator 0.4.9 → 0.4.13

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.
package/README.md CHANGED
@@ -28,17 +28,15 @@ A **5-agent autonomous architecture** designed to solve complex engineering task
28
28
 
29
29
  **Core Philosophy**: Intelligence is a resource. We orchestrate that resource through **Phase-based Workflows** and **Mandatory Environment Scans** to ensure code always fits the project's infrastructure.
30
30
 
31
- > 🦀 **Powered by Rust** — Background tasks and parallel searches run on native Rust binaries for maximum performance.
32
-
33
31
  ### Key Features
34
- - **🎯 Autonomous Loop** — Commander runs relentlessly until the mission is complete.
32
+ - **🎯 Autonomous Loop** — Commander runs relentlessly until mission is complete.
35
33
  - **🔍 Environment Scan** — Mandatory analysis of Infra (Docker/OS), Stack, and Domain before any code change.
36
34
  - **🔨 Smart Implementation** — Builder matches existing codebase patterns exactly.
37
35
  - **🛡️ Rigorous Audit** — Inspector proves success with environment-specific evidence (Builds/Tests/Logs).
38
36
  - **💾 Persistent Context** — Recorder saves session state to disk, enabling resume at any time.
39
37
  - **🏗️ Parallel Agents** — Delegated agent execution (`delegate_task`) with sync/async modes.
40
38
  - **⏳ Background Tasks** — Run long commands (builds, tests) in background and check results later.
41
- - **🔎 mgrep** — Multi-pattern parallel search powered by Rust for blazing-fast codebase analysis.
39
+ - **🔎 mgrep** — Multi-pattern parallel search for fast codebase analysis.
42
40
 
43
41
  ---
44
42
 
@@ -95,158 +93,8 @@ Trigger parallel agent execution with prompts like:
95
93
 
96
94
  Commander will automatically use `delegate_task` with `background: true` for independent tasks.
97
95
 
98
- **Parallel Execution UI**
99
-
100
- When tasks run in parallel, you'll see detailed progress in OpenCode:
101
-
102
- ```
103
- ## 🚀 BACKGROUND TASK SPAWNED
104
-
105
- **Task Details**
106
- - **ID**: `task_a1b2c3d4`
107
- - **Agent**: builder
108
- - **Description**: Implement authentication system
109
- - **Status**: ⏳ Running in background (non-blocking)
110
-
111
- **Active Tasks**
112
- - Running: 2
113
- - Pending: 1
114
-
115
- ---
116
-
117
- **Monitoring Commands**
118
-
119
- Check progress anytime:
120
- - `list_tasks()` - View all parallel tasks
121
- - `get_task_result({ taskId: "task_a1b2c3d4" })` - Get latest result
122
- - `cancel_task({ taskId: "task_a1b2c3d4" })` - Stop this task
123
-
124
- ---
125
-
126
- ✓ System will notify when ALL tasks complete. You can continue working!
127
- ```
128
-
129
- **Terminal Logs**
130
-
131
96
  Monitor parallel tasks in terminal:
132
97
 
133
- ```
134
- [parallel] 🚀 SPAWNED task_a1b2c3d4 → builder: Implement authentication
135
- [parallel] 🚀 SPAWNED task_e5f6g7h8 → inspector: Review module
136
- [parallel] ✅ COMPLETED task_e5f6g7h8 → inspector: Review module (45s)
137
- [parallel] 🗑️ CLEANED task_e5f6g7h8 (session deleted)
138
- ```
139
-
140
- **All Tasks Complete**
141
-
142
- When all parallel tasks finish, you'll see:
143
-
144
- ```
145
- **All Parallel Tasks Complete**
146
-
147
- ✅ `task_a1b2c3d4` (1m 30s): Implement authentication
148
- ✅ `task_e5f6g7h8` (45s): Review module
149
-
150
- ---
151
-
152
- **Retrieval Options**
153
-
154
- Use `get_task_result({ taskId: "task_xxx" })` to retrieve full results.
155
-
156
- ---
157
-
158
- **Task Summary**
159
-
160
- Total Tasks: 2
161
- Status: All Complete
162
- Mode: Background (non-blocking)
163
- ```
164
- "Build and test in parallel"
165
- "Implement feature X while reviewing module Y"
166
- "Run linting, tests, and build at the same time"
167
- ```
168
-
169
- Commander will automatically use `delegate_task` with `background: true` for independent tasks.
170
-
171
- **Parallel Execution UI**
172
-
173
- When tasks run in parallel, you'll see detailed progress in OpenCode:
174
-
175
- ```
176
- ╔════════════════════════════════════════════════════════════╗
177
- ║ 🚀 BACKGROUND TASK SPAWNED ║
178
- ╠═════════════════════════════════════════════════════════════╣
179
- ║ Task ID: task_a1b2c3d4 ║
180
- ║ Agent: builder ║
181
- ║ Description: Implement authentication system ║
182
- ║ Status: ⏳ RUNNING (background) ║
183
- ╠═════════════════════════════════════════════════════════════╣
184
- ║ Running: 2 │ Pending: 1 ║
185
- ╚══════════════════════════════════════════════════════════════╝
186
-
187
- ---
188
-
189
- **Parallel Execution Started**
190
-
191
- - 📌 Task ID: `task_a1b2c3d4`
192
- - 🤖 Agent: builder
193
- - 📝 Description: Implement authentication system
194
- - ⏳ Status: Running in background (non-blocking)
195
- - 🔄 Active Tasks: 2 running, 1 pending
196
-
197
- **Monitoring**
198
-
199
- Check progress anytime with:
200
- - `list_tasks()` - View all parallel tasks
201
- - `get_task_result({ taskId: "task_a1b2c3d4" })` - Get latest result
202
- - `cancel_task({ taskId: "task_a1b2c3d4" })` - Stop this task
203
-
204
- System will notify when ALL tasks complete. You can continue working!
205
- ```
206
-
207
- **Terminal Logs**
208
-
209
- Monitor parallel tasks in terminal:
210
-
211
- ```
212
- [parallel] 🚀 SPAWNED task_a1b2c3d4 → builder: Implement authentication
213
- [parallel] 🚀 SPAWNED task_e5f6g7h8 → inspector: Review module
214
- [parallel] ✅ COMPLETED task_e5f6g7h8 → inspector: Review module (45s)
215
- [parallel] 🗑️ CLEANED task_e5f6g7h8 (session deleted)
216
- ```
217
-
218
- **All Tasks Complete**
219
-
220
- When all parallel tasks finish, you'll see:
221
-
222
- ```
223
- **All Parallel Tasks Complete**
224
-
225
- ✅ `task_a1b2c3d4` (1m 30s): Implement authentication
226
- ✅ `task_e5f6g7h8` (45s): Review module
227
-
228
- ---
229
-
230
- **Retrieval Options**
231
-
232
- Use `get_task_result({ taskId: "task_xxx" })` to retrieve full results.
233
-
234
- ---
235
-
236
- **Task Summary**
237
-
238
- Total Tasks: 2
239
- Status: All Complete
240
- Mode: Background (non-blocking)
241
- ```
242
- "Build and test in parallel"
243
- "Implement feature X while reviewing module Y"
244
- "Run linting, tests, and build at the same time"
245
- ```
246
-
247
- Commander will automatically use `delegate_task` with `background: true` for independent tasks.
248
-
249
- Monitor parallel tasks in the terminal:
250
98
  ```
251
99
  [parallel] 🚀 SPAWNED task_a1b2 → builder: Implement feature X
252
100
  [parallel] 🚀 SPAWNED task_c3d4 → inspector: Review module Y
@@ -8,6 +8,7 @@ import { ChildProcess } from "child_process";
8
8
  export type TaskStatus = "pending" | "running" | "done" | "error" | "timeout";
9
9
  export interface BackgroundTask {
10
10
  id: string;
11
+ sessionID?: string;
11
12
  command: string;
12
13
  args: string[];
13
14
  cwd: string;
@@ -26,17 +27,45 @@ export interface RunBackgroundOptions {
26
27
  cwd?: string;
27
28
  timeout?: number;
28
29
  label?: string;
30
+ sessionID?: string;
29
31
  }
30
32
  declare class BackgroundTaskManager {
31
33
  private static _instance;
32
34
  private tasks;
33
35
  private debugMode;
36
+ private storageDir;
37
+ private storageFile;
38
+ private monitoringInterval?;
34
39
  private constructor();
35
40
  static get instance(): BackgroundTaskManager;
36
41
  /**
37
42
  * Generate a unique task ID in the format job_xxxxxxxx
38
43
  */
39
44
  private generateId;
45
+ /**
46
+ * Ensure storage directory exists
47
+ */
48
+ private ensureStorageDir;
49
+ /**
50
+ * Load tasks from disk on startup
51
+ */
52
+ private loadFromDisk;
53
+ /**
54
+ * Save tasks to disk
55
+ */
56
+ private saveToDisk;
57
+ /**
58
+ * Start periodic monitoring of running processes
59
+ */
60
+ private startMonitoring;
61
+ /**
62
+ * Stop monitoring
63
+ */
64
+ private stopMonitoring;
65
+ /**
66
+ * Monitor running processes and detect zombie processes
67
+ */
68
+ private monitorRunningProcesses;
40
69
  /**
41
70
  * Debug logging helper
42
71
  */
@@ -57,6 +86,10 @@ declare class BackgroundTaskManager {
57
86
  * Get tasks by status
58
87
  */
59
88
  getByStatus(status: TaskStatus): BackgroundTask[];
89
+ /**
90
+ * Clean up tasks by session ID
91
+ */
92
+ cleanupBySession(sessionID: string): number;
60
93
  /**
61
94
  * Clear completed/failed tasks
62
95
  */
package/dist/index.d.ts CHANGED
@@ -82,23 +82,25 @@ declare const OrchestratorPlugin: (input: PluginInput) => Promise<{
82
82
  cwd: import("zod").ZodOptional<import("zod").ZodString>;
83
83
  timeout: import("zod").ZodOptional<import("zod").ZodNumber>;
84
84
  label: import("zod").ZodOptional<import("zod").ZodString>;
85
+ sessionID: import("zod").ZodOptional<import("zod").ZodString>;
85
86
  };
86
87
  execute(args: {
87
88
  command: string;
88
89
  cwd?: string | undefined;
89
90
  timeout?: number | undefined;
90
91
  label?: string | undefined;
92
+ sessionID?: string | undefined;
91
93
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
92
94
  };
93
95
  check_background: {
94
96
  description: string;
95
97
  args: {
96
- task_id: import("zod").ZodString;
97
- tail_lines: import("zod").ZodOptional<import("zod").ZodNumber>;
98
+ taskId: import("zod").ZodString;
99
+ tailLines: import("zod").ZodOptional<import("zod").ZodNumber>;
98
100
  };
99
101
  execute(args: {
100
- task_id: string;
101
- tail_lines?: number | undefined;
102
+ taskId: string;
103
+ tailLines?: number | undefined;
102
104
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
103
105
  };
104
106
  list_background: {
@@ -106,22 +108,22 @@ declare const OrchestratorPlugin: (input: PluginInput) => Promise<{
106
108
  args: {
107
109
  status: import("zod").ZodOptional<import("zod").ZodEnum<{
108
110
  running: "running";
109
- all: "all";
110
111
  done: "done";
111
112
  error: "error";
113
+ all: "all";
112
114
  }>>;
113
115
  };
114
116
  execute(args: {
115
- status?: "running" | "all" | "done" | "error" | undefined;
117
+ status?: "running" | "done" | "error" | "all" | undefined;
116
118
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
117
119
  };
118
120
  kill_background: {
119
121
  description: string;
120
122
  args: {
121
- task_id: import("zod").ZodString;
123
+ taskId: import("zod").ZodString;
122
124
  };
123
125
  execute(args: {
124
- task_id: string;
126
+ taskId: string;
125
127
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
126
128
  };
127
129
  };
package/dist/index.js CHANGED
@@ -99,8 +99,8 @@ PREFER background=true (PARALLEL):
99
99
  EXAMPLE - PARALLEL:
100
100
  \`\`\`
101
101
  // Multiple tasks in parallel
102
- delegate_task({ agent: "builder", description: "Implement X", prompt: "...", background: true })
103
- delegate_task({ agent: "inspector", description: "Review Y", prompt: "...", background: true })
102
+ delegate_task({ agent: "${AGENT_NAMES.BUILDER}", description: "Implement X", prompt: "...", background: true })
103
+ delegate_task({ agent: "${AGENT_NAMES.INSPECTOR}", description: "Review Y", prompt: "...", background: true })
104
104
 
105
105
  // Continue other work (don't wait!)
106
106
 
@@ -111,7 +111,7 @@ get_task_result({ taskId: "task_xxx" })
111
111
  EXAMPLE - SYNC (rare):
112
112
  \`\`\`
113
113
  // Only when you absolutely need the result now
114
- const result = delegate_task({ agent: "builder", ..., background: false })
114
+ const result = delegate_task({ agent: "${AGENT_NAMES.BUILDER}", ..., background: false })
115
115
  // Result is immediately available
116
116
  \`\`\`
117
117
  </agent_calling>
@@ -138,10 +138,10 @@ During implementation:
138
138
  PARALLEL EXECUTION TOOLS:
139
139
 
140
140
  1. **spawn_agent** - Launch agents in parallel sessions
141
- spawn_agent({ agent: "builder", description: "Implement X", prompt: "..." })
142
- spawn_agent({ agent: "inspector", description: "Review Y", prompt: "..." })
143
- \u2192 Agents run concurrently, system notifies when ALL complete
144
- \u2192 Use get_task_result({ taskId }) to retrieve results
141
+ spawn_agent({ agent: "${AGENT_NAMES.BUILDER}", description: "Implement X", prompt: "..." })
142
+ spawn_agent({ agent: "${AGENT_NAMES.INSPECTOR}", description: "Review Y", prompt: "..." })
143
+ \u2192 Agents run concurrently, system notifies when ALL complete
144
+ \u2192 Use get_task_result({ taskId }) to retrieve results
145
145
 
146
146
  2. **run_background** - Run shell commands asynchronously
147
147
  run_background({ command: "npm run build" })
@@ -192,10 +192,10 @@ WORKFLOW:
192
192
  <empty_responses>
193
193
  | Agent Empty (or Gibberish) | Action |
194
194
  |----------------------------|--------|
195
- | recorder | Fresh start. Proceed to survey. |
196
- | architect | Try simpler plan yourself. |
197
- | builder | Call inspector to diagnose. |
198
- | inspector | Retry with more context. |
195
+ | ${AGENT_NAMES.RECORDER} | Fresh start. Proceed to survey. |
196
+ | ${AGENT_NAMES.ARCHITECT} | Try simpler plan yourself. |
197
+ | ${AGENT_NAMES.BUILDER} | Call inspector to diagnose. |
198
+ | ${AGENT_NAMES.INSPECTOR} | Retry with more context. |
199
199
  </empty_responses>
200
200
 
201
201
  STRICT RULE: If any agent output contains gibberish, mixed-language hallucinations, or fails the language rule, REJECT it immediately and trigger a "STRICT_CLEAN_START" retry.
@@ -13319,9 +13319,357 @@ Returns matches grouped by pattern, with file paths and line numbers.
13319
13319
  }
13320
13320
  });
13321
13321
 
13322
+ // src/core/background.ts
13323
+ import { spawn as spawn2 } from "child_process";
13324
+ import { randomBytes } from "crypto";
13325
+ import { mkdirSync, readFileSync, writeFileSync, existsSync as existsSync3 } from "fs";
13326
+ import { join as join2 } from "path";
13327
+ import { homedir } from "os";
13328
+ var BackgroundTaskManager = class _BackgroundTaskManager {
13329
+ static _instance;
13330
+ tasks = /* @__PURE__ */ new Map();
13331
+ debugMode = true;
13332
+ // Enable debug mode
13333
+ storageDir;
13334
+ storageFile;
13335
+ monitoringInterval;
13336
+ constructor() {
13337
+ this.storageDir = join2(homedir(), ".opencode-orchestrator");
13338
+ this.storageFile = join2(this.storageDir, "tasks.json");
13339
+ this.loadFromDisk();
13340
+ this.startMonitoring();
13341
+ }
13342
+ static get instance() {
13343
+ if (!_BackgroundTaskManager._instance) {
13344
+ _BackgroundTaskManager._instance = new _BackgroundTaskManager();
13345
+ }
13346
+ return _BackgroundTaskManager._instance;
13347
+ }
13348
+ /**
13349
+ * Generate a unique task ID in the format job_xxxxxxxx
13350
+ */
13351
+ generateId() {
13352
+ const hex3 = randomBytes(4).toString("hex");
13353
+ return `job_${hex3}`;
13354
+ }
13355
+ /**
13356
+ * Ensure storage directory exists
13357
+ */
13358
+ ensureStorageDir() {
13359
+ if (!existsSync3(this.storageDir)) {
13360
+ mkdirSync(this.storageDir, { recursive: true });
13361
+ this.debug("system", `Created storage directory: ${this.storageDir}`);
13362
+ }
13363
+ }
13364
+ /**
13365
+ * Load tasks from disk on startup
13366
+ */
13367
+ loadFromDisk() {
13368
+ this.ensureStorageDir();
13369
+ if (!existsSync3(this.storageFile)) {
13370
+ this.debug("system", "No existing task data on disk");
13371
+ return;
13372
+ }
13373
+ try {
13374
+ const data = readFileSync(this.storageFile, "utf-8");
13375
+ const tasksData = JSON.parse(data);
13376
+ for (const [id, taskData] of Object.entries(tasksData)) {
13377
+ const task = taskData;
13378
+ task.process = void 0;
13379
+ if (task.status === "running") {
13380
+ task.status = "error";
13381
+ task.errorOutput += "\n[Process lost on restart]";
13382
+ task.endTime = Date.now();
13383
+ task.exitCode = null;
13384
+ }
13385
+ this.tasks.set(id, task);
13386
+ }
13387
+ this.debug("system", `Loaded ${this.tasks.size} tasks from disk`);
13388
+ } catch (error45) {
13389
+ this.debug(
13390
+ "system",
13391
+ `Failed to load tasks: ${error45 instanceof Error ? error45.message : String(error45)}`
13392
+ );
13393
+ }
13394
+ }
13395
+ /**
13396
+ * Save tasks to disk
13397
+ */
13398
+ saveToDisk() {
13399
+ this.ensureStorageDir();
13400
+ try {
13401
+ const tasksData = {};
13402
+ for (const [id, task] of this.tasks.entries()) {
13403
+ tasksData[id] = task;
13404
+ }
13405
+ writeFileSync(
13406
+ this.storageFile,
13407
+ JSON.stringify(tasksData, null, 2),
13408
+ "utf-8"
13409
+ );
13410
+ } catch (error45) {
13411
+ this.debug(
13412
+ "system",
13413
+ `Failed to save tasks: ${error45 instanceof Error ? error45.message : String(error45)}`
13414
+ );
13415
+ }
13416
+ }
13417
+ /**
13418
+ * Start periodic monitoring of running processes
13419
+ */
13420
+ startMonitoring() {
13421
+ const MONITOR_INTERVAL_MS = 5e3;
13422
+ this.monitoringInterval = setInterval(() => {
13423
+ this.monitorRunningProcesses();
13424
+ }, MONITOR_INTERVAL_MS);
13425
+ if (this.monitoringInterval) {
13426
+ this.monitoringInterval.unref();
13427
+ }
13428
+ }
13429
+ /**
13430
+ * Stop monitoring
13431
+ */
13432
+ stopMonitoring() {
13433
+ if (this.monitoringInterval) {
13434
+ clearInterval(this.monitoringInterval);
13435
+ this.monitoringInterval = void 0;
13436
+ }
13437
+ }
13438
+ /**
13439
+ * Monitor running processes and detect zombie processes
13440
+ */
13441
+ monitorRunningProcesses() {
13442
+ const now = Date.now();
13443
+ let hasRunningTasks = false;
13444
+ for (const [id, task] of this.tasks.entries()) {
13445
+ if (task.status !== "running") continue;
13446
+ hasRunningTasks = true;
13447
+ if (task.process && task.process.pid) {
13448
+ const pid = task.process.pid;
13449
+ try {
13450
+ process.kill(pid, 0);
13451
+ } catch (error45) {
13452
+ task.status = "error";
13453
+ task.errorOutput += `
13454
+ Process disappeared (PID ${pid})`;
13455
+ task.endTime = Date.now();
13456
+ task.exitCode = null;
13457
+ task.process = void 0;
13458
+ this.saveToDisk();
13459
+ this.debug(id, `Process dead (PID ${pid}), marked as error`);
13460
+ }
13461
+ } else if (task.process) {
13462
+ task.status = "error";
13463
+ task.errorOutput += "\nProcess reference lost";
13464
+ task.endTime = Date.now();
13465
+ task.exitCode = null;
13466
+ this.saveToDisk();
13467
+ this.debug(id, "Process reference lost, marked as error");
13468
+ }
13469
+ }
13470
+ if (!hasRunningTasks && this.monitoringInterval) {
13471
+ this.stopMonitoring();
13472
+ }
13473
+ }
13474
+ /**
13475
+ * Debug logging helper
13476
+ */
13477
+ debug(taskId, message) {
13478
+ if (this.debugMode) {
13479
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().substring(11, 23);
13480
+ console.log(`[BG-DEBUG ${timestamp}] ${taskId}: ${message}`);
13481
+ }
13482
+ }
13483
+ /**
13484
+ * Run a command in the background
13485
+ */
13486
+ run(options) {
13487
+ const id = this.generateId();
13488
+ const { command, cwd = process.cwd(), timeout = 3e5, label } = options;
13489
+ const isWindows = process.platform === "win32";
13490
+ const shell = isWindows ? "cmd.exe" : "/bin/sh";
13491
+ const shellFlag = isWindows ? "/c" : "-c";
13492
+ const task = {
13493
+ id,
13494
+ command,
13495
+ args: [shellFlag, command],
13496
+ cwd,
13497
+ label,
13498
+ status: "running",
13499
+ output: "",
13500
+ errorOutput: "",
13501
+ exitCode: null,
13502
+ startTime: Date.now(),
13503
+ timeout
13504
+ };
13505
+ this.tasks.set(id, task);
13506
+ this.saveToDisk();
13507
+ this.debug(id, `Starting: ${command} (cwd: ${cwd})`);
13508
+ try {
13509
+ const proc = spawn2(shell, task.args, {
13510
+ cwd,
13511
+ stdio: ["ignore", "pipe", "pipe"],
13512
+ detached: false
13513
+ });
13514
+ task.process = proc;
13515
+ proc.stdout?.on("data", (data) => {
13516
+ const text = data.toString();
13517
+ task.output += text;
13518
+ this.debug(
13519
+ id,
13520
+ `stdout: ${text.substring(0, 100)}${text.length > 100 ? "..." : ""}`
13521
+ );
13522
+ });
13523
+ proc.stderr?.on("data", (data) => {
13524
+ const text = data.toString();
13525
+ task.errorOutput += text;
13526
+ this.debug(
13527
+ id,
13528
+ `stderr: ${text.substring(0, 100)}${text.length > 100 ? "..." : ""}`
13529
+ );
13530
+ });
13531
+ proc.on("close", (code) => {
13532
+ task.exitCode = code;
13533
+ task.endTime = Date.now();
13534
+ task.status = code === 0 ? "done" : "error";
13535
+ task.process = void 0;
13536
+ this.saveToDisk();
13537
+ const duration3 = ((task.endTime - task.startTime) / 1e3).toFixed(2);
13538
+ this.debug(id, `Completed with code ${code} in ${duration3}s`);
13539
+ });
13540
+ proc.on("error", (err) => {
13541
+ task.status = "error";
13542
+ task.errorOutput += `
13543
+ Process error: ${err.message}`;
13544
+ task.endTime = Date.now();
13545
+ task.process = void 0;
13546
+ this.saveToDisk();
13547
+ this.debug(id, `Error: ${err.message}`);
13548
+ });
13549
+ setTimeout(() => {
13550
+ if (task.status === "running" && task.process) {
13551
+ this.debug(id, `Timeout after ${timeout}ms, killing process`);
13552
+ task.process.kill("SIGKILL");
13553
+ task.status = "timeout";
13554
+ task.endTime = Date.now();
13555
+ task.errorOutput += `
13556
+ Process killed: timeout after ${timeout}ms`;
13557
+ this.saveToDisk();
13558
+ }
13559
+ }, timeout);
13560
+ } catch (err) {
13561
+ task.status = "error";
13562
+ task.errorOutput = `Failed to spawn: ${err instanceof Error ? err.message : String(err)}`;
13563
+ task.endTime = Date.now();
13564
+ this.saveToDisk();
13565
+ this.debug(id, `Spawn failed: ${task.errorOutput}`);
13566
+ }
13567
+ return task;
13568
+ }
13569
+ /**
13570
+ * Get task by ID
13571
+ */
13572
+ get(taskId) {
13573
+ return this.tasks.get(taskId);
13574
+ }
13575
+ /**
13576
+ * Get all tasks
13577
+ */
13578
+ getAll() {
13579
+ return Array.from(this.tasks.values());
13580
+ }
13581
+ /**
13582
+ * Get tasks by status
13583
+ */
13584
+ getByStatus(status) {
13585
+ return this.getAll().filter((t) => t.status === status);
13586
+ }
13587
+ /**
13588
+ * Clean up tasks by session ID
13589
+ */
13590
+ cleanupBySession(sessionID) {
13591
+ let count = 0;
13592
+ for (const [id, task] of this.tasks) {
13593
+ if (task.sessionID === sessionID) {
13594
+ if (task.process && task.status === "running") {
13595
+ task.process.kill("SIGKILL");
13596
+ }
13597
+ this.tasks.delete(id);
13598
+ count++;
13599
+ this.debug(id, `Cleaned up for session ${sessionID}`);
13600
+ }
13601
+ }
13602
+ this.saveToDisk();
13603
+ return count;
13604
+ }
13605
+ /**
13606
+ * Clear completed/failed tasks
13607
+ */
13608
+ clearCompleted() {
13609
+ let count = 0;
13610
+ for (const [id, task] of this.tasks) {
13611
+ if (task.status !== "running" && task.status !== "pending") {
13612
+ this.tasks.delete(id);
13613
+ count++;
13614
+ }
13615
+ }
13616
+ this.saveToDisk();
13617
+ return count;
13618
+ }
13619
+ /**
13620
+ * Kill a running task
13621
+ */
13622
+ kill(taskId) {
13623
+ const task = this.tasks.get(taskId);
13624
+ if (task?.process) {
13625
+ task.process.kill("SIGKILL");
13626
+ task.status = "error";
13627
+ task.errorOutput += "\nKilled by user";
13628
+ task.endTime = Date.now();
13629
+ this.saveToDisk();
13630
+ this.debug(taskId, "Killed by user");
13631
+ return true;
13632
+ }
13633
+ return false;
13634
+ }
13635
+ /**
13636
+ * Format duration for display
13637
+ */
13638
+ formatDuration(task) {
13639
+ const end = task.endTime || Date.now();
13640
+ const seconds = (end - task.startTime) / 1e3;
13641
+ if (seconds < 60) {
13642
+ return `${seconds.toFixed(1)}s`;
13643
+ }
13644
+ const minutes = Math.floor(seconds / 60);
13645
+ const remainingSeconds = seconds % 60;
13646
+ return `${minutes}m ${remainingSeconds.toFixed(0)}s`;
13647
+ }
13648
+ /**
13649
+ * Get status emoji
13650
+ */
13651
+ getStatusEmoji(status) {
13652
+ switch (status) {
13653
+ case "pending":
13654
+ return "\u23F8\uFE0F";
13655
+ case "running":
13656
+ return "\u23F3";
13657
+ case "done":
13658
+ return "\u2705";
13659
+ case "error":
13660
+ return "\u274C";
13661
+ case "timeout":
13662
+ return "\u23F0";
13663
+ default:
13664
+ return "\u2753";
13665
+ }
13666
+ }
13667
+ };
13668
+ var backgroundTaskManager = BackgroundTaskManager.instance;
13669
+
13322
13670
  // src/tools/background.ts
13323
13671
  var runBackgroundTool = tool({
13324
- description: `Run a shell command in background and get a task ID.
13672
+ description: `Run a shell command in the background and get a task ID.
13325
13673
 
13326
13674
  <purpose>
13327
13675
  Execute long-running commands (builds, tests, etc.) without blocking.
@@ -13344,10 +13692,31 @@ The command runs asynchronously - use check_background to get results.
13344
13692
  command: tool.schema.string().describe("Shell command to execute"),
13345
13693
  cwd: tool.schema.string().optional().describe("Working directory (default: project root)"),
13346
13694
  timeout: tool.schema.number().optional().describe("Timeout in milliseconds (default: 300000 = 5 min)"),
13347
- label: tool.schema.string().optional().describe("Human-readable label for this task")
13695
+ label: tool.schema.string().optional().describe("Human-readable label for this task"),
13696
+ sessionID: tool.schema.string().optional().describe("Session ID for automatic cleanup on session deletion")
13348
13697
  },
13349
13698
  async execute(args) {
13350
- return callRustTool("run_background", args);
13699
+ const { command, cwd, timeout, label, sessionID } = args;
13700
+ const task = backgroundTaskManager.run({
13701
+ command,
13702
+ cwd: cwd || process.cwd(),
13703
+ timeout: timeout || 3e5,
13704
+ label,
13705
+ sessionID
13706
+ });
13707
+ const displayLabel = label ? ` (${label})` : "";
13708
+ return `\u{1F680} **Background Task Started**${displayLabel}
13709
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
13710
+ | Property | Value |
13711
+ |----------|-------|
13712
+ | **Task ID** | \`${task.id}\` |
13713
+ | **Command** | \`${command}\` |
13714
+ | **Status** | ${backgroundTaskManager.getStatusEmoji(task.status)} ${task.status} |
13715
+ | **Working Dir** | ${task.cwd} |
13716
+ | **Timeout** | ${(task.timeout / 1e3).toFixed(0)}s |
13717
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
13718
+
13719
+ \u{1F4CC} **Next Step**: Use \`check_background\` with task ID \`${task.id}\` to get results.`;
13351
13720
  }
13352
13721
  });
13353
13722
  var checkBackgroundTool = tool({
@@ -13365,11 +13734,75 @@ Use this after run_background to get results.
13365
13734
  - Full output (stdout + stderr)
13366
13735
  </output_includes>`,
13367
13736
  args: {
13368
- task_id: tool.schema.string().describe("Task ID from run_background (e.g., job_a1b2c3d4)"),
13369
- tail_lines: tool.schema.number().optional().describe("Limit output to last N lines (default: show all)")
13737
+ taskId: tool.schema.string().describe("Task ID from run_background (e.g., job_a1b2c3d4)"),
13738
+ tailLines: tool.schema.number().optional().describe("Limit output to last N lines (default: show all)")
13370
13739
  },
13371
13740
  async execute(args) {
13372
- return callRustTool("check_background", args);
13741
+ const { taskId, tailLines } = args;
13742
+ const task = backgroundTaskManager.get(taskId);
13743
+ if (!task) {
13744
+ const allTasks = backgroundTaskManager.getAll();
13745
+ if (allTasks.length === 0) {
13746
+ return `\u274C Task \`${taskId}\` not found. No background tasks exist.`;
13747
+ }
13748
+ const taskList = allTasks.map((t) => `- \`${t.id}\`: ${t.command.substring(0, 30)}...`).join("\n");
13749
+ return `\u274C Task \`${taskId}\` not found.
13750
+
13751
+ **Available tasks:**
13752
+ ${taskList}`;
13753
+ }
13754
+ const duration3 = backgroundTaskManager.formatDuration(task);
13755
+ const statusEmoji = backgroundTaskManager.getStatusEmoji(task.status);
13756
+ let output = task.output;
13757
+ let stderr = task.errorOutput;
13758
+ if (tailLines && tailLines > 0) {
13759
+ const outputLines = output.split("\n");
13760
+ const stderrLines = stderr.split("\n");
13761
+ output = outputLines.slice(-tailLines).join("\n");
13762
+ stderr = stderrLines.slice(-tailLines).join("\n");
13763
+ }
13764
+ const maxLen = 1e4;
13765
+ if (output.length > maxLen) {
13766
+ output = `[...truncated ${output.length - maxLen} chars...]
13767
+ ` + output.substring(output.length - maxLen);
13768
+ }
13769
+ if (stderr.length > maxLen) {
13770
+ stderr = `[...truncated ${stderr.length - maxLen} chars...]
13771
+ ` + stderr.substring(stderr.length - maxLen);
13772
+ }
13773
+ const labelDisplay = task.label ? ` (${task.label})` : "";
13774
+ let result = `${statusEmoji} **Task ${task.id}**${labelDisplay}
13775
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
13776
+ | Property | Value |
13777
+ |----------|-------|
13778
+ | **Command** | \`${task.command}\` |
13779
+ | **Status** | ${statusEmoji} **${task.status.toUpperCase()}** |
13780
+ | **Duration** | ${duration3}${task.status === "running" ? " (ongoing)" : ""} |
13781
+ ${task.exitCode !== null ? `| **Exit Code** | ${task.exitCode} |` : ""}
13782
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`;
13783
+ if (output.trim()) {
13784
+ result += `
13785
+
13786
+ \u{1F4E4} **Output (stdout)**:
13787
+ \`\`\`
13788
+ ${output.trim()}
13789
+ \`\`\``;
13790
+ }
13791
+ if (stderr.trim()) {
13792
+ result += `
13793
+
13794
+ \u26A0\uFE0F **Errors (stderr)**:
13795
+ \`\`\`
13796
+ ${stderr.trim()}
13797
+ \`\`\``;
13798
+ }
13799
+ if (task.status === "running") {
13800
+ result += `
13801
+
13802
+ \u23F3 Task still running... Check again later with:
13803
+ \`check_background({ taskId: "${task.id}" })\``;
13804
+ }
13805
+ return result;
13373
13806
  }
13374
13807
  });
13375
13808
  var listBackgroundTool = tool({
@@ -13383,7 +13816,39 @@ Useful to check what's in progress before starting new tasks.
13383
13816
  status: tool.schema.enum(["all", "running", "done", "error"]).optional().describe("Filter by status (default: all)")
13384
13817
  },
13385
13818
  async execute(args) {
13386
- return callRustTool("list_background", args);
13819
+ const { status = "all" } = args;
13820
+ let tasks;
13821
+ if (status === "all") {
13822
+ tasks = backgroundTaskManager.getAll();
13823
+ } else {
13824
+ tasks = backgroundTaskManager.getByStatus(status);
13825
+ }
13826
+ if (tasks.length === 0) {
13827
+ return `\u{1F4CB} **No background tasks** ${status !== "all" ? `with status "${status}"` : ""}
13828
+
13829
+ Use \`run_background\` to start a new background task.`;
13830
+ }
13831
+ tasks.sort((a, b) => b.startTime - a.startTime);
13832
+ const rows = tasks.map((task) => {
13833
+ const emoji3 = backgroundTaskManager.getStatusEmoji(task.status);
13834
+ const duration3 = backgroundTaskManager.formatDuration(task);
13835
+ const cmdShort = task.command.length > 25 ? task.command.substring(0, 22) + "..." : task.command;
13836
+ const labelPart = task.label ? ` [${task.label}]` : "";
13837
+ return `| \`${task.id}\` | ${emoji3} ${task.status.padEnd(7)} | ${cmdShort.padEnd(25)}${labelPart} | ${duration3.padStart(8)} |`;
13838
+ }).join("\n");
13839
+ const runningCount = tasks.filter((t) => t.status === "running").length;
13840
+ const doneCount = tasks.filter((t) => t.status === "done").length;
13841
+ const errorCount = tasks.filter((t) => t.status === "error" || t.status === "timeout").length;
13842
+ return `\u{1F4CB} **Background Tasks** (${tasks.length} total)
13843
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
13844
+ | \u23F3 Running: ${runningCount} | \u2705 Done: ${doneCount} | \u274C Error/Timeout: ${errorCount} |
13845
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
13846
+
13847
+ | Task ID | Status | Command | Duration |
13848
+ |---------|--------|---------|----------|
13849
+ ${rows}
13850
+
13851
+ \u{1F4A1} Use \`check_background({ taskId: "job_xxxxx" })\` to see full output.`;
13387
13852
  }
13388
13853
  });
13389
13854
  var killBackgroundTool = tool({
@@ -13393,10 +13858,24 @@ var killBackgroundTool = tool({
13393
13858
  Stop a background task that is taking too long or no longer needed.
13394
13859
  </purpose>`,
13395
13860
  args: {
13396
- task_id: tool.schema.string().describe("Task ID to kill (e.g., job_a1b2c3d4)")
13861
+ taskId: tool.schema.string().describe("Task ID to kill (e.g., job_a1b2c3d4)")
13397
13862
  },
13398
13863
  async execute(args) {
13399
- return callRustTool("kill_background", args);
13864
+ const { taskId } = args;
13865
+ const task = backgroundTaskManager.get(taskId);
13866
+ if (!task) {
13867
+ return `\u274C Task \`${taskId}\` not found.`;
13868
+ }
13869
+ if (task.status !== "running") {
13870
+ return `\u26A0\uFE0F Task \`${taskId}\` is not running (status: ${task.status}).`;
13871
+ }
13872
+ const killed = backgroundTaskManager.kill(taskId);
13873
+ if (killed) {
13874
+ return `\u{1F6D1} Task \`${taskId}\` has been killed.
13875
+ Command: \`${task.command}\`
13876
+ Duration before kill: ${backgroundTaskManager.formatDuration(task)}`;
13877
+ }
13878
+ return `\u26A0\uFE0F Could not kill task \`${taskId}\`. It may have already finished.`;
13400
13879
  }
13401
13880
  });
13402
13881
 
@@ -13922,10 +14401,14 @@ var createDelegateTaskTool = (manager, client) => tool({
13922
14401
  - Auto-cleanup: 5 minutes after completion
13923
14402
  </safety>`,
13924
14403
  args: {
13925
- agent: tool.schema.string().describe("Agent name (e.g., 'builder', 'inspector', 'architect')"),
14404
+ agent: tool.schema.string().describe(
14405
+ `Agent name (e.g., '${AGENT_NAMES.BUILDER}', '${AGENT_NAMES.INSPECTOR}', '${AGENT_NAMES.ARCHITECT}')`
14406
+ ),
13926
14407
  description: tool.schema.string().describe("Short task description"),
13927
14408
  prompt: tool.schema.string().describe("Full prompt/instructions for the agent"),
13928
- background: tool.schema.boolean().describe("true=async (returns task_id), false=sync (waits for result). REQUIRED.")
14409
+ background: tool.schema.boolean().describe(
14410
+ "true=async (returns task_id), false=sync (waits for result). REQUIRED."
14411
+ )
13929
14412
  },
13930
14413
  async execute(args, context) {
13931
14414
  const { agent, description, prompt, background } = args;
@@ -13946,7 +14429,9 @@ var createDelegateTaskTool = (manager, client) => tool({
13946
14429
  });
13947
14430
  const runningCount = manager.getRunningTasks().length;
13948
14431
  const pendingCount = manager.getPendingCount(ctx.sessionID);
13949
- console.log(`[parallel] \u{1F680} SPAWNED ${task.id} \u2192 ${agent}: ${description}`);
14432
+ console.log(
14433
+ `[parallel] \u{1F680} SPAWNED ${task.id} \u2192 ${agent}: ${description}`
14434
+ );
13950
14435
  return `
13951
14436
  ## \u{1F680} BACKGROUND TASK SPAWNED
13952
14437
 
@@ -14025,7 +14510,9 @@ Session ID: ${sessionID}`;
14025
14510
  continue;
14026
14511
  }
14027
14512
  if (Date.now() - startTime < MIN_STABILITY_MS2) continue;
14028
- const messagesResult2 = await session.messages({ path: { id: sessionID } });
14513
+ const messagesResult2 = await session.messages({
14514
+ path: { id: sessionID }
14515
+ });
14029
14516
  const messages2 = messagesResult2.data ?? [];
14030
14517
  const currentMsgCount = messages2.length;
14031
14518
  if (currentMsgCount === lastMsgCount) {
@@ -14036,7 +14523,9 @@ Session ID: ${sessionID}`;
14036
14523
  lastMsgCount = currentMsgCount;
14037
14524
  }
14038
14525
  }
14039
- const messagesResult = await session.messages({ path: { id: sessionID } });
14526
+ const messagesResult = await session.messages({
14527
+ path: { id: sessionID }
14528
+ });
14040
14529
  const messages = messagesResult.data ?? [];
14041
14530
  const assistantMsgs = messages.filter((m) => m.info?.role === "assistant").reverse();
14042
14531
  const lastMsg = assistantMsgs[0];
@@ -14050,7 +14539,9 @@ Session ID: ${sessionID}`;
14050
14539
  ) ?? [];
14051
14540
  const textContent = textParts.map((p) => p.text ?? "").filter(Boolean).join("\n");
14052
14541
  const duration3 = Math.floor((Date.now() - startTime) / 1e3);
14053
- console.log(`[delegate] \u2705 COMPLETED ${agent}: ${description} (${duration3}s)`);
14542
+ console.log(
14543
+ `[delegate] \u2705 COMPLETED ${agent}: ${description} (${duration3}s)`
14544
+ );
14054
14545
  return `\u2705 **Task Completed** (${duration3}s)
14055
14546
 
14056
14547
  Agent: ${agent}
@@ -14085,7 +14576,9 @@ Wait for the "All Complete" notification before checking.
14085
14576
  Use \`list_tasks\` to see available tasks.`;
14086
14577
  }
14087
14578
  if (task.status === "running") {
14088
- const elapsed = Math.floor((Date.now() - task.startedAt.getTime()) / 1e3);
14579
+ const elapsed = Math.floor(
14580
+ (Date.now() - task.startedAt.getTime()) / 1e3
14581
+ );
14089
14582
  return `\u23F3 **Task Still Running**
14090
14583
 
14091
14584
  | Property | Value |
@@ -14167,7 +14660,9 @@ Use \`delegate_task({ ..., background: true })\` to spawn background tasks.`;
14167
14660
  }
14168
14661
  };
14169
14662
  const rows = tasks.map((t) => {
14170
- const elapsed = Math.floor((Date.now() - t.startedAt.getTime()) / 1e3);
14663
+ const elapsed = Math.floor(
14664
+ (Date.now() - t.startedAt.getTime()) / 1e3
14665
+ );
14171
14666
  const desc = t.description.length > 25 ? t.description.slice(0, 22) + "..." : t.description;
14172
14667
  return `| \`${t.id}\` | ${statusIcon(t.status)} ${t.status} | ${t.agent} | ${desc} | ${elapsed}s |`;
14173
14668
  }).join("\n");
@@ -14352,11 +14847,11 @@ var PLUGIN_VERSION = "0.2.4";
14352
14847
  var DEFAULT_MAX_STEPS = 500;
14353
14848
  var TASK_COMMAND_MAX_STEPS = 1e3;
14354
14849
  var AGENT_EMOJI2 = {
14355
- "architect": "\u{1F3D7}\uFE0F",
14356
- "builder": "\u{1F528}",
14357
- "inspector": "\u{1F50D}",
14358
- "recorder": "\u{1F4BE}",
14359
- "commander": "\u{1F3AF}"
14850
+ [AGENT_NAMES.ARCHITECT]: "\u{1F3D7}\uFE0F",
14851
+ [AGENT_NAMES.BUILDER]: "\u{1F528}",
14852
+ [AGENT_NAMES.INSPECTOR]: "\u{1F50D}",
14853
+ [AGENT_NAMES.RECORDER]: "\u{1F4BE}",
14854
+ [AGENT_NAMES.COMMANDER]: "\u{1F3AF}"
14360
14855
  };
14361
14856
  var CONTINUE_INSTRUCTION = `<auto_continue>
14362
14857
  <status>Mission not complete. Keep executing.</status>
@@ -14415,7 +14910,7 @@ var OrchestratorPlugin = async (input) => {
14415
14910
  }
14416
14911
  const orchestratorAgents = {
14417
14912
  Commander: {
14418
- name: "Commander",
14913
+ name: AGENT_NAMES.COMMANDER,
14419
14914
  description: "Autonomous orchestrator - executes until mission complete",
14420
14915
  systemPrompt: AGENTS.commander.systemPrompt
14421
14916
  }
@@ -14435,7 +14930,7 @@ var OrchestratorPlugin = async (input) => {
14435
14930
  const parsed = detectSlashCommand(originalText);
14436
14931
  const sessionID = msgInput.sessionID;
14437
14932
  const agentName = (msgInput.agent || "").toLowerCase();
14438
- if (agentName === "commander" && !sessions.has(sessionID)) {
14933
+ if (agentName === AGENT_NAMES.COMMANDER && !sessions.has(sessionID)) {
14439
14934
  const now = Date.now();
14440
14935
  sessions.set(sessionID, {
14441
14936
  active: true,
@@ -14712,8 +15207,13 @@ ${stateSession.graph.getTaskSummary()}`;
14712
15207
  if (event.type === "session.deleted") {
14713
15208
  const props = event.properties;
14714
15209
  if (props?.info?.id) {
14715
- sessions.delete(props.info.id);
14716
- state.sessions.delete(props.info.id);
15210
+ const sessionID = props.info.id;
15211
+ sessions.delete(sessionID);
15212
+ state.sessions.delete(sessionID);
15213
+ const cleanedCount = backgroundTaskManager.cleanupBySession(sessionID);
15214
+ if (cleanedCount > 0) {
15215
+ console.log(`[background] Cleaned up ${cleanedCount} tasks for deleted session ${sessionID}`);
15216
+ }
14717
15217
  }
14718
15218
  }
14719
15219
  }
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Background Task Tools for OpenCode Orchestrator
3
+ *
4
+ * These tools allow the AI to run commands in the background and check their results later.
5
+ * This is useful for long-running builds, tests, or other operations.
6
+ */
1
7
  export declare const runBackgroundTool: {
2
8
  description: string;
3
9
  args: {
@@ -5,23 +11,25 @@ export declare const runBackgroundTool: {
5
11
  cwd: import("zod").ZodOptional<import("zod").ZodString>;
6
12
  timeout: import("zod").ZodOptional<import("zod").ZodNumber>;
7
13
  label: import("zod").ZodOptional<import("zod").ZodString>;
14
+ sessionID: import("zod").ZodOptional<import("zod").ZodString>;
8
15
  };
9
16
  execute(args: {
10
17
  command: string;
11
18
  cwd?: string | undefined;
12
19
  timeout?: number | undefined;
13
20
  label?: string | undefined;
21
+ sessionID?: string | undefined;
14
22
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
15
23
  };
16
24
  export declare const checkBackgroundTool: {
17
25
  description: string;
18
26
  args: {
19
- task_id: import("zod").ZodString;
20
- tail_lines: import("zod").ZodOptional<import("zod").ZodNumber>;
27
+ taskId: import("zod").ZodString;
28
+ tailLines: import("zod").ZodOptional<import("zod").ZodNumber>;
21
29
  };
22
30
  execute(args: {
23
- task_id: string;
24
- tail_lines?: number | undefined;
31
+ taskId: string;
32
+ tailLines?: number | undefined;
25
33
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
26
34
  };
27
35
  export declare const listBackgroundTool: {
@@ -29,21 +37,21 @@ export declare const listBackgroundTool: {
29
37
  args: {
30
38
  status: import("zod").ZodOptional<import("zod").ZodEnum<{
31
39
  running: "running";
32
- all: "all";
33
40
  done: "done";
34
41
  error: "error";
42
+ all: "all";
35
43
  }>>;
36
44
  };
37
45
  execute(args: {
38
- status?: "running" | "all" | "done" | "error" | undefined;
46
+ status?: "running" | "done" | "error" | "all" | undefined;
39
47
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
40
48
  };
41
49
  export declare const killBackgroundTool: {
42
50
  description: string;
43
51
  args: {
44
- task_id: import("zod").ZodString;
52
+ taskId: import("zod").ZodString;
45
53
  };
46
54
  execute(args: {
47
- task_id: string;
55
+ taskId: string;
48
56
  }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
49
57
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "opencode-orchestrator",
3
3
  "displayName": "OpenCode Orchestrator",
4
4
  "description": "Distributed Cognitive Architecture for OpenCode. Turns simple prompts into specialized multi-agent workflows (Planner, Coder, Reviewer).",
5
- "version": "0.4.9",
5
+ "version": "0.4.13",
6
6
  "author": "agnusdei1207",
7
7
  "license": "MIT",
8
8
  "repository": {