opencode-swarm-plugin 0.39.1 → 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/.hive/issues.jsonl +16 -0
- package/CHANGELOG.md +52 -0
- package/bin/swarm.test.ts +406 -0
- package/bin/swarm.ts +303 -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/eval-capture.d.ts +93 -0
- package/dist/eval-capture.d.ts.map +1 -1
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.d.ts +36 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15670 -580
- package/dist/plugin.js +15623 -557
- package/dist/schemas/task.d.ts +3 -3
- package/evals/README.md +113 -0
- package/evals/scorers/coordinator-discipline.evalite-test.ts +163 -0
- package/evals/scorers/coordinator-discipline.ts +335 -2
- 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/package.json +1 -1
- package/src/compaction-hook.ts +258 -110
- 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/eval-capture.test.ts +204 -1
- package/src/eval-capture.ts +194 -2
- 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 +54 -1
- package/src/memory.test.ts +110 -0
- package/src/memory.ts +34 -0
- 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/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
|
|
99
121
|
|
|
100
|
-
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 🚫 FORBIDDEN TOOLS (NEVER Use These Directly)
|
|
125
|
+
|
|
126
|
+
Coordinators do NOT do implementation work. These tools are **FORBIDDEN**:
|
|
101
127
|
|
|
102
|
-
|
|
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
|
|
134
|
+
|
|
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,111 +145,155 @@ 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)
|
|
181
|
+
|
|
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
|
+
---
|
|
136
213
|
|
|
137
|
-
|
|
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)
|
|
157
236
|
\`\`\`
|
|
158
237
|
|
|
159
|
-
###
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
**You are the COORDINATOR. You orchestrate. You do NOT implement. Spawn workers.**
|
|
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
|
|
167
244
|
|
|
168
245
|
---
|
|
169
246
|
|
|
170
|
-
## 📋
|
|
247
|
+
## 📋 REFERENCE: Full Coordinator Workflow
|
|
171
248
|
|
|
172
|
-
You are ALWAYS swarming.
|
|
249
|
+
You are ALWAYS swarming. Use this workflow for any new work:
|
|
173
250
|
|
|
174
|
-
### Phase 1.5: Research
|
|
251
|
+
### Phase 1.5: Research (For Complex Tasks)
|
|
175
252
|
|
|
176
|
-
|
|
253
|
+
If the task requires unfamiliar technologies, spawn a researcher FIRST:
|
|
177
254
|
|
|
178
255
|
\`\`\`
|
|
179
256
|
swarm_spawn_researcher(
|
|
180
|
-
research_id="research
|
|
181
|
-
epic_id="
|
|
182
|
-
tech_stack=["
|
|
183
|
-
project_path="
|
|
257
|
+
research_id="research-TOPIC",
|
|
258
|
+
epic_id="mjkw...", # your epic ID
|
|
259
|
+
tech_stack=["TECHNOLOGY"],
|
|
260
|
+
project_path="PROJECT_PATH"
|
|
184
261
|
)
|
|
185
|
-
// Then spawn with Task(subagent_type="swarm/researcher", prompt="
|
|
262
|
+
// Then spawn with Task(subagent_type="swarm/researcher", prompt="...")
|
|
186
263
|
\`\`\`
|
|
187
264
|
|
|
188
265
|
### Phase 2: Knowledge Gathering
|
|
189
266
|
|
|
190
267
|
\`\`\`
|
|
191
|
-
semantic-memory_find(query="
|
|
192
|
-
cass_search(query="
|
|
193
|
-
skills_list()
|
|
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
|
|
194
271
|
\`\`\`
|
|
195
272
|
|
|
196
273
|
### Phase 3: Decompose
|
|
197
274
|
|
|
198
275
|
\`\`\`
|
|
199
|
-
swarm_select_strategy(task="
|
|
200
|
-
swarm_plan_prompt(task="
|
|
201
|
-
swarm_validate_decomposition(response="
|
|
276
|
+
swarm_select_strategy(task="TASK")
|
|
277
|
+
swarm_plan_prompt(task="TASK", context="KNOWLEDGE")
|
|
278
|
+
swarm_validate_decomposition(response="CELLTREE_JSON")
|
|
202
279
|
\`\`\`
|
|
203
280
|
|
|
204
281
|
### Phase 4: Create Cells
|
|
205
282
|
|
|
206
|
-
\`hive_create_epic(epic_title="
|
|
283
|
+
\`hive_create_epic(epic_title="TASK", subtasks=[...])\`
|
|
207
284
|
|
|
208
|
-
### Phase 5:
|
|
285
|
+
### Phase 5: File Reservations
|
|
209
286
|
|
|
210
|
-
> **⚠️ Coordinator NEVER reserves files.** Workers reserve their own files
|
|
287
|
+
> **⚠️ Coordinator NEVER reserves files.** Workers reserve their own files with \`swarmmail_reserve\`.
|
|
211
288
|
|
|
212
289
|
### Phase 6: Spawn Workers
|
|
213
290
|
|
|
214
291
|
\`\`\`
|
|
215
292
|
swarm_spawn_subtask(bead_id, epic_id, title, files, shared_context, project_path)
|
|
216
|
-
Task(subagent_type="swarm/worker", prompt="
|
|
293
|
+
Task(subagent_type="swarm/worker", prompt="GENERATED_PROMPT")
|
|
217
294
|
\`\`\`
|
|
218
295
|
|
|
219
|
-
### Phase 7:
|
|
296
|
+
### Phase 7: Review Loop (MANDATORY)
|
|
220
297
|
|
|
221
298
|
**AFTER EVERY Task() RETURNS:**
|
|
222
299
|
|
|
@@ -236,7 +313,9 @@ swarm_spawn_retry(bead_id, epic_id, original_prompt, attempt, issues, diff, file
|
|
|
236
313
|
|
|
237
314
|
\`hive_sync()\` - Sync all cells to git
|
|
238
315
|
|
|
239
|
-
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 📊 REFERENCE: Decomposition Strategies
|
|
240
319
|
|
|
241
320
|
| Strategy | Best For | Keywords |
|
|
242
321
|
| -------------- | ------------------------ | -------------------------------------- |
|
|
@@ -244,6 +323,8 @@ swarm_spawn_retry(bead_id, epic_id, original_prompt, attempt, issues, diff, file
|
|
|
244
323
|
| feature-based | New features | add, implement, build, create, feature |
|
|
245
324
|
| risk-based | Bug fixes, security | fix, bug, security, critical, urgent |
|
|
246
325
|
|
|
326
|
+
---
|
|
327
|
+
|
|
247
328
|
**You are the COORDINATOR. You orchestrate. You do NOT implement. Spawn workers.**
|
|
248
329
|
`;
|
|
249
330
|
|
|
@@ -301,7 +382,27 @@ Include this in your summary:
|
|
|
301
382
|
function buildDynamicSwarmState(state: SwarmState): string {
|
|
302
383
|
const parts: string[] = [];
|
|
303
384
|
|
|
304
|
-
|
|
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`);
|
|
305
406
|
|
|
306
407
|
if (state.epicId && state.epicTitle) {
|
|
307
408
|
parts.push(`**Epic:** ${state.epicId} - ${state.epicTitle}`);
|
|
@@ -318,21 +419,7 @@ function buildDynamicSwarmState(state: SwarmState): string {
|
|
|
318
419
|
}
|
|
319
420
|
}
|
|
320
421
|
|
|
321
|
-
parts.push(`**Project:** ${state.projectPath}`);
|
|
322
|
-
|
|
323
|
-
if (state.epicId) {
|
|
324
|
-
parts.push(`\n## 🎯 YOU ARE THE COORDINATOR`);
|
|
325
|
-
parts.push(``);
|
|
326
|
-
parts.push(`**Primary role:** Orchestrate workers, review their output, unblock dependencies.`);
|
|
327
|
-
parts.push(`**Spawn workers** for implementation tasks - don't do them yourself.`);
|
|
328
|
-
parts.push(``);
|
|
329
|
-
parts.push(`**RESUME STEPS:**`);
|
|
330
|
-
parts.push(`1. Check swarm status: \`swarm_status(epic_id="${state.epicId}", project_key="${state.projectPath}")\``);
|
|
331
|
-
parts.push(`2. Check inbox for worker messages: \`swarmmail_inbox(limit=5)\``);
|
|
332
|
-
parts.push(`3. For in_progress subtasks: Review worker results with \`swarm_review\``);
|
|
333
|
-
parts.push(`4. For open subtasks: Spawn workers with \`swarm_spawn_subtask\``);
|
|
334
|
-
parts.push(`5. For blocked subtasks: Investigate and unblock`);
|
|
335
|
-
}
|
|
422
|
+
parts.push(`**Project:** ${state.projectPath}\n`);
|
|
336
423
|
|
|
337
424
|
return parts.join("\n");
|
|
338
425
|
}
|
|
@@ -571,22 +658,44 @@ function buildDynamicSwarmStateFromScanned(
|
|
|
571
658
|
): string {
|
|
572
659
|
const parts: string[] = [];
|
|
573
660
|
|
|
574
|
-
parts.push("## 🐝 Current Swarm State\n");
|
|
575
|
-
|
|
576
661
|
// Prefer scanned data over detected
|
|
577
662
|
const epicId = scanned.epicId || detected.epicId;
|
|
578
663
|
const epicTitle = scanned.epicTitle || detected.epicTitle;
|
|
579
664
|
const projectPath = scanned.projectPath || detected.projectPath;
|
|
580
665
|
|
|
581
|
-
|
|
582
|
-
|
|
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}**`);
|
|
583
671
|
}
|
|
584
672
|
|
|
585
673
|
if (scanned.agentName) {
|
|
586
|
-
parts.push(
|
|
674
|
+
parts.push(`Coordinator: ${scanned.agentName}`);
|
|
675
|
+
}
|
|
676
|
+
|
|
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`);
|
|
587
691
|
}
|
|
588
692
|
|
|
589
|
-
|
|
693
|
+
// Swarm state summary
|
|
694
|
+
parts.push(`## 🐝 Current Swarm State\n`);
|
|
695
|
+
|
|
696
|
+
if (epicId) {
|
|
697
|
+
parts.push(`**Epic:** ${epicId}${epicTitle ? ` - ${epicTitle}` : ""}`);
|
|
698
|
+
}
|
|
590
699
|
|
|
591
700
|
// Show detailed subtask info from scanned state
|
|
592
701
|
if (scanned.subtasks.size > 0) {
|
|
@@ -606,7 +715,7 @@ function buildDynamicSwarmStateFromScanned(
|
|
|
606
715
|
detected.subtasks.blocked;
|
|
607
716
|
|
|
608
717
|
if (total > 0) {
|
|
609
|
-
parts.push(
|
|
718
|
+
parts.push(`\n**Subtasks:**`);
|
|
610
719
|
if (detected.subtasks.closed > 0)
|
|
611
720
|
parts.push(` - ${detected.subtasks.closed} closed`);
|
|
612
721
|
if (detected.subtasks.in_progress > 0)
|
|
@@ -618,29 +727,11 @@ function buildDynamicSwarmStateFromScanned(
|
|
|
618
727
|
}
|
|
619
728
|
}
|
|
620
729
|
|
|
730
|
+
parts.push(`\n**Project:** ${projectPath}`);
|
|
731
|
+
|
|
621
732
|
// Show last action if available
|
|
622
733
|
if (scanned.lastAction) {
|
|
623
|
-
parts.push(
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
if (epicId) {
|
|
627
|
-
parts.push(`\n## 🎯 YOU ARE THE COORDINATOR`);
|
|
628
|
-
parts.push(``);
|
|
629
|
-
parts.push(
|
|
630
|
-
`**Primary role:** Orchestrate workers, review their output, unblock dependencies.`,
|
|
631
|
-
);
|
|
632
|
-
parts.push(`**Spawn workers** for implementation tasks - don't do them yourself.`);
|
|
633
|
-
parts.push(``);
|
|
634
|
-
parts.push(`**RESUME STEPS:**`);
|
|
635
|
-
parts.push(
|
|
636
|
-
`1. Check swarm status: \`swarm_status(epic_id="${epicId}", project_key="${projectPath}")\``,
|
|
637
|
-
);
|
|
638
|
-
parts.push(`2. Check inbox for worker messages: \`swarmmail_inbox(limit=5)\``);
|
|
639
|
-
parts.push(
|
|
640
|
-
`3. For in_progress subtasks: Review worker results with \`swarm_review\``,
|
|
641
|
-
);
|
|
642
|
-
parts.push(`4. For open subtasks: Spawn workers with \`swarm_spawn_subtask\``);
|
|
643
|
-
parts.push(`5. For blocked subtasks: Investigate and unblock`);
|
|
734
|
+
parts.push(`**Last Action:** \`${scanned.lastAction.tool}\``);
|
|
644
735
|
}
|
|
645
736
|
|
|
646
737
|
return parts.join("\n");
|
|
@@ -931,6 +1022,12 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
931
1022
|
output: { context: string[] },
|
|
932
1023
|
): Promise<void> => {
|
|
933
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
|
+
});
|
|
934
1031
|
|
|
935
1032
|
getLog().info(
|
|
936
1033
|
{
|
|
@@ -940,12 +1037,19 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
940
1037
|
},
|
|
941
1038
|
"compaction started",
|
|
942
1039
|
);
|
|
1040
|
+
|
|
1041
|
+
recordPhaseStart(metrics, CompactionPhase.START);
|
|
943
1042
|
|
|
944
1043
|
try {
|
|
1044
|
+
recordPhaseComplete(metrics, CompactionPhase.START);
|
|
1045
|
+
|
|
945
1046
|
// Scan session messages for precise swarm state (if client available)
|
|
1047
|
+
recordPhaseStart(metrics, CompactionPhase.GATHER_SWARM_MAIL);
|
|
946
1048
|
const scannedState = await scanSessionMessages(client, input.sessionID);
|
|
1049
|
+
recordPhaseComplete(metrics, CompactionPhase.GATHER_SWARM_MAIL);
|
|
947
1050
|
|
|
948
1051
|
// Also run heuristic detection from hive/swarm-mail
|
|
1052
|
+
recordPhaseStart(metrics, CompactionPhase.DETECT);
|
|
949
1053
|
const detection = await detectSwarm();
|
|
950
1054
|
|
|
951
1055
|
// Boost confidence if we found swarm evidence in session messages
|
|
@@ -955,13 +1059,21 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
955
1059
|
if (effectiveConfidence === "none" || effectiveConfidence === "low") {
|
|
956
1060
|
effectiveConfidence = "medium";
|
|
957
1061
|
detection.reasons.push("swarm tool calls found in session");
|
|
1062
|
+
recordPatternExtracted(metrics, "swarm_tool_calls", "Found swarm tool calls in session");
|
|
958
1063
|
}
|
|
959
1064
|
if (scannedState.subtasks.size > 0) {
|
|
960
1065
|
effectiveConfidence = "high";
|
|
961
1066
|
detection.reasons.push(`${scannedState.subtasks.size} subtasks spawned`);
|
|
1067
|
+
recordPatternExtracted(metrics, "subtasks", `${scannedState.subtasks.size} subtasks spawned`);
|
|
962
1068
|
}
|
|
963
1069
|
}
|
|
1070
|
+
|
|
1071
|
+
recordPhaseComplete(metrics, CompactionPhase.DETECT, {
|
|
1072
|
+
confidence: effectiveConfidence,
|
|
1073
|
+
detected: detection.detected || scannedState.epicId !== undefined,
|
|
1074
|
+
});
|
|
964
1075
|
|
|
1076
|
+
recordPhaseStart(metrics, CompactionPhase.INJECT);
|
|
965
1077
|
if (
|
|
966
1078
|
effectiveConfidence === "high" ||
|
|
967
1079
|
effectiveConfidence === "medium"
|
|
@@ -988,6 +1100,11 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
988
1100
|
|
|
989
1101
|
const contextContent = header + dynamicState + SWARM_COMPACTION_CONTEXT;
|
|
990
1102
|
output.context.push(contextContent);
|
|
1103
|
+
|
|
1104
|
+
recordPhaseComplete(metrics, CompactionPhase.INJECT, {
|
|
1105
|
+
context_length: contextContent.length,
|
|
1106
|
+
context_type: "full",
|
|
1107
|
+
});
|
|
991
1108
|
|
|
992
1109
|
getLog().info(
|
|
993
1110
|
{
|
|
@@ -1007,6 +1124,11 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
1007
1124
|
const header = `[Possible swarm: ${detection.reasons.join(", ")}]\n\n`;
|
|
1008
1125
|
const contextContent = header + SWARM_DETECTION_FALLBACK;
|
|
1009
1126
|
output.context.push(contextContent);
|
|
1127
|
+
|
|
1128
|
+
recordPhaseComplete(metrics, CompactionPhase.INJECT, {
|
|
1129
|
+
context_length: contextContent.length,
|
|
1130
|
+
context_type: "fallback",
|
|
1131
|
+
});
|
|
1010
1132
|
|
|
1011
1133
|
getLog().info(
|
|
1012
1134
|
{
|
|
@@ -1018,6 +1140,10 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
1018
1140
|
"injected swarm context",
|
|
1019
1141
|
);
|
|
1020
1142
|
} else {
|
|
1143
|
+
recordPhaseComplete(metrics, CompactionPhase.INJECT, {
|
|
1144
|
+
context_type: "none",
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1021
1147
|
getLog().debug(
|
|
1022
1148
|
{
|
|
1023
1149
|
confidence: effectiveConfidence,
|
|
@@ -1028,7 +1154,10 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
1028
1154
|
}
|
|
1029
1155
|
// confidence === "none" - no injection, probably not a swarm
|
|
1030
1156
|
|
|
1157
|
+
recordPhaseStart(metrics, CompactionPhase.COMPLETE);
|
|
1031
1158
|
const duration = Date.now() - startTime;
|
|
1159
|
+
const summary = getMetricsSummary(metrics);
|
|
1160
|
+
|
|
1032
1161
|
getLog().info(
|
|
1033
1162
|
{
|
|
1034
1163
|
duration_ms: duration,
|
|
@@ -1036,11 +1165,30 @@ export function createCompactionHook(client?: OpencodeClient) {
|
|
|
1036
1165
|
detected: detection.detected || scannedState.epicId !== undefined,
|
|
1037
1166
|
confidence: effectiveConfidence,
|
|
1038
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
|
+
},
|
|
1039
1179
|
},
|
|
1040
1180
|
"compaction complete",
|
|
1041
1181
|
);
|
|
1182
|
+
|
|
1183
|
+
recordPhaseComplete(metrics, CompactionPhase.COMPLETE);
|
|
1042
1184
|
} catch (error) {
|
|
1043
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
|
+
|
|
1044
1192
|
getLog().error(
|
|
1045
1193
|
{
|
|
1046
1194
|
duration_ms: duration,
|