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.
Files changed (91) hide show
  1. package/.env +2 -0
  2. package/.hive/eval-results.json +26 -0
  3. package/.hive/issues.jsonl +27 -0
  4. package/.hive/memories.jsonl +23 -1
  5. package/.opencode/eval-history.jsonl +12 -0
  6. package/CHANGELOG.md +182 -0
  7. package/README.md +29 -12
  8. package/bin/swarm.test.ts +881 -0
  9. package/bin/swarm.ts +686 -0
  10. package/dist/compaction-hook.d.ts +8 -1
  11. package/dist/compaction-hook.d.ts.map +1 -1
  12. package/dist/compaction-observability.d.ts +173 -0
  13. package/dist/compaction-observability.d.ts.map +1 -0
  14. package/dist/compaction-prompt-scoring.d.ts +124 -0
  15. package/dist/compaction-prompt-scoring.d.ts.map +1 -0
  16. package/dist/eval-capture.d.ts +174 -1
  17. package/dist/eval-capture.d.ts.map +1 -1
  18. package/dist/eval-gates.d.ts +84 -0
  19. package/dist/eval-gates.d.ts.map +1 -0
  20. package/dist/eval-history.d.ts +117 -0
  21. package/dist/eval-history.d.ts.map +1 -0
  22. package/dist/eval-learning.d.ts +216 -0
  23. package/dist/eval-learning.d.ts.map +1 -0
  24. package/dist/hive.d.ts.map +1 -1
  25. package/dist/index.d.ts +80 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +16098 -651
  28. package/dist/plugin.js +16012 -756
  29. package/dist/post-compaction-tracker.d.ts +133 -0
  30. package/dist/post-compaction-tracker.d.ts.map +1 -0
  31. package/dist/schemas/task.d.ts +3 -3
  32. package/dist/swarm-orchestrate.d.ts +23 -0
  33. package/dist/swarm-orchestrate.d.ts.map +1 -1
  34. package/dist/swarm-prompts.d.ts +25 -1
  35. package/dist/swarm-prompts.d.ts.map +1 -1
  36. package/dist/swarm.d.ts +4 -0
  37. package/dist/swarm.d.ts.map +1 -1
  38. package/evals/README.md +702 -105
  39. package/evals/compaction-prompt.eval.ts +149 -0
  40. package/evals/coordinator-behavior.eval.ts +8 -8
  41. package/evals/fixtures/compaction-prompt-cases.ts +305 -0
  42. package/evals/lib/compaction-loader.test.ts +248 -0
  43. package/evals/lib/compaction-loader.ts +320 -0
  44. package/evals/lib/data-loader.test.ts +345 -0
  45. package/evals/lib/data-loader.ts +107 -6
  46. package/evals/scorers/compaction-prompt-scorers.ts +145 -0
  47. package/evals/scorers/compaction-scorers.ts +13 -13
  48. package/evals/scorers/coordinator-discipline.evalite-test.ts +166 -2
  49. package/evals/scorers/coordinator-discipline.ts +348 -15
  50. package/evals/scorers/index.test.ts +146 -0
  51. package/evals/scorers/index.ts +104 -0
  52. package/evals/swarm-decomposition.eval.ts +9 -2
  53. package/examples/commands/swarm.md +291 -21
  54. package/examples/plugin-wrapper-template.ts +117 -0
  55. package/package.json +7 -5
  56. package/scripts/migrate-unknown-sessions.ts +349 -0
  57. package/src/compaction-capture.integration.test.ts +257 -0
  58. package/src/compaction-hook.test.ts +42 -0
  59. package/src/compaction-hook.ts +315 -86
  60. package/src/compaction-observability.integration.test.ts +139 -0
  61. package/src/compaction-observability.test.ts +187 -0
  62. package/src/compaction-observability.ts +324 -0
  63. package/src/compaction-prompt-scorers.test.ts +299 -0
  64. package/src/compaction-prompt-scoring.ts +298 -0
  65. package/src/eval-capture.test.ts +626 -1
  66. package/src/eval-capture.ts +286 -2
  67. package/src/eval-gates.test.ts +306 -0
  68. package/src/eval-gates.ts +218 -0
  69. package/src/eval-history.test.ts +508 -0
  70. package/src/eval-history.ts +214 -0
  71. package/src/eval-learning.test.ts +378 -0
  72. package/src/eval-learning.ts +360 -0
  73. package/src/eval-runner.test.ts +96 -0
  74. package/src/eval-runner.ts +356 -0
  75. package/src/hive.ts +34 -0
  76. package/src/index.ts +115 -2
  77. package/src/memory.test.ts +110 -0
  78. package/src/memory.ts +34 -0
  79. package/src/post-compaction-tracker.test.ts +251 -0
  80. package/src/post-compaction-tracker.ts +237 -0
  81. package/src/swarm-decompose.ts +2 -2
  82. package/src/swarm-orchestrate.ts +2 -2
  83. package/src/swarm-prompts.ts +2 -2
  84. package/src/swarm-review.ts +3 -3
  85. package/dist/beads.d.ts +0 -386
  86. package/dist/beads.d.ts.map +0 -1
  87. package/dist/schemas/bead-events.d.ts +0 -698
  88. package/dist/schemas/bead-events.d.ts.map +0 -1
  89. package/dist/schemas/bead.d.ts +0 -255
  90. package/dist/schemas/bead.d.ts.map +0 -1
  91. /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", () => {