@phi-code-admin/phi-code 0.58.4 → 0.58.6
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/extensions/phi/orchestrator.ts +164 -62
- package/package.json +1 -1
|
@@ -193,7 +193,7 @@ export default function orchestratorExtension(pi: ExtensionAPI) {
|
|
|
193
193
|
});
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
// ─── Execute All Tasks
|
|
196
|
+
// ─── Execute All Tasks (parallel with dependency resolution) ─────
|
|
197
197
|
|
|
198
198
|
async function executePlan(
|
|
199
199
|
tasks: TaskDef[],
|
|
@@ -205,52 +205,133 @@ export default function orchestratorExtension(pi: ExtensionAPI) {
|
|
|
205
205
|
const progressPath = join(plansDir, progressFile);
|
|
206
206
|
let progress = `# Progress: ${todoFile}\n\n`;
|
|
207
207
|
progress += `**Started:** ${new Date().toLocaleString()}\n`;
|
|
208
|
-
progress += `**Tasks:** ${tasks.length}\n\n`;
|
|
208
|
+
progress += `**Tasks:** ${tasks.length}\n**Mode:** parallel (dependency-aware)\n\n`;
|
|
209
209
|
await writeFile(progressPath, progress, "utf-8");
|
|
210
210
|
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
// Build dependency graph
|
|
212
|
+
// Task indices are 1-based in the plan files
|
|
213
|
+
const completed = new Set<number>();
|
|
214
|
+
const failed = new Set<number>();
|
|
213
215
|
const results: TaskResult[] = [];
|
|
214
216
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
217
|
+
// Check which tasks can run (all dependencies completed successfully)
|
|
218
|
+
function getReadyTasks(): number[] {
|
|
219
|
+
const ready: number[] = [];
|
|
220
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
221
|
+
const taskNum = i + 1;
|
|
222
|
+
if (completed.has(taskNum) || failed.has(taskNum)) continue;
|
|
223
|
+
|
|
224
|
+
const deps = tasks[i].dependencies || [];
|
|
225
|
+
const allDepsMet = deps.every(d => completed.has(d));
|
|
226
|
+
const anyDepFailed = deps.some(d => failed.has(d));
|
|
227
|
+
|
|
228
|
+
if (anyDepFailed) {
|
|
229
|
+
// Skip tasks whose dependencies failed
|
|
230
|
+
failed.add(taskNum);
|
|
231
|
+
results.push({
|
|
232
|
+
taskIndex: taskNum,
|
|
233
|
+
title: tasks[i].title,
|
|
234
|
+
agent: tasks[i].agent || "code",
|
|
235
|
+
status: "skipped",
|
|
236
|
+
output: `Skipped: dependency #${deps.find(d => failed.has(d))} failed`,
|
|
237
|
+
durationMs: 0,
|
|
238
|
+
});
|
|
239
|
+
notify(`⏭️ Task ${taskNum}: **${tasks[i].title}** — skipped (dependency failed)`, "warning");
|
|
240
|
+
} else if (allDepsMet) {
|
|
241
|
+
ready.push(i);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return ready;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const totalTasks = tasks.length;
|
|
248
|
+
let wave = 1;
|
|
249
|
+
|
|
250
|
+
notify(`🚀 Executing ${totalTasks} tasks with sub-agents (parallel mode)...`, "info");
|
|
251
|
+
|
|
252
|
+
// Execute in waves — each wave runs independent tasks in parallel
|
|
253
|
+
while (completed.size + failed.size < totalTasks) {
|
|
254
|
+
const readyIndices = getReadyTasks();
|
|
255
|
+
|
|
256
|
+
if (readyIndices.length === 0) {
|
|
257
|
+
// Deadlock or all done
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const parallelCount = readyIndices.length;
|
|
262
|
+
if (parallelCount > 1) {
|
|
263
|
+
notify(`\n🔄 **Wave ${wave}** — ${parallelCount} tasks in parallel`, "info");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
for (const idx of readyIndices) {
|
|
267
|
+
const t = tasks[idx];
|
|
268
|
+
notify(`⏳ Task ${idx + 1}: **${t.title}** [${t.agent || "code"}]`, "info");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Launch all ready tasks simultaneously
|
|
272
|
+
const promises = readyIndices.map(async (idx) => {
|
|
273
|
+
const task = tasks[idx];
|
|
274
|
+
const result = await executeTask(task, agentDefs, process.cwd());
|
|
275
|
+
result.taskIndex = idx + 1;
|
|
276
|
+
return result;
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const waveResults = await Promise.all(promises);
|
|
280
|
+
|
|
281
|
+
// Process results
|
|
282
|
+
for (const result of waveResults) {
|
|
283
|
+
results.push(result);
|
|
284
|
+
|
|
285
|
+
if (result.status === "success") {
|
|
286
|
+
completed.add(result.taskIndex);
|
|
287
|
+
} else {
|
|
288
|
+
failed.add(result.taskIndex);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const icon = result.status === "success" ? "✅" : "❌";
|
|
292
|
+
const duration = (result.durationMs / 1000).toFixed(1);
|
|
293
|
+
const outputPreview = result.output.length > 500 ? result.output.slice(0, 500) + "..." : result.output;
|
|
294
|
+
notify(`${icon} Task ${result.taskIndex}: **${result.title}** (${duration}s)\n${outputPreview}`,
|
|
295
|
+
result.status === "success" ? "info" : "error");
|
|
296
|
+
|
|
297
|
+
progress += `## Task ${result.taskIndex}: ${result.title}\n\n`;
|
|
298
|
+
progress += `- **Status:** ${result.status}\n`;
|
|
299
|
+
progress += `- **Agent:** ${result.agent}\n`;
|
|
300
|
+
progress += `- **Wave:** ${wave}\n`;
|
|
301
|
+
progress += `- **Duration:** ${duration}s\n`;
|
|
302
|
+
progress += `- **Output:**\n\n\`\`\`\n${result.output.slice(0, 3000)}\n\`\`\`\n\n`;
|
|
303
|
+
}
|
|
304
|
+
|
|
235
305
|
await writeFile(progressPath, progress, "utf-8");
|
|
306
|
+
wave++;
|
|
236
307
|
}
|
|
237
308
|
|
|
238
|
-
|
|
239
|
-
|
|
309
|
+
// Sort results by task index for consistent reporting
|
|
310
|
+
results.sort((a, b) => a.taskIndex - b.taskIndex);
|
|
311
|
+
|
|
312
|
+
const succeededCount = results.filter(r => r.status === "success").length;
|
|
313
|
+
const failedCount = results.filter(r => r.status === "error").length;
|
|
314
|
+
const skippedCount = results.filter(r => r.status === "skipped").length;
|
|
240
315
|
const totalTime = results.reduce((sum, r) => sum + r.durationMs, 0);
|
|
241
316
|
|
|
242
317
|
progress += `---\n\n## Summary\n\n`;
|
|
243
318
|
progress += `- **Completed:** ${new Date().toLocaleString()}\n`;
|
|
244
|
-
progress += `- **
|
|
245
|
-
progress += `- **
|
|
319
|
+
progress += `- **Waves:** ${wave - 1}\n`;
|
|
320
|
+
progress += `- **Succeeded:** ${succeededCount}/${results.length}\n`;
|
|
321
|
+
progress += `- **Failed:** ${failedCount}\n`;
|
|
322
|
+
progress += `- **Skipped:** ${skippedCount}\n`;
|
|
246
323
|
progress += `- **Total time:** ${(totalTime / 1000).toFixed(1)}s\n`;
|
|
247
324
|
await writeFile(progressPath, progress, "utf-8");
|
|
248
325
|
|
|
326
|
+
const statusParts = [`✅ ${succeededCount} succeeded`];
|
|
327
|
+
if (failedCount > 0) statusParts.push(`❌ ${failedCount} failed`);
|
|
328
|
+
if (skippedCount > 0) statusParts.push(`⏭️ ${skippedCount} skipped`);
|
|
329
|
+
|
|
249
330
|
notify(
|
|
250
|
-
`\n🏁 **Execution complete
|
|
251
|
-
|
|
331
|
+
`\n🏁 **Execution complete!** (${wave - 1} waves)\n` +
|
|
332
|
+
statusParts.join(" | ") + ` | ⏱️ ${(totalTime / 1000).toFixed(1)}s\n` +
|
|
252
333
|
`Progress: \`${progressFile}\``,
|
|
253
|
-
|
|
334
|
+
failedCount === 0 ? "info" : "warning"
|
|
254
335
|
);
|
|
255
336
|
|
|
256
337
|
return { results, progressFile };
|
|
@@ -317,33 +398,36 @@ export default function orchestratorExtension(pi: ExtensionAPI) {
|
|
|
317
398
|
pi.registerTool({
|
|
318
399
|
name: "orchestrate",
|
|
319
400
|
label: "Project Orchestrator",
|
|
320
|
-
description: "Create a project plan AND automatically execute all tasks with sub-agents. Each agent gets its own isolated context, model, and system prompt.
|
|
321
|
-
promptSnippet: "Plan + execute projects.
|
|
401
|
+
description: "Create a project plan AND automatically execute all tasks with sub-agents in parallel. Each agent gets its own isolated context, model, and system prompt. Tasks without dependencies run simultaneously.",
|
|
402
|
+
promptSnippet: "Plan + execute projects in parallel waves. Each sub-agent gets isolated context + model. Use prompt-architect patterns for structured task descriptions.",
|
|
322
403
|
promptGuidelines: [
|
|
323
|
-
"When asked to plan or build a project: analyze the request, then call orchestrate. It
|
|
324
|
-
"
|
|
325
|
-
"
|
|
326
|
-
"
|
|
404
|
+
"When asked to plan or build a project: analyze the request thoroughly, then call the orchestrate tool. It plans AND executes automatically.",
|
|
405
|
+
"CRITICAL: Each task description must be SELF-CONTAINED. The sub-agent has NO shared history, NO access to the conversation, NO memory of previous tasks. Include ALL context it needs: file paths, expected behavior, relevant code patterns, and success criteria.",
|
|
406
|
+
"Structure each task description using the prompt-architect pattern: [CONTEXT] what exists and why → [TASK] what to do specifically → [FORMAT] expected output → [CONSTRAINTS] rules and limitations.",
|
|
407
|
+
"Assign agent types strategically: 'explore' (read-only analysis, codebase understanding), 'plan' (architecture, design decisions), 'code' (implementation, file creation/modification), 'test' (write + run tests, validate behavior), 'review' (security audit, quality check, read-only).",
|
|
408
|
+
"Set dependencies to maximize parallelism: tasks without dependencies run simultaneously in the same wave. Only add dependencies when a task truly needs another task's output.",
|
|
409
|
+
"Order tasks logically: explore → plan → code → test → review. But allow independent tasks at each stage to run in parallel.",
|
|
410
|
+
"Set priority=high for critical-path tasks, medium for standard work, low for nice-to-haves.",
|
|
327
411
|
],
|
|
328
412
|
parameters: Type.Object({
|
|
329
|
-
title: Type.String({ description: "
|
|
330
|
-
description: Type.String({ description: "Full project description
|
|
331
|
-
goals: Type.Array(Type.String(), { description: "
|
|
413
|
+
title: Type.String({ description: "Concise project title" }),
|
|
414
|
+
description: Type.String({ description: "Full project description: what to build, why, and any relevant context" }),
|
|
415
|
+
goals: Type.Array(Type.String(), { description: "Measurable project goals (what success looks like)" }),
|
|
332
416
|
requirements: Type.Array(Type.String(), { description: "Technical and functional requirements" }),
|
|
333
|
-
architecture: Type.Optional(Type.Array(Type.String(), { description: "Architecture decisions" })),
|
|
417
|
+
architecture: Type.Optional(Type.Array(Type.String(), { description: "Architecture decisions, tech stack choices, trade-offs" })),
|
|
334
418
|
tasks: Type.Array(
|
|
335
419
|
Type.Object({
|
|
336
|
-
title: Type.String({ description: "
|
|
337
|
-
description: Type.String({ description: "
|
|
338
|
-
agent: Type.Optional(Type.String({ description: "Agent type: explore, plan, code, test,
|
|
339
|
-
priority: Type.Optional(Type.String({ description: "high, medium,
|
|
340
|
-
dependencies: Type.Optional(Type.Array(Type.Number(), { description: "
|
|
341
|
-
subtasks: Type.Optional(Type.Array(Type.String(), { description: "
|
|
420
|
+
title: Type.String({ description: "Clear, action-oriented task title" }),
|
|
421
|
+
description: Type.String({ description: "SELF-CONTAINED task description. Include ALL context the sub-agent needs: file paths, expected behavior, code patterns, conventions. The agent has NO shared history." }),
|
|
422
|
+
agent: Type.Optional(Type.String({ description: "Agent type: explore (read-only analysis), plan (architecture), code (implementation), test (write+run tests), review (quality audit)" })),
|
|
423
|
+
priority: Type.Optional(Type.String({ description: "high (critical path), medium (standard), low (nice-to-have)" })),
|
|
424
|
+
dependencies: Type.Optional(Type.Array(Type.Number(), { description: "Task numbers this depends on (1-indexed). Only add when truly needed — fewer dependencies = more parallelism" })),
|
|
425
|
+
subtasks: Type.Optional(Type.Array(Type.String(), { description: "Specific sub-steps within this task" })),
|
|
342
426
|
}),
|
|
343
|
-
{ description: "Ordered list of tasks
|
|
427
|
+
{ description: "Ordered list of tasks. Independent tasks run in parallel. Dependent tasks wait for prerequisites." }
|
|
344
428
|
),
|
|
345
|
-
constraints: Type.Optional(Type.Array(Type.String(), { description: "
|
|
346
|
-
successCriteria: Type.Optional(Type.Array(Type.String(), { description: "
|
|
429
|
+
constraints: Type.Optional(Type.Array(Type.String(), { description: "Hard constraints: frameworks, patterns, rules, things to avoid" })),
|
|
430
|
+
successCriteria: Type.Optional(Type.Array(Type.String(), { description: "How to verify the project is complete and correct" })),
|
|
347
431
|
}),
|
|
348
432
|
|
|
349
433
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -445,18 +529,36 @@ All files in \`.phi/plans/\``;
|
|
|
445
529
|
}
|
|
446
530
|
|
|
447
531
|
ctx.sendUserMessage(
|
|
448
|
-
`Analyze this project
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
Instructions
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
-
|
|
459
|
-
-
|
|
532
|
+
`Analyze this project and call the orchestrate tool. It will create the plan AND execute all tasks automatically with parallel sub-agents.
|
|
533
|
+
|
|
534
|
+
## Project
|
|
535
|
+
${description}
|
|
536
|
+
|
|
537
|
+
## Instructions
|
|
538
|
+
|
|
539
|
+
1. **Analyze** the project: identify goals, requirements, technical constraints, and architecture decisions.
|
|
540
|
+
|
|
541
|
+
2. **Decompose** into tasks. Each task will be executed by an isolated sub-agent that has:
|
|
542
|
+
- NO access to this conversation
|
|
543
|
+
- NO shared memory or context
|
|
544
|
+
- Its own model and system prompt
|
|
545
|
+
- Full tool access (read, write, edit, bash, grep, find, ls)
|
|
546
|
+
|
|
547
|
+
3. **Write self-contained task descriptions** using this pattern:
|
|
548
|
+
- CONTEXT: What exists, relevant file paths, current state
|
|
549
|
+
- TASK: Exactly what to implement/analyze/test
|
|
550
|
+
- FORMAT: Expected output (files created, test results, etc.)
|
|
551
|
+
- CONSTRAINTS: Rules, conventions, things to avoid
|
|
552
|
+
|
|
553
|
+
4. **Assign agents**: explore (read-only analysis), plan (design), code (implementation), test (write+run tests), review (quality audit)
|
|
554
|
+
|
|
555
|
+
5. **Set dependencies** to maximize parallelism:
|
|
556
|
+
- Tasks without dependencies → same wave → run simultaneously
|
|
557
|
+
- Only add a dependency when a task truly needs another's output
|
|
558
|
+
- Typical flow: explore(wave 1) → plan(wave 2) → code(wave 3) → test(wave 4) → review(wave 5)
|
|
559
|
+
- But independent code tasks can run in parallel within the same wave
|
|
560
|
+
|
|
561
|
+
6. **Call the orchestrate tool** with all structured data. It handles execution automatically.`
|
|
460
562
|
);
|
|
461
563
|
},
|
|
462
564
|
});
|