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.
Files changed (208) 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/.agent-meta/tasks/_active.json +4 -0
  5. package/.agent-meta/tasks/task_0657b028-05a0-4b0c-b0b9-a4eae3d66cd9.json +168 -0
  6. package/.claude/memory.db +0 -0
  7. package/.claude/settings.local.json +4 -1
  8. package/README.md +85 -233
  9. package/SECURITY.md +145 -0
  10. package/dist/agent/task-worker.d.ts +11 -0
  11. package/dist/agent/task-worker.d.ts.map +1 -0
  12. package/dist/agent/task-worker.js +173 -0
  13. package/dist/agent/task-worker.js.map +1 -0
  14. package/dist/agent/worker.d.ts +8 -0
  15. package/dist/agent/worker.d.ts.map +1 -0
  16. package/dist/agent/worker.js +97 -0
  17. package/dist/agent/worker.js.map +1 -0
  18. package/dist/bin.d.ts +7 -0
  19. package/dist/bin.d.ts.map +1 -0
  20. package/dist/bin.js +11 -0
  21. package/dist/bin.js.map +1 -0
  22. package/dist/cli/cost.d.ts +10 -0
  23. package/dist/cli/cost.d.ts.map +1 -0
  24. package/dist/cli/cost.js +163 -0
  25. package/dist/cli/cost.js.map +1 -0
  26. package/dist/cli/gc.d.ts +10 -0
  27. package/dist/cli/gc.d.ts.map +1 -0
  28. package/dist/cli/gc.js +108 -0
  29. package/dist/cli/gc.js.map +1 -0
  30. package/dist/cli/gradients.d.ts +10 -0
  31. package/dist/cli/gradients.d.ts.map +1 -0
  32. package/dist/cli/gradients.js +70 -0
  33. package/dist/cli/gradients.js.map +1 -0
  34. package/dist/cli/grow.d.ts +17 -0
  35. package/dist/cli/grow.d.ts.map +1 -0
  36. package/dist/cli/grow.js +373 -0
  37. package/dist/cli/grow.js.map +1 -0
  38. package/dist/cli/index.d.ts +17 -0
  39. package/dist/cli/index.d.ts.map +1 -0
  40. package/dist/cli/index.js +74 -0
  41. package/dist/cli/index.js.map +1 -0
  42. package/dist/cli/init.d.ts +11 -0
  43. package/dist/cli/init.d.ts.map +1 -0
  44. package/dist/cli/init.js +97 -0
  45. package/dist/cli/init.js.map +1 -0
  46. package/dist/cli/status.d.ts +10 -0
  47. package/dist/cli/status.d.ts.map +1 -0
  48. package/dist/cli/status.js +191 -0
  49. package/dist/cli/status.js.map +1 -0
  50. package/dist/coordination/file-locks.d.ts +42 -0
  51. package/dist/coordination/file-locks.d.ts.map +1 -0
  52. package/dist/coordination/file-locks.js +269 -0
  53. package/dist/coordination/file-locks.js.map +1 -0
  54. package/dist/coordination/index.d.ts +4 -0
  55. package/dist/coordination/index.d.ts.map +1 -1
  56. package/dist/coordination/index.js +4 -0
  57. package/dist/coordination/index.js.map +1 -1
  58. package/dist/coordination/inhibitors.d.ts +84 -0
  59. package/dist/coordination/inhibitors.d.ts.map +1 -0
  60. package/dist/coordination/inhibitors.js +290 -0
  61. package/dist/coordination/inhibitors.js.map +1 -0
  62. package/dist/coordination/process-manager.d.ts +73 -0
  63. package/dist/coordination/process-manager.d.ts.map +1 -0
  64. package/dist/coordination/process-manager.js +144 -0
  65. package/dist/coordination/process-manager.js.map +1 -0
  66. package/dist/core/agent-executor.d.ts +4 -1
  67. package/dist/core/agent-executor.d.ts.map +1 -1
  68. package/dist/core/agent-executor.js +38 -12
  69. package/dist/core/agent-executor.js.map +1 -1
  70. package/dist/core/change-applier.d.ts +29 -5
  71. package/dist/core/change-applier.d.ts.map +1 -1
  72. package/dist/core/change-applier.js +254 -24
  73. package/dist/core/change-applier.js.map +1 -1
  74. package/dist/core/signals/churn.d.ts.map +1 -1
  75. package/dist/core/signals/churn.js +6 -4
  76. package/dist/core/signals/churn.js.map +1 -1
  77. package/dist/core/signals/debt.d.ts.map +1 -1
  78. package/dist/core/signals/debt.js +4 -3
  79. package/dist/core/signals/debt.js.map +1 -1
  80. package/dist/cost/cost-tracker.d.ts.map +1 -1
  81. package/dist/cost/cost-tracker.js +2 -0
  82. package/dist/cost/cost-tracker.js.map +1 -1
  83. package/dist/gc/index.d.ts +17 -0
  84. package/dist/gc/index.d.ts.map +1 -0
  85. package/dist/gc/index.js +17 -0
  86. package/dist/gc/index.js.map +1 -0
  87. package/dist/gc/runner.d.ts +39 -0
  88. package/dist/gc/runner.d.ts.map +1 -0
  89. package/dist/gc/runner.js +277 -0
  90. package/dist/gc/runner.js.map +1 -0
  91. package/dist/gc/trace-compactor.d.ts +31 -0
  92. package/dist/gc/trace-compactor.d.ts.map +1 -0
  93. package/dist/gc/trace-compactor.js +162 -0
  94. package/dist/gc/trace-compactor.js.map +1 -0
  95. package/dist/index.d.ts +5 -1
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +6 -1
  98. package/dist/index.js.map +1 -1
  99. package/dist/prompts/index.d.ts +2 -1
  100. package/dist/prompts/index.d.ts.map +1 -1
  101. package/dist/prompts/index.js.map +1 -1
  102. package/dist/quarantine/explorer.d.ts +65 -0
  103. package/dist/quarantine/explorer.d.ts.map +1 -0
  104. package/dist/quarantine/explorer.js +175 -0
  105. package/dist/quarantine/explorer.js.map +1 -0
  106. package/dist/quarantine/index.d.ts +7 -0
  107. package/dist/quarantine/index.d.ts.map +1 -0
  108. package/dist/quarantine/index.js +7 -0
  109. package/dist/quarantine/index.js.map +1 -0
  110. package/dist/quarantine/manager.d.ts +75 -0
  111. package/dist/quarantine/manager.d.ts.map +1 -0
  112. package/dist/quarantine/manager.js +275 -0
  113. package/dist/quarantine/manager.js.map +1 -0
  114. package/dist/task/acceptance.d.ts +29 -0
  115. package/dist/task/acceptance.d.ts.map +1 -0
  116. package/dist/task/acceptance.js +228 -0
  117. package/dist/task/acceptance.js.map +1 -0
  118. package/dist/task/agent-coordinator.d.ts +40 -0
  119. package/dist/task/agent-coordinator.d.ts.map +1 -0
  120. package/dist/task/agent-coordinator.js +168 -0
  121. package/dist/task/agent-coordinator.js.map +1 -0
  122. package/dist/task/executor.d.ts +37 -0
  123. package/dist/task/executor.d.ts.map +1 -0
  124. package/dist/task/executor.js +462 -0
  125. package/dist/task/executor.js.map +1 -0
  126. package/dist/task/index.d.ts +12 -0
  127. package/dist/task/index.d.ts.map +1 -0
  128. package/dist/task/index.js +12 -0
  129. package/dist/task/index.js.map +1 -0
  130. package/dist/task/planner.d.ts +21 -0
  131. package/dist/task/planner.d.ts.map +1 -0
  132. package/dist/task/planner.js +253 -0
  133. package/dist/task/planner.js.map +1 -0
  134. package/dist/task/storage.d.ts +46 -0
  135. package/dist/task/storage.d.ts.map +1 -0
  136. package/dist/task/storage.js +266 -0
  137. package/dist/task/storage.js.map +1 -0
  138. package/dist/trace/trace-event.d.ts +2 -18
  139. package/dist/trace/trace-event.d.ts.map +1 -1
  140. package/dist/trace/trace-event.js +6 -6
  141. package/dist/trace/trace-event.js.map +1 -1
  142. package/dist/utils/file-utils.d.ts.map +1 -1
  143. package/dist/utils/file-utils.js +54 -15
  144. package/dist/utils/file-utils.js.map +1 -1
  145. package/docs/PHASE5_IMPLEMENTATION.md +237 -0
  146. package/docs/PHASES-3-7-COMPLETE.md +177 -0
  147. package/docs/PHASE_4_COMPLETE.md +135 -0
  148. package/docs/PHASE_7_DELIVERABLES.md +295 -0
  149. package/docs/PHASE_7_IMPLEMENTATION.md +306 -0
  150. package/docs/PHASE_7_SUMMARY.txt +195 -0
  151. package/docs/RELEASE-NOTES-v2.1.md +213 -0
  152. package/docs/ROADMAP.md +194 -107
  153. package/docs/SECURITY-AUDIT.md +387 -0
  154. package/docs/SNAPSHOT.md +59 -32
  155. package/docs/implementation/phase3-summary.md +220 -0
  156. package/package.json +27 -11
  157. package/src/agent/task-worker.ts +196 -0
  158. package/src/agent/worker.ts +111 -0
  159. package/src/bin.ts +13 -0
  160. package/src/cli/cost.ts +210 -0
  161. package/src/cli/gc.ts +138 -0
  162. package/src/cli/gradients.ts +97 -0
  163. package/src/cli/grow.ts +416 -0
  164. package/src/cli/index.ts +81 -0
  165. package/src/cli/init.ts +139 -0
  166. package/src/cli/status.ts +218 -0
  167. package/src/coordination/file-locks.ts +300 -0
  168. package/src/coordination/index.ts +4 -0
  169. package/src/coordination/inhibitors.ts +345 -0
  170. package/src/coordination/process-manager.ts +199 -0
  171. package/src/core/agent-executor.ts +37 -8
  172. package/src/core/signals/churn.ts +8 -5
  173. package/src/core/signals/debt.ts +4 -3
  174. package/src/cost/cost-tracker.ts +2 -0
  175. package/src/gc/index.ts +17 -0
  176. package/src/gc/runner.ts +314 -0
  177. package/src/gc/trace-compactor.ts +187 -0
  178. package/src/index.ts +7 -1
  179. package/src/prompts/index.ts +2 -1
  180. package/src/quarantine/explorer.ts +234 -0
  181. package/src/quarantine/index.ts +7 -0
  182. package/src/quarantine/manager.ts +336 -0
  183. package/src/task/acceptance.ts +267 -0
  184. package/src/task/agent-coordinator.ts +220 -0
  185. package/src/task/executor.ts +543 -0
  186. package/src/task/index.ts +38 -0
  187. package/src/task/planner.ts +294 -0
  188. package/src/task/storage.ts +332 -0
  189. package/src/trace/trace-event.ts +7 -26
  190. package/src/utils/file-utils.ts +61 -15
  191. package/tests/cli/gc.test.ts +206 -0
  192. package/tests/cli/init.test.ts +181 -0
  193. package/tests/cli/status.test.ts +282 -0
  194. package/tests/coordination/file-locks.test.ts +196 -0
  195. package/tests/coordination/inhibitors.test.ts +459 -0
  196. package/tests/coordination/integration.test.ts +195 -0
  197. package/tests/coordination/process-manager.test.ts +165 -0
  198. package/tests/gc/trace-compactor.test.ts +245 -0
  199. package/tests/integration/phase-7.test.ts +145 -0
  200. package/tests/quarantine/explorer.test.ts +381 -0
  201. package/tests/quarantine/manager.test.ts +399 -0
  202. package/tests/security/command-injection.test.ts +88 -0
  203. package/tests/security/path-traversal.test.ts +103 -0
  204. package/tests/task/acceptance.test.ts +411 -0
  205. package/tests/task/executor.test.ts +421 -0
  206. package/tests/task/planner.test.ts +359 -0
  207. package/tests/trace/trace-event.test.ts +62 -20
  208. package/tsconfig.json +2 -2
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Acceptance Criteria Validation
3
+ * Per ROADMAP §Phase 5 Week 6 Day 5-6: Acceptance Criteria
4
+ *
5
+ * Validates task completion against acceptance criteria including
6
+ * file existence, test execution, lint checks, and custom validation.
7
+ */
8
+
9
+ import { Task, AcceptanceCriterion } from '../types/index.js';
10
+ import { fileExists } from '../utils/file-utils.js';
11
+ import { logDebug, logError } from '../utils/logger.js';
12
+ import { exec } from 'child_process';
13
+ import { promisify } from 'util';
14
+
15
+ const execAsync = promisify(exec);
16
+
17
+ /**
18
+ * Validate all acceptance criteria for a task
19
+ */
20
+ export async function validateAcceptance(task: Task): Promise<boolean> {
21
+ logDebug('Validating acceptance criteria', {
22
+ taskId: task.id,
23
+ criteriaCount: task.acceptance_criteria.length,
24
+ });
25
+
26
+ let allMet = true;
27
+
28
+ for (const criterion of task.acceptance_criteria) {
29
+ const met = await validateCriterion(criterion);
30
+ criterion.met = met;
31
+
32
+ if (!met) {
33
+ allMet = false;
34
+ logDebug('Acceptance criterion not met', {
35
+ taskId: task.id,
36
+ type: criterion.type,
37
+ description: criterion.description,
38
+ });
39
+ }
40
+ }
41
+
42
+ logDebug('Acceptance validation complete', {
43
+ taskId: task.id,
44
+ allMet,
45
+ metCount: task.acceptance_criteria.filter(c => c.met).length,
46
+ });
47
+
48
+ return allMet;
49
+ }
50
+
51
+ /**
52
+ * Validate a single acceptance criterion
53
+ */
54
+ async function validateCriterion(criterion: AcceptanceCriterion): Promise<boolean> {
55
+ try {
56
+ switch (criterion.type) {
57
+ case 'file_exists':
58
+ return await checkFileExists(criterion.check);
59
+
60
+ case 'test_passes':
61
+ return await runTests(criterion.check);
62
+
63
+ case 'no_lint_errors':
64
+ return await checkLintErrors(criterion.check);
65
+
66
+ case 'custom':
67
+ return await runCustomValidation(criterion.check);
68
+
69
+ default:
70
+ logError('Unknown criterion type', new Error('Invalid type'), {
71
+ type: criterion.type,
72
+ });
73
+ return false;
74
+ }
75
+ } catch (error) {
76
+ const errorObj = error instanceof Error ? error : new Error(String(error));
77
+ logError('Criterion validation failed', errorObj, {
78
+ type: criterion.type,
79
+ check: criterion.check,
80
+ });
81
+ return false;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Check if a file exists
87
+ */
88
+ export async function checkFileExists(filePath: string): Promise<boolean> {
89
+ const exists = fileExists(filePath);
90
+ logDebug('File existence check', { filePath, exists });
91
+ return exists;
92
+ }
93
+
94
+ /**
95
+ * Run specific tests and check if they pass
96
+ */
97
+ export async function runTests(testPattern: string): Promise<boolean> {
98
+ logDebug('Running tests', { pattern: testPattern });
99
+
100
+ try {
101
+ // Run vitest with specific pattern
102
+ const command = `npm test -- --run ${testPattern}`;
103
+ const { stdout, stderr } = await execAsync(command, {
104
+ timeout: 60000, // 60 second timeout
105
+ });
106
+
107
+ // Check for test failures in output
108
+ const output = stdout + stderr;
109
+ const hasFailed = output.includes('FAILED') ||
110
+ output.includes('failed') ||
111
+ output.includes('Error:');
112
+
113
+ logDebug('Test execution result', {
114
+ pattern: testPattern,
115
+ passed: !hasFailed,
116
+ });
117
+
118
+ return !hasFailed;
119
+ } catch (error) {
120
+ // exec throws on non-zero exit code
121
+ const errorObj = error instanceof Error ? error : new Error(String(error));
122
+ logError('Test execution failed', errorObj, { pattern: testPattern });
123
+ return false;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Check if file has lint errors
129
+ */
130
+ async function checkLintErrors(filePath: string): Promise<boolean> {
131
+ logDebug('Checking lint errors', { filePath });
132
+
133
+ try {
134
+ // Run eslint on specific file
135
+ const command = `npx eslint ${filePath} --format json`;
136
+ const { stdout } = await execAsync(command, {
137
+ timeout: 30000, // 30 second timeout
138
+ });
139
+
140
+ // Parse eslint JSON output
141
+ const results = JSON.parse(stdout);
142
+
143
+ // Check if there are any errors
144
+ const totalErrors = results.reduce(
145
+ (sum: number, result: any) => sum + result.errorCount,
146
+ 0
147
+ );
148
+
149
+ const noErrors = totalErrors === 0;
150
+ logDebug('Lint check result', {
151
+ filePath,
152
+ errors: totalErrors,
153
+ passed: noErrors,
154
+ });
155
+
156
+ return noErrors;
157
+ } catch (error) {
158
+ // eslint returns non-zero exit code when errors found
159
+ // Try to parse the output to get error count
160
+ if (error instanceof Error && 'stdout' in error) {
161
+ try {
162
+ const results = JSON.parse((error as any).stdout);
163
+ const totalErrors = results.reduce(
164
+ (sum: number, result: any) => sum + result.errorCount,
165
+ 0
166
+ );
167
+ return totalErrors === 0;
168
+ } catch {
169
+ // Failed to parse, assume errors
170
+ return false;
171
+ }
172
+ }
173
+
174
+ const errorObj = error instanceof Error ? error : new Error(String(error));
175
+ logError('Lint check failed', errorObj, { filePath });
176
+ return false;
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Run custom validation command
182
+ */
183
+ async function runCustomValidation(command: string): Promise<boolean> {
184
+ logDebug('Running custom validation', { command });
185
+
186
+ try {
187
+ await execAsync(command, {
188
+ timeout: 60000, // 60 second timeout
189
+ });
190
+
191
+ logDebug('Custom validation passed', { command });
192
+ return true;
193
+ } catch (error) {
194
+ const errorObj = error instanceof Error ? error : new Error(String(error));
195
+ logError('Custom validation failed', errorObj, { command });
196
+ return false;
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Generate default acceptance criteria for a task
202
+ */
203
+ export function generateDefaultCriteria(task: Task): AcceptanceCriterion[] {
204
+ const criteria: AcceptanceCriterion[] = [];
205
+
206
+ // If task plan exists, generate criteria based on files
207
+ if (task.plan) {
208
+ // Check that all created files exist
209
+ const createSteps = task.plan.steps.filter(s => s.mode === 'create');
210
+ for (const step of createSteps) {
211
+ criteria.push({
212
+ description: `File ${step.target_file} exists`,
213
+ type: 'file_exists',
214
+ check: step.target_file,
215
+ met: false,
216
+ });
217
+
218
+ // If it's a test file, add a test_passes criterion
219
+ if (step.target_file.includes('.test.')) {
220
+ criteria.push({
221
+ description: `Tests in ${step.target_file} pass`,
222
+ type: 'test_passes',
223
+ check: step.target_file,
224
+ met: false,
225
+ });
226
+ }
227
+ }
228
+
229
+ // Add lint check for all touched files
230
+ const allFiles = new Set([
231
+ ...createSteps.map(s => s.target_file),
232
+ ...task.plan.steps.filter(s => s.mode !== 'create').map(s => s.target_file),
233
+ ]);
234
+
235
+ for (const file of allFiles) {
236
+ // Only check non-test files for lint
237
+ if (!file.includes('.test.')) {
238
+ criteria.push({
239
+ description: `No lint errors in ${file}`,
240
+ type: 'no_lint_errors',
241
+ check: file,
242
+ met: false,
243
+ });
244
+ }
245
+ }
246
+ }
247
+
248
+ return criteria;
249
+ }
250
+
251
+ /**
252
+ * Check if task can be marked as completed
253
+ */
254
+ export function canCompleteTask(task: Task): boolean {
255
+ // All steps must be completed
256
+ if (task.steps_completed < task.steps_total) {
257
+ return false;
258
+ }
259
+
260
+ // All acceptance criteria must be met
261
+ if (task.acceptance_criteria.length > 0) {
262
+ return task.acceptance_criteria.every(c => c.met);
263
+ }
264
+
265
+ // If no criteria defined, task is complete when all steps done
266
+ return true;
267
+ }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Task Agent Coordinator
3
+ *
4
+ * Coordinates multi-agent execution of task steps using the mycelium
5
+ * process spawning system (Phase 3) for true RALPH-style parallelization.
6
+ *
7
+ * This replaces the simple Promise.all() approach with real agent processes
8
+ * that use file locks, inhibitors, and the full executeAgent() cycle.
9
+ */
10
+
11
+ import { fork, ChildProcess } from 'child_process';
12
+ import { Task, TaskStep, Mode } from '../types/index.js';
13
+ import { logDebug, logError, logInfo } from '../utils/logger.js';
14
+ import { randomUUID } from 'crypto';
15
+ import * as path from 'path';
16
+ import { fileURLToPath } from 'url';
17
+ import { dirname } from 'path';
18
+
19
+ const __filename = fileURLToPath(import.meta.url);
20
+ const __dirname = dirname(__filename);
21
+
22
+ export interface StepAssignment {
23
+ stepOrder: number;
24
+ targetFile: string;
25
+ mode: Mode;
26
+ description: string;
27
+ taskId: string;
28
+ }
29
+
30
+ export interface AgentResult {
31
+ stepOrder: number;
32
+ success: boolean;
33
+ traceId?: string;
34
+ error?: string;
35
+ exitCode: number | null;
36
+ }
37
+
38
+ /**
39
+ * Spawn an agent for a specific task step
40
+ * Each agent runs as an independent process via child_process.fork()
41
+ */
42
+ export function spawnAgentForStep(
43
+ task: Task,
44
+ step: TaskStep
45
+ ): Promise<AgentResult> {
46
+ return new Promise((resolve) => {
47
+ const agentId = `agent-${randomUUID().substring(0, 8)}`;
48
+
49
+ logDebug('Spawning agent for task step', {
50
+ agentId,
51
+ taskId: task.id,
52
+ stepOrder: step.order,
53
+ targetFile: step.target_file,
54
+ mode: step.mode,
55
+ });
56
+
57
+ // Path to the compiled task worker file
58
+ const workerPath = path.join(__dirname, '../agent/task-worker.js');
59
+
60
+ // Spawn agent process with task context
61
+ const child = fork(workerPath, [], {
62
+ env: {
63
+ ...process.env,
64
+ AGENT_ID: agentId,
65
+ TASK_ID: task.id,
66
+ STEP_ORDER: String(step.order),
67
+ TARGET_FILE: step.target_file,
68
+ MODE: step.mode,
69
+ STEP_DESCRIPTION: step.description,
70
+ },
71
+ stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
72
+ });
73
+
74
+ let result: AgentResult = {
75
+ stepOrder: step.order,
76
+ success: false,
77
+ exitCode: null,
78
+ };
79
+
80
+ // Set timeout (5 minutes)
81
+ const timeout = setTimeout(() => {
82
+ logError('Agent timeout', new Error('Agent timed out'), {
83
+ agentId,
84
+ stepOrder: step.order,
85
+ });
86
+ child.kill('SIGTERM');
87
+ result.error = 'Agent timed out after 5 minutes';
88
+ result.exitCode = -1;
89
+ resolve(result);
90
+ }, 300_000);
91
+
92
+ // Handle IPC messages from agent
93
+ child.on('message', (message: any) => {
94
+ if (message.type === 'result') {
95
+ logDebug('Agent result received', {
96
+ agentId,
97
+ stepOrder: step.order,
98
+ success: message.success,
99
+ });
100
+
101
+ result = {
102
+ stepOrder: step.order,
103
+ success: message.success,
104
+ traceId: message.traceId,
105
+ error: message.error,
106
+ exitCode: 0,
107
+ };
108
+ } else if (message.type === 'progress') {
109
+ logInfo('Agent progress', {
110
+ agentId,
111
+ stepOrder: step.order,
112
+ message: message.message,
113
+ });
114
+ }
115
+ });
116
+
117
+ // Handle agent exit
118
+ child.on('exit', (code, signal) => {
119
+ clearTimeout(timeout);
120
+
121
+ result.exitCode = code;
122
+
123
+ logInfo('Agent exited', {
124
+ agentId,
125
+ stepOrder: step.order,
126
+ code,
127
+ signal,
128
+ success: result.success,
129
+ });
130
+
131
+ // If we didn't receive a result message, mark as failure
132
+ if (!result.success && !result.error) {
133
+ result.error = `Agent exited with code ${code}`;
134
+ }
135
+
136
+ resolve(result);
137
+ });
138
+
139
+ // Handle errors
140
+ child.on('error', (error) => {
141
+ clearTimeout(timeout);
142
+ logError('Agent error', error, {
143
+ agentId,
144
+ stepOrder: step.order,
145
+ });
146
+
147
+ result.error = error.message;
148
+ result.exitCode = -1;
149
+ resolve(result);
150
+ });
151
+
152
+ // Forward stderr for debugging
153
+ if (child.stderr) {
154
+ child.stderr.on('data', (data) => {
155
+ logDebug('Agent stderr', {
156
+ agentId,
157
+ stepOrder: step.order,
158
+ output: data.toString(),
159
+ });
160
+ });
161
+ }
162
+ });
163
+ }
164
+
165
+ /**
166
+ * Execute a wave of task steps in parallel using agent processes
167
+ * This is the true mycelium multi-agent coordination
168
+ */
169
+ export async function executeWaveWithAgents(
170
+ task: Task,
171
+ wave: TaskStep[]
172
+ ): Promise<AgentResult[]> {
173
+ logInfo('Executing wave with agent processes', {
174
+ taskId: task.id,
175
+ waveSize: wave.length,
176
+ steps: wave.map(s => s.order),
177
+ });
178
+
179
+ // Spawn all agents in parallel
180
+ const agentPromises = wave.map(step => spawnAgentForStep(task, step));
181
+
182
+ // Wait for all agents to complete
183
+ const results = await Promise.all(agentPromises);
184
+
185
+ logInfo('Wave execution complete', {
186
+ taskId: task.id,
187
+ total: results.length,
188
+ successful: results.filter(r => r.success).length,
189
+ failed: results.filter(r => !r.success).length,
190
+ });
191
+
192
+ return results;
193
+ }
194
+
195
+ /**
196
+ * Get parallelizable steps grouped by dependency waves
197
+ * Same logic as before, but now we spawn agents for each wave
198
+ */
199
+ export function getParallelizableSteps(steps: TaskStep[]): TaskStep[][] {
200
+ const waves: TaskStep[][] = [];
201
+ const completed = new Set<number>();
202
+
203
+ while (completed.size < steps.length) {
204
+ // Find steps whose dependencies are all completed
205
+ const ready = steps.filter(
206
+ step =>
207
+ !completed.has(step.order) &&
208
+ step.depends_on.every(dep => completed.has(dep))
209
+ );
210
+
211
+ if (ready.length === 0) {
212
+ throw new Error('Circular dependency detected in task steps');
213
+ }
214
+
215
+ waves.push(ready);
216
+ ready.forEach(step => completed.add(step.order));
217
+ }
218
+
219
+ return waves;
220
+ }