opencode-swarm-plugin 0.12.6 → 0.12.8

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/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
  /**
@@ -1906,6 +1926,9 @@ export const swarm_record_outcome = tool({
1906
1926
  DEFAULT_LEARNING_CONFIG,
1907
1927
  );
1908
1928
 
1929
+ // Get error patterns from accumulator
1930
+ const errorStats = await globalErrorAccumulator.getErrorStats(args.bead_id);
1931
+
1909
1932
  // Generate feedback events for each criterion
1910
1933
  const criteriaToScore = args.criteria ?? [
1911
1934
  "type_safe",
@@ -1920,6 +1943,14 @@ export const swarm_record_outcome = tool({
1920
1943
  event.context =
1921
1944
  `${event.context || ""} [strategy: ${args.strategy}]`.trim();
1922
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
+ }
1923
1954
  return event;
1924
1955
  });
1925
1956
 
@@ -1935,6 +1966,7 @@ export const swarm_record_outcome = tool({
1935
1966
  },
1936
1967
  },
1937
1968
  feedback_events: feedbackEvents,
1969
+ error_patterns: errorStats,
1938
1970
  summary: {
1939
1971
  feedback_type: scored.type,
1940
1972
  duration_seconds: Math.round(args.duration_ms / 1000),
@@ -1942,6 +1974,8 @@ export const swarm_record_outcome = tool({
1942
1974
  retry_count: args.retry_count ?? 0,
1943
1975
  success: args.success,
1944
1976
  strategy: args.strategy,
1977
+ accumulated_errors: errorStats.total,
1978
+ unresolved_errors: errorStats.unresolved,
1945
1979
  },
1946
1980
  note: "Feedback events should be stored for criterion weight calculation. Use learning.ts functions to apply weights.",
1947
1981
  },
@@ -2222,6 +2256,144 @@ export const swarm_evaluation_prompt = tool({
2222
2256
  },
2223
2257
  });
2224
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
+
2225
2397
  /**
2226
2398
  * Initialize swarm and check tool availability
2227
2399
  *
@@ -2328,4 +2500,7 @@ export const swarmTools = {
2328
2500
  swarm_spawn_subtask: swarm_spawn_subtask,
2329
2501
  swarm_complete_subtask: swarm_complete_subtask,
2330
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,
2331
2506
  };