pi-cohort 7.3.1 → 7.4.1
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 +13 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/runs/background/async-execution.ts +12 -6
- package/src/runs/background/subagent-runner.ts +91 -33
- package/src/runs/foreground/chain-execution.ts +515 -498
- package/src/runs/foreground/execution.ts +24 -5
- package/src/runs/foreground/subagent-executor.ts +10 -9
- package/src/runs/shared/acceptance.ts +4 -2
- package/src/runs/shared/chain-outputs.ts +73 -12
- package/src/runs/shared/parallel-utils.ts +1 -0
- package/src/runs/shared/single-output.ts +10 -0
- package/src/runs/shared/worktree.ts +32 -0
- package/src/shared/types.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [7.4.1] - 2026-10-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Non-BLOCKED file-only failures and save errors return an artifact pointer or an explicit not-preserved reason instead of the child reply inline; BLOCKED handoffs and inline mode are unchanged. (#24)
|
|
7
|
+
|
|
8
|
+
## [7.4.0] - 2026-10-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Chains can use `{outputs.name.treePath}` in a later child's `cwd` to review a worktree task's base commit plus captured patch in a lazy detached checkout. Trees are shared per named output and cleaned up at chain end; only `treePath` output references are allowed in `cwd`, and not on `worktree: true` steps.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Children with both acceptance and `outputSchema` are instructed to write their acceptance report before calling `structured_output`, so both contracts can be satisfied.
|
|
15
|
+
|
|
3
16
|
## [7.3.1] - 2026-10-18
|
|
4
17
|
|
|
5
18
|
### Fixed
|
package/README.md
CHANGED
|
@@ -126,8 +126,8 @@ subagent({ async: false, chain: [
|
|
|
126
126
|
{ agent: "implementer", as: "impl_2", task: "Implement task 2", outputSchema: statusSchema },
|
|
127
127
|
], worktree: true, stopOn: { path: "/status", in: ["NEEDS_CONTEXT", "BLOCKED"] } },
|
|
128
128
|
{ parallel: [
|
|
129
|
-
{ agent: "spec-reviewer", as: "sr_1", task: "Review {outputs.impl_1.patchPath}", outputSchema: verdictSchema },
|
|
130
|
-
{ agent: "spec-reviewer", as: "sr_2", task: "Review {outputs.impl_2.patchPath}", outputSchema: verdictSchema },
|
|
129
|
+
{ agent: "spec-reviewer", as: "sr_1", cwd: "{outputs.impl_1.treePath}", acceptance: { level: "attested" }, task: "Review {outputs.impl_1.patchPath}", outputSchema: verdictSchema },
|
|
130
|
+
{ agent: "spec-reviewer", as: "sr_2", cwd: "{outputs.impl_2.treePath}", acceptance: { level: "attested" }, task: "Review {outputs.impl_2.patchPath}", outputSchema: verdictSchema },
|
|
131
131
|
], stopOn: { path: "/verdict", in: ["FAIL"] } },
|
|
132
132
|
]})
|
|
133
133
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cohort",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.4.1",
|
|
4
4
|
"description": "Delegate Pi work to focused child agents: code review, scouting, implementation, parallel audits, saved chains, and background jobs.",
|
|
5
5
|
"author": "Jacek Juraszek",
|
|
6
6
|
"license": "MIT",
|
|
@@ -446,13 +446,16 @@ export function executeAsyncChain(
|
|
|
446
446
|
...(s.model ? { model: s.model } : {}),
|
|
447
447
|
};
|
|
448
448
|
};
|
|
449
|
-
const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior, isolatedLeaf?: { runDir: string; index: number }) => {
|
|
449
|
+
const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior, isolatedLeaf?: { runDir: string; index: number }, groupCwd?: string) => {
|
|
450
450
|
const a = agents.find((x) => x.name === s.agent)!;
|
|
451
|
-
const
|
|
452
|
-
const
|
|
451
|
+
const effectiveCwd = s.cwd ?? groupCwd;
|
|
452
|
+
const templatedCwd = effectiveCwd?.includes("{outputs.");
|
|
453
|
+
const stepCwd = templatedCwd ? effectiveCwd : resolveChildCwd(runnerCwd, effectiveCwd);
|
|
454
|
+
const preparationCwd = templatedCwd ? runnerCwd : stepCwd;
|
|
455
|
+
const instructionCwd = behaviorCwd ?? preparationCwd;
|
|
453
456
|
const behavior = suppressProgressForReadOnlyTask(resolvedBehavior ?? resolveStepBehavior(a, buildStepOverrides(s), chainSkills), s.task, originalTask);
|
|
454
457
|
const skillNames = behavior.skills === false ? [] : behavior.skills;
|
|
455
|
-
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames,
|
|
458
|
+
const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, preparationCwd, ctx.cwd);
|
|
456
459
|
if (missingSkills.includes("pi-cohort")) throw new UnavailableSubagentSkillError(UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
457
460
|
|
|
458
461
|
let systemPrompt = a.systemPrompt?.trim() ?? "";
|
|
@@ -536,13 +539,14 @@ export function executeAsyncChain(
|
|
|
536
539
|
try {
|
|
537
540
|
steps = chain.map((s, stepIndex) => {
|
|
538
541
|
if (isParallelStep(s)) {
|
|
542
|
+
const groupCwd = s.cwd?.includes("{outputs.") ? runnerCwd : resolveChildCwd(runnerCwd, s.cwd);
|
|
539
543
|
const parallelBehaviors = s.parallel.map((task) => {
|
|
540
544
|
const agent = agents.find((candidate) => candidate.name === task.agent)!;
|
|
541
545
|
return suppressProgressForReadOnlyTask(resolveStepBehavior(agent, buildStepOverrides(task), chainSkills), task.task, originalTask);
|
|
542
546
|
});
|
|
543
547
|
const progressPrecreated = parallelBehaviors.some((behavior) => behavior.progress);
|
|
544
548
|
if (progressPrecreated) {
|
|
545
|
-
if (!s.worktree) writeInitialProgressFile(
|
|
549
|
+
if (!s.worktree) writeInitialProgressFile(groupCwd);
|
|
546
550
|
progressInstructionCreated = true;
|
|
547
551
|
}
|
|
548
552
|
return {
|
|
@@ -550,7 +554,7 @@ export function executeAsyncChain(
|
|
|
550
554
|
let behaviorCwd: string | undefined;
|
|
551
555
|
if (s.worktree) {
|
|
552
556
|
try {
|
|
553
|
-
behaviorCwd = resolveExpectedWorktreeAgentCwd(
|
|
557
|
+
behaviorCwd = resolveExpectedWorktreeAgentCwd(groupCwd, `${id}-s${stepIndex}`, taskIndex);
|
|
554
558
|
} catch {
|
|
555
559
|
behaviorCwd = undefined;
|
|
556
560
|
}
|
|
@@ -562,8 +566,10 @@ export function executeAsyncChain(
|
|
|
562
566
|
progressPrecreated,
|
|
563
567
|
parallelBehaviors[taskIndex],
|
|
564
568
|
s.worktree ? { runDir: path.join(asyncDir, `step-${stepIndex}`), index: taskIndex } : undefined,
|
|
569
|
+
s.cwd,
|
|
565
570
|
);
|
|
566
571
|
}),
|
|
572
|
+
cwd: s.cwd,
|
|
567
573
|
concurrency: s.concurrency,
|
|
568
574
|
failFast: s.failFast,
|
|
569
575
|
worktree: s.worktree,
|
|
@@ -7,7 +7,7 @@ import type { Message } from "@earendil-works/pi-ai";
|
|
|
7
7
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
8
8
|
import { appendJsonl, getArtifactPaths } from "../../shared/artifacts.ts";
|
|
9
9
|
import { getPiSpawnCommand, parsePiRuntime, PiRuntimeLaunchError, type PiRuntimeIdentity } from "../shared/pi-spawn.ts";
|
|
10
|
-
import { captureSingleOutputSnapshot, finalizeSingleOutput, formatSavedOutputReference, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
10
|
+
import { captureSingleOutputSnapshot, finalizeSingleOutput, formatFileOnlyFailure, formatSavedOutputReference, resolveSingleOutput, type SingleOutputSnapshot } from "../shared/single-output.ts";
|
|
11
11
|
import {
|
|
12
12
|
type ActivityState,
|
|
13
13
|
type ArtifactConfig,
|
|
@@ -46,14 +46,14 @@ import {
|
|
|
46
46
|
MAX_PARALLEL_CONCURRENCY,
|
|
47
47
|
} from "../shared/parallel-utils.ts";
|
|
48
48
|
import { buildPiArgs, cleanupTempDir, runDirEnv } from "../shared/pi-args.ts";
|
|
49
|
-
import { ChainOutputValidationError, outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.ts";
|
|
49
|
+
import { ChainOutputValidationError, TreeRegistry, outputEntryFromAsyncResult, resolveOutputReferences } from "../shared/chain-outputs.ts";
|
|
50
50
|
import { buildStopRecord, evaluateStopCondition } from "../shared/stop-conditions.ts";
|
|
51
51
|
import { createStructuredOutputRuntime, readStructuredOutput } from "../shared/structured-output.ts";
|
|
52
52
|
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection } from "../shared/dynamic-fanout.ts";
|
|
53
53
|
import { nestedSummaryFromAsyncStatus, writeNestedEvent } from "../shared/nested-events.ts";
|
|
54
54
|
import { formatModelAttemptNote, isRetryableModelFailure } from "../shared/model-fallback.ts";
|
|
55
55
|
import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
56
|
-
import { detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput } from "../../shared/utils.ts";
|
|
56
|
+
import { detectSubagentError, extractTextFromContent, extractToolArgsPreview, getFinalOutput, resolveChildCwd } from "../../shared/utils.ts";
|
|
57
57
|
import { blockedLine, evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
|
|
58
58
|
import {
|
|
59
59
|
createMutatingFailureState,
|
|
@@ -70,6 +70,7 @@ import { parseSessionTokens } from "../../shared/session-tokens.ts";
|
|
|
70
70
|
import type { TokenUsage } from "../../shared/types.ts";
|
|
71
71
|
import {
|
|
72
72
|
cleanupWorktrees,
|
|
73
|
+
removeTrees,
|
|
73
74
|
createWorktrees,
|
|
74
75
|
diffWorktrees,
|
|
75
76
|
findWorktreeTaskCwdConflict,
|
|
@@ -570,6 +571,7 @@ function writeRunLog(
|
|
|
570
571
|
export interface SingleStepContext {
|
|
571
572
|
previousOutput: string;
|
|
572
573
|
outputs?: ChainOutputMap;
|
|
574
|
+
trees?: TreeRegistry;
|
|
573
575
|
placeholder: string;
|
|
574
576
|
cwd: string;
|
|
575
577
|
sessionEnabled: boolean;
|
|
@@ -612,10 +614,20 @@ export async function runSingleStep(
|
|
|
612
614
|
acceptance?: import("../../shared/types.ts").AcceptanceLedger;
|
|
613
615
|
executionSurface?: ExecutionSurfaceResult;
|
|
614
616
|
}> {
|
|
617
|
+
let childCwd: string;
|
|
618
|
+
const placeholderRegex = new RegExp(ctx.placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
|
619
|
+
let task = step.task.replace(placeholderRegex, () => ctx.previousOutput);
|
|
620
|
+
try {
|
|
621
|
+
childCwd = resolveChildCwd(ctx.cwd, resolveOutputReferences(step.cwd ?? ctx.cwd, ctx.outputs ?? {}, ctx.trees));
|
|
622
|
+
task = resolveOutputReferences(task, ctx.outputs ?? {}, ctx.trees);
|
|
623
|
+
} catch (error) {
|
|
624
|
+
if (!(error instanceof ChainOutputValidationError)) throw error;
|
|
625
|
+
return { agent: step.agent, output: error.message, exitCode: 1, error: error.message, sessionFile: step.sessionFile };
|
|
626
|
+
}
|
|
615
627
|
let backendSelection: import("../../execution-backend/selection.ts").ExecutionBackendSelectionResult;
|
|
616
628
|
try {
|
|
617
629
|
backendSelection = await selectConfiguredExecutionBackend({
|
|
618
|
-
cwd:
|
|
630
|
+
cwd: childCwd,
|
|
619
631
|
userConfig: { executionBackend: ctx.executionBackendConfig?.userPreference },
|
|
620
632
|
preparation: ctx.executionBackendCoordinator,
|
|
621
633
|
});
|
|
@@ -635,17 +647,9 @@ export async function runSingleStep(
|
|
|
635
647
|
const effectiveStructuredOutput = step.structuredOutput ?? (step.structuredOutputSchema
|
|
636
648
|
? createStructuredOutputRuntime(step.structuredOutputSchema, path.join(path.dirname(ctx.outputFile), "structured-output"))
|
|
637
649
|
: undefined);
|
|
638
|
-
const placeholderRegex = new RegExp(ctx.placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
|
639
|
-
let task = step.task.replace(placeholderRegex, () => ctx.previousOutput);
|
|
640
|
-
try {
|
|
641
|
-
task = resolveOutputReferences(task, ctx.outputs ?? {});
|
|
642
|
-
} catch (error) {
|
|
643
|
-
if (!(error instanceof ChainOutputValidationError)) throw error;
|
|
644
|
-
return { agent: step.agent, output: error.message, exitCode: 1, error: error.message, sessionFile: step.sessionFile };
|
|
645
|
-
}
|
|
646
650
|
const taskForCompletionGuard = task;
|
|
647
651
|
if (step.effectiveAcceptance) {
|
|
648
|
-
const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance);
|
|
652
|
+
const acceptancePrompt = formatAcceptancePrompt(step.effectiveAcceptance, { structuredOutput: Boolean(effectiveStructuredOutput) });
|
|
649
653
|
if (acceptancePrompt) task = `${task}\n${acceptancePrompt}`;
|
|
650
654
|
}
|
|
651
655
|
const sessionEnabled = Boolean(step.sessionFile) || ctx.sessionEnabled;
|
|
@@ -689,7 +693,7 @@ export async function runSingleStep(
|
|
|
689
693
|
runId: ctx.id,
|
|
690
694
|
childId: `${step.agent}-${ctx.flatIndex}`,
|
|
691
695
|
backend: selection.backend,
|
|
692
|
-
cwd:
|
|
696
|
+
cwd: childCwd,
|
|
693
697
|
title: step.agent,
|
|
694
698
|
signal: ownerController.signal,
|
|
695
699
|
});
|
|
@@ -735,7 +739,7 @@ export async function runSingleStep(
|
|
|
735
739
|
extensions: step.extensions,
|
|
736
740
|
systemPrompt: step.systemPrompt,
|
|
737
741
|
systemPromptMode: step.systemPromptMode,
|
|
738
|
-
cwd:
|
|
742
|
+
cwd: childCwd,
|
|
739
743
|
promptFileStem: step.agent,
|
|
740
744
|
runId: ctx.id,
|
|
741
745
|
childAgentName: step.agent,
|
|
@@ -760,7 +764,7 @@ export async function runSingleStep(
|
|
|
760
764
|
attemptId: `${ctx.id}-${ctx.flatIndex}-attempt-${index}`,
|
|
761
765
|
args,
|
|
762
766
|
environment: mergedEnv,
|
|
763
|
-
cwd:
|
|
767
|
+
cwd: childCwd,
|
|
764
768
|
sessionFile: attemptSessionFile,
|
|
765
769
|
interruptSignal: interruptController.signal,
|
|
766
770
|
onChildEvent: ctx.onChildEvent,
|
|
@@ -881,7 +885,7 @@ export async function runSingleStep(
|
|
|
881
885
|
extensions: step.extensions,
|
|
882
886
|
systemPrompt: step.systemPrompt,
|
|
883
887
|
systemPromptMode: step.systemPromptMode,
|
|
884
|
-
cwd:
|
|
888
|
+
cwd: childCwd,
|
|
885
889
|
promptFileStem: step.agent,
|
|
886
890
|
runId: ctx.id,
|
|
887
891
|
childAgentName: step.agent,
|
|
@@ -897,7 +901,7 @@ export async function runSingleStep(
|
|
|
897
901
|
try {
|
|
898
902
|
run = await runPiStreaming(
|
|
899
903
|
args,
|
|
900
|
-
|
|
904
|
+
childCwd,
|
|
901
905
|
ctx.outputFile,
|
|
902
906
|
env,
|
|
903
907
|
ctx.runtime,
|
|
@@ -1003,6 +1007,8 @@ export async function runSingleStep(
|
|
|
1003
1007
|
let acceptanceCanFailRun = false;
|
|
1004
1008
|
let effectiveFinalExitCode = resolvedOutput.saveError ? 1 : finalResult?.exitCode ?? 1;
|
|
1005
1009
|
let effectiveFinalError = finalResult?.error;
|
|
1010
|
+
let preservedAt: string | undefined;
|
|
1011
|
+
let notPreservedReason = "artifact output disabled";
|
|
1006
1012
|
if (resolvedOutput.saveError) {
|
|
1007
1013
|
effectiveFinalError = effectiveFinalError
|
|
1008
1014
|
? `${effectiveFinalError}\n${resolvedOutput.saveError}`
|
|
@@ -1014,7 +1020,7 @@ export async function runSingleStep(
|
|
|
1014
1020
|
? await evaluateAcceptance({
|
|
1015
1021
|
acceptance: step.effectiveAcceptance,
|
|
1016
1022
|
output: outputForAcceptance,
|
|
1017
|
-
cwd:
|
|
1023
|
+
cwd: childCwd,
|
|
1018
1024
|
})
|
|
1019
1025
|
: undefined;
|
|
1020
1026
|
const acceptanceFailure = acceptance ? acceptanceFailureMessage(acceptance) : undefined;
|
|
@@ -1033,7 +1039,14 @@ export async function runSingleStep(
|
|
|
1033
1039
|
|
|
1034
1040
|
if (artifactPaths && ctx.artifactConfig?.enabled !== false) {
|
|
1035
1041
|
if (ctx.artifactConfig?.includeOutput !== false) {
|
|
1036
|
-
|
|
1042
|
+
try {
|
|
1043
|
+
fs.writeFileSync(artifactPaths.outputPath, output, "utf-8");
|
|
1044
|
+
preservedAt = artifactPaths.outputPath;
|
|
1045
|
+
} catch (error) {
|
|
1046
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1047
|
+
notPreservedReason = `artifact write failed: ${message}`;
|
|
1048
|
+
throw error;
|
|
1049
|
+
}
|
|
1037
1050
|
}
|
|
1038
1051
|
if (ctx.artifactConfig?.includeMetadata !== false) {
|
|
1039
1052
|
fs.writeFileSync(
|
|
@@ -1059,11 +1072,14 @@ export async function runSingleStep(
|
|
|
1059
1072
|
effectiveFinalError = effectiveFinalError ? `${effectiveFinalError}\n${message}` : message;
|
|
1060
1073
|
}
|
|
1061
1074
|
|
|
1075
|
+
const outputBeforeExternalFinalization = outputForSummary;
|
|
1076
|
+
let externalOutputPersisted = false;
|
|
1062
1077
|
// Native runs have runPiStreaming write outputFile live; external runs persist it
|
|
1063
1078
|
// before their surface can be destructively closed as delivered.
|
|
1064
1079
|
if (externalOwner) {
|
|
1065
1080
|
try {
|
|
1066
1081
|
fs.writeFileSync(ctx.outputFile, outputForSummary, "utf-8");
|
|
1082
|
+
externalOutputPersisted = true;
|
|
1067
1083
|
} catch (error) {
|
|
1068
1084
|
const message = safeExternalFailure("output persistence", error);
|
|
1069
1085
|
effectiveFinalExitCode = 1;
|
|
@@ -1086,6 +1102,30 @@ export async function runSingleStep(
|
|
|
1086
1102
|
executionSurface = { handle: finishResult.handle, retained: finishResult.retained };
|
|
1087
1103
|
}
|
|
1088
1104
|
|
|
1105
|
+
if (step.outputMode === "file-only"
|
|
1106
|
+
&& (effectiveFinalExitCode !== 0 || resolvedOutput.saveError)
|
|
1107
|
+
&& !blockedLine(effectiveFinalError ?? "")) {
|
|
1108
|
+
const pointer = formatFileOnlyFailure({
|
|
1109
|
+
outputPath: step.outputPath,
|
|
1110
|
+
saveError: resolvedOutput.saveError,
|
|
1111
|
+
...(preservedAt !== undefined ? { preservedAt } : { notPreservedReason }),
|
|
1112
|
+
});
|
|
1113
|
+
outputForSummary = attemptNotes.length > 0
|
|
1114
|
+
? `${attemptNotes.join("\n")}\n\n${pointer}`
|
|
1115
|
+
: pointer;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// finish can turn a saved success into a failure after the pre-close log write.
|
|
1119
|
+
if (externalOutputPersisted && outputForSummary !== outputBeforeExternalFinalization) {
|
|
1120
|
+
try {
|
|
1121
|
+
fs.writeFileSync(ctx.outputFile, outputForSummary, "utf-8");
|
|
1122
|
+
} catch (error) {
|
|
1123
|
+
const message = safeExternalFailure("output persistence", error);
|
|
1124
|
+
effectiveFinalExitCode = 1;
|
|
1125
|
+
effectiveFinalError = effectiveFinalError ? `${effectiveFinalError}\n${message}` : message;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1089
1129
|
return {
|
|
1090
1130
|
agent: step.agent,
|
|
1091
1131
|
output: outputForSummary,
|
|
@@ -1204,8 +1244,9 @@ function prepareParallelTaskRun(
|
|
|
1204
1244
|
cwd: string,
|
|
1205
1245
|
worktreeSetup: WorktreeSetup | undefined,
|
|
1206
1246
|
taskIndex: number,
|
|
1247
|
+
groupCwd?: string,
|
|
1207
1248
|
): { taskForRun: SubagentStep; taskCwd: string } {
|
|
1208
|
-
if (!worktreeSetup) return { taskForRun: task, taskCwd: cwd };
|
|
1249
|
+
if (!worktreeSetup) return { taskForRun: { ...task, cwd: task.cwd ?? groupCwd }, taskCwd: cwd };
|
|
1209
1250
|
return {
|
|
1210
1251
|
taskForRun: { ...task, cwd: undefined },
|
|
1211
1252
|
taskCwd: worktreeSetup.worktrees[taskIndex]!.agentCwd,
|
|
@@ -1251,6 +1292,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1251
1292
|
const overallStartTime = Date.now();
|
|
1252
1293
|
const shareEnabled = config.share === true;
|
|
1253
1294
|
const asyncDir = config.asyncDir;
|
|
1295
|
+
const trees = new TreeRegistry(asyncDir);
|
|
1254
1296
|
const executionBackendConfig = config.executionBackends ?? {
|
|
1255
1297
|
protocolVersion: 1 as const,
|
|
1256
1298
|
registrations: [],
|
|
@@ -1669,7 +1711,6 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1669
1711
|
if (interrupted || statusPayload.state !== "running") return;
|
|
1670
1712
|
interrupted = true;
|
|
1671
1713
|
const now = Date.now();
|
|
1672
|
-
statusPayload.state = "paused";
|
|
1673
1714
|
currentActivityState = undefined;
|
|
1674
1715
|
statusPayload.activityState = undefined;
|
|
1675
1716
|
statusPayload.lastUpdate = now;
|
|
@@ -1683,11 +1724,6 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1683
1724
|
}
|
|
1684
1725
|
}
|
|
1685
1726
|
writeStatusPayload();
|
|
1686
|
-
appendJsonl(eventsPath, JSON.stringify({
|
|
1687
|
-
type: "subagent.run.paused",
|
|
1688
|
-
ts: now,
|
|
1689
|
-
runId: id,
|
|
1690
|
-
}));
|
|
1691
1727
|
activeChildInterrupt?.();
|
|
1692
1728
|
};
|
|
1693
1729
|
process.on(ASYNC_INTERRUPT_SIGNAL, interruptRunner);
|
|
@@ -1705,6 +1741,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1705
1741
|
|
|
1706
1742
|
let flatIndex = 0;
|
|
1707
1743
|
|
|
1744
|
+
try {
|
|
1708
1745
|
for (let stepIndex = 0; stepIndex < steps.length; stepIndex++) {
|
|
1709
1746
|
if (interrupted) break;
|
|
1710
1747
|
const step = steps[stepIndex];
|
|
@@ -1884,6 +1921,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1884
1921
|
const singleResult = await runSingleStep(task, {
|
|
1885
1922
|
previousOutput, placeholder, cwd, sessionEnabled,
|
|
1886
1923
|
outputs,
|
|
1924
|
+
trees,
|
|
1887
1925
|
sessionDir: config.sessionDir ? path.join(config.sessionDir, `dynamic-${stepIndex}-${taskIdx}`) : undefined,
|
|
1888
1926
|
artifactsDir, artifactConfig, id,
|
|
1889
1927
|
flatIndex: fi, flatStepCount: Math.max(statusPayload.steps.length, 1),
|
|
@@ -2022,13 +2060,14 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2022
2060
|
|
|
2023
2061
|
if (isParallelGroup(step)) {
|
|
2024
2062
|
const group = step;
|
|
2063
|
+
const groupCwd = group.cwd?.includes("{outputs.") ? group.cwd : resolveChildCwd(cwd, group.cwd);
|
|
2025
2064
|
const concurrency = group.concurrency ?? MAX_PARALLEL_CONCURRENCY;
|
|
2026
2065
|
const failFast = group.failFast ?? false;
|
|
2027
2066
|
const groupStartFlatIndex = flatIndex;
|
|
2028
2067
|
let aborted = false;
|
|
2029
2068
|
let worktreeSetup: WorktreeSetup | undefined;
|
|
2030
2069
|
if (group.worktree) {
|
|
2031
|
-
const worktreeTaskCwdConflict = findWorktreeTaskCwdConflict(group.parallel,
|
|
2070
|
+
const worktreeTaskCwdConflict = findWorktreeTaskCwdConflict(group.parallel, groupCwd);
|
|
2032
2071
|
if (worktreeTaskCwdConflict) {
|
|
2033
2072
|
const failedAt = Date.now();
|
|
2034
2073
|
markParallelGroupSetupFailure({
|
|
@@ -2036,7 +2075,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2036
2075
|
results,
|
|
2037
2076
|
group,
|
|
2038
2077
|
groupStartFlatIndex,
|
|
2039
|
-
setupError: formatWorktreeTaskCwdConflict(worktreeTaskCwdConflict,
|
|
2078
|
+
setupError: formatWorktreeTaskCwdConflict(worktreeTaskCwdConflict, groupCwd),
|
|
2040
2079
|
failedAt,
|
|
2041
2080
|
statusPath,
|
|
2042
2081
|
eventsPath,
|
|
@@ -2048,7 +2087,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2048
2087
|
break;
|
|
2049
2088
|
}
|
|
2050
2089
|
try {
|
|
2051
|
-
worktreeSetup = createWorktrees(
|
|
2090
|
+
worktreeSetup = createWorktrees(groupCwd, `${id}-s${stepIndex}`, group.parallel.length, {
|
|
2052
2091
|
agents: group.parallel.map((task) => task.agent),
|
|
2053
2092
|
setupHook: config.worktreeSetupHook
|
|
2054
2093
|
? { hookPath: config.worktreeSetupHook, timeoutMs: config.worktreeSetupHookTimeoutMs }
|
|
@@ -2076,7 +2115,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2076
2115
|
}
|
|
2077
2116
|
|
|
2078
2117
|
try {
|
|
2079
|
-
if (group.worktree) ensureParallelProgressFile(
|
|
2118
|
+
if (group.worktree) ensureParallelProgressFile(groupCwd, group);
|
|
2080
2119
|
const groupStartTime = Date.now();
|
|
2081
2120
|
markParallelGroupRunning({
|
|
2082
2121
|
statusPayload,
|
|
@@ -2133,11 +2172,12 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2133
2172
|
const taskSessionDir = config.sessionDir
|
|
2134
2173
|
? path.join(config.sessionDir, `parallel-${fi}`)
|
|
2135
2174
|
: undefined;
|
|
2136
|
-
const { taskForRun, taskCwd } = prepareParallelTaskRun(task, cwd, worktreeSetup, taskIdx);
|
|
2175
|
+
const { taskForRun, taskCwd } = prepareParallelTaskRun(task, cwd, worktreeSetup, taskIdx, group.cwd);
|
|
2137
2176
|
|
|
2138
2177
|
const singleResult = await runSingleStep(taskForRun, {
|
|
2139
2178
|
previousOutput, placeholder, cwd: taskCwd, sessionEnabled,
|
|
2140
2179
|
outputs,
|
|
2180
|
+
trees,
|
|
2141
2181
|
sessionDir: taskSessionDir,
|
|
2142
2182
|
artifactsDir, artifactConfig, id,
|
|
2143
2183
|
flatIndex: fi, flatStepCount: flatSteps.length,
|
|
@@ -2247,7 +2287,12 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2247
2287
|
agent: parallelResults[t]!.agent,
|
|
2248
2288
|
output: parallelResults[t]!.output,
|
|
2249
2289
|
structuredOutput: parallelResults[t]!.structuredOutput,
|
|
2250
|
-
}, stepIndex,
|
|
2290
|
+
}, stepIndex, worktreeSetup ? {
|
|
2291
|
+
...patchInfoForDiff(diffs[t]),
|
|
2292
|
+
taskIndex: t,
|
|
2293
|
+
baseCommit: worktreeSetup.baseCommit,
|
|
2294
|
+
repoCwd: worktreeSetup.cwd,
|
|
2295
|
+
} : undefined);
|
|
2251
2296
|
}
|
|
2252
2297
|
statusPayload.outputs = outputs;
|
|
2253
2298
|
if (!interrupted && group.stopOn) {
|
|
@@ -2310,6 +2355,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2310
2355
|
const singleResult = await runSingleStep(seqStep, {
|
|
2311
2356
|
previousOutput, placeholder, cwd, sessionEnabled,
|
|
2312
2357
|
outputs,
|
|
2358
|
+
trees,
|
|
2313
2359
|
sessionDir: config.sessionDir ? path.join(config.sessionDir, `parallel-${flatIndex}`) : undefined,
|
|
2314
2360
|
artifactsDir, artifactConfig, id,
|
|
2315
2361
|
flatIndex, flatStepCount: flatSteps.length,
|
|
@@ -2423,6 +2469,18 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2423
2469
|
}
|
|
2424
2470
|
}
|
|
2425
2471
|
|
|
2472
|
+
} finally {
|
|
2473
|
+
removeTrees(trees.created);
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
if (interrupted) {
|
|
2477
|
+
const pausedAt = Date.now();
|
|
2478
|
+
statusPayload.state = "paused";
|
|
2479
|
+
statusPayload.lastUpdate = pausedAt;
|
|
2480
|
+
writeStatusPayload();
|
|
2481
|
+
appendJsonl(eventsPath, JSON.stringify({ type: "subagent.run.paused", ts: pausedAt, runId: id }));
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2426
2484
|
let summary = results.map((r) => `${r.agent}:\n${r.output}`).join("\n\n");
|
|
2427
2485
|
let truncated = false;
|
|
2428
2486
|
|