@trailstep/core 0.2.0 → 0.3.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/dist/index.d.ts +32 -1
- package/dist/index.js +589 -96
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -121,7 +121,8 @@ var PLACEHOLDER_NAMES = /* @__PURE__ */ new Set([
|
|
|
121
121
|
"promptFile",
|
|
122
122
|
"outputFile",
|
|
123
123
|
"model",
|
|
124
|
-
"thinking"
|
|
124
|
+
"thinking",
|
|
125
|
+
"sessionId"
|
|
125
126
|
]);
|
|
126
127
|
var CONDITIONAL_NAMES = /* @__PURE__ */ new Set(["model", "thinking"]);
|
|
127
128
|
var TEMPLATE_TOKEN_PATTERN = /^{{([#/]?)([A-Za-z][A-Za-z0-9]*)}}$/;
|
|
@@ -131,7 +132,8 @@ function renderCustomProviderArgs(options) {
|
|
|
131
132
|
...options.values.promptFile === void 0 ? {} : { promptFile: options.values.promptFile },
|
|
132
133
|
...options.values.outputFile === void 0 ? {} : { outputFile: options.values.outputFile },
|
|
133
134
|
...optionalNonEmptyString(options.values.model) === void 0 ? {} : { model: optionalNonEmptyString(options.values.model) },
|
|
134
|
-
...options.values.thinking === void 0 ? {} : { thinking: options.values.thinking }
|
|
135
|
+
...options.values.thinking === void 0 ? {} : { thinking: options.values.thinking },
|
|
136
|
+
...options.values.sessionId === void 0 ? {} : { sessionId: options.values.sessionId }
|
|
135
137
|
};
|
|
136
138
|
return renderRange({
|
|
137
139
|
argv: options.argv,
|
|
@@ -665,10 +667,54 @@ function parseWorking(path, value, diagnostics) {
|
|
|
665
667
|
const args = parseOptionalStringArray(`${path}.args`, value.args, diagnostics);
|
|
666
668
|
const prompt = parsePrompt(`${path}.prompt`, value.prompt, diagnostics);
|
|
667
669
|
const output = parseOutput(`${path}.output`, value.output, diagnostics);
|
|
668
|
-
|
|
670
|
+
const followUp = parseFollowUp(`${path}.followUp`, value.followUp, diagnostics);
|
|
671
|
+
if (command === void 0 || prompt === void 0 || output === void 0 || followUp === null) {
|
|
669
672
|
return void 0;
|
|
670
673
|
}
|
|
671
|
-
return {
|
|
674
|
+
return {
|
|
675
|
+
supported: true,
|
|
676
|
+
command,
|
|
677
|
+
...args === void 0 ? {} : { args },
|
|
678
|
+
prompt,
|
|
679
|
+
output,
|
|
680
|
+
...followUp === void 0 ? {} : { followUp }
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
function parseFollowUp(path, value, diagnostics) {
|
|
684
|
+
if (value === void 0) {
|
|
685
|
+
return void 0;
|
|
686
|
+
}
|
|
687
|
+
if (!isRecord(value) || typeof value.supported !== "boolean") {
|
|
688
|
+
diagnostics.push(`${path} must be an object with a boolean supported field.`);
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
if (!value.supported) {
|
|
692
|
+
return {
|
|
693
|
+
supported: false,
|
|
694
|
+
...typeof value.reason === "string" ? { reason: value.reason } : {}
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
const sessionIdFlag = parseOptionalNonEmptyString(
|
|
698
|
+
`${path}.sessionIdFlag`,
|
|
699
|
+
value.sessionIdFlag,
|
|
700
|
+
diagnostics
|
|
701
|
+
);
|
|
702
|
+
if (sessionIdFlag === null) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
let resumeArgs;
|
|
706
|
+
if (value.resumeArgs !== void 0) {
|
|
707
|
+
if (!Array.isArray(value.resumeArgs) || value.resumeArgs.some((item) => typeof item !== "string")) {
|
|
708
|
+
diagnostics.push(`${path}.resumeArgs must be an array of strings when present.`);
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
resumeArgs = value.resumeArgs;
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
supported: true,
|
|
715
|
+
...sessionIdFlag === void 0 ? {} : { sessionIdFlag },
|
|
716
|
+
...resumeArgs === void 0 ? {} : { resumeArgs }
|
|
717
|
+
};
|
|
672
718
|
}
|
|
673
719
|
function parseLegacyInteractive(path, value, diagnostics) {
|
|
674
720
|
const parsed = parseSupportedObject(path, value, diagnostics);
|
|
@@ -2892,6 +2938,7 @@ function isPlainObject4(value) {
|
|
|
2892
2938
|
|
|
2893
2939
|
// src/runtime/sub-prompts/run-sub-prompt.ts
|
|
2894
2940
|
import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
|
|
2941
|
+
import { join as join10 } from "path";
|
|
2895
2942
|
|
|
2896
2943
|
// src/agent-execution/adapter-agent/run-adapter-agent-step/run-adapter-agent-step.ts
|
|
2897
2944
|
var PROVIDER_NEUTRAL_ADAPTER_KEY = "custom";
|
|
@@ -2961,7 +3008,7 @@ function createSubmitOutputTool(schema, onOutput) {
|
|
|
2961
3008
|
}
|
|
2962
3009
|
|
|
2963
3010
|
// src/agent-execution/dispatch-agent-step/dispatch-agent-step.ts
|
|
2964
|
-
import { readFile as
|
|
3011
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
2965
3012
|
import { resolve } from "path";
|
|
2966
3013
|
|
|
2967
3014
|
// src/runtime/artifacts/step-artifacts.ts
|
|
@@ -3375,8 +3422,9 @@ var spawnInteractiveProcess2 = async ({
|
|
|
3375
3422
|
env,
|
|
3376
3423
|
signal
|
|
3377
3424
|
}) => {
|
|
3425
|
+
const executable = await resolveCliCommandForSpawn({ command, args, env });
|
|
3378
3426
|
return await new Promise((resolve2, reject) => {
|
|
3379
|
-
const child = spawn2(command, args, {
|
|
3427
|
+
const child = spawn2(executable.command, executable.args, {
|
|
3380
3428
|
cwd,
|
|
3381
3429
|
env,
|
|
3382
3430
|
signal,
|
|
@@ -3420,7 +3468,9 @@ function resolveStepAgentFiles(options) {
|
|
|
3420
3468
|
stepDir: artifactPaths.stepDir,
|
|
3421
3469
|
promptFile: join8(artifactPaths.stepDir, "prompt.md"),
|
|
3422
3470
|
outputFile: artifactPaths.outputFile,
|
|
3423
|
-
usageFile: artifactPaths.usageFile
|
|
3471
|
+
usageFile: artifactPaths.usageFile,
|
|
3472
|
+
workFile: join8(artifactPaths.stepDir, "work.txt"),
|
|
3473
|
+
repairPromptFile: join8(artifactPaths.stepDir, "repair-prompt.md")
|
|
3424
3474
|
};
|
|
3425
3475
|
}
|
|
3426
3476
|
|
|
@@ -3597,9 +3647,13 @@ async function runCustomWorkingProvider(options) {
|
|
|
3597
3647
|
});
|
|
3598
3648
|
let result;
|
|
3599
3649
|
try {
|
|
3600
|
-
|
|
3650
|
+
const executable = await resolveCliCommandForSpawn({
|
|
3601
3651
|
command: agentConfig.binary,
|
|
3602
|
-
args
|
|
3652
|
+
args
|
|
3653
|
+
});
|
|
3654
|
+
result = await (options.runner ?? spawnWorkingAgentProcess)({
|
|
3655
|
+
command: executable.command,
|
|
3656
|
+
args: executable.args,
|
|
3603
3657
|
cwd: options.cwd,
|
|
3604
3658
|
shell: false,
|
|
3605
3659
|
stdio: "inherit",
|
|
@@ -3639,7 +3693,8 @@ async function runCustomWorkingProvider(options) {
|
|
|
3639
3693
|
}
|
|
3640
3694
|
|
|
3641
3695
|
// src/agent-execution/working-agent/targets/manifest-provider/run-manifest-working-provider.ts
|
|
3642
|
-
import {
|
|
3696
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3697
|
+
import { readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
|
|
3643
3698
|
|
|
3644
3699
|
// src/cli-provider-runtime/envelopes/envelope.ts
|
|
3645
3700
|
function extractEnvelopeOutput(rawStdout, options) {
|
|
@@ -3788,6 +3843,50 @@ function isPlainObject5(value) {
|
|
|
3788
3843
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3789
3844
|
}
|
|
3790
3845
|
|
|
3846
|
+
// src/agent-execution/working-agent/prompts/build-two-phase-prompts.ts
|
|
3847
|
+
function buildWorkPrompt(options) {
|
|
3848
|
+
return [
|
|
3849
|
+
"# TrailStep working-agent task",
|
|
3850
|
+
"",
|
|
3851
|
+
"Do the task described below.",
|
|
3852
|
+
"",
|
|
3853
|
+
"When you are done, report what you did as your final answer in natural prose or markdown:",
|
|
3854
|
+
"what you changed or found, the key facts a formatter would need (status intent,",
|
|
3855
|
+
"summary, files changed, results), and any question you need answered if blocked.",
|
|
3856
|
+
"",
|
|
3857
|
+
"## Original prompt",
|
|
3858
|
+
"",
|
|
3859
|
+
options.prompt,
|
|
3860
|
+
""
|
|
3861
|
+
].join("\n");
|
|
3862
|
+
}
|
|
3863
|
+
function buildFormatPrompt(options) {
|
|
3864
|
+
const lines = [
|
|
3865
|
+
"# TrailStep format follow-up",
|
|
3866
|
+
"",
|
|
3867
|
+
"Reformat your last answer in THIS session to exactly one JSON object as your entire final answer, and nothing else.",
|
|
3868
|
+
"Do not redo the work. Do not include prose, markdown fences, or multiple JSON values - only the JSON object itself.",
|
|
3869
|
+
"",
|
|
3870
|
+
"The JSON object must match this output schema:",
|
|
3871
|
+
"",
|
|
3872
|
+
"```json",
|
|
3873
|
+
JSON.stringify(options.outputSchema, null, 2),
|
|
3874
|
+
"```"
|
|
3875
|
+
];
|
|
3876
|
+
if (options.validationErrors !== void 0 && options.validationErrors.length > 0) {
|
|
3877
|
+
lines.push(
|
|
3878
|
+
"",
|
|
3879
|
+
"Your previous reformatted answer failed validation with these errors:",
|
|
3880
|
+
"",
|
|
3881
|
+
...options.validationErrors.map((error) => `- ${error}`),
|
|
3882
|
+
"",
|
|
3883
|
+
"Fix only the formatting to satisfy the schema."
|
|
3884
|
+
);
|
|
3885
|
+
}
|
|
3886
|
+
lines.push("");
|
|
3887
|
+
return lines.join("\n");
|
|
3888
|
+
}
|
|
3889
|
+
|
|
3791
3890
|
// src/agent-execution/working-agent/targets/manifest-provider/run-manifest-working-provider.ts
|
|
3792
3891
|
async function runManifestWorkingProvider(options) {
|
|
3793
3892
|
const registration = options.config.providers?.[options.target.provider];
|
|
@@ -3802,6 +3901,18 @@ async function runManifestWorkingProvider(options) {
|
|
|
3802
3901
|
}
|
|
3803
3902
|
const thinking = options.target.thinking ?? options.role.thinking;
|
|
3804
3903
|
const model = optionalNonEmptyString3(options.target.model);
|
|
3904
|
+
const followUp = working.followUp;
|
|
3905
|
+
if (followUp?.supported === true) {
|
|
3906
|
+
return runTwoPhaseManifestWorking({
|
|
3907
|
+
options,
|
|
3908
|
+
manifest,
|
|
3909
|
+
working,
|
|
3910
|
+
followUp,
|
|
3911
|
+
extractOutputHook: registration?.manifest.hooks?.extractOutput,
|
|
3912
|
+
thinking,
|
|
3913
|
+
model
|
|
3914
|
+
});
|
|
3915
|
+
}
|
|
3805
3916
|
const args = renderCustomProviderArgs({
|
|
3806
3917
|
argv: working.args ?? defaultManifestWorkingArgs({
|
|
3807
3918
|
manifest,
|
|
@@ -3822,9 +3933,10 @@ async function runManifestWorkingProvider(options) {
|
|
|
3822
3933
|
const captureStdout = working.output?.style !== "provider-output-file";
|
|
3823
3934
|
let result;
|
|
3824
3935
|
try {
|
|
3936
|
+
const executable = await resolveCliCommandForSpawn({ command: working.command, args });
|
|
3825
3937
|
result = await (options.runner ?? spawnWorkingAgentProcess)({
|
|
3826
|
-
command:
|
|
3827
|
-
args,
|
|
3938
|
+
command: executable.command,
|
|
3939
|
+
args: executable.args,
|
|
3828
3940
|
cwd: options.cwd,
|
|
3829
3941
|
shell: false,
|
|
3830
3942
|
stdio: captureStdout ? "pipe" : "inherit",
|
|
@@ -3871,6 +3983,219 @@ async function runManifestWorkingProvider(options) {
|
|
|
3871
3983
|
step: options.step
|
|
3872
3984
|
});
|
|
3873
3985
|
}
|
|
3986
|
+
async function runTwoPhaseManifestWorking(context) {
|
|
3987
|
+
const { options, working, followUp } = context;
|
|
3988
|
+
const { files, step: step2, target } = options;
|
|
3989
|
+
const sessionId = randomUUID2();
|
|
3990
|
+
const captureStdout = working.output?.style !== "provider-output-file";
|
|
3991
|
+
const resumeArgs = followUp.resumeArgs;
|
|
3992
|
+
if (resumeArgs === void 0) {
|
|
3993
|
+
throw new TrailStepFailureError({
|
|
3994
|
+
code: "agent_provider_invalid",
|
|
3995
|
+
message: `Working agent step ${step2.id} target '${target.provider}' declares followUp.supported without followUp.resumeArgs for the format phase.`,
|
|
3996
|
+
details: { provider: target.provider }
|
|
3997
|
+
});
|
|
3998
|
+
}
|
|
3999
|
+
await writeFile5(files.promptFile, buildWorkPrompt({ prompt: options.renderedPrompt }), "utf8");
|
|
4000
|
+
const phase1Template = working.args ?? defaultManifestWorkingArgs({
|
|
4001
|
+
manifest: context.manifest,
|
|
4002
|
+
promptFile: files.promptFile,
|
|
4003
|
+
outputFile: files.workFile,
|
|
4004
|
+
model: context.model,
|
|
4005
|
+
thinking: context.thinking
|
|
4006
|
+
});
|
|
4007
|
+
const phase1Args = renderCustomProviderArgs({
|
|
4008
|
+
argv: phase1Template,
|
|
4009
|
+
values: {
|
|
4010
|
+
promptFile: files.promptFile,
|
|
4011
|
+
outputFile: files.workFile,
|
|
4012
|
+
sessionId,
|
|
4013
|
+
...context.model === void 0 ? {} : { model: context.model },
|
|
4014
|
+
...context.thinking === void 0 ? {} : { thinking: context.thinking }
|
|
4015
|
+
},
|
|
4016
|
+
errorCode: "agent_provider_invalid",
|
|
4017
|
+
commandDescription: "Manifest working provider command"
|
|
4018
|
+
});
|
|
4019
|
+
if (followUp.sessionIdFlag !== void 0 && !phase1Template.some((arg) => arg.includes("{{sessionId}}"))) {
|
|
4020
|
+
phase1Args.push(followUp.sessionIdFlag, sessionId);
|
|
4021
|
+
}
|
|
4022
|
+
const phase1Result = await spawnManifestPhase({
|
|
4023
|
+
phase: "work",
|
|
4024
|
+
stepId: step2.id,
|
|
4025
|
+
provider: target.provider,
|
|
4026
|
+
command: working.command ?? "",
|
|
4027
|
+
args: phase1Args,
|
|
4028
|
+
cwd: options.cwd,
|
|
4029
|
+
model: context.model,
|
|
4030
|
+
promptFile: files.promptFile,
|
|
4031
|
+
outputFile: files.workFile,
|
|
4032
|
+
stdio: captureStdout ? "pipe" : "inherit",
|
|
4033
|
+
runner: options.runner,
|
|
4034
|
+
signal: options.signal
|
|
4035
|
+
});
|
|
4036
|
+
if (phase1Result.exitCode !== 0) {
|
|
4037
|
+
throw new TrailStepFailureError({
|
|
4038
|
+
code: "agent_provider_failed",
|
|
4039
|
+
message: `Working agent step ${step2.id} target '${target.provider}' exited with code ${phase1Result.exitCode}.`,
|
|
4040
|
+
details: {
|
|
4041
|
+
phase: "work",
|
|
4042
|
+
exitCode: phase1Result.exitCode,
|
|
4043
|
+
provider: working.command,
|
|
4044
|
+
target: target.provider,
|
|
4045
|
+
...context.model === void 0 ? {} : { model: context.model }
|
|
4046
|
+
}
|
|
4047
|
+
});
|
|
4048
|
+
}
|
|
4049
|
+
if (captureStdout) {
|
|
4050
|
+
await writeFile5(files.workFile, phase1Result.stdout ?? "", "utf8");
|
|
4051
|
+
}
|
|
4052
|
+
const phase1Tail = await readPhase1Tail(files.workFile);
|
|
4053
|
+
await writePhaseUsageFile({
|
|
4054
|
+
usageFile: files.usageFile,
|
|
4055
|
+
sessionId,
|
|
4056
|
+
provider: target.provider,
|
|
4057
|
+
model: context.model
|
|
4058
|
+
});
|
|
4059
|
+
const phase2Args = renderCustomProviderArgs({
|
|
4060
|
+
argv: resumeArgs,
|
|
4061
|
+
values: {
|
|
4062
|
+
sessionId,
|
|
4063
|
+
promptFile: files.repairPromptFile,
|
|
4064
|
+
outputFile: files.outputFile,
|
|
4065
|
+
...context.model === void 0 ? {} : { model: context.model },
|
|
4066
|
+
...context.thinking === void 0 ? {} : { thinking: context.thinking }
|
|
4067
|
+
},
|
|
4068
|
+
errorCode: "agent_provider_invalid",
|
|
4069
|
+
commandDescription: "Manifest follow-up resume command"
|
|
4070
|
+
});
|
|
4071
|
+
const validationDiagnostics = [];
|
|
4072
|
+
let formatFailureMessage = "";
|
|
4073
|
+
for (let attempt = 1; attempt <= MAX_FORMAT_ATTEMPTS; attempt += 1) {
|
|
4074
|
+
await writeFile5(
|
|
4075
|
+
files.repairPromptFile,
|
|
4076
|
+
buildFormatPrompt({
|
|
4077
|
+
outputSchema: step2.output.jsonSchema,
|
|
4078
|
+
...attempt === 1 ? {} : { validationErrors: [formatFailureMessage] }
|
|
4079
|
+
}),
|
|
4080
|
+
"utf8"
|
|
4081
|
+
);
|
|
4082
|
+
const phase2Result = await spawnManifestPhase({
|
|
4083
|
+
phase: "format",
|
|
4084
|
+
stepId: step2.id,
|
|
4085
|
+
provider: target.provider,
|
|
4086
|
+
command: working.command ?? "",
|
|
4087
|
+
args: phase2Args,
|
|
4088
|
+
cwd: options.cwd,
|
|
4089
|
+
model: context.model,
|
|
4090
|
+
promptFile: files.repairPromptFile,
|
|
4091
|
+
outputFile: files.outputFile,
|
|
4092
|
+
stdio: captureStdout ? "pipe" : "inherit",
|
|
4093
|
+
runner: options.runner,
|
|
4094
|
+
signal: options.signal
|
|
4095
|
+
});
|
|
4096
|
+
if (phase2Result.exitCode !== 0) {
|
|
4097
|
+
throw new TrailStepFailureError({
|
|
4098
|
+
code: "agent_provider_failed",
|
|
4099
|
+
message: `Working agent step ${step2.id} target '${target.provider}' exited with code ${phase2Result.exitCode}.`,
|
|
4100
|
+
details: {
|
|
4101
|
+
phase: "format",
|
|
4102
|
+
attempt,
|
|
4103
|
+
sessionId,
|
|
4104
|
+
exitCode: phase2Result.exitCode,
|
|
4105
|
+
provider: working.command,
|
|
4106
|
+
target: target.provider,
|
|
4107
|
+
...context.model === void 0 ? {} : { model: context.model }
|
|
4108
|
+
}
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
4111
|
+
try {
|
|
4112
|
+
await writeCapturedStdoutOutput({
|
|
4113
|
+
provider: target.provider,
|
|
4114
|
+
stepId: step2.id,
|
|
4115
|
+
outputFile: files.outputFile,
|
|
4116
|
+
captureMode: step2.output.captureMode,
|
|
4117
|
+
stdout: phase2Result.stdout,
|
|
4118
|
+
output: working.output,
|
|
4119
|
+
extractOutputHook: context.extractOutputHook
|
|
4120
|
+
});
|
|
4121
|
+
return await readWorkingAgentOutput({ stepId: step2.id, outputFile: files.outputFile, step: step2 });
|
|
4122
|
+
} catch (error) {
|
|
4123
|
+
formatFailureMessage = error instanceof Error ? error.message : String(error);
|
|
4124
|
+
validationDiagnostics.push(`format attempt ${attempt}: ${formatFailureMessage}`);
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
throw new TrailStepFailureError({
|
|
4128
|
+
code: "agent_target_failed",
|
|
4129
|
+
message: `Working agent step ${step2.id} target '${target.provider}' failed output validation after ${MAX_FORMAT_ATTEMPTS} format attempts in session ${sessionId}.`,
|
|
4130
|
+
details: {
|
|
4131
|
+
provider: target.provider,
|
|
4132
|
+
sessionId,
|
|
4133
|
+
attempts: MAX_FORMAT_ATTEMPTS,
|
|
4134
|
+
validationDiagnostics,
|
|
4135
|
+
phase1Tail
|
|
4136
|
+
}
|
|
4137
|
+
});
|
|
4138
|
+
}
|
|
4139
|
+
var MAX_FORMAT_ATTEMPTS = 3;
|
|
4140
|
+
var PHASE_1_TAIL_LIMIT = 2e3;
|
|
4141
|
+
async function spawnManifestPhase(options) {
|
|
4142
|
+
const executable = await resolveCliCommandForSpawn({
|
|
4143
|
+
command: options.command,
|
|
4144
|
+
args: options.args
|
|
4145
|
+
});
|
|
4146
|
+
try {
|
|
4147
|
+
return await (options.runner ?? spawnWorkingAgentProcess)({
|
|
4148
|
+
command: executable.command,
|
|
4149
|
+
args: executable.args,
|
|
4150
|
+
cwd: options.cwd,
|
|
4151
|
+
shell: false,
|
|
4152
|
+
stdio: options.stdio,
|
|
4153
|
+
promptFile: options.promptFile,
|
|
4154
|
+
outputFile: options.outputFile,
|
|
4155
|
+
...options.model === void 0 ? {} : { model: options.model },
|
|
4156
|
+
signal: options.signal
|
|
4157
|
+
});
|
|
4158
|
+
} catch (error) {
|
|
4159
|
+
throw new TrailStepFailureError({
|
|
4160
|
+
code: "agent_provider_spawn_error",
|
|
4161
|
+
message: `Working agent step ${options.stepId} could not start target '${options.provider}'.`,
|
|
4162
|
+
details: {
|
|
4163
|
+
phase: options.phase,
|
|
4164
|
+
target: options.provider,
|
|
4165
|
+
...options.model === void 0 ? {} : { model: options.model },
|
|
4166
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
4167
|
+
}
|
|
4168
|
+
});
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
async function readPhase1Tail(workFile) {
|
|
4172
|
+
try {
|
|
4173
|
+
const workText = await readFile8(workFile, "utf8");
|
|
4174
|
+
return workText.length > PHASE_1_TAIL_LIMIT ? workText.slice(-PHASE_1_TAIL_LIMIT) : workText;
|
|
4175
|
+
} catch {
|
|
4176
|
+
return "";
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
async function writePhaseUsageFile(options) {
|
|
4180
|
+
try {
|
|
4181
|
+
await writeFile5(
|
|
4182
|
+
options.usageFile,
|
|
4183
|
+
`${JSON.stringify(
|
|
4184
|
+
{
|
|
4185
|
+
sessionId: options.sessionId,
|
|
4186
|
+
provider: options.provider,
|
|
4187
|
+
...options.model === void 0 ? {} : { model: options.model },
|
|
4188
|
+
phases: 2
|
|
4189
|
+
},
|
|
4190
|
+
null,
|
|
4191
|
+
2
|
|
4192
|
+
)}
|
|
4193
|
+
`,
|
|
4194
|
+
"utf8"
|
|
4195
|
+
);
|
|
4196
|
+
} catch {
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
3874
4199
|
async function writeCapturedStdoutOutput(options) {
|
|
3875
4200
|
if (options.stdout === void 0) {
|
|
3876
4201
|
return;
|
|
@@ -3984,6 +4309,7 @@ async function runWorkingAgentTargetAttempt(options) {
|
|
|
3984
4309
|
runner: options.runner,
|
|
3985
4310
|
target: options.target,
|
|
3986
4311
|
files: options.files,
|
|
4312
|
+
renderedPrompt: options.renderedPrompt,
|
|
3987
4313
|
signal: options.signal
|
|
3988
4314
|
});
|
|
3989
4315
|
}
|
|
@@ -4178,7 +4504,7 @@ async function dispatchAgentStep(options) {
|
|
|
4178
4504
|
}
|
|
4179
4505
|
async function resolvePromptSource(source, input, cwd) {
|
|
4180
4506
|
if (isPromptTemplateSource(source)) {
|
|
4181
|
-
return await
|
|
4507
|
+
return await readFile9(resolve(cwd, source.path), "utf8");
|
|
4182
4508
|
}
|
|
4183
4509
|
return renderAgentPrompt(source, input);
|
|
4184
4510
|
}
|
|
@@ -4474,7 +4800,9 @@ async function runSubPrompt(source, options, input) {
|
|
|
4474
4800
|
stepDir: artifactPaths.subPromptDir,
|
|
4475
4801
|
promptFile: artifactPaths.promptFile,
|
|
4476
4802
|
outputFile: artifactPaths.outputFile,
|
|
4477
|
-
usageFile: artifactPaths.usageFile
|
|
4803
|
+
usageFile: artifactPaths.usageFile,
|
|
4804
|
+
workFile: join10(artifactPaths.subPromptDir, "work.txt"),
|
|
4805
|
+
repairPromptFile: join10(artifactPaths.subPromptDir, "repair-prompt.md")
|
|
4478
4806
|
}
|
|
4479
4807
|
}) : await runAgentStep({
|
|
4480
4808
|
step: agentStep,
|
|
@@ -4616,8 +4944,8 @@ function toDeprecationStatus(entry, version) {
|
|
|
4616
4944
|
}
|
|
4617
4945
|
|
|
4618
4946
|
// src/runtime/cancellation/cancellation.ts
|
|
4619
|
-
import { mkdir as mkdir7, readFile as
|
|
4620
|
-
import { join as
|
|
4947
|
+
import { mkdir as mkdir7, readFile as readFile10, writeFile as writeFile8 } from "fs/promises";
|
|
4948
|
+
import { join as join11 } from "path";
|
|
4621
4949
|
var CANCELLATION_MARKER_FILE = "cancel.json";
|
|
4622
4950
|
var WorkflowCancellationError = class extends Error {
|
|
4623
4951
|
cancellation;
|
|
@@ -4628,12 +4956,12 @@ var WorkflowCancellationError = class extends Error {
|
|
|
4628
4956
|
}
|
|
4629
4957
|
};
|
|
4630
4958
|
function cancellationMarkerPath(runDir) {
|
|
4631
|
-
return
|
|
4959
|
+
return join11(runDir, CANCELLATION_MARKER_FILE);
|
|
4632
4960
|
}
|
|
4633
4961
|
async function readCancellationMarker(runDir) {
|
|
4634
4962
|
let contents;
|
|
4635
4963
|
try {
|
|
4636
|
-
contents = await
|
|
4964
|
+
contents = await readFile10(cancellationMarkerPath(runDir), "utf8");
|
|
4637
4965
|
} catch (error) {
|
|
4638
4966
|
if (isNodeError6(error) && error.code === "ENOENT") {
|
|
4639
4967
|
return void 0;
|
|
@@ -4716,12 +5044,12 @@ function firstConfiguredPolicy(...policies) {
|
|
|
4716
5044
|
}
|
|
4717
5045
|
|
|
4718
5046
|
// src/runtime/run-workflow/run-workflow.ts
|
|
4719
|
-
import { readFile as
|
|
4720
|
-
import { basename as basename4, join as
|
|
5047
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
5048
|
+
import { basename as basename4, join as join15 } from "path";
|
|
4721
5049
|
|
|
4722
5050
|
// src/runtime/continuation/run-continuation/run-continuation.ts
|
|
4723
|
-
import { mkdir as mkdir8, readFile as
|
|
4724
|
-
import { join as
|
|
5051
|
+
import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
5052
|
+
import { join as join12 } from "path";
|
|
4725
5053
|
|
|
4726
5054
|
// src/runtime/failures/step-execution-failure.ts
|
|
4727
5055
|
function stepExecutionFailure(error) {
|
|
@@ -5740,7 +6068,7 @@ async function handleResumedWaitPhase(options) {
|
|
|
5740
6068
|
}
|
|
5741
6069
|
}
|
|
5742
6070
|
function joinRunPath(runDir, runRelativePath) {
|
|
5743
|
-
return
|
|
6071
|
+
return join12(runDir, ...runRelativePath.split("/"));
|
|
5744
6072
|
}
|
|
5745
6073
|
async function resolveWaitDefinition(phase, context) {
|
|
5746
6074
|
if (typeof phase.wait !== "function") {
|
|
@@ -5818,7 +6146,7 @@ function validateWaitDefinition(wait, stepId, phaseIndex) {
|
|
|
5818
6146
|
}
|
|
5819
6147
|
async function readTextIfExists(path) {
|
|
5820
6148
|
try {
|
|
5821
|
-
return { status: "found", value: await
|
|
6149
|
+
return { status: "found", value: await readFile11(path, "utf8") };
|
|
5822
6150
|
} catch (error) {
|
|
5823
6151
|
if (isNodeError7(error) && error.code === "ENOENT") {
|
|
5824
6152
|
return { status: "missing" };
|
|
@@ -6570,8 +6898,8 @@ function resumeFailure2(code, message) {
|
|
|
6570
6898
|
}
|
|
6571
6899
|
|
|
6572
6900
|
// src/runtime/resume/replay-to-waiting-step/replay-to-waiting-step.ts
|
|
6573
|
-
import { readFile as
|
|
6574
|
-
import { join as
|
|
6901
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
6902
|
+
import { join as join13 } from "path";
|
|
6575
6903
|
async function replayToWaitingStep(options) {
|
|
6576
6904
|
const startedEvent = options.events.find((event) => event.type === "workflow.started");
|
|
6577
6905
|
if (!startedEvent) {
|
|
@@ -6601,7 +6929,21 @@ async function replayToWaitingStep(options) {
|
|
|
6601
6929
|
)
|
|
6602
6930
|
};
|
|
6603
6931
|
}
|
|
6604
|
-
const
|
|
6932
|
+
const scopeBranchId = options.branchId;
|
|
6933
|
+
if (scopeBranchId !== void 0) {
|
|
6934
|
+
try {
|
|
6935
|
+
await readBranchRunState(options.runDir, scopeBranchId);
|
|
6936
|
+
} catch (error) {
|
|
6937
|
+
return {
|
|
6938
|
+
status: "failure",
|
|
6939
|
+
failure: resumeFailure3(
|
|
6940
|
+
"continue_target_not_found",
|
|
6941
|
+
`Branch ${scopeBranchId} run state is missing or unreadable: ${error instanceof Error ? error.message : String(error)}`
|
|
6942
|
+
)
|
|
6943
|
+
};
|
|
6944
|
+
}
|
|
6945
|
+
}
|
|
6946
|
+
const pendingWait = findLatestPendingWait(options.events, scopeBranchId);
|
|
6605
6947
|
if (!pendingWait?.stepId) {
|
|
6606
6948
|
return {
|
|
6607
6949
|
status: "failure",
|
|
@@ -6625,7 +6967,7 @@ async function replayToWaitingStep(options) {
|
|
|
6625
6967
|
failure: resumeFailure3("continue_wait_invalid", "Pending wait event has no wait id.")
|
|
6626
6968
|
};
|
|
6627
6969
|
}
|
|
6628
|
-
const stepIndex = readStepIndex(pendingWait) ?? stepStartedOrdinalBefore(options.events, pendingWait);
|
|
6970
|
+
const stepIndex = options.stepIndex ?? readStepIndex(pendingWait) ?? stepStartedOrdinalBefore(options.events, pendingWait);
|
|
6629
6971
|
if (stepIndex === void 0) {
|
|
6630
6972
|
return {
|
|
6631
6973
|
status: "failure",
|
|
@@ -6645,9 +6987,10 @@ async function replayToWaitingStep(options) {
|
|
|
6645
6987
|
)
|
|
6646
6988
|
};
|
|
6647
6989
|
}
|
|
6990
|
+
const replaySourceEvents = options.events.slice(0, targetStepStartedIndex);
|
|
6648
6991
|
const replay = await replayCompletedSteps({
|
|
6649
6992
|
workflow: options.workflow,
|
|
6650
|
-
events:
|
|
6993
|
+
events: scopeBranchId === void 0 ? replaySourceEvents : branchScopedReplayEvents(replaySourceEvents, scopeBranchId),
|
|
6651
6994
|
input,
|
|
6652
6995
|
targetStepId: pendingWait.stepId,
|
|
6653
6996
|
runDir: options.runDir
|
|
@@ -6662,7 +7005,8 @@ async function replayToWaitingStep(options) {
|
|
|
6662
7005
|
node: replay.node,
|
|
6663
7006
|
events: options.events,
|
|
6664
7007
|
targetStepStartedIndex,
|
|
6665
|
-
pendingWait
|
|
7008
|
+
pendingWait,
|
|
7009
|
+
branchId: scopeBranchId
|
|
6666
7010
|
})
|
|
6667
7011
|
);
|
|
6668
7012
|
if (prefix.status === "failure") {
|
|
@@ -6683,6 +7027,7 @@ async function replayToWaitingStep(options) {
|
|
|
6683
7027
|
phaseValue: prefix.phaseValue,
|
|
6684
7028
|
waitOutputs: prefix.waitOutputs,
|
|
6685
7029
|
seenWaitIds: prefix.seenWaitIds,
|
|
7030
|
+
...scopeBranchId === void 0 ? {} : { branchId: scopeBranchId },
|
|
6686
7031
|
wait: wait.wait
|
|
6687
7032
|
};
|
|
6688
7033
|
}
|
|
@@ -6700,7 +7045,7 @@ async function readResumedWaitDetails(runDir, waitEvent, waitId) {
|
|
|
6700
7045
|
let request;
|
|
6701
7046
|
try {
|
|
6702
7047
|
request = JSON.parse(
|
|
6703
|
-
await
|
|
7048
|
+
await readFile12(join13(runDir, ...artifactPaths.requestFile.split("/")), "utf8")
|
|
6704
7049
|
);
|
|
6705
7050
|
} catch {
|
|
6706
7051
|
return {
|
|
@@ -6733,12 +7078,15 @@ async function readResumedWaitDetails(runDir, waitEvent, waitId) {
|
|
|
6733
7078
|
}
|
|
6734
7079
|
};
|
|
6735
7080
|
}
|
|
6736
|
-
function findLatestPendingWait(events) {
|
|
7081
|
+
function findLatestPendingWait(events, branchId) {
|
|
6737
7082
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
6738
7083
|
const event = events[index];
|
|
6739
7084
|
if (event?.type !== "wait.started") {
|
|
6740
7085
|
continue;
|
|
6741
7086
|
}
|
|
7087
|
+
if (branchId !== void 0 && event.payload.branchId !== branchId) {
|
|
7088
|
+
continue;
|
|
7089
|
+
}
|
|
6742
7090
|
const key = waitEventKey(event);
|
|
6743
7091
|
if (!key) {
|
|
6744
7092
|
continue;
|
|
@@ -6757,11 +7105,12 @@ async function replayWaitingStepPrefix(options) {
|
|
|
6757
7105
|
const targetPhaseIndex = typeof options.pendingWait.payload.phaseIndex === "number" ? options.pendingWait.payload.phaseIndex : void 0;
|
|
6758
7106
|
const pendingWaitIndex = options.events.indexOf(options.pendingWait);
|
|
6759
7107
|
const inStepEvents = options.events.slice(options.targetStepStartedIndex + 1, pendingWaitIndex);
|
|
7108
|
+
const belongsToScopedBranch = (event) => options.branchId === void 0 || event.payload.branchId === void 0 || event.payload.branchId === options.branchId;
|
|
6760
7109
|
const promptOutputs = inStepEvents.filter(
|
|
6761
|
-
(event) => event.type === "step.completed" && event.stepId === options.node.config.id && isPlainObject14(event.payload.output)
|
|
7110
|
+
(event) => event.type === "step.completed" && event.stepId === options.node.config.id && belongsToScopedBranch(event) && isPlainObject14(event.payload.output)
|
|
6762
7111
|
);
|
|
6763
7112
|
const satisfiedWaits = inStepEvents.filter(
|
|
6764
|
-
(event) => event.type === "wait.satisfied" && event.stepId === options.node.config.id && isPlainObject14(event.payload.output) && typeof event.payload.waitId === "string"
|
|
7113
|
+
(event) => event.type === "wait.satisfied" && event.stepId === options.node.config.id && belongsToScopedBranch(event) && isPlainObject14(event.payload.output) && typeof event.payload.waitId === "string"
|
|
6765
7114
|
);
|
|
6766
7115
|
let phaseValue = options.node.config.input;
|
|
6767
7116
|
let promptOutputIndex = 0;
|
|
@@ -6909,7 +7258,7 @@ function readStepIndex(event) {
|
|
|
6909
7258
|
}
|
|
6910
7259
|
function stepDirForIndex(runDir, event, stepIndex) {
|
|
6911
7260
|
const artifactStepId = readArtifactStepId(event) ?? `${String(stepIndex).padStart(4, "0")}-${event.stepId ?? "step"}`;
|
|
6912
|
-
return
|
|
7261
|
+
return join13(runDir, "steps", artifactStepId);
|
|
6913
7262
|
}
|
|
6914
7263
|
function readArtifactStepId(event) {
|
|
6915
7264
|
const artifactPaths = readWaitArtifactPaths(event);
|
|
@@ -6928,6 +7277,22 @@ function readWaitArtifactPaths(event) {
|
|
|
6928
7277
|
}
|
|
6929
7278
|
return { requestFile, answerFile };
|
|
6930
7279
|
}
|
|
7280
|
+
function branchScopedReplayEvents(events, branchId) {
|
|
7281
|
+
let foreignOrdinal = 0;
|
|
7282
|
+
const scoped = [];
|
|
7283
|
+
for (const event of events) {
|
|
7284
|
+
const eventBranchId = typeof event.payload.branchId === "string" ? event.payload.branchId : void 0;
|
|
7285
|
+
if (eventBranchId === void 0 || eventBranchId === branchId) {
|
|
7286
|
+
scoped.push(event);
|
|
7287
|
+
continue;
|
|
7288
|
+
}
|
|
7289
|
+
if (event.type === "step.started") {
|
|
7290
|
+
foreignOrdinal += 1;
|
|
7291
|
+
scoped.push({ ...event, stepId: `__foreign_branch_${foreignOrdinal}__` });
|
|
7292
|
+
}
|
|
7293
|
+
}
|
|
7294
|
+
return scoped;
|
|
7295
|
+
}
|
|
6931
7296
|
function waitEventKey(event) {
|
|
6932
7297
|
const artifactPaths = event.payload.artifactPaths;
|
|
6933
7298
|
if (isPlainObject14(artifactPaths) && typeof artifactPaths.answerFile === "string") {
|
|
@@ -7251,9 +7616,9 @@ async function initializeRun(options) {
|
|
|
7251
7616
|
}
|
|
7252
7617
|
|
|
7253
7618
|
// src/runtime/run-workflow/root-continuation-array-scheduler.ts
|
|
7254
|
-
import { mkdir as mkdir9, readFile as
|
|
7619
|
+
import { mkdir as mkdir9, readFile as readFile13, writeFile as writeFile10 } from "fs/promises";
|
|
7255
7620
|
import { availableParallelism } from "os";
|
|
7256
|
-
import { join as
|
|
7621
|
+
import { join as join14 } from "path";
|
|
7257
7622
|
async function runRootContinuationArrayScheduler(options) {
|
|
7258
7623
|
const resolvedWorkers = resolveSchedulerWorkerCount(options.workers);
|
|
7259
7624
|
if (!resolvedWorkers.ok) {
|
|
@@ -7499,7 +7864,7 @@ async function runRootContinuationArrayScheduler(options) {
|
|
|
7499
7864
|
});
|
|
7500
7865
|
await mkdir9(stepArtifacts.stepDir, { recursive: true });
|
|
7501
7866
|
}
|
|
7502
|
-
await options.emit(decorateBranchEvent(event, options.runId, branch));
|
|
7867
|
+
await options.emit(decorateBranchEvent(event, options.runId, branch, splitOccurred));
|
|
7503
7868
|
},
|
|
7504
7869
|
maxSteps: options.maxSteps,
|
|
7505
7870
|
initialSource: !splitOccurred && branch.branchId === rootBranchId ? source : `${source} branch ${branch.branchId}`,
|
|
@@ -7513,7 +7878,8 @@ async function runRootContinuationArrayScheduler(options) {
|
|
|
7513
7878
|
trailstepConfig: options.trailstepConfig,
|
|
7514
7879
|
workingAgentProcessRunner: options.workingAgentProcessRunner,
|
|
7515
7880
|
providerWorkingRunner: options.providerWorkingRunner,
|
|
7516
|
-
processRunner: options.processRunner
|
|
7881
|
+
processRunner: options.processRunner,
|
|
7882
|
+
...options.waitResumeBranches?.[branch.branchId] === void 0 ? {} : { resumeWait: options.waitResumeBranches[branch.branchId] }
|
|
7517
7883
|
})
|
|
7518
7884
|
);
|
|
7519
7885
|
branch.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -7640,44 +8006,44 @@ async function runRootContinuationArrayScheduler(options) {
|
|
|
7640
8006
|
return branchResultWasSplit ? { status: "success", output: {} } : branchResult;
|
|
7641
8007
|
}
|
|
7642
8008
|
async function enqueueSatisfiedJoins(branch) {
|
|
7643
|
-
for (const
|
|
7644
|
-
if (!
|
|
8009
|
+
for (const join16 of pendingJoins) {
|
|
8010
|
+
if (!join16.childBranchIds.includes(branch.branchId)) {
|
|
7645
8011
|
continue;
|
|
7646
8012
|
}
|
|
7647
|
-
const settlement = branchSettlementForJoin(branch,
|
|
8013
|
+
const settlement = branchSettlementForJoin(branch, join16.failurePolicy);
|
|
7648
8014
|
if (settlement === void 0) {
|
|
7649
8015
|
continue;
|
|
7650
8016
|
}
|
|
7651
|
-
|
|
7652
|
-
if (Object.keys(
|
|
8017
|
+
join16.completed = { ...join16.completed, [branch.branchId]: settlement };
|
|
8018
|
+
if (Object.keys(join16.completed).length !== join16.childBranchIds.length) {
|
|
7653
8019
|
continue;
|
|
7654
8020
|
}
|
|
7655
8021
|
let nextNode;
|
|
7656
8022
|
try {
|
|
7657
|
-
nextNode = await
|
|
8023
|
+
nextNode = await join16.postContinuation({ status: "completed", branches: join16.completed });
|
|
7658
8024
|
} catch (error) {
|
|
7659
8025
|
await terminalizeTrack({
|
|
7660
8026
|
status: "failure",
|
|
7661
8027
|
failure: {
|
|
7662
8028
|
code: "step_execution_failed",
|
|
7663
|
-
message: `parallel post failed for ${
|
|
8029
|
+
message: `parallel post failed for ${join16.source}: ${String(error)}`
|
|
7664
8030
|
}
|
|
7665
8031
|
});
|
|
7666
8032
|
continue;
|
|
7667
8033
|
}
|
|
7668
8034
|
const postBranch = enqueue(
|
|
7669
8035
|
nextNode,
|
|
7670
|
-
|
|
7671
|
-
`post for ${
|
|
8036
|
+
join16.parentBranchId,
|
|
8037
|
+
`post for ${join16.source}`,
|
|
7672
8038
|
false,
|
|
7673
8039
|
true
|
|
7674
8040
|
);
|
|
7675
|
-
if (
|
|
7676
|
-
const afterPostContinuation =
|
|
8041
|
+
if (join16.afterPostContinuation !== void 0) {
|
|
8042
|
+
const afterPostContinuation = join16.afterPostContinuation;
|
|
7677
8043
|
pendingJoins.push({
|
|
7678
|
-
parentBranchId:
|
|
8044
|
+
parentBranchId: join16.parentBranchId,
|
|
7679
8045
|
childBranchIds: [postBranch.branchId],
|
|
7680
|
-
source: `post for ${
|
|
8046
|
+
source: `post for ${join16.source}`,
|
|
7681
8047
|
failurePolicy: "fail-fast",
|
|
7682
8048
|
postContinuation: async (aggregateOutput) => {
|
|
7683
8049
|
const branches2 = aggregateOutput.branches;
|
|
@@ -7877,8 +8243,10 @@ function resolveSchedulerWorkerCount(workers) {
|
|
|
7877
8243
|
}
|
|
7878
8244
|
return { ok: true, workers };
|
|
7879
8245
|
}
|
|
7880
|
-
function decorateBranchEvent(event, trackId, branch) {
|
|
7881
|
-
|
|
8246
|
+
function decorateBranchEvent(event, trackId, branch, decorateWaits) {
|
|
8247
|
+
const isStepEvent = event.type === "step.started" || event.type === "step.completed";
|
|
8248
|
+
const isWaitEvent = event.type === "wait.started" || event.type === "wait.satisfied" || event.type === "wait.failed";
|
|
8249
|
+
if (!isStepEvent && !isWaitEvent || isWaitEvent && !decorateWaits) {
|
|
7882
8250
|
return event;
|
|
7883
8251
|
}
|
|
7884
8252
|
const stepArtifactPaths = event.stepId === void 0 || branch.latestStepIndex < 1 ? void 0 : resolveStepArtifactPaths({
|
|
@@ -7904,7 +8272,7 @@ function decorateBranchEvent(event, trackId, branch) {
|
|
|
7904
8272
|
async function createPersistedTrackRetryPlan(input) {
|
|
7905
8273
|
let track;
|
|
7906
8274
|
try {
|
|
7907
|
-
track = JSON.parse(await
|
|
8275
|
+
track = JSON.parse(await readFile13(join14(input.runDir, "track.json"), "utf8"));
|
|
7908
8276
|
} catch (error) {
|
|
7909
8277
|
return {
|
|
7910
8278
|
status: "failure",
|
|
@@ -7929,7 +8297,7 @@ async function createPersistedTrackRetryPlan(input) {
|
|
|
7929
8297
|
track.branches.map(
|
|
7930
8298
|
async (branchId) => branchFromJson(
|
|
7931
8299
|
JSON.parse(
|
|
7932
|
-
await
|
|
8300
|
+
await readFile13(join14(input.runDir, "branches", `${branchId}.json`), "utf8")
|
|
7933
8301
|
)
|
|
7934
8302
|
)
|
|
7935
8303
|
)
|
|
@@ -7970,10 +8338,12 @@ async function createPersistedTrackRetryPlan(input) {
|
|
|
7970
8338
|
}
|
|
7971
8339
|
branch.status = "queued";
|
|
7972
8340
|
branch.failure = void 0;
|
|
7973
|
-
branch.wait = void 0;
|
|
7974
8341
|
branch.terminalKind = void 0;
|
|
7975
8342
|
branch.message = void 0;
|
|
7976
8343
|
branch.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8344
|
+
if (input.track?.mode !== "wait-answered") {
|
|
8345
|
+
branch.wait = void 0;
|
|
8346
|
+
}
|
|
7977
8347
|
const rootParallelBranchIndex = retryRootParallelNode === void 0 ? void 0 : branches.filter((candidate) => candidate.parentBranchId === track.rootBranchId).findIndex((candidate) => candidate.branchId === branch.branchId);
|
|
7978
8348
|
const node = (branch.requestedBranchId === void 0 ? void 0 : nodesByRequestedBranchId.get(branch.requestedBranchId)) ?? (rootParallelBranchIndex === void 0 || rootParallelBranchIndex < 0 ? retryCandidateNodes[index] : retryCandidateNodes[rootParallelBranchIndex]);
|
|
7979
8349
|
if (node === void 0) {
|
|
@@ -8046,6 +8416,16 @@ function selectPersistedBranchesForRetry(branches, track) {
|
|
|
8046
8416
|
)
|
|
8047
8417
|
};
|
|
8048
8418
|
}
|
|
8419
|
+
if (track.mode === "wait-answered") {
|
|
8420
|
+
return {
|
|
8421
|
+
status: "success",
|
|
8422
|
+
branchIds: new Set(
|
|
8423
|
+
branches.filter(
|
|
8424
|
+
(branch2) => branch2.status === "waiting" && branch2.wait !== void 0 && typeof branch2.wait === "object" && typeof branch2.wait.artifactPaths?.answerFile === "string"
|
|
8425
|
+
).map((branch2) => branch2.branchId)
|
|
8426
|
+
)
|
|
8427
|
+
};
|
|
8428
|
+
}
|
|
8049
8429
|
if (track.mode !== "branch") {
|
|
8050
8430
|
return {
|
|
8051
8431
|
status: "failure",
|
|
@@ -8121,7 +8501,7 @@ async function persistTrack(options, workers, rootBranchId, branches, status, sp
|
|
|
8121
8501
|
...terminalOutput === void 0 ? {} : { terminalOutput },
|
|
8122
8502
|
...failure === void 0 ? {} : { failure }
|
|
8123
8503
|
};
|
|
8124
|
-
await writeJson(
|
|
8504
|
+
await writeJson(join14(options.runDir, "track.json"), trackState);
|
|
8125
8505
|
}
|
|
8126
8506
|
async function persistBranch(runDir, branch) {
|
|
8127
8507
|
const branchState = {
|
|
@@ -8144,10 +8524,10 @@ async function persistBranch(runDir, branch) {
|
|
|
8144
8524
|
latestStepIndex: branch.latestStepIndex,
|
|
8145
8525
|
...branch.latestStepId === void 0 ? {} : { latestStepId: branch.latestStepId }
|
|
8146
8526
|
};
|
|
8147
|
-
await writeJson(
|
|
8527
|
+
await writeJson(join14(runDir, "branches", `${branch.branchId}.json`), branchState);
|
|
8148
8528
|
}
|
|
8149
8529
|
async function writeJson(path, value) {
|
|
8150
|
-
await mkdir9(
|
|
8530
|
+
await mkdir9(join14(path, ".."), { recursive: true });
|
|
8151
8531
|
await writeFile10(path, `${JSON.stringify(value, null, 2)}
|
|
8152
8532
|
`, "utf8");
|
|
8153
8533
|
}
|
|
@@ -8329,6 +8709,8 @@ async function runWorkflow(options) {
|
|
|
8329
8709
|
let workflowInput;
|
|
8330
8710
|
let startNode;
|
|
8331
8711
|
let waitResume;
|
|
8712
|
+
let isWaitContinueTrackResume = false;
|
|
8713
|
+
let branchResumes = {};
|
|
8332
8714
|
let isTrackRetry = false;
|
|
8333
8715
|
if (isResume) {
|
|
8334
8716
|
const danglingAnchor = findDanglingInteractiveSessionStart(previousEvents);
|
|
@@ -8363,40 +8745,105 @@ async function runWorkflow(options) {
|
|
|
8363
8745
|
})
|
|
8364
8746
|
);
|
|
8365
8747
|
} else if (isWaitContinue) {
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8748
|
+
const hasParallelTrack = await hasSupportedRootParallelTrackRetryMetadata(runDir);
|
|
8749
|
+
if (hasParallelTrack) {
|
|
8750
|
+
const answeredBranches = await findAnsweredParallelBranches(runDir);
|
|
8751
|
+
if (answeredBranches.length === 0) {
|
|
8752
|
+
return failResumeValidation({
|
|
8753
|
+
code: "continue_parallel_track_unsupported",
|
|
8754
|
+
message: "Continuing a waiting parallel track is not yet supported; retry the run or restart the workflow instead."
|
|
8755
|
+
});
|
|
8756
|
+
}
|
|
8757
|
+
const persistedInput = readWorkflowStartedInput(previousEvents);
|
|
8758
|
+
if (persistedInput === void 0) {
|
|
8759
|
+
return failResumeValidation({
|
|
8760
|
+
code: "continue_target_not_found",
|
|
8761
|
+
message: "Continue target workflow started payload missing input."
|
|
8762
|
+
});
|
|
8763
|
+
}
|
|
8764
|
+
await runContext.state.hydratePersisted();
|
|
8765
|
+
await runContext.globalState.hydratePersisted();
|
|
8766
|
+
workflowInput = inputSchema ? inputSchema.assert(persistedInput, "workflow input") : persistedInput;
|
|
8767
|
+
startNode = options.workflow.start(workflowInput);
|
|
8768
|
+
isWaitContinueTrackResume = true;
|
|
8769
|
+
waitResume = void 0;
|
|
8770
|
+
branchResumes = {};
|
|
8771
|
+
for (const branchId of answeredBranches) {
|
|
8772
|
+
const replay = await replayWithoutClobberingStateOnFailure(
|
|
8773
|
+
runDir,
|
|
8774
|
+
() => replayToWaitingStep({
|
|
8775
|
+
workflow: options.workflow,
|
|
8776
|
+
events: previousEvents,
|
|
8777
|
+
runDir,
|
|
8778
|
+
branchId
|
|
8779
|
+
})
|
|
8780
|
+
);
|
|
8781
|
+
if (replay.status === "success") {
|
|
8782
|
+
branchResumes[branchId] = {
|
|
8783
|
+
stepId: replay.resumedStepId,
|
|
8784
|
+
stepIndex: replay.stepIndex,
|
|
8785
|
+
phaseIndex: replay.phaseIndex,
|
|
8786
|
+
phaseValue: replay.phaseValue,
|
|
8787
|
+
waitOutputs: replay.waitOutputs,
|
|
8788
|
+
seenWaitIds: replay.seenWaitIds,
|
|
8789
|
+
wait: replay.wait
|
|
8790
|
+
};
|
|
8397
8791
|
}
|
|
8398
|
-
}
|
|
8399
|
-
|
|
8792
|
+
}
|
|
8793
|
+
const firstReplay = await replayWithoutClobberingStateOnFailure(
|
|
8794
|
+
runDir,
|
|
8795
|
+
() => replayToWaitingStep({
|
|
8796
|
+
workflow: options.workflow,
|
|
8797
|
+
events: previousEvents,
|
|
8798
|
+
runDir,
|
|
8799
|
+
branchId: answeredBranches[0]
|
|
8800
|
+
})
|
|
8801
|
+
);
|
|
8802
|
+
if (firstReplay.status === "success") {
|
|
8803
|
+
await emit(
|
|
8804
|
+
createEvent({
|
|
8805
|
+
runId,
|
|
8806
|
+
workflowId: options.workflow.id,
|
|
8807
|
+
type: "workflow.resumed",
|
|
8808
|
+
payload: {
|
|
8809
|
+
resumeKind: "wait",
|
|
8810
|
+
resumedFromRunDir: runDir,
|
|
8811
|
+
resumedStepId: firstReplay.resumedStepId,
|
|
8812
|
+
sourceWaitEventId: firstReplay.sourceWaitEventId
|
|
8813
|
+
}
|
|
8814
|
+
})
|
|
8815
|
+
);
|
|
8816
|
+
}
|
|
8817
|
+
} else {
|
|
8818
|
+
const replay = await replayWithoutClobberingStateOnFailure(
|
|
8819
|
+
runDir,
|
|
8820
|
+
() => replayToWaitingStep({
|
|
8821
|
+
workflow: options.workflow,
|
|
8822
|
+
events: previousEvents,
|
|
8823
|
+
runDir
|
|
8824
|
+
})
|
|
8825
|
+
);
|
|
8826
|
+
if (replay.status === "failure") {
|
|
8827
|
+
return failResumeValidation(replay.failure);
|
|
8828
|
+
}
|
|
8829
|
+
await runContext.state.hydratePersisted();
|
|
8830
|
+
workflowInput = inputSchema ? inputSchema.assert(replay.input, "workflow input") : replay.input;
|
|
8831
|
+
startNode = replay.node;
|
|
8832
|
+
waitResume = replay;
|
|
8833
|
+
await emit(
|
|
8834
|
+
createEvent({
|
|
8835
|
+
runId,
|
|
8836
|
+
workflowId: options.workflow.id,
|
|
8837
|
+
type: "workflow.resumed",
|
|
8838
|
+
payload: {
|
|
8839
|
+
resumeKind: "wait",
|
|
8840
|
+
resumedFromRunDir: runDir,
|
|
8841
|
+
resumedStepId: replay.resumedStepId,
|
|
8842
|
+
sourceWaitEventId: replay.sourceWaitEventId
|
|
8843
|
+
}
|
|
8844
|
+
})
|
|
8845
|
+
);
|
|
8846
|
+
}
|
|
8400
8847
|
} else if (isRetry) {
|
|
8401
8848
|
if (await hasSupportedRootParallelTrackRetryMetadata(runDir)) {
|
|
8402
8849
|
const persistedInput = readWorkflowStartedInput(previousEvents);
|
|
@@ -8465,7 +8912,7 @@ async function runWorkflow(options) {
|
|
|
8465
8912
|
}
|
|
8466
8913
|
const rootNode = startNode ?? options.workflow.start(workflowInput);
|
|
8467
8914
|
const rootNodes = Array.isArray(rootNode) ? rootNode : [rootNode];
|
|
8468
|
-
const shouldUseRootArrayScheduler = !isResume && !isRetry && !isWaitContinue || isTrackRetry;
|
|
8915
|
+
const shouldUseRootArrayScheduler = !isResume && !isRetry && !isWaitContinue || isTrackRetry || isWaitContinueTrackResume;
|
|
8469
8916
|
const continuationResult = shouldUseRootArrayScheduler ? await runRootContinuationArrayScheduler({
|
|
8470
8917
|
nodes: rootNodes,
|
|
8471
8918
|
...Array.isArray(rootNode) ? { rootIsArray: true } : {},
|
|
@@ -8491,6 +8938,16 @@ async function runWorkflow(options) {
|
|
|
8491
8938
|
).length,
|
|
8492
8939
|
track: options.retry?.track
|
|
8493
8940
|
}
|
|
8941
|
+
} : isWaitContinueTrackResume ? {
|
|
8942
|
+
retry: {
|
|
8943
|
+
initialExecutedSteps: previousEvents.filter(
|
|
8944
|
+
(event) => event.type === "step.started"
|
|
8945
|
+
).length,
|
|
8946
|
+
track: {
|
|
8947
|
+
mode: "wait-answered"
|
|
8948
|
+
}
|
|
8949
|
+
},
|
|
8950
|
+
waitResumeBranches: branchResumes
|
|
8494
8951
|
} : {}
|
|
8495
8952
|
}) : await runContinuation({
|
|
8496
8953
|
node: rootNode,
|
|
@@ -8579,14 +9036,14 @@ async function replayWithoutClobberingStateOnFailure(runDir, replay) {
|
|
|
8579
9036
|
}
|
|
8580
9037
|
async function hasSupportedRootParallelTrackRetryMetadata(runDir) {
|
|
8581
9038
|
try {
|
|
8582
|
-
const track = JSON.parse(await
|
|
9039
|
+
const track = JSON.parse(await readFile14(join15(runDir, "track.json"), "utf8"));
|
|
8583
9040
|
if (track.splitOccurred !== true || typeof track.rootBranchId !== "string" || !Array.isArray(track.branches) || !track.branches.every((branchId) => typeof branchId === "string")) {
|
|
8584
9041
|
return false;
|
|
8585
9042
|
}
|
|
8586
9043
|
const branches = await Promise.all(
|
|
8587
9044
|
track.branches.map(async (branchId) => {
|
|
8588
9045
|
const branch = JSON.parse(
|
|
8589
|
-
await
|
|
9046
|
+
await readFile14(join15(runDir, "branches", `${branchId}.json`), "utf8")
|
|
8590
9047
|
);
|
|
8591
9048
|
return branch;
|
|
8592
9049
|
})
|
|
@@ -8601,6 +9058,41 @@ async function hasSupportedRootParallelTrackRetryMetadata(runDir) {
|
|
|
8601
9058
|
throw error;
|
|
8602
9059
|
}
|
|
8603
9060
|
}
|
|
9061
|
+
async function findAnsweredParallelBranches(runDir) {
|
|
9062
|
+
try {
|
|
9063
|
+
const track = JSON.parse(await readFile14(join15(runDir, "track.json"), "utf8"));
|
|
9064
|
+
if (track.splitOccurred !== true || !Array.isArray(track.branches)) {
|
|
9065
|
+
return [];
|
|
9066
|
+
}
|
|
9067
|
+
const answered = [];
|
|
9068
|
+
for (const branchId of track.branches) {
|
|
9069
|
+
if (typeof branchId !== "string") continue;
|
|
9070
|
+
let branchState;
|
|
9071
|
+
try {
|
|
9072
|
+
branchState = JSON.parse(
|
|
9073
|
+
await readFile14(join15(runDir, "branches", `${branchId}.json`), "utf8")
|
|
9074
|
+
);
|
|
9075
|
+
} catch {
|
|
9076
|
+
continue;
|
|
9077
|
+
}
|
|
9078
|
+
if (branchState?.status !== "waiting") continue;
|
|
9079
|
+
if (branchState.wait === void 0 || typeof branchState.wait !== "object" || branchState.wait === null) {
|
|
9080
|
+
continue;
|
|
9081
|
+
}
|
|
9082
|
+
const wait = branchState.wait;
|
|
9083
|
+
const answerFile = wait.artifactPaths?.answerFile;
|
|
9084
|
+
if (typeof answerFile !== "string") continue;
|
|
9085
|
+
try {
|
|
9086
|
+
await readFile14(join15(runDir, answerFile), "utf8");
|
|
9087
|
+
answered.push(branchId);
|
|
9088
|
+
} catch {
|
|
9089
|
+
}
|
|
9090
|
+
}
|
|
9091
|
+
return answered;
|
|
9092
|
+
} catch {
|
|
9093
|
+
return [];
|
|
9094
|
+
}
|
|
9095
|
+
}
|
|
8604
9096
|
function readWorkflowStartedInput(events) {
|
|
8605
9097
|
const started = events.find((event) => event.type === "workflow.started");
|
|
8606
9098
|
const input = started?.payload.input;
|
|
@@ -8686,6 +9178,7 @@ export {
|
|
|
8686
9178
|
readRunState,
|
|
8687
9179
|
readStorageLifecycleStatus,
|
|
8688
9180
|
resolveAgentTargets,
|
|
9181
|
+
resolveCliCommandForSpawn,
|
|
8689
9182
|
resolveRetryPolicy,
|
|
8690
9183
|
resolveTimeoutPolicy,
|
|
8691
9184
|
restoreArchivedRun,
|