opencode-swarm-plugin 0.44.0 → 0.44.1

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 (205) hide show
  1. package/bin/swarm.serve.test.ts +6 -4
  2. package/bin/swarm.ts +16 -10
  3. package/dist/compaction-prompt-scoring.js +139 -0
  4. package/dist/eval-capture.js +12811 -0
  5. package/dist/hive.d.ts.map +1 -1
  6. package/dist/index.js +7644 -62599
  7. package/dist/plugin.js +23766 -78721
  8. package/dist/swarm-orchestrate.d.ts.map +1 -1
  9. package/dist/swarm-prompts.d.ts.map +1 -1
  10. package/dist/swarm-review.d.ts.map +1 -1
  11. package/package.json +17 -5
  12. package/.changeset/swarm-insights-data-layer.md +0 -63
  13. package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
  14. package/.hive/analysis/session-data-quality-audit.md +0 -320
  15. package/.hive/eval-results.json +0 -483
  16. package/.hive/issues.jsonl +0 -138
  17. package/.hive/memories.jsonl +0 -729
  18. package/.opencode/eval-history.jsonl +0 -327
  19. package/.turbo/turbo-build.log +0 -9
  20. package/CHANGELOG.md +0 -2286
  21. package/SCORER-ANALYSIS.md +0 -598
  22. package/docs/analysis/subagent-coordination-patterns.md +0 -902
  23. package/docs/analysis-socratic-planner-pattern.md +0 -504
  24. package/docs/planning/ADR-001-monorepo-structure.md +0 -171
  25. package/docs/planning/ADR-002-package-extraction.md +0 -393
  26. package/docs/planning/ADR-003-performance-improvements.md +0 -451
  27. package/docs/planning/ADR-004-message-queue-features.md +0 -187
  28. package/docs/planning/ADR-005-devtools-observability.md +0 -202
  29. package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
  30. package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
  31. package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
  32. package/docs/planning/ADR-010-cass-inhousing.md +0 -1215
  33. package/docs/planning/ROADMAP.md +0 -368
  34. package/docs/semantic-memory-cli-syntax.md +0 -123
  35. package/docs/swarm-mail-architecture.md +0 -1147
  36. package/docs/testing/context-recovery-test.md +0 -470
  37. package/evals/ARCHITECTURE.md +0 -1189
  38. package/evals/README.md +0 -768
  39. package/evals/compaction-prompt.eval.ts +0 -149
  40. package/evals/compaction-resumption.eval.ts +0 -289
  41. package/evals/coordinator-behavior.eval.ts +0 -307
  42. package/evals/coordinator-session.eval.ts +0 -154
  43. package/evals/evalite.config.ts.bak +0 -15
  44. package/evals/example.eval.ts +0 -31
  45. package/evals/fixtures/cass-baseline.ts +0 -217
  46. package/evals/fixtures/compaction-cases.ts +0 -350
  47. package/evals/fixtures/compaction-prompt-cases.ts +0 -311
  48. package/evals/fixtures/coordinator-sessions.ts +0 -328
  49. package/evals/fixtures/decomposition-cases.ts +0 -105
  50. package/evals/lib/compaction-loader.test.ts +0 -248
  51. package/evals/lib/compaction-loader.ts +0 -320
  52. package/evals/lib/data-loader.evalite-test.ts +0 -289
  53. package/evals/lib/data-loader.test.ts +0 -345
  54. package/evals/lib/data-loader.ts +0 -281
  55. package/evals/lib/llm.ts +0 -115
  56. package/evals/scorers/compaction-prompt-scorers.ts +0 -145
  57. package/evals/scorers/compaction-scorers.ts +0 -305
  58. package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
  59. package/evals/scorers/coordinator-discipline.ts +0 -325
  60. package/evals/scorers/index.test.ts +0 -146
  61. package/evals/scorers/index.ts +0 -328
  62. package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
  63. package/evals/scorers/outcome-scorers.ts +0 -349
  64. package/evals/swarm-decomposition.eval.ts +0 -121
  65. package/examples/commands/swarm.md +0 -745
  66. package/examples/plugin-wrapper-template.ts +0 -2515
  67. package/examples/skills/hive-workflow/SKILL.md +0 -212
  68. package/examples/skills/skill-creator/SKILL.md +0 -223
  69. package/examples/skills/swarm-coordination/SKILL.md +0 -292
  70. package/global-skills/cli-builder/SKILL.md +0 -344
  71. package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
  72. package/global-skills/learning-systems/SKILL.md +0 -644
  73. package/global-skills/skill-creator/LICENSE.txt +0 -202
  74. package/global-skills/skill-creator/SKILL.md +0 -352
  75. package/global-skills/skill-creator/references/output-patterns.md +0 -82
  76. package/global-skills/skill-creator/references/workflows.md +0 -28
  77. package/global-skills/swarm-coordination/SKILL.md +0 -995
  78. package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
  79. package/global-skills/swarm-coordination/references/strategies.md +0 -138
  80. package/global-skills/system-design/SKILL.md +0 -213
  81. package/global-skills/testing-patterns/SKILL.md +0 -430
  82. package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
  83. package/opencode-swarm-plugin-0.30.7.tgz +0 -0
  84. package/opencode-swarm-plugin-0.31.0.tgz +0 -0
  85. package/scripts/cleanup-test-memories.ts +0 -346
  86. package/scripts/init-skill.ts +0 -222
  87. package/scripts/migrate-unknown-sessions.ts +0 -349
  88. package/scripts/validate-skill.ts +0 -204
  89. package/src/agent-mail.ts +0 -1724
  90. package/src/anti-patterns.test.ts +0 -1167
  91. package/src/anti-patterns.ts +0 -448
  92. package/src/compaction-capture.integration.test.ts +0 -257
  93. package/src/compaction-hook.test.ts +0 -838
  94. package/src/compaction-hook.ts +0 -1204
  95. package/src/compaction-observability.integration.test.ts +0 -139
  96. package/src/compaction-observability.test.ts +0 -187
  97. package/src/compaction-observability.ts +0 -324
  98. package/src/compaction-prompt-scorers.test.ts +0 -475
  99. package/src/compaction-prompt-scoring.ts +0 -300
  100. package/src/contributor-tools.test.ts +0 -133
  101. package/src/contributor-tools.ts +0 -201
  102. package/src/dashboard.test.ts +0 -611
  103. package/src/dashboard.ts +0 -462
  104. package/src/error-enrichment.test.ts +0 -403
  105. package/src/error-enrichment.ts +0 -219
  106. package/src/eval-capture.test.ts +0 -1015
  107. package/src/eval-capture.ts +0 -929
  108. package/src/eval-gates.test.ts +0 -306
  109. package/src/eval-gates.ts +0 -218
  110. package/src/eval-history.test.ts +0 -508
  111. package/src/eval-history.ts +0 -214
  112. package/src/eval-learning.test.ts +0 -378
  113. package/src/eval-learning.ts +0 -360
  114. package/src/eval-runner.test.ts +0 -223
  115. package/src/eval-runner.ts +0 -402
  116. package/src/export-tools.test.ts +0 -476
  117. package/src/export-tools.ts +0 -257
  118. package/src/hive.integration.test.ts +0 -2241
  119. package/src/hive.ts +0 -1628
  120. package/src/index.ts +0 -940
  121. package/src/learning.integration.test.ts +0 -1815
  122. package/src/learning.ts +0 -1079
  123. package/src/logger.test.ts +0 -189
  124. package/src/logger.ts +0 -135
  125. package/src/mandate-promotion.test.ts +0 -473
  126. package/src/mandate-promotion.ts +0 -239
  127. package/src/mandate-storage.integration.test.ts +0 -601
  128. package/src/mandate-storage.test.ts +0 -578
  129. package/src/mandate-storage.ts +0 -794
  130. package/src/mandates.ts +0 -540
  131. package/src/memory-tools.test.ts +0 -195
  132. package/src/memory-tools.ts +0 -344
  133. package/src/memory.integration.test.ts +0 -334
  134. package/src/memory.test.ts +0 -158
  135. package/src/memory.ts +0 -527
  136. package/src/model-selection.test.ts +0 -188
  137. package/src/model-selection.ts +0 -68
  138. package/src/observability-tools.test.ts +0 -359
  139. package/src/observability-tools.ts +0 -871
  140. package/src/output-guardrails.test.ts +0 -438
  141. package/src/output-guardrails.ts +0 -381
  142. package/src/pattern-maturity.test.ts +0 -1160
  143. package/src/pattern-maturity.ts +0 -525
  144. package/src/planning-guardrails.test.ts +0 -491
  145. package/src/planning-guardrails.ts +0 -438
  146. package/src/plugin.ts +0 -23
  147. package/src/post-compaction-tracker.test.ts +0 -251
  148. package/src/post-compaction-tracker.ts +0 -237
  149. package/src/query-tools.test.ts +0 -636
  150. package/src/query-tools.ts +0 -324
  151. package/src/rate-limiter.integration.test.ts +0 -466
  152. package/src/rate-limiter.ts +0 -774
  153. package/src/replay-tools.test.ts +0 -496
  154. package/src/replay-tools.ts +0 -240
  155. package/src/repo-crawl.integration.test.ts +0 -441
  156. package/src/repo-crawl.ts +0 -610
  157. package/src/schemas/cell-events.test.ts +0 -347
  158. package/src/schemas/cell-events.ts +0 -807
  159. package/src/schemas/cell.ts +0 -257
  160. package/src/schemas/evaluation.ts +0 -166
  161. package/src/schemas/index.test.ts +0 -199
  162. package/src/schemas/index.ts +0 -286
  163. package/src/schemas/mandate.ts +0 -232
  164. package/src/schemas/swarm-context.ts +0 -115
  165. package/src/schemas/task.ts +0 -161
  166. package/src/schemas/worker-handoff.test.ts +0 -302
  167. package/src/schemas/worker-handoff.ts +0 -131
  168. package/src/sessions/agent-discovery.test.ts +0 -137
  169. package/src/sessions/agent-discovery.ts +0 -112
  170. package/src/sessions/index.ts +0 -15
  171. package/src/skills.integration.test.ts +0 -1192
  172. package/src/skills.test.ts +0 -643
  173. package/src/skills.ts +0 -1549
  174. package/src/storage.integration.test.ts +0 -341
  175. package/src/storage.ts +0 -884
  176. package/src/structured.integration.test.ts +0 -817
  177. package/src/structured.test.ts +0 -1046
  178. package/src/structured.ts +0 -762
  179. package/src/swarm-decompose.test.ts +0 -188
  180. package/src/swarm-decompose.ts +0 -1302
  181. package/src/swarm-deferred.integration.test.ts +0 -157
  182. package/src/swarm-deferred.test.ts +0 -38
  183. package/src/swarm-insights.test.ts +0 -214
  184. package/src/swarm-insights.ts +0 -459
  185. package/src/swarm-mail.integration.test.ts +0 -970
  186. package/src/swarm-mail.ts +0 -739
  187. package/src/swarm-orchestrate.integration.test.ts +0 -282
  188. package/src/swarm-orchestrate.test.ts +0 -548
  189. package/src/swarm-orchestrate.ts +0 -3084
  190. package/src/swarm-prompts.test.ts +0 -1270
  191. package/src/swarm-prompts.ts +0 -2077
  192. package/src/swarm-research.integration.test.ts +0 -701
  193. package/src/swarm-research.test.ts +0 -698
  194. package/src/swarm-research.ts +0 -472
  195. package/src/swarm-review.integration.test.ts +0 -285
  196. package/src/swarm-review.test.ts +0 -879
  197. package/src/swarm-review.ts +0 -709
  198. package/src/swarm-strategies.ts +0 -407
  199. package/src/swarm-worktree.test.ts +0 -501
  200. package/src/swarm-worktree.ts +0 -575
  201. package/src/swarm.integration.test.ts +0 -2377
  202. package/src/swarm.ts +0 -38
  203. package/src/tool-adapter.integration.test.ts +0 -1221
  204. package/src/tool-availability.ts +0 -461
  205. package/tsconfig.json +0 -28
@@ -1,1015 +0,0 @@
1
- /**
2
- * Tests for eval-capture coordinator event schemas and session capture
3
- */
4
- import { type Mock, afterEach, beforeEach, describe, expect, test } from "bun:test";
5
- import * as fs from "node:fs";
6
- import * as os from "node:os";
7
- import * as path from "node:path";
8
- import {
9
- type CoordinatorEvent,
10
- CoordinatorEventSchema,
11
- type CoordinatorSession,
12
- CoordinatorSessionSchema,
13
- captureCoordinatorEvent,
14
- captureCompactionEvent,
15
- saveSession,
16
- } from "./eval-capture.ts";
17
-
18
- describe("CoordinatorEvent schemas", () => {
19
- describe("DECISION events", () => {
20
- test("validates strategy_selected event", () => {
21
- const event: CoordinatorEvent = {
22
- session_id: "test-session",
23
- epic_id: "bd-123",
24
- timestamp: new Date().toISOString(),
25
- event_type: "DECISION",
26
- decision_type: "strategy_selected",
27
- payload: {
28
- strategy: "file-based",
29
- reasoning: "Files are well isolated",
30
- },
31
- };
32
-
33
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
34
- });
35
-
36
- test("validates worker_spawned event", () => {
37
- const event: CoordinatorEvent = {
38
- session_id: "test-session",
39
- epic_id: "bd-123",
40
- timestamp: new Date().toISOString(),
41
- event_type: "DECISION",
42
- decision_type: "worker_spawned",
43
- payload: {
44
- worker_id: "GreenStorm",
45
- subtask_id: "bd-123.1",
46
- files: ["src/test.ts"],
47
- },
48
- };
49
-
50
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
51
- });
52
-
53
- test("validates review_completed event", () => {
54
- const event: CoordinatorEvent = {
55
- session_id: "test-session",
56
- epic_id: "bd-123",
57
- timestamp: new Date().toISOString(),
58
- event_type: "DECISION",
59
- decision_type: "review_completed",
60
- payload: {
61
- subtask_id: "bd-123.1",
62
- approved: true,
63
- issues_found: 0,
64
- },
65
- };
66
-
67
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
68
- });
69
-
70
- test("validates decomposition_complete event", () => {
71
- const event: CoordinatorEvent = {
72
- session_id: "test-session",
73
- epic_id: "bd-123",
74
- timestamp: new Date().toISOString(),
75
- event_type: "DECISION",
76
- decision_type: "decomposition_complete",
77
- payload: {
78
- subtask_count: 3,
79
- strategy: "feature-based",
80
- },
81
- };
82
-
83
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
84
- });
85
-
86
- test("validates researcher_spawned event", () => {
87
- const event: CoordinatorEvent = {
88
- session_id: "test-session",
89
- epic_id: "bd-123",
90
- timestamp: new Date().toISOString(),
91
- event_type: "DECISION",
92
- decision_type: "researcher_spawned",
93
- payload: {
94
- researcher_id: "BlueLake",
95
- research_topic: "Next.js Cache Components",
96
- tools_used: ["pdf-brain", "context7"],
97
- },
98
- };
99
-
100
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
101
- });
102
-
103
- test("validates skill_loaded event", () => {
104
- const event: CoordinatorEvent = {
105
- session_id: "test-session",
106
- epic_id: "bd-123",
107
- timestamp: new Date().toISOString(),
108
- event_type: "DECISION",
109
- decision_type: "skill_loaded",
110
- payload: {
111
- skill_name: "testing-patterns",
112
- context: "Adding tests to legacy code",
113
- },
114
- };
115
-
116
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
117
- });
118
-
119
- test("validates inbox_checked event", () => {
120
- const event: CoordinatorEvent = {
121
- session_id: "test-session",
122
- epic_id: "bd-123",
123
- timestamp: new Date().toISOString(),
124
- event_type: "DECISION",
125
- decision_type: "inbox_checked",
126
- payload: {
127
- message_count: 3,
128
- urgent_count: 1,
129
- },
130
- };
131
-
132
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
133
- });
134
-
135
- test("validates blocker_resolved event", () => {
136
- const event: CoordinatorEvent = {
137
- session_id: "test-session",
138
- epic_id: "bd-123",
139
- timestamp: new Date().toISOString(),
140
- event_type: "DECISION",
141
- decision_type: "blocker_resolved",
142
- payload: {
143
- worker_id: "GreenStorm",
144
- subtask_id: "bd-123.2",
145
- blocker_type: "dependency",
146
- resolution: "Unblocked via coordinator action",
147
- },
148
- };
149
-
150
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
151
- });
152
-
153
- test("validates scope_change_approved event", () => {
154
- const event: CoordinatorEvent = {
155
- session_id: "test-session",
156
- epic_id: "bd-123",
157
- timestamp: new Date().toISOString(),
158
- event_type: "DECISION",
159
- decision_type: "scope_change_approved",
160
- payload: {
161
- worker_id: "BlueLake",
162
- subtask_id: "bd-123.1",
163
- original_scope: "Add auth service",
164
- new_scope: "Add auth service + email validation",
165
- estimated_time_add: 900000, // 15 min in ms
166
- },
167
- };
168
-
169
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
170
- });
171
-
172
- test("validates scope_change_rejected event", () => {
173
- const event: CoordinatorEvent = {
174
- session_id: "test-session",
175
- epic_id: "bd-123",
176
- timestamp: new Date().toISOString(),
177
- event_type: "DECISION",
178
- decision_type: "scope_change_rejected",
179
- payload: {
180
- worker_id: "BlueLake",
181
- subtask_id: "bd-123.1",
182
- requested_scope: "Add auth service + OAuth + SSO",
183
- rejection_reason: "Too large for single subtask",
184
- },
185
- };
186
-
187
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
188
- });
189
- });
190
-
191
- describe("VIOLATION events", () => {
192
- test("validates coordinator_edited_file event", () => {
193
- const event: CoordinatorEvent = {
194
- session_id: "test-session",
195
- epic_id: "bd-123",
196
- timestamp: new Date().toISOString(),
197
- event_type: "VIOLATION",
198
- violation_type: "coordinator_edited_file",
199
- payload: {
200
- file: "src/bad.ts",
201
- operation: "edit",
202
- },
203
- };
204
-
205
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
206
- });
207
-
208
- test("validates coordinator_ran_tests event", () => {
209
- const event: CoordinatorEvent = {
210
- session_id: "test-session",
211
- epic_id: "bd-123",
212
- timestamp: new Date().toISOString(),
213
- event_type: "VIOLATION",
214
- violation_type: "coordinator_ran_tests",
215
- payload: {
216
- command: "bun test",
217
- },
218
- };
219
-
220
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
221
- });
222
-
223
- test("validates coordinator_reserved_files event", () => {
224
- const event: CoordinatorEvent = {
225
- session_id: "test-session",
226
- epic_id: "bd-123",
227
- timestamp: new Date().toISOString(),
228
- event_type: "VIOLATION",
229
- violation_type: "coordinator_reserved_files",
230
- payload: {
231
- files: ["src/auth.ts"],
232
- },
233
- };
234
-
235
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
236
- });
237
-
238
- test("validates no_worker_spawned event", () => {
239
- const event: CoordinatorEvent = {
240
- session_id: "test-session",
241
- epic_id: "bd-123",
242
- timestamp: new Date().toISOString(),
243
- event_type: "VIOLATION",
244
- violation_type: "no_worker_spawned",
245
- payload: {
246
- subtask_id: "bd-123.1",
247
- reason: "Did work directly",
248
- },
249
- };
250
-
251
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
252
- });
253
- });
254
-
255
- describe("OUTCOME events", () => {
256
- test("validates subtask_success event", () => {
257
- const event: CoordinatorEvent = {
258
- session_id: "test-session",
259
- epic_id: "bd-123",
260
- timestamp: new Date().toISOString(),
261
- event_type: "OUTCOME",
262
- outcome_type: "subtask_success",
263
- payload: {
264
- subtask_id: "bd-123.1",
265
- duration_ms: 45000,
266
- files_touched: ["src/auth.ts"],
267
- },
268
- };
269
-
270
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
271
- });
272
-
273
- test("validates subtask_retry event", () => {
274
- const event: CoordinatorEvent = {
275
- session_id: "test-session",
276
- epic_id: "bd-123",
277
- timestamp: new Date().toISOString(),
278
- event_type: "OUTCOME",
279
- outcome_type: "subtask_retry",
280
- payload: {
281
- subtask_id: "bd-123.1",
282
- retry_count: 2,
283
- reason: "Review rejected",
284
- },
285
- };
286
-
287
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
288
- });
289
-
290
- test("validates subtask_failed event", () => {
291
- const event: CoordinatorEvent = {
292
- session_id: "test-session",
293
- epic_id: "bd-123",
294
- timestamp: new Date().toISOString(),
295
- event_type: "OUTCOME",
296
- outcome_type: "subtask_failed",
297
- payload: {
298
- subtask_id: "bd-123.1",
299
- error: "Type error in auth.ts",
300
- },
301
- };
302
-
303
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
304
- });
305
-
306
- test("validates epic_complete event", () => {
307
- const event: CoordinatorEvent = {
308
- session_id: "test-session",
309
- epic_id: "bd-123",
310
- timestamp: new Date().toISOString(),
311
- event_type: "OUTCOME",
312
- outcome_type: "epic_complete",
313
- payload: {
314
- success: true,
315
- total_duration_ms: 180000,
316
- subtasks_completed: 3,
317
- },
318
- };
319
-
320
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
321
- });
322
-
323
- test("validates blocker_detected event", () => {
324
- const event: CoordinatorEvent = {
325
- session_id: "test-session",
326
- epic_id: "bd-123",
327
- timestamp: new Date().toISOString(),
328
- event_type: "OUTCOME",
329
- outcome_type: "blocker_detected",
330
- payload: {
331
- worker_id: "GreenStorm",
332
- subtask_id: "bd-123.2",
333
- blocker_type: "dependency",
334
- blocker_description: "Waiting for database schema from bd-123.1",
335
- reported_at: new Date().toISOString(),
336
- },
337
- };
338
-
339
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
340
- });
341
- });
342
- });
343
-
344
- describe("CoordinatorSession schema", () => {
345
- test("validates complete session", () => {
346
- const session: CoordinatorSession = {
347
- session_id: "test-session",
348
- epic_id: "bd-123",
349
- start_time: new Date().toISOString(),
350
- end_time: new Date().toISOString(),
351
- events: [
352
- {
353
- session_id: "test-session",
354
- epic_id: "bd-123",
355
- timestamp: new Date().toISOString(),
356
- event_type: "DECISION",
357
- decision_type: "strategy_selected",
358
- payload: { strategy: "file-based" },
359
- },
360
- ],
361
- };
362
-
363
- expect(() => CoordinatorSessionSchema.parse(session)).not.toThrow();
364
- });
365
-
366
- test("validates session without end_time", () => {
367
- const session: Partial<CoordinatorSession> = {
368
- session_id: "test-session",
369
- epic_id: "bd-123",
370
- start_time: new Date().toISOString(),
371
- events: [],
372
- };
373
-
374
- expect(() => CoordinatorSessionSchema.parse(session)).not.toThrow();
375
- });
376
- });
377
-
378
- describe("captureCoordinatorEvent", () => {
379
- let sessionDir: string;
380
- let sessionId: string;
381
-
382
- beforeEach(() => {
383
- sessionDir = path.join(os.homedir(), ".config", "swarm-tools", "sessions");
384
- sessionId = `test-${Date.now()}`;
385
- });
386
-
387
- afterEach(() => {
388
- // Clean up test session file
389
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
390
- if (fs.existsSync(sessionPath)) {
391
- fs.unlinkSync(sessionPath);
392
- }
393
- });
394
-
395
- test("creates session directory if not exists", () => {
396
- const event: CoordinatorEvent = {
397
- session_id: sessionId,
398
- epic_id: "bd-123",
399
- timestamp: new Date().toISOString(),
400
- event_type: "DECISION",
401
- decision_type: "strategy_selected",
402
- payload: { strategy: "file-based" },
403
- };
404
-
405
- captureCoordinatorEvent(event);
406
-
407
- expect(fs.existsSync(sessionDir)).toBe(true);
408
- });
409
-
410
- test("appends event to session file", () => {
411
- const event: CoordinatorEvent = {
412
- session_id: sessionId,
413
- epic_id: "bd-123",
414
- timestamp: new Date().toISOString(),
415
- event_type: "DECISION",
416
- decision_type: "strategy_selected",
417
- payload: { strategy: "file-based" },
418
- };
419
-
420
- captureCoordinatorEvent(event);
421
-
422
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
423
- expect(fs.existsSync(sessionPath)).toBe(true);
424
-
425
- const content = fs.readFileSync(sessionPath, "utf-8");
426
- const lines = content.trim().split("\n");
427
- expect(lines).toHaveLength(1);
428
-
429
- const parsed = JSON.parse(lines[0]);
430
- expect(parsed.session_id).toBe(sessionId);
431
- expect(parsed.event_type).toBe("DECISION");
432
- });
433
-
434
- test("appends multiple events to same session", () => {
435
- const event1: CoordinatorEvent = {
436
- session_id: sessionId,
437
- epic_id: "bd-123",
438
- timestamp: new Date().toISOString(),
439
- event_type: "DECISION",
440
- decision_type: "strategy_selected",
441
- payload: { strategy: "file-based" },
442
- };
443
-
444
- const event2: CoordinatorEvent = {
445
- session_id: sessionId,
446
- epic_id: "bd-123",
447
- timestamp: new Date().toISOString(),
448
- event_type: "VIOLATION",
449
- violation_type: "coordinator_edited_file",
450
- payload: { file: "src/bad.ts" },
451
- };
452
-
453
- captureCoordinatorEvent(event1);
454
- captureCoordinatorEvent(event2);
455
-
456
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
457
- const content = fs.readFileSync(sessionPath, "utf-8");
458
- const lines = content.trim().split("\n");
459
- expect(lines).toHaveLength(2);
460
- });
461
- });
462
-
463
- describe("COMPACTION events", () => {
464
- test("validates detection_complete event", () => {
465
- const event: CoordinatorEvent = {
466
- session_id: "test-session",
467
- epic_id: "bd-123",
468
- timestamp: new Date().toISOString(),
469
- event_type: "COMPACTION",
470
- compaction_type: "detection_complete",
471
- payload: {
472
- confidence: "high",
473
- context_type: "full",
474
- epic_id: "bd-456",
475
- },
476
- };
477
-
478
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
479
- });
480
-
481
- test("validates prompt_generated event", () => {
482
- const event: CoordinatorEvent = {
483
- session_id: "test-session",
484
- epic_id: "bd-123",
485
- timestamp: new Date().toISOString(),
486
- event_type: "COMPACTION",
487
- compaction_type: "prompt_generated",
488
- payload: {
489
- prompt_length: 5000,
490
- full_prompt: "You are a coordinator...", // Full prompt content captured
491
- context_type: "full",
492
- },
493
- };
494
-
495
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
496
- });
497
-
498
- test("validates context_injected event", () => {
499
- const event: CoordinatorEvent = {
500
- session_id: "test-session",
501
- epic_id: "bd-123",
502
- timestamp: new Date().toISOString(),
503
- event_type: "COMPACTION",
504
- compaction_type: "context_injected",
505
- payload: {
506
- context_type: "fallback",
507
- injected_sections: ["swarm_status", "mandatory_instructions"],
508
- },
509
- };
510
-
511
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
512
- });
513
-
514
- test("validates resumption_started event", () => {
515
- const event: CoordinatorEvent = {
516
- session_id: "test-session",
517
- epic_id: "bd-123",
518
- timestamp: new Date().toISOString(),
519
- event_type: "COMPACTION",
520
- compaction_type: "resumption_started",
521
- payload: {
522
- epic_id: "bd-456",
523
- agent_role: "coordinator",
524
- context_loaded: true,
525
- },
526
- };
527
-
528
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
529
- });
530
-
531
- test("validates tool_call_tracked event", () => {
532
- const event: CoordinatorEvent = {
533
- session_id: "test-session",
534
- epic_id: "bd-123",
535
- timestamp: new Date().toISOString(),
536
- event_type: "COMPACTION",
537
- compaction_type: "tool_call_tracked",
538
- payload: {
539
- tool_name: "hive_create_epic",
540
- extracted_data: {
541
- epic_id: "bd-789",
542
- epic_title: "Add auth",
543
- },
544
- },
545
- };
546
-
547
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
548
- });
549
-
550
- test("rejects invalid compaction_type", () => {
551
- const event = {
552
- session_id: "test-session",
553
- epic_id: "bd-123",
554
- timestamp: new Date().toISOString(),
555
- event_type: "COMPACTION",
556
- compaction_type: "invalid_type",
557
- payload: {},
558
- };
559
-
560
- expect(() => CoordinatorEventSchema.parse(event)).toThrow();
561
- });
562
-
563
- test("captures full prompt content without truncation", () => {
564
- const longPrompt = "A".repeat(10000); // 10k chars
565
- const event: CoordinatorEvent = {
566
- session_id: "test-session",
567
- epic_id: "bd-123",
568
- timestamp: new Date().toISOString(),
569
- event_type: "COMPACTION",
570
- compaction_type: "prompt_generated",
571
- payload: {
572
- prompt_length: longPrompt.length,
573
- full_prompt: longPrompt,
574
- context_type: "full",
575
- },
576
- };
577
-
578
- expect(() => CoordinatorEventSchema.parse(event)).not.toThrow();
579
- expect(event.payload.full_prompt).toBe(longPrompt);
580
- expect(event.payload.full_prompt.length).toBe(10000);
581
- });
582
- });
583
-
584
- describe("saveSession", () => {
585
- let sessionDir: string;
586
- let sessionId: string;
587
-
588
- beforeEach(() => {
589
- sessionDir = path.join(os.homedir(), ".config", "swarm-tools", "sessions");
590
- sessionId = `test-${Date.now()}`;
591
- });
592
-
593
- afterEach(() => {
594
- // Clean up test session file
595
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
596
- if (fs.existsSync(sessionPath)) {
597
- fs.unlinkSync(sessionPath);
598
- }
599
- });
600
-
601
- test("wraps events in session structure", () => {
602
- // Capture some events
603
- const event1: CoordinatorEvent = {
604
- session_id: sessionId,
605
- epic_id: "bd-123",
606
- timestamp: new Date().toISOString(),
607
- event_type: "DECISION",
608
- decision_type: "strategy_selected",
609
- payload: { strategy: "file-based" },
610
- };
611
-
612
- captureCoordinatorEvent(event1);
613
-
614
- // Save session
615
- const session = saveSession({
616
- session_id: sessionId,
617
- epic_id: "bd-123",
618
- });
619
-
620
- expect(session).toBeDefined();
621
- expect(session.session_id).toBe(sessionId);
622
- expect(session.events).toHaveLength(1);
623
- expect(session.start_time).toBeDefined();
624
- expect(session.end_time).toBeDefined();
625
- });
626
-
627
- test("returns null if session file does not exist", () => {
628
- const session = saveSession({
629
- session_id: "nonexistent",
630
- epic_id: "bd-999",
631
- });
632
-
633
- expect(session).toBeNull();
634
- });
635
- });
636
-
637
- describe("session_id propagation from ctx.sessionID", () => {
638
- let sessionDir: string;
639
- let sessionId: string;
640
-
641
- beforeEach(() => {
642
- sessionDir = path.join(os.homedir(), ".config", "swarm-tools", "sessions");
643
- sessionId = `test-ctx-${Date.now()}`;
644
- });
645
-
646
- afterEach(() => {
647
- // Clean up test session file
648
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
649
- if (fs.existsSync(sessionPath)) {
650
- fs.unlinkSync(sessionPath);
651
- }
652
- });
653
-
654
- test("session_id should come from ctx.sessionID, not process.env", () => {
655
- // GIVEN: process.env.OPENCODE_SESSION_ID is empty (mimics real scenario)
656
- const oldEnv = process.env.OPENCODE_SESSION_ID;
657
- delete process.env.OPENCODE_SESSION_ID;
658
-
659
- try {
660
- // WHEN: captureCoordinatorEvent is called with session_id from ctx.sessionID
661
- const event: CoordinatorEvent = {
662
- session_id: sessionId, // This should come from ctx.sessionID in call sites
663
- epic_id: "bd-123",
664
- timestamp: new Date().toISOString(),
665
- event_type: "DECISION",
666
- decision_type: "strategy_selected",
667
- payload: { strategy: "file-based" },
668
- };
669
-
670
- captureCoordinatorEvent(event);
671
-
672
- // THEN: Event should be captured with correct session_id
673
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
674
- expect(fs.existsSync(sessionPath)).toBe(true);
675
-
676
- const content = fs.readFileSync(sessionPath, "utf-8");
677
- const parsed = JSON.parse(content.trim());
678
- expect(parsed.session_id).toBe(sessionId);
679
- expect(parsed.session_id).not.toBe("unknown");
680
- } finally {
681
- // Restore env
682
- if (oldEnv !== undefined) {
683
- process.env.OPENCODE_SESSION_ID = oldEnv;
684
- }
685
- }
686
- });
687
-
688
- test("demonstrates call sites must pass ctx.sessionID not process.env", () => {
689
- // GIVEN: This simulates what happens in real call sites
690
- const oldEnv = process.env.OPENCODE_SESSION_ID;
691
- delete process.env.OPENCODE_SESSION_ID; // Empty in real OpenCode environment
692
-
693
- try {
694
- // WHEN: Call site uses process.env (CURRENT BAD PATTERN)
695
- const badSessionId = process.env.OPENCODE_SESSION_ID || "unknown";
696
- const badEvent: CoordinatorEvent = {
697
- session_id: badSessionId, // This evaluates to "unknown"
698
- epic_id: "bd-123",
699
- timestamp: new Date().toISOString(),
700
- event_type: "DECISION",
701
- decision_type: "strategy_selected",
702
- payload: { strategy: "file-based" },
703
- };
704
-
705
- captureCoordinatorEvent(badEvent);
706
-
707
- // THEN: Event goes to unknown.jsonl (BAD!)
708
- const unknownPath = path.join(sessionDir, "unknown.jsonl");
709
- expect(fs.existsSync(unknownPath)).toBe(true);
710
-
711
- // WHEN: Call site uses ctx.sessionID (CORRECT PATTERN)
712
- const goodEvent: CoordinatorEvent = {
713
- session_id: sessionId, // From ctx.sessionID
714
- epic_id: "bd-123",
715
- timestamp: new Date().toISOString(),
716
- event_type: "DECISION",
717
- decision_type: "strategy_selected",
718
- payload: { strategy: "file-based" },
719
- };
720
-
721
- captureCoordinatorEvent(goodEvent);
722
-
723
- // THEN: Event goes to correct session file
724
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
725
- expect(fs.existsSync(sessionPath)).toBe(true);
726
- } finally {
727
- if (oldEnv !== undefined) {
728
- process.env.OPENCODE_SESSION_ID = oldEnv;
729
- }
730
- }
731
- });
732
-
733
- test("verifies all call sites now use ctx.sessionID", () => {
734
- // This test documents that we've fixed all call sites to use ctx.sessionID
735
- // instead of process.env.OPENCODE_SESSION_ID
736
-
737
- // The fix was applied to:
738
- // 1. src/swarm-orchestrate.ts:1743, 1852 - swarm_complete uses _ctx.sessionID
739
- // 2. src/swarm-review.ts:515, 565 - swarm_review_feedback uses _ctx.sessionID
740
- // 3. src/swarm-decompose.ts:780 - swarm_delegate_planning uses _ctx.sessionID
741
- // 4. src/swarm-prompts.ts:1407 - swarm_spawn_subtask uses _ctx.sessionID
742
- // 5. src/index.ts:216 - detectCoordinatorViolation uses input.sessionID
743
-
744
- // With ctx.sessionID, events go to proper session files
745
- const oldEnv = process.env.OPENCODE_SESSION_ID;
746
- delete process.env.OPENCODE_SESSION_ID;
747
-
748
- try {
749
- // Simulate tool execution with ctx.sessionID
750
- const mockCtx = { sessionID: sessionId };
751
-
752
- const event: CoordinatorEvent = {
753
- session_id: mockCtx.sessionID || "unknown",
754
- epic_id: "bd-456",
755
- timestamp: new Date().toISOString(),
756
- event_type: "OUTCOME",
757
- outcome_type: "subtask_success",
758
- payload: { bead_id: "bd-456.1" },
759
- };
760
-
761
- captureCoordinatorEvent(event);
762
-
763
- // Verify event captured with correct session_id
764
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
765
- expect(fs.existsSync(sessionPath)).toBe(true);
766
-
767
- const content = fs.readFileSync(sessionPath, "utf-8");
768
- const parsed = JSON.parse(content.trim());
769
- expect(parsed.session_id).toBe(sessionId);
770
- } finally {
771
- if (oldEnv !== undefined) {
772
- process.env.OPENCODE_SESSION_ID = oldEnv;
773
- }
774
- }
775
- });
776
- });
777
-
778
- describe("captureCompactionEvent", () => {
779
- let sessionDir: string;
780
- let sessionId: string;
781
-
782
- beforeEach(() => {
783
- sessionDir = path.join(os.homedir(), ".config", "swarm-tools", "sessions");
784
- sessionId = `test-compaction-${Date.now()}`;
785
- });
786
-
787
- afterEach(() => {
788
- // Clean up test session file
789
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
790
- if (fs.existsSync(sessionPath)) {
791
- fs.unlinkSync(sessionPath);
792
- }
793
- });
794
-
795
- test("writes detection_complete event to session file", () => {
796
- captureCompactionEvent({
797
- session_id: sessionId,
798
- epic_id: "bd-123",
799
- compaction_type: "detection_complete",
800
- payload: {
801
- confidence: "high",
802
- context_type: "full",
803
- epic_id: "bd-456",
804
- },
805
- });
806
-
807
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
808
- expect(fs.existsSync(sessionPath)).toBe(true);
809
-
810
- const content = fs.readFileSync(sessionPath, "utf-8");
811
- const lines = content.trim().split("\n");
812
- expect(lines).toHaveLength(1);
813
-
814
- const parsed = JSON.parse(lines[0]);
815
- expect(parsed.event_type).toBe("COMPACTION");
816
- expect(parsed.compaction_type).toBe("detection_complete");
817
- expect(parsed.payload.confidence).toBe("high");
818
- });
819
-
820
- test("writes prompt_generated event with full prompt content", () => {
821
- const fullPrompt = "You are a coordinator agent. ".repeat(200); // ~6k chars
822
-
823
- captureCompactionEvent({
824
- session_id: sessionId,
825
- epic_id: "bd-123",
826
- compaction_type: "prompt_generated",
827
- payload: {
828
- prompt_length: fullPrompt.length,
829
- full_prompt: fullPrompt,
830
- context_type: "full",
831
- },
832
- });
833
-
834
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
835
- const content = fs.readFileSync(sessionPath, "utf-8");
836
- const parsed = JSON.parse(content.trim());
837
-
838
- expect(parsed.payload.full_prompt).toBe(fullPrompt);
839
- expect(parsed.payload.full_prompt.length).toBe(fullPrompt.length);
840
- });
841
-
842
- test("appends multiple compaction events to same session", () => {
843
- captureCompactionEvent({
844
- session_id: sessionId,
845
- epic_id: "bd-123",
846
- compaction_type: "detection_complete",
847
- payload: { confidence: "high" },
848
- });
849
-
850
- captureCompactionEvent({
851
- session_id: sessionId,
852
- epic_id: "bd-123",
853
- compaction_type: "prompt_generated",
854
- payload: { prompt_length: 1000, full_prompt: "test" },
855
- });
856
-
857
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
858
- const content = fs.readFileSync(sessionPath, "utf-8");
859
- const lines = content.trim().split("\n");
860
- expect(lines).toHaveLength(2);
861
-
862
- const event1 = JSON.parse(lines[0]);
863
- const event2 = JSON.parse(lines[1]);
864
-
865
- expect(event1.compaction_type).toBe("detection_complete");
866
- expect(event2.compaction_type).toBe("prompt_generated");
867
- });
868
-
869
- test("full compaction lifecycle tracking", () => {
870
- // Simulate full compaction hook lifecycle
871
- const lifecycleEvents = [
872
- {
873
- compaction_type: "detection_complete" as const,
874
- payload: {
875
- confidence: "high",
876
- context_type: "full",
877
- epic_id: "bd-789",
878
- },
879
- },
880
- {
881
- compaction_type: "prompt_generated" as const,
882
- payload: {
883
- prompt_length: 3500,
884
- full_prompt: "You are a coordinator agent...",
885
- context_type: "full",
886
- },
887
- },
888
- {
889
- compaction_type: "context_injected" as const,
890
- payload: {
891
- context_type: "full",
892
- injected_sections: ["swarm_status", "mandatory_instructions"],
893
- },
894
- },
895
- {
896
- compaction_type: "resumption_started" as const,
897
- payload: {
898
- epic_id: "bd-789",
899
- agent_role: "coordinator",
900
- context_loaded: true,
901
- },
902
- },
903
- {
904
- compaction_type: "tool_call_tracked" as const,
905
- payload: {
906
- tool_name: "hive_create_epic",
907
- extracted_data: { epic_id: "bd-789" },
908
- },
909
- },
910
- ];
911
-
912
- // Capture all lifecycle events
913
- for (const event of lifecycleEvents) {
914
- captureCompactionEvent({
915
- session_id: sessionId,
916
- epic_id: "bd-123",
917
- ...event,
918
- });
919
- }
920
-
921
- // Verify all events captured
922
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
923
- const content = fs.readFileSync(sessionPath, "utf-8");
924
- const lines = content.trim().split("\n");
925
- expect(lines).toHaveLength(5);
926
-
927
- // Verify lifecycle order
928
- const capturedEvents = lines.map((line) => JSON.parse(line));
929
- expect(capturedEvents[0].compaction_type).toBe("detection_complete");
930
- expect(capturedEvents[1].compaction_type).toBe("prompt_generated");
931
- expect(capturedEvents[2].compaction_type).toBe("context_injected");
932
- expect(capturedEvents[3].compaction_type).toBe("resumption_started");
933
- expect(capturedEvents[4].compaction_type).toBe("tool_call_tracked");
934
- });
935
- });
936
-
937
- describe("hive_create_epic integration - decomposition_complete event", () => {
938
- let sessionDir: string;
939
- let sessionId: string;
940
- const testProjectPath = "/tmp/test-epic-decomposition";
941
-
942
- beforeEach(() => {
943
- sessionDir = path.join(os.homedir(), ".config", "swarm-tools", "sessions");
944
- sessionId = `test-epic-${Date.now()}`;
945
- });
946
-
947
- afterEach(() => {
948
- // Clean up test session file
949
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
950
- if (fs.existsSync(sessionPath)) {
951
- fs.unlinkSync(sessionPath);
952
- }
953
- });
954
-
955
- test("captures decomposition_complete event after hive_create_epic succeeds", async () => {
956
- // Test the event capture by calling captureCoordinatorEvent directly
957
- // Testing hive_create_epic directly would require full plugin infrastructure
958
-
959
- // GIVEN: We simulate what hive_create_epic does after epic creation
960
- const epicId = `test-epic-${Date.now()}`;
961
- const subtasks = [
962
- { title: "Subtask 1", files: ["src/a.ts"] },
963
- { title: "Subtask 2", files: ["src/b.ts", "src/c.ts"] },
964
- { title: "Subtask 3", files: ["src/d.ts"] },
965
- ];
966
-
967
- // Build files_per_subtask map (same logic as hive.ts)
968
- const filesPerSubtask: Record<number, string[]> = {};
969
- subtasks.forEach((subtask, index) => {
970
- if (subtask.files && subtask.files.length > 0) {
971
- filesPerSubtask[index] = subtask.files;
972
- }
973
- });
974
-
975
- // WHEN: decomposition_complete event is captured
976
- captureCoordinatorEvent({
977
- session_id: sessionId,
978
- epic_id: epicId,
979
- timestamp: new Date().toISOString(),
980
- event_type: "DECISION",
981
- decision_type: "decomposition_complete",
982
- payload: {
983
- subtask_count: subtasks.length,
984
- strategy_used: "file-based",
985
- files_per_subtask: filesPerSubtask,
986
- epic_title: "Test Epic for Event Capture",
987
- task: "Original task description",
988
- },
989
- });
990
-
991
- // THEN: Event should be written to session file
992
- const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
993
- expect(fs.existsSync(sessionPath)).toBe(true);
994
-
995
- const content = fs.readFileSync(sessionPath, "utf-8");
996
- const lines = content.trim().split("\n").filter(Boolean);
997
- expect(lines.length).toBe(1);
998
-
999
- // Verify event structure
1000
- const event = JSON.parse(lines[0]);
1001
- expect(event.session_id).toBe(sessionId);
1002
- expect(event.epic_id).toBe(epicId);
1003
- expect(event.event_type).toBe("DECISION");
1004
- expect(event.decision_type).toBe("decomposition_complete");
1005
- expect(event.payload.subtask_count).toBe(3);
1006
- expect(event.payload.strategy_used).toBe("file-based");
1007
- expect(event.payload.files_per_subtask).toEqual({
1008
- 0: ["src/a.ts"],
1009
- 1: ["src/b.ts", "src/c.ts"],
1010
- 2: ["src/d.ts"],
1011
- });
1012
- expect(event.payload.epic_title).toBe("Test Epic for Event Capture");
1013
- expect(event.payload.task).toBe("Original task description");
1014
- });
1015
- });