@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.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
- if (command === void 0 || prompt === void 0 || output === void 0) {
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 { supported: true, command, ...args === void 0 ? {} : { args }, prompt, output };
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 readFile8 } from "fs/promises";
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
- result = await (options.runner ?? spawnWorkingAgentProcess)({
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 { writeFile as writeFile5 } from "fs/promises";
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: working.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 readFile8(resolve(cwd, source.path), "utf8");
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 readFile9, writeFile as writeFile8 } from "fs/promises";
4620
- import { join as join10 } from "path";
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 join10(runDir, CANCELLATION_MARKER_FILE);
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 readFile9(cancellationMarkerPath(runDir), "utf8");
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 readFile13 } from "fs/promises";
4720
- import { basename as basename4, join as join14 } from "path";
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 readFile10, writeFile as writeFile9 } from "fs/promises";
4724
- import { join as join11 } from "path";
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 join11(runDir, ...runRelativePath.split("/"));
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 readFile10(path, "utf8") };
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 readFile11 } from "fs/promises";
6574
- import { join as join12 } from "path";
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 pendingWait = findLatestPendingWait(options.events);
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: options.events.slice(0, targetStepStartedIndex),
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 readFile11(join12(runDir, ...artifactPaths.requestFile.split("/")), "utf8")
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 join12(runDir, "steps", artifactStepId);
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 readFile12, writeFile as writeFile10 } from "fs/promises";
7619
+ import { mkdir as mkdir9, readFile as readFile13, writeFile as writeFile10 } from "fs/promises";
7255
7620
  import { availableParallelism } from "os";
7256
- import { join as join13 } from "path";
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 join15 of pendingJoins) {
7644
- if (!join15.childBranchIds.includes(branch.branchId)) {
8009
+ for (const join16 of pendingJoins) {
8010
+ if (!join16.childBranchIds.includes(branch.branchId)) {
7645
8011
  continue;
7646
8012
  }
7647
- const settlement = branchSettlementForJoin(branch, join15.failurePolicy);
8013
+ const settlement = branchSettlementForJoin(branch, join16.failurePolicy);
7648
8014
  if (settlement === void 0) {
7649
8015
  continue;
7650
8016
  }
7651
- join15.completed = { ...join15.completed, [branch.branchId]: settlement };
7652
- if (Object.keys(join15.completed).length !== join15.childBranchIds.length) {
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 join15.postContinuation({ status: "completed", branches: join15.completed });
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 ${join15.source}: ${String(error)}`
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
- join15.parentBranchId,
7671
- `post for ${join15.source}`,
8036
+ join16.parentBranchId,
8037
+ `post for ${join16.source}`,
7672
8038
  false,
7673
8039
  true
7674
8040
  );
7675
- if (join15.afterPostContinuation !== void 0) {
7676
- const afterPostContinuation = join15.afterPostContinuation;
8041
+ if (join16.afterPostContinuation !== void 0) {
8042
+ const afterPostContinuation = join16.afterPostContinuation;
7677
8043
  pendingJoins.push({
7678
- parentBranchId: join15.parentBranchId,
8044
+ parentBranchId: join16.parentBranchId,
7679
8045
  childBranchIds: [postBranch.branchId],
7680
- source: `post for ${join15.source}`,
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
- if (event.type !== "step.started" && event.type !== "step.completed") {
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 readFile12(join13(input.runDir, "track.json"), "utf8"));
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 readFile12(join13(input.runDir, "branches", `${branchId}.json`), "utf8")
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(join13(options.runDir, "track.json"), trackState);
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(join13(runDir, "branches", `${branch.branchId}.json`), branchState);
8527
+ await writeJson(join14(runDir, "branches", `${branch.branchId}.json`), branchState);
8148
8528
  }
8149
8529
  async function writeJson(path, value) {
8150
- await mkdir9(join13(path, ".."), { recursive: true });
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
- if (await hasSupportedRootParallelTrackRetryMetadata(runDir)) {
8367
- return failResumeValidation({
8368
- code: "continue_parallel_track_unsupported",
8369
- message: "Continuing a waiting parallel track is not yet supported; retry the run or restart the workflow instead."
8370
- });
8371
- }
8372
- const replay = await replayWithoutClobberingStateOnFailure(
8373
- runDir,
8374
- () => replayToWaitingStep({
8375
- workflow: options.workflow,
8376
- events: previousEvents,
8377
- runDir
8378
- })
8379
- );
8380
- if (replay.status === "failure") {
8381
- return failResumeValidation(replay.failure);
8382
- }
8383
- await runContext.state.hydratePersisted();
8384
- workflowInput = inputSchema ? inputSchema.assert(replay.input, "workflow input") : replay.input;
8385
- startNode = replay.node;
8386
- waitResume = replay;
8387
- await emit(
8388
- createEvent({
8389
- runId,
8390
- workflowId: options.workflow.id,
8391
- type: "workflow.resumed",
8392
- payload: {
8393
- resumeKind: "wait",
8394
- resumedFromRunDir: runDir,
8395
- resumedStepId: replay.resumedStepId,
8396
- sourceWaitEventId: replay.sourceWaitEventId
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 readFile13(join14(runDir, "track.json"), "utf8"));
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 readFile13(join14(runDir, "branches", `${branchId}.json`), "utf8")
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,