pi-subagents 0.34.0 → 0.35.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 +78 -9
- package/README.md +213 -32
- package/index.ts +1 -0
- package/install.mjs +1 -1
- package/package.json +23 -8
- package/prompts/review-loop.md +3 -1
- package/skills/pi-subagents/SKILL.md +87 -25
- package/src/agents/agent-management.ts +82 -15
- package/src/agents/agent-serializer.ts +19 -0
- package/src/agents/agents.ts +91 -49
- package/src/agents/frontmatter.ts +67 -13
- package/src/agents/skills.ts +25 -12
- package/src/api/background-work.ts +197 -0
- package/src/api/delegation.ts +158 -0
- package/src/extension/chain-validation.ts +165 -0
- package/src/extension/doctor.ts +15 -0
- package/src/extension/fanout-child.ts +3 -1
- package/src/extension/index.ts +65 -124
- package/src/extension/rpc.ts +10 -2
- package/src/extension/schemas.ts +18 -14
- package/src/extension/steering-notices.ts +35 -0
- package/src/extension/tool-description.ts +20 -9
- package/src/intercom/intercom-bridge.ts +3 -2
- package/src/intercom/native-supervisor-channel.ts +9 -1
- package/src/intercom/result-intercom.ts +4 -0
- package/src/runs/background/async-execution.ts +293 -44
- package/src/runs/background/async-job-tracker.ts +56 -9
- package/src/runs/background/async-resume.ts +159 -52
- package/src/runs/background/async-status.ts +25 -18
- package/src/runs/background/auto-drain.ts +67 -0
- package/src/runs/background/chain-root-attachment.ts +16 -8
- package/src/runs/background/control-channel.ts +260 -13
- package/src/runs/background/fleet-view.ts +23 -2
- package/src/runs/background/notify.ts +79 -10
- package/src/runs/background/result-watcher.ts +12 -9
- package/src/runs/background/run-id-resolver.ts +14 -2
- package/src/runs/background/run-status.ts +23 -15
- package/src/runs/background/scheduled-runs.ts +3 -0
- package/src/runs/background/stale-run-reconciler.ts +32 -10
- package/src/runs/background/steering.ts +237 -0
- package/src/runs/background/subagent-runner.ts +898 -236
- package/src/runs/background/subagent-wait.ts +484 -0
- package/src/runs/background/top-level-async.ts +2 -1
- package/src/runs/background/wait-config.ts +36 -0
- package/src/runs/background/wait-tool.ts +26 -0
- package/src/runs/foreground/async-steering-action.ts +230 -0
- package/src/runs/foreground/chain-clarify.ts +22 -6
- package/src/runs/foreground/chain-execution.ts +50 -32
- package/src/runs/foreground/execution.ts +308 -94
- package/src/runs/foreground/subagent-executor.ts +592 -268
- package/src/runs/shared/acceptance.ts +355 -97
- package/src/runs/shared/child-protocol.ts +121 -0
- package/src/runs/shared/completion-guard.ts +8 -127
- package/src/runs/shared/dynamic-fanout.ts +6 -4
- package/src/runs/shared/model-fallback.ts +36 -0
- package/src/runs/shared/nested-events.ts +9 -4
- package/src/runs/shared/nested-render.ts +4 -1
- package/src/runs/shared/parallel-utils.ts +7 -0
- package/src/runs/shared/pi-args.ts +34 -7
- package/src/runs/shared/pi-spawn.ts +18 -12
- package/src/runs/shared/session-lease.ts +279 -0
- package/src/runs/shared/single-output.ts +61 -6
- package/src/runs/shared/spawn-budget.ts +128 -0
- package/src/runs/shared/subagent-control.ts +10 -6
- package/src/runs/shared/subagent-prompt-runtime.ts +127 -26
- package/src/runs/shared/task-intent.ts +176 -0
- package/src/runs/shared/tool-availability.ts +65 -0
- package/src/runs/shared/turn-budget.ts +49 -4
- package/src/shared/atomic-json.ts +4 -1
- package/src/shared/fork-context.ts +28 -3
- package/src/shared/model-info.ts +7 -4
- package/src/shared/status-format.ts +7 -1
- package/src/shared/types.ts +203 -25
- package/src/shared/utils.ts +35 -7
- package/src/slash/delegation-adapters.ts +457 -0
- package/src/slash/delegation-request.ts +103 -0
- package/src/slash/prompt-template-bridge.ts +167 -344
- package/src/slash/slash-commands.ts +239 -6
- package/src/slash/subagents-admin.ts +428 -0
- package/src/slash/subagents-editor.ts +86 -0
- package/src/tui/fleet.ts +405 -0
- package/src/tui/render.ts +90 -16
- package/src/watchdog/change-signature.ts +127 -0
- package/src/watchdog/child-status.ts +205 -0
- package/src/watchdog/emission-guard.ts +123 -0
- package/src/watchdog/lsp-diagnostics.ts +532 -0
- package/src/watchdog/model-selection.ts +167 -0
- package/src/watchdog/register-child.ts +117 -0
- package/src/watchdog/register-main.ts +433 -0
- package/src/watchdog/render.ts +54 -0
- package/src/watchdog/review.ts +293 -0
- package/src/watchdog/runtime.ts +712 -0
- package/src/watchdog/settings.ts +528 -0
- package/src/watchdog/tool-actions.ts +155 -0
- package/src/watchdog/turn-delta.ts +161 -0
- package/src/watchdog/types.ts +188 -0
- package/src/watchdog/warning-format.ts +73 -0
- package/src/runs/background/wait.ts +0 -394
|
@@ -10,6 +10,7 @@ import { fileURLToPath } from "node:url";
|
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import type { AgentConfig } from "../../agents/agents.ts";
|
|
13
|
+
import { writePrivateAtomicJson } from "../../shared/atomic-json.ts";
|
|
13
14
|
import { applyThinkingSuffix } from "../shared/pi-args.ts";
|
|
14
15
|
import { injectOutputPathSystemPrompt, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
15
16
|
import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
@@ -18,7 +19,7 @@ import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
|
18
19
|
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
19
20
|
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
20
21
|
import { PI_CODING_AGENT_PACKAGE_ROOT_ENV, resolveChildCwd } from "../../shared/utils.ts";
|
|
21
|
-
import { buildModelCandidates, resolveModelCandidate, resolveSubagentModelOverride, type AvailableModelInfo, type ParentModel } from "../shared/model-fallback.ts";
|
|
22
|
+
import { buildModelCandidates, resolveEffectiveSubagentModel, resolveModelCandidate, resolveSubagentModelOverride, type AvailableModelInfo, type ParentModel } from "../shared/model-fallback.ts";
|
|
22
23
|
import type { ModelScopeConfig } from "../shared/model-scope.ts";
|
|
23
24
|
import { resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
24
25
|
import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
type ResolvedTurnBudget,
|
|
37
38
|
type ResolvedToolBudget,
|
|
38
39
|
type SubagentRunMode,
|
|
40
|
+
type SteeringRecoveryDescriptor,
|
|
39
41
|
ASYNC_DIR,
|
|
40
42
|
RESULTS_DIR,
|
|
41
43
|
SUBAGENT_ASYNC_STARTED_EVENT,
|
|
@@ -48,6 +50,7 @@ import { nestedResultsPath, resolveInheritedNestedRouteFromEnv, resolveNestedPar
|
|
|
48
50
|
import { initialTurnBudgetState } from "../shared/turn-budget.ts";
|
|
49
51
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
50
52
|
import type { ImportedAsyncRoot } from "./chain-root-attachment.ts";
|
|
53
|
+
import type { SessionLeaseRequest } from "../shared/session-lease.ts";
|
|
51
54
|
|
|
52
55
|
const require = createRequire(import.meta.url);
|
|
53
56
|
const piPackageRoot = resolvePiPackageRoot();
|
|
@@ -108,11 +111,15 @@ interface AsyncExecutionContext {
|
|
|
108
111
|
currentModel?: ParentModel;
|
|
109
112
|
/** Optional model-scope enforcement resolved from subagent settings. */
|
|
110
113
|
modelScope?: ModelScopeConfig;
|
|
114
|
+
/** Whether the parent session has an interactive UI. */
|
|
115
|
+
interactive?: boolean;
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
interface AsyncChainParams {
|
|
114
119
|
chain: ChainStep[];
|
|
115
120
|
task?: string;
|
|
121
|
+
/** Raw caller-facing goal used only by the started event. */
|
|
122
|
+
goal?: string;
|
|
116
123
|
attachRoot?: ImportedAsyncRoot & { agent: string; outputName?: string; label?: string };
|
|
117
124
|
resultMode?: Exclude<SubagentRunMode, "single">;
|
|
118
125
|
agents: AgentConfig[];
|
|
@@ -130,6 +137,7 @@ interface AsyncChainParams {
|
|
|
130
137
|
progressDir?: string;
|
|
131
138
|
dynamicFanoutMaxItems?: number;
|
|
132
139
|
maxSubagentDepth: number;
|
|
140
|
+
waitToolEnabled?: boolean;
|
|
133
141
|
worktreeSetupHook?: string;
|
|
134
142
|
worktreeSetupHookTimeoutMs?: number;
|
|
135
143
|
worktreeBaseDir?: string;
|
|
@@ -149,6 +157,8 @@ interface AsyncChainParams {
|
|
|
149
157
|
interface AsyncSingleParams {
|
|
150
158
|
agent: string;
|
|
151
159
|
task?: string;
|
|
160
|
+
/** Raw caller-facing goal used only by the started event. */
|
|
161
|
+
goal?: string;
|
|
152
162
|
agentConfig: AgentConfig;
|
|
153
163
|
ctx: AsyncExecutionContext;
|
|
154
164
|
cwd?: string;
|
|
@@ -157,7 +167,9 @@ interface AsyncSingleParams {
|
|
|
157
167
|
artifactConfig: ArtifactConfig;
|
|
158
168
|
shareEnabled: boolean;
|
|
159
169
|
sessionRoot?: string;
|
|
170
|
+
sessionDir?: string;
|
|
160
171
|
sessionFile?: string;
|
|
172
|
+
revivalLease?: SessionLeaseRequest;
|
|
161
173
|
skills?: string[];
|
|
162
174
|
output?: string | boolean;
|
|
163
175
|
outputMode?: "inline" | "file-only";
|
|
@@ -166,6 +178,7 @@ interface AsyncSingleParams {
|
|
|
166
178
|
thinkingOverride?: AgentConfig["thinking"];
|
|
167
179
|
availableModels?: AvailableModelInfo[];
|
|
168
180
|
maxSubagentDepth: number;
|
|
181
|
+
waitToolEnabled?: boolean;
|
|
169
182
|
worktreeSetupHook?: string;
|
|
170
183
|
worktreeSetupHookTimeoutMs?: number;
|
|
171
184
|
worktreeBaseDir?: string;
|
|
@@ -175,6 +188,7 @@ interface AsyncSingleParams {
|
|
|
175
188
|
nestedRoute?: NestedRouteInfo;
|
|
176
189
|
acceptance?: AcceptanceInput;
|
|
177
190
|
timeoutMs?: number;
|
|
191
|
+
absoluteDeadlineAt?: number;
|
|
178
192
|
turnBudget?: ResolvedTurnBudget;
|
|
179
193
|
toolBudget?: ResolvedToolBudget;
|
|
180
194
|
configToolBudget?: ResolvedToolBudget;
|
|
@@ -201,6 +215,7 @@ export interface AsyncRunnerStepBuildParams {
|
|
|
201
215
|
progressDir?: string;
|
|
202
216
|
dynamicFanoutMaxItems?: number;
|
|
203
217
|
maxSubagentDepth: number;
|
|
218
|
+
waitToolEnabled?: boolean;
|
|
204
219
|
worktreeBaseDir?: string;
|
|
205
220
|
asyncDir: string;
|
|
206
221
|
outputBaseDir?: string;
|
|
@@ -219,14 +234,20 @@ export type AsyncRunnerStepBuildResult =
|
|
|
219
234
|
}
|
|
220
235
|
| { error: string };
|
|
221
236
|
|
|
222
|
-
export function formatAsyncStartedMessage(headline: string): string {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
237
|
+
export function formatAsyncStartedMessage(headline: string, interactive: boolean): string {
|
|
238
|
+
const guidance = interactive
|
|
239
|
+
? [
|
|
240
|
+
"The async run is detached and running in the background.",
|
|
241
|
+
"You are in an interactive session. By default, return control to the user now; Pi will wake you on completion when the run finishes or needs attention. Do NOT call subagent_wait() merely to wait, and do not run sleep/polling loops to wait for it.",
|
|
242
|
+
"Override that default and call subagent_wait() before ending the turn only when the current request is run-to-completion — for example, the user asked you to report results back here before continuing, or a skill must finish in one turn. In that case, call subagent_wait() to block until the run completes so its results are delivered in this turn instead of deferred.",
|
|
243
|
+
"Otherwise, continue any independent work or return control to the user. Use subagent({ action: \"status\", id: \"...\" }) for a one-shot status/result or to inspect a blocked/stale run, never as a wait loop.",
|
|
244
|
+
]
|
|
245
|
+
: [
|
|
246
|
+
"The async run is detached. Do not run sleep timers or polling loops just to wait for it.",
|
|
247
|
+
"This is a non-interactive run: Pi auto-drains current-session background work at agent_end so detached children are not abandoned; call subagent_wait() when this turn must receive the run's results before it ends, otherwise let the headless auto-drain finish the work.",
|
|
248
|
+
"Use subagent({ action: \"status\", id: \"...\" }) when you need a one-shot status/result or to inspect a blocked/stale run. To block until completion, use subagent_wait() — do not poll in a loop.",
|
|
249
|
+
];
|
|
250
|
+
return [headline, "", ...guidance].join("\n");
|
|
230
251
|
}
|
|
231
252
|
|
|
232
253
|
/**
|
|
@@ -280,6 +301,92 @@ function closeFd(fd: number | undefined): void {
|
|
|
280
301
|
/**
|
|
281
302
|
* Spawn the async runner process
|
|
282
303
|
*/
|
|
304
|
+
const RUNNER_STARTUP_TIMEOUT_MS = 10_000;
|
|
305
|
+
const RUNNER_STARTUP_WAIT_BUFFER = typeof SharedArrayBuffer !== "undefined" ? new SharedArrayBuffer(4) : undefined;
|
|
306
|
+
const RUNNER_STARTUP_WAIT_VIEW = RUNNER_STARTUP_WAIT_BUFFER ? new Int32Array(RUNNER_STARTUP_WAIT_BUFFER) : undefined;
|
|
307
|
+
|
|
308
|
+
type RunnerStartupState = "ready" | "acknowledged";
|
|
309
|
+
|
|
310
|
+
type RunnerStartupWaitResult =
|
|
311
|
+
| { ok: true; token: string }
|
|
312
|
+
| { ok: false; error: string };
|
|
313
|
+
|
|
314
|
+
function waitForStartupInterval(delayMs = 20): void {
|
|
315
|
+
if (RUNNER_STARTUP_WAIT_VIEW) {
|
|
316
|
+
Atomics.wait(RUNNER_STARTUP_WAIT_VIEW, 0, 0, delayMs);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const waitUntil = Date.now() + delayMs;
|
|
320
|
+
while (Date.now() < waitUntil) {
|
|
321
|
+
// Startup handshakes are synchronous so resume rejects before reporting a run as started.
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function readRunnerStartup(startupPath: string, expectedState: RunnerStartupState, expectedToken?: string): RunnerStartupWaitResult | undefined {
|
|
326
|
+
if (!fs.existsSync(startupPath)) return undefined;
|
|
327
|
+
try {
|
|
328
|
+
const payload = JSON.parse(fs.readFileSync(startupPath, "utf-8")) as { state?: unknown; token?: unknown; error?: unknown };
|
|
329
|
+
if (payload.state === "error" && typeof payload.error === "string") return { ok: false, error: payload.error };
|
|
330
|
+
if (payload.state !== expectedState) return undefined;
|
|
331
|
+
if (typeof payload.token !== "string" || (expectedToken !== undefined && payload.token !== expectedToken)) {
|
|
332
|
+
return { ok: false, error: `Async runner wrote an invalid ${expectedState} startup handshake: ${startupPath}` };
|
|
333
|
+
}
|
|
334
|
+
return { ok: true, token: payload.token };
|
|
335
|
+
} catch (error) {
|
|
336
|
+
return { ok: false, error: `Failed to read async runner startup handshake '${startupPath}': ${error instanceof Error ? error.message : String(error)}` };
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function waitForRunnerStartup(startupPath: string, expectedState: RunnerStartupState, timeoutMs: number, expectedToken?: string): RunnerStartupWaitResult {
|
|
341
|
+
const deadline = Date.now() + timeoutMs;
|
|
342
|
+
for (;;) {
|
|
343
|
+
const result = readRunnerStartup(startupPath, expectedState, expectedToken);
|
|
344
|
+
if (result) return result;
|
|
345
|
+
if (Date.now() >= deadline) break;
|
|
346
|
+
waitForStartupInterval(Math.min(20, Math.max(1, deadline - Date.now())));
|
|
347
|
+
}
|
|
348
|
+
const finalResult = readRunnerStartup(startupPath, expectedState, expectedToken);
|
|
349
|
+
if (finalResult) return finalResult;
|
|
350
|
+
return { ok: false, error: `Timed out after ${timeoutMs}ms waiting for the async runner startup state '${expectedState}'.` };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function writeRunnerStartupControl(filePath: string, payload: { action: "ack" | "proceed"; token: string }): void {
|
|
354
|
+
const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
355
|
+
try {
|
|
356
|
+
fs.writeFileSync(tempPath, JSON.stringify(payload), { encoding: "utf-8", mode: 0o600 });
|
|
357
|
+
fs.renameSync(tempPath, filePath);
|
|
358
|
+
} catch (error) {
|
|
359
|
+
try {
|
|
360
|
+
fs.rmSync(tempPath, { force: true });
|
|
361
|
+
} catch {
|
|
362
|
+
// Preserve the startup-control write error.
|
|
363
|
+
}
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function runnerIsAlive(pid: number): boolean {
|
|
369
|
+
try {
|
|
370
|
+
process.kill(pid, 0);
|
|
371
|
+
return true;
|
|
372
|
+
} catch (error) {
|
|
373
|
+
return (error as NodeJS.ErrnoException).code === "EPERM";
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function terminateRunnerBeforeProceed(pid: number): void {
|
|
378
|
+
for (const signal of ["SIGTERM", "SIGKILL"] as const) {
|
|
379
|
+
if (!runnerIsAlive(pid)) return;
|
|
380
|
+
try {
|
|
381
|
+
process.kill(pid, signal);
|
|
382
|
+
} catch {
|
|
383
|
+
if (!runnerIsAlive(pid)) return;
|
|
384
|
+
}
|
|
385
|
+
const deadline = Date.now() + 1000;
|
|
386
|
+
while (runnerIsAlive(pid) && Date.now() < deadline) waitForStartupInterval();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
283
390
|
function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number; error?: string } {
|
|
284
391
|
if (!jitiCliPath) {
|
|
285
392
|
return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
|
|
@@ -299,6 +406,15 @@ function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number;
|
|
|
299
406
|
fs.writeFileSync(cfgPath, JSON.stringify(cfg));
|
|
300
407
|
const runner = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-runner.ts");
|
|
301
408
|
const nodeCommand = resolveAsyncRunnerNodeCommand();
|
|
409
|
+
const startupPath = typeof (cfg as { revivalLease?: unknown; asyncDir?: unknown }).revivalLease === "object"
|
|
410
|
+
&& typeof (cfg as { asyncDir?: unknown }).asyncDir === "string"
|
|
411
|
+
? path.join((cfg as { asyncDir: string }).asyncDir, "runner-startup.json")
|
|
412
|
+
: undefined;
|
|
413
|
+
const startupAckPath = startupPath ? path.join(path.dirname(startupPath), "runner-startup-ack.json") : undefined;
|
|
414
|
+
const startupProceedPath = startupPath ? path.join(path.dirname(startupPath), "runner-startup-proceed.json") : undefined;
|
|
415
|
+
if (startupPath) fs.rmSync(startupPath, { force: true });
|
|
416
|
+
if (startupAckPath) fs.rmSync(startupAckPath, { force: true });
|
|
417
|
+
if (startupProceedPath) fs.rmSync(startupProceedPath, { force: true });
|
|
302
418
|
|
|
303
419
|
const logPaths = resolveAsyncRunnerLogPaths(cfg);
|
|
304
420
|
let stdoutFd: number | undefined;
|
|
@@ -328,6 +444,35 @@ function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number;
|
|
|
328
444
|
return { error: `async runner did not produce a pid for cwd: ${cwd}` };
|
|
329
445
|
}
|
|
330
446
|
proc.unref();
|
|
447
|
+
if (startupPath && startupAckPath && startupProceedPath) {
|
|
448
|
+
const ready = waitForRunnerStartup(startupPath, "ready", RUNNER_STARTUP_TIMEOUT_MS);
|
|
449
|
+
if (!ready.ok) {
|
|
450
|
+
terminateRunnerBeforeProceed(proc.pid);
|
|
451
|
+
return { error: ready.error };
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
writeRunnerStartupControl(startupAckPath, { action: "ack", token: ready.token });
|
|
455
|
+
} catch (error) {
|
|
456
|
+
terminateRunnerBeforeProceed(proc.pid);
|
|
457
|
+
return { error: `Failed to acknowledge async runner startup: ${error instanceof Error ? error.message : String(error)}` };
|
|
458
|
+
}
|
|
459
|
+
const acknowledged = waitForRunnerStartup(startupPath, "acknowledged", RUNNER_STARTUP_TIMEOUT_MS, ready.token);
|
|
460
|
+
if (!acknowledged.ok) {
|
|
461
|
+
terminateRunnerBeforeProceed(proc.pid);
|
|
462
|
+
return { error: acknowledged.error };
|
|
463
|
+
}
|
|
464
|
+
try {
|
|
465
|
+
writeRunnerStartupControl(startupProceedPath, { action: "proceed", token: ready.token });
|
|
466
|
+
} catch (error) {
|
|
467
|
+
terminateRunnerBeforeProceed(proc.pid);
|
|
468
|
+
return { error: `Failed to authorize async runner startup: ${error instanceof Error ? error.message : String(error)}` };
|
|
469
|
+
}
|
|
470
|
+
try {
|
|
471
|
+
fs.rmSync(startupPath, { force: true });
|
|
472
|
+
} catch {
|
|
473
|
+
// Proceed is the commit point; handshake cleanup cannot turn a running revival into a start error.
|
|
474
|
+
}
|
|
475
|
+
}
|
|
331
476
|
return { pid: proc.pid };
|
|
332
477
|
} catch (error) {
|
|
333
478
|
closeFd(stdoutFd);
|
|
@@ -415,19 +560,33 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
415
560
|
...(s.reads !== undefined ? { reads: s.reads } : {}),
|
|
416
561
|
...(s.progress !== undefined ? { progress: s.progress } : {}),
|
|
417
562
|
...(stepSkillInput !== undefined ? { skills: stepSkillInput } : {}),
|
|
418
|
-
...(s.model ? { model: s.model } : {}),
|
|
563
|
+
...(s.model !== undefined ? { model: s.model } : {}),
|
|
419
564
|
};
|
|
420
565
|
};
|
|
421
|
-
const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior, flatIndex?: number) => {
|
|
566
|
+
const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior, flatIndex?: number, parallelOutputNamespace?: { stepIndex: number; taskIndex?: number }) => {
|
|
422
567
|
const a = agents.find((x) => x.name === s.agent)!;
|
|
423
568
|
const toolBudgetInput = s.toolBudget ?? params.toolBudget ?? a.toolBudget ?? params.configToolBudget;
|
|
424
569
|
const resolvedToolBudget = validateToolBudgetConfig(toolBudgetInput, s.toolBudget ? "toolBudget" : a.toolBudget ? "agent.toolBudget" : "config.toolBudget");
|
|
425
570
|
if (resolvedToolBudget.error) throw new AsyncStartValidationError(resolvedToolBudget.error);
|
|
426
571
|
const stepCwd = resolveChildCwd(runnerCwd, s.cwd);
|
|
427
572
|
const instructionCwd = behaviorCwd ?? stepCwd;
|
|
428
|
-
|
|
573
|
+
let behavior = suppressProgressForReadOnlyTask(resolvedBehavior ?? resolveStepBehavior(a, buildStepOverrides(s), chainSkills), s.task, originalTask);
|
|
574
|
+
const inheritedRelativeParallelOutput = parallelOutputNamespace && s.output === undefined && typeof behavior.output === "string" && !path.isAbsolute(behavior.output);
|
|
575
|
+
if (inheritedRelativeParallelOutput && parallelOutputNamespace.taskIndex !== undefined) {
|
|
576
|
+
behavior = {
|
|
577
|
+
...behavior,
|
|
578
|
+
output: path.join(`parallel-${parallelOutputNamespace.stepIndex}`, `${parallelOutputNamespace.taskIndex}-${s.agent}`, behavior.output),
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
const namespaceOutputPath = Boolean(inheritedRelativeParallelOutput && parallelOutputNamespace.taskIndex === undefined);
|
|
429
582
|
const skillNames = behavior.skills === false ? [] : behavior.skills;
|
|
430
|
-
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(
|
|
583
|
+
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(
|
|
584
|
+
skillNames,
|
|
585
|
+
stepCwd,
|
|
586
|
+
ctx.cwd,
|
|
587
|
+
a.skillPath,
|
|
588
|
+
a.filePath ? path.dirname(a.filePath) : stepCwd,
|
|
589
|
+
);
|
|
431
590
|
if (missingSkills.includes("pi-subagents")) throw new UnavailableSubagentSkillError(UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
432
591
|
|
|
433
592
|
let systemPrompt = a.systemPrompt?.trim() ?? "";
|
|
@@ -445,16 +604,23 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
445
604
|
if (behavior.progress) progressInstructionCreated = true;
|
|
446
605
|
const progressInstructions = buildChainInstructions({ ...behavior, output: false, reads: false }, progressDir, isFirstProgressAgent);
|
|
447
606
|
const outputPath = resolveSingleOutputPath(behavior.output, ctx.cwd, instructionCwd, outputBaseDir);
|
|
448
|
-
systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath);
|
|
607
|
+
if (!namespaceOutputPath) systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath, a);
|
|
449
608
|
const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Async step (${s.agent})`);
|
|
450
609
|
if (validationError) throw new AsyncStartValidationError(validationError);
|
|
451
610
|
let taskTemplate = s.task ?? "{previous}";
|
|
452
611
|
taskTemplate = taskTemplate.replace(/\{task\}/g, originalTask ?? "");
|
|
453
612
|
taskTemplate = taskTemplate.replace(/\{chain_dir\}/g, runnerCwd);
|
|
454
|
-
const
|
|
613
|
+
const taskText = `${readInstructions.prefix}${taskTemplate}${progressInstructions.suffix}`;
|
|
614
|
+
const task = namespaceOutputPath ? taskText : injectSingleOutputInstruction(taskText, outputPath, a);
|
|
455
615
|
|
|
456
|
-
const
|
|
457
|
-
|
|
616
|
+
const primaryModel = resolveEffectiveSubagentModel(
|
|
617
|
+
s.model,
|
|
618
|
+
a.model,
|
|
619
|
+
ctx.currentModel,
|
|
620
|
+
availableModels,
|
|
621
|
+
ctx.currentModelProvider,
|
|
622
|
+
{ scope: ctx.modelScope },
|
|
623
|
+
);
|
|
458
624
|
const thinkingOverride = flatIndex === undefined ? undefined : thinkingOverridesByFlatIndex?.[flatIndex];
|
|
459
625
|
const effectiveThinking = thinkingOverride ?? a.thinking;
|
|
460
626
|
const model = applyThinkingSuffix(primaryModel, effectiveThinking, thinkingOverride !== undefined);
|
|
@@ -483,17 +649,22 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
483
649
|
inheritSkills: a.inheritSkills,
|
|
484
650
|
skills: resolvedSkills.map((r) => r.name),
|
|
485
651
|
outputPath,
|
|
652
|
+
...(namespaceOutputPath ? { namespaceOutputPath: true } : {}),
|
|
486
653
|
outputMode: behavior.outputMode,
|
|
487
654
|
sessionFile,
|
|
488
655
|
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, a.maxSubagentDepth),
|
|
656
|
+
waitToolEnabled: params.waitToolEnabled,
|
|
489
657
|
effectiveAcceptance: resolveEffectiveAcceptance({
|
|
490
658
|
explicit: s.acceptance,
|
|
491
659
|
agentName: s.agent,
|
|
492
|
-
|
|
660
|
+
acceptanceRole: a.acceptanceRole,
|
|
661
|
+
task,
|
|
493
662
|
mode: resultMode,
|
|
494
663
|
async: true,
|
|
495
664
|
dynamic: false,
|
|
496
665
|
}),
|
|
666
|
+
acceptanceInput: s.acceptance,
|
|
667
|
+
acceptanceRole: a.acceptanceRole,
|
|
497
668
|
...(s.outputSchema ? { structuredOutputSchema: s.outputSchema } : {}),
|
|
498
669
|
...(s.outputSchema ? { structuredOutput: createStructuredOutputRuntime(s.outputSchema, path.join(asyncDir, "structured-output")) } : {}),
|
|
499
670
|
...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
|
|
@@ -536,7 +707,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
536
707
|
}
|
|
537
708
|
}
|
|
538
709
|
const staticStep = nextFlatStep();
|
|
539
|
-
return buildSeqStep(t, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index);
|
|
710
|
+
return buildSeqStep(t, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index, { stepIndex, taskIndex });
|
|
540
711
|
}),
|
|
541
712
|
concurrency: s.concurrency,
|
|
542
713
|
failFast: s.failFast,
|
|
@@ -553,9 +724,10 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
553
724
|
}
|
|
554
725
|
const maxItems = s.expand.maxItems ?? params.dynamicFanoutMaxItems ?? 0;
|
|
555
726
|
const dynamicFlatSteps = Array.from({ length: maxItems }, () => nextFlatStep());
|
|
727
|
+
const parallel = buildSeqStep(s.parallel as SequentialStep, undefined, undefined, progressPrecreated, behavior, undefined, { stepIndex });
|
|
556
728
|
return {
|
|
557
729
|
expand: s.expand,
|
|
558
|
-
parallel
|
|
730
|
+
parallel,
|
|
559
731
|
collect: s.collect,
|
|
560
732
|
concurrency: s.concurrency,
|
|
561
733
|
failFast: s.failFast,
|
|
@@ -566,11 +738,14 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
566
738
|
effectiveAcceptance: resolveEffectiveAcceptance({
|
|
567
739
|
explicit: s.acceptance,
|
|
568
740
|
agentName: s.parallel.agent,
|
|
569
|
-
|
|
741
|
+
acceptanceRole: agent.acceptanceRole,
|
|
742
|
+
task: parallel.task,
|
|
570
743
|
mode: resultMode,
|
|
571
744
|
async: true,
|
|
572
745
|
dynamicGroup: true,
|
|
573
746
|
}),
|
|
747
|
+
acceptanceInput: s.acceptance,
|
|
748
|
+
acceptanceRole: agent.acceptanceRole,
|
|
574
749
|
};
|
|
575
750
|
}
|
|
576
751
|
const staticStep = nextFlatStep();
|
|
@@ -592,6 +767,19 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
592
767
|
inheritSkills: false,
|
|
593
768
|
}, ...builtSteps]
|
|
594
769
|
: builtSteps;
|
|
770
|
+
for (const step of steps) {
|
|
771
|
+
if (!("parallel" in step) || !Array.isArray(step.parallel)) continue;
|
|
772
|
+
const seen = new Map<string, { index: number; agent: string }>();
|
|
773
|
+
for (let index = 0; index < step.parallel.length; index++) {
|
|
774
|
+
const task = step.parallel[index]!;
|
|
775
|
+
if (!task.outputPath) continue;
|
|
776
|
+
const previous = seen.get(task.outputPath);
|
|
777
|
+
if (previous) {
|
|
778
|
+
throw new AsyncStartValidationError(`Parallel tasks ${previous.index + 1} (${previous.agent}) and ${index + 1} (${task.agent}) resolve output to the same path: ${task.outputPath}. Use distinct output paths.`);
|
|
779
|
+
}
|
|
780
|
+
seen.set(task.outputPath, { index, agent: task.agent });
|
|
781
|
+
}
|
|
782
|
+
}
|
|
595
783
|
return { steps, runnerCwd, workflowGraph, eventChain: graphChain, ...(originalTask !== undefined ? { originalTask } : {}) };
|
|
596
784
|
} catch (error) {
|
|
597
785
|
if (error instanceof UnavailableSubagentSkillError || error instanceof AsyncStartValidationError) return { error: error.message };
|
|
@@ -660,6 +848,7 @@ export function executeAsyncChain(
|
|
|
660
848
|
outputBaseDir: artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined,
|
|
661
849
|
dynamicFanoutMaxItems: params.dynamicFanoutMaxItems,
|
|
662
850
|
maxSubagentDepth,
|
|
851
|
+
waitToolEnabled: params.waitToolEnabled,
|
|
663
852
|
worktreeBaseDir,
|
|
664
853
|
asyncDir,
|
|
665
854
|
toolBudget: params.toolBudget,
|
|
@@ -751,6 +940,12 @@ export function executeAsyncChain(
|
|
|
751
940
|
: isDynamicParallelStep(eventFirstStep)
|
|
752
941
|
? [eventFirstStep.parallel.agent]
|
|
753
942
|
: [(eventFirstStep as SequentialStep).agent];
|
|
943
|
+
const firstTask = isParallelStep(eventFirstStep)
|
|
944
|
+
? eventFirstStep.parallel[0]?.task
|
|
945
|
+
: isDynamicParallelStep(eventFirstStep)
|
|
946
|
+
? eventFirstStep.parallel.task
|
|
947
|
+
: (eventFirstStep as SequentialStep).task;
|
|
948
|
+
const workflowGoal = params.goal ?? (params.task?.trim() || firstTask);
|
|
754
949
|
const parallelGroups: Array<{ start: number; count: number; stepIndex: number }> = [];
|
|
755
950
|
const flatAgents: string[] = [];
|
|
756
951
|
let flatStepStart = 0;
|
|
@@ -813,11 +1008,8 @@ export function executeAsyncChain(
|
|
|
813
1008
|
mode: resultMode,
|
|
814
1009
|
agent: firstAgents[0],
|
|
815
1010
|
agents: flatAgents,
|
|
816
|
-
task:
|
|
817
|
-
|
|
818
|
-
: isDynamicParallelStep(eventFirstStep)
|
|
819
|
-
? eventFirstStep.parallel.task?.slice(0, 50)
|
|
820
|
-
: (eventFirstStep as SequentialStep).task?.slice(0, 50),
|
|
1011
|
+
task: firstTask?.slice(0, 50),
|
|
1012
|
+
goal: workflowGoal?.slice(0, 120),
|
|
821
1013
|
chain: eventChain.map((s) =>
|
|
822
1014
|
isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : isDynamicParallelStep(s) ? `expand:${s.parallel.agent}` : (s as SequentialStep).agent,
|
|
823
1015
|
),
|
|
@@ -839,7 +1031,7 @@ export function executeAsyncChain(
|
|
|
839
1031
|
.join(" -> ");
|
|
840
1032
|
|
|
841
1033
|
return {
|
|
842
|
-
content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]
|
|
1034
|
+
content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`, ctx.interactive === true) }],
|
|
843
1035
|
details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph, ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
|
|
844
1036
|
};
|
|
845
1037
|
}
|
|
@@ -875,7 +1067,13 @@ export function executeAsyncSingle(
|
|
|
875
1067
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
876
1068
|
const skillNames = params.skills ?? agentConfig.skills ?? [];
|
|
877
1069
|
const availableModels = params.availableModels;
|
|
878
|
-
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(
|
|
1070
|
+
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(
|
|
1071
|
+
skillNames,
|
|
1072
|
+
runnerCwd,
|
|
1073
|
+
ctx.cwd,
|
|
1074
|
+
agentConfig.skillPath,
|
|
1075
|
+
agentConfig.filePath ? path.dirname(agentConfig.filePath) : runnerCwd,
|
|
1076
|
+
);
|
|
879
1077
|
if (missingSkills.includes("pi-subagents")) return formatAsyncStartError("single", UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
880
1078
|
let systemPrompt = agentConfig.systemPrompt?.trim() ?? "";
|
|
881
1079
|
if (resolvedSkills.length > 0) {
|
|
@@ -905,11 +1103,11 @@ export function executeAsyncSingle(
|
|
|
905
1103
|
|
|
906
1104
|
const effectiveOutput = normalizeSingleOutputOverride(params.output, agentConfig.output);
|
|
907
1105
|
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, runnerCwd, params.outputBaseDir ?? (artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined));
|
|
908
|
-
systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath);
|
|
1106
|
+
systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath, agentConfig);
|
|
909
1107
|
const outputMode = params.outputMode ?? "inline";
|
|
910
1108
|
const validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);
|
|
911
1109
|
if (validationError) return formatAsyncStartError("single", validationError);
|
|
912
|
-
const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath);
|
|
1110
|
+
const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath, agentConfig);
|
|
913
1111
|
const primaryModel = resolveSubagentModelOverride(
|
|
914
1112
|
params.modelOverride ?? agentConfig.model,
|
|
915
1113
|
ctx.currentModel,
|
|
@@ -921,8 +1119,62 @@ export function executeAsyncSingle(
|
|
|
921
1119
|
const toolBudgetInput = params.toolBudget ?? agentConfig.toolBudget ?? params.configToolBudget;
|
|
922
1120
|
const resolvedToolBudget = validateToolBudgetConfig(toolBudgetInput, params.toolBudget ? "toolBudget" : agentConfig.toolBudget ? "agent.toolBudget" : "config.toolBudget");
|
|
923
1121
|
if (resolvedToolBudget.error) return formatAsyncStartError("single", resolvedToolBudget.error);
|
|
924
|
-
const deadlineAt = params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined;
|
|
1122
|
+
const deadlineAt = params.absoluteDeadlineAt ?? (params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined);
|
|
1123
|
+
const timeoutMs = params.absoluteDeadlineAt !== undefined && deadlineAt !== undefined
|
|
1124
|
+
? deadlineAt - Date.now()
|
|
1125
|
+
: params.timeoutMs;
|
|
1126
|
+
if (timeoutMs !== undefined && timeoutMs <= 0) return formatAsyncStartError("single", "The source run's absolute deadline expired before recovery could launch.");
|
|
925
1127
|
const initialTurnBudget = params.turnBudget ? initialTurnBudgetState(params.turnBudget) : undefined;
|
|
1128
|
+
const resolvedSessionDir = params.sessionDir ?? (sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined);
|
|
1129
|
+
const resolvedAcceptance = resolveEffectiveAcceptance({
|
|
1130
|
+
explicit: params.acceptance,
|
|
1131
|
+
agentName: agent,
|
|
1132
|
+
acceptanceRole: agentConfig.acceptanceRole,
|
|
1133
|
+
task,
|
|
1134
|
+
mode: "single",
|
|
1135
|
+
async: true,
|
|
1136
|
+
});
|
|
1137
|
+
const recoveryDescriptor: SteeringRecoveryDescriptor = {
|
|
1138
|
+
version: 1,
|
|
1139
|
+
sourceRunId: id,
|
|
1140
|
+
agent,
|
|
1141
|
+
...(sessionFile ? { sessionFile } : {}),
|
|
1142
|
+
cwd: runnerCwd,
|
|
1143
|
+
...(model ? { model } : {}),
|
|
1144
|
+
...(agentConfig.fallbackModels ? { fallbackModels: [...agentConfig.fallbackModels] } : {}),
|
|
1145
|
+
...(effectiveThinking ? { thinking: resolveEffectiveThinking(model, effectiveThinking) } : {}),
|
|
1146
|
+
...(agentConfig.tools ? { tools: [...agentConfig.tools] } : {}),
|
|
1147
|
+
...(agentConfig.extensions ? { extensions: [...agentConfig.extensions] } : {}),
|
|
1148
|
+
...(agentConfig.subagentOnlyExtensions ? { subagentOnlyExtensions: [...agentConfig.subagentOnlyExtensions] } : {}),
|
|
1149
|
+
...(agentConfig.mcpDirectTools ? { mcpDirectTools: [...agentConfig.mcpDirectTools] } : {}),
|
|
1150
|
+
...(agentConfig.systemPrompt ? { systemPrompt: agentConfig.systemPrompt } : {}),
|
|
1151
|
+
systemPromptMode: agentConfig.systemPromptMode,
|
|
1152
|
+
inheritProjectContext: agentConfig.inheritProjectContext,
|
|
1153
|
+
inheritSkills: agentConfig.inheritSkills,
|
|
1154
|
+
...(resolvedSkills.length ? { skills: resolvedSkills.map((skill) => skill.name) } : {}),
|
|
1155
|
+
...(agentConfig.skillPath ? { skillPath: [...agentConfig.skillPath] } : {}),
|
|
1156
|
+
...(agentConfig.filePath ? { agentFilePath: agentConfig.filePath } : {}),
|
|
1157
|
+
...(agentConfig.completionGuard !== undefined ? { completionGuard: agentConfig.completionGuard } : {}),
|
|
1158
|
+
...(agentConfig.memory ? { memory: { ...agentConfig.memory } } : {}),
|
|
1159
|
+
...(outputPath ? { outputPath } : {}),
|
|
1160
|
+
outputMode,
|
|
1161
|
+
...(resolvedAcceptance ? { acceptance: resolvedAcceptance } : {}),
|
|
1162
|
+
...(controlConfig ? { controlConfig } : {}),
|
|
1163
|
+
...(deadlineAt !== undefined ? { absoluteDeadlineAt: deadlineAt } : {}),
|
|
1164
|
+
...(params.turnBudget ? { initialTurnBudget: params.turnBudget } : {}),
|
|
1165
|
+
...(resolvedToolBudget.budget ? { initialToolBudget: resolvedToolBudget.budget } : {}),
|
|
1166
|
+
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
|
|
1167
|
+
...(maxOutput ? { maxOutput } : {}),
|
|
1168
|
+
share: shareEnabled,
|
|
1169
|
+
...(resolvedSessionDir ? { sessionDir: resolvedSessionDir } : {}),
|
|
1170
|
+
...(artifactsDir ? { artifactsDir } : {}),
|
|
1171
|
+
artifactConfig,
|
|
1172
|
+
};
|
|
1173
|
+
try {
|
|
1174
|
+
writePrivateAtomicJson(path.join(asyncDir, "recovery-descriptor.json"), recoveryDescriptor);
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
return formatAsyncStartError("single", `Failed to persist async recovery descriptor for '${id}': ${error instanceof Error ? error.message : String(error)}`);
|
|
1177
|
+
}
|
|
926
1178
|
let spawnResult: { pid?: number; error?: string } = {};
|
|
927
1179
|
try {
|
|
928
1180
|
spawnResult = spawnRunner(
|
|
@@ -953,13 +1205,8 @@ export function executeAsyncSingle(
|
|
|
953
1205
|
outputMode,
|
|
954
1206
|
sessionFile,
|
|
955
1207
|
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
agentName: agent,
|
|
959
|
-
task,
|
|
960
|
-
mode: "single",
|
|
961
|
-
async: true,
|
|
962
|
-
}),
|
|
1208
|
+
waitToolEnabled: params.waitToolEnabled,
|
|
1209
|
+
effectiveAcceptance: resolvedAcceptance,
|
|
963
1210
|
...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
|
|
964
1211
|
},
|
|
965
1212
|
],
|
|
@@ -970,7 +1217,7 @@ export function executeAsyncSingle(
|
|
|
970
1217
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
971
1218
|
artifactConfig,
|
|
972
1219
|
share: shareEnabled,
|
|
973
|
-
sessionDir:
|
|
1220
|
+
sessionDir: resolvedSessionDir,
|
|
974
1221
|
asyncDir,
|
|
975
1222
|
sessionId: ctx.currentSessionId,
|
|
976
1223
|
piPackageRoot,
|
|
@@ -979,13 +1226,14 @@ export function executeAsyncSingle(
|
|
|
979
1226
|
worktreeSetupHookTimeoutMs,
|
|
980
1227
|
worktreeBaseDir,
|
|
981
1228
|
controlConfig,
|
|
982
|
-
timeoutMs
|
|
1229
|
+
timeoutMs,
|
|
983
1230
|
deadlineAt,
|
|
984
1231
|
turnBudget: params.turnBudget,
|
|
985
1232
|
toolBudget: params.toolBudget,
|
|
986
1233
|
controlIntercomTarget,
|
|
987
1234
|
childIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,
|
|
988
1235
|
resultMode: "single",
|
|
1236
|
+
...(params.revivalLease ? { revivalLease: params.revivalLease } : {}),
|
|
989
1237
|
nestedRoute: nestedRoute ?? inheritedNestedRoute,
|
|
990
1238
|
nestedSelf: inheritedNestedRoute && nestedAddress ? {
|
|
991
1239
|
parentRunId: nestedAddress.parentRunId,
|
|
@@ -1032,7 +1280,7 @@ export function executeAsyncSingle(
|
|
|
1032
1280
|
agent,
|
|
1033
1281
|
agents: [agent],
|
|
1034
1282
|
chainStepCount: 1,
|
|
1035
|
-
...(
|
|
1283
|
+
...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}),
|
|
1036
1284
|
...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
|
|
1037
1285
|
startedAt: now,
|
|
1038
1286
|
lastUpdate: now,
|
|
@@ -1050,16 +1298,17 @@ export function executeAsyncSingle(
|
|
|
1050
1298
|
mode: "single",
|
|
1051
1299
|
agent,
|
|
1052
1300
|
task: task?.slice(0, 50),
|
|
1301
|
+
goal: (params.goal ?? task).slice(0, 120),
|
|
1053
1302
|
cwd: runnerCwd,
|
|
1054
1303
|
asyncDir,
|
|
1055
|
-
...(
|
|
1304
|
+
...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}),
|
|
1056
1305
|
...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
|
|
1057
1306
|
nestedRoute,
|
|
1058
1307
|
});
|
|
1059
1308
|
}
|
|
1060
1309
|
|
|
1061
1310
|
return {
|
|
1062
|
-
content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]
|
|
1063
|
-
details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir, ...(
|
|
1311
|
+
content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]`, ctx.interactive === true) }],
|
|
1312
|
+
details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir, ...(timeoutMs !== undefined ? { timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
|
|
1064
1313
|
};
|
|
1065
1314
|
}
|