@promptbook/node 0.113.0-1 → 0.113.0-10

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 (256) hide show
  1. package/README.md +1 -17
  2. package/esm/book/scripts/import-markdown/increaseHeadings.d.ts +8 -0
  3. package/esm/index.es.js +1571 -1200
  4. package/esm/index.es.js.map +1 -1
  5. package/esm/scripts/run-codex-prompts/common/parseDuration.d.ts +19 -0
  6. package/esm/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +4 -0
  7. package/esm/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeSessionResurrection.d.ts +37 -0
  8. package/esm/scripts/run-codex-prompts/runners/claude-code/parseClaudeCodeOutputEvents.d.ts +47 -0
  9. package/esm/src/_packages/types.index.d.ts +2 -2
  10. package/esm/src/avatars/renderAvatarVisualAsciiArt.d.ts +98 -0
  11. package/esm/src/avatars/visuals/octopus3dAvatarVisualShared.d.ts +1 -1
  12. package/esm/src/book-components/BookEditor/BookEditorMonacoUploadPanel.d.ts +1 -1
  13. package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/bookEditorMonacoUploadTypes.d.ts +120 -0
  14. package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/clearScheduledTimer.d.ts +6 -0
  15. package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/enqueueFilesForUpload.d.ts +26 -0
  16. package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadEditorSync.d.ts +23 -0
  17. package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadItemsState.d.ts +21 -0
  18. package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadProgressQueue.d.ts +12 -0
  19. package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadQueue.d.ts +27 -0
  20. package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/useCompletedUploadsAutoClear.d.ts +12 -0
  21. package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads.d.ts +5 -58
  22. package/esm/src/book-components/Chat/Chat/ChatImageAttachmentModal.d.ts +25 -0
  23. package/esm/src/book-components/Chat/Chat/ChatInputArea.d.ts +3 -0
  24. package/esm/src/book-components/Chat/Chat/ChatMessageAttachments.d.ts +27 -0
  25. package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +1 -1
  26. package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +1 -0
  27. package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +8 -0
  28. package/esm/src/book-components/Chat/Chat/CitationIframePreview.d.ts +1 -1
  29. package/esm/src/book-components/Chat/Chat/CitationIframePreview.test.d.ts +2 -0
  30. package/esm/src/book-components/Chat/Chat/insertDictationChunk.d.ts +1 -1
  31. package/esm/src/book-components/Chat/Chat/insertDictationChunk.test.d.ts +1 -0
  32. package/esm/src/book-components/Chat/Chat/learnDictationDictionary.test.d.ts +1 -0
  33. package/esm/src/book-components/Chat/Chat/refineFinalDictationChunk.test.d.ts +1 -0
  34. package/esm/src/book-components/Chat/Chat/useChatInputAreaComposer.d.ts +12 -0
  35. package/esm/src/book-components/Chat/Chat/useChatInputAreaComposer.draftMessage.test.d.ts +2 -0
  36. package/esm/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +1 -1
  37. package/esm/src/book-components/Chat/utils/parseMessageButtons.d.ts +16 -2
  38. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_BUILD_CACHE_FILENAME.d.ts +6 -0
  39. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_BUILD_CACHE_VERSION.d.ts +6 -0
  40. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_NEXT_BUILD_ID_FILENAME.d.ts +6 -0
  41. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/AgentsServerBuildCache.d.ts +10 -0
  42. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/AgentsServerBuildCacheOptions.d.ts +10 -0
  43. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/NODE_MODULES_DIRECTORY_NAME.d.ts +6 -0
  44. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/PreparedAgentsServerRuntime.d.ts +18 -0
  45. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerBuildSourceFingerprint.d.ts +6 -0
  46. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.d.ts +28 -0
  47. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/ensureAgentsServerBuild.d.ts +19 -0
  48. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/getAgentsServerBuildInputRelativePaths.d.ts +11 -0
  49. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerAppPath.d.ts +6 -0
  50. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerAppPathMaterialized.d.ts +6 -0
  51. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerBuildCacheCurrent.d.ts +7 -0
  52. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerBuildInputTestFile.d.ts +6 -0
  53. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerBuildInputDirectoryName.d.ts +6 -0
  54. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerBuildInputPath.d.ts +6 -0
  55. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerRuntimeSourcePath.d.ts +6 -0
  56. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isFile.d.ts +6 -0
  57. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isPathInsideNodeModules.d.ts +6 -0
  58. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/normalizeAgentsServerBuildInputPath.d.ts +6 -0
  59. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/prepareAgentsServerRuntime.d.ts +9 -0
  60. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/readAgentsServerBuildCache.d.ts +8 -0
  61. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerAppPath.d.ts +6 -0
  62. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerBuildAppPath.d.ts +9 -0
  63. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerBuildOutputPath.d.ts +7 -0
  64. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/resolveNextCliPath.d.ts +6 -0
  65. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/resolveNodeModulesPath.d.ts +6 -0
  66. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/runNextBuild.d.ts +12 -0
  67. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/shouldCopyAgentsServerRuntimePath.d.ts +6 -0
  68. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/synchronizeMaterializedAgentsServerRuntime.d.ts +11 -0
  69. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/writeAgentsServerBuildCache.d.ts +7 -0
  70. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +8 -109
  71. package/esm/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.d.ts +1 -1
  72. package/esm/src/cli/cli-commands/agents-server/ensureAgentsServerGitignoreFile.d.ts +1 -1
  73. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerChildEnvironment.d.ts +16 -0
  74. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerLogStreams.d.ts +30 -0
  75. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerRuntimePaths.d.ts +19 -0
  76. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerSupervisorState.d.ts +37 -0
  77. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/LocalAgentRunnerLimits.d.ts +9 -0
  78. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/PreparedAgentsServerLaunch.d.ts +13 -0
  79. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/StartAgentsServerOptions.d.ts +24 -0
  80. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/createInternalRouteErrorMessage.d.ts +6 -0
  81. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/createLocalAgentRunOptions.d.ts +9 -0
  82. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/forwardChildOutput.d.ts +13 -0
  83. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/loadAgentsServerProjectEnvironment.d.ts +6 -0
  84. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/prepareAgentsServerLaunch.d.ts +18 -0
  85. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/readInternalRouteErrorDetails.d.ts +6 -0
  86. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/resolveAgentsServerChildHostname.d.ts +7 -0
  87. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/startNextServer.d.ts +20 -0
  88. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/startUserChatJobWorkerPump.d.ts +15 -0
  89. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/stopChildProcess.d.ts +8 -0
  90. package/esm/src/cli/cli-commands/agents-server/startAgentsServer/waitForLocalAgentRunnerLimits.d.ts +16 -0
  91. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +3 -30
  92. package/esm/src/cli/cli-commands/common/createPositiveIntegerOptionParser.d.ts +10 -0
  93. package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +1 -1
  94. package/esm/src/collection/agent-collection/CreateAgentInput.d.ts +1 -1
  95. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.d.ts +1 -1
  96. package/esm/src/commitments/META/META.d.ts +2 -2
  97. package/esm/src/commitments/META_AVATAR/META_AVATAR.d.ts +9 -4
  98. package/esm/src/commitments/index.d.ts +1 -1
  99. package/esm/src/conversion/validation/validatePipeline/createPipelineValidationContext.d.ts +16 -0
  100. package/esm/src/conversion/validation/validatePipeline/validatePipelineCollectionsStructure.d.ts +7 -0
  101. package/esm/src/conversion/validation/validatePipeline/validatePipelineDependencyResolution.d.ts +7 -0
  102. package/esm/src/conversion/validation/validatePipeline/validatePipelineMetadata.d.ts +7 -0
  103. package/esm/src/conversion/validation/validatePipeline/validatePipelineParameters.d.ts +7 -0
  104. package/esm/src/conversion/validation/validatePipeline/validatePipelineTasks.d.ts +7 -0
  105. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/RegisteredLlmToolsMessageContext.d.ts +46 -0
  106. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/USED_ENV_FILENAME.d.ts +11 -0
  107. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolConfigurationStatusMessage.d.ts +7 -0
  108. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolEntryKey.d.ts +7 -0
  109. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolInstallationStatusMessage.d.ts +7 -0
  110. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolStatus.d.ts +14 -0
  111. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolsMessageContext.d.ts +7 -0
  112. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/getAvailableRegisteredLlmToolsEnvironmentVariables.d.ts +7 -0
  113. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/getRegisteredLlmToolRegisters.d.ts +7 -0
  114. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/hasRegisteredLlmToolEntry.d.ts +7 -0
  115. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/listRegisteredLlmToolEntries.d.ts +9 -0
  116. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/listRegisteredLlmToolStatuses.d.ts +7 -0
  117. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderAvailableRegisteredLlmTools.d.ts +7 -0
  118. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderNoRegisteredLlmToolsMessage.d.ts +7 -0
  119. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderRegisteredLlmToolsMessage.d.ts +8 -0
  120. package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderRelevantRegisteredLlmToolsEnvironmentVariables.d.ts +7 -0
  121. package/esm/src/utils/agent-message-runtime/agentMessageRuntimePaths.d.ts +24 -0
  122. package/esm/src/utils/agents/resolveAgentAvatarImageUrl.d.ts +2 -2
  123. package/esm/src/utils/ascii-art/$detectTerminalAnsiColorDepth.d.ts +15 -0
  124. package/esm/src/utils/ascii-art/convertImageDataToAsciiArt.d.ts +80 -0
  125. package/esm/src/utils/color/Color.d.ts +1 -1
  126. package/esm/src/version.d.ts +1 -1
  127. package/package.json +2 -2
  128. package/umd/book/scripts/import-markdown/increaseHeadings.d.ts +8 -0
  129. package/umd/index.umd.js +1575 -1204
  130. package/umd/index.umd.js.map +1 -1
  131. package/umd/scripts/run-codex-prompts/common/parseDuration.d.ts +19 -0
  132. package/umd/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +4 -0
  133. package/umd/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeSessionResurrection.d.ts +37 -0
  134. package/umd/scripts/run-codex-prompts/runners/claude-code/parseClaudeCodeOutputEvents.d.ts +47 -0
  135. package/umd/src/_packages/types.index.d.ts +2 -2
  136. package/umd/src/avatars/renderAvatarVisualAsciiArt.d.ts +98 -0
  137. package/umd/src/avatars/visuals/octopus3dAvatarVisualShared.d.ts +1 -1
  138. package/umd/src/book-components/BookEditor/BookEditorMonacoUploadPanel.d.ts +1 -1
  139. package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/bookEditorMonacoUploadTypes.d.ts +120 -0
  140. package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/clearScheduledTimer.d.ts +6 -0
  141. package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/enqueueFilesForUpload.d.ts +26 -0
  142. package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadEditorSync.d.ts +23 -0
  143. package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadItemsState.d.ts +21 -0
  144. package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadProgressQueue.d.ts +12 -0
  145. package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadQueue.d.ts +27 -0
  146. package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/useCompletedUploadsAutoClear.d.ts +12 -0
  147. package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads.d.ts +5 -58
  148. package/umd/src/book-components/Chat/Chat/ChatImageAttachmentModal.d.ts +25 -0
  149. package/umd/src/book-components/Chat/Chat/ChatInputArea.d.ts +3 -0
  150. package/umd/src/book-components/Chat/Chat/ChatMessageAttachments.d.ts +27 -0
  151. package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +1 -1
  152. package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +1 -0
  153. package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +8 -0
  154. package/umd/src/book-components/Chat/Chat/CitationIframePreview.d.ts +1 -1
  155. package/umd/src/book-components/Chat/Chat/CitationIframePreview.test.d.ts +2 -0
  156. package/umd/src/book-components/Chat/Chat/insertDictationChunk.d.ts +1 -1
  157. package/umd/src/book-components/Chat/Chat/insertDictationChunk.test.d.ts +1 -0
  158. package/umd/src/book-components/Chat/Chat/learnDictationDictionary.test.d.ts +1 -0
  159. package/umd/src/book-components/Chat/Chat/refineFinalDictationChunk.test.d.ts +1 -0
  160. package/umd/src/book-components/Chat/Chat/useChatInputAreaComposer.d.ts +12 -0
  161. package/umd/src/book-components/Chat/Chat/useChatInputAreaComposer.draftMessage.test.d.ts +2 -0
  162. package/umd/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +1 -1
  163. package/umd/src/book-components/Chat/utils/parseMessageButtons.d.ts +16 -2
  164. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_BUILD_CACHE_FILENAME.d.ts +6 -0
  165. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_BUILD_CACHE_VERSION.d.ts +6 -0
  166. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_NEXT_BUILD_ID_FILENAME.d.ts +6 -0
  167. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/AgentsServerBuildCache.d.ts +10 -0
  168. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/AgentsServerBuildCacheOptions.d.ts +10 -0
  169. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/NODE_MODULES_DIRECTORY_NAME.d.ts +6 -0
  170. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/PreparedAgentsServerRuntime.d.ts +18 -0
  171. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerBuildSourceFingerprint.d.ts +6 -0
  172. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.d.ts +28 -0
  173. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/ensureAgentsServerBuild.d.ts +19 -0
  174. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/getAgentsServerBuildInputRelativePaths.d.ts +11 -0
  175. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerAppPath.d.ts +6 -0
  176. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerAppPathMaterialized.d.ts +6 -0
  177. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerBuildCacheCurrent.d.ts +7 -0
  178. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerBuildInputTestFile.d.ts +6 -0
  179. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerBuildInputDirectoryName.d.ts +6 -0
  180. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerBuildInputPath.d.ts +6 -0
  181. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerRuntimeSourcePath.d.ts +6 -0
  182. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isFile.d.ts +6 -0
  183. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isPathInsideNodeModules.d.ts +6 -0
  184. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/normalizeAgentsServerBuildInputPath.d.ts +6 -0
  185. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/prepareAgentsServerRuntime.d.ts +9 -0
  186. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/readAgentsServerBuildCache.d.ts +8 -0
  187. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerAppPath.d.ts +6 -0
  188. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerBuildAppPath.d.ts +9 -0
  189. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerBuildOutputPath.d.ts +7 -0
  190. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/resolveNextCliPath.d.ts +6 -0
  191. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/resolveNodeModulesPath.d.ts +6 -0
  192. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/runNextBuild.d.ts +12 -0
  193. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/shouldCopyAgentsServerRuntimePath.d.ts +6 -0
  194. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/synchronizeMaterializedAgentsServerRuntime.d.ts +11 -0
  195. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/writeAgentsServerBuildCache.d.ts +7 -0
  196. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +8 -109
  197. package/umd/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.d.ts +1 -1
  198. package/umd/src/cli/cli-commands/agents-server/ensureAgentsServerGitignoreFile.d.ts +1 -1
  199. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerChildEnvironment.d.ts +16 -0
  200. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerLogStreams.d.ts +30 -0
  201. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerRuntimePaths.d.ts +19 -0
  202. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerSupervisorState.d.ts +37 -0
  203. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/LocalAgentRunnerLimits.d.ts +9 -0
  204. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/PreparedAgentsServerLaunch.d.ts +13 -0
  205. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/StartAgentsServerOptions.d.ts +24 -0
  206. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/createInternalRouteErrorMessage.d.ts +6 -0
  207. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/createLocalAgentRunOptions.d.ts +9 -0
  208. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/forwardChildOutput.d.ts +13 -0
  209. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/loadAgentsServerProjectEnvironment.d.ts +6 -0
  210. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/prepareAgentsServerLaunch.d.ts +18 -0
  211. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/readInternalRouteErrorDetails.d.ts +6 -0
  212. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/resolveAgentsServerChildHostname.d.ts +7 -0
  213. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/startNextServer.d.ts +20 -0
  214. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/startUserChatJobWorkerPump.d.ts +15 -0
  215. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/stopChildProcess.d.ts +8 -0
  216. package/umd/src/cli/cli-commands/agents-server/startAgentsServer/waitForLocalAgentRunnerLimits.d.ts +16 -0
  217. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +3 -30
  218. package/umd/src/cli/cli-commands/common/createPositiveIntegerOptionParser.d.ts +10 -0
  219. package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +1 -1
  220. package/umd/src/collection/agent-collection/CreateAgentInput.d.ts +1 -1
  221. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.d.ts +1 -1
  222. package/umd/src/commitments/META/META.d.ts +2 -2
  223. package/umd/src/commitments/META_AVATAR/META_AVATAR.d.ts +9 -4
  224. package/umd/src/commitments/index.d.ts +1 -1
  225. package/umd/src/conversion/validation/validatePipeline/createPipelineValidationContext.d.ts +16 -0
  226. package/umd/src/conversion/validation/validatePipeline/validatePipelineCollectionsStructure.d.ts +7 -0
  227. package/umd/src/conversion/validation/validatePipeline/validatePipelineDependencyResolution.d.ts +7 -0
  228. package/umd/src/conversion/validation/validatePipeline/validatePipelineMetadata.d.ts +7 -0
  229. package/umd/src/conversion/validation/validatePipeline/validatePipelineParameters.d.ts +7 -0
  230. package/umd/src/conversion/validation/validatePipeline/validatePipelineTasks.d.ts +7 -0
  231. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/RegisteredLlmToolsMessageContext.d.ts +46 -0
  232. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/USED_ENV_FILENAME.d.ts +11 -0
  233. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolConfigurationStatusMessage.d.ts +7 -0
  234. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolEntryKey.d.ts +7 -0
  235. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolInstallationStatusMessage.d.ts +7 -0
  236. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolStatus.d.ts +14 -0
  237. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolsMessageContext.d.ts +7 -0
  238. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/getAvailableRegisteredLlmToolsEnvironmentVariables.d.ts +7 -0
  239. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/getRegisteredLlmToolRegisters.d.ts +7 -0
  240. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/hasRegisteredLlmToolEntry.d.ts +7 -0
  241. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/listRegisteredLlmToolEntries.d.ts +9 -0
  242. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/listRegisteredLlmToolStatuses.d.ts +7 -0
  243. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderAvailableRegisteredLlmTools.d.ts +7 -0
  244. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderNoRegisteredLlmToolsMessage.d.ts +7 -0
  245. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderRegisteredLlmToolsMessage.d.ts +8 -0
  246. package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderRelevantRegisteredLlmToolsEnvironmentVariables.d.ts +7 -0
  247. package/umd/src/utils/agent-message-runtime/agentMessageRuntimePaths.d.ts +24 -0
  248. package/umd/src/utils/agents/resolveAgentAvatarImageUrl.d.ts +2 -2
  249. package/umd/src/utils/ascii-art/$detectTerminalAnsiColorDepth.d.ts +15 -0
  250. package/umd/src/utils/ascii-art/convertImageDataToAsciiArt.d.ts +80 -0
  251. package/umd/src/utils/color/Color.d.ts +1 -1
  252. package/umd/src/version.d.ts +1 -1
  253. package/esm/scripts/run-codex-prompts/common/runGoScript/buildTemporaryPromptScriptPath.d.ts +0 -14
  254. package/esm/src/book-components/Chat/utils/livePagePreviewConstants.d.ts +0 -12
  255. package/umd/scripts/run-codex-prompts/common/runGoScript/buildTemporaryPromptScriptPath.d.ts +0 -14
  256. package/umd/src/book-components/Chat/utils/livePagePreviewConstants.d.ts +0 -12
package/umd/index.umd.js CHANGED
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('path'), require('fs/promises'), require('colors'), require('child_process'), require('spacetrim'), require('@openai/agents'), require('url'), require('@mozilla/readability'), require('jsdom'), require('crypto-js'), require('crypto-js/enc-hex'), require('showdown'), require('jszip'), require('waitasecond'), require('dotenv'), require('crypto-js/sha256'), require('rxjs'), require('crypto'), require('moment'), require('mime-types'), require('papaparse'), require('bottleneck'), require('openai')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'path', 'fs/promises', 'colors', 'child_process', 'spacetrim', '@openai/agents', 'url', '@mozilla/readability', 'jsdom', 'crypto-js', 'crypto-js/enc-hex', 'showdown', 'jszip', 'waitasecond', 'dotenv', 'crypto-js/sha256', 'rxjs', 'crypto', 'moment', 'mime-types', 'papaparse', 'bottleneck', 'openai'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-node"] = {}, global.path, global.promises, global.colors, global.child_process, global._spaceTrim, global.agents, global.url, global.readability, global.jsdom, global.CryptoJS, global.hexEncoder, global.showdown, global.JSZip, global.waitasecond, global.dotenv, global.sha256, global.rxjs, null, global.moment, global.mimeTypes, global.papaparse, global.Bottleneck, global.OpenAI));
5
- })(this, (function (exports, path, promises, colors, child_process, _spaceTrim, agents, url, readability, jsdom, CryptoJS, hexEncoder, showdown, JSZip, waitasecond, dotenv, sha256, rxjs, crypto, moment, mimeTypes, papaparse, Bottleneck, OpenAI) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('path'), require('fs/promises'), require('spacetrim'), require('colors'), require('child_process'), require('@openai/agents'), require('url'), require('@mozilla/readability'), require('jsdom'), require('crypto-js'), require('crypto-js/enc-hex'), require('showdown'), require('jszip'), require('waitasecond'), require('dotenv'), require('crypto-js/sha256'), require('rxjs'), require('crypto'), require('moment'), require('mime-types'), require('papaparse'), require('bottleneck'), require('openai')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'path', 'fs/promises', 'spacetrim', 'colors', 'child_process', '@openai/agents', 'url', '@mozilla/readability', 'jsdom', 'crypto-js', 'crypto-js/enc-hex', 'showdown', 'jszip', 'waitasecond', 'dotenv', 'crypto-js/sha256', 'rxjs', 'crypto', 'moment', 'mime-types', 'papaparse', 'bottleneck', 'openai'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-node"] = {}, global.path, global.promises, global._spaceTrim, global.colors, global.child_process, global.agents, global.url, global.readability, global.jsdom, global.CryptoJS, global.hexEncoder, global.showdown, global.JSZip, global.waitasecond, global.dotenv, global.sha256, global.rxjs, null, global.moment, global.mimeTypes, global.papaparse, global.Bottleneck, global.OpenAI));
5
+ })(this, (function (exports, path, promises, _spaceTrim, colors, child_process, agents, url, readability, jsdom, CryptoJS, hexEncoder, showdown, JSZip, waitasecond, dotenv, sha256, rxjs, crypto, moment, mimeTypes, papaparse, Bottleneck, OpenAI) { 'use strict';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
@@ -24,8 +24,8 @@
24
24
  return Object.freeze(n);
25
25
  }
26
26
 
27
- var colors__default = /*#__PURE__*/_interopDefaultLegacy(colors);
28
27
  var _spaceTrim__default = /*#__PURE__*/_interopDefaultLegacy(_spaceTrim);
28
+ var colors__default = /*#__PURE__*/_interopDefaultLegacy(colors);
29
29
  var CryptoJS__default = /*#__PURE__*/_interopDefaultLegacy(CryptoJS);
30
30
  var hexEncoder__default = /*#__PURE__*/_interopDefaultLegacy(hexEncoder);
31
31
  var showdown__default = /*#__PURE__*/_interopDefaultLegacy(showdown);
@@ -51,7 +51,7 @@
51
51
  * @generated
52
52
  * @see https://github.com/webgptorg/promptbook
53
53
  */
54
- const PROMPTBOOK_ENGINE_VERSION = '0.113.0-1';
54
+ const PROMPTBOOK_ENGINE_VERSION = '0.113.0-10';
55
55
  /**
56
56
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
57
57
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -396,6 +396,34 @@
396
396
  // TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
397
397
  // TODO: Maybe use nanoid instead https://github.com/ai/nanoid
398
398
 
399
+ /**
400
+ * Increase the level of all headings in the markdown content
401
+ *
402
+ * @example h1 -> h2, h2 -> h3, ...
403
+ * @param content The markdown content
404
+ * @returns The markdown with increased headings
405
+ */
406
+ function increaseHeadings(content) {
407
+ const lines = content.split(/\r?\n/);
408
+ let inCodeBlock = false;
409
+ for (let i = 0; i < lines.length; i++) {
410
+ const line = lines[i];
411
+ if (line.startsWith('```')) {
412
+ inCodeBlock = !inCodeBlock;
413
+ }
414
+ if (inCodeBlock) {
415
+ continue;
416
+ }
417
+ const headingMatch = line.match(/^(#+)(.*)/);
418
+ if (headingMatch) {
419
+ const headingLevel = headingMatch[1].length;
420
+ const headingText = headingMatch[2];
421
+ lines[i] = `${'#'.repeat(headingLevel + 1)}${headingText}`;
422
+ }
423
+ }
424
+ return lines.join('\n');
425
+ }
426
+
399
427
  /**
400
428
  * Appends optional coding context to a runner prompt.
401
429
  */
@@ -408,7 +436,13 @@
408
436
  if (normalizedPrompt === '') {
409
437
  return normalizedContext;
410
438
  }
411
- return `${normalizedPrompt}\n\n${normalizedContext}`;
439
+ return _spaceTrim.spaceTrim((block) => `
440
+ ${block(normalizedPrompt)}
441
+
442
+ ## Context
443
+
444
+ ${block(increaseHeadings(normalizedContext))}
445
+ `);
412
446
  }
413
447
 
414
448
  /**
@@ -537,6 +571,127 @@
537
571
  });
538
572
  }
539
573
 
574
+ /**
575
+ * Validates that a channel value is a valid number within the range of 0 to 255.
576
+ * Throws an error if the value is not valid.
577
+ *
578
+ * @param channelName - The name of the channel being validated.
579
+ * @param value - The value of the channel to validate.
580
+ * @throws Will throw an error if the value is not a valid channel number.
581
+ *
582
+ * @private util of `@promptbook/color`
583
+ */
584
+ function checkChannelValue(channelName, value) {
585
+ if (typeof value !== 'number') {
586
+ throw new Error(`${channelName} channel value is not number but ${typeof value}`);
587
+ }
588
+ if (isNaN(value)) {
589
+ throw new Error(`${channelName} channel value is NaN`);
590
+ }
591
+ if (Math.round(value) !== value) {
592
+ throw new Error(`${channelName} channel is not whole number, it is ${value}`);
593
+ }
594
+ if (value < 0) {
595
+ throw new Error(`${channelName} channel is lower than 0, it is ${value}`);
596
+ }
597
+ if (value > 255) {
598
+ throw new Error(`${channelName} channel is greater than 255, it is ${value}`);
599
+ }
600
+ }
601
+
602
+ /**
603
+ * Shared immutable channel storage and serialization helpers for `Color`.
604
+ *
605
+ * @private base class of Color
606
+ */
607
+ class ColorValue {
608
+ constructor(red, green, blue, alpha = 255) {
609
+ this.red = red;
610
+ this.green = green;
611
+ this.blue = blue;
612
+ this.alpha = alpha;
613
+ checkChannelValue('Red', red);
614
+ checkChannelValue('Green', green);
615
+ checkChannelValue('Blue', blue);
616
+ checkChannelValue('Alpha', alpha);
617
+ }
618
+ /**
619
+ * Shortcut for `red` property
620
+ * Number from 0 to 255
621
+ * @alias red
622
+ */
623
+ get r() {
624
+ return this.red;
625
+ }
626
+ /**
627
+ * Shortcut for `green` property
628
+ * Number from 0 to 255
629
+ * @alias green
630
+ */
631
+ get g() {
632
+ return this.green;
633
+ }
634
+ /**
635
+ * Shortcut for `blue` property
636
+ * Number from 0 to 255
637
+ * @alias blue
638
+ */
639
+ get b() {
640
+ return this.blue;
641
+ }
642
+ /**
643
+ * Shortcut for `alpha` property
644
+ * Number from 0 (transparent) to 255 (opaque)
645
+ * @alias alpha
646
+ */
647
+ get a() {
648
+ return this.alpha;
649
+ }
650
+ /**
651
+ * Shortcut for `alpha` property
652
+ * Number from 0 (transparent) to 255 (opaque)
653
+ * @alias alpha
654
+ */
655
+ get opacity() {
656
+ return this.alpha;
657
+ }
658
+ /**
659
+ * Shortcut for 1-`alpha` property
660
+ */
661
+ get transparency() {
662
+ return 255 - this.alpha;
663
+ }
664
+ clone() {
665
+ return take(this.createColor(this.red, this.green, this.blue, this.alpha));
666
+ }
667
+ toString() {
668
+ return this.toHex();
669
+ }
670
+ toHex() {
671
+ if (this.alpha === 255) {
672
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
673
+ .toString(16)
674
+ .padStart(2, '0')}`;
675
+ }
676
+ else {
677
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
678
+ .toString(16)
679
+ .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
680
+ }
681
+ }
682
+ toRgb() {
683
+ if (this.alpha === 255) {
684
+ return `rgb(${this.red}, ${this.green}, ${this.blue})`;
685
+ }
686
+ else {
687
+ return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
688
+ }
689
+ }
690
+ toHsl() {
691
+ throw new Error(`Getting HSL is not implemented`);
692
+ }
693
+ }
694
+
540
695
  /**
541
696
  * 🎨 List of all 140 color names which are supported by CSS
542
697
  *
@@ -696,127 +851,6 @@
696
851
  };
697
852
  // Note: [💞] Ignore a discrepancy between file name and entity name
698
853
 
699
- /**
700
- * Validates that a channel value is a valid number within the range of 0 to 255.
701
- * Throws an error if the value is not valid.
702
- *
703
- * @param channelName - The name of the channel being validated.
704
- * @param value - The value of the channel to validate.
705
- * @throws Will throw an error if the value is not a valid channel number.
706
- *
707
- * @private util of `@promptbook/color`
708
- */
709
- function checkChannelValue(channelName, value) {
710
- if (typeof value !== 'number') {
711
- throw new Error(`${channelName} channel value is not number but ${typeof value}`);
712
- }
713
- if (isNaN(value)) {
714
- throw new Error(`${channelName} channel value is NaN`);
715
- }
716
- if (Math.round(value) !== value) {
717
- throw new Error(`${channelName} channel is not whole number, it is ${value}`);
718
- }
719
- if (value < 0) {
720
- throw new Error(`${channelName} channel is lower than 0, it is ${value}`);
721
- }
722
- if (value > 255) {
723
- throw new Error(`${channelName} channel is greater than 255, it is ${value}`);
724
- }
725
- }
726
-
727
- /**
728
- * Shared immutable channel storage and serialization helpers for `Color`.
729
- *
730
- * @private base class of Color
731
- */
732
- class ColorValue {
733
- constructor(red, green, blue, alpha = 255) {
734
- this.red = red;
735
- this.green = green;
736
- this.blue = blue;
737
- this.alpha = alpha;
738
- checkChannelValue('Red', red);
739
- checkChannelValue('Green', green);
740
- checkChannelValue('Blue', blue);
741
- checkChannelValue('Alpha', alpha);
742
- }
743
- /**
744
- * Shortcut for `red` property
745
- * Number from 0 to 255
746
- * @alias red
747
- */
748
- get r() {
749
- return this.red;
750
- }
751
- /**
752
- * Shortcut for `green` property
753
- * Number from 0 to 255
754
- * @alias green
755
- */
756
- get g() {
757
- return this.green;
758
- }
759
- /**
760
- * Shortcut for `blue` property
761
- * Number from 0 to 255
762
- * @alias blue
763
- */
764
- get b() {
765
- return this.blue;
766
- }
767
- /**
768
- * Shortcut for `alpha` property
769
- * Number from 0 (transparent) to 255 (opaque)
770
- * @alias alpha
771
- */
772
- get a() {
773
- return this.alpha;
774
- }
775
- /**
776
- * Shortcut for `alpha` property
777
- * Number from 0 (transparent) to 255 (opaque)
778
- * @alias alpha
779
- */
780
- get opacity() {
781
- return this.alpha;
782
- }
783
- /**
784
- * Shortcut for 1-`alpha` property
785
- */
786
- get transparency() {
787
- return 255 - this.alpha;
788
- }
789
- clone() {
790
- return take(this.createColor(this.red, this.green, this.blue, this.alpha));
791
- }
792
- toString() {
793
- return this.toHex();
794
- }
795
- toHex() {
796
- if (this.alpha === 255) {
797
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
798
- .toString(16)
799
- .padStart(2, '0')}`;
800
- }
801
- else {
802
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
803
- .toString(16)
804
- .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
805
- }
806
- }
807
- toRgb() {
808
- if (this.alpha === 255) {
809
- return `rgb(${this.red}, ${this.green}, ${this.blue})`;
810
- }
811
- else {
812
- return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
813
- }
814
- }
815
- toHsl() {
816
- throw new Error(`Getting HSL is not implemented`);
817
- }
818
- }
819
-
820
854
  /**
821
855
  * Checks if the given value is a valid hex color string
822
856
  *
@@ -2790,6 +2824,31 @@
2790
2824
  * Note: [💞] Ignore a discrepancy between file name and entity name
2791
2825
  */
2792
2826
 
2827
+ /**
2828
+ * Pattern that matches durations like "1h", "30m", "5s", "1h30m", "1h30m5s".
2829
+ */
2830
+ /**
2831
+ * Formats a duration in milliseconds into a compact human-readable string.
2832
+ *
2833
+ * Examples: `3600000` → `"1h"`, `90000` → `"1m 30s"`, `5000` → `"5s"`.
2834
+ *
2835
+ * @private internal utility of `ptbk coder run`
2836
+ */
2837
+ function formatDurationMs(ms) {
2838
+ const totalSeconds = Math.ceil(ms / 1000);
2839
+ const hours = Math.floor(totalSeconds / 3600);
2840
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
2841
+ const seconds = totalSeconds % 60;
2842
+ const parts = [];
2843
+ if (hours > 0)
2844
+ parts.push(`${hours}h`);
2845
+ if (minutes > 0)
2846
+ parts.push(`${minutes}m`);
2847
+ if (seconds > 0 || parts.length === 0)
2848
+ parts.push(`${seconds}s`);
2849
+ return parts.join(' ');
2850
+ }
2851
+
2793
2852
  /**
2794
2853
  * Formats one unknown error-like value into readable text for logs and feedback.
2795
2854
  */
@@ -3025,15 +3084,211 @@
3025
3084
  * Builds the shell script that runs Claude Code with the prompt and coding context.
3026
3085
  */
3027
3086
  function buildClaudeScript(options) {
3087
+ const MODEL_ARGUMENT = options.model ? ` --model ${options.model}` : '';
3028
3088
  const THINKING_LEVEL_ARGUMENT = options.thinkingLevel ? ` --effort ${options.thinkingLevel}` : '';
3089
+ const RESUME_SESSION_ARGUMENT = options.resumeSessionId
3090
+ ? ` --resume ${quoteShellArgument(options.resumeSessionId)}`
3091
+ : '';
3029
3092
  return spaceTrim((block) => `
3030
- claude --allowedTools "Bash,Read,Edit,Write"${THINKING_LEVEL_ARGUMENT} --output-format json --print <<'${CLAUDE_PROMPT_DELIMITER}'
3093
+ claude --allowedTools "Bash,Read,Edit,Write"${MODEL_ARGUMENT}${THINKING_LEVEL_ARGUMENT}${RESUME_SESSION_ARGUMENT} --output-format stream-json --verbose --include-partial-messages --print <<'${CLAUDE_PROMPT_DELIMITER}'
3031
3094
 
3032
3095
  ${block(options.prompt)}
3033
3096
 
3034
3097
  ${CLAUDE_PROMPT_DELIMITER}
3035
3098
  `);
3036
3099
  }
3100
+ /**
3101
+ * Quotes one shell argument for the generated Bash script.
3102
+ */
3103
+ function quoteShellArgument(value) {
3104
+ return JSON.stringify(value);
3105
+ }
3106
+
3107
+ /**
3108
+ * Parses all JSON lines from Claude Code output while ignoring shell prefixes and stack traces.
3109
+ */
3110
+ function parseClaudeCodeOutputEvents(output) {
3111
+ return output
3112
+ .split(/\r?\n/u)
3113
+ .map(parseClaudeCodeOutputLine)
3114
+ .filter((event) => event !== undefined);
3115
+ }
3116
+ /**
3117
+ * Finds the final Claude Code result event in a stream-json output blob.
3118
+ */
3119
+ function findClaudeCodeResultEvent(output) {
3120
+ return parseClaudeCodeOutputEvents(output).find((event) => event.type === 'result');
3121
+ }
3122
+ /**
3123
+ * Parses one output line when it contains a JSON object.
3124
+ */
3125
+ function parseClaudeCodeOutputLine(line) {
3126
+ const jsonStartIndex = line.indexOf('{');
3127
+ if (jsonStartIndex === -1) {
3128
+ return undefined;
3129
+ }
3130
+ const possibleJson = line.slice(jsonStartIndex).trim();
3131
+ try {
3132
+ const parsed = JSON.parse(possibleJson);
3133
+ if (isClaudeCodeOutputEvent(parsed)) {
3134
+ return parsed;
3135
+ }
3136
+ }
3137
+ catch (_a) {
3138
+ return undefined;
3139
+ }
3140
+ return undefined;
3141
+ }
3142
+ /**
3143
+ * Checks whether an unknown parsed JSON value has the object shape expected from Claude Code events.
3144
+ */
3145
+ function isClaudeCodeOutputEvent(value) {
3146
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3147
+ }
3148
+
3149
+ /**
3150
+ * Number of milliseconds added after Claude's reported reset timestamp before retrying.
3151
+ */
3152
+ const CLAUDE_CODE_SESSION_LIMIT_RESET_BUFFER_MS = 30 * 1000;
3153
+ /**
3154
+ * Fallback delay used when Claude reports a session limit without a machine-readable reset timestamp.
3155
+ */
3156
+ const DEFAULT_CLAUDE_CODE_SESSION_LIMIT_RETRY_DELAY_MS = 10 * 60 * 1000;
3157
+ /**
3158
+ * Extracts a Claude Code session-limit failure from a thrown runner error.
3159
+ */
3160
+ function extractClaudeCodeSessionLimitFromError(error) {
3161
+ return extractClaudeCodeSessionLimitFromOutput(formatUnknownErrorDetails(error));
3162
+ }
3163
+ /**
3164
+ * Extracts a Claude Code session-limit failure from raw stream-json output.
3165
+ */
3166
+ function extractClaudeCodeSessionLimitFromOutput(output) {
3167
+ var _a, _b, _c, _d, _e;
3168
+ const events = parseClaudeCodeOutputEvents(output);
3169
+ const rejectedRateLimitEvent = events.find(isRejectedRateLimitEvent);
3170
+ const sessionLimitResultEvent = events.find(isSessionLimitResultEvent);
3171
+ const sessionLimitTextEvent = events.find(isSessionLimitTextEvent);
3172
+ if (!sessionLimitResultEvent && !sessionLimitTextEvent) {
3173
+ return undefined;
3174
+ }
3175
+ const sessionId = findClaudeCodeSessionId(events);
3176
+ if (!sessionId) {
3177
+ return undefined;
3178
+ }
3179
+ const resetDate = extractResetDate(rejectedRateLimitEvent);
3180
+ const rateLimitType = (_b = (_a = rejectedRateLimitEvent === null || rejectedRateLimitEvent === void 0 ? void 0 : rejectedRateLimitEvent.rate_limit_info) === null || _a === void 0 ? void 0 : _a.rateLimitType) !== null && _b !== void 0 ? _b : (_c = sessionLimitResultEvent === null || sessionLimitResultEvent === void 0 ? void 0 : sessionLimitResultEvent.rate_limit_info) === null || _c === void 0 ? void 0 : _c.rateLimitType;
3181
+ const summary = (_e = (_d = extractSessionLimitSummary(sessionLimitResultEvent)) !== null && _d !== void 0 ? _d : extractSessionLimitSummary(sessionLimitTextEvent)) !== null && _e !== void 0 ? _e : 'Claude Code reported a session limit.';
3182
+ return {
3183
+ sessionId,
3184
+ resetDate,
3185
+ rateLimitType,
3186
+ summary,
3187
+ };
3188
+ }
3189
+ /**
3190
+ * Computes how long to wait before trying to resume the Claude Code session.
3191
+ */
3192
+ function getClaudeCodeSessionLimitDelayMs(sessionLimit, nowMs = Date.now()) {
3193
+ if (!sessionLimit.resetDate) {
3194
+ return DEFAULT_CLAUDE_CODE_SESSION_LIMIT_RETRY_DELAY_MS;
3195
+ }
3196
+ return Math.max(0, sessionLimit.resetDate.getTime() - nowMs + CLAUDE_CODE_SESSION_LIMIT_RESET_BUFFER_MS);
3197
+ }
3198
+ /**
3199
+ * Builds the follow-up prompt sent into a resumed Claude Code session after its limit resets.
3200
+ */
3201
+ function buildClaudeCodeSessionResurrectionPrompt(originalPrompt, sessionId) {
3202
+ return spaceTrim((block) => `
3203
+ ## Claude Code session resurrection
3204
+
3205
+ You are resuming Claude Code session \`${sessionId}\` after Claude reported a session limit.
3206
+ Continue the same task from the existing repository and session state.
3207
+ Do not discard useful context, completed analysis, or file changes from the resumed session.
3208
+
3209
+ The original prompt is repeated below as reference in case the resumed session needs it.
3210
+
3211
+ ## Original prompt
3212
+
3213
+ ${block(originalPrompt)}
3214
+ `);
3215
+ }
3216
+ /**
3217
+ * Builds a compact one-line label for logs and terminal UI messages.
3218
+ */
3219
+ function formatClaudeCodeSessionLimitForDisplay(sessionLimit) {
3220
+ const resetSuffix = sessionLimit.resetDate
3221
+ ? `, resets at ${sessionLimit.resetDate.toISOString()}`
3222
+ : `, retrying every ${formatDurationMs(DEFAULT_CLAUDE_CODE_SESSION_LIMIT_RETRY_DELAY_MS)}`;
3223
+ const rateLimitSuffix = sessionLimit.rateLimitType ? ` (${sessionLimit.rateLimitType})` : '';
3224
+ return `${sessionLimit.summary}${rateLimitSuffix}${resetSuffix}`;
3225
+ }
3226
+ /**
3227
+ * Returns true when the event is an explicit rejected Claude Code rate-limit event.
3228
+ */
3229
+ function isRejectedRateLimitEvent(event) {
3230
+ var _a;
3231
+ return event.type === 'rate_limit_event' && ((_a = event.rate_limit_info) === null || _a === void 0 ? void 0 : _a.status) === 'rejected';
3232
+ }
3233
+ /**
3234
+ * Returns true when the final result reports the Claude session limit.
3235
+ */
3236
+ function isSessionLimitResultEvent(event) {
3237
+ return (event.type === 'result' &&
3238
+ event.is_error === true &&
3239
+ event.api_error_status === 429 &&
3240
+ isSessionLimitText(event.result));
3241
+ }
3242
+ /**
3243
+ * Returns true when any assistant event reports the Claude session limit in text.
3244
+ */
3245
+ function isSessionLimitTextEvent(event) {
3246
+ if (event.error !== 'rate_limit') {
3247
+ return false;
3248
+ }
3249
+ return isSessionLimitText(extractSessionLimitSummary(event));
3250
+ }
3251
+ /**
3252
+ * Extracts the first session id reported by Claude Code.
3253
+ */
3254
+ function findClaudeCodeSessionId(events) {
3255
+ return events.map((event) => event.session_id).find((sessionId) => Boolean(sessionId));
3256
+ }
3257
+ /**
3258
+ * Extracts Claude's reset timestamp from a rejected rate-limit event.
3259
+ */
3260
+ function extractResetDate(event) {
3261
+ var _a;
3262
+ const resetsAt = (_a = event === null || event === void 0 ? void 0 : event.rate_limit_info) === null || _a === void 0 ? void 0 : _a.resetsAt;
3263
+ if (typeof resetsAt !== 'number' || !Number.isFinite(resetsAt)) {
3264
+ return undefined;
3265
+ }
3266
+ const resetDate = new Date(resetsAt * 1000);
3267
+ if (Number.isNaN(resetDate.getTime())) {
3268
+ return undefined;
3269
+ }
3270
+ return resetDate;
3271
+ }
3272
+ /**
3273
+ * Extracts a human-readable session-limit summary from an event.
3274
+ */
3275
+ function extractSessionLimitSummary(event) {
3276
+ var _a, _b;
3277
+ if (!event) {
3278
+ return undefined;
3279
+ }
3280
+ if (typeof event.result === 'string' && event.result.trim()) {
3281
+ return event.result.trim();
3282
+ }
3283
+ const textContent = (_b = (_a = event.message) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? void 0 : _b.map((content) => { var _a; return (content.type === 'text' ? (_a = content.text) === null || _a === void 0 ? void 0 : _a.trim() : undefined); }).find((text) => Boolean(text));
3284
+ return textContent;
3285
+ }
3286
+ /**
3287
+ * Checks whether a text value names the Claude Code session limit.
3288
+ */
3289
+ function isSessionLimitText(text) {
3290
+ return /\bsession\s+limit\b/iu.test(text !== null && text !== void 0 ? text : '');
3291
+ }
3037
3292
 
3038
3293
  /**
3039
3294
  * Represents the uncertain value
@@ -3109,14 +3364,8 @@
3109
3364
  function parseClaudeCodeJsonOutput(output) {
3110
3365
  var _a, _b, _c, _d;
3111
3366
  try {
3112
- // Extract JSON from the output - it should be a line starting with {"type":"result"
3113
- const lines = output.split(/\r?\n/);
3114
- const jsonLine = lines.find((line) => line.trim().startsWith('{"type":"result"'));
3115
- if (!jsonLine) {
3116
- return UNCERTAIN_USAGE;
3117
- }
3118
- const parsed = JSON.parse(jsonLine);
3119
- if (parsed.type !== 'result') {
3367
+ const parsed = findClaudeCodeResultEvent(output);
3368
+ if (!parsed) {
3120
3369
  return UNCERTAIN_USAGE;
3121
3370
  }
3122
3371
  const totalInputTokens = (((_a = parsed.usage) === null || _a === void 0 ? void 0 : _a.input_tokens) || 0) +
@@ -3160,6 +3409,10 @@
3160
3409
  }
3161
3410
  }
3162
3411
 
3412
+ /**
3413
+ * Polling interval used while waiting for Claude Code session limits to reset.
3414
+ */
3415
+ const CLAUDE_CODE_SESSION_RESURRECTION_POLL_MS = 30 * 1000;
3163
3416
  /**
3164
3417
  * Runs prompts via the Claude Code CLI.
3165
3418
  */
@@ -3175,21 +3428,102 @@
3175
3428
  * Runs the prompt using Claude Code and parses usage output.
3176
3429
  */
3177
3430
  async runPrompt(options) {
3431
+ let resumeSessionId;
3432
+ let prompt = options.prompt;
3433
+ let resurrectionCount = 0;
3434
+ while (true) {
3435
+ const output = await this.runClaudeCodeOnce({
3436
+ ...options,
3437
+ prompt,
3438
+ resumeSessionId,
3439
+ }).catch(async (error) => {
3440
+ const sessionLimit = extractClaudeCodeSessionLimitFromError(error);
3441
+ if (!sessionLimit) {
3442
+ throw error;
3443
+ }
3444
+ resurrectionCount++;
3445
+ await waitForClaudeCodeSessionLimitReset(sessionLimit, resurrectionCount, options);
3446
+ resumeSessionId = sessionLimit.sessionId;
3447
+ prompt = buildClaudeCodeSessionResurrectionPrompt(options.prompt, sessionLimit.sessionId);
3448
+ return undefined;
3449
+ });
3450
+ if (output === undefined) {
3451
+ continue;
3452
+ }
3453
+ const sessionLimit = extractClaudeCodeSessionLimitFromOutput(output);
3454
+ if (sessionLimit) {
3455
+ resurrectionCount++;
3456
+ await waitForClaudeCodeSessionLimitReset(sessionLimit, resurrectionCount, options);
3457
+ resumeSessionId = sessionLimit.sessionId;
3458
+ prompt = buildClaudeCodeSessionResurrectionPrompt(options.prompt, sessionLimit.sessionId);
3459
+ continue;
3460
+ }
3461
+ const usage = parseClaudeCodeJsonOutput(output);
3462
+ return { usage };
3463
+ }
3464
+ }
3465
+ /**
3466
+ * Runs one Claude Code CLI process and returns its raw output.
3467
+ */
3468
+ async runClaudeCodeOnce(options) {
3178
3469
  const scriptContent = buildClaudeScript({
3179
3470
  prompt: options.prompt,
3471
+ model: this.options.model,
3180
3472
  thinkingLevel: this.options.thinkingLevel,
3473
+ resumeSessionId: options.resumeSessionId,
3181
3474
  });
3182
- const output = await $runGoScriptWithOutput({
3475
+ return await $runGoScriptWithOutput({
3183
3476
  scriptPath: options.scriptPath,
3184
3477
  scriptContent,
3185
3478
  logPath: options.logPath,
3186
3479
  shouldPrintLiveOutput: options.shouldPrintLiveOutput,
3187
3480
  preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
3188
3481
  });
3189
- const usage = parseClaudeCodeJsonOutput(output);
3190
- return { usage };
3191
3482
  }
3192
3483
  }
3484
+ /**
3485
+ * Waits until the Claude Code session can be resumed, keeping terminal status clear.
3486
+ */
3487
+ async function waitForClaudeCodeSessionLimitReset(sessionLimit, resurrectionCount, options) {
3488
+ var _a, _b, _c;
3489
+ const delayMs = getClaudeCodeSessionLimitDelayMs(sessionLimit);
3490
+ const sessionLabel = formatClaudeCodeSessionIdForDisplay(sessionLimit.sessionId);
3491
+ const resetSummary = formatClaudeCodeSessionLimitForDisplay(sessionLimit);
3492
+ if ((_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : true) {
3493
+ console.warn(colors__default["default"].yellow(`[claude-code] Session limit detected for ${sessionLimit.sessionId}. Resurrection #${resurrectionCount} will resume with --resume after ${formatDurationMs(delayMs)}. ${resetSummary}`));
3494
+ }
3495
+ let remainingDelayMs = delayMs;
3496
+ while (remainingDelayMs > 0) {
3497
+ await ((_b = options.waitForPauseCheckpoint) === null || _b === void 0 ? void 0 : _b.call(options, {
3498
+ checkpointLabel: 'the Claude Code session limit reset',
3499
+ phase: 'waiting',
3500
+ statusMessage: `Claude Code session ${sessionLabel} hit its limit; resurrection #${resurrectionCount} resumes in ${formatDurationMs(remainingDelayMs)}`,
3501
+ }));
3502
+ const currentDelayMs = Math.min(CLAUDE_CODE_SESSION_RESURRECTION_POLL_MS, remainingDelayMs);
3503
+ await waitFor$1(currentDelayMs);
3504
+ remainingDelayMs -= currentDelayMs;
3505
+ }
3506
+ await ((_c = options.waitForPauseCheckpoint) === null || _c === void 0 ? void 0 : _c.call(options, {
3507
+ checkpointLabel: 'resurrecting the Claude Code session with --resume',
3508
+ phase: 'running',
3509
+ statusMessage: `Resurrecting Claude Code session ${sessionLabel} with --resume`,
3510
+ }));
3511
+ }
3512
+ /**
3513
+ * Waits for a fixed amount of milliseconds.
3514
+ */
3515
+ async function waitFor$1(delayMs) {
3516
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
3517
+ }
3518
+ /**
3519
+ * Formats a Claude Code session id for compact terminal status lines.
3520
+ */
3521
+ function formatClaudeCodeSessionIdForDisplay(sessionId) {
3522
+ if (sessionId.length <= 8) {
3523
+ return sessionId;
3524
+ }
3525
+ return `${sessionId.slice(0, 8)}...`;
3526
+ }
3193
3527
 
3194
3528
  /**
3195
3529
  * Creates a temporary script file, runs it, and cleans it up unless preservation is requested or the run fails.
@@ -4437,8 +4771,9 @@
4437
4771
  }
4438
4772
  if (agentName === 'claude-code') {
4439
4773
  return createRunnerResolution(options, new ClaudeCodeRunner({
4774
+ model: options.model,
4440
4775
  thinkingLevel: options.thinkingLevel,
4441
- }));
4776
+ }), options.model);
4442
4777
  }
4443
4778
  if (agentName === 'opencode') {
4444
4779
  return createRunnerResolution(options, new OpencodeRunner({
@@ -4512,7 +4847,7 @@
4512
4847
  if (options.agentName === 'cline') {
4513
4848
  return { runnerName, modelName: CLINE_MODEL };
4514
4849
  }
4515
- if (options.agentName === 'opencode') {
4850
+ if (options.agentName === 'opencode' || options.agentName === 'claude-code') {
4516
4851
  return { runnerName, modelName: options.model };
4517
4852
  }
4518
4853
  return { runnerName };
@@ -4589,9 +4924,10 @@
4589
4924
 
4590
4925
  [Up to 50,000 CZK](?message=We are posting an order up to 50,000 CZK)
4591
4926
  [Up to 100,000 CZK](?message=We are posting an order up to 100,000 CZK)
4592
- [Over 100,000 CZK](?message=We are posting an order over 100,000 CZK)
4927
+ [Over 100,000 CZK](?messageDraft=We are posting an order over 100,000 CZK)
4593
4928
  \`\`\`
4594
4929
 
4930
+ - You can use \`message\` for message that will be sent immediately after clicking the button, or \`messageDraft\` for message that will be prefilled in the input field for editing before sending.
4595
4931
 
4596
4932
  ## This is how you should behave
4597
4933
 
@@ -4599,28 +4935,38 @@
4599
4935
  `);
4600
4936
  }
4601
4937
 
4602
- /**
4603
- * Builds a normalized temporary shell script path for prompt runners.
4604
- */
4605
- function buildTemporaryPromptScriptPath(options) {
4606
- const sourceFileName = path.basename(options.sourceFileName);
4607
- const scriptFileName = `${sourceFileName.replace(/\.[^.]+$/u, '')}${options.suffix || ''}.sh`;
4608
- return resolvePromptbookTemporaryPath(options.projectPath, options.scriptDirectoryName, scriptFileName);
4609
- }
4610
-
4611
4938
  /**
4612
4939
  * Temporary subdirectory used for agent-message runner shell scripts.
4940
+ *
4941
+ * @private internal constant of agent-message runtime paths
4613
4942
  */
4614
4943
  const AGENT_MESSAGE_SCRIPT_DIRECTORY_NAME = 'agent-messages';
4944
+ /**
4945
+ * File extension used for temporary prompt-runner shell scripts.
4946
+ *
4947
+ * @private internal constant of agent-message runtime paths
4948
+ */
4949
+ const AGENT_MESSAGE_SCRIPT_EXTENSION = '.sh';
4950
+ /**
4951
+ * Builds the temporary shell script path used by one queued agent-message harness run.
4952
+ *
4953
+ * @param projectPath - Local agent project path containing the message queue.
4954
+ * @param sourceFileName - Queued message filename used to name the temporary shell script.
4955
+ * @returns Temporary shell script path.
4956
+ * @private internal utility of agent-message runners
4957
+ */
4958
+ function buildAgentMessageScriptPathFromFileName(projectPath, sourceFileName) {
4959
+ const sourceFileBasename = path.basename(sourceFileName);
4960
+ const scriptFileName = `${sourceFileBasename.replace(/\.[^.]+$/u, '')}${AGENT_MESSAGE_SCRIPT_EXTENSION}`;
4961
+ return resolvePromptbookTemporaryPath(projectPath, AGENT_MESSAGE_SCRIPT_DIRECTORY_NAME, scriptFileName);
4962
+ }
4963
+ // Note: [💞] Ignore a discrepancy between file name and entity name
4964
+
4615
4965
  /**
4616
4966
  * Builds a temporary script path for one agent message runner invocation.
4617
4967
  */
4618
4968
  function buildAgentMessageScriptPath(projectPath, messageFile) {
4619
- return buildTemporaryPromptScriptPath({
4620
- projectPath,
4621
- scriptDirectoryName: AGENT_MESSAGE_SCRIPT_DIRECTORY_NAME,
4622
- sourceFileName: messageFile.fileName,
4623
- });
4969
+ return buildAgentMessageScriptPathFromFileName(projectPath, messageFile.fileName);
4624
4970
  }
4625
4971
 
4626
4972
  var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book",formfactorName:"GENERIC",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}",resultingParameterName:"knowledgePieces",dependentParameterNames:["knowledgeContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge from Markdown\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book`\n- INPUT PARAMETER `{knowledgeContent}` Markdown document content\n- OUTPUT PARAMETER `{knowledgePieces}` The knowledge JSON object\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}\n```\n\n`-> {knowledgePieces}`\n"}],sourceFile:"./books/prepare-knowledge-from-markdown.book"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}",resultingParameterName:"keywords",dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-keywords.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{keywords}` Keywords separated by comma\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}\n```\n\n`-> {keywords}`\n"}],sourceFile:"./books/prepare-knowledge-keywords.book"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge-piece Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book",formfactorName:"GENERIC",parameters:[{name:"availableModels",description:"List of available model names together with their descriptions as JSON",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelsRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n```json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n```\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n```json\n{availableModels}\n```\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}",resultingParameterName:"modelsRequirements",format:"JSON",dependentParameterNames:["availableModels","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Persona\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book`\n- INPUT PARAMETER `{availableModels}` List of available model names together with their descriptions as JSON\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelsRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n\\`\\`\\`json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n\\`\\`\\`json\n{availableModels}\n\\`\\`\\`\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}\n```\n\n`-> {modelsRequirements}`\n"}],sourceFile:"./books/prepare-persona.book"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book",formfactorName:"GENERIC",parameters:[{name:"book",description:"The book to prepare the title for",isInput:true,isOutput:false},{name:"title",description:"Best title for the book",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-title",title:"Make title",content:"Make best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}",resultingParameterName:"title",expectations:{words:{min:1,max:8},lines:{min:1,max:1}},dependentParameterNames:["book"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-title.book`\n- INPUT PARAMETER `{book}` The book to prepare the title for\n- OUTPUT PARAMETER `{title}` Best title for the book\n\n## Make title\n\n- EXPECT MIN 1 Word\n- EXPECT MAX 8 Words\n- EXPECT EXACTLY 1 Line\n\n```markdown\nMake best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book"}];
@@ -5083,6 +5429,95 @@
5083
5429
  // TODO: [🏛] Escape all
5084
5430
  // TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
5085
5431
 
5432
+ /**
5433
+ * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
5434
+ *
5435
+ * @public exported from `@promptbook/core`
5436
+ */
5437
+ class PipelineLogicError extends Error {
5438
+ constructor(message) {
5439
+ super(message);
5440
+ this.name = 'PipelineLogicError';
5441
+ Object.setPrototypeOf(this, PipelineLogicError.prototype);
5442
+ }
5443
+ }
5444
+
5445
+ /**
5446
+ * Creates the shared validation context for one pipeline.
5447
+ *
5448
+ * @private function of `validatePipeline`
5449
+ */
5450
+ function createPipelineValidationContext(pipeline) {
5451
+ return {
5452
+ pipeline,
5453
+ pipelineIdentification: getPipelineIdentification$1(pipeline),
5454
+ };
5455
+ }
5456
+ /**
5457
+ * Builds a short file/url identification block for validation errors.
5458
+ *
5459
+ * @private internal utility of `validatePipeline`
5460
+ */
5461
+ function getPipelineIdentification$1(pipeline) {
5462
+ // Note: This is a 😐 implementation of [🚞]
5463
+ const pipelineIdentificationParts = [];
5464
+ if (pipeline.sourceFile !== undefined) {
5465
+ pipelineIdentificationParts.push(`File: ${pipeline.sourceFile}`);
5466
+ }
5467
+ if (pipeline.pipelineUrl !== undefined) {
5468
+ pipelineIdentificationParts.push(`Url: ${pipeline.pipelineUrl}`);
5469
+ }
5470
+ return pipelineIdentificationParts.join('\n');
5471
+ }
5472
+
5473
+ /**
5474
+ * Validates that the expected top-level collections have array structure.
5475
+ *
5476
+ * @private function of `validatePipeline`
5477
+ */
5478
+ function validatePipelineCollectionsStructure({ pipeline, pipelineIdentification, }) {
5479
+ validatePipelineParametersCollection(pipeline, pipelineIdentification);
5480
+ validatePipelineTasksCollection(pipeline, pipelineIdentification);
5481
+ }
5482
+ /**
5483
+ * Validates that `pipeline.parameters` is an array.
5484
+ *
5485
+ * @private internal utility of `validatePipelineCollectionsStructure`
5486
+ */
5487
+ function validatePipelineParametersCollection(pipeline, pipelineIdentification) {
5488
+ // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
5489
+ if (Array.isArray(pipeline.parameters)) {
5490
+ return;
5491
+ }
5492
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
5493
+ throw new ParseError(_spaceTrim.spaceTrim((block) => `
5494
+ Pipeline is valid JSON but with wrong structure
5495
+
5496
+ \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
5497
+
5498
+ ${block(pipelineIdentification)}
5499
+ `));
5500
+ }
5501
+ /**
5502
+ * Validates that `pipeline.tasks` is an array.
5503
+ *
5504
+ * @private internal utility of `validatePipelineCollectionsStructure`
5505
+ */
5506
+ function validatePipelineTasksCollection(pipeline, pipelineIdentification) {
5507
+ // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
5508
+ if (Array.isArray(pipeline.tasks)) {
5509
+ return;
5510
+ }
5511
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
5512
+ throw new ParseError(_spaceTrim.spaceTrim((block) => `
5513
+ Pipeline is valid JSON but with wrong structure
5514
+
5515
+ \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
5516
+
5517
+ ${block(pipelineIdentification)}
5518
+ `));
5519
+ }
5520
+
5086
5521
  /**
5087
5522
  * Order of keys in the pipeline JSON
5088
5523
  *
@@ -5195,17 +5630,138 @@
5195
5630
  // Note: [💞] Ignore a discrepancy between file name and entity name
5196
5631
 
5197
5632
  /**
5198
- * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
5633
+ * Validates that task dependencies can be resolved without cycles or missing definitions.
5199
5634
  *
5200
- * @public exported from `@promptbook/core`
5635
+ * @private function of `validatePipeline`
5201
5636
  */
5202
- class PipelineLogicError extends Error {
5203
- constructor(message) {
5204
- super(message);
5205
- this.name = 'PipelineLogicError';
5206
- Object.setPrototypeOf(this, PipelineLogicError.prototype);
5637
+ function validatePipelineDependencyResolution({ pipeline, pipelineIdentification, }) {
5638
+ let dependencyResolutionState = createInitialDependencyResolutionState(pipeline);
5639
+ let loopLimit = LOOP_LIMIT;
5640
+ while (hasUnresolvedTasks(dependencyResolutionState)) {
5641
+ if (loopLimit-- < 0) {
5642
+ throw createDependencyResolutionLoopLimitError(pipelineIdentification);
5643
+ }
5644
+ dependencyResolutionState = resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification);
5207
5645
  }
5208
5646
  }
5647
+ /**
5648
+ * Collects the parameter names that are already resolvable before dependency traversal starts.
5649
+ *
5650
+ * @private internal utility of `validatePipelineDependencyResolution`
5651
+ */
5652
+ function createInitialDependencyResolutionState(pipeline) {
5653
+ return {
5654
+ resolvedParameterNames: createInitiallyResolvedParameterNames(pipeline),
5655
+ unresolvedTasks: [...pipeline.tasks],
5656
+ };
5657
+ }
5658
+ /**
5659
+ * Checks whether dependency resolution still has tasks left to process.
5660
+ *
5661
+ * @private internal utility of `validatePipelineDependencyResolution`
5662
+ */
5663
+ function hasUnresolvedTasks({ unresolvedTasks }) {
5664
+ return unresolvedTasks.length > 0;
5665
+ }
5666
+ /**
5667
+ * Resolves the next batch of currently satisfiable tasks.
5668
+ *
5669
+ * @private internal utility of `validatePipelineDependencyResolution`
5670
+ */
5671
+ function resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification) {
5672
+ const currentlyResolvedTasks = getCurrentlyResolvedTasks(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames);
5673
+ if (currentlyResolvedTasks.length === 0) {
5674
+ throw createUnresolvedTasksError(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames, pipelineIdentification);
5675
+ }
5676
+ return {
5677
+ resolvedParameterNames: appendResolvedTaskParameterNames(dependencyResolutionState.resolvedParameterNames, currentlyResolvedTasks),
5678
+ unresolvedTasks: dependencyResolutionState.unresolvedTasks.filter((task) => !currentlyResolvedTasks.includes(task)),
5679
+ };
5680
+ }
5681
+ /**
5682
+ * Collects the parameter names that are already resolvable before dependency traversal starts.
5683
+ *
5684
+ * @private internal utility of `validatePipelineDependencyResolution`
5685
+ */
5686
+ function createInitiallyResolvedParameterNames(pipeline) {
5687
+ let resolvedParameterNames = pipeline.parameters
5688
+ .filter(({ isInput }) => isInput)
5689
+ .map(({ name }) => name);
5690
+ for (const reservedParameterName of RESERVED_PARAMETER_NAMES) {
5691
+ resolvedParameterNames = [...resolvedParameterNames, reservedParameterName];
5692
+ }
5693
+ return resolvedParameterNames;
5694
+ }
5695
+ /**
5696
+ * Adds newly resolved task outputs to the resolved parameter list.
5697
+ *
5698
+ * @private internal utility of `validatePipelineDependencyResolution`
5699
+ */
5700
+ function appendResolvedTaskParameterNames(resolvedParameterNames, currentlyResolvedTasks) {
5701
+ return [
5702
+ ...resolvedParameterNames,
5703
+ ...currentlyResolvedTasks.map(({ resultingParameterName }) => resultingParameterName),
5704
+ ];
5705
+ }
5706
+ /**
5707
+ * Selects tasks whose dependencies are already resolved.
5708
+ *
5709
+ * @private internal utility of `validatePipelineDependencyResolution`
5710
+ */
5711
+ function getCurrentlyResolvedTasks(unresolvedTasks, resolvedParameterNames) {
5712
+ return unresolvedTasks.filter((task) => task.dependentParameterNames.every((name) => resolvedParameterNames.includes(name)));
5713
+ }
5714
+ /**
5715
+ * Creates the unexpected loop-limit error for dependency resolution.
5716
+ *
5717
+ * @private internal utility of `validatePipelineDependencyResolution`
5718
+ */
5719
+ function createDependencyResolutionLoopLimitError(pipelineIdentification) {
5720
+ // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
5721
+ return new UnexpectedError(_spaceTrim.spaceTrim((block) => `
5722
+ Loop limit reached during detection of circular dependencies in \`validatePipeline\`
5723
+
5724
+ ${block(pipelineIdentification)}
5725
+ `));
5726
+ }
5727
+ /**
5728
+ * Creates the detailed error for unresolved or circular task dependencies.
5729
+ *
5730
+ * @private internal utility of `validatePipelineDependencyResolution`
5731
+ */
5732
+ function createUnresolvedTasksError(unresolvedTasks, resolvedParameterNames, pipelineIdentification) {
5733
+ return new PipelineLogicError(
5734
+ // TODO: [🐎] DRY
5735
+ _spaceTrim.spaceTrim((block) => `
5736
+
5737
+ Can not resolve some parameters:
5738
+ Either you are using a parameter that is not defined, or there are some circular dependencies.
5739
+
5740
+ ${block(pipelineIdentification)}
5741
+
5742
+ **Can not resolve:**
5743
+ ${block(unresolvedTasks
5744
+ .map(({ resultingParameterName, dependentParameterNames }) => `- Parameter \`{${resultingParameterName}}\` which depends on ${dependentParameterNames
5745
+ .map((dependentParameterName) => `\`{${dependentParameterName}}\``)
5746
+ .join(' and ')}`)
5747
+ .join('\n'))}
5748
+
5749
+ **Resolved:**
5750
+ ${block(resolvedParameterNames
5751
+ .filter((name) => !RESERVED_PARAMETER_NAMES.includes(name))
5752
+ .map((name) => `- Parameter \`{${name}}\``)
5753
+ .join('\n'))}
5754
+
5755
+
5756
+ **Reserved (which are available):**
5757
+ ${block(resolvedParameterNames
5758
+ .filter((name) => RESERVED_PARAMETER_NAMES.includes(name))
5759
+ .map((name) => `- Parameter \`{${name}}\``)
5760
+ .join('\n'))}
5761
+
5762
+
5763
+ `));
5764
+ }
5209
5765
 
5210
5766
  /**
5211
5767
  * Tests if given string is valid semantic version
@@ -5281,168 +5837,19 @@
5281
5837
  }
5282
5838
  // TODO: [🐠] Maybe more info why the URL is invalid
5283
5839
 
5284
- /**
5285
- * Validates PipelineJson if it is logically valid
5286
- *
5287
- * It checks:
5288
- * - if it has correct parameters dependency
5289
- *
5290
- * It does NOT check:
5291
- * - if it is valid json
5292
- * - if it is meaningful
5293
- *
5294
- * Note: [🔂] This function is idempotent.
5295
- *
5296
- * @param pipeline valid or invalid PipelineJson
5297
- * @returns the same pipeline if it is logically valid
5298
- * @throws {PipelineLogicError} on logical error in the pipeline
5299
- *
5300
- * @public exported from `@promptbook/core`
5301
- */
5302
- function validatePipeline(pipeline) {
5303
- if (IS_PIPELINE_LOGIC_VALIDATED) {
5304
- validatePipeline_InnerFunction(pipeline);
5305
- }
5306
- else {
5307
- try {
5308
- validatePipeline_InnerFunction(pipeline);
5309
- }
5310
- catch (error) {
5311
- if (!(error instanceof PipelineLogicError)) {
5312
- throw error;
5313
- }
5314
- console.error(_spaceTrim.spaceTrim((block) => `
5315
- Pipeline is not valid but logic errors are temporarily disabled via \`IS_PIPELINE_LOGIC_VALIDATED\`
5316
-
5317
- ${block(error.message)}
5318
- `));
5319
- }
5320
- }
5321
- return pipeline;
5322
- }
5323
- /**
5324
- * Validates pipeline inner function.
5325
- *
5326
- * @private internal function for `validatePipeline`
5327
- */
5328
- function validatePipeline_InnerFunction(pipeline) {
5329
- // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
5330
- const context = createPipelineValidationContext(pipeline);
5331
- validatePipelineMetadata(context);
5332
- validatePipelineCollectionsStructure(context);
5333
- validatePipelineParameters(context);
5334
- validatePipelineTasks(context);
5335
- validatePipelineDependencyResolution(context);
5336
- // Note: Check that formfactor is corresponding to the pipeline interface
5337
- // TODO: !!6 Implement this
5338
- // pipeline.formfactorName
5339
- }
5340
- /**
5341
- * Creates the shared validation context for one pipeline.
5342
- *
5343
- * @private internal utility of `validatePipeline`
5344
- */
5345
- function createPipelineValidationContext(pipeline) {
5346
- return {
5347
- pipeline,
5348
- pipelineIdentification: getPipelineIdentification$1(pipeline),
5349
- };
5350
- }
5351
- /**
5352
- * Builds a short file/url identification block for validation errors.
5353
- *
5354
- * @private internal utility of `validatePipeline`
5355
- */
5356
- function getPipelineIdentification$1(pipeline) {
5357
- // Note: This is a 😐 implementation of [🚞]
5358
- const pipelineIdentificationParts = [];
5359
- if (pipeline.sourceFile !== undefined) {
5360
- pipelineIdentificationParts.push(`File: ${pipeline.sourceFile}`);
5361
- }
5362
- if (pipeline.pipelineUrl !== undefined) {
5363
- pipelineIdentificationParts.push(`Url: ${pipeline.pipelineUrl}`);
5364
- }
5365
- return pipelineIdentificationParts.join('\n');
5366
- }
5367
5840
  /**
5368
5841
  * Validates pipeline-level metadata fields.
5369
5842
  *
5370
- * @private internal step of `validatePipeline`
5843
+ * @private function of `validatePipeline`
5371
5844
  */
5372
5845
  function validatePipelineMetadata({ pipeline, pipelineIdentification }) {
5373
5846
  validatePipelineUrl(pipeline, pipelineIdentification);
5374
5847
  validatePipelineBookVersion(pipeline, pipelineIdentification);
5375
5848
  }
5376
- /**
5377
- * Validates that the expected top-level collections have array structure.
5378
- *
5379
- * @private internal step of `validatePipeline`
5380
- */
5381
- function validatePipelineCollectionsStructure({ pipeline, pipelineIdentification }) {
5382
- validatePipelineParametersCollection(pipeline, pipelineIdentification);
5383
- validatePipelineTasksCollection(pipeline, pipelineIdentification);
5384
- }
5385
- /**
5386
- * Validates all pipeline parameter declarations.
5387
- *
5388
- * @private internal step of `validatePipeline`
5389
- */
5390
- function validatePipelineParameters({ pipeline, pipelineIdentification }) {
5391
- for (const parameter of pipeline.parameters) {
5392
- validatePipelineParameter(parameter, pipeline, pipelineIdentification);
5393
- }
5394
- }
5395
- /**
5396
- * Validates all pipeline tasks and their per-task invariants.
5397
- *
5398
- * @private internal step of `validatePipeline`
5399
- */
5400
- function validatePipelineTasks({ pipeline, pipelineIdentification }) {
5401
- // Note: All input parameters are defined - so that they can be used as result of some task
5402
- const definedParameters = createInitiallyDefinedParameters(pipeline);
5403
- for (const task of pipeline.tasks) {
5404
- validatePipelineTask(task, definedParameters, pipelineIdentification);
5405
- }
5406
- }
5407
- /**
5408
- * Validates that task dependencies can be resolved without cycles or missing definitions.
5409
- *
5410
- * @private internal step of `validatePipeline`
5411
- */
5412
- function validatePipelineDependencyResolution({ pipeline, pipelineIdentification }) {
5413
- let dependencyResolutionState = createInitialDependencyResolutionState(pipeline);
5414
- let loopLimit = LOOP_LIMIT;
5415
- while (hasUnresolvedTasks(dependencyResolutionState)) {
5416
- if (loopLimit-- < 0) {
5417
- throw createDependencyResolutionLoopLimitError(pipelineIdentification);
5418
- }
5419
- dependencyResolutionState = resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification);
5420
- }
5421
- }
5422
- /**
5423
- * Validates one pipeline parameter declaration.
5424
- *
5425
- * @private internal step of `validatePipeline`
5426
- */
5427
- function validatePipelineParameter(parameter, pipeline, pipelineIdentification) {
5428
- validateParameterDirection(parameter, pipelineIdentification);
5429
- validateParameterUsage(parameter, pipeline, pipelineIdentification);
5430
- validateParameterDefinition(parameter, pipeline, pipelineIdentification);
5431
- }
5432
- /**
5433
- * Validates one pipeline task and its invariants.
5434
- *
5435
- * @private internal step of `validatePipeline`
5436
- */
5437
- function validatePipelineTask(task, definedParameters, pipelineIdentification) {
5438
- validateTaskResultingParameter(task, definedParameters, pipelineIdentification);
5439
- validateTaskJokers(task, pipelineIdentification);
5440
- validateTaskExpectations(task, pipelineIdentification);
5441
- }
5442
5849
  /**
5443
5850
  * Validates the pipeline URL, when present.
5444
5851
  *
5445
- * @private internal utility of `validatePipeline`
5852
+ * @private internal utility of `validatePipelineMetadata`
5446
5853
  */
5447
5854
  function validatePipelineUrl(pipeline, pipelineIdentification) {
5448
5855
  if (pipeline.pipelineUrl === undefined || isValidPipelineUrl(pipeline.pipelineUrl)) {
@@ -5458,7 +5865,7 @@
5458
5865
  /**
5459
5866
  * Validates the Promptbook version, when present.
5460
5867
  *
5461
- * @private internal utility of `validatePipeline`
5868
+ * @private internal utility of `validatePipelineMetadata`
5462
5869
  */
5463
5870
  function validatePipelineBookVersion(pipeline, pipelineIdentification) {
5464
5871
  if (pipeline.bookVersion === undefined || isValidPromptbookVersion(pipeline.bookVersion)) {
@@ -5471,48 +5878,31 @@
5471
5878
  ${block(pipelineIdentification)}
5472
5879
  `));
5473
5880
  }
5881
+
5474
5882
  /**
5475
- * Validates that `pipeline.parameters` is an array.
5883
+ * Validates all pipeline parameter declarations.
5476
5884
  *
5477
- * @private internal utility of `validatePipeline`
5885
+ * @private function of `validatePipeline`
5478
5886
  */
5479
- function validatePipelineParametersCollection(pipeline, pipelineIdentification) {
5480
- // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
5481
- if (Array.isArray(pipeline.parameters)) {
5482
- return;
5887
+ function validatePipelineParameters({ pipeline, pipelineIdentification }) {
5888
+ for (const parameter of pipeline.parameters) {
5889
+ validatePipelineParameter(parameter, pipeline, pipelineIdentification);
5483
5890
  }
5484
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
5485
- throw new ParseError(_spaceTrim.spaceTrim((block) => `
5486
- Pipeline is valid JSON but with wrong structure
5487
-
5488
- \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
5489
-
5490
- ${block(pipelineIdentification)}
5491
- `));
5492
5891
  }
5493
5892
  /**
5494
- * Validates that `pipeline.tasks` is an array.
5893
+ * Validates one pipeline parameter declaration.
5495
5894
  *
5496
- * @private internal utility of `validatePipeline`
5895
+ * @private internal utility of `validatePipelineParameters`
5497
5896
  */
5498
- function validatePipelineTasksCollection(pipeline, pipelineIdentification) {
5499
- // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
5500
- if (Array.isArray(pipeline.tasks)) {
5501
- return;
5502
- }
5503
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
5504
- throw new ParseError(_spaceTrim.spaceTrim((block) => `
5505
- Pipeline is valid JSON but with wrong structure
5506
-
5507
- \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
5508
-
5509
- ${block(pipelineIdentification)}
5510
- `));
5897
+ function validatePipelineParameter(parameter, pipeline, pipelineIdentification) {
5898
+ validateParameterDirection(parameter, pipelineIdentification);
5899
+ validateParameterUsage(parameter, pipeline, pipelineIdentification);
5900
+ validateParameterDefinition(parameter, pipeline, pipelineIdentification);
5511
5901
  }
5512
5902
  /**
5513
5903
  * Validates that one parameter does not declare incompatible directions.
5514
5904
  *
5515
- * @private internal utility of `validatePipeline`
5905
+ * @private internal utility of `validatePipelineParameters`
5516
5906
  */
5517
5907
  function validateParameterDirection(parameter, pipelineIdentification) {
5518
5908
  if (!parameter.isInput || !parameter.isOutput) {
@@ -5529,7 +5919,7 @@
5529
5919
  /**
5530
5920
  * Validates that one intermediate parameter is actually consumed by at least one task.
5531
5921
  *
5532
- * @private internal utility of `validatePipeline`
5922
+ * @private internal utility of `validatePipelineParameters`
5533
5923
  */
5534
5924
  function validateParameterUsage(parameter, pipeline, pipelineIdentification) {
5535
5925
  if (parameter.isInput || parameter.isOutput || isParameterUsedByAnyTask(parameter, pipeline.tasks)) {
@@ -5548,7 +5938,7 @@
5548
5938
  /**
5549
5939
  * Validates that one non-input parameter is produced by at least one task.
5550
5940
  *
5551
- * @private internal utility of `validatePipeline`
5941
+ * @private internal utility of `validatePipelineParameters`
5552
5942
  */
5553
5943
  function validateParameterDefinition(parameter, pipeline, pipelineIdentification) {
5554
5944
  if (parameter.isInput || isParameterDefinedByAnyTask(parameter, pipeline.tasks)) {
@@ -5567,7 +5957,7 @@
5567
5957
  /**
5568
5958
  * Checks whether one parameter is consumed by at least one task dependency list.
5569
5959
  *
5570
- * @private internal utility of `validatePipeline`
5960
+ * @private internal utility of `validatePipelineParameters`
5571
5961
  */
5572
5962
  function isParameterUsedByAnyTask(parameter, tasks) {
5573
5963
  return tasks.some((task) => task.dependentParameterNames.includes(parameter.name));
@@ -5575,23 +5965,46 @@
5575
5965
  /**
5576
5966
  * Checks whether one parameter is produced by at least one task.
5577
5967
  *
5578
- * @private internal utility of `validatePipeline`
5968
+ * @private internal utility of `validatePipelineParameters`
5579
5969
  */
5580
5970
  function isParameterDefinedByAnyTask(parameter, tasks) {
5581
5971
  return tasks.some((task) => task.resultingParameterName === parameter.name);
5582
5972
  }
5973
+
5974
+ /**
5975
+ * Validates all pipeline tasks and their per-task invariants.
5976
+ *
5977
+ * @private function of `validatePipeline`
5978
+ */
5979
+ function validatePipelineTasks({ pipeline, pipelineIdentification }) {
5980
+ // Note: All input parameters are defined - so that they can be used as result of some task
5981
+ const definedParameters = createInitiallyDefinedParameters(pipeline);
5982
+ for (const task of pipeline.tasks) {
5983
+ validatePipelineTask(task, definedParameters, pipelineIdentification);
5984
+ }
5985
+ }
5583
5986
  /**
5584
5987
  * Collects the parameter names that are already defined before task validation starts.
5585
5988
  *
5586
- * @private internal utility of `validatePipeline`
5989
+ * @private internal utility of `validatePipelineTasks`
5587
5990
  */
5588
5991
  function createInitiallyDefinedParameters(pipeline) {
5589
5992
  return new Set(pipeline.parameters.filter(({ isInput }) => isInput).map(({ name }) => name));
5590
5993
  }
5994
+ /**
5995
+ * Validates one pipeline task and its invariants.
5996
+ *
5997
+ * @private internal utility of `validatePipelineTasks`
5998
+ */
5999
+ function validatePipelineTask(task, definedParameters, pipelineIdentification) {
6000
+ validateTaskResultingParameter(task, definedParameters, pipelineIdentification);
6001
+ validateTaskJokers(task, pipelineIdentification);
6002
+ validateTaskExpectations(task, pipelineIdentification);
6003
+ }
5591
6004
  /**
5592
6005
  * Validates one task result parameter declaration and marks it as defined.
5593
6006
  *
5594
- * @private internal utility of `validatePipeline`
6007
+ * @private internal utility of `validatePipelineTasks`
5595
6008
  */
5596
6009
  function validateTaskResultingParameter(task, definedParameters, pipelineIdentification) {
5597
6010
  if (definedParameters.has(task.resultingParameterName)) {
@@ -5613,7 +6026,7 @@
5613
6026
  /**
5614
6027
  * Validates joker parameters for one task.
5615
6028
  *
5616
- * @private internal utility of `validatePipeline`
6029
+ * @private internal utility of `validatePipelineTasks`
5617
6030
  */
5618
6031
  function validateTaskJokers(task, pipelineIdentification) {
5619
6032
  if (!hasTaskJokers(task)) {
@@ -5625,7 +6038,7 @@
5625
6038
  /**
5626
6039
  * Checks whether one task declares any joker parameters.
5627
6040
  *
5628
- * @private internal utility of `validatePipeline`
6041
+ * @private internal utility of `validatePipelineTasks`
5629
6042
  */
5630
6043
  function hasTaskJokers(task) {
5631
6044
  return !!task.jokerParameterNames && task.jokerParameterNames.length > 0;
@@ -5633,7 +6046,7 @@
5633
6046
  /**
5634
6047
  * Validates that a task has the required supporting features when using jokers.
5635
6048
  *
5636
- * @private internal utility of `validatePipeline`
6049
+ * @private internal utility of `validatePipelineTasks`
5637
6050
  */
5638
6051
  function validateTaskSupportsJokers(task, pipelineIdentification) {
5639
6052
  if (task.format || task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
@@ -5648,7 +6061,7 @@
5648
6061
  /**
5649
6062
  * Validates that every joker parameter is also listed among task dependencies.
5650
6063
  *
5651
- * @private internal utility of `validatePipeline`
6064
+ * @private internal utility of `validatePipelineTasks`
5652
6065
  */
5653
6066
  function validateTaskJokerDependencies(task, pipelineIdentification) {
5654
6067
  for (const joker of task.jokerParameterNames) {
@@ -5665,7 +6078,7 @@
5665
6078
  /**
5666
6079
  * Validates all expectation bounds configured on one task.
5667
6080
  *
5668
- * @private internal utility of `validatePipeline`
6081
+ * @private internal utility of `validatePipelineTasks`
5669
6082
  */
5670
6083
  function validateTaskExpectations(task, pipelineIdentification) {
5671
6084
  if (!task.expectations) {
@@ -5680,7 +6093,7 @@
5680
6093
  /**
5681
6094
  * Validates the minimum and maximum expectation ordering for one unit.
5682
6095
  *
5683
- * @private internal utility of `validatePipeline`
6096
+ * @private internal utility of `validatePipelineTasks`
5684
6097
  */
5685
6098
  function validateTaskExpectationRange(unit, min, max, pipelineIdentification) {
5686
6099
  if (min === undefined || max === undefined || min <= max) {
@@ -5695,7 +6108,7 @@
5695
6108
  /**
5696
6109
  * Validates the minimum expectation bound for one unit.
5697
6110
  *
5698
- * @private internal utility of `validatePipeline`
6111
+ * @private internal utility of `validatePipelineTasks`
5699
6112
  */
5700
6113
  function validateTaskExpectationMin(unit, min, pipelineIdentification) {
5701
6114
  if (min === undefined || min >= 0) {
@@ -5710,7 +6123,7 @@
5710
6123
  /**
5711
6124
  * Validates the maximum expectation bound for one unit.
5712
6125
  *
5713
- * @private internal utility of `validatePipeline`
6126
+ * @private internal utility of `validatePipelineTasks`
5714
6127
  */
5715
6128
  function validateTaskExpectationMax(unit, max, pipelineIdentification) {
5716
6129
  if (max === undefined || max > 0) {
@@ -5722,123 +6135,62 @@
5722
6135
  ${block(pipelineIdentification)}
5723
6136
  `));
5724
6137
  }
6138
+
5725
6139
  /**
5726
- * Collects the parameter names that are already resolvable before dependency traversal starts.
5727
- *
5728
- * @private internal utility of `validatePipeline`
5729
- */
5730
- function createInitialDependencyResolutionState(pipeline) {
5731
- return {
5732
- resolvedParameterNames: createInitiallyResolvedParameterNames(pipeline),
5733
- unresolvedTasks: [...pipeline.tasks],
5734
- };
5735
- }
5736
- /**
5737
- * Checks whether dependency resolution still has tasks left to process.
5738
- *
5739
- * @private internal utility of `validatePipeline`
5740
- */
5741
- function hasUnresolvedTasks({ unresolvedTasks }) {
5742
- return unresolvedTasks.length > 0;
5743
- }
5744
- /**
5745
- * Resolves the next batch of currently satisfiable tasks.
6140
+ * Validates PipelineJson if it is logically valid
5746
6141
  *
5747
- * @private internal utility of `validatePipeline`
5748
- */
5749
- function resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification) {
5750
- const currentlyResolvedTasks = getCurrentlyResolvedTasks(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames);
5751
- if (currentlyResolvedTasks.length === 0) {
5752
- throw createUnresolvedTasksError(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames, pipelineIdentification);
5753
- }
5754
- return {
5755
- resolvedParameterNames: appendResolvedTaskParameterNames(dependencyResolutionState.resolvedParameterNames, currentlyResolvedTasks),
5756
- unresolvedTasks: dependencyResolutionState.unresolvedTasks.filter((task) => !currentlyResolvedTasks.includes(task)),
5757
- };
5758
- }
5759
- /**
5760
- * Collects the parameter names that are already resolvable before dependency traversal starts.
6142
+ * It checks:
6143
+ * - if it has correct parameters dependency
5761
6144
  *
5762
- * @private internal utility of `validatePipeline`
5763
- */
5764
- function createInitiallyResolvedParameterNames(pipeline) {
5765
- let resolvedParameterNames = pipeline.parameters
5766
- .filter(({ isInput }) => isInput)
5767
- .map(({ name }) => name);
5768
- for (const reservedParameterName of RESERVED_PARAMETER_NAMES) {
5769
- resolvedParameterNames = [...resolvedParameterNames, reservedParameterName];
5770
- }
5771
- return resolvedParameterNames;
5772
- }
5773
- /**
5774
- * Adds newly resolved task outputs to the resolved parameter list.
6145
+ * It does NOT check:
6146
+ * - if it is valid json
6147
+ * - if it is meaningful
5775
6148
  *
5776
- * @private internal utility of `validatePipeline`
5777
- */
5778
- function appendResolvedTaskParameterNames(resolvedParameterNames, currentlyResolvedTasks) {
5779
- return [
5780
- ...resolvedParameterNames,
5781
- ...currentlyResolvedTasks.map(({ resultingParameterName }) => resultingParameterName),
5782
- ];
5783
- }
5784
- /**
5785
- * Selects tasks whose dependencies are already resolved.
6149
+ * Note: [🔂] This function is idempotent.
5786
6150
  *
5787
- * @private internal utility of `validatePipeline`
5788
- */
5789
- function getCurrentlyResolvedTasks(unresolvedTasks, resolvedParameterNames) {
5790
- return unresolvedTasks.filter((task) => task.dependentParameterNames.every((name) => resolvedParameterNames.includes(name)));
5791
- }
5792
- /**
5793
- * Creates the unexpected loop-limit error for dependency resolution.
6151
+ * @param pipeline valid or invalid PipelineJson
6152
+ * @returns the same pipeline if it is logically valid
6153
+ * @throws {PipelineLogicError} on logical error in the pipeline
5794
6154
  *
5795
- * @private internal utility of `validatePipeline`
6155
+ * @public exported from `@promptbook/core`
5796
6156
  */
5797
- function createDependencyResolutionLoopLimitError(pipelineIdentification) {
5798
- // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
5799
- return new UnexpectedError(_spaceTrim.spaceTrim((block) => `
5800
- Loop limit reached during detection of circular dependencies in \`validatePipeline\`
6157
+ function validatePipeline(pipeline) {
6158
+ if (IS_PIPELINE_LOGIC_VALIDATED) {
6159
+ validatePipeline_InnerFunction(pipeline);
6160
+ }
6161
+ else {
6162
+ try {
6163
+ validatePipeline_InnerFunction(pipeline);
6164
+ }
6165
+ catch (error) {
6166
+ if (!(error instanceof PipelineLogicError)) {
6167
+ throw error;
6168
+ }
6169
+ console.error(_spaceTrim.spaceTrim((block) => `
6170
+ Pipeline is not valid but logic errors are temporarily disabled via \`IS_PIPELINE_LOGIC_VALIDATED\`
5801
6171
 
5802
- ${block(pipelineIdentification)}
5803
- `));
6172
+ ${block(error.message)}
6173
+ `));
6174
+ }
6175
+ }
6176
+ return pipeline;
5804
6177
  }
5805
6178
  /**
5806
- * Creates the detailed error for unresolved or circular task dependencies.
6179
+ * Validates pipeline inner function.
5807
6180
  *
5808
- * @private internal utility of `validatePipeline`
6181
+ * @private internal function for `validatePipeline`
5809
6182
  */
5810
- function createUnresolvedTasksError(unresolvedTasks, resolvedParameterNames, pipelineIdentification) {
5811
- return new PipelineLogicError(
5812
- // TODO: [🐎] DRY
5813
- _spaceTrim.spaceTrim((block) => `
5814
-
5815
- Can not resolve some parameters:
5816
- Either you are using a parameter that is not defined, or there are some circular dependencies.
5817
-
5818
- ${block(pipelineIdentification)}
5819
-
5820
- **Can not resolve:**
5821
- ${block(unresolvedTasks
5822
- .map(({ resultingParameterName, dependentParameterNames }) => `- Parameter \`{${resultingParameterName}}\` which depends on ${dependentParameterNames
5823
- .map((dependentParameterName) => `\`{${dependentParameterName}}\``)
5824
- .join(' and ')}`)
5825
- .join('\n'))}
5826
-
5827
- **Resolved:**
5828
- ${block(resolvedParameterNames
5829
- .filter((name) => !RESERVED_PARAMETER_NAMES.includes(name))
5830
- .map((name) => `- Parameter \`{${name}}\``)
5831
- .join('\n'))}
5832
-
5833
-
5834
- **Reserved (which are available):**
5835
- ${block(resolvedParameterNames
5836
- .filter((name) => RESERVED_PARAMETER_NAMES.includes(name))
5837
- .map((name) => `- Parameter \`{${name}}\``)
5838
- .join('\n'))}
5839
-
5840
-
5841
- `));
6183
+ function validatePipeline_InnerFunction(pipeline) {
6184
+ // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
6185
+ const context = createPipelineValidationContext(pipeline);
6186
+ validatePipelineMetadata(context);
6187
+ validatePipelineCollectionsStructure(context);
6188
+ validatePipelineParameters(context);
6189
+ validatePipelineTasks(context);
6190
+ validatePipelineDependencyResolution(context);
6191
+ // Note: Check that formfactor is corresponding to the pipeline interface
6192
+ // TODO: !!6 Implement this
6193
+ // pipeline.formfactorName
5842
6194
  }
5843
6195
  /**
5844
6196
  * TODO: [🧞‍♀️] Do not allow joker + foreach
@@ -14158,7 +14510,7 @@
14158
14510
  * META commitment definition
14159
14511
  *
14160
14512
  * The META commitment handles all meta-information about the agent such as:
14161
- * - META AVATAR: Sets the agent's built-in default avatar visual
14513
+ * - META AVATAR / META VISUAL: Sets the agent's built-in default avatar visual
14162
14514
  * - META IMAGE: Sets the agent's avatar/profile image URL
14163
14515
  * - META LINK: Provides profile/source links for the person the agent models
14164
14516
  * - META DOMAIN: Sets the canonical custom domain/host of the agent
@@ -14173,7 +14525,7 @@
14173
14525
  * Example usage in agent source:
14174
14526
  *
14175
14527
  * ```book
14176
- * META AVATAR pixel-art
14528
+ * META VISUAL pixel-art
14177
14529
  * META IMAGE https://example.com/avatar.jpg
14178
14530
  * META LINK https://twitter.com/username
14179
14531
  * META DOMAIN my-agent.com
@@ -14213,7 +14565,7 @@
14213
14565
 
14214
14566
  ## Supported META types
14215
14567
 
14216
- - **META AVATAR** - Sets the agent's built-in default avatar visual
14568
+ - **META AVATAR** / **META VISUAL** - Sets the agent's built-in default avatar visual
14217
14569
  - **META IMAGE** - Sets the agent's avatar/profile image URL
14218
14570
  - **META LINK** - Provides profile/source links for the person the agent models
14219
14571
  - **META DOMAIN** - Sets the canonical custom domain/host of the agent
@@ -14237,7 +14589,7 @@
14237
14589
  \`\`\`book
14238
14590
  Professional Assistant
14239
14591
 
14240
- META AVATAR octopus3
14592
+ META VISUAL octopus3
14241
14593
  META IMAGE https://example.com/professional-avatar.jpg
14242
14594
  META TITLE Senior Business Consultant
14243
14595
  META DESCRIPTION Specialized in strategic planning and project management
@@ -17408,452 +17760,13 @@
17408
17760
  .padStart(2, '0');
17409
17761
  }
17410
17762
 
17411
- /* eslint-disable no-magic-numbers */
17412
- /**
17413
- * Light direction used by the organic 3D octopus shading.
17414
- *
17415
- * @private helper of `octopus3dAvatarVisual`
17416
- */
17417
- const LIGHT_DIRECTION$3 = normalizeVector3({
17418
- x: 0.48,
17419
- y: -0.62,
17420
- z: 0.94,
17421
- });
17422
- /**
17423
- * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
17424
- *
17425
- * @private helper of `octopus3dAvatarVisual`
17426
- */
17427
- const octopus3dStableStateCache = new WeakMap();
17428
- /**
17429
- * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
17430
- *
17431
- * @private helper of `octopus3dAvatarVisual`
17432
- */
17433
- function getOctopus3dStableState(createRandom) {
17434
- const cached = octopus3dStableStateCache.get(createRandom);
17435
- if (cached !== undefined) {
17436
- return cached;
17437
- }
17438
- const animationRandom = createRandom('octopus3d-animation-profile');
17439
- const eyeRandom = createRandom('octopus3d-eye-profile');
17440
- const leftEyePhaseOffset = eyeRandom() * 0.6;
17441
- const rightEyePhaseOffset = eyeRandom() * 0.6;
17442
- const state = {
17443
- morphologyProfile: createOctopus3MorphologyProfile(createRandom),
17444
- animationPhase: animationRandom() * Math.PI * 2,
17445
- leftEyePhaseOffset,
17446
- rightEyePhaseOffset,
17447
- };
17448
- octopus3dStableStateCache.set(createRandom, state);
17449
- return state;
17450
- }
17451
- /**
17452
- * Proper 3D Octopus visual built from projected organic meshes and tentacles.
17453
- *
17454
- * @private built-in avatar visual
17455
- */
17456
- const octopus3dAvatarVisual = {
17457
- id: 'octopus3d',
17458
- title: 'Octopus 3D',
17459
- description: 'Proper 3D octopus portrait with a turning silhouette, expressive eyes, and depth-sorted tentacles.',
17460
- isAnimated: true,
17461
- supportsPointerTracking: true,
17462
- render({ context, size, palette, createRandom, timeMs, interaction }) {
17463
- const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
17464
- const sceneCenterX = size * 0.5;
17465
- const sceneCenterY = size * 0.56;
17466
- const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
17467
- const mantleCenter = {
17468
- x: interaction.bodyOffsetX * size * 0.042 + size * morphologyProfile.body.centerXJitterRatio * 0.65,
17469
- y: -size * 0.09 + interaction.bodyOffsetY * size * 0.028 + bob,
17470
- z: interaction.intensity * size * 0.012,
17471
- };
17472
- const underbodyCenter = {
17473
- x: mantleCenter.x * 0.86,
17474
- y: mantleCenter.y + size * 0.168,
17475
- z: mantleCenter.z - size * 0.018,
17476
- };
17477
- const mantleRadiusX = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch;
17478
- const mantleRadiusY = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1;
17479
- const mantleRadiusZ = size *
17480
- morphologyProfile.body.bodyRadiusRatio *
17481
- (0.9 + (morphologyProfile.body.horizontalStretch - 1) * 0.3);
17482
- const underbodyRadiusX = mantleRadiusX * (0.9 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.08);
17483
- const underbodyRadiusY = mantleRadiusY * (0.44 + morphologyProfile.body.lowerDropRatio * 3.1);
17484
- const underbodyRadiusZ = mantleRadiusZ * 0.78;
17485
- const bodyYaw = -0.18 +
17486
- Math.sin(timeMs / 2400 + animationPhase) * 0.05 +
17487
- interaction.bodyOffsetX * 0.18 +
17488
- interaction.gazeX * 0.22;
17489
- const bodyPitch = -0.08 +
17490
- Math.cos(timeMs / 2700 + animationPhase * 0.6) * 0.025 -
17491
- interaction.bodyOffsetY * 0.08 -
17492
- interaction.gazeY * 0.08;
17493
- const headYaw = bodyYaw - 0.04 + interaction.gazeX * 0.56;
17494
- const headPitch = bodyPitch - 0.02 - interaction.gazeY * 0.32;
17495
- const mantlePatches = resolveVisibleEllipsoidPatches({
17496
- center: mantleCenter,
17497
- radiusX: mantleRadiusX,
17498
- radiusY: mantleRadiusY,
17499
- radiusZ: mantleRadiusZ,
17500
- rotationX: headPitch,
17501
- rotationY: headYaw,
17502
- sceneCenterX,
17503
- sceneCenterY,
17504
- size,
17505
- palette,
17506
- verticalColorBias: 0,
17507
- outlineColor: `${palette.highlight}7a`,
17508
- });
17509
- const underbodyPatches = resolveVisibleEllipsoidPatches({
17510
- center: underbodyCenter,
17511
- radiusX: underbodyRadiusX,
17512
- radiusY: underbodyRadiusY,
17513
- radiusZ: underbodyRadiusZ,
17514
- rotationX: bodyPitch,
17515
- rotationY: bodyYaw,
17516
- sceneCenterX,
17517
- sceneCenterY,
17518
- size,
17519
- palette,
17520
- verticalColorBias: 0.18,
17521
- outlineColor: `${palette.shadow}8f`,
17522
- });
17523
- const tentacleStrokes = createOctopusTentacleStrokes({
17524
- createRandom,
17525
- morphologyProfile,
17526
- timeMs,
17527
- size,
17528
- center: underbodyCenter,
17529
- radiusX: underbodyRadiusX,
17530
- radiusY: underbodyRadiusY,
17531
- radiusZ: underbodyRadiusZ,
17532
- rotationX: bodyPitch,
17533
- rotationY: bodyYaw,
17534
- sceneCenterX,
17535
- sceneCenterY,
17536
- animationPhase,
17537
- });
17538
- const faceEyeSpacing = size * morphologyProfile.face.eyeSpacingRatio * 0.92;
17539
- const faceEyeYOffset = size * morphologyProfile.face.eyeCenterYOffsetRatio - mantleRadiusY * 0.02;
17540
- const faceEyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.82;
17541
- const faceEyeRadiusY = faceEyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.96;
17542
- const mouthHalfWidth = size * morphologyProfile.face.mouthWidthRatio * 0.92;
17543
- const mouthY = size * morphologyProfile.face.mouthYOffsetRatio + mantleRadiusY * 0.08;
17544
- drawAvatarFrame(context, size, palette);
17545
- drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
17546
- drawOctopus3dShadow(context, size, palette, interaction, timeMs);
17547
- for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => !candidateTentacleStroke.isFrontFacing)) {
17548
- drawTentacleStroke(context, tentacleStroke, palette);
17549
- }
17550
- for (const surfacePatch of [...mantlePatches, ...underbodyPatches].sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
17551
- drawSurfacePatch(context, surfacePatch);
17552
- }
17553
- for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
17554
- drawTentacleStroke(context, tentacleStroke, palette);
17555
- }
17556
- drawProjectedOrganicEye(context, {
17557
- x: -faceEyeSpacing,
17558
- y: faceEyeYOffset,
17559
- z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
17560
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
17561
- drawProjectedOrganicEye(context, {
17562
- x: faceEyeSpacing,
17563
- y: faceEyeYOffset,
17564
- z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
17565
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
17566
- drawProjectedOrganicMouth(context, [
17567
- {
17568
- x: -mouthHalfWidth,
17569
- y: mouthY,
17570
- z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -mouthHalfWidth, mouthY),
17571
- },
17572
- {
17573
- x: size * morphologyProfile.face.mouthCenterOffsetRatio,
17574
- y: mouthY +
17575
- size * morphologyProfile.face.mouthCurveDepthRatio * 0.38 +
17576
- Math.sin(timeMs / 760 + animationPhase) * size * 0.01 +
17577
- interaction.gazeY * size * 0.01,
17578
- z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, size * morphologyProfile.face.mouthCenterOffsetRatio, mouthY),
17579
- },
17580
- {
17581
- x: mouthHalfWidth,
17582
- y: mouthY,
17583
- z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, mouthHalfWidth, mouthY),
17584
- },
17585
- ], mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, palette, size);
17586
- },
17587
- };
17588
- /**
17589
- * Draws the atmospheric underwater glow behind the octopus mesh.
17590
- *
17591
- * @private helper of `octopus3dAvatarVisual`
17592
- */
17593
- function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
17594
- const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.1, sceneCenterY - size * 0.2 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY - size * 0.04, size * 0.62);
17595
- glowGradient.addColorStop(0, `${palette.highlight}5c`);
17596
- glowGradient.addColorStop(0.36, `${palette.accent}24`);
17597
- glowGradient.addColorStop(1, `${palette.highlight}00`);
17598
- context.fillStyle = glowGradient;
17599
- context.fillRect(0, 0, size, size);
17600
- const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1700) * size * 0.05, sceneCenterY + size * 0.23, size * 0.08, sceneCenterX, sceneCenterY + size * 0.28, size * 0.5);
17601
- lowerGradient.addColorStop(0, `${palette.secondary}1d`);
17602
- lowerGradient.addColorStop(1, `${palette.secondary}00`);
17603
- context.fillStyle = lowerGradient;
17604
- context.fillRect(0, 0, size, size);
17605
- }
17606
- /**
17607
- * Draws the soft ground shadow below the octopus.
17608
- *
17609
- * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
17610
- * blurry ellipse without triggering a costly software rasterization pass on every frame.
17611
- *
17612
- * @private helper of `octopus3dAvatarVisual`
17613
- */
17614
- function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
17615
- const cx = size * 0.5 + interaction.gazeX * size * 0.04;
17616
- const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
17617
- const rx = size * (0.18 + interaction.intensity * 0.02);
17618
- const ry = size * 0.06;
17619
- context.save();
17620
- context.translate(cx, cy);
17621
- context.scale(1, ry / rx);
17622
- const blurRadius = rx * 1.4;
17623
- const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
17624
- shadowGradient.addColorStop(0, `${palette.shadow}7a`);
17625
- shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
17626
- shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
17627
- shadowGradient.addColorStop(1, `${palette.shadow}00`);
17628
- context.fillStyle = shadowGradient;
17629
- context.beginPath();
17630
- context.arc(0, 0, blurRadius, 0, Math.PI * 2);
17631
- context.fill();
17632
- context.restore();
17633
- }
17634
- /**
17635
- * Resolves visible projected patches for one rotated ellipsoid mesh.
17636
- *
17637
- * @private helper of `octopus3dAvatarVisual`
17638
- */
17639
- function resolveVisibleEllipsoidPatches(options) {
17640
- const { center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, verticalColorBias, outlineColor, } = options;
17641
- const latitudePatchCount = 10;
17642
- const longitudePatchCount = 18;
17643
- const surfacePatches = [];
17644
- for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
17645
- const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
17646
- const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
17647
- const verticalProgress = (latitudeIndex + 0.5) / latitudePatchCount;
17648
- for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
17649
- const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
17650
- const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
17651
- const localCorners = [
17652
- sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, startLongitude),
17653
- sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, endLongitude),
17654
- sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, endLongitude),
17655
- sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, startLongitude),
17656
- ];
17657
- const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
17658
- const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
17659
- if (surfaceNormal.z <= 0.01) {
17660
- continue;
17661
- }
17662
- const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
17663
- surfacePatches.push({
17664
- corners: projectedCorners,
17665
- averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
17666
- transformedCorners.length,
17667
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
17668
- fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
17669
- outlineColor,
17670
- });
17671
- }
17672
- }
17673
- return surfacePatches;
17674
- }
17675
- /**
17676
- * Samples one point on an ellipsoid aligned to the local axes.
17677
- *
17678
- * @private helper of `octopus3dAvatarVisual`
17679
- */
17680
- function sampleEllipsoidPoint(radiusX, radiusY, radiusZ, latitude, longitude) {
17681
- const cosineLatitude = Math.cos(latitude);
17682
- return {
17683
- x: Math.sin(longitude) * cosineLatitude * radiusX,
17684
- y: Math.sin(latitude) * radiusY,
17685
- z: Math.cos(longitude) * cosineLatitude * radiusZ,
17686
- };
17687
- }
17688
- /**
17689
- * Resolves one base fill tone for a surface patch across the octopus body.
17690
- *
17691
- * @private helper of `octopus3dAvatarVisual`
17692
- */
17693
- function resolveSurfacePatchFillStyle(palette, verticalProgress) {
17694
- const clampedVerticalProgress = clampNumber$1(verticalProgress, 0, 1);
17695
- if (clampedVerticalProgress < 0.2) {
17696
- return palette.highlight;
17697
- }
17698
- if (clampedVerticalProgress < 0.45) {
17699
- return palette.secondary;
17700
- }
17701
- if (clampedVerticalProgress < 0.8) {
17702
- return palette.primary;
17703
- }
17704
- return `${palette.shadow}f2`;
17705
- }
17706
- /**
17707
- * Draws one projected mesh patch with organic shading.
17708
- *
17709
- * @private helper of `octopus3dAvatarVisual`
17710
- */
17711
- function drawSurfacePatch(context, surfacePatch) {
17712
- drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
17713
- if (surfacePatch.lightIntensity > 0) {
17714
- drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.15 * surfacePatch.lightIntensity})`);
17715
- }
17716
- else if (surfacePatch.lightIntensity < 0) {
17717
- drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
17718
- }
17719
- context.save();
17720
- context.beginPath();
17721
- context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
17722
- for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
17723
- context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
17724
- }
17725
- context.closePath();
17726
- context.strokeStyle = surfacePatch.outlineColor;
17727
- context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0044);
17728
- context.lineJoin = 'round';
17729
- context.stroke();
17730
- context.restore();
17731
- }
17732
- /**
17733
- * Creates the projected 3D tentacle strokes orbiting around the lower octopus body.
17734
- *
17735
- * @private helper of `octopus3dAvatarVisual`
17736
- */
17737
- function createOctopusTentacleStrokes(options) {
17738
- const { createRandom, morphologyProfile, timeMs, size, center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, animationPhase, } = options;
17739
- return Array.from({ length: morphologyProfile.tentacles.count }, (_, tentacleIndex) => {
17740
- const tentacleRandom = createRandom(`octopus3d-tentacle-${tentacleIndex}`);
17741
- const spreadProgress = morphologyProfile.tentacles.count === 1 ? 0.5 : tentacleIndex / (morphologyProfile.tentacles.count - 1);
17742
- const orbitAngle = -Math.PI * 0.92 + spreadProgress * Math.PI * 1.84 + (tentacleRandom() - 0.5) * 0.16;
17743
- const flowLength = size * (0.19 + morphologyProfile.tentacles.flowLengthScale * 0.075 + tentacleRandom() * 0.018);
17744
- const lateralReach = size *
17745
- (0.08 + morphologyProfile.tentacles.lateralReachScale * 0.05 + Math.abs(Math.sin(orbitAngle)) * 0.018);
17746
- const depthReach = size * (0.028 + morphologyProfile.tentacles.tipReachScale * 0.032);
17747
- const sway = Math.sin(timeMs / (760 + tentacleIndex * 36) + animationPhase + tentacleRandom() * Math.PI * 2);
17748
- const anchorPoint = {
17749
- x: Math.sin(orbitAngle) * radiusX * (0.84 + tentacleRandom() * 0.08),
17750
- y: radiusY * (0.22 + tentacleRandom() * 0.18),
17751
- z: Math.cos(orbitAngle) * radiusZ * (0.72 + tentacleRandom() * 0.12),
17752
- };
17753
- const controlPointOne = {
17754
- x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * 0.44,
17755
- y: anchorPoint.y + flowLength * 0.26,
17756
- z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.3 + sway * size * 0.012,
17757
- };
17758
- const controlPointTwo = {
17759
- x: anchorPoint.x +
17760
- Math.sin(orbitAngle) * lateralReach * (0.82 + morphologyProfile.tentacles.swayScale * 0.12),
17761
- y: anchorPoint.y + flowLength * 0.66,
17762
- z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.72 + sway * size * 0.02,
17763
- };
17764
- const endPoint = {
17765
- x: anchorPoint.x +
17766
- Math.sin(orbitAngle) * lateralReach * (1.02 + morphologyProfile.tentacles.tipWidthScale * 0.12) +
17767
- sway * size * 0.028,
17768
- y: anchorPoint.y + flowLength,
17769
- z: anchorPoint.z + Math.cos(orbitAngle) * depthReach + sway * size * 0.016,
17770
- };
17771
- const scenePoints = Array.from({ length: 12 }, (_, sampleIndex) => transformScenePoint(sampleCubicBezierPoint3D(anchorPoint, controlPointOne, controlPointTwo, endPoint, sampleIndex / 11), center, rotationX, rotationY));
17772
- const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
17773
- const averageDepth = scenePoints.reduce((depthSum, scenePoint) => depthSum + scenePoint.z, 0) / scenePoints.length;
17774
- return {
17775
- projectedPoints,
17776
- averageDepth,
17777
- isFrontFacing: averageDepth >= center.z - size * 0.006,
17778
- baseWidth: size *
17779
- (0.019 +
17780
- morphologyProfile.tentacles.baseWidthScale * 0.007 +
17781
- tentacleRandom() * 0.003 +
17782
- Math.abs(Math.sin(orbitAngle)) * 0.002),
17783
- tipWidth: size * (0.0046 + morphologyProfile.tentacles.tipWidthScale * 0.0018),
17784
- colorBias: tentacleRandom(),
17785
- };
17786
- });
17787
- }
17788
- /**
17789
- * Samples one point on a cubic Bezier curve in 3D.
17790
- *
17791
- * @private helper of `octopus3dAvatarVisual`
17792
- */
17793
- function sampleCubicBezierPoint3D(startPoint, controlPointOne, controlPointTwo, endPoint, progress) {
17794
- const inverseProgress = 1 - progress;
17795
- return {
17796
- x: inverseProgress * inverseProgress * inverseProgress * startPoint.x +
17797
- 3 * inverseProgress * inverseProgress * progress * controlPointOne.x +
17798
- 3 * inverseProgress * progress * progress * controlPointTwo.x +
17799
- progress * progress * progress * endPoint.x,
17800
- y: inverseProgress * inverseProgress * inverseProgress * startPoint.y +
17801
- 3 * inverseProgress * inverseProgress * progress * controlPointOne.y +
17802
- 3 * inverseProgress * progress * progress * controlPointTwo.y +
17803
- progress * progress * progress * endPoint.y,
17804
- z: inverseProgress * inverseProgress * inverseProgress * startPoint.z +
17805
- 3 * inverseProgress * inverseProgress * progress * controlPointOne.z +
17806
- 3 * inverseProgress * progress * progress * controlPointTwo.z +
17807
- progress * progress * progress * endPoint.z,
17808
- };
17809
- }
17810
- /**
17811
- * Draws one projected tentacle stroke with a slim highlight ridge.
17812
- *
17813
- * @private helper of `octopus3dAvatarVisual`
17814
- */
17815
- function drawTentacleStroke(context, tentacleStroke, palette) {
17816
- const projectedSegments = tentacleStroke.projectedPoints.length - 1;
17817
- for (let segmentIndex = 0; segmentIndex < projectedSegments; segmentIndex++) {
17818
- const startPoint = tentacleStroke.projectedPoints[segmentIndex];
17819
- const endPoint = tentacleStroke.projectedPoints[segmentIndex + 1];
17820
- const progress = segmentIndex / projectedSegments;
17821
- const width = tentacleStroke.baseWidth + (tentacleStroke.tipWidth - tentacleStroke.baseWidth) * progress;
17822
- context.beginPath();
17823
- context.moveTo(startPoint.x, startPoint.y);
17824
- context.lineTo(endPoint.x, endPoint.y);
17825
- context.strokeStyle = tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
17826
- context.lineWidth = width;
17827
- context.lineCap = 'round';
17828
- context.stroke();
17829
- context.beginPath();
17830
- context.moveTo(startPoint.x, startPoint.y);
17831
- context.lineTo(endPoint.x, endPoint.y);
17832
- context.strokeStyle = tentacleStroke.isFrontFacing ? `${palette.highlight}80` : `${palette.highlight}40`;
17833
- context.lineWidth = Math.max(1, width * 0.34);
17834
- context.lineCap = 'round';
17835
- context.stroke();
17836
- }
17837
- }
17838
- /**
17839
- * Resolves the front surface depth on an ellipsoid for one local face point.
17840
- *
17841
- * @private helper of `octopus3dAvatarVisual`
17842
- */
17843
- function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
17844
- const normalizedX = x / radiusX;
17845
- const normalizedY = y / radiusY;
17846
- const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
17847
- return Math.sqrt(remainingDepthRatio) * radiusZ;
17848
- }
17849
-
17850
17763
  /* eslint-disable no-magic-numbers */
17851
17764
  /**
17852
17765
  * Light direction used by the single-mesh octopus shading.
17853
17766
  *
17854
17767
  * @private helper of `octopus3d2AvatarVisual`
17855
17768
  */
17856
- const LIGHT_DIRECTION$2 = normalizeVector3({
17769
+ const LIGHT_DIRECTION$3 = normalizeVector3({
17857
17770
  x: 0.38,
17858
17771
  y: -0.6,
17859
17772
  z: 0.98,
@@ -18092,7 +18005,7 @@
18092
18005
  transformedCorners[2].z +
18093
18006
  transformedCorners[3].z) /
18094
18007
  4,
18095
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
18008
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
18096
18009
  fillStyle: resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
18097
18010
  outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
18098
18011
  });
@@ -18207,7 +18120,7 @@
18207
18120
  *
18208
18121
  * @private helper of `octopus3d3AvatarVisual`
18209
18122
  */
18210
- const LIGHT_DIRECTION$1 = normalizeVector3({
18123
+ const LIGHT_DIRECTION$2 = normalizeVector3({
18211
18124
  x: 0.34,
18212
18125
  y: -0.62,
18213
18126
  z: 1,
@@ -18514,7 +18427,7 @@
18514
18427
  transformedCorners[2].z +
18515
18428
  transformedCorners[3].z) /
18516
18429
  4,
18517
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
18430
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
18518
18431
  fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
18519
18432
  outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
18520
18433
  });
@@ -18829,7 +18742,7 @@
18829
18742
  *
18830
18743
  * @private helper of `octopus3d4AvatarVisual`
18831
18744
  */
18832
- const LIGHT_DIRECTION = normalizeVector3({
18745
+ const LIGHT_DIRECTION$1 = normalizeVector3({
18833
18746
  x: 0.32,
18834
18747
  y: -0.66,
18835
18748
  z: 1,
@@ -18900,7 +18813,7 @@
18900
18813
  isAnimated: true,
18901
18814
  supportsPointerTracking: true,
18902
18815
  render({ context, size, palette, createRandom, timeMs, interaction }) {
18903
- const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots } = getOctopus3d4StableState(createRandom);
18816
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots, } = getOctopus3d4StableState(createRandom);
18904
18817
  const sceneCenterX = size * 0.5;
18905
18818
  const sceneCenterY = size * 0.535;
18906
18819
  const bob = Math.sin(timeMs / 980 + animationPhase) * size * 0.013;
@@ -19175,7 +19088,7 @@
19175
19088
  projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
19176
19089
  projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
19177
19090
  ];
19178
- const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1);
19091
+ const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1);
19179
19092
  const rimLightIntensity = Math.pow(clampNumber$1(dotProduct3D(surfaceNormal, RIM_LIGHT_DIRECTION), 0, 1), 2.6);
19180
19093
  surfacePatches.push({
19181
19094
  corners: projectedCorners,
@@ -19248,10 +19161,7 @@
19248
19161
  Math.max(0, Math.cos(effectiveLongitude)) * 0.12 +
19249
19162
  lowerBlend * tentacleInfluence.core * (0.12 + tentacleInfluence.depthScale * 0.07) -
19250
19163
  Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
19251
- const tentacleTubeRadius = lowerBlend *
19252
- tentacleInfluence.core *
19253
- (0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) *
19254
- radiusX;
19164
+ const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) * radiusX;
19255
19165
  const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
19256
19166
  const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.74;
19257
19167
  const lowerDrop = lowerBlend *
@@ -19263,9 +19173,7 @@
19263
19173
  (morphologyProfile.tentacles.flowLengthScale - 1) * 0.1));
19264
19174
  const combinedTentacleSway = (primaryTentacleWave + secondaryTentacleWave) * radiusX * (0.06 + tipBlend * 0.06);
19265
19175
  return {
19266
- x: Math.sin(effectiveLongitude) * planarRadiusX +
19267
- combinedTentacleSway +
19268
- tentacleCurl * radiusX * 0.18,
19176
+ x: Math.sin(effectiveLongitude) * planarRadiusX + combinedTentacleSway + tentacleCurl * radiusX * 0.18,
19269
19177
  y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
19270
19178
  upperBlend * radiusY * 0.12 +
19271
19179
  lowerDrop +
@@ -19579,6 +19487,445 @@
19579
19487
  .padStart(2, '0');
19580
19488
  }
19581
19489
 
19490
+ /* eslint-disable no-magic-numbers */
19491
+ /**
19492
+ * Light direction used by the organic 3D octopus shading.
19493
+ *
19494
+ * @private helper of `octopus3dAvatarVisual`
19495
+ */
19496
+ const LIGHT_DIRECTION = normalizeVector3({
19497
+ x: 0.48,
19498
+ y: -0.62,
19499
+ z: 0.94,
19500
+ });
19501
+ /**
19502
+ * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
19503
+ *
19504
+ * @private helper of `octopus3dAvatarVisual`
19505
+ */
19506
+ const octopus3dStableStateCache = new WeakMap();
19507
+ /**
19508
+ * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
19509
+ *
19510
+ * @private helper of `octopus3dAvatarVisual`
19511
+ */
19512
+ function getOctopus3dStableState(createRandom) {
19513
+ const cached = octopus3dStableStateCache.get(createRandom);
19514
+ if (cached !== undefined) {
19515
+ return cached;
19516
+ }
19517
+ const animationRandom = createRandom('octopus3d-animation-profile');
19518
+ const eyeRandom = createRandom('octopus3d-eye-profile');
19519
+ const leftEyePhaseOffset = eyeRandom() * 0.6;
19520
+ const rightEyePhaseOffset = eyeRandom() * 0.6;
19521
+ const state = {
19522
+ morphologyProfile: createOctopus3MorphologyProfile(createRandom),
19523
+ animationPhase: animationRandom() * Math.PI * 2,
19524
+ leftEyePhaseOffset,
19525
+ rightEyePhaseOffset,
19526
+ };
19527
+ octopus3dStableStateCache.set(createRandom, state);
19528
+ return state;
19529
+ }
19530
+ /**
19531
+ * Proper 3D Octopus visual built from projected organic meshes and tentacles.
19532
+ *
19533
+ * @private built-in avatar visual
19534
+ */
19535
+ const octopus3dAvatarVisual = {
19536
+ id: 'octopus3d',
19537
+ title: 'Octopus 3D',
19538
+ description: 'Proper 3D octopus portrait with a turning silhouette, expressive eyes, and depth-sorted tentacles.',
19539
+ isAnimated: true,
19540
+ supportsPointerTracking: true,
19541
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
19542
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
19543
+ const sceneCenterX = size * 0.5;
19544
+ const sceneCenterY = size * 0.56;
19545
+ const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
19546
+ const mantleCenter = {
19547
+ x: interaction.bodyOffsetX * size * 0.042 + size * morphologyProfile.body.centerXJitterRatio * 0.65,
19548
+ y: -size * 0.09 + interaction.bodyOffsetY * size * 0.028 + bob,
19549
+ z: interaction.intensity * size * 0.012,
19550
+ };
19551
+ const underbodyCenter = {
19552
+ x: mantleCenter.x * 0.86,
19553
+ y: mantleCenter.y + size * 0.168,
19554
+ z: mantleCenter.z - size * 0.018,
19555
+ };
19556
+ const mantleRadiusX = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch;
19557
+ const mantleRadiusY = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1;
19558
+ const mantleRadiusZ = size *
19559
+ morphologyProfile.body.bodyRadiusRatio *
19560
+ (0.9 + (morphologyProfile.body.horizontalStretch - 1) * 0.3);
19561
+ const underbodyRadiusX = mantleRadiusX * (0.9 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.08);
19562
+ const underbodyRadiusY = mantleRadiusY * (0.44 + morphologyProfile.body.lowerDropRatio * 3.1);
19563
+ const underbodyRadiusZ = mantleRadiusZ * 0.78;
19564
+ const bodyYaw = -0.18 +
19565
+ Math.sin(timeMs / 2400 + animationPhase) * 0.05 +
19566
+ interaction.bodyOffsetX * 0.18 +
19567
+ interaction.gazeX * 0.22;
19568
+ const bodyPitch = -0.08 +
19569
+ Math.cos(timeMs / 2700 + animationPhase * 0.6) * 0.025 -
19570
+ interaction.bodyOffsetY * 0.08 -
19571
+ interaction.gazeY * 0.08;
19572
+ const headYaw = bodyYaw - 0.04 + interaction.gazeX * 0.56;
19573
+ const headPitch = bodyPitch - 0.02 - interaction.gazeY * 0.32;
19574
+ const mantlePatches = resolveVisibleEllipsoidPatches({
19575
+ center: mantleCenter,
19576
+ radiusX: mantleRadiusX,
19577
+ radiusY: mantleRadiusY,
19578
+ radiusZ: mantleRadiusZ,
19579
+ rotationX: headPitch,
19580
+ rotationY: headYaw,
19581
+ sceneCenterX,
19582
+ sceneCenterY,
19583
+ size,
19584
+ palette,
19585
+ verticalColorBias: 0,
19586
+ outlineColor: `${palette.highlight}7a`,
19587
+ });
19588
+ const underbodyPatches = resolveVisibleEllipsoidPatches({
19589
+ center: underbodyCenter,
19590
+ radiusX: underbodyRadiusX,
19591
+ radiusY: underbodyRadiusY,
19592
+ radiusZ: underbodyRadiusZ,
19593
+ rotationX: bodyPitch,
19594
+ rotationY: bodyYaw,
19595
+ sceneCenterX,
19596
+ sceneCenterY,
19597
+ size,
19598
+ palette,
19599
+ verticalColorBias: 0.18,
19600
+ outlineColor: `${palette.shadow}8f`,
19601
+ });
19602
+ const tentacleStrokes = createOctopusTentacleStrokes({
19603
+ createRandom,
19604
+ morphologyProfile,
19605
+ timeMs,
19606
+ size,
19607
+ center: underbodyCenter,
19608
+ radiusX: underbodyRadiusX,
19609
+ radiusY: underbodyRadiusY,
19610
+ radiusZ: underbodyRadiusZ,
19611
+ rotationX: bodyPitch,
19612
+ rotationY: bodyYaw,
19613
+ sceneCenterX,
19614
+ sceneCenterY,
19615
+ animationPhase,
19616
+ });
19617
+ const faceEyeSpacing = size * morphologyProfile.face.eyeSpacingRatio * 0.92;
19618
+ const faceEyeYOffset = size * morphologyProfile.face.eyeCenterYOffsetRatio - mantleRadiusY * 0.02;
19619
+ const faceEyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.82;
19620
+ const faceEyeRadiusY = faceEyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.96;
19621
+ const mouthHalfWidth = size * morphologyProfile.face.mouthWidthRatio * 0.92;
19622
+ const mouthY = size * morphologyProfile.face.mouthYOffsetRatio + mantleRadiusY * 0.08;
19623
+ drawAvatarFrame(context, size, palette);
19624
+ drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
19625
+ drawOctopus3dShadow(context, size, palette, interaction, timeMs);
19626
+ for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => !candidateTentacleStroke.isFrontFacing)) {
19627
+ drawTentacleStroke(context, tentacleStroke, palette);
19628
+ }
19629
+ for (const surfacePatch of [...mantlePatches, ...underbodyPatches].sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
19630
+ drawSurfacePatch(context, surfacePatch);
19631
+ }
19632
+ for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
19633
+ drawTentacleStroke(context, tentacleStroke, palette);
19634
+ }
19635
+ drawProjectedOrganicEye(context, {
19636
+ x: -faceEyeSpacing,
19637
+ y: faceEyeYOffset,
19638
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
19639
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
19640
+ drawProjectedOrganicEye(context, {
19641
+ x: faceEyeSpacing,
19642
+ y: faceEyeYOffset,
19643
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
19644
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
19645
+ drawProjectedOrganicMouth(context, [
19646
+ {
19647
+ x: -mouthHalfWidth,
19648
+ y: mouthY,
19649
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -mouthHalfWidth, mouthY),
19650
+ },
19651
+ {
19652
+ x: size * morphologyProfile.face.mouthCenterOffsetRatio,
19653
+ y: mouthY +
19654
+ size * morphologyProfile.face.mouthCurveDepthRatio * 0.38 +
19655
+ Math.sin(timeMs / 760 + animationPhase) * size * 0.01 +
19656
+ interaction.gazeY * size * 0.01,
19657
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, size * morphologyProfile.face.mouthCenterOffsetRatio, mouthY),
19658
+ },
19659
+ {
19660
+ x: mouthHalfWidth,
19661
+ y: mouthY,
19662
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, mouthHalfWidth, mouthY),
19663
+ },
19664
+ ], mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, palette, size);
19665
+ },
19666
+ };
19667
+ /**
19668
+ * Draws the atmospheric underwater glow behind the octopus mesh.
19669
+ *
19670
+ * @private helper of `octopus3dAvatarVisual`
19671
+ */
19672
+ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
19673
+ const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.1, sceneCenterY - size * 0.2 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY - size * 0.04, size * 0.62);
19674
+ glowGradient.addColorStop(0, `${palette.highlight}5c`);
19675
+ glowGradient.addColorStop(0.36, `${palette.accent}24`);
19676
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
19677
+ context.fillStyle = glowGradient;
19678
+ context.fillRect(0, 0, size, size);
19679
+ const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1700) * size * 0.05, sceneCenterY + size * 0.23, size * 0.08, sceneCenterX, sceneCenterY + size * 0.28, size * 0.5);
19680
+ lowerGradient.addColorStop(0, `${palette.secondary}1d`);
19681
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
19682
+ context.fillStyle = lowerGradient;
19683
+ context.fillRect(0, 0, size, size);
19684
+ }
19685
+ /**
19686
+ * Draws the soft ground shadow below the octopus.
19687
+ *
19688
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
19689
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
19690
+ *
19691
+ * @private helper of `octopus3dAvatarVisual`
19692
+ */
19693
+ function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
19694
+ const cx = size * 0.5 + interaction.gazeX * size * 0.04;
19695
+ const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
19696
+ const rx = size * (0.18 + interaction.intensity * 0.02);
19697
+ const ry = size * 0.06;
19698
+ context.save();
19699
+ context.translate(cx, cy);
19700
+ context.scale(1, ry / rx);
19701
+ const blurRadius = rx * 1.4;
19702
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
19703
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
19704
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
19705
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
19706
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
19707
+ context.fillStyle = shadowGradient;
19708
+ context.beginPath();
19709
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
19710
+ context.fill();
19711
+ context.restore();
19712
+ }
19713
+ /**
19714
+ * Resolves visible projected patches for one rotated ellipsoid mesh.
19715
+ *
19716
+ * @private helper of `octopus3dAvatarVisual`
19717
+ */
19718
+ function resolveVisibleEllipsoidPatches(options) {
19719
+ const { center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, verticalColorBias, outlineColor, } = options;
19720
+ const latitudePatchCount = 10;
19721
+ const longitudePatchCount = 18;
19722
+ const surfacePatches = [];
19723
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
19724
+ const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
19725
+ const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
19726
+ const verticalProgress = (latitudeIndex + 0.5) / latitudePatchCount;
19727
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
19728
+ const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
19729
+ const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
19730
+ const localCorners = [
19731
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, startLongitude),
19732
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, endLongitude),
19733
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, endLongitude),
19734
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, startLongitude),
19735
+ ];
19736
+ const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
19737
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
19738
+ if (surfaceNormal.z <= 0.01) {
19739
+ continue;
19740
+ }
19741
+ const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
19742
+ surfacePatches.push({
19743
+ corners: projectedCorners,
19744
+ averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
19745
+ transformedCorners.length,
19746
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
19747
+ fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
19748
+ outlineColor,
19749
+ });
19750
+ }
19751
+ }
19752
+ return surfacePatches;
19753
+ }
19754
+ /**
19755
+ * Samples one point on an ellipsoid aligned to the local axes.
19756
+ *
19757
+ * @private helper of `octopus3dAvatarVisual`
19758
+ */
19759
+ function sampleEllipsoidPoint(radiusX, radiusY, radiusZ, latitude, longitude) {
19760
+ const cosineLatitude = Math.cos(latitude);
19761
+ return {
19762
+ x: Math.sin(longitude) * cosineLatitude * radiusX,
19763
+ y: Math.sin(latitude) * radiusY,
19764
+ z: Math.cos(longitude) * cosineLatitude * radiusZ,
19765
+ };
19766
+ }
19767
+ /**
19768
+ * Resolves one base fill tone for a surface patch across the octopus body.
19769
+ *
19770
+ * @private helper of `octopus3dAvatarVisual`
19771
+ */
19772
+ function resolveSurfacePatchFillStyle(palette, verticalProgress) {
19773
+ const clampedVerticalProgress = clampNumber$1(verticalProgress, 0, 1);
19774
+ if (clampedVerticalProgress < 0.2) {
19775
+ return palette.highlight;
19776
+ }
19777
+ if (clampedVerticalProgress < 0.45) {
19778
+ return palette.secondary;
19779
+ }
19780
+ if (clampedVerticalProgress < 0.8) {
19781
+ return palette.primary;
19782
+ }
19783
+ return `${palette.shadow}f2`;
19784
+ }
19785
+ /**
19786
+ * Draws one projected mesh patch with organic shading.
19787
+ *
19788
+ * @private helper of `octopus3dAvatarVisual`
19789
+ */
19790
+ function drawSurfacePatch(context, surfacePatch) {
19791
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
19792
+ if (surfacePatch.lightIntensity > 0) {
19793
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.15 * surfacePatch.lightIntensity})`);
19794
+ }
19795
+ else if (surfacePatch.lightIntensity < 0) {
19796
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
19797
+ }
19798
+ context.save();
19799
+ context.beginPath();
19800
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
19801
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
19802
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
19803
+ }
19804
+ context.closePath();
19805
+ context.strokeStyle = surfacePatch.outlineColor;
19806
+ context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0044);
19807
+ context.lineJoin = 'round';
19808
+ context.stroke();
19809
+ context.restore();
19810
+ }
19811
+ /**
19812
+ * Creates the projected 3D tentacle strokes orbiting around the lower octopus body.
19813
+ *
19814
+ * @private helper of `octopus3dAvatarVisual`
19815
+ */
19816
+ function createOctopusTentacleStrokes(options) {
19817
+ const { createRandom, morphologyProfile, timeMs, size, center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, animationPhase, } = options;
19818
+ return Array.from({ length: morphologyProfile.tentacles.count }, (_, tentacleIndex) => {
19819
+ const tentacleRandom = createRandom(`octopus3d-tentacle-${tentacleIndex}`);
19820
+ const spreadProgress = morphologyProfile.tentacles.count === 1 ? 0.5 : tentacleIndex / (morphologyProfile.tentacles.count - 1);
19821
+ const orbitAngle = -Math.PI * 0.92 + spreadProgress * Math.PI * 1.84 + (tentacleRandom() - 0.5) * 0.16;
19822
+ const flowLength = size * (0.19 + morphologyProfile.tentacles.flowLengthScale * 0.075 + tentacleRandom() * 0.018);
19823
+ const lateralReach = size *
19824
+ (0.08 + morphologyProfile.tentacles.lateralReachScale * 0.05 + Math.abs(Math.sin(orbitAngle)) * 0.018);
19825
+ const depthReach = size * (0.028 + morphologyProfile.tentacles.tipReachScale * 0.032);
19826
+ const sway = Math.sin(timeMs / (760 + tentacleIndex * 36) + animationPhase + tentacleRandom() * Math.PI * 2);
19827
+ const anchorPoint = {
19828
+ x: Math.sin(orbitAngle) * radiusX * (0.84 + tentacleRandom() * 0.08),
19829
+ y: radiusY * (0.22 + tentacleRandom() * 0.18),
19830
+ z: Math.cos(orbitAngle) * radiusZ * (0.72 + tentacleRandom() * 0.12),
19831
+ };
19832
+ const controlPointOne = {
19833
+ x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * 0.44,
19834
+ y: anchorPoint.y + flowLength * 0.26,
19835
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.3 + sway * size * 0.012,
19836
+ };
19837
+ const controlPointTwo = {
19838
+ x: anchorPoint.x +
19839
+ Math.sin(orbitAngle) * lateralReach * (0.82 + morphologyProfile.tentacles.swayScale * 0.12),
19840
+ y: anchorPoint.y + flowLength * 0.66,
19841
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.72 + sway * size * 0.02,
19842
+ };
19843
+ const endPoint = {
19844
+ x: anchorPoint.x +
19845
+ Math.sin(orbitAngle) * lateralReach * (1.02 + morphologyProfile.tentacles.tipWidthScale * 0.12) +
19846
+ sway * size * 0.028,
19847
+ y: anchorPoint.y + flowLength,
19848
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach + sway * size * 0.016,
19849
+ };
19850
+ const scenePoints = Array.from({ length: 12 }, (_, sampleIndex) => transformScenePoint(sampleCubicBezierPoint3D(anchorPoint, controlPointOne, controlPointTwo, endPoint, sampleIndex / 11), center, rotationX, rotationY));
19851
+ const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
19852
+ const averageDepth = scenePoints.reduce((depthSum, scenePoint) => depthSum + scenePoint.z, 0) / scenePoints.length;
19853
+ return {
19854
+ projectedPoints,
19855
+ averageDepth,
19856
+ isFrontFacing: averageDepth >= center.z - size * 0.006,
19857
+ baseWidth: size *
19858
+ (0.019 +
19859
+ morphologyProfile.tentacles.baseWidthScale * 0.007 +
19860
+ tentacleRandom() * 0.003 +
19861
+ Math.abs(Math.sin(orbitAngle)) * 0.002),
19862
+ tipWidth: size * (0.0046 + morphologyProfile.tentacles.tipWidthScale * 0.0018),
19863
+ colorBias: tentacleRandom(),
19864
+ };
19865
+ });
19866
+ }
19867
+ /**
19868
+ * Samples one point on a cubic Bezier curve in 3D.
19869
+ *
19870
+ * @private helper of `octopus3dAvatarVisual`
19871
+ */
19872
+ function sampleCubicBezierPoint3D(startPoint, controlPointOne, controlPointTwo, endPoint, progress) {
19873
+ const inverseProgress = 1 - progress;
19874
+ return {
19875
+ x: inverseProgress * inverseProgress * inverseProgress * startPoint.x +
19876
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.x +
19877
+ 3 * inverseProgress * progress * progress * controlPointTwo.x +
19878
+ progress * progress * progress * endPoint.x,
19879
+ y: inverseProgress * inverseProgress * inverseProgress * startPoint.y +
19880
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.y +
19881
+ 3 * inverseProgress * progress * progress * controlPointTwo.y +
19882
+ progress * progress * progress * endPoint.y,
19883
+ z: inverseProgress * inverseProgress * inverseProgress * startPoint.z +
19884
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.z +
19885
+ 3 * inverseProgress * progress * progress * controlPointTwo.z +
19886
+ progress * progress * progress * endPoint.z,
19887
+ };
19888
+ }
19889
+ /**
19890
+ * Draws one projected tentacle stroke with a slim highlight ridge.
19891
+ *
19892
+ * @private helper of `octopus3dAvatarVisual`
19893
+ */
19894
+ function drawTentacleStroke(context, tentacleStroke, palette) {
19895
+ const projectedSegments = tentacleStroke.projectedPoints.length - 1;
19896
+ for (let segmentIndex = 0; segmentIndex < projectedSegments; segmentIndex++) {
19897
+ const startPoint = tentacleStroke.projectedPoints[segmentIndex];
19898
+ const endPoint = tentacleStroke.projectedPoints[segmentIndex + 1];
19899
+ const progress = segmentIndex / projectedSegments;
19900
+ const width = tentacleStroke.baseWidth + (tentacleStroke.tipWidth - tentacleStroke.baseWidth) * progress;
19901
+ context.beginPath();
19902
+ context.moveTo(startPoint.x, startPoint.y);
19903
+ context.lineTo(endPoint.x, endPoint.y);
19904
+ context.strokeStyle = tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
19905
+ context.lineWidth = width;
19906
+ context.lineCap = 'round';
19907
+ context.stroke();
19908
+ context.beginPath();
19909
+ context.moveTo(startPoint.x, startPoint.y);
19910
+ context.lineTo(endPoint.x, endPoint.y);
19911
+ context.strokeStyle = tentacleStroke.isFrontFacing ? `${palette.highlight}80` : `${palette.highlight}40`;
19912
+ context.lineWidth = Math.max(1, width * 0.34);
19913
+ context.lineCap = 'round';
19914
+ context.stroke();
19915
+ }
19916
+ }
19917
+ /**
19918
+ * Resolves the front surface depth on an ellipsoid for one local face point.
19919
+ *
19920
+ * @private helper of `octopus3dAvatarVisual`
19921
+ */
19922
+ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
19923
+ const normalizedX = x / radiusX;
19924
+ const normalizedY = y / radiusY;
19925
+ const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
19926
+ return Math.sqrt(remainingDepthRatio) * radiusZ;
19927
+ }
19928
+
19582
19929
  /* eslint-disable no-magic-numbers */
19583
19930
  /**
19584
19931
  * Octopus avatar visual.
@@ -20399,14 +20746,14 @@
20399
20746
  /**
20400
20747
  * META AVATAR commitment definition
20401
20748
  *
20402
- * The `META AVATAR` commitment sets the built-in default avatar visual used when
20403
- * the agent does not provide an explicit `META IMAGE`.
20749
+ * The `META AVATAR` and `META VISUAL` commitments set the built-in default avatar visual
20750
+ * used when the agent does not provide an explicit `META IMAGE`.
20404
20751
  *
20405
20752
  * @private [🪔] Maybe export the commitments through some package
20406
20753
  */
20407
20754
  class MetaAvatarCommitmentDefinition extends BaseCommitmentDefinition {
20408
- constructor() {
20409
- super('META AVATAR');
20755
+ constructor(type = 'META AVATAR') {
20756
+ super(type);
20410
20757
  }
20411
20758
  /**
20412
20759
  * Short one-line description of META AVATAR.
@@ -20426,14 +20773,15 @@
20426
20773
  get documentation() {
20427
20774
  const supportedVisuals = AVATAR_VISUALS.map((avatarVisual) => `\`${avatarVisual.id}\``).join(', ');
20428
20775
  return _spaceTrim.spaceTrim(`
20429
- # META AVATAR
20776
+ # ${this.type}
20430
20777
 
20431
20778
  Sets the built-in avatar visual used for the agent when no explicit \`META IMAGE\` is provided.
20779
+ \`META AVATAR\` and \`META VISUAL\` are equivalent.
20432
20780
 
20433
20781
  ## Key aspects
20434
20782
 
20435
20783
  - Does not modify the agent's behavior or responses.
20436
- - Only one \`META AVATAR\` should be used per agent.
20784
+ - Only one \`META AVATAR\` or \`META VISUAL\` should be used per agent.
20437
20785
  - If multiple are specified, the last one takes precedence.
20438
20786
  - Values are matched case-insensitively and spaces, hyphens, and underscores are normalized.
20439
20787
  - Supported visuals are derived from the shared avatar registry: ${supportedVisuals}.
@@ -20448,15 +20796,15 @@
20448
20796
  \`\`\`
20449
20797
 
20450
20798
  \`\`\`book
20451
- Orb Assistant
20799
+ Minecraft Assistant
20452
20800
 
20453
- META AVATAR orb
20801
+ META VISUAL Minecraft2
20454
20802
  GOAL Answer in a calm and focused way.
20455
20803
  \`\`\`
20456
20804
  `);
20457
20805
  }
20458
20806
  applyToAgentModelRequirements(requirements, content) {
20459
- // META AVATAR doesn't modify the system message or model requirements.
20807
+ // META AVATAR and META VISUAL don't modify the system message or model requirements.
20460
20808
  // It's handled separately in the parsing logic for profile avatar resolution.
20461
20809
  return requirements;
20462
20810
  }
@@ -22435,10 +22783,10 @@
22435
22783
  *
22436
22784
  * @private
22437
22785
  */
22438
- const TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES = [
22439
- '- If a teammate is relevant to the request, consult that teammate using the matching tool.',
22440
- '- Do not ask the user for information that a listed teammate can provide directly.',
22441
- ];
22786
+ const TEAM_SYSTEM_MESSAGE_GUIDANCE = _spaceTrim.spaceTrim(`
22787
+ - If a teammate is relevant to the request, consult that teammate using the matching tool.
22788
+ - Do not ask the user for information that a listed teammate can provide directly.
22789
+ `);
22442
22790
  /**
22443
22791
  * Constant for remote agents by Url.
22444
22792
  */
@@ -22620,7 +22968,7 @@
22620
22968
  `);
22621
22969
  });
22622
22970
  return _spaceTrim.spaceTrim((block) => `
22623
- ${block(TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES.join('\n'))}
22971
+ ${block(TEAM_SYSTEM_MESSAGE_GUIDANCE)}
22624
22972
 
22625
22973
  ${block(teammateSections.join('\n\n'))}
22626
22974
  `);
@@ -29644,6 +29992,7 @@
29644
29992
  new ActionCommitmentDefinition('ACTIONS'),
29645
29993
  new ComponentCommitmentDefinition(),
29646
29994
  new MetaAvatarCommitmentDefinition(),
29995
+ new MetaAvatarCommitmentDefinition('META VISUAL'),
29647
29996
  new MetaImageCommitmentDefinition(),
29648
29997
  new MetaColorCommitmentDefinition(),
29649
29998
  new MetaFontCommitmentDefinition(),
@@ -30995,6 +31344,7 @@
30995
31344
  */
30996
31345
  const META_COMMITMENT_APPLIERS = {
30997
31346
  'META AVATAR': applyMetaAvatarContent,
31347
+ 'META VISUAL': applyMetaAvatarContent,
30998
31348
  'META LINK': applyMetaLinkContent,
30999
31349
  'META DOMAIN': applyMetaDomainContent,
31000
31350
  'META IMAGE': applyMetaImageContent,
@@ -31043,7 +31393,7 @@
31043
31393
  state.meta[metaType] = metaValue;
31044
31394
  }
31045
31395
  /**
31046
- * Applies META AVATAR content into the canonical `meta.avatar` field.
31396
+ * Applies META AVATAR / META VISUAL content into the canonical `meta.avatar` field.
31047
31397
  */
31048
31398
  function applyMetaAvatarContent(state, content) {
31049
31399
  const avatarVisualId = resolveAvatarVisualId(content);
@@ -32107,6 +32457,78 @@
32107
32457
  .replace(/^[._-]+|[._-]+$/gu, '');
32108
32458
  }
32109
32459
 
32460
+ /**
32461
+ * Helper of usage compute
32462
+ *
32463
+ * @param content the content of prompt or response
32464
+ * @returns part of UsageCounts
32465
+ *
32466
+ * @private internal utility of LlmExecutionTools
32467
+ */
32468
+ function computeUsageCounts(content) {
32469
+ return {
32470
+ charactersCount: { value: countCharacters(content) },
32471
+ wordsCount: { value: countWords(content) },
32472
+ sentencesCount: { value: countSentences(content) },
32473
+ linesCount: { value: countLines(content) },
32474
+ paragraphsCount: { value: countParagraphs(content) },
32475
+ pagesCount: { value: countPages(content) },
32476
+ };
32477
+ }
32478
+
32479
+ /**
32480
+ * Computes the usage of the OpenAI API based on the response from OpenAI
32481
+ *
32482
+ * @param promptContent The content of the prompt
32483
+ * @param resultContent The content of the result (for embedding prompts or failed prompts pass empty string)
32484
+ * @param rawResponse The raw response from OpenAI API
32485
+ * @param duration The duration of the execution
32486
+ * @throws {PipelineExecutionError} If the usage is not defined in the response from OpenAI
32487
+ *
32488
+ * @private internal utility of `OpenAiExecutionTools`
32489
+ */
32490
+ function computeOpenAiUsage(promptContent, // <- Note: Intentionally using [] to access type properties to bring jsdoc from Prompt/PromptResult to consumer
32491
+ resultContent, rawResponse, duration = ZERO_VALUE) {
32492
+ var _a, _b;
32493
+ if (rawResponse.usage === undefined) {
32494
+ throw new PipelineExecutionError('The usage is not defined in the response from OpenAI');
32495
+ }
32496
+ if (((_a = rawResponse.usage) === null || _a === void 0 ? void 0 : _a.prompt_tokens) === undefined) {
32497
+ throw new PipelineExecutionError('In OpenAI response `usage.prompt_tokens` not defined');
32498
+ }
32499
+ const inputTokens = rawResponse.usage.prompt_tokens;
32500
+ const outputTokens = ((_b = rawResponse.usage) === null || _b === void 0 ? void 0 : _b.completion_tokens) || 0;
32501
+ let isUncertain = false;
32502
+ let modelInfo = OPENAI_MODELS.find((model) => model.modelName === rawResponse.model);
32503
+ if (modelInfo === undefined) {
32504
+ // Note: Model is not in the list of known models, fallback to the family of the models and mark price as uncertain
32505
+ modelInfo = OPENAI_MODELS.find((model) => (rawResponse.model || SALT_NONCE).startsWith(model.modelName));
32506
+ if (modelInfo !== undefined) {
32507
+ isUncertain = true;
32508
+ }
32509
+ }
32510
+ let price;
32511
+ if (modelInfo === undefined || modelInfo.pricing === undefined) {
32512
+ price = uncertainNumber();
32513
+ }
32514
+ else {
32515
+ price = uncertainNumber(inputTokens * modelInfo.pricing.prompt + outputTokens * modelInfo.pricing.output, isUncertain);
32516
+ }
32517
+ return {
32518
+ price,
32519
+ duration,
32520
+ input: {
32521
+ tokensCount: uncertainNumber(rawResponse.usage.prompt_tokens),
32522
+ ...computeUsageCounts(promptContent),
32523
+ },
32524
+ output: {
32525
+ tokensCount: uncertainNumber(outputTokens),
32526
+ ...computeUsageCounts(resultContent),
32527
+ },
32528
+ };
32529
+ }
32530
+ // TODO: [🤝] DRY Maybe some common abstraction between `computeOpenAiUsage` and `computeAnthropicClaudeUsage`
32531
+
32110
32532
  /**
32111
32533
  * Simple wrapper `new Date().toISOString()`
32112
32534
  *
@@ -32515,78 +32937,6 @@
32515
32937
  }
32516
32938
  }
32517
32939
 
32518
- /**
32519
- * Helper of usage compute
32520
- *
32521
- * @param content the content of prompt or response
32522
- * @returns part of UsageCounts
32523
- *
32524
- * @private internal utility of LlmExecutionTools
32525
- */
32526
- function computeUsageCounts(content) {
32527
- return {
32528
- charactersCount: { value: countCharacters(content) },
32529
- wordsCount: { value: countWords(content) },
32530
- sentencesCount: { value: countSentences(content) },
32531
- linesCount: { value: countLines(content) },
32532
- paragraphsCount: { value: countParagraphs(content) },
32533
- pagesCount: { value: countPages(content) },
32534
- };
32535
- }
32536
-
32537
- /**
32538
- * Computes the usage of the OpenAI API based on the response from OpenAI
32539
- *
32540
- * @param promptContent The content of the prompt
32541
- * @param resultContent The content of the result (for embedding prompts or failed prompts pass empty string)
32542
- * @param rawResponse The raw response from OpenAI API
32543
- * @param duration The duration of the execution
32544
- * @throws {PipelineExecutionError} If the usage is not defined in the response from OpenAI
32545
- *
32546
- * @private internal utility of `OpenAiExecutionTools`
32547
- */
32548
- function computeOpenAiUsage(promptContent, // <- Note: Intentionally using [] to access type properties to bring jsdoc from Prompt/PromptResult to consumer
32549
- resultContent, rawResponse, duration = ZERO_VALUE) {
32550
- var _a, _b;
32551
- if (rawResponse.usage === undefined) {
32552
- throw new PipelineExecutionError('The usage is not defined in the response from OpenAI');
32553
- }
32554
- if (((_a = rawResponse.usage) === null || _a === void 0 ? void 0 : _a.prompt_tokens) === undefined) {
32555
- throw new PipelineExecutionError('In OpenAI response `usage.prompt_tokens` not defined');
32556
- }
32557
- const inputTokens = rawResponse.usage.prompt_tokens;
32558
- const outputTokens = ((_b = rawResponse.usage) === null || _b === void 0 ? void 0 : _b.completion_tokens) || 0;
32559
- let isUncertain = false;
32560
- let modelInfo = OPENAI_MODELS.find((model) => model.modelName === rawResponse.model);
32561
- if (modelInfo === undefined) {
32562
- // Note: Model is not in the list of known models, fallback to the family of the models and mark price as uncertain
32563
- modelInfo = OPENAI_MODELS.find((model) => (rawResponse.model || SALT_NONCE).startsWith(model.modelName));
32564
- if (modelInfo !== undefined) {
32565
- isUncertain = true;
32566
- }
32567
- }
32568
- let price;
32569
- if (modelInfo === undefined || modelInfo.pricing === undefined) {
32570
- price = uncertainNumber();
32571
- }
32572
- else {
32573
- price = uncertainNumber(inputTokens * modelInfo.pricing.prompt + outputTokens * modelInfo.pricing.output, isUncertain);
32574
- }
32575
- return {
32576
- price,
32577
- duration,
32578
- input: {
32579
- tokensCount: uncertainNumber(rawResponse.usage.prompt_tokens),
32580
- ...computeUsageCounts(promptContent),
32581
- },
32582
- output: {
32583
- tokensCount: uncertainNumber(outputTokens),
32584
- ...computeUsageCounts(resultContent),
32585
- },
32586
- };
32587
- }
32588
- // TODO: [🤝] DRY Maybe some common abstraction between `computeOpenAiUsage` and `computeAnthropicClaudeUsage`
32589
-
32590
32940
  /**
32591
32941
  * Finds the best hardcoded-model match for one API-listed model identifier.
32592
32942
  */
@@ -43044,18 +43394,6 @@
43044
43394
  const $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
43045
43395
  // TODO: [®] DRY Register logic
43046
43396
 
43047
- /**
43048
- * Register for LLM tools.
43049
- *
43050
- * Note: `$` is used to indicate that this interacts with the global scope
43051
- *
43052
- * @singleton Only one instance of each register is created per build, but there can be more instances across different builds or environments.
43053
- *
43054
- * @public exported from `@promptbook/core`
43055
- */
43056
- const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
43057
- // TODO: [®] DRY Register logic
43058
-
43059
43397
  /**
43060
43398
  * Path to the `.env` file which was used to configure LLM tools
43061
43399
  *
@@ -43063,61 +43401,53 @@
43063
43401
  */
43064
43402
  let $usedEnvFilename = null;
43065
43403
  /**
43066
- * Pass the `.env` file which was used to configure LLM tools
43067
- *
43068
- * Note: `$` is used to indicate that this variable is making side effect
43069
- *
43070
- * @private internal log of `$provideLlmToolsConfigurationFromEnv` and `$registeredLlmToolsMessage`
43071
- */
43072
- function $setUsedEnvFilename(filepath) {
43073
- $usedEnvFilename = filepath;
43074
- }
43075
- /**
43076
- * Creates a message with all registered LLM tools
43077
- *
43078
- * Note: This function is used to create a (error) message when there is no constructor for some LLM provider
43079
- *
43080
- * @private internal function of `createLlmToolsFromConfiguration` and `$provideLlmToolsFromEnv`
43081
- */
43082
- function $registeredLlmToolsMessage() {
43083
- const registeredLlmToolsMessageContext = createRegisteredLlmToolsMessageContext();
43084
- if (registeredLlmToolsMessageContext.llmToolStatuses.length === 0) {
43085
- return renderNoRegisteredLlmToolsMessage(registeredLlmToolsMessageContext.usedEnvMessage);
43086
- }
43087
- return renderRegisteredLlmToolsMessage(registeredLlmToolsMessageContext);
43088
- }
43089
- /**
43090
- * Collects all state needed to render the provider summary.
43404
+ * Shared state for the `.env` file used to configure LLM tools.
43091
43405
  *
43092
- * @private internal function of `$registeredLlmToolsMessage`
43406
+ * @private internal state of `$registeredLlmToolsMessage`
43093
43407
  */
43094
- function createRegisteredLlmToolsMessageContext() {
43095
- const isRunningInNode = $isRunningInNode();
43096
- const env = getAvailableEnvironmentVariables(isRunningInNode);
43097
- const registeredLlmToolRegisters = getRegisteredLlmToolRegisters();
43098
- return {
43099
- env,
43100
- llmToolStatuses: listRegisteredLlmToolStatuses(registeredLlmToolRegisters, env),
43101
- usedEnvMessage: createUsedEnvMessage(),
43102
- isRunningInNode,
43103
- };
43104
- }
43408
+ const USED_ENV_FILENAME = {
43409
+ set(filepath) {
43410
+ $usedEnvFilename = filepath;
43411
+ },
43412
+ createMessage() {
43413
+ return $usedEnvFilename === null
43414
+ ? `Unknown \`.env\` file`
43415
+ : _spaceTrim.spaceTrim(`
43416
+ Used \`.env\` file:
43417
+ ${$usedEnvFilename}
43418
+ `);
43419
+ },
43420
+ };
43421
+
43105
43422
  /**
43106
43423
  * Reads environment variables relevant for provider configuration.
43107
43424
  *
43108
- * @private internal function of `$registeredLlmToolsMessage`
43425
+ * @private function of `$registeredLlmToolsMessage`
43109
43426
  */
43110
- function getAvailableEnvironmentVariables(isRunningInNode) {
43427
+ function getAvailableRegisteredLlmToolsEnvironmentVariables(isRunningInNode) {
43111
43428
  if (isRunningInNode) {
43112
43429
  return process.env;
43113
43430
  // <- TODO: [⚛] Some DRY way how to get to `process.env` and pass it into functions - ACRY search for `env`
43114
43431
  }
43115
43432
  return {};
43116
43433
  }
43434
+
43435
+ /**
43436
+ * Register for LLM tools.
43437
+ *
43438
+ * Note: `$` is used to indicate that this interacts with the global scope
43439
+ *
43440
+ * @singleton Only one instance of each register is created per build, but there can be more instances across different builds or environments.
43441
+ *
43442
+ * @public exported from `@promptbook/core`
43443
+ */
43444
+ const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
43445
+ // TODO: [®] DRY Register logic
43446
+
43117
43447
  /**
43118
43448
  * Takes stable snapshots of both LLM provider registers.
43119
43449
  *
43120
- * @private internal function of `$registeredLlmToolsMessage`
43450
+ * @private function of `$registeredLlmToolsMessage`
43121
43451
  */
43122
43452
  function getRegisteredLlmToolRegisters() {
43123
43453
  return {
@@ -43125,62 +43455,30 @@
43125
43455
  registeredTools: $llmToolsRegister.list(),
43126
43456
  };
43127
43457
  }
43128
- /**
43129
- * Lists provider entries enriched with installation and configuration state.
43130
- *
43131
- * @private internal function of `$registeredLlmToolsMessage`
43132
- */
43133
- function listRegisteredLlmToolStatuses({ registeredMetadata, registeredTools }, env) {
43134
- return listRegisteredLlmToolEntries(registeredMetadata, registeredTools).map((registeredLlmToolEntry) => createRegisteredLlmToolStatus({ registeredLlmToolEntry, registeredMetadata, registeredTools, env }));
43135
- }
43136
- /**
43137
- * Merges provider entries from the metadata and constructor registers.
43138
- *
43139
- * @private internal function of `$registeredLlmToolsMessage`
43140
- */
43141
- function listRegisteredLlmToolEntries(registeredMetadata, registeredTools) {
43142
- const registeredLlmToolEntries = new Map();
43143
- for (const { title, packageName, className, envVariables } of registeredMetadata) {
43144
- addRegisteredLlmToolEntry(registeredLlmToolEntries, { title, packageName, className, envVariables });
43145
- }
43146
- for (const { packageName, className } of registeredTools) {
43147
- addRegisteredLlmToolEntry(registeredLlmToolEntries, { packageName, className });
43148
- }
43149
- return [...registeredLlmToolEntries.values()];
43150
- }
43151
- /**
43152
- * Adds a provider entry only when the provider is not already present.
43153
- *
43154
- * @private internal function of `$registeredLlmToolsMessage`
43155
- */
43156
- function addRegisteredLlmToolEntry(registeredLlmToolEntries, registeredLlmToolEntry) {
43157
- const registeredLlmToolEntryKey = createRegisteredLlmToolEntryKey(registeredLlmToolEntry);
43158
- if (registeredLlmToolEntries.has(registeredLlmToolEntryKey)) {
43159
- return;
43160
- }
43161
- registeredLlmToolEntries.set(registeredLlmToolEntryKey, registeredLlmToolEntry);
43162
- }
43458
+
43163
43459
  /**
43164
43460
  * Creates a deduplication key for a provider entry.
43165
43461
  *
43166
- * @private internal function of `$registeredLlmToolsMessage`
43462
+ * @private function of `$registeredLlmToolsMessage`
43167
43463
  */
43168
43464
  function createRegisteredLlmToolEntryKey({ packageName, className }) {
43169
43465
  return `${packageName}::${className}`;
43170
43466
  }
43467
+
43171
43468
  /**
43172
43469
  * Checks whether the given provider entry already exists in the target register list.
43173
43470
  *
43174
- * @private internal function of `$registeredLlmToolsMessage`
43471
+ * @private function of `$registeredLlmToolsMessage`
43175
43472
  */
43176
43473
  function hasRegisteredLlmToolEntry(registeredLlmToolEntries, registeredLlmToolEntry) {
43177
43474
  const registeredLlmToolEntryKey = createRegisteredLlmToolEntryKey(registeredLlmToolEntry);
43178
43475
  return registeredLlmToolEntries.some((listedRegisteredLlmToolEntry) => createRegisteredLlmToolEntryKey(listedRegisteredLlmToolEntry) === registeredLlmToolEntryKey);
43179
43476
  }
43477
+
43180
43478
  /**
43181
43479
  * Builds the runtime status for one provider entry.
43182
43480
  *
43183
- * @private internal function of `$registeredLlmToolsMessage`
43481
+ * @private function of `$registeredLlmToolsMessage`
43184
43482
  */
43185
43483
  function createRegisteredLlmToolStatus({ registeredLlmToolEntry, registeredMetadata, registeredTools, env, }) {
43186
43484
  const availabilityStatus = createRegisteredLlmToolAvailabilityStatus(registeredLlmToolEntry, registeredMetadata, registeredTools);
@@ -43194,7 +43492,7 @@
43194
43492
  /**
43195
43493
  * Resolves whether the provider has metadata and a registered constructor.
43196
43494
  *
43197
- * @private internal function of `$registeredLlmToolsMessage`
43495
+ * @private function of `$registeredLlmToolsMessage`
43198
43496
  */
43199
43497
  function createRegisteredLlmToolAvailabilityStatus(registeredLlmToolEntry, registeredMetadata, registeredTools) {
43200
43498
  return {
@@ -43205,7 +43503,7 @@
43205
43503
  /**
43206
43504
  * Resolves whether the provider has enough environment variables to be configured.
43207
43505
  *
43208
- * @private internal function of `$registeredLlmToolsMessage`
43506
+ * @private function of `$registeredLlmToolsMessage`
43209
43507
  */
43210
43508
  function createRegisteredLlmToolConfigurationStatus({ envVariables }, env) {
43211
43509
  return {
@@ -43216,7 +43514,7 @@
43216
43514
  /**
43217
43515
  * Checks whether all required environment variables are present for a provider.
43218
43516
  *
43219
- * @private internal function of `$registeredLlmToolsMessage`
43517
+ * @private function of `$registeredLlmToolsMessage`
43220
43518
  */
43221
43519
  function isRegisteredLlmToolFullyConfigured(envVariables, env) {
43222
43520
  if (envVariables === undefined || envVariables === null) {
@@ -43227,7 +43525,7 @@
43227
43525
  /**
43228
43526
  * Checks whether at least one required environment variable is present for a provider.
43229
43527
  *
43230
- * @private internal function of `$registeredLlmToolsMessage`
43528
+ * @private function of `$registeredLlmToolsMessage`
43231
43529
  */
43232
43530
  function isRegisteredLlmToolPartiallyConfigured(envVariables, env) {
43233
43531
  if (envVariables === undefined || envVariables === null) {
@@ -43235,109 +43533,128 @@
43235
43533
  }
43236
43534
  return envVariables.some((envVariableName) => env[envVariableName] !== undefined);
43237
43535
  }
43536
+
43238
43537
  /**
43239
- * Renders the fallback message for environments with no registered providers.
43538
+ * Merges provider entries from the metadata and constructor registers.
43240
43539
  *
43241
- * @private internal function of `$registeredLlmToolsMessage`
43540
+ * @private function of `$registeredLlmToolsMessage`
43242
43541
  */
43243
- function renderNoRegisteredLlmToolsMessage(usedEnvMessage) {
43244
- return _spaceTrim.spaceTrim((block) => `
43245
- No LLM providers are available.
43246
-
43247
- ${block(usedEnvMessage)}
43248
- `);
43542
+ function listRegisteredLlmToolEntries(registeredMetadata, registeredTools) {
43543
+ const registeredLlmToolEntries = new Map();
43544
+ for (const { title, packageName, className, envVariables } of registeredMetadata) {
43545
+ addRegisteredLlmToolEntry(registeredLlmToolEntries, { title, packageName, className, envVariables });
43546
+ }
43547
+ for (const { packageName, className } of registeredTools) {
43548
+ addRegisteredLlmToolEntry(registeredLlmToolEntries, { packageName, className });
43549
+ }
43550
+ return [...registeredLlmToolEntries.values()];
43249
43551
  }
43250
43552
  /**
43251
- * Renders the full provider status summary.
43553
+ * Adds a provider entry only when the provider is not already present.
43252
43554
  *
43253
- * @private internal function of `$registeredLlmToolsMessage`
43555
+ * @private function of `$registeredLlmToolsMessage`
43254
43556
  */
43255
- function renderRegisteredLlmToolsMessage({ env, llmToolStatuses, usedEnvMessage, isRunningInNode, }) {
43256
- return _spaceTrim.spaceTrim((block) => `
43257
-
43258
- ${block(usedEnvMessage)}
43259
-
43260
- Relevant environment variables:
43261
- ${block(renderRelevantEnvironmentVariables(env, llmToolStatuses))}
43262
-
43263
- Available LLM providers are:
43264
- ${block(renderAvailableProviders(llmToolStatuses, env, isRunningInNode))}
43265
- `);
43557
+ function addRegisteredLlmToolEntry(registeredLlmToolEntries, registeredLlmToolEntry) {
43558
+ const registeredLlmToolEntryKey = createRegisteredLlmToolEntryKey(registeredLlmToolEntry);
43559
+ if (registeredLlmToolEntries.has(registeredLlmToolEntryKey)) {
43560
+ return;
43561
+ }
43562
+ registeredLlmToolEntries.set(registeredLlmToolEntryKey, registeredLlmToolEntry);
43266
43563
  }
43564
+
43267
43565
  /**
43268
- * Renders the list of environment variables used by at least one registered provider.
43566
+ * Lists provider entries enriched with installation and configuration state.
43269
43567
  *
43270
- * @private internal function of `$registeredLlmToolsMessage`
43568
+ * @private function of `$registeredLlmToolsMessage`
43271
43569
  */
43272
- function renderRelevantEnvironmentVariables(env, llmToolStatuses) {
43273
- return listRelevantEnvironmentVariables(env, llmToolStatuses)
43274
- .map((envVariableName) => `- \`${envVariableName}\``)
43275
- .join('\n');
43570
+ function listRegisteredLlmToolStatuses({ registeredMetadata, registeredTools }, env) {
43571
+ return listRegisteredLlmToolEntries(registeredMetadata, registeredTools).map((registeredLlmToolEntry) => createRegisteredLlmToolStatus({ registeredLlmToolEntry, registeredMetadata, registeredTools, env }));
43276
43572
  }
43573
+
43277
43574
  /**
43278
- * Lists environment variables that are both present and used by at least one provider.
43575
+ * Collects all state needed to render the provider summary.
43279
43576
  *
43280
- * @private internal function of `$registeredLlmToolsMessage`
43577
+ * @private function of `$registeredLlmToolsMessage`
43281
43578
  */
43282
- function listRelevantEnvironmentVariables(env, llmToolStatuses) {
43283
- return Object.keys(env).filter((envVariableName) => llmToolStatuses.some(({ envVariables }) => envVariables === null || envVariables === void 0 ? void 0 : envVariables.includes(envVariableName)));
43579
+ function createRegisteredLlmToolsMessageContext() {
43580
+ const isRunningInNode = $isRunningInNode();
43581
+ const env = getAvailableRegisteredLlmToolsEnvironmentVariables(isRunningInNode);
43582
+ const registeredLlmToolRegisters = getRegisteredLlmToolRegisters();
43583
+ return {
43584
+ env,
43585
+ llmToolStatuses: listRegisteredLlmToolStatuses(registeredLlmToolRegisters, env),
43586
+ usedEnvMessage: USED_ENV_FILENAME.createMessage(),
43587
+ isRunningInNode,
43588
+ };
43284
43589
  }
43590
+
43285
43591
  /**
43286
- * Renders all provider lines in the status summary.
43592
+ * Renders the fallback message for environments with no registered providers.
43287
43593
  *
43288
- * @private internal function of `$registeredLlmToolsMessage`
43594
+ * @private function of `$registeredLlmToolsMessage`
43289
43595
  */
43290
- function renderAvailableProviders(llmToolStatuses, env, isRunningInNode) {
43291
- return llmToolStatuses
43292
- .map((llmToolStatus, index) => renderAvailableProvider(llmToolStatus, index, env, isRunningInNode))
43293
- .join('\n');
43596
+ function renderNoRegisteredLlmToolsMessage(usedEnvMessage) {
43597
+ return _spaceTrim.spaceTrim((block) => `
43598
+ No LLM providers are available.
43599
+
43600
+ ${block(usedEnvMessage)}
43601
+ `);
43294
43602
  }
43603
+
43295
43604
  /**
43296
- * Renders one provider line in the status summary.
43605
+ * Creates the configuration-status sentence for one provider.
43297
43606
  *
43298
- * @private internal function of `$registeredLlmToolsMessage`
43607
+ * @private function of `$registeredLlmToolsMessage`
43299
43608
  */
43300
- function renderAvailableProvider(llmToolStatus, index, env, isRunningInNode) {
43301
- const providerMessage = createAvailableProviderMessage(llmToolStatus, index, env);
43302
- if (!isRunningInNode) {
43303
- return providerMessage;
43609
+ function createRegisteredLlmToolConfigurationStatusMessage({ envVariables, isFullyConfigured, isPartiallyConfigured }, env) {
43610
+ if (isFullyConfigured) {
43611
+ return `Configured`;
43612
+ }
43613
+ if (isPartiallyConfigured) {
43614
+ return createPartiallyConfiguredStatusMessage(envVariables, env);
43615
+ }
43616
+ if (envVariables !== null) {
43617
+ return createNotConfiguredStatusMessage(envVariables);
43304
43618
  }
43305
- return colorizeAvailableProviderMessage(providerMessage, llmToolStatus);
43619
+ return `Not configured`;
43620
+ // <- Note: Can not be configured via environment variables
43306
43621
  }
43307
43622
  /**
43308
- * Creates the plain-text provider line before optional colorization.
43623
+ * Creates the partial-configuration sentence including missing variables.
43309
43624
  *
43310
- * @private internal function of `$registeredLlmToolsMessage`
43625
+ * @private function of `$registeredLlmToolsMessage`
43311
43626
  */
43312
- function createAvailableProviderMessage(llmToolStatus, index, env) {
43313
- const { title, packageName, className } = llmToolStatus;
43314
- const providerStatusMessages = createProviderStatusMessages(llmToolStatus, env);
43315
- return _spaceTrim.spaceTrim(`
43316
- ${index + 1}) **${title}** \`${className}\` from \`${packageName}\`
43317
- ${providerStatusMessages.join('; ')}
43318
- `);
43627
+ function createPartiallyConfiguredStatusMessage(envVariables, env) {
43628
+ return `Partially confugured, missing ${listMissingEnvironmentVariables(envVariables, env).join(' + ')}`;
43319
43629
  }
43320
43630
  /**
43321
- * Creates the install and configuration fragments for one provider line.
43631
+ * Creates the not-configured sentence including the expected environment variables.
43322
43632
  *
43323
- * @private internal function of `$registeredLlmToolsMessage`
43633
+ * @private function of `$registeredLlmToolsMessage`
43324
43634
  */
43325
- function createProviderStatusMessages(llmToolStatus, env) {
43326
- return [createInstallationStatusMessage(llmToolStatus), createConfigurationStatusMessage(llmToolStatus, env)];
43635
+ function createNotConfiguredStatusMessage(envVariables) {
43636
+ return `Not configured, to configure set env ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.join(' + ')}`;
43637
+ }
43638
+ /**
43639
+ * Lists environment variables that are required but currently missing.
43640
+ *
43641
+ * @private function of `$registeredLlmToolsMessage`
43642
+ */
43643
+ function listMissingEnvironmentVariables(envVariables, env) {
43644
+ return (envVariables === null || envVariables === void 0 ? void 0 : envVariables.filter((envVariable) => env[envVariable] === undefined)) || [];
43327
43645
  }
43646
+
43328
43647
  /**
43329
43648
  * Creates the installation-status sentence for one provider.
43330
43649
  *
43331
- * @private internal function of `$registeredLlmToolsMessage`
43650
+ * @private function of `$registeredLlmToolsMessage`
43332
43651
  */
43333
- function createInstallationStatusMessage({ isMetadataAvailable, isInstalled, }) {
43334
- const installationStatusKey = createInstallationStatusKey(isMetadataAvailable, isInstalled);
43652
+ function createRegisteredLlmToolInstallationStatusMessage({ isMetadataAvailable, isInstalled, }) {
43653
+ const installationStatusKey = createRegisteredLlmToolInstallationStatusKey(isMetadataAvailable, isInstalled);
43335
43654
  switch (installationStatusKey) {
43336
43655
  case 'missing-metadata-and-installation':
43337
- // TODO: [�][�] Maybe do allow to do auto-install if package not registered and not found
43338
43656
  return `Not installed and no metadata, looks like a unexpected behavior`;
43339
43657
  case 'metadata-without-installation':
43340
- // TODO: [�][�]
43341
43658
  return `Not installed`;
43342
43659
  case 'installation-without-metadata':
43343
43660
  return `No metadata but installed, looks like a unexpected behavior`;
@@ -43350,9 +43667,9 @@
43350
43667
  /**
43351
43668
  * Creates a stable installation-state key from metadata and constructor availability.
43352
43669
  *
43353
- * @private internal function of `$registeredLlmToolsMessage`
43670
+ * @private function of `$registeredLlmToolsMessage`
43354
43671
  */
43355
- function createInstallationStatusKey(isMetadataAvailable, isInstalled) {
43672
+ function createRegisteredLlmToolInstallationStatusKey(isMetadataAvailable, isInstalled) {
43356
43673
  if (!isMetadataAvailable && !isInstalled) {
43357
43674
  return 'missing-metadata-and-installation';
43358
43675
  }
@@ -43367,54 +43684,59 @@
43367
43684
  }
43368
43685
  return 'unknown';
43369
43686
  }
43687
+
43370
43688
  /**
43371
- * Creates the configuration-status sentence for one provider.
43689
+ * Renders all provider lines in the status summary.
43372
43690
  *
43373
- * @private internal function of `$registeredLlmToolsMessage`
43691
+ * @private function of `$registeredLlmToolsMessage`
43374
43692
  */
43375
- function createConfigurationStatusMessage({ envVariables, isFullyConfigured, isPartiallyConfigured }, env) {
43376
- if (isFullyConfigured) {
43377
- return `Configured`;
43378
- }
43379
- if (isPartiallyConfigured) {
43380
- return createPartiallyConfiguredStatusMessage(envVariables, env);
43381
- }
43382
- if (envVariables !== null) {
43383
- return createNotConfiguredStatusMessage(envVariables);
43384
- }
43385
- return `Not configured`;
43386
- // <- Note: Can not be configured via environment variables
43693
+ function renderAvailableRegisteredLlmTools(llmToolStatuses, env, isRunningInNode) {
43694
+ return llmToolStatuses
43695
+ .map((llmToolStatus, index) => renderAvailableRegisteredLlmTool(llmToolStatus, index, env, isRunningInNode))
43696
+ .join('\n');
43387
43697
  }
43388
43698
  /**
43389
- * Creates the partial-configuration sentence including missing variables.
43699
+ * Renders one provider line in the status summary.
43390
43700
  *
43391
- * @private internal function of `$registeredLlmToolsMessage`
43701
+ * @private function of `$registeredLlmToolsMessage`
43392
43702
  */
43393
- function createPartiallyConfiguredStatusMessage(envVariables, env) {
43394
- return `Partially confugured, missing ${listMissingEnvironmentVariables(envVariables, env).join(' + ')}`;
43703
+ function renderAvailableRegisteredLlmTool(llmToolStatus, index, env, isRunningInNode) {
43704
+ const providerMessage = createAvailableRegisteredLlmToolMessage(llmToolStatus, index, env);
43705
+ if (!isRunningInNode) {
43706
+ return providerMessage;
43707
+ }
43708
+ return colorizeAvailableRegisteredLlmToolMessage(providerMessage, llmToolStatus);
43395
43709
  }
43396
43710
  /**
43397
- * Creates the not-configured sentence including the expected environment variables.
43711
+ * Creates the plain-text provider line before optional colorization.
43398
43712
  *
43399
- * @private internal function of `$registeredLlmToolsMessage`
43713
+ * @private function of `$registeredLlmToolsMessage`
43400
43714
  */
43401
- function createNotConfiguredStatusMessage(envVariables) {
43402
- return `Not configured, to configure set env ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.join(' + ')}`;
43715
+ function createAvailableRegisteredLlmToolMessage(llmToolStatus, index, env) {
43716
+ const { title, packageName, className } = llmToolStatus;
43717
+ const providerStatusMessages = createRegisteredLlmToolStatusMessages(llmToolStatus, env);
43718
+ return _spaceTrim.spaceTrim(`
43719
+ ${index + 1}) **${title}** \`${className}\` from \`${packageName}\`
43720
+ ${providerStatusMessages.join('; ')}
43721
+ `);
43403
43722
  }
43404
43723
  /**
43405
- * Lists environment variables that are required but currently missing.
43724
+ * Creates the install and configuration fragments for one provider line.
43406
43725
  *
43407
- * @private internal function of `$registeredLlmToolsMessage`
43726
+ * @private function of `$registeredLlmToolsMessage`
43408
43727
  */
43409
- function listMissingEnvironmentVariables(envVariables, env) {
43410
- return (envVariables === null || envVariables === void 0 ? void 0 : envVariables.filter((envVariable) => env[envVariable] === undefined)) || [];
43728
+ function createRegisteredLlmToolStatusMessages(llmToolStatus, env) {
43729
+ return [
43730
+ createRegisteredLlmToolInstallationStatusMessage(llmToolStatus),
43731
+ createRegisteredLlmToolConfigurationStatusMessage(llmToolStatus, env),
43732
+ ];
43411
43733
  }
43412
43734
  /**
43413
43735
  * Applies the same terminal coloring rules as the original summary renderer.
43414
43736
  *
43415
- * @private internal function of `$registeredLlmToolsMessage`
43737
+ * @private function of `$registeredLlmToolsMessage`
43416
43738
  */
43417
- function colorizeAvailableProviderMessage(providerMessage, { isInstalled, isFullyConfigured, isPartiallyConfigured }) {
43739
+ function colorizeAvailableRegisteredLlmToolMessage(providerMessage, { isInstalled, isFullyConfigured, isPartiallyConfigured }) {
43418
43740
  if (isInstalled && isFullyConfigured) {
43419
43741
  return colors__default["default"].green(providerMessage);
43420
43742
  }
@@ -43423,18 +43745,67 @@
43423
43745
  }
43424
43746
  return colors__default["default"].gray(providerMessage);
43425
43747
  }
43748
+
43426
43749
  /**
43427
- * Creates the `.env` origin sentence used in the summary header.
43750
+ * Renders the list of environment variables used by at least one registered provider.
43428
43751
  *
43429
- * @private internal function of `$registeredLlmToolsMessage`
43752
+ * @private function of `$registeredLlmToolsMessage`
43430
43753
  */
43431
- function createUsedEnvMessage() {
43432
- return $usedEnvFilename === null
43433
- ? `Unknown \`.env\` file`
43434
- : _spaceTrim.spaceTrim(`
43435
- Used \`.env\` file:
43436
- ${$usedEnvFilename}
43437
- `);
43754
+ function renderRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses) {
43755
+ return listRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses)
43756
+ .map((envVariableName) => `- \`${envVariableName}\``)
43757
+ .join('\n');
43758
+ }
43759
+ /**
43760
+ * Lists environment variables that are both present and used by at least one provider.
43761
+ *
43762
+ * @private function of `$registeredLlmToolsMessage`
43763
+ */
43764
+ function listRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses) {
43765
+ return Object.keys(env).filter((envVariableName) => llmToolStatuses.some(({ envVariables }) => envVariables === null || envVariables === void 0 ? void 0 : envVariables.includes(envVariableName)));
43766
+ }
43767
+
43768
+ /**
43769
+ * Renders the full provider status summary.
43770
+ *
43771
+ * @private function of `$registeredLlmToolsMessage`
43772
+ */
43773
+ function renderRegisteredLlmToolsMessage({ env, llmToolStatuses, usedEnvMessage, isRunningInNode, }) {
43774
+ return _spaceTrim.spaceTrim((block) => `
43775
+
43776
+ ${block(usedEnvMessage)}
43777
+
43778
+ Relevant environment variables:
43779
+ ${block(renderRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses))}
43780
+
43781
+ Available LLM providers are:
43782
+ ${block(renderAvailableRegisteredLlmTools(llmToolStatuses, env, isRunningInNode))}
43783
+ `);
43784
+ }
43785
+
43786
+ /**
43787
+ * Pass the `.env` file which was used to configure LLM tools
43788
+ *
43789
+ * Note: `$` is used to indicate that this variable is making side effect
43790
+ *
43791
+ * @private internal log of `$provideLlmToolsConfigurationFromEnv` and `$registeredLlmToolsMessage`
43792
+ */
43793
+ function $setUsedEnvFilename(filepath) {
43794
+ USED_ENV_FILENAME.set(filepath);
43795
+ }
43796
+ /**
43797
+ * Creates a message with all registered LLM tools
43798
+ *
43799
+ * Note: This function is used to create a (error) message when there is no constructor for some LLM provider
43800
+ *
43801
+ * @private internal function of `createLlmToolsFromConfiguration` and `$provideLlmToolsFromEnv`
43802
+ */
43803
+ function $registeredLlmToolsMessage() {
43804
+ const registeredLlmToolsMessageContext = createRegisteredLlmToolsMessageContext();
43805
+ if (registeredLlmToolsMessageContext.llmToolStatuses.length === 0) {
43806
+ return renderNoRegisteredLlmToolsMessage(registeredLlmToolsMessageContext.usedEnvMessage);
43807
+ }
43808
+ return renderRegisteredLlmToolsMessage(registeredLlmToolsMessageContext);
43438
43809
  }
43439
43810
  // TODO: [®] DRY Register logic
43440
43811
  // TODO: [🧠][⚛] Maybe pass env as argument