opencode-swarm-plugin 0.38.0 → 0.40.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/.env +2 -0
- package/.hive/eval-results.json +26 -0
- package/.hive/issues.jsonl +27 -0
- package/.hive/memories.jsonl +23 -1
- package/.opencode/eval-history.jsonl +12 -0
- package/CHANGELOG.md +182 -0
- package/README.md +29 -12
- package/bin/swarm.test.ts +881 -0
- package/bin/swarm.ts +686 -0
- package/dist/compaction-hook.d.ts +8 -1
- package/dist/compaction-hook.d.ts.map +1 -1
- package/dist/compaction-observability.d.ts +173 -0
- package/dist/compaction-observability.d.ts.map +1 -0
- package/dist/compaction-prompt-scoring.d.ts +124 -0
- package/dist/compaction-prompt-scoring.d.ts.map +1 -0
- package/dist/eval-capture.d.ts +174 -1
- package/dist/eval-capture.d.ts.map +1 -1
- package/dist/eval-gates.d.ts +84 -0
- package/dist/eval-gates.d.ts.map +1 -0
- package/dist/eval-history.d.ts +117 -0
- package/dist/eval-history.d.ts.map +1 -0
- package/dist/eval-learning.d.ts +216 -0
- package/dist/eval-learning.d.ts.map +1 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.d.ts +80 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16098 -651
- package/dist/plugin.js +16012 -756
- package/dist/post-compaction-tracker.d.ts +133 -0
- package/dist/post-compaction-tracker.d.ts.map +1 -0
- package/dist/schemas/task.d.ts +3 -3
- package/dist/swarm-orchestrate.d.ts +23 -0
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts +25 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm.d.ts +4 -0
- package/dist/swarm.d.ts.map +1 -1
- package/evals/README.md +702 -105
- package/evals/compaction-prompt.eval.ts +149 -0
- package/evals/coordinator-behavior.eval.ts +8 -8
- package/evals/fixtures/compaction-prompt-cases.ts +305 -0
- package/evals/lib/compaction-loader.test.ts +248 -0
- package/evals/lib/compaction-loader.ts +320 -0
- package/evals/lib/data-loader.test.ts +345 -0
- package/evals/lib/data-loader.ts +107 -6
- package/evals/scorers/compaction-prompt-scorers.ts +145 -0
- package/evals/scorers/compaction-scorers.ts +13 -13
- package/evals/scorers/coordinator-discipline.evalite-test.ts +166 -2
- package/evals/scorers/coordinator-discipline.ts +348 -15
- package/evals/scorers/index.test.ts +146 -0
- package/evals/scorers/index.ts +104 -0
- package/evals/swarm-decomposition.eval.ts +9 -2
- package/examples/commands/swarm.md +291 -21
- package/examples/plugin-wrapper-template.ts +117 -0
- package/package.json +7 -5
- package/scripts/migrate-unknown-sessions.ts +349 -0
- package/src/compaction-capture.integration.test.ts +257 -0
- package/src/compaction-hook.test.ts +42 -0
- package/src/compaction-hook.ts +315 -86
- package/src/compaction-observability.integration.test.ts +139 -0
- package/src/compaction-observability.test.ts +187 -0
- package/src/compaction-observability.ts +324 -0
- package/src/compaction-prompt-scorers.test.ts +299 -0
- package/src/compaction-prompt-scoring.ts +298 -0
- package/src/eval-capture.test.ts +626 -1
- package/src/eval-capture.ts +286 -2
- package/src/eval-gates.test.ts +306 -0
- package/src/eval-gates.ts +218 -0
- package/src/eval-history.test.ts +508 -0
- package/src/eval-history.ts +214 -0
- package/src/eval-learning.test.ts +378 -0
- package/src/eval-learning.ts +360 -0
- package/src/eval-runner.test.ts +96 -0
- package/src/eval-runner.ts +356 -0
- package/src/hive.ts +34 -0
- package/src/index.ts +115 -2
- package/src/memory.test.ts +110 -0
- package/src/memory.ts +34 -0
- package/src/post-compaction-tracker.test.ts +251 -0
- package/src/post-compaction-tracker.ts +237 -0
- package/src/swarm-decompose.ts +2 -2
- package/src/swarm-orchestrate.ts +2 -2
- package/src/swarm-prompts.ts +2 -2
- package/src/swarm-review.ts +3 -3
- package/dist/beads.d.ts +0 -386
- package/dist/beads.d.ts.map +0 -1
- package/dist/schemas/bead-events.d.ts +0 -698
- package/dist/schemas/bead-events.d.ts.map +0 -1
- package/dist/schemas/bead.d.ts +0 -255
- package/dist/schemas/bead.d.ts.map +0 -1
- /package/evals/{evalite.config.ts → evalite.config.ts.bak} +0 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test for compaction event capture
|
|
3
|
+
*
|
|
4
|
+
* Verifies that captureCompactionEvent writes events to session JSONL
|
|
5
|
+
* and that all event types are captured with correct data.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, expect, it, afterAll } from "bun:test";
|
|
9
|
+
import { existsSync, unlinkSync } from "node:fs";
|
|
10
|
+
import {
|
|
11
|
+
captureCompactionEvent,
|
|
12
|
+
readSessionEvents,
|
|
13
|
+
getSessionPath,
|
|
14
|
+
} from "./eval-capture";
|
|
15
|
+
|
|
16
|
+
describe("Compaction Event Capture Integration", () => {
|
|
17
|
+
const testSessionId = `test-compaction-${Date.now()}`;
|
|
18
|
+
const sessionPath = getSessionPath(testSessionId);
|
|
19
|
+
|
|
20
|
+
afterAll(() => {
|
|
21
|
+
// Clean up test session file
|
|
22
|
+
if (existsSync(sessionPath)) {
|
|
23
|
+
unlinkSync(sessionPath);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("captures detection_complete event with confidence and reasons", () => {
|
|
28
|
+
captureCompactionEvent({
|
|
29
|
+
session_id: testSessionId,
|
|
30
|
+
epic_id: "bd-test-123",
|
|
31
|
+
compaction_type: "detection_complete",
|
|
32
|
+
payload: {
|
|
33
|
+
confidence: "high",
|
|
34
|
+
detected: true,
|
|
35
|
+
reasons: ["3 cells in_progress", "2 open subtasks"],
|
|
36
|
+
session_scan_contributed: true,
|
|
37
|
+
session_scan_reasons: ["swarm tool calls found in session"],
|
|
38
|
+
epic_id: "bd-test-123",
|
|
39
|
+
epic_title: "Test Epic",
|
|
40
|
+
subtask_count: 5,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Verify event was written to session file
|
|
45
|
+
expect(existsSync(sessionPath)).toBe(true);
|
|
46
|
+
|
|
47
|
+
// Read events from session
|
|
48
|
+
const events = readSessionEvents(testSessionId);
|
|
49
|
+
expect(events.length).toBe(1);
|
|
50
|
+
|
|
51
|
+
const event = events[0];
|
|
52
|
+
expect(event.session_id).toBe(testSessionId);
|
|
53
|
+
expect(event.epic_id).toBe("bd-test-123");
|
|
54
|
+
expect(event.event_type).toBe("COMPACTION");
|
|
55
|
+
expect(event.compaction_type).toBe("detection_complete");
|
|
56
|
+
|
|
57
|
+
// Verify payload structure
|
|
58
|
+
expect(event.payload.confidence).toBe("high");
|
|
59
|
+
expect(event.payload.detected).toBe(true);
|
|
60
|
+
expect(event.payload.reasons).toEqual(["3 cells in_progress", "2 open subtasks"]);
|
|
61
|
+
expect(event.payload.epic_id).toBe("bd-test-123");
|
|
62
|
+
expect(event.payload.epic_title).toBe("Test Epic");
|
|
63
|
+
expect(event.payload.subtask_count).toBe(5);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("captures prompt_generated event with FULL prompt content", () => {
|
|
67
|
+
const fullPrompt = `
|
|
68
|
+
┌─────────────────────────────────────────┐
|
|
69
|
+
│ 🐝 YOU ARE THE COORDINATOR 🐝 │
|
|
70
|
+
└─────────────────────────────────────────┘
|
|
71
|
+
|
|
72
|
+
# Swarm Continuation
|
|
73
|
+
|
|
74
|
+
**NON-NEGOTIABLE: YOU ARE THE COORDINATOR.**
|
|
75
|
+
|
|
76
|
+
## Epic State
|
|
77
|
+
**ID:** bd-epic-456
|
|
78
|
+
**Title:** Refactor authentication
|
|
79
|
+
**Status:** 2/5 subtasks complete
|
|
80
|
+
|
|
81
|
+
## Next Actions
|
|
82
|
+
1. Check swarm_status(epic_id="bd-epic-456")
|
|
83
|
+
2. Review completed work
|
|
84
|
+
3. Spawn remaining subtasks
|
|
85
|
+
`.trim();
|
|
86
|
+
|
|
87
|
+
captureCompactionEvent({
|
|
88
|
+
session_id: testSessionId,
|
|
89
|
+
epic_id: "bd-epic-456",
|
|
90
|
+
compaction_type: "prompt_generated",
|
|
91
|
+
payload: {
|
|
92
|
+
prompt_length: fullPrompt.length,
|
|
93
|
+
full_prompt: fullPrompt, // FULL content, not truncated
|
|
94
|
+
context_type: "llm_generated",
|
|
95
|
+
duration_ms: 1234,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const events = readSessionEvents(testSessionId);
|
|
100
|
+
const promptEvent = events.find((e) => e.compaction_type === "prompt_generated");
|
|
101
|
+
|
|
102
|
+
expect(promptEvent).toBeDefined();
|
|
103
|
+
if (promptEvent) {
|
|
104
|
+
expect(promptEvent.payload.full_prompt).toBe(fullPrompt);
|
|
105
|
+
expect(promptEvent.payload.prompt_length).toBe(fullPrompt.length);
|
|
106
|
+
expect(promptEvent.payload.context_type).toBe("llm_generated");
|
|
107
|
+
expect(promptEvent.payload.duration_ms).toBe(1234);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("captures context_injected event with FULL content", () => {
|
|
112
|
+
const fullContent = `[Swarm compaction: LLM-generated, high confidence]
|
|
113
|
+
|
|
114
|
+
# 🐝 Swarm State
|
|
115
|
+
|
|
116
|
+
**Epic:** bd-epic-789 - Add user permissions
|
|
117
|
+
**Project:** /Users/test/project
|
|
118
|
+
|
|
119
|
+
**Subtasks:**
|
|
120
|
+
- 2 closed
|
|
121
|
+
- 1 in_progress
|
|
122
|
+
- 2 open
|
|
123
|
+
|
|
124
|
+
## COORDINATOR MANDATES
|
|
125
|
+
|
|
126
|
+
⛔ NEVER use edit/write directly - SPAWN A WORKER
|
|
127
|
+
✅ ALWAYS use swarm_spawn_subtask for implementation
|
|
128
|
+
✅ ALWAYS review with swarm_review
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
captureCompactionEvent({
|
|
132
|
+
session_id: testSessionId,
|
|
133
|
+
epic_id: "bd-epic-789",
|
|
134
|
+
compaction_type: "context_injected",
|
|
135
|
+
payload: {
|
|
136
|
+
full_content: fullContent, // FULL content, not truncated
|
|
137
|
+
content_length: fullContent.length,
|
|
138
|
+
injection_method: "output.prompt",
|
|
139
|
+
context_type: "llm_generated",
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const events = readSessionEvents(testSessionId);
|
|
144
|
+
const injectEvent = events.find((e) => e.compaction_type === "context_injected");
|
|
145
|
+
|
|
146
|
+
expect(injectEvent).toBeDefined();
|
|
147
|
+
if (injectEvent) {
|
|
148
|
+
expect(injectEvent.payload.full_content).toBe(fullContent);
|
|
149
|
+
expect(injectEvent.payload.content_length).toBe(fullContent.length);
|
|
150
|
+
expect(injectEvent.payload.injection_method).toBe("output.prompt");
|
|
151
|
+
expect(injectEvent.payload.context_type).toBe("llm_generated");
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("captures all three event types in sequence", () => {
|
|
156
|
+
const sequenceSessionId = `test-sequence-${Date.now()}`;
|
|
157
|
+
const sequencePath = getSessionPath(sequenceSessionId);
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// Simulate compaction lifecycle
|
|
161
|
+
|
|
162
|
+
// 1. Detection
|
|
163
|
+
captureCompactionEvent({
|
|
164
|
+
session_id: sequenceSessionId,
|
|
165
|
+
epic_id: "bd-seq-123",
|
|
166
|
+
compaction_type: "detection_complete",
|
|
167
|
+
payload: {
|
|
168
|
+
confidence: "medium",
|
|
169
|
+
detected: true,
|
|
170
|
+
reasons: ["1 unclosed epic"],
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// 2. Prompt generation
|
|
175
|
+
captureCompactionEvent({
|
|
176
|
+
session_id: sequenceSessionId,
|
|
177
|
+
epic_id: "bd-seq-123",
|
|
178
|
+
compaction_type: "prompt_generated",
|
|
179
|
+
payload: {
|
|
180
|
+
full_prompt: "Test prompt content",
|
|
181
|
+
prompt_length: 19,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// 3. Context injection
|
|
186
|
+
captureCompactionEvent({
|
|
187
|
+
session_id: sequenceSessionId,
|
|
188
|
+
epic_id: "bd-seq-123",
|
|
189
|
+
compaction_type: "context_injected",
|
|
190
|
+
payload: {
|
|
191
|
+
full_content: "Test context content",
|
|
192
|
+
content_length: 20,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Verify all three events captured
|
|
197
|
+
const events = readSessionEvents(sequenceSessionId);
|
|
198
|
+
expect(events.length).toBe(3);
|
|
199
|
+
|
|
200
|
+
const types = events.map((e) => e.compaction_type);
|
|
201
|
+
expect(types).toContain("detection_complete");
|
|
202
|
+
expect(types).toContain("prompt_generated");
|
|
203
|
+
expect(types).toContain("context_injected");
|
|
204
|
+
|
|
205
|
+
// Verify order (chronological by timestamp)
|
|
206
|
+
const timestamps = events.map((e) => new Date(e.timestamp).getTime());
|
|
207
|
+
expect(timestamps[0]).toBeLessThanOrEqual(timestamps[1]);
|
|
208
|
+
expect(timestamps[1]).toBeLessThanOrEqual(timestamps[2]);
|
|
209
|
+
} finally {
|
|
210
|
+
// Clean up
|
|
211
|
+
if (existsSync(sequencePath)) {
|
|
212
|
+
unlinkSync(sequencePath);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("validates event schema with Zod", () => {
|
|
218
|
+
// This should not throw - captureCompactionEvent validates internally
|
|
219
|
+
expect(() => {
|
|
220
|
+
captureCompactionEvent({
|
|
221
|
+
session_id: testSessionId,
|
|
222
|
+
epic_id: "bd-validate-123",
|
|
223
|
+
compaction_type: "detection_complete",
|
|
224
|
+
payload: { confidence: "high" },
|
|
225
|
+
});
|
|
226
|
+
}).not.toThrow();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("rejects invalid compaction_type", () => {
|
|
230
|
+
expect(() => {
|
|
231
|
+
captureCompactionEvent({
|
|
232
|
+
session_id: testSessionId,
|
|
233
|
+
epic_id: "bd-invalid-123",
|
|
234
|
+
// @ts-expect-error - intentionally invalid type
|
|
235
|
+
compaction_type: "invalid_type",
|
|
236
|
+
payload: {},
|
|
237
|
+
});
|
|
238
|
+
}).toThrow();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("handles empty epic_id gracefully", () => {
|
|
242
|
+
captureCompactionEvent({
|
|
243
|
+
session_id: testSessionId,
|
|
244
|
+
epic_id: "unknown",
|
|
245
|
+
compaction_type: "detection_complete",
|
|
246
|
+
payload: {
|
|
247
|
+
confidence: "none",
|
|
248
|
+
detected: false,
|
|
249
|
+
reasons: [],
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const events = readSessionEvents(testSessionId);
|
|
254
|
+
const unknownEvent = events.find((e) => e.epic_id === "unknown");
|
|
255
|
+
expect(unknownEvent).toBeDefined();
|
|
256
|
+
});
|
|
257
|
+
});
|
|
@@ -85,6 +85,48 @@ describe("Compaction Hook", () => {
|
|
|
85
85
|
expect(SWARM_COMPACTION_CONTEXT).toContain("Blocked:");
|
|
86
86
|
expect(SWARM_COMPACTION_CONTEXT).toContain("Completed:");
|
|
87
87
|
});
|
|
88
|
+
|
|
89
|
+
// NEW: Full coordinator workflow must be present post-compaction
|
|
90
|
+
it("contains FULL coordinator workflow phases", () => {
|
|
91
|
+
// Phase 1.5: Research Phase
|
|
92
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("swarm_spawn_researcher");
|
|
93
|
+
|
|
94
|
+
// Phase 3: Decompose
|
|
95
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("swarm_select_strategy");
|
|
96
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("swarm_plan_prompt");
|
|
97
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("swarm_validate_decomposition");
|
|
98
|
+
|
|
99
|
+
// Phase 4: Create Cells
|
|
100
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("hive_create_epic");
|
|
101
|
+
|
|
102
|
+
// Phase 6: Spawn Workers
|
|
103
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("swarm_spawn_subtask");
|
|
104
|
+
|
|
105
|
+
// Phase 7: Review Loop
|
|
106
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("swarm_review");
|
|
107
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("swarm_review_feedback");
|
|
108
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("swarm_spawn_retry");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("contains forbidden tools section with ALL forbidden tools", () => {
|
|
112
|
+
// Repository fetching
|
|
113
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("repo-crawl_file");
|
|
114
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("repo-autopsy");
|
|
115
|
+
|
|
116
|
+
// Web/documentation fetching
|
|
117
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("webfetch");
|
|
118
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("fetch_fetch");
|
|
119
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("context7");
|
|
120
|
+
|
|
121
|
+
// Knowledge base
|
|
122
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("pdf-brain");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("contains strategy reference table", () => {
|
|
126
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("file-based");
|
|
127
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("feature-based");
|
|
128
|
+
expect(SWARM_COMPACTION_CONTEXT).toContain("risk-based");
|
|
129
|
+
});
|
|
88
130
|
});
|
|
89
131
|
|
|
90
132
|
describe("SWARM_DETECTION_FALLBACK", () => {
|