@promptbook/cli 0.112.0-103 → 0.112.0-105

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 (190) hide show
  1. package/apps/agents-server/src/app/AddAgentButton.tsx +0 -5
  2. package/apps/agents-server/src/app/actions.ts +50 -0
  3. package/apps/agents-server/src/app/admin/image-generator-test/ImageAttachmentsEditor.tsx +19 -3
  4. package/apps/agents-server/src/app/admin/limits/LimitsClient.tsx +11 -12
  5. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +34 -2
  6. package/apps/agents-server/src/app/admin/servers/CreateServerDialog.tsx +6 -1
  7. package/apps/agents-server/src/app/admin/servers/useCreateServerWizard.ts +13 -1
  8. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +11 -2
  9. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +14 -5
  10. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +7 -1
  11. package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +11 -1
  12. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +6 -2
  13. package/apps/agents-server/src/app/api/health/route.ts +18 -0
  14. package/apps/agents-server/src/app/api/images/[filename]/route.ts +6 -2
  15. package/apps/agents-server/src/app/api/internal/agent-runner-limits/route.ts +51 -0
  16. package/apps/agents-server/src/app/api/upload/route.ts +48 -12
  17. package/apps/agents-server/src/app/layout.tsx +13 -0
  18. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +1 -4
  19. package/apps/agents-server/src/components/FileUploadAvailability/FileUploadAvailabilityContext.tsx +50 -0
  20. package/apps/agents-server/src/components/FileUploadAvailability/FileUploadUnavailableNotice.tsx +45 -0
  21. package/apps/agents-server/src/components/Header/Header.tsx +0 -11
  22. package/apps/agents-server/src/components/Header/useHeaderAgentMenus.tsx +0 -5
  23. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +85 -76
  24. package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +7 -3
  25. package/apps/agents-server/src/components/NewAgentDialog/NewAgentWizardKnowledgeStep.tsx +6 -0
  26. package/apps/agents-server/src/components/NewAgentDialog/useNewAgentDialog.tsx +39 -16
  27. package/apps/agents-server/src/components/NewAgentDialog/useNewAgentWizardKnowledgeState.ts +8 -1
  28. package/apps/agents-server/src/constants/defaultAgentAvatarVisual.ts +1 -1
  29. package/apps/agents-server/src/constants/serverLimits.ts +22 -2
  30. package/apps/agents-server/src/database/migrations/2026-06-0200-default-agent-avatar-visual-octopus3d3.sql +16 -0
  31. package/apps/agents-server/src/database/seedDefaultAgents.ts +218 -0
  32. package/apps/agents-server/src/middleware.ts +2 -1
  33. package/apps/agents-server/src/tools/$provideCdnForServer.ts +114 -9
  34. package/apps/agents-server/src/utils/agentRouting/resolveAgentRouteTarget.ts +27 -4
  35. package/apps/agents-server/src/utils/defaultAgents/loadDefaultAgentBooks.ts +103 -0
  36. package/apps/agents-server/src/utils/knowledge/createInlineKnowledgeSourceUploader.ts +24 -5
  37. package/apps/agents-server/src/utils/serverLimits.ts +26 -1
  38. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +1 -85
  39. package/apps/agents-server/src/utils/shareTargetPayloads.ts +20 -2
  40. package/apps/agents-server/src/utils/upload/fileUploadAvailability.ts +91 -0
  41. package/apps/agents-server/src/utils/upload/uploadFileToServer.ts +46 -2
  42. package/esm/apps/agents-server/src/constants/federatedAgentImport.d.ts +42 -0
  43. package/esm/apps/agents-server/src/constants/serverLimits.d.ts +207 -0
  44. package/esm/apps/agents-server/src/constants/toolUsageLimits.d.ts +55 -0
  45. package/esm/index.es.js +1109 -35
  46. package/esm/index.es.js.map +1 -1
  47. package/esm/scripts/run-agent-messages/main/AgentMessageFailureTracker.d.ts +27 -0
  48. package/esm/scripts/run-agent-messages/main/handleAgentWatchError.d.ts +4 -0
  49. package/esm/scripts/run-agent-messages/main/runAgentMessages.d.ts +1 -0
  50. package/esm/scripts/run-agent-messages/messages/moveAgentMessageToFailed.d.ts +17 -0
  51. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  52. package/esm/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
  53. package/esm/src/book-components/BookEditor/BookEditor.d.ts +5 -4
  54. package/esm/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
  55. package/esm/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
  56. package/esm/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
  57. package/esm/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
  58. package/esm/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +6 -0
  59. package/esm/src/version.d.ts +1 -1
  60. package/package.json +1 -1
  61. package/src/avatars/types/AvatarVisualDefinition.ts +1 -0
  62. package/src/avatars/visuals/avatarVisualRegistry.ts +2 -0
  63. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +902 -0
  64. package/src/book-components/BookEditor/BookEditor.tsx +10 -7
  65. package/src/book-components/BookEditor/BookEditorMonaco.tsx +3 -1
  66. package/src/book-components/BookEditor/BookEditorTheme.ts +32 -0
  67. package/src/book-components/BookEditor/useBookEditorMonacoLanguage.ts +12 -15
  68. package/src/book-components/BookEditor/useBookEditorMonacoLifecycle.ts +1 -5
  69. package/src/book-components/BookEditor/useBookEditorMonacoStyles.ts +2 -1
  70. package/src/cli/cli-commands/agent-folder/agentProjectPaths.ts +7 -0
  71. package/src/cli/cli-commands/agents-server/buildAgentsServer.ts +109 -9
  72. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +132 -4
  73. package/src/other/templates/getTemplatesPipelineCollection.ts +690 -747
  74. package/src/utils/agents/resolveAgentAvatarImageUrl.ts +1 -1
  75. package/src/version.ts +2 -2
  76. package/src/versions.txt +2 -1
  77. package/umd/apps/agents-server/src/constants/federatedAgentImport.d.ts +42 -0
  78. package/umd/apps/agents-server/src/constants/serverLimits.d.ts +207 -0
  79. package/umd/apps/agents-server/src/constants/toolUsageLimits.d.ts +55 -0
  80. package/umd/index.umd.js +1109 -35
  81. package/umd/index.umd.js.map +1 -1
  82. package/umd/scripts/run-agent-messages/main/AgentMessageFailureTracker.d.ts +27 -0
  83. package/umd/scripts/run-agent-messages/main/handleAgentWatchError.d.ts +4 -0
  84. package/umd/scripts/run-agent-messages/main/runAgentMessages.d.ts +1 -0
  85. package/umd/scripts/run-agent-messages/messages/moveAgentMessageToFailed.d.ts +17 -0
  86. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  87. package/umd/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
  88. package/umd/src/book-components/BookEditor/BookEditor.d.ts +5 -4
  89. package/umd/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
  90. package/umd/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
  91. package/umd/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
  92. package/umd/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
  93. package/umd/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +6 -0
  94. package/umd/src/version.d.ts +1 -1
  95. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +0 -108
  96. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +0 -117
  97. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +0 -119
  98. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +0 -74
  99. package/apps/agents-server/tests/e2e/authentication-and-navigation.spec.ts.todo +0 -178
  100. package/src/_packages/browser.index.ts +0 -31
  101. package/src/_packages/browser.readme.md +0 -43
  102. package/src/book-2.0/agent-source/parseAgentSourceWithCommitments.test.ts.todo +0 -265
  103. package/src/book-components/BookEditor/BookEditorMonaco.test.tsx.todo +0 -115
  104. package/src/book-components/Chat/utils/renderMarkdown.test.ts.tmp +0 -199
  105. package/src/collection/agent-collection/constructors/agent-collection-in-directory/AgentCollectionInDirectory.test.ts.todo +0 -131
  106. package/src/commands/_common/parseCommand.test.ts.todo +0 -48
  107. package/src/commitments/META_LINK/META_LINK.test.ts.todo +0 -75
  108. package/src/conversion/validation/pipelineStringToJson-errors.test.ts.todo +0 -33
  109. package/src/dialogs/simple-prompt/SimplePromptInterfaceTools.ts +0 -51
  110. package/src/executables/browsers/locateSafari.test.ts.tmp +0 -15
  111. package/src/execution/PromptbookFetch.test-type.ts +0 -14
  112. package/src/execution/createPipelineExecutor/00-createPipelineExecutor.test.ts.todo +0 -0
  113. package/src/execution/execution-report/executionReportJsonToString.test.ts.todo +0 -83
  114. package/src/execution/utils/usageToHuman.test.ts.todo +0 -80
  115. package/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.ts +0 -76
  116. package/src/llm-providers/_common/utils/assertUniqueModels.ts +0 -27
  117. package/src/llm-providers/_multiple/playground/playground.ts +0 -141
  118. package/src/llm-providers/_multiple/playground/tsconfig.json +0 -19
  119. package/src/llm-providers/agent/playground/playground.ts +0 -190
  120. package/src/llm-providers/agent/playground/tsconfig.json +0 -19
  121. package/src/llm-providers/anthropic-claude/playground/playground.ts +0 -99
  122. package/src/llm-providers/anthropic-claude/playground/tsconfig.json +0 -19
  123. package/src/llm-providers/azure-openai/playground/playground.ts +0 -101
  124. package/src/llm-providers/azure-openai/playground/tsconfig.json +0 -19
  125. package/src/llm-providers/ollama/playground/playground.ts +0 -120
  126. package/src/llm-providers/ollama/playground/tsconfig.json +0 -19
  127. package/src/llm-providers/openai/playground/playground.ts +0 -406
  128. package/src/llm-providers/openai/playground/tsconfig.json +0 -19
  129. package/src/llm-providers/remote/playground/playground.ts +0 -144
  130. package/src/llm-providers/remote/playground/tsconfig.json +0 -19
  131. package/src/llm-providers/vercel/playground/playground.ts +0 -133
  132. package/src/llm-providers/vercel/playground/tsconfig.json +0 -19
  133. package/src/personas/preparePersona.test.ts.todo +0 -126
  134. package/src/playground/backup/_playground-boilerplate.ts.txt +0 -37
  135. package/src/playground/backup/playground-agent-os.txt +0 -62
  136. package/src/playground/backup/playground-brj-app.ts.txt +0 -302
  137. package/src/playground/backup/playground-browser-playwright.txt +0 -110
  138. package/src/playground/backup/playground-claude-mcp.txt +0 -43
  139. package/src/playground/backup/playground-document-conversion.txt +0 -84
  140. package/src/playground/backup/playground-glob.ts.txt +0 -42
  141. package/src/playground/backup/playground-mcp-server.txt +0 -1
  142. package/src/playground/backup/playground-openai-agent-kit.txt +0 -73
  143. package/src/playground/backup/playground-openai-function-calling.txt +0 -131
  144. package/src/playground/backup/playground-openai-streaming.ts.txt +0 -68
  145. package/src/playground/backup/playground-scrape-knowledge.txt +0 -65
  146. package/src/playground/backup/playground-scraperFetch.ts.txt +0 -44
  147. package/src/playground/backup/playground-using-openai-compatible-route-on-agents-server.ts.txt +0 -49
  148. package/src/playground/backup/playground-write-pavolhejny-bio.txt +0 -120
  149. package/src/playground/permanent/_boilerplate.ts +0 -54
  150. package/src/playground/permanent/agent-with-browser-playground.ts +0 -92
  151. package/src/playground/permanent/error-handling-playground.ts +0 -103
  152. package/src/playground/playground.ts +0 -36
  153. package/src/playground/tsconfig.json +0 -19
  154. package/src/scrapers/_boilerplate/BoilerplateScraper.test.ts.todo +0 -73
  155. package/src/scrapers/_boilerplate/playground/boilerplate-scraper-playground.ts +0 -79
  156. package/src/scrapers/_boilerplate/playground/tsconfig.json +0 -19
  157. package/src/scrapers/_common/utils/files/blobToDataurl.test.ts.todo +0 -17
  158. package/src/scrapers/_common/utils/files/dataurlToBlob.test.ts.todo +0 -52
  159. package/src/scrapers/_common/utils/files/isValidDataurl.test.ts.todo +0 -42
  160. package/src/scrapers/_common/utils/files/shorten.test.ts.todo +0 -13
  161. package/src/scrapers/document/playground/document-scraper-playground.ts +0 -80
  162. package/src/scrapers/document/playground/tsconfig.json +0 -19
  163. package/src/scrapers/document-legacy/playground/legacy-document-scraper-playground.ts +0 -80
  164. package/src/scrapers/document-legacy/playground/tsconfig.json +0 -19
  165. package/src/scrapers/markdown/playground/markdown-scraper-playground.ts +0 -74
  166. package/src/scrapers/markdown/playground/tsconfig.json +0 -19
  167. package/src/scrapers/markitdown/MarkitdownScraper.test.ts.todo +0 -132
  168. package/src/scrapers/markitdown/playground/markitdown-scraper-playground.ts +0 -91
  169. package/src/scrapers/markitdown/playground/tsconfig.json +0 -19
  170. package/src/scrapers/pdf/PdfScraper.test.ts.todo +0 -52
  171. package/src/scrapers/pdf/playground/pdf-scraper-playground.ts +0 -75
  172. package/src/scrapers/pdf/playground/tsconfig.json +0 -19
  173. package/src/scrapers/website/playground/tsconfig.json +0 -19
  174. package/src/scrapers/website/playground/website-scraper-playground.ts +0 -82
  175. package/src/storage/_common/PromptbookStorage.test-type.ts +0 -14
  176. package/src/storage/local-storage/getIndexedDbStorage.ts +0 -36
  177. package/src/storage/local-storage/getLocalStorage.ts +0 -33
  178. package/src/storage/local-storage/getSessionStorage.ts +0 -33
  179. package/src/storage/local-storage/utils/IndexedDbStorageOptions.ts +0 -16
  180. package/src/storage/local-storage/utils/makePromptbookStorageFromIndexedDb.ts +0 -58
  181. package/src/storage/local-storage/utils/makePromptbookStorageFromWebStorage.ts +0 -45
  182. package/src/transpilers/formatted-book-in-markdown/FormattedBookInMarkdownTranspiler.test.ts.todo +0 -35
  183. package/src/transpilers/openai-sdk/playground/playground.ts +0 -85
  184. package/src/transpilers/openai-sdk/playground/tmp/chatbot-openaisdk-1.js +0 -194
  185. package/src/transpilers/openai-sdk/playground/tmp/package.json +0 -3
  186. package/src/transpilers/openai-sdk/playground/tsconfig.json +0 -18
  187. package/src/utils/editable/utils/findUsableParameters.test.ts.todo +0 -43
  188. package/src/utils/editable/utils/stringifyPipelineJson.test.ts.todo +0 -38
  189. package/src/utils/markdown/prettifyMarkdown.test.ts.tmp +0 -42
  190. package/src/utils/serialization/serializeToPromptbookJavascript.test.ts.todo +0 -116
package/umd/index.umd.js CHANGED
@@ -60,7 +60,7 @@
60
60
  * @generated
61
61
  * @see https://github.com/webgptorg/promptbook
62
62
  */
63
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-103';
63
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-105';
64
64
  /**
65
65
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
66
66
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1750,6 +1750,12 @@
1750
1750
  * @private internal utility of `ptbk agent-folder`
1751
1751
  */
1752
1752
  const AGENT_FINISHED_MESSAGES_DIRECTORY_PATH = path.join(AGENT_MESSAGES_DIRECTORY_PATH, 'finished');
1753
+ /**
1754
+ * Relative path to messages that the agent runner stopped retrying.
1755
+ *
1756
+ * @private internal utility of `ptbk agent-folder`
1757
+ */
1758
+ const AGENT_FAILED_MESSAGES_DIRECTORY_PATH = path.join(AGENT_MESSAGES_DIRECTORY_PATH, 'failed');
1753
1759
  /**
1754
1760
  * Relative path to generated local agent documentation initialized by `ptbk agent-folder init`.
1755
1761
  *
@@ -3160,12 +3166,38 @@
3160
3166
  'playwright-report',
3161
3167
  'test-results',
3162
3168
  ]);
3169
+ /**
3170
+ * Runtime source files excluded from the packaged Agents Server copy.
3171
+ *
3172
+ * @private internal constant of `ptbk agents-server`
3173
+ */
3174
+ const AGENTS_SERVER_BUILD_INPUT_EXCLUDED_SOURCE_PATHS = new Set([
3175
+ 'src/_packages/browser.index.ts',
3176
+ 'src/_packages/browser.readme.md',
3177
+ 'src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.ts',
3178
+ 'src/llm-providers/_common/utils/assertUniqueModels.ts',
3179
+ ]);
3180
+ /**
3181
+ * Runtime source folders excluded from the packaged Agents Server copy.
3182
+ *
3183
+ * @private internal constant of `ptbk agents-server`
3184
+ */
3185
+ const AGENTS_SERVER_BUILD_INPUT_EXCLUDED_SOURCE_PATH_PREFIXES = [
3186
+ 'src/dialogs/simple-prompt',
3187
+ 'src/storage/local-storage',
3188
+ ];
3163
3189
  /**
3164
3190
  * Test files copied out of packaged runtime input paths because Next does not build them.
3165
3191
  *
3166
3192
  * @private internal constant of `ptbk agents-server`
3167
3193
  */
3168
- const AGENTS_SERVER_BUILD_INPUT_TEST_FILE_PATTERN = /\.(?:spec|test)\.[jt]sx?$/iu;
3194
+ const AGENTS_SERVER_BUILD_INPUT_TEST_FILE_PATTERN = /\.(?:spec|test)(?:\.|$)/iu;
3195
+ /**
3196
+ * Type-only compile check files copied out of packaged runtime input paths.
3197
+ *
3198
+ * @private internal constant of `ptbk agents-server`
3199
+ */
3200
+ const AGENTS_SERVER_BUILD_INPUT_TEST_TYPE_FILE_PATTERN = /\.test-type\.[jt]sx?$/iu;
3169
3201
  /**
3170
3202
  * Ensures that the local Agents Server production build exists and matches its source fingerprint.
3171
3203
  *
@@ -3360,15 +3392,48 @@
3360
3392
  */
3361
3393
  function shouldCopyAgentsServerRuntimePath(sourcePath, sourceRootPath) {
3362
3394
  const sourceRelativePath = path.relative(sourceRootPath, sourcePath).replace(/\\/gu, '/');
3395
+ const sourceRuntimeRelativePath = normalizeRuntimeSourceRelativePath(sourcePath, sourceRootPath);
3363
3396
  const sourcePathSegments = sourceRelativePath.split('/').filter(Boolean);
3364
3397
  const sourceBasename = path.basename(sourcePath);
3365
3398
  if (sourcePathSegments.some((sourcePathSegment) => AGENTS_SERVER_BUILD_INPUT_EXCLUDED_DIRECTORY_NAMES.has(sourcePathSegment))) {
3366
3399
  return false;
3367
3400
  }
3401
+ if (sourcePathSegments.includes('playground')) {
3402
+ return false;
3403
+ }
3404
+ if (isExcludedAgentsServerRuntimeSourcePath(sourceRuntimeRelativePath)) {
3405
+ return false;
3406
+ }
3368
3407
  if (sourceBasename.startsWith('.env')) {
3369
3408
  return false;
3370
3409
  }
3371
- return !AGENTS_SERVER_BUILD_INPUT_TEST_FILE_PATTERN.test(sourceBasename);
3410
+ return (!AGENTS_SERVER_BUILD_INPUT_TEST_FILE_PATTERN.test(sourceBasename) &&
3411
+ !AGENTS_SERVER_BUILD_INPUT_TEST_TYPE_FILE_PATTERN.test(sourceBasename));
3412
+ }
3413
+ /**
3414
+ * Normalizes a copied runtime path to the shape used inside the packaged runtime root.
3415
+ */
3416
+ function normalizeRuntimeSourceRelativePath(sourcePath, sourceRootPath) {
3417
+ const sourceRelativePath = path.relative(sourceRootPath, sourcePath).replace(/\\/gu, '/');
3418
+ const sourceRootBasename = path.basename(sourceRootPath);
3419
+ if (!sourceRelativePath) {
3420
+ return sourceRootBasename;
3421
+ }
3422
+ return `${sourceRootBasename}/${sourceRelativePath}`;
3423
+ }
3424
+ /**
3425
+ * Checks whether one normalized runtime path is equal to or nested below another path.
3426
+ */
3427
+ function isRuntimePathWithin(sourceRuntimeRelativePath, excludedSourcePathPrefix) {
3428
+ return (sourceRuntimeRelativePath === excludedSourcePathPrefix ||
3429
+ sourceRuntimeRelativePath.startsWith(`${excludedSourcePathPrefix}/`));
3430
+ }
3431
+ /**
3432
+ * Returns true for runtime source files and folders that are not needed by the Agents Server build.
3433
+ */
3434
+ function isExcludedAgentsServerRuntimeSourcePath(sourceRuntimeRelativePath) {
3435
+ return (AGENTS_SERVER_BUILD_INPUT_EXCLUDED_SOURCE_PATHS.has(sourceRuntimeRelativePath) ||
3436
+ AGENTS_SERVER_BUILD_INPUT_EXCLUDED_SOURCE_PATH_PREFIXES.some((excludedSourcePathPrefix) => isRuntimePathWithin(sourceRuntimeRelativePath, excludedSourcePathPrefix)));
3372
3437
  }
3373
3438
  /**
3374
3439
  * Links the materialized runtime back to the package dependency tree used by the installed CLI.
@@ -3486,6 +3551,9 @@
3486
3551
  * Adds one runtime source file or directory subtree to the production build fingerprint.
3487
3552
  */
3488
3553
  async function addAgentsServerBuildInputToFingerprint(fingerprint, options) {
3554
+ if (isExcludedAgentsServerBuildInputPath(options.inputPath, options.runtimeRootPath)) {
3555
+ return;
3556
+ }
3489
3557
  let inputStats;
3490
3558
  try {
3491
3559
  inputStats = await promises.stat(options.inputPath);
@@ -3495,9 +3563,6 @@
3495
3563
  return;
3496
3564
  }
3497
3565
  if (inputStats.isFile()) {
3498
- if (isExcludedAgentsServerBuildInputFile(options.inputPath)) {
3499
- return;
3500
- }
3501
3566
  fingerprint.update(`file:${normalizeBuildInputPath(options.runtimeRootPath, options.inputPath)}\n`);
3502
3567
  fingerprint.update(await promises.readFile(options.inputPath));
3503
3568
  fingerprint.update('\n');
@@ -3520,10 +3585,23 @@
3520
3585
  }
3521
3586
  }
3522
3587
  /**
3523
- * Returns true for non-build test files inside shared runtime source paths.
3588
+ * Returns true for non-build files and folders inside shared runtime source paths.
3524
3589
  */
3525
- function isExcludedAgentsServerBuildInputFile(inputPath) {
3526
- return AGENTS_SERVER_BUILD_INPUT_TEST_FILE_PATTERN.test(path.basename(inputPath));
3590
+ function isExcludedAgentsServerBuildInputPath(inputPath, runtimeRootPath) {
3591
+ const inputRelativePath = normalizeBuildInputPath(runtimeRootPath, inputPath);
3592
+ const inputPathSegments = inputRelativePath.split('/').filter(Boolean);
3593
+ const inputBasename = path.basename(inputPath);
3594
+ if (inputPathSegments.some((inputPathSegment) => AGENTS_SERVER_BUILD_INPUT_EXCLUDED_DIRECTORY_NAMES.has(inputPathSegment))) {
3595
+ return true;
3596
+ }
3597
+ if (inputPathSegments.includes('playground')) {
3598
+ return true;
3599
+ }
3600
+ if (isExcludedAgentsServerRuntimeSourcePath(inputRelativePath)) {
3601
+ return true;
3602
+ }
3603
+ return (AGENTS_SERVER_BUILD_INPUT_TEST_FILE_PATTERN.test(inputBasename) ||
3604
+ AGENTS_SERVER_BUILD_INPUT_TEST_TYPE_FILE_PATTERN.test(inputBasename));
3527
3605
  }
3528
3606
  /**
3529
3607
  * Normalizes one absolute runtime path so cache fingerprints are stable across platforms.
@@ -4944,7 +5022,7 @@
4944
5022
  .filter((entry) => entry.isFile() && isBookFileName(entry.name))
4945
5023
  .map((entry) => ({
4946
5024
  absolutePath: path.join(queuedMessagesDirectoryPath, entry.name),
4947
- relativePath: normalizeRelativePath$1(path.join(AGENT_QUEUED_MESSAGES_DIRECTORY_PATH, entry.name)),
5025
+ relativePath: normalizeRelativePath$2(path.join(AGENT_QUEUED_MESSAGES_DIRECTORY_PATH, entry.name)),
4948
5026
  fileName: entry.name,
4949
5027
  }))
4950
5028
  .sort((firstMessage, secondMessage) => firstMessage.fileName.localeCompare(secondMessage.fileName));
@@ -4972,7 +5050,7 @@
4972
5050
  /**
4973
5051
  * Normalizes a relative path for Markdown links and Git path matching.
4974
5052
  */
4975
- function normalizeRelativePath$1(relativePath) {
5053
+ function normalizeRelativePath$2(relativePath) {
4976
5054
  return relativePath.replace(/\\/gu, '/');
4977
5055
  }
4978
5056
  /**
@@ -11043,7 +11121,7 @@
11043
11121
  *
11044
11122
  * @private helper of `minecraft2AvatarVisual`
11045
11123
  */
11046
- const LIGHT_DIRECTION$2 = normalizeVector3({
11124
+ const LIGHT_DIRECTION$3 = normalizeVector3({
11047
11125
  x: 0.4,
11048
11126
  y: -0.65,
11049
11127
  z: 0.92,
@@ -11255,7 +11333,7 @@
11255
11333
  corners: projectedCorners,
11256
11334
  texture: faceDefinition.texture,
11257
11335
  averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
11258
- lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$2), -1, 1),
11336
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$3), -1, 1),
11259
11337
  outlineColor: cuboid.outlineColor,
11260
11338
  };
11261
11339
  });
@@ -12446,7 +12524,7 @@
12446
12524
  *
12447
12525
  * @private helper of `octopus3dAvatarVisual`
12448
12526
  */
12449
- const LIGHT_DIRECTION$1 = normalizeVector3({
12527
+ const LIGHT_DIRECTION$2 = normalizeVector3({
12450
12528
  x: 0.48,
12451
12529
  y: -0.62,
12452
12530
  z: 0.94,
@@ -12656,7 +12734,7 @@
12656
12734
  corners: projectedCorners,
12657
12735
  averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
12658
12736
  transformedCorners.length,
12659
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
12737
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
12660
12738
  fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
12661
12739
  outlineColor,
12662
12740
  });
@@ -12845,7 +12923,7 @@
12845
12923
  *
12846
12924
  * @private helper of `octopus3d2AvatarVisual`
12847
12925
  */
12848
- const LIGHT_DIRECTION = normalizeVector3({
12926
+ const LIGHT_DIRECTION$1 = normalizeVector3({
12849
12927
  x: 0.38,
12850
12928
  y: -0.6,
12851
12929
  z: 0.98,
@@ -12994,7 +13072,7 @@
12994
13072
  corners: projectedCorners,
12995
13073
  averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
12996
13074
  transformedCorners.length,
12997
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
13075
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
12998
13076
  fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
12999
13077
  outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
13000
13078
  });
@@ -13094,6 +13172,514 @@
13094
13172
  context.restore();
13095
13173
  }
13096
13174
 
13175
+ /* eslint-disable no-magic-numbers */
13176
+ /**
13177
+ * Light direction used by the continuous octopus mesh shading.
13178
+ *
13179
+ * @private helper of `octopus3d3AvatarVisual`
13180
+ */
13181
+ const LIGHT_DIRECTION = normalizeVector3({
13182
+ x: 0.34,
13183
+ y: -0.62,
13184
+ z: 1,
13185
+ });
13186
+ /**
13187
+ * Real-octopus tentacle count used by the continuous lower mesh.
13188
+ *
13189
+ * @private helper of `octopus3d3AvatarVisual`
13190
+ */
13191
+ const OCTOPUS_TENTACLE_COUNT = 8;
13192
+ /**
13193
+ * Octopus 3D 3 avatar visual.
13194
+ *
13195
+ * @private built-in avatar visual
13196
+ */
13197
+ const octopus3d3AvatarVisual = {
13198
+ id: 'octopus3d3',
13199
+ title: 'Octopus 3D 3',
13200
+ description: 'Cute continuous 3D octopus with a blobby single mesh, waving tentacle lobes, rich shading, and cursor-aware eyes.',
13201
+ isAnimated: true,
13202
+ supportsPointerTracking: true,
13203
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
13204
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
13205
+ const animationRandom = createRandom('octopus3d3-animation-profile');
13206
+ const eyeRandom = createRandom('octopus3d3-eye-profile');
13207
+ const animationPhase = animationRandom() * Math.PI * 2;
13208
+ const tentacleProfiles = createContinuousTentacleProfiles(createRandom, morphologyProfile);
13209
+ const sceneCenterX = size * 0.5;
13210
+ const sceneCenterY = size * 0.535;
13211
+ const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
13212
+ const meshCenter = {
13213
+ x: interaction.bodyOffsetX * size * 0.048 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
13214
+ y: -size * 0.07 + interaction.bodyOffsetY * size * 0.026 + bob,
13215
+ z: interaction.intensity * size * 0.018,
13216
+ };
13217
+ const rotationY = -0.1 +
13218
+ Math.sin(timeMs / 2700 + animationPhase) * 0.035 +
13219
+ interaction.bodyOffsetX * 0.22 +
13220
+ interaction.gazeX * 0.88;
13221
+ const rotationX = -0.07 +
13222
+ Math.cos(timeMs / 3100 + animationPhase * 0.7) * 0.018 -
13223
+ interaction.bodyOffsetY * 0.08 -
13224
+ interaction.gazeY * 0.38;
13225
+ const surfaceOptions = {
13226
+ radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.1,
13227
+ radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.08,
13228
+ radiusZ: size *
13229
+ morphologyProfile.body.bodyRadiusRatio *
13230
+ (1.02 + (morphologyProfile.body.horizontalStretch - 1) * 0.18),
13231
+ morphologyProfile,
13232
+ timeMs,
13233
+ animationPhase,
13234
+ tentacleProfiles,
13235
+ };
13236
+ const surfacePatches = resolveVisibleContinuousOctopusPatches({
13237
+ ...surfaceOptions,
13238
+ center: meshCenter,
13239
+ rotationX,
13240
+ rotationY,
13241
+ sceneCenterX,
13242
+ sceneCenterY,
13243
+ size,
13244
+ palette,
13245
+ });
13246
+ const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.2 - 0.03, -0.22, 0.08);
13247
+ const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.1, 0.18, 0.32);
13248
+ const mouthLatitude = clampNumber$1(eyeLatitude + 0.2 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
13249
+ const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
13250
+ const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
13251
+ const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
13252
+ const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.76;
13253
+ const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.9;
13254
+ drawAvatarFrame(context, size, palette);
13255
+ drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
13256
+ drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
13257
+ for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
13258
+ drawContinuousSurfacePatch(context, surfacePatch);
13259
+ }
13260
+ drawProjectedSurfaceCurrents({
13261
+ context,
13262
+ surfaceOptions,
13263
+ center: meshCenter,
13264
+ rotationX,
13265
+ rotationY,
13266
+ sceneCenterX,
13267
+ sceneCenterY,
13268
+ size,
13269
+ palette,
13270
+ morphologyProfile,
13271
+ timeMs,
13272
+ animationPhase,
13273
+ });
13274
+ drawProjectedTentacleSuckers({
13275
+ context,
13276
+ surfaceOptions,
13277
+ center: meshCenter,
13278
+ rotationX,
13279
+ rotationY,
13280
+ sceneCenterX,
13281
+ sceneCenterY,
13282
+ size,
13283
+ palette,
13284
+ });
13285
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
13286
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
13287
+ drawProjectedOrganicMouth(context, [
13288
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
13289
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
13290
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
13291
+ ], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
13292
+ },
13293
+ };
13294
+ /**
13295
+ * Creates seeded tentacle-lobe profiles around the visible lower octopus body.
13296
+ *
13297
+ * @private helper of `octopus3d3AvatarVisual`
13298
+ */
13299
+ function createContinuousTentacleProfiles(createRandom, morphologyProfile) {
13300
+ return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
13301
+ const tentacleRandom = createRandom(`octopus3d3-tentacle-${tentacleIndex}`);
13302
+ const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
13303
+ return {
13304
+ centerLongitude: -Math.PI * 0.86 +
13305
+ progress * Math.PI * 1.72 +
13306
+ (tentacleRandom() - 0.5) * (0.08 + morphologyProfile.tentacles.rootSpreadScale * 0.03),
13307
+ widthScale: 0.86 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.16,
13308
+ lengthScale: 0.86 + tentacleRandom() * 0.36 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.22,
13309
+ swayScale: 0.82 + tentacleRandom() * 0.38 + (morphologyProfile.tentacles.swayScale - 1) * 0.2,
13310
+ depthScale: 0.86 + tentacleRandom() * 0.32 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.2,
13311
+ phase: tentacleRandom() * Math.PI * 2,
13312
+ suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
13313
+ };
13314
+ });
13315
+ }
13316
+ /**
13317
+ * Draws the soft underwater atmosphere behind the continuous octopus mesh.
13318
+ *
13319
+ * @private helper of `octopus3d3AvatarVisual`
13320
+ */
13321
+ function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
13322
+ const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.11, sceneCenterY - size * 0.17 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY, size * (0.66 + interaction.intensity * 0.02));
13323
+ glowGradient.addColorStop(0, `${palette.highlight}66`);
13324
+ glowGradient.addColorStop(0.34, `${palette.accent}2e`);
13325
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
13326
+ context.fillStyle = glowGradient;
13327
+ context.fillRect(0, 0, size, size);
13328
+ const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1550) * size * 0.05, sceneCenterY + size * 0.29, size * 0.06, sceneCenterX, sceneCenterY + size * 0.3, size * 0.54);
13329
+ lowerGradient.addColorStop(0, `${palette.secondary}25`);
13330
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
13331
+ context.fillStyle = lowerGradient;
13332
+ context.fillRect(0, 0, size, size);
13333
+ }
13334
+ /**
13335
+ * Draws the soft lower shadow that anchors the octopus in the avatar frame.
13336
+ *
13337
+ * @private helper of `octopus3d3AvatarVisual`
13338
+ */
13339
+ function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
13340
+ context.save();
13341
+ context.fillStyle = `${palette.shadow}66`;
13342
+ context.filter = `blur(${size * 0.025}px)`;
13343
+ context.beginPath();
13344
+ context.ellipse(size * 0.5 + interaction.gazeX * size * 0.045, size * 0.9 + Math.sin(timeMs / 980) * size * 0.007, size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02), size * 0.06, 0, 0, Math.PI * 2);
13345
+ context.fill();
13346
+ context.restore();
13347
+ }
13348
+ /**
13349
+ * Resolves visible projected patches for the continuous octopus mesh.
13350
+ *
13351
+ * @private helper of `octopus3d3AvatarVisual`
13352
+ */
13353
+ function resolveVisibleContinuousOctopusPatches(options) {
13354
+ const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
13355
+ const latitudePatchCount = 16;
13356
+ const longitudePatchCount = 40;
13357
+ const surfacePatches = [];
13358
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
13359
+ const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
13360
+ const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
13361
+ const centerLatitude = (startLatitude + endLatitude) / 2;
13362
+ const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
13363
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
13364
+ const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
13365
+ const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
13366
+ const centerLongitude = (startLongitude + endLongitude) / 2;
13367
+ const localCorners = [
13368
+ sampleContinuousOctopusSurfacePoint(options, startLatitude, startLongitude),
13369
+ sampleContinuousOctopusSurfacePoint(options, startLatitude, endLongitude),
13370
+ sampleContinuousOctopusSurfacePoint(options, endLatitude, endLongitude),
13371
+ sampleContinuousOctopusSurfacePoint(options, endLatitude, startLongitude),
13372
+ ];
13373
+ const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
13374
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
13375
+ if (surfaceNormal.z <= 0.008) {
13376
+ continue;
13377
+ }
13378
+ const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
13379
+ const tentacleInfluence = resolveContinuousTentacleInfluence(options, centerLongitude);
13380
+ const lowerLobeWave = resolveContinuousLobeWave(options, centerLongitude);
13381
+ surfacePatches.push({
13382
+ corners: projectedCorners,
13383
+ averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
13384
+ transformedCorners.length,
13385
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
13386
+ fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), tentacleInfluence.core, lowerLobeWave),
13387
+ outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
13388
+ });
13389
+ }
13390
+ }
13391
+ return surfacePatches;
13392
+ }
13393
+ /**
13394
+ * Samples one point on the continuous Octopus 3D 3 surface.
13395
+ *
13396
+ * The lower hemisphere is pulled into eight seeded waving lobes, so the portrait reads as
13397
+ * tentacled while still being rendered as one connected blobby mesh.
13398
+ *
13399
+ * @private helper of `octopus3d3AvatarVisual`
13400
+ */
13401
+ function sampleContinuousOctopusSurfacePoint(options, latitude, longitude) {
13402
+ const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
13403
+ const cosineLatitude = Math.max(0, Math.cos(latitude));
13404
+ const verticalProgress = (Math.sin(latitude) + 1) / 2;
13405
+ const upperBlend = Math.pow(1 - verticalProgress, 1.28);
13406
+ const lowerBlend = smoothStep(0.38, 1, verticalProgress);
13407
+ const tipBlend = smoothStep(0.68, 1, verticalProgress);
13408
+ const tentacleInfluence = resolveContinuousTentacleInfluence(options, longitude);
13409
+ const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
13410
+ const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
13411
+ const lowerLobeWave = resolveContinuousLobeWave(options, longitude);
13412
+ const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
13413
+ animationPhase * 0.6 +
13414
+ timeMs / (1750 + morphologyProfile.body.lobeCount * 30)) *
13415
+ (0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.8) *
13416
+ (0.3 + lowerBlend * 0.7);
13417
+ const tentacleWave = Math.sin(timeMs / 760 + tentacleInfluence.phase + verticalProgress * 2.4) *
13418
+ lowerBlend *
13419
+ tentacleInfluence.core *
13420
+ tentacleInfluence.swayScale;
13421
+ const horizontalScale = 1.04 +
13422
+ mantleRipple +
13423
+ lowerBlend * (0.16 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.1) +
13424
+ lowerBlend * tentacleInfluence.core * (0.2 + lowerLobeWave * 0.12) -
13425
+ upperBlend * 0.08;
13426
+ const depthScale = 1.06 +
13427
+ upperBlend * 0.16 +
13428
+ Math.max(0, Math.cos(effectiveLongitude)) * 0.1 +
13429
+ lowerBlend * tentacleInfluence.core * (0.1 + tentacleInfluence.depthScale * 0.06) -
13430
+ Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
13431
+ const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.11 + tipBlend * 0.06 + tentacleInfluence.widthScale * 0.025) * radiusX;
13432
+ const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
13433
+ const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.72;
13434
+ const lowerDrop = lowerBlend *
13435
+ radiusY *
13436
+ (0.18 +
13437
+ tentacleInfluence.core *
13438
+ (0.38 +
13439
+ tentacleInfluence.lengthScale * 0.22 +
13440
+ (morphologyProfile.tentacles.flowLengthScale - 1) * 0.08));
13441
+ return {
13442
+ x: Math.sin(effectiveLongitude) * planarRadiusX + tentacleWave * radiusX * (0.052 + tipBlend * 0.05),
13443
+ y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.12) -
13444
+ upperBlend * radiusY * 0.1 +
13445
+ lowerDrop +
13446
+ Math.sin(timeMs / 1420 + animationPhase + latitude * 1.6) * lowerBlend * radiusY * 0.018 +
13447
+ Math.cos(timeMs / 880 + tentacleInfluence.phase) *
13448
+ lowerBlend *
13449
+ tipBlend *
13450
+ tentacleInfluence.core *
13451
+ radiusY *
13452
+ 0.034,
13453
+ z: Math.cos(effectiveLongitude) * planarRadiusZ +
13454
+ Math.cos(timeMs / 980 + tentacleInfluence.phase + verticalProgress) *
13455
+ lowerBlend *
13456
+ tentacleInfluence.core *
13457
+ radiusZ *
13458
+ 0.04,
13459
+ };
13460
+ }
13461
+ /**
13462
+ * Blends nearby seeded tentacle profiles at one mesh longitude.
13463
+ *
13464
+ * @private helper of `octopus3d3AvatarVisual`
13465
+ */
13466
+ function resolveContinuousTentacleInfluence(options, longitude) {
13467
+ let totalWeight = 0;
13468
+ let weightedSin = 0;
13469
+ let weightedCos = 0;
13470
+ let weightedWidthScale = 0;
13471
+ let weightedLengthScale = 0;
13472
+ let weightedSwayScale = 0;
13473
+ let weightedDepthScale = 0;
13474
+ let weightedPhase = 0;
13475
+ for (const tentacleProfile of options.tentacleProfiles) {
13476
+ const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
13477
+ const width = 0.2 * tentacleProfile.widthScale;
13478
+ const weight = Math.exp(-(distance * distance) / (width * width));
13479
+ totalWeight += weight;
13480
+ weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
13481
+ weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
13482
+ weightedWidthScale += tentacleProfile.widthScale * weight;
13483
+ weightedLengthScale += tentacleProfile.lengthScale * weight;
13484
+ weightedSwayScale += tentacleProfile.swayScale * weight;
13485
+ weightedDepthScale += tentacleProfile.depthScale * weight;
13486
+ weightedPhase += tentacleProfile.phase * weight;
13487
+ }
13488
+ if (totalWeight < 0.0001) {
13489
+ return {
13490
+ core: 0,
13491
+ centerLongitude: longitude,
13492
+ widthScale: 1,
13493
+ lengthScale: 1,
13494
+ swayScale: 1,
13495
+ depthScale: 1,
13496
+ phase: 0,
13497
+ };
13498
+ }
13499
+ return {
13500
+ core: clampNumber$1(totalWeight, 0, 1),
13501
+ centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
13502
+ widthScale: weightedWidthScale / totalWeight,
13503
+ lengthScale: weightedLengthScale / totalWeight,
13504
+ swayScale: weightedSwayScale / totalWeight,
13505
+ depthScale: weightedDepthScale / totalWeight,
13506
+ phase: weightedPhase / totalWeight,
13507
+ };
13508
+ }
13509
+ /**
13510
+ * Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
13511
+ *
13512
+ * @private helper of `octopus3d3AvatarVisual`
13513
+ */
13514
+ function resolveContinuousLobeWave(options, longitude) {
13515
+ const { morphologyProfile, animationPhase, timeMs } = options;
13516
+ return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT +
13517
+ animationPhase +
13518
+ timeMs / (980 + morphologyProfile.body.lobeCount * 18)) +
13519
+ 1) /
13520
+ 2);
13521
+ }
13522
+ /**
13523
+ * Resolves one base fill tone for a patch on the continuous octopus mesh.
13524
+ *
13525
+ * @private helper of `octopus3d3AvatarVisual`
13526
+ */
13527
+ function resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
13528
+ const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
13529
+ if (tonalProgress < 0.14) {
13530
+ return palette.highlight;
13531
+ }
13532
+ if (tonalProgress < 0.32) {
13533
+ return palette.secondary;
13534
+ }
13535
+ if (tonalProgress < 0.72) {
13536
+ return forwardness > 0.55 ? palette.secondary : palette.primary;
13537
+ }
13538
+ return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
13539
+ }
13540
+ /**
13541
+ * Draws one projected mesh patch with soft shading and a subtle edge.
13542
+ *
13543
+ * @private helper of `octopus3d3AvatarVisual`
13544
+ */
13545
+ function drawContinuousSurfacePatch(context, surfacePatch) {
13546
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
13547
+ if (surfacePatch.lightIntensity > 0) {
13548
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.18 * surfacePatch.lightIntensity})`);
13549
+ }
13550
+ else if (surfacePatch.lightIntensity < 0) {
13551
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.25 * Math.abs(surfacePatch.lightIntensity)})`);
13552
+ }
13553
+ context.save();
13554
+ context.beginPath();
13555
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
13556
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
13557
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
13558
+ }
13559
+ context.closePath();
13560
+ context.strokeStyle = surfacePatch.outlineColor;
13561
+ context.lineWidth = Math.max(0.7, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0032);
13562
+ context.lineJoin = 'round';
13563
+ context.stroke();
13564
+ context.restore();
13565
+ }
13566
+ /**
13567
+ * Draws projected mantle-current lines on the front of the mesh.
13568
+ *
13569
+ * @private helper of `octopus3d3AvatarVisual`
13570
+ */
13571
+ function drawProjectedSurfaceCurrents(options) {
13572
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
13573
+ const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
13574
+ const centerIndex = (currentCount - 1) / 2;
13575
+ context.save();
13576
+ context.lineCap = 'round';
13577
+ context.lineJoin = 'round';
13578
+ for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
13579
+ const baseLongitude = (currentIndex - centerIndex) * 0.15;
13580
+ const projectedPoints = [];
13581
+ for (let sampleIndex = 0; sampleIndex < 8; sampleIndex++) {
13582
+ const progress = sampleIndex / 7;
13583
+ const latitude = -0.46 + progress * 0.74;
13584
+ const longitude = baseLongitude + Math.sin(timeMs / 1160 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.035;
13585
+ const scenePoint = transformScenePoint(sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
13586
+ if (scenePoint.z > center.z - size * 0.016) {
13587
+ projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
13588
+ }
13589
+ }
13590
+ if (projectedPoints.length < 3) {
13591
+ continue;
13592
+ }
13593
+ context.beginPath();
13594
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
13595
+ for (const projectedPoint of projectedPoints.slice(1)) {
13596
+ context.lineTo(projectedPoint.x, projectedPoint.y);
13597
+ }
13598
+ context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
13599
+ context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
13600
+ context.stroke();
13601
+ }
13602
+ context.restore();
13603
+ }
13604
+ /**
13605
+ * Draws small projected sucker highlights on the waving lower mesh lobes.
13606
+ *
13607
+ * @private helper of `octopus3d3AvatarVisual`
13608
+ */
13609
+ function drawProjectedTentacleSuckers(options) {
13610
+ const { surfaceOptions, size } = options;
13611
+ const { timeMs } = surfaceOptions;
13612
+ for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
13613
+ if (Math.cos(tentacleProfile.centerLongitude) < -0.12) {
13614
+ continue;
13615
+ }
13616
+ for (let suckerIndex = 0; suckerIndex < 3; suckerIndex++) {
13617
+ const latitude = 0.52 + suckerIndex * 0.14;
13618
+ const sideOffset = tentacleProfile.suckerSide * (0.035 + suckerIndex * 0.012) * tentacleProfile.widthScale;
13619
+ const waveOffset = Math.sin(timeMs / 900 + tentacleProfile.phase + suckerIndex * 0.8) * 0.018;
13620
+ drawProjectedSurfaceSpot({
13621
+ ...options,
13622
+ latitude,
13623
+ longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
13624
+ radiusScale: size * (0.0065 - suckerIndex * 0.0007),
13625
+ });
13626
+ }
13627
+ }
13628
+ }
13629
+ /**
13630
+ * Draws one tiny projected surface spot by sampling local mesh tangents.
13631
+ *
13632
+ * @private helper of `octopus3d3AvatarVisual`
13633
+ */
13634
+ function drawProjectedSurfaceSpot(options) {
13635
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
13636
+ const localCenter = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude);
13637
+ const localHorizontal = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
13638
+ const localVertical = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
13639
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
13640
+ if (sceneCenterPoint.z <= center.z - size * 0.012) {
13641
+ return;
13642
+ }
13643
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
13644
+ const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
13645
+ const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
13646
+ const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
13647
+ radiusScale *
13648
+ 0.74, size * 0.003, size * 0.018);
13649
+ const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
13650
+ radiusScale *
13651
+ 0.52, size * 0.0024, size * 0.014);
13652
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
13653
+ context.save();
13654
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
13655
+ context.rotate(rotation);
13656
+ context.beginPath();
13657
+ context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
13658
+ context.fillStyle = `${palette.highlight}73`;
13659
+ context.fill();
13660
+ context.strokeStyle = `${palette.highlight}99`;
13661
+ context.lineWidth = Math.max(0.7, size * 0.0028);
13662
+ context.stroke();
13663
+ context.restore();
13664
+ }
13665
+ /**
13666
+ * Resolves a signed angular distance from the source longitude to the target longitude.
13667
+ *
13668
+ * @private helper of `octopus3d3AvatarVisual`
13669
+ */
13670
+ function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
13671
+ return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
13672
+ }
13673
+ /**
13674
+ * Smoothly maps a value between two bounds into `[0, 1]`.
13675
+ *
13676
+ * @private helper of `octopus3d3AvatarVisual`
13677
+ */
13678
+ function smoothStep(edgeStart, edgeEnd, value) {
13679
+ const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
13680
+ return progress * progress * (3 - 2 * progress);
13681
+ }
13682
+
13097
13683
  /* eslint-disable no-magic-numbers */
13098
13684
  /**
13099
13685
  * Octopus avatar visual.
@@ -13864,6 +14450,7 @@
13864
14450
  octopus3AvatarVisual,
13865
14451
  octopus3dAvatarVisual,
13866
14452
  octopus3d2AvatarVisual,
14453
+ octopus3d3AvatarVisual,
13867
14454
  asciiOctopusAvatarVisual,
13868
14455
  minecraftAvatarVisual,
13869
14456
  minecraft2AvatarVisual,
@@ -34569,7 +35156,7 @@
34569
35156
  async function moveAgentMessageToFinished(projectPath, messageFile) {
34570
35157
  const finishedDirectoryPath = path.join(projectPath, AGENT_FINISHED_MESSAGES_DIRECTORY_PATH);
34571
35158
  const finishedMessagePath = path.join(finishedDirectoryPath, messageFile.fileName);
34572
- const finishedMessageRelativePath = normalizeRelativePath(path.join(AGENT_FINISHED_MESSAGES_DIRECTORY_PATH, messageFile.fileName));
35159
+ const finishedMessageRelativePath = normalizeRelativePath$1(path.join(AGENT_FINISHED_MESSAGES_DIRECTORY_PATH, messageFile.fileName));
34573
35160
  await promises.mkdir(finishedDirectoryPath, { recursive: true });
34574
35161
  if (await isExistingPath(finishedMessagePath)) {
34575
35162
  await promises.rm(finishedMessagePath, { force: true });
@@ -34599,7 +35186,7 @@
34599
35186
  /**
34600
35187
  * Normalizes a relative path for Git and display.
34601
35188
  */
34602
- function normalizeRelativePath(relativePath) {
35189
+ function normalizeRelativePath$1(relativePath) {
34603
35190
  return relativePath.replace(/\\/gu, '/');
34604
35191
  }
34605
35192
  /**
@@ -35618,6 +36205,177 @@
35618
36205
  `));
35619
36206
  }
35620
36207
 
36208
+ /**
36209
+ * Moves one repeatedly failing queued message to `messages/failed` with a visible agent failure reply.
36210
+ */
36211
+ async function moveAgentMessageToFailed(options) {
36212
+ const { projectPath, messageFile, failureReason } = options;
36213
+ const failedDirectoryPath = path.join(projectPath, AGENT_FAILED_MESSAGES_DIRECTORY_PATH);
36214
+ const failedMessagePath = path.join(failedDirectoryPath, messageFile.fileName);
36215
+ const failedMessageRelativePath = normalizeRelativePath(path.join(AGENT_FAILED_MESSAGES_DIRECTORY_PATH, messageFile.fileName));
36216
+ const queuedMessageContent = await promises.readFile(messageFile.absolutePath, 'utf-8');
36217
+ await promises.mkdir(failedDirectoryPath, { recursive: true });
36218
+ await promises.rm(failedMessagePath, { force: true });
36219
+ await promises.rename(messageFile.absolutePath, failedMessagePath);
36220
+ await promises.writeFile(failedMessagePath, appendFailureReply(queuedMessageContent, failureReason), 'utf-8');
36221
+ return {
36222
+ absolutePath: failedMessagePath,
36223
+ relativePath: failedMessageRelativePath,
36224
+ fileName: messageFile.fileName,
36225
+ };
36226
+ }
36227
+ /**
36228
+ * Appends a synthetic `@Agent` reply so the Agents Server can display the terminal failure reason.
36229
+ */
36230
+ function appendFailureReply(bookContent, failureReason) {
36231
+ const normalizedBookContent = bookContent.trimEnd();
36232
+ const normalizedFailureReason = failureReason.trim();
36233
+ return `${normalizedBookContent}\n\nMESSAGE @Agent\n${normalizedFailureReason}\n`;
36234
+ }
36235
+ /**
36236
+ * Normalizes a relative path for Git and display.
36237
+ */
36238
+ function normalizeRelativePath(relativePath) {
36239
+ return relativePath.replace(/\\/gu, '/');
36240
+ }
36241
+
36242
+ /**
36243
+ * Default number of failed queued-message attempts allowed before the watcher stops retrying that message.
36244
+ */
36245
+ const DEFAULT_MAX_MESSAGE_PROCESSING_FAILURES = 3;
36246
+ /**
36247
+ * Maximum number of characters copied from the final runner error into the failed chat message.
36248
+ */
36249
+ const MAX_FAILED_MESSAGE_ERROR_DETAILS_LENGTH = 4000;
36250
+ /**
36251
+ * Tracks repeated watch-level failures for queued message files and stops retrying them after a configured cap.
36252
+ */
36253
+ class AgentMessageFailureTracker {
36254
+ constructor(options = {}) {
36255
+ this.failuresByMessageKey = new Map();
36256
+ this.maxMessageProcessingFailures = normalizeMaxMessageProcessingFailures(options.maxMessageProcessingFailures);
36257
+ }
36258
+ /**
36259
+ * Clears the failure counter after a queued message is processed successfully.
36260
+ */
36261
+ clearMessageFailure(projectPath, messageFile) {
36262
+ if (!messageFile) {
36263
+ return;
36264
+ }
36265
+ this.failuresByMessageKey.delete(createAgentMessageFailureKey(projectPath, messageFile.relativePath));
36266
+ }
36267
+ /**
36268
+ * Records one queued-message failure and moves the message into `messages/failed` when the retry cap is reached.
36269
+ */
36270
+ async recordFailure(error) {
36271
+ var _a, _b;
36272
+ const errorContext = getAgentWatchErrorContext(error);
36273
+ if (!(errorContext === null || errorContext === void 0 ? void 0 : errorContext.projectPath) || !errorContext.queuedMessageRelativePath) {
36274
+ return null;
36275
+ }
36276
+ const messageKey = createAgentMessageFailureKey(errorContext.projectPath, errorContext.queuedMessageRelativePath);
36277
+ const previousFailureCount = (_b = (_a = this.failuresByMessageKey.get(messageKey)) === null || _a === void 0 ? void 0 : _a.failureCount) !== null && _b !== void 0 ? _b : 0;
36278
+ const failureCount = previousFailureCount + 1;
36279
+ if (failureCount < this.maxMessageProcessingFailures) {
36280
+ this.failuresByMessageKey.set(messageKey, { failureCount });
36281
+ return {
36282
+ failureCount,
36283
+ maxMessageProcessingFailures: this.maxMessageProcessingFailures,
36284
+ isMessageMovedToFailed: false,
36285
+ };
36286
+ }
36287
+ try {
36288
+ await moveAgentMessageToFailed({
36289
+ projectPath: errorContext.projectPath,
36290
+ messageFile: createAgentMessageFileFromContext(errorContext.projectPath, errorContext.queuedMessageRelativePath),
36291
+ failureReason: buildFailedQueuedMessageReason({
36292
+ error,
36293
+ failureCount,
36294
+ maxMessageProcessingFailures: this.maxMessageProcessingFailures,
36295
+ }),
36296
+ });
36297
+ this.failuresByMessageKey.delete(messageKey);
36298
+ console.error(colors__default["default"].yellow(`Moved ${errorContext.queuedMessageRelativePath} to messages/failed after ${failureCount} failed attempt(s).`));
36299
+ }
36300
+ catch (moveError) {
36301
+ this.failuresByMessageKey.set(messageKey, { failureCount });
36302
+ console.error(colors__default["default"].yellow(`Failed to move ${errorContext.queuedMessageRelativePath} to messages/failed: ${formatUnknownErrorDetails(moveError)}`));
36303
+ return {
36304
+ failureCount,
36305
+ maxMessageProcessingFailures: this.maxMessageProcessingFailures,
36306
+ isMessageMovedToFailed: false,
36307
+ };
36308
+ }
36309
+ return {
36310
+ failureCount,
36311
+ maxMessageProcessingFailures: this.maxMessageProcessingFailures,
36312
+ isMessageMovedToFailed: true,
36313
+ };
36314
+ }
36315
+ }
36316
+ /**
36317
+ * Creates the stable in-memory key for one queued message.
36318
+ */
36319
+ function createAgentMessageFailureKey(projectPath, queuedMessageRelativePath) {
36320
+ return `${projectPath}\0${queuedMessageRelativePath}`;
36321
+ }
36322
+ /**
36323
+ * Recreates the selected queued message descriptor from watch-loop error context.
36324
+ */
36325
+ function createAgentMessageFileFromContext(projectPath, queuedMessageRelativePath) {
36326
+ return {
36327
+ absolutePath: path.join(projectPath, queuedMessageRelativePath),
36328
+ relativePath: queuedMessageRelativePath.replace(/\\/gu, '/'),
36329
+ fileName: getFileNameFromRelativePath(queuedMessageRelativePath),
36330
+ };
36331
+ }
36332
+ /**
36333
+ * Extracts the filename from a normalized or platform-specific relative path.
36334
+ */
36335
+ function getFileNameFromRelativePath(relativePath) {
36336
+ const pathParts = relativePath.replace(/\\/gu, '/').split('/');
36337
+ return pathParts[pathParts.length - 1] || 'message.book';
36338
+ }
36339
+ /**
36340
+ * Builds the message shown to the chat user when the local runner stops retrying.
36341
+ */
36342
+ function buildFailedQueuedMessageReason(options) {
36343
+ const errorDetails = limitFailureDetails(formatUnknownErrorDetails(options.error));
36344
+ return _spaceTrim.spaceTrim((block) => `
36345
+ Local agent runner failed after ${options.failureCount} attempt(s) and stopped retrying.
36346
+
36347
+ Fix the runner or API configuration before trying again. The maximum failed-attempt limit is currently ${options.maxMessageProcessingFailures}.
36348
+
36349
+ Last runner error:
36350
+ \`\`\`
36351
+ ${block(errorDetails)}
36352
+ \`\`\`
36353
+ `);
36354
+ }
36355
+ /**
36356
+ * Keeps the synthetic failed chat message bounded even when runner logs are long.
36357
+ */
36358
+ function limitFailureDetails(errorDetails) {
36359
+ const normalizedErrorDetails = errorDetails.trim();
36360
+ if (normalizedErrorDetails.length <= MAX_FAILED_MESSAGE_ERROR_DETAILS_LENGTH) {
36361
+ return normalizedErrorDetails;
36362
+ }
36363
+ return _spaceTrim.spaceTrim(`
36364
+ [...runner error truncated to the last ${MAX_FAILED_MESSAGE_ERROR_DETAILS_LENGTH} characters...]
36365
+ ${normalizedErrorDetails.slice(-MAX_FAILED_MESSAGE_ERROR_DETAILS_LENGTH)}
36366
+ `);
36367
+ }
36368
+ /**
36369
+ * Normalizes the retry cap used by standalone CLI runs when no server-provided limit is available.
36370
+ */
36371
+ function normalizeMaxMessageProcessingFailures(rawValue) {
36372
+ const parsedValue = Number(rawValue);
36373
+ if (!Number.isFinite(parsedValue) || parsedValue <= 0) {
36374
+ return DEFAULT_MAX_MESSAGE_PROCESSING_FAILURES;
36375
+ }
36376
+ return Math.floor(parsedValue);
36377
+ }
36378
+
35621
36379
  /**
35622
36380
  * Delay between multi-agent watch iterations while all queues stay empty.
35623
36381
  */
@@ -35654,6 +36412,9 @@
35654
36412
  let lastObservedProjectCount = 0;
35655
36413
  let isWatchSessionInitialized = false;
35656
36414
  let uiHandle;
36415
+ const messageFailureTracker = new AgentMessageFailureTracker({
36416
+ maxMessageProcessingFailures: options.maxMessageProcessingFailures,
36417
+ });
35657
36418
  while (shouldContinue()) {
35658
36419
  try {
35659
36420
  if (!isWatchSessionInitialized) {
@@ -35702,7 +36463,7 @@
35702
36463
  const queuedProjects = projectSummaries.filter((projectSummary) => projectSummary.queuedMessageCount > 0);
35703
36464
  if (queuedProjects.length === 0) {
35704
36465
  updateMultipleAgentRunUiForWatching(uiHandle, options, rootPath, projectSummaries, ignoredAgentCount);
35705
- await wait$2(MULTI_AGENT_QUEUE_POLL_INTERVAL_MS);
36466
+ await wait$3(MULTI_AGENT_QUEUE_POLL_INTERVAL_MS);
35706
36467
  continue;
35707
36468
  }
35708
36469
  if (!uiHandle) {
@@ -35742,6 +36503,9 @@
35742
36503
  if (tickResult.value.tickResult.autoPullTimestamp !== undefined) {
35743
36504
  autoPullTimestampsByProjectPath.set(tickResult.value.projectPath, tickResult.value.tickResult.autoPullTimestamp);
35744
36505
  }
36506
+ if (tickResult.value.tickResult.isMessageProcessed) {
36507
+ messageFailureTracker.clearMessageFailure(tickResult.value.projectPath, tickResult.value.tickResult.queuedMessage);
36508
+ }
35745
36509
  continue;
35746
36510
  }
35747
36511
  await handleAgentWatchError({
@@ -35749,6 +36513,7 @@
35749
36513
  logDirectoryPath: controls.watchErrorLogDirectoryPath || rootPath,
35750
36514
  error: tickResult.reason,
35751
36515
  });
36516
+ await messageFailureTracker.recordFailure(tickResult.reason);
35752
36517
  }
35753
36518
  }
35754
36519
  catch (error) {
@@ -35757,7 +36522,8 @@
35757
36522
  logDirectoryPath: controls.watchErrorLogDirectoryPath || rootPath,
35758
36523
  error,
35759
36524
  });
35760
- await wait$2(MULTI_AGENT_QUEUE_POLL_INTERVAL_MS);
36525
+ await messageFailureTracker.recordFailure(error);
36526
+ await wait$3(MULTI_AGENT_QUEUE_POLL_INTERVAL_MS);
35761
36527
  }
35762
36528
  }
35763
36529
  }
@@ -35841,7 +36607,9 @@
35841
36607
  const autoPullTimestamp = await pullLatestChangesForAgentQueueIfEnabled({
35842
36608
  projectPath,
35843
36609
  runOptions,
35844
- logMessage: uiHandle ? undefined : `Pulling latest changes in ${formatProjectPath(rootPath, projectPath)}...`,
36610
+ logMessage: uiHandle
36611
+ ? undefined
36612
+ : `Pulling latest changes in ${formatProjectPath(rootPath, projectPath)}...`,
35845
36613
  });
35846
36614
  if (autoPullTimestamp !== undefined) {
35847
36615
  autoPullTimestampsByProjectPath.set(projectPath, autoPullTimestamp);
@@ -36055,9 +36823,7 @@
36055
36823
  const answeringMessageLines = projectSummaries
36056
36824
  .filter((projectSummary) => answeringProjectPaths.has(projectSummary.project.projectPath))
36057
36825
  .flatMap((projectSummary) => buildAnsweringAgentMessageLines(projectSummary));
36058
- return answeringMessageLines.length > 0
36059
- ? answeringMessageLines
36060
- : [WAITING_FOR_MESSAGE_LABEL];
36826
+ return answeringMessageLines.length > 0 ? answeringMessageLines : [WAITING_FOR_MESSAGE_LABEL];
36061
36827
  }
36062
36828
  /**
36063
36829
  * Builds one dedicated message-preview panel per answering agent.
@@ -36148,7 +36914,7 @@
36148
36914
  /**
36149
36915
  * Waits for the next idle poll interval in multi-agent watch mode.
36150
36916
  */
36151
- async function wait$2(delayMs) {
36917
+ async function wait$3(delayMs) {
36152
36918
  await new Promise((resolve) => setTimeout(resolve, delayMs));
36153
36919
  }
36154
36920
 
@@ -36171,6 +36937,218 @@
36171
36937
  }
36172
36938
  }
36173
36939
 
36940
+ /**
36941
+ * Metadata key storing the delay between retry attempts when importing federated agent books.
36942
+ *
36943
+ * @private internal Agents Server constant
36944
+ */
36945
+ const FEDERATED_AGENT_IMPORT_RETRY_DELAY_MS_METADATA_KEY = 'FEDERATED_AGENT_IMPORT_RETRY_DELAY_MS';
36946
+ /**
36947
+ * Total number of attempts used when loading a federated imported agent before falling back.
36948
+ *
36949
+ * @private internal Agents Server constant
36950
+ */
36951
+ const FEDERATED_AGENT_IMPORT_MAX_ATTEMPTS = 3;
36952
+ /**
36953
+ * Default delay in milliseconds between federated imported-agent retry attempts.
36954
+ *
36955
+ * @private internal Agents Server constant
36956
+ */
36957
+ const DEFAULT_FEDERATED_AGENT_IMPORT_RETRY_DELAY_MS = 500;
36958
+ /**
36959
+ * Default retry configuration for federated imported-agent loading.
36960
+ *
36961
+ * @private internal Agents Server constant
36962
+ */
36963
+ Object.freeze({
36964
+ maxAttempts: FEDERATED_AGENT_IMPORT_MAX_ATTEMPTS,
36965
+ retryDelayMs: DEFAULT_FEDERATED_AGENT_IMPORT_RETRY_DELAY_MS,
36966
+ });
36967
+
36968
+ /**
36969
+ * Metadata key used to persist tool-usage limits.
36970
+ *
36971
+ * @private shared constant for Agents Server
36972
+ */
36973
+ const TOOL_USAGE_LIMITS_METADATA_KEY = 'TOOL_USAGE_LIMITS';
36974
+ /**
36975
+ * Default timeout limits applied when administrators do not override them.
36976
+ *
36977
+ * @private shared constant for Agents Server
36978
+ */
36979
+ const DEFAULT_TIMEOUT_TOOL_USAGE_LIMITS = {
36980
+ maxActivePerChat: 5,
36981
+ maxFiredPerDayPerChat: 10,
36982
+ };
36983
+
36984
+ /**
36985
+ * Legacy metadata key storing the maximum accepted file upload size in megabytes.
36986
+ *
36987
+ * @private shared Agents Server constant
36988
+ */
36989
+ const MAX_FILE_UPLOAD_SIZE_MB_METADATA_KEY = 'MAX_FILE_UPLOAD_SIZE_MB';
36990
+ /**
36991
+ * Default maximum accepted spawn depth in one tool-runtime context.
36992
+ *
36993
+ * @private shared Agents Server constant
36994
+ */
36995
+ const DEFAULT_SPAWN_AGENT_MAX_DEPTH = 2;
36996
+ /**
36997
+ * Default maximum number of spawned agents allowed per actor in one time window.
36998
+ *
36999
+ * @private shared Agents Server constant
37000
+ */
37001
+ const DEFAULT_SPAWN_AGENT_RATE_LIMIT_MAX = 5;
37002
+ /**
37003
+ * Default spawn rate-limit window size in milliseconds.
37004
+ *
37005
+ * @private shared Agents Server constant
37006
+ */
37007
+ const DEFAULT_SPAWN_AGENT_RATE_LIMIT_WINDOW_MS = 10 * 60 * 1000;
37008
+ /**
37009
+ * Default maximum number of failed local runner attempts before a queued message is moved to `messages/failed`.
37010
+ *
37011
+ * @private shared Agents Server constant
37012
+ */
37013
+ const DEFAULT_LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS = 3;
37014
+ /**
37015
+ * Stable keys used by the dedicated server-limits table.
37016
+ *
37017
+ * @private shared Agents Server constant
37018
+ */
37019
+ const SERVER_LIMIT_KEYS = {
37020
+ TIMEOUT_MAX_ACTIVE_PER_CHAT: 'TIMEOUT_MAX_ACTIVE_PER_CHAT',
37021
+ TIMEOUT_MAX_FIRED_PER_DAY_PER_CHAT: 'TIMEOUT_MAX_FIRED_PER_DAY_PER_CHAT',
37022
+ MAX_FILE_UPLOAD_SIZE_MB: MAX_FILE_UPLOAD_SIZE_MB_METADATA_KEY,
37023
+ FEDERATED_AGENT_IMPORT_RETRY_DELAY_MS: FEDERATED_AGENT_IMPORT_RETRY_DELAY_MS_METADATA_KEY,
37024
+ SPAWN_AGENT_MAX_DEPTH: 'SPAWN_AGENT_MAX_DEPTH',
37025
+ SPAWN_AGENT_RATE_LIMIT_MAX: 'SPAWN_AGENT_RATE_LIMIT_MAX',
37026
+ SPAWN_AGENT_RATE_LIMIT_WINDOW_MS: 'SPAWN_AGENT_RATE_LIMIT_WINDOW_MS',
37027
+ LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS: 'LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS',
37028
+ };
37029
+ /**
37030
+ * Shared metadata for all server limits exposed in the admin UI and runtime loaders.
37031
+ *
37032
+ * @private shared Agents Server constant
37033
+ */
37034
+ const SERVER_LIMIT_DEFINITIONS = [
37035
+ {
37036
+ key: SERVER_LIMIT_KEYS.TIMEOUT_MAX_ACTIVE_PER_CHAT,
37037
+ category: 'Timeout tools',
37038
+ title: 'Max active timers per chat',
37039
+ description: 'Prevents one chat thread from scheduling too many timers at once.',
37040
+ unit: 'count',
37041
+ defaultValue: DEFAULT_TIMEOUT_TOOL_USAGE_LIMITS.maxActivePerChat,
37042
+ minimumValue: 1,
37043
+ step: 1,
37044
+ legacyMetadataKeys: [TOOL_USAGE_LIMITS_METADATA_KEY],
37045
+ },
37046
+ {
37047
+ key: SERVER_LIMIT_KEYS.TIMEOUT_MAX_FIRED_PER_DAY_PER_CHAT,
37048
+ category: 'Timeout tools',
37049
+ title: 'Max timers fired per day per chat',
37050
+ description: 'Limits how many scheduled wake-ups can execute inside one chat within one UTC day.',
37051
+ unit: 'count',
37052
+ defaultValue: DEFAULT_TIMEOUT_TOOL_USAGE_LIMITS.maxFiredPerDayPerChat,
37053
+ minimumValue: 1,
37054
+ step: 1,
37055
+ legacyMetadataKeys: [TOOL_USAGE_LIMITS_METADATA_KEY],
37056
+ },
37057
+ {
37058
+ key: SERVER_LIMIT_KEYS.MAX_FILE_UPLOAD_SIZE_MB,
37059
+ category: 'Files',
37060
+ title: 'Max file upload size',
37061
+ description: 'Caps the size of uploaded files accepted by chat uploads and Android share-target imports.',
37062
+ unit: 'MB',
37063
+ defaultValue: 50,
37064
+ minimumValue: 1,
37065
+ step: 1,
37066
+ legacyMetadataKeys: [MAX_FILE_UPLOAD_SIZE_MB_METADATA_KEY],
37067
+ },
37068
+ {
37069
+ key: SERVER_LIMIT_KEYS.FEDERATED_AGENT_IMPORT_RETRY_DELAY_MS,
37070
+ category: 'Federation',
37071
+ title: 'Federated import retry delay',
37072
+ description: 'Wait time between retries when importing agent books from federated servers.',
37073
+ unit: 'ms',
37074
+ defaultValue: DEFAULT_FEDERATED_AGENT_IMPORT_RETRY_DELAY_MS,
37075
+ minimumValue: 0,
37076
+ step: 100,
37077
+ legacyMetadataKeys: [FEDERATED_AGENT_IMPORT_RETRY_DELAY_MS_METADATA_KEY],
37078
+ },
37079
+ {
37080
+ key: SERVER_LIMIT_KEYS.SPAWN_AGENT_MAX_DEPTH,
37081
+ category: 'Agent spawning',
37082
+ title: 'Max spawn depth',
37083
+ description: 'Restricts how many nested `spawn_agent` hops can happen inside one tool runtime context.',
37084
+ unit: 'count',
37085
+ defaultValue: DEFAULT_SPAWN_AGENT_MAX_DEPTH,
37086
+ minimumValue: 1,
37087
+ step: 1,
37088
+ legacyMetadataKeys: [],
37089
+ },
37090
+ {
37091
+ key: SERVER_LIMIT_KEYS.SPAWN_AGENT_RATE_LIMIT_MAX,
37092
+ category: 'Agent spawning',
37093
+ title: 'Max spawned agents per window',
37094
+ description: 'Limits how many persistent agents one actor can create through `spawn_agent` inside one rate-limit window.',
37095
+ unit: 'count',
37096
+ defaultValue: DEFAULT_SPAWN_AGENT_RATE_LIMIT_MAX,
37097
+ minimumValue: 1,
37098
+ step: 1,
37099
+ legacyMetadataKeys: [],
37100
+ },
37101
+ {
37102
+ key: SERVER_LIMIT_KEYS.SPAWN_AGENT_RATE_LIMIT_WINDOW_MS,
37103
+ category: 'Agent spawning',
37104
+ title: 'Spawn rate-limit window',
37105
+ description: 'Time window used together with the spawn quota for `spawn_agent` abuse protection.',
37106
+ unit: 'ms',
37107
+ defaultValue: DEFAULT_SPAWN_AGENT_RATE_LIMIT_WINDOW_MS,
37108
+ minimumValue: 1000,
37109
+ step: 1000,
37110
+ legacyMetadataKeys: [],
37111
+ },
37112
+ {
37113
+ key: SERVER_LIMIT_KEYS.LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS,
37114
+ category: 'Local agent runner',
37115
+ title: 'Max failed message attempts',
37116
+ description: 'Stops the local coding-agent watcher from retrying the same queued chat message forever.',
37117
+ unit: 'count',
37118
+ defaultValue: DEFAULT_LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS,
37119
+ minimumValue: 1,
37120
+ step: 1,
37121
+ legacyMetadataKeys: [],
37122
+ },
37123
+ ];
37124
+ /**
37125
+ * Fast lookup map used by server-limit validators and UI helpers.
37126
+ *
37127
+ * @private shared Agents Server constant
37128
+ */
37129
+ new Map(SERVER_LIMIT_DEFINITIONS.map((definition) => [definition.key, definition]));
37130
+ /**
37131
+ * Fully normalized default values keyed by dedicated server-limit ids.
37132
+ *
37133
+ * @private shared Agents Server constant
37134
+ */
37135
+ const defaultServerLimitValues = {};
37136
+ for (const definition of SERVER_LIMIT_DEFINITIONS) {
37137
+ defaultServerLimitValues[definition.key] = definition.defaultValue;
37138
+ }
37139
+ /**
37140
+ * Default values used whenever a server-limit row has not been persisted yet.
37141
+ *
37142
+ * @private shared Agents Server constant
37143
+ */
37144
+ Object.freeze(defaultServerLimitValues);
37145
+ /**
37146
+ * Deprecated metadata keys that now mirror dedicated server-limit rows for backward compatibility.
37147
+ *
37148
+ * @private shared Agents Server constant
37149
+ */
37150
+ Array.from(new Set(SERVER_LIMIT_DEFINITIONS.flatMap((definition) => definition.legacyMetadataKeys)));
37151
+
36174
37152
  /**
36175
37153
  * Local worker-pump delay while the Agents Server foreground process stays active.
36176
37154
  *
@@ -36189,6 +37167,18 @@
36189
37167
  * @private internal constant of `ptbk agents-server`
36190
37168
  */
36191
37169
  const USER_CHAT_JOB_WORKER_ERROR_BODY_MAX_LENGTH = 2000;
37170
+ /**
37171
+ * Delay between foreground CLI attempts to load internal Agents Server limits during startup.
37172
+ *
37173
+ * @private internal constant of `ptbk agents-server`
37174
+ */
37175
+ const INTERNAL_SERVER_LIMITS_RETRY_DELAY_MS = 1000;
37176
+ /**
37177
+ * Maximum time spent waiting for the internal limits route before startup fails.
37178
+ *
37179
+ * @private internal constant of `ptbk agents-server`
37180
+ */
37181
+ const INTERNAL_SERVER_LIMITS_STARTUP_TIMEOUT_MS = 60000;
36192
37182
  /**
36193
37183
  * HTTP status used by an idle internal worker tick with no job to process.
36194
37184
  *
@@ -36295,6 +37285,12 @@
36295
37285
  logStreams,
36296
37286
  state,
36297
37287
  });
37288
+ const localAgentRunnerLimits = await waitForLocalAgentRunnerLimits({
37289
+ port: options.port,
37290
+ environment: runtimeChildEnvironment,
37291
+ logStreams,
37292
+ state,
37293
+ });
36298
37294
  stopUserChatJobWorkerPump = startUserChatJobWorkerPump({
36299
37295
  port: options.port,
36300
37296
  environment: runtimeChildEnvironment,
@@ -36302,7 +37298,7 @@
36302
37298
  state,
36303
37299
  });
36304
37300
  await withCurrentWorkingDirectory(runtimePaths.agentRootPath, async () => {
36305
- await runMultipleAgentMessages(createLocalAgentRunOptions(options), {
37301
+ await runMultipleAgentMessages(createLocalAgentRunOptions(options, localAgentRunnerLimits), {
36306
37302
  shouldContinue: () => state.isContinuing,
36307
37303
  watchErrorLogDirectoryPath: runtimePaths.logDirectoryPath,
36308
37304
  onUiInitialized: (uiHandle) => {
@@ -36431,7 +37427,7 @@
36431
37427
  /**
36432
37428
  * Creates local no-git agent runner options for folders managed by the Agents Server database.
36433
37429
  */
36434
- function createLocalAgentRunOptions(options) {
37430
+ function createLocalAgentRunOptions(options, localAgentRunnerLimits) {
36435
37431
  return {
36436
37432
  agentName: options.agentName,
36437
37433
  model: options.model,
@@ -36444,8 +37440,67 @@
36444
37440
  autoPush: false,
36445
37441
  autoPull: false,
36446
37442
  autoClone: false,
37443
+ maxMessageProcessingFailures: localAgentRunnerLimits.maxFailedAttempts,
37444
+ };
37445
+ }
37446
+ /**
37447
+ * Waits until the internal Next route can return current local runner limits.
37448
+ */
37449
+ async function waitForLocalAgentRunnerLimits(options) {
37450
+ const startedAt = Date.now();
37451
+ let lastError;
37452
+ while (options.state.isContinuing && Date.now() - startedAt < INTERNAL_SERVER_LIMITS_STARTUP_TIMEOUT_MS) {
37453
+ try {
37454
+ const limits = await fetchLocalAgentRunnerLimits(options);
37455
+ logRunnerEvent(options.logStreams.runner, `Local agent runner max failed attempts: ${limits.maxFailedAttempts}.`);
37456
+ return limits;
37457
+ }
37458
+ catch (error) {
37459
+ lastError = error;
37460
+ await wait$2(INTERNAL_SERVER_LIMITS_RETRY_DELAY_MS);
37461
+ }
37462
+ }
37463
+ if (!options.state.isContinuing) {
37464
+ return {
37465
+ maxFailedAttempts: DEFAULT_LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS,
37466
+ };
37467
+ }
37468
+ throw new NotAllowed(_spaceTrim.spaceTrim(`
37469
+ Failed to load local agent runner limits from the Agents Server.
37470
+
37471
+ ${lastError instanceof Error ? lastError.message : String(lastError)}
37472
+ `));
37473
+ }
37474
+ /**
37475
+ * Loads local runner limits through the token-protected internal Agents Server route.
37476
+ */
37477
+ async function fetchLocalAgentRunnerLimits(options) {
37478
+ const response = await fetch(`http://localhost:${options.port}/api/internal/agent-runner-limits`, {
37479
+ method: 'GET',
37480
+ cache: 'no-store',
37481
+ headers: {
37482
+ 'x-user-chat-worker-token': options.environment.PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN,
37483
+ },
37484
+ });
37485
+ if (!response.ok) {
37486
+ const details = await readUserChatJobWorkerErrorDetails(response);
37487
+ throw new Error(createInternalRouteErrorMessage('agent runner limits', response, details));
37488
+ }
37489
+ const payload = (await response.json());
37490
+ return {
37491
+ maxFailedAttempts: normalizeLocalAgentRunnerMaxFailedAttempts(payload.maxFailedAttempts),
36447
37492
  };
36448
37493
  }
37494
+ /**
37495
+ * Normalizes the local runner retry cap returned by the internal server route.
37496
+ */
37497
+ function normalizeLocalAgentRunnerMaxFailedAttempts(rawValue) {
37498
+ const parsedValue = Number(rawValue);
37499
+ if (!Number.isFinite(parsedValue) || parsedValue <= 0) {
37500
+ return DEFAULT_LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS;
37501
+ }
37502
+ return Math.floor(parsedValue);
37503
+ }
36449
37504
  /**
36450
37505
  * Starts periodic internal worker calls that queue and reconcile local message-folder jobs.
36451
37506
  */
@@ -36554,12 +37609,18 @@
36554
37609
  * Builds the foreground worker failure message from HTTP status and route details.
36555
37610
  */
36556
37611
  function createUserChatJobWorkerErrorMessage(response, details) {
37612
+ return createInternalRouteErrorMessage('user chat worker', response, details);
37613
+ }
37614
+ /**
37615
+ * Builds a foreground failure message for one internal Agents Server route.
37616
+ */
37617
+ function createInternalRouteErrorMessage(routeLabel, response, details) {
36557
37618
  const statusText = response.statusText ? ` ${response.statusText}` : '';
36558
37619
  const statusMessage = `${response.status}${statusText}`;
36559
37620
  if (!details) {
36560
- return `Internal user chat worker returned ${statusMessage}.`;
37621
+ return `Internal ${routeLabel} route returned ${statusMessage}.`;
36561
37622
  }
36562
- return `Internal user chat worker returned ${statusMessage}: ${details}`;
37623
+ return `Internal ${routeLabel} route returned ${statusMessage}: ${details}`;
36563
37624
  }
36564
37625
  /**
36565
37626
  * Keeps foreground worker diagnostics bounded when a route returns a large payload.
@@ -36629,6 +37690,12 @@
36629
37690
  }
36630
37691
  commandProcess.kill();
36631
37692
  }
37693
+ /**
37694
+ * Waits for the given delay.
37695
+ */
37696
+ async function wait$2(delayMs) {
37697
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
37698
+ }
36632
37699
  // Note: [🟡] Code for CLI runtime [startAgentsServer](src/cli/cli-commands/agents-server/startAgentsServer.ts) should never be published outside of `@promptbook/cli`
36633
37700
 
36634
37701
  /**
@@ -61286,14 +62353,18 @@
61286
62353
  * Watches the queued message directory and answers messages one by one.
61287
62354
  */
61288
62355
  async function runAgentMessages(options, controls = {}) {
61289
- var _a;
62356
+ var _a, _b;
61290
62357
  validateAgentRunOptions(options);
61291
62358
  validateAgentWatchOptions('ptbk agent-folder run-agent', options);
61292
62359
  const projectPath = process.cwd();
61293
62360
  let autoPullTimestamp = options.autoPull ? Date.now() : undefined;
61294
62361
  const shouldContinue = controls.shouldContinue || (() => just(true));
62362
+ const queuePollIntervalMs = (_a = controls.queuePollIntervalMs) !== null && _a !== void 0 ? _a : AGENT_QUEUE_POLL_INTERVAL_MS;
61295
62363
  let isWatchSessionInitialized = false;
61296
62364
  let uiHandle;
62365
+ const messageFailureTracker = new AgentMessageFailureTracker({
62366
+ maxMessageProcessingFailures: options.maxMessageProcessingFailures,
62367
+ });
61297
62368
  while (shouldContinue()) {
61298
62369
  try {
61299
62370
  if (!isWatchSessionInitialized) {
@@ -61305,8 +62376,9 @@
61305
62376
  }
61306
62377
  }
61307
62378
  const result = await tickAgentMessages(options, { isQuietWhenIdle: true, uiHandle });
61308
- autoPullTimestamp = (_a = result.autoPullTimestamp) !== null && _a !== void 0 ? _a : autoPullTimestamp;
62379
+ autoPullTimestamp = (_b = result.autoPullTimestamp) !== null && _b !== void 0 ? _b : autoPullTimestamp;
61309
62380
  if (result.isMessageProcessed) {
62381
+ messageFailureTracker.clearMessageFailure(projectPath, result.queuedMessage);
61310
62382
  continue;
61311
62383
  }
61312
62384
  autoPullTimestamp = await waitForQueuedAgentMessage({
@@ -61315,6 +62387,7 @@
61315
62387
  autoPullTimestamp,
61316
62388
  uiHandle,
61317
62389
  shouldContinue,
62390
+ queuePollIntervalMs,
61318
62391
  });
61319
62392
  }
61320
62393
  catch (error) {
@@ -61323,7 +62396,8 @@
61323
62396
  logDirectoryPath: projectPath,
61324
62397
  error,
61325
62398
  });
61326
- await wait(AGENT_QUEUE_POLL_INTERVAL_MS);
62399
+ await messageFailureTracker.recordFailure(error);
62400
+ await wait(queuePollIntervalMs);
61327
62401
  }
61328
62402
  }
61329
62403
  }
@@ -61331,14 +62405,14 @@
61331
62405
  * Polls until at least one queued `.book` message is available.
61332
62406
  */
61333
62407
  async function waitForQueuedAgentMessage(options) {
61334
- const { projectPath, options: runOptions, uiHandle, shouldContinue } = options;
62408
+ const { projectPath, options: runOptions, uiHandle, shouldContinue, queuePollIntervalMs } = options;
61335
62409
  let { autoPullTimestamp } = options;
61336
62410
  let queueSnapshot = await loadAgentMessageQueueSnapshot(projectPath);
61337
62411
  if (uiHandle) {
61338
62412
  updateAgentRunUiForWatching(uiHandle, queueSnapshot);
61339
62413
  }
61340
62414
  while (shouldContinue()) {
61341
- await wait(AGENT_QUEUE_POLL_INTERVAL_MS);
62415
+ await wait(queuePollIntervalMs);
61342
62416
  queueSnapshot = await loadAgentMessageQueueSnapshot(projectPath);
61343
62417
  if (queueSnapshot.queuedMessages.length > 0) {
61344
62418
  return autoPullTimestamp;