opencode-swarm-plugin 0.51.0 → 0.53.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/bin/commands/log.test.ts +117 -0
- package/bin/commands/log.ts +362 -0
- package/bin/swarm.ts +53 -2
- package/dist/bin/swarm.js +1392 -1332
- package/dist/compaction-hook.d.ts.map +1 -1
- package/dist/examples/plugin-wrapper-template.ts +271 -23
- package/dist/index.js +130 -6
- package/dist/plugin.js +130 -6
- package/dist/swarm-insights.d.ts +54 -0
- package/dist/swarm-insights.d.ts.map +1 -1
- package/dist/swarm-prompts.js +89 -2
- package/dist/swarm-strategies.d.ts.map +1 -1
- package/examples/plugin-wrapper-template.ts +271 -23
- package/package.json +2 -2
- package/dist/tools/ubs/index.d.ts +0 -28
- package/dist/tools/ubs/index.d.ts.map +0 -1
- package/dist/tools/ubs/patterns/stub-patterns.d.ts +0 -43
- package/dist/tools/ubs/patterns/stub-patterns.d.ts.map +0 -1
- package/dist/tools/ubs/scanner.d.ts +0 -49
- package/dist/tools/ubs/scanner.d.ts.map +0 -1
- package/dist/tools/ubs/types.d.ts +0 -46
- package/dist/tools/ubs/types.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-hook.d.ts","sourceRoot":"","sources":["../src/compaction-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAyCH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,wBAAwB,s9RA+OpC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,0nCAiCpC,CAAC;AA2FF;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC;AAErC;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,GAAG,CACX,MAAM,EACN;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CACrE,CAAC;IACF,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CACjE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,MAAY,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAgJ5B;
|
|
1
|
+
{"version":3,"file":"compaction-hook.d.ts","sourceRoot":"","sources":["../src/compaction-hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -349,43 +349,193 @@ function logCompaction(
|
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
/**
|
|
352
|
-
*
|
|
352
|
+
* Get date-stamped log file path
|
|
353
|
+
* Format: ~/.config/swarm-tools/logs/{type}-YYYY-MM-DD.log
|
|
354
|
+
*/
|
|
355
|
+
function getDateStampedLogPath(type: "tools" | "swarmmail" | "errors"): string {
|
|
356
|
+
const today = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
|
|
357
|
+
return join(LOG_DIR, `${type}-${today}.log`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Rotate old log files (delete files older than 7 days)
|
|
362
|
+
*
|
|
363
|
+
* Runs silently - never breaks the plugin if rotation fails.
|
|
364
|
+
*/
|
|
365
|
+
function rotateLogFiles(): void {
|
|
366
|
+
try {
|
|
367
|
+
ensureLogDir();
|
|
368
|
+
const { readdirSync, unlinkSync, statSync } = require("node:fs");
|
|
369
|
+
const files = readdirSync(LOG_DIR);
|
|
370
|
+
const now = Date.now();
|
|
371
|
+
const sevenDaysMs = 7 * 24 * 60 * 60 * 1000;
|
|
372
|
+
|
|
373
|
+
for (const file of files) {
|
|
374
|
+
// Only rotate date-stamped files (tools-*, swarmmail-*, errors-*)
|
|
375
|
+
if (!/^(tools|swarmmail|errors)-\d{4}-\d{2}-\d{2}\.log$/.test(file)) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const filePath = join(LOG_DIR, file);
|
|
380
|
+
const stats = statSync(filePath);
|
|
381
|
+
const age = now - stats.mtimeMs;
|
|
382
|
+
|
|
383
|
+
if (age > sevenDaysMs) {
|
|
384
|
+
unlinkSync(filePath);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
} catch {
|
|
388
|
+
// Silently fail - rotation failures shouldn't break the plugin
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Log a tool invocation to date-stamped file
|
|
394
|
+
*
|
|
395
|
+
* @param toolName - Tool name (e.g., "hive_create", "swarm_status")
|
|
396
|
+
* @param args - Tool arguments
|
|
397
|
+
* @param result - Tool result (optional, for successful calls)
|
|
398
|
+
* @param error - Error message (optional, for failed calls)
|
|
399
|
+
*/
|
|
400
|
+
function logTool(
|
|
401
|
+
toolName: string,
|
|
402
|
+
args: Record<string, unknown>,
|
|
403
|
+
result?: string,
|
|
404
|
+
error?: string,
|
|
405
|
+
): void {
|
|
406
|
+
try {
|
|
407
|
+
ensureLogDir();
|
|
408
|
+
rotateLogFiles(); // Rotate on every log call (cheap operation)
|
|
409
|
+
|
|
410
|
+
const logPath = getDateStampedLogPath("tools");
|
|
411
|
+
const entry = JSON.stringify({
|
|
412
|
+
time: new Date().toISOString(),
|
|
413
|
+
level: error ? "error" : "info",
|
|
414
|
+
msg: `tool_call: ${toolName}`,
|
|
415
|
+
tool: toolName,
|
|
416
|
+
args,
|
|
417
|
+
...(result && { result }),
|
|
418
|
+
...(error && { error }),
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
appendFileSync(logPath, entry + "\n");
|
|
422
|
+
} catch {
|
|
423
|
+
// Silently fail - logging should never break the plugin
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Log a Swarm Mail event to date-stamped file
|
|
353
429
|
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
430
|
+
* @param event - Event type (e.g., "message_sent", "inbox_fetched")
|
|
431
|
+
* @param data - Event data
|
|
432
|
+
*/
|
|
433
|
+
function logSwarmMail(
|
|
434
|
+
event: string,
|
|
435
|
+
data: Record<string, unknown>,
|
|
436
|
+
): void {
|
|
437
|
+
try {
|
|
438
|
+
ensureLogDir();
|
|
439
|
+
rotateLogFiles();
|
|
440
|
+
|
|
441
|
+
const logPath = getDateStampedLogPath("swarmmail");
|
|
442
|
+
const entry = JSON.stringify({
|
|
443
|
+
time: new Date().toISOString(),
|
|
444
|
+
level: "info",
|
|
445
|
+
msg: event,
|
|
446
|
+
...data,
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
appendFileSync(logPath, entry + "\n");
|
|
450
|
+
} catch {
|
|
451
|
+
// Silently fail
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Log an error to date-stamped file
|
|
457
|
+
*
|
|
458
|
+
* @param error - Error message
|
|
459
|
+
* @param data - Additional error context
|
|
460
|
+
*/
|
|
461
|
+
function logError(
|
|
462
|
+
error: string,
|
|
463
|
+
data?: Record<string, unknown>,
|
|
464
|
+
): void {
|
|
465
|
+
try {
|
|
466
|
+
ensureLogDir();
|
|
467
|
+
rotateLogFiles();
|
|
468
|
+
|
|
469
|
+
const logPath = getDateStampedLogPath("errors");
|
|
470
|
+
const entry = JSON.stringify({
|
|
471
|
+
time: new Date().toISOString(),
|
|
472
|
+
level: "error",
|
|
473
|
+
msg: error,
|
|
474
|
+
...data,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
appendFileSync(logPath, entry + "\n");
|
|
478
|
+
} catch {
|
|
479
|
+
// Silently fail
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Capture compaction event for evals (INLINED - do not import from opencode-swarm-plugin)
|
|
485
|
+
*
|
|
486
|
+
* Writes COMPACTION events directly to session JSONL file.
|
|
487
|
+
* This is inlined to avoid import issues - plugin wrapper must be 100% self-contained.
|
|
488
|
+
*
|
|
489
|
+
* Matches the structure of captureCompactionEvent from eval-capture.ts but writes
|
|
490
|
+
* ONLY to JSONL (not libSQL) to avoid swarm-mail dependency.
|
|
356
491
|
*
|
|
357
492
|
* @param sessionID - Session ID
|
|
358
493
|
* @param epicID - Epic ID (or "unknown" if not detected)
|
|
359
|
-
* @param compactionType - Event type (detection_complete, prompt_generated, context_injected)
|
|
494
|
+
* @param compactionType - Event type (detection_complete, prompt_generated, context_injected, resumption_started, tool_call_tracked)
|
|
360
495
|
* @param payload - Event-specific data (full prompts, detection results, etc.)
|
|
361
496
|
*/
|
|
362
497
|
async function captureCompaction(
|
|
363
498
|
sessionID: string,
|
|
364
499
|
epicID: string,
|
|
365
|
-
compactionType: "detection_complete" | "prompt_generated" | "context_injected",
|
|
500
|
+
compactionType: "detection_complete" | "prompt_generated" | "context_injected" | "resumption_started" | "tool_call_tracked",
|
|
366
501
|
payload: any,
|
|
367
502
|
): Promise<void> {
|
|
368
503
|
try {
|
|
369
|
-
//
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
"
|
|
375
|
-
|
|
376
|
-
|
|
504
|
+
// Build the CoordinatorEvent object matching eval-capture.ts schema
|
|
505
|
+
const event = {
|
|
506
|
+
session_id: sessionID,
|
|
507
|
+
epic_id: epicID,
|
|
508
|
+
timestamp: new Date().toISOString(),
|
|
509
|
+
event_type: "COMPACTION",
|
|
510
|
+
compaction_type: compactionType,
|
|
511
|
+
payload: payload,
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
// Session directory: ~/.config/swarm-tools/sessions/
|
|
515
|
+
const sessionDir = process.env.SWARM_SESSIONS_DIR ||
|
|
516
|
+
join(homedir(), ".config", "swarm-tools", "sessions");
|
|
377
517
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
518
|
+
// Ensure directory exists
|
|
519
|
+
if (!existsSync(sessionDir)) {
|
|
520
|
+
mkdirSync(sessionDir, { recursive: true });
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Write to JSONL (append mode)
|
|
524
|
+
const sessionPath = join(sessionDir, `${sessionID}.jsonl`);
|
|
525
|
+
const line = `${JSON.stringify(event)}\n`;
|
|
526
|
+
appendFileSync(sessionPath, line, "utf-8");
|
|
527
|
+
|
|
528
|
+
logCompaction("debug", "compaction_event_captured", {
|
|
529
|
+
session_id: sessionID,
|
|
530
|
+
epic_id: epicID,
|
|
531
|
+
compaction_type: compactionType,
|
|
532
|
+
session_path: sessionPath,
|
|
381
533
|
});
|
|
382
|
-
|
|
383
|
-
// Don't wait - capture is non-blocking
|
|
384
|
-
proc.unref();
|
|
385
534
|
} catch (err) {
|
|
386
535
|
// Non-fatal - capture failures shouldn't break compaction
|
|
387
536
|
logCompaction("warn", "compaction_capture_failed", {
|
|
388
537
|
session_id: sessionID,
|
|
538
|
+
epic_id: epicID,
|
|
389
539
|
compaction_type: compactionType,
|
|
390
540
|
error: err instanceof Error ? err.message : String(err),
|
|
391
541
|
});
|
|
@@ -451,6 +601,14 @@ async function execTool(
|
|
|
451
601
|
try {
|
|
452
602
|
const result = JSON.parse(stdout);
|
|
453
603
|
if (result.success && result.data !== undefined) {
|
|
604
|
+
// Log successful tool call
|
|
605
|
+
logTool(name, args, typeof result.data === "string" ? result.data : JSON.stringify(result.data));
|
|
606
|
+
|
|
607
|
+
// Log Swarm Mail events separately
|
|
608
|
+
if (name.startsWith("swarmmail_")) {
|
|
609
|
+
logSwarmMail(`tool_${name}`, { args, result: result.data });
|
|
610
|
+
}
|
|
611
|
+
|
|
454
612
|
// Unwrap the data for cleaner tool output
|
|
455
613
|
resolve(
|
|
456
614
|
typeof result.data === "string"
|
|
@@ -463,17 +621,30 @@ async function execTool(
|
|
|
463
621
|
const errorMsg = typeof result.error === "string"
|
|
464
622
|
? result.error
|
|
465
623
|
: (result.error.message || "Tool execution failed");
|
|
624
|
+
|
|
625
|
+
// Log failed tool call
|
|
626
|
+
logTool(name, args, undefined, errorMsg);
|
|
627
|
+
logError(`Tool ${name} failed`, { args, error: errorMsg });
|
|
628
|
+
|
|
466
629
|
reject(new Error(errorMsg));
|
|
467
630
|
} else {
|
|
631
|
+
// Log successful (non-standard response)
|
|
632
|
+
logTool(name, args, stdout);
|
|
468
633
|
resolve(stdout);
|
|
469
634
|
}
|
|
470
635
|
} catch {
|
|
636
|
+
// Log successful (unparseable response)
|
|
637
|
+
logTool(name, args, stdout);
|
|
471
638
|
resolve(stdout);
|
|
472
639
|
}
|
|
473
640
|
} else if (code === 2) {
|
|
474
|
-
|
|
641
|
+
const errorMsg = `Unknown tool: ${name}`;
|
|
642
|
+
logError(errorMsg, { args });
|
|
643
|
+
reject(new Error(errorMsg));
|
|
475
644
|
} else if (code === 3) {
|
|
476
|
-
|
|
645
|
+
const errorMsg = `Invalid JSON args: ${stderr}`;
|
|
646
|
+
logError(errorMsg, { tool: name, args });
|
|
647
|
+
reject(new Error(errorMsg));
|
|
477
648
|
} else {
|
|
478
649
|
// Tool returned error
|
|
479
650
|
try {
|
|
@@ -483,15 +654,27 @@ async function execTool(
|
|
|
483
654
|
const errorMsg = typeof result.error === "string"
|
|
484
655
|
? result.error
|
|
485
656
|
: (result.error.message || `Tool failed with code ${code}`);
|
|
657
|
+
|
|
658
|
+
logTool(name, args, undefined, errorMsg);
|
|
659
|
+
logError(`Tool ${name} failed with code ${code}`, { args, error: errorMsg });
|
|
660
|
+
|
|
486
661
|
reject(new Error(errorMsg));
|
|
487
662
|
} else {
|
|
663
|
+
const errorMsg = stderr || stdout || `Tool failed with code ${code}`;
|
|
664
|
+
logTool(name, args, undefined, errorMsg);
|
|
665
|
+
logError(`Tool ${name} failed with code ${code}`, { args, stderr, stdout });
|
|
666
|
+
|
|
488
667
|
reject(
|
|
489
|
-
new Error(
|
|
668
|
+
new Error(errorMsg),
|
|
490
669
|
);
|
|
491
670
|
}
|
|
492
671
|
} catch {
|
|
672
|
+
const errorMsg = stderr || stdout || `Tool failed with code ${code}`;
|
|
673
|
+
logTool(name, args, undefined, errorMsg);
|
|
674
|
+
logError(`Tool ${name} failed with code ${code}`, { args, stderr, stdout });
|
|
675
|
+
|
|
493
676
|
reject(
|
|
494
|
-
new Error(
|
|
677
|
+
new Error(errorMsg),
|
|
495
678
|
);
|
|
496
679
|
}
|
|
497
680
|
}
|
|
@@ -669,6 +852,42 @@ const beads_link_thread = tool({
|
|
|
669
852
|
execute: (args, ctx) => execTool("beads_link_thread", args, ctx),
|
|
670
853
|
});
|
|
671
854
|
|
|
855
|
+
// =============================================================================
|
|
856
|
+
// Session Handoff Tools (Chainlink-inspired)
|
|
857
|
+
// =============================================================================
|
|
858
|
+
|
|
859
|
+
const hive_session_start = tool({
|
|
860
|
+
description: `Start a new work session with optional handoff notes from previous session.
|
|
861
|
+
|
|
862
|
+
Chainlink-inspired session management for context preservation across sessions.
|
|
863
|
+
Returns previous session's handoff notes if available.
|
|
864
|
+
|
|
865
|
+
Credit: Chainlink session handoff pattern from https://github.com/dollspace-gay/chainlink`,
|
|
866
|
+
args: {
|
|
867
|
+
active_cell_id: tool.schema
|
|
868
|
+
.string()
|
|
869
|
+
.optional()
|
|
870
|
+
.describe("ID of cell being worked on"),
|
|
871
|
+
},
|
|
872
|
+
execute: (args, ctx) => execTool("hive_session_start", args, ctx),
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
const hive_session_end = tool({
|
|
876
|
+
description: `End current session with handoff notes for next session.
|
|
877
|
+
|
|
878
|
+
Save context for the next agent/session to pick up where you left off.
|
|
879
|
+
Include: what was done, what's next, any blockers or gotchas.
|
|
880
|
+
|
|
881
|
+
Credit: Chainlink session handoff pattern from https://github.com/dollspace-gay/chainlink`,
|
|
882
|
+
args: {
|
|
883
|
+
handoff_notes: tool.schema
|
|
884
|
+
.string()
|
|
885
|
+
.optional()
|
|
886
|
+
.describe("Notes for next session (e.g., 'Completed X. Next: do Y. Watch out for Z.')"),
|
|
887
|
+
},
|
|
888
|
+
execute: (args, ctx) => execTool("hive_session_end", args, ctx),
|
|
889
|
+
});
|
|
890
|
+
|
|
672
891
|
// =============================================================================
|
|
673
892
|
// Swarm Mail Tools (Embedded)
|
|
674
893
|
// =============================================================================
|
|
@@ -1184,6 +1403,30 @@ const swarm_review_feedback = tool({
|
|
|
1184
1403
|
execute: (args, ctx) => execTool("swarm_review_feedback", args, ctx),
|
|
1185
1404
|
});
|
|
1186
1405
|
|
|
1406
|
+
// =============================================================================
|
|
1407
|
+
// Adversarial Review Tools (VDD/Chainlink-inspired)
|
|
1408
|
+
// =============================================================================
|
|
1409
|
+
|
|
1410
|
+
const swarm_adversarial_review = tool({
|
|
1411
|
+
description: `VDD-style adversarial code review using hostile, fresh-context agent.
|
|
1412
|
+
|
|
1413
|
+
Spawns Sarcasmotron - a hyper-critical reviewer with zero tolerance for slop.
|
|
1414
|
+
Fresh context per review prevents "relationship drift" (becoming lenient over time).
|
|
1415
|
+
|
|
1416
|
+
Returns structured critique with verdict:
|
|
1417
|
+
- APPROVED: Code is solid
|
|
1418
|
+
- NEEDS_CHANGES: Real issues found
|
|
1419
|
+
- HALLUCINATING: Adversary invented issues (code is excellent!)
|
|
1420
|
+
|
|
1421
|
+
Credit: VDD methodology from https://github.com/Vomikron/VDD
|
|
1422
|
+
Credit: Chainlink patterns from https://github.com/dollspace-gay/chainlink`,
|
|
1423
|
+
args: {
|
|
1424
|
+
diff: tool.schema.string().describe("Git diff of changes to review"),
|
|
1425
|
+
test_output: tool.schema.string().optional().describe("Test output (optional)"),
|
|
1426
|
+
},
|
|
1427
|
+
execute: (args, ctx) => execTool("swarm_adversarial_review", args, ctx),
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1187
1430
|
// =============================================================================
|
|
1188
1431
|
// Skills Tools
|
|
1189
1432
|
// =============================================================================
|
|
@@ -2579,6 +2822,9 @@ const SwarmPlugin: Plugin = async (
|
|
|
2579
2822
|
hive_cells,
|
|
2580
2823
|
hive_sync,
|
|
2581
2824
|
beads_link_thread,
|
|
2825
|
+
// Session Handoff (Chainlink)
|
|
2826
|
+
hive_session_start,
|
|
2827
|
+
hive_session_end,
|
|
2582
2828
|
// Swarm Mail (Embedded)
|
|
2583
2829
|
swarmmail_init,
|
|
2584
2830
|
swarmmail_send,
|
|
@@ -2617,6 +2863,8 @@ const SwarmPlugin: Plugin = async (
|
|
|
2617
2863
|
// Structured Review
|
|
2618
2864
|
swarm_review,
|
|
2619
2865
|
swarm_review_feedback,
|
|
2866
|
+
// Adversarial Review (VDD/Chainlink)
|
|
2867
|
+
swarm_adversarial_review,
|
|
2620
2868
|
// Skills
|
|
2621
2869
|
skills_list,
|
|
2622
2870
|
skills_read,
|
package/dist/index.js
CHANGED
|
@@ -22764,7 +22764,8 @@ var init_swarm_strategies = __esm(() => {
|
|
|
22764
22764
|
"remove",
|
|
22765
22765
|
"cleanup",
|
|
22766
22766
|
"lint",
|
|
22767
|
-
"format"
|
|
22767
|
+
"format",
|
|
22768
|
+
"move"
|
|
22768
22769
|
],
|
|
22769
22770
|
guidelines: [
|
|
22770
22771
|
"Group files by directory or type (e.g., all components, all tests)",
|
|
@@ -22830,7 +22831,8 @@ var init_swarm_strategies = __esm(() => {
|
|
|
22830
22831
|
"hotfix",
|
|
22831
22832
|
"patch",
|
|
22832
22833
|
"audit",
|
|
22833
|
-
"review"
|
|
22834
|
+
"review",
|
|
22835
|
+
"cve"
|
|
22834
22836
|
],
|
|
22835
22837
|
guidelines: [
|
|
22836
22838
|
"Write tests FIRST to capture expected behavior",
|
|
@@ -45539,6 +45541,7 @@ __export(exports_swarm_insights, {
|
|
|
45539
45541
|
getFileInsights: () => getFileInsights,
|
|
45540
45542
|
getFileGotchas: () => getFileGotchas,
|
|
45541
45543
|
getFileFailureHistory: () => getFileFailureHistory,
|
|
45544
|
+
getCompactionAnalytics: () => getCompactionAnalytics,
|
|
45542
45545
|
getCachedInsights: () => getCachedInsights,
|
|
45543
45546
|
formatInsightsForPrompt: () => formatInsightsForPrompt,
|
|
45544
45547
|
formatFileHistoryWarnings: () => formatFileHistoryWarnings,
|
|
@@ -45933,6 +45936,90 @@ async function getViolationAnalytics(swarmMail, projectKey) {
|
|
|
45933
45936
|
violationRate
|
|
45934
45937
|
};
|
|
45935
45938
|
}
|
|
45939
|
+
async function getCompactionAnalytics(swarmMail) {
|
|
45940
|
+
const db = await swarmMail.getDatabase();
|
|
45941
|
+
const query = `
|
|
45942
|
+
SELECT data, timestamp
|
|
45943
|
+
FROM events
|
|
45944
|
+
WHERE type = 'coordinator_compaction'
|
|
45945
|
+
ORDER BY timestamp DESC
|
|
45946
|
+
`;
|
|
45947
|
+
const result = await db.query(query, []);
|
|
45948
|
+
if (!result.rows || result.rows.length === 0) {
|
|
45949
|
+
return {
|
|
45950
|
+
totalEvents: 0,
|
|
45951
|
+
byType: {
|
|
45952
|
+
prompt_generated: 0,
|
|
45953
|
+
detection_complete: 0,
|
|
45954
|
+
context_injected: 0,
|
|
45955
|
+
resumption_started: 0,
|
|
45956
|
+
tool_call_tracked: 0
|
|
45957
|
+
},
|
|
45958
|
+
avgPromptSize: 0,
|
|
45959
|
+
successRate: 0,
|
|
45960
|
+
recentPrompts: [],
|
|
45961
|
+
byConfidence: { high: 0, medium: 0, low: 0 }
|
|
45962
|
+
};
|
|
45963
|
+
}
|
|
45964
|
+
const byType = {};
|
|
45965
|
+
const byConfidence = { high: 0, medium: 0, low: 0 };
|
|
45966
|
+
const promptSizes = [];
|
|
45967
|
+
const recentPrompts = [];
|
|
45968
|
+
let totalEvents = 0;
|
|
45969
|
+
let successfulCompactions = 0;
|
|
45970
|
+
for (const row of result.rows) {
|
|
45971
|
+
try {
|
|
45972
|
+
const data = JSON.parse(row.data);
|
|
45973
|
+
if (data.event_type === "COMPACTION") {
|
|
45974
|
+
totalEvents++;
|
|
45975
|
+
const compactionType = data.compaction_type || "unknown";
|
|
45976
|
+
byType[compactionType] = (byType[compactionType] || 0) + 1;
|
|
45977
|
+
if (data.payload?.confidence) {
|
|
45978
|
+
const conf = data.payload.confidence.toLowerCase();
|
|
45979
|
+
if (conf in byConfidence) {
|
|
45980
|
+
byConfidence[conf]++;
|
|
45981
|
+
}
|
|
45982
|
+
}
|
|
45983
|
+
if (compactionType === "prompt_generated") {
|
|
45984
|
+
successfulCompactions++;
|
|
45985
|
+
if (data.payload?.prompt_length) {
|
|
45986
|
+
promptSizes.push(data.payload.prompt_length);
|
|
45987
|
+
}
|
|
45988
|
+
if (recentPrompts.length < 10) {
|
|
45989
|
+
const preview = {
|
|
45990
|
+
timestamp: new Date(row.timestamp).toISOString(),
|
|
45991
|
+
length: data.payload?.prompt_length || 0,
|
|
45992
|
+
confidence: data.payload?.confidence
|
|
45993
|
+
};
|
|
45994
|
+
if (data.payload?.full_prompt) {
|
|
45995
|
+
preview.preview = truncateText(data.payload.full_prompt, 200);
|
|
45996
|
+
}
|
|
45997
|
+
recentPrompts.push(preview);
|
|
45998
|
+
}
|
|
45999
|
+
}
|
|
46000
|
+
}
|
|
46001
|
+
} catch (e) {
|
|
46002
|
+
continue;
|
|
46003
|
+
}
|
|
46004
|
+
}
|
|
46005
|
+
const avgPromptSize = promptSizes.length > 0 ? promptSizes.reduce((sum2, size10) => sum2 + size10, 0) / promptSizes.length : 0;
|
|
46006
|
+
const successRate = totalEvents > 0 ? successfulCompactions / totalEvents * 100 : 0;
|
|
46007
|
+
return {
|
|
46008
|
+
totalEvents,
|
|
46009
|
+
byType: {
|
|
46010
|
+
prompt_generated: byType.prompt_generated || 0,
|
|
46011
|
+
detection_complete: byType.detection_complete || 0,
|
|
46012
|
+
context_injected: byType.context_injected || 0,
|
|
46013
|
+
resumption_started: byType.resumption_started || 0,
|
|
46014
|
+
tool_call_tracked: byType.tool_call_tracked || 0,
|
|
46015
|
+
...byType
|
|
46016
|
+
},
|
|
46017
|
+
avgPromptSize: Math.round(avgPromptSize),
|
|
46018
|
+
successRate: Math.round(successRate * 100) / 100,
|
|
46019
|
+
recentPrompts,
|
|
46020
|
+
byConfidence
|
|
46021
|
+
};
|
|
46022
|
+
}
|
|
45936
46023
|
function formatFileHistoryWarnings(histories) {
|
|
45937
46024
|
if (histories.length === 0) {
|
|
45938
46025
|
return "";
|
|
@@ -70751,11 +70838,11 @@ async function detectSwarm(getHiveAdapterFn, checkSwarmHealthFn, getHiveWorkingD
|
|
|
70751
70838
|
}, "captured epic state for context");
|
|
70752
70839
|
}
|
|
70753
70840
|
}
|
|
70754
|
-
const
|
|
70755
|
-
const recentCells = cells.filter((c) => c.updated_at >
|
|
70841
|
+
const thirtyMinutesAgo = Date.now() - 30 * 60 * 1000;
|
|
70842
|
+
const recentCells = cells.filter((c) => c.updated_at > thirtyMinutesAgo);
|
|
70756
70843
|
if (recentCells.length > 0) {
|
|
70757
70844
|
mediumConfidence = true;
|
|
70758
|
-
reasons.push(`${recentCells.length} cells updated in last
|
|
70845
|
+
reasons.push(`${recentCells.length} cells updated in last 30 minutes`);
|
|
70759
70846
|
}
|
|
70760
70847
|
if (cells.length > 0) {
|
|
70761
70848
|
lowConfidence = true;
|
|
@@ -70843,6 +70930,16 @@ function createCompactionHook(options2) {
|
|
|
70843
70930
|
recordPhaseStart(metrics, "DETECT" /* DETECT */);
|
|
70844
70931
|
const detection = await detectSwarm(customGetHiveAdapter || getHiveAdapter, customCheckSwarmHealth || checkSwarmHealth3, customGetHiveWorkingDirectory || getHiveWorkingDirectory, log3);
|
|
70845
70932
|
let effectiveConfidence = detection.confidence;
|
|
70933
|
+
if (scannedState.agentName && effectiveConfidence === "none") {
|
|
70934
|
+
effectiveConfidence = "medium";
|
|
70935
|
+
detection.reasons.push("coordinator initialized (swarmmail_init)");
|
|
70936
|
+
recordPatternExtracted(metrics, "coordinator_init", "swarmmail_init detected");
|
|
70937
|
+
}
|
|
70938
|
+
if (scannedState.epicId && effectiveConfidence === "none") {
|
|
70939
|
+
effectiveConfidence = "medium";
|
|
70940
|
+
detection.reasons.push("epic created (hive_create_epic)");
|
|
70941
|
+
recordPatternExtracted(metrics, "epic_created", "hive_create_epic detected");
|
|
70942
|
+
}
|
|
70846
70943
|
if (scannedState.epicId || scannedState.subtasks.size > 0) {
|
|
70847
70944
|
if (effectiveConfidence === "none" || effectiveConfidence === "low") {
|
|
70848
70945
|
effectiveConfidence = "medium";
|
|
@@ -70955,6 +71052,32 @@ function createCompactionHook(options2) {
|
|
|
70955
71052
|
recordPhaseStart(metrics, "COMPLETE" /* COMPLETE */);
|
|
70956
71053
|
const duration3 = Date.now() - startTime;
|
|
70957
71054
|
const summary5 = getMetricsSummary(metrics);
|
|
71055
|
+
const openSubtasksCount = detection.state?.subtasks.open || 0;
|
|
71056
|
+
const activeReservationsCount = detection.reasons.find((r) => r.includes("active file reservations")) ? parseInt(detection.reasons.find((r) => r.includes("active file reservations"))?.match(/\d+/)?.[0] || "0") : 0;
|
|
71057
|
+
const registeredAgentsCount = detection.reasons.find((r) => r.includes("registered agents")) ? parseInt(detection.reasons.find((r) => r.includes("registered agents"))?.match(/\d+/)?.[0] || "0") : 0;
|
|
71058
|
+
const compactionSignals = [];
|
|
71059
|
+
let compactionRecommended = false;
|
|
71060
|
+
if (openSubtasksCount >= 3) {
|
|
71061
|
+
compactionSignals.push(`${openSubtasksCount} open subtasks`);
|
|
71062
|
+
compactionRecommended = true;
|
|
71063
|
+
}
|
|
71064
|
+
if (activeReservationsCount >= 2) {
|
|
71065
|
+
compactionSignals.push(`${activeReservationsCount} active reservations`);
|
|
71066
|
+
compactionRecommended = true;
|
|
71067
|
+
}
|
|
71068
|
+
if (registeredAgentsCount >= 2) {
|
|
71069
|
+
compactionSignals.push(`${registeredAgentsCount} registered agents`);
|
|
71070
|
+
compactionRecommended = true;
|
|
71071
|
+
}
|
|
71072
|
+
if (compactionRecommended) {
|
|
71073
|
+
log3.info({
|
|
71074
|
+
compaction_recommended: true,
|
|
71075
|
+
reasons: compactionSignals,
|
|
71076
|
+
open_subtasks: openSubtasksCount,
|
|
71077
|
+
active_reservations: activeReservationsCount,
|
|
71078
|
+
registered_agents: registeredAgentsCount
|
|
71079
|
+
}, "compaction recommended");
|
|
71080
|
+
}
|
|
70958
71081
|
log3.info({
|
|
70959
71082
|
duration_ms: duration3,
|
|
70960
71083
|
success: true,
|
|
@@ -70969,7 +71092,8 @@ function createCompactionHook(options2) {
|
|
|
70969
71092
|
})),
|
|
70970
71093
|
patterns_extracted: summary5.patterns_extracted,
|
|
70971
71094
|
patterns_skipped: summary5.patterns_skipped,
|
|
70972
|
-
extraction_success_rate: summary5.extraction_success_rate
|
|
71095
|
+
extraction_success_rate: summary5.extraction_success_rate,
|
|
71096
|
+
compaction_signals: compactionSignals
|
|
70973
71097
|
}
|
|
70974
71098
|
}, "compaction complete");
|
|
70975
71099
|
recordPhaseComplete(metrics, "COMPLETE" /* COMPLETE */);
|