opencode-orchestrator 1.0.5 β 1.0.7
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 +51 -25
- package/dist/agents/prompts/common/index.d.ts +1 -0
- package/dist/agents/prompts/common/modularity.d.ts +6 -0
- package/dist/core/agents/config.d.ts +1 -1
- package/dist/core/agents/manager/task-launcher.d.ts +14 -1
- package/dist/core/agents/manager.d.ts +1 -1
- package/dist/index.js +233 -89
- package/dist/shared/task/constants/parallel-task.d.ts +3 -3
- package/dist/utils/compatibility/claude.d.ts +9 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,33 +66,59 @@ Built for "Infinite Missions," the OpenCode Orchestrator is engineered to handle
|
|
|
66
66
|
## ποΈ Workflow Architecture
|
|
67
67
|
|
|
68
68
|
```
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
β
|
|
79
|
-
β
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
69
|
+
/extreme-mission "Build REST API"
|
|
70
|
+
β
|
|
71
|
+
βββββββββββββββββββββββββββββββββββββββββ
|
|
72
|
+
β π― COMMANDER β Orchestrator β
|
|
73
|
+
β (Main Session - Single) β
|
|
74
|
+
βββββββββββββββββββββ€ββββββββββββββββββββ
|
|
75
|
+
β
|
|
76
|
+
βββββββββββββββββββββΌββββββββββββββββββββ
|
|
77
|
+
β π PLANNER β Create Plan β
|
|
78
|
+
β (Single, Sync Call) β
|
|
79
|
+
β β Outputs: .opencode/todo.md β
|
|
80
|
+
βββββββββββββββββββββ¬ββββββββββββββββββββ
|
|
81
|
+
β
|
|
82
|
+
βββββββββββββββββββββββββββ§ββββββββββββββββββββββββββ
|
|
83
|
+
β π₯ PARALLEL ZONE β
|
|
84
|
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
85
|
+
β β β
|
|
86
|
+
βΌ βΌ βΌ
|
|
87
|
+
ββββββββββββ ββββββββββββ ββββββββββββ
|
|
88
|
+
β π¨ WORKERβ β π¨ WORKERβ β π¨ WORKERβ β Up to 50
|
|
89
|
+
β Task 1 β β Task 2 β β Task 3 β concurrent
|
|
90
|
+
β auth.ts β β api.ts β β db.ts β sessions
|
|
91
|
+
ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ
|
|
92
|
+
β β β
|
|
93
|
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
94
|
+
β β³ SYNC BARRIER β
|
|
95
|
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
96
|
+
β
|
|
97
|
+
βββββββββββββββββββββΌββββββββββββββββββββ
|
|
98
|
+
β β
REVIEWER β Verify All β
|
|
99
|
+
β (Single, Sync Call) β
|
|
100
|
+
β β Tests, Lint, Integration β
|
|
101
|
+
βββββββββββββββββββββ¬ββββββββββββββββββββ
|
|
102
|
+
β
|
|
103
|
+
βββββββββββ΄ββββββββββ
|
|
104
|
+
β All Complete? β
|
|
105
|
+
β Issues = 0? β
|
|
106
|
+
βββββββββββ¬ββββββββββ
|
|
107
|
+
No β β Yes
|
|
108
|
+
β»οΈ LOOP ποΈ MISSION
|
|
109
|
+
(back to SEALED
|
|
110
|
+
Commander)
|
|
94
111
|
```
|
|
95
112
|
|
|
113
|
+
### Execution Model
|
|
114
|
+
|
|
115
|
+
| Phase | Agent | Parallelism | Blocking |
|
|
116
|
+
|:------|:------|:------------|:---------|
|
|
117
|
+
| 1οΈβ£ Plan | Planner | **Single** | Sync (waits) |
|
|
118
|
+
| 2οΈβ£ Execute | Workers | **Parallel** (up to 50) | Async (background) |
|
|
119
|
+
| 3οΈβ£ Verify | Reviewer | **Single** | Sync (waits) |
|
|
120
|
+
| 4οΈβ£ Loop | Commander | **Single** | Coordinates |
|
|
121
|
+
|
|
96
122
|
## Features
|
|
97
123
|
|
|
98
124
|
| Feature | What It Does |
|
|
@@ -12,3 +12,4 @@ export { VERIFICATION_REQUIREMENTS } from "./verification.js";
|
|
|
12
12
|
export { CORE_PHILOSOPHY } from "./core-philosophy.js";
|
|
13
13
|
export { SHARED_LSP_TOOLS } from "./lsp.js";
|
|
14
14
|
export { SHARED_AST_TOOLS } from "./ast.js";
|
|
15
|
+
export { MODULARITY_ENFORCEMENT } from "./modularity.js";
|
|
@@ -15,6 +15,19 @@ export declare class TaskLauncher {
|
|
|
15
15
|
private onTaskError;
|
|
16
16
|
private startPolling;
|
|
17
17
|
constructor(client: OpencodeClient, directory: string, store: TaskStore, concurrency: ConcurrencyController, onTaskError: (taskId: string, error: unknown) => void, startPolling: () => void);
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Unified launch method - handles both single and multiple tasks efficiently.
|
|
20
|
+
* All session creations happen in parallel immediately.
|
|
21
|
+
* Concurrency acquisition and prompt firing happen in the background.
|
|
22
|
+
*/
|
|
23
|
+
launch(inputs: LaunchInput | LaunchInput[]): Promise<ParallelTask | ParallelTask[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Prepare task: Create session and registration without blocking on concurrency
|
|
26
|
+
*/
|
|
27
|
+
private prepareTask;
|
|
28
|
+
/**
|
|
29
|
+
* Background execution: Acquire slot and fire prompt
|
|
30
|
+
*/
|
|
31
|
+
private executeBackground;
|
|
19
32
|
}
|
|
20
33
|
export {};
|
|
@@ -30,7 +30,7 @@ export declare class ParallelAgentManager {
|
|
|
30
30
|
private eventHandler;
|
|
31
31
|
private constructor();
|
|
32
32
|
static getInstance(client?: OpencodeClient, directory?: string): ParallelAgentManager;
|
|
33
|
-
launch(
|
|
33
|
+
launch(inputs: LaunchInput | LaunchInput[]): Promise<ParallelTask | ParallelTask[]>;
|
|
34
34
|
resume(input: ResumeInput): Promise<ParallelTask>;
|
|
35
35
|
getTask(id: string): ParallelTask | undefined;
|
|
36
36
|
getRunningTasks(): ParallelTask[];
|
package/dist/index.js
CHANGED
|
@@ -293,12 +293,18 @@ var PARALLEL_TASK = {
|
|
|
293
293
|
DEFAULT_CONCURRENCY: 3,
|
|
294
294
|
MAX_CONCURRENCY: 10,
|
|
295
295
|
// Sync polling (for delegate_task sync mode)
|
|
296
|
+
// Optimized: Reduced polling frequency while relying more on events
|
|
296
297
|
SYNC_TIMEOUT_MS: 5 * TIME.MINUTE,
|
|
297
|
-
POLL_INTERVAL_MS:
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
298
|
+
POLL_INTERVAL_MS: 2e3,
|
|
299
|
+
// 500 β 2000ms (75% less API calls)
|
|
300
|
+
MIN_IDLE_TIME_MS: 3 * TIME.SECOND,
|
|
301
|
+
// 5s β 3s (faster detection)
|
|
302
|
+
MIN_STABILITY_MS: 2 * TIME.SECOND,
|
|
303
|
+
// 3s β 2s (faster stability)
|
|
304
|
+
STABLE_POLLS_REQUIRED: 2,
|
|
305
|
+
// 3 β 2 (faster completion)
|
|
306
|
+
MAX_POLL_COUNT: 150,
|
|
307
|
+
// 600 β 150 (adjusted for 2s interval)
|
|
302
308
|
// Session naming
|
|
303
309
|
SESSION_TITLE_PREFIX: "Parallel",
|
|
304
310
|
// Labels for output
|
|
@@ -1610,10 +1616,10 @@ function mergeDefs(...defs) {
|
|
|
1610
1616
|
function cloneDef(schema) {
|
|
1611
1617
|
return mergeDefs(schema._zod.def);
|
|
1612
1618
|
}
|
|
1613
|
-
function getElementAtPath(obj,
|
|
1614
|
-
if (!
|
|
1619
|
+
function getElementAtPath(obj, path6) {
|
|
1620
|
+
if (!path6)
|
|
1615
1621
|
return obj;
|
|
1616
|
-
return
|
|
1622
|
+
return path6.reduce((acc, key) => acc?.[key], obj);
|
|
1617
1623
|
}
|
|
1618
1624
|
function promiseAllObject(promisesObj) {
|
|
1619
1625
|
const keys = Object.keys(promisesObj);
|
|
@@ -1974,11 +1980,11 @@ function aborted(x, startIndex = 0) {
|
|
|
1974
1980
|
}
|
|
1975
1981
|
return false;
|
|
1976
1982
|
}
|
|
1977
|
-
function prefixIssues(
|
|
1983
|
+
function prefixIssues(path6, issues) {
|
|
1978
1984
|
return issues.map((iss) => {
|
|
1979
1985
|
var _a;
|
|
1980
1986
|
(_a = iss).path ?? (_a.path = []);
|
|
1981
|
-
iss.path.unshift(
|
|
1987
|
+
iss.path.unshift(path6);
|
|
1982
1988
|
return iss;
|
|
1983
1989
|
});
|
|
1984
1990
|
}
|
|
@@ -2146,7 +2152,7 @@ function treeifyError(error45, _mapper) {
|
|
|
2146
2152
|
return issue2.message;
|
|
2147
2153
|
};
|
|
2148
2154
|
const result = { errors: [] };
|
|
2149
|
-
const processError = (error46,
|
|
2155
|
+
const processError = (error46, path6 = []) => {
|
|
2150
2156
|
var _a, _b;
|
|
2151
2157
|
for (const issue2 of error46.issues) {
|
|
2152
2158
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -2156,7 +2162,7 @@ function treeifyError(error45, _mapper) {
|
|
|
2156
2162
|
} else if (issue2.code === "invalid_element") {
|
|
2157
2163
|
processError({ issues: issue2.issues }, issue2.path);
|
|
2158
2164
|
} else {
|
|
2159
|
-
const fullpath = [...
|
|
2165
|
+
const fullpath = [...path6, ...issue2.path];
|
|
2160
2166
|
if (fullpath.length === 0) {
|
|
2161
2167
|
result.errors.push(mapper(issue2));
|
|
2162
2168
|
continue;
|
|
@@ -2188,8 +2194,8 @@ function treeifyError(error45, _mapper) {
|
|
|
2188
2194
|
}
|
|
2189
2195
|
function toDotPath(_path) {
|
|
2190
2196
|
const segs = [];
|
|
2191
|
-
const
|
|
2192
|
-
for (const seg of
|
|
2197
|
+
const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2198
|
+
for (const seg of path6) {
|
|
2193
2199
|
if (typeof seg === "number")
|
|
2194
2200
|
segs.push(`[${seg}]`);
|
|
2195
2201
|
else if (typeof seg === "symbol")
|
|
@@ -13679,6 +13685,34 @@ var SHARED_AST_TOOLS = `<ast_tools>
|
|
|
13679
13685
|
- Always verify structural changes with \`${TOOL_NAMES.LSP_DIAGNOSTICS}\`.
|
|
13680
13686
|
</ast_tools>`;
|
|
13681
13687
|
|
|
13688
|
+
// src/agents/prompts/common/modularity.ts
|
|
13689
|
+
var MODULARITY_ENFORCEMENT = `${PROMPT_TAGS.QUALITY_CHECKLIST.open}
|
|
13690
|
+
\u{1F3D7}\uFE0F UNIVERSAL ARCHITECTURAL MODULARITY (Language-Agnostic)
|
|
13691
|
+
|
|
13692
|
+
To maintain a scalable and maintainable codebase, follow these structural principles regardless of the programming language:
|
|
13693
|
+
|
|
13694
|
+
### 1. Structural Layering (The "Layer Separation" Rule)
|
|
13695
|
+
Physically separate code based on its functional role. Do not mix these layers in a single file:
|
|
13696
|
+
- **Definitions & Contracts**: Data structures, types, interfaces, or schemas that define the "shape" of data.
|
|
13697
|
+
- **Static Values & Constants**: Hard-coded values, configuration keys, translations, or design tokens.
|
|
13698
|
+
- **Core Implementation**: The active logic, algorithms, functions, or classes that perform the work.
|
|
13699
|
+
- **State & Storage**: Persistent data handling, database interactions, or memory management logic.
|
|
13700
|
+
|
|
13701
|
+
### 2. Folder-Based Encapsulation (Feature-Oriented)
|
|
13702
|
+
- **Group by Domain/Feature**: Instead of grouping by technical type (e.g., all "utils" in one flat folder), create a directory for each meaningful feature or domain.
|
|
13703
|
+
- **Internal Structure**: For any feature complex enough to need multiple files, use a dedicated folder.
|
|
13704
|
+
- **Public Interface**: Each folder should have a clear entry point (e.g., \`index\`, \`mod.rs\`, \`main\`, or exports) that acts as the "Receptionist" for that module, hiding internal complexity.
|
|
13705
|
+
|
|
13706
|
+
### 3. Complexity Sharding
|
|
13707
|
+
If a single unit of code (file or module) starts to handle multiple distinct concerns, **shard it** into a directory:
|
|
13708
|
+
- **High Cohesion**: Keep related code close together within the same folder.
|
|
13709
|
+
- **Low Coupling**: Minimize dependencies between folders. Use "Shared/Common" directories only for truly universal helpers.
|
|
13710
|
+
|
|
13711
|
+
### 4. Code "Mass" Limits
|
|
13712
|
+
- Keep individual files concise and focused on a single responsibility.
|
|
13713
|
+
- If you have to scroll through "screens of code" to find a different type of logic, it belongs in a new file or sub-folder.
|
|
13714
|
+
${PROMPT_TAGS.QUALITY_CHECKLIST.close}`;
|
|
13715
|
+
|
|
13682
13716
|
// src/agents/prompts/commander/role.ts
|
|
13683
13717
|
var COMMANDER_ROLE = `${PROMPT_TAGS.ROLE.open}
|
|
13684
13718
|
You are ${AGENT_NAMES.COMMANDER}. Autonomous mission controller.
|
|
@@ -15295,6 +15329,7 @@ var commander = {
|
|
|
15295
15329
|
// src/agents/subagents/planner.ts
|
|
15296
15330
|
var systemPrompt2 = [
|
|
15297
15331
|
PLANNER_ROLE,
|
|
15332
|
+
MODULARITY_ENFORCEMENT,
|
|
15298
15333
|
PLANNER_FORBIDDEN,
|
|
15299
15334
|
PLANNER_REQUIRED,
|
|
15300
15335
|
ENVIRONMENT_DISCOVERY,
|
|
@@ -15320,6 +15355,7 @@ var planner = {
|
|
|
15320
15355
|
// src/agents/subagents/worker.ts
|
|
15321
15356
|
var systemPrompt3 = [
|
|
15322
15357
|
WORKER_ROLE,
|
|
15358
|
+
MODULARITY_ENFORCEMENT,
|
|
15323
15359
|
WORKER_FORBIDDEN,
|
|
15324
15360
|
WORKER_REQUIRED,
|
|
15325
15361
|
ANTI_HALLUCINATION_CORE,
|
|
@@ -15347,6 +15383,7 @@ var worker = {
|
|
|
15347
15383
|
// src/agents/subagents/reviewer.ts
|
|
15348
15384
|
var systemPrompt4 = [
|
|
15349
15385
|
REVIEWER_ROLE,
|
|
15386
|
+
MODULARITY_ENFORCEMENT,
|
|
15350
15387
|
REVIEWER_FORBIDDEN,
|
|
15351
15388
|
REVIEWER_REQUIRED,
|
|
15352
15389
|
REVIEWER_VERIFICATION,
|
|
@@ -16229,7 +16266,7 @@ var TaskStore = class {
|
|
|
16229
16266
|
return Array.from(this.tasks.values());
|
|
16230
16267
|
}
|
|
16231
16268
|
getRunning() {
|
|
16232
|
-
return this.getAll().filter((t) => t.status === TASK_STATUS.RUNNING);
|
|
16269
|
+
return this.getAll().filter((t) => t.status === TASK_STATUS.RUNNING || t.status === TASK_STATUS.PENDING);
|
|
16233
16270
|
}
|
|
16234
16271
|
getByParent(parentSessionID) {
|
|
16235
16272
|
return this.getAll().filter((t) => t.parentSessionID === parentSessionID);
|
|
@@ -16948,74 +16985,111 @@ var TaskLauncher = class {
|
|
|
16948
16985
|
this.onTaskError = onTaskError;
|
|
16949
16986
|
this.startPolling = startPolling;
|
|
16950
16987
|
}
|
|
16951
|
-
|
|
16952
|
-
|
|
16953
|
-
|
|
16954
|
-
|
|
16955
|
-
|
|
16956
|
-
|
|
16957
|
-
|
|
16958
|
-
|
|
16959
|
-
|
|
16988
|
+
/**
|
|
16989
|
+
* Unified launch method - handles both single and multiple tasks efficiently.
|
|
16990
|
+
* All session creations happen in parallel immediately.
|
|
16991
|
+
* Concurrency acquisition and prompt firing happen in the background.
|
|
16992
|
+
*/
|
|
16993
|
+
async launch(inputs) {
|
|
16994
|
+
const isArray = Array.isArray(inputs);
|
|
16995
|
+
const taskInputs = isArray ? inputs : [inputs];
|
|
16996
|
+
if (taskInputs.length === 0) return isArray ? [] : null;
|
|
16997
|
+
log(`[task-launcher.ts] Batch launching ${taskInputs.length} task(s)`);
|
|
16998
|
+
const startTime = Date.now();
|
|
16999
|
+
const tasks = await Promise.all(taskInputs.map(
|
|
17000
|
+
(input) => this.prepareTask(input).catch((error45) => {
|
|
17001
|
+
log(`[task-launcher.ts] Failed to prepare task ${input.description}:`, error45);
|
|
17002
|
+
return null;
|
|
17003
|
+
})
|
|
17004
|
+
));
|
|
17005
|
+
const successfulTasks = tasks.filter((t) => t !== null);
|
|
17006
|
+
successfulTasks.forEach((task) => {
|
|
17007
|
+
this.executeBackground(task).catch((error45) => {
|
|
17008
|
+
log(`[task-launcher.ts] Background execution failed for ${task.id}:`, error45);
|
|
17009
|
+
this.onTaskError(task.id, error45);
|
|
16960
17010
|
});
|
|
16961
|
-
|
|
16962
|
-
|
|
16963
|
-
|
|
16964
|
-
|
|
16965
|
-
|
|
16966
|
-
|
|
16967
|
-
|
|
16968
|
-
|
|
16969
|
-
|
|
17011
|
+
});
|
|
17012
|
+
const elapsed = Date.now() - startTime;
|
|
17013
|
+
log(`[task-launcher.ts] Batch launch prepared: ${successfulTasks.length} tasks in ${elapsed}ms`);
|
|
17014
|
+
if (successfulTasks.length > 0) {
|
|
17015
|
+
this.startPolling();
|
|
17016
|
+
}
|
|
17017
|
+
return isArray ? successfulTasks : successfulTasks[0] || null;
|
|
17018
|
+
}
|
|
17019
|
+
/**
|
|
17020
|
+
* Prepare task: Create session and registration without blocking on concurrency
|
|
17021
|
+
*/
|
|
17022
|
+
async prepareTask(input) {
|
|
17023
|
+
const createResult = await this.client.session.create({
|
|
17024
|
+
body: {
|
|
17025
|
+
parentID: input.parentSessionID,
|
|
17026
|
+
title: `${PARALLEL_TASK.SESSION_TITLE_PREFIX}: ${input.description}`
|
|
17027
|
+
},
|
|
17028
|
+
query: { directory: this.directory }
|
|
17029
|
+
});
|
|
17030
|
+
if (createResult.error || !createResult.data?.id) {
|
|
17031
|
+
throw new Error(`Session creation failed: ${createResult.error || "No ID"}`);
|
|
17032
|
+
}
|
|
17033
|
+
const sessionID = createResult.data.id;
|
|
17034
|
+
const taskId = `${ID_PREFIX.TASK}${crypto.randomUUID().slice(0, 8)}`;
|
|
17035
|
+
const task = {
|
|
17036
|
+
id: taskId,
|
|
17037
|
+
sessionID,
|
|
17038
|
+
parentSessionID: input.parentSessionID,
|
|
17039
|
+
description: input.description,
|
|
17040
|
+
prompt: input.prompt,
|
|
17041
|
+
agent: input.agent,
|
|
17042
|
+
status: TASK_STATUS.PENDING,
|
|
17043
|
+
// Start as PENDING
|
|
17044
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
17045
|
+
concurrencyKey: input.agent,
|
|
17046
|
+
depth: (input.depth ?? 0) + 1
|
|
17047
|
+
};
|
|
17048
|
+
this.store.set(taskId, task);
|
|
17049
|
+
this.store.trackPending(input.parentSessionID, taskId);
|
|
17050
|
+
taskWAL.log(WAL_ACTIONS.LAUNCH, task).catch(() => {
|
|
17051
|
+
});
|
|
17052
|
+
const toastManager = getTaskToastManager();
|
|
17053
|
+
if (toastManager) {
|
|
17054
|
+
toastManager.addTask({
|
|
16970
17055
|
id: taskId,
|
|
16971
|
-
sessionID,
|
|
16972
|
-
parentSessionID: input.parentSessionID,
|
|
16973
17056
|
description: input.description,
|
|
16974
|
-
prompt: input.prompt,
|
|
16975
17057
|
agent: input.agent,
|
|
16976
|
-
|
|
16977
|
-
|
|
16978
|
-
|
|
16979
|
-
|
|
16980
|
-
|
|
16981
|
-
|
|
16982
|
-
|
|
17058
|
+
isBackground: true,
|
|
17059
|
+
parentSessionID: input.parentSessionID,
|
|
17060
|
+
sessionID
|
|
17061
|
+
});
|
|
17062
|
+
}
|
|
17063
|
+
presets.sessionCreated(sessionID, input.agent);
|
|
17064
|
+
return task;
|
|
17065
|
+
}
|
|
17066
|
+
/**
|
|
17067
|
+
* Background execution: Acquire slot and fire prompt
|
|
17068
|
+
*/
|
|
17069
|
+
async executeBackground(task) {
|
|
17070
|
+
try {
|
|
17071
|
+
await this.concurrency.acquire(task.agent);
|
|
17072
|
+
task.status = TASK_STATUS.RUNNING;
|
|
17073
|
+
task.startedAt = /* @__PURE__ */ new Date();
|
|
17074
|
+
this.store.set(task.id, task);
|
|
16983
17075
|
taskWAL.log(WAL_ACTIONS.LAUNCH, task).catch(() => {
|
|
16984
17076
|
});
|
|
16985
|
-
this.
|
|
16986
|
-
|
|
16987
|
-
path: { id: sessionID },
|
|
17077
|
+
await this.client.session.prompt({
|
|
17078
|
+
path: { id: task.sessionID },
|
|
16988
17079
|
body: {
|
|
16989
|
-
agent:
|
|
17080
|
+
agent: task.agent,
|
|
16990
17081
|
tools: {
|
|
16991
|
-
// Prevent recursive task spawning from subagents
|
|
16992
17082
|
delegate_task: false,
|
|
16993
17083
|
get_task_result: false,
|
|
16994
17084
|
list_tasks: false,
|
|
16995
17085
|
cancel_task: false
|
|
16996
17086
|
},
|
|
16997
|
-
parts: [{ type: PART_TYPES.TEXT, text:
|
|
17087
|
+
parts: [{ type: PART_TYPES.TEXT, text: task.prompt }]
|
|
16998
17088
|
}
|
|
16999
|
-
}).catch((error45) => {
|
|
17000
|
-
log(`Prompt error for ${taskId}:`, error45);
|
|
17001
|
-
this.onTaskError(taskId, error45);
|
|
17002
17089
|
});
|
|
17003
|
-
|
|
17004
|
-
if (toastManager) {
|
|
17005
|
-
toastManager.addTask({
|
|
17006
|
-
id: taskId,
|
|
17007
|
-
description: input.description,
|
|
17008
|
-
agent: input.agent,
|
|
17009
|
-
isBackground: true,
|
|
17010
|
-
parentSessionID: input.parentSessionID,
|
|
17011
|
-
sessionID
|
|
17012
|
-
});
|
|
17013
|
-
}
|
|
17014
|
-
presets.sessionCreated(sessionID, input.agent);
|
|
17015
|
-
log(`Launched ${taskId} in session ${sessionID}`);
|
|
17016
|
-
return task;
|
|
17090
|
+
log(`[task-launcher.ts] Task ${task.id} (${task.agent}) started running`);
|
|
17017
17091
|
} catch (error45) {
|
|
17018
|
-
this.concurrency.release(
|
|
17092
|
+
this.concurrency.release(task.agent);
|
|
17019
17093
|
throw error45;
|
|
17020
17094
|
}
|
|
17021
17095
|
}
|
|
@@ -17116,6 +17190,7 @@ var TaskPoller = class {
|
|
|
17116
17190
|
const allStatuses = statusResult.data ?? {};
|
|
17117
17191
|
for (const task of running) {
|
|
17118
17192
|
try {
|
|
17193
|
+
if (task.status === TASK_STATUS.PENDING) continue;
|
|
17119
17194
|
const sessionStatus = allStatuses[task.sessionID];
|
|
17120
17195
|
if (sessionStatus?.type === SESSION_STATUS.IDLE) {
|
|
17121
17196
|
const elapsed2 = Date.now() - task.startedAt.getTime();
|
|
@@ -17468,9 +17543,9 @@ var ParallelAgentManager = class _ParallelAgentManager {
|
|
|
17468
17543
|
// ========================================================================
|
|
17469
17544
|
// Public API
|
|
17470
17545
|
// ========================================================================
|
|
17471
|
-
async launch(
|
|
17546
|
+
async launch(inputs) {
|
|
17472
17547
|
this.cleaner.pruneExpiredTasks();
|
|
17473
|
-
return this.launcher.launch(
|
|
17548
|
+
return this.launcher.launch(inputs);
|
|
17474
17549
|
}
|
|
17475
17550
|
async resume(input) {
|
|
17476
17551
|
return this.resumer.resume(input);
|
|
@@ -17761,15 +17836,28 @@ var createDelegateTaskTool = (manager, client) => tool({
|
|
|
17761
17836
|
}
|
|
17762
17837
|
if (resume) {
|
|
17763
17838
|
try {
|
|
17764
|
-
const
|
|
17839
|
+
const input = {
|
|
17765
17840
|
sessionId: resume,
|
|
17766
17841
|
prompt,
|
|
17767
|
-
parentSessionID: ctx.sessionID
|
|
17768
|
-
|
|
17842
|
+
parentSessionID: ctx.sessionID,
|
|
17843
|
+
agent,
|
|
17844
|
+
// Assuming agent is needed for resume context
|
|
17845
|
+
description
|
|
17846
|
+
// Assuming description is needed for resume context
|
|
17847
|
+
};
|
|
17848
|
+
const launchResult = await manager.launch(input);
|
|
17849
|
+
const task = Array.isArray(launchResult) ? launchResult[0] : launchResult;
|
|
17850
|
+
if (!task) {
|
|
17851
|
+
return `Failed to launch task: ${input.description}`;
|
|
17852
|
+
}
|
|
17853
|
+
const taskId = task.id;
|
|
17769
17854
|
if (background === true) {
|
|
17770
|
-
|
|
17855
|
+
const message = `Launched ${input.agent} task: ${input.description}
|
|
17856
|
+
Task ID: ${taskId}
|
|
17857
|
+
Session: ${task.sessionID}`;
|
|
17858
|
+
return `${OUTPUT_LABEL.RESUME} task: \`${taskId}\` (${task.agent}) in session \`${task.sessionID}\`
|
|
17771
17859
|
|
|
17772
|
-
Previous context preserved. Use \`get_task_result({ taskId: "${
|
|
17860
|
+
Previous context preserved. Use \`get_task_result({ taskId: "${taskId}" })\` when complete.`;
|
|
17773
17861
|
}
|
|
17774
17862
|
const startTime = Date.now();
|
|
17775
17863
|
const session = sessionClient.session;
|
|
@@ -17789,12 +17877,13 @@ ${text || "(No output)"}`;
|
|
|
17789
17877
|
}
|
|
17790
17878
|
if (background === true) {
|
|
17791
17879
|
try {
|
|
17792
|
-
const
|
|
17880
|
+
const launchResult = await manager.launch({
|
|
17793
17881
|
agent,
|
|
17794
17882
|
description,
|
|
17795
17883
|
prompt,
|
|
17796
17884
|
parentSessionID: ctx.sessionID
|
|
17797
17885
|
});
|
|
17886
|
+
const task = Array.isArray(launchResult) ? launchResult[0] : launchResult;
|
|
17798
17887
|
presets.taskStarted(task.id, agent);
|
|
17799
17888
|
return `${OUTPUT_LABEL.SPAWNED} task: \`${task.id}\` (${agent})
|
|
17800
17889
|
Session: \`${task.sessionID}\` (save for resume)`;
|
|
@@ -18841,10 +18930,10 @@ async function resolveCommandPath(key, commandName) {
|
|
|
18841
18930
|
const currentPending = pending.get(key);
|
|
18842
18931
|
if (currentPending) return currentPending;
|
|
18843
18932
|
const promise2 = (async () => {
|
|
18844
|
-
const
|
|
18845
|
-
cache[key] =
|
|
18933
|
+
const path6 = await findCommand(commandName);
|
|
18934
|
+
cache[key] = path6;
|
|
18846
18935
|
pending.delete(key);
|
|
18847
|
-
return
|
|
18936
|
+
return path6;
|
|
18848
18937
|
})();
|
|
18849
18938
|
pending.set(key, promise2);
|
|
18850
18939
|
return promise2;
|
|
@@ -18903,21 +18992,21 @@ import { exec as exec2 } from "node:child_process";
|
|
|
18903
18992
|
import { promisify as promisify2 } from "node:util";
|
|
18904
18993
|
var execAsync2 = promisify2(exec2);
|
|
18905
18994
|
async function notifyDarwin(title, message) {
|
|
18906
|
-
const
|
|
18995
|
+
const path6 = await resolveCommandPath(
|
|
18907
18996
|
NOTIFICATION_COMMAND_KEYS.OSASCRIPT,
|
|
18908
18997
|
NOTIFICATION_COMMANDS.OSASCRIPT
|
|
18909
18998
|
);
|
|
18910
|
-
if (!
|
|
18999
|
+
if (!path6) return;
|
|
18911
19000
|
const escT = title.replace(/"/g, '\\"');
|
|
18912
19001
|
const escM = message.replace(/"/g, '\\"');
|
|
18913
|
-
await execAsync2(`${
|
|
19002
|
+
await execAsync2(`${path6} -e 'display notification "${escM}" with title "${escT}" sound name "Glass"'`);
|
|
18914
19003
|
}
|
|
18915
19004
|
async function notifyLinux(title, message) {
|
|
18916
|
-
const
|
|
19005
|
+
const path6 = await resolveCommandPath(
|
|
18917
19006
|
NOTIFICATION_COMMAND_KEYS.NOTIFY_SEND,
|
|
18918
19007
|
NOTIFICATION_COMMANDS.NOTIFY_SEND
|
|
18919
19008
|
);
|
|
18920
|
-
if (
|
|
19009
|
+
if (path6) await execAsync2(`${path6} "${title}" "${message}" 2>/dev/null`);
|
|
18921
19010
|
}
|
|
18922
19011
|
async function notifyWindows(title, message) {
|
|
18923
19012
|
const ps = await resolveCommandPath(
|
|
@@ -18963,11 +19052,11 @@ import { exec as exec3 } from "node:child_process";
|
|
|
18963
19052
|
async function playDarwin(soundPath) {
|
|
18964
19053
|
if (!soundPath) return;
|
|
18965
19054
|
try {
|
|
18966
|
-
const
|
|
19055
|
+
const path6 = await resolveCommandPath(
|
|
18967
19056
|
NOTIFICATION_COMMAND_KEYS.AFPLAY,
|
|
18968
19057
|
NOTIFICATION_COMMANDS.AFPLAY
|
|
18969
19058
|
);
|
|
18970
|
-
if (
|
|
19059
|
+
if (path6) exec3(`"${path6}" "${soundPath}"`);
|
|
18971
19060
|
} catch (err) {
|
|
18972
19061
|
log(`[session-notify] Error playing sound (Darwin): ${err}`);
|
|
18973
19062
|
}
|
|
@@ -20237,10 +20326,65 @@ function createEventHandler(ctx) {
|
|
|
20237
20326
|
};
|
|
20238
20327
|
}
|
|
20239
20328
|
|
|
20329
|
+
// src/utils/compatibility/claude.ts
|
|
20330
|
+
import fs6 from "fs";
|
|
20331
|
+
import path5 from "path";
|
|
20332
|
+
function findClaudeRules(startDir = process.cwd()) {
|
|
20333
|
+
try {
|
|
20334
|
+
let currentDir = startDir;
|
|
20335
|
+
const root = path5.parse(startDir).root;
|
|
20336
|
+
while (true) {
|
|
20337
|
+
const claudeMdPath = path5.join(currentDir, "CLAUDE.md");
|
|
20338
|
+
if (fs6.existsSync(claudeMdPath)) {
|
|
20339
|
+
try {
|
|
20340
|
+
const content = fs6.readFileSync(claudeMdPath, "utf-8");
|
|
20341
|
+
log(`[compatibility] Loaded CLAUDE.md from ${claudeMdPath}`);
|
|
20342
|
+
return formatRules("CLAUDE.md", content);
|
|
20343
|
+
} catch (e) {
|
|
20344
|
+
log(`[compatibility] Error reading CLAUDE.md: ${e}`);
|
|
20345
|
+
}
|
|
20346
|
+
}
|
|
20347
|
+
if (currentDir === root) break;
|
|
20348
|
+
currentDir = path5.dirname(currentDir);
|
|
20349
|
+
}
|
|
20350
|
+
const copilotPath = path5.join(startDir, ".github", "copilot-instructions.md");
|
|
20351
|
+
if (fs6.existsSync(copilotPath)) {
|
|
20352
|
+
return formatRules("Copilot Instructions", fs6.readFileSync(copilotPath, "utf-8"));
|
|
20353
|
+
}
|
|
20354
|
+
return null;
|
|
20355
|
+
} catch (error45) {
|
|
20356
|
+
log(`[compatibility] Error finding Claude rules: ${error45}`);
|
|
20357
|
+
return null;
|
|
20358
|
+
}
|
|
20359
|
+
}
|
|
20360
|
+
function formatRules(source, content) {
|
|
20361
|
+
return `
|
|
20362
|
+
<project_rules source="${source}">
|
|
20363
|
+
${content}
|
|
20364
|
+
</project_rules>
|
|
20365
|
+
|
|
20366
|
+
<claude_compatibility>
|
|
20367
|
+
These rules are from the project's ${source}.
|
|
20368
|
+
You MUST follow them as strictly as if they were your system prompt.
|
|
20369
|
+
This plugin runs in "Claude Code Compatibility Mode".
|
|
20370
|
+
</claude_compatibility>
|
|
20371
|
+
`;
|
|
20372
|
+
}
|
|
20373
|
+
|
|
20240
20374
|
// src/plugin-handlers/config-handler.ts
|
|
20241
20375
|
function createConfigHandler() {
|
|
20242
|
-
const commanderPrompt = AGENTS[AGENT_NAMES.COMMANDER]?.systemPrompt || "";
|
|
20243
20376
|
return async (config2) => {
|
|
20377
|
+
const claudeRules = findClaudeRules();
|
|
20378
|
+
const injectRules = (prompt) => {
|
|
20379
|
+
if (!claudeRules) return prompt;
|
|
20380
|
+
return `${prompt}
|
|
20381
|
+
|
|
20382
|
+
${claudeRules}`;
|
|
20383
|
+
};
|
|
20384
|
+
const commanderPrompt = injectRules(AGENTS[AGENT_NAMES.COMMANDER]?.systemPrompt || "");
|
|
20385
|
+
const plannerPrompt = injectRules(AGENTS[AGENT_NAMES.PLANNER]?.systemPrompt || "");
|
|
20386
|
+
const workerPrompt = injectRules(AGENTS[AGENT_NAMES.WORKER]?.systemPrompt || "");
|
|
20387
|
+
const reviewerPrompt = injectRules(AGENTS[AGENT_NAMES.REVIEWER]?.systemPrompt || "");
|
|
20244
20388
|
const existingCommands = config2.command ?? {};
|
|
20245
20389
|
const existingAgents = config2.agent ?? {};
|
|
20246
20390
|
const orchestratorCommands = {};
|
|
@@ -20266,7 +20410,7 @@ function createConfigHandler() {
|
|
|
20266
20410
|
description: "Strategic planning and research specialist",
|
|
20267
20411
|
mode: "subagent",
|
|
20268
20412
|
hidden: true,
|
|
20269
|
-
prompt:
|
|
20413
|
+
prompt: plannerPrompt,
|
|
20270
20414
|
maxTokens: AGENT_TOKENS.SUBAGENT_MAX_TOKENS,
|
|
20271
20415
|
color: "#9B59B6"
|
|
20272
20416
|
},
|
|
@@ -20274,7 +20418,7 @@ function createConfigHandler() {
|
|
|
20274
20418
|
description: "Implementation and documentation specialist",
|
|
20275
20419
|
mode: "subagent",
|
|
20276
20420
|
hidden: true,
|
|
20277
|
-
prompt:
|
|
20421
|
+
prompt: workerPrompt,
|
|
20278
20422
|
maxTokens: AGENT_TOKENS.SUBAGENT_MAX_TOKENS,
|
|
20279
20423
|
color: "#E67E22"
|
|
20280
20424
|
},
|
|
@@ -20282,7 +20426,7 @@ function createConfigHandler() {
|
|
|
20282
20426
|
description: "Verification and context management specialist",
|
|
20283
20427
|
mode: "subagent",
|
|
20284
20428
|
hidden: true,
|
|
20285
|
-
prompt:
|
|
20429
|
+
prompt: reviewerPrompt,
|
|
20286
20430
|
maxTokens: AGENT_TOKENS.SUBAGENT_MAX_TOKENS,
|
|
20287
20431
|
color: "#27AE60"
|
|
20288
20432
|
}
|
|
@@ -8,11 +8,11 @@ export declare const PARALLEL_TASK: {
|
|
|
8
8
|
readonly DEFAULT_CONCURRENCY: 3;
|
|
9
9
|
readonly MAX_CONCURRENCY: 10;
|
|
10
10
|
readonly SYNC_TIMEOUT_MS: number;
|
|
11
|
-
readonly POLL_INTERVAL_MS:
|
|
11
|
+
readonly POLL_INTERVAL_MS: 2000;
|
|
12
12
|
readonly MIN_IDLE_TIME_MS: number;
|
|
13
13
|
readonly MIN_STABILITY_MS: number;
|
|
14
|
-
readonly STABLE_POLLS_REQUIRED:
|
|
15
|
-
readonly MAX_POLL_COUNT:
|
|
14
|
+
readonly STABLE_POLLS_REQUIRED: 2;
|
|
15
|
+
readonly MAX_POLL_COUNT: 150;
|
|
16
16
|
readonly SESSION_TITLE_PREFIX: "Parallel";
|
|
17
17
|
readonly LABEL: "parallel";
|
|
18
18
|
readonly GROUP_PREFIX: "parallel:";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find and read CLAUDE.md guidelines.
|
|
3
|
+
* Follows Claude Code's discovery logic:
|
|
4
|
+
* 1. CLAUDE.md in current directory or parents
|
|
5
|
+
* 2. .github/instructions/ *.md (checking main ones)
|
|
6
|
+
* 3. .cursor/rules/ *.md
|
|
7
|
+
* 4. .claude/rules/ *.md
|
|
8
|
+
*/
|
|
9
|
+
export declare function findClaudeRules(startDir?: string): string | null;
|
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": "1.0.
|
|
5
|
+
"version": "1.0.7",
|
|
6
6
|
"author": "agnusdei1207",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|