opencode-swarm-plugin 0.35.0 → 0.36.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.
- package/.hive/issues.jsonl +4 -4
- package/.hive/memories.jsonl +274 -1
- package/.turbo/turbo-build.log +4 -4
- package/.turbo/turbo-test.log +307 -307
- package/CHANGELOG.md +133 -0
- package/bin/swarm.ts +234 -179
- package/dist/compaction-hook.d.ts +54 -4
- package/dist/compaction-hook.d.ts.map +1 -1
- package/dist/eval-capture.d.ts +122 -17
- package/dist/eval-capture.d.ts.map +1 -1
- package/dist/index.d.ts +1 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1278 -619
- package/dist/planning-guardrails.d.ts +121 -0
- package/dist/planning-guardrails.d.ts.map +1 -1
- package/dist/plugin.d.ts +9 -9
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +1283 -329
- package/dist/schemas/task.d.ts +0 -1
- package/dist/schemas/task.d.ts.map +1 -1
- package/dist/swarm-decompose.d.ts +0 -8
- package/dist/swarm-decompose.d.ts.map +1 -1
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts +0 -4
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-review.d.ts.map +1 -1
- package/dist/swarm.d.ts +0 -6
- package/dist/swarm.d.ts.map +1 -1
- package/evals/README.md +38 -0
- package/evals/coordinator-session.eval.ts +154 -0
- package/evals/fixtures/coordinator-sessions.ts +328 -0
- package/evals/lib/data-loader.ts +69 -0
- package/evals/scorers/coordinator-discipline.evalite-test.ts +536 -0
- package/evals/scorers/coordinator-discipline.ts +315 -0
- package/evals/scorers/index.ts +12 -0
- package/examples/plugin-wrapper-template.ts +747 -34
- package/package.json +2 -2
- package/src/compaction-hook.test.ts +234 -281
- package/src/compaction-hook.ts +221 -63
- package/src/eval-capture.test.ts +390 -0
- package/src/eval-capture.ts +168 -10
- package/src/index.ts +89 -2
- package/src/learning.integration.test.ts +0 -2
- package/src/planning-guardrails.test.ts +387 -2
- package/src/planning-guardrails.ts +289 -0
- package/src/plugin.ts +10 -10
- package/src/schemas/task.ts +0 -1
- package/src/swarm-decompose.ts +21 -8
- package/src/swarm-orchestrate.ts +44 -0
- package/src/swarm-prompts.ts +20 -0
- package/src/swarm-review.ts +41 -0
- package/src/swarm.integration.test.ts +0 -40
package/src/compaction-hook.ts
CHANGED
|
@@ -68,11 +68,31 @@ function getLog() {
|
|
|
68
68
|
* This is NOT about preserving state for a human - it's about the swarm continuing
|
|
69
69
|
* autonomously after context compression.
|
|
70
70
|
*/
|
|
71
|
-
export const SWARM_COMPACTION_CONTEXT = `## 🐝 SWARM ACTIVE -
|
|
71
|
+
export const SWARM_COMPACTION_CONTEXT = `## 🐝 SWARM ACTIVE - You Are The COORDINATOR
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
Context was compacted but the swarm is still running. You are the **COORDINATOR**.
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
### ⛔ NEVER DO THESE (Coordinator Anti-Patterns)
|
|
76
|
+
|
|
77
|
+
**CRITICAL: Coordinators NEVER do implementation work. ALWAYS spawn workers.**
|
|
78
|
+
|
|
79
|
+
- ❌ **NEVER** use \`edit\` or \`write\` tools - SPAWN A WORKER
|
|
80
|
+
- ❌ **NEVER** run tests with \`bash\` - SPAWN A WORKER
|
|
81
|
+
- ❌ **NEVER** implement features yourself - SPAWN A WORKER
|
|
82
|
+
- ❌ **NEVER** "just do it myself to save time" - NO. SPAWN A WORKER.
|
|
83
|
+
- ❌ **NEVER** reserve files with \`swarmmail_reserve\` - Workers reserve files
|
|
84
|
+
|
|
85
|
+
**If you catch yourself about to edit a file, STOP. Use \`swarm_spawn_subtask\` instead.**
|
|
86
|
+
|
|
87
|
+
### ✅ ALWAYS DO THESE (Coordinator Checklist)
|
|
88
|
+
|
|
89
|
+
On resume, execute this checklist IN ORDER:
|
|
90
|
+
|
|
91
|
+
1. \`swarm_status(epic_id="<epic>", project_key="<path>")\` - Get current state
|
|
92
|
+
2. \`swarmmail_inbox(limit=5)\` - Check for agent messages
|
|
93
|
+
3. For completed work: \`swarm_review\` → \`swarm_review_feedback\`
|
|
94
|
+
4. For open subtasks: \`swarm_spawn_subtask\` (NOT "do it yourself")
|
|
95
|
+
5. For blocked work: Investigate, unblock, reassign
|
|
76
96
|
|
|
77
97
|
### Preserve in Summary
|
|
78
98
|
|
|
@@ -89,41 +109,31 @@ Extract from session context:
|
|
|
89
109
|
\`\`\`
|
|
90
110
|
## 🐝 Swarm State
|
|
91
111
|
|
|
92
|
-
**Epic:** <
|
|
112
|
+
**Epic:** <cell-xxx> - <title>
|
|
93
113
|
**Project:** <path>
|
|
94
114
|
**Progress:** X/Y subtasks complete
|
|
95
115
|
|
|
96
116
|
**Active:**
|
|
97
|
-
- <
|
|
117
|
+
- <cell-xxx>: <title> [in_progress] → <agent> working on <files>
|
|
98
118
|
|
|
99
119
|
**Blocked:**
|
|
100
|
-
- <
|
|
120
|
+
- <cell-xxx>: <title> - BLOCKED: <reason>
|
|
101
121
|
|
|
102
122
|
**Completed:**
|
|
103
|
-
- <
|
|
123
|
+
- <cell-xxx>: <title> ✓
|
|
104
124
|
|
|
105
125
|
**Ready to Spawn:**
|
|
106
|
-
- <
|
|
126
|
+
- <cell-xxx>: <title> (files: <...>)
|
|
107
127
|
\`\`\`
|
|
108
128
|
|
|
109
|
-
###
|
|
110
|
-
|
|
111
|
-
1. \`swarm_status(epic_id="<epic>", project_key="<path>")\` - Get current state
|
|
112
|
-
2. \`swarmmail_inbox(limit=5)\` - Check for agent messages
|
|
113
|
-
3. \`swarm_review(project_key, epic_id, task_id, files_touched)\` - Review any completed work
|
|
114
|
-
4. \`swarm_review_feedback(project_key, task_id, worker_id, status, issues)\` - Approve or request changes
|
|
115
|
-
5. **Spawn ready subtasks** - Don't wait, fire them off
|
|
116
|
-
6. **Unblock blocked work** - Resolve dependencies, reassign if needed
|
|
117
|
-
7. **Collect completed work** - Close done subtasks, verify quality
|
|
118
|
-
|
|
119
|
-
### Keep the Swarm Cooking
|
|
129
|
+
### Your Role
|
|
120
130
|
|
|
121
131
|
- **Spawn aggressively** - If a subtask is ready and unblocked, spawn an agent
|
|
122
132
|
- **Monitor actively** - Check status, read messages, respond to blockers
|
|
133
|
+
- **Review work** - Use \`swarm_review\` and \`swarm_review_feedback\` for completed work
|
|
123
134
|
- **Close the loop** - When all subtasks done, verify and close the epic
|
|
124
|
-
- **Don't stop** - The swarm runs until the epic is closed
|
|
125
135
|
|
|
126
|
-
**You are
|
|
136
|
+
**You are the COORDINATOR. You orchestrate. You do NOT implement. Spawn workers.**
|
|
127
137
|
`;
|
|
128
138
|
|
|
129
139
|
/**
|
|
@@ -236,29 +246,30 @@ interface ToolPart {
|
|
|
236
246
|
/**
|
|
237
247
|
* Tool state (completed tools have input/output we need)
|
|
238
248
|
*/
|
|
239
|
-
type ToolState =
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
249
|
+
type ToolState =
|
|
250
|
+
| {
|
|
251
|
+
status: "completed";
|
|
252
|
+
input: { [key: string]: unknown };
|
|
253
|
+
output: string;
|
|
254
|
+
title: string;
|
|
255
|
+
metadata: { [key: string]: unknown };
|
|
256
|
+
time: { start: number; end: number };
|
|
257
|
+
}
|
|
258
|
+
| {
|
|
259
|
+
status: string;
|
|
260
|
+
[key: string]: unknown;
|
|
261
|
+
};
|
|
250
262
|
|
|
251
263
|
/**
|
|
252
264
|
* SDK Client type (minimal interface for scanSessionMessages)
|
|
265
|
+
*
|
|
266
|
+
* The actual SDK client uses a more complex Options-based API:
|
|
267
|
+
* client.session.messages({ path: { id: sessionID }, query: { limit } })
|
|
268
|
+
*
|
|
269
|
+
* We accept `unknown` and handle the type internally to avoid
|
|
270
|
+
* tight coupling to SDK internals.
|
|
253
271
|
*/
|
|
254
|
-
|
|
255
|
-
session: {
|
|
256
|
-
messages: (opts: { sessionID: string; limit?: number }) => Promise<{
|
|
257
|
-
info: { id: string; sessionID: string };
|
|
258
|
-
parts: ToolPart[];
|
|
259
|
-
}[]>;
|
|
260
|
-
};
|
|
261
|
-
}
|
|
272
|
+
export type OpencodeClient = unknown;
|
|
262
273
|
|
|
263
274
|
/**
|
|
264
275
|
* Scanned swarm state extracted from session messages
|
|
@@ -268,29 +279,32 @@ export interface ScannedSwarmState {
|
|
|
268
279
|
epicTitle?: string;
|
|
269
280
|
projectPath?: string;
|
|
270
281
|
agentName?: string;
|
|
271
|
-
subtasks: Map<
|
|
282
|
+
subtasks: Map<
|
|
283
|
+
string,
|
|
284
|
+
{ title: string; status: string; worker?: string; files?: string[] }
|
|
285
|
+
>;
|
|
272
286
|
lastAction?: { tool: string; args: unknown; timestamp: number };
|
|
273
287
|
}
|
|
274
288
|
|
|
275
289
|
/**
|
|
276
290
|
* Scan session messages for swarm state using SDK client
|
|
277
|
-
*
|
|
291
|
+
*
|
|
278
292
|
* Extracts swarm coordination state from actual tool calls:
|
|
279
293
|
* - swarm_spawn_subtask → subtask tracking
|
|
280
294
|
* - swarmmail_init → agent name, project path
|
|
281
295
|
* - hive_create_epic → epic ID and title
|
|
282
296
|
* - swarm_status → epic reference
|
|
283
297
|
* - swarm_complete → subtask completion
|
|
284
|
-
*
|
|
298
|
+
*
|
|
285
299
|
* @param client - OpenCode SDK client (undefined if not available)
|
|
286
300
|
* @param sessionID - Session to scan
|
|
287
301
|
* @param limit - Max messages to fetch (default 100)
|
|
288
302
|
* @returns Extracted swarm state
|
|
289
303
|
*/
|
|
290
304
|
export async function scanSessionMessages(
|
|
291
|
-
client: OpencodeClient
|
|
305
|
+
client: OpencodeClient,
|
|
292
306
|
sessionID: string,
|
|
293
|
-
limit: number = 100
|
|
307
|
+
limit: number = 100,
|
|
294
308
|
): Promise<ScannedSwarmState> {
|
|
295
309
|
const state: ScannedSwarmState = {
|
|
296
310
|
subtasks: new Map(),
|
|
@@ -301,7 +315,22 @@ export async function scanSessionMessages(
|
|
|
301
315
|
}
|
|
302
316
|
|
|
303
317
|
try {
|
|
304
|
-
|
|
318
|
+
// SDK client uses Options-based API: { path: { id }, query: { limit } }
|
|
319
|
+
const sdkClient = client as {
|
|
320
|
+
session: {
|
|
321
|
+
messages: (opts: {
|
|
322
|
+
path: { id: string };
|
|
323
|
+
query?: { limit?: number };
|
|
324
|
+
}) => Promise<{ data?: Array<{ info: unknown; parts: ToolPart[] }> }>;
|
|
325
|
+
};
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const response = await sdkClient.session.messages({
|
|
329
|
+
path: { id: sessionID },
|
|
330
|
+
query: { limit },
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const messages = response.data || [];
|
|
305
334
|
|
|
306
335
|
for (const message of messages) {
|
|
307
336
|
for (const part of message.parts) {
|
|
@@ -310,7 +339,10 @@ export async function scanSessionMessages(
|
|
|
310
339
|
}
|
|
311
340
|
|
|
312
341
|
const { tool, state: toolState } = part;
|
|
313
|
-
const { input, output, time } = toolState as Extract<
|
|
342
|
+
const { input, output, time } = toolState as Extract<
|
|
343
|
+
ToolState,
|
|
344
|
+
{ status: "completed" }
|
|
345
|
+
>;
|
|
314
346
|
|
|
315
347
|
// Track last action
|
|
316
348
|
state.lastAction = {
|
|
@@ -407,12 +439,102 @@ export async function scanSessionMessages(
|
|
|
407
439
|
}
|
|
408
440
|
}
|
|
409
441
|
} catch (error) {
|
|
442
|
+
getLog().debug(
|
|
443
|
+
{
|
|
444
|
+
error: error instanceof Error ? error.message : String(error),
|
|
445
|
+
},
|
|
446
|
+
"SDK message scanning failed",
|
|
447
|
+
);
|
|
410
448
|
// SDK not available or error fetching messages - return what we have
|
|
411
449
|
}
|
|
412
450
|
|
|
413
451
|
return state;
|
|
414
452
|
}
|
|
415
453
|
|
|
454
|
+
/**
|
|
455
|
+
* Build dynamic swarm state from scanned messages (more precise than hive detection)
|
|
456
|
+
*/
|
|
457
|
+
function buildDynamicSwarmStateFromScanned(
|
|
458
|
+
scanned: ScannedSwarmState,
|
|
459
|
+
detected: SwarmState,
|
|
460
|
+
): string {
|
|
461
|
+
const parts: string[] = [];
|
|
462
|
+
|
|
463
|
+
parts.push("## 🐝 Current Swarm State\n");
|
|
464
|
+
|
|
465
|
+
// Prefer scanned data over detected
|
|
466
|
+
const epicId = scanned.epicId || detected.epicId;
|
|
467
|
+
const epicTitle = scanned.epicTitle || detected.epicTitle;
|
|
468
|
+
const projectPath = scanned.projectPath || detected.projectPath;
|
|
469
|
+
|
|
470
|
+
if (epicId) {
|
|
471
|
+
parts.push(`**Epic:** ${epicId}${epicTitle ? ` - ${epicTitle}` : ""}`);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (scanned.agentName) {
|
|
475
|
+
parts.push(`**Coordinator:** ${scanned.agentName}`);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
parts.push(`**Project:** ${projectPath}`);
|
|
479
|
+
|
|
480
|
+
// Show detailed subtask info from scanned state
|
|
481
|
+
if (scanned.subtasks.size > 0) {
|
|
482
|
+
parts.push(`\n**Subtasks:**`);
|
|
483
|
+
for (const [id, subtask] of scanned.subtasks) {
|
|
484
|
+
const status = subtask.status === "completed" ? "✓" : `[${subtask.status}]`;
|
|
485
|
+
const worker = subtask.worker ? ` → ${subtask.worker}` : "";
|
|
486
|
+
const files = subtask.files?.length ? ` (${subtask.files.join(", ")})` : "";
|
|
487
|
+
parts.push(` - ${id}: ${subtask.title} ${status}${worker}${files}`);
|
|
488
|
+
}
|
|
489
|
+
} else if (detected.subtasks) {
|
|
490
|
+
// Fall back to counts from hive detection
|
|
491
|
+
const total =
|
|
492
|
+
detected.subtasks.closed +
|
|
493
|
+
detected.subtasks.in_progress +
|
|
494
|
+
detected.subtasks.open +
|
|
495
|
+
detected.subtasks.blocked;
|
|
496
|
+
|
|
497
|
+
if (total > 0) {
|
|
498
|
+
parts.push(`**Subtasks:**`);
|
|
499
|
+
if (detected.subtasks.closed > 0)
|
|
500
|
+
parts.push(` - ${detected.subtasks.closed} closed`);
|
|
501
|
+
if (detected.subtasks.in_progress > 0)
|
|
502
|
+
parts.push(` - ${detected.subtasks.in_progress} in_progress`);
|
|
503
|
+
if (detected.subtasks.open > 0)
|
|
504
|
+
parts.push(` - ${detected.subtasks.open} open`);
|
|
505
|
+
if (detected.subtasks.blocked > 0)
|
|
506
|
+
parts.push(` - ${detected.subtasks.blocked} blocked`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Show last action if available
|
|
511
|
+
if (scanned.lastAction) {
|
|
512
|
+
parts.push(`\n**Last Action:** \`${scanned.lastAction.tool}\``);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (epicId) {
|
|
516
|
+
parts.push(`\n## 🎯 YOU ARE THE COORDINATOR`);
|
|
517
|
+
parts.push(``);
|
|
518
|
+
parts.push(
|
|
519
|
+
`**Primary role:** Orchestrate workers, review their output, unblock dependencies.`,
|
|
520
|
+
);
|
|
521
|
+
parts.push(`**Spawn workers** for implementation tasks - don't do them yourself.`);
|
|
522
|
+
parts.push(``);
|
|
523
|
+
parts.push(`**RESUME STEPS:**`);
|
|
524
|
+
parts.push(
|
|
525
|
+
`1. Check swarm status: \`swarm_status(epic_id="${epicId}", project_key="${projectPath}")\``,
|
|
526
|
+
);
|
|
527
|
+
parts.push(`2. Check inbox for worker messages: \`swarmmail_inbox(limit=5)\``);
|
|
528
|
+
parts.push(
|
|
529
|
+
`3. For in_progress subtasks: Review worker results with \`swarm_review\``,
|
|
530
|
+
);
|
|
531
|
+
parts.push(`4. For open subtasks: Spawn workers with \`swarm_spawn_subtask\``);
|
|
532
|
+
parts.push(`5. For blocked subtasks: Investigate and unblock`);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return parts.join("\n");
|
|
536
|
+
}
|
|
537
|
+
|
|
416
538
|
// ============================================================================
|
|
417
539
|
// Swarm Detection
|
|
418
540
|
// ============================================================================
|
|
@@ -678,17 +800,21 @@ async function detectSwarm(): Promise<SwarmDetection> {
|
|
|
678
800
|
* Philosophy: Err on the side of continuation. A false positive costs
|
|
679
801
|
* a bit of context space. A false negative loses the swarm.
|
|
680
802
|
*
|
|
803
|
+
* @param client - Optional OpenCode SDK client for scanning session messages.
|
|
804
|
+
* When provided, extracts PRECISE swarm state from actual tool calls.
|
|
805
|
+
* When undefined, falls back to hive/swarm-mail heuristic detection.
|
|
806
|
+
*
|
|
681
807
|
* @example
|
|
682
808
|
* ```typescript
|
|
683
809
|
* import { createCompactionHook } from "opencode-swarm-plugin";
|
|
684
810
|
*
|
|
685
|
-
* export const SwarmPlugin: Plugin = async () => ({
|
|
811
|
+
* export const SwarmPlugin: Plugin = async (input) => ({
|
|
686
812
|
* tool: { ... },
|
|
687
|
-
* "experimental.session.compacting": createCompactionHook(),
|
|
813
|
+
* "experimental.session.compacting": createCompactionHook(input.client),
|
|
688
814
|
* });
|
|
689
815
|
* ```
|
|
690
816
|
*/
|
|
691
|
-
export function createCompactionHook() {
|
|
817
|
+
export function createCompactionHook(client?: OpencodeClient) {
|
|
692
818
|
return async (
|
|
693
819
|
input: { sessionID: string },
|
|
694
820
|
output: { context: string[] },
|
|
@@ -699,41 +825,73 @@ export function createCompactionHook() {
|
|
|
699
825
|
{
|
|
700
826
|
session_id: input.sessionID,
|
|
701
827
|
trigger: "session_compaction",
|
|
828
|
+
has_sdk_client: !!client,
|
|
702
829
|
},
|
|
703
830
|
"compaction started",
|
|
704
831
|
);
|
|
705
832
|
|
|
706
833
|
try {
|
|
834
|
+
// Scan session messages for precise swarm state (if client available)
|
|
835
|
+
const scannedState = await scanSessionMessages(client, input.sessionID);
|
|
836
|
+
|
|
837
|
+
// Also run heuristic detection from hive/swarm-mail
|
|
707
838
|
const detection = await detectSwarm();
|
|
708
839
|
|
|
840
|
+
// Boost confidence if we found swarm evidence in session messages
|
|
841
|
+
let effectiveConfidence = detection.confidence;
|
|
842
|
+
if (scannedState.epicId || scannedState.subtasks.size > 0) {
|
|
843
|
+
// Session messages show swarm activity - this is HIGH confidence
|
|
844
|
+
if (effectiveConfidence === "none" || effectiveConfidence === "low") {
|
|
845
|
+
effectiveConfidence = "medium";
|
|
846
|
+
detection.reasons.push("swarm tool calls found in session");
|
|
847
|
+
}
|
|
848
|
+
if (scannedState.subtasks.size > 0) {
|
|
849
|
+
effectiveConfidence = "high";
|
|
850
|
+
detection.reasons.push(`${scannedState.subtasks.size} subtasks spawned`);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
709
854
|
if (
|
|
710
|
-
|
|
711
|
-
|
|
855
|
+
effectiveConfidence === "high" ||
|
|
856
|
+
effectiveConfidence === "medium"
|
|
712
857
|
) {
|
|
713
858
|
// Definite or probable swarm - inject full context
|
|
714
859
|
const header = `[Swarm detected: ${detection.reasons.join(", ")}]\n\n`;
|
|
715
|
-
|
|
716
|
-
// Build dynamic state section
|
|
860
|
+
|
|
861
|
+
// Build dynamic state section - prefer scanned state (ground truth) over detected
|
|
717
862
|
let dynamicState = "";
|
|
718
|
-
if (
|
|
863
|
+
if (scannedState.epicId || scannedState.subtasks.size > 0) {
|
|
864
|
+
// Use scanned state (more precise)
|
|
865
|
+
dynamicState =
|
|
866
|
+
buildDynamicSwarmStateFromScanned(
|
|
867
|
+
scannedState,
|
|
868
|
+
detection.state || {
|
|
869
|
+
projectPath: scannedState.projectPath || process.cwd(),
|
|
870
|
+
subtasks: { closed: 0, in_progress: 0, open: 0, blocked: 0 },
|
|
871
|
+
},
|
|
872
|
+
) + "\n\n";
|
|
873
|
+
} else if (detection.state && detection.state.epicId) {
|
|
874
|
+
// Fall back to hive-detected state
|
|
719
875
|
dynamicState = buildDynamicSwarmState(detection.state) + "\n\n";
|
|
720
876
|
}
|
|
721
|
-
|
|
877
|
+
|
|
722
878
|
const contextContent = header + dynamicState + SWARM_COMPACTION_CONTEXT;
|
|
723
879
|
output.context.push(contextContent);
|
|
724
880
|
|
|
725
881
|
getLog().info(
|
|
726
882
|
{
|
|
727
|
-
confidence:
|
|
883
|
+
confidence: effectiveConfidence,
|
|
728
884
|
context_length: contextContent.length,
|
|
729
885
|
context_type: "full",
|
|
730
886
|
reasons: detection.reasons,
|
|
731
887
|
has_dynamic_state: !!dynamicState,
|
|
732
|
-
epic_id: detection.state?.epicId,
|
|
888
|
+
epic_id: scannedState.epicId || detection.state?.epicId,
|
|
889
|
+
scanned_subtasks: scannedState.subtasks.size,
|
|
890
|
+
scanned_agent: scannedState.agentName,
|
|
733
891
|
},
|
|
734
892
|
"injected swarm context",
|
|
735
893
|
);
|
|
736
|
-
} else if (
|
|
894
|
+
} else if (effectiveConfidence === "low") {
|
|
737
895
|
// Possible swarm - inject fallback detection prompt
|
|
738
896
|
const header = `[Possible swarm: ${detection.reasons.join(", ")}]\n\n`;
|
|
739
897
|
const contextContent = header + SWARM_DETECTION_FALLBACK;
|
|
@@ -741,7 +899,7 @@ export function createCompactionHook() {
|
|
|
741
899
|
|
|
742
900
|
getLog().info(
|
|
743
901
|
{
|
|
744
|
-
confidence:
|
|
902
|
+
confidence: effectiveConfidence,
|
|
745
903
|
context_length: contextContent.length,
|
|
746
904
|
context_type: "fallback",
|
|
747
905
|
reasons: detection.reasons,
|
|
@@ -751,7 +909,7 @@ export function createCompactionHook() {
|
|
|
751
909
|
} else {
|
|
752
910
|
getLog().debug(
|
|
753
911
|
{
|
|
754
|
-
confidence:
|
|
912
|
+
confidence: effectiveConfidence,
|
|
755
913
|
context_type: "none",
|
|
756
914
|
},
|
|
757
915
|
"no swarm detected, skipping injection",
|
|
@@ -764,8 +922,8 @@ export function createCompactionHook() {
|
|
|
764
922
|
{
|
|
765
923
|
duration_ms: duration,
|
|
766
924
|
success: true,
|
|
767
|
-
detected: detection.detected,
|
|
768
|
-
confidence:
|
|
925
|
+
detected: detection.detected || scannedState.epicId !== undefined,
|
|
926
|
+
confidence: effectiveConfidence,
|
|
769
927
|
context_injected: output.context.length > 0,
|
|
770
928
|
},
|
|
771
929
|
"compaction complete",
|