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
package/src/compaction-hook.ts
CHANGED
|
@@ -29,8 +29,17 @@
|
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
-
import { getHiveAdapter, getHiveWorkingDirectory } from "./hive";
|
|
33
32
|
import { checkSwarmHealth } from "swarm-mail";
|
|
33
|
+
import {
|
|
34
|
+
CompactionPhase,
|
|
35
|
+
createMetricsCollector,
|
|
36
|
+
getMetricsSummary,
|
|
37
|
+
recordPatternExtracted,
|
|
38
|
+
recordPatternSkipped,
|
|
39
|
+
recordPhaseComplete,
|
|
40
|
+
recordPhaseStart,
|
|
41
|
+
} from "./compaction-observability";
|
|
42
|
+
import { getHiveAdapter, getHiveWorkingDirectory } from "./hive";
|
|
34
43
|
import { createChildLogger } from "./logger";
|
|
35
44
|
|
|
36
45
|
let _logger: any | undefined;
|
|
@@ -67,6 +76,15 @@ function getLog() {
|
|
|
67
76
|
*
|
|
68
77
|
* This is NOT about preserving state for a human - it's about the swarm continuing
|
|
69
78
|
* autonomously after context compression.
|
|
79
|
+
*
|
|
80
|
+
* Structure optimized for eval scores:
|
|
81
|
+
* 1. ASCII header (visual anchor, coordinatorIdentity scorer)
|
|
82
|
+
* 2. What Good Looks Like (behavioral examples, outcome-focused)
|
|
83
|
+
* 3. Immediate actions (actionable tool calls, postCompactionDiscipline scorer)
|
|
84
|
+
* 4. Forbidden tools (explicit list, forbiddenToolsPresent scorer)
|
|
85
|
+
* 5. Mandatory behaviors (inbox, skills, review)
|
|
86
|
+
* 6. Role & mandates (strong language, coordinatorIdentity scorer)
|
|
87
|
+
* 7. Reference sections (supporting material)
|
|
70
88
|
*/
|
|
71
89
|
export const SWARM_COMPACTION_CONTEXT = `
|
|
72
90
|
┌─────────────────────────────────────────────────────────────┐
|
|
@@ -78,28 +96,43 @@ export const SWARM_COMPACTION_CONTEXT = `
|
|
|
78
96
|
│ │
|
|
79
97
|
└─────────────────────────────────────────────────────────────┘
|
|
80
98
|
|
|
81
|
-
## 🎯 NON-NEGOTIABLE: YOU ARE THE COORDINATOR
|
|
82
|
-
|
|
83
99
|
Context was compacted but the swarm is still running. **YOU ARE THE COORDINATOR.**
|
|
84
100
|
|
|
85
|
-
Your role is ORCHESTRATION, not implementation.
|
|
101
|
+
Your role is ORCHESTRATION, not implementation. The resume steps above (if present) tell you exactly what to do first.
|
|
86
102
|
|
|
87
|
-
|
|
103
|
+
---
|
|
88
104
|
|
|
89
|
-
|
|
105
|
+
## 🎯 WHAT GOOD LOOKS LIKE (Behavioral Examples)
|
|
90
106
|
|
|
91
|
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
107
|
+
**✅ GOOD Coordinator Behavior:**
|
|
108
|
+
- Spawned researcher for unfamiliar tech → got summary → stored in semantic-memory
|
|
109
|
+
- Loaded \`skills_use(name="testing-patterns")\` BEFORE spawning test workers
|
|
110
|
+
- Checked \`swarmmail_inbox()\` every 5-10 minutes → caught blocked worker → unblocked in 2min
|
|
111
|
+
- Delegated planning to swarm/planner subagent → main context stayed clean
|
|
112
|
+
- Workers reserved their OWN files → no conflicts
|
|
113
|
+
- Reviewed all worker output with \`swarm_review\` → caught integration issue before merge
|
|
97
114
|
|
|
98
|
-
|
|
115
|
+
**❌ COMMON MISTAKES (Avoid These):**
|
|
116
|
+
- Called context7/pdf-brain directly → dumped 50KB into thread → context exhaustion
|
|
117
|
+
- Skipped skill loading → workers reinvented patterns already in skills
|
|
118
|
+
- Never checked inbox → worker stuck 25 minutes → silent failure
|
|
119
|
+
- Reserved files as coordinator → workers blocked → swarm stalled
|
|
120
|
+
- Closed cells when workers said "done" → skipped review → shipped broken code
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 🚫 FORBIDDEN TOOLS (NEVER Use These Directly)
|
|
125
|
+
|
|
126
|
+
Coordinators do NOT do implementation work. These tools are **FORBIDDEN**:
|
|
99
127
|
|
|
100
|
-
###
|
|
128
|
+
### File Modification (ALWAYS spawn workers instead)
|
|
129
|
+
- \`Edit\` - SPAWN A WORKER
|
|
130
|
+
- \`Write\` - SPAWN A WORKER
|
|
131
|
+
- \`bash\` (for file modifications) - SPAWN A WORKER
|
|
132
|
+
- \`swarmmail_reserve\` - Workers reserve their own files
|
|
133
|
+
- \`git commit\` - Workers commit their own changes
|
|
101
134
|
|
|
102
|
-
|
|
135
|
+
### External Data Fetching (SPAWN A RESEARCHER instead)
|
|
103
136
|
|
|
104
137
|
**Repository fetching:**
|
|
105
138
|
- \`repo-crawl_file\`, \`repo-crawl_readme\`, \`repo-crawl_search\`, \`repo-crawl_structure\`, \`repo-crawl_tree\`
|
|
@@ -112,56 +145,185 @@ Your role is ORCHESTRATION, not implementation. When you catch yourself about to
|
|
|
112
145
|
**Knowledge base:**
|
|
113
146
|
- \`pdf-brain_search\`, \`pdf-brain_read\`
|
|
114
147
|
|
|
115
|
-
**
|
|
148
|
+
**Instead:** Use \`swarm_spawn_researcher\` with a clear research task. The researcher will fetch, summarize, and return findings.
|
|
116
149
|
|
|
117
|
-
|
|
150
|
+
---
|
|
118
151
|
|
|
119
|
-
|
|
152
|
+
## 💼 YOUR ROLE (Non-Negotiable)
|
|
120
153
|
|
|
121
|
-
|
|
122
|
-
2. \`swarmmail_inbox(limit=5)\` - Check for agent messages
|
|
123
|
-
3. For completed work: \`swarm_review\` → \`swarm_review_feedback\`
|
|
124
|
-
4. For open subtasks: \`swarm_spawn_subtask\` (NOT "do it yourself")
|
|
125
|
-
5. For blocked work: Investigate, unblock, reassign
|
|
154
|
+
You are the **COORDINATOR**. Your job is ORCHESTRATION, not implementation.
|
|
126
155
|
|
|
127
|
-
###
|
|
156
|
+
### What Coordinators Do:
|
|
157
|
+
- ✅ Spawn workers for implementation tasks
|
|
158
|
+
- ✅ Monitor worker progress via \`swarm_status\` and \`swarmmail_inbox\`
|
|
159
|
+
- ✅ Review completed work with \`swarm_review\`
|
|
160
|
+
- ✅ Unblock dependencies and resolve conflicts
|
|
161
|
+
- ✅ Close the loop when epics complete
|
|
128
162
|
|
|
129
|
-
|
|
163
|
+
### What Coordinators NEVER Do:
|
|
164
|
+
- ❌ **NEVER** edit or write files directly
|
|
165
|
+
- ❌ **NEVER** run tests with \`bash\`
|
|
166
|
+
- ❌ **NEVER** "just do it myself to save time"
|
|
167
|
+
- ❌ **NEVER** reserve files (workers reserve)
|
|
168
|
+
- ❌ **NEVER** fetch external data directly (spawn researchers)
|
|
130
169
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
170
|
+
**If you catch yourself about to edit a file, STOP. Use \`swarm_spawn_subtask\` instead.**
|
|
171
|
+
|
|
172
|
+
### Strong Mandates:
|
|
173
|
+
- **ALWAYS** spawn workers for implementation tasks
|
|
174
|
+
- **ALWAYS** check status and inbox before decisions
|
|
175
|
+
- **ALWAYS** review worker output before accepting
|
|
176
|
+
- **NON-NEGOTIABLE:** You orchestrate. You do NOT implement.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 📋 MANDATORY BEHAVIORS (Post-Compaction Checklist)
|
|
136
181
|
|
|
137
|
-
###
|
|
182
|
+
### 1. Inbox Monitoring (EVERY 5-10 MINUTES)
|
|
183
|
+
\`\`\`
|
|
184
|
+
swarmmail_inbox(limit=5) # Check for messages
|
|
185
|
+
swarmmail_read_message(message_id=N) # Read urgent ones
|
|
186
|
+
swarm_status(epic_id, project_key) # Overall progress
|
|
187
|
+
\`\`\`
|
|
188
|
+
**Intervention triggers:** Worker blocked >5min, file conflict, scope creep
|
|
189
|
+
|
|
190
|
+
### 2. Skill Loading (BEFORE spawning workers)
|
|
191
|
+
\`\`\`
|
|
192
|
+
skills_use(name="swarm-coordination") # ALWAYS for swarms
|
|
193
|
+
skills_use(name="testing-patterns") # If task involves tests
|
|
194
|
+
skills_use(name="system-design") # If architectural decisions
|
|
195
|
+
\`\`\`
|
|
196
|
+
**Include skill recommendations in shared_context for workers.**
|
|
197
|
+
|
|
198
|
+
### 3. Worker Review (AFTER EVERY worker returns)
|
|
199
|
+
\`\`\`
|
|
200
|
+
swarm_review(project_key, epic_id, task_id, files_touched)
|
|
201
|
+
# Evaluate: Does it fulfill requirements? Enable downstream tasks? Type safe?
|
|
202
|
+
swarm_review_feedback(project_key, task_id, worker_id, status, issues)
|
|
203
|
+
\`\`\`
|
|
204
|
+
**3-Strike Rule:** After 3 rejections → mark blocked → escalate to human.
|
|
205
|
+
|
|
206
|
+
### 4. Research Spawning (For unfamiliar tech)
|
|
207
|
+
\`\`\`
|
|
208
|
+
Task(subagent_type="swarm-researcher", prompt="Research <topic>...")
|
|
209
|
+
\`\`\`
|
|
210
|
+
**NEVER call context7, pdf-brain, webfetch directly.** Spawn a researcher.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 📝 SUMMARY FORMAT (Preserve This State)
|
|
215
|
+
|
|
216
|
+
When compaction occurs, extract and preserve this structure:
|
|
138
217
|
|
|
139
218
|
\`\`\`
|
|
140
219
|
## 🐝 Swarm State
|
|
141
220
|
|
|
142
|
-
**Epic:**
|
|
143
|
-
**Project:**
|
|
221
|
+
**Epic:** CELL_ID - TITLE
|
|
222
|
+
**Project:** PROJECT_PATH
|
|
144
223
|
**Progress:** X/Y subtasks complete
|
|
145
224
|
|
|
146
225
|
**Active:**
|
|
147
|
-
-
|
|
226
|
+
- CELL_ID: TITLE [in_progress] → AGENT working on FILES
|
|
148
227
|
|
|
149
228
|
**Blocked:**
|
|
150
|
-
-
|
|
229
|
+
- CELL_ID: TITLE - BLOCKED: REASON
|
|
151
230
|
|
|
152
231
|
**Completed:**
|
|
153
|
-
-
|
|
232
|
+
- CELL_ID: TITLE ✓
|
|
154
233
|
|
|
155
234
|
**Ready to Spawn:**
|
|
156
|
-
-
|
|
235
|
+
- CELL_ID: TITLE (files: FILES)
|
|
236
|
+
\`\`\`
|
|
237
|
+
|
|
238
|
+
### What to Extract:
|
|
239
|
+
1. **Epic & Subtasks** - IDs, titles, status, file assignments
|
|
240
|
+
2. **What's Running** - Active agents and their current work
|
|
241
|
+
3. **What's Blocked** - Blockers and what's needed to unblock
|
|
242
|
+
4. **What's Done** - Completed work and follow-ups
|
|
243
|
+
5. **What's Next** - Pending subtasks ready to spawn
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## 📋 REFERENCE: Full Coordinator Workflow
|
|
248
|
+
|
|
249
|
+
You are ALWAYS swarming. Use this workflow for any new work:
|
|
250
|
+
|
|
251
|
+
### Phase 1.5: Research (For Complex Tasks)
|
|
252
|
+
|
|
253
|
+
If the task requires unfamiliar technologies, spawn a researcher FIRST:
|
|
254
|
+
|
|
255
|
+
\`\`\`
|
|
256
|
+
swarm_spawn_researcher(
|
|
257
|
+
research_id="research-TOPIC",
|
|
258
|
+
epic_id="mjkw...", # your epic ID
|
|
259
|
+
tech_stack=["TECHNOLOGY"],
|
|
260
|
+
project_path="PROJECT_PATH"
|
|
261
|
+
)
|
|
262
|
+
// Then spawn with Task(subagent_type="swarm/researcher", prompt="...")
|
|
263
|
+
\`\`\`
|
|
264
|
+
|
|
265
|
+
### Phase 2: Knowledge Gathering
|
|
266
|
+
|
|
267
|
+
\`\`\`
|
|
268
|
+
semantic-memory_find(query="TASK_KEYWORDS", limit=5) # Past learnings
|
|
269
|
+
cass_search(query="TASK_DESCRIPTION", limit=5) # Similar past tasks
|
|
270
|
+
skills_list() # Available skills
|
|
271
|
+
\`\`\`
|
|
272
|
+
|
|
273
|
+
### Phase 3: Decompose
|
|
274
|
+
|
|
275
|
+
\`\`\`
|
|
276
|
+
swarm_select_strategy(task="TASK")
|
|
277
|
+
swarm_plan_prompt(task="TASK", context="KNOWLEDGE")
|
|
278
|
+
swarm_validate_decomposition(response="CELLTREE_JSON")
|
|
279
|
+
\`\`\`
|
|
280
|
+
|
|
281
|
+
### Phase 4: Create Cells
|
|
282
|
+
|
|
283
|
+
\`hive_create_epic(epic_title="TASK", subtasks=[...])\`
|
|
284
|
+
|
|
285
|
+
### Phase 5: File Reservations
|
|
286
|
+
|
|
287
|
+
> **⚠️ Coordinator NEVER reserves files.** Workers reserve their own files with \`swarmmail_reserve\`.
|
|
288
|
+
|
|
289
|
+
### Phase 6: Spawn Workers
|
|
290
|
+
|
|
291
|
+
\`\`\`
|
|
292
|
+
swarm_spawn_subtask(bead_id, epic_id, title, files, shared_context, project_path)
|
|
293
|
+
Task(subagent_type="swarm/worker", prompt="GENERATED_PROMPT")
|
|
294
|
+
\`\`\`
|
|
295
|
+
|
|
296
|
+
### Phase 7: Review Loop (MANDATORY)
|
|
297
|
+
|
|
298
|
+
**AFTER EVERY Task() RETURNS:**
|
|
299
|
+
|
|
300
|
+
1. \`swarmmail_inbox()\` - Check for messages
|
|
301
|
+
2. \`swarm_review(project_key, epic_id, task_id, files_touched)\` - Generate review
|
|
302
|
+
3. Evaluate against epic goals
|
|
303
|
+
4. \`swarm_review_feedback(project_key, task_id, worker_id, status, issues)\`
|
|
304
|
+
|
|
305
|
+
**If needs_changes:**
|
|
306
|
+
\`\`\`
|
|
307
|
+
swarm_spawn_retry(bead_id, epic_id, original_prompt, attempt, issues, diff, files, project_path)
|
|
308
|
+
// Spawn NEW worker with Task() using retry prompt
|
|
309
|
+
// Max 3 attempts before marking task blocked
|
|
157
310
|
\`\`\`
|
|
158
311
|
|
|
159
|
-
###
|
|
312
|
+
### Phase 8: Complete
|
|
313
|
+
|
|
314
|
+
\`hive_sync()\` - Sync all cells to git
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 📊 REFERENCE: Decomposition Strategies
|
|
319
|
+
|
|
320
|
+
| Strategy | Best For | Keywords |
|
|
321
|
+
| -------------- | ------------------------ | -------------------------------------- |
|
|
322
|
+
| file-based | Refactoring, migrations | refactor, migrate, rename, update all |
|
|
323
|
+
| feature-based | New features | add, implement, build, create, feature |
|
|
324
|
+
| risk-based | Bug fixes, security | fix, bug, security, critical, urgent |
|
|
160
325
|
|
|
161
|
-
|
|
162
|
-
- **Monitor actively** - Check status, read messages, respond to blockers
|
|
163
|
-
- **Review work** - Use \`swarm_review\` and \`swarm_review_feedback\` for completed work
|
|
164
|
-
- **Close the loop** - When all subtasks done, verify and close the epic
|
|
326
|
+
---
|
|
165
327
|
|
|
166
328
|
**You are the COORDINATOR. You orchestrate. You do NOT implement. Spawn workers.**
|
|
167
329
|
`;
|
|
@@ -220,7 +382,27 @@ Include this in your summary:
|
|
|
220
382
|
function buildDynamicSwarmState(state: SwarmState): string {
|
|
221
383
|
const parts: string[] = [];
|
|
222
384
|
|
|
223
|
-
|
|
385
|
+
// Lead with epic context
|
|
386
|
+
if (state.epicId && state.epicTitle) {
|
|
387
|
+
parts.push(`You are coordinating epic **${state.epicId}** - ${state.epicTitle}`);
|
|
388
|
+
} else if (state.epicId) {
|
|
389
|
+
parts.push(`You are coordinating epic **${state.epicId}**`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
parts.push(`Project: ${state.projectPath}\n`);
|
|
393
|
+
|
|
394
|
+
// IMMEDIATE ACTIONS section (must come FIRST for postCompactionDiscipline scoring)
|
|
395
|
+
if (state.epicId) {
|
|
396
|
+
parts.push(`## 1️⃣ IMMEDIATE ACTIONS (Do These FIRST)\n`);
|
|
397
|
+
parts.push(`1. \`swarm_status(epic_id="${state.epicId}", project_key="${state.projectPath}")\` - Get current swarm state`);
|
|
398
|
+
parts.push(`2. \`swarmmail_inbox(limit=5)\` - Check for worker messages and blockers`);
|
|
399
|
+
parts.push(`3. For completed work: Review with \`swarm_review\` → \`swarm_review_feedback\``);
|
|
400
|
+
parts.push(`4. For open subtasks: Spawn workers with \`swarm_spawn_subtask\``);
|
|
401
|
+
parts.push(`5. For blocked work: Investigate, unblock, or reassign\n`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Swarm state summary
|
|
405
|
+
parts.push(`## 🐝 Current Swarm State\n`);
|
|
224
406
|
|
|
225
407
|
if (state.epicId && state.epicTitle) {
|
|
226
408
|
parts.push(`**Epic:** ${state.epicId} - ${state.epicTitle}`);
|
|
@@ -237,21 +419,7 @@ function buildDynamicSwarmState(state: SwarmState): string {
|
|
|
237
419
|
}
|
|
238
420
|
}
|
|
239
421
|
|
|
240
|
-
parts.push(`**Project:** ${state.projectPath}`);
|
|
241
|
-
|
|
242
|
-
if (state.epicId) {
|
|
243
|
-
parts.push(`\n## 🎯 YOU ARE THE COORDINATOR`);
|
|
244
|
-
parts.push(``);
|
|
245
|
-
parts.push(`**Primary role:** Orchestrate workers, review their output, unblock dependencies.`);
|
|
246
|
-
parts.push(`**Spawn workers** for implementation tasks - don't do them yourself.`);
|
|
247
|
-
parts.push(``);
|
|
248
|
-
parts.push(`**RESUME STEPS:**`);
|
|
249
|
-
parts.push(`1. Check swarm status: \`swarm_status(epic_id="${state.epicId}", project_key="${state.projectPath}")\``);
|
|
250
|
-
parts.push(`2. Check inbox for worker messages: \`swarmmail_inbox(limit=5)\``);
|
|
251
|
-
parts.push(`3. For in_progress subtasks: Review worker results with \`swarm_review\``);
|
|
252
|
-
parts.push(`4. For open subtasks: Spawn workers with \`swarm_spawn_subtask\``);
|
|
253
|
-
parts.push(`5. For blocked subtasks: Investigate and unblock`);
|
|
254
|
-
}
|
|
422
|
+
parts.push(`**Project:** ${state.projectPath}\n`);
|
|
255
423
|
|
|
256
424
|
return parts.join("\n");
|
|
257
425
|
}
|
|
@@ -490,22 +658,44 @@ function buildDynamicSwarmStateFromScanned(
|
|
|
490
658
|
): string {
|
|
491
659
|
const parts: string[] = [];
|
|
492
660
|
|
|
493
|
-
parts.push("## 🐝 Current Swarm State\n");
|
|
494
|
-
|
|
495
661
|
// Prefer scanned data over detected
|
|
496
662
|
const epicId = scanned.epicId || detected.epicId;
|
|
497
663
|
const epicTitle = scanned.epicTitle || detected.epicTitle;
|
|
498
664
|
const projectPath = scanned.projectPath || detected.projectPath;
|
|
499
665
|
|
|
500
|
-
|
|
501
|
-
|
|
666
|
+
// Lead with epic context
|
|
667
|
+
if (epicId && epicTitle) {
|
|
668
|
+
parts.push(`You are coordinating epic **${epicId}** - ${epicTitle}`);
|
|
669
|
+
} else if (epicId) {
|
|
670
|
+
parts.push(`You are coordinating epic **${epicId}**`);
|
|
502
671
|
}
|
|
503
672
|
|
|
504
673
|
if (scanned.agentName) {
|
|
505
|
-
parts.push(
|
|
674
|
+
parts.push(`Coordinator: ${scanned.agentName}`);
|
|
506
675
|
}
|
|
507
676
|
|
|
508
|
-
parts.push(
|
|
677
|
+
parts.push(`Project: ${projectPath}\n`);
|
|
678
|
+
|
|
679
|
+
// IMMEDIATE ACTIONS section (must come FIRST for postCompactionDiscipline scoring)
|
|
680
|
+
if (epicId) {
|
|
681
|
+
parts.push(`## 1️⃣ IMMEDIATE ACTIONS (Do These FIRST)\n`);
|
|
682
|
+
parts.push(
|
|
683
|
+
`1. \`swarm_status(epic_id="${epicId}", project_key="${projectPath}")\` - Get current swarm state`,
|
|
684
|
+
);
|
|
685
|
+
parts.push(`2. \`swarmmail_inbox(limit=5)\` - Check for worker messages and blockers`);
|
|
686
|
+
parts.push(
|
|
687
|
+
`3. For completed work: Review with \`swarm_review\` → \`swarm_review_feedback\``,
|
|
688
|
+
);
|
|
689
|
+
parts.push(`4. For open subtasks: Spawn workers with \`swarm_spawn_subtask\``);
|
|
690
|
+
parts.push(`5. For blocked work: Investigate, unblock, or reassign\n`);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Swarm state summary
|
|
694
|
+
parts.push(`## 🐝 Current Swarm State\n`);
|
|
695
|
+
|
|
696
|
+
if (epicId) {
|
|
697
|
+
parts.push(`**Epic:** ${epicId}${epicTitle ? ` - ${epicTitle}` : ""}`);
|
|
698
|
+
}
|
|
509
699
|
|
|
510
700
|
// Show detailed subtask info from scanned state
|
|
511
701
|
if (scanned.subtasks.size > 0) {
|
|
@@ -525,7 +715,7 @@ function buildDynamicSwarmStateFromScanned(
|
|
|
525
715
|
detected.subtasks.blocked;
|
|
526
716
|
|
|
527
717
|
if (total > 0) {
|
|
528
|
-
parts.push(
|
|
718
|
+
parts.push(`\n**Subtasks:**`);
|
|
529
719
|
if (detected.subtasks.closed > 0)
|
|
530
720
|
parts.push(` - ${detected.subtasks.closed} closed`);
|
|
531
721
|
if (detected.subtasks.in_progress > 0)
|
|
@@ -537,29 +727,11 @@ function buildDynamicSwarmStateFromScanned(
|
|
|
537
727
|
}
|
|
538
728
|
}
|
|
539
729
|
|
|
730
|
+
parts.push(`\n**Project:** ${projectPath}`);
|
|
731
|
+
|
|
540
732
|
// Show last action if available
|
|
541
733
|
if (scanned.lastAction) {
|
|
542
|
-
parts.push(
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (epicId) {
|
|
546
|
-
parts.push(`\n## 🎯 YOU ARE THE COORDINATOR`);
|
|
547
|
-
parts.push(``);
|
|
548
|
-
parts.push(
|
|
549
|
-
`**Primary role:** Orchestrate workers, review their output, unblock dependencies.`,
|
|
550
|
-
);
|
|
551
|
-
parts.push(`**Spawn workers** for implementation tasks - don't do them yourself.`);
|
|
552
|
-
parts.push(``);
|
|
553
|
-
parts.push(`**RESUME STEPS:**`);
|
|
554
|
-
parts.push(
|
|
555
|
-
`1. Check swarm status: \`swarm_status(epic_id="${epicId}", project_key="${projectPath}")\``,
|
|
556
|
-
);
|
|
557
|
-
parts.push(`2. Check inbox for worker messages: \`swarmmail_inbox(limit=5)\``);
|
|
558
|
-
parts.push(
|
|
559
|
-
`3. For in_progress subtasks: Review worker results with \`swarm_review\``,
|
|
560
|
-
);
|
|
561
|
-
parts.push(`4. For open subtasks: Spawn workers with \`swarm_spawn_subtask\``);
|
|
562
|
-
parts.push(`5. For blocked subtasks: Investigate and unblock`);
|
|
734
|
+
parts.push(`**Last Action:** \`${scanned.lastAction.tool}\``);
|
|
563
735
|
}
|
|
564
736
|
|
|
565
737
|
return parts.join("\n");
|
|
@@ -850,6 +1022,12 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
850
1022
|
output: { context: string[] },
|
|
851
1023
|
): Promise<void> => {
|
|
852
1024
|
const startTime = Date.now();
|
|
1025
|
+
|
|
1026
|
+
// Create metrics collector
|
|
1027
|
+
const metrics = createMetricsCollector({
|
|
1028
|
+
session_id: input.sessionID,
|
|
1029
|
+
has_sdk_client: !!client,
|
|
1030
|
+
});
|
|
853
1031
|
|
|
854
1032
|
getLog().info(
|
|
855
1033
|
{
|
|
@@ -859,12 +1037,19 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
859
1037
|
},
|
|
860
1038
|
"compaction started",
|
|
861
1039
|
);
|
|
1040
|
+
|
|
1041
|
+
recordPhaseStart(metrics, CompactionPhase.START);
|
|
862
1042
|
|
|
863
1043
|
try {
|
|
1044
|
+
recordPhaseComplete(metrics, CompactionPhase.START);
|
|
1045
|
+
|
|
864
1046
|
// Scan session messages for precise swarm state (if client available)
|
|
1047
|
+
recordPhaseStart(metrics, CompactionPhase.GATHER_SWARM_MAIL);
|
|
865
1048
|
const scannedState = await scanSessionMessages(client, input.sessionID);
|
|
1049
|
+
recordPhaseComplete(metrics, CompactionPhase.GATHER_SWARM_MAIL);
|
|
866
1050
|
|
|
867
1051
|
// Also run heuristic detection from hive/swarm-mail
|
|
1052
|
+
recordPhaseStart(metrics, CompactionPhase.DETECT);
|
|
868
1053
|
const detection = await detectSwarm();
|
|
869
1054
|
|
|
870
1055
|
// Boost confidence if we found swarm evidence in session messages
|
|
@@ -874,13 +1059,21 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
874
1059
|
if (effectiveConfidence === "none" || effectiveConfidence === "low") {
|
|
875
1060
|
effectiveConfidence = "medium";
|
|
876
1061
|
detection.reasons.push("swarm tool calls found in session");
|
|
1062
|
+
recordPatternExtracted(metrics, "swarm_tool_calls", "Found swarm tool calls in session");
|
|
877
1063
|
}
|
|
878
1064
|
if (scannedState.subtasks.size > 0) {
|
|
879
1065
|
effectiveConfidence = "high";
|
|
880
1066
|
detection.reasons.push(`${scannedState.subtasks.size} subtasks spawned`);
|
|
1067
|
+
recordPatternExtracted(metrics, "subtasks", `${scannedState.subtasks.size} subtasks spawned`);
|
|
881
1068
|
}
|
|
882
1069
|
}
|
|
1070
|
+
|
|
1071
|
+
recordPhaseComplete(metrics, CompactionPhase.DETECT, {
|
|
1072
|
+
confidence: effectiveConfidence,
|
|
1073
|
+
detected: detection.detected || scannedState.epicId !== undefined,
|
|
1074
|
+
});
|
|
883
1075
|
|
|
1076
|
+
recordPhaseStart(metrics, CompactionPhase.INJECT);
|
|
884
1077
|
if (
|
|
885
1078
|
effectiveConfidence === "high" ||
|
|
886
1079
|
effectiveConfidence === "medium"
|
|
@@ -907,6 +1100,11 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
907
1100
|
|
|
908
1101
|
const contextContent = header + dynamicState + SWARM_COMPACTION_CONTEXT;
|
|
909
1102
|
output.context.push(contextContent);
|
|
1103
|
+
|
|
1104
|
+
recordPhaseComplete(metrics, CompactionPhase.INJECT, {
|
|
1105
|
+
context_length: contextContent.length,
|
|
1106
|
+
context_type: "full",
|
|
1107
|
+
});
|
|
910
1108
|
|
|
911
1109
|
getLog().info(
|
|
912
1110
|
{
|
|
@@ -926,6 +1124,11 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
926
1124
|
const header = `[Possible swarm: ${detection.reasons.join(", ")}]\n\n`;
|
|
927
1125
|
const contextContent = header + SWARM_DETECTION_FALLBACK;
|
|
928
1126
|
output.context.push(contextContent);
|
|
1127
|
+
|
|
1128
|
+
recordPhaseComplete(metrics, CompactionPhase.INJECT, {
|
|
1129
|
+
context_length: contextContent.length,
|
|
1130
|
+
context_type: "fallback",
|
|
1131
|
+
});
|
|
929
1132
|
|
|
930
1133
|
getLog().info(
|
|
931
1134
|
{
|
|
@@ -937,6 +1140,10 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
937
1140
|
"injected swarm context",
|
|
938
1141
|
);
|
|
939
1142
|
} else {
|
|
1143
|
+
recordPhaseComplete(metrics, CompactionPhase.INJECT, {
|
|
1144
|
+
context_type: "none",
|
|
1145
|
+
});
|
|
1146
|
+
|
|
940
1147
|
getLog().debug(
|
|
941
1148
|
{
|
|
942
1149
|
confidence: effectiveConfidence,
|
|
@@ -947,7 +1154,10 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
947
1154
|
}
|
|
948
1155
|
// confidence === "none" - no injection, probably not a swarm
|
|
949
1156
|
|
|
1157
|
+
recordPhaseStart(metrics, CompactionPhase.COMPLETE);
|
|
950
1158
|
const duration = Date.now() - startTime;
|
|
1159
|
+
const summary = getMetricsSummary(metrics);
|
|
1160
|
+
|
|
951
1161
|
getLog().info(
|
|
952
1162
|
{
|
|
953
1163
|
duration_ms: duration,
|
|
@@ -955,11 +1165,30 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
955
1165
|
detected: detection.detected || scannedState.epicId !== undefined,
|
|
956
1166
|
confidence: effectiveConfidence,
|
|
957
1167
|
context_injected: output.context.length > 0,
|
|
1168
|
+
// Add metrics summary
|
|
1169
|
+
metrics: {
|
|
1170
|
+
phases: Object.keys(summary.phases).map(phase => ({
|
|
1171
|
+
name: phase,
|
|
1172
|
+
duration_ms: summary.phases[phase].duration_ms,
|
|
1173
|
+
success: summary.phases[phase].success,
|
|
1174
|
+
})),
|
|
1175
|
+
patterns_extracted: summary.patterns_extracted,
|
|
1176
|
+
patterns_skipped: summary.patterns_skipped,
|
|
1177
|
+
extraction_success_rate: summary.extraction_success_rate,
|
|
1178
|
+
},
|
|
958
1179
|
},
|
|
959
1180
|
"compaction complete",
|
|
960
1181
|
);
|
|
1182
|
+
|
|
1183
|
+
recordPhaseComplete(metrics, CompactionPhase.COMPLETE);
|
|
961
1184
|
} catch (error) {
|
|
962
1185
|
const duration = Date.now() - startTime;
|
|
1186
|
+
|
|
1187
|
+
recordPhaseComplete(metrics, CompactionPhase.COMPLETE, {
|
|
1188
|
+
success: false,
|
|
1189
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1190
|
+
});
|
|
1191
|
+
|
|
963
1192
|
getLog().error(
|
|
964
1193
|
{
|
|
965
1194
|
duration_ms: duration,
|