@pi-agents/orchid 0.1.0-beta.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/CHANGELOG.md +41 -0
- package/LICENSE +21 -0
- package/README.md +246 -0
- package/agents/AGENTS-MANIFEST.md +42 -0
- package/agents/brain.md +42 -0
- package/agents/context-builder.md +46 -0
- package/agents/delegate.md +12 -0
- package/agents/dev-1.md +42 -0
- package/agents/oracle.md +73 -0
- package/agents/planner.md +55 -0
- package/agents/researcher.md +52 -0
- package/agents/reviewer.md +79 -0
- package/agents/scout.md +50 -0
- package/agents/tester.md +45 -0
- package/agents/worker.md +55 -0
- package/extensions/ralph.ts +1 -0
- package/extensions/reviewer-extension.ts +125 -0
- package/extensions/task-orchestrator.ts +28 -0
- package/package.json +63 -0
- package/prompts/gather-context-and-clarify.md +13 -0
- package/prompts/parallel-cleanup.md +59 -0
- package/prompts/parallel-context-build.md +53 -0
- package/prompts/parallel-handoff-plan.md +59 -0
- package/prompts/parallel-research.md +50 -0
- package/prompts/parallel-review.md +54 -0
- package/prompts/review-loop.md +41 -0
- package/skills/orchid/SKILL.md +214 -0
- package/skills/orchid/orchid-cleanup/SKILL.md +122 -0
- package/skills/orchid/orchid-converge/SKILL.md +124 -0
- package/skills/orchid/orchid-decompose/SKILL.md +201 -0
- package/skills/orchid/orchid-doctor/SKILL.md +162 -0
- package/skills/orchid/orchid-investigate/SKILL.md +102 -0
- package/skills/orchid/orchid-launch/SKILL.md +147 -0
- package/skills/ralph/SKILL.md +73 -0
- package/skills/subagents/pi-subagents/SKILL.md +813 -0
- package/src/index.ts +7 -0
- package/src/orchestrator/abort.ts +534 -0
- package/src/orchestrator/agent-bridge-extension.ts +1020 -0
- package/src/orchestrator/agent-host.ts +954 -0
- package/src/orchestrator/cleanup.ts +776 -0
- package/src/orchestrator/config-loader.ts +1412 -0
- package/src/orchestrator/config-schema.ts +690 -0
- package/src/orchestrator/config.ts +81 -0
- package/src/orchestrator/context-window.ts +66 -0
- package/src/orchestrator/diagnostic-reports.ts +475 -0
- package/src/orchestrator/diagnostics.ts +394 -0
- package/src/orchestrator/discovery.ts +1833 -0
- package/src/orchestrator/engine-worker.ts +415 -0
- package/src/orchestrator/engine.ts +5940 -0
- package/src/orchestrator/execution.ts +3104 -0
- package/src/orchestrator/extension.ts +5934 -0
- package/src/orchestrator/formatting.ts +785 -0
- package/src/orchestrator/git.ts +88 -0
- package/src/orchestrator/index.ts +28 -0
- package/src/orchestrator/lane-runner.ts +1787 -0
- package/src/orchestrator/mailbox.ts +780 -0
- package/src/orchestrator/merge.ts +3414 -0
- package/src/orchestrator/messages.ts +1062 -0
- package/src/orchestrator/migrations.ts +278 -0
- package/src/orchestrator/naming.ts +117 -0
- package/src/orchestrator/path-resolver.ts +275 -0
- package/src/orchestrator/persistence.ts +2625 -0
- package/src/orchestrator/process-registry.ts +452 -0
- package/src/orchestrator/quality-gate.ts +1085 -0
- package/src/orchestrator/resume.ts +3488 -0
- package/src/orchestrator/sessions.ts +57 -0
- package/src/orchestrator/settings-loader.ts +136 -0
- package/src/orchestrator/settings-tui.ts +2208 -0
- package/src/orchestrator/sidecar-telemetry.ts +267 -0
- package/src/orchestrator/supervisor.ts +4548 -0
- package/src/orchestrator/task-executor-core.ts +675 -0
- package/src/orchestrator/tmux-compat.ts +37 -0
- package/src/orchestrator/tool-allowlist-constants.ts +37 -0
- package/src/orchestrator/types.ts +4465 -0
- package/src/orchestrator/verification.ts +547 -0
- package/src/orchestrator/waves.ts +1564 -0
- package/src/orchestrator/workspace.ts +707 -0
- package/src/orchestrator/worktree.ts +2725 -0
- package/src/ralph/index.ts +825 -0
- package/src/subagents/agents/agent-management.ts +648 -0
- package/src/subagents/agents/agent-scope.ts +6 -0
- package/src/subagents/agents/agent-selection.ts +23 -0
- package/src/subagents/agents/agent-serializer.ts +86 -0
- package/src/subagents/agents/agents.ts +832 -0
- package/src/subagents/agents/chain-serializer.ts +137 -0
- package/src/subagents/agents/frontmatter.ts +29 -0
- package/src/subagents/agents/identity.ts +30 -0
- package/src/subagents/agents/skills.ts +632 -0
- package/src/subagents/extension/config.ts +16 -0
- package/src/subagents/extension/control-notices.ts +92 -0
- package/src/subagents/extension/doctor.ts +199 -0
- package/src/subagents/extension/fanout-child.ts +170 -0
- package/src/subagents/extension/index.ts +573 -0
- package/src/subagents/extension/schemas.ts +168 -0
- package/src/subagents/intercom/intercom-bridge.ts +379 -0
- package/src/subagents/intercom/result-intercom.ts +377 -0
- package/src/subagents/runs/background/async-execution.ts +712 -0
- package/src/subagents/runs/background/async-job-tracker.ts +310 -0
- package/src/subagents/runs/background/async-resume.ts +345 -0
- package/src/subagents/runs/background/async-status.ts +325 -0
- package/src/subagents/runs/background/completion-dedupe.ts +63 -0
- package/src/subagents/runs/background/notify.ts +108 -0
- package/src/subagents/runs/background/parallel-groups.ts +45 -0
- package/src/subagents/runs/background/result-watcher.ts +307 -0
- package/src/subagents/runs/background/run-id-resolver.ts +83 -0
- package/src/subagents/runs/background/run-status.ts +269 -0
- package/src/subagents/runs/background/stale-run-reconciler.ts +336 -0
- package/src/subagents/runs/background/subagent-runner.ts +1808 -0
- package/src/subagents/runs/background/top-level-async.ts +13 -0
- package/src/subagents/runs/foreground/chain-clarify.ts +1333 -0
- package/src/subagents/runs/foreground/chain-execution.ts +938 -0
- package/src/subagents/runs/foreground/execution.ts +918 -0
- package/src/subagents/runs/foreground/subagent-executor.ts +2527 -0
- package/src/subagents/runs/shared/completion-guard.ts +147 -0
- package/src/subagents/runs/shared/long-running-guard.ts +175 -0
- package/src/subagents/runs/shared/mcp-direct-tool-allowlist.ts +365 -0
- package/src/subagents/runs/shared/model-fallback.ts +103 -0
- package/src/subagents/runs/shared/nested-events.ts +819 -0
- package/src/subagents/runs/shared/nested-path.ts +52 -0
- package/src/subagents/runs/shared/nested-render.ts +115 -0
- package/src/subagents/runs/shared/parallel-utils.ts +109 -0
- package/src/subagents/runs/shared/pi-args.ts +220 -0
- package/src/subagents/runs/shared/pi-spawn.ts +115 -0
- package/src/subagents/runs/shared/run-history.ts +60 -0
- package/src/subagents/runs/shared/single-output.ts +164 -0
- package/src/subagents/runs/shared/subagent-control.ts +226 -0
- package/src/subagents/runs/shared/subagent-prompt-runtime.ts +170 -0
- package/src/subagents/runs/shared/worktree.ts +577 -0
- package/src/subagents/shared/artifacts.ts +98 -0
- package/src/subagents/shared/atomic-json.ts +16 -0
- package/src/subagents/shared/file-coalescer.ts +40 -0
- package/src/subagents/shared/fork-context.ts +76 -0
- package/src/subagents/shared/formatters.ts +133 -0
- package/src/subagents/shared/jsonl-writer.ts +81 -0
- package/src/subagents/shared/model-info.ts +78 -0
- package/src/subagents/shared/post-exit-stdio-guard.ts +85 -0
- package/src/subagents/shared/session-identity.ts +10 -0
- package/src/subagents/shared/session-tokens.ts +44 -0
- package/src/subagents/shared/settings.ts +397 -0
- package/src/subagents/shared/status-format.ts +49 -0
- package/src/subagents/shared/types.ts +822 -0
- package/src/subagents/shared/utils.ts +450 -0
- package/src/subagents/slash/prompt-template-bridge.ts +397 -0
- package/src/subagents/slash/slash-bridge.ts +174 -0
- package/src/subagents/slash/slash-commands.ts +528 -0
- package/src/subagents/slash/slash-live-state.ts +292 -0
- package/src/subagents/tui/render-helpers.ts +80 -0
- package/src/subagents/tui/render.ts +1358 -0
- package/templates/agents/local/supervisor.md +33 -0
- package/templates/agents/local/task-merger.md +27 -0
- package/templates/agents/local/task-reviewer.md +30 -0
- package/templates/agents/local/task-worker.md +34 -0
- package/templates/agents/supervisor-routing.md +92 -0
- package/templates/agents/supervisor.md +229 -0
- package/templates/agents/task-merger.md +214 -0
- package/templates/agents/task-reviewer.md +260 -0
- package/templates/agents/task-worker-segment.md +44 -0
- package/templates/agents/task-worker.md +557 -0
- package/templates/tasks/CONTEXT.md +30 -0
- package/templates/tasks/EXAMPLE-001-hello-world/PROMPT.md +98 -0
- package/templates/tasks/EXAMPLE-001-hello-world/STATUS.md +73 -0
- package/templates/tasks/EXAMPLE-002-parallel-smoke/PROMPT.md +97 -0
- package/templates/tasks/EXAMPLE-002-parallel-smoke/STATUS.md +73 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async execution logic for subagent tool
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import * as os from "node:os";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { createRequire } from "node:module";
|
|
11
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
12
|
+
import type { AgentConfig } from "../../agents/agents.ts";
|
|
13
|
+
import { applyThinkingSuffix } from "../shared/pi-args.ts";
|
|
14
|
+
import { injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
15
|
+
import { buildChainInstructions, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
16
|
+
import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
17
|
+
import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
18
|
+
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
19
|
+
import { resolveChildCwd } from "../../shared/utils.ts";
|
|
20
|
+
import { buildModelCandidates, resolveModelCandidate, type AvailableModelInfo } from "../shared/model-fallback.ts";
|
|
21
|
+
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
22
|
+
import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
|
|
23
|
+
import {
|
|
24
|
+
type ArtifactConfig,
|
|
25
|
+
type Details,
|
|
26
|
+
type MaxOutputConfig,
|
|
27
|
+
type NestedRouteInfo,
|
|
28
|
+
type ResolvedControlConfig,
|
|
29
|
+
type SubagentRunMode,
|
|
30
|
+
ASYNC_DIR,
|
|
31
|
+
RESULTS_DIR,
|
|
32
|
+
SUBAGENT_ASYNC_STARTED_EVENT,
|
|
33
|
+
TEMP_ROOT_DIR,
|
|
34
|
+
getAsyncConfigPath,
|
|
35
|
+
resolveChildMaxSubagentDepth,
|
|
36
|
+
} from "../../shared/types.ts";
|
|
37
|
+
import { nestedResultsPath, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
|
|
38
|
+
|
|
39
|
+
const require = createRequire(import.meta.url);
|
|
40
|
+
const piPackageRoot = resolvePiPackageRoot();
|
|
41
|
+
|
|
42
|
+
function resolveJitiCliFromPackageJson(packageJsonPath: string): string | undefined {
|
|
43
|
+
if (!fs.existsSync(packageJsonPath)) return undefined;
|
|
44
|
+
const packageRoot = path.dirname(packageJsonPath);
|
|
45
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as {
|
|
46
|
+
bin?: string | Record<string, string>;
|
|
47
|
+
};
|
|
48
|
+
const binField = pkg.bin;
|
|
49
|
+
const binPath = typeof binField === "string"
|
|
50
|
+
? binField
|
|
51
|
+
: binField?.jiti ?? Object.values(binField ?? {})[0];
|
|
52
|
+
const candidates = [binPath, "lib/jiti-cli.mjs"].filter((candidate): candidate is string => Boolean(candidate));
|
|
53
|
+
for (const candidate of candidates) {
|
|
54
|
+
const cliPath = path.resolve(packageRoot, candidate);
|
|
55
|
+
if (fs.existsSync(cliPath)) return cliPath;
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resolveJitiCliPath(): string | undefined {
|
|
61
|
+
const candidates: Array<() => string | undefined> = [
|
|
62
|
+
() => require.resolve("jiti/package.json"),
|
|
63
|
+
() => piPackageRoot
|
|
64
|
+
? createRequire(path.join(piPackageRoot, "package.json")).resolve("jiti/package.json")
|
|
65
|
+
: undefined,
|
|
66
|
+
() => {
|
|
67
|
+
if (!process.argv[1]) return undefined;
|
|
68
|
+
const piEntry = fs.realpathSync(process.argv[1]);
|
|
69
|
+
return createRequire(piEntry).resolve("jiti/package.json");
|
|
70
|
+
},
|
|
71
|
+
() => piPackageRoot ? path.join(piPackageRoot, "node_modules", "jiti", "package.json") : undefined,
|
|
72
|
+
];
|
|
73
|
+
for (const candidate of candidates) {
|
|
74
|
+
try {
|
|
75
|
+
const packageJsonPath = candidate();
|
|
76
|
+
if (!packageJsonPath) continue;
|
|
77
|
+
const cliPath = resolveJitiCliFromPackageJson(packageJsonPath);
|
|
78
|
+
if (cliPath) return cliPath;
|
|
79
|
+
} catch {
|
|
80
|
+
// Candidate not available in this install, continue probing.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const jitiCliPath = resolveJitiCliPath();
|
|
87
|
+
|
|
88
|
+
interface AsyncExecutionContext {
|
|
89
|
+
pi: ExtensionAPI;
|
|
90
|
+
cwd: string;
|
|
91
|
+
currentSessionId: string;
|
|
92
|
+
currentModelProvider?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
interface AsyncChainParams {
|
|
96
|
+
chain: ChainStep[];
|
|
97
|
+
task?: string;
|
|
98
|
+
resultMode?: Exclude<SubagentRunMode, "single">;
|
|
99
|
+
agents: AgentConfig[];
|
|
100
|
+
ctx: AsyncExecutionContext;
|
|
101
|
+
availableModels?: AvailableModelInfo[];
|
|
102
|
+
cwd?: string;
|
|
103
|
+
maxOutput?: MaxOutputConfig;
|
|
104
|
+
artifactsDir?: string;
|
|
105
|
+
artifactConfig: ArtifactConfig;
|
|
106
|
+
shareEnabled: boolean;
|
|
107
|
+
sessionRoot?: string;
|
|
108
|
+
chainSkills?: string[];
|
|
109
|
+
sessionFilesByFlatIndex?: (string | undefined)[];
|
|
110
|
+
maxSubagentDepth: number;
|
|
111
|
+
worktreeSetupHook?: string;
|
|
112
|
+
worktreeSetupHookTimeoutMs?: number;
|
|
113
|
+
controlConfig?: ResolvedControlConfig;
|
|
114
|
+
controlIntercomTarget?: string;
|
|
115
|
+
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
116
|
+
nestedRoute?: NestedRouteInfo;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface AsyncSingleParams {
|
|
120
|
+
agent: string;
|
|
121
|
+
task?: string;
|
|
122
|
+
agentConfig: AgentConfig;
|
|
123
|
+
ctx: AsyncExecutionContext;
|
|
124
|
+
cwd?: string;
|
|
125
|
+
maxOutput?: MaxOutputConfig;
|
|
126
|
+
artifactsDir?: string;
|
|
127
|
+
artifactConfig: ArtifactConfig;
|
|
128
|
+
shareEnabled: boolean;
|
|
129
|
+
sessionRoot?: string;
|
|
130
|
+
sessionFile?: string;
|
|
131
|
+
skills?: string[];
|
|
132
|
+
output?: string | boolean;
|
|
133
|
+
outputMode?: "inline" | "file-only";
|
|
134
|
+
modelOverride?: string;
|
|
135
|
+
availableModels?: AvailableModelInfo[];
|
|
136
|
+
maxSubagentDepth: number;
|
|
137
|
+
worktreeSetupHook?: string;
|
|
138
|
+
worktreeSetupHookTimeoutMs?: number;
|
|
139
|
+
controlConfig?: ResolvedControlConfig;
|
|
140
|
+
controlIntercomTarget?: string;
|
|
141
|
+
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
142
|
+
nestedRoute?: NestedRouteInfo;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
interface AsyncExecutionResult {
|
|
146
|
+
content: Array<{ type: "text"; text: string }>;
|
|
147
|
+
details: Details;
|
|
148
|
+
isError?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function formatAsyncStartedMessage(headline: string): string {
|
|
152
|
+
return [
|
|
153
|
+
headline,
|
|
154
|
+
"",
|
|
155
|
+
"The async run is detached. Do not run sleep timers or polling loops just to wait for it.",
|
|
156
|
+
"If you have independent work, continue that work. If you have nothing else to do until the async result arrives, end your turn now; Pi will deliver the completion when the run finishes.",
|
|
157
|
+
"Use subagent({ action: \"status\", id: \"...\" }) when you need the current status/result, or to inspect a blocked/stale run. Do not poll just to wait.",
|
|
158
|
+
].join("\n");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check if jiti is available for async execution
|
|
163
|
+
*/
|
|
164
|
+
export function isAsyncAvailable(): boolean {
|
|
165
|
+
return jitiCliPath !== undefined;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Spawn the async runner process
|
|
170
|
+
*/
|
|
171
|
+
function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number; error?: string } {
|
|
172
|
+
if (!jitiCliPath) {
|
|
173
|
+
return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const cwdStats = fs.statSync(cwd);
|
|
178
|
+
if (!cwdStats.isDirectory()) {
|
|
179
|
+
return { error: `cwd is not a directory: ${cwd}` };
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
return { error: `cwd does not exist: ${cwd}` };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
fs.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
|
|
186
|
+
const cfgPath = getAsyncConfigPath(suffix);
|
|
187
|
+
fs.writeFileSync(cfgPath, JSON.stringify(cfg));
|
|
188
|
+
const runner = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-runner.ts");
|
|
189
|
+
|
|
190
|
+
const proc = spawn(process.execPath, [jitiCliPath, runner, cfgPath], {
|
|
191
|
+
cwd,
|
|
192
|
+
detached: true,
|
|
193
|
+
stdio: "ignore",
|
|
194
|
+
windowsHide: true,
|
|
195
|
+
});
|
|
196
|
+
proc.on("error", (error) => {
|
|
197
|
+
console.error(`[pi-subagents] async spawn failed: ${error.message}`);
|
|
198
|
+
});
|
|
199
|
+
if (typeof proc.pid !== "number") {
|
|
200
|
+
return { error: `async runner did not produce a pid for cwd: ${cwd}` };
|
|
201
|
+
}
|
|
202
|
+
proc.unref();
|
|
203
|
+
return { pid: proc.pid };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function formatAsyncStartError(mode: SubagentRunMode, message: string): AsyncExecutionResult {
|
|
207
|
+
return {
|
|
208
|
+
content: [{ type: "text", text: message }],
|
|
209
|
+
isError: true,
|
|
210
|
+
details: { mode, results: [] },
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const UNAVAILABLE_SUBAGENT_SKILL_ERROR = "Skills not found: pi-subagents";
|
|
215
|
+
|
|
216
|
+
class UnavailableSubagentSkillError extends Error {}
|
|
217
|
+
class AsyncStartValidationError extends Error {}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Execute a chain asynchronously
|
|
221
|
+
*/
|
|
222
|
+
export function executeAsyncChain(
|
|
223
|
+
id: string,
|
|
224
|
+
params: AsyncChainParams,
|
|
225
|
+
): AsyncExecutionResult {
|
|
226
|
+
const {
|
|
227
|
+
chain,
|
|
228
|
+
agents,
|
|
229
|
+
ctx,
|
|
230
|
+
cwd,
|
|
231
|
+
maxOutput,
|
|
232
|
+
artifactsDir,
|
|
233
|
+
artifactConfig,
|
|
234
|
+
shareEnabled,
|
|
235
|
+
sessionRoot,
|
|
236
|
+
sessionFilesByFlatIndex,
|
|
237
|
+
maxSubagentDepth,
|
|
238
|
+
worktreeSetupHook,
|
|
239
|
+
worktreeSetupHookTimeoutMs,
|
|
240
|
+
controlConfig,
|
|
241
|
+
controlIntercomTarget,
|
|
242
|
+
childIntercomTarget,
|
|
243
|
+
nestedRoute,
|
|
244
|
+
} = params;
|
|
245
|
+
const resultMode = params.resultMode ?? "chain";
|
|
246
|
+
const chainSkills = params.chainSkills ?? [];
|
|
247
|
+
const availableModels = params.availableModels;
|
|
248
|
+
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
249
|
+
const firstStep = chain[0];
|
|
250
|
+
const originalTask = params.task ?? (firstStep
|
|
251
|
+
? (isParallelStep(firstStep) ? firstStep.parallel[0]?.task : (firstStep as SequentialStep).task)
|
|
252
|
+
: undefined);
|
|
253
|
+
|
|
254
|
+
for (const s of chain) {
|
|
255
|
+
const stepAgents = isParallelStep(s)
|
|
256
|
+
? s.parallel.map((t) => t.agent)
|
|
257
|
+
: [(s as SequentialStep).agent];
|
|
258
|
+
for (const agentName of stepAgents) {
|
|
259
|
+
if (!agents.find((x) => x.name === agentName)) {
|
|
260
|
+
return {
|
|
261
|
+
content: [{ type: "text", text: `Unknown agent: ${agentName}` }],
|
|
262
|
+
isError: true,
|
|
263
|
+
details: { mode: resultMode, results: [] },
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
|
|
270
|
+
const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
|
|
271
|
+
const asyncDir = inheritedNestedRoute
|
|
272
|
+
? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute.rootRunId, id)
|
|
273
|
+
: path.join(ASYNC_DIR, id);
|
|
274
|
+
try {
|
|
275
|
+
fs.mkdirSync(asyncDir, { recursive: true });
|
|
276
|
+
} catch (error) {
|
|
277
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
278
|
+
return {
|
|
279
|
+
content: [{ type: "text", text: `Failed to create async run directory '${asyncDir}': ${message}` }],
|
|
280
|
+
isError: true,
|
|
281
|
+
details: { mode: resultMode, results: [] },
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let progressInstructionCreated = false;
|
|
286
|
+
const buildStepOverrides = (s: SequentialStep): StepOverrides => {
|
|
287
|
+
const stepSkillInput = normalizeSkillInput(s.skill);
|
|
288
|
+
return {
|
|
289
|
+
...(s.output !== undefined ? { output: s.output } : {}),
|
|
290
|
+
...(s.outputMode !== undefined ? { outputMode: s.outputMode } : {}),
|
|
291
|
+
...(s.reads !== undefined ? { reads: s.reads } : {}),
|
|
292
|
+
...(s.progress !== undefined ? { progress: s.progress } : {}),
|
|
293
|
+
...(stepSkillInput !== undefined ? { skills: stepSkillInput } : {}),
|
|
294
|
+
...(s.model ? { model: s.model } : {}),
|
|
295
|
+
};
|
|
296
|
+
};
|
|
297
|
+
const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior) => {
|
|
298
|
+
const a = agents.find((x) => x.name === s.agent)!;
|
|
299
|
+
const stepCwd = resolveChildCwd(runnerCwd, s.cwd);
|
|
300
|
+
const instructionCwd = behaviorCwd ?? stepCwd;
|
|
301
|
+
const behavior = suppressProgressForReadOnlyTask(resolvedBehavior ?? resolveStepBehavior(a, buildStepOverrides(s), chainSkills), s.task, originalTask);
|
|
302
|
+
const skillNames = behavior.skills === false ? [] : behavior.skills;
|
|
303
|
+
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, stepCwd, ctx.cwd);
|
|
304
|
+
if (missingSkills.includes("pi-subagents")) throw new UnavailableSubagentSkillError(UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
305
|
+
|
|
306
|
+
let systemPrompt = a.systemPrompt?.trim() ?? "";
|
|
307
|
+
if (resolvedSkills.length > 0) {
|
|
308
|
+
const injection = buildSkillInjection(resolvedSkills);
|
|
309
|
+
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${injection}` : injection;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const readInstructions = buildChainInstructions({ ...behavior, output: false, progress: false }, instructionCwd, false);
|
|
313
|
+
const isFirstProgressAgent = behavior.progress && !progressPrecreated && !progressInstructionCreated;
|
|
314
|
+
if (behavior.progress) progressInstructionCreated = true;
|
|
315
|
+
const progressInstructions = buildChainInstructions({ ...behavior, output: false, reads: false }, runnerCwd, isFirstProgressAgent);
|
|
316
|
+
const outputPath = resolveSingleOutputPath(behavior.output, ctx.cwd, instructionCwd);
|
|
317
|
+
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Async step (${s.agent})`);
|
|
318
|
+
if (validationError) throw new AsyncStartValidationError(validationError);
|
|
319
|
+
const task = injectSingleOutputInstruction(`${readInstructions.prefix}${s.task ?? "{previous}"}${progressInstructions.suffix}`, outputPath);
|
|
320
|
+
|
|
321
|
+
const primaryModel = resolveModelCandidate(behavior.model ?? a.model, availableModels, ctx.currentModelProvider);
|
|
322
|
+
const model = applyThinkingSuffix(primaryModel, a.thinking);
|
|
323
|
+
return {
|
|
324
|
+
agent: s.agent,
|
|
325
|
+
task,
|
|
326
|
+
cwd: stepCwd,
|
|
327
|
+
model,
|
|
328
|
+
thinking: resolveEffectiveThinking(model, a.thinking),
|
|
329
|
+
modelCandidates: buildModelCandidates(behavior.model ?? a.model, a.fallbackModels, availableModels, ctx.currentModelProvider).map((candidate) =>
|
|
330
|
+
applyThinkingSuffix(candidate, a.thinking),
|
|
331
|
+
),
|
|
332
|
+
tools: a.tools,
|
|
333
|
+
extensions: a.extensions,
|
|
334
|
+
mcpDirectTools: a.mcpDirectTools,
|
|
335
|
+
completionGuard: a.completionGuard,
|
|
336
|
+
systemPrompt,
|
|
337
|
+
systemPromptMode: a.systemPromptMode,
|
|
338
|
+
inheritProjectContext: a.inheritProjectContext,
|
|
339
|
+
inheritSkills: a.inheritSkills,
|
|
340
|
+
skills: resolvedSkills.map((r) => r.name),
|
|
341
|
+
outputPath,
|
|
342
|
+
outputMode: behavior.outputMode,
|
|
343
|
+
sessionFile,
|
|
344
|
+
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, a.maxSubagentDepth),
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
let flatStepIndex = 0;
|
|
349
|
+
const nextSessionFile = (): string | undefined => {
|
|
350
|
+
const sessionFile = sessionFilesByFlatIndex?.[flatStepIndex];
|
|
351
|
+
flatStepIndex++;
|
|
352
|
+
return sessionFile;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
let steps: RunnerStep[];
|
|
356
|
+
try {
|
|
357
|
+
steps = chain.map((s, stepIndex) => {
|
|
358
|
+
if (isParallelStep(s)) {
|
|
359
|
+
const parallelBehaviors = s.parallel.map((task) => {
|
|
360
|
+
const agent = agents.find((candidate) => candidate.name === task.agent)!;
|
|
361
|
+
return suppressProgressForReadOnlyTask(resolveStepBehavior(agent, buildStepOverrides(task), chainSkills), task.task, originalTask);
|
|
362
|
+
});
|
|
363
|
+
const progressPrecreated = parallelBehaviors.some((behavior) => behavior.progress);
|
|
364
|
+
if (progressPrecreated) {
|
|
365
|
+
if (!s.worktree) writeInitialProgressFile(runnerCwd);
|
|
366
|
+
progressInstructionCreated = true;
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
parallel: s.parallel.map((t, taskIndex) => {
|
|
370
|
+
let behaviorCwd: string | undefined;
|
|
371
|
+
if (s.worktree) {
|
|
372
|
+
try {
|
|
373
|
+
behaviorCwd = resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s${stepIndex}`, taskIndex);
|
|
374
|
+
} catch {
|
|
375
|
+
behaviorCwd = undefined;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return buildSeqStep(t, nextSessionFile(), behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex]);
|
|
379
|
+
}),
|
|
380
|
+
concurrency: s.concurrency,
|
|
381
|
+
failFast: s.failFast,
|
|
382
|
+
worktree: s.worktree,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return buildSeqStep(s as SequentialStep, nextSessionFile());
|
|
386
|
+
});
|
|
387
|
+
} catch (error) {
|
|
388
|
+
if (error instanceof UnavailableSubagentSkillError || error instanceof AsyncStartValidationError) return formatAsyncStartError(resultMode, error.message);
|
|
389
|
+
throw error;
|
|
390
|
+
}
|
|
391
|
+
let childTargetIndex = 0;
|
|
392
|
+
const childIntercomTargets = childIntercomTarget ? steps.flatMap((step) => {
|
|
393
|
+
if ("parallel" in step) {
|
|
394
|
+
return step.parallel.map((task) => childIntercomTarget(task.agent, childTargetIndex++));
|
|
395
|
+
}
|
|
396
|
+
return [childIntercomTarget(step.agent, childTargetIndex++)];
|
|
397
|
+
}) : undefined;
|
|
398
|
+
|
|
399
|
+
let spawnResult: { pid?: number; error?: string } = {};
|
|
400
|
+
try {
|
|
401
|
+
spawnResult = spawnRunner(
|
|
402
|
+
{
|
|
403
|
+
id,
|
|
404
|
+
steps,
|
|
405
|
+
resultPath: inheritedNestedRoute ? nestedResultsPath(inheritedNestedRoute.rootRunId, id) : path.join(RESULTS_DIR, `${id}.json`),
|
|
406
|
+
cwd: runnerCwd,
|
|
407
|
+
placeholder: "{previous}",
|
|
408
|
+
maxOutput,
|
|
409
|
+
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
410
|
+
artifactConfig,
|
|
411
|
+
share: shareEnabled,
|
|
412
|
+
sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
|
|
413
|
+
asyncDir,
|
|
414
|
+
sessionId: ctx.currentSessionId,
|
|
415
|
+
piPackageRoot,
|
|
416
|
+
piArgv1: process.argv[1],
|
|
417
|
+
worktreeSetupHook,
|
|
418
|
+
worktreeSetupHookTimeoutMs,
|
|
419
|
+
controlConfig,
|
|
420
|
+
controlIntercomTarget,
|
|
421
|
+
childIntercomTargets,
|
|
422
|
+
resultMode,
|
|
423
|
+
nestedRoute: nestedRoute ?? inheritedNestedRoute,
|
|
424
|
+
nestedSelf: inheritedNestedRoute && nestedAddress ? {
|
|
425
|
+
parentRunId: nestedAddress.parentRunId,
|
|
426
|
+
parentStepIndex: nestedAddress.parentStepIndex,
|
|
427
|
+
depth: nestedAddress.depth,
|
|
428
|
+
path: nestedAddress.path,
|
|
429
|
+
} : undefined,
|
|
430
|
+
},
|
|
431
|
+
id,
|
|
432
|
+
runnerCwd,
|
|
433
|
+
);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
436
|
+
return formatAsyncStartError(resultMode, `Failed to start async ${resultMode} '${id}': ${message}`);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (spawnResult.error) {
|
|
440
|
+
return formatAsyncStartError(resultMode, `Failed to start async ${resultMode} '${id}': ${spawnResult.error}`);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (spawnResult.pid) {
|
|
444
|
+
const firstStep = chain[0];
|
|
445
|
+
const firstAgents = isParallelStep(firstStep)
|
|
446
|
+
? firstStep.parallel.map((t) => t.agent)
|
|
447
|
+
: [(firstStep as SequentialStep).agent];
|
|
448
|
+
const parallelGroups: Array<{ start: number; count: number; stepIndex: number }> = [];
|
|
449
|
+
const flatAgents: string[] = [];
|
|
450
|
+
let flatStepStart = 0;
|
|
451
|
+
for (let stepIndex = 0; stepIndex < chain.length; stepIndex++) {
|
|
452
|
+
const step = chain[stepIndex]!;
|
|
453
|
+
if (isParallelStep(step)) {
|
|
454
|
+
parallelGroups.push({ start: flatStepStart, count: step.parallel.length, stepIndex });
|
|
455
|
+
flatAgents.push(...step.parallel.map((task) => task.agent));
|
|
456
|
+
flatStepStart += step.parallel.length;
|
|
457
|
+
} else {
|
|
458
|
+
flatAgents.push((step as SequentialStep).agent);
|
|
459
|
+
flatStepStart++;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (inheritedNestedRoute && nestedAddress) {
|
|
463
|
+
const now = Date.now();
|
|
464
|
+
try {
|
|
465
|
+
writeNestedEvent(inheritedNestedRoute, {
|
|
466
|
+
type: "subagent.nested.started",
|
|
467
|
+
ts: now,
|
|
468
|
+
parentRunId: nestedAddress.parentRunId,
|
|
469
|
+
parentStepIndex: nestedAddress.parentStepIndex,
|
|
470
|
+
child: {
|
|
471
|
+
id,
|
|
472
|
+
parentRunId: nestedAddress.parentRunId,
|
|
473
|
+
parentStepIndex: nestedAddress.parentStepIndex,
|
|
474
|
+
depth: nestedAddress.depth,
|
|
475
|
+
path: nestedAddress.path,
|
|
476
|
+
asyncDir,
|
|
477
|
+
pid: spawnResult.pid,
|
|
478
|
+
ownerIntercomTarget: process.env.PI_SUBAGENT_INTERCOM_SESSION_NAME,
|
|
479
|
+
leafIntercomTarget: childIntercomTargets?.[0],
|
|
480
|
+
intercomTarget: childIntercomTargets?.[0],
|
|
481
|
+
ownerState: "live",
|
|
482
|
+
mode: resultMode,
|
|
483
|
+
state: "running",
|
|
484
|
+
agent: firstAgents[0],
|
|
485
|
+
agents: flatAgents,
|
|
486
|
+
chainStepCount: chain.length,
|
|
487
|
+
parallelGroups,
|
|
488
|
+
startedAt: now,
|
|
489
|
+
lastUpdate: now,
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
} catch (error) {
|
|
493
|
+
console.error("Failed to emit nested async start event:", error);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
ctx.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
|
|
497
|
+
id,
|
|
498
|
+
pid: spawnResult.pid,
|
|
499
|
+
sessionId: ctx.currentSessionId,
|
|
500
|
+
mode: resultMode,
|
|
501
|
+
agent: firstAgents[0],
|
|
502
|
+
agents: flatAgents,
|
|
503
|
+
task: isParallelStep(firstStep)
|
|
504
|
+
? firstStep.parallel[0]?.task?.slice(0, 50)
|
|
505
|
+
: (firstStep as SequentialStep).task?.slice(0, 50),
|
|
506
|
+
chain: chain.map((s) =>
|
|
507
|
+
isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : (s as SequentialStep).agent,
|
|
508
|
+
),
|
|
509
|
+
chainStepCount: chain.length,
|
|
510
|
+
parallelGroups,
|
|
511
|
+
cwd: runnerCwd,
|
|
512
|
+
asyncDir,
|
|
513
|
+
nestedRoute,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const chainDesc = chain
|
|
518
|
+
.map((s) =>
|
|
519
|
+
isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : (s as SequentialStep).agent,
|
|
520
|
+
)
|
|
521
|
+
.join(" -> ");
|
|
522
|
+
|
|
523
|
+
return {
|
|
524
|
+
content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`) }],
|
|
525
|
+
details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir },
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Execute a single agent asynchronously
|
|
531
|
+
*/
|
|
532
|
+
export function executeAsyncSingle(
|
|
533
|
+
id: string,
|
|
534
|
+
params: AsyncSingleParams,
|
|
535
|
+
): AsyncExecutionResult {
|
|
536
|
+
const {
|
|
537
|
+
agent,
|
|
538
|
+
agentConfig,
|
|
539
|
+
ctx,
|
|
540
|
+
cwd,
|
|
541
|
+
maxOutput,
|
|
542
|
+
artifactsDir,
|
|
543
|
+
artifactConfig,
|
|
544
|
+
shareEnabled,
|
|
545
|
+
sessionRoot,
|
|
546
|
+
sessionFile,
|
|
547
|
+
maxSubagentDepth,
|
|
548
|
+
worktreeSetupHook,
|
|
549
|
+
worktreeSetupHookTimeoutMs,
|
|
550
|
+
controlConfig,
|
|
551
|
+
controlIntercomTarget,
|
|
552
|
+
childIntercomTarget,
|
|
553
|
+
nestedRoute,
|
|
554
|
+
} = params;
|
|
555
|
+
const task = params.task ?? "";
|
|
556
|
+
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
557
|
+
const skillNames = params.skills ?? agentConfig.skills ?? [];
|
|
558
|
+
const availableModels = params.availableModels;
|
|
559
|
+
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, runnerCwd, ctx.cwd);
|
|
560
|
+
if (missingSkills.includes("pi-subagents")) return formatAsyncStartError("single", UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
561
|
+
let systemPrompt = agentConfig.systemPrompt?.trim() ?? "";
|
|
562
|
+
if (resolvedSkills.length > 0) {
|
|
563
|
+
const injection = buildSkillInjection(resolvedSkills);
|
|
564
|
+
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${injection}` : injection;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
|
|
568
|
+
const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
|
|
569
|
+
const asyncDir = inheritedNestedRoute
|
|
570
|
+
? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute.rootRunId, id)
|
|
571
|
+
: path.join(ASYNC_DIR, id);
|
|
572
|
+
try {
|
|
573
|
+
fs.mkdirSync(asyncDir, { recursive: true });
|
|
574
|
+
} catch (error) {
|
|
575
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
576
|
+
return {
|
|
577
|
+
content: [{ type: "text", text: `Failed to create async run directory '${asyncDir}': ${message}` }],
|
|
578
|
+
isError: true,
|
|
579
|
+
details: { mode: "single" as const, results: [] },
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const effectiveOutput = normalizeSingleOutputOverride(params.output, agentConfig.output);
|
|
584
|
+
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, runnerCwd);
|
|
585
|
+
const outputMode = params.outputMode ?? "inline";
|
|
586
|
+
const validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);
|
|
587
|
+
if (validationError) return formatAsyncStartError("single", validationError);
|
|
588
|
+
const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath);
|
|
589
|
+
const model = applyThinkingSuffix(
|
|
590
|
+
resolveModelCandidate(params.modelOverride ?? agentConfig.model, availableModels, ctx.currentModelProvider),
|
|
591
|
+
agentConfig.thinking,
|
|
592
|
+
);
|
|
593
|
+
let spawnResult: { pid?: number; error?: string } = {};
|
|
594
|
+
try {
|
|
595
|
+
spawnResult = spawnRunner(
|
|
596
|
+
{
|
|
597
|
+
id,
|
|
598
|
+
steps: [
|
|
599
|
+
{
|
|
600
|
+
agent,
|
|
601
|
+
task: taskWithOutputInstruction,
|
|
602
|
+
cwd: runnerCwd,
|
|
603
|
+
model,
|
|
604
|
+
thinking: resolveEffectiveThinking(model, agentConfig.thinking),
|
|
605
|
+
modelCandidates: buildModelCandidates(params.modelOverride ?? agentConfig.model, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider).map((candidate) =>
|
|
606
|
+
applyThinkingSuffix(candidate, agentConfig.thinking),
|
|
607
|
+
),
|
|
608
|
+
tools: agentConfig.tools,
|
|
609
|
+
extensions: agentConfig.extensions,
|
|
610
|
+
mcpDirectTools: agentConfig.mcpDirectTools,
|
|
611
|
+
completionGuard: agentConfig.completionGuard,
|
|
612
|
+
systemPrompt,
|
|
613
|
+
systemPromptMode: agentConfig.systemPromptMode,
|
|
614
|
+
inheritProjectContext: agentConfig.inheritProjectContext,
|
|
615
|
+
inheritSkills: agentConfig.inheritSkills,
|
|
616
|
+
skills: resolvedSkills.map((r) => r.name),
|
|
617
|
+
outputPath,
|
|
618
|
+
outputMode,
|
|
619
|
+
sessionFile,
|
|
620
|
+
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
|
|
621
|
+
},
|
|
622
|
+
],
|
|
623
|
+
resultPath: inheritedNestedRoute ? nestedResultsPath(inheritedNestedRoute.rootRunId, id) : path.join(RESULTS_DIR, `${id}.json`),
|
|
624
|
+
cwd: runnerCwd,
|
|
625
|
+
placeholder: "{previous}",
|
|
626
|
+
maxOutput,
|
|
627
|
+
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
628
|
+
artifactConfig,
|
|
629
|
+
share: shareEnabled,
|
|
630
|
+
sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
|
|
631
|
+
asyncDir,
|
|
632
|
+
sessionId: ctx.currentSessionId,
|
|
633
|
+
piPackageRoot,
|
|
634
|
+
piArgv1: process.argv[1],
|
|
635
|
+
worktreeSetupHook,
|
|
636
|
+
worktreeSetupHookTimeoutMs,
|
|
637
|
+
controlConfig,
|
|
638
|
+
controlIntercomTarget,
|
|
639
|
+
childIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,
|
|
640
|
+
resultMode: "single",
|
|
641
|
+
nestedRoute: nestedRoute ?? inheritedNestedRoute,
|
|
642
|
+
nestedSelf: inheritedNestedRoute && nestedAddress ? {
|
|
643
|
+
parentRunId: nestedAddress.parentRunId,
|
|
644
|
+
parentStepIndex: nestedAddress.parentStepIndex,
|
|
645
|
+
depth: nestedAddress.depth,
|
|
646
|
+
path: nestedAddress.path,
|
|
647
|
+
} : undefined,
|
|
648
|
+
},
|
|
649
|
+
id,
|
|
650
|
+
runnerCwd,
|
|
651
|
+
);
|
|
652
|
+
} catch (error) {
|
|
653
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
654
|
+
return formatAsyncStartError("single", `Failed to start async run '${id}': ${message}`);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (spawnResult.error) {
|
|
658
|
+
return formatAsyncStartError("single", `Failed to start async run '${id}': ${spawnResult.error}`);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (spawnResult.pid) {
|
|
662
|
+
if (inheritedNestedRoute && nestedAddress) {
|
|
663
|
+
const now = Date.now();
|
|
664
|
+
try {
|
|
665
|
+
writeNestedEvent(inheritedNestedRoute, {
|
|
666
|
+
type: "subagent.nested.started",
|
|
667
|
+
ts: now,
|
|
668
|
+
parentRunId: nestedAddress.parentRunId,
|
|
669
|
+
parentStepIndex: nestedAddress.parentStepIndex,
|
|
670
|
+
child: {
|
|
671
|
+
id,
|
|
672
|
+
parentRunId: nestedAddress.parentRunId,
|
|
673
|
+
parentStepIndex: nestedAddress.parentStepIndex,
|
|
674
|
+
depth: nestedAddress.depth,
|
|
675
|
+
path: nestedAddress.path,
|
|
676
|
+
asyncDir,
|
|
677
|
+
pid: spawnResult.pid,
|
|
678
|
+
ownerIntercomTarget: process.env.PI_SUBAGENT_INTERCOM_SESSION_NAME,
|
|
679
|
+
leafIntercomTarget: childIntercomTarget?.(agent, 0),
|
|
680
|
+
intercomTarget: childIntercomTarget?.(agent, 0),
|
|
681
|
+
ownerState: "live",
|
|
682
|
+
mode: "single",
|
|
683
|
+
state: "running",
|
|
684
|
+
agent,
|
|
685
|
+
agents: [agent],
|
|
686
|
+
chainStepCount: 1,
|
|
687
|
+
startedAt: now,
|
|
688
|
+
lastUpdate: now,
|
|
689
|
+
},
|
|
690
|
+
});
|
|
691
|
+
} catch (error) {
|
|
692
|
+
console.error("Failed to emit nested async start event:", error);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
ctx.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
|
|
696
|
+
id,
|
|
697
|
+
pid: spawnResult.pid,
|
|
698
|
+
sessionId: ctx.currentSessionId,
|
|
699
|
+
mode: "single",
|
|
700
|
+
agent,
|
|
701
|
+
task: task?.slice(0, 50),
|
|
702
|
+
cwd: runnerCwd,
|
|
703
|
+
asyncDir,
|
|
704
|
+
nestedRoute,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return {
|
|
709
|
+
content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]`) }],
|
|
710
|
+
details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir },
|
|
711
|
+
};
|
|
712
|
+
}
|