opencode-orchestrator 0.4.19 → 0.4.21
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 +61 -3
- package/dist/agents/definitions.d.ts +0 -0
- package/dist/agents/orchestrator.d.ts +0 -0
- package/dist/agents/subagents/architect.d.ts +0 -0
- package/dist/agents/subagents/builder.d.ts +0 -0
- package/dist/agents/subagents/inspector.d.ts +0 -0
- package/dist/agents/subagents/recorder.d.ts +0 -0
- package/dist/core/async-agent.d.ts +14 -2
- package/dist/core/state.d.ts +0 -0
- package/dist/core/tasks.d.ts +1 -0
- package/dist/index.d.ts +20 -2
- package/dist/index.js +1497 -164
- package/dist/shared/contracts/interfaces.d.ts +0 -0
- package/dist/shared/contracts/names.d.ts +0 -0
- package/dist/tools/background.d.ts +2 -2
- package/dist/tools/callAgent.d.ts +0 -0
- package/dist/tools/rust.d.ts +0 -0
- package/dist/tools/search.d.ts +0 -0
- package/dist/tools/slashCommand.d.ts +0 -0
- package/dist/utils/binary.d.ts +0 -0
- package/dist/utils/common.d.ts +0 -0
- package/dist/utils/sanity.d.ts +0 -22
- package/package.json +6 -4
- package/dist/agents/coder.d.ts +0 -2
- package/dist/agents/fixer.d.ts +0 -2
- package/dist/agents/names.d.ts +0 -12
- package/dist/agents/planner.d.ts +0 -2
- package/dist/agents/reviewer.d.ts +0 -2
- package/dist/agents/searcher.d.ts +0 -2
- package/dist/agents/subagents/coder.d.ts +0 -2
- package/dist/agents/subagents/executor.d.ts +0 -2
- package/dist/agents/subagents/fixer.d.ts +0 -2
- package/dist/agents/subagents/memory.d.ts +0 -2
- package/dist/agents/subagents/planner.d.ts +0 -2
- package/dist/agents/subagents/publisher.d.ts +0 -2
- package/dist/agents/subagents/reviewer.d.ts +0 -2
- package/dist/agents/subagents/searcher.d.ts +0 -2
- package/dist/agents/subagents/strategist.d.ts +0 -2
- package/dist/agents/subagents/surgeon.d.ts +0 -2
- package/dist/agents/subagents/types.d.ts +0 -7
- package/dist/agents/subagents/visualist.d.ts +0 -2
- package/dist/agents/types.d.ts +0 -7
- package/dist/cli.d.ts +0 -2
- package/dist/tasks.d.ts +0 -29
package/dist/index.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
2
8
|
var __export = (target, all) => {
|
|
3
9
|
for (var name in all)
|
|
4
10
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -19,6 +25,159 @@ var AGENT_NAMES = {
|
|
|
19
25
|
// Persistent context - saves/loads session state
|
|
20
26
|
};
|
|
21
27
|
|
|
28
|
+
// src/constants/prompts.ts
|
|
29
|
+
var REASONING_CONSTRAINTS = `
|
|
30
|
+
<constraints>
|
|
31
|
+
1. Reasoning MUST be in English for maximum stability
|
|
32
|
+
2. If reasoning collapses into gibberish, stop and output: "ERROR: REASONING_COLLAPSE"
|
|
33
|
+
3. Never suppress type errors with 'as any', '@ts-ignore', '@ts-expect-error'
|
|
34
|
+
4. Never leave code in broken state
|
|
35
|
+
5. Always verify with evidence before claiming completion
|
|
36
|
+
</constraints>
|
|
37
|
+
`;
|
|
38
|
+
var LANGUAGE_RULE = `
|
|
39
|
+
<language_rule>
|
|
40
|
+
THINK and REASON in English for maximum model stability.
|
|
41
|
+
|
|
42
|
+
FINAL RESPONSE LANGUAGE:
|
|
43
|
+
- Detect user's language from their request
|
|
44
|
+
- Respond in SAME language
|
|
45
|
+
- Korean \u2192 Korean, English \u2192 English, Japanese \u2192 Japanese, Chinese \u2192 Chinese
|
|
46
|
+
- Default to English if unclear
|
|
47
|
+
</language_rule>
|
|
48
|
+
`;
|
|
49
|
+
var ANTI_PATTERNS = `
|
|
50
|
+
<anti_patterns>
|
|
51
|
+
\u274C Delegate without environment/codebase context
|
|
52
|
+
\u274C Leave code broken or with LSP errors
|
|
53
|
+
\u274C Make random changes without understanding root cause
|
|
54
|
+
\u274C Use 'as any', '@ts-ignore', or '@ts-expect-error'
|
|
55
|
+
\u274C Suppress errors instead of fixing them
|
|
56
|
+
</anti_patterns>
|
|
57
|
+
`;
|
|
58
|
+
var WORKFLOW = `
|
|
59
|
+
<workflow>
|
|
60
|
+
1. THINK - Reason about the task
|
|
61
|
+
2. ACT - Execute the work
|
|
62
|
+
3. OBSERVE - Check the result
|
|
63
|
+
4. ADJUST - Fix if needed
|
|
64
|
+
5. VERIFY - Prove success with evidence
|
|
65
|
+
</workflow>
|
|
66
|
+
`;
|
|
67
|
+
var BASE_PROMPT = `
|
|
68
|
+
${REASONING_CONSTRAINTS}
|
|
69
|
+
|
|
70
|
+
${LANGUAGE_RULE}
|
|
71
|
+
|
|
72
|
+
${ANTI_PATTERNS}
|
|
73
|
+
|
|
74
|
+
${WORKFLOW}
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
// src/utils/sanity.ts
|
|
78
|
+
function checkOutputSanity(text) {
|
|
79
|
+
if (!text || text.length < 50) {
|
|
80
|
+
return { isHealthy: true, severity: "ok" };
|
|
81
|
+
}
|
|
82
|
+
if (/(.)\1{15,}/.test(text)) {
|
|
83
|
+
return {
|
|
84
|
+
isHealthy: false,
|
|
85
|
+
reason: "Single character repetition detected",
|
|
86
|
+
severity: "critical"
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (/(.{2,6})\1{8,}/.test(text)) {
|
|
90
|
+
return {
|
|
91
|
+
isHealthy: false,
|
|
92
|
+
reason: "Pattern loop detected",
|
|
93
|
+
severity: "critical"
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (text.length > 200) {
|
|
97
|
+
const cleanText = text.replace(/\s/g, "");
|
|
98
|
+
if (cleanText.length > 100) {
|
|
99
|
+
const uniqueChars = new Set(cleanText).size;
|
|
100
|
+
const ratio = uniqueChars / cleanText.length;
|
|
101
|
+
if (ratio < 0.02) {
|
|
102
|
+
return {
|
|
103
|
+
isHealthy: false,
|
|
104
|
+
reason: "Low information density",
|
|
105
|
+
severity: "critical"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const boxChars = (text.match(/[\u2500-\u257f\u2580-\u259f\u2800-\u28ff]/g) || []).length;
|
|
111
|
+
if (boxChars > 100 && boxChars / text.length > 0.3) {
|
|
112
|
+
return {
|
|
113
|
+
isHealthy: false,
|
|
114
|
+
reason: "Visual gibberish detected",
|
|
115
|
+
severity: "critical"
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const lines = text.split("\n").filter((l) => l.trim().length > 10);
|
|
119
|
+
if (lines.length > 10) {
|
|
120
|
+
const lineSet = new Set(lines);
|
|
121
|
+
if (lineSet.size < lines.length * 0.2) {
|
|
122
|
+
return {
|
|
123
|
+
isHealthy: false,
|
|
124
|
+
reason: "Excessive line repetition",
|
|
125
|
+
severity: "warning"
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const cjkChars = (text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []).length;
|
|
130
|
+
if (cjkChars > 200) {
|
|
131
|
+
const uniqueCjk = new Set(
|
|
132
|
+
text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []
|
|
133
|
+
).size;
|
|
134
|
+
if (uniqueCjk < 10 && cjkChars / uniqueCjk > 20) {
|
|
135
|
+
return {
|
|
136
|
+
isHealthy: false,
|
|
137
|
+
reason: "CJK character spam detected",
|
|
138
|
+
severity: "critical"
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { isHealthy: true, severity: "ok" };
|
|
143
|
+
}
|
|
144
|
+
var RECOVERY_PROMPT = `<anomaly_recovery>
|
|
145
|
+
\u26A0\uFE0F SYSTEM NOTICE: Previous output was malformed (gibberish/loop detected).
|
|
146
|
+
|
|
147
|
+
<recovery_protocol>
|
|
148
|
+
1. DISCARD the corrupted output completely - do not reference it
|
|
149
|
+
2. RECALL the original mission objective
|
|
150
|
+
3. IDENTIFY the last confirmed successful step
|
|
151
|
+
4. RESTART with a simpler, more focused approach
|
|
152
|
+
</recovery_protocol>
|
|
153
|
+
|
|
154
|
+
<instructions>
|
|
155
|
+
- If a sub-agent produced bad output: try a different agent or simpler task
|
|
156
|
+
- If stuck in a loop: break down the task into smaller pieces
|
|
157
|
+
- If context seems corrupted: call recorder to restore context
|
|
158
|
+
- THINK in English for maximum stability
|
|
159
|
+
</instructions>
|
|
160
|
+
|
|
161
|
+
What was the original task? Proceed from the last known good state.
|
|
162
|
+
</anomaly_recovery>`;
|
|
163
|
+
var ESCALATION_PROMPT = `<critical_anomaly>
|
|
164
|
+
\u{1F6A8} CRITICAL: Multiple consecutive malformed outputs detected.
|
|
165
|
+
|
|
166
|
+
<emergency_protocol>
|
|
167
|
+
1. STOP current execution path immediately
|
|
168
|
+
2. DO NOT continue with the same approach - it is failing
|
|
169
|
+
3. CALL architect for a completely new strategy
|
|
170
|
+
4. If architect also fails: report status to user and await guidance
|
|
171
|
+
</emergency_protocol>
|
|
172
|
+
|
|
173
|
+
<diagnosis>
|
|
174
|
+
The current approach is producing corrupted output.
|
|
175
|
+
This may indicate: context overload, model instability, or task complexity.
|
|
176
|
+
</diagnosis>
|
|
177
|
+
|
|
178
|
+
Request a fresh plan from architect with reduced scope.
|
|
179
|
+
</critical_anomaly>`;
|
|
180
|
+
|
|
22
181
|
// src/agents/orchestrator.ts
|
|
23
182
|
var orchestrator = {
|
|
24
183
|
id: AGENT_NAMES.COMMANDER,
|
|
@@ -27,19 +186,16 @@ var orchestrator = {
|
|
|
27
186
|
You are Commander. Complete missions autonomously. Never stop until done.
|
|
28
187
|
</role>
|
|
29
188
|
|
|
189
|
+
${REASONING_CONSTRAINTS}
|
|
190
|
+
|
|
191
|
+
${LANGUAGE_RULE}
|
|
192
|
+
|
|
30
193
|
<core_rules>
|
|
31
194
|
1. Never stop until "\u2705 MISSION COMPLETE"
|
|
32
195
|
2. Never wait for user during execution
|
|
33
196
|
3. Never stop because agent returned nothing
|
|
34
197
|
4. Always survey environment & codebase BEFORE coding
|
|
35
198
|
5. Always verify with evidence based on runtime context
|
|
36
|
-
6. LANGUAGE:
|
|
37
|
-
- THINK and REASON in English for maximum stability
|
|
38
|
-
- FINAL REPORT: Detect the user's language from their request and respond in the SAME language
|
|
39
|
-
- If user writes in Korean \u2192 Report in Korean
|
|
40
|
-
- If user writes in English \u2192 Report in English
|
|
41
|
-
- If user writes in Japanese \u2192 Report in Japanese
|
|
42
|
-
- Default to English if language is unclear
|
|
43
199
|
</core_rules>
|
|
44
200
|
|
|
45
201
|
<phase_0 name="TRIAGE">
|
|
@@ -207,6 +363,169 @@ STRICT RULE: If any agent output contains gibberish, mixed-language hallucinatio
|
|
|
207
363
|
\u274C Make random changes without understanding root cause
|
|
208
364
|
</anti_patterns>
|
|
209
365
|
|
|
366
|
+
${WORKFLOW}
|
|
367
|
+
|
|
368
|
+
<phase_0 name="TRIAGE">
|
|
369
|
+
Evaluate the complexity of the request:
|
|
370
|
+
|
|
371
|
+
| Level | Signal | Track |
|
|
372
|
+
|-------|--------|-------|
|
|
373
|
+
| \u{1F7E2} L1: Simple | One file, clear fix, no dependencies | **FAST TRACK** |
|
|
374
|
+
| \u{1F7E1} L2: Feature | New functionality, clear patterns | **NORMAL TRACK** |
|
|
375
|
+
| \u{1F534} L3: Complex | Refactoring, infra change, unknown scope | **DEEP TRACK** |
|
|
376
|
+
</phase_0>
|
|
377
|
+
|
|
378
|
+
<phase_1 name="CONTEXT_GATHERING">
|
|
379
|
+
IF FAST TRACK (L1):
|
|
380
|
+
- Scan ONLY the target file and its immediate imports.
|
|
381
|
+
- Skip broad infra/domain/doc scans unless an error occurs.
|
|
382
|
+
- Proceed directly to execution.
|
|
383
|
+
|
|
384
|
+
IF NORMAL/DEEP TRACK (L2/L3):
|
|
385
|
+
- **Deep Scan Required**: Execute the full "MANDATORY ENVIRONMENT SCAN".
|
|
386
|
+
- 1. Infra check (Docker/OS)
|
|
387
|
+
- 2. Domain & Stack check
|
|
388
|
+
- 3. Pattern check
|
|
389
|
+
|
|
390
|
+
RECORD findings if on Deep Track.
|
|
391
|
+
</phase_1>
|
|
392
|
+
|
|
393
|
+
<phase_2 name="TOOL_AGENT_SELECTION">
|
|
394
|
+
| Track | Strategy |
|
|
395
|
+
|-------|----------|
|
|
396
|
+
| Fast | Use \`builder\` directly. Skip \`architect\`. |
|
|
397
|
+
| Normal | Call \`architect\` for lightweight plan. |
|
|
398
|
+
| Deep | Full \`architect\` DAG + \`recorder\` state tracking. |
|
|
399
|
+
|
|
400
|
+
DEFAULT to Deep Track if unsure to act safely.
|
|
401
|
+
</phase_2>
|
|
402
|
+
|
|
403
|
+
<phase_3 name="DELEGATION">
|
|
404
|
+
<agent_calling>
|
|
405
|
+
CRITICAL: USE delegate_task FOR ALL DELEGATION
|
|
406
|
+
|
|
407
|
+
delegate_task has TWO MODES:
|
|
408
|
+
- background=true: Non-blocking, parallel execution
|
|
409
|
+
- background=false: Blocking, waits for result
|
|
410
|
+
|
|
411
|
+
| Situation | How to Call |
|
|
412
|
+
|-----------|-------------|
|
|
413
|
+
| Multiple independent tasks | \`delegate_task({ ..., background: true })\` for each |
|
|
414
|
+
| Single task, continue working | \`delegate_task({ ..., background: true })\` |
|
|
415
|
+
| Need result for VERY next step | \`delegate_task({ ..., background: false })\` |
|
|
416
|
+
|
|
417
|
+
PREFER background=true (PARALLEL):
|
|
418
|
+
- Run multiple agents simultaneously
|
|
419
|
+
- Continue analysis while they work
|
|
420
|
+
- System notifies when ALL complete
|
|
421
|
+
|
|
422
|
+
EXAMPLE - PARALLEL:
|
|
423
|
+
\`\`\`
|
|
424
|
+
// Multiple tasks in parallel
|
|
425
|
+
delegate_task({ agent: "${AGENT_NAMES.BUILDER}", description: "Implement X", prompt: "...", background: true })
|
|
426
|
+
delegate_task({ agent: "${AGENT_NAMES.INSPECTOR}", description: "Review Y", prompt: "...", background: true })
|
|
427
|
+
|
|
428
|
+
// Continue other work (don't wait!)
|
|
429
|
+
|
|
430
|
+
// When notified "All Complete":
|
|
431
|
+
get_task_result({ taskId: "task_xxx" })
|
|
432
|
+
\`\`\`
|
|
433
|
+
|
|
434
|
+
EXAMPLE - SYNC (rare):
|
|
435
|
+
\`\`\`
|
|
436
|
+
// Only when you absolutely need the result now
|
|
437
|
+
const result = delegate_task({ agent: "${AGENT_NAMES.BUILDER}", ..., background: false })
|
|
438
|
+
// Result is immediately available
|
|
439
|
+
\`\`\`
|
|
440
|
+
</agent_calling>
|
|
441
|
+
|
|
442
|
+
<delegation_template>
|
|
443
|
+
AGENT: [name]
|
|
444
|
+
TASK: [one atomic action]
|
|
445
|
+
ENVIRONMENT:
|
|
446
|
+
- Infra: [e.g. Docker + Volume mount]
|
|
447
|
+
- Stack: [e.g. Next.js + PostgreSQL]
|
|
448
|
+
- Patterns: [existing code conventions to follow]
|
|
449
|
+
MUST: [Specific requirements]
|
|
450
|
+
AVOID: [Restrictions]
|
|
451
|
+
VERIFY: [Success criteria with evidence]
|
|
452
|
+
</delegation_template>
|
|
453
|
+
</phase_3>
|
|
454
|
+
|
|
455
|
+
<phase_4 name="EXECUTION_VERIFICATION">
|
|
456
|
+
During implementation:
|
|
457
|
+
- Match existing codebase style exactly
|
|
458
|
+
- Run lsp_diagnostics after each change
|
|
459
|
+
|
|
460
|
+
<background_parallel_execution>
|
|
461
|
+
PARALLEL EXECUTION TOOLS:
|
|
462
|
+
|
|
463
|
+
1. **spawn_agent** - Launch agents in parallel sessions
|
|
464
|
+
spawn_agent({ agent: "${AGENT_NAMES.BUILDER}", description: "Implement X", prompt: "..." })
|
|
465
|
+
spawn_agent({ agent: "${AGENT_NAMES.INSPECTOR}", description: "Review Y", prompt: "..." })
|
|
466
|
+
\u2192 Agents run concurrently, system notifies when ALL complete
|
|
467
|
+
\u2192 Use get_task_result({ taskId }) to retrieve results
|
|
468
|
+
|
|
469
|
+
2. **run_background** - Run shell commands asynchronously
|
|
470
|
+
run_background({ command: "npm run build" })
|
|
471
|
+
\u2192 Use check_background({ taskId }) for results
|
|
472
|
+
|
|
473
|
+
SAFETY FEATURES:
|
|
474
|
+
- Queue-based concurrency: Max 3 per agent type (extras queue automatically)
|
|
475
|
+
- Auto-timeout: 30 minutes max runtime
|
|
476
|
+
- Auto-cleanup: Removed from memory 5 min after completion
|
|
477
|
+
- Batched notifications: Notifies when ALL tasks complete (not individually)
|
|
478
|
+
|
|
479
|
+
MANAGEMENT TOOLS:
|
|
480
|
+
- list_tasks: View all parallel tasks and status
|
|
481
|
+
- cancel_task: Stop a running task (frees concurrency slot)
|
|
482
|
+
|
|
483
|
+
SAFE PATTERNS:
|
|
484
|
+
\u2705 Builder on file A + Inspector on file B (different files)
|
|
485
|
+
\u2705 Multiple research agents (read-only)
|
|
486
|
+
\u2705 Build command + Test command (independent)
|
|
487
|
+
|
|
488
|
+
UNSAFE PATTERNS:
|
|
489
|
+
\u274C Multiple builders editing SAME FILE (conflict!)
|
|
490
|
+
|
|
491
|
+
WORKFLOW:
|
|
492
|
+
1. list_tasks: Check current status first
|
|
493
|
+
2. spawn_agent: Launch for INDEPENDENT tasks
|
|
494
|
+
3. Continue working (NO WAITING)
|
|
495
|
+
4. Wait for "All Complete" notification
|
|
496
|
+
5. get_task_result: Retrieve each result
|
|
497
|
+
</background_parallel_execution>
|
|
498
|
+
|
|
499
|
+
<verification_methods>
|
|
500
|
+
| Infra | Proof Method |
|
|
501
|
+
|-------|--------------|
|
|
502
|
+
| OS-Native | npm run build, cargo build, specific test runs |
|
|
503
|
+
| Container | Docker syntax check + config validation |
|
|
504
|
+
| Live API | curl /health if reachable, check logs |
|
|
505
|
+
| Generic | Manual audit by Inspector with logic summary |
|
|
506
|
+
</verification_methods>
|
|
507
|
+
</phase_4>
|
|
508
|
+
|
|
509
|
+
<failure_recovery>
|
|
510
|
+
| Failures | Action |
|
|
511
|
+
|----------|--------|
|
|
512
|
+
| 1-2 | Adjust approach, retry |
|
|
513
|
+
| 3+ | STOP. Call architect for new strategy |
|
|
514
|
+
|
|
515
|
+
<empty_responses>
|
|
516
|
+
| Agent Empty (or Gibberish) | Action |
|
|
517
|
+
|----------------------------|--------|
|
|
518
|
+
| ${AGENT_NAMES.RECORDER} | Fresh start. Proceed to survey. |
|
|
519
|
+
| ${AGENT_NAMES.ARCHITECT} | Try simpler plan yourself. |
|
|
520
|
+
| ${AGENT_NAMES.BUILDER} | Call inspector to diagnose. |
|
|
521
|
+
| ${AGENT_NAMES.INSPECTOR} | Retry with more context. |
|
|
522
|
+
</empty_responses>
|
|
523
|
+
|
|
524
|
+
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.
|
|
525
|
+
</failure_recovery>
|
|
526
|
+
|
|
527
|
+
${ANTI_PATTERNS}
|
|
528
|
+
|
|
210
529
|
<completion>
|
|
211
530
|
Done when: Request fulfilled + lsp clean + build/test/audit pass.
|
|
212
531
|
|
|
@@ -228,10 +547,9 @@ var architect = {
|
|
|
228
547
|
You are Architect. Break complex tasks into atomic pieces.
|
|
229
548
|
</role>
|
|
230
549
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
</constraints>
|
|
550
|
+
${REASONING_CONSTRAINTS}
|
|
551
|
+
|
|
552
|
+
${WORKFLOW}
|
|
235
553
|
|
|
236
554
|
<scalable_planning>
|
|
237
555
|
- **Fast Track**: Skip JSON overhead. Just acknowledge simple task.
|
|
@@ -250,25 +568,67 @@ If your reasoning collapses into gibberish, stop and output "ERROR: REASONING_CO
|
|
|
250
568
|
4. Assign: builder (code) or inspector (verify)
|
|
251
569
|
|
|
252
570
|
<output_format>
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
571
|
+
MUST output valid JSON block wrapped in backticks:
|
|
572
|
+
|
|
573
|
+
<json_output>
|
|
574
|
+
{
|
|
575
|
+
"mission": "goal in one line",
|
|
576
|
+
"tasks": [
|
|
577
|
+
{
|
|
578
|
+
"id": "T1",
|
|
579
|
+
"action": "one atomic action",
|
|
580
|
+
"agent": "builder",
|
|
581
|
+
"file": "path/to/file",
|
|
582
|
+
"parallel_group": 1,
|
|
583
|
+
"dependencies": [],
|
|
584
|
+
"success_criteria": "how to verify"
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
"id": "T2",
|
|
588
|
+
"action": "one atomic action",
|
|
589
|
+
"agent": "builder",
|
|
590
|
+
"file": "path/to/file",
|
|
591
|
+
"parallel_group": 1,
|
|
592
|
+
"dependencies": [],
|
|
593
|
+
"success_criteria": "how to verify"
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
"id": "T3",
|
|
597
|
+
"action": "one atomic action",
|
|
598
|
+
"agent": "inspector",
|
|
599
|
+
"file": "path/to/file",
|
|
600
|
+
"parallel_group": 2,
|
|
601
|
+
"dependencies": ["T1", "T2"],
|
|
602
|
+
"success_criteria": "verify method"
|
|
603
|
+
}
|
|
604
|
+
]
|
|
605
|
+
}
|
|
606
|
+
</json_output>
|
|
258
607
|
</output_format>
|
|
259
608
|
</plan_mode>
|
|
260
609
|
|
|
261
610
|
<strategy_mode trigger="failures > 2">
|
|
262
611
|
<output_format>
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
612
|
+
<json_output>
|
|
613
|
+
{
|
|
614
|
+
"failed_attempts": [
|
|
615
|
+
{"attempt": "what was tried", "reason": "why failed"}
|
|
616
|
+
],
|
|
617
|
+
"root_cause": "actual problem",
|
|
618
|
+
"new_approach": "different strategy",
|
|
619
|
+
"revised_tasks": [
|
|
620
|
+
{
|
|
621
|
+
"id": "T1",
|
|
622
|
+
"action": "one atomic action",
|
|
623
|
+
"agent": "builder",
|
|
624
|
+
"file": "path/to/file",
|
|
625
|
+
"parallel_group": 1,
|
|
626
|
+
"dependencies": [],
|
|
627
|
+
"success_criteria": "how to verify"
|
|
628
|
+
}
|
|
629
|
+
]
|
|
630
|
+
}
|
|
631
|
+
</json_output>
|
|
272
632
|
</output_format>
|
|
273
633
|
</strategy_mode>
|
|
274
634
|
|
|
@@ -277,6 +637,7 @@ T1: ...
|
|
|
277
637
|
- Always end with inspector task
|
|
278
638
|
- Group unrelated tasks (parallel)
|
|
279
639
|
- Be specific about files and verification
|
|
640
|
+
- Output MUST be valid JSON wrapped in <json_output> tags
|
|
280
641
|
</rules>`,
|
|
281
642
|
canWrite: false,
|
|
282
643
|
canBash: false
|
|
@@ -290,10 +651,11 @@ var builder = {
|
|
|
290
651
|
You are Builder. Write code that works.
|
|
291
652
|
</role>
|
|
292
653
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
654
|
+
${REASONING_CONSTRAINTS}
|
|
655
|
+
|
|
656
|
+
${WORKFLOW}
|
|
657
|
+
|
|
658
|
+
${ANTI_PATTERNS}
|
|
297
659
|
|
|
298
660
|
<scalable_attention>
|
|
299
661
|
- **Simple Fix (L1)**: Read file \u2192 Implement fix directly. Efficiency first.
|
|
@@ -356,10 +718,11 @@ var inspector = {
|
|
|
356
718
|
You are Inspector. Prove failure or success with evidence.
|
|
357
719
|
</role>
|
|
358
720
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
721
|
+
${REASONING_CONSTRAINTS}
|
|
722
|
+
|
|
723
|
+
${WORKFLOW}
|
|
724
|
+
|
|
725
|
+
${ANTI_PATTERNS}
|
|
363
726
|
|
|
364
727
|
<scalable_audit>
|
|
365
728
|
- **Fast Track**: Verify syntax + quick logic check.
|
|
@@ -423,10 +786,7 @@ var recorder = {
|
|
|
423
786
|
You are Recorder. Save and load work progress.
|
|
424
787
|
</role>
|
|
425
788
|
|
|
426
|
-
|
|
427
|
-
Reasoning MUST be in English for model stability.
|
|
428
|
-
If your reasoning collapses into gibberish, stop and output "ERROR: REASONING_COLLAPSE".
|
|
429
|
-
</constraints>
|
|
789
|
+
${REASONING_CONSTRAINTS}
|
|
430
790
|
|
|
431
791
|
<purpose>
|
|
432
792
|
Context can be lost between sessions. You save it to disk.
|
|
@@ -556,12 +916,20 @@ var TaskGraph = class _TaskGraph {
|
|
|
556
916
|
}
|
|
557
917
|
};
|
|
558
918
|
|
|
919
|
+
// src/constants/time.ts
|
|
920
|
+
var TASK_TTL_MS = 30 * 60 * 1e3;
|
|
921
|
+
var BACKGROUND_TASK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
922
|
+
var SYNC_TASK_MAX_POLL_MS = 10 * 60 * 1e3;
|
|
923
|
+
var MIN_STABILITY_MS = 5 * 1e3;
|
|
924
|
+
var CLEANUP_DELAY_MS = 5 * 60 * 1e3;
|
|
925
|
+
var MAX_TASK_RETRIES = 3;
|
|
926
|
+
|
|
559
927
|
// src/core/state.ts
|
|
560
928
|
var state = {
|
|
561
929
|
missionActive: false,
|
|
562
930
|
maxIterations: 1e3,
|
|
563
931
|
// Effectively infinite - "Relentless" mode
|
|
564
|
-
maxRetries:
|
|
932
|
+
maxRetries: MAX_TASK_RETRIES,
|
|
565
933
|
sessions: /* @__PURE__ */ new Map()
|
|
566
934
|
};
|
|
567
935
|
|
|
@@ -12987,6 +13355,113 @@ function tool(input) {
|
|
|
12987
13355
|
}
|
|
12988
13356
|
tool.schema = external_exports;
|
|
12989
13357
|
|
|
13358
|
+
// src/context/enforcer.ts
|
|
13359
|
+
var ContextEnforcer = class {
|
|
13360
|
+
requirements = /* @__PURE__ */ new Map([
|
|
13361
|
+
["builder", {
|
|
13362
|
+
agent: "builder",
|
|
13363
|
+
required: ["infra", "stack", "patterns", "files"],
|
|
13364
|
+
optional: ["examples", "tests", "environment"]
|
|
13365
|
+
}],
|
|
13366
|
+
["inspector", {
|
|
13367
|
+
agent: "inspector",
|
|
13368
|
+
required: ["infra", "verification_method", "changed_files"],
|
|
13369
|
+
optional: ["test_results", "build_logs", "environment"]
|
|
13370
|
+
}],
|
|
13371
|
+
["architect", {
|
|
13372
|
+
agent: "architect",
|
|
13373
|
+
required: ["mission", "scope", "complexity"],
|
|
13374
|
+
optional: ["environment", "constraints", "patterns"]
|
|
13375
|
+
}],
|
|
13376
|
+
["recorder", {
|
|
13377
|
+
agent: "recorder",
|
|
13378
|
+
required: [],
|
|
13379
|
+
optional: ["mission", "progress", "context"]
|
|
13380
|
+
}]
|
|
13381
|
+
]);
|
|
13382
|
+
/**
|
|
13383
|
+
* Validate context for agent delegation
|
|
13384
|
+
*/
|
|
13385
|
+
validate(agent, context) {
|
|
13386
|
+
const req = this.requirements.get(agent);
|
|
13387
|
+
if (!req) {
|
|
13388
|
+
return { valid: true, errors: [], warnings: [] };
|
|
13389
|
+
}
|
|
13390
|
+
const errors = [];
|
|
13391
|
+
const warnings = [];
|
|
13392
|
+
const contextLower = context.toLowerCase();
|
|
13393
|
+
for (const key of req.required) {
|
|
13394
|
+
if (!contextLower.includes(key.toLowerCase())) {
|
|
13395
|
+
errors.push("Missing required context: " + key);
|
|
13396
|
+
}
|
|
13397
|
+
}
|
|
13398
|
+
for (const key of req.optional) {
|
|
13399
|
+
if (!contextLower.includes(key.toLowerCase())) {
|
|
13400
|
+
warnings.push("Optional context missing: " + key);
|
|
13401
|
+
}
|
|
13402
|
+
}
|
|
13403
|
+
return {
|
|
13404
|
+
valid: errors.length === 0,
|
|
13405
|
+
errors,
|
|
13406
|
+
warnings
|
|
13407
|
+
};
|
|
13408
|
+
}
|
|
13409
|
+
/**
|
|
13410
|
+
* Get requirements for an agent
|
|
13411
|
+
*/
|
|
13412
|
+
getRequirements(agent) {
|
|
13413
|
+
return this.requirements.get(agent);
|
|
13414
|
+
}
|
|
13415
|
+
/**
|
|
13416
|
+
* Add or update requirements for an agent
|
|
13417
|
+
*/
|
|
13418
|
+
setRequirements(agent, requirements) {
|
|
13419
|
+
this.requirements.set(agent, requirements);
|
|
13420
|
+
}
|
|
13421
|
+
/**
|
|
13422
|
+
* Format validation result as user-friendly message
|
|
13423
|
+
*/
|
|
13424
|
+
formatValidation(result) {
|
|
13425
|
+
if (result.valid && result.warnings.length === 0) {
|
|
13426
|
+
return "Valid context with all required information.";
|
|
13427
|
+
}
|
|
13428
|
+
let message = "";
|
|
13429
|
+
if (result.errors.length > 0) {
|
|
13430
|
+
message += "Errors:\n" + result.errors.map((e) => " \u274C " + e).join("\n") + "\n";
|
|
13431
|
+
}
|
|
13432
|
+
if (result.warnings.length > 0) {
|
|
13433
|
+
message += "\nWarnings:\n" + result.warnings.map((w) => " \u26A0\uFE0F " + w).join("\n");
|
|
13434
|
+
}
|
|
13435
|
+
return message;
|
|
13436
|
+
}
|
|
13437
|
+
/**
|
|
13438
|
+
* Check if context has minimal required information
|
|
13439
|
+
*/
|
|
13440
|
+
hasMinimumContext(agent, context) {
|
|
13441
|
+
const req = this.requirements.get(agent);
|
|
13442
|
+
if (!req || req.required.length === 0) {
|
|
13443
|
+
return true;
|
|
13444
|
+
}
|
|
13445
|
+
const presentCount = req.required.filter(
|
|
13446
|
+
(key) => context.toLowerCase().includes(key.toLowerCase())
|
|
13447
|
+
).length;
|
|
13448
|
+
return presentCount > 0;
|
|
13449
|
+
}
|
|
13450
|
+
/**
|
|
13451
|
+
* Suggest missing context based on agent type
|
|
13452
|
+
*/
|
|
13453
|
+
suggestContext(agent, context) {
|
|
13454
|
+
const req = this.requirements.get(agent);
|
|
13455
|
+
if (!req) {
|
|
13456
|
+
return [];
|
|
13457
|
+
}
|
|
13458
|
+
return req.required.filter(
|
|
13459
|
+
(key) => !context.toLowerCase().includes(key.toLowerCase())
|
|
13460
|
+
);
|
|
13461
|
+
}
|
|
13462
|
+
};
|
|
13463
|
+
var contextEnforcer = new ContextEnforcer();
|
|
13464
|
+
|
|
12990
13465
|
// src/tools/callAgent.ts
|
|
12991
13466
|
var AGENT_EMOJI = {
|
|
12992
13467
|
[AGENT_NAMES.ARCHITECT]: "\u{1F3D7}\uFE0F",
|
|
@@ -13025,13 +13500,19 @@ var callAgentTool = tool({
|
|
|
13025
13500
|
async execute(args) {
|
|
13026
13501
|
const agentDef = AGENTS[args.agent];
|
|
13027
13502
|
if (!agentDef) {
|
|
13028
|
-
return
|
|
13503
|
+
return "Error: Unknown agent: " + args.agent;
|
|
13029
13504
|
}
|
|
13030
13505
|
const emoji3 = AGENT_EMOJI[args.agent] || "\u{1F916}";
|
|
13506
|
+
if (args.context) {
|
|
13507
|
+
const validation = contextEnforcer.validate(args.agent, args.context);
|
|
13508
|
+
if (!validation.valid) {
|
|
13509
|
+
return "Context Validation Failed:\n" + contextEnforcer.formatValidation(validation);
|
|
13510
|
+
}
|
|
13511
|
+
}
|
|
13031
13512
|
const prompt = `
|
|
13032
|
-
\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
|
|
13513
|
+
\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
|
|
13033
13514
|
${emoji3} ${agentDef.id.toUpperCase()} :: ${agentDef.description}
|
|
13034
|
-
\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
|
|
13515
|
+
\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
|
|
13035
13516
|
|
|
13036
13517
|
<system>
|
|
13037
13518
|
${agentDef.systemPrompt}
|
|
@@ -13041,9 +13522,7 @@ ${agentDef.systemPrompt}
|
|
|
13041
13522
|
${args.task}
|
|
13042
13523
|
</task>
|
|
13043
13524
|
|
|
13044
|
-
${args.context ?
|
|
13045
|
-
${args.context}
|
|
13046
|
-
</context>` : ""}
|
|
13525
|
+
${args.context ? "<context>\n" + args.context + "\n</context>" : ""}
|
|
13047
13526
|
|
|
13048
13527
|
<execution>
|
|
13049
13528
|
Follow this pattern:
|
|
@@ -13319,6 +13798,206 @@ Returns matches grouped by pattern, with file paths and line numbers.
|
|
|
13319
13798
|
}
|
|
13320
13799
|
});
|
|
13321
13800
|
|
|
13801
|
+
// src/tools/git.ts
|
|
13802
|
+
var gitBranchTool = (directory) => tool({
|
|
13803
|
+
description: `Get Git branch information and status.
|
|
13804
|
+
|
|
13805
|
+
<purpose>
|
|
13806
|
+
Analyze current Git workspace context including:
|
|
13807
|
+
- Current branch name
|
|
13808
|
+
- All branches (local and remote)
|
|
13809
|
+
- Branch relationships (upstream, ahead/behind)
|
|
13810
|
+
- Staged, unstaged, and untracked files
|
|
13811
|
+
- Recent commits
|
|
13812
|
+
</purpose>
|
|
13813
|
+
|
|
13814
|
+
<examples>
|
|
13815
|
+
- Get current branch: Returns branch name and status
|
|
13816
|
+
- List all branches: Shows local and remote branches
|
|
13817
|
+
- Check status: Shows modified files
|
|
13818
|
+
- Recent commits: Last 5 commits with file changes
|
|
13819
|
+
</examples>
|
|
13820
|
+
|
|
13821
|
+
<output>
|
|
13822
|
+
Returns structured Git information for better code decisions.
|
|
13823
|
+
</output>`,
|
|
13824
|
+
args: {
|
|
13825
|
+
action: tool.schema.enum([
|
|
13826
|
+
"current",
|
|
13827
|
+
"list",
|
|
13828
|
+
"status",
|
|
13829
|
+
"diff",
|
|
13830
|
+
"recent",
|
|
13831
|
+
"all"
|
|
13832
|
+
]).describe("Action to perform: current, list, status, diff, recent, all"),
|
|
13833
|
+
baseBranch: tool.schema.string().optional().describe("Base branch for comparison (e.g., 'main', 'develop'). Required for diff action.")
|
|
13834
|
+
},
|
|
13835
|
+
async execute(args) {
|
|
13836
|
+
const { action, baseBranch } = args;
|
|
13837
|
+
try {
|
|
13838
|
+
switch (action) {
|
|
13839
|
+
case "current":
|
|
13840
|
+
return await getCurrentBranch(directory);
|
|
13841
|
+
case "list":
|
|
13842
|
+
return await listBranches(directory);
|
|
13843
|
+
case "status":
|
|
13844
|
+
return await getGitStatus(directory);
|
|
13845
|
+
case "diff":
|
|
13846
|
+
return await getDiff(directory, baseBranch);
|
|
13847
|
+
case "recent":
|
|
13848
|
+
return await getRecentCommits(directory, 5);
|
|
13849
|
+
case "all":
|
|
13850
|
+
return await getAllInfo(directory);
|
|
13851
|
+
default:
|
|
13852
|
+
return await getCurrentBranch(directory);
|
|
13853
|
+
}
|
|
13854
|
+
} catch (error45) {
|
|
13855
|
+
return "\u274C Git error: " + (error45 instanceof Error ? error45.message : String(error45));
|
|
13856
|
+
}
|
|
13857
|
+
}
|
|
13858
|
+
});
|
|
13859
|
+
async function execGit(directory, args) {
|
|
13860
|
+
const { execSync } = await import("child_process");
|
|
13861
|
+
try {
|
|
13862
|
+
return execSync("git " + args.join(" "), {
|
|
13863
|
+
cwd: directory,
|
|
13864
|
+
encoding: "utf-8",
|
|
13865
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
13866
|
+
maxBuffer: 10 * 1024 * 1024
|
|
13867
|
+
// 10MB
|
|
13868
|
+
}).toString();
|
|
13869
|
+
} catch (error45) {
|
|
13870
|
+
if (error45.status === 128) {
|
|
13871
|
+
throw new Error("Not a git repository");
|
|
13872
|
+
}
|
|
13873
|
+
throw error45;
|
|
13874
|
+
}
|
|
13875
|
+
}
|
|
13876
|
+
async function getAheadBehind(directory, branch) {
|
|
13877
|
+
try {
|
|
13878
|
+
const output = await execGit(directory, ["rev-list", "--left-right", "--count", branch + "...@{u}"]);
|
|
13879
|
+
const parts = output.trim().split(/\s+/);
|
|
13880
|
+
const ahead = parseInt(parts[0], 10);
|
|
13881
|
+
const behind = parseInt(parts[1] || "0", 10);
|
|
13882
|
+
const resultParts = [];
|
|
13883
|
+
if (ahead > 0) resultParts.push(ahead + " ahead");
|
|
13884
|
+
if (behind > 0) resultParts.push(behind + " behind");
|
|
13885
|
+
return resultParts.length > 0 ? "| **Sync** | " + resultParts.join(", ") + " |" : "";
|
|
13886
|
+
} catch {
|
|
13887
|
+
return "";
|
|
13888
|
+
}
|
|
13889
|
+
}
|
|
13890
|
+
async function getCurrentBranch(directory) {
|
|
13891
|
+
const output = await execGit(directory, ["branch", "--show-current"]);
|
|
13892
|
+
const current = output.trim() || "HEAD (detached)";
|
|
13893
|
+
const upstream = await execGit(directory, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]).catch(() => "");
|
|
13894
|
+
return "\u{1F33F} **Current Branch**: `" + current + "`\n\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\n" + (upstream ? "| **Upstream** | `" + upstream + "` |" : "") + await getAheadBehind(directory, current);
|
|
13895
|
+
}
|
|
13896
|
+
async function listBranches(directory) {
|
|
13897
|
+
const branches = await execGit(directory, ["branch", "-vv"]);
|
|
13898
|
+
const branchList = branches.split("\n").filter((b) => b.trim()).map((b) => {
|
|
13899
|
+
const isCurrent = b.startsWith("*");
|
|
13900
|
+
const name = isCurrent ? b.substring(2).trim() : b.trim();
|
|
13901
|
+
const parts = name.split(/\s+/);
|
|
13902
|
+
const branchName = parts[0];
|
|
13903
|
+
const upstream = parts[1] ? parts[1].match(/\[([^\]]+)\]/)?.[1] : void 0;
|
|
13904
|
+
const icon = isCurrent ? "\u{1F33F}" : "\u{1F4C2}";
|
|
13905
|
+
const status = isCurrent ? "(current)" : "";
|
|
13906
|
+
const upstreamInfo = upstream ? "\u2192 " + upstream : "";
|
|
13907
|
+
return icon + " `" + branchName + "` " + status + " " + upstreamInfo;
|
|
13908
|
+
}).join("\n");
|
|
13909
|
+
return "\u{1F33F} **All Branches**\n\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\n" + branchList;
|
|
13910
|
+
}
|
|
13911
|
+
async function getGitStatus(directory) {
|
|
13912
|
+
const status = await execGit(directory, ["status", "--porcelain"]);
|
|
13913
|
+
const lines = status.split("\n").filter((l) => l.trim());
|
|
13914
|
+
if (lines.length === 0) {
|
|
13915
|
+
return "\u2705 Working directory clean";
|
|
13916
|
+
}
|
|
13917
|
+
const staged = [];
|
|
13918
|
+
const unstaged = [];
|
|
13919
|
+
const untracked = [];
|
|
13920
|
+
const conflicted = [];
|
|
13921
|
+
for (const line of lines) {
|
|
13922
|
+
if (!line || line.length < 2) continue;
|
|
13923
|
+
const statusCode = line.substring(0, 2);
|
|
13924
|
+
const filename = line.substring(3);
|
|
13925
|
+
if (statusCode === "??") {
|
|
13926
|
+
untracked.push(filename);
|
|
13927
|
+
} else if (statusCode.startsWith("U") || statusCode === "AA") {
|
|
13928
|
+
conflicted.push(filename);
|
|
13929
|
+
} else if (statusCode[0] !== " ") {
|
|
13930
|
+
staged.push(filename);
|
|
13931
|
+
}
|
|
13932
|
+
if (statusCode[1] !== " " && statusCode[1] !== "?") {
|
|
13933
|
+
unstaged.push(filename);
|
|
13934
|
+
}
|
|
13935
|
+
}
|
|
13936
|
+
const current = await execGit(directory, ["branch", "--show-current"]).catch(() => "unknown");
|
|
13937
|
+
let result = "\u{1F33F} **Branch**: `" + current + "`\n\n";
|
|
13938
|
+
if (staged.length > 0) {
|
|
13939
|
+
result += "\u2705 **Staged** (" + staged.length + ")\n" + staged.map((f) => " + " + f).join("\n") + "\n\n";
|
|
13940
|
+
}
|
|
13941
|
+
if (unstaged.length > 0) {
|
|
13942
|
+
result += "\u{1F4DD} **Modified** (" + unstaged.length + ")\n" + unstaged.map((f) => " ~ " + f).join("\n") + "\n\n";
|
|
13943
|
+
}
|
|
13944
|
+
if (untracked.length > 0) {
|
|
13945
|
+
result += "\u2795 **Untracked** (" + untracked.length + ")\n" + untracked.slice(0, 10).map((f) => " ? " + f).join("\n") + (untracked.length > 10 ? "\n ... and " + (untracked.length - 10) + " more" : "") + "\n\n";
|
|
13946
|
+
}
|
|
13947
|
+
if (conflicted.length > 0) {
|
|
13948
|
+
result += "\u26A0\uFE0F **Conflicts** (" + conflicted.length + ")\n" + conflicted.map((f) => " ! " + f).join("\n") + "\n\n";
|
|
13949
|
+
}
|
|
13950
|
+
return result.trim();
|
|
13951
|
+
}
|
|
13952
|
+
async function getDiff(directory, baseBranch) {
|
|
13953
|
+
const current = await execGit(directory, ["branch", "--show-current"]).catch(() => "HEAD");
|
|
13954
|
+
const base = baseBranch || "main";
|
|
13955
|
+
const diff = await execGit(directory, ["diff", base + "..." + current, "--stat"]);
|
|
13956
|
+
if (!diff.trim()) {
|
|
13957
|
+
return "\u2705 No differences between `" + current + "` and `" + base + "`";
|
|
13958
|
+
}
|
|
13959
|
+
const files = await execGit(directory, ["diff", base + "..." + current, "--name-only"]);
|
|
13960
|
+
const fileList = files.split("\n").filter((f) => f.trim());
|
|
13961
|
+
return "\u{1F4CA} **Diff**: `" + current + "` vs `" + base + "`\n\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\n" + diff + "\n\n\u{1F4C1} **Changed Files** (" + fileList.length + "):\n" + fileList.map((f, i) => " " + (i + 1) + ". " + f).join("\n");
|
|
13962
|
+
}
|
|
13963
|
+
async function getRecentCommits(directory, count = 5) {
|
|
13964
|
+
const log2 = await execGit(directory, [
|
|
13965
|
+
"log",
|
|
13966
|
+
"-" + count,
|
|
13967
|
+
"--pretty=format:%H|%an|%ad|%s",
|
|
13968
|
+
"--date=short",
|
|
13969
|
+
"--name-only"
|
|
13970
|
+
]);
|
|
13971
|
+
const blocks = log2.split("\n\n").filter((b) => b.trim());
|
|
13972
|
+
const commits = [];
|
|
13973
|
+
for (const block of blocks) {
|
|
13974
|
+
const lines = block.split("\n").filter((l) => l.trim());
|
|
13975
|
+
if (lines.length < 2) continue;
|
|
13976
|
+
const [hash2, author, date5, message] = lines[0].split("|");
|
|
13977
|
+
const files = lines.slice(1);
|
|
13978
|
+
commits.push({
|
|
13979
|
+
hash: hash2.substring(0, 7),
|
|
13980
|
+
author,
|
|
13981
|
+
date: date5,
|
|
13982
|
+
message,
|
|
13983
|
+
files
|
|
13984
|
+
});
|
|
13985
|
+
}
|
|
13986
|
+
let result = "\u{1F4DC} **Recent Commits** (last " + commits.length + ")\n\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\n";
|
|
13987
|
+
for (const commit of commits) {
|
|
13988
|
+
result += "\n\u{1F539} `" + commit.hash + "` - " + commit.message + "\n";
|
|
13989
|
+
result += " \u{1F464} " + commit.author + " | \u{1F4C5} " + commit.date + "\n";
|
|
13990
|
+
result += " \u{1F4C1} " + commit.files.length + " file" + (commit.files.length > 1 ? "s" : "") + "\n";
|
|
13991
|
+
}
|
|
13992
|
+
return result;
|
|
13993
|
+
}
|
|
13994
|
+
async function getAllInfo(directory) {
|
|
13995
|
+
const current = await getCurrentBranch(directory);
|
|
13996
|
+
const status = await getGitStatus(directory);
|
|
13997
|
+
const recent = await getRecentCommits(directory, 3);
|
|
13998
|
+
return "\u{1F50D} **Full Git Context**\n\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\n\n" + current + "\n\n" + status + "\n\n" + recent;
|
|
13999
|
+
}
|
|
14000
|
+
|
|
13322
14001
|
// src/core/background.ts
|
|
13323
14002
|
import { spawn as spawn2 } from "child_process";
|
|
13324
14003
|
import { randomBytes } from "crypto";
|
|
@@ -13879,13 +14558,264 @@ Duration before kill: ${backgroundTaskManager.formatDuration(task)}`;
|
|
|
13879
14558
|
}
|
|
13880
14559
|
});
|
|
13881
14560
|
|
|
13882
|
-
// src/core/
|
|
13883
|
-
var
|
|
13884
|
-
|
|
13885
|
-
|
|
13886
|
-
|
|
13887
|
-
|
|
13888
|
-
|
|
14561
|
+
// src/core/config.ts
|
|
14562
|
+
var DEFAULT_PARALLEL_AGENT = {
|
|
14563
|
+
taskTtlMs: 30 * 60 * 1e3,
|
|
14564
|
+
// 30 minutes
|
|
14565
|
+
cleanupDelayMs: 5 * 60 * 1e3,
|
|
14566
|
+
// 5 minutes
|
|
14567
|
+
minStabilityMs: 5 * 1e3,
|
|
14568
|
+
// 5 seconds
|
|
14569
|
+
pollIntervalMs: 2 * 1e3,
|
|
14570
|
+
// 2 seconds
|
|
14571
|
+
defaultConcurrency: 3,
|
|
14572
|
+
maxConcurrency: 10,
|
|
14573
|
+
enableDebug: false,
|
|
14574
|
+
enableDetailedLogs: false
|
|
14575
|
+
};
|
|
14576
|
+
var DEFAULT_BACKGROUND_TASK = {
|
|
14577
|
+
monitorIntervalMs: 5 * 1e3,
|
|
14578
|
+
// 5 seconds
|
|
14579
|
+
storageDir: process.env.OPENCODE_ORCHESTRATOR_DIR || `${__require("os").homedir()}/.opencode-orchestrator`,
|
|
14580
|
+
defaultTimeoutMs: 5 * 60 * 1e3,
|
|
14581
|
+
// 5 minutes
|
|
14582
|
+
maxCompletedTasksToKeep: 100,
|
|
14583
|
+
enableDebug: true
|
|
14584
|
+
};
|
|
14585
|
+
var DEFAULT_SESSION = {
|
|
14586
|
+
defaultMaxSteps: 50,
|
|
14587
|
+
taskCommandMaxSteps: 100
|
|
14588
|
+
};
|
|
14589
|
+
var ConfigManager = class _ConfigManager {
|
|
14590
|
+
static _instance;
|
|
14591
|
+
parallelAgentConfig;
|
|
14592
|
+
backgroundTaskConfig;
|
|
14593
|
+
sessionConfig;
|
|
14594
|
+
constructor() {
|
|
14595
|
+
this.parallelAgentConfig = this.loadParallelAgentConfig();
|
|
14596
|
+
this.backgroundTaskConfig = this.loadBackgroundTaskConfig();
|
|
14597
|
+
this.sessionConfig = this.loadSessionConfig();
|
|
14598
|
+
}
|
|
14599
|
+
static getInstance() {
|
|
14600
|
+
if (!_ConfigManager._instance) {
|
|
14601
|
+
_ConfigManager._instance = new _ConfigManager();
|
|
14602
|
+
}
|
|
14603
|
+
return _ConfigManager._instance;
|
|
14604
|
+
}
|
|
14605
|
+
// ------------------------------------------------------------------------
|
|
14606
|
+
// Parallel Agent Config
|
|
14607
|
+
// ------------------------------------------------------------------------
|
|
14608
|
+
loadParallelAgentConfig() {
|
|
14609
|
+
return {
|
|
14610
|
+
...DEFAULT_PARALLEL_AGENT,
|
|
14611
|
+
taskTtlMs: this.parseEnvInt(
|
|
14612
|
+
"OPENCODE_TASK_TTL_MS",
|
|
14613
|
+
DEFAULT_PARALLEL_AGENT.taskTtlMs
|
|
14614
|
+
),
|
|
14615
|
+
cleanupDelayMs: this.parseEnvInt(
|
|
14616
|
+
"OPENCODE_CLEANUP_DELAY_MS",
|
|
14617
|
+
DEFAULT_PARALLEL_AGENT.cleanupDelayMs
|
|
14618
|
+
),
|
|
14619
|
+
minStabilityMs: this.parseEnvInt(
|
|
14620
|
+
"OPENCODE_MIN_STABILITY_MS",
|
|
14621
|
+
DEFAULT_PARALLEL_AGENT.minStabilityMs
|
|
14622
|
+
),
|
|
14623
|
+
pollIntervalMs: this.parseEnvInt(
|
|
14624
|
+
"OPENCODE_POLL_INTERVAL_MS",
|
|
14625
|
+
DEFAULT_PARALLEL_AGENT.pollIntervalMs
|
|
14626
|
+
),
|
|
14627
|
+
defaultConcurrency: this.parseEnvInt(
|
|
14628
|
+
"OPENCODE_DEFAULT_CONCURRENCY",
|
|
14629
|
+
DEFAULT_PARALLEL_AGENT.defaultConcurrency
|
|
14630
|
+
),
|
|
14631
|
+
maxConcurrency: this.parseEnvInt(
|
|
14632
|
+
"OPENCODE_MAX_CONCURRENCY",
|
|
14633
|
+
DEFAULT_PARALLEL_AGENT.maxConcurrency
|
|
14634
|
+
),
|
|
14635
|
+
enableDebug: process.env.OPENCODE_DEBUG_PARALLEL === "true",
|
|
14636
|
+
enableDetailedLogs: process.env.OPENCODE_DETAILED_LOGS === "true"
|
|
14637
|
+
};
|
|
14638
|
+
}
|
|
14639
|
+
getParallelAgentConfig() {
|
|
14640
|
+
return this.parallelAgentConfig;
|
|
14641
|
+
}
|
|
14642
|
+
// ------------------------------------------------------------------------
|
|
14643
|
+
// Background Task Config
|
|
14644
|
+
// ------------------------------------------------------------------------
|
|
14645
|
+
loadBackgroundTaskConfig() {
|
|
14646
|
+
return {
|
|
14647
|
+
...DEFAULT_BACKGROUND_TASK,
|
|
14648
|
+
monitorIntervalMs: this.parseEnvInt(
|
|
14649
|
+
"OPENCODE_MONITOR_INTERVAL_MS",
|
|
14650
|
+
DEFAULT_BACKGROUND_TASK.monitorIntervalMs
|
|
14651
|
+
),
|
|
14652
|
+
defaultTimeoutMs: this.parseEnvInt(
|
|
14653
|
+
"OPENCODE_DEFAULT_TIMEOUT_MS",
|
|
14654
|
+
DEFAULT_BACKGROUND_TASK.defaultTimeoutMs
|
|
14655
|
+
),
|
|
14656
|
+
maxCompletedTasksToKeep: this.parseEnvInt(
|
|
14657
|
+
"OPENCODE_MAX_COMPLETED_TASKS",
|
|
14658
|
+
DEFAULT_BACKGROUND_TASK.maxCompletedTasksToKeep
|
|
14659
|
+
),
|
|
14660
|
+
enableDebug: process.env.OPENCODE_DEBUG_BACKGROUND === "true"
|
|
14661
|
+
};
|
|
14662
|
+
}
|
|
14663
|
+
getBackgroundTaskConfig() {
|
|
14664
|
+
return this.backgroundTaskConfig;
|
|
14665
|
+
}
|
|
14666
|
+
// ------------------------------------------------------------------------
|
|
14667
|
+
// Session Config
|
|
14668
|
+
// ------------------------------------------------------------------------
|
|
14669
|
+
loadSessionConfig() {
|
|
14670
|
+
return {
|
|
14671
|
+
...DEFAULT_SESSION,
|
|
14672
|
+
defaultMaxSteps: this.parseEnvInt(
|
|
14673
|
+
"OPENCODE_DEFAULT_MAX_STEPS",
|
|
14674
|
+
DEFAULT_SESSION.defaultMaxSteps
|
|
14675
|
+
),
|
|
14676
|
+
taskCommandMaxSteps: this.parseEnvInt(
|
|
14677
|
+
"OPENCODE_TASK_MAX_STEPS",
|
|
14678
|
+
DEFAULT_SESSION.taskCommandMaxSteps
|
|
14679
|
+
)
|
|
14680
|
+
};
|
|
14681
|
+
}
|
|
14682
|
+
getSessionConfig() {
|
|
14683
|
+
return this.sessionConfig;
|
|
14684
|
+
}
|
|
14685
|
+
// ------------------------------------------------------------------------
|
|
14686
|
+
// Runtime Updates (Dynamic Configuration)
|
|
14687
|
+
// ------------------------------------------------------------------------
|
|
14688
|
+
/**
|
|
14689
|
+
* Update configuration at runtime
|
|
14690
|
+
* Useful for adaptive behavior or user preferences
|
|
14691
|
+
*/
|
|
14692
|
+
updateParallelAgentConfig(updates) {
|
|
14693
|
+
this.parallelAgentConfig = {
|
|
14694
|
+
...this.parallelAgentConfig,
|
|
14695
|
+
...updates
|
|
14696
|
+
};
|
|
14697
|
+
this.validateParallelAgentConfig();
|
|
14698
|
+
}
|
|
14699
|
+
updateBackgroundTaskConfig(updates) {
|
|
14700
|
+
this.backgroundTaskConfig = {
|
|
14701
|
+
...this.backgroundTaskConfig,
|
|
14702
|
+
...updates
|
|
14703
|
+
};
|
|
14704
|
+
this.validateBackgroundTaskConfig();
|
|
14705
|
+
}
|
|
14706
|
+
updateSessionConfig(updates) {
|
|
14707
|
+
this.sessionConfig = {
|
|
14708
|
+
...this.sessionConfig,
|
|
14709
|
+
...updates
|
|
14710
|
+
};
|
|
14711
|
+
this.validateSessionConfig();
|
|
14712
|
+
}
|
|
14713
|
+
// ------------------------------------------------------------------------
|
|
14714
|
+
// Validation
|
|
14715
|
+
// ------------------------------------------------------------------------
|
|
14716
|
+
validateParallelAgentConfig() {
|
|
14717
|
+
const { taskTtlMs, cleanupDelayMs, minStabilityMs, pollIntervalMs, defaultConcurrency, maxConcurrency } = this.parallelAgentConfig;
|
|
14718
|
+
if (taskTtlMs < 60 * 1e3) {
|
|
14719
|
+
console.warn("[Config] TASK_TTL_MS too low (< 1min), using 1min minimum");
|
|
14720
|
+
this.parallelAgentConfig.taskTtlMs = 60 * 1e3;
|
|
14721
|
+
}
|
|
14722
|
+
if (cleanupDelayMs > taskTtlMs) {
|
|
14723
|
+
console.warn("[Config] CLEANUP_DELAY_MS cannot exceed TASK_TTL_MS");
|
|
14724
|
+
this.parallelAgentConfig.cleanupDelayMs = Math.floor(taskTtlMs / 2);
|
|
14725
|
+
}
|
|
14726
|
+
if (minStabilityMs < 1e3) {
|
|
14727
|
+
console.warn("[Config] MIN_STABILITY_MS too low (< 1s), using 1s minimum");
|
|
14728
|
+
this.parallelAgentConfig.minStabilityMs = 1e3;
|
|
14729
|
+
}
|
|
14730
|
+
if (pollIntervalMs < 500) {
|
|
14731
|
+
console.warn("[Config] POLL_INTERVAL_MS too low (< 500ms), using 500ms minimum");
|
|
14732
|
+
this.parallelAgentConfig.pollIntervalMs = 500;
|
|
14733
|
+
}
|
|
14734
|
+
if (defaultConcurrency < 1 || defaultConcurrency > maxConcurrency) {
|
|
14735
|
+
console.warn(`[Config] DEFAULT_CONCURRENCY must be 1-${maxConcurrency}`);
|
|
14736
|
+
this.parallelAgentConfig.defaultConcurrency = Math.min(
|
|
14737
|
+
Math.max(defaultConcurrency, 1),
|
|
14738
|
+
maxConcurrency
|
|
14739
|
+
);
|
|
14740
|
+
}
|
|
14741
|
+
}
|
|
14742
|
+
validateBackgroundTaskConfig() {
|
|
14743
|
+
const { monitorIntervalMs, defaultTimeoutMs, maxCompletedTasksToKeep } = this.backgroundTaskConfig;
|
|
14744
|
+
if (monitorIntervalMs < 1e3) {
|
|
14745
|
+
console.warn("[Config] MONITOR_INTERVAL_MS too low (< 1s), using 1s minimum");
|
|
14746
|
+
this.backgroundTaskConfig.monitorIntervalMs = 1e3;
|
|
14747
|
+
}
|
|
14748
|
+
if (defaultTimeoutMs < 10 * 1e3) {
|
|
14749
|
+
console.warn("[Config] DEFAULT_TIMEOUT_MS too low (< 10s), using 10s minimum");
|
|
14750
|
+
this.backgroundTaskConfig.defaultTimeoutMs = 10 * 1e3;
|
|
14751
|
+
}
|
|
14752
|
+
if (maxCompletedTasksToKeep < 0) {
|
|
14753
|
+
console.warn("[Config] MAX_COMPLETED_TASKS must be >= 0, using 0");
|
|
14754
|
+
this.backgroundTaskConfig.maxCompletedTasksToKeep = 0;
|
|
14755
|
+
}
|
|
14756
|
+
}
|
|
14757
|
+
validateSessionConfig() {
|
|
14758
|
+
const { defaultMaxSteps, taskCommandMaxSteps } = this.sessionConfig;
|
|
14759
|
+
if (defaultMaxSteps < 1) {
|
|
14760
|
+
console.warn("[Config] DEFAULT_MAX_STEPS must be >= 1, using 1");
|
|
14761
|
+
this.sessionConfig.defaultMaxSteps = 1;
|
|
14762
|
+
}
|
|
14763
|
+
if (taskCommandMaxSteps < defaultMaxSteps) {
|
|
14764
|
+
console.warn("[Config] TASK_MAX_STEPS should be >= DEFAULT_MAX_STEPS");
|
|
14765
|
+
this.sessionConfig.taskCommandMaxSteps = defaultMaxSteps;
|
|
14766
|
+
}
|
|
14767
|
+
}
|
|
14768
|
+
// ------------------------------------------------------------------------
|
|
14769
|
+
// Helpers
|
|
14770
|
+
// ------------------------------------------------------------------------
|
|
14771
|
+
parseEnvInt(key, defaultValue) {
|
|
14772
|
+
const value = process.env[key];
|
|
14773
|
+
if (value === void 0) return defaultValue;
|
|
14774
|
+
const parsed = parseInt(value, 10);
|
|
14775
|
+
if (isNaN(parsed)) {
|
|
14776
|
+
console.warn(`[Config] Invalid ${key} value: "${value}", using default ${defaultValue}`);
|
|
14777
|
+
return defaultValue;
|
|
14778
|
+
}
|
|
14779
|
+
return parsed;
|
|
14780
|
+
}
|
|
14781
|
+
// ------------------------------------------------------------------------
|
|
14782
|
+
// Export / Debug
|
|
14783
|
+
// ------------------------------------------------------------------------
|
|
14784
|
+
exportConfigs() {
|
|
14785
|
+
console.log("\n\u{1F4CB} Current Configuration:\n");
|
|
14786
|
+
console.log("\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");
|
|
14787
|
+
console.log("Parallel Agent:");
|
|
14788
|
+
console.log(` Task TTL: ${this.parallelAgentConfig.taskTtlMs / 1e3}s`);
|
|
14789
|
+
console.log(` Cleanup Delay: ${this.parallelAgentConfig.cleanupDelayMs / 1e3}s`);
|
|
14790
|
+
console.log(` Min Stability: ${this.parallelAgentConfig.minStabilityMs / 1e3}s`);
|
|
14791
|
+
console.log(` Poll Interval: ${this.parallelAgentConfig.pollIntervalMs / 1e3}s`);
|
|
14792
|
+
console.log(` Default Concurrency: ${this.parallelAgentConfig.defaultConcurrency}`);
|
|
14793
|
+
console.log(` Max Concurrency: ${this.parallelAgentConfig.maxConcurrency}`);
|
|
14794
|
+
console.log(` Debug: ${this.parallelAgentConfig.enableDebug}`);
|
|
14795
|
+
console.log("");
|
|
14796
|
+
console.log("Background Task:");
|
|
14797
|
+
console.log(` Monitor Interval: ${this.backgroundTaskConfig.monitorIntervalMs / 1e3}s`);
|
|
14798
|
+
console.log(` Default Timeout: ${this.backgroundTaskConfig.defaultTimeoutMs / 1e3}s`);
|
|
14799
|
+
console.log(` Max Completed Tasks: ${this.backgroundTaskConfig.maxCompletedTasksToKeep}`);
|
|
14800
|
+
console.log(` Storage Dir: ${this.backgroundTaskConfig.storageDir}`);
|
|
14801
|
+
console.log(` Debug: ${this.backgroundTaskConfig.enableDebug}`);
|
|
14802
|
+
console.log("");
|
|
14803
|
+
console.log("Session:");
|
|
14804
|
+
console.log(` Default Max Steps: ${this.sessionConfig.defaultMaxSteps}`);
|
|
14805
|
+
console.log(` Task Max Steps: ${this.sessionConfig.taskCommandMaxSteps}`);
|
|
14806
|
+
console.log("\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\n");
|
|
14807
|
+
}
|
|
14808
|
+
};
|
|
14809
|
+
var configManager = ConfigManager.getInstance();
|
|
14810
|
+
|
|
14811
|
+
// src/core/async-agent.ts
|
|
14812
|
+
var TASK_TTL_MS2 = configManager.getParallelAgentConfig().taskTtlMs;
|
|
14813
|
+
var CLEANUP_DELAY_MS2 = configManager.getParallelAgentConfig().cleanupDelayMs;
|
|
14814
|
+
var MIN_STABILITY_MS2 = configManager.getParallelAgentConfig().minStabilityMs;
|
|
14815
|
+
var POLL_INTERVAL_MS = configManager.getParallelAgentConfig().pollIntervalMs;
|
|
14816
|
+
var DEFAULT_CONCURRENCY = configManager.getParallelAgentConfig().defaultConcurrency;
|
|
14817
|
+
var MAX_CONCURRENCY = configManager.getParallelAgentConfig().maxConcurrency;
|
|
14818
|
+
var DEBUG = configManager.getParallelAgentConfig().enableDebug;
|
|
13889
14819
|
var log = (...args) => {
|
|
13890
14820
|
if (DEBUG) console.log("[parallel-agent]", ...args);
|
|
13891
14821
|
};
|
|
@@ -13894,7 +14824,9 @@ var ConcurrencyController = class {
|
|
|
13894
14824
|
queues = /* @__PURE__ */ new Map();
|
|
13895
14825
|
limits = /* @__PURE__ */ new Map();
|
|
13896
14826
|
setLimit(key, limit) {
|
|
13897
|
-
|
|
14827
|
+
const cappedLimit = Math.min(limit, MAX_CONCURRENCY);
|
|
14828
|
+
this.limits.set(key, cappedLimit);
|
|
14829
|
+
log(`Set limit for ${key}: ${cappedLimit}`);
|
|
13898
14830
|
}
|
|
13899
14831
|
getLimit(key) {
|
|
13900
14832
|
return this.limits.get(key) ?? DEFAULT_CONCURRENCY;
|
|
@@ -13934,6 +14866,21 @@ var ConcurrencyController = class {
|
|
|
13934
14866
|
getQueueLength(key) {
|
|
13935
14867
|
return this.queues.get(key)?.length ?? 0;
|
|
13936
14868
|
}
|
|
14869
|
+
updateConcurrency(agentType, newLimit) {
|
|
14870
|
+
this.setLimit(agentType, newLimit);
|
|
14871
|
+
log(`Updated concurrency for ${agentType}: ${newLimit}`);
|
|
14872
|
+
}
|
|
14873
|
+
getStats() {
|
|
14874
|
+
const stats = {};
|
|
14875
|
+
for (const [agentType, limit] of this.limits.entries()) {
|
|
14876
|
+
stats[agentType] = {
|
|
14877
|
+
running: this.counts.get(agentType) ?? 0,
|
|
14878
|
+
queued: this.getQueueLength(agentType),
|
|
14879
|
+
limit
|
|
14880
|
+
};
|
|
14881
|
+
}
|
|
14882
|
+
return stats;
|
|
14883
|
+
}
|
|
13937
14884
|
};
|
|
13938
14885
|
var ParallelAgentManager = class _ParallelAgentManager {
|
|
13939
14886
|
static _instance;
|
|
@@ -14113,11 +15060,23 @@ var ParallelAgentManager = class _ParallelAgentManager {
|
|
|
14113
15060
|
}
|
|
14114
15061
|
}
|
|
14115
15062
|
/**
|
|
14116
|
-
|
|
14117
|
-
|
|
15063
|
+
* Set concurrency limit for agent type
|
|
15064
|
+
*/
|
|
14118
15065
|
setConcurrencyLimit(agentType, limit) {
|
|
14119
15066
|
this.concurrency.setLimit(agentType, limit);
|
|
14120
15067
|
}
|
|
15068
|
+
/**
|
|
15069
|
+
* Get concurrency statistics for all agent types
|
|
15070
|
+
*/
|
|
15071
|
+
getConcurrencyStats() {
|
|
15072
|
+
return this.concurrency.getStats();
|
|
15073
|
+
}
|
|
15074
|
+
/**
|
|
15075
|
+
* Update concurrency limit dynamically at runtime
|
|
15076
|
+
*/
|
|
15077
|
+
updateConcurrency(agentType, newLimit) {
|
|
15078
|
+
this.concurrency.updateConcurrency(agentType, newLimit);
|
|
15079
|
+
}
|
|
14121
15080
|
/**
|
|
14122
15081
|
* Get pending notification count
|
|
14123
15082
|
*/
|
|
@@ -14197,7 +15156,7 @@ var ParallelAgentManager = class _ParallelAgentManager {
|
|
|
14197
15156
|
const sessionStatus = allStatuses[task.sessionID];
|
|
14198
15157
|
if (sessionStatus?.type === "idle") {
|
|
14199
15158
|
const elapsed = Date.now() - task.startedAt.getTime();
|
|
14200
|
-
if (elapsed <
|
|
15159
|
+
if (elapsed < MIN_STABILITY_MS2) continue;
|
|
14201
15160
|
const hasOutput = await this.validateSessionHasOutput(task.sessionID);
|
|
14202
15161
|
if (!hasOutput) continue;
|
|
14203
15162
|
task.status = "completed";
|
|
@@ -14251,7 +15210,7 @@ var ParallelAgentManager = class _ParallelAgentManager {
|
|
|
14251
15210
|
const now = Date.now();
|
|
14252
15211
|
for (const [taskId, task] of this.tasks.entries()) {
|
|
14253
15212
|
const age = now - task.startedAt.getTime();
|
|
14254
|
-
if (age >
|
|
15213
|
+
if (age > TASK_TTL_MS2) {
|
|
14255
15214
|
log(`Timeout: ${taskId} (${Math.round(age / 1e3)}s)`);
|
|
14256
15215
|
if (task.status === "running") {
|
|
14257
15216
|
task.status = "timeout";
|
|
@@ -14302,7 +15261,7 @@ var ParallelAgentManager = class _ParallelAgentManager {
|
|
|
14302
15261
|
}
|
|
14303
15262
|
this.tasks.delete(taskId);
|
|
14304
15263
|
log(`Cleaned up ${taskId} from memory`);
|
|
14305
|
-
},
|
|
15264
|
+
}, CLEANUP_DELAY_MS2);
|
|
14306
15265
|
}
|
|
14307
15266
|
// ========================================================================
|
|
14308
15267
|
// Internal: Notifications
|
|
@@ -14495,7 +15454,7 @@ Session ID: ${sessionID}`;
|
|
|
14495
15454
|
}
|
|
14496
15455
|
const POLL_INTERVAL_MS2 = 500;
|
|
14497
15456
|
const MAX_POLL_TIME_MS = 10 * 60 * 1e3;
|
|
14498
|
-
const
|
|
15457
|
+
const MIN_STABILITY_MS3 = 5e3;
|
|
14499
15458
|
const STABILITY_POLLS_REQUIRED = 3;
|
|
14500
15459
|
let stablePolls = 0;
|
|
14501
15460
|
let lastMsgCount = 0;
|
|
@@ -14509,7 +15468,7 @@ Session ID: ${sessionID}`;
|
|
|
14509
15468
|
lastMsgCount = 0;
|
|
14510
15469
|
continue;
|
|
14511
15470
|
}
|
|
14512
|
-
if (Date.now() - startTime <
|
|
15471
|
+
if (Date.now() - startTime < MIN_STABILITY_MS3) continue;
|
|
14513
15472
|
const messagesResult2 = await session.messages({
|
|
14514
15473
|
path: { id: sessionID }
|
|
14515
15474
|
});
|
|
@@ -14715,6 +15674,476 @@ function createAsyncAgentTools(manager, client) {
|
|
|
14715
15674
|
};
|
|
14716
15675
|
}
|
|
14717
15676
|
|
|
15677
|
+
// src/core/batch-processor.ts
|
|
15678
|
+
var SmartBatchProcessor = class {
|
|
15679
|
+
parallelAgentManager;
|
|
15680
|
+
tasks = /* @__PURE__ */ new Map();
|
|
15681
|
+
constructor(parallelAgentManager2) {
|
|
15682
|
+
this.parallelAgentManager = parallelAgentManager2;
|
|
15683
|
+
}
|
|
15684
|
+
/**
|
|
15685
|
+
* Process a batch of tasks with smart validation
|
|
15686
|
+
*/
|
|
15687
|
+
async processBatch(tasks, options = {
|
|
15688
|
+
concurrency: 3,
|
|
15689
|
+
maxRetries: 2,
|
|
15690
|
+
validateAfterEach: false,
|
|
15691
|
+
continueOnError: true
|
|
15692
|
+
}) {
|
|
15693
|
+
const startTime = Date.now();
|
|
15694
|
+
for (const task of tasks) {
|
|
15695
|
+
this.tasks.set(task.id, {
|
|
15696
|
+
...task,
|
|
15697
|
+
status: "pending",
|
|
15698
|
+
attempts: 0
|
|
15699
|
+
});
|
|
15700
|
+
}
|
|
15701
|
+
console.log(`
|
|
15702
|
+
\u{1F4E6} [Smart Batch] Starting ${tasks.length} tasks with concurrency ${options.concurrency}`);
|
|
15703
|
+
console.log(` Strategy: ${options.validateAfterEach ? "Validate each" : "Centralized validation"}`);
|
|
15704
|
+
await this.executePhase(tasks, options, "initial");
|
|
15705
|
+
const failedTasks = Array.from(this.tasks.values()).filter((t) => t.status === "failed" || t.status === "pending");
|
|
15706
|
+
if (failedTasks.length === 0) {
|
|
15707
|
+
console.log(`
|
|
15708
|
+
\u2705 [Smart Batch] All ${tasks.length} tasks succeeded!`);
|
|
15709
|
+
return this.buildResult(startTime, tasks);
|
|
15710
|
+
}
|
|
15711
|
+
console.log(`
|
|
15712
|
+
\u{1F50D} [Smart Batch] Validation complete: ${failedTasks.length}/${tasks.length} tasks need retry`);
|
|
15713
|
+
let retryCount = 0;
|
|
15714
|
+
while (failedTasks.length > 0 && retryCount < options.maxRetries) {
|
|
15715
|
+
retryCount++;
|
|
15716
|
+
console.log(`
|
|
15717
|
+
\u{1F504} [Smart Batch] Retry round ${retryCount}/${options.maxRetries} for ${failedTasks.length} tasks`);
|
|
15718
|
+
await this.executePhase(failedTasks, options, `retry-${retryCount}`);
|
|
15719
|
+
const stillFailed = Array.from(this.tasks.values()).filter((t) => t.status === "failed");
|
|
15720
|
+
if (stillFailed.length === failedTasks.length) {
|
|
15721
|
+
console.log(`\u26A0\uFE0F [Smart Batch] No progress in retry round ${retryCount}, stopping`);
|
|
15722
|
+
break;
|
|
15723
|
+
}
|
|
15724
|
+
}
|
|
15725
|
+
return this.buildResult(startTime, tasks);
|
|
15726
|
+
}
|
|
15727
|
+
/**
|
|
15728
|
+
* Execute a phase with concurrency control
|
|
15729
|
+
*/
|
|
15730
|
+
async executePhase(tasks, options, phase) {
|
|
15731
|
+
for (const task of tasks) {
|
|
15732
|
+
this.parallelAgentManager.setConcurrencyLimit(task.agent, options.concurrency);
|
|
15733
|
+
}
|
|
15734
|
+
const batches = [];
|
|
15735
|
+
for (let i = 0; i < tasks.length; i += options.concurrency) {
|
|
15736
|
+
batches.push(tasks.slice(i, i + options.concurrency));
|
|
15737
|
+
}
|
|
15738
|
+
for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
|
|
15739
|
+
const batch = batches[batchIndex];
|
|
15740
|
+
console.log(` [${phase}] Processing batch ${batchIndex + 1}/${batches.length} (${batch.length} tasks)`);
|
|
15741
|
+
const batchPromises = batch.map((task) => this.executeTask(task, options));
|
|
15742
|
+
await Promise.all(batchPromises);
|
|
15743
|
+
if (!options.validateAfterEach) {
|
|
15744
|
+
continue;
|
|
15745
|
+
}
|
|
15746
|
+
const failedInBatch = batch.filter((t) => t.status === "failed");
|
|
15747
|
+
if (failedInBatch.length > 0) {
|
|
15748
|
+
console.log(` [${phase}] ${failedInBatch.length} failed in batch, immediate retry`);
|
|
15749
|
+
const retryPromises = failedInBatch.map((t) => this.executeTask(t, options));
|
|
15750
|
+
await Promise.all(retryPromises);
|
|
15751
|
+
}
|
|
15752
|
+
}
|
|
15753
|
+
}
|
|
15754
|
+
/**
|
|
15755
|
+
* Execute a single task
|
|
15756
|
+
*/
|
|
15757
|
+
async executeTask(task, options) {
|
|
15758
|
+
const currentTask = this.tasks.get(task.id);
|
|
15759
|
+
if (!options.continueOnError && currentTask.status === "failed") {
|
|
15760
|
+
return;
|
|
15761
|
+
}
|
|
15762
|
+
try {
|
|
15763
|
+
currentTask.attempts++;
|
|
15764
|
+
currentTask.status = "pending";
|
|
15765
|
+
const launched = await this.parallelAgentManager.launch({
|
|
15766
|
+
agent: task.agent,
|
|
15767
|
+
description: task.description,
|
|
15768
|
+
prompt: task.prompt,
|
|
15769
|
+
parentSessionID: ""
|
|
15770
|
+
// Would need actual parent ID
|
|
15771
|
+
});
|
|
15772
|
+
const maxWaitTime = 5 * 60 * 1e3;
|
|
15773
|
+
const startTime = Date.now();
|
|
15774
|
+
while (Date.now() - startTime < maxWaitTime) {
|
|
15775
|
+
const taskData = this.parallelAgentManager.getTask(launched.id);
|
|
15776
|
+
if (!taskData) break;
|
|
15777
|
+
if (taskData.status === "completed") {
|
|
15778
|
+
currentTask.status = "success";
|
|
15779
|
+
console.log(` \u2705 [${task.id}] Success on attempt ${currentTask.attempts}`);
|
|
15780
|
+
return;
|
|
15781
|
+
}
|
|
15782
|
+
if (taskData.status === "error" || taskData.status === "timeout") {
|
|
15783
|
+
currentTask.status = "failed";
|
|
15784
|
+
currentTask.error = taskData.error || "Unknown error";
|
|
15785
|
+
console.log(` \u274C [${task.id}] Failed: ${currentTask.error}`);
|
|
15786
|
+
return;
|
|
15787
|
+
}
|
|
15788
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
15789
|
+
}
|
|
15790
|
+
currentTask.status = "failed";
|
|
15791
|
+
currentTask.error = "Timeout";
|
|
15792
|
+
} catch (error45) {
|
|
15793
|
+
currentTask.status = "failed";
|
|
15794
|
+
currentTask.error = error45 instanceof Error ? error45.message : String(error45);
|
|
15795
|
+
console.log(` \u274C [${task.id}] Exception: ${currentTask.error}`);
|
|
15796
|
+
}
|
|
15797
|
+
}
|
|
15798
|
+
/**
|
|
15799
|
+
* Build result summary
|
|
15800
|
+
*/
|
|
15801
|
+
buildResult(startTime, tasks) {
|
|
15802
|
+
const allTasks = Array.from(this.tasks.values());
|
|
15803
|
+
const successCount = allTasks.filter((t) => t.status === "success").length;
|
|
15804
|
+
const failedCount = allTasks.filter((t) => t.status === "failed").length;
|
|
15805
|
+
const retriedCount = allTasks.filter((t) => t.attempts > 1).length;
|
|
15806
|
+
return {
|
|
15807
|
+
total: tasks.length,
|
|
15808
|
+
success: successCount,
|
|
15809
|
+
failed: failedCount,
|
|
15810
|
+
retried: retriedCount,
|
|
15811
|
+
duration: Date.now() - startTime,
|
|
15812
|
+
tasks: allTasks
|
|
15813
|
+
};
|
|
15814
|
+
}
|
|
15815
|
+
/**
|
|
15816
|
+
* Export failed tasks for manual review
|
|
15817
|
+
*/
|
|
15818
|
+
exportFailedTasks() {
|
|
15819
|
+
const failedTasks = Array.from(this.tasks.values()).filter((t) => t.status === "failed");
|
|
15820
|
+
if (failedTasks.length === 0) {
|
|
15821
|
+
return "\u2705 No failed tasks to export.";
|
|
15822
|
+
}
|
|
15823
|
+
const output = failedTasks.map((task) => {
|
|
15824
|
+
return `
|
|
15825
|
+
---
|
|
15826
|
+
Task ID: ${task.id}
|
|
15827
|
+
Agent: ${task.agent}
|
|
15828
|
+
Attempts: ${task.attempts}
|
|
15829
|
+
Error: ${task.error}
|
|
15830
|
+
Description: ${task.description}
|
|
15831
|
+
---
|
|
15832
|
+
`.trim();
|
|
15833
|
+
}).join("\n");
|
|
15834
|
+
return `\u274C ${failedTasks.length} failed tasks:
|
|
15835
|
+
${output}`;
|
|
15836
|
+
}
|
|
15837
|
+
/**
|
|
15838
|
+
* Clear all tasks
|
|
15839
|
+
*/
|
|
15840
|
+
clear() {
|
|
15841
|
+
this.tasks.clear();
|
|
15842
|
+
}
|
|
15843
|
+
};
|
|
15844
|
+
|
|
15845
|
+
// src/tools/batch.ts
|
|
15846
|
+
var createProcessBatchTool = (parallelAgentManager2, client) => tool({
|
|
15847
|
+
description: `Execute a batch of tasks with intelligent validation and retry.
|
|
15848
|
+
|
|
15849
|
+
<strategy>
|
|
15850
|
+
1. Execute all tasks in parallel (respecting concurrency limits)
|
|
15851
|
+
2. Centralized validation: Identify ALL failed tasks at once
|
|
15852
|
+
3. Retry ONLY failed tasks (not everything)
|
|
15853
|
+
</strategy>
|
|
15854
|
+
|
|
15855
|
+
<benefits>
|
|
15856
|
+
- Faster than naive retry: Failed tasks batch-identified
|
|
15857
|
+
- More efficient: No redundant work on successful tasks
|
|
15858
|
+
- Controlled: Concurrency limits prevent API overload
|
|
15859
|
+
</benefits>
|
|
15860
|
+
|
|
15861
|
+
<examples>
|
|
15862
|
+
process_batch({
|
|
15863
|
+
concurrency: 10,
|
|
15864
|
+
tasks: [
|
|
15865
|
+
{ id: "task1", agent: "builder", description: "Test A", prompt: "..." },
|
|
15866
|
+
{ id: "task2", agent: "inspector", description: "Test B", prompt: "..." }
|
|
15867
|
+
]
|
|
15868
|
+
})
|
|
15869
|
+
</examples>`,
|
|
15870
|
+
args: {
|
|
15871
|
+
concurrency: tool.schema.string().describe("Concurrency limit (default: 3, max: 10)"),
|
|
15872
|
+
maxRetries: tool.schema.string().optional().describe("Maximum retry rounds (default: 2)"),
|
|
15873
|
+
validateAfterEach: tool.schema.boolean().optional().describe("Validate after each task (default: false = centralized validation)"),
|
|
15874
|
+
tasks: tool.schema.string().describe("Array of task objects in JSON format")
|
|
15875
|
+
},
|
|
15876
|
+
async execute(args) {
|
|
15877
|
+
const { concurrency, maxRetries = "2", validateAfterEach = false, tasks } = args;
|
|
15878
|
+
let taskList;
|
|
15879
|
+
try {
|
|
15880
|
+
taskList = JSON.parse(tasks);
|
|
15881
|
+
} catch {
|
|
15882
|
+
return "\u274C Invalid tasks JSON. Must be valid array.";
|
|
15883
|
+
}
|
|
15884
|
+
if (!Array.isArray(taskList)) {
|
|
15885
|
+
return "\u274C tasks must be an array of task objects.";
|
|
15886
|
+
}
|
|
15887
|
+
for (const task of taskList) {
|
|
15888
|
+
if (!task.id || !task.agent || !task.description || !task.prompt) {
|
|
15889
|
+
return `\u274C Task missing required fields (id, agent, description, prompt)`;
|
|
15890
|
+
}
|
|
15891
|
+
}
|
|
15892
|
+
const numConcurrency = parseInt(concurrency, 10);
|
|
15893
|
+
const numRetries = parseInt(maxRetries, 10);
|
|
15894
|
+
if (isNaN(numConcurrency) || numConcurrency < 1 || numConcurrency > 10) {
|
|
15895
|
+
return "\u274C Invalid concurrency. Must be 1-10.";
|
|
15896
|
+
}
|
|
15897
|
+
if (isNaN(numRetries) || numRetries < 0 || numRetries > 5) {
|
|
15898
|
+
return "\u274C Invalid maxRetries. Must be 0-5.";
|
|
15899
|
+
}
|
|
15900
|
+
const processor = new SmartBatchProcessor(parallelAgentManager2);
|
|
15901
|
+
const result = await processor.processBatch(taskList, {
|
|
15902
|
+
concurrency: numConcurrency,
|
|
15903
|
+
maxRetries: numRetries,
|
|
15904
|
+
validateAfterEach,
|
|
15905
|
+
continueOnError: true
|
|
15906
|
+
});
|
|
15907
|
+
const durationSecs = Math.floor(result.duration / 1e3);
|
|
15908
|
+
const successRate = (result.success / result.total * 100).toFixed(1);
|
|
15909
|
+
let output = `\u2705 **Batch Processing Complete**
|
|
15910
|
+
|
|
15911
|
+
| Metric | Value |
|
|
15912
|
+
|---------|--------|
|
|
15913
|
+
| **Total Tasks** | ${result.total} |
|
|
15914
|
+
| **Successful** | ${result.success} (${successRate}%) |
|
|
15915
|
+
| **Failed** | ${result.failed} |
|
|
15916
|
+
| **Retried** | ${result.retried} |
|
|
15917
|
+
| **Duration** | ${durationSecs}s |
|
|
15918
|
+
|
|
15919
|
+
`;
|
|
15920
|
+
if (result.failed > 0) {
|
|
15921
|
+
output += `\u26A0\uFE0F **Failed Tasks**
|
|
15922
|
+
|
|
15923
|
+
Use \`export_failed_tasks()\` to review failed tasks and manually fix issues.
|
|
15924
|
+
|
|
15925
|
+
---
|
|
15926
|
+
|
|
15927
|
+
Failed Task IDs:
|
|
15928
|
+
${result.tasks.filter((t) => t.status === "failed").map((t) => ` - ${t.id}`).join("\n")}
|
|
15929
|
+
---
|
|
15930
|
+
`;
|
|
15931
|
+
}
|
|
15932
|
+
return output;
|
|
15933
|
+
}
|
|
15934
|
+
});
|
|
15935
|
+
var createExportFailedTasksTool = (parallelAgentManager2) => tool({
|
|
15936
|
+
description: `Export failed tasks from the last batch for manual review.`,
|
|
15937
|
+
args: {},
|
|
15938
|
+
async execute() {
|
|
15939
|
+
const processor = new SmartBatchProcessor(parallelAgentManager2);
|
|
15940
|
+
return processor.exportFailedTasks();
|
|
15941
|
+
}
|
|
15942
|
+
});
|
|
15943
|
+
var createCompareStrategiesTool = (parallelAgentManager2) => tool({
|
|
15944
|
+
description: `Compare naive retry vs smart batch validation performance.
|
|
15945
|
+
|
|
15946
|
+
<comparison>
|
|
15947
|
+
**Naive Strategy** (current):
|
|
15948
|
+
- Concurrency: 3 fixed
|
|
15949
|
+
- Retry: Per-task immediate retry
|
|
15950
|
+
- Issue: Slow for large batches, redundant work
|
|
15951
|
+
|
|
15952
|
+
**Smart Batch Strategy** (new):
|
|
15953
|
+
- Concurrency: Configurable (up to 10)
|
|
15954
|
+
- Validation: Centralized, batch-identify failures
|
|
15955
|
+
- Retry: Only failed tasks
|
|
15956
|
+
- Benefit: Faster, less redundant work
|
|
15957
|
+
</comparison>`,
|
|
15958
|
+
args: {
|
|
15959
|
+
taskCount: tool.schema.string().describe("Number of simulated tasks (default: 100)"),
|
|
15960
|
+
concurrency1: tool.schema.string().optional().describe("Strategy 1 concurrency (default: 3)"),
|
|
15961
|
+
concurrency2: tool.schema.string().optional().describe("Strategy 2 concurrency (default: 10)")
|
|
15962
|
+
},
|
|
15963
|
+
async execute(args) {
|
|
15964
|
+
const taskCount = parseInt(args.taskCount || "100", 10);
|
|
15965
|
+
const concurrency1 = parseInt(args.concurrency1 || "3", 10);
|
|
15966
|
+
const concurrency2 = parseInt(args.concurrency2 || "10", 10);
|
|
15967
|
+
const naiveBatches = Math.ceil(taskCount / concurrency1);
|
|
15968
|
+
const naiveTime = naiveBatches * 60;
|
|
15969
|
+
const smartBatches = Math.ceil(taskCount / concurrency2);
|
|
15970
|
+
const smartTime = smartBatches * 60 + 30;
|
|
15971
|
+
const timeDiff = naiveTime - smartTime;
|
|
15972
|
+
const improvement = (timeDiff / naiveTime * 100).toFixed(1);
|
|
15973
|
+
return `\u{1F4CA} **Strategy Comparison for ${taskCount} tasks**
|
|
15974
|
+
|
|
15975
|
+
**Strategy 1: Naive (Current)**
|
|
15976
|
+
| Metric | Value |
|
|
15977
|
+
|---------|--------|
|
|
15978
|
+
| Concurrency | ${concurrency1} |
|
|
15979
|
+
| Batches | ${naiveBatches} |
|
|
15980
|
+
| Est. Time | ${naiveTime}s |
|
|
15981
|
+
|
|
15982
|
+
**Strategy 2: Smart Batch (Proposed)**
|
|
15983
|
+
| Metric | Value |
|
|
15984
|
+
|---------|--------|
|
|
15985
|
+
| Concurrency | ${concurrency2} |
|
|
15986
|
+
| Batches | ${smartBatches} |
|
|
15987
|
+
| Est. Time | ${smartTime}s |
|
|
15988
|
+
|
|
15989
|
+
**Summary**
|
|
15990
|
+
| Metric | Value |
|
|
15991
|
+
|---------|--------|
|
|
15992
|
+
| Time Saved | ${timeDiff}s (${improvement}%) |
|
|
15993
|
+
| Batches Saved | ${naiveBatches - smartBatches} |
|
|
15994
|
+
|
|
15995
|
+
Recommendation: Use **Smart Batch** with concurrency ${concurrency2} for ${timeDiff}s improvement.`;
|
|
15996
|
+
}
|
|
15997
|
+
});
|
|
15998
|
+
function createBatchTools(parallelAgentManager2, client) {
|
|
15999
|
+
return {
|
|
16000
|
+
process_batch: createProcessBatchTool(parallelAgentManager2, client),
|
|
16001
|
+
export_failed_tasks: createExportFailedTasksTool(parallelAgentManager2),
|
|
16002
|
+
compare_strategies: createCompareStrategiesTool(parallelAgentManager2)
|
|
16003
|
+
};
|
|
16004
|
+
}
|
|
16005
|
+
|
|
16006
|
+
// src/tools/config.ts
|
|
16007
|
+
var createShowConfigTool = () => tool({
|
|
16008
|
+
description: `Display current OpenCode Orchestrator configuration.
|
|
16009
|
+
|
|
16010
|
+
Shows all dynamic settings including timeouts, concurrency limits, and debug flags.`,
|
|
16011
|
+
args: {},
|
|
16012
|
+
async execute() {
|
|
16013
|
+
configManager.exportConfigs();
|
|
16014
|
+
return "";
|
|
16015
|
+
}
|
|
16016
|
+
});
|
|
16017
|
+
var createSetConcurrencyTool = (client) => tool({
|
|
16018
|
+
description: `Update concurrency limit for a specific agent type.
|
|
16019
|
+
|
|
16020
|
+
<examples>
|
|
16021
|
+
set_concurrency({ agent: "builder", limit: 5 })
|
|
16022
|
+
set_concurrency({ agent: "inspector", limit: 2 })
|
|
16023
|
+
</examples>
|
|
16024
|
+
|
|
16025
|
+
<notes>
|
|
16026
|
+
- Changes take effect immediately
|
|
16027
|
+
- Queued tasks will start when slots become available
|
|
16028
|
+
- Limit cannot exceed global max (10)
|
|
16029
|
+
</notes>`,
|
|
16030
|
+
args: {
|
|
16031
|
+
agent: tool.schema.string().describe('Agent type (e.g., "builder", "inspector", "architect")'),
|
|
16032
|
+
limit: tool.schema.string().describe("New concurrency limit (number)")
|
|
16033
|
+
},
|
|
16034
|
+
async execute(args) {
|
|
16035
|
+
const { agent, limit } = args;
|
|
16036
|
+
const numLimit = parseInt(limit, 10);
|
|
16037
|
+
if (isNaN(numLimit) || numLimit < 1) {
|
|
16038
|
+
return `\u274C Invalid limit: "${limit}". Must be a number >= 1.`;
|
|
16039
|
+
}
|
|
16040
|
+
const maxLimit = configManager.getParallelAgentConfig().maxConcurrency;
|
|
16041
|
+
if (numLimit > maxLimit) {
|
|
16042
|
+
return `\u274C Limit ${numLimit} exceeds global max ${maxLimit}. Using ${maxLimit}.`;
|
|
16043
|
+
}
|
|
16044
|
+
configManager.updateParallelAgentConfig({
|
|
16045
|
+
defaultConcurrency: numLimit
|
|
16046
|
+
});
|
|
16047
|
+
return `\u2705 **Concurrency Updated**
|
|
16048
|
+
|
|
16049
|
+
| Property | Value |
|
|
16050
|
+
|----------|-------|
|
|
16051
|
+
| **Agent** | ${agent} |
|
|
16052
|
+
| **New Limit** | ${numLimit} parallel tasks |
|
|
16053
|
+
|
|
16054
|
+
Changes take effect immediately. New tasks will respect to the new limit.`;
|
|
16055
|
+
}
|
|
16056
|
+
});
|
|
16057
|
+
var createSetTimeoutTool = () => tool({
|
|
16058
|
+
description: `Update task timeout duration.
|
|
16059
|
+
|
|
16060
|
+
<examples>
|
|
16061
|
+
set_timeout({ taskTtlMinutes: 45 })
|
|
16062
|
+
set_timeout({ cleanupDelayMinutes: 2 })
|
|
16063
|
+
</examples>`,
|
|
16064
|
+
args: {
|
|
16065
|
+
taskTtlMinutes: tool.schema.string().optional().describe("Task timeout in minutes (default: 30)"),
|
|
16066
|
+
cleanupDelayMinutes: tool.schema.string().optional().describe("Cleanup delay after completion in minutes (default: 5)")
|
|
16067
|
+
},
|
|
16068
|
+
async execute(args) {
|
|
16069
|
+
const { taskTtlMinutes, cleanupDelayMinutes } = args;
|
|
16070
|
+
const updates = {};
|
|
16071
|
+
if (taskTtlMinutes) {
|
|
16072
|
+
const ttl = parseInt(taskTtlMinutes, 10);
|
|
16073
|
+
if (isNaN(ttl) || ttl < 1) {
|
|
16074
|
+
return `\u274C Invalid taskTtlMinutes: "${taskTtlMinutes}". Must be number >= 1.`;
|
|
16075
|
+
}
|
|
16076
|
+
updates.taskTtlMs = ttl * 60 * 1e3;
|
|
16077
|
+
}
|
|
16078
|
+
if (cleanupDelayMinutes) {
|
|
16079
|
+
const delay = parseInt(cleanupDelayMinutes, 10);
|
|
16080
|
+
if (isNaN(delay) || delay < 0) {
|
|
16081
|
+
return `\u274C Invalid cleanupDelayMinutes: "${cleanupDelayMinutes}". Must be number >= 0.`;
|
|
16082
|
+
}
|
|
16083
|
+
updates.cleanupDelayMs = delay * 60 * 1e3;
|
|
16084
|
+
}
|
|
16085
|
+
if (Object.keys(updates).length === 0) {
|
|
16086
|
+
return "\u26A0\uFE0F No changes specified. Provide at least one parameter.";
|
|
16087
|
+
}
|
|
16088
|
+
configManager.updateParallelAgentConfig(updates);
|
|
16089
|
+
let output = "\u2705 **Timeout Configuration Updated**\n\n";
|
|
16090
|
+
if (updates.taskTtlMs) {
|
|
16091
|
+
output += `| Task TTL | ${updates.taskTtlMs / 60 / 1e3} minutes |
|
|
16092
|
+
`;
|
|
16093
|
+
}
|
|
16094
|
+
if (updates.cleanupDelayMs) {
|
|
16095
|
+
output += `| Cleanup Delay | ${updates.cleanupDelayMs / 60 / 1e3} minutes |
|
|
16096
|
+
`;
|
|
16097
|
+
}
|
|
16098
|
+
output += "\nChanges take effect immediately.";
|
|
16099
|
+
return output;
|
|
16100
|
+
}
|
|
16101
|
+
});
|
|
16102
|
+
var createSetDebugTool = () => tool({
|
|
16103
|
+
description: `Enable or disable debug logging.
|
|
16104
|
+
|
|
16105
|
+
<examples>
|
|
16106
|
+
set_debug({ component: "parallel_agent", enable: true })
|
|
16107
|
+
set_debug({ component: "background_task", enable: false })
|
|
16108
|
+
</examples>`,
|
|
16109
|
+
args: {
|
|
16110
|
+
component: tool.schema.string().describe('Component to debug: "parallel_agent", "background_task", or "all"'),
|
|
16111
|
+
enable: tool.schema.boolean().describe("Enable (true) or disable (false) debug logs")
|
|
16112
|
+
},
|
|
16113
|
+
async execute(args) {
|
|
16114
|
+
const { component, enable } = args;
|
|
16115
|
+
const enableValue = enable ? "true" : "false";
|
|
16116
|
+
if (component === "parallel_agent" || component === "all") {
|
|
16117
|
+
process.env.OPENCODE_DEBUG_PARALLEL = enableValue;
|
|
16118
|
+
configManager.updateParallelAgentConfig({
|
|
16119
|
+
enableDebug: enable
|
|
16120
|
+
});
|
|
16121
|
+
}
|
|
16122
|
+
if (component === "background_task" || component === "all") {
|
|
16123
|
+
process.env.OPENCODE_DEBUG_BACKGROUND = enableValue;
|
|
16124
|
+
configManager.updateBackgroundTaskConfig({
|
|
16125
|
+
enableDebug: enable
|
|
16126
|
+
});
|
|
16127
|
+
}
|
|
16128
|
+
return `\u2705 **Debug Logging ${enable ? "Enabled" : "Disabled"}**
|
|
16129
|
+
|
|
16130
|
+
| Component | Debug Status |
|
|
16131
|
+
|-----------|--------------|
|
|
16132
|
+
| ${component === "all" ? "parallel_agent" : component} | ${enable ? "\u{1F527} Enabled" : "\u{1F507} Disabled"} |
|
|
16133
|
+
${component === "all" ? "| background_task | \u{1F527} Enabled |" : ""}
|
|
16134
|
+
|
|
16135
|
+
Changes take effect immediately.`;
|
|
16136
|
+
}
|
|
16137
|
+
});
|
|
16138
|
+
function createConfigTools(client) {
|
|
16139
|
+
return {
|
|
16140
|
+
show_config: createShowConfigTool(),
|
|
16141
|
+
set_concurrency: createSetConcurrencyTool(client),
|
|
16142
|
+
set_timeout: createSetTimeoutTool(),
|
|
16143
|
+
set_debug: createSetDebugTool()
|
|
16144
|
+
};
|
|
16145
|
+
}
|
|
16146
|
+
|
|
14718
16147
|
// src/utils/common.ts
|
|
14719
16148
|
function detectSlashCommand(text) {
|
|
14720
16149
|
const match = text.trim().match(/^\/([a-zA-Z0-9_-]+)(?:\s+(.*))?$/);
|
|
@@ -14738,110 +16167,6 @@ function formatElapsedTime(startMs, endMs = Date.now()) {
|
|
|
14738
16167
|
return parts.join(" ");
|
|
14739
16168
|
}
|
|
14740
16169
|
|
|
14741
|
-
// src/utils/sanity.ts
|
|
14742
|
-
function checkOutputSanity(text) {
|
|
14743
|
-
if (!text || text.length < 50) {
|
|
14744
|
-
return { isHealthy: true, severity: "ok" };
|
|
14745
|
-
}
|
|
14746
|
-
if (/(.)\1{15,}/.test(text)) {
|
|
14747
|
-
return {
|
|
14748
|
-
isHealthy: false,
|
|
14749
|
-
reason: "Single character repetition detected",
|
|
14750
|
-
severity: "critical"
|
|
14751
|
-
};
|
|
14752
|
-
}
|
|
14753
|
-
if (/(.{2,6})\1{8,}/.test(text)) {
|
|
14754
|
-
return {
|
|
14755
|
-
isHealthy: false,
|
|
14756
|
-
reason: "Pattern loop detected",
|
|
14757
|
-
severity: "critical"
|
|
14758
|
-
};
|
|
14759
|
-
}
|
|
14760
|
-
if (text.length > 200) {
|
|
14761
|
-
const cleanText = text.replace(/\s/g, "");
|
|
14762
|
-
if (cleanText.length > 100) {
|
|
14763
|
-
const uniqueChars = new Set(cleanText).size;
|
|
14764
|
-
const ratio = uniqueChars / cleanText.length;
|
|
14765
|
-
if (ratio < 0.02) {
|
|
14766
|
-
return {
|
|
14767
|
-
isHealthy: false,
|
|
14768
|
-
reason: "Low information density",
|
|
14769
|
-
severity: "critical"
|
|
14770
|
-
};
|
|
14771
|
-
}
|
|
14772
|
-
}
|
|
14773
|
-
}
|
|
14774
|
-
const boxChars = (text.match(/[\u2500-\u257f\u2580-\u259f\u2800-\u28ff]/g) || []).length;
|
|
14775
|
-
if (boxChars > 100 && boxChars / text.length > 0.3) {
|
|
14776
|
-
return {
|
|
14777
|
-
isHealthy: false,
|
|
14778
|
-
reason: "Visual gibberish detected",
|
|
14779
|
-
severity: "critical"
|
|
14780
|
-
};
|
|
14781
|
-
}
|
|
14782
|
-
const lines = text.split("\n").filter((l) => l.trim().length > 10);
|
|
14783
|
-
if (lines.length > 10) {
|
|
14784
|
-
const lineSet = new Set(lines);
|
|
14785
|
-
if (lineSet.size < lines.length * 0.2) {
|
|
14786
|
-
return {
|
|
14787
|
-
isHealthy: false,
|
|
14788
|
-
reason: "Excessive line repetition",
|
|
14789
|
-
severity: "warning"
|
|
14790
|
-
};
|
|
14791
|
-
}
|
|
14792
|
-
}
|
|
14793
|
-
const cjkChars = (text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []).length;
|
|
14794
|
-
if (cjkChars > 200) {
|
|
14795
|
-
const uniqueCjk = new Set(
|
|
14796
|
-
text.match(/[\u4e00-\u9fff\u3400-\u4dbf]/g) || []
|
|
14797
|
-
).size;
|
|
14798
|
-
if (uniqueCjk < 10 && cjkChars / uniqueCjk > 20) {
|
|
14799
|
-
return {
|
|
14800
|
-
isHealthy: false,
|
|
14801
|
-
reason: "CJK character spam detected",
|
|
14802
|
-
severity: "critical"
|
|
14803
|
-
};
|
|
14804
|
-
}
|
|
14805
|
-
}
|
|
14806
|
-
return { isHealthy: true, severity: "ok" };
|
|
14807
|
-
}
|
|
14808
|
-
var RECOVERY_PROMPT = `<anomaly_recovery>
|
|
14809
|
-
\u26A0\uFE0F SYSTEM NOTICE: Previous output was malformed (gibberish/loop detected).
|
|
14810
|
-
|
|
14811
|
-
<recovery_protocol>
|
|
14812
|
-
1. DISCARD the corrupted output completely - do not reference it
|
|
14813
|
-
2. RECALL the original mission objective
|
|
14814
|
-
3. IDENTIFY the last confirmed successful step
|
|
14815
|
-
4. RESTART with a simpler, more focused approach
|
|
14816
|
-
</recovery_protocol>
|
|
14817
|
-
|
|
14818
|
-
<instructions>
|
|
14819
|
-
- If a sub-agent produced bad output: try a different agent or simpler task
|
|
14820
|
-
- If stuck in a loop: break down the task into smaller pieces
|
|
14821
|
-
- If context seems corrupted: call recorder to restore context
|
|
14822
|
-
- THINK in English for maximum stability
|
|
14823
|
-
</instructions>
|
|
14824
|
-
|
|
14825
|
-
What was the original task? Proceed from the last known good state.
|
|
14826
|
-
</anomaly_recovery>`;
|
|
14827
|
-
var ESCALATION_PROMPT = `<critical_anomaly>
|
|
14828
|
-
\u{1F6A8} CRITICAL: Multiple consecutive malformed outputs detected.
|
|
14829
|
-
|
|
14830
|
-
<emergency_protocol>
|
|
14831
|
-
1. STOP current execution path immediately
|
|
14832
|
-
2. DO NOT continue with the same approach - it is failing
|
|
14833
|
-
3. CALL architect for a completely new strategy
|
|
14834
|
-
4. If architect also fails: report status to user and await guidance
|
|
14835
|
-
</emergency_protocol>
|
|
14836
|
-
|
|
14837
|
-
<diagnosis>
|
|
14838
|
-
The current approach is producing corrupted output.
|
|
14839
|
-
This may indicate: context overload, model instability, or task complexity.
|
|
14840
|
-
</diagnosis>
|
|
14841
|
-
|
|
14842
|
-
Request a fresh plan from architect with reduced scope.
|
|
14843
|
-
</critical_anomaly>`;
|
|
14844
|
-
|
|
14845
16170
|
// src/index.ts
|
|
14846
16171
|
var PLUGIN_VERSION = "0.2.4";
|
|
14847
16172
|
var DEFAULT_MAX_STEPS = 500;
|
|
@@ -14875,6 +16200,8 @@ var OrchestratorPlugin = async (input) => {
|
|
|
14875
16200
|
const sessions = /* @__PURE__ */ new Map();
|
|
14876
16201
|
const parallelAgentManager2 = ParallelAgentManager.getInstance(client, directory);
|
|
14877
16202
|
const asyncAgentTools = createAsyncAgentTools(parallelAgentManager2, client);
|
|
16203
|
+
const batchTools = createBatchTools(parallelAgentManager2, client);
|
|
16204
|
+
const configTools = createConfigTools(client);
|
|
14878
16205
|
return {
|
|
14879
16206
|
// -----------------------------------------------------------------
|
|
14880
16207
|
// Tools we expose to the LLM
|
|
@@ -14892,7 +16219,13 @@ var OrchestratorPlugin = async (input) => {
|
|
|
14892
16219
|
list_background: listBackgroundTool,
|
|
14893
16220
|
kill_background: killBackgroundTool,
|
|
14894
16221
|
// Async agent tools - spawn agents in parallel sessions
|
|
14895
|
-
...asyncAgentTools
|
|
16222
|
+
...asyncAgentTools,
|
|
16223
|
+
// Git tools - branch info and status
|
|
16224
|
+
git_branch: gitBranchTool(directory),
|
|
16225
|
+
// Smart batch tools - centralized validation and retry
|
|
16226
|
+
...batchTools,
|
|
16227
|
+
// Configuration tools - dynamic runtime settings
|
|
16228
|
+
...configTools
|
|
14896
16229
|
},
|
|
14897
16230
|
// -----------------------------------------------------------------
|
|
14898
16231
|
// Config hook - registers our commands and agents with OpenCode
|