opencode-swarm-plugin 0.18.0 → 0.20.0
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 +74 -61
- package/.github/workflows/ci.yml +5 -1
- package/README.md +48 -4
- package/dist/index.js +6643 -6326
- package/dist/plugin.js +2726 -2404
- package/package.json +1 -1
- package/src/agent-mail.ts +20 -7
- package/src/anti-patterns.test.ts +1167 -0
- package/src/anti-patterns.ts +29 -11
- package/src/index.ts +8 -0
- package/src/pattern-maturity.test.ts +1160 -0
- package/src/pattern-maturity.ts +51 -13
- package/src/plugin.ts +15 -3
- package/src/schemas/bead.ts +35 -4
- package/src/schemas/evaluation.ts +18 -6
- package/src/schemas/index.ts +25 -2
- package/src/schemas/task.ts +49 -21
- package/src/streams/debug.ts +101 -3
- package/src/streams/events.ts +22 -0
- package/src/streams/index.ts +58 -1
- package/src/streams/migrations.ts +46 -4
- package/src/streams/store.integration.test.ts +110 -0
- package/src/streams/store.ts +311 -126
- package/src/structured.test.ts +1046 -0
- package/src/structured.ts +74 -27
- package/src/swarm-decompose.ts +912 -0
- package/src/swarm-mail.ts +7 -7
- package/src/swarm-orchestrate.ts +1869 -0
- package/src/swarm-prompts.ts +756 -0
- package/src/swarm-strategies.ts +407 -0
- package/src/swarm.ts +23 -3876
- package/src/tool-availability.ts +29 -6
- package/test-bug-fixes.ts +86 -0
package/src/structured.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Structured Output Module -
|
|
2
|
+
* Structured Output Module - JSON extraction and schema validation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* validation with detailed error feedback for retry loops.
|
|
4
|
+
* Handles parsing agent responses that contain JSON, with multiple fallback
|
|
5
|
+
* strategies for malformed or wrapped content.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
7
|
+
* ## Usage
|
|
8
|
+
* 1. `structured_extract_json` - Raw JSON extraction from text (no validation)
|
|
9
|
+
* 2. `structured_validate` - Extract + validate against named schema
|
|
10
|
+
* 3. `structured_parse_evaluation` - Typed parsing for agent self-evaluations
|
|
11
|
+
* 4. `structured_parse_decomposition` - Typed parsing for task breakdowns
|
|
12
|
+
* 5. `structured_parse_bead_tree` - Typed parsing for epic + subtasks
|
|
13
|
+
*
|
|
14
|
+
* @module structured
|
|
12
15
|
*/
|
|
13
16
|
import { tool } from "@opencode-ai/plugin";
|
|
14
17
|
import { z, type ZodSchema } from "zod";
|
|
@@ -17,6 +20,7 @@ import {
|
|
|
17
20
|
TaskDecompositionSchema,
|
|
18
21
|
BeadTreeSchema,
|
|
19
22
|
ValidationResultSchema,
|
|
23
|
+
CriterionEvaluationSchema,
|
|
20
24
|
type Evaluation,
|
|
21
25
|
type TaskDecomposition,
|
|
22
26
|
type BeadTree,
|
|
@@ -109,11 +113,18 @@ function getSchemaByName(name: string): ZodSchema {
|
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
/**
|
|
112
|
-
*
|
|
116
|
+
* Extract JSON from text using multiple strategies.
|
|
117
|
+
*
|
|
118
|
+
* Strategies tried in priority order:
|
|
119
|
+
* 1. Direct parse - fastest, works for clean JSON
|
|
120
|
+
* 2. JSON code block - common in markdown responses
|
|
121
|
+
* 3. Generic code block - fallback for unlabeled blocks
|
|
122
|
+
* 4. First brace match - finds outermost {...}
|
|
123
|
+
* 5. Last brace match - handles trailing content
|
|
124
|
+
* 6. Repair attempt - fixes common issues (quotes, trailing commas)
|
|
113
125
|
*
|
|
114
|
-
* @param text
|
|
115
|
-
* @returns
|
|
116
|
-
* @throws JsonExtractionError if no JSON can be extracted
|
|
126
|
+
* @param text Raw text potentially containing JSON
|
|
127
|
+
* @returns Parsed JSON object or null if all strategies fail
|
|
117
128
|
*/
|
|
118
129
|
function extractJsonFromText(text: string): [unknown, string] {
|
|
119
130
|
const trimmed = text.trim();
|
|
@@ -189,6 +200,9 @@ function extractJsonFromText(text: string): [unknown, string] {
|
|
|
189
200
|
);
|
|
190
201
|
}
|
|
191
202
|
|
|
203
|
+
/** Maximum nesting depth before aborting (prevents stack overflow on malformed input) */
|
|
204
|
+
const MAX_BRACE_DEPTH = 100;
|
|
205
|
+
|
|
192
206
|
/**
|
|
193
207
|
* Find a balanced pair of braces/brackets
|
|
194
208
|
*/
|
|
@@ -226,8 +240,14 @@ function findBalancedBraces(
|
|
|
226
240
|
|
|
227
241
|
if (char === open) {
|
|
228
242
|
depth++;
|
|
243
|
+
if (depth > MAX_BRACE_DEPTH) {
|
|
244
|
+
return null; // Malformed input - too deeply nested
|
|
245
|
+
}
|
|
229
246
|
} else if (char === close) {
|
|
230
247
|
depth--;
|
|
248
|
+
if (depth < 0) {
|
|
249
|
+
return null; // Malformed input - unbalanced braces
|
|
250
|
+
}
|
|
231
251
|
if (depth === 0) {
|
|
232
252
|
return text.slice(startIdx, i + 1);
|
|
233
253
|
}
|
|
@@ -238,13 +258,18 @@ function findBalancedBraces(
|
|
|
238
258
|
}
|
|
239
259
|
|
|
240
260
|
/**
|
|
241
|
-
* Attempt common JSON
|
|
261
|
+
* Attempt to repair common JSON issues.
|
|
242
262
|
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
* -
|
|
247
|
-
* -
|
|
263
|
+
* This is a simple heuristic - won't work for all cases.
|
|
264
|
+
*
|
|
265
|
+
* **Known Limitations:**
|
|
266
|
+
* - Single quotes in nested objects may not be handled correctly
|
|
267
|
+
* - Escaped quotes in keys can confuse the regex
|
|
268
|
+
* - Multiline strings are not detected
|
|
269
|
+
* - Trailing commas in nested arrays may be missed
|
|
270
|
+
*
|
|
271
|
+
* @param text Potentially malformed JSON string
|
|
272
|
+
* @returns Repaired JSON string (may still be invalid)
|
|
248
273
|
*/
|
|
249
274
|
function attemptJsonRepair(text: string): string {
|
|
250
275
|
let repaired = text;
|
|
@@ -275,6 +300,9 @@ function attemptJsonRepair(text: string): string {
|
|
|
275
300
|
// Validation Result Types
|
|
276
301
|
// ============================================================================
|
|
277
302
|
|
|
303
|
+
/** Maximum characters to show in raw input previews */
|
|
304
|
+
const RAW_INPUT_PREVIEW_LENGTH = 200;
|
|
305
|
+
|
|
278
306
|
/**
|
|
279
307
|
* Result of a structured validation attempt
|
|
280
308
|
*/
|
|
@@ -326,7 +354,7 @@ export const structured_extract_json = tool({
|
|
|
326
354
|
success: false,
|
|
327
355
|
error: error.message,
|
|
328
356
|
attempted_strategies: error.attemptedStrategies,
|
|
329
|
-
raw_input_preview: args.text.slice(0,
|
|
357
|
+
raw_input_preview: args.text.slice(0, RAW_INPUT_PREVIEW_LENGTH),
|
|
330
358
|
},
|
|
331
359
|
null,
|
|
332
360
|
2,
|
|
@@ -350,7 +378,12 @@ export const structured_validate = tool({
|
|
|
350
378
|
response: tool.schema.string().describe("Agent response to validate"),
|
|
351
379
|
schema_name: tool.schema
|
|
352
380
|
.enum(["evaluation", "task_decomposition", "bead_tree"])
|
|
353
|
-
.describe(
|
|
381
|
+
.describe(
|
|
382
|
+
"Schema to validate against: " +
|
|
383
|
+
"evaluation = agent self-eval with criteria, " +
|
|
384
|
+
"task_decomposition = swarm task breakdown, " +
|
|
385
|
+
"bead_tree = epic with subtasks",
|
|
386
|
+
),
|
|
354
387
|
max_retries: tool.schema
|
|
355
388
|
.number()
|
|
356
389
|
.min(1)
|
|
@@ -366,6 +399,15 @@ export const structured_validate = tool({
|
|
|
366
399
|
errors: [],
|
|
367
400
|
};
|
|
368
401
|
|
|
402
|
+
// Check for empty response before attempting extraction
|
|
403
|
+
if (!args.response || args.response.trim().length === 0) {
|
|
404
|
+
return JSON.stringify({
|
|
405
|
+
valid: false,
|
|
406
|
+
error: "Response is empty or contains only whitespace",
|
|
407
|
+
raw_input: "(empty)",
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
369
411
|
// Step 1: Extract JSON
|
|
370
412
|
let extracted: unknown;
|
|
371
413
|
let extractionMethod: string;
|
|
@@ -377,7 +419,7 @@ export const structured_validate = tool({
|
|
|
377
419
|
if (error instanceof JsonExtractionError) {
|
|
378
420
|
result.errors = [
|
|
379
421
|
`JSON extraction failed after trying: ${error.attemptedStrategies.join(", ")}`,
|
|
380
|
-
`Input preview: ${args.response.slice(0,
|
|
422
|
+
`Input preview: ${args.response.slice(0, RAW_INPUT_PREVIEW_LENGTH)}...`,
|
|
381
423
|
];
|
|
382
424
|
return JSON.stringify(result, null, 2);
|
|
383
425
|
}
|
|
@@ -441,7 +483,12 @@ export const structured_parse_evaluation = tool({
|
|
|
441
483
|
passed: validated.passed,
|
|
442
484
|
criteria_count: Object.keys(validated.criteria).length,
|
|
443
485
|
failed_criteria: Object.entries(validated.criteria)
|
|
444
|
-
.filter(([_, v]) =>
|
|
486
|
+
.filter(([_, v]) => {
|
|
487
|
+
const criterion = v as z.infer<
|
|
488
|
+
typeof CriterionEvaluationSchema
|
|
489
|
+
>;
|
|
490
|
+
return !criterion.passed;
|
|
491
|
+
})
|
|
445
492
|
.map(([k]) => k),
|
|
446
493
|
},
|
|
447
494
|
},
|
|
@@ -700,9 +747,9 @@ export { extractJsonFromText, formatZodErrors, getSchemaByName };
|
|
|
700
747
|
// ============================================================================
|
|
701
748
|
|
|
702
749
|
export const structuredTools = {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
750
|
+
structured_extract_json: structured_extract_json,
|
|
751
|
+
structured_validate: structured_validate,
|
|
752
|
+
structured_parse_evaluation: structured_parse_evaluation,
|
|
753
|
+
structured_parse_decomposition: structured_parse_decomposition,
|
|
754
|
+
structured_parse_bead_tree: structured_parse_bead_tree,
|
|
708
755
|
};
|