claude-mycelium 2.0.0 → 2.1.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.
Files changed (189) hide show
  1. package/.agent-meta/_inhibitors.ndjson +1287 -0
  2. package/.agent-meta/_quarantine.json +45 -0
  3. package/.agent-meta/config.json +9 -0
  4. package/.claude/memory.db +0 -0
  5. package/.claude/settings.local.json +4 -1
  6. package/README.md +81 -235
  7. package/SECURITY.md +145 -0
  8. package/dist/agent/worker.d.ts +8 -0
  9. package/dist/agent/worker.d.ts.map +1 -0
  10. package/dist/agent/worker.js +97 -0
  11. package/dist/agent/worker.js.map +1 -0
  12. package/dist/bin.d.ts +7 -0
  13. package/dist/bin.d.ts.map +1 -0
  14. package/dist/bin.js +11 -0
  15. package/dist/bin.js.map +1 -0
  16. package/dist/cli/cost.d.ts +10 -0
  17. package/dist/cli/cost.d.ts.map +1 -0
  18. package/dist/cli/cost.js +163 -0
  19. package/dist/cli/cost.js.map +1 -0
  20. package/dist/cli/gc.d.ts +10 -0
  21. package/dist/cli/gc.d.ts.map +1 -0
  22. package/dist/cli/gc.js +108 -0
  23. package/dist/cli/gc.js.map +1 -0
  24. package/dist/cli/gradients.d.ts +10 -0
  25. package/dist/cli/gradients.d.ts.map +1 -0
  26. package/dist/cli/gradients.js +69 -0
  27. package/dist/cli/gradients.js.map +1 -0
  28. package/dist/cli/index.d.ts +17 -0
  29. package/dist/cli/index.d.ts.map +1 -0
  30. package/dist/cli/index.js +72 -0
  31. package/dist/cli/index.js.map +1 -0
  32. package/dist/cli/init.d.ts +11 -0
  33. package/dist/cli/init.d.ts.map +1 -0
  34. package/dist/cli/init.js +97 -0
  35. package/dist/cli/init.js.map +1 -0
  36. package/dist/cli/status.d.ts +10 -0
  37. package/dist/cli/status.d.ts.map +1 -0
  38. package/dist/cli/status.js +191 -0
  39. package/dist/cli/status.js.map +1 -0
  40. package/dist/coordination/file-locks.d.ts +42 -0
  41. package/dist/coordination/file-locks.d.ts.map +1 -0
  42. package/dist/coordination/file-locks.js +269 -0
  43. package/dist/coordination/file-locks.js.map +1 -0
  44. package/dist/coordination/index.d.ts +4 -0
  45. package/dist/coordination/index.d.ts.map +1 -1
  46. package/dist/coordination/index.js +4 -0
  47. package/dist/coordination/index.js.map +1 -1
  48. package/dist/coordination/inhibitors.d.ts +84 -0
  49. package/dist/coordination/inhibitors.d.ts.map +1 -0
  50. package/dist/coordination/inhibitors.js +290 -0
  51. package/dist/coordination/inhibitors.js.map +1 -0
  52. package/dist/coordination/process-manager.d.ts +73 -0
  53. package/dist/coordination/process-manager.d.ts.map +1 -0
  54. package/dist/coordination/process-manager.js +144 -0
  55. package/dist/coordination/process-manager.js.map +1 -0
  56. package/dist/core/agent-executor.d.ts.map +1 -1
  57. package/dist/core/agent-executor.js +28 -10
  58. package/dist/core/agent-executor.js.map +1 -1
  59. package/dist/core/change-applier.d.ts +29 -5
  60. package/dist/core/change-applier.d.ts.map +1 -1
  61. package/dist/core/change-applier.js +254 -24
  62. package/dist/core/change-applier.js.map +1 -1
  63. package/dist/core/signals/churn.d.ts.map +1 -1
  64. package/dist/core/signals/churn.js +6 -4
  65. package/dist/core/signals/churn.js.map +1 -1
  66. package/dist/core/signals/debt.d.ts.map +1 -1
  67. package/dist/core/signals/debt.js +4 -3
  68. package/dist/core/signals/debt.js.map +1 -1
  69. package/dist/cost/cost-tracker.d.ts.map +1 -1
  70. package/dist/cost/cost-tracker.js +2 -0
  71. package/dist/cost/cost-tracker.js.map +1 -1
  72. package/dist/gc/index.d.ts +17 -0
  73. package/dist/gc/index.d.ts.map +1 -0
  74. package/dist/gc/index.js +17 -0
  75. package/dist/gc/index.js.map +1 -0
  76. package/dist/gc/runner.d.ts +39 -0
  77. package/dist/gc/runner.d.ts.map +1 -0
  78. package/dist/gc/runner.js +277 -0
  79. package/dist/gc/runner.js.map +1 -0
  80. package/dist/gc/trace-compactor.d.ts +31 -0
  81. package/dist/gc/trace-compactor.d.ts.map +1 -0
  82. package/dist/gc/trace-compactor.js +162 -0
  83. package/dist/gc/trace-compactor.js.map +1 -0
  84. package/dist/index.d.ts +5 -1
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +6 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/prompts/index.d.ts +2 -1
  89. package/dist/prompts/index.d.ts.map +1 -1
  90. package/dist/prompts/index.js.map +1 -1
  91. package/dist/quarantine/explorer.d.ts +65 -0
  92. package/dist/quarantine/explorer.d.ts.map +1 -0
  93. package/dist/quarantine/explorer.js +175 -0
  94. package/dist/quarantine/explorer.js.map +1 -0
  95. package/dist/quarantine/index.d.ts +7 -0
  96. package/dist/quarantine/index.d.ts.map +1 -0
  97. package/dist/quarantine/index.js +7 -0
  98. package/dist/quarantine/index.js.map +1 -0
  99. package/dist/quarantine/manager.d.ts +75 -0
  100. package/dist/quarantine/manager.d.ts.map +1 -0
  101. package/dist/quarantine/manager.js +275 -0
  102. package/dist/quarantine/manager.js.map +1 -0
  103. package/dist/task/acceptance.d.ts +29 -0
  104. package/dist/task/acceptance.d.ts.map +1 -0
  105. package/dist/task/acceptance.js +228 -0
  106. package/dist/task/acceptance.js.map +1 -0
  107. package/dist/task/executor.d.ts +30 -0
  108. package/dist/task/executor.d.ts.map +1 -0
  109. package/dist/task/executor.js +429 -0
  110. package/dist/task/executor.js.map +1 -0
  111. package/dist/task/index.d.ts +12 -0
  112. package/dist/task/index.d.ts.map +1 -0
  113. package/dist/task/index.js +12 -0
  114. package/dist/task/index.js.map +1 -0
  115. package/dist/task/planner.d.ts +21 -0
  116. package/dist/task/planner.d.ts.map +1 -0
  117. package/dist/task/planner.js +253 -0
  118. package/dist/task/planner.js.map +1 -0
  119. package/dist/task/storage.d.ts +46 -0
  120. package/dist/task/storage.d.ts.map +1 -0
  121. package/dist/task/storage.js +266 -0
  122. package/dist/task/storage.js.map +1 -0
  123. package/dist/trace/trace-event.d.ts +2 -18
  124. package/dist/trace/trace-event.d.ts.map +1 -1
  125. package/dist/trace/trace-event.js +6 -6
  126. package/dist/trace/trace-event.js.map +1 -1
  127. package/dist/utils/file-utils.d.ts.map +1 -1
  128. package/dist/utils/file-utils.js +54 -15
  129. package/dist/utils/file-utils.js.map +1 -1
  130. package/docs/PHASE5_IMPLEMENTATION.md +237 -0
  131. package/docs/PHASES-3-7-COMPLETE.md +177 -0
  132. package/docs/PHASE_4_COMPLETE.md +135 -0
  133. package/docs/PHASE_7_DELIVERABLES.md +295 -0
  134. package/docs/PHASE_7_IMPLEMENTATION.md +306 -0
  135. package/docs/PHASE_7_SUMMARY.txt +195 -0
  136. package/docs/RELEASE-NOTES-v2.1.md +213 -0
  137. package/docs/ROADMAP.md +64 -57
  138. package/docs/SECURITY-AUDIT.md +387 -0
  139. package/docs/SNAPSHOT.md +59 -32
  140. package/docs/implementation/phase3-summary.md +220 -0
  141. package/package.json +19 -11
  142. package/src/agent/worker.ts +111 -0
  143. package/src/bin.ts +13 -0
  144. package/src/cli/cost.ts +210 -0
  145. package/src/cli/gc.ts +138 -0
  146. package/src/cli/gradients.ts +95 -0
  147. package/src/cli/index.ts +79 -0
  148. package/src/cli/init.ts +139 -0
  149. package/src/cli/status.ts +218 -0
  150. package/src/coordination/file-locks.ts +300 -0
  151. package/src/coordination/index.ts +4 -0
  152. package/src/coordination/inhibitors.ts +345 -0
  153. package/src/coordination/process-manager.ts +199 -0
  154. package/src/core/agent-executor.ts +20 -4
  155. package/src/core/signals/churn.ts +8 -5
  156. package/src/core/signals/debt.ts +4 -3
  157. package/src/cost/cost-tracker.ts +2 -0
  158. package/src/gc/index.ts +17 -0
  159. package/src/gc/runner.ts +314 -0
  160. package/src/gc/trace-compactor.ts +187 -0
  161. package/src/index.ts +7 -1
  162. package/src/prompts/index.ts +2 -1
  163. package/src/quarantine/explorer.ts +234 -0
  164. package/src/quarantine/index.ts +7 -0
  165. package/src/quarantine/manager.ts +336 -0
  166. package/src/task/acceptance.ts +267 -0
  167. package/src/task/executor.ts +538 -0
  168. package/src/task/index.ts +38 -0
  169. package/src/task/planner.ts +294 -0
  170. package/src/task/storage.ts +332 -0
  171. package/src/trace/trace-event.ts +7 -26
  172. package/src/utils/file-utils.ts +61 -15
  173. package/tests/cli/gc.test.ts +206 -0
  174. package/tests/cli/init.test.ts +181 -0
  175. package/tests/cli/status.test.ts +282 -0
  176. package/tests/coordination/file-locks.test.ts +196 -0
  177. package/tests/coordination/inhibitors.test.ts +459 -0
  178. package/tests/coordination/integration.test.ts +195 -0
  179. package/tests/coordination/process-manager.test.ts +165 -0
  180. package/tests/gc/trace-compactor.test.ts +245 -0
  181. package/tests/integration/phase-7.test.ts +145 -0
  182. package/tests/quarantine/explorer.test.ts +381 -0
  183. package/tests/quarantine/manager.test.ts +399 -0
  184. package/tests/security/command-injection.test.ts +88 -0
  185. package/tests/security/path-traversal.test.ts +103 -0
  186. package/tests/task/acceptance.test.ts +411 -0
  187. package/tests/task/executor.test.ts +421 -0
  188. package/tests/task/planner.test.ts +359 -0
  189. package/tsconfig.json +2 -2
@@ -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 2 spec.
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
 
@@ -7,7 +7,8 @@
7
7
  */
8
8
 
9
9
  import { readFile } from 'fs/promises';
10
- import type { Mode, GradientScore } from '../types/index.js';
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';