@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/esm/index.es.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { join, dirname, basename, isAbsolute, resolve, relative } from 'path';
2
2
  import { unlink, mkdir, appendFile, writeFile, readFile, stat, access, constants, readdir, watch, rm } from 'fs/promises';
3
+ import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
3
4
  import colors from 'colors';
4
5
  import { spawn } from 'child_process';
5
- import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
6
6
  import { fileSearchTool, tool, Agent as Agent$1, webSearchTool, run, setDefaultOpenAIClient, setDefaultOpenAIKey } from '@openai/agents';
7
7
  import { fileURLToPath } from 'url';
8
8
  import { Readability } from '@mozilla/readability';
@@ -36,7 +36,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
36
36
  * @generated
37
37
  * @see https://github.com/webgptorg/promptbook
38
38
  */
39
- const PROMPTBOOK_ENGINE_VERSION = '0.113.0-1';
39
+ const PROMPTBOOK_ENGINE_VERSION = '0.113.0-10';
40
40
  /**
41
41
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
42
42
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -381,6 +381,34 @@ function $randomToken(randomness) {
381
381
  // TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
382
382
  // TODO: Maybe use nanoid instead https://github.com/ai/nanoid
383
383
 
384
+ /**
385
+ * Increase the level of all headings in the markdown content
386
+ *
387
+ * @example h1 -> h2, h2 -> h3, ...
388
+ * @param content The markdown content
389
+ * @returns The markdown with increased headings
390
+ */
391
+ function increaseHeadings(content) {
392
+ const lines = content.split(/\r?\n/);
393
+ let inCodeBlock = false;
394
+ for (let i = 0; i < lines.length; i++) {
395
+ const line = lines[i];
396
+ if (line.startsWith('```')) {
397
+ inCodeBlock = !inCodeBlock;
398
+ }
399
+ if (inCodeBlock) {
400
+ continue;
401
+ }
402
+ const headingMatch = line.match(/^(#+)(.*)/);
403
+ if (headingMatch) {
404
+ const headingLevel = headingMatch[1].length;
405
+ const headingText = headingMatch[2];
406
+ lines[i] = `${'#'.repeat(headingLevel + 1)}${headingText}`;
407
+ }
408
+ }
409
+ return lines.join('\n');
410
+ }
411
+
384
412
  /**
385
413
  * Appends optional coding context to a runner prompt.
386
414
  */
@@ -393,7 +421,13 @@ function appendCoderContext(prompt, context) {
393
421
  if (normalizedPrompt === '') {
394
422
  return normalizedContext;
395
423
  }
396
- return `${normalizedPrompt}\n\n${normalizedContext}`;
424
+ return spaceTrim$1((block) => `
425
+ ${block(normalizedPrompt)}
426
+
427
+ ## Context
428
+
429
+ ${block(increaseHeadings(normalizedContext))}
430
+ `);
397
431
  }
398
432
 
399
433
  /**
@@ -522,6 +556,127 @@ function take(initialValue) {
522
556
  });
523
557
  }
524
558
 
559
+ /**
560
+ * Validates that a channel value is a valid number within the range of 0 to 255.
561
+ * Throws an error if the value is not valid.
562
+ *
563
+ * @param channelName - The name of the channel being validated.
564
+ * @param value - The value of the channel to validate.
565
+ * @throws Will throw an error if the value is not a valid channel number.
566
+ *
567
+ * @private util of `@promptbook/color`
568
+ */
569
+ function checkChannelValue(channelName, value) {
570
+ if (typeof value !== 'number') {
571
+ throw new Error(`${channelName} channel value is not number but ${typeof value}`);
572
+ }
573
+ if (isNaN(value)) {
574
+ throw new Error(`${channelName} channel value is NaN`);
575
+ }
576
+ if (Math.round(value) !== value) {
577
+ throw new Error(`${channelName} channel is not whole number, it is ${value}`);
578
+ }
579
+ if (value < 0) {
580
+ throw new Error(`${channelName} channel is lower than 0, it is ${value}`);
581
+ }
582
+ if (value > 255) {
583
+ throw new Error(`${channelName} channel is greater than 255, it is ${value}`);
584
+ }
585
+ }
586
+
587
+ /**
588
+ * Shared immutable channel storage and serialization helpers for `Color`.
589
+ *
590
+ * @private base class of Color
591
+ */
592
+ class ColorValue {
593
+ constructor(red, green, blue, alpha = 255) {
594
+ this.red = red;
595
+ this.green = green;
596
+ this.blue = blue;
597
+ this.alpha = alpha;
598
+ checkChannelValue('Red', red);
599
+ checkChannelValue('Green', green);
600
+ checkChannelValue('Blue', blue);
601
+ checkChannelValue('Alpha', alpha);
602
+ }
603
+ /**
604
+ * Shortcut for `red` property
605
+ * Number from 0 to 255
606
+ * @alias red
607
+ */
608
+ get r() {
609
+ return this.red;
610
+ }
611
+ /**
612
+ * Shortcut for `green` property
613
+ * Number from 0 to 255
614
+ * @alias green
615
+ */
616
+ get g() {
617
+ return this.green;
618
+ }
619
+ /**
620
+ * Shortcut for `blue` property
621
+ * Number from 0 to 255
622
+ * @alias blue
623
+ */
624
+ get b() {
625
+ return this.blue;
626
+ }
627
+ /**
628
+ * Shortcut for `alpha` property
629
+ * Number from 0 (transparent) to 255 (opaque)
630
+ * @alias alpha
631
+ */
632
+ get a() {
633
+ return this.alpha;
634
+ }
635
+ /**
636
+ * Shortcut for `alpha` property
637
+ * Number from 0 (transparent) to 255 (opaque)
638
+ * @alias alpha
639
+ */
640
+ get opacity() {
641
+ return this.alpha;
642
+ }
643
+ /**
644
+ * Shortcut for 1-`alpha` property
645
+ */
646
+ get transparency() {
647
+ return 255 - this.alpha;
648
+ }
649
+ clone() {
650
+ return take(this.createColor(this.red, this.green, this.blue, this.alpha));
651
+ }
652
+ toString() {
653
+ return this.toHex();
654
+ }
655
+ toHex() {
656
+ if (this.alpha === 255) {
657
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
658
+ .toString(16)
659
+ .padStart(2, '0')}`;
660
+ }
661
+ else {
662
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
663
+ .toString(16)
664
+ .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
665
+ }
666
+ }
667
+ toRgb() {
668
+ if (this.alpha === 255) {
669
+ return `rgb(${this.red}, ${this.green}, ${this.blue})`;
670
+ }
671
+ else {
672
+ return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
673
+ }
674
+ }
675
+ toHsl() {
676
+ throw new Error(`Getting HSL is not implemented`);
677
+ }
678
+ }
679
+
525
680
  /**
526
681
  * 🎨 List of all 140 color names which are supported by CSS
527
682
  *
@@ -681,127 +836,6 @@ const CSS_COLORS = {
681
836
  };
682
837
  // Note: [💞] Ignore a discrepancy between file name and entity name
683
838
 
684
- /**
685
- * Validates that a channel value is a valid number within the range of 0 to 255.
686
- * Throws an error if the value is not valid.
687
- *
688
- * @param channelName - The name of the channel being validated.
689
- * @param value - The value of the channel to validate.
690
- * @throws Will throw an error if the value is not a valid channel number.
691
- *
692
- * @private util of `@promptbook/color`
693
- */
694
- function checkChannelValue(channelName, value) {
695
- if (typeof value !== 'number') {
696
- throw new Error(`${channelName} channel value is not number but ${typeof value}`);
697
- }
698
- if (isNaN(value)) {
699
- throw new Error(`${channelName} channel value is NaN`);
700
- }
701
- if (Math.round(value) !== value) {
702
- throw new Error(`${channelName} channel is not whole number, it is ${value}`);
703
- }
704
- if (value < 0) {
705
- throw new Error(`${channelName} channel is lower than 0, it is ${value}`);
706
- }
707
- if (value > 255) {
708
- throw new Error(`${channelName} channel is greater than 255, it is ${value}`);
709
- }
710
- }
711
-
712
- /**
713
- * Shared immutable channel storage and serialization helpers for `Color`.
714
- *
715
- * @private base class of Color
716
- */
717
- class ColorValue {
718
- constructor(red, green, blue, alpha = 255) {
719
- this.red = red;
720
- this.green = green;
721
- this.blue = blue;
722
- this.alpha = alpha;
723
- checkChannelValue('Red', red);
724
- checkChannelValue('Green', green);
725
- checkChannelValue('Blue', blue);
726
- checkChannelValue('Alpha', alpha);
727
- }
728
- /**
729
- * Shortcut for `red` property
730
- * Number from 0 to 255
731
- * @alias red
732
- */
733
- get r() {
734
- return this.red;
735
- }
736
- /**
737
- * Shortcut for `green` property
738
- * Number from 0 to 255
739
- * @alias green
740
- */
741
- get g() {
742
- return this.green;
743
- }
744
- /**
745
- * Shortcut for `blue` property
746
- * Number from 0 to 255
747
- * @alias blue
748
- */
749
- get b() {
750
- return this.blue;
751
- }
752
- /**
753
- * Shortcut for `alpha` property
754
- * Number from 0 (transparent) to 255 (opaque)
755
- * @alias alpha
756
- */
757
- get a() {
758
- return this.alpha;
759
- }
760
- /**
761
- * Shortcut for `alpha` property
762
- * Number from 0 (transparent) to 255 (opaque)
763
- * @alias alpha
764
- */
765
- get opacity() {
766
- return this.alpha;
767
- }
768
- /**
769
- * Shortcut for 1-`alpha` property
770
- */
771
- get transparency() {
772
- return 255 - this.alpha;
773
- }
774
- clone() {
775
- return take(this.createColor(this.red, this.green, this.blue, this.alpha));
776
- }
777
- toString() {
778
- return this.toHex();
779
- }
780
- toHex() {
781
- if (this.alpha === 255) {
782
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
783
- .toString(16)
784
- .padStart(2, '0')}`;
785
- }
786
- else {
787
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
788
- .toString(16)
789
- .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
790
- }
791
- }
792
- toRgb() {
793
- if (this.alpha === 255) {
794
- return `rgb(${this.red}, ${this.green}, ${this.blue})`;
795
- }
796
- else {
797
- return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
798
- }
799
- }
800
- toHsl() {
801
- throw new Error(`Getting HSL is not implemented`);
802
- }
803
- }
804
-
805
839
  /**
806
840
  * Checks if the given value is a valid hex color string
807
841
  *
@@ -2775,6 +2809,31 @@ const OPENAI_MODELS = exportJson({
2775
2809
  * Note: [💞] Ignore a discrepancy between file name and entity name
2776
2810
  */
2777
2811
 
2812
+ /**
2813
+ * Pattern that matches durations like "1h", "30m", "5s", "1h30m", "1h30m5s".
2814
+ */
2815
+ /**
2816
+ * Formats a duration in milliseconds into a compact human-readable string.
2817
+ *
2818
+ * Examples: `3600000` → `"1h"`, `90000` → `"1m 30s"`, `5000` → `"5s"`.
2819
+ *
2820
+ * @private internal utility of `ptbk coder run`
2821
+ */
2822
+ function formatDurationMs(ms) {
2823
+ const totalSeconds = Math.ceil(ms / 1000);
2824
+ const hours = Math.floor(totalSeconds / 3600);
2825
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
2826
+ const seconds = totalSeconds % 60;
2827
+ const parts = [];
2828
+ if (hours > 0)
2829
+ parts.push(`${hours}h`);
2830
+ if (minutes > 0)
2831
+ parts.push(`${minutes}m`);
2832
+ if (seconds > 0 || parts.length === 0)
2833
+ parts.push(`${seconds}s`);
2834
+ return parts.join(' ');
2835
+ }
2836
+
2778
2837
  /**
2779
2838
  * Formats one unknown error-like value into readable text for logs and feedback.
2780
2839
  */
@@ -3010,15 +3069,211 @@ const CLAUDE_PROMPT_DELIMITER = 'CLAUDE_PROMPT';
3010
3069
  * Builds the shell script that runs Claude Code with the prompt and coding context.
3011
3070
  */
3012
3071
  function buildClaudeScript(options) {
3072
+ const MODEL_ARGUMENT = options.model ? ` --model ${options.model}` : '';
3013
3073
  const THINKING_LEVEL_ARGUMENT = options.thinkingLevel ? ` --effort ${options.thinkingLevel}` : '';
3074
+ const RESUME_SESSION_ARGUMENT = options.resumeSessionId
3075
+ ? ` --resume ${quoteShellArgument(options.resumeSessionId)}`
3076
+ : '';
3014
3077
  return spaceTrim((block) => `
3015
- claude --allowedTools "Bash,Read,Edit,Write"${THINKING_LEVEL_ARGUMENT} --output-format json --print <<'${CLAUDE_PROMPT_DELIMITER}'
3078
+ 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}'
3016
3079
 
3017
3080
  ${block(options.prompt)}
3018
3081
 
3019
3082
  ${CLAUDE_PROMPT_DELIMITER}
3020
3083
  `);
3021
3084
  }
3085
+ /**
3086
+ * Quotes one shell argument for the generated Bash script.
3087
+ */
3088
+ function quoteShellArgument(value) {
3089
+ return JSON.stringify(value);
3090
+ }
3091
+
3092
+ /**
3093
+ * Parses all JSON lines from Claude Code output while ignoring shell prefixes and stack traces.
3094
+ */
3095
+ function parseClaudeCodeOutputEvents(output) {
3096
+ return output
3097
+ .split(/\r?\n/u)
3098
+ .map(parseClaudeCodeOutputLine)
3099
+ .filter((event) => event !== undefined);
3100
+ }
3101
+ /**
3102
+ * Finds the final Claude Code result event in a stream-json output blob.
3103
+ */
3104
+ function findClaudeCodeResultEvent(output) {
3105
+ return parseClaudeCodeOutputEvents(output).find((event) => event.type === 'result');
3106
+ }
3107
+ /**
3108
+ * Parses one output line when it contains a JSON object.
3109
+ */
3110
+ function parseClaudeCodeOutputLine(line) {
3111
+ const jsonStartIndex = line.indexOf('{');
3112
+ if (jsonStartIndex === -1) {
3113
+ return undefined;
3114
+ }
3115
+ const possibleJson = line.slice(jsonStartIndex).trim();
3116
+ try {
3117
+ const parsed = JSON.parse(possibleJson);
3118
+ if (isClaudeCodeOutputEvent(parsed)) {
3119
+ return parsed;
3120
+ }
3121
+ }
3122
+ catch (_a) {
3123
+ return undefined;
3124
+ }
3125
+ return undefined;
3126
+ }
3127
+ /**
3128
+ * Checks whether an unknown parsed JSON value has the object shape expected from Claude Code events.
3129
+ */
3130
+ function isClaudeCodeOutputEvent(value) {
3131
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3132
+ }
3133
+
3134
+ /**
3135
+ * Number of milliseconds added after Claude's reported reset timestamp before retrying.
3136
+ */
3137
+ const CLAUDE_CODE_SESSION_LIMIT_RESET_BUFFER_MS = 30 * 1000;
3138
+ /**
3139
+ * Fallback delay used when Claude reports a session limit without a machine-readable reset timestamp.
3140
+ */
3141
+ const DEFAULT_CLAUDE_CODE_SESSION_LIMIT_RETRY_DELAY_MS = 10 * 60 * 1000;
3142
+ /**
3143
+ * Extracts a Claude Code session-limit failure from a thrown runner error.
3144
+ */
3145
+ function extractClaudeCodeSessionLimitFromError(error) {
3146
+ return extractClaudeCodeSessionLimitFromOutput(formatUnknownErrorDetails(error));
3147
+ }
3148
+ /**
3149
+ * Extracts a Claude Code session-limit failure from raw stream-json output.
3150
+ */
3151
+ function extractClaudeCodeSessionLimitFromOutput(output) {
3152
+ var _a, _b, _c, _d, _e;
3153
+ const events = parseClaudeCodeOutputEvents(output);
3154
+ const rejectedRateLimitEvent = events.find(isRejectedRateLimitEvent);
3155
+ const sessionLimitResultEvent = events.find(isSessionLimitResultEvent);
3156
+ const sessionLimitTextEvent = events.find(isSessionLimitTextEvent);
3157
+ if (!sessionLimitResultEvent && !sessionLimitTextEvent) {
3158
+ return undefined;
3159
+ }
3160
+ const sessionId = findClaudeCodeSessionId(events);
3161
+ if (!sessionId) {
3162
+ return undefined;
3163
+ }
3164
+ const resetDate = extractResetDate(rejectedRateLimitEvent);
3165
+ 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;
3166
+ const summary = (_e = (_d = extractSessionLimitSummary(sessionLimitResultEvent)) !== null && _d !== void 0 ? _d : extractSessionLimitSummary(sessionLimitTextEvent)) !== null && _e !== void 0 ? _e : 'Claude Code reported a session limit.';
3167
+ return {
3168
+ sessionId,
3169
+ resetDate,
3170
+ rateLimitType,
3171
+ summary,
3172
+ };
3173
+ }
3174
+ /**
3175
+ * Computes how long to wait before trying to resume the Claude Code session.
3176
+ */
3177
+ function getClaudeCodeSessionLimitDelayMs(sessionLimit, nowMs = Date.now()) {
3178
+ if (!sessionLimit.resetDate) {
3179
+ return DEFAULT_CLAUDE_CODE_SESSION_LIMIT_RETRY_DELAY_MS;
3180
+ }
3181
+ return Math.max(0, sessionLimit.resetDate.getTime() - nowMs + CLAUDE_CODE_SESSION_LIMIT_RESET_BUFFER_MS);
3182
+ }
3183
+ /**
3184
+ * Builds the follow-up prompt sent into a resumed Claude Code session after its limit resets.
3185
+ */
3186
+ function buildClaudeCodeSessionResurrectionPrompt(originalPrompt, sessionId) {
3187
+ return spaceTrim((block) => `
3188
+ ## Claude Code session resurrection
3189
+
3190
+ You are resuming Claude Code session \`${sessionId}\` after Claude reported a session limit.
3191
+ Continue the same task from the existing repository and session state.
3192
+ Do not discard useful context, completed analysis, or file changes from the resumed session.
3193
+
3194
+ The original prompt is repeated below as reference in case the resumed session needs it.
3195
+
3196
+ ## Original prompt
3197
+
3198
+ ${block(originalPrompt)}
3199
+ `);
3200
+ }
3201
+ /**
3202
+ * Builds a compact one-line label for logs and terminal UI messages.
3203
+ */
3204
+ function formatClaudeCodeSessionLimitForDisplay(sessionLimit) {
3205
+ const resetSuffix = sessionLimit.resetDate
3206
+ ? `, resets at ${sessionLimit.resetDate.toISOString()}`
3207
+ : `, retrying every ${formatDurationMs(DEFAULT_CLAUDE_CODE_SESSION_LIMIT_RETRY_DELAY_MS)}`;
3208
+ const rateLimitSuffix = sessionLimit.rateLimitType ? ` (${sessionLimit.rateLimitType})` : '';
3209
+ return `${sessionLimit.summary}${rateLimitSuffix}${resetSuffix}`;
3210
+ }
3211
+ /**
3212
+ * Returns true when the event is an explicit rejected Claude Code rate-limit event.
3213
+ */
3214
+ function isRejectedRateLimitEvent(event) {
3215
+ var _a;
3216
+ return event.type === 'rate_limit_event' && ((_a = event.rate_limit_info) === null || _a === void 0 ? void 0 : _a.status) === 'rejected';
3217
+ }
3218
+ /**
3219
+ * Returns true when the final result reports the Claude session limit.
3220
+ */
3221
+ function isSessionLimitResultEvent(event) {
3222
+ return (event.type === 'result' &&
3223
+ event.is_error === true &&
3224
+ event.api_error_status === 429 &&
3225
+ isSessionLimitText(event.result));
3226
+ }
3227
+ /**
3228
+ * Returns true when any assistant event reports the Claude session limit in text.
3229
+ */
3230
+ function isSessionLimitTextEvent(event) {
3231
+ if (event.error !== 'rate_limit') {
3232
+ return false;
3233
+ }
3234
+ return isSessionLimitText(extractSessionLimitSummary(event));
3235
+ }
3236
+ /**
3237
+ * Extracts the first session id reported by Claude Code.
3238
+ */
3239
+ function findClaudeCodeSessionId(events) {
3240
+ return events.map((event) => event.session_id).find((sessionId) => Boolean(sessionId));
3241
+ }
3242
+ /**
3243
+ * Extracts Claude's reset timestamp from a rejected rate-limit event.
3244
+ */
3245
+ function extractResetDate(event) {
3246
+ var _a;
3247
+ const resetsAt = (_a = event === null || event === void 0 ? void 0 : event.rate_limit_info) === null || _a === void 0 ? void 0 : _a.resetsAt;
3248
+ if (typeof resetsAt !== 'number' || !Number.isFinite(resetsAt)) {
3249
+ return undefined;
3250
+ }
3251
+ const resetDate = new Date(resetsAt * 1000);
3252
+ if (Number.isNaN(resetDate.getTime())) {
3253
+ return undefined;
3254
+ }
3255
+ return resetDate;
3256
+ }
3257
+ /**
3258
+ * Extracts a human-readable session-limit summary from an event.
3259
+ */
3260
+ function extractSessionLimitSummary(event) {
3261
+ var _a, _b;
3262
+ if (!event) {
3263
+ return undefined;
3264
+ }
3265
+ if (typeof event.result === 'string' && event.result.trim()) {
3266
+ return event.result.trim();
3267
+ }
3268
+ 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));
3269
+ return textContent;
3270
+ }
3271
+ /**
3272
+ * Checks whether a text value names the Claude Code session limit.
3273
+ */
3274
+ function isSessionLimitText(text) {
3275
+ return /\bsession\s+limit\b/iu.test(text !== null && text !== void 0 ? text : '');
3276
+ }
3022
3277
 
3023
3278
  /**
3024
3279
  * Represents the uncertain value
@@ -3094,14 +3349,8 @@ const UNCERTAIN_USAGE = $deepFreeze({
3094
3349
  function parseClaudeCodeJsonOutput(output) {
3095
3350
  var _a, _b, _c, _d;
3096
3351
  try {
3097
- // Extract JSON from the output - it should be a line starting with {"type":"result"
3098
- const lines = output.split(/\r?\n/);
3099
- const jsonLine = lines.find((line) => line.trim().startsWith('{"type":"result"'));
3100
- if (!jsonLine) {
3101
- return UNCERTAIN_USAGE;
3102
- }
3103
- const parsed = JSON.parse(jsonLine);
3104
- if (parsed.type !== 'result') {
3352
+ const parsed = findClaudeCodeResultEvent(output);
3353
+ if (!parsed) {
3105
3354
  return UNCERTAIN_USAGE;
3106
3355
  }
3107
3356
  const totalInputTokens = (((_a = parsed.usage) === null || _a === void 0 ? void 0 : _a.input_tokens) || 0) +
@@ -3145,6 +3394,10 @@ function parseClaudeCodeJsonOutput(output) {
3145
3394
  }
3146
3395
  }
3147
3396
 
3397
+ /**
3398
+ * Polling interval used while waiting for Claude Code session limits to reset.
3399
+ */
3400
+ const CLAUDE_CODE_SESSION_RESURRECTION_POLL_MS = 30 * 1000;
3148
3401
  /**
3149
3402
  * Runs prompts via the Claude Code CLI.
3150
3403
  */
@@ -3160,21 +3413,102 @@ class ClaudeCodeRunner {
3160
3413
  * Runs the prompt using Claude Code and parses usage output.
3161
3414
  */
3162
3415
  async runPrompt(options) {
3416
+ let resumeSessionId;
3417
+ let prompt = options.prompt;
3418
+ let resurrectionCount = 0;
3419
+ while (true) {
3420
+ const output = await this.runClaudeCodeOnce({
3421
+ ...options,
3422
+ prompt,
3423
+ resumeSessionId,
3424
+ }).catch(async (error) => {
3425
+ const sessionLimit = extractClaudeCodeSessionLimitFromError(error);
3426
+ if (!sessionLimit) {
3427
+ throw error;
3428
+ }
3429
+ resurrectionCount++;
3430
+ await waitForClaudeCodeSessionLimitReset(sessionLimit, resurrectionCount, options);
3431
+ resumeSessionId = sessionLimit.sessionId;
3432
+ prompt = buildClaudeCodeSessionResurrectionPrompt(options.prompt, sessionLimit.sessionId);
3433
+ return undefined;
3434
+ });
3435
+ if (output === undefined) {
3436
+ continue;
3437
+ }
3438
+ const sessionLimit = extractClaudeCodeSessionLimitFromOutput(output);
3439
+ if (sessionLimit) {
3440
+ resurrectionCount++;
3441
+ await waitForClaudeCodeSessionLimitReset(sessionLimit, resurrectionCount, options);
3442
+ resumeSessionId = sessionLimit.sessionId;
3443
+ prompt = buildClaudeCodeSessionResurrectionPrompt(options.prompt, sessionLimit.sessionId);
3444
+ continue;
3445
+ }
3446
+ const usage = parseClaudeCodeJsonOutput(output);
3447
+ return { usage };
3448
+ }
3449
+ }
3450
+ /**
3451
+ * Runs one Claude Code CLI process and returns its raw output.
3452
+ */
3453
+ async runClaudeCodeOnce(options) {
3163
3454
  const scriptContent = buildClaudeScript({
3164
3455
  prompt: options.prompt,
3456
+ model: this.options.model,
3165
3457
  thinkingLevel: this.options.thinkingLevel,
3458
+ resumeSessionId: options.resumeSessionId,
3166
3459
  });
3167
- const output = await $runGoScriptWithOutput({
3460
+ return await $runGoScriptWithOutput({
3168
3461
  scriptPath: options.scriptPath,
3169
3462
  scriptContent,
3170
3463
  logPath: options.logPath,
3171
3464
  shouldPrintLiveOutput: options.shouldPrintLiveOutput,
3172
3465
  preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
3173
3466
  });
3174
- const usage = parseClaudeCodeJsonOutput(output);
3175
- return { usage };
3176
3467
  }
3177
3468
  }
3469
+ /**
3470
+ * Waits until the Claude Code session can be resumed, keeping terminal status clear.
3471
+ */
3472
+ async function waitForClaudeCodeSessionLimitReset(sessionLimit, resurrectionCount, options) {
3473
+ var _a, _b, _c;
3474
+ const delayMs = getClaudeCodeSessionLimitDelayMs(sessionLimit);
3475
+ const sessionLabel = formatClaudeCodeSessionIdForDisplay(sessionLimit.sessionId);
3476
+ const resetSummary = formatClaudeCodeSessionLimitForDisplay(sessionLimit);
3477
+ if ((_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : true) {
3478
+ console.warn(colors.yellow(`[claude-code] Session limit detected for ${sessionLimit.sessionId}. Resurrection #${resurrectionCount} will resume with --resume after ${formatDurationMs(delayMs)}. ${resetSummary}`));
3479
+ }
3480
+ let remainingDelayMs = delayMs;
3481
+ while (remainingDelayMs > 0) {
3482
+ await ((_b = options.waitForPauseCheckpoint) === null || _b === void 0 ? void 0 : _b.call(options, {
3483
+ checkpointLabel: 'the Claude Code session limit reset',
3484
+ phase: 'waiting',
3485
+ statusMessage: `Claude Code session ${sessionLabel} hit its limit; resurrection #${resurrectionCount} resumes in ${formatDurationMs(remainingDelayMs)}`,
3486
+ }));
3487
+ const currentDelayMs = Math.min(CLAUDE_CODE_SESSION_RESURRECTION_POLL_MS, remainingDelayMs);
3488
+ await waitFor$1(currentDelayMs);
3489
+ remainingDelayMs -= currentDelayMs;
3490
+ }
3491
+ await ((_c = options.waitForPauseCheckpoint) === null || _c === void 0 ? void 0 : _c.call(options, {
3492
+ checkpointLabel: 'resurrecting the Claude Code session with --resume',
3493
+ phase: 'running',
3494
+ statusMessage: `Resurrecting Claude Code session ${sessionLabel} with --resume`,
3495
+ }));
3496
+ }
3497
+ /**
3498
+ * Waits for a fixed amount of milliseconds.
3499
+ */
3500
+ async function waitFor$1(delayMs) {
3501
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
3502
+ }
3503
+ /**
3504
+ * Formats a Claude Code session id for compact terminal status lines.
3505
+ */
3506
+ function formatClaudeCodeSessionIdForDisplay(sessionId) {
3507
+ if (sessionId.length <= 8) {
3508
+ return sessionId;
3509
+ }
3510
+ return `${sessionId.slice(0, 8)}...`;
3511
+ }
3178
3512
 
3179
3513
  /**
3180
3514
  * Creates a temporary script file, runs it, and cleans it up unless preservation is requested or the run fails.
@@ -4422,8 +4756,9 @@ function resolvePromptRunner(options) {
4422
4756
  }
4423
4757
  if (agentName === 'claude-code') {
4424
4758
  return createRunnerResolution(options, new ClaudeCodeRunner({
4759
+ model: options.model,
4425
4760
  thinkingLevel: options.thinkingLevel,
4426
- }));
4761
+ }), options.model);
4427
4762
  }
4428
4763
  if (agentName === 'opencode') {
4429
4764
  return createRunnerResolution(options, new OpencodeRunner({
@@ -4497,7 +4832,7 @@ function getRunnerMetadata(options, actualRunnerModel) {
4497
4832
  if (options.agentName === 'cline') {
4498
4833
  return { runnerName, modelName: CLINE_MODEL };
4499
4834
  }
4500
- if (options.agentName === 'opencode') {
4835
+ if (options.agentName === 'opencode' || options.agentName === 'claude-code') {
4501
4836
  return { runnerName, modelName: options.model };
4502
4837
  }
4503
4838
  return { runnerName };
@@ -4574,9 +4909,10 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage) {
4574
4909
 
4575
4910
  [Up to 50,000 CZK](?message=We are posting an order up to 50,000 CZK)
4576
4911
  [Up to 100,000 CZK](?message=We are posting an order up to 100,000 CZK)
4577
- [Over 100,000 CZK](?message=We are posting an order over 100,000 CZK)
4912
+ [Over 100,000 CZK](?messageDraft=We are posting an order over 100,000 CZK)
4578
4913
  \`\`\`
4579
4914
 
4915
+ - 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.
4580
4916
 
4581
4917
  ## This is how you should behave
4582
4918
 
@@ -4584,28 +4920,38 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage) {
4584
4920
  `);
4585
4921
  }
4586
4922
 
4587
- /**
4588
- * Builds a normalized temporary shell script path for prompt runners.
4589
- */
4590
- function buildTemporaryPromptScriptPath(options) {
4591
- const sourceFileName = basename(options.sourceFileName);
4592
- const scriptFileName = `${sourceFileName.replace(/\.[^.]+$/u, '')}${options.suffix || ''}.sh`;
4593
- return resolvePromptbookTemporaryPath(options.projectPath, options.scriptDirectoryName, scriptFileName);
4594
- }
4595
-
4596
4923
  /**
4597
4924
  * Temporary subdirectory used for agent-message runner shell scripts.
4925
+ *
4926
+ * @private internal constant of agent-message runtime paths
4598
4927
  */
4599
4928
  const AGENT_MESSAGE_SCRIPT_DIRECTORY_NAME = 'agent-messages';
4929
+ /**
4930
+ * File extension used for temporary prompt-runner shell scripts.
4931
+ *
4932
+ * @private internal constant of agent-message runtime paths
4933
+ */
4934
+ const AGENT_MESSAGE_SCRIPT_EXTENSION = '.sh';
4935
+ /**
4936
+ * Builds the temporary shell script path used by one queued agent-message harness run.
4937
+ *
4938
+ * @param projectPath - Local agent project path containing the message queue.
4939
+ * @param sourceFileName - Queued message filename used to name the temporary shell script.
4940
+ * @returns Temporary shell script path.
4941
+ * @private internal utility of agent-message runners
4942
+ */
4943
+ function buildAgentMessageScriptPathFromFileName(projectPath, sourceFileName) {
4944
+ const sourceFileBasename = basename(sourceFileName);
4945
+ const scriptFileName = `${sourceFileBasename.replace(/\.[^.]+$/u, '')}${AGENT_MESSAGE_SCRIPT_EXTENSION}`;
4946
+ return resolvePromptbookTemporaryPath(projectPath, AGENT_MESSAGE_SCRIPT_DIRECTORY_NAME, scriptFileName);
4947
+ }
4948
+ // Note: [💞] Ignore a discrepancy between file name and entity name
4949
+
4600
4950
  /**
4601
4951
  * Builds a temporary script path for one agent message runner invocation.
4602
4952
  */
4603
4953
  function buildAgentMessageScriptPath(projectPath, messageFile) {
4604
- return buildTemporaryPromptScriptPath({
4605
- projectPath,
4606
- scriptDirectoryName: AGENT_MESSAGE_SCRIPT_DIRECTORY_NAME,
4607
- sourceFileName: messageFile.fileName,
4608
- });
4954
+ return buildAgentMessageScriptPathFromFileName(projectPath, messageFile.fileName);
4609
4955
  }
4610
4956
 
4611
4957
  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"}];
@@ -5068,6 +5414,95 @@ function pipelineJsonToString(pipelineJson) {
5068
5414
  // TODO: [🏛] Escape all
5069
5415
  // TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
5070
5416
 
5417
+ /**
5418
+ * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
5419
+ *
5420
+ * @public exported from `@promptbook/core`
5421
+ */
5422
+ class PipelineLogicError extends Error {
5423
+ constructor(message) {
5424
+ super(message);
5425
+ this.name = 'PipelineLogicError';
5426
+ Object.setPrototypeOf(this, PipelineLogicError.prototype);
5427
+ }
5428
+ }
5429
+
5430
+ /**
5431
+ * Creates the shared validation context for one pipeline.
5432
+ *
5433
+ * @private function of `validatePipeline`
5434
+ */
5435
+ function createPipelineValidationContext(pipeline) {
5436
+ return {
5437
+ pipeline,
5438
+ pipelineIdentification: getPipelineIdentification$1(pipeline),
5439
+ };
5440
+ }
5441
+ /**
5442
+ * Builds a short file/url identification block for validation errors.
5443
+ *
5444
+ * @private internal utility of `validatePipeline`
5445
+ */
5446
+ function getPipelineIdentification$1(pipeline) {
5447
+ // Note: This is a 😐 implementation of [🚞]
5448
+ const pipelineIdentificationParts = [];
5449
+ if (pipeline.sourceFile !== undefined) {
5450
+ pipelineIdentificationParts.push(`File: ${pipeline.sourceFile}`);
5451
+ }
5452
+ if (pipeline.pipelineUrl !== undefined) {
5453
+ pipelineIdentificationParts.push(`Url: ${pipeline.pipelineUrl}`);
5454
+ }
5455
+ return pipelineIdentificationParts.join('\n');
5456
+ }
5457
+
5458
+ /**
5459
+ * Validates that the expected top-level collections have array structure.
5460
+ *
5461
+ * @private function of `validatePipeline`
5462
+ */
5463
+ function validatePipelineCollectionsStructure({ pipeline, pipelineIdentification, }) {
5464
+ validatePipelineParametersCollection(pipeline, pipelineIdentification);
5465
+ validatePipelineTasksCollection(pipeline, pipelineIdentification);
5466
+ }
5467
+ /**
5468
+ * Validates that `pipeline.parameters` is an array.
5469
+ *
5470
+ * @private internal utility of `validatePipelineCollectionsStructure`
5471
+ */
5472
+ function validatePipelineParametersCollection(pipeline, pipelineIdentification) {
5473
+ // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
5474
+ if (Array.isArray(pipeline.parameters)) {
5475
+ return;
5476
+ }
5477
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
5478
+ throw new ParseError(spaceTrim$1((block) => `
5479
+ Pipeline is valid JSON but with wrong structure
5480
+
5481
+ \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
5482
+
5483
+ ${block(pipelineIdentification)}
5484
+ `));
5485
+ }
5486
+ /**
5487
+ * Validates that `pipeline.tasks` is an array.
5488
+ *
5489
+ * @private internal utility of `validatePipelineCollectionsStructure`
5490
+ */
5491
+ function validatePipelineTasksCollection(pipeline, pipelineIdentification) {
5492
+ // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
5493
+ if (Array.isArray(pipeline.tasks)) {
5494
+ return;
5495
+ }
5496
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
5497
+ throw new ParseError(spaceTrim$1((block) => `
5498
+ Pipeline is valid JSON but with wrong structure
5499
+
5500
+ \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
5501
+
5502
+ ${block(pipelineIdentification)}
5503
+ `));
5504
+ }
5505
+
5071
5506
  /**
5072
5507
  * Order of keys in the pipeline JSON
5073
5508
  *
@@ -5180,17 +5615,138 @@ const LIMITS = {
5180
5615
  // Note: [💞] Ignore a discrepancy between file name and entity name
5181
5616
 
5182
5617
  /**
5183
- * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
5618
+ * Validates that task dependencies can be resolved without cycles or missing definitions.
5184
5619
  *
5185
- * @public exported from `@promptbook/core`
5620
+ * @private function of `validatePipeline`
5186
5621
  */
5187
- class PipelineLogicError extends Error {
5188
- constructor(message) {
5189
- super(message);
5190
- this.name = 'PipelineLogicError';
5191
- Object.setPrototypeOf(this, PipelineLogicError.prototype);
5622
+ function validatePipelineDependencyResolution({ pipeline, pipelineIdentification, }) {
5623
+ let dependencyResolutionState = createInitialDependencyResolutionState(pipeline);
5624
+ let loopLimit = LOOP_LIMIT;
5625
+ while (hasUnresolvedTasks(dependencyResolutionState)) {
5626
+ if (loopLimit-- < 0) {
5627
+ throw createDependencyResolutionLoopLimitError(pipelineIdentification);
5628
+ }
5629
+ dependencyResolutionState = resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification);
5192
5630
  }
5193
5631
  }
5632
+ /**
5633
+ * Collects the parameter names that are already resolvable before dependency traversal starts.
5634
+ *
5635
+ * @private internal utility of `validatePipelineDependencyResolution`
5636
+ */
5637
+ function createInitialDependencyResolutionState(pipeline) {
5638
+ return {
5639
+ resolvedParameterNames: createInitiallyResolvedParameterNames(pipeline),
5640
+ unresolvedTasks: [...pipeline.tasks],
5641
+ };
5642
+ }
5643
+ /**
5644
+ * Checks whether dependency resolution still has tasks left to process.
5645
+ *
5646
+ * @private internal utility of `validatePipelineDependencyResolution`
5647
+ */
5648
+ function hasUnresolvedTasks({ unresolvedTasks }) {
5649
+ return unresolvedTasks.length > 0;
5650
+ }
5651
+ /**
5652
+ * Resolves the next batch of currently satisfiable tasks.
5653
+ *
5654
+ * @private internal utility of `validatePipelineDependencyResolution`
5655
+ */
5656
+ function resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification) {
5657
+ const currentlyResolvedTasks = getCurrentlyResolvedTasks(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames);
5658
+ if (currentlyResolvedTasks.length === 0) {
5659
+ throw createUnresolvedTasksError(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames, pipelineIdentification);
5660
+ }
5661
+ return {
5662
+ resolvedParameterNames: appendResolvedTaskParameterNames(dependencyResolutionState.resolvedParameterNames, currentlyResolvedTasks),
5663
+ unresolvedTasks: dependencyResolutionState.unresolvedTasks.filter((task) => !currentlyResolvedTasks.includes(task)),
5664
+ };
5665
+ }
5666
+ /**
5667
+ * Collects the parameter names that are already resolvable before dependency traversal starts.
5668
+ *
5669
+ * @private internal utility of `validatePipelineDependencyResolution`
5670
+ */
5671
+ function createInitiallyResolvedParameterNames(pipeline) {
5672
+ let resolvedParameterNames = pipeline.parameters
5673
+ .filter(({ isInput }) => isInput)
5674
+ .map(({ name }) => name);
5675
+ for (const reservedParameterName of RESERVED_PARAMETER_NAMES) {
5676
+ resolvedParameterNames = [...resolvedParameterNames, reservedParameterName];
5677
+ }
5678
+ return resolvedParameterNames;
5679
+ }
5680
+ /**
5681
+ * Adds newly resolved task outputs to the resolved parameter list.
5682
+ *
5683
+ * @private internal utility of `validatePipelineDependencyResolution`
5684
+ */
5685
+ function appendResolvedTaskParameterNames(resolvedParameterNames, currentlyResolvedTasks) {
5686
+ return [
5687
+ ...resolvedParameterNames,
5688
+ ...currentlyResolvedTasks.map(({ resultingParameterName }) => resultingParameterName),
5689
+ ];
5690
+ }
5691
+ /**
5692
+ * Selects tasks whose dependencies are already resolved.
5693
+ *
5694
+ * @private internal utility of `validatePipelineDependencyResolution`
5695
+ */
5696
+ function getCurrentlyResolvedTasks(unresolvedTasks, resolvedParameterNames) {
5697
+ return unresolvedTasks.filter((task) => task.dependentParameterNames.every((name) => resolvedParameterNames.includes(name)));
5698
+ }
5699
+ /**
5700
+ * Creates the unexpected loop-limit error for dependency resolution.
5701
+ *
5702
+ * @private internal utility of `validatePipelineDependencyResolution`
5703
+ */
5704
+ function createDependencyResolutionLoopLimitError(pipelineIdentification) {
5705
+ // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
5706
+ return new UnexpectedError(spaceTrim$1((block) => `
5707
+ Loop limit reached during detection of circular dependencies in \`validatePipeline\`
5708
+
5709
+ ${block(pipelineIdentification)}
5710
+ `));
5711
+ }
5712
+ /**
5713
+ * Creates the detailed error for unresolved or circular task dependencies.
5714
+ *
5715
+ * @private internal utility of `validatePipelineDependencyResolution`
5716
+ */
5717
+ function createUnresolvedTasksError(unresolvedTasks, resolvedParameterNames, pipelineIdentification) {
5718
+ return new PipelineLogicError(
5719
+ // TODO: [🐎] DRY
5720
+ spaceTrim$1((block) => `
5721
+
5722
+ Can not resolve some parameters:
5723
+ Either you are using a parameter that is not defined, or there are some circular dependencies.
5724
+
5725
+ ${block(pipelineIdentification)}
5726
+
5727
+ **Can not resolve:**
5728
+ ${block(unresolvedTasks
5729
+ .map(({ resultingParameterName, dependentParameterNames }) => `- Parameter \`{${resultingParameterName}}\` which depends on ${dependentParameterNames
5730
+ .map((dependentParameterName) => `\`{${dependentParameterName}}\``)
5731
+ .join(' and ')}`)
5732
+ .join('\n'))}
5733
+
5734
+ **Resolved:**
5735
+ ${block(resolvedParameterNames
5736
+ .filter((name) => !RESERVED_PARAMETER_NAMES.includes(name))
5737
+ .map((name) => `- Parameter \`{${name}}\``)
5738
+ .join('\n'))}
5739
+
5740
+
5741
+ **Reserved (which are available):**
5742
+ ${block(resolvedParameterNames
5743
+ .filter((name) => RESERVED_PARAMETER_NAMES.includes(name))
5744
+ .map((name) => `- Parameter \`{${name}}\``)
5745
+ .join('\n'))}
5746
+
5747
+
5748
+ `));
5749
+ }
5194
5750
 
5195
5751
  /**
5196
5752
  * Tests if given string is valid semantic version
@@ -5266,168 +5822,19 @@ function isValidPipelineUrl(url) {
5266
5822
  }
5267
5823
  // TODO: [🐠] Maybe more info why the URL is invalid
5268
5824
 
5269
- /**
5270
- * Validates PipelineJson if it is logically valid
5271
- *
5272
- * It checks:
5273
- * - if it has correct parameters dependency
5274
- *
5275
- * It does NOT check:
5276
- * - if it is valid json
5277
- * - if it is meaningful
5278
- *
5279
- * Note: [🔂] This function is idempotent.
5280
- *
5281
- * @param pipeline valid or invalid PipelineJson
5282
- * @returns the same pipeline if it is logically valid
5283
- * @throws {PipelineLogicError} on logical error in the pipeline
5284
- *
5285
- * @public exported from `@promptbook/core`
5286
- */
5287
- function validatePipeline(pipeline) {
5288
- if (IS_PIPELINE_LOGIC_VALIDATED) {
5289
- validatePipeline_InnerFunction(pipeline);
5290
- }
5291
- else {
5292
- try {
5293
- validatePipeline_InnerFunction(pipeline);
5294
- }
5295
- catch (error) {
5296
- if (!(error instanceof PipelineLogicError)) {
5297
- throw error;
5298
- }
5299
- console.error(spaceTrim$1((block) => `
5300
- Pipeline is not valid but logic errors are temporarily disabled via \`IS_PIPELINE_LOGIC_VALIDATED\`
5301
-
5302
- ${block(error.message)}
5303
- `));
5304
- }
5305
- }
5306
- return pipeline;
5307
- }
5308
- /**
5309
- * Validates pipeline inner function.
5310
- *
5311
- * @private internal function for `validatePipeline`
5312
- */
5313
- function validatePipeline_InnerFunction(pipeline) {
5314
- // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
5315
- const context = createPipelineValidationContext(pipeline);
5316
- validatePipelineMetadata(context);
5317
- validatePipelineCollectionsStructure(context);
5318
- validatePipelineParameters(context);
5319
- validatePipelineTasks(context);
5320
- validatePipelineDependencyResolution(context);
5321
- // Note: Check that formfactor is corresponding to the pipeline interface
5322
- // TODO: !!6 Implement this
5323
- // pipeline.formfactorName
5324
- }
5325
- /**
5326
- * Creates the shared validation context for one pipeline.
5327
- *
5328
- * @private internal utility of `validatePipeline`
5329
- */
5330
- function createPipelineValidationContext(pipeline) {
5331
- return {
5332
- pipeline,
5333
- pipelineIdentification: getPipelineIdentification$1(pipeline),
5334
- };
5335
- }
5336
- /**
5337
- * Builds a short file/url identification block for validation errors.
5338
- *
5339
- * @private internal utility of `validatePipeline`
5340
- */
5341
- function getPipelineIdentification$1(pipeline) {
5342
- // Note: This is a 😐 implementation of [🚞]
5343
- const pipelineIdentificationParts = [];
5344
- if (pipeline.sourceFile !== undefined) {
5345
- pipelineIdentificationParts.push(`File: ${pipeline.sourceFile}`);
5346
- }
5347
- if (pipeline.pipelineUrl !== undefined) {
5348
- pipelineIdentificationParts.push(`Url: ${pipeline.pipelineUrl}`);
5349
- }
5350
- return pipelineIdentificationParts.join('\n');
5351
- }
5352
5825
  /**
5353
5826
  * Validates pipeline-level metadata fields.
5354
5827
  *
5355
- * @private internal step of `validatePipeline`
5828
+ * @private function of `validatePipeline`
5356
5829
  */
5357
5830
  function validatePipelineMetadata({ pipeline, pipelineIdentification }) {
5358
5831
  validatePipelineUrl(pipeline, pipelineIdentification);
5359
5832
  validatePipelineBookVersion(pipeline, pipelineIdentification);
5360
5833
  }
5361
- /**
5362
- * Validates that the expected top-level collections have array structure.
5363
- *
5364
- * @private internal step of `validatePipeline`
5365
- */
5366
- function validatePipelineCollectionsStructure({ pipeline, pipelineIdentification }) {
5367
- validatePipelineParametersCollection(pipeline, pipelineIdentification);
5368
- validatePipelineTasksCollection(pipeline, pipelineIdentification);
5369
- }
5370
- /**
5371
- * Validates all pipeline parameter declarations.
5372
- *
5373
- * @private internal step of `validatePipeline`
5374
- */
5375
- function validatePipelineParameters({ pipeline, pipelineIdentification }) {
5376
- for (const parameter of pipeline.parameters) {
5377
- validatePipelineParameter(parameter, pipeline, pipelineIdentification);
5378
- }
5379
- }
5380
- /**
5381
- * Validates all pipeline tasks and their per-task invariants.
5382
- *
5383
- * @private internal step of `validatePipeline`
5384
- */
5385
- function validatePipelineTasks({ pipeline, pipelineIdentification }) {
5386
- // Note: All input parameters are defined - so that they can be used as result of some task
5387
- const definedParameters = createInitiallyDefinedParameters(pipeline);
5388
- for (const task of pipeline.tasks) {
5389
- validatePipelineTask(task, definedParameters, pipelineIdentification);
5390
- }
5391
- }
5392
- /**
5393
- * Validates that task dependencies can be resolved without cycles or missing definitions.
5394
- *
5395
- * @private internal step of `validatePipeline`
5396
- */
5397
- function validatePipelineDependencyResolution({ pipeline, pipelineIdentification }) {
5398
- let dependencyResolutionState = createInitialDependencyResolutionState(pipeline);
5399
- let loopLimit = LOOP_LIMIT;
5400
- while (hasUnresolvedTasks(dependencyResolutionState)) {
5401
- if (loopLimit-- < 0) {
5402
- throw createDependencyResolutionLoopLimitError(pipelineIdentification);
5403
- }
5404
- dependencyResolutionState = resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification);
5405
- }
5406
- }
5407
- /**
5408
- * Validates one pipeline parameter declaration.
5409
- *
5410
- * @private internal step of `validatePipeline`
5411
- */
5412
- function validatePipelineParameter(parameter, pipeline, pipelineIdentification) {
5413
- validateParameterDirection(parameter, pipelineIdentification);
5414
- validateParameterUsage(parameter, pipeline, pipelineIdentification);
5415
- validateParameterDefinition(parameter, pipeline, pipelineIdentification);
5416
- }
5417
- /**
5418
- * Validates one pipeline task and its invariants.
5419
- *
5420
- * @private internal step of `validatePipeline`
5421
- */
5422
- function validatePipelineTask(task, definedParameters, pipelineIdentification) {
5423
- validateTaskResultingParameter(task, definedParameters, pipelineIdentification);
5424
- validateTaskJokers(task, pipelineIdentification);
5425
- validateTaskExpectations(task, pipelineIdentification);
5426
- }
5427
5834
  /**
5428
5835
  * Validates the pipeline URL, when present.
5429
5836
  *
5430
- * @private internal utility of `validatePipeline`
5837
+ * @private internal utility of `validatePipelineMetadata`
5431
5838
  */
5432
5839
  function validatePipelineUrl(pipeline, pipelineIdentification) {
5433
5840
  if (pipeline.pipelineUrl === undefined || isValidPipelineUrl(pipeline.pipelineUrl)) {
@@ -5443,7 +5850,7 @@ function validatePipelineUrl(pipeline, pipelineIdentification) {
5443
5850
  /**
5444
5851
  * Validates the Promptbook version, when present.
5445
5852
  *
5446
- * @private internal utility of `validatePipeline`
5853
+ * @private internal utility of `validatePipelineMetadata`
5447
5854
  */
5448
5855
  function validatePipelineBookVersion(pipeline, pipelineIdentification) {
5449
5856
  if (pipeline.bookVersion === undefined || isValidPromptbookVersion(pipeline.bookVersion)) {
@@ -5456,48 +5863,31 @@ function validatePipelineBookVersion(pipeline, pipelineIdentification) {
5456
5863
  ${block(pipelineIdentification)}
5457
5864
  `));
5458
5865
  }
5866
+
5459
5867
  /**
5460
- * Validates that `pipeline.parameters` is an array.
5868
+ * Validates all pipeline parameter declarations.
5461
5869
  *
5462
- * @private internal utility of `validatePipeline`
5870
+ * @private function of `validatePipeline`
5463
5871
  */
5464
- function validatePipelineParametersCollection(pipeline, pipelineIdentification) {
5465
- // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
5466
- if (Array.isArray(pipeline.parameters)) {
5467
- return;
5872
+ function validatePipelineParameters({ pipeline, pipelineIdentification }) {
5873
+ for (const parameter of pipeline.parameters) {
5874
+ validatePipelineParameter(parameter, pipeline, pipelineIdentification);
5468
5875
  }
5469
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
5470
- throw new ParseError(spaceTrim$1((block) => `
5471
- Pipeline is valid JSON but with wrong structure
5472
-
5473
- \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
5474
-
5475
- ${block(pipelineIdentification)}
5476
- `));
5477
5876
  }
5478
5877
  /**
5479
- * Validates that `pipeline.tasks` is an array.
5878
+ * Validates one pipeline parameter declaration.
5480
5879
  *
5481
- * @private internal utility of `validatePipeline`
5880
+ * @private internal utility of `validatePipelineParameters`
5482
5881
  */
5483
- function validatePipelineTasksCollection(pipeline, pipelineIdentification) {
5484
- // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
5485
- if (Array.isArray(pipeline.tasks)) {
5486
- return;
5487
- }
5488
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
5489
- throw new ParseError(spaceTrim$1((block) => `
5490
- Pipeline is valid JSON but with wrong structure
5491
-
5492
- \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
5493
-
5494
- ${block(pipelineIdentification)}
5495
- `));
5882
+ function validatePipelineParameter(parameter, pipeline, pipelineIdentification) {
5883
+ validateParameterDirection(parameter, pipelineIdentification);
5884
+ validateParameterUsage(parameter, pipeline, pipelineIdentification);
5885
+ validateParameterDefinition(parameter, pipeline, pipelineIdentification);
5496
5886
  }
5497
5887
  /**
5498
5888
  * Validates that one parameter does not declare incompatible directions.
5499
5889
  *
5500
- * @private internal utility of `validatePipeline`
5890
+ * @private internal utility of `validatePipelineParameters`
5501
5891
  */
5502
5892
  function validateParameterDirection(parameter, pipelineIdentification) {
5503
5893
  if (!parameter.isInput || !parameter.isOutput) {
@@ -5514,7 +5904,7 @@ function validateParameterDirection(parameter, pipelineIdentification) {
5514
5904
  /**
5515
5905
  * Validates that one intermediate parameter is actually consumed by at least one task.
5516
5906
  *
5517
- * @private internal utility of `validatePipeline`
5907
+ * @private internal utility of `validatePipelineParameters`
5518
5908
  */
5519
5909
  function validateParameterUsage(parameter, pipeline, pipelineIdentification) {
5520
5910
  if (parameter.isInput || parameter.isOutput || isParameterUsedByAnyTask(parameter, pipeline.tasks)) {
@@ -5533,7 +5923,7 @@ function validateParameterUsage(parameter, pipeline, pipelineIdentification) {
5533
5923
  /**
5534
5924
  * Validates that one non-input parameter is produced by at least one task.
5535
5925
  *
5536
- * @private internal utility of `validatePipeline`
5926
+ * @private internal utility of `validatePipelineParameters`
5537
5927
  */
5538
5928
  function validateParameterDefinition(parameter, pipeline, pipelineIdentification) {
5539
5929
  if (parameter.isInput || isParameterDefinedByAnyTask(parameter, pipeline.tasks)) {
@@ -5552,7 +5942,7 @@ function validateParameterDefinition(parameter, pipeline, pipelineIdentification
5552
5942
  /**
5553
5943
  * Checks whether one parameter is consumed by at least one task dependency list.
5554
5944
  *
5555
- * @private internal utility of `validatePipeline`
5945
+ * @private internal utility of `validatePipelineParameters`
5556
5946
  */
5557
5947
  function isParameterUsedByAnyTask(parameter, tasks) {
5558
5948
  return tasks.some((task) => task.dependentParameterNames.includes(parameter.name));
@@ -5560,23 +5950,46 @@ function isParameterUsedByAnyTask(parameter, tasks) {
5560
5950
  /**
5561
5951
  * Checks whether one parameter is produced by at least one task.
5562
5952
  *
5563
- * @private internal utility of `validatePipeline`
5953
+ * @private internal utility of `validatePipelineParameters`
5564
5954
  */
5565
5955
  function isParameterDefinedByAnyTask(parameter, tasks) {
5566
5956
  return tasks.some((task) => task.resultingParameterName === parameter.name);
5567
5957
  }
5958
+
5959
+ /**
5960
+ * Validates all pipeline tasks and their per-task invariants.
5961
+ *
5962
+ * @private function of `validatePipeline`
5963
+ */
5964
+ function validatePipelineTasks({ pipeline, pipelineIdentification }) {
5965
+ // Note: All input parameters are defined - so that they can be used as result of some task
5966
+ const definedParameters = createInitiallyDefinedParameters(pipeline);
5967
+ for (const task of pipeline.tasks) {
5968
+ validatePipelineTask(task, definedParameters, pipelineIdentification);
5969
+ }
5970
+ }
5568
5971
  /**
5569
5972
  * Collects the parameter names that are already defined before task validation starts.
5570
5973
  *
5571
- * @private internal utility of `validatePipeline`
5974
+ * @private internal utility of `validatePipelineTasks`
5572
5975
  */
5573
5976
  function createInitiallyDefinedParameters(pipeline) {
5574
5977
  return new Set(pipeline.parameters.filter(({ isInput }) => isInput).map(({ name }) => name));
5575
5978
  }
5979
+ /**
5980
+ * Validates one pipeline task and its invariants.
5981
+ *
5982
+ * @private internal utility of `validatePipelineTasks`
5983
+ */
5984
+ function validatePipelineTask(task, definedParameters, pipelineIdentification) {
5985
+ validateTaskResultingParameter(task, definedParameters, pipelineIdentification);
5986
+ validateTaskJokers(task, pipelineIdentification);
5987
+ validateTaskExpectations(task, pipelineIdentification);
5988
+ }
5576
5989
  /**
5577
5990
  * Validates one task result parameter declaration and marks it as defined.
5578
5991
  *
5579
- * @private internal utility of `validatePipeline`
5992
+ * @private internal utility of `validatePipelineTasks`
5580
5993
  */
5581
5994
  function validateTaskResultingParameter(task, definedParameters, pipelineIdentification) {
5582
5995
  if (definedParameters.has(task.resultingParameterName)) {
@@ -5598,7 +6011,7 @@ function validateTaskResultingParameter(task, definedParameters, pipelineIdentif
5598
6011
  /**
5599
6012
  * Validates joker parameters for one task.
5600
6013
  *
5601
- * @private internal utility of `validatePipeline`
6014
+ * @private internal utility of `validatePipelineTasks`
5602
6015
  */
5603
6016
  function validateTaskJokers(task, pipelineIdentification) {
5604
6017
  if (!hasTaskJokers(task)) {
@@ -5610,7 +6023,7 @@ function validateTaskJokers(task, pipelineIdentification) {
5610
6023
  /**
5611
6024
  * Checks whether one task declares any joker parameters.
5612
6025
  *
5613
- * @private internal utility of `validatePipeline`
6026
+ * @private internal utility of `validatePipelineTasks`
5614
6027
  */
5615
6028
  function hasTaskJokers(task) {
5616
6029
  return !!task.jokerParameterNames && task.jokerParameterNames.length > 0;
@@ -5618,7 +6031,7 @@ function hasTaskJokers(task) {
5618
6031
  /**
5619
6032
  * Validates that a task has the required supporting features when using jokers.
5620
6033
  *
5621
- * @private internal utility of `validatePipeline`
6034
+ * @private internal utility of `validatePipelineTasks`
5622
6035
  */
5623
6036
  function validateTaskSupportsJokers(task, pipelineIdentification) {
5624
6037
  if (task.format || task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
@@ -5633,7 +6046,7 @@ function validateTaskSupportsJokers(task, pipelineIdentification) {
5633
6046
  /**
5634
6047
  * Validates that every joker parameter is also listed among task dependencies.
5635
6048
  *
5636
- * @private internal utility of `validatePipeline`
6049
+ * @private internal utility of `validatePipelineTasks`
5637
6050
  */
5638
6051
  function validateTaskJokerDependencies(task, pipelineIdentification) {
5639
6052
  for (const joker of task.jokerParameterNames) {
@@ -5650,7 +6063,7 @@ function validateTaskJokerDependencies(task, pipelineIdentification) {
5650
6063
  /**
5651
6064
  * Validates all expectation bounds configured on one task.
5652
6065
  *
5653
- * @private internal utility of `validatePipeline`
6066
+ * @private internal utility of `validatePipelineTasks`
5654
6067
  */
5655
6068
  function validateTaskExpectations(task, pipelineIdentification) {
5656
6069
  if (!task.expectations) {
@@ -5665,7 +6078,7 @@ function validateTaskExpectations(task, pipelineIdentification) {
5665
6078
  /**
5666
6079
  * Validates the minimum and maximum expectation ordering for one unit.
5667
6080
  *
5668
- * @private internal utility of `validatePipeline`
6081
+ * @private internal utility of `validatePipelineTasks`
5669
6082
  */
5670
6083
  function validateTaskExpectationRange(unit, min, max, pipelineIdentification) {
5671
6084
  if (min === undefined || max === undefined || min <= max) {
@@ -5680,7 +6093,7 @@ function validateTaskExpectationRange(unit, min, max, pipelineIdentification) {
5680
6093
  /**
5681
6094
  * Validates the minimum expectation bound for one unit.
5682
6095
  *
5683
- * @private internal utility of `validatePipeline`
6096
+ * @private internal utility of `validatePipelineTasks`
5684
6097
  */
5685
6098
  function validateTaskExpectationMin(unit, min, pipelineIdentification) {
5686
6099
  if (min === undefined || min >= 0) {
@@ -5695,7 +6108,7 @@ function validateTaskExpectationMin(unit, min, pipelineIdentification) {
5695
6108
  /**
5696
6109
  * Validates the maximum expectation bound for one unit.
5697
6110
  *
5698
- * @private internal utility of `validatePipeline`
6111
+ * @private internal utility of `validatePipelineTasks`
5699
6112
  */
5700
6113
  function validateTaskExpectationMax(unit, max, pipelineIdentification) {
5701
6114
  if (max === undefined || max > 0) {
@@ -5707,123 +6120,62 @@ function validateTaskExpectationMax(unit, max, pipelineIdentification) {
5707
6120
  ${block(pipelineIdentification)}
5708
6121
  `));
5709
6122
  }
6123
+
5710
6124
  /**
5711
- * Collects the parameter names that are already resolvable before dependency traversal starts.
5712
- *
5713
- * @private internal utility of `validatePipeline`
5714
- */
5715
- function createInitialDependencyResolutionState(pipeline) {
5716
- return {
5717
- resolvedParameterNames: createInitiallyResolvedParameterNames(pipeline),
5718
- unresolvedTasks: [...pipeline.tasks],
5719
- };
5720
- }
5721
- /**
5722
- * Checks whether dependency resolution still has tasks left to process.
5723
- *
5724
- * @private internal utility of `validatePipeline`
5725
- */
5726
- function hasUnresolvedTasks({ unresolvedTasks }) {
5727
- return unresolvedTasks.length > 0;
5728
- }
5729
- /**
5730
- * Resolves the next batch of currently satisfiable tasks.
6125
+ * Validates PipelineJson if it is logically valid
5731
6126
  *
5732
- * @private internal utility of `validatePipeline`
5733
- */
5734
- function resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification) {
5735
- const currentlyResolvedTasks = getCurrentlyResolvedTasks(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames);
5736
- if (currentlyResolvedTasks.length === 0) {
5737
- throw createUnresolvedTasksError(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames, pipelineIdentification);
5738
- }
5739
- return {
5740
- resolvedParameterNames: appendResolvedTaskParameterNames(dependencyResolutionState.resolvedParameterNames, currentlyResolvedTasks),
5741
- unresolvedTasks: dependencyResolutionState.unresolvedTasks.filter((task) => !currentlyResolvedTasks.includes(task)),
5742
- };
5743
- }
5744
- /**
5745
- * Collects the parameter names that are already resolvable before dependency traversal starts.
6127
+ * It checks:
6128
+ * - if it has correct parameters dependency
5746
6129
  *
5747
- * @private internal utility of `validatePipeline`
5748
- */
5749
- function createInitiallyResolvedParameterNames(pipeline) {
5750
- let resolvedParameterNames = pipeline.parameters
5751
- .filter(({ isInput }) => isInput)
5752
- .map(({ name }) => name);
5753
- for (const reservedParameterName of RESERVED_PARAMETER_NAMES) {
5754
- resolvedParameterNames = [...resolvedParameterNames, reservedParameterName];
5755
- }
5756
- return resolvedParameterNames;
5757
- }
5758
- /**
5759
- * Adds newly resolved task outputs to the resolved parameter list.
6130
+ * It does NOT check:
6131
+ * - if it is valid json
6132
+ * - if it is meaningful
5760
6133
  *
5761
- * @private internal utility of `validatePipeline`
5762
- */
5763
- function appendResolvedTaskParameterNames(resolvedParameterNames, currentlyResolvedTasks) {
5764
- return [
5765
- ...resolvedParameterNames,
5766
- ...currentlyResolvedTasks.map(({ resultingParameterName }) => resultingParameterName),
5767
- ];
5768
- }
5769
- /**
5770
- * Selects tasks whose dependencies are already resolved.
6134
+ * Note: [🔂] This function is idempotent.
5771
6135
  *
5772
- * @private internal utility of `validatePipeline`
5773
- */
5774
- function getCurrentlyResolvedTasks(unresolvedTasks, resolvedParameterNames) {
5775
- return unresolvedTasks.filter((task) => task.dependentParameterNames.every((name) => resolvedParameterNames.includes(name)));
5776
- }
5777
- /**
5778
- * Creates the unexpected loop-limit error for dependency resolution.
6136
+ * @param pipeline valid or invalid PipelineJson
6137
+ * @returns the same pipeline if it is logically valid
6138
+ * @throws {PipelineLogicError} on logical error in the pipeline
5779
6139
  *
5780
- * @private internal utility of `validatePipeline`
6140
+ * @public exported from `@promptbook/core`
5781
6141
  */
5782
- function createDependencyResolutionLoopLimitError(pipelineIdentification) {
5783
- // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
5784
- return new UnexpectedError(spaceTrim$1((block) => `
5785
- Loop limit reached during detection of circular dependencies in \`validatePipeline\`
6142
+ function validatePipeline(pipeline) {
6143
+ if (IS_PIPELINE_LOGIC_VALIDATED) {
6144
+ validatePipeline_InnerFunction(pipeline);
6145
+ }
6146
+ else {
6147
+ try {
6148
+ validatePipeline_InnerFunction(pipeline);
6149
+ }
6150
+ catch (error) {
6151
+ if (!(error instanceof PipelineLogicError)) {
6152
+ throw error;
6153
+ }
6154
+ console.error(spaceTrim$1((block) => `
6155
+ Pipeline is not valid but logic errors are temporarily disabled via \`IS_PIPELINE_LOGIC_VALIDATED\`
5786
6156
 
5787
- ${block(pipelineIdentification)}
5788
- `));
6157
+ ${block(error.message)}
6158
+ `));
6159
+ }
6160
+ }
6161
+ return pipeline;
5789
6162
  }
5790
6163
  /**
5791
- * Creates the detailed error for unresolved or circular task dependencies.
6164
+ * Validates pipeline inner function.
5792
6165
  *
5793
- * @private internal utility of `validatePipeline`
6166
+ * @private internal function for `validatePipeline`
5794
6167
  */
5795
- function createUnresolvedTasksError(unresolvedTasks, resolvedParameterNames, pipelineIdentification) {
5796
- return new PipelineLogicError(
5797
- // TODO: [🐎] DRY
5798
- spaceTrim$1((block) => `
5799
-
5800
- Can not resolve some parameters:
5801
- Either you are using a parameter that is not defined, or there are some circular dependencies.
5802
-
5803
- ${block(pipelineIdentification)}
5804
-
5805
- **Can not resolve:**
5806
- ${block(unresolvedTasks
5807
- .map(({ resultingParameterName, dependentParameterNames }) => `- Parameter \`{${resultingParameterName}}\` which depends on ${dependentParameterNames
5808
- .map((dependentParameterName) => `\`{${dependentParameterName}}\``)
5809
- .join(' and ')}`)
5810
- .join('\n'))}
5811
-
5812
- **Resolved:**
5813
- ${block(resolvedParameterNames
5814
- .filter((name) => !RESERVED_PARAMETER_NAMES.includes(name))
5815
- .map((name) => `- Parameter \`{${name}}\``)
5816
- .join('\n'))}
5817
-
5818
-
5819
- **Reserved (which are available):**
5820
- ${block(resolvedParameterNames
5821
- .filter((name) => RESERVED_PARAMETER_NAMES.includes(name))
5822
- .map((name) => `- Parameter \`{${name}}\``)
5823
- .join('\n'))}
5824
-
5825
-
5826
- `));
6168
+ function validatePipeline_InnerFunction(pipeline) {
6169
+ // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
6170
+ const context = createPipelineValidationContext(pipeline);
6171
+ validatePipelineMetadata(context);
6172
+ validatePipelineCollectionsStructure(context);
6173
+ validatePipelineParameters(context);
6174
+ validatePipelineTasks(context);
6175
+ validatePipelineDependencyResolution(context);
6176
+ // Note: Check that formfactor is corresponding to the pipeline interface
6177
+ // TODO: !!6 Implement this
6178
+ // pipeline.formfactorName
5827
6179
  }
5828
6180
  /**
5829
6181
  * TODO: [🧞‍♀️] Do not allow joker + foreach
@@ -14143,7 +14495,7 @@ class MessageSuffixCommitmentDefinition extends BaseCommitmentDefinition {
14143
14495
  * META commitment definition
14144
14496
  *
14145
14497
  * The META commitment handles all meta-information about the agent such as:
14146
- * - META AVATAR: Sets the agent's built-in default avatar visual
14498
+ * - META AVATAR / META VISUAL: Sets the agent's built-in default avatar visual
14147
14499
  * - META IMAGE: Sets the agent's avatar/profile image URL
14148
14500
  * - META LINK: Provides profile/source links for the person the agent models
14149
14501
  * - META DOMAIN: Sets the canonical custom domain/host of the agent
@@ -14158,7 +14510,7 @@ class MessageSuffixCommitmentDefinition extends BaseCommitmentDefinition {
14158
14510
  * Example usage in agent source:
14159
14511
  *
14160
14512
  * ```book
14161
- * META AVATAR pixel-art
14513
+ * META VISUAL pixel-art
14162
14514
  * META IMAGE https://example.com/avatar.jpg
14163
14515
  * META LINK https://twitter.com/username
14164
14516
  * META DOMAIN my-agent.com
@@ -14198,7 +14550,7 @@ class MetaCommitmentDefinition extends BaseCommitmentDefinition {
14198
14550
 
14199
14551
  ## Supported META types
14200
14552
 
14201
- - **META AVATAR** - Sets the agent's built-in default avatar visual
14553
+ - **META AVATAR** / **META VISUAL** - Sets the agent's built-in default avatar visual
14202
14554
  - **META IMAGE** - Sets the agent's avatar/profile image URL
14203
14555
  - **META LINK** - Provides profile/source links for the person the agent models
14204
14556
  - **META DOMAIN** - Sets the canonical custom domain/host of the agent
@@ -14222,7 +14574,7 @@ class MetaCommitmentDefinition extends BaseCommitmentDefinition {
14222
14574
  \`\`\`book
14223
14575
  Professional Assistant
14224
14576
 
14225
- META AVATAR octopus3
14577
+ META VISUAL octopus3
14226
14578
  META IMAGE https://example.com/professional-avatar.jpg
14227
14579
  META TITLE Senior Business Consultant
14228
14580
  META DESCRIPTION Specialized in strategic planning and project management
@@ -17393,452 +17745,13 @@ function formatAlphaHex$1(opacity) {
17393
17745
  .padStart(2, '0');
17394
17746
  }
17395
17747
 
17396
- /* eslint-disable no-magic-numbers */
17397
- /**
17398
- * Light direction used by the organic 3D octopus shading.
17399
- *
17400
- * @private helper of `octopus3dAvatarVisual`
17401
- */
17402
- const LIGHT_DIRECTION$3 = normalizeVector3({
17403
- x: 0.48,
17404
- y: -0.62,
17405
- z: 0.94,
17406
- });
17407
- /**
17408
- * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
17409
- *
17410
- * @private helper of `octopus3dAvatarVisual`
17411
- */
17412
- const octopus3dStableStateCache = new WeakMap();
17413
- /**
17414
- * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
17415
- *
17416
- * @private helper of `octopus3dAvatarVisual`
17417
- */
17418
- function getOctopus3dStableState(createRandom) {
17419
- const cached = octopus3dStableStateCache.get(createRandom);
17420
- if (cached !== undefined) {
17421
- return cached;
17422
- }
17423
- const animationRandom = createRandom('octopus3d-animation-profile');
17424
- const eyeRandom = createRandom('octopus3d-eye-profile');
17425
- const leftEyePhaseOffset = eyeRandom() * 0.6;
17426
- const rightEyePhaseOffset = eyeRandom() * 0.6;
17427
- const state = {
17428
- morphologyProfile: createOctopus3MorphologyProfile(createRandom),
17429
- animationPhase: animationRandom() * Math.PI * 2,
17430
- leftEyePhaseOffset,
17431
- rightEyePhaseOffset,
17432
- };
17433
- octopus3dStableStateCache.set(createRandom, state);
17434
- return state;
17435
- }
17436
- /**
17437
- * Proper 3D Octopus visual built from projected organic meshes and tentacles.
17438
- *
17439
- * @private built-in avatar visual
17440
- */
17441
- const octopus3dAvatarVisual = {
17442
- id: 'octopus3d',
17443
- title: 'Octopus 3D',
17444
- description: 'Proper 3D octopus portrait with a turning silhouette, expressive eyes, and depth-sorted tentacles.',
17445
- isAnimated: true,
17446
- supportsPointerTracking: true,
17447
- render({ context, size, palette, createRandom, timeMs, interaction }) {
17448
- const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
17449
- const sceneCenterX = size * 0.5;
17450
- const sceneCenterY = size * 0.56;
17451
- const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
17452
- const mantleCenter = {
17453
- x: interaction.bodyOffsetX * size * 0.042 + size * morphologyProfile.body.centerXJitterRatio * 0.65,
17454
- y: -size * 0.09 + interaction.bodyOffsetY * size * 0.028 + bob,
17455
- z: interaction.intensity * size * 0.012,
17456
- };
17457
- const underbodyCenter = {
17458
- x: mantleCenter.x * 0.86,
17459
- y: mantleCenter.y + size * 0.168,
17460
- z: mantleCenter.z - size * 0.018,
17461
- };
17462
- const mantleRadiusX = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch;
17463
- const mantleRadiusY = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1;
17464
- const mantleRadiusZ = size *
17465
- morphologyProfile.body.bodyRadiusRatio *
17466
- (0.9 + (morphologyProfile.body.horizontalStretch - 1) * 0.3);
17467
- const underbodyRadiusX = mantleRadiusX * (0.9 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.08);
17468
- const underbodyRadiusY = mantleRadiusY * (0.44 + morphologyProfile.body.lowerDropRatio * 3.1);
17469
- const underbodyRadiusZ = mantleRadiusZ * 0.78;
17470
- const bodyYaw = -0.18 +
17471
- Math.sin(timeMs / 2400 + animationPhase) * 0.05 +
17472
- interaction.bodyOffsetX * 0.18 +
17473
- interaction.gazeX * 0.22;
17474
- const bodyPitch = -0.08 +
17475
- Math.cos(timeMs / 2700 + animationPhase * 0.6) * 0.025 -
17476
- interaction.bodyOffsetY * 0.08 -
17477
- interaction.gazeY * 0.08;
17478
- const headYaw = bodyYaw - 0.04 + interaction.gazeX * 0.56;
17479
- const headPitch = bodyPitch - 0.02 - interaction.gazeY * 0.32;
17480
- const mantlePatches = resolveVisibleEllipsoidPatches({
17481
- center: mantleCenter,
17482
- radiusX: mantleRadiusX,
17483
- radiusY: mantleRadiusY,
17484
- radiusZ: mantleRadiusZ,
17485
- rotationX: headPitch,
17486
- rotationY: headYaw,
17487
- sceneCenterX,
17488
- sceneCenterY,
17489
- size,
17490
- palette,
17491
- verticalColorBias: 0,
17492
- outlineColor: `${palette.highlight}7a`,
17493
- });
17494
- const underbodyPatches = resolveVisibleEllipsoidPatches({
17495
- center: underbodyCenter,
17496
- radiusX: underbodyRadiusX,
17497
- radiusY: underbodyRadiusY,
17498
- radiusZ: underbodyRadiusZ,
17499
- rotationX: bodyPitch,
17500
- rotationY: bodyYaw,
17501
- sceneCenterX,
17502
- sceneCenterY,
17503
- size,
17504
- palette,
17505
- verticalColorBias: 0.18,
17506
- outlineColor: `${palette.shadow}8f`,
17507
- });
17508
- const tentacleStrokes = createOctopusTentacleStrokes({
17509
- createRandom,
17510
- morphologyProfile,
17511
- timeMs,
17512
- size,
17513
- center: underbodyCenter,
17514
- radiusX: underbodyRadiusX,
17515
- radiusY: underbodyRadiusY,
17516
- radiusZ: underbodyRadiusZ,
17517
- rotationX: bodyPitch,
17518
- rotationY: bodyYaw,
17519
- sceneCenterX,
17520
- sceneCenterY,
17521
- animationPhase,
17522
- });
17523
- const faceEyeSpacing = size * morphologyProfile.face.eyeSpacingRatio * 0.92;
17524
- const faceEyeYOffset = size * morphologyProfile.face.eyeCenterYOffsetRatio - mantleRadiusY * 0.02;
17525
- const faceEyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.82;
17526
- const faceEyeRadiusY = faceEyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.96;
17527
- const mouthHalfWidth = size * morphologyProfile.face.mouthWidthRatio * 0.92;
17528
- const mouthY = size * morphologyProfile.face.mouthYOffsetRatio + mantleRadiusY * 0.08;
17529
- drawAvatarFrame(context, size, palette);
17530
- drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
17531
- drawOctopus3dShadow(context, size, palette, interaction, timeMs);
17532
- for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => !candidateTentacleStroke.isFrontFacing)) {
17533
- drawTentacleStroke(context, tentacleStroke, palette);
17534
- }
17535
- for (const surfacePatch of [...mantlePatches, ...underbodyPatches].sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
17536
- drawSurfacePatch(context, surfacePatch);
17537
- }
17538
- for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
17539
- drawTentacleStroke(context, tentacleStroke, palette);
17540
- }
17541
- drawProjectedOrganicEye(context, {
17542
- x: -faceEyeSpacing,
17543
- y: faceEyeYOffset,
17544
- z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
17545
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
17546
- drawProjectedOrganicEye(context, {
17547
- x: faceEyeSpacing,
17548
- y: faceEyeYOffset,
17549
- z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
17550
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
17551
- drawProjectedOrganicMouth(context, [
17552
- {
17553
- x: -mouthHalfWidth,
17554
- y: mouthY,
17555
- z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -mouthHalfWidth, mouthY),
17556
- },
17557
- {
17558
- x: size * morphologyProfile.face.mouthCenterOffsetRatio,
17559
- y: mouthY +
17560
- size * morphologyProfile.face.mouthCurveDepthRatio * 0.38 +
17561
- Math.sin(timeMs / 760 + animationPhase) * size * 0.01 +
17562
- interaction.gazeY * size * 0.01,
17563
- z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, size * morphologyProfile.face.mouthCenterOffsetRatio, mouthY),
17564
- },
17565
- {
17566
- x: mouthHalfWidth,
17567
- y: mouthY,
17568
- z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, mouthHalfWidth, mouthY),
17569
- },
17570
- ], mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, palette, size);
17571
- },
17572
- };
17573
- /**
17574
- * Draws the atmospheric underwater glow behind the octopus mesh.
17575
- *
17576
- * @private helper of `octopus3dAvatarVisual`
17577
- */
17578
- function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
17579
- 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);
17580
- glowGradient.addColorStop(0, `${palette.highlight}5c`);
17581
- glowGradient.addColorStop(0.36, `${palette.accent}24`);
17582
- glowGradient.addColorStop(1, `${palette.highlight}00`);
17583
- context.fillStyle = glowGradient;
17584
- context.fillRect(0, 0, size, size);
17585
- 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);
17586
- lowerGradient.addColorStop(0, `${palette.secondary}1d`);
17587
- lowerGradient.addColorStop(1, `${palette.secondary}00`);
17588
- context.fillStyle = lowerGradient;
17589
- context.fillRect(0, 0, size, size);
17590
- }
17591
- /**
17592
- * Draws the soft ground shadow below the octopus.
17593
- *
17594
- * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
17595
- * blurry ellipse without triggering a costly software rasterization pass on every frame.
17596
- *
17597
- * @private helper of `octopus3dAvatarVisual`
17598
- */
17599
- function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
17600
- const cx = size * 0.5 + interaction.gazeX * size * 0.04;
17601
- const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
17602
- const rx = size * (0.18 + interaction.intensity * 0.02);
17603
- const ry = size * 0.06;
17604
- context.save();
17605
- context.translate(cx, cy);
17606
- context.scale(1, ry / rx);
17607
- const blurRadius = rx * 1.4;
17608
- const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
17609
- shadowGradient.addColorStop(0, `${palette.shadow}7a`);
17610
- shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
17611
- shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
17612
- shadowGradient.addColorStop(1, `${palette.shadow}00`);
17613
- context.fillStyle = shadowGradient;
17614
- context.beginPath();
17615
- context.arc(0, 0, blurRadius, 0, Math.PI * 2);
17616
- context.fill();
17617
- context.restore();
17618
- }
17619
- /**
17620
- * Resolves visible projected patches for one rotated ellipsoid mesh.
17621
- *
17622
- * @private helper of `octopus3dAvatarVisual`
17623
- */
17624
- function resolveVisibleEllipsoidPatches(options) {
17625
- const { center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, verticalColorBias, outlineColor, } = options;
17626
- const latitudePatchCount = 10;
17627
- const longitudePatchCount = 18;
17628
- const surfacePatches = [];
17629
- for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
17630
- const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
17631
- const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
17632
- const verticalProgress = (latitudeIndex + 0.5) / latitudePatchCount;
17633
- for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
17634
- const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
17635
- const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
17636
- const localCorners = [
17637
- sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, startLongitude),
17638
- sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, endLongitude),
17639
- sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, endLongitude),
17640
- sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, startLongitude),
17641
- ];
17642
- const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
17643
- const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
17644
- if (surfaceNormal.z <= 0.01) {
17645
- continue;
17646
- }
17647
- const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
17648
- surfacePatches.push({
17649
- corners: projectedCorners,
17650
- averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
17651
- transformedCorners.length,
17652
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
17653
- fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
17654
- outlineColor,
17655
- });
17656
- }
17657
- }
17658
- return surfacePatches;
17659
- }
17660
- /**
17661
- * Samples one point on an ellipsoid aligned to the local axes.
17662
- *
17663
- * @private helper of `octopus3dAvatarVisual`
17664
- */
17665
- function sampleEllipsoidPoint(radiusX, radiusY, radiusZ, latitude, longitude) {
17666
- const cosineLatitude = Math.cos(latitude);
17667
- return {
17668
- x: Math.sin(longitude) * cosineLatitude * radiusX,
17669
- y: Math.sin(latitude) * radiusY,
17670
- z: Math.cos(longitude) * cosineLatitude * radiusZ,
17671
- };
17672
- }
17673
- /**
17674
- * Resolves one base fill tone for a surface patch across the octopus body.
17675
- *
17676
- * @private helper of `octopus3dAvatarVisual`
17677
- */
17678
- function resolveSurfacePatchFillStyle(palette, verticalProgress) {
17679
- const clampedVerticalProgress = clampNumber$1(verticalProgress, 0, 1);
17680
- if (clampedVerticalProgress < 0.2) {
17681
- return palette.highlight;
17682
- }
17683
- if (clampedVerticalProgress < 0.45) {
17684
- return palette.secondary;
17685
- }
17686
- if (clampedVerticalProgress < 0.8) {
17687
- return palette.primary;
17688
- }
17689
- return `${palette.shadow}f2`;
17690
- }
17691
- /**
17692
- * Draws one projected mesh patch with organic shading.
17693
- *
17694
- * @private helper of `octopus3dAvatarVisual`
17695
- */
17696
- function drawSurfacePatch(context, surfacePatch) {
17697
- drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
17698
- if (surfacePatch.lightIntensity > 0) {
17699
- drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.15 * surfacePatch.lightIntensity})`);
17700
- }
17701
- else if (surfacePatch.lightIntensity < 0) {
17702
- drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
17703
- }
17704
- context.save();
17705
- context.beginPath();
17706
- context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
17707
- for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
17708
- context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
17709
- }
17710
- context.closePath();
17711
- context.strokeStyle = surfacePatch.outlineColor;
17712
- context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0044);
17713
- context.lineJoin = 'round';
17714
- context.stroke();
17715
- context.restore();
17716
- }
17717
- /**
17718
- * Creates the projected 3D tentacle strokes orbiting around the lower octopus body.
17719
- *
17720
- * @private helper of `octopus3dAvatarVisual`
17721
- */
17722
- function createOctopusTentacleStrokes(options) {
17723
- const { createRandom, morphologyProfile, timeMs, size, center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, animationPhase, } = options;
17724
- return Array.from({ length: morphologyProfile.tentacles.count }, (_, tentacleIndex) => {
17725
- const tentacleRandom = createRandom(`octopus3d-tentacle-${tentacleIndex}`);
17726
- const spreadProgress = morphologyProfile.tentacles.count === 1 ? 0.5 : tentacleIndex / (morphologyProfile.tentacles.count - 1);
17727
- const orbitAngle = -Math.PI * 0.92 + spreadProgress * Math.PI * 1.84 + (tentacleRandom() - 0.5) * 0.16;
17728
- const flowLength = size * (0.19 + morphologyProfile.tentacles.flowLengthScale * 0.075 + tentacleRandom() * 0.018);
17729
- const lateralReach = size *
17730
- (0.08 + morphologyProfile.tentacles.lateralReachScale * 0.05 + Math.abs(Math.sin(orbitAngle)) * 0.018);
17731
- const depthReach = size * (0.028 + morphologyProfile.tentacles.tipReachScale * 0.032);
17732
- const sway = Math.sin(timeMs / (760 + tentacleIndex * 36) + animationPhase + tentacleRandom() * Math.PI * 2);
17733
- const anchorPoint = {
17734
- x: Math.sin(orbitAngle) * radiusX * (0.84 + tentacleRandom() * 0.08),
17735
- y: radiusY * (0.22 + tentacleRandom() * 0.18),
17736
- z: Math.cos(orbitAngle) * radiusZ * (0.72 + tentacleRandom() * 0.12),
17737
- };
17738
- const controlPointOne = {
17739
- x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * 0.44,
17740
- y: anchorPoint.y + flowLength * 0.26,
17741
- z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.3 + sway * size * 0.012,
17742
- };
17743
- const controlPointTwo = {
17744
- x: anchorPoint.x +
17745
- Math.sin(orbitAngle) * lateralReach * (0.82 + morphologyProfile.tentacles.swayScale * 0.12),
17746
- y: anchorPoint.y + flowLength * 0.66,
17747
- z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.72 + sway * size * 0.02,
17748
- };
17749
- const endPoint = {
17750
- x: anchorPoint.x +
17751
- Math.sin(orbitAngle) * lateralReach * (1.02 + morphologyProfile.tentacles.tipWidthScale * 0.12) +
17752
- sway * size * 0.028,
17753
- y: anchorPoint.y + flowLength,
17754
- z: anchorPoint.z + Math.cos(orbitAngle) * depthReach + sway * size * 0.016,
17755
- };
17756
- const scenePoints = Array.from({ length: 12 }, (_, sampleIndex) => transformScenePoint(sampleCubicBezierPoint3D(anchorPoint, controlPointOne, controlPointTwo, endPoint, sampleIndex / 11), center, rotationX, rotationY));
17757
- const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
17758
- const averageDepth = scenePoints.reduce((depthSum, scenePoint) => depthSum + scenePoint.z, 0) / scenePoints.length;
17759
- return {
17760
- projectedPoints,
17761
- averageDepth,
17762
- isFrontFacing: averageDepth >= center.z - size * 0.006,
17763
- baseWidth: size *
17764
- (0.019 +
17765
- morphologyProfile.tentacles.baseWidthScale * 0.007 +
17766
- tentacleRandom() * 0.003 +
17767
- Math.abs(Math.sin(orbitAngle)) * 0.002),
17768
- tipWidth: size * (0.0046 + morphologyProfile.tentacles.tipWidthScale * 0.0018),
17769
- colorBias: tentacleRandom(),
17770
- };
17771
- });
17772
- }
17773
- /**
17774
- * Samples one point on a cubic Bezier curve in 3D.
17775
- *
17776
- * @private helper of `octopus3dAvatarVisual`
17777
- */
17778
- function sampleCubicBezierPoint3D(startPoint, controlPointOne, controlPointTwo, endPoint, progress) {
17779
- const inverseProgress = 1 - progress;
17780
- return {
17781
- x: inverseProgress * inverseProgress * inverseProgress * startPoint.x +
17782
- 3 * inverseProgress * inverseProgress * progress * controlPointOne.x +
17783
- 3 * inverseProgress * progress * progress * controlPointTwo.x +
17784
- progress * progress * progress * endPoint.x,
17785
- y: inverseProgress * inverseProgress * inverseProgress * startPoint.y +
17786
- 3 * inverseProgress * inverseProgress * progress * controlPointOne.y +
17787
- 3 * inverseProgress * progress * progress * controlPointTwo.y +
17788
- progress * progress * progress * endPoint.y,
17789
- z: inverseProgress * inverseProgress * inverseProgress * startPoint.z +
17790
- 3 * inverseProgress * inverseProgress * progress * controlPointOne.z +
17791
- 3 * inverseProgress * progress * progress * controlPointTwo.z +
17792
- progress * progress * progress * endPoint.z,
17793
- };
17794
- }
17795
- /**
17796
- * Draws one projected tentacle stroke with a slim highlight ridge.
17797
- *
17798
- * @private helper of `octopus3dAvatarVisual`
17799
- */
17800
- function drawTentacleStroke(context, tentacleStroke, palette) {
17801
- const projectedSegments = tentacleStroke.projectedPoints.length - 1;
17802
- for (let segmentIndex = 0; segmentIndex < projectedSegments; segmentIndex++) {
17803
- const startPoint = tentacleStroke.projectedPoints[segmentIndex];
17804
- const endPoint = tentacleStroke.projectedPoints[segmentIndex + 1];
17805
- const progress = segmentIndex / projectedSegments;
17806
- const width = tentacleStroke.baseWidth + (tentacleStroke.tipWidth - tentacleStroke.baseWidth) * progress;
17807
- context.beginPath();
17808
- context.moveTo(startPoint.x, startPoint.y);
17809
- context.lineTo(endPoint.x, endPoint.y);
17810
- context.strokeStyle = tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
17811
- context.lineWidth = width;
17812
- context.lineCap = 'round';
17813
- context.stroke();
17814
- context.beginPath();
17815
- context.moveTo(startPoint.x, startPoint.y);
17816
- context.lineTo(endPoint.x, endPoint.y);
17817
- context.strokeStyle = tentacleStroke.isFrontFacing ? `${palette.highlight}80` : `${palette.highlight}40`;
17818
- context.lineWidth = Math.max(1, width * 0.34);
17819
- context.lineCap = 'round';
17820
- context.stroke();
17821
- }
17822
- }
17823
- /**
17824
- * Resolves the front surface depth on an ellipsoid for one local face point.
17825
- *
17826
- * @private helper of `octopus3dAvatarVisual`
17827
- */
17828
- function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
17829
- const normalizedX = x / radiusX;
17830
- const normalizedY = y / radiusY;
17831
- const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
17832
- return Math.sqrt(remainingDepthRatio) * radiusZ;
17833
- }
17834
-
17835
17748
  /* eslint-disable no-magic-numbers */
17836
17749
  /**
17837
17750
  * Light direction used by the single-mesh octopus shading.
17838
17751
  *
17839
17752
  * @private helper of `octopus3d2AvatarVisual`
17840
17753
  */
17841
- const LIGHT_DIRECTION$2 = normalizeVector3({
17754
+ const LIGHT_DIRECTION$3 = normalizeVector3({
17842
17755
  x: 0.38,
17843
17756
  y: -0.6,
17844
17757
  z: 0.98,
@@ -18077,7 +17990,7 @@ function resolveVisibleBlobbyOctopusPatches(options) {
18077
17990
  transformedCorners[2].z +
18078
17991
  transformedCorners[3].z) /
18079
17992
  4,
18080
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
17993
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
18081
17994
  fillStyle: resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
18082
17995
  outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
18083
17996
  });
@@ -18192,7 +18105,7 @@ function drawBlobbySurfacePatch(context, surfacePatch) {
18192
18105
  *
18193
18106
  * @private helper of `octopus3d3AvatarVisual`
18194
18107
  */
18195
- const LIGHT_DIRECTION$1 = normalizeVector3({
18108
+ const LIGHT_DIRECTION$2 = normalizeVector3({
18196
18109
  x: 0.34,
18197
18110
  y: -0.62,
18198
18111
  z: 1,
@@ -18499,7 +18412,7 @@ function resolveVisibleContinuousOctopusPatches(options) {
18499
18412
  transformedCorners[2].z +
18500
18413
  transformedCorners[3].z) /
18501
18414
  4,
18502
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
18415
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
18503
18416
  fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
18504
18417
  outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
18505
18418
  });
@@ -18814,7 +18727,7 @@ function smoothStep$1(edgeStart, edgeEnd, value) {
18814
18727
  *
18815
18728
  * @private helper of `octopus3d4AvatarVisual`
18816
18729
  */
18817
- const LIGHT_DIRECTION = normalizeVector3({
18730
+ const LIGHT_DIRECTION$1 = normalizeVector3({
18818
18731
  x: 0.32,
18819
18732
  y: -0.66,
18820
18733
  z: 1,
@@ -18885,7 +18798,7 @@ const octopus3d4AvatarVisual = {
18885
18798
  isAnimated: true,
18886
18799
  supportsPointerTracking: true,
18887
18800
  render({ context, size, palette, createRandom, timeMs, interaction }) {
18888
- const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots } = getOctopus3d4StableState(createRandom);
18801
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots, } = getOctopus3d4StableState(createRandom);
18889
18802
  const sceneCenterX = size * 0.5;
18890
18803
  const sceneCenterY = size * 0.535;
18891
18804
  const bob = Math.sin(timeMs / 980 + animationPhase) * size * 0.013;
@@ -19160,7 +19073,7 @@ function resolveVisibleBlobbyContinuousPatches(options) {
19160
19073
  projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
19161
19074
  projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
19162
19075
  ];
19163
- const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1);
19076
+ const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1);
19164
19077
  const rimLightIntensity = Math.pow(clampNumber$1(dotProduct3D(surfaceNormal, RIM_LIGHT_DIRECTION), 0, 1), 2.6);
19165
19078
  surfacePatches.push({
19166
19079
  corners: projectedCorners,
@@ -19233,10 +19146,7 @@ function sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude,
19233
19146
  Math.max(0, Math.cos(effectiveLongitude)) * 0.12 +
19234
19147
  lowerBlend * tentacleInfluence.core * (0.12 + tentacleInfluence.depthScale * 0.07) -
19235
19148
  Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
19236
- const tentacleTubeRadius = lowerBlend *
19237
- tentacleInfluence.core *
19238
- (0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) *
19239
- radiusX;
19149
+ const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) * radiusX;
19240
19150
  const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
19241
19151
  const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.74;
19242
19152
  const lowerDrop = lowerBlend *
@@ -19248,9 +19158,7 @@ function sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude,
19248
19158
  (morphologyProfile.tentacles.flowLengthScale - 1) * 0.1));
19249
19159
  const combinedTentacleSway = (primaryTentacleWave + secondaryTentacleWave) * radiusX * (0.06 + tipBlend * 0.06);
19250
19160
  return {
19251
- x: Math.sin(effectiveLongitude) * planarRadiusX +
19252
- combinedTentacleSway +
19253
- tentacleCurl * radiusX * 0.18,
19161
+ x: Math.sin(effectiveLongitude) * planarRadiusX + combinedTentacleSway + tentacleCurl * radiusX * 0.18,
19254
19162
  y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
19255
19163
  upperBlend * radiusY * 0.12 +
19256
19164
  lowerDrop +
@@ -19564,6 +19472,445 @@ function formatAlphaHex(opacity) {
19564
19472
  .padStart(2, '0');
19565
19473
  }
19566
19474
 
19475
+ /* eslint-disable no-magic-numbers */
19476
+ /**
19477
+ * Light direction used by the organic 3D octopus shading.
19478
+ *
19479
+ * @private helper of `octopus3dAvatarVisual`
19480
+ */
19481
+ const LIGHT_DIRECTION = normalizeVector3({
19482
+ x: 0.48,
19483
+ y: -0.62,
19484
+ z: 0.94,
19485
+ });
19486
+ /**
19487
+ * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
19488
+ *
19489
+ * @private helper of `octopus3dAvatarVisual`
19490
+ */
19491
+ const octopus3dStableStateCache = new WeakMap();
19492
+ /**
19493
+ * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
19494
+ *
19495
+ * @private helper of `octopus3dAvatarVisual`
19496
+ */
19497
+ function getOctopus3dStableState(createRandom) {
19498
+ const cached = octopus3dStableStateCache.get(createRandom);
19499
+ if (cached !== undefined) {
19500
+ return cached;
19501
+ }
19502
+ const animationRandom = createRandom('octopus3d-animation-profile');
19503
+ const eyeRandom = createRandom('octopus3d-eye-profile');
19504
+ const leftEyePhaseOffset = eyeRandom() * 0.6;
19505
+ const rightEyePhaseOffset = eyeRandom() * 0.6;
19506
+ const state = {
19507
+ morphologyProfile: createOctopus3MorphologyProfile(createRandom),
19508
+ animationPhase: animationRandom() * Math.PI * 2,
19509
+ leftEyePhaseOffset,
19510
+ rightEyePhaseOffset,
19511
+ };
19512
+ octopus3dStableStateCache.set(createRandom, state);
19513
+ return state;
19514
+ }
19515
+ /**
19516
+ * Proper 3D Octopus visual built from projected organic meshes and tentacles.
19517
+ *
19518
+ * @private built-in avatar visual
19519
+ */
19520
+ const octopus3dAvatarVisual = {
19521
+ id: 'octopus3d',
19522
+ title: 'Octopus 3D',
19523
+ description: 'Proper 3D octopus portrait with a turning silhouette, expressive eyes, and depth-sorted tentacles.',
19524
+ isAnimated: true,
19525
+ supportsPointerTracking: true,
19526
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
19527
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
19528
+ const sceneCenterX = size * 0.5;
19529
+ const sceneCenterY = size * 0.56;
19530
+ const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
19531
+ const mantleCenter = {
19532
+ x: interaction.bodyOffsetX * size * 0.042 + size * morphologyProfile.body.centerXJitterRatio * 0.65,
19533
+ y: -size * 0.09 + interaction.bodyOffsetY * size * 0.028 + bob,
19534
+ z: interaction.intensity * size * 0.012,
19535
+ };
19536
+ const underbodyCenter = {
19537
+ x: mantleCenter.x * 0.86,
19538
+ y: mantleCenter.y + size * 0.168,
19539
+ z: mantleCenter.z - size * 0.018,
19540
+ };
19541
+ const mantleRadiusX = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch;
19542
+ const mantleRadiusY = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1;
19543
+ const mantleRadiusZ = size *
19544
+ morphologyProfile.body.bodyRadiusRatio *
19545
+ (0.9 + (morphologyProfile.body.horizontalStretch - 1) * 0.3);
19546
+ const underbodyRadiusX = mantleRadiusX * (0.9 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.08);
19547
+ const underbodyRadiusY = mantleRadiusY * (0.44 + morphologyProfile.body.lowerDropRatio * 3.1);
19548
+ const underbodyRadiusZ = mantleRadiusZ * 0.78;
19549
+ const bodyYaw = -0.18 +
19550
+ Math.sin(timeMs / 2400 + animationPhase) * 0.05 +
19551
+ interaction.bodyOffsetX * 0.18 +
19552
+ interaction.gazeX * 0.22;
19553
+ const bodyPitch = -0.08 +
19554
+ Math.cos(timeMs / 2700 + animationPhase * 0.6) * 0.025 -
19555
+ interaction.bodyOffsetY * 0.08 -
19556
+ interaction.gazeY * 0.08;
19557
+ const headYaw = bodyYaw - 0.04 + interaction.gazeX * 0.56;
19558
+ const headPitch = bodyPitch - 0.02 - interaction.gazeY * 0.32;
19559
+ const mantlePatches = resolveVisibleEllipsoidPatches({
19560
+ center: mantleCenter,
19561
+ radiusX: mantleRadiusX,
19562
+ radiusY: mantleRadiusY,
19563
+ radiusZ: mantleRadiusZ,
19564
+ rotationX: headPitch,
19565
+ rotationY: headYaw,
19566
+ sceneCenterX,
19567
+ sceneCenterY,
19568
+ size,
19569
+ palette,
19570
+ verticalColorBias: 0,
19571
+ outlineColor: `${palette.highlight}7a`,
19572
+ });
19573
+ const underbodyPatches = resolveVisibleEllipsoidPatches({
19574
+ center: underbodyCenter,
19575
+ radiusX: underbodyRadiusX,
19576
+ radiusY: underbodyRadiusY,
19577
+ radiusZ: underbodyRadiusZ,
19578
+ rotationX: bodyPitch,
19579
+ rotationY: bodyYaw,
19580
+ sceneCenterX,
19581
+ sceneCenterY,
19582
+ size,
19583
+ palette,
19584
+ verticalColorBias: 0.18,
19585
+ outlineColor: `${palette.shadow}8f`,
19586
+ });
19587
+ const tentacleStrokes = createOctopusTentacleStrokes({
19588
+ createRandom,
19589
+ morphologyProfile,
19590
+ timeMs,
19591
+ size,
19592
+ center: underbodyCenter,
19593
+ radiusX: underbodyRadiusX,
19594
+ radiusY: underbodyRadiusY,
19595
+ radiusZ: underbodyRadiusZ,
19596
+ rotationX: bodyPitch,
19597
+ rotationY: bodyYaw,
19598
+ sceneCenterX,
19599
+ sceneCenterY,
19600
+ animationPhase,
19601
+ });
19602
+ const faceEyeSpacing = size * morphologyProfile.face.eyeSpacingRatio * 0.92;
19603
+ const faceEyeYOffset = size * morphologyProfile.face.eyeCenterYOffsetRatio - mantleRadiusY * 0.02;
19604
+ const faceEyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.82;
19605
+ const faceEyeRadiusY = faceEyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.96;
19606
+ const mouthHalfWidth = size * morphologyProfile.face.mouthWidthRatio * 0.92;
19607
+ const mouthY = size * morphologyProfile.face.mouthYOffsetRatio + mantleRadiusY * 0.08;
19608
+ drawAvatarFrame(context, size, palette);
19609
+ drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
19610
+ drawOctopus3dShadow(context, size, palette, interaction, timeMs);
19611
+ for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => !candidateTentacleStroke.isFrontFacing)) {
19612
+ drawTentacleStroke(context, tentacleStroke, palette);
19613
+ }
19614
+ for (const surfacePatch of [...mantlePatches, ...underbodyPatches].sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
19615
+ drawSurfacePatch(context, surfacePatch);
19616
+ }
19617
+ for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
19618
+ drawTentacleStroke(context, tentacleStroke, palette);
19619
+ }
19620
+ drawProjectedOrganicEye(context, {
19621
+ x: -faceEyeSpacing,
19622
+ y: faceEyeYOffset,
19623
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
19624
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
19625
+ drawProjectedOrganicEye(context, {
19626
+ x: faceEyeSpacing,
19627
+ y: faceEyeYOffset,
19628
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
19629
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
19630
+ drawProjectedOrganicMouth(context, [
19631
+ {
19632
+ x: -mouthHalfWidth,
19633
+ y: mouthY,
19634
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -mouthHalfWidth, mouthY),
19635
+ },
19636
+ {
19637
+ x: size * morphologyProfile.face.mouthCenterOffsetRatio,
19638
+ y: mouthY +
19639
+ size * morphologyProfile.face.mouthCurveDepthRatio * 0.38 +
19640
+ Math.sin(timeMs / 760 + animationPhase) * size * 0.01 +
19641
+ interaction.gazeY * size * 0.01,
19642
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, size * morphologyProfile.face.mouthCenterOffsetRatio, mouthY),
19643
+ },
19644
+ {
19645
+ x: mouthHalfWidth,
19646
+ y: mouthY,
19647
+ z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, mouthHalfWidth, mouthY),
19648
+ },
19649
+ ], mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, palette, size);
19650
+ },
19651
+ };
19652
+ /**
19653
+ * Draws the atmospheric underwater glow behind the octopus mesh.
19654
+ *
19655
+ * @private helper of `octopus3dAvatarVisual`
19656
+ */
19657
+ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
19658
+ 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);
19659
+ glowGradient.addColorStop(0, `${palette.highlight}5c`);
19660
+ glowGradient.addColorStop(0.36, `${palette.accent}24`);
19661
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
19662
+ context.fillStyle = glowGradient;
19663
+ context.fillRect(0, 0, size, size);
19664
+ 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);
19665
+ lowerGradient.addColorStop(0, `${palette.secondary}1d`);
19666
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
19667
+ context.fillStyle = lowerGradient;
19668
+ context.fillRect(0, 0, size, size);
19669
+ }
19670
+ /**
19671
+ * Draws the soft ground shadow below the octopus.
19672
+ *
19673
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
19674
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
19675
+ *
19676
+ * @private helper of `octopus3dAvatarVisual`
19677
+ */
19678
+ function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
19679
+ const cx = size * 0.5 + interaction.gazeX * size * 0.04;
19680
+ const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
19681
+ const rx = size * (0.18 + interaction.intensity * 0.02);
19682
+ const ry = size * 0.06;
19683
+ context.save();
19684
+ context.translate(cx, cy);
19685
+ context.scale(1, ry / rx);
19686
+ const blurRadius = rx * 1.4;
19687
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
19688
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
19689
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
19690
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
19691
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
19692
+ context.fillStyle = shadowGradient;
19693
+ context.beginPath();
19694
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
19695
+ context.fill();
19696
+ context.restore();
19697
+ }
19698
+ /**
19699
+ * Resolves visible projected patches for one rotated ellipsoid mesh.
19700
+ *
19701
+ * @private helper of `octopus3dAvatarVisual`
19702
+ */
19703
+ function resolveVisibleEllipsoidPatches(options) {
19704
+ const { center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, verticalColorBias, outlineColor, } = options;
19705
+ const latitudePatchCount = 10;
19706
+ const longitudePatchCount = 18;
19707
+ const surfacePatches = [];
19708
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
19709
+ const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
19710
+ const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
19711
+ const verticalProgress = (latitudeIndex + 0.5) / latitudePatchCount;
19712
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
19713
+ const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
19714
+ const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
19715
+ const localCorners = [
19716
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, startLongitude),
19717
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, endLongitude),
19718
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, endLongitude),
19719
+ sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, startLongitude),
19720
+ ];
19721
+ const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
19722
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
19723
+ if (surfaceNormal.z <= 0.01) {
19724
+ continue;
19725
+ }
19726
+ const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
19727
+ surfacePatches.push({
19728
+ corners: projectedCorners,
19729
+ averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
19730
+ transformedCorners.length,
19731
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
19732
+ fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
19733
+ outlineColor,
19734
+ });
19735
+ }
19736
+ }
19737
+ return surfacePatches;
19738
+ }
19739
+ /**
19740
+ * Samples one point on an ellipsoid aligned to the local axes.
19741
+ *
19742
+ * @private helper of `octopus3dAvatarVisual`
19743
+ */
19744
+ function sampleEllipsoidPoint(radiusX, radiusY, radiusZ, latitude, longitude) {
19745
+ const cosineLatitude = Math.cos(latitude);
19746
+ return {
19747
+ x: Math.sin(longitude) * cosineLatitude * radiusX,
19748
+ y: Math.sin(latitude) * radiusY,
19749
+ z: Math.cos(longitude) * cosineLatitude * radiusZ,
19750
+ };
19751
+ }
19752
+ /**
19753
+ * Resolves one base fill tone for a surface patch across the octopus body.
19754
+ *
19755
+ * @private helper of `octopus3dAvatarVisual`
19756
+ */
19757
+ function resolveSurfacePatchFillStyle(palette, verticalProgress) {
19758
+ const clampedVerticalProgress = clampNumber$1(verticalProgress, 0, 1);
19759
+ if (clampedVerticalProgress < 0.2) {
19760
+ return palette.highlight;
19761
+ }
19762
+ if (clampedVerticalProgress < 0.45) {
19763
+ return palette.secondary;
19764
+ }
19765
+ if (clampedVerticalProgress < 0.8) {
19766
+ return palette.primary;
19767
+ }
19768
+ return `${palette.shadow}f2`;
19769
+ }
19770
+ /**
19771
+ * Draws one projected mesh patch with organic shading.
19772
+ *
19773
+ * @private helper of `octopus3dAvatarVisual`
19774
+ */
19775
+ function drawSurfacePatch(context, surfacePatch) {
19776
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
19777
+ if (surfacePatch.lightIntensity > 0) {
19778
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.15 * surfacePatch.lightIntensity})`);
19779
+ }
19780
+ else if (surfacePatch.lightIntensity < 0) {
19781
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
19782
+ }
19783
+ context.save();
19784
+ context.beginPath();
19785
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
19786
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
19787
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
19788
+ }
19789
+ context.closePath();
19790
+ context.strokeStyle = surfacePatch.outlineColor;
19791
+ context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0044);
19792
+ context.lineJoin = 'round';
19793
+ context.stroke();
19794
+ context.restore();
19795
+ }
19796
+ /**
19797
+ * Creates the projected 3D tentacle strokes orbiting around the lower octopus body.
19798
+ *
19799
+ * @private helper of `octopus3dAvatarVisual`
19800
+ */
19801
+ function createOctopusTentacleStrokes(options) {
19802
+ const { createRandom, morphologyProfile, timeMs, size, center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, animationPhase, } = options;
19803
+ return Array.from({ length: morphologyProfile.tentacles.count }, (_, tentacleIndex) => {
19804
+ const tentacleRandom = createRandom(`octopus3d-tentacle-${tentacleIndex}`);
19805
+ const spreadProgress = morphologyProfile.tentacles.count === 1 ? 0.5 : tentacleIndex / (morphologyProfile.tentacles.count - 1);
19806
+ const orbitAngle = -Math.PI * 0.92 + spreadProgress * Math.PI * 1.84 + (tentacleRandom() - 0.5) * 0.16;
19807
+ const flowLength = size * (0.19 + morphologyProfile.tentacles.flowLengthScale * 0.075 + tentacleRandom() * 0.018);
19808
+ const lateralReach = size *
19809
+ (0.08 + morphologyProfile.tentacles.lateralReachScale * 0.05 + Math.abs(Math.sin(orbitAngle)) * 0.018);
19810
+ const depthReach = size * (0.028 + morphologyProfile.tentacles.tipReachScale * 0.032);
19811
+ const sway = Math.sin(timeMs / (760 + tentacleIndex * 36) + animationPhase + tentacleRandom() * Math.PI * 2);
19812
+ const anchorPoint = {
19813
+ x: Math.sin(orbitAngle) * radiusX * (0.84 + tentacleRandom() * 0.08),
19814
+ y: radiusY * (0.22 + tentacleRandom() * 0.18),
19815
+ z: Math.cos(orbitAngle) * radiusZ * (0.72 + tentacleRandom() * 0.12),
19816
+ };
19817
+ const controlPointOne = {
19818
+ x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * 0.44,
19819
+ y: anchorPoint.y + flowLength * 0.26,
19820
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.3 + sway * size * 0.012,
19821
+ };
19822
+ const controlPointTwo = {
19823
+ x: anchorPoint.x +
19824
+ Math.sin(orbitAngle) * lateralReach * (0.82 + morphologyProfile.tentacles.swayScale * 0.12),
19825
+ y: anchorPoint.y + flowLength * 0.66,
19826
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.72 + sway * size * 0.02,
19827
+ };
19828
+ const endPoint = {
19829
+ x: anchorPoint.x +
19830
+ Math.sin(orbitAngle) * lateralReach * (1.02 + morphologyProfile.tentacles.tipWidthScale * 0.12) +
19831
+ sway * size * 0.028,
19832
+ y: anchorPoint.y + flowLength,
19833
+ z: anchorPoint.z + Math.cos(orbitAngle) * depthReach + sway * size * 0.016,
19834
+ };
19835
+ const scenePoints = Array.from({ length: 12 }, (_, sampleIndex) => transformScenePoint(sampleCubicBezierPoint3D(anchorPoint, controlPointOne, controlPointTwo, endPoint, sampleIndex / 11), center, rotationX, rotationY));
19836
+ const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
19837
+ const averageDepth = scenePoints.reduce((depthSum, scenePoint) => depthSum + scenePoint.z, 0) / scenePoints.length;
19838
+ return {
19839
+ projectedPoints,
19840
+ averageDepth,
19841
+ isFrontFacing: averageDepth >= center.z - size * 0.006,
19842
+ baseWidth: size *
19843
+ (0.019 +
19844
+ morphologyProfile.tentacles.baseWidthScale * 0.007 +
19845
+ tentacleRandom() * 0.003 +
19846
+ Math.abs(Math.sin(orbitAngle)) * 0.002),
19847
+ tipWidth: size * (0.0046 + morphologyProfile.tentacles.tipWidthScale * 0.0018),
19848
+ colorBias: tentacleRandom(),
19849
+ };
19850
+ });
19851
+ }
19852
+ /**
19853
+ * Samples one point on a cubic Bezier curve in 3D.
19854
+ *
19855
+ * @private helper of `octopus3dAvatarVisual`
19856
+ */
19857
+ function sampleCubicBezierPoint3D(startPoint, controlPointOne, controlPointTwo, endPoint, progress) {
19858
+ const inverseProgress = 1 - progress;
19859
+ return {
19860
+ x: inverseProgress * inverseProgress * inverseProgress * startPoint.x +
19861
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.x +
19862
+ 3 * inverseProgress * progress * progress * controlPointTwo.x +
19863
+ progress * progress * progress * endPoint.x,
19864
+ y: inverseProgress * inverseProgress * inverseProgress * startPoint.y +
19865
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.y +
19866
+ 3 * inverseProgress * progress * progress * controlPointTwo.y +
19867
+ progress * progress * progress * endPoint.y,
19868
+ z: inverseProgress * inverseProgress * inverseProgress * startPoint.z +
19869
+ 3 * inverseProgress * inverseProgress * progress * controlPointOne.z +
19870
+ 3 * inverseProgress * progress * progress * controlPointTwo.z +
19871
+ progress * progress * progress * endPoint.z,
19872
+ };
19873
+ }
19874
+ /**
19875
+ * Draws one projected tentacle stroke with a slim highlight ridge.
19876
+ *
19877
+ * @private helper of `octopus3dAvatarVisual`
19878
+ */
19879
+ function drawTentacleStroke(context, tentacleStroke, palette) {
19880
+ const projectedSegments = tentacleStroke.projectedPoints.length - 1;
19881
+ for (let segmentIndex = 0; segmentIndex < projectedSegments; segmentIndex++) {
19882
+ const startPoint = tentacleStroke.projectedPoints[segmentIndex];
19883
+ const endPoint = tentacleStroke.projectedPoints[segmentIndex + 1];
19884
+ const progress = segmentIndex / projectedSegments;
19885
+ const width = tentacleStroke.baseWidth + (tentacleStroke.tipWidth - tentacleStroke.baseWidth) * progress;
19886
+ context.beginPath();
19887
+ context.moveTo(startPoint.x, startPoint.y);
19888
+ context.lineTo(endPoint.x, endPoint.y);
19889
+ context.strokeStyle = tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
19890
+ context.lineWidth = width;
19891
+ context.lineCap = 'round';
19892
+ context.stroke();
19893
+ context.beginPath();
19894
+ context.moveTo(startPoint.x, startPoint.y);
19895
+ context.lineTo(endPoint.x, endPoint.y);
19896
+ context.strokeStyle = tentacleStroke.isFrontFacing ? `${palette.highlight}80` : `${palette.highlight}40`;
19897
+ context.lineWidth = Math.max(1, width * 0.34);
19898
+ context.lineCap = 'round';
19899
+ context.stroke();
19900
+ }
19901
+ }
19902
+ /**
19903
+ * Resolves the front surface depth on an ellipsoid for one local face point.
19904
+ *
19905
+ * @private helper of `octopus3dAvatarVisual`
19906
+ */
19907
+ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
19908
+ const normalizedX = x / radiusX;
19909
+ const normalizedY = y / radiusY;
19910
+ const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
19911
+ return Math.sqrt(remainingDepthRatio) * radiusZ;
19912
+ }
19913
+
19567
19914
  /* eslint-disable no-magic-numbers */
19568
19915
  /**
19569
19916
  * Octopus avatar visual.
@@ -20384,14 +20731,14 @@ function resolveAvatarVisualId(value) {
20384
20731
  /**
20385
20732
  * META AVATAR commitment definition
20386
20733
  *
20387
- * The `META AVATAR` commitment sets the built-in default avatar visual used when
20388
- * the agent does not provide an explicit `META IMAGE`.
20734
+ * The `META AVATAR` and `META VISUAL` commitments set the built-in default avatar visual
20735
+ * used when the agent does not provide an explicit `META IMAGE`.
20389
20736
  *
20390
20737
  * @private [🪔] Maybe export the commitments through some package
20391
20738
  */
20392
20739
  class MetaAvatarCommitmentDefinition extends BaseCommitmentDefinition {
20393
- constructor() {
20394
- super('META AVATAR');
20740
+ constructor(type = 'META AVATAR') {
20741
+ super(type);
20395
20742
  }
20396
20743
  /**
20397
20744
  * Short one-line description of META AVATAR.
@@ -20411,14 +20758,15 @@ class MetaAvatarCommitmentDefinition extends BaseCommitmentDefinition {
20411
20758
  get documentation() {
20412
20759
  const supportedVisuals = AVATAR_VISUALS.map((avatarVisual) => `\`${avatarVisual.id}\``).join(', ');
20413
20760
  return spaceTrim$1(`
20414
- # META AVATAR
20761
+ # ${this.type}
20415
20762
 
20416
20763
  Sets the built-in avatar visual used for the agent when no explicit \`META IMAGE\` is provided.
20764
+ \`META AVATAR\` and \`META VISUAL\` are equivalent.
20417
20765
 
20418
20766
  ## Key aspects
20419
20767
 
20420
20768
  - Does not modify the agent's behavior or responses.
20421
- - Only one \`META AVATAR\` should be used per agent.
20769
+ - Only one \`META AVATAR\` or \`META VISUAL\` should be used per agent.
20422
20770
  - If multiple are specified, the last one takes precedence.
20423
20771
  - Values are matched case-insensitively and spaces, hyphens, and underscores are normalized.
20424
20772
  - Supported visuals are derived from the shared avatar registry: ${supportedVisuals}.
@@ -20433,15 +20781,15 @@ class MetaAvatarCommitmentDefinition extends BaseCommitmentDefinition {
20433
20781
  \`\`\`
20434
20782
 
20435
20783
  \`\`\`book
20436
- Orb Assistant
20784
+ Minecraft Assistant
20437
20785
 
20438
- META AVATAR orb
20786
+ META VISUAL Minecraft2
20439
20787
  GOAL Answer in a calm and focused way.
20440
20788
  \`\`\`
20441
20789
  `);
20442
20790
  }
20443
20791
  applyToAgentModelRequirements(requirements, content) {
20444
- // META AVATAR doesn't modify the system message or model requirements.
20792
+ // META AVATAR and META VISUAL don't modify the system message or model requirements.
20445
20793
  // It's handled separately in the parsing logic for profile avatar resolution.
20446
20794
  return requirements;
20447
20795
  }
@@ -22420,10 +22768,10 @@ const teamToolTitles = {};
22420
22768
  *
22421
22769
  * @private
22422
22770
  */
22423
- const TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES = [
22424
- '- If a teammate is relevant to the request, consult that teammate using the matching tool.',
22425
- '- Do not ask the user for information that a listed teammate can provide directly.',
22426
- ];
22771
+ const TEAM_SYSTEM_MESSAGE_GUIDANCE = spaceTrim$1(`
22772
+ - If a teammate is relevant to the request, consult that teammate using the matching tool.
22773
+ - Do not ask the user for information that a listed teammate can provide directly.
22774
+ `);
22427
22775
  /**
22428
22776
  * Constant for remote agents by Url.
22429
22777
  */
@@ -22605,7 +22953,7 @@ function buildTeamSystemMessageBody(teamEntries) {
22605
22953
  `);
22606
22954
  });
22607
22955
  return spaceTrim$1((block) => `
22608
- ${block(TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES.join('\n'))}
22956
+ ${block(TEAM_SYSTEM_MESSAGE_GUIDANCE)}
22609
22957
 
22610
22958
  ${block(teammateSections.join('\n\n'))}
22611
22959
  `);
@@ -29629,6 +29977,7 @@ const COMMITMENT_REGISTRY = [
29629
29977
  new ActionCommitmentDefinition('ACTIONS'),
29630
29978
  new ComponentCommitmentDefinition(),
29631
29979
  new MetaAvatarCommitmentDefinition(),
29980
+ new MetaAvatarCommitmentDefinition('META VISUAL'),
29632
29981
  new MetaImageCommitmentDefinition(),
29633
29982
  new MetaColorCommitmentDefinition(),
29634
29983
  new MetaFontCommitmentDefinition(),
@@ -30980,6 +31329,7 @@ function hasHttpProtocol(value) {
30980
31329
  */
30981
31330
  const META_COMMITMENT_APPLIERS = {
30982
31331
  'META AVATAR': applyMetaAvatarContent,
31332
+ 'META VISUAL': applyMetaAvatarContent,
30983
31333
  'META LINK': applyMetaLinkContent,
30984
31334
  'META DOMAIN': applyMetaDomainContent,
30985
31335
  'META IMAGE': applyMetaImageContent,
@@ -31028,7 +31378,7 @@ function applyGenericMetaCommitment(state, content) {
31028
31378
  state.meta[metaType] = metaValue;
31029
31379
  }
31030
31380
  /**
31031
- * Applies META AVATAR content into the canonical `meta.avatar` field.
31381
+ * Applies META AVATAR / META VISUAL content into the canonical `meta.avatar` field.
31032
31382
  */
31033
31383
  function applyMetaAvatarContent(state, content) {
31034
31384
  const avatarVisualId = resolveAvatarVisualId(content);
@@ -32092,6 +32442,78 @@ function normalizeCliAgentFileSegment(value) {
32092
32442
  .replace(/^[._-]+|[._-]+$/gu, '');
32093
32443
  }
32094
32444
 
32445
+ /**
32446
+ * Helper of usage compute
32447
+ *
32448
+ * @param content the content of prompt or response
32449
+ * @returns part of UsageCounts
32450
+ *
32451
+ * @private internal utility of LlmExecutionTools
32452
+ */
32453
+ function computeUsageCounts(content) {
32454
+ return {
32455
+ charactersCount: { value: countCharacters(content) },
32456
+ wordsCount: { value: countWords(content) },
32457
+ sentencesCount: { value: countSentences(content) },
32458
+ linesCount: { value: countLines(content) },
32459
+ paragraphsCount: { value: countParagraphs(content) },
32460
+ pagesCount: { value: countPages(content) },
32461
+ };
32462
+ }
32463
+
32464
+ /**
32465
+ * Computes the usage of the OpenAI API based on the response from OpenAI
32466
+ *
32467
+ * @param promptContent The content of the prompt
32468
+ * @param resultContent The content of the result (for embedding prompts or failed prompts pass empty string)
32469
+ * @param rawResponse The raw response from OpenAI API
32470
+ * @param duration The duration of the execution
32471
+ * @throws {PipelineExecutionError} If the usage is not defined in the response from OpenAI
32472
+ *
32473
+ * @private internal utility of `OpenAiExecutionTools`
32474
+ */
32475
+ function computeOpenAiUsage(promptContent, // <- Note: Intentionally using [] to access type properties to bring jsdoc from Prompt/PromptResult to consumer
32476
+ resultContent, rawResponse, duration = ZERO_VALUE) {
32477
+ var _a, _b;
32478
+ if (rawResponse.usage === undefined) {
32479
+ throw new PipelineExecutionError('The usage is not defined in the response from OpenAI');
32480
+ }
32481
+ if (((_a = rawResponse.usage) === null || _a === void 0 ? void 0 : _a.prompt_tokens) === undefined) {
32482
+ throw new PipelineExecutionError('In OpenAI response `usage.prompt_tokens` not defined');
32483
+ }
32484
+ const inputTokens = rawResponse.usage.prompt_tokens;
32485
+ const outputTokens = ((_b = rawResponse.usage) === null || _b === void 0 ? void 0 : _b.completion_tokens) || 0;
32486
+ let isUncertain = false;
32487
+ let modelInfo = OPENAI_MODELS.find((model) => model.modelName === rawResponse.model);
32488
+ if (modelInfo === undefined) {
32489
+ // Note: Model is not in the list of known models, fallback to the family of the models and mark price as uncertain
32490
+ modelInfo = OPENAI_MODELS.find((model) => (rawResponse.model || SALT_NONCE).startsWith(model.modelName));
32491
+ if (modelInfo !== undefined) {
32492
+ isUncertain = true;
32493
+ }
32494
+ }
32495
+ let price;
32496
+ if (modelInfo === undefined || modelInfo.pricing === undefined) {
32497
+ price = uncertainNumber();
32498
+ }
32499
+ else {
32500
+ price = uncertainNumber(inputTokens * modelInfo.pricing.prompt + outputTokens * modelInfo.pricing.output, isUncertain);
32501
+ }
32502
+ return {
32503
+ price,
32504
+ duration,
32505
+ input: {
32506
+ tokensCount: uncertainNumber(rawResponse.usage.prompt_tokens),
32507
+ ...computeUsageCounts(promptContent),
32508
+ },
32509
+ output: {
32510
+ tokensCount: uncertainNumber(outputTokens),
32511
+ ...computeUsageCounts(resultContent),
32512
+ },
32513
+ };
32514
+ }
32515
+ // TODO: [🤝] DRY Maybe some common abstraction between `computeOpenAiUsage` and `computeAnthropicClaudeUsage`
32516
+
32095
32517
  /**
32096
32518
  * Simple wrapper `new Date().toISOString()`
32097
32519
  *
@@ -32500,78 +32922,6 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
32500
32922
  }
32501
32923
  }
32502
32924
 
32503
- /**
32504
- * Helper of usage compute
32505
- *
32506
- * @param content the content of prompt or response
32507
- * @returns part of UsageCounts
32508
- *
32509
- * @private internal utility of LlmExecutionTools
32510
- */
32511
- function computeUsageCounts(content) {
32512
- return {
32513
- charactersCount: { value: countCharacters(content) },
32514
- wordsCount: { value: countWords(content) },
32515
- sentencesCount: { value: countSentences(content) },
32516
- linesCount: { value: countLines(content) },
32517
- paragraphsCount: { value: countParagraphs(content) },
32518
- pagesCount: { value: countPages(content) },
32519
- };
32520
- }
32521
-
32522
- /**
32523
- * Computes the usage of the OpenAI API based on the response from OpenAI
32524
- *
32525
- * @param promptContent The content of the prompt
32526
- * @param resultContent The content of the result (for embedding prompts or failed prompts pass empty string)
32527
- * @param rawResponse The raw response from OpenAI API
32528
- * @param duration The duration of the execution
32529
- * @throws {PipelineExecutionError} If the usage is not defined in the response from OpenAI
32530
- *
32531
- * @private internal utility of `OpenAiExecutionTools`
32532
- */
32533
- function computeOpenAiUsage(promptContent, // <- Note: Intentionally using [] to access type properties to bring jsdoc from Prompt/PromptResult to consumer
32534
- resultContent, rawResponse, duration = ZERO_VALUE) {
32535
- var _a, _b;
32536
- if (rawResponse.usage === undefined) {
32537
- throw new PipelineExecutionError('The usage is not defined in the response from OpenAI');
32538
- }
32539
- if (((_a = rawResponse.usage) === null || _a === void 0 ? void 0 : _a.prompt_tokens) === undefined) {
32540
- throw new PipelineExecutionError('In OpenAI response `usage.prompt_tokens` not defined');
32541
- }
32542
- const inputTokens = rawResponse.usage.prompt_tokens;
32543
- const outputTokens = ((_b = rawResponse.usage) === null || _b === void 0 ? void 0 : _b.completion_tokens) || 0;
32544
- let isUncertain = false;
32545
- let modelInfo = OPENAI_MODELS.find((model) => model.modelName === rawResponse.model);
32546
- if (modelInfo === undefined) {
32547
- // Note: Model is not in the list of known models, fallback to the family of the models and mark price as uncertain
32548
- modelInfo = OPENAI_MODELS.find((model) => (rawResponse.model || SALT_NONCE).startsWith(model.modelName));
32549
- if (modelInfo !== undefined) {
32550
- isUncertain = true;
32551
- }
32552
- }
32553
- let price;
32554
- if (modelInfo === undefined || modelInfo.pricing === undefined) {
32555
- price = uncertainNumber();
32556
- }
32557
- else {
32558
- price = uncertainNumber(inputTokens * modelInfo.pricing.prompt + outputTokens * modelInfo.pricing.output, isUncertain);
32559
- }
32560
- return {
32561
- price,
32562
- duration,
32563
- input: {
32564
- tokensCount: uncertainNumber(rawResponse.usage.prompt_tokens),
32565
- ...computeUsageCounts(promptContent),
32566
- },
32567
- output: {
32568
- tokensCount: uncertainNumber(outputTokens),
32569
- ...computeUsageCounts(resultContent),
32570
- },
32571
- };
32572
- }
32573
- // TODO: [🤝] DRY Maybe some common abstraction between `computeOpenAiUsage` and `computeAnthropicClaudeUsage`
32574
-
32575
32925
  /**
32576
32926
  * Finds the best hardcoded-model match for one API-listed model identifier.
32577
32927
  */
@@ -43029,18 +43379,6 @@ async function $provideExecutablesForNode(options) {
43029
43379
  const $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
43030
43380
  // TODO: [®] DRY Register logic
43031
43381
 
43032
- /**
43033
- * Register for LLM tools.
43034
- *
43035
- * Note: `$` is used to indicate that this interacts with the global scope
43036
- *
43037
- * @singleton Only one instance of each register is created per build, but there can be more instances across different builds or environments.
43038
- *
43039
- * @public exported from `@promptbook/core`
43040
- */
43041
- const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
43042
- // TODO: [®] DRY Register logic
43043
-
43044
43382
  /**
43045
43383
  * Path to the `.env` file which was used to configure LLM tools
43046
43384
  *
@@ -43048,61 +43386,53 @@ const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
43048
43386
  */
43049
43387
  let $usedEnvFilename = null;
43050
43388
  /**
43051
- * Pass the `.env` file which was used to configure LLM tools
43052
- *
43053
- * Note: `$` is used to indicate that this variable is making side effect
43054
- *
43055
- * @private internal log of `$provideLlmToolsConfigurationFromEnv` and `$registeredLlmToolsMessage`
43056
- */
43057
- function $setUsedEnvFilename(filepath) {
43058
- $usedEnvFilename = filepath;
43059
- }
43060
- /**
43061
- * Creates a message with all registered LLM tools
43062
- *
43063
- * Note: This function is used to create a (error) message when there is no constructor for some LLM provider
43064
- *
43065
- * @private internal function of `createLlmToolsFromConfiguration` and `$provideLlmToolsFromEnv`
43066
- */
43067
- function $registeredLlmToolsMessage() {
43068
- const registeredLlmToolsMessageContext = createRegisteredLlmToolsMessageContext();
43069
- if (registeredLlmToolsMessageContext.llmToolStatuses.length === 0) {
43070
- return renderNoRegisteredLlmToolsMessage(registeredLlmToolsMessageContext.usedEnvMessage);
43071
- }
43072
- return renderRegisteredLlmToolsMessage(registeredLlmToolsMessageContext);
43073
- }
43074
- /**
43075
- * Collects all state needed to render the provider summary.
43389
+ * Shared state for the `.env` file used to configure LLM tools.
43076
43390
  *
43077
- * @private internal function of `$registeredLlmToolsMessage`
43391
+ * @private internal state of `$registeredLlmToolsMessage`
43078
43392
  */
43079
- function createRegisteredLlmToolsMessageContext() {
43080
- const isRunningInNode = $isRunningInNode();
43081
- const env = getAvailableEnvironmentVariables(isRunningInNode);
43082
- const registeredLlmToolRegisters = getRegisteredLlmToolRegisters();
43083
- return {
43084
- env,
43085
- llmToolStatuses: listRegisteredLlmToolStatuses(registeredLlmToolRegisters, env),
43086
- usedEnvMessage: createUsedEnvMessage(),
43087
- isRunningInNode,
43088
- };
43089
- }
43393
+ const USED_ENV_FILENAME = {
43394
+ set(filepath) {
43395
+ $usedEnvFilename = filepath;
43396
+ },
43397
+ createMessage() {
43398
+ return $usedEnvFilename === null
43399
+ ? `Unknown \`.env\` file`
43400
+ : spaceTrim$1(`
43401
+ Used \`.env\` file:
43402
+ ${$usedEnvFilename}
43403
+ `);
43404
+ },
43405
+ };
43406
+
43090
43407
  /**
43091
43408
  * Reads environment variables relevant for provider configuration.
43092
43409
  *
43093
- * @private internal function of `$registeredLlmToolsMessage`
43410
+ * @private function of `$registeredLlmToolsMessage`
43094
43411
  */
43095
- function getAvailableEnvironmentVariables(isRunningInNode) {
43412
+ function getAvailableRegisteredLlmToolsEnvironmentVariables(isRunningInNode) {
43096
43413
  if (isRunningInNode) {
43097
43414
  return process.env;
43098
43415
  // <- TODO: [⚛] Some DRY way how to get to `process.env` and pass it into functions - ACRY search for `env`
43099
43416
  }
43100
43417
  return {};
43101
43418
  }
43419
+
43420
+ /**
43421
+ * Register for LLM tools.
43422
+ *
43423
+ * Note: `$` is used to indicate that this interacts with the global scope
43424
+ *
43425
+ * @singleton Only one instance of each register is created per build, but there can be more instances across different builds or environments.
43426
+ *
43427
+ * @public exported from `@promptbook/core`
43428
+ */
43429
+ const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
43430
+ // TODO: [®] DRY Register logic
43431
+
43102
43432
  /**
43103
43433
  * Takes stable snapshots of both LLM provider registers.
43104
43434
  *
43105
- * @private internal function of `$registeredLlmToolsMessage`
43435
+ * @private function of `$registeredLlmToolsMessage`
43106
43436
  */
43107
43437
  function getRegisteredLlmToolRegisters() {
43108
43438
  return {
@@ -43110,62 +43440,30 @@ function getRegisteredLlmToolRegisters() {
43110
43440
  registeredTools: $llmToolsRegister.list(),
43111
43441
  };
43112
43442
  }
43113
- /**
43114
- * Lists provider entries enriched with installation and configuration state.
43115
- *
43116
- * @private internal function of `$registeredLlmToolsMessage`
43117
- */
43118
- function listRegisteredLlmToolStatuses({ registeredMetadata, registeredTools }, env) {
43119
- return listRegisteredLlmToolEntries(registeredMetadata, registeredTools).map((registeredLlmToolEntry) => createRegisteredLlmToolStatus({ registeredLlmToolEntry, registeredMetadata, registeredTools, env }));
43120
- }
43121
- /**
43122
- * Merges provider entries from the metadata and constructor registers.
43123
- *
43124
- * @private internal function of `$registeredLlmToolsMessage`
43125
- */
43126
- function listRegisteredLlmToolEntries(registeredMetadata, registeredTools) {
43127
- const registeredLlmToolEntries = new Map();
43128
- for (const { title, packageName, className, envVariables } of registeredMetadata) {
43129
- addRegisteredLlmToolEntry(registeredLlmToolEntries, { title, packageName, className, envVariables });
43130
- }
43131
- for (const { packageName, className } of registeredTools) {
43132
- addRegisteredLlmToolEntry(registeredLlmToolEntries, { packageName, className });
43133
- }
43134
- return [...registeredLlmToolEntries.values()];
43135
- }
43136
- /**
43137
- * Adds a provider entry only when the provider is not already present.
43138
- *
43139
- * @private internal function of `$registeredLlmToolsMessage`
43140
- */
43141
- function addRegisteredLlmToolEntry(registeredLlmToolEntries, registeredLlmToolEntry) {
43142
- const registeredLlmToolEntryKey = createRegisteredLlmToolEntryKey(registeredLlmToolEntry);
43143
- if (registeredLlmToolEntries.has(registeredLlmToolEntryKey)) {
43144
- return;
43145
- }
43146
- registeredLlmToolEntries.set(registeredLlmToolEntryKey, registeredLlmToolEntry);
43147
- }
43443
+
43148
43444
  /**
43149
43445
  * Creates a deduplication key for a provider entry.
43150
43446
  *
43151
- * @private internal function of `$registeredLlmToolsMessage`
43447
+ * @private function of `$registeredLlmToolsMessage`
43152
43448
  */
43153
43449
  function createRegisteredLlmToolEntryKey({ packageName, className }) {
43154
43450
  return `${packageName}::${className}`;
43155
43451
  }
43452
+
43156
43453
  /**
43157
43454
  * Checks whether the given provider entry already exists in the target register list.
43158
43455
  *
43159
- * @private internal function of `$registeredLlmToolsMessage`
43456
+ * @private function of `$registeredLlmToolsMessage`
43160
43457
  */
43161
43458
  function hasRegisteredLlmToolEntry(registeredLlmToolEntries, registeredLlmToolEntry) {
43162
43459
  const registeredLlmToolEntryKey = createRegisteredLlmToolEntryKey(registeredLlmToolEntry);
43163
43460
  return registeredLlmToolEntries.some((listedRegisteredLlmToolEntry) => createRegisteredLlmToolEntryKey(listedRegisteredLlmToolEntry) === registeredLlmToolEntryKey);
43164
43461
  }
43462
+
43165
43463
  /**
43166
43464
  * Builds the runtime status for one provider entry.
43167
43465
  *
43168
- * @private internal function of `$registeredLlmToolsMessage`
43466
+ * @private function of `$registeredLlmToolsMessage`
43169
43467
  */
43170
43468
  function createRegisteredLlmToolStatus({ registeredLlmToolEntry, registeredMetadata, registeredTools, env, }) {
43171
43469
  const availabilityStatus = createRegisteredLlmToolAvailabilityStatus(registeredLlmToolEntry, registeredMetadata, registeredTools);
@@ -43179,7 +43477,7 @@ function createRegisteredLlmToolStatus({ registeredLlmToolEntry, registeredMetad
43179
43477
  /**
43180
43478
  * Resolves whether the provider has metadata and a registered constructor.
43181
43479
  *
43182
- * @private internal function of `$registeredLlmToolsMessage`
43480
+ * @private function of `$registeredLlmToolsMessage`
43183
43481
  */
43184
43482
  function createRegisteredLlmToolAvailabilityStatus(registeredLlmToolEntry, registeredMetadata, registeredTools) {
43185
43483
  return {
@@ -43190,7 +43488,7 @@ function createRegisteredLlmToolAvailabilityStatus(registeredLlmToolEntry, regis
43190
43488
  /**
43191
43489
  * Resolves whether the provider has enough environment variables to be configured.
43192
43490
  *
43193
- * @private internal function of `$registeredLlmToolsMessage`
43491
+ * @private function of `$registeredLlmToolsMessage`
43194
43492
  */
43195
43493
  function createRegisteredLlmToolConfigurationStatus({ envVariables }, env) {
43196
43494
  return {
@@ -43201,7 +43499,7 @@ function createRegisteredLlmToolConfigurationStatus({ envVariables }, env) {
43201
43499
  /**
43202
43500
  * Checks whether all required environment variables are present for a provider.
43203
43501
  *
43204
- * @private internal function of `$registeredLlmToolsMessage`
43502
+ * @private function of `$registeredLlmToolsMessage`
43205
43503
  */
43206
43504
  function isRegisteredLlmToolFullyConfigured(envVariables, env) {
43207
43505
  if (envVariables === undefined || envVariables === null) {
@@ -43212,7 +43510,7 @@ function isRegisteredLlmToolFullyConfigured(envVariables, env) {
43212
43510
  /**
43213
43511
  * Checks whether at least one required environment variable is present for a provider.
43214
43512
  *
43215
- * @private internal function of `$registeredLlmToolsMessage`
43513
+ * @private function of `$registeredLlmToolsMessage`
43216
43514
  */
43217
43515
  function isRegisteredLlmToolPartiallyConfigured(envVariables, env) {
43218
43516
  if (envVariables === undefined || envVariables === null) {
@@ -43220,109 +43518,128 @@ function isRegisteredLlmToolPartiallyConfigured(envVariables, env) {
43220
43518
  }
43221
43519
  return envVariables.some((envVariableName) => env[envVariableName] !== undefined);
43222
43520
  }
43521
+
43223
43522
  /**
43224
- * Renders the fallback message for environments with no registered providers.
43523
+ * Merges provider entries from the metadata and constructor registers.
43225
43524
  *
43226
- * @private internal function of `$registeredLlmToolsMessage`
43525
+ * @private function of `$registeredLlmToolsMessage`
43227
43526
  */
43228
- function renderNoRegisteredLlmToolsMessage(usedEnvMessage) {
43229
- return spaceTrim$1((block) => `
43230
- No LLM providers are available.
43231
-
43232
- ${block(usedEnvMessage)}
43233
- `);
43527
+ function listRegisteredLlmToolEntries(registeredMetadata, registeredTools) {
43528
+ const registeredLlmToolEntries = new Map();
43529
+ for (const { title, packageName, className, envVariables } of registeredMetadata) {
43530
+ addRegisteredLlmToolEntry(registeredLlmToolEntries, { title, packageName, className, envVariables });
43531
+ }
43532
+ for (const { packageName, className } of registeredTools) {
43533
+ addRegisteredLlmToolEntry(registeredLlmToolEntries, { packageName, className });
43534
+ }
43535
+ return [...registeredLlmToolEntries.values()];
43234
43536
  }
43235
43537
  /**
43236
- * Renders the full provider status summary.
43538
+ * Adds a provider entry only when the provider is not already present.
43237
43539
  *
43238
- * @private internal function of `$registeredLlmToolsMessage`
43540
+ * @private function of `$registeredLlmToolsMessage`
43239
43541
  */
43240
- function renderRegisteredLlmToolsMessage({ env, llmToolStatuses, usedEnvMessage, isRunningInNode, }) {
43241
- return spaceTrim$1((block) => `
43242
-
43243
- ${block(usedEnvMessage)}
43244
-
43245
- Relevant environment variables:
43246
- ${block(renderRelevantEnvironmentVariables(env, llmToolStatuses))}
43247
-
43248
- Available LLM providers are:
43249
- ${block(renderAvailableProviders(llmToolStatuses, env, isRunningInNode))}
43250
- `);
43542
+ function addRegisteredLlmToolEntry(registeredLlmToolEntries, registeredLlmToolEntry) {
43543
+ const registeredLlmToolEntryKey = createRegisteredLlmToolEntryKey(registeredLlmToolEntry);
43544
+ if (registeredLlmToolEntries.has(registeredLlmToolEntryKey)) {
43545
+ return;
43546
+ }
43547
+ registeredLlmToolEntries.set(registeredLlmToolEntryKey, registeredLlmToolEntry);
43251
43548
  }
43549
+
43252
43550
  /**
43253
- * Renders the list of environment variables used by at least one registered provider.
43551
+ * Lists provider entries enriched with installation and configuration state.
43254
43552
  *
43255
- * @private internal function of `$registeredLlmToolsMessage`
43553
+ * @private function of `$registeredLlmToolsMessage`
43256
43554
  */
43257
- function renderRelevantEnvironmentVariables(env, llmToolStatuses) {
43258
- return listRelevantEnvironmentVariables(env, llmToolStatuses)
43259
- .map((envVariableName) => `- \`${envVariableName}\``)
43260
- .join('\n');
43555
+ function listRegisteredLlmToolStatuses({ registeredMetadata, registeredTools }, env) {
43556
+ return listRegisteredLlmToolEntries(registeredMetadata, registeredTools).map((registeredLlmToolEntry) => createRegisteredLlmToolStatus({ registeredLlmToolEntry, registeredMetadata, registeredTools, env }));
43261
43557
  }
43558
+
43262
43559
  /**
43263
- * Lists environment variables that are both present and used by at least one provider.
43560
+ * Collects all state needed to render the provider summary.
43264
43561
  *
43265
- * @private internal function of `$registeredLlmToolsMessage`
43562
+ * @private function of `$registeredLlmToolsMessage`
43266
43563
  */
43267
- function listRelevantEnvironmentVariables(env, llmToolStatuses) {
43268
- return Object.keys(env).filter((envVariableName) => llmToolStatuses.some(({ envVariables }) => envVariables === null || envVariables === void 0 ? void 0 : envVariables.includes(envVariableName)));
43564
+ function createRegisteredLlmToolsMessageContext() {
43565
+ const isRunningInNode = $isRunningInNode();
43566
+ const env = getAvailableRegisteredLlmToolsEnvironmentVariables(isRunningInNode);
43567
+ const registeredLlmToolRegisters = getRegisteredLlmToolRegisters();
43568
+ return {
43569
+ env,
43570
+ llmToolStatuses: listRegisteredLlmToolStatuses(registeredLlmToolRegisters, env),
43571
+ usedEnvMessage: USED_ENV_FILENAME.createMessage(),
43572
+ isRunningInNode,
43573
+ };
43269
43574
  }
43575
+
43270
43576
  /**
43271
- * Renders all provider lines in the status summary.
43577
+ * Renders the fallback message for environments with no registered providers.
43272
43578
  *
43273
- * @private internal function of `$registeredLlmToolsMessage`
43579
+ * @private function of `$registeredLlmToolsMessage`
43274
43580
  */
43275
- function renderAvailableProviders(llmToolStatuses, env, isRunningInNode) {
43276
- return llmToolStatuses
43277
- .map((llmToolStatus, index) => renderAvailableProvider(llmToolStatus, index, env, isRunningInNode))
43278
- .join('\n');
43581
+ function renderNoRegisteredLlmToolsMessage(usedEnvMessage) {
43582
+ return spaceTrim$1((block) => `
43583
+ No LLM providers are available.
43584
+
43585
+ ${block(usedEnvMessage)}
43586
+ `);
43279
43587
  }
43588
+
43280
43589
  /**
43281
- * Renders one provider line in the status summary.
43590
+ * Creates the configuration-status sentence for one provider.
43282
43591
  *
43283
- * @private internal function of `$registeredLlmToolsMessage`
43592
+ * @private function of `$registeredLlmToolsMessage`
43284
43593
  */
43285
- function renderAvailableProvider(llmToolStatus, index, env, isRunningInNode) {
43286
- const providerMessage = createAvailableProviderMessage(llmToolStatus, index, env);
43287
- if (!isRunningInNode) {
43288
- return providerMessage;
43594
+ function createRegisteredLlmToolConfigurationStatusMessage({ envVariables, isFullyConfigured, isPartiallyConfigured }, env) {
43595
+ if (isFullyConfigured) {
43596
+ return `Configured`;
43597
+ }
43598
+ if (isPartiallyConfigured) {
43599
+ return createPartiallyConfiguredStatusMessage(envVariables, env);
43600
+ }
43601
+ if (envVariables !== null) {
43602
+ return createNotConfiguredStatusMessage(envVariables);
43289
43603
  }
43290
- return colorizeAvailableProviderMessage(providerMessage, llmToolStatus);
43604
+ return `Not configured`;
43605
+ // <- Note: Can not be configured via environment variables
43291
43606
  }
43292
43607
  /**
43293
- * Creates the plain-text provider line before optional colorization.
43608
+ * Creates the partial-configuration sentence including missing variables.
43294
43609
  *
43295
- * @private internal function of `$registeredLlmToolsMessage`
43610
+ * @private function of `$registeredLlmToolsMessage`
43296
43611
  */
43297
- function createAvailableProviderMessage(llmToolStatus, index, env) {
43298
- const { title, packageName, className } = llmToolStatus;
43299
- const providerStatusMessages = createProviderStatusMessages(llmToolStatus, env);
43300
- return spaceTrim$1(`
43301
- ${index + 1}) **${title}** \`${className}\` from \`${packageName}\`
43302
- ${providerStatusMessages.join('; ')}
43303
- `);
43612
+ function createPartiallyConfiguredStatusMessage(envVariables, env) {
43613
+ return `Partially confugured, missing ${listMissingEnvironmentVariables(envVariables, env).join(' + ')}`;
43304
43614
  }
43305
43615
  /**
43306
- * Creates the install and configuration fragments for one provider line.
43616
+ * Creates the not-configured sentence including the expected environment variables.
43307
43617
  *
43308
- * @private internal function of `$registeredLlmToolsMessage`
43618
+ * @private function of `$registeredLlmToolsMessage`
43309
43619
  */
43310
- function createProviderStatusMessages(llmToolStatus, env) {
43311
- return [createInstallationStatusMessage(llmToolStatus), createConfigurationStatusMessage(llmToolStatus, env)];
43620
+ function createNotConfiguredStatusMessage(envVariables) {
43621
+ return `Not configured, to configure set env ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.join(' + ')}`;
43622
+ }
43623
+ /**
43624
+ * Lists environment variables that are required but currently missing.
43625
+ *
43626
+ * @private function of `$registeredLlmToolsMessage`
43627
+ */
43628
+ function listMissingEnvironmentVariables(envVariables, env) {
43629
+ return (envVariables === null || envVariables === void 0 ? void 0 : envVariables.filter((envVariable) => env[envVariable] === undefined)) || [];
43312
43630
  }
43631
+
43313
43632
  /**
43314
43633
  * Creates the installation-status sentence for one provider.
43315
43634
  *
43316
- * @private internal function of `$registeredLlmToolsMessage`
43635
+ * @private function of `$registeredLlmToolsMessage`
43317
43636
  */
43318
- function createInstallationStatusMessage({ isMetadataAvailable, isInstalled, }) {
43319
- const installationStatusKey = createInstallationStatusKey(isMetadataAvailable, isInstalled);
43637
+ function createRegisteredLlmToolInstallationStatusMessage({ isMetadataAvailable, isInstalled, }) {
43638
+ const installationStatusKey = createRegisteredLlmToolInstallationStatusKey(isMetadataAvailable, isInstalled);
43320
43639
  switch (installationStatusKey) {
43321
43640
  case 'missing-metadata-and-installation':
43322
- // TODO: [�][�] Maybe do allow to do auto-install if package not registered and not found
43323
43641
  return `Not installed and no metadata, looks like a unexpected behavior`;
43324
43642
  case 'metadata-without-installation':
43325
- // TODO: [�][�]
43326
43643
  return `Not installed`;
43327
43644
  case 'installation-without-metadata':
43328
43645
  return `No metadata but installed, looks like a unexpected behavior`;
@@ -43335,9 +43652,9 @@ function createInstallationStatusMessage({ isMetadataAvailable, isInstalled, })
43335
43652
  /**
43336
43653
  * Creates a stable installation-state key from metadata and constructor availability.
43337
43654
  *
43338
- * @private internal function of `$registeredLlmToolsMessage`
43655
+ * @private function of `$registeredLlmToolsMessage`
43339
43656
  */
43340
- function createInstallationStatusKey(isMetadataAvailable, isInstalled) {
43657
+ function createRegisteredLlmToolInstallationStatusKey(isMetadataAvailable, isInstalled) {
43341
43658
  if (!isMetadataAvailable && !isInstalled) {
43342
43659
  return 'missing-metadata-and-installation';
43343
43660
  }
@@ -43352,54 +43669,59 @@ function createInstallationStatusKey(isMetadataAvailable, isInstalled) {
43352
43669
  }
43353
43670
  return 'unknown';
43354
43671
  }
43672
+
43355
43673
  /**
43356
- * Creates the configuration-status sentence for one provider.
43674
+ * Renders all provider lines in the status summary.
43357
43675
  *
43358
- * @private internal function of `$registeredLlmToolsMessage`
43676
+ * @private function of `$registeredLlmToolsMessage`
43359
43677
  */
43360
- function createConfigurationStatusMessage({ envVariables, isFullyConfigured, isPartiallyConfigured }, env) {
43361
- if (isFullyConfigured) {
43362
- return `Configured`;
43363
- }
43364
- if (isPartiallyConfigured) {
43365
- return createPartiallyConfiguredStatusMessage(envVariables, env);
43366
- }
43367
- if (envVariables !== null) {
43368
- return createNotConfiguredStatusMessage(envVariables);
43369
- }
43370
- return `Not configured`;
43371
- // <- Note: Can not be configured via environment variables
43678
+ function renderAvailableRegisteredLlmTools(llmToolStatuses, env, isRunningInNode) {
43679
+ return llmToolStatuses
43680
+ .map((llmToolStatus, index) => renderAvailableRegisteredLlmTool(llmToolStatus, index, env, isRunningInNode))
43681
+ .join('\n');
43372
43682
  }
43373
43683
  /**
43374
- * Creates the partial-configuration sentence including missing variables.
43684
+ * Renders one provider line in the status summary.
43375
43685
  *
43376
- * @private internal function of `$registeredLlmToolsMessage`
43686
+ * @private function of `$registeredLlmToolsMessage`
43377
43687
  */
43378
- function createPartiallyConfiguredStatusMessage(envVariables, env) {
43379
- return `Partially confugured, missing ${listMissingEnvironmentVariables(envVariables, env).join(' + ')}`;
43688
+ function renderAvailableRegisteredLlmTool(llmToolStatus, index, env, isRunningInNode) {
43689
+ const providerMessage = createAvailableRegisteredLlmToolMessage(llmToolStatus, index, env);
43690
+ if (!isRunningInNode) {
43691
+ return providerMessage;
43692
+ }
43693
+ return colorizeAvailableRegisteredLlmToolMessage(providerMessage, llmToolStatus);
43380
43694
  }
43381
43695
  /**
43382
- * Creates the not-configured sentence including the expected environment variables.
43696
+ * Creates the plain-text provider line before optional colorization.
43383
43697
  *
43384
- * @private internal function of `$registeredLlmToolsMessage`
43698
+ * @private function of `$registeredLlmToolsMessage`
43385
43699
  */
43386
- function createNotConfiguredStatusMessage(envVariables) {
43387
- return `Not configured, to configure set env ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.join(' + ')}`;
43700
+ function createAvailableRegisteredLlmToolMessage(llmToolStatus, index, env) {
43701
+ const { title, packageName, className } = llmToolStatus;
43702
+ const providerStatusMessages = createRegisteredLlmToolStatusMessages(llmToolStatus, env);
43703
+ return spaceTrim$1(`
43704
+ ${index + 1}) **${title}** \`${className}\` from \`${packageName}\`
43705
+ ${providerStatusMessages.join('; ')}
43706
+ `);
43388
43707
  }
43389
43708
  /**
43390
- * Lists environment variables that are required but currently missing.
43709
+ * Creates the install and configuration fragments for one provider line.
43391
43710
  *
43392
- * @private internal function of `$registeredLlmToolsMessage`
43711
+ * @private function of `$registeredLlmToolsMessage`
43393
43712
  */
43394
- function listMissingEnvironmentVariables(envVariables, env) {
43395
- return (envVariables === null || envVariables === void 0 ? void 0 : envVariables.filter((envVariable) => env[envVariable] === undefined)) || [];
43713
+ function createRegisteredLlmToolStatusMessages(llmToolStatus, env) {
43714
+ return [
43715
+ createRegisteredLlmToolInstallationStatusMessage(llmToolStatus),
43716
+ createRegisteredLlmToolConfigurationStatusMessage(llmToolStatus, env),
43717
+ ];
43396
43718
  }
43397
43719
  /**
43398
43720
  * Applies the same terminal coloring rules as the original summary renderer.
43399
43721
  *
43400
- * @private internal function of `$registeredLlmToolsMessage`
43722
+ * @private function of `$registeredLlmToolsMessage`
43401
43723
  */
43402
- function colorizeAvailableProviderMessage(providerMessage, { isInstalled, isFullyConfigured, isPartiallyConfigured }) {
43724
+ function colorizeAvailableRegisteredLlmToolMessage(providerMessage, { isInstalled, isFullyConfigured, isPartiallyConfigured }) {
43403
43725
  if (isInstalled && isFullyConfigured) {
43404
43726
  return colors.green(providerMessage);
43405
43727
  }
@@ -43408,18 +43730,67 @@ function colorizeAvailableProviderMessage(providerMessage, { isInstalled, isFull
43408
43730
  }
43409
43731
  return colors.gray(providerMessage);
43410
43732
  }
43733
+
43411
43734
  /**
43412
- * Creates the `.env` origin sentence used in the summary header.
43735
+ * Renders the list of environment variables used by at least one registered provider.
43413
43736
  *
43414
- * @private internal function of `$registeredLlmToolsMessage`
43737
+ * @private function of `$registeredLlmToolsMessage`
43415
43738
  */
43416
- function createUsedEnvMessage() {
43417
- return $usedEnvFilename === null
43418
- ? `Unknown \`.env\` file`
43419
- : spaceTrim$1(`
43420
- Used \`.env\` file:
43421
- ${$usedEnvFilename}
43422
- `);
43739
+ function renderRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses) {
43740
+ return listRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses)
43741
+ .map((envVariableName) => `- \`${envVariableName}\``)
43742
+ .join('\n');
43743
+ }
43744
+ /**
43745
+ * Lists environment variables that are both present and used by at least one provider.
43746
+ *
43747
+ * @private function of `$registeredLlmToolsMessage`
43748
+ */
43749
+ function listRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses) {
43750
+ return Object.keys(env).filter((envVariableName) => llmToolStatuses.some(({ envVariables }) => envVariables === null || envVariables === void 0 ? void 0 : envVariables.includes(envVariableName)));
43751
+ }
43752
+
43753
+ /**
43754
+ * Renders the full provider status summary.
43755
+ *
43756
+ * @private function of `$registeredLlmToolsMessage`
43757
+ */
43758
+ function renderRegisteredLlmToolsMessage({ env, llmToolStatuses, usedEnvMessage, isRunningInNode, }) {
43759
+ return spaceTrim$1((block) => `
43760
+
43761
+ ${block(usedEnvMessage)}
43762
+
43763
+ Relevant environment variables:
43764
+ ${block(renderRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses))}
43765
+
43766
+ Available LLM providers are:
43767
+ ${block(renderAvailableRegisteredLlmTools(llmToolStatuses, env, isRunningInNode))}
43768
+ `);
43769
+ }
43770
+
43771
+ /**
43772
+ * Pass the `.env` file which was used to configure LLM tools
43773
+ *
43774
+ * Note: `$` is used to indicate that this variable is making side effect
43775
+ *
43776
+ * @private internal log of `$provideLlmToolsConfigurationFromEnv` and `$registeredLlmToolsMessage`
43777
+ */
43778
+ function $setUsedEnvFilename(filepath) {
43779
+ USED_ENV_FILENAME.set(filepath);
43780
+ }
43781
+ /**
43782
+ * Creates a message with all registered LLM tools
43783
+ *
43784
+ * Note: This function is used to create a (error) message when there is no constructor for some LLM provider
43785
+ *
43786
+ * @private internal function of `createLlmToolsFromConfiguration` and `$provideLlmToolsFromEnv`
43787
+ */
43788
+ function $registeredLlmToolsMessage() {
43789
+ const registeredLlmToolsMessageContext = createRegisteredLlmToolsMessageContext();
43790
+ if (registeredLlmToolsMessageContext.llmToolStatuses.length === 0) {
43791
+ return renderNoRegisteredLlmToolsMessage(registeredLlmToolsMessageContext.usedEnvMessage);
43792
+ }
43793
+ return renderRegisteredLlmToolsMessage(registeredLlmToolsMessageContext);
43423
43794
  }
43424
43795
  // TODO: [®] DRY Register logic
43425
43796
  // TODO: [🧠][⚛] Maybe pass env as argument