@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/src/version.ts CHANGED
@@ -16,11 +16,11 @@ export const BOOK_LANGUAGE_VERSION: string_semantic_version = '2.0.0';
16
16
  * @generated
17
17
  * @see https://github.com/webgptorg/promptbook
18
18
  */
19
- export const PROMPTBOOK_ENGINE_VERSION: string_promptbook_version = '0.112.0-127';
19
+ export const PROMPTBOOK_ENGINE_VERSION: string_promptbook_version = '0.112.0-129';
20
20
 
21
21
  /**
22
22
  * Represents the version string of the Promptbook engine.
23
- * It follows semantic versioning (e.g., `0.112.0-126`).
23
+ * It follows semantic versioning (e.g., `0.112.0-128`).
24
24
  *
25
25
  * @generated
26
26
  */
package/src/versions.txt CHANGED
@@ -1156,4 +1156,5 @@
1156
1156
  0.112.0-122
1157
1157
  0.112.0-123
1158
1158
  0.112.0-126
1159
- 0.112.0-127
1159
+ 0.112.0-128
1160
+ 0.112.0-129
package/umd/index.umd.js CHANGED
@@ -63,7 +63,7 @@
63
63
  * @generated
64
64
  * @see https://github.com/webgptorg/promptbook
65
65
  */
66
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-127';
66
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-129';
67
67
  /**
68
68
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
69
69
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -5197,7 +5197,9 @@
5197
5197
  noUi: options.noUi,
5198
5198
  thinkingLevel: options.thinkingLevel,
5199
5199
  waitForUser: false,
5200
+ waitAfterPrompt: 0,
5200
5201
  waitBetweenPrompts: 0,
5202
+ waitAfterError: 0,
5201
5203
  noCommit: options.noCommit,
5202
5204
  ignoreGitChanges: options.ignoreGitChanges,
5203
5205
  normalizeLineEndings: options.normalizeLineEndings,
@@ -39927,6 +39929,27 @@
39927
39929
  return parts.join(' ');
39928
39930
  }
39929
39931
 
39932
+ /**
39933
+ * Default wait duration applied before retrying a prompt round after an error (10 minutes).
39934
+ *
39935
+ * @private internal constant of `ptbk coder` wait handling
39936
+ */
39937
+ const DEFAULT_WAIT_AFTER_ERROR_MS$1 = 10 * 60 * 1000;
39938
+ /**
39939
+ * Parses an optional Commander duration string and returns the resolved milliseconds.
39940
+ *
39941
+ * Returns `defaultMs` when the flag was not provided or was provided without a non-empty value.
39942
+ *
39943
+ * @private internal utility of `ptbk coder` wait handling
39944
+ */
39945
+ function parseOptionalWaitDuration(value, defaultMs) {
39946
+ if (typeof value !== 'string' || value === '') {
39947
+ return defaultMs;
39948
+ }
39949
+ return parseDuration(value);
39950
+ }
39951
+ // Note: [💞] Ignore a discrepancy between file name and entity name
39952
+
39930
39953
  /**
39931
39954
  * Initializes `coder run` command for Promptbook CLI utilities
39932
39955
  *
@@ -39960,31 +39983,46 @@
39960
39983
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
39961
39984
  addPromptRunnerExecutionOptions(command);
39962
39985
  command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption$1, 0);
39963
- command.option('--wait <duration>', _spaceTrim.spaceTrim(`
39964
- Wait this long between prompt rounds to avoid hitting rate limits of the harness.
39965
- Accepts durations like 1h, 30m, 5s.
39986
+ command.option('--wait-after-prompt <duration>', _spaceTrim.spaceTrim(`
39987
+ Wait this long after each prompt has been implemented, verified and committed before starting the next prompt.
39988
+ Accepts durations like 1h, 30m, 5s. Defaults to 0 (no wait).
39989
+ `));
39990
+ command.option('--wait-between-prompts <duration>', _spaceTrim.spaceTrim(`
39991
+ Pace prompts so that each next prompt starts at least this long after the previous one began.
39992
+ If the previous prompt already took longer than this, the next prompt starts immediately.
39993
+ Accepts durations like 1h, 30m, 5s. Defaults to 0 (no pacing).
39994
+ `));
39995
+ command.option('--wait-after-error <duration>', _spaceTrim.spaceTrim(`
39996
+ Wait this long before retrying a prompt after an error occurs (up to 3 retries before giving up).
39997
+ Accepts durations like 1h, 30m, 5s. Defaults to 10m.
39966
39998
  `));
39967
39999
  // Note: --no-auto disables the default auto behaviour and waits for user confirmation before each prompt
39968
40000
  command.option('--no-auto', 'Wait for user confirmation before each prompt instead of running automatically through the queue');
39969
40001
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
39970
40002
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
39971
40003
  command.action(handleActionErrors(async (cliOptions) => {
39972
- const { dryRun, agent, context, test, preserveLogs, priority, wait, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
40004
+ const { dryRun, agent, context, test, preserveLogs, priority, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
39973
40005
  const testCommand = normalizeCommandOptionValue$1(test);
39974
40006
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
39975
40007
  isAgentRequired: !dryRun,
39976
40008
  });
39977
- // [1] Parse the --wait and --no-auto options:
39978
- // default: run automatically through the queue (no waiting)
40009
+ // [1] Parse the wait options and --no-auto:
40010
+ // default: run automatically through the queue (no waiting between prompts)
39979
40011
  // --no-auto: wait for user confirmation before each prompt (interactive mode)
39980
- // --wait <duration>: wait that long between prompt rounds to avoid rate limits
40012
+ // --wait-after-prompt: pause after a successful round before starting the next prompt
40013
+ // --wait-between-prompts: pace from start of one prompt to start of next
40014
+ // --wait-after-error: wait before retrying after an error (default 10m)
39981
40015
  const waitForUser = !auto;
39982
- const waitBetweenPrompts = typeof wait === 'string' && wait !== '' ? parseDuration(wait) : 0;
40016
+ const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
40017
+ const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
40018
+ const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
39983
40019
  // Convert commander options to RunOptions format
39984
40020
  const runOptions = {
39985
40021
  dryRun,
39986
40022
  waitForUser,
40023
+ waitAfterPrompt,
39987
40024
  waitBetweenPrompts,
40025
+ waitAfterError,
39988
40026
  noCommit: runnerOptions.noCommit,
39989
40027
  ignoreGitChanges: runnerOptions.ignoreGitChanges,
39990
40028
  agentName: runnerOptions.agentName,
@@ -40092,28 +40130,41 @@
40092
40130
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
40093
40131
  addPromptRunnerExecutionOptions(command);
40094
40132
  command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption, 0);
40095
- command.option('--wait <duration>', _spaceTrim.spaceTrim(`
40096
- Wait this long between prompt rounds to avoid hitting rate limits of the harness.
40097
- Accepts durations like 1h, 30m, 5s.
40133
+ command.option('--wait-after-prompt <duration>', _spaceTrim.spaceTrim(`
40134
+ Wait this long after each prompt has been implemented, verified and committed before starting the next prompt.
40135
+ Accepts durations like 1h, 30m, 5s. Defaults to 0 (no wait).
40136
+ `));
40137
+ command.option('--wait-between-prompts <duration>', _spaceTrim.spaceTrim(`
40138
+ Pace prompts so that each next prompt starts at least this long after the previous one began.
40139
+ If the previous prompt already took longer than this, the next prompt starts immediately.
40140
+ Accepts durations like 1h, 30m, 5s. Defaults to 0 (no pacing).
40141
+ `));
40142
+ command.option('--wait-after-error <duration>', _spaceTrim.spaceTrim(`
40143
+ Wait this long before retrying a prompt after an error occurs (up to 3 retries before giving up).
40144
+ Accepts durations like 1h, 30m, 5s. Defaults to 10m.
40098
40145
  `));
40099
40146
  command.option('--no-auto', 'Wait for user confirmation before each prompt instead of running automatically through the queue');
40100
40147
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
40101
40148
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
40102
40149
  command.action(handleActionErrors(async (cliOptions) => {
40103
- const { port: rawPort, dryRun, agent, context, test, preserveLogs, priority, wait, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
40150
+ const { port: rawPort, dryRun, agent, context, test, preserveLogs, priority, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
40104
40151
  const port = parseCoderServerPort(rawPort);
40105
40152
  const testCommand = normalizeCommandOptionValue(test);
40106
40153
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
40107
40154
  isAgentRequired: !dryRun,
40108
40155
  });
40109
- // [1] Parse the --wait and --no-auto options (same logic as `coder run`)
40156
+ // [1] Parse the wait options and --no-auto (same logic as `coder run`)
40110
40157
  const waitForUser = !auto;
40111
- const waitBetweenPrompts = typeof wait === 'string' && wait !== '' ? parseDuration(wait) : 0;
40158
+ const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
40159
+ const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
40160
+ const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
40112
40161
  const runOptions = {
40113
40162
  port,
40114
40163
  dryRun,
40115
40164
  waitForUser,
40165
+ waitAfterPrompt,
40116
40166
  waitBetweenPrompts,
40167
+ waitAfterError,
40117
40168
  noCommit: runnerOptions.noCommit,
40118
40169
  ignoreGitChanges: runnerOptions.ignoreGitChanges,
40119
40170
  agentName: runnerOptions.agentName,
@@ -63588,7 +63639,9 @@
63588
63639
  noUi: options.noUi,
63589
63640
  thinkingLevel: options.thinkingLevel,
63590
63641
  waitForUser: false,
63642
+ waitAfterPrompt: 0,
63591
63643
  waitBetweenPrompts: 0,
63644
+ waitAfterError: 0,
63592
63645
  noCommit: true,
63593
63646
  ignoreGitChanges: true,
63594
63647
  normalizeLineEndings: false,
@@ -69423,10 +69476,14 @@
69423
69476
  findUnwrittenPrompts: findUnwrittenPrompts
69424
69477
  });
69425
69478
 
69479
+ /**
69480
+ * Default wait duration applied before retrying a failed prompt round.
69481
+ */
69482
+ const DEFAULT_WAIT_AFTER_ERROR_MS = 10 * 60 * 1000;
69426
69483
  /**
69427
69484
  * CLI usage text for this script.
69428
69485
  */
69429
- 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]';
69486
+ 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]';
69430
69487
  /**
69431
69488
  * Top-level flags supported by this command.
69432
69489
  */
@@ -69443,7 +69500,9 @@
69443
69500
  '--allow-credits',
69444
69501
  '--auto-migrate',
69445
69502
  '--allow-destructive-auto-migrate',
69446
- '--wait',
69503
+ '--wait-after-prompt',
69504
+ '--wait-between-prompts',
69505
+ '--wait-after-error',
69447
69506
  '--no-auto',
69448
69507
  '--no-commit',
69449
69508
  '--ignore-git-changes',
@@ -69487,15 +69546,9 @@
69487
69546
  // --no-auto: wait for user confirmation before each prompt (interactive mode)
69488
69547
  // --wait 1h: wait 1h between prompt rounds to avoid rate limits
69489
69548
  const waitForUser = args.includes('--no-auto');
69490
- let waitBetweenPrompts = 0;
69491
- const hasWaitFlag = args.includes('--wait');
69492
- const waitValue = readOptionValue(args, '--wait');
69493
- if (hasWaitFlag) {
69494
- if (waitValue === undefined || waitValue.startsWith('-')) {
69495
- exitWithUsageError('Missing value for --wait. Use a duration like 1h, 30m, 5s.');
69496
- }
69497
- waitBetweenPrompts = parseDuration(waitValue);
69498
- }
69549
+ const waitAfterPrompt = parseWaitOption(args, '--wait-after-prompt', 0);
69550
+ const waitBetweenPrompts = parseWaitOption(args, '--wait-between-prompts', 0);
69551
+ const waitAfterError = parseWaitOption(args, '--wait-after-error', DEFAULT_WAIT_AFTER_ERROR_MS);
69499
69552
  let thinkingLevel;
69500
69553
  if (hasTestCommandFlag && testCommand === undefined) {
69501
69554
  exitWithUsageError('Missing value for --test. Use a shell command such as `npm run test` and quote it when it contains top-level CLI flags.');
@@ -69515,7 +69568,9 @@
69515
69568
  return {
69516
69569
  dryRun,
69517
69570
  waitForUser,
69571
+ waitAfterPrompt,
69518
69572
  waitBetweenPrompts,
69573
+ waitAfterError,
69519
69574
  noCommit,
69520
69575
  ignoreGitChanges,
69521
69576
  normalizeLineEndings,
@@ -69534,6 +69589,19 @@
69534
69589
  priority,
69535
69590
  };
69536
69591
  }
69592
+ /**
69593
+ * Reads a duration-typed CLI flag, applying the provided default when the flag is absent.
69594
+ */
69595
+ function parseWaitOption(args, flag, defaultMs) {
69596
+ if (!args.includes(flag)) {
69597
+ return defaultMs;
69598
+ }
69599
+ const value = readOptionValue(args, flag);
69600
+ if (value === undefined || value.startsWith('-')) {
69601
+ exitWithUsageError(`Missing value for ${flag}. Use a duration like 1h, 30m, 5s.`);
69602
+ }
69603
+ return parseDuration(value);
69604
+ }
69537
69605
  /**
69538
69606
  * Reads a value of a CLI option that follows a given flag.
69539
69607
  */
@@ -69770,6 +69838,58 @@
69770
69838
  return createAgentRunnerSystemMessage(agentSource);
69771
69839
  }
69772
69840
 
69841
+ /**
69842
+ * Refresh interval used by the countdown display while waiting.
69843
+ *
69844
+ * @private internal constant of `sleepWithCountdown`
69845
+ */
69846
+ const WAIT_COUNTDOWN_UPDATE_INTERVAL_MS = 30000;
69847
+ /**
69848
+ * Returns the human-readable status message used for one wait kind.
69849
+ *
69850
+ * @private internal utility of `sleepWithCountdown`
69851
+ */
69852
+ function describeCoderRunWait(waitKind, remainingMs, totalMs) {
69853
+ const formattedRemaining = formatDurationMs(remainingMs);
69854
+ const formattedTotal = totalMs !== undefined ? formatDurationMs(totalMs) : undefined;
69855
+ const totalSuffix = formattedTotal !== undefined ? ` of ${formattedTotal}` : '';
69856
+ switch (waitKind) {
69857
+ case 'between-prompts':
69858
+ return `Waiting ${formattedRemaining}${totalSuffix} between prompts (paced from previous start)`;
69859
+ case 'after-prompt':
69860
+ return `Waiting ${formattedRemaining}${totalSuffix} after previous prompt`;
69861
+ case 'after-error':
69862
+ return `Waiting ${formattedRemaining}${totalSuffix} before retrying after error`;
69863
+ }
69864
+ }
69865
+ /**
69866
+ * Sleeps the requested duration while refreshing the status message on a UI handle and the plain console.
69867
+ *
69868
+ * The wait kind is shown in the UI status so the user can distinguish `--wait-after-prompt`,
69869
+ * `--wait-between-prompts`, and `--wait-after-error` waits at a glance.
69870
+ *
69871
+ * @public exported from `@promptbook/cli`
69872
+ */
69873
+ async function sleepWithCountdown(options) {
69874
+ const { durationMs, waitKind, isRichUiEnabled, uiHandle } = options;
69875
+ if (durationMs <= 0) {
69876
+ return;
69877
+ }
69878
+ let remaining = durationMs;
69879
+ while (remaining > 0) {
69880
+ const statusMessage = describeCoderRunWait(waitKind, remaining, durationMs);
69881
+ if (!isRichUiEnabled) {
69882
+ console.info(colors__default["default"].gray(`${statusMessage}...`));
69883
+ }
69884
+ else {
69885
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`${statusMessage}...`);
69886
+ }
69887
+ const sleepMs = Math.min(WAIT_COUNTDOWN_UPDATE_INTERVAL_MS, remaining);
69888
+ await new Promise((resolve) => setTimeout(resolve, sleepMs));
69889
+ remaining -= sleepMs;
69890
+ }
69891
+ }
69892
+
69773
69893
  /**
69774
69894
  * Resolves optional coding context provided inline or via a file path.
69775
69895
  */
@@ -71500,6 +71620,13 @@
71500
71620
  await promises.writeFile(file.path, content, 'utf-8');
71501
71621
  }
71502
71622
 
71623
+ /**
71624
+ * Maximum number of retry attempts performed after a prompt round throws an error.
71625
+ * After this many retries the round is finalized as failed.
71626
+ *
71627
+ * @private internal constant of `runPromptRound`
71628
+ */
71629
+ const MAX_RETRY_ATTEMPTS_AFTER_ERROR = 3;
71503
71630
  /**
71504
71631
  * Runs one prompt round from prompt construction through commit or failure logging.
71505
71632
  *
@@ -71532,54 +71659,101 @@
71532
71659
  ? await captureChangedFilesSnapshot(process.cwd())
71533
71660
  : undefined;
71534
71661
  await withPromptRuntimeLog(scriptPath, async (logPath) => {
71535
- try {
71536
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
71537
- const result = await runPromptWithTestFeedback({
71538
- runner,
71539
- prompt: codexPrompt,
71540
- scriptPath,
71541
- projectPath: process.cwd(),
71542
- promptLabel,
71543
- testCommand: options.testCommand,
71544
- preserveArtifactsOnSuccess: options.preserveLogs,
71545
- logPath,
71546
- onAttemptStarted: (nextAttemptCount) => {
71547
- attemptCount = nextAttemptCount;
71548
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(nextAttemptCount);
71549
- },
71550
- waitForPauseCheckpoint: waitForRequestedPause,
71551
- });
71552
- await finalizeSuccessfulPromptRound({
71553
- options,
71554
- nextPrompt,
71555
- runnerMetadata,
71556
- promptExecutionStartedDate,
71557
- result,
71558
- commitMessage,
71559
- logPath,
71560
- roundChangedFilesSnapshot,
71561
- isRichUiEnabled,
71562
- progressDisplay,
71563
- uiHandle,
71564
- waitForRequestedPause,
71565
- });
71566
- }
71567
- catch (error) {
71568
- await finalizeFailedPromptRound({
71569
- nextPrompt,
71570
- runnerMetadata,
71571
- promptExecutionStartedDate,
71572
- attemptCount,
71573
- error,
71574
- options,
71575
- roundChangedFilesSnapshot,
71576
- uiHandle,
71577
- waitForRequestedPause,
71578
- });
71579
- throw error;
71662
+ let lastError;
71663
+ for (let errorRetryAttempt = 0; errorRetryAttempt <= MAX_RETRY_ATTEMPTS_AFTER_ERROR; errorRetryAttempt++) {
71664
+ try {
71665
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
71666
+ const result = await runPromptWithTestFeedback({
71667
+ runner,
71668
+ prompt: codexPrompt,
71669
+ scriptPath,
71670
+ projectPath: process.cwd(),
71671
+ promptLabel,
71672
+ testCommand: options.testCommand,
71673
+ preserveArtifactsOnSuccess: options.preserveLogs,
71674
+ logPath,
71675
+ onAttemptStarted: (nextAttemptCount) => {
71676
+ attemptCount = nextAttemptCount;
71677
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(nextAttemptCount);
71678
+ },
71679
+ waitForPauseCheckpoint: waitForRequestedPause,
71680
+ });
71681
+ await finalizeSuccessfulPromptRound({
71682
+ options,
71683
+ nextPrompt,
71684
+ runnerMetadata,
71685
+ promptExecutionStartedDate,
71686
+ result,
71687
+ commitMessage,
71688
+ logPath,
71689
+ roundChangedFilesSnapshot,
71690
+ isRichUiEnabled,
71691
+ progressDisplay,
71692
+ uiHandle,
71693
+ waitForRequestedPause,
71694
+ });
71695
+ return;
71696
+ }
71697
+ catch (error) {
71698
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
71699
+ lastError = error;
71700
+ if (errorRetryAttempt >= MAX_RETRY_ATTEMPTS_AFTER_ERROR) {
71701
+ break;
71702
+ }
71703
+ await waitAfterErrorBeforeRetry({
71704
+ options,
71705
+ error,
71706
+ attemptedRetries: errorRetryAttempt + 1,
71707
+ isRichUiEnabled,
71708
+ progressDisplay,
71709
+ uiHandle,
71710
+ waitForRequestedPause,
71711
+ });
71712
+ }
71580
71713
  }
71714
+ await finalizeFailedPromptRound({
71715
+ nextPrompt,
71716
+ runnerMetadata,
71717
+ promptExecutionStartedDate,
71718
+ attemptCount,
71719
+ error: lastError,
71720
+ options,
71721
+ roundChangedFilesSnapshot,
71722
+ uiHandle,
71723
+ waitForRequestedPause,
71724
+ });
71725
+ throw lastError;
71581
71726
  }, { preserveArtifactsOnSuccess: options.preserveLogs });
71582
71727
  }
71728
+ /**
71729
+ * Sleeps `options.waitAfterError` while keeping the rich UI and plain console in sync, then resets state for the retry.
71730
+ */
71731
+ async function waitAfterErrorBeforeRetry(options) {
71732
+ const { options: runOptions, error, attemptedRetries, isRichUiEnabled, progressDisplay, uiHandle, waitForRequestedPause, } = options;
71733
+ const errorMessage = error instanceof Error ? error.message : String(error);
71734
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(errorMessage);
71735
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('waiting');
71736
+ if (!isRichUiEnabled) {
71737
+ console.warn(colors__default["default"].yellow(`Prompt round failed (retry ${attemptedRetries}/${MAX_RETRY_ATTEMPTS_AFTER_ERROR}): ${errorMessage}`));
71738
+ }
71739
+ await waitForRequestedPause({
71740
+ checkpointLabel: 'waiting after error before retrying the prompt',
71741
+ phase: 'waiting',
71742
+ statusMessage: `Waiting before retry ${attemptedRetries}/${MAX_RETRY_ATTEMPTS_AFTER_ERROR} after error`,
71743
+ });
71744
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
71745
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
71746
+ await sleepWithCountdown({
71747
+ durationMs: runOptions.waitAfterError,
71748
+ waitKind: 'after-error',
71749
+ isRichUiEnabled,
71750
+ uiHandle,
71751
+ });
71752
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
71753
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
71754
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('running');
71755
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Retrying prompt (retry ${attemptedRetries}/${MAX_RETRY_ATTEMPTS_AFTER_ERROR})`);
71756
+ }
71583
71757
  /**
71584
71758
  * Updates UI or console output to reflect that the selected prompt is being processed.
71585
71759
  */
@@ -71760,7 +71934,8 @@
71760
71934
  initializeRunUi(uiHandle, runner.name, actualRunnerModel, options);
71761
71935
  let hasShownUpcomingTasks = false;
71762
71936
  let hasWaitedForStart = false;
71763
- let hasRunAtLeastOneRound = false;
71937
+ let previousRoundStartTime;
71938
+ let previousRoundEndTime;
71764
71939
  while (just(true)) {
71765
71940
  if (options.autoPull && !options.dryRun) {
71766
71941
  await waitForRequestedPause({
@@ -71803,15 +71978,16 @@
71803
71978
  const nextPrompt = promptQueueSnapshot.nextPrompt;
71804
71979
  const promptLabel = buildPromptLabelForDisplay(nextPrompt.file, nextPrompt.section);
71805
71980
  // Wait between prompt rounds (skipped for the first round)
71806
- if (hasRunAtLeastOneRound) {
71981
+ if (previousRoundStartTime !== undefined && previousRoundEndTime !== undefined) {
71807
71982
  await waitBetweenPromptRoundsIfNeeded({
71808
71983
  options,
71984
+ previousRoundStartTime,
71985
+ previousRoundEndTime,
71809
71986
  isRichUiEnabled,
71810
71987
  progressDisplay,
71811
71988
  uiHandle,
71812
71989
  });
71813
71990
  }
71814
- hasRunAtLeastOneRound = true;
71815
71991
  hasWaitedForStart = await waitForPromptConfirmationIfNeeded({
71816
71992
  options,
71817
71993
  nextPrompt,
@@ -71829,6 +72005,7 @@
71829
72005
  });
71830
72006
  await ensureWorkingTreeClean();
71831
72007
  }
72008
+ const currentRoundStartTime = Date.now();
71832
72009
  await runPromptRound({
71833
72010
  options,
71834
72011
  runner,
@@ -71842,6 +72019,8 @@
71842
72019
  uiHandle,
71843
72020
  waitForRequestedPause,
71844
72021
  });
72022
+ previousRoundStartTime = currentRoundStartTime;
72023
+ previousRoundEndTime = Date.now();
71845
72024
  }
71846
72025
  }
71847
72026
  finally {
@@ -72066,39 +72245,50 @@
72066
72245
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
72067
72246
  return true;
72068
72247
  }
72069
- /**
72070
- * Countdown update interval for the between-rounds wait display.
72071
- */
72072
- const WAIT_COUNTDOWN_UPDATE_INTERVAL_MS = 30000;
72073
72248
  /**
72074
72249
  * Polling interval when in keepAlive server mode and no runnable prompts are available.
72075
72250
  */
72076
72251
  const KEEP_ALIVE_POLL_INTERVAL_MS = 5000;
72077
72252
  /**
72078
- * Waits the configured time between prompt rounds to avoid hitting harness rate limits.
72079
- * Does nothing when `waitBetweenPrompts` is zero.
72253
+ * Waits between prompt rounds according to `--wait-between-prompts` (paced from the previous round's start)
72254
+ * and `--wait-after-prompt` (measured from the previous round's end).
72255
+ * Both phases are shown separately in the UI so the user can see which type of wait is active.
72080
72256
  */
72081
72257
  async function waitBetweenPromptRoundsIfNeeded(options) {
72082
- const { options: runOptions, isRichUiEnabled, progressDisplay, uiHandle } = options;
72083
- const { waitBetweenPrompts } = runOptions;
72084
- if (waitBetweenPrompts <= 0) {
72258
+ const { options: runOptions, previousRoundStartTime, previousRoundEndTime, isRichUiEnabled, progressDisplay, uiHandle, } = options;
72259
+ const { waitAfterPrompt, waitBetweenPrompts } = runOptions;
72260
+ if (waitAfterPrompt <= 0 && waitBetweenPrompts <= 0) {
72261
+ return;
72262
+ }
72263
+ const now = Date.now();
72264
+ const waitBetweenPromptsEndTime = previousRoundStartTime + waitBetweenPrompts;
72265
+ const waitAfterPromptEndTime = previousRoundEndTime + waitAfterPrompt;
72266
+ // Phase 1: pace from start of previous prompt (`--wait-between-prompts`)
72267
+ const phase1Duration = Math.max(0, waitBetweenPromptsEndTime - now);
72268
+ // Phase 2: rest from end of previous prompt (`--wait-after-prompt`)
72269
+ const phase2StartTime = Math.max(now, waitBetweenPromptsEndTime);
72270
+ const phase2Duration = Math.max(0, waitAfterPromptEndTime - phase2StartTime);
72271
+ if (phase1Duration <= 0 && phase2Duration <= 0) {
72085
72272
  return;
72086
72273
  }
72087
72274
  progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
72088
72275
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
72089
72276
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('waiting');
72090
- let remaining = waitBetweenPrompts;
72091
- while (remaining > 0) {
72092
- const statusMessage = `Waiting ${formatDurationMs(remaining)} before next prompt to avoid rate limits...`;
72093
- if (!isRichUiEnabled) {
72094
- console.info(colors__default["default"].gray(statusMessage));
72095
- }
72096
- else {
72097
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(statusMessage);
72098
- }
72099
- const sleepMs = Math.min(WAIT_COUNTDOWN_UPDATE_INTERVAL_MS, remaining);
72100
- await new Promise((resolve) => setTimeout(resolve, sleepMs));
72101
- remaining -= sleepMs;
72277
+ if (phase1Duration > 0) {
72278
+ await sleepWithCountdown({
72279
+ durationMs: phase1Duration,
72280
+ waitKind: 'between-prompts',
72281
+ isRichUiEnabled,
72282
+ uiHandle,
72283
+ });
72284
+ }
72285
+ if (phase2Duration > 0) {
72286
+ await sleepWithCountdown({
72287
+ durationMs: phase2Duration,
72288
+ waitKind: 'after-prompt',
72289
+ isRichUiEnabled,
72290
+ uiHandle,
72291
+ });
72102
72292
  }
72103
72293
  progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
72104
72294
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();