pi-crew 0.1.21 → 0.1.22
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/docs/source-runtime-refactor-map.md +83 -0
- package/package.json +1 -1
- package/src/extension/team-tool.ts +2 -43
- package/src/runtime/async-runner.ts +12 -7
- package/src/runtime/background-runner.ts +4 -2
- package/src/runtime/child-pi.ts +12 -7
- package/src/runtime/concurrency.ts +1 -1
- package/src/runtime/parallel-research.ts +44 -0
- package/src/runtime/team-runner.ts +16 -6
- package/teams/parallel-research.team.md +1 -1
- package/workflows/parallel-research.workflow.md +1 -5
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# pi-crew runtime refactor source map
|
|
2
|
+
|
|
3
|
+
This document records the source projects used as the baseline for the pi-crew subagent/runtime refactor. The goal is to avoid ad-hoc fixes in critical process orchestration paths and instead align pi-crew with proven Pi extension patterns.
|
|
4
|
+
|
|
5
|
+
## Source/pi-subagents
|
|
6
|
+
|
|
7
|
+
Primary source for child-process worker execution.
|
|
8
|
+
|
|
9
|
+
- `pi-spawn.ts`: robust Pi CLI resolution on Windows and package installs.
|
|
10
|
+
- `async-execution.ts`: detached async runner with `windowsHide: true` to avoid blank console windows.
|
|
11
|
+
- `subagent-runner.ts`: streaming child Pi process runner, output capture, result extraction.
|
|
12
|
+
- `post-exit-stdio-guard.ts`: guards for child processes that exit before stdio fully closes.
|
|
13
|
+
- `result-watcher.ts` and `async-job-tracker.ts`: durable async job/result observation patterns.
|
|
14
|
+
- `model-fallback.ts`: model fallback policy independent of hardcoded provider assumptions.
|
|
15
|
+
- `subagent-control.ts`, `run-status.ts`: status and control semantics.
|
|
16
|
+
|
|
17
|
+
pi-crew alignment:
|
|
18
|
+
|
|
19
|
+
- Background runner and child worker spawn options now explicitly set `windowsHide: true`.
|
|
20
|
+
- Parallel research no longer gates all shard workers behind a single discover worker.
|
|
21
|
+
- Further work should consolidate `child-pi.ts`, `async-runner.ts`, and `subagent-manager.ts` into a durable-first subagent runtime module.
|
|
22
|
+
|
|
23
|
+
## Source/pi-subagents2
|
|
24
|
+
|
|
25
|
+
Primary source for higher-level agent management and UI patterns.
|
|
26
|
+
|
|
27
|
+
- `src/agent-manager.ts`: agent lifecycle registry boundaries.
|
|
28
|
+
- `src/agent-runner.ts`: invocation/run abstraction separate from UI registration.
|
|
29
|
+
- `src/model-resolver.ts`: cleaner model resolution responsibility.
|
|
30
|
+
- `src/output-file.ts`: output file abstraction.
|
|
31
|
+
- `src/ui/agent-widget.ts`, `src/ui/conversation-viewer.ts`: compact live status and transcript viewing.
|
|
32
|
+
|
|
33
|
+
pi-crew alignment:
|
|
34
|
+
|
|
35
|
+
- Keep `Agent`/`crew_agent` tools as thin adapters over a durable manager.
|
|
36
|
+
- Avoid storing essential run mapping in memory only.
|
|
37
|
+
- Keep UI active-only and file-backed.
|
|
38
|
+
|
|
39
|
+
## Source/pi-mono
|
|
40
|
+
|
|
41
|
+
Primary source for Pi extension API/lifecycle constraints.
|
|
42
|
+
|
|
43
|
+
- `packages/coding-agent/src/core/extensions/types.ts`: extension context/tool contracts.
|
|
44
|
+
- `packages/coding-agent/src/core/extensions/runner.ts`: extension execution boundaries.
|
|
45
|
+
- `packages/coding-agent/src/core/model-registry.ts`: available model discovery.
|
|
46
|
+
- `packages/coding-agent/src/modes/interactive/interactive-mode.ts`: session lifecycle/UI behavior.
|
|
47
|
+
|
|
48
|
+
pi-crew alignment:
|
|
49
|
+
|
|
50
|
+
- Treat session-bound foreground workers differently from explicit async background workers.
|
|
51
|
+
- Do not assume hardcoded providers/models.
|
|
52
|
+
- Use Pi-native UI calls without modal auto-open by default.
|
|
53
|
+
|
|
54
|
+
## Source/pi-powerbar, pi-plan, pi-diff-review, pi-extensions*
|
|
55
|
+
|
|
56
|
+
Sources for UI and small-extension patterns.
|
|
57
|
+
|
|
58
|
+
- `pi-powerbar/src/powerbar/*`: low-noise status segment publishing.
|
|
59
|
+
- `pi-plan/src/plan-action-ui.ts`: action-oriented UI without persistent heavy overlays.
|
|
60
|
+
- `pi-diff-review/src/*`: command/tool registration and review UX patterns.
|
|
61
|
+
- `pi-extensions2/files-widget/*`: file-backed UI composition and navigation.
|
|
62
|
+
|
|
63
|
+
pi-crew alignment:
|
|
64
|
+
|
|
65
|
+
- Keep persistent widget active-only.
|
|
66
|
+
- Prefer manual dashboard/transcript commands for history.
|
|
67
|
+
- Avoid expensive render scans and auto-opening focus-capturing overlays.
|
|
68
|
+
|
|
69
|
+
## Current refactor checkpoints
|
|
70
|
+
|
|
71
|
+
- [x] Hide Windows console windows for background runner and child Pi workers.
|
|
72
|
+
- [x] Make parallel research shard workers start in parallel instead of depending on a single discover worker.
|
|
73
|
+
- [x] Keep direct-agent reconstruction gated by `workflow === "direct-agent"` only.
|
|
74
|
+
- [x] Persist subagent records and recover terminal results after restart.
|
|
75
|
+
- [x] Fail fast for unrecoverable persisted records without `runId` instead of hanging.
|
|
76
|
+
- [x] Persist direct-agent model override into task state for background/resume reconstruction.
|
|
77
|
+
|
|
78
|
+
## Remaining larger subsystem work
|
|
79
|
+
|
|
80
|
+
- Consolidate subagent runtime into `src/subagents/*` or equivalent durable-first module.
|
|
81
|
+
- Move model routing transparency into persisted task/subagent records: requested model, selected model, fallback chain, fallback reason.
|
|
82
|
+
- Add real integration smoke scripts for Windows process visibility, async restart recovery, and multi-shard fanout.
|
|
83
|
+
- Add adaptive planner repair/retry for invalid JSON instead of immediate block when safe.
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
|
5
5
|
import { allAgents, discoverAgents } from "../agents/discover-agents.ts";
|
|
6
6
|
import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
|
|
7
7
|
import { allWorkflows, discoverWorkflows } from "../workflows/discover-workflows.ts";
|
|
8
|
-
import type { WorkflowConfig
|
|
8
|
+
import type { WorkflowConfig } from "../workflows/workflow-config.ts";
|
|
9
9
|
import { effectiveAutonomousConfig, loadConfig, updateAutonomousConfig, updateConfig, type PiTeamsAutonomousConfig, type PiTeamsConfig } from "../config/config.ts";
|
|
10
10
|
import { projectPiRoot, userPiRoot } from "../utils/paths.ts";
|
|
11
11
|
import type { TeamToolParamsValue } from "../schema/team-tool-schema.ts";
|
|
@@ -47,6 +47,7 @@ import { appendLiveAgentControlRequest } from "../runtime/live-agent-control.ts"
|
|
|
47
47
|
import { liveControlRealtimeMessage, publishLiveControlRealtime } from "../runtime/live-control-realtime.ts";
|
|
48
48
|
import { formatTaskGraphLines, waitingReason } from "../runtime/task-display.ts";
|
|
49
49
|
import { directTeamAndWorkflowFromRun } from "../runtime/direct-run.ts";
|
|
50
|
+
import { expandParallelResearchWorkflow } from "../runtime/parallel-research.ts";
|
|
50
51
|
|
|
51
52
|
export interface TeamToolDetails {
|
|
52
53
|
action: string;
|
|
@@ -172,48 +173,6 @@ function commandExists(command: string, args: string[]): { ok: boolean; detail:
|
|
|
172
173
|
return { ok: false, detail: output.error?.message ?? firstOutputLine(output.stdout, output.stderr) };
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
function sourcePiProjects(cwd: string): string[] {
|
|
176
|
-
const sourceDir = path.join(cwd, "Source");
|
|
177
|
-
try {
|
|
178
|
-
return fs.readdirSync(sourceDir, { withFileTypes: true })
|
|
179
|
-
.filter((entry) => entry.isDirectory() && entry.name.startsWith("pi-"))
|
|
180
|
-
.map((entry) => `Source/${entry.name}`)
|
|
181
|
-
.sort();
|
|
182
|
-
} catch {
|
|
183
|
-
return [];
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function chunkProjects(projects: string[], target = 4): string[][] {
|
|
188
|
-
const chunks = Array.from({ length: Math.min(Math.max(1, target), Math.max(1, projects.length)) }, () => [] as string[]);
|
|
189
|
-
projects.forEach((project, index) => chunks[index % chunks.length]!.push(project));
|
|
190
|
-
return chunks.filter((chunk) => chunk.length > 0);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function expandParallelResearchWorkflow(workflow: WorkflowConfig, cwd: string): WorkflowConfig {
|
|
194
|
-
if (workflow.name !== "parallel-research") return workflow;
|
|
195
|
-
const projects = sourcePiProjects(cwd);
|
|
196
|
-
if (projects.length === 0) return workflow;
|
|
197
|
-
const chunks = chunkProjects(projects, Math.min(6, Math.max(4, Math.ceil(projects.length / 4))));
|
|
198
|
-
const exploreSteps: WorkflowStep[] = chunks.map((paths, index) => ({
|
|
199
|
-
id: `explore-shard-${index + 1}`,
|
|
200
|
-
role: "explorer",
|
|
201
|
-
dependsOn: ["discover"],
|
|
202
|
-
parallelGroup: "explore",
|
|
203
|
-
reads: paths,
|
|
204
|
-
task: [`Explore this dynamic shard for: {goal}`, "", "Paths:", ...paths.map((item) => `- ${item}`), "", "Focus on purpose, architecture, runtime/UI patterns, package config, docs, and lessons for pi-crew."].join("\n"),
|
|
205
|
-
}));
|
|
206
|
-
return {
|
|
207
|
-
...workflow,
|
|
208
|
-
steps: [
|
|
209
|
-
{ id: "discover", role: "explorer", task: `Discover and validate ${projects.length} pi-* projects for: {goal}\n\nProjects:\n${projects.map((item) => `- ${item}`).join("\n")}` },
|
|
210
|
-
...exploreSteps,
|
|
211
|
-
{ id: "synthesize", role: "analyst", dependsOn: exploreSteps.map((step) => step.id), task: "Synthesize all dynamic shard findings. Identify common patterns, gaps, and concrete recommendations." },
|
|
212
|
-
{ id: "write", role: "writer", dependsOn: ["synthesize"], output: "research-summary.md", task: "Write a concise final summary with evidence, risks, and actionable next steps." },
|
|
213
|
-
],
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
|
|
217
176
|
function effectiveRunConfig(base: PiTeamsConfig, rawOverride: unknown): PiTeamsConfig {
|
|
218
177
|
const patch = configPatchFromConfig(rawOverride);
|
|
219
178
|
return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { spawn, type SpawnOptions } from "node:child_process";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
@@ -36,6 +36,16 @@ export interface SpawnBackgroundTeamRunResult {
|
|
|
36
36
|
logPath: string;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
export function buildBackgroundSpawnOptions(manifest: TeamRunManifest, logFd: number): SpawnOptions {
|
|
40
|
+
return {
|
|
41
|
+
cwd: manifest.cwd,
|
|
42
|
+
detached: true,
|
|
43
|
+
stdio: ["ignore", logFd, logFd],
|
|
44
|
+
env: { ...process.env },
|
|
45
|
+
windowsHide: true,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
39
49
|
export function spawnBackgroundTeamRun(manifest: TeamRunManifest): SpawnBackgroundTeamRunResult {
|
|
40
50
|
const runnerPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "background-runner.ts");
|
|
41
51
|
const logPath = path.join(manifest.stateRoot, "background.log");
|
|
@@ -43,12 +53,7 @@ export function spawnBackgroundTeamRun(manifest: TeamRunManifest): SpawnBackgrou
|
|
|
43
53
|
const logFd = fs.openSync(logPath, "a");
|
|
44
54
|
const command = getBackgroundRunnerCommand(runnerPath, manifest.cwd, manifest.runId);
|
|
45
55
|
fs.appendFileSync(logPath, `[pi-crew] background loader=${command.loader}\n`, "utf-8");
|
|
46
|
-
const child = spawn(process.execPath, command.args,
|
|
47
|
-
cwd: manifest.cwd,
|
|
48
|
-
detached: true,
|
|
49
|
-
stdio: ["ignore", logFd, logFd],
|
|
50
|
-
env: { ...process.env },
|
|
51
|
-
});
|
|
56
|
+
const child = spawn(process.execPath, command.args, buildBackgroundSpawnOptions(manifest, logFd));
|
|
52
57
|
child.unref();
|
|
53
58
|
fs.closeSync(logFd);
|
|
54
59
|
return { pid: child.pid, logPath };
|
|
@@ -7,6 +7,7 @@ import { loadConfig } from "../config/config.ts";
|
|
|
7
7
|
import { executeTeamRun } from "./team-runner.ts";
|
|
8
8
|
import { resolveCrewRuntime } from "./runtime-resolver.ts";
|
|
9
9
|
import { directTeamAndWorkflowFromRun } from "./direct-run.ts";
|
|
10
|
+
import { expandParallelResearchWorkflow } from "./parallel-research.ts";
|
|
10
11
|
|
|
11
12
|
function argValue(name: string): string | undefined {
|
|
12
13
|
const index = process.argv.indexOf(name);
|
|
@@ -29,8 +30,9 @@ async function main(): Promise<void> {
|
|
|
29
30
|
const direct = directTeamAndWorkflowFromRun(manifest, tasks, agents);
|
|
30
31
|
const team = direct?.team ?? allTeams(discoverTeams(cwd)).find((candidate) => candidate.name === manifest.team);
|
|
31
32
|
if (!team) throw new Error(`Team '${manifest.team}' not found.`);
|
|
32
|
-
const
|
|
33
|
-
if (!
|
|
33
|
+
const baseWorkflow = direct?.workflow ?? allWorkflows(discoverWorkflows(cwd)).find((candidate) => candidate.name === manifest.workflow);
|
|
34
|
+
if (!baseWorkflow) throw new Error(`Workflow '${manifest.workflow ?? ""}' not found.`);
|
|
35
|
+
const workflow = expandParallelResearchWorkflow(baseWorkflow, cwd);
|
|
34
36
|
const loadedConfig = loadConfig(cwd);
|
|
35
37
|
const runtime = await resolveCrewRuntime(loadedConfig.config);
|
|
36
38
|
const executeWorkers = runtime.kind !== "scaffold";
|
package/src/runtime/child-pi.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn, type ChildProcess } from "node:child_process";
|
|
1
|
+
import { spawn, type ChildProcess, type SpawnOptions } from "node:child_process";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { AgentConfig } from "../agents/agent-config.ts";
|
|
@@ -66,6 +66,16 @@ export interface ChildPiRunResult {
|
|
|
66
66
|
error?: string;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
export function buildChildPiSpawnOptions(cwd: string, env: NodeJS.ProcessEnv): SpawnOptions {
|
|
70
|
+
return {
|
|
71
|
+
cwd,
|
|
72
|
+
env,
|
|
73
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
74
|
+
detached: process.platform !== "win32",
|
|
75
|
+
windowsHide: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
69
79
|
function appendTranscript(input: ChildPiRunInput, line: string): void {
|
|
70
80
|
if (!input.transcriptPath) return;
|
|
71
81
|
fs.mkdirSync(path.dirname(input.transcriptPath), { recursive: true });
|
|
@@ -225,12 +235,7 @@ export async function runChildPi(input: ChildPiRunInput): Promise<ChildPiRunResu
|
|
|
225
235
|
const spawnSpec = getPiSpawnCommand(built.args);
|
|
226
236
|
try {
|
|
227
237
|
return await new Promise<ChildPiRunResult>((resolve) => {
|
|
228
|
-
const child = spawn(spawnSpec.command, spawnSpec.args, {
|
|
229
|
-
cwd: input.cwd,
|
|
230
|
-
env: { ...process.env, ...built.env },
|
|
231
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
232
|
-
detached: process.platform !== "win32",
|
|
233
|
-
});
|
|
238
|
+
const child = spawn(spawnSpec.command, spawnSpec.args, buildChildPiSpawnOptions(input.cwd, { ...process.env, ...built.env }));
|
|
234
239
|
if (child.pid) activeChildProcesses.set(child.pid, child);
|
|
235
240
|
let stdout = "";
|
|
236
241
|
let stderr = "";
|
|
@@ -15,7 +15,7 @@ export interface BatchConcurrencyDecision {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function defaultWorkflowConcurrency(workflowName: string): number {
|
|
18
|
-
if (workflowName === "parallel-research") return
|
|
18
|
+
if (workflowName === "parallel-research") return 6;
|
|
19
19
|
if (workflowName === "research") return 2;
|
|
20
20
|
if (workflowName === "implementation" || workflowName === "review" || workflowName === "default") return 2;
|
|
21
21
|
return 1;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { WorkflowConfig, WorkflowStep } from "../workflows/workflow-config.ts";
|
|
4
|
+
|
|
5
|
+
export function sourcePiProjects(cwd: string): string[] {
|
|
6
|
+
const sourceDir = path.join(cwd, "Source");
|
|
7
|
+
try {
|
|
8
|
+
return fs.readdirSync(sourceDir, { withFileTypes: true })
|
|
9
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith("pi-"))
|
|
10
|
+
.map((entry) => `Source/${entry.name}`)
|
|
11
|
+
.sort();
|
|
12
|
+
} catch {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function chunkProjects(projects: string[], target = 6): string[][] {
|
|
18
|
+
const chunks = Array.from({ length: Math.min(Math.max(1, target), Math.max(1, projects.length)) }, () => [] as string[]);
|
|
19
|
+
projects.forEach((project, index) => chunks[index % chunks.length]!.push(project));
|
|
20
|
+
return chunks.filter((chunk) => chunk.length > 0);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function expandParallelResearchWorkflow(workflow: WorkflowConfig, cwd: string): WorkflowConfig {
|
|
24
|
+
if (workflow.name !== "parallel-research") return workflow;
|
|
25
|
+
const projects = sourcePiProjects(cwd);
|
|
26
|
+
if (projects.length === 0) return workflow;
|
|
27
|
+
const chunks = chunkProjects(projects, Math.min(8, Math.max(4, Math.ceil(projects.length / 3))));
|
|
28
|
+
const exploreSteps: WorkflowStep[] = chunks.map((paths, index) => ({
|
|
29
|
+
id: `explore-shard-${index + 1}`,
|
|
30
|
+
role: "explorer",
|
|
31
|
+
parallelGroup: "explore",
|
|
32
|
+
reads: paths,
|
|
33
|
+
task: [`Explore this dynamic shard for: {goal}`, "", "Paths:", ...paths.map((item) => `- ${item}`), "", "Focus on purpose, architecture, runtime/UI patterns, package config, docs, and lessons for pi-crew."].join("\n"),
|
|
34
|
+
}));
|
|
35
|
+
return {
|
|
36
|
+
...workflow,
|
|
37
|
+
steps: [
|
|
38
|
+
{ id: "discover", role: "explorer", parallelGroup: "inventory", task: `Quickly inventory and validate ${projects.length} pi-* projects for: {goal}\n\nProjects:\n${projects.map((item) => `- ${item}`).join("\n")}\n\nDo not block shard work; summarize routing notes only.` },
|
|
39
|
+
...exploreSteps,
|
|
40
|
+
{ id: "synthesize", role: "analyst", dependsOn: exploreSteps.map((step) => step.id), task: "Synthesize all dynamic shard findings. Identify common patterns, gaps, and concrete recommendations. Use discover output if available, but prioritize completed shard outputs." },
|
|
41
|
+
{ id: "write", role: "writer", dependsOn: ["synthesize"], output: "research-summary.md", task: "Write a concise final summary with evidence, risks, and actionable next steps." },
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -63,15 +63,25 @@ function mergeArtifacts(items: ArtifactDescriptor[]): ArtifactDescriptor[] {
|
|
|
63
63
|
return [...byPath.values()];
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
function
|
|
66
|
+
function isNonTerminalTaskStatus(status: TeamTaskState["status"]): boolean {
|
|
67
|
+
return status === "queued" || status === "running";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function shouldMergeTaskUpdate(current: TeamTaskState, updated: TeamTaskState): boolean {
|
|
71
|
+
// Parallel workers receive the same input snapshot. A later result may still
|
|
72
|
+
// contain stale queued/running copies of tasks that another worker already
|
|
73
|
+
// completed. Never let those stale snapshots regress durable task state.
|
|
74
|
+
if (!isNonTerminalTaskStatus(current.status) && isNonTerminalTaskStatus(updated.status)) return false;
|
|
75
|
+
return updated.status !== current.status || updated.finishedAt !== current.finishedAt || updated.startedAt !== current.startedAt || Boolean(updated.resultArtifact) || Boolean(updated.error) || Boolean(updated.modelAttempts?.length) || Boolean(updated.usage);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function __test__mergeTaskUpdates(base: TeamTaskState[], results: Array<{ tasks: TeamTaskState[] }>): TeamTaskState[] {
|
|
67
79
|
let merged = base;
|
|
68
80
|
for (const result of results) {
|
|
69
81
|
for (const updated of result.tasks) {
|
|
70
82
|
const current = merged.find((task) => task.id === updated.id);
|
|
71
|
-
if (!current) continue;
|
|
72
|
-
|
|
73
|
-
merged = merged.map((task) => task.id === updated.id ? updated : task);
|
|
74
|
-
}
|
|
83
|
+
if (!current || !shouldMergeTaskUpdate(current, updated)) continue;
|
|
84
|
+
merged = merged.map((task) => task.id === updated.id ? updated : task);
|
|
75
85
|
}
|
|
76
86
|
}
|
|
77
87
|
return refreshTaskGraphQueues(merged);
|
|
@@ -341,7 +351,7 @@ export async function executeTeamRun(input: ExecuteTeamRunInput): Promise<{ mani
|
|
|
341
351
|
return runTeamTask({ manifest, tasks, task, step, agent, signal: input.signal, executeWorkers: input.executeWorkers, runtimeKind: input.runtime?.kind, runtimeConfig: input.runtimeConfig, parentContext: input.parentContext, parentModel: input.parentModel, modelRegistry: input.modelRegistry, modelOverride: input.modelOverride, limits: input.limits });
|
|
342
352
|
}));
|
|
343
353
|
manifest = { ...results.at(-1)!.manifest, artifacts: mergeArtifacts([manifest.artifacts, ...results.map((item) => item.manifest.artifacts)].flat()) };
|
|
344
|
-
tasks =
|
|
354
|
+
tasks = __test__mergeTaskUpdates(tasks, results);
|
|
345
355
|
const injectedAfterBatch = injectAdaptivePlanIfReady({ manifest, tasks, workflow, team: input.team });
|
|
346
356
|
if (injectedAfterBatch.missingPlan) {
|
|
347
357
|
tasks = markBlocked(tasks, "Adaptive planner did not produce a valid subagent plan.");
|
|
@@ -3,7 +3,7 @@ name: parallel-research
|
|
|
3
3
|
description: Parallel research team for multi-project/source audits
|
|
4
4
|
workspaceMode: single
|
|
5
5
|
defaultWorkflow: parallel-research
|
|
6
|
-
maxConcurrency:
|
|
6
|
+
maxConcurrency: 6
|
|
7
7
|
triggers: đọc sâu, deep read, deep research, source audit, multiple projects, parallel research, pi-*
|
|
8
8
|
category: research
|
|
9
9
|
cost: cheap
|
|
@@ -10,28 +10,24 @@ Discover the relevant files/projects for: {goal}. Return a shard plan with paths
|
|
|
10
10
|
|
|
11
11
|
## explore-core
|
|
12
12
|
role: explorer
|
|
13
|
-
dependsOn: discover
|
|
14
13
|
parallelGroup: explore
|
|
15
14
|
|
|
16
15
|
Explore the core/runtime shard from the discover output. Focus on architecture, package config, docs, and reusable patterns for: {goal}
|
|
17
16
|
|
|
18
17
|
## explore-ui
|
|
19
18
|
role: explorer
|
|
20
|
-
dependsOn: discover
|
|
21
19
|
parallelGroup: explore
|
|
22
20
|
|
|
23
21
|
Explore the UI/TUI/extension-interface shard from the discover output. Focus on widgets, overlays, commands, status bars, package config, docs, and reusable patterns for: {goal}
|
|
24
22
|
|
|
25
23
|
## explore-runtime
|
|
26
24
|
role: explorer
|
|
27
|
-
dependsOn: discover
|
|
28
25
|
parallelGroup: explore
|
|
29
26
|
|
|
30
27
|
Explore the worker/runtime/subagent/runtime-control shard from the discover output. Focus on process/session/runtime orchestration, event streams, logs, package config, docs, and reusable patterns for: {goal}
|
|
31
28
|
|
|
32
29
|
## explore-extensions
|
|
33
30
|
role: explorer
|
|
34
|
-
dependsOn: discover
|
|
35
31
|
parallelGroup: explore
|
|
36
32
|
|
|
37
33
|
Explore the extension bundle/small-package shard from the discover output. Focus on package config, extension registration, commands/tools, docs, and reusable patterns for: {goal}
|
|
@@ -40,7 +36,7 @@ Explore the extension bundle/small-package shard from the discover output. Focus
|
|
|
40
36
|
role: analyst
|
|
41
37
|
dependsOn: explore-core, explore-ui, explore-runtime, explore-extensions
|
|
42
38
|
|
|
43
|
-
Synthesize all shard findings. Identify common patterns, gaps, and concrete recommendations.
|
|
39
|
+
Synthesize all shard findings. Use discover output if available, but do not require it. Identify common patterns, gaps, and concrete recommendations.
|
|
44
40
|
|
|
45
41
|
## write
|
|
46
42
|
role: writer
|