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
|
@@ -1,438 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for output-guardrails module
|
|
3
|
-
*
|
|
4
|
-
* Validates smart truncation preserves structure boundaries and respects tool limits.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, expect, test } from "bun:test";
|
|
8
|
-
import {
|
|
9
|
-
truncateWithBoundaries,
|
|
10
|
-
guardrailOutput,
|
|
11
|
-
createMetrics,
|
|
12
|
-
DEFAULT_GUARDRAIL_CONFIG,
|
|
13
|
-
type GuardrailConfig,
|
|
14
|
-
} from "./output-guardrails";
|
|
15
|
-
|
|
16
|
-
describe("truncateWithBoundaries", () => {
|
|
17
|
-
test("returns unchanged text when under limit", () => {
|
|
18
|
-
const text = "Hello, world!";
|
|
19
|
-
const result = truncateWithBoundaries(text, 100);
|
|
20
|
-
expect(result).toBe(text);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("preserves complete JSON objects", () => {
|
|
24
|
-
const text = `{"users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]}`;
|
|
25
|
-
const result = truncateWithBoundaries(text, 40);
|
|
26
|
-
|
|
27
|
-
// Should truncate before the unclosed object
|
|
28
|
-
expect(result).toContain("[TRUNCATED");
|
|
29
|
-
expect(result).not.toContain('{"id": 2'); // Don't cut mid-object
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("preserves nested JSON structure", () => {
|
|
33
|
-
const text = JSON.stringify(
|
|
34
|
-
{
|
|
35
|
-
level1: {
|
|
36
|
-
level2: {
|
|
37
|
-
level3: {
|
|
38
|
-
data: "This is deeply nested data that will be truncated",
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
null,
|
|
44
|
-
2,
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
const result = truncateWithBoundaries(text, 50);
|
|
48
|
-
|
|
49
|
-
// Should find matching braces or truncate before unclosed structure
|
|
50
|
-
expect(result).toContain("[TRUNCATED");
|
|
51
|
-
|
|
52
|
-
// Truncation logic will try to preserve structure when possible
|
|
53
|
-
// Main requirement: it should truncate and add the marker
|
|
54
|
-
const truncatedLength = result.split("[TRUNCATED")[0].length;
|
|
55
|
-
expect(truncatedLength).toBeLessThan(text.length);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("preserves code block boundaries", () => {
|
|
59
|
-
const text = `
|
|
60
|
-
Here's some code:
|
|
61
|
-
|
|
62
|
-
\`\`\`typescript
|
|
63
|
-
function example() {
|
|
64
|
-
console.log("This is a long function");
|
|
65
|
-
console.log("With multiple lines");
|
|
66
|
-
console.log("That should be preserved");
|
|
67
|
-
}
|
|
68
|
-
\`\`\`
|
|
69
|
-
|
|
70
|
-
More text after.
|
|
71
|
-
`;
|
|
72
|
-
|
|
73
|
-
const result = truncateWithBoundaries(text, 80);
|
|
74
|
-
|
|
75
|
-
// Should either include closing ``` or truncate before opening ```
|
|
76
|
-
const backtickCount = (result.split("[TRUNCATED")[0].match(/```/g) || [])
|
|
77
|
-
.length;
|
|
78
|
-
expect(backtickCount % 2).toBe(0); // Even number of backticks
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("preserves markdown header boundaries", () => {
|
|
82
|
-
const text = `
|
|
83
|
-
# Main Title
|
|
84
|
-
|
|
85
|
-
Some intro text.
|
|
86
|
-
|
|
87
|
-
## Section 1
|
|
88
|
-
|
|
89
|
-
Content for section 1 with lots of detail that will eventually get truncated.
|
|
90
|
-
|
|
91
|
-
## Section 2
|
|
92
|
-
|
|
93
|
-
Content for section 2.
|
|
94
|
-
|
|
95
|
-
### Subsection 2.1
|
|
96
|
-
|
|
97
|
-
More content here.
|
|
98
|
-
`;
|
|
99
|
-
|
|
100
|
-
const result = truncateWithBoundaries(text, 120);
|
|
101
|
-
|
|
102
|
-
// Should truncate at a header boundary when possible
|
|
103
|
-
expect(result).toContain("[TRUNCATED");
|
|
104
|
-
|
|
105
|
-
// Check if it truncated at a header boundary
|
|
106
|
-
const beforeTruncate = result.split("[TRUNCATED")[0];
|
|
107
|
-
|
|
108
|
-
// Either ends with a header or doesn't have headers in the range
|
|
109
|
-
const hasHeaders = beforeTruncate.includes("##");
|
|
110
|
-
if (hasHeaders && beforeTruncate.length > 100) {
|
|
111
|
-
// If we have headers and enough content, should end near a header
|
|
112
|
-
expect(beforeTruncate).toMatch(/\n\n$/); // Should end at paragraph boundary at least
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
test("handles text without structure boundaries", () => {
|
|
117
|
-
const text = "a".repeat(1000);
|
|
118
|
-
const result = truncateWithBoundaries(text, 100);
|
|
119
|
-
|
|
120
|
-
expect(result).toContain("[TRUNCATED");
|
|
121
|
-
expect(result.length).toBeLessThan(200); // Much shorter than original
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test("adds truncation suffix with character count", () => {
|
|
125
|
-
const text = "a".repeat(1000);
|
|
126
|
-
const result = truncateWithBoundaries(text, 100);
|
|
127
|
-
|
|
128
|
-
expect(result).toMatch(/\[TRUNCATED - \d{1,3}(,\d{3})* chars removed\]/);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test("avoids truncating mid-word", () => {
|
|
132
|
-
const text = "The quick brown fox jumps over the lazy dog ".repeat(20);
|
|
133
|
-
const result = truncateWithBoundaries(text, 100);
|
|
134
|
-
|
|
135
|
-
const beforeTruncate = result.split("[TRUNCATED")[0];
|
|
136
|
-
|
|
137
|
-
// Should try to truncate at whitespace boundary when possible
|
|
138
|
-
// At minimum, it should truncate
|
|
139
|
-
expect(result).toContain("[TRUNCATED");
|
|
140
|
-
expect(beforeTruncate.length).toBeLessThan(text.length);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test("handles empty string", () => {
|
|
144
|
-
const result = truncateWithBoundaries("", 100);
|
|
145
|
-
expect(result).toBe("");
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
test("handles exact limit length", () => {
|
|
149
|
-
const text = "a".repeat(100);
|
|
150
|
-
const result = truncateWithBoundaries(text, 100);
|
|
151
|
-
expect(result).toBe(text);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test("handles just over limit", () => {
|
|
155
|
-
const text = "a".repeat(101);
|
|
156
|
-
const result = truncateWithBoundaries(text, 100);
|
|
157
|
-
|
|
158
|
-
expect(result).toContain("[TRUNCATED");
|
|
159
|
-
// Should remove at least some characters
|
|
160
|
-
const charsRemoved = text.length - result.split("[TRUNCATED")[0].length;
|
|
161
|
-
expect(charsRemoved).toBeGreaterThan(0);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test("extends limit by 20% to include matching braces", () => {
|
|
165
|
-
// Create a JSON object that ends just after the limit
|
|
166
|
-
const shortContent = '{"data": "x"}';
|
|
167
|
-
const padding = "x".repeat(85);
|
|
168
|
-
const text = padding + shortContent; // Total ~98 chars
|
|
169
|
-
|
|
170
|
-
const result = truncateWithBoundaries(text, 100);
|
|
171
|
-
|
|
172
|
-
// If the closing brace is within the 20% buffer (120 chars), should try to include it
|
|
173
|
-
// At minimum, the function should handle this gracefully
|
|
174
|
-
expect(result.length).toBeGreaterThan(0);
|
|
175
|
-
|
|
176
|
-
// If it truncated, should have the marker
|
|
177
|
-
if (result.length < text.length) {
|
|
178
|
-
expect(result).toContain("[TRUNCATED");
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test("extends limit by 20% to include closing code block", () => {
|
|
183
|
-
const text = `${"x".repeat(85)}\n\`\`\`\ncode\n\`\`\``; // ~98 chars
|
|
184
|
-
|
|
185
|
-
const result = truncateWithBoundaries(text, 100);
|
|
186
|
-
|
|
187
|
-
// If the closing ``` is within the 20% buffer, should try to include it
|
|
188
|
-
// At minimum, should handle gracefully
|
|
189
|
-
expect(result.length).toBeGreaterThan(0);
|
|
190
|
-
|
|
191
|
-
// If it did truncate and we're within the buffer, backticks should be balanced
|
|
192
|
-
if (result.length < text.length && text.length <= 120) {
|
|
193
|
-
const beforeTruncate = result.split("[TRUNCATED")[0];
|
|
194
|
-
const backtickCount = (beforeTruncate.match(/```/g) || []).length;
|
|
195
|
-
// Should either have no backticks or balanced backticks
|
|
196
|
-
expect(backtickCount === 0 || backtickCount % 2 === 0).toBe(true);
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe("guardrailOutput", () => {
|
|
202
|
-
test("skips configured tools", () => {
|
|
203
|
-
const longOutput = "a".repeat(50000);
|
|
204
|
-
|
|
205
|
-
// Beads tools should never be truncated
|
|
206
|
-
const result = guardrailOutput("hive_create", longOutput);
|
|
207
|
-
|
|
208
|
-
expect(result.truncated).toBe(false);
|
|
209
|
-
expect(result.output).toBe(longOutput);
|
|
210
|
-
expect(result.originalLength).toBe(50000);
|
|
211
|
-
expect(result.truncatedLength).toBe(50000);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test("truncates oversized output for non-skip tools", () => {
|
|
215
|
-
const longOutput = "a".repeat(50000);
|
|
216
|
-
|
|
217
|
-
// Random tool should be truncated at default limit
|
|
218
|
-
const result = guardrailOutput("some_random_tool", longOutput);
|
|
219
|
-
|
|
220
|
-
expect(result.truncated).toBe(true);
|
|
221
|
-
expect(result.output).toContain("[TRUNCATED");
|
|
222
|
-
expect(result.originalLength).toBe(50000);
|
|
223
|
-
expect(result.truncatedLength).toBeLessThan(50000);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
test("respects per-tool limits", () => {
|
|
227
|
-
const mediumOutput = "a".repeat(40000);
|
|
228
|
-
|
|
229
|
-
// repo-autopsy_file has 64000 char limit
|
|
230
|
-
const result1 = guardrailOutput("repo-autopsy_file", mediumOutput);
|
|
231
|
-
expect(result1.truncated).toBe(false);
|
|
232
|
-
|
|
233
|
-
// cass_stats has 8000 char limit
|
|
234
|
-
const result2 = guardrailOutput("cass_stats", mediumOutput);
|
|
235
|
-
expect(result2.truncated).toBe(true);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
test("uses custom config when provided", () => {
|
|
239
|
-
const customConfig: GuardrailConfig = {
|
|
240
|
-
defaultMaxChars: 100,
|
|
241
|
-
toolLimits: {
|
|
242
|
-
custom_tool: 200,
|
|
243
|
-
},
|
|
244
|
-
skipTools: ["never_truncate"],
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
const text150 = "a".repeat(150);
|
|
248
|
-
|
|
249
|
-
// Should truncate at default 100
|
|
250
|
-
const result1 = guardrailOutput("random_tool", text150, customConfig);
|
|
251
|
-
expect(result1.truncated).toBe(true);
|
|
252
|
-
|
|
253
|
-
// Should not truncate at custom limit 200
|
|
254
|
-
const result2 = guardrailOutput("custom_tool", text150, customConfig);
|
|
255
|
-
expect(result2.truncated).toBe(false);
|
|
256
|
-
|
|
257
|
-
// Should skip configured tool
|
|
258
|
-
const text500 = "a".repeat(500);
|
|
259
|
-
const result3 = guardrailOutput("never_truncate", text500, customConfig);
|
|
260
|
-
expect(result3.truncated).toBe(false);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
test("returns complete metadata", () => {
|
|
264
|
-
const output = "a".repeat(50000);
|
|
265
|
-
const result = guardrailOutput("test_tool", output);
|
|
266
|
-
|
|
267
|
-
expect(result).toHaveProperty("output");
|
|
268
|
-
expect(result).toHaveProperty("truncated");
|
|
269
|
-
expect(result).toHaveProperty("originalLength");
|
|
270
|
-
expect(result).toHaveProperty("truncatedLength");
|
|
271
|
-
|
|
272
|
-
expect(typeof result.truncated).toBe("boolean");
|
|
273
|
-
expect(typeof result.originalLength).toBe("number");
|
|
274
|
-
expect(typeof result.truncatedLength).toBe("number");
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
test("handles all skip tools from DEFAULT_GUARDRAIL_CONFIG", () => {
|
|
278
|
-
const longOutput = "a".repeat(100000);
|
|
279
|
-
|
|
280
|
-
const skipTools = DEFAULT_GUARDRAIL_CONFIG.skipTools;
|
|
281
|
-
|
|
282
|
-
// Test a sample of skip tools
|
|
283
|
-
const samplesToTest = [
|
|
284
|
-
"hive_create",
|
|
285
|
-
"agentmail_send",
|
|
286
|
-
"swarmmail_inbox",
|
|
287
|
-
"structured_validate",
|
|
288
|
-
"swarm_complete",
|
|
289
|
-
"mandate_query",
|
|
290
|
-
];
|
|
291
|
-
|
|
292
|
-
for (const toolName of samplesToTest) {
|
|
293
|
-
expect(skipTools).toContain(toolName);
|
|
294
|
-
const result = guardrailOutput(toolName, longOutput);
|
|
295
|
-
expect(result.truncated).toBe(false);
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
describe("createMetrics", () => {
|
|
301
|
-
test("creates metrics entry from guardrail result", () => {
|
|
302
|
-
const result = {
|
|
303
|
-
output: "truncated output",
|
|
304
|
-
truncated: true,
|
|
305
|
-
originalLength: 50000,
|
|
306
|
-
truncatedLength: 32000,
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
const metrics = createMetrics(result, "test_tool");
|
|
310
|
-
|
|
311
|
-
expect(metrics).toEqual({
|
|
312
|
-
toolName: "test_tool",
|
|
313
|
-
originalLength: 50000,
|
|
314
|
-
truncatedLength: 32000,
|
|
315
|
-
timestamp: expect.any(Number),
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
test("timestamp is reasonable", () => {
|
|
320
|
-
const result = {
|
|
321
|
-
output: "output",
|
|
322
|
-
truncated: false,
|
|
323
|
-
originalLength: 100,
|
|
324
|
-
truncatedLength: 100,
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
const before = Date.now();
|
|
328
|
-
const metrics = createMetrics(result, "test_tool");
|
|
329
|
-
const after = Date.now();
|
|
330
|
-
|
|
331
|
-
expect(metrics.timestamp).toBeGreaterThanOrEqual(before);
|
|
332
|
-
expect(metrics.timestamp).toBeLessThanOrEqual(after);
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
describe("DEFAULT_GUARDRAIL_CONFIG", () => {
|
|
337
|
-
test("has sensible defaults", () => {
|
|
338
|
-
expect(DEFAULT_GUARDRAIL_CONFIG.defaultMaxChars).toBe(32000);
|
|
339
|
-
expect(DEFAULT_GUARDRAIL_CONFIG.toolLimits).toBeDefined();
|
|
340
|
-
expect(DEFAULT_GUARDRAIL_CONFIG.skipTools).toBeDefined();
|
|
341
|
-
expect(DEFAULT_GUARDRAIL_CONFIG.skipTools.length).toBeGreaterThan(0);
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
test("includes higher limits for code/doc tools", () => {
|
|
345
|
-
const config = DEFAULT_GUARDRAIL_CONFIG;
|
|
346
|
-
|
|
347
|
-
expect(config.toolLimits["repo-autopsy_file"]).toBe(64000);
|
|
348
|
-
expect(config.toolLimits["context7_get-library-docs"]).toBe(64000);
|
|
349
|
-
expect(config.toolLimits["cass_view"]).toBe(64000);
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
test("includes lower limits for stats tools", () => {
|
|
353
|
-
const config = DEFAULT_GUARDRAIL_CONFIG;
|
|
354
|
-
|
|
355
|
-
expect(config.toolLimits["cass_stats"]).toBe(8000);
|
|
356
|
-
expect(config.toolLimits["repo-autopsy_stats"]).toBe(16000);
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
test("skips all internal coordination tools", () => {
|
|
360
|
-
const config = DEFAULT_GUARDRAIL_CONFIG;
|
|
361
|
-
|
|
362
|
-
// Sample of tools that should be in skipTools
|
|
363
|
-
const expectedSkips = [
|
|
364
|
-
"hive_create",
|
|
365
|
-
"hive_sync",
|
|
366
|
-
"agentmail_init",
|
|
367
|
-
"swarmmail_send",
|
|
368
|
-
"structured_parse_evaluation",
|
|
369
|
-
"swarm_decompose",
|
|
370
|
-
"mandate_file",
|
|
371
|
-
];
|
|
372
|
-
|
|
373
|
-
for (const tool of expectedSkips) {
|
|
374
|
-
expect(config.skipTools).toContain(tool);
|
|
375
|
-
}
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
describe("edge cases", () => {
|
|
380
|
-
test("handles JSON array at truncation boundary", () => {
|
|
381
|
-
const text = `[
|
|
382
|
-
{"id": 1, "data": "item1"},
|
|
383
|
-
{"id": 2, "data": "item2"},
|
|
384
|
-
{"id": 3, "data": "item3"}
|
|
385
|
-
]`;
|
|
386
|
-
|
|
387
|
-
const result = truncateWithBoundaries(text, 50);
|
|
388
|
-
|
|
389
|
-
expect(result).toContain("[TRUNCATED");
|
|
390
|
-
|
|
391
|
-
// Should not cut mid-item
|
|
392
|
-
const beforeTruncate = result.split("[TRUNCATED")[0];
|
|
393
|
-
const openBrackets = (beforeTruncate.match(/\[/g) || []).length;
|
|
394
|
-
const closeBrackets = (beforeTruncate.match(/\]/g) || []).length;
|
|
395
|
-
|
|
396
|
-
expect(openBrackets).toBeLessThanOrEqual(closeBrackets + 1);
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
test("handles mixed code blocks and JSON", () => {
|
|
400
|
-
const text = `
|
|
401
|
-
\`\`\`json
|
|
402
|
-
{"data": "This is JSON inside a code block"}
|
|
403
|
-
\`\`\`
|
|
404
|
-
|
|
405
|
-
And then some JSON outside:
|
|
406
|
-
{"more": "data"}
|
|
407
|
-
`;
|
|
408
|
-
|
|
409
|
-
const result = truncateWithBoundaries(text, 80);
|
|
410
|
-
|
|
411
|
-
// Should respect both code block and JSON boundaries
|
|
412
|
-
expect(result).toContain("[TRUNCATED");
|
|
413
|
-
|
|
414
|
-
const beforeTruncate = result.split("[TRUNCATED")[0];
|
|
415
|
-
const backtickCount = (beforeTruncate.match(/```/g) || []).length;
|
|
416
|
-
|
|
417
|
-
// Backticks should be balanced
|
|
418
|
-
expect(backtickCount % 2).toBe(0);
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
test("handles unicode characters correctly", () => {
|
|
422
|
-
const text = "Hello 世界! 🌍 ".repeat(100);
|
|
423
|
-
const result = truncateWithBoundaries(text, 100);
|
|
424
|
-
|
|
425
|
-
expect(result).toContain("[TRUNCATED");
|
|
426
|
-
// Length should be reasonable (not corrupted by unicode)
|
|
427
|
-
expect(result.length).toBeLessThan(text.length);
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
test("handles CRLF line endings", () => {
|
|
431
|
-
const text = "Line 1\r\nLine 2\r\nLine 3\r\n".repeat(50);
|
|
432
|
-
const result = truncateWithBoundaries(text, 100);
|
|
433
|
-
|
|
434
|
-
expect(result).toContain("[TRUNCATED");
|
|
435
|
-
// Should handle line endings gracefully
|
|
436
|
-
expect(result).not.toContain("\r\n[TRUNCATED");
|
|
437
|
-
});
|
|
438
|
-
});
|