pi-subagents 0.49.0 → 0.51.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 +97 -0
- package/agents/gpt-pro.md +17 -0
- package/agents/oracle.md +7 -5
- package/agents/researcher.md +1 -1
- package/agents/reviewer.md +2 -2
- package/agents/scout.md +1 -1
- package/agents/worker.md +1 -1
- package/async-retention-discovery-worker.mjs +180 -0
- package/docs/agents.md +37 -2
- package/docs/configuration.md +76 -14
- package/docs/extension-api.md +78 -1
- package/docs/missions.md +1 -1
- package/docs/observability.md +20 -4
- package/docs/tool-reference.md +55 -39
- package/docs/workflows.md +171 -5
- package/package.json +4 -2
- package/skills/pi-subagents/SKILL.md +5 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +9 -6
- package/skills/pi-subagents/references/execution-controls.md +22 -18
- package/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
- package/skills/pi-subagents/references/prompting-and-roles.md +33 -17
- package/src/agents/agent-management.ts +100 -345
- package/src/agents/agent-serializer.ts +2 -0
- package/src/agents/agents.ts +135 -25
- package/src/api/external-job-provider.ts +185 -0
- package/src/api/external-runs.ts +174 -84
- package/src/api/preflight.ts +42 -11
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +36 -3
- package/src/extension/doctor.ts +3 -6
- package/src/extension/fanout-child.ts +2 -2
- package/src/extension/index.ts +210 -90
- package/src/extension/public-execution.ts +31 -2
- package/src/extension/rpc.ts +5 -1
- package/src/extension/schemas.ts +14 -36
- package/src/extension/tool-description.ts +37 -24
- package/src/inspectors/herdr/actions.ts +5 -9
- package/src/inspectors/herdr/inspector-runner.ts +2 -1
- package/src/inspectors/herdr/project-panes.ts +4 -8
- package/src/inspectors/herdr/shell-command.ts +16 -0
- package/src/intercom/intercom-bridge.ts +2 -3
- package/src/intercom/native-supervisor-channel.ts +49 -51
- package/src/missions/goal-driver.ts +3 -1
- package/src/missions/lifecycle.ts +6 -1
- package/src/missions/store.ts +4 -9
- package/src/profiles/profiles.ts +3 -1
- package/src/runs/background/active-run-index.ts +94 -1
- package/src/runs/background/async-execution.ts +98 -38
- package/src/runs/background/async-job-tracker.ts +21 -4
- package/src/runs/background/async-resume.ts +30 -17
- package/src/runs/background/async-retention.ts +888 -0
- package/src/runs/background/async-status-snapshot.ts +277 -0
- package/src/runs/background/async-status.ts +47 -56
- package/src/runs/background/chain-append.ts +3 -33
- package/src/runs/background/chain-root-attachment.ts +2 -2
- package/src/runs/background/completion-replay.ts +11 -1
- package/src/runs/background/control-channel.ts +14 -68
- package/src/runs/background/fleet-view.ts +3 -1
- package/src/runs/background/index-segment.ts +59 -0
- package/src/runs/background/notify.ts +3 -1
- package/src/runs/background/result-files.ts +505 -0
- package/src/runs/background/result-watcher.ts +250 -51
- package/src/runs/background/retained-children.ts +79 -20
- package/src/runs/background/run-id-query.ts +7 -0
- package/src/runs/background/run-id-resolver.ts +37 -29
- package/src/runs/background/run-status.ts +34 -20
- package/src/runs/background/scheduled-runs.ts +71 -27
- package/src/runs/background/stale-run-reconciler.ts +33 -16
- package/src/runs/background/steering.ts +11 -1
- package/src/runs/background/subagent-runner.ts +535 -161
- package/src/runs/background/subagent-wait.ts +9 -7
- package/src/runs/background/terminal-run-index.ts +129 -0
- package/src/runs/background/wait-completions.ts +22 -2
- package/src/runs/background/wait-subscriptions.ts +80 -1
- package/src/runs/foreground/async-dismiss-action.ts +2 -1
- package/src/runs/foreground/async-steering-action.ts +21 -14
- package/src/runs/foreground/execution.ts +221 -15
- package/src/runs/foreground/subagent-executor.ts +529 -1519
- package/src/runs/foreground/workflow-foreground-steering.ts +6 -5
- package/src/runs/shared/chain-outputs.ts +1 -3
- package/src/runs/shared/completion-guard.ts +96 -6
- package/src/runs/shared/external-cli-runner.ts +4 -0
- package/src/runs/shared/external-job-bridge.ts +450 -0
- package/src/runs/shared/external-job-runner.ts +286 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
- package/src/runs/shared/model-fallback.ts +34 -3
- package/src/runs/shared/nested-events.ts +66 -62
- package/src/runs/shared/orca-progress-tabs.ts +437 -0
- package/src/runs/shared/parallel-handoff.ts +46 -4
- package/src/runs/shared/parallel-utils.ts +6 -15
- package/src/runs/shared/permissions.ts +5 -1
- package/src/runs/shared/pi-args.ts +8 -1
- package/src/runs/shared/subagent-control.ts +41 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +13 -15
- package/src/runs/shared/subagent-startup-retry.ts +12 -0
- package/src/runs/shared/tool-timeout.ts +93 -0
- package/src/runs/shared/workflow-graph.ts +1 -23
- package/src/runs/shared/worktree.ts +12 -1
- package/src/shared/atomic-json.ts +22 -2
- package/src/shared/capacity-resilient-json.ts +102 -0
- package/src/shared/completion-owner.ts +14 -0
- package/src/shared/file-system-retry.ts +49 -1
- package/src/shared/fork-context.ts +42 -0
- package/src/shared/prompt-resources.ts +0 -40
- package/src/shared/settings.ts +3 -27
- package/src/shared/types.ts +88 -27
- package/src/shared/utils.ts +8 -0
- package/src/shared/watch-strategy.ts +10 -0
- package/src/slash/slash-commands.ts +45 -28
- package/src/slash/slash-live-state.ts +3 -0
- package/src/tui/fleet-status.ts +160 -45
- package/src/tui/fleet.ts +186 -28
- package/src/tui/render.ts +41 -10
- package/src/workflows/chat-progress.ts +7 -5
- package/src/workflows/scripted-workflow.ts +424 -125
- package/src/runs/foreground/chain-clarify.ts +0 -1354
- package/src/runs/foreground/chain-execution.ts +0 -1565
|
@@ -20,6 +20,8 @@ import { normalizeParallelGroups } from "./parallel-groups.ts";
|
|
|
20
20
|
import { reconcileAsyncRun, reconcileNestedAsyncDescendants } from "./stale-run-reconciler.ts";
|
|
21
21
|
import { findNestedRouteForRootId, hasLiveNestedDescendants, updateAsyncJobNestedProjection } from "../shared/nested-events.ts";
|
|
22
22
|
import { listAsyncRuns, type AsyncRunSummary } from "./async-status.ts";
|
|
23
|
+
import { EXTERNAL_JOB_BRIDGE_REQUEST_DIR, serviceExternalJobBridgeRequests } from "../shared/external-job-bridge.ts";
|
|
24
|
+
import { shouldUseNativeFsWatch } from "../../shared/watch-strategy.ts";
|
|
23
25
|
|
|
24
26
|
interface AsyncJobTrackerOptions {
|
|
25
27
|
completionRetentionMs?: number;
|
|
@@ -27,6 +29,9 @@ interface AsyncJobTrackerOptions {
|
|
|
27
29
|
pollIntervalMs?: number;
|
|
28
30
|
resultsDir?: string;
|
|
29
31
|
widgetEnabled?: boolean;
|
|
32
|
+
platform?: NodeJS.Platform;
|
|
33
|
+
onJobTerminal?: () => void;
|
|
34
|
+
watch?: typeof fs.watch;
|
|
30
35
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
31
36
|
now?: () => number;
|
|
32
37
|
}
|
|
@@ -66,6 +71,9 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
66
71
|
const runningJobIds = new Set<string>();
|
|
67
72
|
let rootWatcher: fs.FSWatcher | undefined;
|
|
68
73
|
let nextLivenessAt = Date.now() + livenessIntervalMs;
|
|
74
|
+
const watch = options.watch ?? fs.watch;
|
|
75
|
+
const useNativeWatcher = () => shouldUseNativeFsWatch("async-job-tracker", options.platform);
|
|
76
|
+
const terminalStatus = (status: string) => status === "complete" || status === "failed" || status === "paused" || status === "stopped";
|
|
69
77
|
const rerenderWidget = (ctx: ExtensionContext, jobs = Array.from(state.asyncJobs.values())) => {
|
|
70
78
|
if (state.widgetsSuspended) return;
|
|
71
79
|
renderWidget(ctx, options.widgetEnabled === false ? [] : jobs);
|
|
@@ -125,6 +133,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
125
133
|
return {
|
|
126
134
|
asyncId: run.id,
|
|
127
135
|
asyncDir: run.asyncDir,
|
|
136
|
+
toolCallId: run.toolCallId,
|
|
128
137
|
status: run.state,
|
|
129
138
|
sessionId: run.sessionId,
|
|
130
139
|
activityState: run.activityState,
|
|
@@ -138,6 +147,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
138
147
|
mode: run.mode,
|
|
139
148
|
context: run.context,
|
|
140
149
|
cwd: run.cwd,
|
|
150
|
+
sessionRoot: run.sessionRoot,
|
|
141
151
|
agents: visibleSteps.map((step) => step.agent),
|
|
142
152
|
currentStep: run.currentStep,
|
|
143
153
|
chainStepCount: run.chainStepCount,
|
|
@@ -326,6 +336,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
326
336
|
};
|
|
327
337
|
try {
|
|
328
338
|
emitNewControlEvents(job);
|
|
339
|
+
serviceExternalJobBridgeRequests(job.asyncDir);
|
|
329
340
|
try {
|
|
330
341
|
if (job.nestedRoute) reconcileNestedAsyncDescendants(job.nestedRoute, { resultsDir, kill: options.kill, now: options.now });
|
|
331
342
|
} catch (error) {
|
|
@@ -341,6 +352,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
341
352
|
runId: job.asyncId,
|
|
342
353
|
pid: job.pid,
|
|
343
354
|
sessionId: job.sessionId,
|
|
355
|
+
completionOwnerId: job.completionOwnerId,
|
|
344
356
|
mode: job.mode,
|
|
345
357
|
agents: job.agents,
|
|
346
358
|
chainStepCount: job.chainStepCount,
|
|
@@ -403,7 +415,8 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
403
415
|
job.turnBudgetExceeded = status.turnBudgetExceeded ?? job.turnBudgetExceeded;
|
|
404
416
|
job.wrapUpRequested = status.wrapUpRequested ?? job.wrapUpRequested;
|
|
405
417
|
job.sessionFile = status.sessionFile ?? job.sessionFile;
|
|
406
|
-
if (job.status
|
|
418
|
+
if (terminalStatus(job.status)) {
|
|
419
|
+
if (!terminalStatus(previousStatus)) options.onJobTerminal?.();
|
|
407
420
|
rememberFleetJob(state, job);
|
|
408
421
|
if (!nestedRefreshFailed && !hasLiveNestedDescendants(job.nestedChildren) && (previousStatus !== job.status || !state.cleanupTimers.has(job.asyncId))) {
|
|
409
422
|
scheduleCleanup(job.asyncId);
|
|
@@ -441,6 +454,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
441
454
|
};
|
|
442
455
|
|
|
443
456
|
const watchJob = (job: AsyncJobState) => {
|
|
457
|
+
if (!useNativeWatcher()) return;
|
|
444
458
|
const watched = jobWatchers.get(job.asyncId) ?? { watchers: new Map<string, fs.FSWatcher>() };
|
|
445
459
|
const active = job.status === "queued" || job.status === "running";
|
|
446
460
|
const watchPath = (watchPath: string): boolean => {
|
|
@@ -448,11 +462,11 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
448
462
|
const nestedEventSink = job.nestedRoute?.eventSink === watchPath;
|
|
449
463
|
let watcher: fs.FSWatcher;
|
|
450
464
|
try {
|
|
451
|
-
watcher =
|
|
465
|
+
watcher = watch(resolveWatchPath(watchPath), (_event, file) => {
|
|
452
466
|
const rawFileName = file?.toString();
|
|
453
467
|
const fileName = rawFileName ?? path.basename(watchPath);
|
|
454
468
|
const nestedEventFile = nestedEventSink && (!rawFileName || rawFileName.endsWith(".json") || rawFileName.endsWith(".jsonl"));
|
|
455
|
-
if (fileName === "status.json" || fileName === "events.jsonl" || nestedEventFile) {
|
|
469
|
+
if (fileName === "status.json" || fileName === "events.jsonl" || fileName === EXTERNAL_JOB_BRIDGE_REQUEST_DIR || fileName.endsWith(".json") || nestedEventFile) {
|
|
456
470
|
scheduleJobRefresh(job.asyncId);
|
|
457
471
|
}
|
|
458
472
|
if (watchPath !== job.asyncDir && !nestedEventSink) {
|
|
@@ -479,6 +493,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
479
493
|
const statusWatched = watchPath(statusPath);
|
|
480
494
|
if (statusWatched && !hadStatusWatcher) scheduleJobRefresh(job.asyncId);
|
|
481
495
|
watchPath(path.join(job.asyncDir, "events.jsonl"));
|
|
496
|
+
watchPath(path.join(job.asyncDir, EXTERNAL_JOB_BRIDGE_REQUEST_DIR));
|
|
482
497
|
if (job.nestedRoute) watchPath(job.nestedRoute.eventSink);
|
|
483
498
|
if (!statusWatched && active && !watched.retryTimer) {
|
|
484
499
|
watched.retryTimer = setTimeout(() => {
|
|
@@ -496,9 +511,10 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
496
511
|
};
|
|
497
512
|
|
|
498
513
|
const watchAsyncRoot = () => {
|
|
514
|
+
if (!useNativeWatcher()) return;
|
|
499
515
|
if (rootWatcher) return;
|
|
500
516
|
try {
|
|
501
|
-
rootWatcher =
|
|
517
|
+
rootWatcher = watch(resolveWatchPath(asyncDirRoot), (_event, file) => {
|
|
502
518
|
const runDirName = file?.toString();
|
|
503
519
|
for (const job of state.asyncJobs.values()) {
|
|
504
520
|
if (jobWatchers.has(job.asyncId) || (runDirName && path.basename(job.asyncDir) !== runDirName)) continue;
|
|
@@ -567,6 +583,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
567
583
|
status: "queued",
|
|
568
584
|
pid: typeof info.pid === "number" ? info.pid : undefined,
|
|
569
585
|
...(typeof info.sessionId === "string" ? { sessionId: info.sessionId } : {}),
|
|
586
|
+
...(typeof info.completionOwnerId === "string" ? { completionOwnerId: info.completionOwnerId } : {}),
|
|
570
587
|
mode: info.mode ?? (info.chain ? "chain" : "single"),
|
|
571
588
|
description: info.goal ?? info.task,
|
|
572
589
|
agents,
|
|
@@ -8,6 +8,9 @@ import { intersectSubagentCapabilityCeilings, parseSubagentCapabilityCeiling, ty
|
|
|
8
8
|
import { validateRunFanoutBudgetDescriptor } from "../shared/run-fanout-budget.ts";
|
|
9
9
|
import { resolveTurnBudgetConfig } from "../shared/turn-budget.ts";
|
|
10
10
|
import { reconcileAsyncRun } from "./stale-run-reconciler.ts";
|
|
11
|
+
import { resultFilePath, resultPayloadPathForIndexedRun } from "./result-files.ts";
|
|
12
|
+
import { canScanAsyncRunPrefix, MIN_SAFE_ASYNC_RUN_PREFIX_LENGTH } from "./run-id-query.ts";
|
|
13
|
+
import { parallelHandoffPath, resolveRetainedWorktreeCwd } from "../shared/parallel-handoff.ts";
|
|
11
14
|
|
|
12
15
|
export interface AsyncResumeParams {
|
|
13
16
|
id?: string;
|
|
@@ -172,7 +175,9 @@ function prefixedRunIds(dir: string, prefix: string, suffix = ""): string[] {
|
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
function exactResultPath(resultsDir: string, runId: string): string | null {
|
|
175
|
-
const
|
|
178
|
+
const indexed = resultPayloadPathForIndexedRun(resultsDir, runId);
|
|
179
|
+
if (indexed) return indexed;
|
|
180
|
+
const resultPath = resultFilePath(resultsDir, runId);
|
|
176
181
|
assertInsideRoot(resultsDir, resultPath, "Async result file");
|
|
177
182
|
return fs.existsSync(resultPath) ? resultPath : null;
|
|
178
183
|
}
|
|
@@ -180,12 +185,10 @@ function exactResultPath(resultsDir: string, runId: string): string | null {
|
|
|
180
185
|
export function findAsyncRunPrefixMatches(prefix: string, asyncDirRoot: string, resultsDir: string): Array<{ id: string; location: AsyncRunLocation }> {
|
|
181
186
|
const requestedId = assertRunId(prefix, "id");
|
|
182
187
|
if (!requestedId) return [];
|
|
188
|
+
if (!canScanAsyncRunPrefix(requestedId)) return [];
|
|
183
189
|
const asyncRoot = path.resolve(asyncDirRoot);
|
|
184
190
|
const resultRoot = path.resolve(resultsDir);
|
|
185
|
-
const matchingIds =
|
|
186
|
-
...prefixedRunIds(asyncRoot, requestedId),
|
|
187
|
-
...prefixedRunIds(resultRoot, requestedId, ".json"),
|
|
188
|
-
])].sort();
|
|
191
|
+
const matchingIds = prefixedRunIds(asyncRoot, requestedId).sort();
|
|
189
192
|
return matchingIds.map((id) => {
|
|
190
193
|
const asyncDir = path.join(asyncRoot, id);
|
|
191
194
|
assertInsideRoot(asyncRoot, asyncDir, "Async run directory");
|
|
@@ -225,6 +228,10 @@ export function resolveAsyncRunLocation(params: AsyncResumeParams, asyncDirRoot:
|
|
|
225
228
|
resolvedId: requestedId,
|
|
226
229
|
};
|
|
227
230
|
}
|
|
231
|
+
if (requestedId.length < MIN_SAFE_ASYNC_RUN_PREFIX_LENGTH) {
|
|
232
|
+
throw new Error(`Async run id prefix '${requestedId}' is too short. Provide at least ${MIN_SAFE_ASYNC_RUN_PREFIX_LENGTH} characters.`);
|
|
233
|
+
}
|
|
234
|
+
if (!canScanAsyncRunPrefix(requestedId)) return { asyncDir: null, resultPath: null, resolvedId: requestedId };
|
|
228
235
|
|
|
229
236
|
const matching = findAsyncRunPrefixMatches(requestedId, asyncRoot, resultRoot);
|
|
230
237
|
if (matching.length === 0) return { asyncDir: null, resultPath: null, resolvedId: requestedId };
|
|
@@ -263,12 +270,7 @@ function validateStatusForResume(status: AsyncStatus | null, source: string): vo
|
|
|
263
270
|
}
|
|
264
271
|
}
|
|
265
272
|
|
|
266
|
-
function normalizeRecoveryAcceptance(value: unknown, descriptorPath: string): AcceptanceInput
|
|
267
|
-
if (value && typeof value === "object" && !Array.isArray(value) && ("explicit" in value || "inferredReason" in value)) {
|
|
268
|
-
const { explicit, inferredReason: _inferredReason, ...publicAcceptance } = value as Record<string, unknown>;
|
|
269
|
-
if (explicit === false) return undefined;
|
|
270
|
-
value = publicAcceptance;
|
|
271
|
-
}
|
|
273
|
+
function normalizeRecoveryAcceptance(value: unknown, descriptorPath: string): AcceptanceInput {
|
|
272
274
|
const errors = validateAcceptanceInput(value, "recoveryDescriptor.acceptance");
|
|
273
275
|
if (errors.length) throw new Error(`Invalid async recovery descriptor '${descriptorPath}': ${errors.join(" ")}`);
|
|
274
276
|
return value as AcceptanceInput;
|
|
@@ -397,11 +399,7 @@ export function readAsyncRecoveryDescriptor(asyncDir: string | undefined): Steer
|
|
|
397
399
|
if (!Array.isArray(control.notifyOn) || control.notifyOn.some((item) => item !== "active_long_running" && item !== "needs_attention")) throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.notifyOn is invalid.`);
|
|
398
400
|
if (!Array.isArray(control.notifyChannels) || control.notifyChannels.some((item) => item !== "event" && item !== "async" && item !== "intercom")) throw new Error(`Invalid async recovery descriptor '${descriptorPath}': controlConfig.notifyChannels is invalid.`);
|
|
399
401
|
}
|
|
400
|
-
if (parsed.acceptance !== undefined)
|
|
401
|
-
const acceptance = normalizeRecoveryAcceptance(parsed.acceptance, descriptorPath);
|
|
402
|
-
if (acceptance === undefined) delete parsed.acceptance;
|
|
403
|
-
else parsed.acceptance = acceptance;
|
|
404
|
-
}
|
|
402
|
+
if (parsed.acceptance !== undefined) parsed.acceptance = normalizeRecoveryAcceptance(parsed.acceptance, descriptorPath);
|
|
405
403
|
return parsed as unknown as SteeringRecoveryDescriptor;
|
|
406
404
|
}
|
|
407
405
|
|
|
@@ -412,6 +410,17 @@ function validateResumeSessionFile(runId: string, sessionFile: string): string {
|
|
|
412
410
|
return resolved;
|
|
413
411
|
}
|
|
414
412
|
|
|
413
|
+
function validateResumeCwd(runId: string, cwd: string | undefined): string | undefined {
|
|
414
|
+
if (!cwd) return undefined;
|
|
415
|
+
const resolved = path.resolve(cwd);
|
|
416
|
+
try {
|
|
417
|
+
if (!fs.statSync(resolved).isDirectory()) throw new Error("path is not a directory");
|
|
418
|
+
} catch (error) {
|
|
419
|
+
throw new Error(`Async run '${runId}' required cwd does not exist: ${cwd}`, { cause: error instanceof Error ? error : undefined });
|
|
420
|
+
}
|
|
421
|
+
return resolved;
|
|
422
|
+
}
|
|
423
|
+
|
|
415
424
|
export function resolveAsyncResumeTarget(params: AsyncResumeParams, deps: AsyncResumeDeps = {}, options: AsyncResumeOptions = {}): AsyncResumeTarget {
|
|
416
425
|
const asyncDirRoot = deps.asyncDirRoot ?? DIRS.async;
|
|
417
426
|
const resultsDir = deps.resultsDir ?? DIRS.results;
|
|
@@ -512,6 +521,10 @@ export function resolveAsyncResumeTarget(params: AsyncResumeParams, deps: AsyncR
|
|
|
512
521
|
const stepModel = statusSteps[index]?.model ?? resultSteps[index]?.model ?? (stepCount === 1 ? result?.model : undefined);
|
|
513
522
|
const stepThinking = statusSteps[index]?.thinking ?? resultSteps[index]?.thinking ?? (stepCount === 1 ? result?.thinking : undefined);
|
|
514
523
|
const capabilityCeiling = intersectSubagentCapabilityCeilings(status?.capabilityCeiling, statusSteps[index]?.capabilityCeiling, result?.capabilityCeiling, resultSteps[index]?.capabilityCeiling);
|
|
524
|
+
const managedWorktreeCwd = location.asyncDir
|
|
525
|
+
? resolveRetainedWorktreeCwd(parallelHandoffPath(location.asyncDir), runId, index)
|
|
526
|
+
: undefined;
|
|
527
|
+
const resumeCwd = validateResumeCwd(runId, managedWorktreeCwd ?? status?.cwd ?? result?.cwd ?? recoveryDescriptor?.cwd);
|
|
515
528
|
|
|
516
529
|
return {
|
|
517
530
|
kind: "revive",
|
|
@@ -520,7 +533,7 @@ export function resolveAsyncResumeTarget(params: AsyncResumeParams, deps: AsyncR
|
|
|
520
533
|
state,
|
|
521
534
|
agent,
|
|
522
535
|
index,
|
|
523
|
-
cwd:
|
|
536
|
+
...(resumeCwd ? { cwd: resumeCwd } : {}),
|
|
524
537
|
...(resolvedSessionFile ? { sessionFile: resolvedSessionFile } : {}),
|
|
525
538
|
...(stepModel ? { model: stepModel } : {}),
|
|
526
539
|
...(stepThinking ? { thinking: stepThinking } : {}),
|