opencode-swarm-plugin 0.5.0 → 0.6.1
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 +443 -0
- package/README.md +76 -0
- package/dist/index.js +335 -5
- package/dist/plugin.js +332 -5
- package/examples/agents/swarm-planner.md +138 -0
- package/examples/commands/swarm.md +261 -34
- package/package.json +1 -1
- package/src/index.ts +12 -0
- package/src/learning.ts +13 -0
- package/src/swarm.integration.test.ts +284 -0
- package/src/swarm.ts +481 -3
package/src/swarm.ts
CHANGED
|
@@ -28,11 +28,13 @@ import {
|
|
|
28
28
|
import { mcpCall } from "./agent-mail";
|
|
29
29
|
import {
|
|
30
30
|
OutcomeSignalsSchema,
|
|
31
|
+
DecompositionStrategySchema,
|
|
31
32
|
scoreImplicitFeedback,
|
|
32
33
|
outcomeToFeedback,
|
|
33
34
|
type OutcomeSignals,
|
|
34
35
|
type ScoredOutcome,
|
|
35
36
|
type FeedbackEvent,
|
|
37
|
+
type DecompositionStrategy as LearningDecompositionStrategy,
|
|
36
38
|
DEFAULT_LEARNING_CONFIG,
|
|
37
39
|
} from "./learning";
|
|
38
40
|
import {
|
|
@@ -193,6 +195,244 @@ export function detectInstructionConflicts(
|
|
|
193
195
|
return conflicts;
|
|
194
196
|
}
|
|
195
197
|
|
|
198
|
+
// ============================================================================
|
|
199
|
+
// Strategy Definitions
|
|
200
|
+
// ============================================================================
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Decomposition strategy types
|
|
204
|
+
*/
|
|
205
|
+
export type DecompositionStrategy =
|
|
206
|
+
| "file-based"
|
|
207
|
+
| "feature-based"
|
|
208
|
+
| "risk-based"
|
|
209
|
+
| "auto";
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Strategy definition with keywords, guidelines, and anti-patterns
|
|
213
|
+
*/
|
|
214
|
+
export interface StrategyDefinition {
|
|
215
|
+
name: DecompositionStrategy;
|
|
216
|
+
description: string;
|
|
217
|
+
keywords: string[];
|
|
218
|
+
guidelines: string[];
|
|
219
|
+
antiPatterns: string[];
|
|
220
|
+
examples: string[];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Strategy definitions for task decomposition
|
|
225
|
+
*/
|
|
226
|
+
export const STRATEGIES: Record<
|
|
227
|
+
Exclude<DecompositionStrategy, "auto">,
|
|
228
|
+
StrategyDefinition
|
|
229
|
+
> = {
|
|
230
|
+
"file-based": {
|
|
231
|
+
name: "file-based",
|
|
232
|
+
description:
|
|
233
|
+
"Group by file type or directory. Best for refactoring, migrations, and pattern changes across codebase.",
|
|
234
|
+
keywords: [
|
|
235
|
+
"refactor",
|
|
236
|
+
"migrate",
|
|
237
|
+
"update all",
|
|
238
|
+
"rename",
|
|
239
|
+
"replace",
|
|
240
|
+
"convert",
|
|
241
|
+
"upgrade",
|
|
242
|
+
"deprecate",
|
|
243
|
+
"remove",
|
|
244
|
+
"cleanup",
|
|
245
|
+
"lint",
|
|
246
|
+
"format",
|
|
247
|
+
],
|
|
248
|
+
guidelines: [
|
|
249
|
+
"Group files by directory or type (e.g., all components, all tests)",
|
|
250
|
+
"Minimize cross-directory dependencies within a subtask",
|
|
251
|
+
"Handle shared types/utilities first if they change",
|
|
252
|
+
"Each subtask should be a complete transformation of its file set",
|
|
253
|
+
"Consider import/export relationships when grouping",
|
|
254
|
+
],
|
|
255
|
+
antiPatterns: [
|
|
256
|
+
"Don't split tightly coupled files across subtasks",
|
|
257
|
+
"Don't group files that have no relationship",
|
|
258
|
+
"Don't forget to update imports when moving/renaming",
|
|
259
|
+
],
|
|
260
|
+
examples: [
|
|
261
|
+
"Migrate all components to new API → split by component directory",
|
|
262
|
+
"Rename userId to accountId → split by module (types first, then consumers)",
|
|
263
|
+
"Update all tests to use new matcher → split by test directory",
|
|
264
|
+
],
|
|
265
|
+
},
|
|
266
|
+
"feature-based": {
|
|
267
|
+
name: "feature-based",
|
|
268
|
+
description:
|
|
269
|
+
"Vertical slices with UI + API + data. Best for new features and adding functionality.",
|
|
270
|
+
keywords: [
|
|
271
|
+
"add",
|
|
272
|
+
"implement",
|
|
273
|
+
"build",
|
|
274
|
+
"create",
|
|
275
|
+
"feature",
|
|
276
|
+
"new",
|
|
277
|
+
"integrate",
|
|
278
|
+
"connect",
|
|
279
|
+
"enable",
|
|
280
|
+
"support",
|
|
281
|
+
],
|
|
282
|
+
guidelines: [
|
|
283
|
+
"Each subtask is a complete vertical slice (UI + logic + data)",
|
|
284
|
+
"Start with data layer/types, then logic, then UI",
|
|
285
|
+
"Keep related components together (form + validation + submission)",
|
|
286
|
+
"Separate concerns that can be developed independently",
|
|
287
|
+
"Consider user-facing features as natural boundaries",
|
|
288
|
+
],
|
|
289
|
+
antiPatterns: [
|
|
290
|
+
"Don't split a single feature across multiple subtasks",
|
|
291
|
+
"Don't create subtasks that can't be tested independently",
|
|
292
|
+
"Don't forget integration points between features",
|
|
293
|
+
],
|
|
294
|
+
examples: [
|
|
295
|
+
"Add user auth → [OAuth setup, Session management, Protected routes]",
|
|
296
|
+
"Build dashboard → [Data fetching, Chart components, Layout/navigation]",
|
|
297
|
+
"Add search → [Search API, Search UI, Results display]",
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
"risk-based": {
|
|
301
|
+
name: "risk-based",
|
|
302
|
+
description:
|
|
303
|
+
"Isolate high-risk changes, add tests first. Best for bug fixes, security issues, and critical changes.",
|
|
304
|
+
keywords: [
|
|
305
|
+
"fix",
|
|
306
|
+
"bug",
|
|
307
|
+
"security",
|
|
308
|
+
"vulnerability",
|
|
309
|
+
"critical",
|
|
310
|
+
"urgent",
|
|
311
|
+
"hotfix",
|
|
312
|
+
"patch",
|
|
313
|
+
"audit",
|
|
314
|
+
"review",
|
|
315
|
+
"investigate",
|
|
316
|
+
],
|
|
317
|
+
guidelines: [
|
|
318
|
+
"Write tests FIRST to capture expected behavior",
|
|
319
|
+
"Isolate the risky change to minimize blast radius",
|
|
320
|
+
"Add monitoring/logging around the change",
|
|
321
|
+
"Create rollback plan as part of the task",
|
|
322
|
+
"Audit similar code for the same issue",
|
|
323
|
+
],
|
|
324
|
+
antiPatterns: [
|
|
325
|
+
"Don't make multiple risky changes in one subtask",
|
|
326
|
+
"Don't skip tests for 'simple' fixes",
|
|
327
|
+
"Don't forget to check for similar issues elsewhere",
|
|
328
|
+
],
|
|
329
|
+
examples: [
|
|
330
|
+
"Fix auth bypass → [Add regression test, Fix vulnerability, Audit similar endpoints]",
|
|
331
|
+
"Fix race condition → [Add test reproducing issue, Implement fix, Add concurrency tests]",
|
|
332
|
+
"Security audit → [Scan for vulnerabilities, Fix critical issues, Document remaining risks]",
|
|
333
|
+
],
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Analyze task description and select best decomposition strategy
|
|
339
|
+
*
|
|
340
|
+
* @param task - Task description
|
|
341
|
+
* @returns Selected strategy with reasoning
|
|
342
|
+
*/
|
|
343
|
+
export function selectStrategy(task: string): {
|
|
344
|
+
strategy: Exclude<DecompositionStrategy, "auto">;
|
|
345
|
+
confidence: number;
|
|
346
|
+
reasoning: string;
|
|
347
|
+
alternatives: Array<{
|
|
348
|
+
strategy: Exclude<DecompositionStrategy, "auto">;
|
|
349
|
+
score: number;
|
|
350
|
+
}>;
|
|
351
|
+
} {
|
|
352
|
+
const taskLower = task.toLowerCase();
|
|
353
|
+
|
|
354
|
+
// Score each strategy based on keyword matches
|
|
355
|
+
const scores: Record<Exclude<DecompositionStrategy, "auto">, number> = {
|
|
356
|
+
"file-based": 0,
|
|
357
|
+
"feature-based": 0,
|
|
358
|
+
"risk-based": 0,
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
for (const [strategyName, definition] of Object.entries(STRATEGIES)) {
|
|
362
|
+
const name = strategyName as Exclude<DecompositionStrategy, "auto">;
|
|
363
|
+
for (const keyword of definition.keywords) {
|
|
364
|
+
if (taskLower.includes(keyword)) {
|
|
365
|
+
scores[name] += 1;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Find the winner
|
|
371
|
+
const entries = Object.entries(scores) as Array<
|
|
372
|
+
[Exclude<DecompositionStrategy, "auto">, number]
|
|
373
|
+
>;
|
|
374
|
+
entries.sort((a, b) => b[1] - a[1]);
|
|
375
|
+
|
|
376
|
+
const [winner, winnerScore] = entries[0];
|
|
377
|
+
const [runnerUp, runnerUpScore] = entries[1] || [null, 0];
|
|
378
|
+
|
|
379
|
+
// Calculate confidence based on margin
|
|
380
|
+
const totalScore = entries.reduce((sum, [, score]) => sum + score, 0);
|
|
381
|
+
const confidence =
|
|
382
|
+
totalScore > 0
|
|
383
|
+
? Math.min(0.95, 0.5 + (winnerScore - runnerUpScore) / totalScore)
|
|
384
|
+
: 0.5; // Default to 50% if no keywords matched
|
|
385
|
+
|
|
386
|
+
// Build reasoning
|
|
387
|
+
let reasoning: string;
|
|
388
|
+
if (winnerScore === 0) {
|
|
389
|
+
reasoning = `No strong keyword signals. Defaulting to feature-based as it's most versatile.`;
|
|
390
|
+
} else {
|
|
391
|
+
const matchedKeywords = STRATEGIES[winner].keywords.filter((k) =>
|
|
392
|
+
taskLower.includes(k),
|
|
393
|
+
);
|
|
394
|
+
reasoning = `Matched keywords: ${matchedKeywords.join(", ")}. ${STRATEGIES[winner].description}`;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// If no keywords matched, default to feature-based
|
|
398
|
+
const finalStrategy = winnerScore === 0 ? "feature-based" : winner;
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
strategy: finalStrategy,
|
|
402
|
+
confidence,
|
|
403
|
+
reasoning,
|
|
404
|
+
alternatives: entries
|
|
405
|
+
.filter(([s]) => s !== finalStrategy)
|
|
406
|
+
.map(([strategy, score]) => ({ strategy, score })),
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Format strategy-specific guidelines for the decomposition prompt
|
|
412
|
+
*/
|
|
413
|
+
export function formatStrategyGuidelines(
|
|
414
|
+
strategy: Exclude<DecompositionStrategy, "auto">,
|
|
415
|
+
): string {
|
|
416
|
+
const def = STRATEGIES[strategy];
|
|
417
|
+
|
|
418
|
+
const guidelines = def.guidelines.map((g) => `- ${g}`).join("\n");
|
|
419
|
+
const antiPatterns = def.antiPatterns.map((a) => `- ${a}`).join("\n");
|
|
420
|
+
const examples = def.examples.map((e) => `- ${e}`).join("\n");
|
|
421
|
+
|
|
422
|
+
return `## Strategy: ${strategy}
|
|
423
|
+
|
|
424
|
+
${def.description}
|
|
425
|
+
|
|
426
|
+
### Guidelines
|
|
427
|
+
${guidelines}
|
|
428
|
+
|
|
429
|
+
### Anti-Patterns (Avoid These)
|
|
430
|
+
${antiPatterns}
|
|
431
|
+
|
|
432
|
+
### Examples
|
|
433
|
+
${examples}`;
|
|
434
|
+
}
|
|
435
|
+
|
|
196
436
|
// ============================================================================
|
|
197
437
|
// Prompt Templates
|
|
198
438
|
// ============================================================================
|
|
@@ -744,6 +984,227 @@ function formatCassHistoryForPrompt(history: CassSearchResult): string {
|
|
|
744
984
|
// Tool Definitions
|
|
745
985
|
// ============================================================================
|
|
746
986
|
|
|
987
|
+
/**
|
|
988
|
+
* Select the best decomposition strategy for a task
|
|
989
|
+
*
|
|
990
|
+
* Analyzes task description and recommends a strategy with reasoning.
|
|
991
|
+
* Use this before swarm_plan_prompt to understand the recommended approach.
|
|
992
|
+
*/
|
|
993
|
+
export const swarm_select_strategy = tool({
|
|
994
|
+
description:
|
|
995
|
+
"Analyze task and recommend decomposition strategy (file-based, feature-based, or risk-based)",
|
|
996
|
+
args: {
|
|
997
|
+
task: tool.schema.string().min(1).describe("Task description to analyze"),
|
|
998
|
+
codebase_context: tool.schema
|
|
999
|
+
.string()
|
|
1000
|
+
.optional()
|
|
1001
|
+
.describe("Optional codebase context (file structure, tech stack, etc.)"),
|
|
1002
|
+
},
|
|
1003
|
+
async execute(args) {
|
|
1004
|
+
const result = selectStrategy(args.task);
|
|
1005
|
+
|
|
1006
|
+
// Enhance reasoning with codebase context if provided
|
|
1007
|
+
let enhancedReasoning = result.reasoning;
|
|
1008
|
+
if (args.codebase_context) {
|
|
1009
|
+
enhancedReasoning += `\n\nCodebase context considered: ${args.codebase_context.slice(0, 200)}...`;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
return JSON.stringify(
|
|
1013
|
+
{
|
|
1014
|
+
strategy: result.strategy,
|
|
1015
|
+
confidence: Math.round(result.confidence * 100) / 100,
|
|
1016
|
+
reasoning: enhancedReasoning,
|
|
1017
|
+
description: STRATEGIES[result.strategy].description,
|
|
1018
|
+
guidelines: STRATEGIES[result.strategy].guidelines,
|
|
1019
|
+
anti_patterns: STRATEGIES[result.strategy].antiPatterns,
|
|
1020
|
+
alternatives: result.alternatives.map((alt) => ({
|
|
1021
|
+
strategy: alt.strategy,
|
|
1022
|
+
description: STRATEGIES[alt.strategy].description,
|
|
1023
|
+
score: alt.score,
|
|
1024
|
+
})),
|
|
1025
|
+
},
|
|
1026
|
+
null,
|
|
1027
|
+
2,
|
|
1028
|
+
);
|
|
1029
|
+
},
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Strategy-specific decomposition prompt template
|
|
1034
|
+
*/
|
|
1035
|
+
const STRATEGY_DECOMPOSITION_PROMPT = `You are decomposing a task into parallelizable subtasks for a swarm of agents.
|
|
1036
|
+
|
|
1037
|
+
## Task
|
|
1038
|
+
{task}
|
|
1039
|
+
|
|
1040
|
+
{strategy_guidelines}
|
|
1041
|
+
|
|
1042
|
+
{context_section}
|
|
1043
|
+
|
|
1044
|
+
{cass_history}
|
|
1045
|
+
|
|
1046
|
+
## MANDATORY: Beads Issue Tracking
|
|
1047
|
+
|
|
1048
|
+
**Every subtask MUST become a bead.** This is non-negotiable.
|
|
1049
|
+
|
|
1050
|
+
After decomposition, the coordinator will:
|
|
1051
|
+
1. Create an epic bead for the overall task
|
|
1052
|
+
2. Create child beads for each subtask
|
|
1053
|
+
3. Track progress through bead status updates
|
|
1054
|
+
4. Close beads with summaries when complete
|
|
1055
|
+
|
|
1056
|
+
Agents MUST update their bead status as they work. No silent progress.
|
|
1057
|
+
|
|
1058
|
+
## Requirements
|
|
1059
|
+
|
|
1060
|
+
1. **Break into 2-{max_subtasks} independent subtasks** that can run in parallel
|
|
1061
|
+
2. **Assign files** - each subtask must specify which files it will modify
|
|
1062
|
+
3. **No file overlap** - files cannot appear in multiple subtasks (they get exclusive locks)
|
|
1063
|
+
4. **Order by dependency** - if subtask B needs subtask A's output, A must come first in the array
|
|
1064
|
+
5. **Estimate complexity** - 1 (trivial) to 5 (complex)
|
|
1065
|
+
6. **Plan aggressively** - break down more than you think necessary, smaller is better
|
|
1066
|
+
|
|
1067
|
+
## Response Format
|
|
1068
|
+
|
|
1069
|
+
Respond with a JSON object matching this schema:
|
|
1070
|
+
|
|
1071
|
+
\`\`\`typescript
|
|
1072
|
+
{
|
|
1073
|
+
epic: {
|
|
1074
|
+
title: string, // Epic title for the beads tracker
|
|
1075
|
+
description?: string // Brief description of the overall goal
|
|
1076
|
+
},
|
|
1077
|
+
subtasks: [
|
|
1078
|
+
{
|
|
1079
|
+
title: string, // What this subtask accomplishes
|
|
1080
|
+
description?: string, // Detailed instructions for the agent
|
|
1081
|
+
files: string[], // Files this subtask will modify (globs allowed)
|
|
1082
|
+
dependencies: number[], // Indices of subtasks this depends on (0-indexed)
|
|
1083
|
+
estimated_complexity: 1-5 // Effort estimate
|
|
1084
|
+
},
|
|
1085
|
+
// ... more subtasks
|
|
1086
|
+
]
|
|
1087
|
+
}
|
|
1088
|
+
\`\`\`
|
|
1089
|
+
|
|
1090
|
+
Now decompose the task:`;
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Generate a strategy-specific planning prompt
|
|
1094
|
+
*
|
|
1095
|
+
* Higher-level than swarm_decompose - includes strategy selection and guidelines.
|
|
1096
|
+
* Use this when you want the full planning experience with strategy-specific advice.
|
|
1097
|
+
*/
|
|
1098
|
+
export const swarm_plan_prompt = tool({
|
|
1099
|
+
description:
|
|
1100
|
+
"Generate strategy-specific decomposition prompt. Auto-selects strategy or uses provided one. Queries CASS for similar tasks.",
|
|
1101
|
+
args: {
|
|
1102
|
+
task: tool.schema.string().min(1).describe("Task description to decompose"),
|
|
1103
|
+
strategy: tool.schema
|
|
1104
|
+
.enum(["file-based", "feature-based", "risk-based", "auto"])
|
|
1105
|
+
.optional()
|
|
1106
|
+
.describe("Decomposition strategy (default: auto-detect)"),
|
|
1107
|
+
max_subtasks: tool.schema
|
|
1108
|
+
.number()
|
|
1109
|
+
.int()
|
|
1110
|
+
.min(2)
|
|
1111
|
+
.max(10)
|
|
1112
|
+
.default(5)
|
|
1113
|
+
.describe("Maximum number of subtasks (default: 5)"),
|
|
1114
|
+
context: tool.schema
|
|
1115
|
+
.string()
|
|
1116
|
+
.optional()
|
|
1117
|
+
.describe("Additional context (codebase info, constraints, etc.)"),
|
|
1118
|
+
query_cass: tool.schema
|
|
1119
|
+
.boolean()
|
|
1120
|
+
.optional()
|
|
1121
|
+
.describe("Query CASS for similar past tasks (default: true)"),
|
|
1122
|
+
cass_limit: tool.schema
|
|
1123
|
+
.number()
|
|
1124
|
+
.int()
|
|
1125
|
+
.min(1)
|
|
1126
|
+
.max(10)
|
|
1127
|
+
.optional()
|
|
1128
|
+
.describe("Max CASS results to include (default: 3)"),
|
|
1129
|
+
},
|
|
1130
|
+
async execute(args) {
|
|
1131
|
+
// Select strategy
|
|
1132
|
+
let selectedStrategy: Exclude<DecompositionStrategy, "auto">;
|
|
1133
|
+
let strategyReasoning: string;
|
|
1134
|
+
|
|
1135
|
+
if (args.strategy && args.strategy !== "auto") {
|
|
1136
|
+
selectedStrategy = args.strategy;
|
|
1137
|
+
strategyReasoning = `User-specified strategy: ${selectedStrategy}`;
|
|
1138
|
+
} else {
|
|
1139
|
+
const selection = selectStrategy(args.task);
|
|
1140
|
+
selectedStrategy = selection.strategy;
|
|
1141
|
+
strategyReasoning = selection.reasoning;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// Query CASS for similar past tasks
|
|
1145
|
+
let cassContext = "";
|
|
1146
|
+
let cassResult: CassSearchResult | null = null;
|
|
1147
|
+
|
|
1148
|
+
if (args.query_cass !== false) {
|
|
1149
|
+
cassResult = await queryCassHistory(args.task, args.cass_limit ?? 3);
|
|
1150
|
+
if (cassResult && cassResult.results.length > 0) {
|
|
1151
|
+
cassContext = formatCassHistoryForPrompt(cassResult);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// Format strategy guidelines
|
|
1156
|
+
const strategyGuidelines = formatStrategyGuidelines(selectedStrategy);
|
|
1157
|
+
|
|
1158
|
+
// Combine user context
|
|
1159
|
+
const contextSection = args.context
|
|
1160
|
+
? `## Additional Context\n${args.context}`
|
|
1161
|
+
: "## Additional Context\n(none provided)";
|
|
1162
|
+
|
|
1163
|
+
// Build the prompt
|
|
1164
|
+
const prompt = STRATEGY_DECOMPOSITION_PROMPT.replace("{task}", args.task)
|
|
1165
|
+
.replace("{strategy_guidelines}", strategyGuidelines)
|
|
1166
|
+
.replace("{context_section}", contextSection)
|
|
1167
|
+
.replace("{cass_history}", cassContext || "")
|
|
1168
|
+
.replace("{max_subtasks}", (args.max_subtasks ?? 5).toString());
|
|
1169
|
+
|
|
1170
|
+
return JSON.stringify(
|
|
1171
|
+
{
|
|
1172
|
+
prompt,
|
|
1173
|
+
strategy: {
|
|
1174
|
+
selected: selectedStrategy,
|
|
1175
|
+
reasoning: strategyReasoning,
|
|
1176
|
+
guidelines: STRATEGIES[selectedStrategy].guidelines,
|
|
1177
|
+
anti_patterns: STRATEGIES[selectedStrategy].antiPatterns,
|
|
1178
|
+
},
|
|
1179
|
+
expected_schema: "BeadTree",
|
|
1180
|
+
schema_hint: {
|
|
1181
|
+
epic: { title: "string", description: "string?" },
|
|
1182
|
+
subtasks: [
|
|
1183
|
+
{
|
|
1184
|
+
title: "string",
|
|
1185
|
+
description: "string?",
|
|
1186
|
+
files: "string[]",
|
|
1187
|
+
dependencies: "number[]",
|
|
1188
|
+
estimated_complexity: "1-5",
|
|
1189
|
+
},
|
|
1190
|
+
],
|
|
1191
|
+
},
|
|
1192
|
+
validation_note:
|
|
1193
|
+
"Parse agent response as JSON and validate with swarm_validate_decomposition",
|
|
1194
|
+
cass_history: cassResult
|
|
1195
|
+
? {
|
|
1196
|
+
queried: true,
|
|
1197
|
+
results_found: cassResult.results.length,
|
|
1198
|
+
included_in_context: cassResult.results.length > 0,
|
|
1199
|
+
}
|
|
1200
|
+
: { queried: false, reason: "disabled or unavailable" },
|
|
1201
|
+
},
|
|
1202
|
+
null,
|
|
1203
|
+
2,
|
|
1204
|
+
);
|
|
1205
|
+
},
|
|
1206
|
+
});
|
|
1207
|
+
|
|
747
1208
|
/**
|
|
748
1209
|
* Decompose a task into a bead tree
|
|
749
1210
|
*
|
|
@@ -1367,6 +1828,9 @@ export const swarm_complete = tool({
|
|
|
1367
1828
|
* decomposition quality over time. This data feeds into criterion
|
|
1368
1829
|
* weight calculations.
|
|
1369
1830
|
*
|
|
1831
|
+
* Strategy tracking enables learning about which decomposition strategies
|
|
1832
|
+
* work best for different task types.
|
|
1833
|
+
*
|
|
1370
1834
|
* @see src/learning.ts for scoring logic
|
|
1371
1835
|
*/
|
|
1372
1836
|
export const swarm_record_outcome = tool({
|
|
@@ -1402,6 +1866,10 @@ export const swarm_record_outcome = tool({
|
|
|
1402
1866
|
.describe(
|
|
1403
1867
|
"Criteria to generate feedback for (default: all default criteria)",
|
|
1404
1868
|
),
|
|
1869
|
+
strategy: tool.schema
|
|
1870
|
+
.enum(["file-based", "feature-based", "risk-based"])
|
|
1871
|
+
.optional()
|
|
1872
|
+
.describe("Decomposition strategy used for this task"),
|
|
1405
1873
|
},
|
|
1406
1874
|
async execute(args) {
|
|
1407
1875
|
// Build outcome signals
|
|
@@ -1413,6 +1881,7 @@ export const swarm_record_outcome = tool({
|
|
|
1413
1881
|
success: args.success,
|
|
1414
1882
|
files_touched: args.files_touched ?? [],
|
|
1415
1883
|
timestamp: new Date().toISOString(),
|
|
1884
|
+
strategy: args.strategy as LearningDecompositionStrategy | undefined,
|
|
1416
1885
|
};
|
|
1417
1886
|
|
|
1418
1887
|
// Validate signals
|
|
@@ -1431,9 +1900,15 @@ export const swarm_record_outcome = tool({
|
|
|
1431
1900
|
"patterns",
|
|
1432
1901
|
"readable",
|
|
1433
1902
|
];
|
|
1434
|
-
const feedbackEvents: FeedbackEvent[] = criteriaToScore.map((criterion) =>
|
|
1435
|
-
outcomeToFeedback(scored, criterion)
|
|
1436
|
-
|
|
1903
|
+
const feedbackEvents: FeedbackEvent[] = criteriaToScore.map((criterion) => {
|
|
1904
|
+
const event = outcomeToFeedback(scored, criterion);
|
|
1905
|
+
// Include strategy in feedback context for future analysis
|
|
1906
|
+
if (args.strategy) {
|
|
1907
|
+
event.context =
|
|
1908
|
+
`${event.context || ""} [strategy: ${args.strategy}]`.trim();
|
|
1909
|
+
}
|
|
1910
|
+
return event;
|
|
1911
|
+
});
|
|
1437
1912
|
|
|
1438
1913
|
return JSON.stringify(
|
|
1439
1914
|
{
|
|
@@ -1453,6 +1928,7 @@ export const swarm_record_outcome = tool({
|
|
|
1453
1928
|
error_count: args.error_count ?? 0,
|
|
1454
1929
|
retry_count: args.retry_count ?? 0,
|
|
1455
1930
|
success: args.success,
|
|
1931
|
+
strategy: args.strategy,
|
|
1456
1932
|
},
|
|
1457
1933
|
note: "Feedback events should be stored for criterion weight calculation. Use learning.ts functions to apply weights.",
|
|
1458
1934
|
},
|
|
@@ -1827,6 +2303,8 @@ export const swarm_init = tool({
|
|
|
1827
2303
|
|
|
1828
2304
|
export const swarmTools = {
|
|
1829
2305
|
swarm_init: swarm_init,
|
|
2306
|
+
swarm_select_strategy: swarm_select_strategy,
|
|
2307
|
+
swarm_plan_prompt: swarm_plan_prompt,
|
|
1830
2308
|
swarm_decompose: swarm_decompose,
|
|
1831
2309
|
swarm_validate_decomposition: swarm_validate_decomposition,
|
|
1832
2310
|
swarm_status: swarm_status,
|