@promptbook/cli 0.112.0-127 → 0.112.0-129

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/esm/index.es.js CHANGED
@@ -58,7 +58,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
58
58
  * @generated
59
59
  * @see https://github.com/webgptorg/promptbook
60
60
  */
61
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-127';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-129';
62
62
  /**
63
63
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
64
64
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -5192,7 +5192,9 @@ function createCoderRunOptionsForAgent(options) {
5192
5192
  noUi: options.noUi,
5193
5193
  thinkingLevel: options.thinkingLevel,
5194
5194
  waitForUser: false,
5195
+ waitAfterPrompt: 0,
5195
5196
  waitBetweenPrompts: 0,
5197
+ waitAfterError: 0,
5196
5198
  noCommit: options.noCommit,
5197
5199
  ignoreGitChanges: options.ignoreGitChanges,
5198
5200
  normalizeLineEndings: options.normalizeLineEndings,
@@ -39922,6 +39924,27 @@ function formatDurationMs(ms) {
39922
39924
  return parts.join(' ');
39923
39925
  }
39924
39926
 
39927
+ /**
39928
+ * Default wait duration applied before retrying a prompt round after an error (10 minutes).
39929
+ *
39930
+ * @private internal constant of `ptbk coder` wait handling
39931
+ */
39932
+ const DEFAULT_WAIT_AFTER_ERROR_MS$1 = 10 * 60 * 1000;
39933
+ /**
39934
+ * Parses an optional Commander duration string and returns the resolved milliseconds.
39935
+ *
39936
+ * Returns `defaultMs` when the flag was not provided or was provided without a non-empty value.
39937
+ *
39938
+ * @private internal utility of `ptbk coder` wait handling
39939
+ */
39940
+ function parseOptionalWaitDuration(value, defaultMs) {
39941
+ if (typeof value !== 'string' || value === '') {
39942
+ return defaultMs;
39943
+ }
39944
+ return parseDuration(value);
39945
+ }
39946
+ // Note: [💞] Ignore a discrepancy between file name and entity name
39947
+
39925
39948
  /**
39926
39949
  * Initializes `coder run` command for Promptbook CLI utilities
39927
39950
  *
@@ -39955,31 +39978,46 @@ function $initializeCoderRunCommand(program) {
39955
39978
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
39956
39979
  addPromptRunnerExecutionOptions(command);
39957
39980
  command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption$1, 0);
39958
- command.option('--wait <duration>', spaceTrim$1(`
39959
- Wait this long between prompt rounds to avoid hitting rate limits of the harness.
39960
- Accepts durations like 1h, 30m, 5s.
39981
+ command.option('--wait-after-prompt <duration>', spaceTrim$1(`
39982
+ Wait this long after each prompt has been implemented, verified and committed before starting the next prompt.
39983
+ Accepts durations like 1h, 30m, 5s. Defaults to 0 (no wait).
39984
+ `));
39985
+ command.option('--wait-between-prompts <duration>', spaceTrim$1(`
39986
+ Pace prompts so that each next prompt starts at least this long after the previous one began.
39987
+ If the previous prompt already took longer than this, the next prompt starts immediately.
39988
+ Accepts durations like 1h, 30m, 5s. Defaults to 0 (no pacing).
39989
+ `));
39990
+ command.option('--wait-after-error <duration>', spaceTrim$1(`
39991
+ Wait this long before retrying a prompt after an error occurs (up to 3 retries before giving up).
39992
+ Accepts durations like 1h, 30m, 5s. Defaults to 10m.
39961
39993
  `));
39962
39994
  // Note: --no-auto disables the default auto behaviour and waits for user confirmation before each prompt
39963
39995
  command.option('--no-auto', 'Wait for user confirmation before each prompt instead of running automatically through the queue');
39964
39996
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
39965
39997
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
39966
39998
  command.action(handleActionErrors(async (cliOptions) => {
39967
- const { dryRun, agent, context, test, preserveLogs, priority, wait, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
39999
+ const { dryRun, agent, context, test, preserveLogs, priority, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
39968
40000
  const testCommand = normalizeCommandOptionValue$1(test);
39969
40001
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
39970
40002
  isAgentRequired: !dryRun,
39971
40003
  });
39972
- // [1] Parse the --wait and --no-auto options:
39973
- // default: run automatically through the queue (no waiting)
40004
+ // [1] Parse the wait options and --no-auto:
40005
+ // default: run automatically through the queue (no waiting between prompts)
39974
40006
  // --no-auto: wait for user confirmation before each prompt (interactive mode)
39975
- // --wait <duration>: wait that long between prompt rounds to avoid rate limits
40007
+ // --wait-after-prompt: pause after a successful round before starting the next prompt
40008
+ // --wait-between-prompts: pace from start of one prompt to start of next
40009
+ // --wait-after-error: wait before retrying after an error (default 10m)
39976
40010
  const waitForUser = !auto;
39977
- const waitBetweenPrompts = typeof wait === 'string' && wait !== '' ? parseDuration(wait) : 0;
40011
+ const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
40012
+ const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
40013
+ const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
39978
40014
  // Convert commander options to RunOptions format
39979
40015
  const runOptions = {
39980
40016
  dryRun,
39981
40017
  waitForUser,
40018
+ waitAfterPrompt,
39982
40019
  waitBetweenPrompts,
40020
+ waitAfterError,
39983
40021
  noCommit: runnerOptions.noCommit,
39984
40022
  ignoreGitChanges: runnerOptions.ignoreGitChanges,
39985
40023
  agentName: runnerOptions.agentName,
@@ -40087,28 +40125,41 @@ function $initializeCoderServerCommand(program) {
40087
40125
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
40088
40126
  addPromptRunnerExecutionOptions(command);
40089
40127
  command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption, 0);
40090
- command.option('--wait <duration>', spaceTrim$1(`
40091
- Wait this long between prompt rounds to avoid hitting rate limits of the harness.
40092
- Accepts durations like 1h, 30m, 5s.
40128
+ command.option('--wait-after-prompt <duration>', spaceTrim$1(`
40129
+ Wait this long after each prompt has been implemented, verified and committed before starting the next prompt.
40130
+ Accepts durations like 1h, 30m, 5s. Defaults to 0 (no wait).
40131
+ `));
40132
+ command.option('--wait-between-prompts <duration>', spaceTrim$1(`
40133
+ Pace prompts so that each next prompt starts at least this long after the previous one began.
40134
+ If the previous prompt already took longer than this, the next prompt starts immediately.
40135
+ Accepts durations like 1h, 30m, 5s. Defaults to 0 (no pacing).
40136
+ `));
40137
+ command.option('--wait-after-error <duration>', spaceTrim$1(`
40138
+ Wait this long before retrying a prompt after an error occurs (up to 3 retries before giving up).
40139
+ Accepts durations like 1h, 30m, 5s. Defaults to 10m.
40093
40140
  `));
40094
40141
  command.option('--no-auto', 'Wait for user confirmation before each prompt instead of running automatically through the queue');
40095
40142
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
40096
40143
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
40097
40144
  command.action(handleActionErrors(async (cliOptions) => {
40098
- const { port: rawPort, dryRun, agent, context, test, preserveLogs, priority, wait, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
40145
+ const { port: rawPort, dryRun, agent, context, test, preserveLogs, priority, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
40099
40146
  const port = parseCoderServerPort(rawPort);
40100
40147
  const testCommand = normalizeCommandOptionValue(test);
40101
40148
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
40102
40149
  isAgentRequired: !dryRun,
40103
40150
  });
40104
- // [1] Parse the --wait and --no-auto options (same logic as `coder run`)
40151
+ // [1] Parse the wait options and --no-auto (same logic as `coder run`)
40105
40152
  const waitForUser = !auto;
40106
- const waitBetweenPrompts = typeof wait === 'string' && wait !== '' ? parseDuration(wait) : 0;
40153
+ const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
40154
+ const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
40155
+ const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
40107
40156
  const runOptions = {
40108
40157
  port,
40109
40158
  dryRun,
40110
40159
  waitForUser,
40160
+ waitAfterPrompt,
40111
40161
  waitBetweenPrompts,
40162
+ waitAfterError,
40112
40163
  noCommit: runnerOptions.noCommit,
40113
40164
  ignoreGitChanges: runnerOptions.ignoreGitChanges,
40114
40165
  agentName: runnerOptions.agentName,
@@ -63583,7 +63634,9 @@ function createPromptRunnerOptions(options) {
63583
63634
  noUi: options.noUi,
63584
63635
  thinkingLevel: options.thinkingLevel,
63585
63636
  waitForUser: false,
63637
+ waitAfterPrompt: 0,
63586
63638
  waitBetweenPrompts: 0,
63639
+ waitAfterError: 0,
63587
63640
  noCommit: true,
63588
63641
  ignoreGitChanges: true,
63589
63642
  normalizeLineEndings: false,
@@ -69418,10 +69471,14 @@ var findUnwrittenPrompts$1 = /*#__PURE__*/Object.freeze({
69418
69471
  findUnwrittenPrompts: findUnwrittenPrompts
69419
69472
  });
69420
69473
 
69474
+ /**
69475
+ * Default wait duration applied before retrying a failed prompt round.
69476
+ */
69477
+ const DEFAULT_WAIT_AFTER_ERROR_MS = 10 * 60 * 1000;
69421
69478
  /**
69422
69479
  * CLI usage text for this script.
69423
69480
  */
69424
- const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--preserve-logs] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait <duration>] [--no-auto] [--no-commit] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
69481
+ const USAGE = 'Usage: run-codex-prompts [--dry-run] [--harness <harness-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--preserve-logs] [--no-ui] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--wait-after-prompt <duration>] [--wait-between-prompts <duration>] [--wait-after-error <duration>] [--no-auto] [--no-commit] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push] [--auto-pull]';
69425
69482
  /**
69426
69483
  * Top-level flags supported by this command.
69427
69484
  */
@@ -69438,7 +69495,9 @@ const KNOWN_OPTION_FLAGS = new Set([
69438
69495
  '--allow-credits',
69439
69496
  '--auto-migrate',
69440
69497
  '--allow-destructive-auto-migrate',
69441
- '--wait',
69498
+ '--wait-after-prompt',
69499
+ '--wait-between-prompts',
69500
+ '--wait-after-error',
69442
69501
  '--no-auto',
69443
69502
  '--no-commit',
69444
69503
  '--ignore-git-changes',
@@ -69482,15 +69541,9 @@ function parseRunOptions(args) {
69482
69541
  // --no-auto: wait for user confirmation before each prompt (interactive mode)
69483
69542
  // --wait 1h: wait 1h between prompt rounds to avoid rate limits
69484
69543
  const waitForUser = args.includes('--no-auto');
69485
- let waitBetweenPrompts = 0;
69486
- const hasWaitFlag = args.includes('--wait');
69487
- const waitValue = readOptionValue(args, '--wait');
69488
- if (hasWaitFlag) {
69489
- if (waitValue === undefined || waitValue.startsWith('-')) {
69490
- exitWithUsageError('Missing value for --wait. Use a duration like 1h, 30m, 5s.');
69491
- }
69492
- waitBetweenPrompts = parseDuration(waitValue);
69493
- }
69544
+ const waitAfterPrompt = parseWaitOption(args, '--wait-after-prompt', 0);
69545
+ const waitBetweenPrompts = parseWaitOption(args, '--wait-between-prompts', 0);
69546
+ const waitAfterError = parseWaitOption(args, '--wait-after-error', DEFAULT_WAIT_AFTER_ERROR_MS);
69494
69547
  let thinkingLevel;
69495
69548
  if (hasTestCommandFlag && testCommand === undefined) {
69496
69549
  exitWithUsageError('Missing value for --test. Use a shell command such as `npm run test` and quote it when it contains top-level CLI flags.');
@@ -69510,7 +69563,9 @@ function parseRunOptions(args) {
69510
69563
  return {
69511
69564
  dryRun,
69512
69565
  waitForUser,
69566
+ waitAfterPrompt,
69513
69567
  waitBetweenPrompts,
69568
+ waitAfterError,
69514
69569
  noCommit,
69515
69570
  ignoreGitChanges,
69516
69571
  normalizeLineEndings,
@@ -69529,6 +69584,19 @@ function parseRunOptions(args) {
69529
69584
  priority,
69530
69585
  };
69531
69586
  }
69587
+ /**
69588
+ * Reads a duration-typed CLI flag, applying the provided default when the flag is absent.
69589
+ */
69590
+ function parseWaitOption(args, flag, defaultMs) {
69591
+ if (!args.includes(flag)) {
69592
+ return defaultMs;
69593
+ }
69594
+ const value = readOptionValue(args, flag);
69595
+ if (value === undefined || value.startsWith('-')) {
69596
+ exitWithUsageError(`Missing value for ${flag}. Use a duration like 1h, 30m, 5s.`);
69597
+ }
69598
+ return parseDuration(value);
69599
+ }
69532
69600
  /**
69533
69601
  * Reads a value of a CLI option that follows a given flag.
69534
69602
  */
@@ -69765,6 +69833,58 @@ async function resolveAgentSystemMessage(agentPath, currentWorkingDirectory) {
69765
69833
  return createAgentRunnerSystemMessage(agentSource);
69766
69834
  }
69767
69835
 
69836
+ /**
69837
+ * Refresh interval used by the countdown display while waiting.
69838
+ *
69839
+ * @private internal constant of `sleepWithCountdown`
69840
+ */
69841
+ const WAIT_COUNTDOWN_UPDATE_INTERVAL_MS = 30000;
69842
+ /**
69843
+ * Returns the human-readable status message used for one wait kind.
69844
+ *
69845
+ * @private internal utility of `sleepWithCountdown`
69846
+ */
69847
+ function describeCoderRunWait(waitKind, remainingMs, totalMs) {
69848
+ const formattedRemaining = formatDurationMs(remainingMs);
69849
+ const formattedTotal = totalMs !== undefined ? formatDurationMs(totalMs) : undefined;
69850
+ const totalSuffix = formattedTotal !== undefined ? ` of ${formattedTotal}` : '';
69851
+ switch (waitKind) {
69852
+ case 'between-prompts':
69853
+ return `Waiting ${formattedRemaining}${totalSuffix} between prompts (paced from previous start)`;
69854
+ case 'after-prompt':
69855
+ return `Waiting ${formattedRemaining}${totalSuffix} after previous prompt`;
69856
+ case 'after-error':
69857
+ return `Waiting ${formattedRemaining}${totalSuffix} before retrying after error`;
69858
+ }
69859
+ }
69860
+ /**
69861
+ * Sleeps the requested duration while refreshing the status message on a UI handle and the plain console.
69862
+ *
69863
+ * The wait kind is shown in the UI status so the user can distinguish `--wait-after-prompt`,
69864
+ * `--wait-between-prompts`, and `--wait-after-error` waits at a glance.
69865
+ *
69866
+ * @public exported from `@promptbook/cli`
69867
+ */
69868
+ async function sleepWithCountdown(options) {
69869
+ const { durationMs, waitKind, isRichUiEnabled, uiHandle } = options;
69870
+ if (durationMs <= 0) {
69871
+ return;
69872
+ }
69873
+ let remaining = durationMs;
69874
+ while (remaining > 0) {
69875
+ const statusMessage = describeCoderRunWait(waitKind, remaining, durationMs);
69876
+ if (!isRichUiEnabled) {
69877
+ console.info(colors.gray(`${statusMessage}...`));
69878
+ }
69879
+ else {
69880
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`${statusMessage}...`);
69881
+ }
69882
+ const sleepMs = Math.min(WAIT_COUNTDOWN_UPDATE_INTERVAL_MS, remaining);
69883
+ await new Promise((resolve) => setTimeout(resolve, sleepMs));
69884
+ remaining -= sleepMs;
69885
+ }
69886
+ }
69887
+
69768
69888
  /**
69769
69889
  * Resolves optional coding context provided inline or via a file path.
69770
69890
  */
@@ -71495,6 +71615,13 @@ async function writePromptFile(file) {
71495
71615
  await writeFile(file.path, content, 'utf-8');
71496
71616
  }
71497
71617
 
71618
+ /**
71619
+ * Maximum number of retry attempts performed after a prompt round throws an error.
71620
+ * After this many retries the round is finalized as failed.
71621
+ *
71622
+ * @private internal constant of `runPromptRound`
71623
+ */
71624
+ const MAX_RETRY_ATTEMPTS_AFTER_ERROR = 3;
71498
71625
  /**
71499
71626
  * Runs one prompt round from prompt construction through commit or failure logging.
71500
71627
  *
@@ -71527,54 +71654,101 @@ async function runPromptRound({ options, runner, runnerMetadata, nextPrompt, pro
71527
71654
  ? await captureChangedFilesSnapshot(process.cwd())
71528
71655
  : undefined;
71529
71656
  await withPromptRuntimeLog(scriptPath, async (logPath) => {
71530
- try {
71531
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
71532
- const result = await runPromptWithTestFeedback({
71533
- runner,
71534
- prompt: codexPrompt,
71535
- scriptPath,
71536
- projectPath: process.cwd(),
71537
- promptLabel,
71538
- testCommand: options.testCommand,
71539
- preserveArtifactsOnSuccess: options.preserveLogs,
71540
- logPath,
71541
- onAttemptStarted: (nextAttemptCount) => {
71542
- attemptCount = nextAttemptCount;
71543
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(nextAttemptCount);
71544
- },
71545
- waitForPauseCheckpoint: waitForRequestedPause,
71546
- });
71547
- await finalizeSuccessfulPromptRound({
71548
- options,
71549
- nextPrompt,
71550
- runnerMetadata,
71551
- promptExecutionStartedDate,
71552
- result,
71553
- commitMessage,
71554
- logPath,
71555
- roundChangedFilesSnapshot,
71556
- isRichUiEnabled,
71557
- progressDisplay,
71558
- uiHandle,
71559
- waitForRequestedPause,
71560
- });
71561
- }
71562
- catch (error) {
71563
- await finalizeFailedPromptRound({
71564
- nextPrompt,
71565
- runnerMetadata,
71566
- promptExecutionStartedDate,
71567
- attemptCount,
71568
- error,
71569
- options,
71570
- roundChangedFilesSnapshot,
71571
- uiHandle,
71572
- waitForRequestedPause,
71573
- });
71574
- throw error;
71657
+ let lastError;
71658
+ for (let errorRetryAttempt = 0; errorRetryAttempt <= MAX_RETRY_ATTEMPTS_AFTER_ERROR; errorRetryAttempt++) {
71659
+ try {
71660
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
71661
+ const result = await runPromptWithTestFeedback({
71662
+ runner,
71663
+ prompt: codexPrompt,
71664
+ scriptPath,
71665
+ projectPath: process.cwd(),
71666
+ promptLabel,
71667
+ testCommand: options.testCommand,
71668
+ preserveArtifactsOnSuccess: options.preserveLogs,
71669
+ logPath,
71670
+ onAttemptStarted: (nextAttemptCount) => {
71671
+ attemptCount = nextAttemptCount;
71672
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(nextAttemptCount);
71673
+ },
71674
+ waitForPauseCheckpoint: waitForRequestedPause,
71675
+ });
71676
+ await finalizeSuccessfulPromptRound({
71677
+ options,
71678
+ nextPrompt,
71679
+ runnerMetadata,
71680
+ promptExecutionStartedDate,
71681
+ result,
71682
+ commitMessage,
71683
+ logPath,
71684
+ roundChangedFilesSnapshot,
71685
+ isRichUiEnabled,
71686
+ progressDisplay,
71687
+ uiHandle,
71688
+ waitForRequestedPause,
71689
+ });
71690
+ return;
71691
+ }
71692
+ catch (error) {
71693
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
71694
+ lastError = error;
71695
+ if (errorRetryAttempt >= MAX_RETRY_ATTEMPTS_AFTER_ERROR) {
71696
+ break;
71697
+ }
71698
+ await waitAfterErrorBeforeRetry({
71699
+ options,
71700
+ error,
71701
+ attemptedRetries: errorRetryAttempt + 1,
71702
+ isRichUiEnabled,
71703
+ progressDisplay,
71704
+ uiHandle,
71705
+ waitForRequestedPause,
71706
+ });
71707
+ }
71575
71708
  }
71709
+ await finalizeFailedPromptRound({
71710
+ nextPrompt,
71711
+ runnerMetadata,
71712
+ promptExecutionStartedDate,
71713
+ attemptCount,
71714
+ error: lastError,
71715
+ options,
71716
+ roundChangedFilesSnapshot,
71717
+ uiHandle,
71718
+ waitForRequestedPause,
71719
+ });
71720
+ throw lastError;
71576
71721
  }, { preserveArtifactsOnSuccess: options.preserveLogs });
71577
71722
  }
71723
+ /**
71724
+ * Sleeps `options.waitAfterError` while keeping the rich UI and plain console in sync, then resets state for the retry.
71725
+ */
71726
+ async function waitAfterErrorBeforeRetry(options) {
71727
+ const { options: runOptions, error, attemptedRetries, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, } = options;
71728
+ const errorMessage = error instanceof Error ? error.message : String(error);
71729
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(errorMessage);
71730
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('waiting');
71731
+ if (!isRichUiEnabled) {
71732
+ console.warn(colors.yellow(`Prompt round failed (retry ${attemptedRetries}/${MAX_RETRY_ATTEMPTS_AFTER_ERROR}): ${errorMessage}`));
71733
+ }
71734
+ await waitForRequestedPause({
71735
+ checkpointLabel: 'waiting after error before retrying the prompt',
71736
+ phase: 'waiting',
71737
+ statusMessage: `Waiting before retry ${attemptedRetries}/${MAX_RETRY_ATTEMPTS_AFTER_ERROR} after error`,
71738
+ });
71739
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
71740
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
71741
+ await sleepWithCountdown({
71742
+ durationMs: runOptions.waitAfterError,
71743
+ waitKind: 'after-error',
71744
+ isRichUiEnabled,
71745
+ uiHandle,
71746
+ });
71747
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
71748
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
71749
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('running');
71750
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Retrying prompt (retry ${attemptedRetries}/${MAX_RETRY_ATTEMPTS_AFTER_ERROR})`);
71751
+ }
71578
71752
  /**
71579
71753
  * Updates UI or console output to reflect that the selected prompt is being processed.
71580
71754
  */
@@ -71755,7 +71929,8 @@ async function runCodexPrompts(providedOptions) {
71755
71929
  initializeRunUi(uiHandle, runner.name, actualRunnerModel, options);
71756
71930
  let hasShownUpcomingTasks = false;
71757
71931
  let hasWaitedForStart = false;
71758
- let hasRunAtLeastOneRound = false;
71932
+ let previousRoundStartTime;
71933
+ let previousRoundEndTime;
71759
71934
  while (just(true)) {
71760
71935
  if (options.autoPull && !options.dryRun) {
71761
71936
  await waitForRequestedPause({
@@ -71798,15 +71973,16 @@ async function runCodexPrompts(providedOptions) {
71798
71973
  const nextPrompt = promptQueueSnapshot.nextPrompt;
71799
71974
  const promptLabel = buildPromptLabelForDisplay(nextPrompt.file, nextPrompt.section);
71800
71975
  // Wait between prompt rounds (skipped for the first round)
71801
- if (hasRunAtLeastOneRound) {
71976
+ if (previousRoundStartTime !== undefined && previousRoundEndTime !== undefined) {
71802
71977
  await waitBetweenPromptRoundsIfNeeded({
71803
71978
  options,
71979
+ previousRoundStartTime,
71980
+ previousRoundEndTime,
71804
71981
  isRichUiEnabled,
71805
71982
  progressDisplay,
71806
71983
  uiHandle,
71807
71984
  });
71808
71985
  }
71809
- hasRunAtLeastOneRound = true;
71810
71986
  hasWaitedForStart = await waitForPromptConfirmationIfNeeded({
71811
71987
  options,
71812
71988
  nextPrompt,
@@ -71824,6 +72000,7 @@ async function runCodexPrompts(providedOptions) {
71824
72000
  });
71825
72001
  await ensureWorkingTreeClean();
71826
72002
  }
72003
+ const currentRoundStartTime = Date.now();
71827
72004
  await runPromptRound({
71828
72005
  options,
71829
72006
  runner,
@@ -71837,6 +72014,8 @@ async function runCodexPrompts(providedOptions) {
71837
72014
  uiHandle,
71838
72015
  waitForRequestedPause,
71839
72016
  });
72017
+ previousRoundStartTime = currentRoundStartTime;
72018
+ previousRoundEndTime = Date.now();
71840
72019
  }
71841
72020
  }
71842
72021
  finally {
@@ -72061,39 +72240,50 @@ async function waitForPromptConfirmationIfNeeded(options) {
72061
72240
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
72062
72241
  return true;
72063
72242
  }
72064
- /**
72065
- * Countdown update interval for the between-rounds wait display.
72066
- */
72067
- const WAIT_COUNTDOWN_UPDATE_INTERVAL_MS = 30000;
72068
72243
  /**
72069
72244
  * Polling interval when in keepAlive server mode and no runnable prompts are available.
72070
72245
  */
72071
72246
  const KEEP_ALIVE_POLL_INTERVAL_MS = 5000;
72072
72247
  /**
72073
- * Waits the configured time between prompt rounds to avoid hitting harness rate limits.
72074
- * Does nothing when `waitBetweenPrompts` is zero.
72248
+ * Waits between prompt rounds according to `--wait-between-prompts` (paced from the previous round's start)
72249
+ * and `--wait-after-prompt` (measured from the previous round's end).
72250
+ * Both phases are shown separately in the UI so the user can see which type of wait is active.
72075
72251
  */
72076
72252
  async function waitBetweenPromptRoundsIfNeeded(options) {
72077
- const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle } = options;
72078
- const { waitBetweenPrompts } = runOptions;
72079
- if (waitBetweenPrompts <= 0) {
72253
+ const { options: runOptions, previousRoundStartTime, previousRoundEndTime, isRichUiEnabled, progressDisplay, uiHandle, } = options;
72254
+ const { waitAfterPrompt, waitBetweenPrompts } = runOptions;
72255
+ if (waitAfterPrompt <= 0 && waitBetweenPrompts <= 0) {
72256
+ return;
72257
+ }
72258
+ const now = Date.now();
72259
+ const waitBetweenPromptsEndTime = previousRoundStartTime + waitBetweenPrompts;
72260
+ const waitAfterPromptEndTime = previousRoundEndTime + waitAfterPrompt;
72261
+ // Phase 1: pace from start of previous prompt (`--wait-between-prompts`)
72262
+ const phase1Duration = Math.max(0, waitBetweenPromptsEndTime - now);
72263
+ // Phase 2: rest from end of previous prompt (`--wait-after-prompt`)
72264
+ const phase2StartTime = Math.max(now, waitBetweenPromptsEndTime);
72265
+ const phase2Duration = Math.max(0, waitAfterPromptEndTime - phase2StartTime);
72266
+ if (phase1Duration <= 0 && phase2Duration <= 0) {
72080
72267
  return;
72081
72268
  }
72082
72269
  progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
72083
72270
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
72084
72271
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('waiting');
72085
- let remaining = waitBetweenPrompts;
72086
- while (remaining > 0) {
72087
- const statusMessage = `Waiting ${formatDurationMs(remaining)} before next prompt to avoid rate limits...`;
72088
- if (!isRichUiEnabled) {
72089
- console.info(colors.gray(statusMessage));
72090
- }
72091
- else {
72092
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(statusMessage);
72093
- }
72094
- const sleepMs = Math.min(WAIT_COUNTDOWN_UPDATE_INTERVAL_MS, remaining);
72095
- await new Promise((resolve) => setTimeout(resolve, sleepMs));
72096
- remaining -= sleepMs;
72272
+ if (phase1Duration > 0) {
72273
+ await sleepWithCountdown({
72274
+ durationMs: phase1Duration,
72275
+ waitKind: 'between-prompts',
72276
+ isRichUiEnabled,
72277
+ uiHandle,
72278
+ });
72279
+ }
72280
+ if (phase2Duration > 0) {
72281
+ await sleepWithCountdown({
72282
+ durationMs: phase2Duration,
72283
+ waitKind: 'after-prompt',
72284
+ isRichUiEnabled,
72285
+ uiHandle,
72286
+ });
72097
72287
  }
72098
72288
  progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
72099
72289
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();