claude-mycelium 2.0.0 → 2.2.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/.agent-meta/_inhibitors.ndjson +1287 -0
- package/.agent-meta/_quarantine.json +45 -0
- package/.agent-meta/config.json +9 -0
- package/.agent-meta/tasks/_active.json +4 -0
- package/.agent-meta/tasks/task_0657b028-05a0-4b0c-b0b9-a4eae3d66cd9.json +168 -0
- package/.claude/memory.db +0 -0
- package/.claude/settings.local.json +4 -1
- package/README.md +85 -233
- package/SECURITY.md +145 -0
- package/dist/agent/task-worker.d.ts +11 -0
- package/dist/agent/task-worker.d.ts.map +1 -0
- package/dist/agent/task-worker.js +173 -0
- package/dist/agent/task-worker.js.map +1 -0
- package/dist/agent/worker.d.ts +8 -0
- package/dist/agent/worker.d.ts.map +1 -0
- package/dist/agent/worker.js +97 -0
- package/dist/agent/worker.js.map +1 -0
- package/dist/bin.d.ts +7 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +11 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/cost.d.ts +10 -0
- package/dist/cli/cost.d.ts.map +1 -0
- package/dist/cli/cost.js +163 -0
- package/dist/cli/cost.js.map +1 -0
- package/dist/cli/gc.d.ts +10 -0
- package/dist/cli/gc.d.ts.map +1 -0
- package/dist/cli/gc.js +108 -0
- package/dist/cli/gc.js.map +1 -0
- package/dist/cli/gradients.d.ts +10 -0
- package/dist/cli/gradients.d.ts.map +1 -0
- package/dist/cli/gradients.js +70 -0
- package/dist/cli/gradients.js.map +1 -0
- package/dist/cli/grow.d.ts +17 -0
- package/dist/cli/grow.d.ts.map +1 -0
- package/dist/cli/grow.js +373 -0
- package/dist/cli/grow.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +74 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +11 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +97 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/status.d.ts +10 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +191 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/coordination/file-locks.d.ts +42 -0
- package/dist/coordination/file-locks.d.ts.map +1 -0
- package/dist/coordination/file-locks.js +269 -0
- package/dist/coordination/file-locks.js.map +1 -0
- package/dist/coordination/index.d.ts +4 -0
- package/dist/coordination/index.d.ts.map +1 -1
- package/dist/coordination/index.js +4 -0
- package/dist/coordination/index.js.map +1 -1
- package/dist/coordination/inhibitors.d.ts +84 -0
- package/dist/coordination/inhibitors.d.ts.map +1 -0
- package/dist/coordination/inhibitors.js +290 -0
- package/dist/coordination/inhibitors.js.map +1 -0
- package/dist/coordination/process-manager.d.ts +73 -0
- package/dist/coordination/process-manager.d.ts.map +1 -0
- package/dist/coordination/process-manager.js +144 -0
- package/dist/coordination/process-manager.js.map +1 -0
- package/dist/core/agent-executor.d.ts +4 -1
- package/dist/core/agent-executor.d.ts.map +1 -1
- package/dist/core/agent-executor.js +38 -12
- package/dist/core/agent-executor.js.map +1 -1
- package/dist/core/change-applier.d.ts +29 -5
- package/dist/core/change-applier.d.ts.map +1 -1
- package/dist/core/change-applier.js +254 -24
- package/dist/core/change-applier.js.map +1 -1
- package/dist/core/signals/churn.d.ts.map +1 -1
- package/dist/core/signals/churn.js +6 -4
- package/dist/core/signals/churn.js.map +1 -1
- package/dist/core/signals/debt.d.ts.map +1 -1
- package/dist/core/signals/debt.js +4 -3
- package/dist/core/signals/debt.js.map +1 -1
- package/dist/cost/cost-tracker.d.ts.map +1 -1
- package/dist/cost/cost-tracker.js +2 -0
- package/dist/cost/cost-tracker.js.map +1 -1
- package/dist/gc/index.d.ts +17 -0
- package/dist/gc/index.d.ts.map +1 -0
- package/dist/gc/index.js +17 -0
- package/dist/gc/index.js.map +1 -0
- package/dist/gc/runner.d.ts +39 -0
- package/dist/gc/runner.d.ts.map +1 -0
- package/dist/gc/runner.js +277 -0
- package/dist/gc/runner.js.map +1 -0
- package/dist/gc/trace-compactor.d.ts +31 -0
- package/dist/gc/trace-compactor.d.ts.map +1 -0
- package/dist/gc/trace-compactor.js +162 -0
- package/dist/gc/trace-compactor.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/prompts/index.d.ts +2 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js.map +1 -1
- package/dist/quarantine/explorer.d.ts +65 -0
- package/dist/quarantine/explorer.d.ts.map +1 -0
- package/dist/quarantine/explorer.js +175 -0
- package/dist/quarantine/explorer.js.map +1 -0
- package/dist/quarantine/index.d.ts +7 -0
- package/dist/quarantine/index.d.ts.map +1 -0
- package/dist/quarantine/index.js +7 -0
- package/dist/quarantine/index.js.map +1 -0
- package/dist/quarantine/manager.d.ts +75 -0
- package/dist/quarantine/manager.d.ts.map +1 -0
- package/dist/quarantine/manager.js +275 -0
- package/dist/quarantine/manager.js.map +1 -0
- package/dist/task/acceptance.d.ts +29 -0
- package/dist/task/acceptance.d.ts.map +1 -0
- package/dist/task/acceptance.js +228 -0
- package/dist/task/acceptance.js.map +1 -0
- package/dist/task/agent-coordinator.d.ts +40 -0
- package/dist/task/agent-coordinator.d.ts.map +1 -0
- package/dist/task/agent-coordinator.js +168 -0
- package/dist/task/agent-coordinator.js.map +1 -0
- package/dist/task/executor.d.ts +37 -0
- package/dist/task/executor.d.ts.map +1 -0
- package/dist/task/executor.js +462 -0
- package/dist/task/executor.js.map +1 -0
- package/dist/task/index.d.ts +12 -0
- package/dist/task/index.d.ts.map +1 -0
- package/dist/task/index.js +12 -0
- package/dist/task/index.js.map +1 -0
- package/dist/task/planner.d.ts +21 -0
- package/dist/task/planner.d.ts.map +1 -0
- package/dist/task/planner.js +253 -0
- package/dist/task/planner.js.map +1 -0
- package/dist/task/storage.d.ts +46 -0
- package/dist/task/storage.d.ts.map +1 -0
- package/dist/task/storage.js +266 -0
- package/dist/task/storage.js.map +1 -0
- package/dist/trace/trace-event.d.ts +2 -18
- package/dist/trace/trace-event.d.ts.map +1 -1
- package/dist/trace/trace-event.js +6 -6
- package/dist/trace/trace-event.js.map +1 -1
- package/dist/utils/file-utils.d.ts.map +1 -1
- package/dist/utils/file-utils.js +54 -15
- package/dist/utils/file-utils.js.map +1 -1
- package/docs/PHASE5_IMPLEMENTATION.md +237 -0
- package/docs/PHASES-3-7-COMPLETE.md +177 -0
- package/docs/PHASE_4_COMPLETE.md +135 -0
- package/docs/PHASE_7_DELIVERABLES.md +295 -0
- package/docs/PHASE_7_IMPLEMENTATION.md +306 -0
- package/docs/PHASE_7_SUMMARY.txt +195 -0
- package/docs/RELEASE-NOTES-v2.1.md +213 -0
- package/docs/ROADMAP.md +194 -107
- package/docs/SECURITY-AUDIT.md +387 -0
- package/docs/SNAPSHOT.md +59 -32
- package/docs/implementation/phase3-summary.md +220 -0
- package/package.json +27 -11
- package/src/agent/task-worker.ts +196 -0
- package/src/agent/worker.ts +111 -0
- package/src/bin.ts +13 -0
- package/src/cli/cost.ts +210 -0
- package/src/cli/gc.ts +138 -0
- package/src/cli/gradients.ts +97 -0
- package/src/cli/grow.ts +416 -0
- package/src/cli/index.ts +81 -0
- package/src/cli/init.ts +139 -0
- package/src/cli/status.ts +218 -0
- package/src/coordination/file-locks.ts +300 -0
- package/src/coordination/index.ts +4 -0
- package/src/coordination/inhibitors.ts +345 -0
- package/src/coordination/process-manager.ts +199 -0
- package/src/core/agent-executor.ts +37 -8
- package/src/core/signals/churn.ts +8 -5
- package/src/core/signals/debt.ts +4 -3
- package/src/cost/cost-tracker.ts +2 -0
- package/src/gc/index.ts +17 -0
- package/src/gc/runner.ts +314 -0
- package/src/gc/trace-compactor.ts +187 -0
- package/src/index.ts +7 -1
- package/src/prompts/index.ts +2 -1
- package/src/quarantine/explorer.ts +234 -0
- package/src/quarantine/index.ts +7 -0
- package/src/quarantine/manager.ts +336 -0
- package/src/task/acceptance.ts +267 -0
- package/src/task/agent-coordinator.ts +220 -0
- package/src/task/executor.ts +543 -0
- package/src/task/index.ts +38 -0
- package/src/task/planner.ts +294 -0
- package/src/task/storage.ts +332 -0
- package/src/trace/trace-event.ts +7 -26
- package/src/utils/file-utils.ts +61 -15
- package/tests/cli/gc.test.ts +206 -0
- package/tests/cli/init.test.ts +181 -0
- package/tests/cli/status.test.ts +282 -0
- package/tests/coordination/file-locks.test.ts +196 -0
- package/tests/coordination/inhibitors.test.ts +459 -0
- package/tests/coordination/integration.test.ts +195 -0
- package/tests/coordination/process-manager.test.ts +165 -0
- package/tests/gc/trace-compactor.test.ts +245 -0
- package/tests/integration/phase-7.test.ts +145 -0
- package/tests/quarantine/explorer.test.ts +381 -0
- package/tests/quarantine/manager.test.ts +399 -0
- package/tests/security/command-injection.test.ts +88 -0
- package/tests/security/path-traversal.test.ts +103 -0
- package/tests/task/acceptance.test.ts +411 -0
- package/tests/task/executor.test.ts +421 -0
- package/tests/task/planner.test.ts +359 -0
- package/tests/trace/trace-event.test.ts +62 -20
- package/tsconfig.json +2 -2
package/src/cost/cost-tracker.ts
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
appendFile,
|
|
7
7
|
ensureDir,
|
|
8
8
|
} from '../utils/file-utils';
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
10
|
+
// @ts-ignore
|
|
9
11
|
import { logDebug, logWarn, createLogger } from '../utils/logger';
|
|
10
12
|
|
|
11
13
|
const logger = createLogger({ module: 'cost-tracker' });
|
package/src/gc/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GC Module - Garbage Collection System
|
|
3
|
+
*
|
|
4
|
+
* Per Phase 7 specification §8, §14
|
|
5
|
+
* - Trace compaction (keep last 10 samples + 7 days)
|
|
6
|
+
* - Inhibitor cleanup (remove weak signals)
|
|
7
|
+
* - Automatic GC every 100 spawns
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { runGC } from './gc/index.js';
|
|
12
|
+
* const report = await runGC();
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export * from './trace-compactor.js';
|
|
17
|
+
export * from './runner.js';
|
package/src/gc/runner.ts
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Garbage Collection Runner
|
|
3
|
+
*
|
|
4
|
+
* Per Phase 7 specification §8 & §14, this module implements:
|
|
5
|
+
* - Delete traces older than retention period
|
|
6
|
+
* - Compact remaining traces
|
|
7
|
+
* - Delete weak inhibitors (strength <0.05)
|
|
8
|
+
* - Generate GC report
|
|
9
|
+
* - Auto-run every 100 spawns
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { runGC } from './runner.js';
|
|
14
|
+
* const report = await runGC();
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import * as fs from 'fs/promises';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import { logDebug, logWarn } from '../utils/logger.js';
|
|
21
|
+
import { fileExists } from '../utils/file-utils.js';
|
|
22
|
+
import { compactTraces } from './trace-compactor.js';
|
|
23
|
+
import type { TraceEvent, GCReport, Inhibitor } from '../types/index.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Garbage collection configuration (per spec §13)
|
|
27
|
+
*/
|
|
28
|
+
export const GC_CONFIG = {
|
|
29
|
+
lowConfidenceRetentionDays: 7, // Keep recent traces for 7 days
|
|
30
|
+
traceRetentionDays: 30, // Keep high-efficiency traces for 30 days
|
|
31
|
+
compactionThreshold: 50, // Compact if >50 events per file
|
|
32
|
+
inhibitorStrengthThreshold: 0.05, // Delete inhibitors with strength <0.05
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Run garbage collection
|
|
37
|
+
* - Delete old traces
|
|
38
|
+
* - Compact remaining traces
|
|
39
|
+
* - Clean weak inhibitors
|
|
40
|
+
* - Generate report
|
|
41
|
+
*/
|
|
42
|
+
export async function runGC(): Promise<GCReport> {
|
|
43
|
+
const startTime = Date.now();
|
|
44
|
+
const report: GCReport = {
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
deleted: 0,
|
|
47
|
+
compacted: 0,
|
|
48
|
+
size_before_mb: 0,
|
|
49
|
+
size_after_mb: 0,
|
|
50
|
+
errors: [],
|
|
51
|
+
duration_ms: 0,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const tracesDir = '.agent-meta/traces';
|
|
55
|
+
|
|
56
|
+
// Ensure traces directory exists
|
|
57
|
+
if (!fileExists(tracesDir)) {
|
|
58
|
+
logDebug('Traces directory does not exist, skipping GC');
|
|
59
|
+
report.duration_ms = Date.now() - startTime;
|
|
60
|
+
return report;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
// Step 1: Calculate size before
|
|
65
|
+
report.size_before_mb = await getDirectorySize(tracesDir);
|
|
66
|
+
|
|
67
|
+
// Step 2: Clean old traces
|
|
68
|
+
const cleanReport = await cleanOldTraces(tracesDir);
|
|
69
|
+
report.deleted += cleanReport.deleted;
|
|
70
|
+
report.compacted += cleanReport.compacted;
|
|
71
|
+
report.errors.push(...cleanReport.errors);
|
|
72
|
+
|
|
73
|
+
// Step 3: Clean weak inhibitors
|
|
74
|
+
const inhibitorReport = await cleanWeakInhibitors();
|
|
75
|
+
report.deleted += inhibitorReport.deleted;
|
|
76
|
+
report.errors.push(...inhibitorReport.errors);
|
|
77
|
+
|
|
78
|
+
// Step 4: Calculate size after
|
|
79
|
+
report.size_before_mb = await getDirectorySize(tracesDir);
|
|
80
|
+
|
|
81
|
+
// Step 5: Log report
|
|
82
|
+
await logGCReport(report);
|
|
83
|
+
|
|
84
|
+
logDebug('GC complete', {
|
|
85
|
+
deleted: report.deleted,
|
|
86
|
+
compacted: report.compacted,
|
|
87
|
+
size_before_mb: report.size_before_mb.toFixed(2),
|
|
88
|
+
size_after_mb: report.size_after_mb.toFixed(2),
|
|
89
|
+
duration_ms: report.duration_ms,
|
|
90
|
+
});
|
|
91
|
+
} catch (error) {
|
|
92
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
93
|
+
report.errors.push(`GC runner error: ${message}`);
|
|
94
|
+
logWarn('GC runner failed', { error: message });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
report.duration_ms = Date.now() - startTime;
|
|
98
|
+
return report;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Delete traces older than retention period
|
|
103
|
+
* Compact remaining traces
|
|
104
|
+
*/
|
|
105
|
+
async function cleanOldTraces(tracesDir: string): Promise<{
|
|
106
|
+
deleted: number;
|
|
107
|
+
compacted: number;
|
|
108
|
+
errors: string[];
|
|
109
|
+
}> {
|
|
110
|
+
const result = { deleted: 0, compacted: 0, errors: [] as string[] };
|
|
111
|
+
const now = Date.now();
|
|
112
|
+
|
|
113
|
+
let traceFiles: string[];
|
|
114
|
+
try {
|
|
115
|
+
const entries = await fs.readdir(tracesDir);
|
|
116
|
+
traceFiles = entries.filter(f => f.endsWith('.ndjson'));
|
|
117
|
+
} catch (error) {
|
|
118
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
119
|
+
result.errors.push(`Failed to list trace files: ${message}`);
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const file of traceFiles) {
|
|
124
|
+
const filePath = path.join(tracesDir, file);
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
// Read trace events
|
|
128
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
129
|
+
const lines = content.split('\n').filter(Boolean);
|
|
130
|
+
const events: TraceEvent[] = [];
|
|
131
|
+
|
|
132
|
+
for (const line of lines) {
|
|
133
|
+
try {
|
|
134
|
+
events.push(JSON.parse(line) as TraceEvent);
|
|
135
|
+
} catch {
|
|
136
|
+
// Skip malformed lines
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Filter old events
|
|
141
|
+
const filtered = events.filter((event) => {
|
|
142
|
+
const ageMs = now - new Date(event.timestamp).getTime();
|
|
143
|
+
const ageDays = ageMs / (1000 * 60 * 60 * 24);
|
|
144
|
+
|
|
145
|
+
// Keep recent events (< 7 days)
|
|
146
|
+
if (ageDays < GC_CONFIG.lowConfidenceRetentionDays) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Keep high-efficiency events longer (< 30 days + efficiency > 0.2)
|
|
151
|
+
if (ageDays < GC_CONFIG.traceRetentionDays && event.efficiency > 0.2) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
result.deleted++;
|
|
156
|
+
return false;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Compact if too many events
|
|
160
|
+
let output: TraceEvent[];
|
|
161
|
+
if (filtered.length > GC_CONFIG.compactionThreshold) {
|
|
162
|
+
output = compactTraces(filtered);
|
|
163
|
+
result.compacted++;
|
|
164
|
+
} else {
|
|
165
|
+
output = filtered;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Write back (or delete if empty)
|
|
169
|
+
if (output.length === 0) {
|
|
170
|
+
await fs.unlink(filePath);
|
|
171
|
+
} else {
|
|
172
|
+
const outputContent = output.map(e => JSON.stringify(e)).join('\n') + '\n';
|
|
173
|
+
await fs.writeFile(filePath, outputContent);
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
177
|
+
result.errors.push(`${file}: ${message}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Delete weak inhibitors (strength < 0.05)
|
|
186
|
+
*/
|
|
187
|
+
async function cleanWeakInhibitors(): Promise<{
|
|
188
|
+
deleted: number;
|
|
189
|
+
errors: string[];
|
|
190
|
+
}> {
|
|
191
|
+
const result = { deleted: 0, errors: [] as string[] };
|
|
192
|
+
const inhibitorsPath = '.agent-meta/_inhibitors.ndjson';
|
|
193
|
+
|
|
194
|
+
if (!fileExists(inhibitorsPath)) {
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
const content = await fs.readFile(inhibitorsPath, 'utf-8');
|
|
200
|
+
const lines = content.split('\n').filter(Boolean);
|
|
201
|
+
const inhibitors: Inhibitor[] = [];
|
|
202
|
+
|
|
203
|
+
for (const line of lines) {
|
|
204
|
+
try {
|
|
205
|
+
const inhibitor = JSON.parse(line) as Inhibitor;
|
|
206
|
+
inhibitors.push(inhibitor);
|
|
207
|
+
} catch {
|
|
208
|
+
// Skip malformed lines
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Filter out weak inhibitors
|
|
213
|
+
const strong = inhibitors.filter((i) => {
|
|
214
|
+
const isWeak = i.strength < GC_CONFIG.inhibitorStrengthThreshold;
|
|
215
|
+
if (isWeak) {
|
|
216
|
+
result.deleted++;
|
|
217
|
+
}
|
|
218
|
+
return !isWeak;
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Write back
|
|
222
|
+
const outputContent = strong.map(i => JSON.stringify(i)).join('\n');
|
|
223
|
+
if (strong.length > 0) {
|
|
224
|
+
await fs.writeFile(inhibitorsPath, outputContent + '\n');
|
|
225
|
+
} else {
|
|
226
|
+
await fs.writeFile(inhibitorsPath, '');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
logDebug('Inhibitor cleanup', {
|
|
230
|
+
total: inhibitors.length,
|
|
231
|
+
removed: result.deleted,
|
|
232
|
+
remaining: strong.length,
|
|
233
|
+
});
|
|
234
|
+
} catch (error) {
|
|
235
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
236
|
+
result.errors.push(`Inhibitor cleanup failed: ${message}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Calculate total size of a directory in MB
|
|
244
|
+
*/
|
|
245
|
+
async function getDirectorySize(dirPath: string): Promise<number> {
|
|
246
|
+
let totalBytes = 0;
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
const entries = await fs.readdir(dirPath);
|
|
250
|
+
|
|
251
|
+
for (const entry of entries) {
|
|
252
|
+
const fullPath = path.join(dirPath, entry);
|
|
253
|
+
const stat = await fs.stat(fullPath);
|
|
254
|
+
|
|
255
|
+
if (stat.isFile()) {
|
|
256
|
+
totalBytes += stat.size;
|
|
257
|
+
} else if (stat.isDirectory()) {
|
|
258
|
+
totalBytes += await getDirectorySize(fullPath);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
} catch {
|
|
262
|
+
// Directory doesn't exist or can't be read
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return totalBytes / (1024 * 1024); // Convert to MB
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Append GC report to GC log
|
|
270
|
+
*/
|
|
271
|
+
async function logGCReport(report: GCReport): Promise<void> {
|
|
272
|
+
const gcLogPath = '.agent-meta/_gc.log';
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const logEntry = JSON.stringify(report);
|
|
276
|
+
await fs.appendFile(gcLogPath, logEntry + '\n');
|
|
277
|
+
} catch (error) {
|
|
278
|
+
logWarn('Failed to log GC report', {
|
|
279
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Read GC log
|
|
286
|
+
*/
|
|
287
|
+
export async function readGCLog(): Promise<GCReport[]> {
|
|
288
|
+
const gcLogPath = '.agent-meta/_gc.log';
|
|
289
|
+
|
|
290
|
+
if (!fileExists(gcLogPath)) {
|
|
291
|
+
return [];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
const content = await fs.readFile(gcLogPath, 'utf-8');
|
|
296
|
+
const lines = content.split('\n').filter(Boolean);
|
|
297
|
+
const reports: GCReport[] = [];
|
|
298
|
+
|
|
299
|
+
for (const line of lines) {
|
|
300
|
+
try {
|
|
301
|
+
reports.push(JSON.parse(line) as GCReport);
|
|
302
|
+
} catch {
|
|
303
|
+
// Skip malformed lines
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return reports;
|
|
308
|
+
} catch (error) {
|
|
309
|
+
logWarn('Failed to read GC log', {
|
|
310
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
311
|
+
});
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace Compaction Module
|
|
3
|
+
*
|
|
4
|
+
* Per Phase 7 specification §14, this module implements:
|
|
5
|
+
* - Keep last 10 samples per file
|
|
6
|
+
* - Keep all samples from last 7 days
|
|
7
|
+
* - Summarize older samples (avg efficiency, date range)
|
|
8
|
+
* - Write summaries with __summary__: true flag
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { compactTraces, summarizeOld, keepRecent } from './trace-compactor.js';
|
|
13
|
+
* const compacted = compactTraces(events);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { TraceEvent, Mode } from '../types/index.js';
|
|
18
|
+
import { logDebug } from '../utils/logger.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Keep last 10 samples per file + all samples from last 7 days
|
|
22
|
+
* Summarize older samples by mode
|
|
23
|
+
*/
|
|
24
|
+
export function compactTraces(events: TraceEvent[]): TraceEvent[] {
|
|
25
|
+
if (events.length === 0) return [];
|
|
26
|
+
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
const sevenDaysMs = 7 * 24 * 60 * 60 * 1000;
|
|
29
|
+
|
|
30
|
+
// Separate events into recent (last 7 days) and old
|
|
31
|
+
const recent: TraceEvent[] = [];
|
|
32
|
+
const old: TraceEvent[] = [];
|
|
33
|
+
|
|
34
|
+
for (const event of events) {
|
|
35
|
+
const ageMs = now - new Date(event.timestamp).getTime();
|
|
36
|
+
if (ageMs < sevenDaysMs) {
|
|
37
|
+
recent.push(event);
|
|
38
|
+
} else {
|
|
39
|
+
old.push(event);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Keep last 10 from recent, summarize the rest of old
|
|
44
|
+
const recentToKeep = recent.slice(-10);
|
|
45
|
+
const oldToSummarize = [
|
|
46
|
+
...recent.slice(0, -10),
|
|
47
|
+
...old,
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
logDebug('Trace compaction', {
|
|
51
|
+
total_events: events.length,
|
|
52
|
+
recent_kept: recentToKeep.length,
|
|
53
|
+
old_to_summarize: oldToSummarize.length,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (oldToSummarize.length === 0) {
|
|
57
|
+
return recentToKeep;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Summarize old events by mode
|
|
61
|
+
const summaries = summarizeOld(oldToSummarize);
|
|
62
|
+
|
|
63
|
+
return [...summaries, ...recentToKeep];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Summarize old events by mode
|
|
68
|
+
* Creates summary events with aggregated metrics
|
|
69
|
+
*/
|
|
70
|
+
export function summarizeOld(events: TraceEvent[]): TraceEvent[] {
|
|
71
|
+
if (events.length === 0) return [];
|
|
72
|
+
|
|
73
|
+
const byMode = new Map<Mode, TraceEvent[]>();
|
|
74
|
+
|
|
75
|
+
// Group by mode
|
|
76
|
+
for (const event of events) {
|
|
77
|
+
if (!byMode.has(event.mode)) {
|
|
78
|
+
byMode.set(event.mode, []);
|
|
79
|
+
}
|
|
80
|
+
byMode.get(event.mode)!.push(event);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const summaries: TraceEvent[] = [];
|
|
84
|
+
|
|
85
|
+
for (const [mode, traces] of byMode) {
|
|
86
|
+
if (traces.length === 0) continue;
|
|
87
|
+
|
|
88
|
+
const summary = createSummaryEvent(mode, traces);
|
|
89
|
+
summaries.push(summary);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
logDebug('Summarized old traces', {
|
|
93
|
+
modes: summaries.length,
|
|
94
|
+
total_compressed: events.length,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return summaries;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Keep recent samples (last N days) verbatim
|
|
102
|
+
*/
|
|
103
|
+
export function keepRecent(events: TraceEvent[], days: number = 7): TraceEvent[] {
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
const msPerDay = 24 * 60 * 60 * 1000;
|
|
106
|
+
const cutoffMs = days * msPerDay;
|
|
107
|
+
|
|
108
|
+
return events.filter((event) => {
|
|
109
|
+
const ageMs = now - new Date(event.timestamp).getTime();
|
|
110
|
+
return ageMs < cutoffMs;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create a summary event from multiple trace events
|
|
116
|
+
*/
|
|
117
|
+
function createSummaryEvent(mode: Mode, traces: TraceEvent[]): TraceEvent {
|
|
118
|
+
if (traces.length === 0) {
|
|
119
|
+
throw new Error('Cannot create summary from empty trace list');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const timestamps = traces.map(t => new Date(t.timestamp).getTime());
|
|
123
|
+
const minTime = Math.min(...timestamps);
|
|
124
|
+
const maxTime = Math.max(...timestamps);
|
|
125
|
+
|
|
126
|
+
const efficiencies = traces.map(t => t.efficiency);
|
|
127
|
+
const avgEfficiency = efficiencies.reduce((a, b) => a + b, 0) / efficiencies.length;
|
|
128
|
+
const minEfficiency = Math.min(...efficiencies);
|
|
129
|
+
const maxEfficiency = Math.max(...efficiencies);
|
|
130
|
+
|
|
131
|
+
const ciPassed = traces.filter(t => t.ci_passed).length;
|
|
132
|
+
const successRate = ciPassed / traces.length;
|
|
133
|
+
|
|
134
|
+
// Create summary event (inherits from first event for structure)
|
|
135
|
+
const firstEvent = traces[0];
|
|
136
|
+
const lastEvent = traces[traces.length - 1];
|
|
137
|
+
|
|
138
|
+
const summaryEvent: TraceEvent = {
|
|
139
|
+
id: `summary-${mode}-${Date.now()}`,
|
|
140
|
+
timestamp: lastEvent.timestamp,
|
|
141
|
+
file_path: firstEvent.file_path,
|
|
142
|
+
mode,
|
|
143
|
+
gradient_before: average(traces.map(t => t.gradient_before)),
|
|
144
|
+
gradient_after: average(traces.map(t => t.gradient_after)),
|
|
145
|
+
gradient_delta: average(traces.map(t => t.gradient_delta)),
|
|
146
|
+
metabolic_cost: sum(traces.map(t => t.metabolic_cost)),
|
|
147
|
+
efficiency: avgEfficiency,
|
|
148
|
+
ci_passed: traces.every(t => t.ci_passed),
|
|
149
|
+
changes: {
|
|
150
|
+
additions: sum(traces.map(t => t.changes.additions)),
|
|
151
|
+
deletions: sum(traces.map(t => t.changes.deletions)),
|
|
152
|
+
files_touched: [...new Set(traces.flatMap(t => t.changes.files_touched))],
|
|
153
|
+
},
|
|
154
|
+
notes: [
|
|
155
|
+
`Compacted ${traces.length} ${mode} events`,
|
|
156
|
+
`Date range: ${new Date(minTime).toISOString().split('T')[0]} to ${new Date(maxTime).toISOString().split('T')[0]}`,
|
|
157
|
+
`Efficiency: avg=${avgEfficiency.toFixed(3)}, range=[${minEfficiency.toFixed(3)}, ${maxEfficiency.toFixed(3)}]`,
|
|
158
|
+
`CI success rate: ${(successRate * 100).toFixed(1)}%`,
|
|
159
|
+
],
|
|
160
|
+
cost: {
|
|
161
|
+
tokens_in: sum(traces.map(t => t.cost.tokens_in)),
|
|
162
|
+
tokens_out: sum(traces.map(t => t.cost.tokens_out)),
|
|
163
|
+
model: firstEvent.cost.model,
|
|
164
|
+
estimated_usd: sum(traces.map(t => t.cost.estimated_usd)),
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Add metadata flag for queries
|
|
169
|
+
(summaryEvent as any).__summary__ = true;
|
|
170
|
+
|
|
171
|
+
return summaryEvent;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Helper: calculate average of numbers
|
|
176
|
+
*/
|
|
177
|
+
function average(nums: number[]): number {
|
|
178
|
+
if (nums.length === 0) return 0;
|
|
179
|
+
return sum(nums) / nums.length;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Helper: sum array of numbers
|
|
184
|
+
*/
|
|
185
|
+
function sum(nums: number[]): number {
|
|
186
|
+
return nums.reduce((a, b) => a + b, 0);
|
|
187
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
* - LLM: Anthropic integration
|
|
10
10
|
* - Trace: Event tracking
|
|
11
11
|
* - Cost: Token and cost tracking
|
|
12
|
+
* - Task: LLM-based task planning and execution (Phase 5)
|
|
12
13
|
* - Utils: Logging, file I/O, configuration
|
|
13
14
|
* - Types: Complete TypeScript interfaces
|
|
14
15
|
*
|
|
15
|
-
* Per initial-spec and Phase
|
|
16
|
+
* Per initial-spec, Phase 2 spec, and Phase 5 (Task System).
|
|
16
17
|
*
|
|
17
18
|
* Usage:
|
|
18
19
|
* ```typescript
|
|
@@ -22,6 +23,8 @@
|
|
|
22
23
|
* createAnthropicClient,
|
|
23
24
|
* recordTraceEvent,
|
|
24
25
|
* calculateCost,
|
|
26
|
+
* planTask,
|
|
27
|
+
* executeTask,
|
|
25
28
|
* } from 'claude-mycelium';
|
|
26
29
|
* ```
|
|
27
30
|
*/
|
|
@@ -36,6 +39,9 @@ export { createAnthropicClient, type LLMRequest, type LLMResponse, LLMError } fr
|
|
|
36
39
|
export * from './trace/index.js';
|
|
37
40
|
export * from './cost/index.js';
|
|
38
41
|
|
|
42
|
+
// Phase 5: Task System
|
|
43
|
+
export * from './task/index.js';
|
|
44
|
+
|
|
39
45
|
// Utilities
|
|
40
46
|
export * from './utils/index.js';
|
|
41
47
|
|
package/src/prompts/index.ts
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { readFile } from 'fs/promises';
|
|
10
|
-
import type { Mode
|
|
10
|
+
import type { Mode } from '../types/index.js';
|
|
11
|
+
import type { GradientScore } from '../core/gradient.js';
|
|
11
12
|
import type { PromptContext } from './types.js';
|
|
12
13
|
|
|
13
14
|
export { generatePrompt as generateErrorReducerPrompt } from './error-reducer.js';
|