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