@stackmemoryai/stackmemory 1.6.2 → 1.8.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/dist/src/agents/core/agent-task-manager.js +3 -3
- package/dist/src/agents/verifiers/formatter-verifier.js +1 -1
- package/dist/src/agents/verifiers/llm-judge.js +2 -2
- package/dist/src/cli/commands/conductor-traces.js +314 -0
- package/dist/src/cli/commands/context-rehydrate.js +2 -2
- package/dist/src/cli/commands/log.js +1 -1
- package/dist/src/cli/commands/onboard.js +4 -4
- package/dist/src/cli/commands/orchestrate.js +328 -27
- package/dist/src/cli/commands/orchestrator.js +50 -3
- package/dist/src/cli/commands/search.js +3 -3
- package/dist/src/cli/commands/setup.js +1 -1
- package/dist/src/cli/commands/signup.js +1 -1
- package/dist/src/cli/commands/skills.js +2 -2
- package/dist/src/cli/commands/worktree.js +1 -1
- package/dist/src/core/config/config-manager.js +3 -1
- package/dist/src/core/config/types.js +7 -0
- package/dist/src/core/context/dual-stack-manager.js +2 -2
- package/dist/src/core/context/frame-database.js +38 -0
- package/dist/src/core/context/frame-enrichment.js +93 -0
- package/dist/src/core/context/frame-handoff-manager.js +1 -1
- package/dist/src/core/context/frame-manager.js +4 -4
- package/dist/src/core/context/rehydration.js +6 -6
- package/dist/src/core/database/sqlite-adapter.js +136 -15
- package/dist/src/core/digest/frame-digest-integration.js +1 -1
- package/dist/src/core/digest/hybrid-digest-generator.js +1 -1
- package/dist/src/core/digest/hybrid-digest.js +3 -3
- package/dist/src/core/errors/recovery.js +1 -1
- package/dist/src/core/extensions/loader.js +10 -10
- package/dist/src/core/extensions/plugin-system.js +1 -1
- package/dist/src/core/monitoring/logger.js +2 -2
- package/dist/src/core/monitoring/metrics.js +2 -2
- package/dist/src/core/monitoring/progress-tracker.js +2 -2
- package/dist/src/core/performance/lazy-context-loader.js +1 -1
- package/dist/src/core/performance/performance-benchmark.js +8 -8
- package/dist/src/core/performance/performance-profiler.js +2 -2
- package/dist/src/core/performance/streaming-jsonl-parser.js +1 -1
- package/dist/src/core/projects/project-isolation.js +1 -1
- package/dist/src/core/projects/project-manager.js +5 -5
- package/dist/src/core/session/clear-survival.js +3 -3
- package/dist/src/core/session/handoff-generator.js +1 -1
- package/dist/src/core/session/session-manager.js +4 -4
- package/dist/src/core/skills/skill-storage.js +2 -2
- package/dist/src/core/storage/remote-storage.js +2 -2
- package/dist/src/core/trace/cli-trace-wrapper.js +3 -3
- package/dist/src/core/trace/debug-trace.js +2 -2
- package/dist/src/core/trace/trace-detector.js +1 -1
- package/dist/src/core/worktree/worktree-manager.js +2 -2
- package/dist/src/features/analytics/api/analytics-api.js +1 -1
- package/dist/src/features/analytics/core/analytics-service.js +2 -2
- package/dist/src/features/analytics/queries/metrics-queries.js +1 -1
- package/dist/src/features/web/server/index.js +8 -8
- package/dist/src/integrations/anthropic/client.js +4 -4
- package/dist/src/integrations/claude-code/post-task-hooks.js +3 -3
- package/dist/src/integrations/claude-code/subagent-client.js +2 -2
- package/dist/src/integrations/diffmem/client.js +1 -1
- package/dist/src/integrations/linear/client.js +1 -1
- package/dist/src/integrations/linear/migration.js +1 -1
- package/dist/src/integrations/linear/oauth-server.js +2 -2
- package/dist/src/integrations/linear/sync.js +1 -1
- package/dist/src/integrations/linear/webhook-handler.js +2 -2
- package/dist/src/integrations/linear/webhook-server.js +2 -2
- package/dist/src/integrations/linear/webhook.js +2 -2
- package/dist/src/integrations/mcp/handlers/discovery-handlers.js +3 -3
- package/dist/src/integrations/mcp/handlers/linear-handlers.js +1 -1
- package/dist/src/integrations/mcp/handlers/skill-handlers.js +2 -2
- package/dist/src/integrations/mcp/handlers/task-handlers.js +2 -2
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +1 -1
- package/dist/src/integrations/mcp/refactored-server.js +2 -2
- package/dist/src/integrations/mcp/remote-server.js +1 -1
- package/dist/src/integrations/mcp/server.js +4 -4
- package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +13 -13
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +1 -1
- package/dist/src/integrations/ralph/learning/pattern-learner.js +3 -3
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +1 -1
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +2 -2
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +19 -19
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +5 -5
- package/dist/src/integrations/ralph/performance/performance-optimizer.js +1 -1
- package/dist/src/integrations/ralph/recovery/crash-recovery.js +1 -1
- package/dist/src/integrations/ralph/state/state-reconciler.js +1 -1
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +4 -4
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +8 -8
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +3 -3
- package/dist/src/orchestrators/multimodal/harness.js +3 -3
- package/dist/src/servers/production/auth-middleware.js +2 -2
- package/package.json +1 -1
|
@@ -22,6 +22,16 @@ import {
|
|
|
22
22
|
getAgentStatusDir,
|
|
23
23
|
getOutcomesLogPath
|
|
24
24
|
} from "./orchestrator.js";
|
|
25
|
+
import {
|
|
26
|
+
openTracesDb,
|
|
27
|
+
listSessions,
|
|
28
|
+
getSessionTurns,
|
|
29
|
+
getPhaseBreakdown,
|
|
30
|
+
getToolFrequencies,
|
|
31
|
+
getFailureTurns,
|
|
32
|
+
getTraceStats,
|
|
33
|
+
classifyErrorText
|
|
34
|
+
} from "./conductor-traces.js";
|
|
25
35
|
function getGlobalStorePath() {
|
|
26
36
|
const dir = join(homedir(), ".stackmemory", "conductor");
|
|
27
37
|
if (!existsSync(dir)) {
|
|
@@ -70,6 +80,17 @@ function fmtTokens(n) {
|
|
|
70
80
|
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
71
81
|
return String(n);
|
|
72
82
|
}
|
|
83
|
+
function formatTokens(n) {
|
|
84
|
+
return fmtTokens(n) + " tok";
|
|
85
|
+
}
|
|
86
|
+
function formatDuration(ms) {
|
|
87
|
+
const seconds = Math.floor(ms / 1e3);
|
|
88
|
+
if (seconds < 60) return `${seconds}s`;
|
|
89
|
+
const minutes = Math.floor(seconds / 60);
|
|
90
|
+
if (minutes < 60) return `${minutes}m ${seconds % 60}s`;
|
|
91
|
+
const hours = Math.floor(minutes / 60);
|
|
92
|
+
return `${hours}h ${minutes % 60}m`;
|
|
93
|
+
}
|
|
73
94
|
function budgetBar(pct, width = 30) {
|
|
74
95
|
const filled = Math.min(Math.round(pct / 100 * width), width);
|
|
75
96
|
const empty = width - filled;
|
|
@@ -354,6 +375,44 @@ function predictDifficulty(labels, description, priority, outcomes) {
|
|
|
354
375
|
}
|
|
355
376
|
return { difficulty, confidence, reasons };
|
|
356
377
|
}
|
|
378
|
+
function analyzeErrorsFromTraces(failedIssues) {
|
|
379
|
+
const patterns = {};
|
|
380
|
+
const evidence = [];
|
|
381
|
+
let db;
|
|
382
|
+
try {
|
|
383
|
+
db = openTracesDb();
|
|
384
|
+
} catch {
|
|
385
|
+
return { patterns, evidence };
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
for (const issueId of failedIssues) {
|
|
389
|
+
const turns = getFailureTurns(issueId, 3, db);
|
|
390
|
+
if (turns.length === 0) continue;
|
|
391
|
+
for (const turn of turns) {
|
|
392
|
+
const preview = turn.message_preview || "";
|
|
393
|
+
const tools = turn.tool_names ? JSON.parse(turn.tool_names) : [];
|
|
394
|
+
const pattern = classifyErrorText(preview);
|
|
395
|
+
if (pattern) {
|
|
396
|
+
patterns[pattern] = (patterns[pattern] || 0) + 1;
|
|
397
|
+
}
|
|
398
|
+
if (evidence.length < 15) {
|
|
399
|
+
evidence.push({
|
|
400
|
+
issue: issueId,
|
|
401
|
+
phase: turn.phase || "unknown",
|
|
402
|
+
tools,
|
|
403
|
+
preview: preview.slice(0, 300)
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
} finally {
|
|
409
|
+
db.close();
|
|
410
|
+
}
|
|
411
|
+
if (Object.keys(patterns).length === 0 && failedIssues.length > 0) {
|
|
412
|
+
patterns["unknown"] = failedIssues.length;
|
|
413
|
+
}
|
|
414
|
+
return { patterns, evidence };
|
|
415
|
+
}
|
|
357
416
|
function loadOutcomes() {
|
|
358
417
|
const logPath = getOutcomesLogPath();
|
|
359
418
|
if (!existsSync(logPath)) return [];
|
|
@@ -465,7 +524,8 @@ async function evolvePromptTemplate(input) {
|
|
|
465
524
|
errorPatterns,
|
|
466
525
|
recs,
|
|
467
526
|
outcomes,
|
|
468
|
-
dryRun
|
|
527
|
+
dryRun,
|
|
528
|
+
traceEvidence
|
|
469
529
|
} = input;
|
|
470
530
|
let currentTemplate;
|
|
471
531
|
if (existsSync(templatePath)) {
|
|
@@ -478,11 +538,24 @@ async function evolvePromptTemplate(input) {
|
|
|
478
538
|
}
|
|
479
539
|
const failPhaseSummary = Object.entries(failPhases).sort((a, b) => b[1] - a[1]).map(([phase, count]) => ` - ${phase}: ${count} failures`).join("\n");
|
|
480
540
|
const errorSummary = Object.entries(errorPatterns).sort((a, b) => b[1] - a[1]).map(([pattern, count]) => ` - ${pattern}: ${count} occurrences`).join("\n");
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
541
|
+
let failureEvidenceSection;
|
|
542
|
+
if (traceEvidence && traceEvidence.length > 0) {
|
|
543
|
+
const evidenceLines = traceEvidence.slice(0, 10).map((ev) => {
|
|
544
|
+
const tools = ev.tools.length > 0 ? ev.tools.join(", ") : "none";
|
|
545
|
+
return ` [${ev.issue}, phase: ${ev.phase}, tools: ${tools}]
|
|
546
|
+
${ev.preview.slice(0, 200)}`;
|
|
547
|
+
});
|
|
548
|
+
failureEvidenceSection = `ACTUAL FAILURE EVIDENCE (from conversation traces):
|
|
549
|
+
${evidenceLines.join("\n\n")}`;
|
|
550
|
+
} else {
|
|
551
|
+
const failedOutcomes = outcomes.filter((o) => o.outcome === "failure" && o.errorTail).slice(-5);
|
|
552
|
+
const errorTails = failedOutcomes.map(
|
|
553
|
+
(o) => ` [${o.issue} attempt ${o.attempt}, phase: ${o.phase}]
|
|
484
554
|
${o.errorTail}`
|
|
485
|
-
|
|
555
|
+
).join("\n\n");
|
|
556
|
+
failureEvidenceSection = `SAMPLE ERROR TAILS FROM RECENT FAILURES:
|
|
557
|
+
${errorTails || " (none)"}`;
|
|
558
|
+
}
|
|
486
559
|
const mutationPrompt = `You are optimizing a prompt template for autonomous AI coding agents managed by a conductor system.
|
|
487
560
|
|
|
488
561
|
CURRENT PROMPT TEMPLATE:
|
|
@@ -500,8 +573,7 @@ ${failPhaseSummary || " (none)"}
|
|
|
500
573
|
ERROR PATTERNS:
|
|
501
574
|
${errorSummary || " (none)"}
|
|
502
575
|
|
|
503
|
-
|
|
504
|
-
${errorTails || " (none)"}
|
|
576
|
+
${failureEvidenceSection}
|
|
505
577
|
|
|
506
578
|
RECOMMENDATIONS FROM ANALYSIS:
|
|
507
579
|
${recs.map((r) => `- ${r}`).join("\n")}
|
|
@@ -991,6 +1063,9 @@ function createConductorCommands() {
|
|
|
991
1063
|
"--dry-run",
|
|
992
1064
|
"Show evolved template without writing (use with --evolve)",
|
|
993
1065
|
false
|
|
1066
|
+
).option(
|
|
1067
|
+
"--no-evidence",
|
|
1068
|
+
"Disable trace-based evidence display (on by default when traces.db exists)"
|
|
994
1069
|
).option(
|
|
995
1070
|
"--predict",
|
|
996
1071
|
"Show difficulty predictions alongside actual outcomes",
|
|
@@ -1039,22 +1114,23 @@ function createConductorCommands() {
|
|
|
1039
1114
|
const retrySuccessRate = retries.length > 0 ? Math.round(
|
|
1040
1115
|
retries.filter((o) => o.outcome === "success").length / retries.length * 100
|
|
1041
1116
|
) : 0;
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1117
|
+
const failedIssueIds = [
|
|
1118
|
+
...new Set(
|
|
1119
|
+
outcomes.filter((o) => o.outcome === "failure").map((o) => o.issue)
|
|
1120
|
+
)
|
|
1121
|
+
];
|
|
1122
|
+
const traceAnalysis = analyzeErrorsFromTraces(failedIssueIds);
|
|
1123
|
+
let errorPatterns = traceAnalysis.patterns;
|
|
1124
|
+
let traceEvidence = traceAnalysis.evidence;
|
|
1125
|
+
if (Object.keys(errorPatterns).length === 0 || Object.keys(errorPatterns).length === 1 && errorPatterns["unknown"]) {
|
|
1126
|
+
errorPatterns = {};
|
|
1127
|
+
traceEvidence = [];
|
|
1128
|
+
for (const o of outcomes.filter(
|
|
1129
|
+
(o2) => o2.outcome === "failure" && o2.errorTail
|
|
1130
|
+
)) {
|
|
1131
|
+
const pattern = classifyErrorText(o.errorTail) ?? "unknown";
|
|
1132
|
+
errorPatterns[pattern] = (errorPatterns[pattern] || 0) + 1;
|
|
1133
|
+
}
|
|
1058
1134
|
}
|
|
1059
1135
|
if (options.export) {
|
|
1060
1136
|
const analysis = {
|
|
@@ -1101,16 +1177,42 @@ function createConductorCommands() {
|
|
|
1101
1177
|
}
|
|
1102
1178
|
}
|
|
1103
1179
|
if (Object.keys(errorPatterns).length > 0) {
|
|
1104
|
-
|
|
1105
|
-
|
|
1180
|
+
const sourceLabel = traceEvidence.length > 0 ? "(from traces)" : "(from errorTail)";
|
|
1181
|
+
console.log(
|
|
1182
|
+
`
|
|
1183
|
+
${c.b}Error Patterns${c.r} ${c.d}${sourceLabel}${c.r}`
|
|
1184
|
+
);
|
|
1106
1185
|
const sorted = Object.entries(errorPatterns).sort(
|
|
1107
1186
|
(a, b) => b[1] - a[1]
|
|
1108
1187
|
);
|
|
1109
1188
|
for (const [pattern, count] of sorted) {
|
|
1110
1189
|
console.log(
|
|
1111
|
-
` ${c.red}\u25CF${c.r} ${pattern.padEnd(
|
|
1190
|
+
` ${c.red}\u25CF${c.r} ${pattern.padEnd(20)} ${c.white}${count}${c.r}`
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
if (options.evidence && traceEvidence.length > 0) {
|
|
1195
|
+
console.log(
|
|
1196
|
+
`
|
|
1197
|
+
${c.b}Failure Evidence${c.r} ${c.d}(from traces)${c.r}`
|
|
1198
|
+
);
|
|
1199
|
+
for (const ev of traceEvidence.slice(0, 15)) {
|
|
1200
|
+
const tools = ev.tools.length > 0 ? ev.tools.join(", ") : "-";
|
|
1201
|
+
console.log(
|
|
1202
|
+
` ${c.cyan}${ev.issue}${c.r} [${ev.phase}] tools: ${tools}`
|
|
1112
1203
|
);
|
|
1204
|
+
if (ev.preview) {
|
|
1205
|
+
const lines = ev.preview.split("\n").slice(0, 3);
|
|
1206
|
+
for (const line of lines) {
|
|
1207
|
+
console.log(` ${c.d}${line.slice(0, 100)}${c.r}`);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1113
1210
|
}
|
|
1211
|
+
} else if (options.evidence && traceEvidence.length === 0) {
|
|
1212
|
+
console.log(
|
|
1213
|
+
`
|
|
1214
|
+
${c.d}No trace data available. Run conductor with trace logging enabled to collect evidence.${c.r}`
|
|
1215
|
+
);
|
|
1114
1216
|
}
|
|
1115
1217
|
console.log(`
|
|
1116
1218
|
${c.b}Recommendations${c.r}`);
|
|
@@ -1186,7 +1288,8 @@ function createConductorCommands() {
|
|
|
1186
1288
|
errorPatterns,
|
|
1187
1289
|
recs,
|
|
1188
1290
|
outcomes,
|
|
1189
|
-
dryRun: options.dryRun
|
|
1291
|
+
dryRun: options.dryRun,
|
|
1292
|
+
traceEvidence
|
|
1190
1293
|
});
|
|
1191
1294
|
}
|
|
1192
1295
|
if (options.predict) {
|
|
@@ -1704,6 +1807,204 @@ function createConductorCommands() {
|
|
|
1704
1807
|
process.on("SIGTERM", cleanup);
|
|
1705
1808
|
});
|
|
1706
1809
|
});
|
|
1810
|
+
cmd.command("traces <issue-id>").description("Show conversation traces for an agent run").option("--session <id>", "Show specific session").option("--json", "Output as JSON", false).option("--tools", "Show tool frequency breakdown", false).option("--failures", "Show failure-turn details", false).option("-n, --tail <count>", "Failure tail turns", "5").action(
|
|
1811
|
+
(issueId, options) => {
|
|
1812
|
+
const db = openTracesDb();
|
|
1813
|
+
try {
|
|
1814
|
+
if (options.tools) {
|
|
1815
|
+
const freq = getToolFrequencies(issueId, db);
|
|
1816
|
+
if (freq.length === 0) {
|
|
1817
|
+
console.log(`No traces found for ${issueId}`);
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
console.log(
|
|
1821
|
+
`
|
|
1822
|
+
${c.b}Tool Frequency${c.r} \u2014 ${c.cyan}${issueId}${c.r}
|
|
1823
|
+
`
|
|
1824
|
+
);
|
|
1825
|
+
for (const { tool_name, count } of freq.slice(0, 20)) {
|
|
1826
|
+
const bar = "\u2501".repeat(Math.min(count, 40));
|
|
1827
|
+
console.log(
|
|
1828
|
+
` ${c.d}${tool_name.padEnd(20)}${c.r} ${bar} ${count}`
|
|
1829
|
+
);
|
|
1830
|
+
}
|
|
1831
|
+
console.log("");
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1834
|
+
if (options.failures) {
|
|
1835
|
+
const turns = getFailureTurns(
|
|
1836
|
+
issueId,
|
|
1837
|
+
parseInt(options.tail, 10),
|
|
1838
|
+
db
|
|
1839
|
+
);
|
|
1840
|
+
if (turns.length === 0) {
|
|
1841
|
+
console.log(`No traces found for ${issueId}`);
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
console.log(
|
|
1845
|
+
`
|
|
1846
|
+
${c.b}Failure Turns${c.r} \u2014 ${c.cyan}${issueId}${c.r}
|
|
1847
|
+
`
|
|
1848
|
+
);
|
|
1849
|
+
for (const t of turns) {
|
|
1850
|
+
const tools = t.tool_names ? JSON.parse(t.tool_names).join(", ") : "-";
|
|
1851
|
+
const ts = new Date(t.timestamp).toISOString().slice(11, 19);
|
|
1852
|
+
console.log(
|
|
1853
|
+
` ${c.d}[${ts}]${c.r} turn ${t.turn_number} | phase: ${t.phase || "-"} | tools: ${tools}`
|
|
1854
|
+
);
|
|
1855
|
+
if (t.message_preview) {
|
|
1856
|
+
const preview = t.message_preview.slice(0, 200);
|
|
1857
|
+
console.log(` ${c.d}${preview}${c.r}`);
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
console.log("");
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
if (options.session) {
|
|
1864
|
+
const turns = getSessionTurns(options.session, db);
|
|
1865
|
+
if (turns.length === 0) {
|
|
1866
|
+
console.log(`No turns found for session ${options.session}`);
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
if (options.json) {
|
|
1870
|
+
console.log(JSON.stringify(turns, null, 2));
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
const phases = getPhaseBreakdown(options.session, db);
|
|
1874
|
+
console.log(
|
|
1875
|
+
`
|
|
1876
|
+
${c.b}Session${c.r} ${c.cyan}${options.session}${c.r}`
|
|
1877
|
+
);
|
|
1878
|
+
console.log(` ${turns.length} turns
|
|
1879
|
+
`);
|
|
1880
|
+
if (phases.length > 0) {
|
|
1881
|
+
console.log(` ${c.b}Phase Breakdown${c.r}`);
|
|
1882
|
+
for (const p of phases) {
|
|
1883
|
+
console.log(
|
|
1884
|
+
` ${(p.phase || "unknown").padEnd(14)} ${String(p.turns).padStart(3)} turns ${String(p.tool_calls).padStart(4)} tools ${formatTokens(p.input_tokens + p.output_tokens)}`
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
console.log("");
|
|
1888
|
+
}
|
|
1889
|
+
console.log(` ${c.b}Turn Log${c.r}`);
|
|
1890
|
+
for (const t of turns) {
|
|
1891
|
+
const tools = t.tool_names ? JSON.parse(t.tool_names).join(", ") : "-";
|
|
1892
|
+
const ts = new Date(t.timestamp).toISOString().slice(11, 19);
|
|
1893
|
+
const tokens = t.input_tokens + t.output_tokens;
|
|
1894
|
+
console.log(
|
|
1895
|
+
` ${c.d}${String(t.turn_number).padStart(3)}${c.r} [${ts}] ${(t.phase || "-").padEnd(12)} tools: ${tools.padEnd(30)} ${formatTokens(tokens)}`
|
|
1896
|
+
);
|
|
1897
|
+
}
|
|
1898
|
+
console.log("");
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
const sessions = listSessions(issueId, db);
|
|
1902
|
+
if (sessions.length === 0) {
|
|
1903
|
+
console.log(`No traces found for ${issueId}`);
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
if (options.json) {
|
|
1907
|
+
console.log(JSON.stringify(sessions, null, 2));
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
console.log(
|
|
1911
|
+
`
|
|
1912
|
+
${c.b}Trace Sessions${c.r} \u2014 ${c.cyan}${issueId}${c.r}
|
|
1913
|
+
`
|
|
1914
|
+
);
|
|
1915
|
+
for (const s of sessions) {
|
|
1916
|
+
const dur = s.duration_ms > 0 ? formatDuration(s.duration_ms) : "-";
|
|
1917
|
+
const tokens = formatTokens(
|
|
1918
|
+
s.total_input_tokens + s.total_output_tokens
|
|
1919
|
+
);
|
|
1920
|
+
const time = new Date(s.started_at).toISOString().slice(0, 19);
|
|
1921
|
+
console.log(
|
|
1922
|
+
` ${c.d}attempt ${s.attempt}${c.r} ${s.total_turns} turns ${s.total_tool_calls} tools ${tokens} ${dur} ${c.d}${time}${c.r}`
|
|
1923
|
+
);
|
|
1924
|
+
console.log(` ${c.d}session: ${s.session_id}${c.r}`);
|
|
1925
|
+
console.log(` phases: ${s.phases.join(" \u2192 ")}`);
|
|
1926
|
+
console.log("");
|
|
1927
|
+
}
|
|
1928
|
+
} finally {
|
|
1929
|
+
db.close();
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
);
|
|
1933
|
+
cmd.command("replay <session-id>").description("Replay a full agent conversation from traces").option("-n, --turns <count>", "Show only last N turns").option("--json", "Output raw event JSON", false).action((sessionId, options) => {
|
|
1934
|
+
const db = openTracesDb();
|
|
1935
|
+
try {
|
|
1936
|
+
let turns = getSessionTurns(sessionId, db);
|
|
1937
|
+
if (turns.length === 0) {
|
|
1938
|
+
console.error(`No traces found for session ${sessionId}`);
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
if (options.turns) {
|
|
1942
|
+
const n = parseInt(options.turns, 10);
|
|
1943
|
+
turns = turns.slice(-n);
|
|
1944
|
+
}
|
|
1945
|
+
if (options.json) {
|
|
1946
|
+
for (const t of turns) {
|
|
1947
|
+
console.log(t.event_json);
|
|
1948
|
+
}
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1951
|
+
console.log(
|
|
1952
|
+
`
|
|
1953
|
+
${c.b}Replay${c.r} ${c.cyan}${sessionId}${c.r} \u2014 ${turns.length} turns
|
|
1954
|
+
`
|
|
1955
|
+
);
|
|
1956
|
+
for (const t of turns) {
|
|
1957
|
+
const ts = new Date(t.timestamp).toISOString().slice(11, 19);
|
|
1958
|
+
const tokens = t.input_tokens + t.output_tokens;
|
|
1959
|
+
console.log(
|
|
1960
|
+
`${c.purple}\u2500\u2500 Turn ${t.turn_number} \u2500\u2500${c.r} [${ts}] ${t.phase || ""} ${tokens > 0 ? formatTokens(tokens) : ""}`
|
|
1961
|
+
);
|
|
1962
|
+
if (t.tool_names) {
|
|
1963
|
+
const tools = JSON.parse(t.tool_names);
|
|
1964
|
+
for (const tool of tools) {
|
|
1965
|
+
console.log(` ${c.cyan}\u25B8 ${tool}${c.r}`);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
if (t.message_preview) {
|
|
1969
|
+
const lines = t.message_preview.split("\n");
|
|
1970
|
+
for (const line of lines.slice(0, 10)) {
|
|
1971
|
+
console.log(` ${c.d}${line}${c.r}`);
|
|
1972
|
+
}
|
|
1973
|
+
if (lines.length > 10) {
|
|
1974
|
+
console.log(
|
|
1975
|
+
` ${c.d}... (${lines.length - 10} more lines)${c.r}`
|
|
1976
|
+
);
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
console.log("");
|
|
1980
|
+
}
|
|
1981
|
+
} finally {
|
|
1982
|
+
db.close();
|
|
1983
|
+
}
|
|
1984
|
+
});
|
|
1985
|
+
cmd.command("trace-stats").description("Show aggregate trace statistics").option("--json", "Output as JSON", false).action((options) => {
|
|
1986
|
+
const stats = getTraceStats();
|
|
1987
|
+
if (options.json) {
|
|
1988
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
console.log(`
|
|
1992
|
+
${c.b}Conductor Trace Stats${c.r}
|
|
1993
|
+
`);
|
|
1994
|
+
console.log(` Sessions: ${stats.total_sessions}`);
|
|
1995
|
+
console.log(` Turns: ${stats.total_turns}`);
|
|
1996
|
+
console.log(` Issues: ${stats.issues_traced}`);
|
|
1997
|
+
console.log(
|
|
1998
|
+
` Tokens: ${formatTokens((stats.total_input_tokens || 0) + (stats.total_output_tokens || 0))}`
|
|
1999
|
+
);
|
|
2000
|
+
console.log(
|
|
2001
|
+
` Input: ${formatTokens(stats.total_input_tokens || 0)}`
|
|
2002
|
+
);
|
|
2003
|
+
console.log(
|
|
2004
|
+
` Output: ${formatTokens(stats.total_output_tokens || 0)}`
|
|
2005
|
+
);
|
|
2006
|
+
console.log("");
|
|
2007
|
+
});
|
|
1707
2008
|
return cmd;
|
|
1708
2009
|
}
|
|
1709
2010
|
export {
|
|
@@ -30,6 +30,10 @@ import {
|
|
|
30
30
|
} from "../../core/worktree/preflight.js";
|
|
31
31
|
import { ContextCapture } from "../../core/worktree/capture.js";
|
|
32
32
|
import { extractKeywords } from "../../core/utils/text.js";
|
|
33
|
+
import {
|
|
34
|
+
TraceCollector,
|
|
35
|
+
stringifyEventTruncated
|
|
36
|
+
} from "./conductor-traces.js";
|
|
33
37
|
function getOutcomesLogPath() {
|
|
34
38
|
return join(homedir(), ".stackmemory", "conductor", "outcomes.jsonl");
|
|
35
39
|
}
|
|
@@ -1272,6 +1276,17 @@ class Conductor {
|
|
|
1272
1276
|
run.logStream = logStream;
|
|
1273
1277
|
const tee = new TeeTransform(logStream);
|
|
1274
1278
|
proc.stdout.pipe(tee);
|
|
1279
|
+
let traceCollector;
|
|
1280
|
+
try {
|
|
1281
|
+
traceCollector = new TraceCollector({
|
|
1282
|
+
issueId: issue.identifier,
|
|
1283
|
+
attempt: run.attempt
|
|
1284
|
+
});
|
|
1285
|
+
} catch {
|
|
1286
|
+
logger.warn("Failed to initialize trace collector", {
|
|
1287
|
+
identifier: issue.identifier
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1275
1290
|
this.writeAgentStatus(issue.identifier, run);
|
|
1276
1291
|
let stderr = "";
|
|
1277
1292
|
let lastResultText = "";
|
|
@@ -1303,23 +1318,53 @@ class Conductor {
|
|
|
1303
1318
|
this.trackUsage(issue.identifier, msgUsage);
|
|
1304
1319
|
}
|
|
1305
1320
|
const content = message?.content || [];
|
|
1321
|
+
const turnToolNames = [];
|
|
1322
|
+
let turnFilesModified = 0;
|
|
1323
|
+
const turnTextParts = [];
|
|
1306
1324
|
for (const block of content) {
|
|
1307
1325
|
if (block.type === "tool_use") {
|
|
1308
1326
|
run.toolCalls++;
|
|
1309
|
-
const
|
|
1327
|
+
const name = block.name || "";
|
|
1328
|
+
turnToolNames.push(name);
|
|
1329
|
+
const toolLower = name.toLowerCase();
|
|
1310
1330
|
if (toolLower.includes("edit") || toolLower.includes("write")) {
|
|
1311
1331
|
run.filesModified++;
|
|
1332
|
+
turnFilesModified++;
|
|
1312
1333
|
}
|
|
1313
1334
|
}
|
|
1314
1335
|
if (block.type === "text" && block.text) {
|
|
1315
|
-
|
|
1316
|
-
|
|
1336
|
+
const text = block.text;
|
|
1337
|
+
run.tokensUsed += Math.ceil(text.length / 4);
|
|
1338
|
+
turnTextParts.push(text);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
try {
|
|
1342
|
+
if (traceCollector) {
|
|
1343
|
+
const turnData = {
|
|
1344
|
+
toolNames: turnToolNames,
|
|
1345
|
+
toolCount: turnToolNames.length,
|
|
1346
|
+
filesModified: turnFilesModified,
|
|
1347
|
+
textPreview: turnTextParts.length > 0 ? turnTextParts.join("\n").slice(0, 500) : null,
|
|
1348
|
+
inputTokens: msgUsage?.input_tokens ?? 0,
|
|
1349
|
+
outputTokens: msgUsage?.output_tokens ?? 0,
|
|
1350
|
+
cacheCreationTokens: msgUsage?.cache_creation_input_tokens ?? 0,
|
|
1351
|
+
cacheReadTokens: msgUsage?.cache_read_input_tokens ?? 0
|
|
1352
|
+
};
|
|
1353
|
+
traceCollector.recordTurn(
|
|
1354
|
+
turnData,
|
|
1355
|
+
phase,
|
|
1356
|
+
stringifyEventTruncated(event)
|
|
1317
1357
|
);
|
|
1318
1358
|
}
|
|
1359
|
+
} catch {
|
|
1319
1360
|
}
|
|
1320
1361
|
}
|
|
1321
1362
|
if (event.type === "result" && event.result) {
|
|
1322
1363
|
lastResultText = typeof event.result === "string" ? event.result : JSON.stringify(event.result);
|
|
1364
|
+
try {
|
|
1365
|
+
traceCollector?.recordResult(event);
|
|
1366
|
+
} catch {
|
|
1367
|
+
}
|
|
1323
1368
|
}
|
|
1324
1369
|
if (run.toolCalls % 5 === 0 || phase) {
|
|
1325
1370
|
this.writeAgentStatus(issue.identifier, run);
|
|
@@ -1337,10 +1382,12 @@ class Conductor {
|
|
|
1337
1382
|
});
|
|
1338
1383
|
proc.on("error", (err) => {
|
|
1339
1384
|
clearTimeout(timer);
|
|
1385
|
+
traceCollector?.close();
|
|
1340
1386
|
reject(new Error(`Failed to spawn claude: ${err.message}`));
|
|
1341
1387
|
});
|
|
1342
1388
|
proc.on("close", (code) => {
|
|
1343
1389
|
clearTimeout(timer);
|
|
1390
|
+
traceCollector?.close();
|
|
1344
1391
|
run.process = null;
|
|
1345
1392
|
if (run.logStream && !run.logStream.destroyed) {
|
|
1346
1393
|
run.logStream.end();
|
|
@@ -88,7 +88,7 @@ function createSearchCommand() {
|
|
|
88
88
|
console.log("");
|
|
89
89
|
totalResults += tasks.length;
|
|
90
90
|
}
|
|
91
|
-
} catch
|
|
91
|
+
} catch {
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
if (searchContext) {
|
|
@@ -123,7 +123,7 @@ function createSearchCommand() {
|
|
|
123
123
|
console.log("");
|
|
124
124
|
totalResults += contexts.length;
|
|
125
125
|
}
|
|
126
|
-
} catch
|
|
126
|
+
} catch {
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
if (searchContext) {
|
|
@@ -154,7 +154,7 @@ function createSearchCommand() {
|
|
|
154
154
|
console.log("");
|
|
155
155
|
totalResults += events.length;
|
|
156
156
|
}
|
|
157
|
-
} catch
|
|
157
|
+
} catch {
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
db.close();
|
|
@@ -759,7 +759,7 @@ function createSetupRemoteCommand() {
|
|
|
759
759
|
const serviceDir = platform === "darwin" ? join(home, "Library", "LaunchAgents") : join(home, ".config", "systemd", "user");
|
|
760
760
|
const serviceFile = platform === "darwin" ? join(serviceDir, `${serviceName}.plist`) : join(serviceDir, `${serviceName}.service`);
|
|
761
761
|
const logDir = join(home, ".stackmemory", "logs");
|
|
762
|
-
const
|
|
762
|
+
const _pidFile = join(home, ".stackmemory", "remote-mcp.pid");
|
|
763
763
|
if (options.status) {
|
|
764
764
|
console.log(chalk.cyan("\nRemote MCP Server Status\n"));
|
|
765
765
|
if (platform === "darwin") {
|
|
@@ -15,7 +15,7 @@ function registerSignupCommand(program) {
|
|
|
15
15
|
try {
|
|
16
16
|
await open(signupUrl);
|
|
17
17
|
console.log(chalk.green("\u2713 Opened: ") + chalk.cyan(signupUrl));
|
|
18
|
-
} catch
|
|
18
|
+
} catch {
|
|
19
19
|
console.log(chalk.yellow("Could not open browser automatically."));
|
|
20
20
|
console.log(chalk.gray("Please visit: ") + chalk.cyan(signupUrl));
|
|
21
21
|
}
|
|
@@ -46,7 +46,7 @@ function getVersion() {
|
|
|
46
46
|
_version = "0.0.0";
|
|
47
47
|
return _version;
|
|
48
48
|
}
|
|
49
|
-
function
|
|
49
|
+
function _getEnv(key, defaultValue) {
|
|
50
50
|
const value = process.env[key];
|
|
51
51
|
if (value === void 0) {
|
|
52
52
|
if (defaultValue !== void 0) return defaultValue;
|
|
@@ -58,7 +58,7 @@ function getEnv(key, defaultValue) {
|
|
|
58
58
|
}
|
|
59
59
|
return value;
|
|
60
60
|
}
|
|
61
|
-
function
|
|
61
|
+
function _getOptionalEnv(key) {
|
|
62
62
|
return process.env[key];
|
|
63
63
|
}
|
|
64
64
|
async function initializeSkillContext() {
|
|
@@ -7,7 +7,8 @@ import * as path from "path";
|
|
|
7
7
|
import * as yaml from "js-yaml";
|
|
8
8
|
import {
|
|
9
9
|
DEFAULT_CONFIG,
|
|
10
|
-
PRESET_PROFILES
|
|
10
|
+
PRESET_PROFILES,
|
|
11
|
+
DEFAULT_ENRICHMENT
|
|
11
12
|
} from "./types.js";
|
|
12
13
|
class ConfigManager {
|
|
13
14
|
static instance = null;
|
|
@@ -75,6 +76,7 @@ class ConfigManager {
|
|
|
75
76
|
}
|
|
76
77
|
},
|
|
77
78
|
performance: { ...DEFAULT_CONFIG.performance, ...loaded.performance },
|
|
79
|
+
enrichment: { ...DEFAULT_ENRICHMENT, ...loaded.enrichment },
|
|
78
80
|
profiles: { ...PRESET_PROFILES, ...loaded.profiles }
|
|
79
81
|
};
|
|
80
82
|
if (config.profile && config.profiles?.[config.profile]) {
|
|
@@ -103,6 +103,11 @@ const PRESET_PROFILES = {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
};
|
|
106
|
+
const DEFAULT_ENRICHMENT = {
|
|
107
|
+
enabled: false,
|
|
108
|
+
lookbackDepth: 3,
|
|
109
|
+
extractEntities: true
|
|
110
|
+
};
|
|
106
111
|
const DEFAULT_CONFIG = {
|
|
107
112
|
version: "1.0",
|
|
108
113
|
scoring: {
|
|
@@ -133,10 +138,12 @@ const DEFAULT_CONFIG = {
|
|
|
133
138
|
retrieval_timeout_ms: 500,
|
|
134
139
|
batch_upload_size: 100
|
|
135
140
|
},
|
|
141
|
+
enrichment: DEFAULT_ENRICHMENT,
|
|
136
142
|
profiles: PRESET_PROFILES
|
|
137
143
|
};
|
|
138
144
|
export {
|
|
139
145
|
DEFAULT_CONFIG,
|
|
146
|
+
DEFAULT_ENRICHMENT,
|
|
140
147
|
DEFAULT_TOOL_SCORES,
|
|
141
148
|
DEFAULT_WEIGHTS,
|
|
142
149
|
PRESET_PROFILES
|
|
@@ -19,7 +19,7 @@ class DualStackManager {
|
|
|
19
19
|
activeContext;
|
|
20
20
|
handoffRequests = /* @__PURE__ */ new Map();
|
|
21
21
|
permissionManager;
|
|
22
|
-
constructor(adapter, projectId, userId,
|
|
22
|
+
constructor(adapter, projectId, userId, _defaultTeamId) {
|
|
23
23
|
this.adapter = adapter;
|
|
24
24
|
this.permissionManager = new PermissionManager();
|
|
25
25
|
const rawDb = adapter instanceof SQLiteAdapter ? adapter.getRawDatabase() : null;
|
|
@@ -796,7 +796,7 @@ class DualStackManager {
|
|
|
796
796
|
)
|
|
797
797
|
);
|
|
798
798
|
stacks.push(context);
|
|
799
|
-
} catch
|
|
799
|
+
} catch {
|
|
800
800
|
logger.debug("User lacks access to stack", {
|
|
801
801
|
stackId: context.stackId,
|
|
802
802
|
userId: this.activeContext.ownerId
|