@promptbook/cli 0.112.0-131 → 0.112.0-133

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.
Files changed (100) hide show
  1. package/apps/agents-server/src/app/admin/api-tokens/ApiTokensClient.tsx +4 -1
  2. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +3 -0
  3. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackTable.tsx +13 -8
  4. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +3 -0
  5. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryTable.tsx +13 -8
  6. package/apps/agents-server/src/app/admin/custom-css/CustomCssClient.tsx +4 -0
  7. package/apps/agents-server/src/app/admin/custom-css/CustomCssEditorPanel.tsx +7 -1
  8. package/apps/agents-server/src/app/admin/custom-css/CustomCssFilesPanel.tsx +7 -1
  9. package/apps/agents-server/src/app/admin/custom-js/CustomJsClient.tsx +11 -0
  10. package/apps/agents-server/src/app/admin/custom-js/CustomJsEditorPanel.tsx +7 -1
  11. package/apps/agents-server/src/app/admin/custom-js/CustomJsFilesPanel.tsx +7 -1
  12. package/apps/agents-server/src/app/admin/files/FilesGalleryClient.tsx +4 -0
  13. package/apps/agents-server/src/app/admin/files/FilesGalleryGrid.tsx +9 -11
  14. package/apps/agents-server/src/app/admin/files/FilesGalleryTable.tsx +9 -8
  15. package/apps/agents-server/src/app/admin/images/ImagesGalleryClient.tsx +4 -0
  16. package/apps/agents-server/src/app/admin/images/ImagesGalleryGrid.tsx +16 -11
  17. package/apps/agents-server/src/app/admin/images/ImagesGalleryTable.tsx +18 -9
  18. package/apps/agents-server/src/app/admin/login-methods/shibboleth/page.tsx +9 -9
  19. package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +7 -6
  20. package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +11 -10
  21. package/apps/agents-server/src/app/admin/task-manager/TaskManagerClient.tsx +4 -2
  22. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTaskRow.tsx +14 -15
  23. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTasksCard.tsx +4 -1
  24. package/apps/agents-server/src/app/admin/task-manager/useTaskManagerState.ts +6 -9
  25. package/apps/agents-server/src/app/admin/update/CustomCommitPicker.tsx +258 -0
  26. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +305 -108
  27. package/apps/agents-server/src/app/admin/usage/UsageClient.tsx +4 -2
  28. package/apps/agents-server/src/app/admin/usage/UsageClientAnalyticsPanels.tsx +6 -4
  29. package/apps/agents-server/src/app/admin/usage/UsageClientFormatting.ts +6 -12
  30. package/apps/agents-server/src/app/admin/usage/UsageClientTimelineChart.tsx +6 -3
  31. package/apps/agents-server/src/app/admin/usage/useUsageClientState.ts +12 -4
  32. package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +8 -3
  33. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +6 -20
  34. package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorHistory.ts +9 -2
  35. package/apps/agents-server/src/app/agents/[agentName]/chat/useAgentChatHistoryClientState.ts +2 -15
  36. package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +6 -1
  37. package/apps/agents-server/src/app/agents/[agentName]/integration/CalendarIntegrationSection.tsx +23 -5
  38. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsClient.tsx +4 -2
  39. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsFiltersCard.tsx +9 -4
  40. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableCard.tsx +4 -1
  41. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableRow.tsx +10 -3
  42. package/apps/agents-server/src/app/api/admin/update/commits/route.ts +35 -0
  43. package/apps/agents-server/src/app/api/admin/update/route.ts +10 -2
  44. package/apps/agents-server/src/app/api/internal/user-chat-jobs/run/route.ts +14 -2
  45. package/apps/agents-server/src/app/dashboard/page.tsx +11 -16
  46. package/apps/agents-server/src/app/layout.tsx +12 -3
  47. package/apps/agents-server/src/app/swagger/SwaggerApiKeysPanel.tsx +4 -1
  48. package/apps/agents-server/src/app/system/user-memory/UserMemoryClient.tsx +4 -2
  49. package/apps/agents-server/src/app/system/user-wallet/UserWalletClient.tsx +3 -0
  50. package/apps/agents-server/src/app/system/user-wallet/UserWalletRecordsTable.tsx +5 -1
  51. package/apps/agents-server/src/app/system/utilities/mocked-chats/MockedChatsEditorClient.tsx +3 -0
  52. package/apps/agents-server/src/app/system/utilities/mocked-chats/MockedChatsEditorSidebar.tsx +7 -8
  53. package/apps/agents-server/src/components/Footer/Footer.tsx +3 -2
  54. package/apps/agents-server/src/components/Footer/getCommitFooterEmoji.ts +112 -0
  55. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -2
  56. package/apps/agents-server/src/components/UsersList/UsersList.tsx +4 -2
  57. package/apps/agents-server/src/languages/translations/czech.yaml +1 -1
  58. package/apps/agents-server/src/languages/translations/english.yaml +1 -1
  59. package/apps/agents-server/src/middleware/applyEmbeddingHeader.ts +4 -2
  60. package/apps/agents-server/src/middleware/contentSecurityPolicy.ts +97 -0
  61. package/apps/agents-server/src/tools/createAgentProgressTools.ts +8 -6
  62. package/apps/agents-server/src/utils/localization/formatServerLanguageHumanReadableDate.ts +70 -0
  63. package/apps/agents-server/src/utils/localization/getRequestServerLanguage.ts +32 -0
  64. package/apps/agents-server/src/utils/userChat/createRunUserChatJobExecutionContext.ts +17 -0
  65. package/apps/agents-server/src/utils/userChat/createRunUserChatJobPersistenceController.ts +0 -21
  66. package/apps/agents-server/src/utils/userChat/persistUserChatJobProgressCard.ts +31 -0
  67. package/apps/agents-server/src/utils/userChat/retryUserChatJob.ts +0 -4
  68. package/apps/agents-server/src/utils/userChat/runUserChatJob.ts +29 -0
  69. package/apps/agents-server/src/utils/userChat/userChatMessageLifecycle.ts +2 -2
  70. package/apps/agents-server/src/utils/userChat/userChatProgressCard.ts +99 -401
  71. package/apps/agents-server/src/utils/vpsSelfUpdate.ts +625 -51
  72. package/esm/index.es.js +252 -41
  73. package/esm/index.es.js.map +1 -1
  74. package/esm/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +5 -1
  75. package/esm/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +7 -0
  76. package/esm/scripts/run-codex-prompts/common/coderRunEstimateCache.d.ts +31 -0
  77. package/esm/scripts/run-codex-prompts/common/progressFormatting.d.ts +2 -0
  78. package/esm/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +3 -1
  79. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +7 -0
  80. package/esm/src/book-3.0/cliAgentEnv.d.ts +1 -1
  81. package/esm/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  82. package/esm/src/version.d.ts +1 -1
  83. package/package.json +1 -1
  84. package/src/book-3.0/cliAgentEnv.ts +1 -1
  85. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +1 -1
  86. package/src/cli/cli-commands/coder/ThinkingLevel.ts +3 -1
  87. package/src/other/templates/getTemplatesPipelineCollection.ts +681 -766
  88. package/src/version.ts +2 -2
  89. package/src/versions.txt +2 -0
  90. package/umd/index.umd.js +254 -44
  91. package/umd/index.umd.js.map +1 -1
  92. package/umd/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +5 -1
  93. package/umd/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +7 -0
  94. package/umd/scripts/run-codex-prompts/common/coderRunEstimateCache.d.ts +31 -0
  95. package/umd/scripts/run-codex-prompts/common/progressFormatting.d.ts +2 -0
  96. package/umd/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +3 -1
  97. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +7 -0
  98. package/umd/src/book-3.0/cliAgentEnv.d.ts +1 -1
  99. package/umd/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  100. package/umd/src/version.d.ts +1 -1
package/esm/index.es.js CHANGED
@@ -6,7 +6,7 @@ import { join, resolve, dirname, relative, basename, delimiter, isAbsolute, extn
6
6
  import { spawn } from 'child_process';
7
7
  import { createHash, randomBytes } from 'crypto';
8
8
  import * as fs from 'fs';
9
- import { createWriteStream, mkdirSync, writeFileSync, statSync, readFileSync, existsSync } from 'fs';
9
+ import { createWriteStream, mkdirSync, writeFileSync, statSync, readFileSync, existsSync, promises } from 'fs';
10
10
  import * as dotenv from 'dotenv';
11
11
  import moment from 'moment';
12
12
  import { forTime, forEver } from 'waitasecond';
@@ -38,6 +38,7 @@ import ignore from 'ignore';
38
38
  import * as readline from 'readline';
39
39
  import { emitKeypressEvents, clearLine, cursorTo, createInterface } from 'readline';
40
40
  import { EventEmitter } from 'events';
41
+ import { tmpdir } from 'os';
41
42
  import { lookup, extension } from 'mime-types';
42
43
  import papaparse from 'papaparse';
43
44
  import { pathToFileURL } from 'url';
@@ -58,7 +59,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
58
59
  * @generated
59
60
  * @see https://github.com/webgptorg/promptbook
60
61
  */
61
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-131';
62
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-133';
62
63
  /**
63
64
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
64
65
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1718,28 +1719,6 @@ function $initializeAboutCommand(program) {
1718
1719
  // TODO: [🗽] Unite branding and make single place for it
1719
1720
  // Note: [💞] Ignore a discrepancy between file name and entity name
1720
1721
 
1721
- /**
1722
- * Supported thinking-level CLI values for coding-agent runners with configurable reasoning effort.
1723
- *
1724
- * @private internal shared utility of `ptbk coder run`
1725
- */
1726
- const THINKING_LEVEL_VALUES = ['low', 'medium', 'high', 'xhigh'];
1727
- /**
1728
- * Parses and validates an optional thinking-level option value.
1729
- *
1730
- * @private internal shared utility of `ptbk coder run`
1731
- */
1732
- function parseThinkingLevel(thinkingLevelValue) {
1733
- if (thinkingLevelValue === undefined) {
1734
- return undefined;
1735
- }
1736
- if (THINKING_LEVEL_VALUES.includes(thinkingLevelValue)) {
1737
- return thinkingLevelValue;
1738
- }
1739
- throw new Error(`Invalid thinking level "${thinkingLevelValue}". Use one of: ${THINKING_LEVEL_VALUES.join(', ')}.`);
1740
- }
1741
- // Note: [🟡] Code for CLI command [run](src/cli/cli-commands/coder/run.ts) should never be published outside of `@promptbook/cli`
1742
-
1743
1722
  // Note: [💞] Ignore a discrepancy between file name and entity name
1744
1723
  /**
1745
1724
  * All CLI harness names supported by `CliAgent` and `ptbk agent exec`.
@@ -1754,6 +1733,12 @@ const CLI_AGENT_HARNESS_NAMES = [
1754
1733
  'opencode',
1755
1734
  'gemini',
1756
1735
  ];
1736
+ /**
1737
+ * All supported thinking-level values for CLI coding-agent runners.
1738
+ *
1739
+ * @public exported from `@promptbook/node`
1740
+ */
1741
+ const CLI_AGENT_THINKING_LEVEL_VALUES = ['low', 'medium', 'high', 'xhigh', 'max'];
1757
1742
  /**
1758
1743
  * Environment variable used as the default runner identifier when `--harness` is omitted or not set in `CliAgent`.
1759
1744
  *
@@ -1776,6 +1761,28 @@ const PTBK_MODEL_ENV = 'PTBK_MODEL';
1776
1761
  */
1777
1762
  const PTBK_THINKING_LEVEL_ENV = 'PTBK_THINKING_LEVEL';
1778
1763
 
1764
+ /**
1765
+ * Supported thinking-level CLI values for coding-agent runners with configurable reasoning effort.
1766
+ *
1767
+ * @private internal shared utility of `ptbk coder run`
1768
+ */
1769
+ const THINKING_LEVEL_VALUES = CLI_AGENT_THINKING_LEVEL_VALUES;
1770
+ /**
1771
+ * Parses and validates an optional thinking-level option value.
1772
+ *
1773
+ * @private internal shared utility of `ptbk coder run`
1774
+ */
1775
+ function parseThinkingLevel(thinkingLevelValue) {
1776
+ if (thinkingLevelValue === undefined) {
1777
+ return undefined;
1778
+ }
1779
+ if (THINKING_LEVEL_VALUES.includes(thinkingLevelValue)) {
1780
+ return thinkingLevelValue;
1781
+ }
1782
+ throw new Error(`Invalid thinking level "${thinkingLevelValue}". Use one of: ${THINKING_LEVEL_VALUES.join(', ')}.`);
1783
+ }
1784
+ // Note: [🟡] Code for CLI command [run](src/cli/cli-commands/coder/run.ts) should never be published outside of `@promptbook/cli`
1785
+
1779
1786
  /**
1780
1787
  * Runner identifiers supported by Promptbook CLI agent orchestration commands.
1781
1788
  *
@@ -4561,14 +4568,16 @@ function formatPromptCount$1(count) {
4561
4568
 
4562
4569
  /**
4563
4570
  * Calendar formats used when displaying the estimated completion time.
4571
+ *
4572
+ * Uses 24-hour format (`HH:mm`) so estimates are unambiguous (for example `17:30` instead of `5:30`).
4564
4573
  */
4565
4574
  const ESTIMATED_DONE_CALENDAR_FORMATS = {
4566
- sameDay: '[Today] h:mm',
4567
- nextDay: '[Tomorrow] h:mm',
4568
- nextWeek: 'dddd h:mm',
4569
- lastDay: '[Yesterday] h:mm',
4570
- lastWeek: 'dddd h:mm',
4571
- sameElse: 'MMM D h:mm',
4575
+ sameDay: '[Today] HH:mm',
4576
+ nextDay: '[Tomorrow] HH:mm',
4577
+ nextWeek: 'dddd HH:mm',
4578
+ lastDay: '[Yesterday] HH:mm',
4579
+ lastWeek: 'dddd HH:mm',
4580
+ sameElse: 'MMM D HH:mm',
4572
4581
  };
4573
4582
  /**
4574
4583
  * Formats a duration into a compact string such as "3h 12m" or "45s".
@@ -4596,8 +4605,12 @@ function formatDurationBrief(duration) {
4596
4605
 
4597
4606
  /**
4598
4607
  * Builds a session-scoped progress snapshot from prompt stats and elapsed active time.
4608
+ *
4609
+ * When the current session has not finished any prompt yet but a `cachedAveragePromptDurationMs` is
4610
+ * provided (loaded from the per-config estimate cache stored in the temp folder), the snapshot uses
4611
+ * that historical average to project the remaining work instead of waiting for the first completion.
4599
4612
  */
4600
- function buildCoderRunProgressSnapshot(stats, elapsedDuration, initialDone) {
4613
+ function buildCoderRunProgressSnapshot(stats, elapsedDuration, initialDone, cachedAveragePromptDurationMs) {
4601
4614
  const totalPrompts = stats.done + stats.forAgent + stats.toBeWritten;
4602
4615
  const sessionDone = Math.max(0, stats.done - initialDone);
4603
4616
  const sessionRemaining = stats.forAgent;
@@ -4605,12 +4618,22 @@ function buildCoderRunProgressSnapshot(stats, elapsedDuration, initialDone) {
4605
4618
  const currentPromptIndex = sessionTotal > 0 ? Math.min(sessionDone + 1, sessionTotal) : 0;
4606
4619
  const percentage = sessionTotal > 0 ? Math.round((sessionDone / sessionTotal) * 100) : 0;
4607
4620
  const elapsedText = formatDurationBrief(elapsedDuration);
4621
+ const elapsedMs = elapsedDuration.asMilliseconds();
4608
4622
  let estimatedTotalText = 'estimating...';
4609
4623
  let estimatedLabel = 'after first completion';
4610
4624
  let isEstimatedTotalKnown = false;
4625
+ let estimatedTotalMs;
4611
4626
  if (sessionTotal > 0 && sessionDone > 0) {
4612
- const estimatedTotalMs = (elapsedDuration.asMilliseconds() * sessionTotal) / sessionDone;
4613
- const estimatedRemainingMs = Math.max(0, estimatedTotalMs - elapsedDuration.asMilliseconds());
4627
+ estimatedTotalMs = (elapsedMs * sessionTotal) / sessionDone;
4628
+ }
4629
+ else if (sessionTotal > 0 &&
4630
+ cachedAveragePromptDurationMs !== undefined &&
4631
+ cachedAveragePromptDurationMs > 0) {
4632
+ const projectedRemainingMs = sessionRemaining * cachedAveragePromptDurationMs;
4633
+ estimatedTotalMs = elapsedMs + projectedRemainingMs;
4634
+ }
4635
+ if (estimatedTotalMs !== undefined) {
4636
+ const estimatedRemainingMs = Math.max(0, estimatedTotalMs - elapsedMs);
4614
4637
  const estimatedTotalDuration = moment.duration(estimatedTotalMs);
4615
4638
  const estimatedCompletion = moment().add(estimatedRemainingMs, 'milliseconds');
4616
4639
  estimatedTotalText = formatDurationBrief(estimatedTotalDuration);
@@ -4744,12 +4767,21 @@ class CoderRunUiState extends EventEmitter {
4744
4767
  this.stats = stats;
4745
4768
  this.emitChange();
4746
4769
  }
4770
+ /**
4771
+ * Sets the cached average prompt duration (in milliseconds) loaded from the temp-folder estimate cache.
4772
+ *
4773
+ * Used to project completion estimates before the first prompt of the current session finishes.
4774
+ */
4775
+ setCachedAveragePromptDurationMs(cachedAveragePromptDurationMs) {
4776
+ this.cachedAveragePromptDurationMs = cachedAveragePromptDurationMs;
4777
+ this.emitChange();
4778
+ }
4747
4779
  /**
4748
4780
  * Computes a progress snapshot on demand so elapsed time ticks with periodic re-renders.
4749
4781
  */
4750
4782
  getProgress() {
4751
4783
  var _a;
4752
- return buildCoderRunProgressSnapshot(this.stats, this.timer.getElapsedDuration(), (_a = this.initialDone) !== null && _a !== void 0 ? _a : this.stats.done);
4784
+ return buildCoderRunProgressSnapshot(this.stats, this.timer.getElapsedDuration(), (_a = this.initialDone) !== null && _a !== void 0 ? _a : this.stats.done, this.cachedAveragePromptDurationMs);
4753
4785
  }
4754
4786
  /**
4755
4787
  * Sets the label of the prompt currently being processed and resets per-prompt state.
@@ -27456,17 +27488,21 @@ async function $runGoScriptWithOutput(options) {
27456
27488
  });
27457
27489
  }
27458
27490
 
27491
+ /**
27492
+ * Base delimiter used for passing large prompts through stdin.
27493
+ */
27494
+ const CLAUDE_PROMPT_DELIMITER = 'CLAUDE_PROMPT';
27459
27495
  /**
27460
27496
  * Builds the shell script that runs Claude Code with the prompt and coding context.
27461
27497
  */
27462
27498
  function buildClaudeScript(options) {
27463
- const delimiter = 'CLAUDE_PROMPT';
27499
+ const THINKING_LEVEL_ARGUMENT = options.thinkingLevel ? ` --effort ${options.thinkingLevel}` : '';
27464
27500
  return spaceTrim((block) => `
27465
- claude --allowedTools "Bash,Read,Edit,Write" --output-format json --print <<'${delimiter}'
27501
+ claude --allowedTools "Bash,Read,Edit,Write"${THINKING_LEVEL_ARGUMENT} --output-format json --print <<'${CLAUDE_PROMPT_DELIMITER}'
27466
27502
 
27467
27503
  ${block(options.prompt)}
27468
27504
 
27469
- ${delimiter}
27505
+ ${CLAUDE_PROMPT_DELIMITER}
27470
27506
  `);
27471
27507
  }
27472
27508
 
@@ -27602,7 +27638,8 @@ class ClaudeCodeRunner {
27602
27638
  /**
27603
27639
  * Creates a new Claude Code runner.
27604
27640
  */
27605
- constructor() {
27641
+ constructor(options = {}) {
27642
+ this.options = options;
27606
27643
  this.name = 'claude-code';
27607
27644
  }
27608
27645
  /**
@@ -27611,6 +27648,7 @@ class ClaudeCodeRunner {
27611
27648
  async runPrompt(options) {
27612
27649
  const scriptContent = buildClaudeScript({
27613
27650
  prompt: options.prompt,
27651
+ thinkingLevel: this.options.thinkingLevel,
27614
27652
  });
27615
27653
  const output = await $runGoScriptWithOutput({
27616
27654
  scriptPath: options.scriptPath,
@@ -28783,7 +28821,9 @@ function resolvePromptRunner(options) {
28783
28821
  }), actualRunnerModel);
28784
28822
  }
28785
28823
  if (agentName === 'claude-code') {
28786
- return createRunnerResolution(options, new ClaudeCodeRunner());
28824
+ return createRunnerResolution(options, new ClaudeCodeRunner({
28825
+ thinkingLevel: options.thinkingLevel,
28826
+ }));
28787
28827
  }
28788
28828
  if (agentName === 'opencode') {
28789
28829
  return createRunnerResolution(options, new OpencodeRunner({
@@ -37857,7 +37897,7 @@ Array.from(new Set(SERVER_LIMIT_DEFINITIONS.flatMap((definition) => definition.l
37857
37897
  *
37858
37898
  * @private internal constant of `ptbk agents-server`
37859
37899
  */
37860
- const USER_CHAT_JOB_WORKER_POLL_INTERVAL_MS = 2000;
37900
+ const USER_CHAT_JOB_WORKER_POLL_INTERVAL_MS = 10000;
37861
37901
  /**
37862
37902
  * Number of identical worker failures suppressed before logging a repeated summary.
37863
37903
  *
@@ -69682,6 +69722,15 @@ class CliProgressDisplay {
69682
69722
  this.render();
69683
69723
  this.interval = setInterval(() => this.render(), PROGRESS_REFRESH_INTERVAL_MS);
69684
69724
  }
69725
+ /**
69726
+ * Sets the cached average prompt duration (in milliseconds) loaded from the temp-folder estimate cache.
69727
+ *
69728
+ * Used to project completion estimates before the first prompt of the current session finishes.
69729
+ */
69730
+ setCachedAveragePromptDurationMs(cachedAveragePromptDurationMs) {
69731
+ this.cachedAveragePromptDurationMs = cachedAveragePromptDurationMs;
69732
+ this.render();
69733
+ }
69685
69734
  /**
69686
69735
  * Updates the progress statistics shown in the header.
69687
69736
  */
@@ -69752,7 +69801,7 @@ class CliProgressDisplay {
69752
69801
  */
69753
69802
  buildProgressLines() {
69754
69803
  var _a, _b;
69755
- const snapshot = buildCoderRunProgressSnapshot(this.stats, this.timer.getElapsedDuration(), (_a = this.initialDone) !== null && _a !== void 0 ? _a : this.stats.done);
69804
+ const snapshot = buildCoderRunProgressSnapshot(this.stats, this.timer.getElapsedDuration(), (_a = this.initialDone) !== null && _a !== void 0 ? _a : this.stats.done, this.cachedAveragePromptDurationMs);
69756
69805
  const columns = Math.max(40, (_b = process.stdout.columns) !== null && _b !== void 0 ? _b : 80);
69757
69806
  const workingLine = snapshot.sessionTotal > 0
69758
69807
  ? [
@@ -69810,6 +69859,118 @@ function formatPromptCount(count) {
69810
69859
  return `${count} prompt${count === 1 ? '' : 's'}`;
69811
69860
  }
69812
69861
 
69862
+ /**
69863
+ * Sub-directory of the OS temp folder where per-config estimate caches are stored.
69864
+ *
69865
+ * @private internal constant of `coderRunEstimateCache`
69866
+ */
69867
+ const ESTIMATE_CACHE_DIRECTORY_NAME = 'ptbk-coder-estimates';
69868
+ /**
69869
+ * Absolute path to the directory where estimate caches are persisted.
69870
+ *
69871
+ * @private internal utility of `coderRunEstimateCache`
69872
+ */
69873
+ function getEstimateCacheDirectory() {
69874
+ return join(tmpdir(), ESTIMATE_CACHE_DIRECTORY_NAME);
69875
+ }
69876
+ /**
69877
+ * Sanitizes a single identifier component so that it is safe to use as a filename segment.
69878
+ *
69879
+ * @private internal utility of `coderRunEstimateCache`
69880
+ */
69881
+ function sanitizeFileNameSegment(value) {
69882
+ return value.replace(/[^a-zA-Z0-9._-]/g, '_');
69883
+ }
69884
+ /**
69885
+ * Builds the cache filename for a given runner configuration.
69886
+ *
69887
+ * @private internal utility of `coderRunEstimateCache`
69888
+ */
69889
+ function buildEstimateCacheFileName(key) {
69890
+ var _a, _b;
69891
+ const harnessSegment = sanitizeFileNameSegment(key.harness);
69892
+ const modelSegment = sanitizeFileNameSegment((_a = key.model) !== null && _a !== void 0 ? _a : 'default');
69893
+ const thinkingLevelSegment = sanitizeFileNameSegment((_b = key.thinkingLevel) !== null && _b !== void 0 ? _b : 'default');
69894
+ return `${harnessSegment}__${modelSegment}__${thinkingLevelSegment}.json`;
69895
+ }
69896
+ /**
69897
+ * Reads the previously persisted cache entry for a configuration if available.
69898
+ *
69899
+ * @private internal utility of `coderRunEstimateCache`
69900
+ */
69901
+ async function readEstimateCacheEntry(filePath) {
69902
+ try {
69903
+ const content = await promises.readFile(filePath, 'utf-8');
69904
+ const parsed = JSON.parse(content);
69905
+ if (typeof parsed.averagePromptDurationMs !== 'number' ||
69906
+ !Number.isFinite(parsed.averagePromptDurationMs) ||
69907
+ parsed.averagePromptDurationMs <= 0 ||
69908
+ typeof parsed.sampleCount !== 'number' ||
69909
+ !Number.isFinite(parsed.sampleCount) ||
69910
+ parsed.sampleCount <= 0) {
69911
+ return undefined;
69912
+ }
69913
+ return {
69914
+ averagePromptDurationMs: parsed.averagePromptDurationMs,
69915
+ sampleCount: parsed.sampleCount,
69916
+ updatedAt: typeof parsed.updatedAt === 'string' ? parsed.updatedAt : new Date().toISOString(),
69917
+ };
69918
+ }
69919
+ catch (_a) {
69920
+ return undefined;
69921
+ }
69922
+ }
69923
+ /**
69924
+ * Loads the cached average prompt duration for the given runner configuration.
69925
+ *
69926
+ * Returns `undefined` when no usable cache entry exists. Cache failures must never break the run, so this
69927
+ * function swallows IO and parsing errors instead of throwing.
69928
+ *
69929
+ * @public exported for use by the coder run progress displays
69930
+ */
69931
+ async function loadCachedAveragePromptDurationMs(key) {
69932
+ const filePath = join(getEstimateCacheDirectory(), buildEstimateCacheFileName(key));
69933
+ const entry = await readEstimateCacheEntry(filePath);
69934
+ return entry === null || entry === void 0 ? void 0 : entry.averagePromptDurationMs;
69935
+ }
69936
+ /**
69937
+ * Records a new prompt-duration sample for the given runner configuration into the temp folder cache.
69938
+ *
69939
+ * Updates a running average so future `ptbk coder run` / `ptbk coder server` invocations can show
69940
+ * meaningful estimates immediately, before the first prompt of the new session has completed.
69941
+ *
69942
+ * Cache failures are swallowed because the next-run estimate is best-effort and must not affect the
69943
+ * current run.
69944
+ *
69945
+ * @public exported for use by the prompt round finalizer
69946
+ */
69947
+ async function recordPromptDurationSample(key, promptDurationMs) {
69948
+ var _a, _b;
69949
+ if (!Number.isFinite(promptDurationMs) || promptDurationMs <= 0) {
69950
+ return;
69951
+ }
69952
+ try {
69953
+ const cacheDirectory = getEstimateCacheDirectory();
69954
+ await promises.mkdir(cacheDirectory, { recursive: true });
69955
+ const filePath = join(cacheDirectory, buildEstimateCacheFileName(key));
69956
+ const previousEntry = await readEstimateCacheEntry(filePath);
69957
+ const previousSampleCount = (_a = previousEntry === null || previousEntry === void 0 ? void 0 : previousEntry.sampleCount) !== null && _a !== void 0 ? _a : 0;
69958
+ const previousTotalMs = ((_b = previousEntry === null || previousEntry === void 0 ? void 0 : previousEntry.averagePromptDurationMs) !== null && _b !== void 0 ? _b : 0) * previousSampleCount;
69959
+ const nextSampleCount = previousSampleCount + 1;
69960
+ const nextAveragePromptDurationMs = (previousTotalMs + promptDurationMs) / nextSampleCount;
69961
+ const nextEntry = {
69962
+ averagePromptDurationMs: nextAveragePromptDurationMs,
69963
+ sampleCount: nextSampleCount,
69964
+ updatedAt: new Date().toISOString(),
69965
+ };
69966
+ await promises.writeFile(filePath, JSON.stringify(nextEntry, null, 4), 'utf-8');
69967
+ }
69968
+ catch (_c) {
69969
+ // Note: Persisting the estimate cache is best-effort. Failures must not affect the current run.
69970
+ }
69971
+ }
69972
+ // Note: [🟡] Code in this file is only used from `@promptbook/cli` orchestration scripts and is not shipped to other consumers.
69973
+
69813
69974
  /**
69814
69975
  * Reads an optional agent `.book` file and compiles its system message for injection into coder prompts.
69815
69976
  *
@@ -71777,6 +71938,11 @@ async function finalizeSuccessfulPromptRound(options) {
71777
71938
  markPromptDone(nextPrompt.file, nextPrompt.section, result.usage, runnerMetadata.runnerName, runnerMetadata.modelName, promptExecutionStartedDate, result.attemptCount);
71778
71939
  await writePromptFile(nextPrompt.file);
71779
71940
  await normalizeLineEndingsForCurrentRound(runOptions, roundChangedFilesSnapshot);
71941
+ await recordPromptDurationInEstimateCache({
71942
+ options: runOptions,
71943
+ runnerMetadata,
71944
+ promptExecutionStartedDate,
71945
+ });
71780
71946
  if (!runOptions.noCommit) {
71781
71947
  await waitForCommitConfirmationIfNeeded({
71782
71948
  options: runOptions,
@@ -71878,6 +72044,24 @@ async function runPostPromptAutoMigrationIfEnabled(options) {
71878
72044
  logger: console,
71879
72045
  });
71880
72046
  }
72047
+ /**
72048
+ * Persists the duration of one successful prompt round into the per-config estimate cache so the
72049
+ * next `ptbk coder run` / `ptbk coder server` invocation can show a meaningful completion estimate
72050
+ * before its own first prompt has finished.
72051
+ */
72052
+ async function recordPromptDurationInEstimateCache(options) {
72053
+ var _a;
72054
+ const { options: runOptions, runnerMetadata, promptExecutionStartedDate } = options;
72055
+ if (!runOptions.agentName) {
72056
+ return;
72057
+ }
72058
+ const promptDurationMs = moment().diff(promptExecutionStartedDate);
72059
+ await recordPromptDurationSample({
72060
+ harness: runOptions.agentName,
72061
+ model: (_a = runnerMetadata.modelName) !== null && _a !== void 0 ? _a : runOptions.model,
72062
+ thinkingLevel: runOptions.thinkingLevel,
72063
+ }, promptDurationMs);
72064
+ }
71881
72065
  /**
71882
72066
  * Normalizes line endings in files modified during the current coding round.
71883
72067
  */
@@ -71927,6 +72111,12 @@ async function runCodexPrompts(providedOptions) {
71927
72111
  const { runner, actualRunnerModel, runnerMetadata } = resolvePromptRunner(options);
71928
72112
  console.info(colors.green(`Running prompts with ${runner.name}`));
71929
72113
  initializeRunUi(uiHandle, runner.name, actualRunnerModel, options);
72114
+ await seedCachedAveragePromptDuration({
72115
+ options,
72116
+ actualRunnerModel,
72117
+ progressDisplay,
72118
+ uiHandle,
72119
+ });
71930
72120
  let hasShownUpcomingTasks = false;
71931
72121
  let hasWaitedForStart = false;
71932
72122
  let previousRoundStartTime;
@@ -72288,6 +72478,27 @@ async function waitBetweenPromptRoundsIfNeeded(options) {
72288
72478
  progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
72289
72479
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
72290
72480
  }
72481
+ /**
72482
+ * Loads the cached average prompt duration for the current runner configuration and seeds both
72483
+ * progress displays with it so estimates are shown immediately, even before the first prompt of
72484
+ * the current session completes.
72485
+ */
72486
+ async function seedCachedAveragePromptDuration(options) {
72487
+ const { options: runOptions, actualRunnerModel, progressDisplay, uiHandle } = options;
72488
+ if (!runOptions.agentName) {
72489
+ return;
72490
+ }
72491
+ const cachedAveragePromptDurationMs = await loadCachedAveragePromptDurationMs({
72492
+ harness: runOptions.agentName,
72493
+ model: actualRunnerModel !== null && actualRunnerModel !== void 0 ? actualRunnerModel : runOptions.model,
72494
+ thinkingLevel: runOptions.thinkingLevel,
72495
+ });
72496
+ if (cachedAveragePromptDurationMs === undefined) {
72497
+ return;
72498
+ }
72499
+ progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.setCachedAveragePromptDurationMs(cachedAveragePromptDurationMs);
72500
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCachedAveragePromptDurationMs(cachedAveragePromptDurationMs);
72501
+ }
72291
72502
  /**
72292
72503
  * Stops active displays and prints the git identity tip for real runs.
72293
72504
  */