opencode-swarm-plugin 0.12.4 → 0.12.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/.beads/issues.jsonl +224 -0
- package/README.md +94 -106
- package/bin/swarm.ts +6 -7
- package/dist/index.js +409 -71
- package/dist/plugin.js +376 -58
- package/examples/commands/swarm.md +51 -216
- package/package.json +1 -1
- package/src/agent-mail.ts +183 -37
- package/src/beads.ts +75 -20
- package/src/learning.ts +277 -0
- package/src/rate-limiter.ts +12 -6
- package/src/schemas/bead.ts +4 -4
- package/src/schemas/evaluation.ts +2 -2
- package/src/schemas/task.ts +3 -3
- package/src/storage.ts +37 -15
- package/src/swarm.ts +189 -1
- package/examples/agents/swarm-planner.md +0 -138
package/src/swarm.ts
CHANGED
|
@@ -31,9 +31,13 @@ import {
|
|
|
31
31
|
DecompositionStrategySchema,
|
|
32
32
|
scoreImplicitFeedback,
|
|
33
33
|
outcomeToFeedback,
|
|
34
|
+
ErrorAccumulator,
|
|
35
|
+
ErrorEntrySchema,
|
|
34
36
|
type OutcomeSignals,
|
|
35
37
|
type ScoredOutcome,
|
|
36
38
|
type FeedbackEvent,
|
|
39
|
+
type ErrorEntry,
|
|
40
|
+
type ErrorType,
|
|
37
41
|
type DecompositionStrategy as LearningDecompositionStrategy,
|
|
38
42
|
DEFAULT_LEARNING_CONFIG,
|
|
39
43
|
} from "./learning";
|
|
@@ -604,6 +608,8 @@ Begin work on your subtask now.`;
|
|
|
604
608
|
*
|
|
605
609
|
* This is a cleaner version of SUBTASK_PROMPT that's easier to parse.
|
|
606
610
|
* Agents MUST use Agent Mail for communication and beads for tracking.
|
|
611
|
+
*
|
|
612
|
+
* Supports {error_context} placeholder for retry prompts.
|
|
607
613
|
*/
|
|
608
614
|
export const SUBTASK_PROMPT_V2 = `You are a swarm agent working on: **{subtask_title}**
|
|
609
615
|
|
|
@@ -622,6 +628,10 @@ Only modify these files. Need others? Message the coordinator.
|
|
|
622
628
|
## Context
|
|
623
629
|
{shared_context}
|
|
624
630
|
|
|
631
|
+
{compressed_context}
|
|
632
|
+
|
|
633
|
+
{error_context}
|
|
634
|
+
|
|
625
635
|
## MANDATORY: Use These Tools
|
|
626
636
|
|
|
627
637
|
### Agent Mail - communicate with the swarm
|
|
@@ -663,12 +673,20 @@ export function formatSubtaskPromptV2(params: {
|
|
|
663
673
|
subtask_description: string;
|
|
664
674
|
files: string[];
|
|
665
675
|
shared_context?: string;
|
|
676
|
+
compressed_context?: string;
|
|
677
|
+
error_context?: string;
|
|
666
678
|
}): string {
|
|
667
679
|
const fileList =
|
|
668
680
|
params.files.length > 0
|
|
669
681
|
? params.files.map((f) => `- \`${f}\``).join("\n")
|
|
670
682
|
: "(no specific files - use judgment)";
|
|
671
683
|
|
|
684
|
+
const compressedSection = params.compressed_context
|
|
685
|
+
? params.compressed_context
|
|
686
|
+
: "";
|
|
687
|
+
|
|
688
|
+
const errorSection = params.error_context ? params.error_context : "";
|
|
689
|
+
|
|
672
690
|
return SUBTASK_PROMPT_V2.replace(/{bead_id}/g, params.bead_id)
|
|
673
691
|
.replace(/{epic_id}/g, params.epic_id)
|
|
674
692
|
.replace("{subtask_title}", params.subtask_title)
|
|
@@ -677,7 +695,9 @@ export function formatSubtaskPromptV2(params: {
|
|
|
677
695
|
params.subtask_description || "(see title)",
|
|
678
696
|
)
|
|
679
697
|
.replace("{file_list}", fileList)
|
|
680
|
-
.replace("{shared_context}", params.shared_context || "(none)")
|
|
698
|
+
.replace("{shared_context}", params.shared_context || "(none)")
|
|
699
|
+
.replace("{compressed_context}", compressedSection)
|
|
700
|
+
.replace("{error_context}", errorSection);
|
|
681
701
|
}
|
|
682
702
|
|
|
683
703
|
/**
|
|
@@ -1343,6 +1363,19 @@ export const swarm_validate_decomposition = tool({
|
|
|
1343
1363
|
for (let i = 0; i < validated.subtasks.length; i++) {
|
|
1344
1364
|
const deps = validated.subtasks[i].dependencies;
|
|
1345
1365
|
for (const dep of deps) {
|
|
1366
|
+
// Check bounds first
|
|
1367
|
+
if (dep < 0 || dep >= validated.subtasks.length) {
|
|
1368
|
+
return JSON.stringify(
|
|
1369
|
+
{
|
|
1370
|
+
valid: false,
|
|
1371
|
+
error: `Invalid dependency: subtask ${i} depends on ${dep}, but only ${validated.subtasks.length} subtasks exist (indices 0-${validated.subtasks.length - 1})`,
|
|
1372
|
+
hint: "Dependency index is out of bounds",
|
|
1373
|
+
},
|
|
1374
|
+
null,
|
|
1375
|
+
2,
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1378
|
+
// Check forward references
|
|
1346
1379
|
if (dep >= i) {
|
|
1347
1380
|
return JSON.stringify(
|
|
1348
1381
|
{
|
|
@@ -1893,6 +1926,9 @@ export const swarm_record_outcome = tool({
|
|
|
1893
1926
|
DEFAULT_LEARNING_CONFIG,
|
|
1894
1927
|
);
|
|
1895
1928
|
|
|
1929
|
+
// Get error patterns from accumulator
|
|
1930
|
+
const errorStats = await globalErrorAccumulator.getErrorStats(args.bead_id);
|
|
1931
|
+
|
|
1896
1932
|
// Generate feedback events for each criterion
|
|
1897
1933
|
const criteriaToScore = args.criteria ?? [
|
|
1898
1934
|
"type_safe",
|
|
@@ -1907,6 +1943,14 @@ export const swarm_record_outcome = tool({
|
|
|
1907
1943
|
event.context =
|
|
1908
1944
|
`${event.context || ""} [strategy: ${args.strategy}]`.trim();
|
|
1909
1945
|
}
|
|
1946
|
+
// Include error patterns in feedback context
|
|
1947
|
+
if (errorStats.total > 0) {
|
|
1948
|
+
const errorSummary = Object.entries(errorStats.by_type)
|
|
1949
|
+
.map(([type, count]) => `${type}:${count}`)
|
|
1950
|
+
.join(", ");
|
|
1951
|
+
event.context =
|
|
1952
|
+
`${event.context || ""} [errors: ${errorSummary}]`.trim();
|
|
1953
|
+
}
|
|
1910
1954
|
return event;
|
|
1911
1955
|
});
|
|
1912
1956
|
|
|
@@ -1922,6 +1966,7 @@ export const swarm_record_outcome = tool({
|
|
|
1922
1966
|
},
|
|
1923
1967
|
},
|
|
1924
1968
|
feedback_events: feedbackEvents,
|
|
1969
|
+
error_patterns: errorStats,
|
|
1925
1970
|
summary: {
|
|
1926
1971
|
feedback_type: scored.type,
|
|
1927
1972
|
duration_seconds: Math.round(args.duration_ms / 1000),
|
|
@@ -1929,6 +1974,8 @@ export const swarm_record_outcome = tool({
|
|
|
1929
1974
|
retry_count: args.retry_count ?? 0,
|
|
1930
1975
|
success: args.success,
|
|
1931
1976
|
strategy: args.strategy,
|
|
1977
|
+
accumulated_errors: errorStats.total,
|
|
1978
|
+
unresolved_errors: errorStats.unresolved,
|
|
1932
1979
|
},
|
|
1933
1980
|
note: "Feedback events should be stored for criterion weight calculation. Use learning.ts functions to apply weights.",
|
|
1934
1981
|
},
|
|
@@ -2209,6 +2256,144 @@ export const swarm_evaluation_prompt = tool({
|
|
|
2209
2256
|
},
|
|
2210
2257
|
});
|
|
2211
2258
|
|
|
2259
|
+
// ============================================================================
|
|
2260
|
+
// Error Accumulator
|
|
2261
|
+
// ============================================================================
|
|
2262
|
+
|
|
2263
|
+
/**
|
|
2264
|
+
* Global error accumulator for tracking errors across subtasks
|
|
2265
|
+
*
|
|
2266
|
+
* This is a session-level singleton that accumulates errors during
|
|
2267
|
+
* swarm execution for feeding into retry prompts.
|
|
2268
|
+
*/
|
|
2269
|
+
const globalErrorAccumulator = new ErrorAccumulator();
|
|
2270
|
+
|
|
2271
|
+
/**
|
|
2272
|
+
* Record an error during subtask execution
|
|
2273
|
+
*
|
|
2274
|
+
* Implements pattern from "Patterns for Building AI Agents" p.40:
|
|
2275
|
+
* "Good agents examine and correct errors when something goes wrong"
|
|
2276
|
+
*
|
|
2277
|
+
* Errors are accumulated and can be fed into retry prompts to help
|
|
2278
|
+
* agents learn from past failures.
|
|
2279
|
+
*/
|
|
2280
|
+
export const swarm_accumulate_error = tool({
|
|
2281
|
+
description:
|
|
2282
|
+
"Record an error during subtask execution. Errors feed into retry prompts.",
|
|
2283
|
+
args: {
|
|
2284
|
+
bead_id: tool.schema.string().describe("Bead ID where error occurred"),
|
|
2285
|
+
error_type: tool.schema
|
|
2286
|
+
.enum(["validation", "timeout", "conflict", "tool_failure", "unknown"])
|
|
2287
|
+
.describe("Category of error"),
|
|
2288
|
+
message: tool.schema.string().describe("Human-readable error message"),
|
|
2289
|
+
stack_trace: tool.schema
|
|
2290
|
+
.string()
|
|
2291
|
+
.optional()
|
|
2292
|
+
.describe("Stack trace for debugging"),
|
|
2293
|
+
tool_name: tool.schema.string().optional().describe("Tool that failed"),
|
|
2294
|
+
context: tool.schema
|
|
2295
|
+
.string()
|
|
2296
|
+
.optional()
|
|
2297
|
+
.describe("What was happening when error occurred"),
|
|
2298
|
+
},
|
|
2299
|
+
async execute(args) {
|
|
2300
|
+
const entry = await globalErrorAccumulator.recordError(
|
|
2301
|
+
args.bead_id,
|
|
2302
|
+
args.error_type as ErrorType,
|
|
2303
|
+
args.message,
|
|
2304
|
+
{
|
|
2305
|
+
stack_trace: args.stack_trace,
|
|
2306
|
+
tool_name: args.tool_name,
|
|
2307
|
+
context: args.context,
|
|
2308
|
+
},
|
|
2309
|
+
);
|
|
2310
|
+
|
|
2311
|
+
return JSON.stringify(
|
|
2312
|
+
{
|
|
2313
|
+
success: true,
|
|
2314
|
+
error_id: entry.id,
|
|
2315
|
+
bead_id: entry.bead_id,
|
|
2316
|
+
error_type: entry.error_type,
|
|
2317
|
+
message: entry.message,
|
|
2318
|
+
timestamp: entry.timestamp,
|
|
2319
|
+
note: "Error recorded for retry context. Use swarm_get_error_context to retrieve accumulated errors.",
|
|
2320
|
+
},
|
|
2321
|
+
null,
|
|
2322
|
+
2,
|
|
2323
|
+
);
|
|
2324
|
+
},
|
|
2325
|
+
});
|
|
2326
|
+
|
|
2327
|
+
/**
|
|
2328
|
+
* Get accumulated errors for a bead to feed into retry prompts
|
|
2329
|
+
*
|
|
2330
|
+
* Returns formatted error context that can be injected into retry prompts
|
|
2331
|
+
* to help agents learn from past failures.
|
|
2332
|
+
*/
|
|
2333
|
+
export const swarm_get_error_context = tool({
|
|
2334
|
+
description:
|
|
2335
|
+
"Get accumulated errors for a bead. Returns formatted context for retry prompts.",
|
|
2336
|
+
args: {
|
|
2337
|
+
bead_id: tool.schema.string().describe("Bead ID to get errors for"),
|
|
2338
|
+
include_resolved: tool.schema
|
|
2339
|
+
.boolean()
|
|
2340
|
+
.optional()
|
|
2341
|
+
.describe("Include resolved errors (default: false)"),
|
|
2342
|
+
},
|
|
2343
|
+
async execute(args) {
|
|
2344
|
+
const errorContext = await globalErrorAccumulator.getErrorContext(
|
|
2345
|
+
args.bead_id,
|
|
2346
|
+
args.include_resolved ?? false,
|
|
2347
|
+
);
|
|
2348
|
+
|
|
2349
|
+
const stats = await globalErrorAccumulator.getErrorStats(args.bead_id);
|
|
2350
|
+
|
|
2351
|
+
return JSON.stringify(
|
|
2352
|
+
{
|
|
2353
|
+
bead_id: args.bead_id,
|
|
2354
|
+
error_context: errorContext,
|
|
2355
|
+
stats: {
|
|
2356
|
+
total_errors: stats.total,
|
|
2357
|
+
unresolved: stats.unresolved,
|
|
2358
|
+
by_type: stats.by_type,
|
|
2359
|
+
},
|
|
2360
|
+
has_errors: errorContext.length > 0,
|
|
2361
|
+
usage:
|
|
2362
|
+
"Inject error_context into retry prompt using {error_context} placeholder",
|
|
2363
|
+
},
|
|
2364
|
+
null,
|
|
2365
|
+
2,
|
|
2366
|
+
);
|
|
2367
|
+
},
|
|
2368
|
+
});
|
|
2369
|
+
|
|
2370
|
+
/**
|
|
2371
|
+
* Mark an error as resolved
|
|
2372
|
+
*
|
|
2373
|
+
* Call this after an agent successfully addresses an error to update
|
|
2374
|
+
* the accumulator state.
|
|
2375
|
+
*/
|
|
2376
|
+
export const swarm_resolve_error = tool({
|
|
2377
|
+
description:
|
|
2378
|
+
"Mark an error as resolved after fixing it. Updates error accumulator state.",
|
|
2379
|
+
args: {
|
|
2380
|
+
error_id: tool.schema.string().describe("Error ID to mark as resolved"),
|
|
2381
|
+
},
|
|
2382
|
+
async execute(args) {
|
|
2383
|
+
await globalErrorAccumulator.resolveError(args.error_id);
|
|
2384
|
+
|
|
2385
|
+
return JSON.stringify(
|
|
2386
|
+
{
|
|
2387
|
+
success: true,
|
|
2388
|
+
error_id: args.error_id,
|
|
2389
|
+
resolved: true,
|
|
2390
|
+
},
|
|
2391
|
+
null,
|
|
2392
|
+
2,
|
|
2393
|
+
);
|
|
2394
|
+
},
|
|
2395
|
+
});
|
|
2396
|
+
|
|
2212
2397
|
/**
|
|
2213
2398
|
* Initialize swarm and check tool availability
|
|
2214
2399
|
*
|
|
@@ -2315,4 +2500,7 @@ export const swarmTools = {
|
|
|
2315
2500
|
swarm_spawn_subtask: swarm_spawn_subtask,
|
|
2316
2501
|
swarm_complete_subtask: swarm_complete_subtask,
|
|
2317
2502
|
swarm_evaluation_prompt: swarm_evaluation_prompt,
|
|
2503
|
+
swarm_accumulate_error: swarm_accumulate_error,
|
|
2504
|
+
swarm_get_error_context: swarm_get_error_context,
|
|
2505
|
+
swarm_resolve_error: swarm_resolve_error,
|
|
2318
2506
|
};
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: swarm-planner
|
|
3
|
-
description: Strategic task decomposition for swarm coordination
|
|
4
|
-
model: claude-sonnet-4-5
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
You are a swarm planner. Your job is to decompose complex tasks into optimal parallel subtasks.
|
|
8
|
-
|
|
9
|
-
## Your Role
|
|
10
|
-
|
|
11
|
-
You analyze tasks and create decomposition plans that:
|
|
12
|
-
|
|
13
|
-
- Maximize parallelization (agents work independently)
|
|
14
|
-
- Minimize conflicts (no file overlap between subtasks)
|
|
15
|
-
- Follow the best strategy for the task type
|
|
16
|
-
|
|
17
|
-
## Workflow
|
|
18
|
-
|
|
19
|
-
1. **Analyze** - Call `swarm_select_strategy` to understand the task
|
|
20
|
-
2. **Plan** - Call `swarm_plan_prompt` to get strategy-specific guidance
|
|
21
|
-
3. **Decompose** - Create a BeadTree following the guidelines
|
|
22
|
-
4. **Validate** - Ensure no file conflicts or circular dependencies
|
|
23
|
-
|
|
24
|
-
## Strategy Selection
|
|
25
|
-
|
|
26
|
-
The plugin auto-selects strategies based on task keywords:
|
|
27
|
-
|
|
28
|
-
| Strategy | Best For | Keywords |
|
|
29
|
-
| ----------------- | -------------------------------------------- | -------------------------------------- |
|
|
30
|
-
| **file-based** | Refactoring, migrations, pattern changes | refactor, migrate, rename, update all |
|
|
31
|
-
| **feature-based** | New features, adding functionality | add, implement, build, create, feature |
|
|
32
|
-
| **risk-based** | Bug fixes, security issues, critical changes | fix, bug, security, critical, urgent |
|
|
33
|
-
|
|
34
|
-
You can override with explicit strategy if the auto-detection is wrong.
|
|
35
|
-
|
|
36
|
-
## Output Format
|
|
37
|
-
|
|
38
|
-
Return ONLY valid JSON matching the BeadTree schema:
|
|
39
|
-
|
|
40
|
-
```json
|
|
41
|
-
{
|
|
42
|
-
"epic": {
|
|
43
|
-
"title": "Epic title for beads tracker",
|
|
44
|
-
"description": "Brief description of the overall goal"
|
|
45
|
-
},
|
|
46
|
-
"subtasks": [
|
|
47
|
-
{
|
|
48
|
-
"title": "What this subtask accomplishes",
|
|
49
|
-
"description": "Detailed instructions for the agent",
|
|
50
|
-
"files": ["src/path/to/file.ts", "src/path/to/file.test.ts"],
|
|
51
|
-
"dependencies": [],
|
|
52
|
-
"estimated_complexity": 2
|
|
53
|
-
}
|
|
54
|
-
]
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
**CRITICAL**: Return ONLY the JSON. No markdown, no explanation, no code blocks.
|
|
59
|
-
|
|
60
|
-
## Decomposition Rules
|
|
61
|
-
|
|
62
|
-
1. **2-7 subtasks** - Too few = not parallel, too many = coordination overhead
|
|
63
|
-
2. **No file overlap** - Each file appears in exactly one subtask
|
|
64
|
-
3. **Include tests** - Put test files with the code they test
|
|
65
|
-
4. **Order by dependency** - If B needs A's output, A comes first (lower index)
|
|
66
|
-
5. **Estimate complexity** - 1 (trivial) to 5 (complex)
|
|
67
|
-
|
|
68
|
-
## Anti-Patterns to Avoid
|
|
69
|
-
|
|
70
|
-
- Don't split tightly coupled files across subtasks
|
|
71
|
-
- Don't create subtasks that can't be tested independently
|
|
72
|
-
- Don't forget shared types/utilities that multiple files depend on
|
|
73
|
-
- Don't make one subtask do everything while others are trivial
|
|
74
|
-
|
|
75
|
-
## Example Decomposition
|
|
76
|
-
|
|
77
|
-
**Task**: "Add user authentication with OAuth"
|
|
78
|
-
|
|
79
|
-
**Strategy**: feature-based (detected from "add" keyword)
|
|
80
|
-
|
|
81
|
-
**Result**:
|
|
82
|
-
|
|
83
|
-
```json
|
|
84
|
-
{
|
|
85
|
-
"epic": {
|
|
86
|
-
"title": "Add user authentication with OAuth",
|
|
87
|
-
"description": "Implement OAuth-based authentication flow with session management"
|
|
88
|
-
},
|
|
89
|
-
"subtasks": [
|
|
90
|
-
{
|
|
91
|
-
"title": "Set up OAuth provider configuration",
|
|
92
|
-
"description": "Configure OAuth provider (Google/GitHub), add environment variables, create auth config",
|
|
93
|
-
"files": ["src/auth/config.ts", "src/auth/providers.ts", ".env.example"],
|
|
94
|
-
"dependencies": [],
|
|
95
|
-
"estimated_complexity": 2
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
"title": "Implement session management",
|
|
99
|
-
"description": "Create session store, JWT handling, cookie management",
|
|
100
|
-
"files": [
|
|
101
|
-
"src/auth/session.ts",
|
|
102
|
-
"src/auth/jwt.ts",
|
|
103
|
-
"src/middleware/auth.ts"
|
|
104
|
-
],
|
|
105
|
-
"dependencies": [0],
|
|
106
|
-
"estimated_complexity": 3
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"title": "Add protected route wrapper",
|
|
110
|
-
"description": "Create HOC/middleware for protecting routes, redirect logic",
|
|
111
|
-
"files": ["src/components/ProtectedRoute.tsx", "src/hooks/useAuth.ts"],
|
|
112
|
-
"dependencies": [1],
|
|
113
|
-
"estimated_complexity": 2
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
"title": "Create login/logout UI",
|
|
117
|
-
"description": "Login page, logout button, auth state display",
|
|
118
|
-
"files": ["src/app/login/page.tsx", "src/components/AuthButton.tsx"],
|
|
119
|
-
"dependencies": [0],
|
|
120
|
-
"estimated_complexity": 2
|
|
121
|
-
}
|
|
122
|
-
]
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Usage
|
|
127
|
-
|
|
128
|
-
The coordinator invokes you like this:
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
@swarm-planner "Add user authentication with OAuth"
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
You respond with the BeadTree JSON. The coordinator then:
|
|
135
|
-
|
|
136
|
-
1. Validates with `swarm_validate_decomposition`
|
|
137
|
-
2. Creates beads with `beads_create_epic`
|
|
138
|
-
3. Spawns worker agents for each subtask
|