opencode-swarm-plugin 0.44.0 → 0.44.2
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/bin/swarm.serve.test.ts +6 -4
- package/bin/swarm.ts +18 -12
- package/dist/compaction-prompt-scoring.js +139 -0
- package/dist/eval-capture.js +12811 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/hive.js +14834 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7743 -62593
- package/dist/plugin.js +24052 -78907
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-prompts.js +39407 -0
- package/dist/swarm-review.d.ts.map +1 -1
- package/dist/swarm-validation.d.ts +127 -0
- package/dist/swarm-validation.d.ts.map +1 -0
- package/dist/validators/index.d.ts +7 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/schema-validator.d.ts +58 -0
- package/dist/validators/schema-validator.d.ts.map +1 -0
- package/package.json +17 -5
- package/.changeset/swarm-insights-data-layer.md +0 -63
- package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
- package/.hive/analysis/session-data-quality-audit.md +0 -320
- package/.hive/eval-results.json +0 -483
- package/.hive/issues.jsonl +0 -138
- package/.hive/memories.jsonl +0 -729
- package/.opencode/eval-history.jsonl +0 -327
- package/.turbo/turbo-build.log +0 -9
- package/CHANGELOG.md +0 -2286
- package/SCORER-ANALYSIS.md +0 -598
- package/docs/analysis/subagent-coordination-patterns.md +0 -902
- package/docs/analysis-socratic-planner-pattern.md +0 -504
- package/docs/planning/ADR-001-monorepo-structure.md +0 -171
- package/docs/planning/ADR-002-package-extraction.md +0 -393
- package/docs/planning/ADR-003-performance-improvements.md +0 -451
- package/docs/planning/ADR-004-message-queue-features.md +0 -187
- package/docs/planning/ADR-005-devtools-observability.md +0 -202
- package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
- package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
- package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
- package/docs/planning/ADR-010-cass-inhousing.md +0 -1215
- package/docs/planning/ROADMAP.md +0 -368
- package/docs/semantic-memory-cli-syntax.md +0 -123
- package/docs/swarm-mail-architecture.md +0 -1147
- package/docs/testing/context-recovery-test.md +0 -470
- package/evals/ARCHITECTURE.md +0 -1189
- package/evals/README.md +0 -768
- package/evals/compaction-prompt.eval.ts +0 -149
- package/evals/compaction-resumption.eval.ts +0 -289
- package/evals/coordinator-behavior.eval.ts +0 -307
- package/evals/coordinator-session.eval.ts +0 -154
- package/evals/evalite.config.ts.bak +0 -15
- package/evals/example.eval.ts +0 -31
- package/evals/fixtures/cass-baseline.ts +0 -217
- package/evals/fixtures/compaction-cases.ts +0 -350
- package/evals/fixtures/compaction-prompt-cases.ts +0 -311
- package/evals/fixtures/coordinator-sessions.ts +0 -328
- package/evals/fixtures/decomposition-cases.ts +0 -105
- package/evals/lib/compaction-loader.test.ts +0 -248
- package/evals/lib/compaction-loader.ts +0 -320
- package/evals/lib/data-loader.evalite-test.ts +0 -289
- package/evals/lib/data-loader.test.ts +0 -345
- package/evals/lib/data-loader.ts +0 -281
- package/evals/lib/llm.ts +0 -115
- package/evals/scorers/compaction-prompt-scorers.ts +0 -145
- package/evals/scorers/compaction-scorers.ts +0 -305
- package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
- package/evals/scorers/coordinator-discipline.ts +0 -325
- package/evals/scorers/index.test.ts +0 -146
- package/evals/scorers/index.ts +0 -328
- package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
- package/evals/scorers/outcome-scorers.ts +0 -349
- package/evals/swarm-decomposition.eval.ts +0 -121
- package/examples/commands/swarm.md +0 -745
- package/examples/plugin-wrapper-template.ts +0 -2515
- package/examples/skills/hive-workflow/SKILL.md +0 -212
- package/examples/skills/skill-creator/SKILL.md +0 -223
- package/examples/skills/swarm-coordination/SKILL.md +0 -292
- package/global-skills/cli-builder/SKILL.md +0 -344
- package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
- package/global-skills/learning-systems/SKILL.md +0 -644
- package/global-skills/skill-creator/LICENSE.txt +0 -202
- package/global-skills/skill-creator/SKILL.md +0 -352
- package/global-skills/skill-creator/references/output-patterns.md +0 -82
- package/global-skills/skill-creator/references/workflows.md +0 -28
- package/global-skills/swarm-coordination/SKILL.md +0 -995
- package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
- package/global-skills/swarm-coordination/references/strategies.md +0 -138
- package/global-skills/system-design/SKILL.md +0 -213
- package/global-skills/testing-patterns/SKILL.md +0 -430
- package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
- package/opencode-swarm-plugin-0.30.7.tgz +0 -0
- package/opencode-swarm-plugin-0.31.0.tgz +0 -0
- package/scripts/cleanup-test-memories.ts +0 -346
- package/scripts/init-skill.ts +0 -222
- package/scripts/migrate-unknown-sessions.ts +0 -349
- package/scripts/validate-skill.ts +0 -204
- package/src/agent-mail.ts +0 -1724
- package/src/anti-patterns.test.ts +0 -1167
- package/src/anti-patterns.ts +0 -448
- package/src/compaction-capture.integration.test.ts +0 -257
- package/src/compaction-hook.test.ts +0 -838
- package/src/compaction-hook.ts +0 -1204
- package/src/compaction-observability.integration.test.ts +0 -139
- package/src/compaction-observability.test.ts +0 -187
- package/src/compaction-observability.ts +0 -324
- package/src/compaction-prompt-scorers.test.ts +0 -475
- package/src/compaction-prompt-scoring.ts +0 -300
- package/src/contributor-tools.test.ts +0 -133
- package/src/contributor-tools.ts +0 -201
- package/src/dashboard.test.ts +0 -611
- package/src/dashboard.ts +0 -462
- package/src/error-enrichment.test.ts +0 -403
- package/src/error-enrichment.ts +0 -219
- package/src/eval-capture.test.ts +0 -1015
- package/src/eval-capture.ts +0 -929
- package/src/eval-gates.test.ts +0 -306
- package/src/eval-gates.ts +0 -218
- package/src/eval-history.test.ts +0 -508
- package/src/eval-history.ts +0 -214
- package/src/eval-learning.test.ts +0 -378
- package/src/eval-learning.ts +0 -360
- package/src/eval-runner.test.ts +0 -223
- package/src/eval-runner.ts +0 -402
- package/src/export-tools.test.ts +0 -476
- package/src/export-tools.ts +0 -257
- package/src/hive.integration.test.ts +0 -2241
- package/src/hive.ts +0 -1628
- package/src/index.ts +0 -940
- package/src/learning.integration.test.ts +0 -1815
- package/src/learning.ts +0 -1079
- package/src/logger.test.ts +0 -189
- package/src/logger.ts +0 -135
- package/src/mandate-promotion.test.ts +0 -473
- package/src/mandate-promotion.ts +0 -239
- package/src/mandate-storage.integration.test.ts +0 -601
- package/src/mandate-storage.test.ts +0 -578
- package/src/mandate-storage.ts +0 -794
- package/src/mandates.ts +0 -540
- package/src/memory-tools.test.ts +0 -195
- package/src/memory-tools.ts +0 -344
- package/src/memory.integration.test.ts +0 -334
- package/src/memory.test.ts +0 -158
- package/src/memory.ts +0 -527
- package/src/model-selection.test.ts +0 -188
- package/src/model-selection.ts +0 -68
- package/src/observability-tools.test.ts +0 -359
- package/src/observability-tools.ts +0 -871
- package/src/output-guardrails.test.ts +0 -438
- package/src/output-guardrails.ts +0 -381
- package/src/pattern-maturity.test.ts +0 -1160
- package/src/pattern-maturity.ts +0 -525
- package/src/planning-guardrails.test.ts +0 -491
- package/src/planning-guardrails.ts +0 -438
- package/src/plugin.ts +0 -23
- package/src/post-compaction-tracker.test.ts +0 -251
- package/src/post-compaction-tracker.ts +0 -237
- package/src/query-tools.test.ts +0 -636
- package/src/query-tools.ts +0 -324
- package/src/rate-limiter.integration.test.ts +0 -466
- package/src/rate-limiter.ts +0 -774
- package/src/replay-tools.test.ts +0 -496
- package/src/replay-tools.ts +0 -240
- package/src/repo-crawl.integration.test.ts +0 -441
- package/src/repo-crawl.ts +0 -610
- package/src/schemas/cell-events.test.ts +0 -347
- package/src/schemas/cell-events.ts +0 -807
- package/src/schemas/cell.ts +0 -257
- package/src/schemas/evaluation.ts +0 -166
- package/src/schemas/index.test.ts +0 -199
- package/src/schemas/index.ts +0 -286
- package/src/schemas/mandate.ts +0 -232
- package/src/schemas/swarm-context.ts +0 -115
- package/src/schemas/task.ts +0 -161
- package/src/schemas/worker-handoff.test.ts +0 -302
- package/src/schemas/worker-handoff.ts +0 -131
- package/src/sessions/agent-discovery.test.ts +0 -137
- package/src/sessions/agent-discovery.ts +0 -112
- package/src/sessions/index.ts +0 -15
- package/src/skills.integration.test.ts +0 -1192
- package/src/skills.test.ts +0 -643
- package/src/skills.ts +0 -1549
- package/src/storage.integration.test.ts +0 -341
- package/src/storage.ts +0 -884
- package/src/structured.integration.test.ts +0 -817
- package/src/structured.test.ts +0 -1046
- package/src/structured.ts +0 -762
- package/src/swarm-decompose.test.ts +0 -188
- package/src/swarm-decompose.ts +0 -1302
- package/src/swarm-deferred.integration.test.ts +0 -157
- package/src/swarm-deferred.test.ts +0 -38
- package/src/swarm-insights.test.ts +0 -214
- package/src/swarm-insights.ts +0 -459
- package/src/swarm-mail.integration.test.ts +0 -970
- package/src/swarm-mail.ts +0 -739
- package/src/swarm-orchestrate.integration.test.ts +0 -282
- package/src/swarm-orchestrate.test.ts +0 -548
- package/src/swarm-orchestrate.ts +0 -3084
- package/src/swarm-prompts.test.ts +0 -1270
- package/src/swarm-prompts.ts +0 -2077
- package/src/swarm-research.integration.test.ts +0 -701
- package/src/swarm-research.test.ts +0 -698
- package/src/swarm-research.ts +0 -472
- package/src/swarm-review.integration.test.ts +0 -285
- package/src/swarm-review.test.ts +0 -879
- package/src/swarm-review.ts +0 -709
- package/src/swarm-strategies.ts +0 -407
- package/src/swarm-worktree.test.ts +0 -501
- package/src/swarm-worktree.ts +0 -575
- package/src/swarm.integration.test.ts +0 -2377
- package/src/swarm.ts +0 -38
- package/src/tool-adapter.integration.test.ts +0 -1221
- package/src/tool-availability.ts +0 -461
- package/tsconfig.json +0 -28
package/src/structured.ts
DELETED
|
@@ -1,762 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Structured Output Module - JSON extraction and schema validation
|
|
3
|
-
*
|
|
4
|
-
* Handles parsing agent responses that contain JSON, with multiple fallback
|
|
5
|
-
* strategies for malformed or wrapped content.
|
|
6
|
-
*
|
|
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_cell_tree` - Typed parsing for epic + subtasks
|
|
13
|
-
*
|
|
14
|
-
* @module structured
|
|
15
|
-
*/
|
|
16
|
-
import { tool } from "@opencode-ai/plugin";
|
|
17
|
-
import { z, type ZodSchema } from "zod";
|
|
18
|
-
import {
|
|
19
|
-
EvaluationSchema,
|
|
20
|
-
TaskDecompositionSchema,
|
|
21
|
-
CellTreeSchema,
|
|
22
|
-
ValidationResultSchema,
|
|
23
|
-
CriterionEvaluationSchema,
|
|
24
|
-
type Evaluation,
|
|
25
|
-
type TaskDecomposition,
|
|
26
|
-
type CellTree,
|
|
27
|
-
type ValidationResult,
|
|
28
|
-
} from "./schemas";
|
|
29
|
-
|
|
30
|
-
// ============================================================================
|
|
31
|
-
// Error Types
|
|
32
|
-
// ============================================================================
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Structured validation error with formatted feedback
|
|
36
|
-
*
|
|
37
|
-
* Contains both raw Zod errors for programmatic access and
|
|
38
|
-
* pre-formatted error bullets suitable for retry prompts.
|
|
39
|
-
*/
|
|
40
|
-
export class StructuredValidationError extends Error {
|
|
41
|
-
public readonly errorBullets: string[];
|
|
42
|
-
|
|
43
|
-
constructor(
|
|
44
|
-
message: string,
|
|
45
|
-
public readonly zodError: z.ZodError | null,
|
|
46
|
-
public readonly rawInput: string,
|
|
47
|
-
public readonly extractionMethod?: string,
|
|
48
|
-
) {
|
|
49
|
-
super(message);
|
|
50
|
-
this.name = "StructuredValidationError";
|
|
51
|
-
this.errorBullets = zodError ? formatZodErrors(zodError) : [message];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Format errors as bullet list for retry prompts
|
|
56
|
-
*/
|
|
57
|
-
toFeedback(): string {
|
|
58
|
-
return this.errorBullets.map((e) => `- ${e}`).join("\n");
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Error when JSON cannot be extracted from text
|
|
64
|
-
*/
|
|
65
|
-
export class JsonExtractionError extends Error {
|
|
66
|
-
constructor(
|
|
67
|
-
message: string,
|
|
68
|
-
public readonly rawInput: string,
|
|
69
|
-
public readonly attemptedStrategies: string[],
|
|
70
|
-
) {
|
|
71
|
-
super(message);
|
|
72
|
-
this.name = "JsonExtractionError";
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// ============================================================================
|
|
77
|
-
// Helper Functions
|
|
78
|
-
// ============================================================================
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Format Zod validation errors as readable bullet points
|
|
82
|
-
*
|
|
83
|
-
* @param error - Zod error from schema validation
|
|
84
|
-
* @returns Array of error messages suitable for feedback
|
|
85
|
-
*/
|
|
86
|
-
function formatZodErrors(error: z.ZodError): string[] {
|
|
87
|
-
return error.issues.map((issue) => {
|
|
88
|
-
const path = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
|
|
89
|
-
return `${path}${issue.message}`;
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Schema registry for named schema lookups
|
|
95
|
-
*/
|
|
96
|
-
const SCHEMA_REGISTRY: Record<string, ZodSchema> = {
|
|
97
|
-
evaluation: EvaluationSchema,
|
|
98
|
-
task_decomposition: TaskDecompositionSchema,
|
|
99
|
-
cell_tree: CellTreeSchema,
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Get schema by name from registry
|
|
104
|
-
*/
|
|
105
|
-
function getSchemaByName(name: string): ZodSchema {
|
|
106
|
-
const schema = SCHEMA_REGISTRY[name];
|
|
107
|
-
if (!schema) {
|
|
108
|
-
throw new Error(
|
|
109
|
-
`Unknown schema: ${name}. Available: ${Object.keys(SCHEMA_REGISTRY).join(", ")}`,
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
return schema;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
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)
|
|
125
|
-
*
|
|
126
|
-
* @param text Raw text potentially containing JSON
|
|
127
|
-
* @returns Parsed JSON object or null if all strategies fail
|
|
128
|
-
*/
|
|
129
|
-
function extractJsonFromText(text: string): [unknown, string] {
|
|
130
|
-
const trimmed = text.trim();
|
|
131
|
-
const strategies: string[] = [];
|
|
132
|
-
|
|
133
|
-
// Strategy 1: Direct parse (entire string is valid JSON)
|
|
134
|
-
strategies.push("direct_parse");
|
|
135
|
-
try {
|
|
136
|
-
return [JSON.parse(trimmed), "direct_parse"];
|
|
137
|
-
} catch {
|
|
138
|
-
// Continue to other strategies
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Strategy 2: Extract from ```json code blocks
|
|
142
|
-
strategies.push("json_code_block");
|
|
143
|
-
const jsonBlockMatch = trimmed.match(/```json\s*([\s\S]*?)```/i);
|
|
144
|
-
if (jsonBlockMatch) {
|
|
145
|
-
try {
|
|
146
|
-
return [JSON.parse(jsonBlockMatch[1].trim()), "json_code_block"];
|
|
147
|
-
} catch {
|
|
148
|
-
// Continue to other strategies
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Strategy 3: Extract from any code block (```...```)
|
|
153
|
-
strategies.push("any_code_block");
|
|
154
|
-
const codeBlockMatch = trimmed.match(/```\s*([\s\S]*?)```/);
|
|
155
|
-
if (codeBlockMatch) {
|
|
156
|
-
try {
|
|
157
|
-
return [JSON.parse(codeBlockMatch[1].trim()), "any_code_block"];
|
|
158
|
-
} catch {
|
|
159
|
-
// Continue to other strategies
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Strategy 4: Find first balanced {...} object
|
|
164
|
-
strategies.push("brace_match_object");
|
|
165
|
-
const objectJson = findBalancedBraces(trimmed, "{", "}");
|
|
166
|
-
if (objectJson) {
|
|
167
|
-
try {
|
|
168
|
-
return [JSON.parse(objectJson), "brace_match_object"];
|
|
169
|
-
} catch {
|
|
170
|
-
// Continue to other strategies
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Strategy 5: Find first balanced [...] array
|
|
175
|
-
strategies.push("brace_match_array");
|
|
176
|
-
const arrayJson = findBalancedBraces(trimmed, "[", "]");
|
|
177
|
-
if (arrayJson) {
|
|
178
|
-
try {
|
|
179
|
-
return [JSON.parse(arrayJson), "brace_match_array"];
|
|
180
|
-
} catch {
|
|
181
|
-
// Continue to other strategies
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Strategy 6: Try to repair common JSON issues and parse
|
|
186
|
-
strategies.push("repair_json");
|
|
187
|
-
const repaired = attemptJsonRepair(trimmed);
|
|
188
|
-
if (repaired !== trimmed) {
|
|
189
|
-
try {
|
|
190
|
-
return [JSON.parse(repaired), "repair_json"];
|
|
191
|
-
} catch {
|
|
192
|
-
// All strategies failed
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
throw new JsonExtractionError(
|
|
197
|
-
"Could not extract valid JSON from response",
|
|
198
|
-
text,
|
|
199
|
-
strategies,
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/** Maximum nesting depth before aborting (prevents stack overflow on malformed input) */
|
|
204
|
-
const MAX_BRACE_DEPTH = 100;
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Find a balanced pair of braces/brackets
|
|
208
|
-
*/
|
|
209
|
-
function findBalancedBraces(
|
|
210
|
-
text: string,
|
|
211
|
-
open: string,
|
|
212
|
-
close: string,
|
|
213
|
-
): string | null {
|
|
214
|
-
const startIdx = text.indexOf(open);
|
|
215
|
-
if (startIdx === -1) return null;
|
|
216
|
-
|
|
217
|
-
let depth = 0;
|
|
218
|
-
let inString = false;
|
|
219
|
-
let escapeNext = false;
|
|
220
|
-
|
|
221
|
-
for (let i = startIdx; i < text.length; i++) {
|
|
222
|
-
const char = text[i];
|
|
223
|
-
|
|
224
|
-
if (escapeNext) {
|
|
225
|
-
escapeNext = false;
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (char === "\\") {
|
|
230
|
-
escapeNext = true;
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (char === '"') {
|
|
235
|
-
inString = !inString;
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (inString) continue;
|
|
240
|
-
|
|
241
|
-
if (char === open) {
|
|
242
|
-
depth++;
|
|
243
|
-
if (depth > MAX_BRACE_DEPTH) {
|
|
244
|
-
return null; // Malformed input - too deeply nested
|
|
245
|
-
}
|
|
246
|
-
} else if (char === close) {
|
|
247
|
-
depth--;
|
|
248
|
-
if (depth < 0) {
|
|
249
|
-
return null; // Malformed input - unbalanced braces
|
|
250
|
-
}
|
|
251
|
-
if (depth === 0) {
|
|
252
|
-
return text.slice(startIdx, i + 1);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Attempt to repair common JSON issues.
|
|
262
|
-
*
|
|
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)
|
|
273
|
-
*/
|
|
274
|
-
function attemptJsonRepair(text: string): string {
|
|
275
|
-
let repaired = text;
|
|
276
|
-
|
|
277
|
-
// Find JSON-like content first
|
|
278
|
-
const match = repaired.match(/[\[{][\s\S]*[\]}]/);
|
|
279
|
-
if (!match) return text;
|
|
280
|
-
|
|
281
|
-
repaired = match[0];
|
|
282
|
-
|
|
283
|
-
// Replace single quotes with double quotes (but not inside strings)
|
|
284
|
-
// This is a simple heuristic - won't work for all cases
|
|
285
|
-
repaired = repaired.replace(/(?<![\\])'([^']*)'(?=\s*:)/g, '"$1"');
|
|
286
|
-
|
|
287
|
-
// Remove trailing commas before } or ]
|
|
288
|
-
repaired = repaired.replace(/,(\s*[}\]])/g, "$1");
|
|
289
|
-
|
|
290
|
-
// Replace literal newlines in strings with \n
|
|
291
|
-
repaired = repaired.replace(
|
|
292
|
-
/"([^"]*)\n([^"]*)"/g,
|
|
293
|
-
(_, before, after) => `"${before}\\n${after}"`,
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
return repaired;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// ============================================================================
|
|
300
|
-
// Validation Result Types
|
|
301
|
-
// ============================================================================
|
|
302
|
-
|
|
303
|
-
/** Maximum characters to show in raw input previews */
|
|
304
|
-
const RAW_INPUT_PREVIEW_LENGTH = 200;
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Result of a structured validation attempt
|
|
308
|
-
*/
|
|
309
|
-
interface StructuredValidationResult<T = unknown> {
|
|
310
|
-
success: boolean;
|
|
311
|
-
data?: T;
|
|
312
|
-
attempts: number;
|
|
313
|
-
errors?: string[];
|
|
314
|
-
extractionMethod?: string;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// ============================================================================
|
|
318
|
-
// Tool Definitions
|
|
319
|
-
// ============================================================================
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Extract JSON from markdown/text response
|
|
323
|
-
*
|
|
324
|
-
* Tries multiple extraction strategies in order:
|
|
325
|
-
* 1. Direct JSON parse
|
|
326
|
-
* 2. ```json code blocks
|
|
327
|
-
* 3. Any code blocks
|
|
328
|
-
* 4. Brace matching for objects
|
|
329
|
-
* 5. Bracket matching for arrays
|
|
330
|
-
* 6. JSON repair attempts
|
|
331
|
-
*/
|
|
332
|
-
export const structured_extract_json = tool({
|
|
333
|
-
description:
|
|
334
|
-
"Extract JSON from markdown/text response. Tries multiple strategies: direct parse, code blocks, brace matching, JSON repair.",
|
|
335
|
-
args: {
|
|
336
|
-
text: tool.schema.string().describe("Text containing JSON to extract"),
|
|
337
|
-
},
|
|
338
|
-
async execute(args, ctx) {
|
|
339
|
-
try {
|
|
340
|
-
const [parsed, method] = extractJsonFromText(args.text);
|
|
341
|
-
return JSON.stringify(
|
|
342
|
-
{
|
|
343
|
-
success: true,
|
|
344
|
-
data: parsed,
|
|
345
|
-
extraction_method: method,
|
|
346
|
-
},
|
|
347
|
-
null,
|
|
348
|
-
2,
|
|
349
|
-
);
|
|
350
|
-
} catch (error) {
|
|
351
|
-
if (error instanceof JsonExtractionError) {
|
|
352
|
-
return JSON.stringify(
|
|
353
|
-
{
|
|
354
|
-
success: false,
|
|
355
|
-
error: error.message,
|
|
356
|
-
attempted_strategies: error.attemptedStrategies,
|
|
357
|
-
raw_input_preview: args.text.slice(0, RAW_INPUT_PREVIEW_LENGTH),
|
|
358
|
-
},
|
|
359
|
-
null,
|
|
360
|
-
2,
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
throw error;
|
|
364
|
-
}
|
|
365
|
-
},
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Validate agent response against a named schema
|
|
370
|
-
*
|
|
371
|
-
* Extracts JSON from the response using multiple strategies,
|
|
372
|
-
* then validates against the specified schema.
|
|
373
|
-
*/
|
|
374
|
-
export const structured_validate = tool({
|
|
375
|
-
description:
|
|
376
|
-
"Validate agent response against a schema. Extracts JSON and validates with Zod. Returns structured errors for retry feedback.",
|
|
377
|
-
args: {
|
|
378
|
-
response: tool.schema.string().describe("Agent response to validate"),
|
|
379
|
-
schema_name: tool.schema
|
|
380
|
-
.enum(["evaluation", "task_decomposition", "cell_tree"])
|
|
381
|
-
.describe(
|
|
382
|
-
"Schema to validate against: " +
|
|
383
|
-
"evaluation = agent self-eval with criteria, " +
|
|
384
|
-
"task_decomposition = swarm task breakdown, " +
|
|
385
|
-
"cell_tree = epic with subtasks",
|
|
386
|
-
),
|
|
387
|
-
max_retries: tool.schema
|
|
388
|
-
.number()
|
|
389
|
-
.min(1)
|
|
390
|
-
.max(5)
|
|
391
|
-
.optional()
|
|
392
|
-
.describe("Max retries (for tracking - actual retry logic is external)"),
|
|
393
|
-
},
|
|
394
|
-
async execute(args, ctx) {
|
|
395
|
-
const maxRetries = args.max_retries ?? 3;
|
|
396
|
-
const result: ValidationResult = {
|
|
397
|
-
success: false,
|
|
398
|
-
attempts: 1,
|
|
399
|
-
errors: [],
|
|
400
|
-
};
|
|
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
|
-
|
|
411
|
-
// Step 1: Extract JSON
|
|
412
|
-
let extracted: unknown;
|
|
413
|
-
let extractionMethod: string;
|
|
414
|
-
|
|
415
|
-
try {
|
|
416
|
-
[extracted, extractionMethod] = extractJsonFromText(args.response);
|
|
417
|
-
result.extractionMethod = extractionMethod;
|
|
418
|
-
} catch (error) {
|
|
419
|
-
if (error instanceof JsonExtractionError) {
|
|
420
|
-
result.errors = [
|
|
421
|
-
`JSON extraction failed after trying: ${error.attemptedStrategies.join(", ")}`,
|
|
422
|
-
`Input preview: ${args.response.slice(0, RAW_INPUT_PREVIEW_LENGTH)}...`,
|
|
423
|
-
];
|
|
424
|
-
return JSON.stringify(result, null, 2);
|
|
425
|
-
}
|
|
426
|
-
throw error;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Step 2: Validate against schema
|
|
430
|
-
try {
|
|
431
|
-
const schema = getSchemaByName(args.schema_name);
|
|
432
|
-
const validated = schema.parse(extracted);
|
|
433
|
-
|
|
434
|
-
result.success = true;
|
|
435
|
-
result.data = validated;
|
|
436
|
-
delete result.errors;
|
|
437
|
-
|
|
438
|
-
return JSON.stringify(result, null, 2);
|
|
439
|
-
} catch (error) {
|
|
440
|
-
if (error instanceof z.ZodError) {
|
|
441
|
-
const formatted = formatZodErrors(error);
|
|
442
|
-
result.errors = formatted;
|
|
443
|
-
|
|
444
|
-
// Add hint for retries
|
|
445
|
-
if (result.attempts < maxRetries) {
|
|
446
|
-
result.errors.push(
|
|
447
|
-
`\nFix these issues and try again (attempt ${result.attempts}/${maxRetries})`,
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return JSON.stringify(result, null, 2);
|
|
452
|
-
}
|
|
453
|
-
throw error;
|
|
454
|
-
}
|
|
455
|
-
},
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* Parse and validate evaluation response from an agent
|
|
460
|
-
*
|
|
461
|
-
* Specialized tool for parsing self-evaluations. Returns
|
|
462
|
-
* the validated Evaluation or structured errors.
|
|
463
|
-
*/
|
|
464
|
-
export const structured_parse_evaluation = tool({
|
|
465
|
-
description:
|
|
466
|
-
"Parse and validate evaluation response from an agent. Uses EvaluationSchema.",
|
|
467
|
-
args: {
|
|
468
|
-
response: tool.schema
|
|
469
|
-
.string()
|
|
470
|
-
.describe("Agent response containing evaluation"),
|
|
471
|
-
},
|
|
472
|
-
async execute(args, ctx) {
|
|
473
|
-
try {
|
|
474
|
-
const [extracted, method] = extractJsonFromText(args.response);
|
|
475
|
-
const validated = EvaluationSchema.parse(extracted) as Evaluation;
|
|
476
|
-
|
|
477
|
-
return JSON.stringify(
|
|
478
|
-
{
|
|
479
|
-
success: true,
|
|
480
|
-
data: validated,
|
|
481
|
-
extraction_method: method,
|
|
482
|
-
summary: {
|
|
483
|
-
passed: validated.passed,
|
|
484
|
-
criteria_count: Object.keys(validated.criteria).length,
|
|
485
|
-
failed_criteria: Object.entries(validated.criteria)
|
|
486
|
-
.filter(([_, v]) => {
|
|
487
|
-
const criterion = v as z.infer<
|
|
488
|
-
typeof CriterionEvaluationSchema
|
|
489
|
-
>;
|
|
490
|
-
return !criterion.passed;
|
|
491
|
-
})
|
|
492
|
-
.map(([k]) => k),
|
|
493
|
-
},
|
|
494
|
-
},
|
|
495
|
-
null,
|
|
496
|
-
2,
|
|
497
|
-
);
|
|
498
|
-
} catch (error) {
|
|
499
|
-
if (error instanceof JsonExtractionError) {
|
|
500
|
-
return JSON.stringify(
|
|
501
|
-
{
|
|
502
|
-
success: false,
|
|
503
|
-
error: "Failed to extract JSON from response",
|
|
504
|
-
details: error.message,
|
|
505
|
-
attempted_strategies: error.attemptedStrategies,
|
|
506
|
-
feedback: [
|
|
507
|
-
"- Response must contain valid JSON",
|
|
508
|
-
"- Use ```json code blocks for clarity",
|
|
509
|
-
"- Ensure all braces and brackets are balanced",
|
|
510
|
-
].join("\n"),
|
|
511
|
-
},
|
|
512
|
-
null,
|
|
513
|
-
2,
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (error instanceof z.ZodError) {
|
|
518
|
-
const bullets = formatZodErrors(error);
|
|
519
|
-
return JSON.stringify(
|
|
520
|
-
{
|
|
521
|
-
success: false,
|
|
522
|
-
error: "Evaluation does not match schema",
|
|
523
|
-
validation_errors: bullets,
|
|
524
|
-
feedback: bullets.map((e) => `- ${e}`).join("\n"),
|
|
525
|
-
expected_shape: {
|
|
526
|
-
passed: "boolean",
|
|
527
|
-
criteria: "Record<string, { passed: boolean, feedback: string }>",
|
|
528
|
-
overall_feedback: "string",
|
|
529
|
-
retry_suggestion: "string | null",
|
|
530
|
-
},
|
|
531
|
-
},
|
|
532
|
-
null,
|
|
533
|
-
2,
|
|
534
|
-
);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
throw error;
|
|
538
|
-
}
|
|
539
|
-
},
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* Parse and validate task decomposition response
|
|
544
|
-
*
|
|
545
|
-
* Specialized tool for parsing decomposition results.
|
|
546
|
-
* Validates the structure and returns file lists for reservations.
|
|
547
|
-
*/
|
|
548
|
-
export const structured_parse_decomposition = tool({
|
|
549
|
-
description:
|
|
550
|
-
"Parse and validate task decomposition response. Uses TaskDecompositionSchema. Returns validated decomposition with file lists.",
|
|
551
|
-
args: {
|
|
552
|
-
response: tool.schema
|
|
553
|
-
.string()
|
|
554
|
-
.describe("Agent response containing decomposition"),
|
|
555
|
-
},
|
|
556
|
-
async execute(args, ctx) {
|
|
557
|
-
try {
|
|
558
|
-
const [extracted, method] = extractJsonFromText(args.response);
|
|
559
|
-
const validated = TaskDecompositionSchema.parse(
|
|
560
|
-
extracted,
|
|
561
|
-
) as TaskDecomposition;
|
|
562
|
-
|
|
563
|
-
// Collect all files for reservation planning
|
|
564
|
-
const allFiles = validated.subtasks.flatMap((s) => s.files);
|
|
565
|
-
const uniqueFiles = [...new Set(allFiles)];
|
|
566
|
-
|
|
567
|
-
return JSON.stringify(
|
|
568
|
-
{
|
|
569
|
-
success: true,
|
|
570
|
-
data: validated,
|
|
571
|
-
extraction_method: method,
|
|
572
|
-
summary: {
|
|
573
|
-
task:
|
|
574
|
-
validated.task.slice(0, 50) +
|
|
575
|
-
(validated.task.length > 50 ? "..." : ""),
|
|
576
|
-
subtask_count: validated.subtasks.length,
|
|
577
|
-
dependency_count: validated.dependencies?.length ?? 0,
|
|
578
|
-
total_files: uniqueFiles.length,
|
|
579
|
-
files: uniqueFiles,
|
|
580
|
-
effort_breakdown: validated.subtasks.reduce(
|
|
581
|
-
(acc, s) => {
|
|
582
|
-
acc[s.estimated_effort] = (acc[s.estimated_effort] || 0) + 1;
|
|
583
|
-
return acc;
|
|
584
|
-
},
|
|
585
|
-
{} as Record<string, number>,
|
|
586
|
-
),
|
|
587
|
-
},
|
|
588
|
-
},
|
|
589
|
-
null,
|
|
590
|
-
2,
|
|
591
|
-
);
|
|
592
|
-
} catch (error) {
|
|
593
|
-
if (error instanceof JsonExtractionError) {
|
|
594
|
-
return JSON.stringify(
|
|
595
|
-
{
|
|
596
|
-
success: false,
|
|
597
|
-
error: "Failed to extract JSON from response",
|
|
598
|
-
details: error.message,
|
|
599
|
-
attempted_strategies: error.attemptedStrategies,
|
|
600
|
-
feedback: [
|
|
601
|
-
"- Response must contain valid JSON",
|
|
602
|
-
"- Use ```json code blocks for clarity",
|
|
603
|
-
"- Ensure all braces and brackets are balanced",
|
|
604
|
-
].join("\n"),
|
|
605
|
-
},
|
|
606
|
-
null,
|
|
607
|
-
2,
|
|
608
|
-
);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
if (error instanceof z.ZodError) {
|
|
612
|
-
const bullets = formatZodErrors(error);
|
|
613
|
-
return JSON.stringify(
|
|
614
|
-
{
|
|
615
|
-
success: false,
|
|
616
|
-
error: "Decomposition does not match schema",
|
|
617
|
-
validation_errors: bullets,
|
|
618
|
-
feedback: bullets.map((e) => `- ${e}`).join("\n"),
|
|
619
|
-
expected_shape: {
|
|
620
|
-
task: "string (original task)",
|
|
621
|
-
reasoning: "string (optional)",
|
|
622
|
-
subtasks: [
|
|
623
|
-
{
|
|
624
|
-
title: "string",
|
|
625
|
-
description: "string",
|
|
626
|
-
files: ["string array of file paths"],
|
|
627
|
-
estimated_effort: "trivial | small | medium | large",
|
|
628
|
-
risks: ["optional string array"],
|
|
629
|
-
},
|
|
630
|
-
],
|
|
631
|
-
dependencies: [
|
|
632
|
-
{
|
|
633
|
-
from: "number (subtask index)",
|
|
634
|
-
to: "number (subtask index)",
|
|
635
|
-
type: "blocks | requires | related",
|
|
636
|
-
},
|
|
637
|
-
],
|
|
638
|
-
},
|
|
639
|
-
},
|
|
640
|
-
null,
|
|
641
|
-
2,
|
|
642
|
-
);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
throw error;
|
|
646
|
-
}
|
|
647
|
-
},
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
/**
|
|
651
|
-
* Parse and validate a bead tree (epic with subtasks)
|
|
652
|
-
*
|
|
653
|
-
* Validates the structure before creating cells.
|
|
654
|
-
*/
|
|
655
|
-
export const structured_parse_cell_tree = tool({
|
|
656
|
-
description:
|
|
657
|
-
"Parse and validate bead tree response. Uses CellTreeSchema. Validates before creating epic with subtasks.",
|
|
658
|
-
args: {
|
|
659
|
-
response: tool.schema
|
|
660
|
-
.string()
|
|
661
|
-
.describe("Agent response containing bead tree"),
|
|
662
|
-
},
|
|
663
|
-
async execute(args, ctx) {
|
|
664
|
-
try {
|
|
665
|
-
const [extracted, method] = extractJsonFromText(args.response);
|
|
666
|
-
const validated = CellTreeSchema.parse(extracted) as CellTree;
|
|
667
|
-
|
|
668
|
-
// Collect all files for reservation planning
|
|
669
|
-
const allFiles = validated.subtasks.flatMap((s) => s.files);
|
|
670
|
-
const uniqueFiles = [...new Set(allFiles)];
|
|
671
|
-
|
|
672
|
-
return JSON.stringify(
|
|
673
|
-
{
|
|
674
|
-
success: true,
|
|
675
|
-
data: validated,
|
|
676
|
-
extraction_method: method,
|
|
677
|
-
summary: {
|
|
678
|
-
epic_title: validated.epic.title,
|
|
679
|
-
subtask_count: validated.subtasks.length,
|
|
680
|
-
total_files: uniqueFiles.length,
|
|
681
|
-
files: uniqueFiles,
|
|
682
|
-
complexity_total: validated.subtasks.reduce(
|
|
683
|
-
(sum, s) => sum + s.estimated_complexity,
|
|
684
|
-
0,
|
|
685
|
-
),
|
|
686
|
-
},
|
|
687
|
-
},
|
|
688
|
-
null,
|
|
689
|
-
2,
|
|
690
|
-
);
|
|
691
|
-
} catch (error) {
|
|
692
|
-
if (error instanceof JsonExtractionError) {
|
|
693
|
-
return JSON.stringify(
|
|
694
|
-
{
|
|
695
|
-
success: false,
|
|
696
|
-
error: "Failed to extract JSON from response",
|
|
697
|
-
details: error.message,
|
|
698
|
-
feedback: [
|
|
699
|
-
"- Response must contain valid JSON",
|
|
700
|
-
"- Use ```json code blocks for clarity",
|
|
701
|
-
].join("\n"),
|
|
702
|
-
},
|
|
703
|
-
null,
|
|
704
|
-
2,
|
|
705
|
-
);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
if (error instanceof z.ZodError) {
|
|
709
|
-
const bullets = formatZodErrors(error);
|
|
710
|
-
return JSON.stringify(
|
|
711
|
-
{
|
|
712
|
-
success: false,
|
|
713
|
-
error: "Bead tree does not match schema",
|
|
714
|
-
validation_errors: bullets,
|
|
715
|
-
feedback: bullets.map((e) => `- ${e}`).join("\n"),
|
|
716
|
-
expected_shape: {
|
|
717
|
-
epic: { title: "string", description: "string (optional)" },
|
|
718
|
-
subtasks: [
|
|
719
|
-
{
|
|
720
|
-
title: "string",
|
|
721
|
-
description: "string (optional)",
|
|
722
|
-
files: ["string array"],
|
|
723
|
-
dependencies: ["number array of subtask indices"],
|
|
724
|
-
estimated_complexity: "1-5",
|
|
725
|
-
},
|
|
726
|
-
],
|
|
727
|
-
},
|
|
728
|
-
},
|
|
729
|
-
null,
|
|
730
|
-
2,
|
|
731
|
-
);
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
throw error;
|
|
735
|
-
}
|
|
736
|
-
},
|
|
737
|
-
});
|
|
738
|
-
|
|
739
|
-
// ============================================================================
|
|
740
|
-
// Utility Exports (for use by other modules)
|
|
741
|
-
// ============================================================================
|
|
742
|
-
|
|
743
|
-
export { extractJsonFromText, formatZodErrors, getSchemaByName };
|
|
744
|
-
|
|
745
|
-
// ============================================================================
|
|
746
|
-
// Tool Exports
|
|
747
|
-
// ============================================================================
|
|
748
|
-
|
|
749
|
-
export const structuredTools = {
|
|
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_cell_tree: structured_parse_cell_tree,
|
|
755
|
-
};
|
|
756
|
-
|
|
757
|
-
// ============================================================================
|
|
758
|
-
// Backward Compatibility Aliases
|
|
759
|
-
// ============================================================================
|
|
760
|
-
|
|
761
|
-
/** @deprecated Use structured_parse_cell_tree instead */
|
|
762
|
-
export const structured_parse_bead_tree = structured_parse_cell_tree;
|