@promptbook/cli 0.112.0 → 0.113.0-1

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 (109) hide show
  1. package/README.md +45 -29
  2. package/agents/default/developer.book +2 -1
  3. package/apps/agents-server/next.config.ts +21 -0
  4. package/apps/agents-server/src/app/admin/limits/LimitsClient.tsx +1 -1
  5. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTaskRow.tsx +7 -2
  6. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +52 -33
  7. package/apps/agents-server/src/app/api/agent-folders/[folderId]/visibility/route.ts +19 -7
  8. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +8 -18
  9. package/apps/agents-server/src/app/api/internal/agent-runner-limits/route.ts +1 -1
  10. package/apps/agents-server/src/app/api/page-preview/check/route.ts +5 -20
  11. package/apps/agents-server/src/app/api/page-preview/interact/route.ts +179 -0
  12. package/apps/agents-server/src/app/api/page-preview/live/route.ts +67 -0
  13. package/apps/agents-server/src/app/api/page-preview/screenshot/route.ts +13 -21
  14. package/apps/agents-server/src/app/api/v1/agents/[agentId]/route.ts +13 -8
  15. package/apps/agents-server/src/components/AgentProfile/AgentCapabilityChips.tsx +39 -5
  16. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +7 -1
  17. package/apps/agents-server/src/components/Homepage/AgentsList.tsx +3 -0
  18. package/apps/agents-server/src/components/Homepage/AgentsListHeader.tsx +41 -0
  19. package/apps/agents-server/src/components/Homepage/hiddenFolders.ts +104 -0
  20. package/apps/agents-server/src/components/Homepage/useAgentsListQueryState.ts +35 -0
  21. package/apps/agents-server/src/components/Homepage/useAgentsListState.ts +24 -4
  22. package/apps/agents-server/src/constants/chatVisualMode.ts +1 -1
  23. package/apps/agents-server/src/constants/serverLimits.ts +19 -0
  24. package/apps/agents-server/src/constants/themeMode.ts +1 -1
  25. package/apps/agents-server/src/database/migrations/2026-05-2600-default-theme.sql +2 -2
  26. package/apps/agents-server/src/database/seedCoreAgents.ts +0 -2
  27. package/apps/agents-server/src/database/seedDefaultAgents.ts +0 -2
  28. package/apps/agents-server/src/tools/agent_progress.ts +4 -10
  29. package/apps/agents-server/src/utils/agentVisibility.ts +25 -62
  30. package/apps/agents-server/src/utils/chatTasksAdmin.ts +3 -1
  31. package/apps/agents-server/src/utils/createAgentWithDefaultVisibility.ts +3 -1
  32. package/apps/agents-server/src/utils/externalChatRunner/processExternalUserChatJob.ts +9 -17
  33. package/apps/agents-server/src/utils/getAdminChatTasksResponse/mapVpsSelfUpdateJobToAdminChatTask.ts +84 -0
  34. package/apps/agents-server/src/utils/getAdminChatTasksResponse.ts +195 -5
  35. package/apps/agents-server/src/utils/localChatRunner/LocalUserChatJobMetadata.ts +15 -5
  36. package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +17 -26
  37. package/apps/agents-server/src/utils/pagePreview/livePagePreviewSessions.ts +365 -0
  38. package/apps/agents-server/src/utils/pagePreview/resolvePagePreviewRequestUrl.ts +74 -0
  39. package/apps/agents-server/src/utils/serverLimits.ts +2 -0
  40. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerCoreAgents.ts +2 -2
  41. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +1 -1
  42. package/apps/agents-server/src/utils/userChat/createUserChatHarnessProgressCard.ts +0 -6
  43. package/apps/agents-server/src/utils/userChat/createUserChatRunnerProgressCard.ts +0 -6
  44. package/apps/agents-server/src/utils/userChat/userChatMessageLifecycle.ts +69 -0
  45. package/apps/agents-server/src/utils/userChat/userChatProgressCard.ts +0 -6
  46. package/apps/agents-server/src/utils/vpsSelfUpdate.ts +150 -24
  47. package/esm/apps/agents-server/src/constants/serverLimits.d.ts +17 -0
  48. package/esm/index.es.js +581 -93
  49. package/esm/index.es.js.map +1 -1
  50. package/esm/scripts/run-agent-messages/main/runMultipleAgentMessages.d.ts +1 -0
  51. package/esm/scripts/run-agent-messages/main/tickAgentMessages.d.ts +1 -0
  52. package/esm/src/_packages/core.index.d.ts +22 -0
  53. package/esm/src/_packages/types.index.d.ts +2 -0
  54. package/esm/src/book-2.0/agent-source/AgentBasicInformation.d.ts +2 -0
  55. package/esm/src/book-2.0/agent-source/agentSourceVisibility.d.ts +97 -0
  56. package/esm/src/book-2.0/agent-source/agentSourceVisibility.test.d.ts +1 -0
  57. package/esm/src/book-components/BookEditor/BookEditorMonacoTokenization.d.ts +1 -0
  58. package/esm/src/book-components/Chat/Chat/CitationIframePreview.d.ts +2 -2
  59. package/esm/src/book-components/Chat/utils/livePagePreviewConstants.d.ts +12 -0
  60. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +6 -0
  61. package/esm/src/collection/agent-collection/CreateAgentInput.d.ts +2 -1
  62. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.d.ts +21 -1
  63. package/esm/src/commitments/META_VISIBILITY/META_VISIBILITY.d.ts +27 -0
  64. package/esm/src/commitments/index.d.ts +2 -1
  65. package/esm/src/version.d.ts +1 -1
  66. package/package.json +1 -1
  67. package/src/_packages/core.index.ts +22 -0
  68. package/src/_packages/types.index.ts +2 -0
  69. package/src/book-2.0/agent-source/AgentBasicInformation.ts +2 -0
  70. package/src/book-2.0/agent-source/agentSourceVisibility.ts +214 -0
  71. package/src/book-2.0/agent-source/parseAgentSource/applyMetaCommitment.ts +17 -0
  72. package/src/book-2.0/book-language-documentation/createStandaloneBookLanguageMarkdown.ts +1 -0
  73. package/src/book-components/BookEditor/BookEditorMonacoTokenization.ts +11 -0
  74. package/src/book-components/BookEditor/useBookEditorMonacoLanguage.ts +7 -3
  75. package/src/book-components/Chat/Chat/Chat.module.css +14 -5
  76. package/src/book-components/Chat/Chat/CitationIframePreview.tsx +197 -22
  77. package/src/book-components/Chat/utils/livePagePreviewConstants.ts +17 -0
  78. package/src/cli/cli-commands/agents-server/buildAgentsServer.ts +144 -16
  79. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +22 -2
  80. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +1 -1
  81. package/src/cli/cli-commands/coder/run.ts +30 -0
  82. package/src/collection/agent-collection/CreateAgentInput.ts +10 -5
  83. package/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.ts +7 -3
  84. package/src/collection/agent-collection/constructors/agent-collection-in-supabase/createAgentPersistenceRecords.ts +7 -4
  85. package/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.ts +44 -8
  86. package/src/commitments/META_VISIBILITY/META_VISIBILITY.ts +78 -0
  87. package/src/commitments/index.ts +2 -0
  88. package/src/other/templates/getTemplatesPipelineCollection.ts +826 -650
  89. package/src/version.ts +2 -2
  90. package/src/versions.txt +2 -0
  91. package/umd/apps/agents-server/src/constants/serverLimits.d.ts +17 -0
  92. package/umd/index.umd.js +581 -93
  93. package/umd/index.umd.js.map +1 -1
  94. package/umd/scripts/run-agent-messages/main/runMultipleAgentMessages.d.ts +1 -0
  95. package/umd/scripts/run-agent-messages/main/tickAgentMessages.d.ts +1 -0
  96. package/umd/src/_packages/core.index.d.ts +22 -0
  97. package/umd/src/_packages/types.index.d.ts +2 -0
  98. package/umd/src/book-2.0/agent-source/AgentBasicInformation.d.ts +2 -0
  99. package/umd/src/book-2.0/agent-source/agentSourceVisibility.d.ts +97 -0
  100. package/umd/src/book-2.0/agent-source/agentSourceVisibility.test.d.ts +1 -0
  101. package/umd/src/book-components/BookEditor/BookEditorMonacoTokenization.d.ts +1 -0
  102. package/umd/src/book-components/Chat/Chat/CitationIframePreview.d.ts +2 -2
  103. package/umd/src/book-components/Chat/utils/livePagePreviewConstants.d.ts +12 -0
  104. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +6 -0
  105. package/umd/src/collection/agent-collection/CreateAgentInput.d.ts +2 -1
  106. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.d.ts +21 -1
  107. package/umd/src/commitments/META_VISIBILITY/META_VISIBILITY.d.ts +27 -0
  108. package/umd/src/commitments/index.d.ts +2 -1
  109. package/umd/src/version.d.ts +1 -1
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';
66
+ const PROMPTBOOK_ENGINE_VERSION = '0.113.0-1';
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
@@ -3303,6 +3303,28 @@
3303
3303
  * @private internal constant of `ptbk agents-server`
3304
3304
  */
3305
3305
  const AGENTS_SERVER_BUILD_MAX_OLD_SPACE_MIB = 4096;
3306
+ /**
3307
+ * Conservative Next.js build worker count used by CLI-owned Agents Server production builds.
3308
+ *
3309
+ * Standalone VPS self-updates build the replacement server while the current pm2 process is
3310
+ * still serving traffic. Keeping Next's static workers serial avoids CPU-count-based memory
3311
+ * spikes that can make the OS kill the build child with no normal exit code.
3312
+ *
3313
+ * @private internal constant of `ptbk agents-server`
3314
+ */
3315
+ const AGENTS_SERVER_BUILD_WORKER_COUNT = 1;
3316
+ /**
3317
+ * Maximum attempts for a Next.js build that was killed by the operating system.
3318
+ *
3319
+ * @private internal constant of `ptbk agents-server`
3320
+ */
3321
+ const AGENTS_SERVER_BUILD_MAX_ATTEMPTS = 2;
3322
+ /**
3323
+ * Signals that indicate the Next.js build may have been terminated by resource pressure.
3324
+ *
3325
+ * @private internal constant of `ptbk agents-server`
3326
+ */
3327
+ const RETRYABLE_NEXT_BUILD_SIGNALS = new Set(['SIGKILL', null]);
3306
3328
  /**
3307
3329
  * Environment variable passed to the bundled Next app so webpack can resolve dependencies
3308
3330
  * installed beside `ptbk` even when the app sources are materialized into a project cache.
@@ -3310,6 +3332,12 @@
3310
3332
  * @private internal constant of `ptbk agents-server`
3311
3333
  */
3312
3334
  const PTBK_AGENTS_SERVER_NODE_MODULES_PATH_ENV = 'PTBK_AGENTS_SERVER_NODE_MODULES_PATH';
3335
+ /**
3336
+ * Environment variable consumed by `apps/agents-server/next.config.ts` to throttle build workers.
3337
+ *
3338
+ * @private internal constant of `ptbk agents-server`
3339
+ */
3340
+ const PTBK_AGENTS_SERVER_BUILD_WORKER_COUNT_ENV = 'PTBK_AGENTS_SERVER_BUILD_WORKER_COUNT';
3313
3341
  /**
3314
3342
  * Environment variable used only by the CLI-owned production build.
3315
3343
  *
@@ -3510,6 +3538,7 @@
3510
3538
  ...environment,
3511
3539
  NODE_PATH: mergeNodePath(nodeModulesPath, environment.NODE_PATH),
3512
3540
  [PTBK_AGENTS_SERVER_NODE_MODULES_PATH_ENV]: nodeModulesPath,
3541
+ [PTBK_AGENTS_SERVER_BUILD_WORKER_COUNT_ENV]: environment[PTBK_AGENTS_SERVER_BUILD_WORKER_COUNT_ENV] || String(AGENTS_SERVER_BUILD_WORKER_COUNT),
3513
3542
  ...(options.isNextValidationIgnored
3514
3543
  ? {
3515
3544
  [PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV]: 'true',
@@ -3696,14 +3725,27 @@
3696
3725
  * Runs the finite Next production build used by local Agents Server commands.
3697
3726
  */
3698
3727
  async function runNextBuild(options) {
3699
- await new Promise((resolveBuild, rejectBuild) => {
3728
+ for (let attempt = 1; attempt <= AGENTS_SERVER_BUILD_MAX_ATTEMPTS; attempt++) {
3729
+ const exitStatus = await runNextBuildAttempt(options);
3730
+ if (exitStatus.code === 0) {
3731
+ return;
3732
+ }
3733
+ if (attempt < AGENTS_SERVER_BUILD_MAX_ATTEMPTS && isNextBuildTerminationRetryable(exitStatus)) {
3734
+ forwardBuildOutput(createNextBuildRetryMessage(exitStatus, attempt + 1), options.onBuildOutput);
3735
+ continue;
3736
+ }
3737
+ throw createNextBuildExitError(exitStatus, attempt);
3738
+ }
3739
+ }
3740
+ /**
3741
+ * Runs one Next.js production build attempt and returns its exit status.
3742
+ */
3743
+ async function runNextBuildAttempt(options) {
3744
+ return await new Promise((resolveBuild, rejectBuild) => {
3700
3745
  var _a, _b;
3701
3746
  const buildProcess = child_process.spawn(process.execPath, [options.nextCliPath, 'build'], {
3702
3747
  cwd: options.appPath,
3703
- env: {
3704
- ...options.environment,
3705
- NODE_OPTIONS: mergeNodeOptionsWithHeapSize(options.environment.NODE_OPTIONS, AGENTS_SERVER_BUILD_MAX_OLD_SPACE_MIB),
3706
- },
3748
+ env: createNextBuildProcessEnvironment(options.environment),
3707
3749
  stdio: ['ignore', 'pipe', 'pipe'],
3708
3750
  });
3709
3751
  (_a = buildProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (chunk) => {
@@ -3712,16 +3754,70 @@
3712
3754
  (_b = buildProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (chunk) => {
3713
3755
  forwardBuildOutput(chunk.toString(), options.onBuildOutput);
3714
3756
  });
3715
- buildProcess.once('error', rejectBuild);
3716
- buildProcess.once('exit', (code) => {
3717
- if (code === 0) {
3718
- resolveBuild();
3719
- return;
3720
- }
3721
- rejectBuild(new Error(`next-build exited with code ${String(code)}.`));
3757
+ buildProcess.once('error', (error) => {
3758
+ rejectBuild(createNextBuildSpawnError(error));
3759
+ });
3760
+ buildProcess.once('close', (code, signal) => {
3761
+ resolveBuild({ code, signal });
3722
3762
  });
3723
3763
  });
3724
3764
  }
3765
+ /**
3766
+ * Creates the environment passed to the spawned Next.js build process.
3767
+ */
3768
+ function createNextBuildProcessEnvironment(environment) {
3769
+ return {
3770
+ ...environment,
3771
+ NODE_OPTIONS: mergeNodeOptionsWithHeapSize(environment.NODE_OPTIONS, AGENTS_SERVER_BUILD_MAX_OLD_SPACE_MIB),
3772
+ };
3773
+ }
3774
+ /**
3775
+ * Returns true when one failed build attempt is worth retrying.
3776
+ */
3777
+ function isNextBuildTerminationRetryable(exitStatus) {
3778
+ return exitStatus.code === null && RETRYABLE_NEXT_BUILD_SIGNALS.has(exitStatus.signal);
3779
+ }
3780
+ /**
3781
+ * Creates one visible retry message for a killed Next.js build attempt.
3782
+ */
3783
+ function createNextBuildRetryMessage(exitStatus, nextAttempt) {
3784
+ return `\nAgents Server Next build was terminated by ${describeNextBuildExitStatus(exitStatus)}. Retrying attempt ${nextAttempt}/${AGENTS_SERVER_BUILD_MAX_ATTEMPTS}.\n`;
3785
+ }
3786
+ /**
3787
+ * Creates a branded error for a failed Next.js build child process.
3788
+ */
3789
+ function createNextBuildExitError(exitStatus, attemptCount) {
3790
+ const lowMemoryHint = isNextBuildTerminationRetryable(exitStatus)
3791
+ ? '\n\nThe build process was terminated by the operating system. On standalone VPS self-updates this usually means the host ran out of memory while Next.js was compiling or prerendering the app.'
3792
+ : '';
3793
+ return new NotAllowed(_spaceTrim.spaceTrim(`
3794
+ Agents Server Next production build failed.
3795
+
3796
+ - Exit code: \`${String(exitStatus.code)}\`
3797
+ - Signal: \`${String(exitStatus.signal)}\`
3798
+ - Attempts: \`${String(attemptCount)}\`
3799
+ ${lowMemoryHint}
3800
+ `));
3801
+ }
3802
+ /**
3803
+ * Creates a branded error for failures to start the Next.js build child process.
3804
+ */
3805
+ function createNextBuildSpawnError(error) {
3806
+ return new NotAllowed(_spaceTrim.spaceTrim(`
3807
+ Cannot start the Agents Server Next production build.
3808
+
3809
+ ${error.message}
3810
+ `));
3811
+ }
3812
+ /**
3813
+ * Formats one Next.js build exit status for foreground logs.
3814
+ */
3815
+ function describeNextBuildExitStatus(exitStatus) {
3816
+ if (exitStatus.signal) {
3817
+ return `signal \`${exitStatus.signal}\``;
3818
+ }
3819
+ return `exit code \`${String(exitStatus.code)}\``;
3820
+ }
3725
3821
  /**
3726
3822
  * Prepends `--max-old-space-size=<mib>` to `NODE_OPTIONS` unless the caller already set one.
3727
3823
  */
@@ -16452,6 +16548,70 @@
16452
16548
  }
16453
16549
  // Note: [💞] Ignore a discrepancy between file name and entity name
16454
16550
 
16551
+ /**
16552
+ * META VISIBILITY commitment definition.
16553
+ *
16554
+ * The `META VISIBILITY` commitment stores whether an agent is public, private, or unlisted.
16555
+ * Agents Server mirrors this value into the database for efficient filtering, but the book
16556
+ * commitment remains the editable source of truth.
16557
+ *
16558
+ * @private Metadata-only commitment used by Agents Server.
16559
+ */
16560
+ class MetaVisibilityCommitmentDefinition extends BaseCommitmentDefinition {
16561
+ constructor() {
16562
+ super('META VISIBILITY');
16563
+ }
16564
+ /**
16565
+ * Short one-line description of META VISIBILITY.
16566
+ */
16567
+ get description() {
16568
+ return 'Set whether the agent is private, unlisted, or public.';
16569
+ }
16570
+ /**
16571
+ * Icon for this commitment.
16572
+ */
16573
+ get icon() {
16574
+ return '👁️';
16575
+ }
16576
+ /**
16577
+ * Markdown documentation for META VISIBILITY commitment.
16578
+ */
16579
+ get documentation() {
16580
+ return _spaceTrim.spaceTrim(`
16581
+ # META VISIBILITY
16582
+
16583
+ Sets the agent visibility used by Agents Server.
16584
+
16585
+ ## Allowed values
16586
+
16587
+ - \`PRIVATE\` - accessible only to signed-in users with access.
16588
+ - \`UNLISTED\` - accessible by direct link but hidden from public listings.
16589
+ - \`PUBLIC\` - visible in public listings and accessible by anyone.
16590
+
16591
+ ## Key aspects
16592
+
16593
+ - Does not modify the agent's behavior, system message, or tools.
16594
+ - Whitespace and letter case are normalized when persisted.
16595
+ - If multiple \`META VISIBILITY\` commitments are present, persistence keeps one normalized value.
16596
+ - Agents Server mirrors the value into the database for filtering, but the book is the source of truth.
16597
+
16598
+ ## Example
16599
+
16600
+ \`\`\`book
16601
+ Helpful Assistant
16602
+
16603
+ GOAL Be helpful and friendly.
16604
+ META VISIBILITY PUBLIC
16605
+ \`\`\`
16606
+ `);
16607
+ }
16608
+ applyToAgentModelRequirements(requirements, content) {
16609
+ // META VISIBILITY is metadata only and does not alter model requirements.
16610
+ return requirements;
16611
+ }
16612
+ }
16613
+ // Note: [💞] Ignore a discrepancy between file name and entity name
16614
+
16455
16615
  /**
16456
16616
  * META VOICE commitment definition
16457
16617
  *
@@ -22947,31 +23107,65 @@
22947
23107
  // Note: [💞] Ignore a discrepancy between file name and entity name
22948
23108
 
22949
23109
  /**
22950
- * This error type indicates that some limit was reached
23110
+ * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
22951
23111
  *
22952
23112
  * @public exported from `@promptbook/core`
22953
23113
  */
22954
- class LimitReachedError extends Error {
23114
+ class ParseError extends Error {
22955
23115
  constructor(message) {
22956
23116
  super(message);
22957
- this.name = 'LimitReachedError';
22958
- Object.setPrototypeOf(this, LimitReachedError.prototype);
23117
+ this.name = 'ParseError';
23118
+ Object.setPrototypeOf(this, ParseError.prototype);
22959
23119
  }
22960
23120
  }
23121
+ // TODO: Maybe split `ParseError` and `ApplyError`
22961
23122
 
22962
23123
  /**
22963
- * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
23124
+ * Supported visibility states for persisted agents.
22964
23125
  *
22965
23126
  * @public exported from `@promptbook/core`
22966
23127
  */
22967
- class ParseError extends Error {
23128
+ const AGENT_VISIBILITY_VALUES = ['PRIVATE', 'UNLISTED', 'PUBLIC'];
23129
+ /**
23130
+ * Returns `true` when the value is one of supported visibility states.
23131
+ *
23132
+ * @param value - Raw value to validate.
23133
+ * @returns Whether the value is a valid `AgentVisibility`.
23134
+ *
23135
+ * @public exported from `@promptbook/core`
23136
+ */
23137
+ function isAgentVisibility(value) {
23138
+ return typeof value === 'string' && AGENT_VISIBILITY_VALUES.includes(value);
23139
+ }
23140
+ /**
23141
+ * Normalizes raw visibility text into a supported value.
23142
+ *
23143
+ * @param value - Raw visibility value.
23144
+ * @returns Normalized visibility, or `null` when invalid.
23145
+ *
23146
+ * @public exported from `@promptbook/core`
23147
+ */
23148
+ function normalizeAgentVisibility(value) {
23149
+ if (typeof value !== 'string') {
23150
+ return null;
23151
+ }
23152
+ const normalized = value.trim().toUpperCase();
23153
+ return isAgentVisibility(normalized) ? normalized : null;
23154
+ }
23155
+ // Note: [💞] Ignore a discrepancy between file name and entity name
23156
+
23157
+ /**
23158
+ * This error type indicates that some limit was reached
23159
+ *
23160
+ * @public exported from `@promptbook/core`
23161
+ */
23162
+ class LimitReachedError extends Error {
22968
23163
  constructor(message) {
22969
23164
  super(message);
22970
- this.name = 'ParseError';
22971
- Object.setPrototypeOf(this, ParseError.prototype);
23165
+ this.name = 'LimitReachedError';
23166
+ Object.setPrototypeOf(this, LimitReachedError.prototype);
22972
23167
  }
22973
23168
  }
22974
- // TODO: Maybe split `ParseError` and `ApplyError`
22975
23169
 
22976
23170
  /**
22977
23171
  * Maximum allowed source length for create-agent payloads.
@@ -25306,6 +25500,7 @@
25306
25500
  new MetaDomainCommitmentDefinition(),
25307
25501
  new MetaDisclaimerCommitmentDefinition(),
25308
25502
  new MetaInputPlaceholderCommitmentDefinition(),
25503
+ new MetaVisibilityCommitmentDefinition(),
25309
25504
  new MetaCommitmentDefinition(),
25310
25505
  new MetaVoiceCommitmentDefinition(),
25311
25506
  new NoteCommitmentDefinition('NOTE'),
@@ -25757,6 +25952,7 @@
25757
25952
  'META COLOR': applyMetaColorContent,
25758
25953
  'META FONT': applyMetaFontContent,
25759
25954
  'META VOICE': applyMetaVoiceContent,
25955
+ 'META VISIBILITY': applyMetaVisibilityContent,
25760
25956
  };
25761
25957
  /**
25762
25958
  * Applies META-style commitments that mutate parsed profile metadata.
@@ -25786,6 +25982,10 @@
25786
25982
  applyMetaAvatarContent(state, metaValue);
25787
25983
  return;
25788
25984
  }
25985
+ if (metaTypeRaw.toUpperCase() === 'VISIBILITY') {
25986
+ applyMetaVisibilityContent(state, metaValue);
25987
+ return;
25988
+ }
25789
25989
  const metaType = normalizeTo_camelCase(metaTypeRaw);
25790
25990
  state.meta[metaType] = metaValue;
25791
25991
  }
@@ -25862,6 +26062,15 @@
25862
26062
  function applyMetaVoiceContent(state, content) {
25863
26063
  state.meta.voice = _spaceTrim.spaceTrim(content);
25864
26064
  }
26065
+ /**
26066
+ * Applies META VISIBILITY content into the normalized `meta.visibility` field.
26067
+ */
26068
+ function applyMetaVisibilityContent(state, content) {
26069
+ const visibility = normalizeAgentVisibility(content);
26070
+ if (visibility) {
26071
+ state.meta.visibility = visibility;
26072
+ }
26073
+ }
25865
26074
  /**
25866
26075
  * Normalizes the separator in the content
25867
26076
  *
@@ -37434,7 +37643,7 @@
37434
37643
  validateAgentRunOptions(options);
37435
37644
  const projectPath = tickOptions.projectPath || process.cwd();
37436
37645
  let queueSnapshot = await loadAgentMessageQueueSnapshot(projectPath);
37437
- let queuedMessage = queueSnapshot.queuedMessages[0];
37646
+ let queuedMessage = resolveSelectedQueuedMessage(queueSnapshot, tickOptions.queuedMessage);
37438
37647
  if (!queuedMessage) {
37439
37648
  announceNoQueuedMessages(tickOptions);
37440
37649
  return { isMessageProcessed: false };
@@ -37449,7 +37658,7 @@
37449
37658
  });
37450
37659
  if (autoPullTimestamp !== undefined) {
37451
37660
  queueSnapshot = await loadAgentMessageQueueSnapshot(projectPath);
37452
- queuedMessage = queueSnapshot.queuedMessages[0];
37661
+ queuedMessage = resolveSelectedQueuedMessage(queueSnapshot, tickOptions.queuedMessage);
37453
37662
  if (!queuedMessage) {
37454
37663
  if (tickOptions.uiHandle && !((_b = tickOptions.uiPresentation) === null || _b === void 0 ? void 0 : _b.isSharedDashboard)) {
37455
37664
  updateAgentRunUiForWatching(tickOptions.uiHandle, queueSnapshot);
@@ -37475,7 +37684,7 @@
37475
37684
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.updateProgress(((_e = tickOptions.uiPresentation) === null || _e === void 0 ? void 0 : _e.completedProgressStats) ||
37476
37685
  createAgentQueueProgressSnapshot({
37477
37686
  finishedMessageCount: queueSnapshot.finishedMessageCount + 1,
37478
- queuedMessages: queueSnapshot.queuedMessages.slice(1),
37687
+ queuedMessages: removeQueuedMessageFromSnapshot(queueSnapshot, queuedMessage),
37479
37688
  }));
37480
37689
  if ((_f = tickOptions.uiPresentation) === null || _f === void 0 ? void 0 : _f.completedAgentStatusLines) {
37481
37690
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAgentStatusLines([...tickOptions.uiPresentation.completedAgentStatusLines]);
@@ -37487,7 +37696,9 @@
37487
37696
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setMessagePreviewLines([...tickOptions.uiPresentation.completedMessagePreviewLines]);
37488
37697
  }
37489
37698
  if ((_j = tickOptions.uiPresentation) === null || _j === void 0 ? void 0 : _j.completedMessagePreviewSections) {
37490
- uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setMessagePreviewSections([...tickOptions.uiPresentation.completedMessagePreviewSections]);
37699
+ uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setMessagePreviewSections([
37700
+ ...tickOptions.uiPresentation.completedMessagePreviewSections,
37701
+ ]);
37491
37702
  }
37492
37703
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Message answered');
37493
37704
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('done');
@@ -37515,6 +37726,21 @@
37515
37726
  printAgentGitIdentityTipAtProcessExitIfNeeded();
37516
37727
  }
37517
37728
  }
37729
+ /**
37730
+ * Resolves either the explicitly selected queued message or the first available queue item.
37731
+ */
37732
+ function resolveSelectedQueuedMessage(queueSnapshot, selectedQueuedMessage) {
37733
+ if (!selectedQueuedMessage) {
37734
+ return queueSnapshot.queuedMessages[0];
37735
+ }
37736
+ return queueSnapshot.queuedMessages.find((queuedMessage) => queuedMessage.relativePath === selectedQueuedMessage.relativePath);
37737
+ }
37738
+ /**
37739
+ * Creates a progress queue list with the processed message removed from its original snapshot.
37740
+ */
37741
+ function removeQueuedMessageFromSnapshot(queueSnapshot, processedQueuedMessage) {
37742
+ return queueSnapshot.queuedMessages.filter((queuedMessage) => queuedMessage.relativePath !== processedQueuedMessage.relativePath);
37743
+ }
37518
37744
  /**
37519
37745
  * Runs the selected coding runner for one message and finalizes the answered file.
37520
37746
  */
@@ -37635,7 +37861,9 @@
37635
37861
  return;
37636
37862
  }
37637
37863
  uiHandle.state.setCurrentPrompt(queuedMessage.relativePath);
37638
- uiHandle.state.setMessagePreviewLines([...((uiPresentation === null || uiPresentation === void 0 ? void 0 : uiPresentation.messagePreviewLines) || agentUiMetadata.latestUserMessageLines)]);
37864
+ uiHandle.state.setMessagePreviewLines([
37865
+ ...((uiPresentation === null || uiPresentation === void 0 ? void 0 : uiPresentation.messagePreviewLines) || agentUiMetadata.latestUserMessageLines),
37866
+ ]);
37639
37867
  uiHandle.state.setPhase('loading');
37640
37868
  uiHandle.state.setStatusMessage('Preparing message');
37641
37869
  }
@@ -37914,12 +38142,16 @@
37914
38142
  * Watches all direct child agent repositories from the current directory in one shared session.
37915
38143
  */
37916
38144
  async function runMultipleAgentMessages(options, controls = {}) {
37917
- var _a;
38145
+ var _a, _b;
37918
38146
  validateAgentRunOptions(options);
37919
38147
  validateAgentWatchOptions('ptbk agent-folder run-multiple', options);
37920
38148
  const rootPath = process.cwd();
37921
38149
  const shouldContinue = controls.shouldContinue || (() => just(true));
38150
+ const queuePollIntervalMs = (_a = controls.queuePollIntervalMs) !== null && _a !== void 0 ? _a : MULTI_AGENT_QUEUE_POLL_INTERVAL_MS;
37922
38151
  const ignoreMatcher = createAgentIgnoreMatcher(options.ignorePatterns);
38152
+ const isParallelMessageLimitConfigured = options.maxParallelMessages !== undefined;
38153
+ const maxParallelMessageCount = normalizeMaxParallelMessageCount(options.maxParallelMessages);
38154
+ const activeMessageTasksByKey = new Map();
37923
38155
  let githubSynchronizationTimestamp;
37924
38156
  let githubIgnoredRepositoryCount = 0;
37925
38157
  const autoPullTimestampsByProjectPath = new Map();
@@ -37934,7 +38166,7 @@
37934
38166
  if (!isWatchSessionInitialized) {
37935
38167
  uiHandle = await initializeMultipleAgentRunUi(options);
37936
38168
  isWatchSessionInitialized = true;
37937
- (_a = controls.onUiInitialized) === null || _a === void 0 ? void 0 : _a.call(controls, uiHandle);
38169
+ (_b = controls.onUiInitialized) === null || _b === void 0 ? void 0 : _b.call(controls, uiHandle);
37938
38170
  if (!uiHandle) {
37939
38171
  console.info(colors__default["default"].green('Watching direct child agent repositories for queued messages.'));
37940
38172
  }
@@ -37957,14 +38189,19 @@
37957
38189
  let projectSummaries = projectSummariesResult.projectSummaries;
37958
38190
  lastObservedProjectCount = projectSummaries.length;
37959
38191
  let ignoredAgentCount = projectSummariesResult.ignoredProjectCount + githubIgnoredRepositoryCount;
37960
- const autoPullResult = await pullLatestChangesForLocalAgentRunnerProjectsIfNeeded({
37961
- rootPath,
37962
- runOptions: options,
37963
- uiHandle,
37964
- projectSummaries,
37965
- ignoredAgentCount,
37966
- autoPullTimestampsByProjectPath,
37967
- });
38192
+ const autoPullResult = activeMessageTasksByKey.size === 0
38193
+ ? await pullLatestChangesForLocalAgentRunnerProjectsIfNeeded({
38194
+ rootPath,
38195
+ runOptions: options,
38196
+ uiHandle,
38197
+ projectSummaries,
38198
+ ignoredAgentCount,
38199
+ autoPullTimestampsByProjectPath,
38200
+ })
38201
+ : {
38202
+ isAnyRepositoryPulled: false,
38203
+ pulledProjectPaths: new Set(),
38204
+ };
37968
38205
  if (autoPullResult.isAnyRepositoryPulled) {
37969
38206
  projectSummariesResult = await loadLocalAgentRunnerProjectSummaries(rootPath, {
37970
38207
  includeMessagePreviews: Boolean(uiHandle),
@@ -37974,61 +38211,49 @@
37974
38211
  lastObservedProjectCount = projectSummaries.length;
37975
38212
  ignoredAgentCount = projectSummariesResult.ignoredProjectCount + githubIgnoredRepositoryCount;
37976
38213
  }
37977
- const queuedProjects = projectSummaries.filter((projectSummary) => projectSummary.queuedMessageCount > 0);
37978
- if (queuedProjects.length === 0) {
38214
+ const availableParallelMessageSlots = Math.max(0, maxParallelMessageCount - activeMessageTasksByKey.size);
38215
+ const queuedWorkItems = availableParallelMessageSlots > 0
38216
+ ? selectQueuedWorkItems({
38217
+ projectSummaries,
38218
+ activeMessageTasksByKey,
38219
+ availableParallelMessageSlots,
38220
+ isParallelMessageLimitConfigured,
38221
+ })
38222
+ : [];
38223
+ const answeringProjectPaths = createAnsweringProjectPaths(activeMessageTasksByKey, queuedWorkItems);
38224
+ if (queuedWorkItems.length === 0 && activeMessageTasksByKey.size === 0) {
37979
38225
  updateMultipleAgentRunUiForWatching(uiHandle, options, rootPath, projectSummaries, ignoredAgentCount);
37980
- await wait$3(MULTI_AGENT_QUEUE_POLL_INTERVAL_MS);
38226
+ await wait$3(queuePollIntervalMs);
38227
+ continue;
38228
+ }
38229
+ if (queuedWorkItems.length === 0) {
38230
+ updateMultipleAgentRunUiForAnswering(uiHandle, options, rootPath, projectSummaries, answeringProjectPaths, ignoredAgentCount, activeMessageTasksByKey.size);
38231
+ await waitForNextSchedulerTurn(activeMessageTasksByKey, queuePollIntervalMs);
37981
38232
  continue;
37982
38233
  }
37983
38234
  if (!uiHandle) {
37984
- for (const queuedProject of queuedProjects) {
37985
- console.info(colors__default["default"].blue(`Processing ${formatProjectPath(rootPath, queuedProject.project.projectPath)} with ${queuedProject.localAgentName}.`));
38235
+ for (const queuedWorkItem of queuedWorkItems) {
38236
+ console.info(colors__default["default"].blue(`Processing ${formatProjectPath(rootPath, queuedWorkItem.projectSummary.project.projectPath)}/${queuedWorkItem.queuedMessage.relativePath} with ${queuedWorkItem.projectSummary.localAgentName}.`));
37986
38237
  }
37987
38238
  }
37988
- const answeringProjectPaths = new Set(queuedProjects.map((queuedProject) => queuedProject.project.projectPath));
37989
- if (uiHandle) {
37990
- updateMultipleAgentRunUiForAnswering(uiHandle, options, rootPath, projectSummaries, answeringProjectPaths, ignoredAgentCount);
37991
- }
37992
- const tickResults = await Promise.allSettled(queuedProjects.map(async (queuedProject) => {
37993
- const tickRunOptions = createAgentRunOptionsForQueuedProjectTick({
37994
- runOptions: options,
37995
- isProjectPulledInCurrentIteration: autoPullResult.pulledProjectPaths.has(queuedProject.project.projectPath),
37996
- });
37997
- const tickResult = await tickAgentMessages(tickRunOptions, {
37998
- isQuietWhenIdle: true,
37999
- projectPath: queuedProject.project.projectPath,
38239
+ updateMultipleAgentRunUiForAnswering(uiHandle, options, rootPath, projectSummaries, answeringProjectPaths, ignoredAgentCount, activeMessageTasksByKey.size + queuedWorkItems.length);
38240
+ for (const queuedWorkItem of queuedWorkItems) {
38241
+ startActiveAgentMessageTask({
38242
+ rootPath,
38243
+ options,
38244
+ queuedWorkItem,
38245
+ projectSummaries,
38246
+ answeringProjectPaths,
38247
+ ignoredAgentCount,
38248
+ autoPullResult,
38249
+ autoPullTimestampsByProjectPath,
38250
+ activeMessageTasksByKey,
38251
+ messageFailureTracker,
38000
38252
  uiHandle,
38001
- uiPresentation: uiHandle
38002
- ? buildMultiAgentTickUiPresentation({
38003
- rootPath,
38004
- projectSummaries,
38005
- answeringProjectPaths,
38006
- ignoredAgentCount,
38007
- })
38008
- : undefined,
38009
- });
38010
- return {
38011
- projectPath: queuedProject.project.projectPath,
38012
- tickResult,
38013
- };
38014
- }));
38015
- for (const tickResult of tickResults) {
38016
- if (tickResult.status === 'fulfilled') {
38017
- if (tickResult.value.tickResult.autoPullTimestamp !== undefined) {
38018
- autoPullTimestampsByProjectPath.set(tickResult.value.projectPath, tickResult.value.tickResult.autoPullTimestamp);
38019
- }
38020
- if (tickResult.value.tickResult.isMessageProcessed) {
38021
- messageFailureTracker.clearMessageFailure(tickResult.value.projectPath, tickResult.value.tickResult.queuedMessage);
38022
- }
38023
- continue;
38024
- }
38025
- await handleAgentWatchError({
38026
- commandDisplayName: 'ptbk agent-folder run-multiple',
38027
- logDirectoryPath: controls.watchErrorLogDirectoryPath || rootPath,
38028
- error: tickResult.reason,
38253
+ watchErrorLogDirectoryPath: controls.watchErrorLogDirectoryPath || rootPath,
38029
38254
  });
38030
- await messageFailureTracker.recordFailure(tickResult.reason);
38031
38255
  }
38256
+ await waitForNextSchedulerTurn(activeMessageTasksByKey, queuePollIntervalMs);
38032
38257
  }
38033
38258
  catch (error) {
38034
38259
  await handleAgentWatchError({
@@ -38037,9 +38262,167 @@
38037
38262
  error,
38038
38263
  });
38039
38264
  await messageFailureTracker.recordFailure(error);
38040
- await wait$3(MULTI_AGENT_QUEUE_POLL_INTERVAL_MS);
38265
+ await wait$3(queuePollIntervalMs);
38266
+ }
38267
+ }
38268
+ await waitForActiveMessageTasksToSettle(activeMessageTasksByKey);
38269
+ }
38270
+ /**
38271
+ * Starts one selected queued-message harness task and records it until it settles.
38272
+ */
38273
+ function startActiveAgentMessageTask(options) {
38274
+ const { queuedWorkItem } = options;
38275
+ const projectPath = queuedWorkItem.projectSummary.project.projectPath;
38276
+ const messageKey = createAgentMessageTaskKey(projectPath, queuedWorkItem.queuedMessage);
38277
+ const promise = runActiveAgentMessageTask({
38278
+ rootPath: options.rootPath,
38279
+ options: options.options,
38280
+ queuedWorkItem,
38281
+ projectSummaries: options.projectSummaries,
38282
+ answeringProjectPaths: options.answeringProjectPaths,
38283
+ ignoredAgentCount: options.ignoredAgentCount,
38284
+ autoPullResult: options.autoPullResult,
38285
+ autoPullTimestampsByProjectPath: options.autoPullTimestampsByProjectPath,
38286
+ messageFailureTracker: options.messageFailureTracker,
38287
+ uiHandle: options.uiHandle,
38288
+ watchErrorLogDirectoryPath: options.watchErrorLogDirectoryPath,
38289
+ }).finally(() => {
38290
+ options.activeMessageTasksByKey.delete(messageKey);
38291
+ });
38292
+ options.activeMessageTasksByKey.set(messageKey, {
38293
+ projectPath,
38294
+ queuedMessage: queuedWorkItem.queuedMessage,
38295
+ promise,
38296
+ });
38297
+ }
38298
+ /**
38299
+ * Runs one queued-message tick and folds its result back into shared watcher state.
38300
+ */
38301
+ async function runActiveAgentMessageTask(options) {
38302
+ const projectPath = options.queuedWorkItem.projectSummary.project.projectPath;
38303
+ try {
38304
+ const tickRunOptions = createAgentRunOptionsForQueuedProjectTick({
38305
+ runOptions: options.options,
38306
+ isProjectPulledInCurrentIteration: options.autoPullResult.pulledProjectPaths.has(projectPath),
38307
+ });
38308
+ const tickResult = await tickAgentMessages(tickRunOptions, {
38309
+ isQuietWhenIdle: true,
38310
+ projectPath,
38311
+ queuedMessage: options.queuedWorkItem.queuedMessage,
38312
+ uiHandle: options.uiHandle,
38313
+ uiPresentation: options.uiHandle
38314
+ ? buildMultiAgentTickUiPresentation({
38315
+ rootPath: options.rootPath,
38316
+ projectSummaries: options.projectSummaries,
38317
+ answeringProjectPaths: options.answeringProjectPaths,
38318
+ ignoredAgentCount: options.ignoredAgentCount,
38319
+ })
38320
+ : undefined,
38321
+ });
38322
+ if (tickResult.autoPullTimestamp !== undefined) {
38323
+ options.autoPullTimestampsByProjectPath.set(projectPath, tickResult.autoPullTimestamp);
38324
+ }
38325
+ if (tickResult.isMessageProcessed) {
38326
+ options.messageFailureTracker.clearMessageFailure(projectPath, tickResult.queuedMessage);
38327
+ }
38328
+ }
38329
+ catch (error) {
38330
+ await handleAgentWatchError({
38331
+ commandDisplayName: 'ptbk agent-folder run-multiple',
38332
+ logDirectoryPath: options.watchErrorLogDirectoryPath,
38333
+ error,
38334
+ });
38335
+ await options.messageFailureTracker.recordFailure(error);
38336
+ }
38337
+ }
38338
+ /**
38339
+ * Selects the next queued messages that can be started without exceeding active harness capacity.
38340
+ */
38341
+ function selectQueuedWorkItems(options) {
38342
+ const activeProjectPaths = new Set(Array.from(options.activeMessageTasksByKey.values()).map((activeMessageTask) => activeMessageTask.projectPath));
38343
+ const queuedProjectSummaries = options.projectSummaries.filter((projectSummary) => {
38344
+ if (projectSummary.queuedMessageCount === 0) {
38345
+ return false;
38346
+ }
38347
+ return options.isParallelMessageLimitConfigured || !activeProjectPaths.has(projectSummary.project.projectPath);
38348
+ });
38349
+ if (!options.isParallelMessageLimitConfigured) {
38350
+ return queuedProjectSummaries
38351
+ .map((projectSummary) => selectFirstInactiveQueuedWorkItem(projectSummary, options.activeMessageTasksByKey))
38352
+ .filter((workItem) => workItem !== null);
38353
+ }
38354
+ const workItems = [];
38355
+ for (let messageIndex = 0; workItems.length < options.availableParallelMessageSlots; messageIndex++) {
38356
+ let isAnyMessageAtIndex = false;
38357
+ for (const projectSummary of queuedProjectSummaries) {
38358
+ const queuedMessage = projectSummary.queuedMessages[messageIndex];
38359
+ if (!queuedMessage) {
38360
+ continue;
38361
+ }
38362
+ isAnyMessageAtIndex = true;
38363
+ if (options.activeMessageTasksByKey.has(createAgentMessageTaskKey(projectSummary.project.projectPath, queuedMessage))) {
38364
+ continue;
38365
+ }
38366
+ workItems.push({ projectSummary, queuedMessage });
38367
+ if (workItems.length >= options.availableParallelMessageSlots) {
38368
+ return workItems;
38369
+ }
38370
+ }
38371
+ if (!isAnyMessageAtIndex) {
38372
+ return workItems;
38041
38373
  }
38042
38374
  }
38375
+ return workItems;
38376
+ }
38377
+ /**
38378
+ * Selects the first inactive queued message from a project while preserving legacy one-message-per-project behavior.
38379
+ */
38380
+ function selectFirstInactiveQueuedWorkItem(projectSummary, activeMessageTasksByKey) {
38381
+ const queuedMessage = projectSummary.queuedMessages.find((message) => !activeMessageTasksByKey.has(createAgentMessageTaskKey(projectSummary.project.projectPath, message)));
38382
+ return queuedMessage ? { projectSummary, queuedMessage } : null;
38383
+ }
38384
+ /**
38385
+ * Builds the set of project paths currently represented by active or newly scheduled harness tasks.
38386
+ */
38387
+ function createAnsweringProjectPaths(activeMessageTasksByKey, queuedWorkItems) {
38388
+ return new Set([
38389
+ ...Array.from(activeMessageTasksByKey.values()).map((activeMessageTask) => activeMessageTask.projectPath),
38390
+ ...queuedWorkItems.map((queuedWorkItem) => queuedWorkItem.projectSummary.project.projectPath),
38391
+ ]);
38392
+ }
38393
+ /**
38394
+ * Creates a stable scheduler key for one queued message file.
38395
+ */
38396
+ function createAgentMessageTaskKey(projectPath, queuedMessage) {
38397
+ return `${projectPath}\0${queuedMessage.relativePath}`;
38398
+ }
38399
+ /**
38400
+ * Normalizes the maximum number of parallel queued-message tasks.
38401
+ */
38402
+ function normalizeMaxParallelMessageCount(rawValue) {
38403
+ const parsedValue = Number(rawValue);
38404
+ if (!Number.isFinite(parsedValue) || parsedValue <= 0) {
38405
+ return Number.POSITIVE_INFINITY;
38406
+ }
38407
+ return Math.floor(parsedValue);
38408
+ }
38409
+ /**
38410
+ * Waits for either one active task to finish or the next queue polling interval.
38411
+ */
38412
+ async function waitForNextSchedulerTurn(activeMessageTasksByKey, queuePollIntervalMs) {
38413
+ if (activeMessageTasksByKey.size === 0) {
38414
+ return;
38415
+ }
38416
+ await Promise.race([
38417
+ ...Array.from(activeMessageTasksByKey.values()).map((activeMessageTask) => activeMessageTask.promise),
38418
+ wait$3(queuePollIntervalMs),
38419
+ ]);
38420
+ }
38421
+ /**
38422
+ * Preserves previous shutdown behavior by letting active harness tasks finish before returning.
38423
+ */
38424
+ async function waitForActiveMessageTasksToSettle(activeMessageTasksByKey) {
38425
+ await Promise.allSettled(Array.from(activeMessageTasksByKey.values()).map((activeMessageTask) => activeMessageTask.promise));
38043
38426
  }
38044
38427
  /**
38045
38428
  * Creates the shared multi-agent rich UI when the terminal supports it.
@@ -38185,6 +38568,7 @@
38185
38568
  project,
38186
38569
  localAgentName: localAgentIdentity.localAgentName,
38187
38570
  localAgentUrl: localAgentIdentity.localAgentUrl || formatProjectPath(rootPath, project.projectPath),
38571
+ queuedMessages: queueSnapshot.queuedMessages,
38188
38572
  queuedMessageCount: queueSnapshot.queuedMessages.length,
38189
38573
  finishedMessageCount: queueSnapshot.finishedMessageCount,
38190
38574
  ...(queuedMessagePreview ? { queuedMessagePreview } : {}),
@@ -38216,14 +38600,16 @@
38216
38600
  /**
38217
38601
  * Updates the shared UI while one or more child repositories are answering queued messages.
38218
38602
  */
38219
- function updateMultipleAgentRunUiForAnswering(uiHandle, options, rootPath, projectSummaries, answeringProjectPaths, ignoredAgentCount) {
38220
- const activeProjectCount = projectSummaries.filter((projectSummary) => answeringProjectPaths.has(projectSummary.project.projectPath)).length;
38603
+ function updateMultipleAgentRunUiForAnswering(uiHandle, options, rootPath, projectSummaries, answeringProjectPaths, ignoredAgentCount, activeMessageCount) {
38604
+ if (!uiHandle) {
38605
+ return;
38606
+ }
38221
38607
  setMultipleAgentRunUiConfig(uiHandle, options, projectSummaries.length, ignoredAgentCount);
38222
38608
  uiHandle.state.updateProgress(createMultiAgentQueueProgressSnapshot(projectSummaries));
38223
38609
  uiHandle.state.setCurrentPrompt('');
38224
38610
  uiHandle.state.setCurrentScriptPaths(buildMultiAgentScriptPaths(projectSummaries, answeringProjectPaths));
38225
38611
  uiHandle.state.setPhase('running');
38226
- uiHandle.state.setStatusMessage(`Answering ${activeProjectCount} queued message${activeProjectCount === 1 ? '' : 's'}`);
38612
+ uiHandle.state.setStatusMessage(`Answering ${activeMessageCount} queued message${activeMessageCount === 1 ? '' : 's'}`);
38227
38613
  uiHandle.state.setDetailLines(buildMultiAgentAnsweringDetailLines(rootPath, projectSummaries, answeringProjectPaths));
38228
38614
  uiHandle.state.setAgentStatusLines(buildMultiAgentStatusLines(rootPath, projectSummaries, answeringProjectPaths));
38229
38615
  uiHandle.state.setAgentStatusTableRows(buildMultiAgentStatusTableRows(projectSummaries, answeringProjectPaths));
@@ -38525,6 +38911,12 @@
38525
38911
  * @private shared Agents Server constant
38526
38912
  */
38527
38913
  const DEFAULT_LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS = 3;
38914
+ /**
38915
+ * Default maximum number of local runner harness instances allowed to answer queued messages at once.
38916
+ *
38917
+ * @private shared Agents Server constant
38918
+ */
38919
+ const DEFAULT_LOCAL_AGENT_RUNNER_MAX_PARALLEL_MESSAGES = 3;
38528
38920
  /**
38529
38921
  * Stable keys used by the dedicated server-limits table.
38530
38922
  *
@@ -38539,6 +38931,7 @@
38539
38931
  SPAWN_AGENT_RATE_LIMIT_MAX: 'SPAWN_AGENT_RATE_LIMIT_MAX',
38540
38932
  SPAWN_AGENT_RATE_LIMIT_WINDOW_MS: 'SPAWN_AGENT_RATE_LIMIT_WINDOW_MS',
38541
38933
  LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS: 'LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS',
38934
+ LOCAL_AGENT_RUNNER_MAX_PARALLEL_MESSAGES: 'LOCAL_AGENT_RUNNER_MAX_PARALLEL_MESSAGES',
38542
38935
  };
38543
38936
  /**
38544
38937
  * Shared metadata for all server limits exposed in the admin UI and runtime loaders.
@@ -38634,6 +39027,17 @@
38634
39027
  step: 1,
38635
39028
  legacyMetadataKeys: [],
38636
39029
  },
39030
+ {
39031
+ key: SERVER_LIMIT_KEYS.LOCAL_AGENT_RUNNER_MAX_PARALLEL_MESSAGES,
39032
+ category: 'Local agent runner',
39033
+ title: 'Max parallel harness instances',
39034
+ description: 'Caps how many queued chat messages the local coding-agent harness may answer at the same time.',
39035
+ unit: 'count',
39036
+ defaultValue: DEFAULT_LOCAL_AGENT_RUNNER_MAX_PARALLEL_MESSAGES,
39037
+ minimumValue: 1,
39038
+ step: 1,
39039
+ legacyMetadataKeys: [],
39040
+ },
38637
39041
  ];
38638
39042
  /**
38639
39043
  * Fast lookup map used by server-limit validators and UI helpers.
@@ -38995,6 +39399,7 @@
38995
39399
  autoPull: false,
38996
39400
  autoClone: false,
38997
39401
  maxMessageProcessingFailures: localAgentRunnerLimits.maxFailedAttempts,
39402
+ maxParallelMessages: localAgentRunnerLimits.maxParallelMessages,
38998
39403
  };
38999
39404
  }
39000
39405
  /**
@@ -39006,7 +39411,7 @@
39006
39411
  while (options.state.isContinuing && Date.now() - startedAt < INTERNAL_SERVER_LIMITS_STARTUP_TIMEOUT_MS) {
39007
39412
  try {
39008
39413
  const limits = await fetchLocalAgentRunnerLimits(options);
39009
- logRunnerEvent(options.logStreams.runner, `Local agent runner max failed attempts: ${limits.maxFailedAttempts}.`);
39414
+ logRunnerEvent(options.logStreams.runner, `Local agent runner limits: ${limits.maxFailedAttempts} failed attempt(s), ${limits.maxParallelMessages} parallel message(s).`);
39010
39415
  return limits;
39011
39416
  }
39012
39417
  catch (error) {
@@ -39017,6 +39422,7 @@
39017
39422
  if (!options.state.isContinuing) {
39018
39423
  return {
39019
39424
  maxFailedAttempts: DEFAULT_LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS,
39425
+ maxParallelMessages: DEFAULT_LOCAL_AGENT_RUNNER_MAX_PARALLEL_MESSAGES,
39020
39426
  };
39021
39427
  }
39022
39428
  throw new NotAllowed(_spaceTrim.spaceTrim(`
@@ -39043,6 +39449,7 @@
39043
39449
  const payload = (await response.json());
39044
39450
  return {
39045
39451
  maxFailedAttempts: normalizeLocalAgentRunnerMaxFailedAttempts(payload.maxFailedAttempts),
39452
+ maxParallelMessages: normalizeLocalAgentRunnerMaxParallelMessages(payload.maxParallelMessages),
39046
39453
  };
39047
39454
  }
39048
39455
  /**
@@ -39055,6 +39462,16 @@
39055
39462
  }
39056
39463
  return Math.floor(parsedValue);
39057
39464
  }
39465
+ /**
39466
+ * Normalizes the local runner parallel message cap returned by the internal server route.
39467
+ */
39468
+ function normalizeLocalAgentRunnerMaxParallelMessages(rawValue) {
39469
+ const parsedValue = Number(rawValue);
39470
+ if (!Number.isFinite(parsedValue) || parsedValue <= 0) {
39471
+ return DEFAULT_LOCAL_AGENT_RUNNER_MAX_PARALLEL_MESSAGES;
39472
+ }
39473
+ return Math.floor(parsedValue);
39474
+ }
39058
39475
  /**
39059
39476
  * Starts periodic internal worker calls that queue and reconcile local message-folder jobs.
39060
39477
  */
@@ -40309,7 +40726,7 @@
40309
40726
  */
40310
40727
  const DEFAULT_CODER_PACKAGE_JSON_SCRIPTS = {
40311
40728
  'coder:generate-boilerplates': 'ptbk coder generate-boilerplates --template ./prompts/templates/common.md',
40312
- 'coder:run': 'ptbk coder run --harness openai-codex --model gpt-5.4 --thinking-level xhigh --agent agents/developer.book --context AGENTS.md',
40729
+ 'coder:run': 'ptbk coder run --harness openai-codex --model gpt-5.5 --thinking-level xhigh --agent agents/developer.book --context AGENTS.md',
40313
40730
  // 'coder:find-refactor-candidates': 'ptbk coder find-refactor-candidates',
40314
40731
  'coder:verify': 'ptbk coder verify',
40315
40732
  };
@@ -40789,6 +41206,7 @@
40789
41206
  command.option('--preserve-logs', 'Keep generated temp prompt/log artifacts after successful rounds for debugging and analytics', false);
40790
41207
  addPromptRunnerExecutionOptions(command);
40791
41208
  command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption$1, 0);
41209
+ command.option('--limit <run-count>', 'Stop after processing this many prompt runs', parsePositiveIntegerOption);
40792
41210
  command.option('--wait-after-prompt <duration>', _spaceTrim.spaceTrim(`
40793
41211
  Wait this long after each prompt has been implemented, verified and committed before starting the next prompt.
40794
41212
  Accepts durations like 1h, 30m, 5s. Defaults to 0 (no wait).
@@ -40807,7 +41225,7 @@
40807
41225
  command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
40808
41226
  command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
40809
41227
  command.action(handleActionErrors(async (cliOptions) => {
40810
- const { dryRun, agent, context, test, preserveLogs, priority, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
41228
+ const { dryRun, agent, context, test, preserveLogs, priority, limit, waitAfterPrompt: waitAfterPromptValue, waitBetweenPrompts: waitBetweenPromptsValue, waitAfterError: waitAfterErrorValue, auto, autoMigrate, allowDestructiveAutoMigrate, } = cliOptions;
40811
41229
  const testCommand = normalizeCommandOptionValue$1(test);
40812
41230
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
40813
41231
  isAgentRequired: !dryRun,
@@ -40840,6 +41258,7 @@
40840
41258
  noUi: runnerOptions.noUi,
40841
41259
  thinkingLevel: runnerOptions.thinkingLevel,
40842
41260
  priority,
41261
+ limit,
40843
41262
  normalizeLineEndings: runnerOptions.normalizeLineEndings,
40844
41263
  allowCredits: runnerOptions.allowCredits,
40845
41264
  autoMigrate,
@@ -40874,6 +41293,22 @@
40874
41293
  }
40875
41294
  return parsed;
40876
41295
  }
41296
+ /**
41297
+ * Parses a positive integer option value.
41298
+ *
41299
+ * @private internal utility of `coder run` command
41300
+ */
41301
+ function parsePositiveIntegerOption(value) {
41302
+ const parsed = Number(value);
41303
+ if (!Number.isInteger(parsed) || parsed <= 0) {
41304
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
41305
+ Invalid value for \`--limit\`: \`${value}\`.
41306
+
41307
+ Use a positive integer.
41308
+ `));
41309
+ }
41310
+ return parsed;
41311
+ }
40877
41312
  /**
40878
41313
  * Joins one Commander option that may be parsed either as a single string or a variadic token array.
40879
41314
  *
@@ -70289,7 +70724,7 @@
70289
70724
  /**
70290
70725
  * CLI usage text for this script.
70291
70726
  */
70292
- 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]';
70727
+ 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>] [--limit <run-count>] [--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]';
70293
70728
  /**
70294
70729
  * Top-level flags supported by this command.
70295
70730
  */
@@ -70303,6 +70738,7 @@
70303
70738
  '--no-ui',
70304
70739
  '--thinking-level',
70305
70740
  '--priority',
70741
+ '--limit',
70306
70742
  '--allow-credits',
70307
70743
  '--auto-migrate',
70308
70744
  '--allow-destructive-auto-migrate',
@@ -70339,6 +70775,8 @@
70339
70775
  const thinkingLevelValue = readOptionValue(args, '--thinking-level');
70340
70776
  const hasPriorityFlag = args.includes('--priority');
70341
70777
  const priority = parsePriority(readOptionValue(args, '--priority'), hasPriorityFlag);
70778
+ const hasLimitFlag = args.includes('--limit');
70779
+ const limit = parseLimit(readOptionValue(args, '--limit'), hasLimitFlag);
70342
70780
  const noCommit = args.includes('--no-commit');
70343
70781
  const ignoreGitChanges = args.includes('--ignore-git-changes');
70344
70782
  const normalizeLineEndings = !args.includes('--no-normalize-line-endings');
@@ -70393,6 +70831,7 @@
70393
70831
  testCommand,
70394
70832
  thinkingLevel,
70395
70833
  priority,
70834
+ limit,
70396
70835
  };
70397
70836
  }
70398
70837
  /**
@@ -70456,6 +70895,22 @@
70456
70895
  }
70457
70896
  return priority;
70458
70897
  }
70898
+ /**
70899
+ * Parses and validates the optional successful prompt-run limit.
70900
+ */
70901
+ function parseLimit(limitValue, hasLimitFlag) {
70902
+ if (limitValue === undefined) {
70903
+ if (hasLimitFlag) {
70904
+ exitWithUsageError('Missing value for --limit. Use a positive integer.');
70905
+ }
70906
+ return undefined;
70907
+ }
70908
+ const limit = Number(limitValue);
70909
+ if (!Number.isInteger(limit) || limit <= 0) {
70910
+ exitWithUsageError(`Invalid value for --limit: "${limitValue}". Use a positive integer.`);
70911
+ }
70912
+ return limit;
70913
+ }
70459
70914
  /**
70460
70915
  * Prints an argument error with usage and exits the process.
70461
70916
  */
@@ -72565,11 +73020,12 @@
72565
73020
  // Prepend agent system message before the task so the harness sees agent instructions first
72566
73021
  const promptWithAgent = resolvedAgentSystemMessage
72567
73022
  ? _spaceTrim.spaceTrim((block) => `
72568
- ${block(resolvedAgentSystemMessage.trim())}
72569
-
73023
+
72570
73024
  ## Your Task
72571
73025
 
72572
73026
  ${block(taskPrompt)}
73027
+
73028
+ ${block(resolvedAgentSystemMessage.trim())}
72573
73029
  `)
72574
73030
  : taskPrompt;
72575
73031
  const codexPrompt = appendCoderContext(promptWithAgent, resolvedCoderContext);
@@ -72892,6 +73348,7 @@
72892
73348
  let hasWaitedForStart = false;
72893
73349
  let previousRoundStartTime;
72894
73350
  let previousRoundEndTime;
73351
+ let completedRunCount = 0;
72895
73352
  while (just(true)) {
72896
73353
  if (options.autoPull && !options.dryRun) {
72897
73354
  await waitForRequestedPause({
@@ -72977,6 +73434,15 @@
72977
73434
  });
72978
73435
  previousRoundStartTime = currentRoundStartTime;
72979
73436
  previousRoundEndTime = Date.now();
73437
+ completedRunCount += 1;
73438
+ if (isRunLimitReached({ completedRunCount, limit: options.limit })) {
73439
+ finishWhenRunLimitIsReached({
73440
+ completedRunCount,
73441
+ isRichUiEnabled,
73442
+ uiHandle,
73443
+ });
73444
+ return;
73445
+ }
72980
73446
  }
72981
73447
  }
72982
73448
  finally {
@@ -73006,6 +73472,13 @@
73006
73472
  Auto-pull keeps the repository up to date between prompt rounds, which requires each successful round to end with a clean committed working tree.
73007
73473
  `));
73008
73474
  }
73475
+ if (options.limit !== undefined && (!Number.isInteger(options.limit) || options.limit <= 0)) {
73476
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
73477
+ Flag \`--limit\` expects a positive integer.
73478
+
73479
+ Received: \`${options.limit}\`
73480
+ `));
73481
+ }
73009
73482
  }
73010
73483
  /**
73011
73484
  * Pulls the latest repository state before loading prompts when the feature is enabled.
@@ -73152,6 +73625,21 @@
73152
73625
  }
73153
73626
  return true;
73154
73627
  }
73628
+ /**
73629
+ * Checks whether the configured successful prompt-run limit has been reached.
73630
+ */
73631
+ function isRunLimitReached(options) {
73632
+ const { completedRunCount, limit } = options;
73633
+ return limit !== undefined && completedRunCount >= limit;
73634
+ }
73635
+ /**
73636
+ * Updates UI and console output when a user-configured run limit stops the loop.
73637
+ */
73638
+ function finishWhenRunLimitIsReached(options) {
73639
+ const { completedRunCount, isRichUiEnabled, uiHandle } = options;
73640
+ const runCountLabel = completedRunCount === 1 ? '1 prompt run' : `${completedRunCount} prompt runs`;
73641
+ announceRunCompletion(`Run limit reached after ${runCountLabel}.`, colors__default["default"].green, isRichUiEnabled, uiHandle);
73642
+ }
73155
73643
  /**
73156
73644
  * Updates the UI status message while waiting for new prompts in keepAlive server mode.
73157
73645
  */