@promptbook/node 0.113.0-1 → 0.113.0-11
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.
- package/README.md +1 -17
- package/esm/book/scripts/import-markdown/increaseHeadings.d.ts +8 -0
- package/esm/index.es.js +1630 -1222
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-codex-prompts/common/parseDuration.d.ts +19 -0
- package/esm/scripts/run-codex-prompts/common/waitUntilWorldTimeDeadline.d.ts +17 -0
- package/esm/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +4 -0
- package/esm/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeSessionResurrection.d.ts +37 -0
- package/esm/scripts/run-codex-prompts/runners/claude-code/parseClaudeCodeOutputEvents.d.ts +47 -0
- package/esm/src/_packages/types.index.d.ts +2 -2
- package/esm/src/avatars/renderAvatarVisualAsciiArt.d.ts +98 -0
- package/esm/src/avatars/visuals/octopus3dAvatarVisualShared.d.ts +1 -1
- package/esm/src/book-components/BookEditor/BookEditorMonacoUploadPanel.d.ts +1 -1
- package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/bookEditorMonacoUploadTypes.d.ts +120 -0
- package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/clearScheduledTimer.d.ts +6 -0
- package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/enqueueFilesForUpload.d.ts +26 -0
- package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadEditorSync.d.ts +23 -0
- package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadItemsState.d.ts +21 -0
- package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadProgressQueue.d.ts +12 -0
- package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadQueue.d.ts +27 -0
- package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads/useCompletedUploadsAutoClear.d.ts +12 -0
- package/esm/src/book-components/BookEditor/useBookEditorMonacoUploads.d.ts +5 -58
- package/esm/src/book-components/Chat/Chat/ChatImageAttachmentModal.d.ts +25 -0
- package/esm/src/book-components/Chat/Chat/ChatInputArea.d.ts +3 -0
- package/esm/src/book-components/Chat/Chat/ChatMessageAttachments.d.ts +27 -0
- package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +1 -1
- package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +1 -0
- package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +8 -0
- package/esm/src/book-components/Chat/Chat/CitationIframePreview.d.ts +1 -1
- package/esm/src/book-components/Chat/Chat/CitationIframePreview.test.d.ts +2 -0
- package/esm/src/book-components/Chat/Chat/insertDictationChunk.d.ts +1 -1
- package/esm/src/book-components/Chat/Chat/insertDictationChunk.test.d.ts +1 -0
- package/esm/src/book-components/Chat/Chat/learnDictationDictionary.test.d.ts +1 -0
- package/esm/src/book-components/Chat/Chat/refineFinalDictationChunk.test.d.ts +1 -0
- package/esm/src/book-components/Chat/Chat/useChatInputAreaComposer.d.ts +12 -0
- package/esm/src/book-components/Chat/Chat/useChatInputAreaComposer.draftMessage.test.d.ts +2 -0
- package/esm/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +1 -1
- package/esm/src/book-components/Chat/utils/parseMessageButtons.d.ts +16 -2
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_BUILD_CACHE_FILENAME.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_BUILD_CACHE_VERSION.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_NEXT_BUILD_ID_FILENAME.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/AgentsServerBuildCache.d.ts +10 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/AgentsServerBuildCacheOptions.d.ts +10 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/NODE_MODULES_DIRECTORY_NAME.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/PreparedAgentsServerRuntime.d.ts +18 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerBuildSourceFingerprint.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.d.ts +28 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/ensureAgentsServerBuild.d.ts +19 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/getAgentsServerBuildInputRelativePaths.d.ts +11 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerAppPath.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerAppPathMaterialized.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerBuildCacheCurrent.d.ts +7 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerBuildInputTestFile.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerBuildInputDirectoryName.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerBuildInputPath.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerRuntimeSourcePath.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isFile.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/isPathInsideNodeModules.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/normalizeAgentsServerBuildInputPath.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/prepareAgentsServerRuntime.d.ts +9 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/readAgentsServerBuildCache.d.ts +8 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerAppPath.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerBuildAppPath.d.ts +9 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerBuildOutputPath.d.ts +7 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/resolveNextCliPath.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/resolveNodeModulesPath.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/runNextBuild.d.ts +12 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/shouldCopyAgentsServerRuntimePath.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/synchronizeMaterializedAgentsServerRuntime.d.ts +11 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/writeAgentsServerBuildCache.d.ts +7 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +8 -109
- package/esm/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.d.ts +1 -1
- package/esm/src/cli/cli-commands/agents-server/ensureAgentsServerGitignoreFile.d.ts +1 -1
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerChildEnvironment.d.ts +16 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerLogStreams.d.ts +30 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerRuntimePaths.d.ts +19 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerSupervisorState.d.ts +37 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/LocalAgentRunnerLimits.d.ts +9 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/PreparedAgentsServerLaunch.d.ts +13 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/StartAgentsServerOptions.d.ts +24 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/createInternalRouteErrorMessage.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/createLocalAgentRunOptions.d.ts +9 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/forwardChildOutput.d.ts +13 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/loadAgentsServerProjectEnvironment.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/prepareAgentsServerLaunch.d.ts +18 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/readInternalRouteErrorDetails.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/resolveAgentsServerChildHostname.d.ts +7 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/startNextServer.d.ts +20 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/startUserChatJobWorkerPump.d.ts +15 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/stopChildProcess.d.ts +8 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer/waitForLocalAgentRunnerLimits.d.ts +16 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +3 -30
- package/esm/src/cli/cli-commands/common/createPositiveIntegerOptionParser.d.ts +10 -0
- package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +1 -1
- package/esm/src/collection/agent-collection/CreateAgentInput.d.ts +1 -1
- package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.d.ts +1 -1
- package/esm/src/commitments/META/META.d.ts +2 -2
- package/esm/src/commitments/META_AVATAR/META_AVATAR.d.ts +9 -4
- package/esm/src/commitments/index.d.ts +1 -1
- package/esm/src/conversion/validation/validatePipeline/createPipelineValidationContext.d.ts +16 -0
- package/esm/src/conversion/validation/validatePipeline/validatePipelineCollectionsStructure.d.ts +7 -0
- package/esm/src/conversion/validation/validatePipeline/validatePipelineDependencyResolution.d.ts +7 -0
- package/esm/src/conversion/validation/validatePipeline/validatePipelineMetadata.d.ts +7 -0
- package/esm/src/conversion/validation/validatePipeline/validatePipelineParameters.d.ts +7 -0
- package/esm/src/conversion/validation/validatePipeline/validatePipelineTasks.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/RegisteredLlmToolsMessageContext.d.ts +46 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/USED_ENV_FILENAME.d.ts +11 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolConfigurationStatusMessage.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolEntryKey.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolInstallationStatusMessage.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolStatus.d.ts +14 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolsMessageContext.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/getAvailableRegisteredLlmToolsEnvironmentVariables.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/getRegisteredLlmToolRegisters.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/hasRegisteredLlmToolEntry.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/listRegisteredLlmToolEntries.d.ts +9 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/listRegisteredLlmToolStatuses.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderAvailableRegisteredLlmTools.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderNoRegisteredLlmToolsMessage.d.ts +7 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderRegisteredLlmToolsMessage.d.ts +8 -0
- package/esm/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderRelevantRegisteredLlmToolsEnvironmentVariables.d.ts +7 -0
- package/esm/src/utils/agent-message-runtime/agentMessageRuntimePaths.d.ts +24 -0
- package/esm/src/utils/agents/resolveAgentAvatarImageUrl.d.ts +2 -2
- package/esm/src/utils/agents/terminalAgentAvatarVisual.d.ts +94 -0
- package/esm/src/utils/agents/terminalAgentAvatarVisual.test.d.ts +1 -0
- package/esm/src/utils/ascii-art/$detectTerminalAnsiColorDepth.d.ts +15 -0
- package/esm/src/utils/ascii-art/convertImageDataToAsciiArt.d.ts +80 -0
- package/esm/src/utils/color/Color.d.ts +1 -1
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/book/scripts/import-markdown/increaseHeadings.d.ts +8 -0
- package/umd/index.umd.js +1634 -1226
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-codex-prompts/common/parseDuration.d.ts +19 -0
- package/umd/scripts/run-codex-prompts/common/waitUntilWorldTimeDeadline.d.ts +17 -0
- package/umd/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +4 -0
- package/umd/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeSessionResurrection.d.ts +37 -0
- package/umd/scripts/run-codex-prompts/runners/claude-code/parseClaudeCodeOutputEvents.d.ts +47 -0
- package/umd/src/_packages/types.index.d.ts +2 -2
- package/umd/src/avatars/renderAvatarVisualAsciiArt.d.ts +98 -0
- package/umd/src/avatars/visuals/octopus3dAvatarVisualShared.d.ts +1 -1
- package/umd/src/book-components/BookEditor/BookEditorMonacoUploadPanel.d.ts +1 -1
- package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/bookEditorMonacoUploadTypes.d.ts +120 -0
- package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/clearScheduledTimer.d.ts +6 -0
- package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/enqueueFilesForUpload.d.ts +26 -0
- package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadEditorSync.d.ts +23 -0
- package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadItemsState.d.ts +21 -0
- package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadProgressQueue.d.ts +12 -0
- package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/useBookEditorMonacoUploadQueue.d.ts +27 -0
- package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads/useCompletedUploadsAutoClear.d.ts +12 -0
- package/umd/src/book-components/BookEditor/useBookEditorMonacoUploads.d.ts +5 -58
- package/umd/src/book-components/Chat/Chat/ChatImageAttachmentModal.d.ts +25 -0
- package/umd/src/book-components/Chat/Chat/ChatInputArea.d.ts +3 -0
- package/umd/src/book-components/Chat/Chat/ChatMessageAttachments.d.ts +27 -0
- package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +1 -1
- package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +1 -0
- package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +8 -0
- package/umd/src/book-components/Chat/Chat/CitationIframePreview.d.ts +1 -1
- package/umd/src/book-components/Chat/Chat/CitationIframePreview.test.d.ts +2 -0
- package/umd/src/book-components/Chat/Chat/insertDictationChunk.d.ts +1 -1
- package/umd/src/book-components/Chat/Chat/insertDictationChunk.test.d.ts +1 -0
- package/umd/src/book-components/Chat/Chat/learnDictationDictionary.test.d.ts +1 -0
- package/umd/src/book-components/Chat/Chat/refineFinalDictationChunk.test.d.ts +1 -0
- package/umd/src/book-components/Chat/Chat/useChatInputAreaComposer.d.ts +12 -0
- package/umd/src/book-components/Chat/Chat/useChatInputAreaComposer.draftMessage.test.d.ts +2 -0
- package/umd/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +1 -1
- package/umd/src/book-components/Chat/utils/parseMessageButtons.d.ts +16 -2
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_BUILD_CACHE_FILENAME.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_BUILD_CACHE_VERSION.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/AGENTS_SERVER_NEXT_BUILD_ID_FILENAME.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/AgentsServerBuildCache.d.ts +10 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/AgentsServerBuildCacheOptions.d.ts +10 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/NODE_MODULES_DIRECTORY_NAME.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/PreparedAgentsServerRuntime.d.ts +18 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerBuildSourceFingerprint.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.d.ts +28 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/ensureAgentsServerBuild.d.ts +19 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/getAgentsServerBuildInputRelativePaths.d.ts +11 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerAppPath.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerAppPathMaterialized.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerBuildCacheCurrent.d.ts +7 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isAgentsServerBuildInputTestFile.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerBuildInputDirectoryName.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerBuildInputPath.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isExcludedAgentsServerRuntimeSourcePath.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isFile.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/isPathInsideNodeModules.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/normalizeAgentsServerBuildInputPath.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/prepareAgentsServerRuntime.d.ts +9 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/readAgentsServerBuildCache.d.ts +8 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerAppPath.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerBuildAppPath.d.ts +9 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/resolveAgentsServerBuildOutputPath.d.ts +7 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/resolveNextCliPath.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/resolveNodeModulesPath.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/runNextBuild.d.ts +12 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/shouldCopyAgentsServerRuntimePath.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/synchronizeMaterializedAgentsServerRuntime.d.ts +11 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/writeAgentsServerBuildCache.d.ts +7 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +8 -109
- package/umd/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.d.ts +1 -1
- package/umd/src/cli/cli-commands/agents-server/ensureAgentsServerGitignoreFile.d.ts +1 -1
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerChildEnvironment.d.ts +16 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerLogStreams.d.ts +30 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerRuntimePaths.d.ts +19 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/AgentsServerSupervisorState.d.ts +37 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/LocalAgentRunnerLimits.d.ts +9 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/PreparedAgentsServerLaunch.d.ts +13 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/StartAgentsServerOptions.d.ts +24 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/createInternalRouteErrorMessage.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/createLocalAgentRunOptions.d.ts +9 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/forwardChildOutput.d.ts +13 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/loadAgentsServerProjectEnvironment.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/prepareAgentsServerLaunch.d.ts +18 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/readInternalRouteErrorDetails.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/resolveAgentsServerChildHostname.d.ts +7 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/startNextServer.d.ts +20 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/startUserChatJobWorkerPump.d.ts +15 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/stopChildProcess.d.ts +8 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer/waitForLocalAgentRunnerLimits.d.ts +16 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +3 -30
- package/umd/src/cli/cli-commands/common/createPositiveIntegerOptionParser.d.ts +10 -0
- package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +1 -1
- package/umd/src/collection/agent-collection/CreateAgentInput.d.ts +1 -1
- package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.d.ts +1 -1
- package/umd/src/commitments/META/META.d.ts +2 -2
- package/umd/src/commitments/META_AVATAR/META_AVATAR.d.ts +9 -4
- package/umd/src/commitments/index.d.ts +1 -1
- package/umd/src/conversion/validation/validatePipeline/createPipelineValidationContext.d.ts +16 -0
- package/umd/src/conversion/validation/validatePipeline/validatePipelineCollectionsStructure.d.ts +7 -0
- package/umd/src/conversion/validation/validatePipeline/validatePipelineDependencyResolution.d.ts +7 -0
- package/umd/src/conversion/validation/validatePipeline/validatePipelineMetadata.d.ts +7 -0
- package/umd/src/conversion/validation/validatePipeline/validatePipelineParameters.d.ts +7 -0
- package/umd/src/conversion/validation/validatePipeline/validatePipelineTasks.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/RegisteredLlmToolsMessageContext.d.ts +46 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/USED_ENV_FILENAME.d.ts +11 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolConfigurationStatusMessage.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolEntryKey.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolInstallationStatusMessage.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolStatus.d.ts +14 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/createRegisteredLlmToolsMessageContext.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/getAvailableRegisteredLlmToolsEnvironmentVariables.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/getRegisteredLlmToolRegisters.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/hasRegisteredLlmToolEntry.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/listRegisteredLlmToolEntries.d.ts +9 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/listRegisteredLlmToolStatuses.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderAvailableRegisteredLlmTools.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderNoRegisteredLlmToolsMessage.d.ts +7 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderRegisteredLlmToolsMessage.d.ts +8 -0
- package/umd/src/llm-providers/_common/register/$registeredLlmToolsMessage/renderRelevantRegisteredLlmToolsEnvironmentVariables.d.ts +7 -0
- package/umd/src/utils/agent-message-runtime/agentMessageRuntimePaths.d.ts +24 -0
- package/umd/src/utils/agents/resolveAgentAvatarImageUrl.d.ts +2 -2
- package/umd/src/utils/agents/terminalAgentAvatarVisual.d.ts +94 -0
- package/umd/src/utils/agents/terminalAgentAvatarVisual.test.d.ts +1 -0
- package/umd/src/utils/ascii-art/$detectTerminalAnsiColorDepth.d.ts +15 -0
- package/umd/src/utils/ascii-art/convertImageDataToAsciiArt.d.ts +80 -0
- package/umd/src/utils/color/Color.d.ts +1 -1
- package/umd/src/version.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/common/runGoScript/buildTemporaryPromptScriptPath.d.ts +0 -14
- package/esm/src/book-components/Chat/utils/livePagePreviewConstants.d.ts +0 -12
- package/umd/scripts/run-codex-prompts/common/runGoScript/buildTemporaryPromptScriptPath.d.ts +0 -14
- 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-
|
|
39
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.113.0-11';
|
|
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
|
|
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
|
*/
|
|
@@ -3002,6 +3061,47 @@ async function $runGoScriptWithOutput(options) {
|
|
|
3002
3061
|
});
|
|
3003
3062
|
}
|
|
3004
3063
|
|
|
3064
|
+
/**
|
|
3065
|
+
* Minimum timer duration used to avoid a zero-millisecond polling loop.
|
|
3066
|
+
*/
|
|
3067
|
+
const MINIMUM_WORLD_TIME_WAIT_POLL_INTERVAL_MS = 1;
|
|
3068
|
+
/**
|
|
3069
|
+
* Waits until one wall-clock deadline has passed.
|
|
3070
|
+
*
|
|
3071
|
+
* The remaining time is recalculated from `Date.now()` after every poll and after every tick callback.
|
|
3072
|
+
* This makes waits elapse while the process is paused at a checkpoint or the computer is asleep.
|
|
3073
|
+
*
|
|
3074
|
+
* @private internal utility of `ptbk coder` wait handling
|
|
3075
|
+
*/
|
|
3076
|
+
async function waitUntilWorldTimeDeadline(options) {
|
|
3077
|
+
const { deadlineTimeMs, pollIntervalMs, onTick } = options;
|
|
3078
|
+
const normalizedPollIntervalMs = Math.max(MINIMUM_WORLD_TIME_WAIT_POLL_INTERVAL_MS, pollIntervalMs);
|
|
3079
|
+
while (true) {
|
|
3080
|
+
const remainingDurationMs = getRemainingDurationMs(deadlineTimeMs);
|
|
3081
|
+
if (remainingDurationMs <= 0) {
|
|
3082
|
+
return;
|
|
3083
|
+
}
|
|
3084
|
+
await (onTick === null || onTick === void 0 ? void 0 : onTick(remainingDurationMs));
|
|
3085
|
+
const remainingDurationAfterTickMs = getRemainingDurationMs(deadlineTimeMs);
|
|
3086
|
+
if (remainingDurationAfterTickMs <= 0) {
|
|
3087
|
+
return;
|
|
3088
|
+
}
|
|
3089
|
+
await waitForMilliseconds(Math.min(normalizedPollIntervalMs, remainingDurationAfterTickMs));
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Returns the remaining wall-clock duration until a timestamp.
|
|
3094
|
+
*/
|
|
3095
|
+
function getRemainingDurationMs(deadlineTimeMs) {
|
|
3096
|
+
return Math.max(0, deadlineTimeMs - Date.now());
|
|
3097
|
+
}
|
|
3098
|
+
/**
|
|
3099
|
+
* Waits for one short polling interval.
|
|
3100
|
+
*/
|
|
3101
|
+
async function waitForMilliseconds(durationMs) {
|
|
3102
|
+
await new Promise((resolve) => setTimeout(resolve, durationMs));
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3005
3105
|
/**
|
|
3006
3106
|
* Base delimiter used for passing large prompts through stdin.
|
|
3007
3107
|
*/
|
|
@@ -3010,15 +3110,211 @@ const CLAUDE_PROMPT_DELIMITER = 'CLAUDE_PROMPT';
|
|
|
3010
3110
|
* Builds the shell script that runs Claude Code with the prompt and coding context.
|
|
3011
3111
|
*/
|
|
3012
3112
|
function buildClaudeScript(options) {
|
|
3113
|
+
const MODEL_ARGUMENT = options.model ? ` --model ${options.model}` : '';
|
|
3013
3114
|
const THINKING_LEVEL_ARGUMENT = options.thinkingLevel ? ` --effort ${options.thinkingLevel}` : '';
|
|
3115
|
+
const RESUME_SESSION_ARGUMENT = options.resumeSessionId
|
|
3116
|
+
? ` --resume ${quoteShellArgument(options.resumeSessionId)}`
|
|
3117
|
+
: '';
|
|
3014
3118
|
return spaceTrim((block) => `
|
|
3015
|
-
claude --allowedTools "Bash,Read,Edit,Write"${THINKING_LEVEL_ARGUMENT} --output-format json --print <<'${CLAUDE_PROMPT_DELIMITER}'
|
|
3119
|
+
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
3120
|
|
|
3017
3121
|
${block(options.prompt)}
|
|
3018
3122
|
|
|
3019
3123
|
${CLAUDE_PROMPT_DELIMITER}
|
|
3020
3124
|
`);
|
|
3021
3125
|
}
|
|
3126
|
+
/**
|
|
3127
|
+
* Quotes one shell argument for the generated Bash script.
|
|
3128
|
+
*/
|
|
3129
|
+
function quoteShellArgument(value) {
|
|
3130
|
+
return JSON.stringify(value);
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
/**
|
|
3134
|
+
* Parses all JSON lines from Claude Code output while ignoring shell prefixes and stack traces.
|
|
3135
|
+
*/
|
|
3136
|
+
function parseClaudeCodeOutputEvents(output) {
|
|
3137
|
+
return output
|
|
3138
|
+
.split(/\r?\n/u)
|
|
3139
|
+
.map(parseClaudeCodeOutputLine)
|
|
3140
|
+
.filter((event) => event !== undefined);
|
|
3141
|
+
}
|
|
3142
|
+
/**
|
|
3143
|
+
* Finds the final Claude Code result event in a stream-json output blob.
|
|
3144
|
+
*/
|
|
3145
|
+
function findClaudeCodeResultEvent(output) {
|
|
3146
|
+
return parseClaudeCodeOutputEvents(output).find((event) => event.type === 'result');
|
|
3147
|
+
}
|
|
3148
|
+
/**
|
|
3149
|
+
* Parses one output line when it contains a JSON object.
|
|
3150
|
+
*/
|
|
3151
|
+
function parseClaudeCodeOutputLine(line) {
|
|
3152
|
+
const jsonStartIndex = line.indexOf('{');
|
|
3153
|
+
if (jsonStartIndex === -1) {
|
|
3154
|
+
return undefined;
|
|
3155
|
+
}
|
|
3156
|
+
const possibleJson = line.slice(jsonStartIndex).trim();
|
|
3157
|
+
try {
|
|
3158
|
+
const parsed = JSON.parse(possibleJson);
|
|
3159
|
+
if (isClaudeCodeOutputEvent(parsed)) {
|
|
3160
|
+
return parsed;
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
catch (_a) {
|
|
3164
|
+
return undefined;
|
|
3165
|
+
}
|
|
3166
|
+
return undefined;
|
|
3167
|
+
}
|
|
3168
|
+
/**
|
|
3169
|
+
* Checks whether an unknown parsed JSON value has the object shape expected from Claude Code events.
|
|
3170
|
+
*/
|
|
3171
|
+
function isClaudeCodeOutputEvent(value) {
|
|
3172
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
/**
|
|
3176
|
+
* Number of milliseconds added after Claude's reported reset timestamp before retrying.
|
|
3177
|
+
*/
|
|
3178
|
+
const CLAUDE_CODE_SESSION_LIMIT_RESET_BUFFER_MS = 30 * 1000;
|
|
3179
|
+
/**
|
|
3180
|
+
* Fallback delay used when Claude reports a session limit without a machine-readable reset timestamp.
|
|
3181
|
+
*/
|
|
3182
|
+
const DEFAULT_CLAUDE_CODE_SESSION_LIMIT_RETRY_DELAY_MS = 10 * 60 * 1000;
|
|
3183
|
+
/**
|
|
3184
|
+
* Extracts a Claude Code session-limit failure from a thrown runner error.
|
|
3185
|
+
*/
|
|
3186
|
+
function extractClaudeCodeSessionLimitFromError(error) {
|
|
3187
|
+
return extractClaudeCodeSessionLimitFromOutput(formatUnknownErrorDetails(error));
|
|
3188
|
+
}
|
|
3189
|
+
/**
|
|
3190
|
+
* Extracts a Claude Code session-limit failure from raw stream-json output.
|
|
3191
|
+
*/
|
|
3192
|
+
function extractClaudeCodeSessionLimitFromOutput(output) {
|
|
3193
|
+
var _a, _b, _c, _d, _e;
|
|
3194
|
+
const events = parseClaudeCodeOutputEvents(output);
|
|
3195
|
+
const rejectedRateLimitEvent = events.find(isRejectedRateLimitEvent);
|
|
3196
|
+
const sessionLimitResultEvent = events.find(isSessionLimitResultEvent);
|
|
3197
|
+
const sessionLimitTextEvent = events.find(isSessionLimitTextEvent);
|
|
3198
|
+
if (!sessionLimitResultEvent && !sessionLimitTextEvent) {
|
|
3199
|
+
return undefined;
|
|
3200
|
+
}
|
|
3201
|
+
const sessionId = findClaudeCodeSessionId(events);
|
|
3202
|
+
if (!sessionId) {
|
|
3203
|
+
return undefined;
|
|
3204
|
+
}
|
|
3205
|
+
const resetDate = extractResetDate(rejectedRateLimitEvent);
|
|
3206
|
+
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;
|
|
3207
|
+
const summary = (_e = (_d = extractSessionLimitSummary(sessionLimitResultEvent)) !== null && _d !== void 0 ? _d : extractSessionLimitSummary(sessionLimitTextEvent)) !== null && _e !== void 0 ? _e : 'Claude Code reported a session limit.';
|
|
3208
|
+
return {
|
|
3209
|
+
sessionId,
|
|
3210
|
+
resetDate,
|
|
3211
|
+
rateLimitType,
|
|
3212
|
+
summary,
|
|
3213
|
+
};
|
|
3214
|
+
}
|
|
3215
|
+
/**
|
|
3216
|
+
* Computes how long to wait before trying to resume the Claude Code session.
|
|
3217
|
+
*/
|
|
3218
|
+
function getClaudeCodeSessionLimitDelayMs(sessionLimit, nowMs = Date.now()) {
|
|
3219
|
+
if (!sessionLimit.resetDate) {
|
|
3220
|
+
return DEFAULT_CLAUDE_CODE_SESSION_LIMIT_RETRY_DELAY_MS;
|
|
3221
|
+
}
|
|
3222
|
+
return Math.max(0, sessionLimit.resetDate.getTime() - nowMs + CLAUDE_CODE_SESSION_LIMIT_RESET_BUFFER_MS);
|
|
3223
|
+
}
|
|
3224
|
+
/**
|
|
3225
|
+
* Builds the follow-up prompt sent into a resumed Claude Code session after its limit resets.
|
|
3226
|
+
*/
|
|
3227
|
+
function buildClaudeCodeSessionResurrectionPrompt(originalPrompt, sessionId) {
|
|
3228
|
+
return spaceTrim((block) => `
|
|
3229
|
+
## Claude Code session resurrection
|
|
3230
|
+
|
|
3231
|
+
You are resuming Claude Code session \`${sessionId}\` after Claude reported a session limit.
|
|
3232
|
+
Continue the same task from the existing repository and session state.
|
|
3233
|
+
Do not discard useful context, completed analysis, or file changes from the resumed session.
|
|
3234
|
+
|
|
3235
|
+
The original prompt is repeated below as reference in case the resumed session needs it.
|
|
3236
|
+
|
|
3237
|
+
## Original prompt
|
|
3238
|
+
|
|
3239
|
+
${block(originalPrompt)}
|
|
3240
|
+
`);
|
|
3241
|
+
}
|
|
3242
|
+
/**
|
|
3243
|
+
* Builds a compact one-line label for logs and terminal UI messages.
|
|
3244
|
+
*/
|
|
3245
|
+
function formatClaudeCodeSessionLimitForDisplay(sessionLimit) {
|
|
3246
|
+
const resetSuffix = sessionLimit.resetDate
|
|
3247
|
+
? `, resets at ${sessionLimit.resetDate.toISOString()}`
|
|
3248
|
+
: `, retrying every ${formatDurationMs(DEFAULT_CLAUDE_CODE_SESSION_LIMIT_RETRY_DELAY_MS)}`;
|
|
3249
|
+
const rateLimitSuffix = sessionLimit.rateLimitType ? ` (${sessionLimit.rateLimitType})` : '';
|
|
3250
|
+
return `${sessionLimit.summary}${rateLimitSuffix}${resetSuffix}`;
|
|
3251
|
+
}
|
|
3252
|
+
/**
|
|
3253
|
+
* Returns true when the event is an explicit rejected Claude Code rate-limit event.
|
|
3254
|
+
*/
|
|
3255
|
+
function isRejectedRateLimitEvent(event) {
|
|
3256
|
+
var _a;
|
|
3257
|
+
return event.type === 'rate_limit_event' && ((_a = event.rate_limit_info) === null || _a === void 0 ? void 0 : _a.status) === 'rejected';
|
|
3258
|
+
}
|
|
3259
|
+
/**
|
|
3260
|
+
* Returns true when the final result reports the Claude session limit.
|
|
3261
|
+
*/
|
|
3262
|
+
function isSessionLimitResultEvent(event) {
|
|
3263
|
+
return (event.type === 'result' &&
|
|
3264
|
+
event.is_error === true &&
|
|
3265
|
+
event.api_error_status === 429 &&
|
|
3266
|
+
isSessionLimitText(event.result));
|
|
3267
|
+
}
|
|
3268
|
+
/**
|
|
3269
|
+
* Returns true when any assistant event reports the Claude session limit in text.
|
|
3270
|
+
*/
|
|
3271
|
+
function isSessionLimitTextEvent(event) {
|
|
3272
|
+
if (event.error !== 'rate_limit') {
|
|
3273
|
+
return false;
|
|
3274
|
+
}
|
|
3275
|
+
return isSessionLimitText(extractSessionLimitSummary(event));
|
|
3276
|
+
}
|
|
3277
|
+
/**
|
|
3278
|
+
* Extracts the first session id reported by Claude Code.
|
|
3279
|
+
*/
|
|
3280
|
+
function findClaudeCodeSessionId(events) {
|
|
3281
|
+
return events.map((event) => event.session_id).find((sessionId) => Boolean(sessionId));
|
|
3282
|
+
}
|
|
3283
|
+
/**
|
|
3284
|
+
* Extracts Claude's reset timestamp from a rejected rate-limit event.
|
|
3285
|
+
*/
|
|
3286
|
+
function extractResetDate(event) {
|
|
3287
|
+
var _a;
|
|
3288
|
+
const resetsAt = (_a = event === null || event === void 0 ? void 0 : event.rate_limit_info) === null || _a === void 0 ? void 0 : _a.resetsAt;
|
|
3289
|
+
if (typeof resetsAt !== 'number' || !Number.isFinite(resetsAt)) {
|
|
3290
|
+
return undefined;
|
|
3291
|
+
}
|
|
3292
|
+
const resetDate = new Date(resetsAt * 1000);
|
|
3293
|
+
if (Number.isNaN(resetDate.getTime())) {
|
|
3294
|
+
return undefined;
|
|
3295
|
+
}
|
|
3296
|
+
return resetDate;
|
|
3297
|
+
}
|
|
3298
|
+
/**
|
|
3299
|
+
* Extracts a human-readable session-limit summary from an event.
|
|
3300
|
+
*/
|
|
3301
|
+
function extractSessionLimitSummary(event) {
|
|
3302
|
+
var _a, _b;
|
|
3303
|
+
if (!event) {
|
|
3304
|
+
return undefined;
|
|
3305
|
+
}
|
|
3306
|
+
if (typeof event.result === 'string' && event.result.trim()) {
|
|
3307
|
+
return event.result.trim();
|
|
3308
|
+
}
|
|
3309
|
+
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));
|
|
3310
|
+
return textContent;
|
|
3311
|
+
}
|
|
3312
|
+
/**
|
|
3313
|
+
* Checks whether a text value names the Claude Code session limit.
|
|
3314
|
+
*/
|
|
3315
|
+
function isSessionLimitText(text) {
|
|
3316
|
+
return /\bsession\s+limit\b/iu.test(text !== null && text !== void 0 ? text : '');
|
|
3317
|
+
}
|
|
3022
3318
|
|
|
3023
3319
|
/**
|
|
3024
3320
|
* Represents the uncertain value
|
|
@@ -3094,14 +3390,8 @@ const UNCERTAIN_USAGE = $deepFreeze({
|
|
|
3094
3390
|
function parseClaudeCodeJsonOutput(output) {
|
|
3095
3391
|
var _a, _b, _c, _d;
|
|
3096
3392
|
try {
|
|
3097
|
-
|
|
3098
|
-
|
|
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') {
|
|
3393
|
+
const parsed = findClaudeCodeResultEvent(output);
|
|
3394
|
+
if (!parsed) {
|
|
3105
3395
|
return UNCERTAIN_USAGE;
|
|
3106
3396
|
}
|
|
3107
3397
|
const totalInputTokens = (((_a = parsed.usage) === null || _a === void 0 ? void 0 : _a.input_tokens) || 0) +
|
|
@@ -3145,6 +3435,10 @@ function parseClaudeCodeJsonOutput(output) {
|
|
|
3145
3435
|
}
|
|
3146
3436
|
}
|
|
3147
3437
|
|
|
3438
|
+
/**
|
|
3439
|
+
* Polling interval used while waiting for Claude Code session limits to reset.
|
|
3440
|
+
*/
|
|
3441
|
+
const CLAUDE_CODE_SESSION_RESURRECTION_POLL_MS = 30 * 1000;
|
|
3148
3442
|
/**
|
|
3149
3443
|
* Runs prompts via the Claude Code CLI.
|
|
3150
3444
|
*/
|
|
@@ -3160,21 +3454,98 @@ class ClaudeCodeRunner {
|
|
|
3160
3454
|
* Runs the prompt using Claude Code and parses usage output.
|
|
3161
3455
|
*/
|
|
3162
3456
|
async runPrompt(options) {
|
|
3457
|
+
let resumeSessionId;
|
|
3458
|
+
let prompt = options.prompt;
|
|
3459
|
+
let resurrectionCount = 0;
|
|
3460
|
+
while (true) {
|
|
3461
|
+
const output = await this.runClaudeCodeOnce({
|
|
3462
|
+
...options,
|
|
3463
|
+
prompt,
|
|
3464
|
+
resumeSessionId,
|
|
3465
|
+
}).catch(async (error) => {
|
|
3466
|
+
const sessionLimit = extractClaudeCodeSessionLimitFromError(error);
|
|
3467
|
+
if (!sessionLimit) {
|
|
3468
|
+
throw error;
|
|
3469
|
+
}
|
|
3470
|
+
resurrectionCount++;
|
|
3471
|
+
await waitForClaudeCodeSessionLimitReset(sessionLimit, resurrectionCount, options);
|
|
3472
|
+
resumeSessionId = sessionLimit.sessionId;
|
|
3473
|
+
prompt = buildClaudeCodeSessionResurrectionPrompt(options.prompt, sessionLimit.sessionId);
|
|
3474
|
+
return undefined;
|
|
3475
|
+
});
|
|
3476
|
+
if (output === undefined) {
|
|
3477
|
+
continue;
|
|
3478
|
+
}
|
|
3479
|
+
const sessionLimit = extractClaudeCodeSessionLimitFromOutput(output);
|
|
3480
|
+
if (sessionLimit) {
|
|
3481
|
+
resurrectionCount++;
|
|
3482
|
+
await waitForClaudeCodeSessionLimitReset(sessionLimit, resurrectionCount, options);
|
|
3483
|
+
resumeSessionId = sessionLimit.sessionId;
|
|
3484
|
+
prompt = buildClaudeCodeSessionResurrectionPrompt(options.prompt, sessionLimit.sessionId);
|
|
3485
|
+
continue;
|
|
3486
|
+
}
|
|
3487
|
+
const usage = parseClaudeCodeJsonOutput(output);
|
|
3488
|
+
return { usage };
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
/**
|
|
3492
|
+
* Runs one Claude Code CLI process and returns its raw output.
|
|
3493
|
+
*/
|
|
3494
|
+
async runClaudeCodeOnce(options) {
|
|
3163
3495
|
const scriptContent = buildClaudeScript({
|
|
3164
3496
|
prompt: options.prompt,
|
|
3497
|
+
model: this.options.model,
|
|
3165
3498
|
thinkingLevel: this.options.thinkingLevel,
|
|
3499
|
+
resumeSessionId: options.resumeSessionId,
|
|
3166
3500
|
});
|
|
3167
|
-
|
|
3501
|
+
return await $runGoScriptWithOutput({
|
|
3168
3502
|
scriptPath: options.scriptPath,
|
|
3169
3503
|
scriptContent,
|
|
3170
3504
|
logPath: options.logPath,
|
|
3171
3505
|
shouldPrintLiveOutput: options.shouldPrintLiveOutput,
|
|
3172
3506
|
preserveArtifactsOnSuccess: options.preserveArtifactsOnSuccess,
|
|
3173
3507
|
});
|
|
3174
|
-
const usage = parseClaudeCodeJsonOutput(output);
|
|
3175
|
-
return { usage };
|
|
3176
3508
|
}
|
|
3177
3509
|
}
|
|
3510
|
+
/**
|
|
3511
|
+
* Waits until the Claude Code session can be resumed, keeping terminal status clear.
|
|
3512
|
+
*/
|
|
3513
|
+
async function waitForClaudeCodeSessionLimitReset(sessionLimit, resurrectionCount, options) {
|
|
3514
|
+
var _a, _b;
|
|
3515
|
+
const delayMs = getClaudeCodeSessionLimitDelayMs(sessionLimit);
|
|
3516
|
+
const resetDeadlineTimeMs = Date.now() + delayMs;
|
|
3517
|
+
const sessionLabel = formatClaudeCodeSessionIdForDisplay(sessionLimit.sessionId);
|
|
3518
|
+
const resetSummary = formatClaudeCodeSessionLimitForDisplay(sessionLimit);
|
|
3519
|
+
if ((_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : true) {
|
|
3520
|
+
console.warn(colors.yellow(`[claude-code] Session limit detected for ${sessionLimit.sessionId}. Resurrection #${resurrectionCount} will resume with --resume after ${formatDurationMs(delayMs)}. ${resetSummary}`));
|
|
3521
|
+
}
|
|
3522
|
+
await waitUntilWorldTimeDeadline({
|
|
3523
|
+
deadlineTimeMs: resetDeadlineTimeMs,
|
|
3524
|
+
pollIntervalMs: CLAUDE_CODE_SESSION_RESURRECTION_POLL_MS,
|
|
3525
|
+
onTick: async (remainingDelayMs) => {
|
|
3526
|
+
var _a;
|
|
3527
|
+
await ((_a = options.waitForPauseCheckpoint) === null || _a === void 0 ? void 0 : _a.call(options, {
|
|
3528
|
+
checkpointLabel: 'the Claude Code session limit reset',
|
|
3529
|
+
phase: 'waiting',
|
|
3530
|
+
statusMessage: `Claude Code session ${sessionLabel} hit its limit; resurrection #${resurrectionCount} resumes in ${formatDurationMs(Math.min(remainingDelayMs, delayMs))}`,
|
|
3531
|
+
}));
|
|
3532
|
+
},
|
|
3533
|
+
});
|
|
3534
|
+
await ((_b = options.waitForPauseCheckpoint) === null || _b === void 0 ? void 0 : _b.call(options, {
|
|
3535
|
+
checkpointLabel: 'resurrecting the Claude Code session with --resume',
|
|
3536
|
+
phase: 'running',
|
|
3537
|
+
statusMessage: `Resurrecting Claude Code session ${sessionLabel} with --resume`,
|
|
3538
|
+
}));
|
|
3539
|
+
}
|
|
3540
|
+
/**
|
|
3541
|
+
* Formats a Claude Code session id for compact terminal status lines.
|
|
3542
|
+
*/
|
|
3543
|
+
function formatClaudeCodeSessionIdForDisplay(sessionId) {
|
|
3544
|
+
if (sessionId.length <= 8) {
|
|
3545
|
+
return sessionId;
|
|
3546
|
+
}
|
|
3547
|
+
return `${sessionId.slice(0, 8)}...`;
|
|
3548
|
+
}
|
|
3178
3549
|
|
|
3179
3550
|
/**
|
|
3180
3551
|
* Creates a temporary script file, runs it, and cleans it up unless preservation is requested or the run fails.
|
|
@@ -4126,12 +4497,6 @@ const RATE_LIMIT_BACKOFF_MAX_MS = 30 * 60 * 1000;
|
|
|
4126
4497
|
* Randomized delay proportion added/subtracted for retry jitter.
|
|
4127
4498
|
*/
|
|
4128
4499
|
const RATE_LIMIT_BACKOFF_JITTER_RATIO = 0.15;
|
|
4129
|
-
/**
|
|
4130
|
-
* Waits for one given amount of milliseconds.
|
|
4131
|
-
*/
|
|
4132
|
-
async function waitFor(delayMs) {
|
|
4133
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
4134
|
-
}
|
|
4135
4500
|
/**
|
|
4136
4501
|
* Formats a delay value into a concise `xh ym zs` style label.
|
|
4137
4502
|
*/
|
|
@@ -4221,13 +4586,18 @@ class OpenAiCodexRunner {
|
|
|
4221
4586
|
throw error;
|
|
4222
4587
|
}
|
|
4223
4588
|
const delayMs = this.rateLimitBackoff.nextDelayMs();
|
|
4224
|
-
const
|
|
4589
|
+
const retryDeadlineTimeMs = Date.now() + delayMs;
|
|
4590
|
+
const retryAt = new Date(retryDeadlineTimeMs).toISOString();
|
|
4225
4591
|
const retryIndex = this.rateLimitBackoff.retryCount;
|
|
4226
4592
|
const summary = extractFailureSummary(details);
|
|
4227
4593
|
if ((_b = options.shouldPrintLiveOutput) !== null && _b !== void 0 ? _b : true) {
|
|
4228
4594
|
console.warn(colors.yellow(`[codex] Rate limit/quota detected (${summary}). Retry #${retryIndex} in ${formatDelay(delayMs)} at ${retryAt}.`));
|
|
4229
4595
|
}
|
|
4230
|
-
await waitForRetryDelay(
|
|
4596
|
+
await waitForRetryDelay({
|
|
4597
|
+
delayMs,
|
|
4598
|
+
retryDeadlineTimeMs,
|
|
4599
|
+
promptRunOptions: options,
|
|
4600
|
+
});
|
|
4231
4601
|
}
|
|
4232
4602
|
}
|
|
4233
4603
|
}
|
|
@@ -4235,20 +4605,21 @@ class OpenAiCodexRunner {
|
|
|
4235
4605
|
/**
|
|
4236
4606
|
* Waits for the next Codex retry while polling for requested pause checkpoints.
|
|
4237
4607
|
*/
|
|
4238
|
-
async function waitForRetryDelay(
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4608
|
+
async function waitForRetryDelay(options) {
|
|
4609
|
+
const { delayMs, retryDeadlineTimeMs, promptRunOptions } = options;
|
|
4610
|
+
await waitUntilWorldTimeDeadline({
|
|
4611
|
+
deadlineTimeMs: retryDeadlineTimeMs,
|
|
4612
|
+
pollIntervalMs: RATE_LIMIT_BACKOFF_POLL_MS,
|
|
4613
|
+
onTick: async (remainingDelayMs) => {
|
|
4614
|
+
var _a;
|
|
4615
|
+
const remainingDelayLabel = formatDelay(Math.min(remainingDelayMs, delayMs));
|
|
4616
|
+
await ((_a = promptRunOptions.waitForPauseCheckpoint) === null || _a === void 0 ? void 0 : _a.call(promptRunOptions, {
|
|
4617
|
+
checkpointLabel: 'the next OpenAI Codex retry after rate limit',
|
|
4618
|
+
phase: 'running',
|
|
4619
|
+
statusMessage: `Waiting ${remainingDelayLabel} before retrying OpenAI Codex`,
|
|
4620
|
+
}));
|
|
4621
|
+
},
|
|
4622
|
+
});
|
|
4252
4623
|
}
|
|
4253
4624
|
|
|
4254
4625
|
/**
|
|
@@ -4422,8 +4793,9 @@ function resolvePromptRunner(options) {
|
|
|
4422
4793
|
}
|
|
4423
4794
|
if (agentName === 'claude-code') {
|
|
4424
4795
|
return createRunnerResolution(options, new ClaudeCodeRunner({
|
|
4796
|
+
model: options.model,
|
|
4425
4797
|
thinkingLevel: options.thinkingLevel,
|
|
4426
|
-
}));
|
|
4798
|
+
}), options.model);
|
|
4427
4799
|
}
|
|
4428
4800
|
if (agentName === 'opencode') {
|
|
4429
4801
|
return createRunnerResolution(options, new OpencodeRunner({
|
|
@@ -4497,7 +4869,7 @@ function getRunnerMetadata(options, actualRunnerModel) {
|
|
|
4497
4869
|
if (options.agentName === 'cline') {
|
|
4498
4870
|
return { runnerName, modelName: CLINE_MODEL };
|
|
4499
4871
|
}
|
|
4500
|
-
if (options.agentName === 'opencode') {
|
|
4872
|
+
if (options.agentName === 'opencode' || options.agentName === 'claude-code') {
|
|
4501
4873
|
return { runnerName, modelName: options.model };
|
|
4502
4874
|
}
|
|
4503
4875
|
return { runnerName };
|
|
@@ -4574,9 +4946,10 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage) {
|
|
|
4574
4946
|
|
|
4575
4947
|
[Up to 50,000 CZK](?message=We are posting an order up to 50,000 CZK)
|
|
4576
4948
|
[Up to 100,000 CZK](?message=We are posting an order up to 100,000 CZK)
|
|
4577
|
-
[Over 100,000 CZK](?
|
|
4949
|
+
[Over 100,000 CZK](?messageDraft=We are posting an order over 100,000 CZK)
|
|
4578
4950
|
\`\`\`
|
|
4579
4951
|
|
|
4952
|
+
- 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
4953
|
|
|
4581
4954
|
## This is how you should behave
|
|
4582
4955
|
|
|
@@ -4584,28 +4957,38 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage) {
|
|
|
4584
4957
|
`);
|
|
4585
4958
|
}
|
|
4586
4959
|
|
|
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
4960
|
/**
|
|
4597
4961
|
* Temporary subdirectory used for agent-message runner shell scripts.
|
|
4962
|
+
*
|
|
4963
|
+
* @private internal constant of agent-message runtime paths
|
|
4598
4964
|
*/
|
|
4599
4965
|
const AGENT_MESSAGE_SCRIPT_DIRECTORY_NAME = 'agent-messages';
|
|
4966
|
+
/**
|
|
4967
|
+
* File extension used for temporary prompt-runner shell scripts.
|
|
4968
|
+
*
|
|
4969
|
+
* @private internal constant of agent-message runtime paths
|
|
4970
|
+
*/
|
|
4971
|
+
const AGENT_MESSAGE_SCRIPT_EXTENSION = '.sh';
|
|
4972
|
+
/**
|
|
4973
|
+
* Builds the temporary shell script path used by one queued agent-message harness run.
|
|
4974
|
+
*
|
|
4975
|
+
* @param projectPath - Local agent project path containing the message queue.
|
|
4976
|
+
* @param sourceFileName - Queued message filename used to name the temporary shell script.
|
|
4977
|
+
* @returns Temporary shell script path.
|
|
4978
|
+
* @private internal utility of agent-message runners
|
|
4979
|
+
*/
|
|
4980
|
+
function buildAgentMessageScriptPathFromFileName(projectPath, sourceFileName) {
|
|
4981
|
+
const sourceFileBasename = basename(sourceFileName);
|
|
4982
|
+
const scriptFileName = `${sourceFileBasename.replace(/\.[^.]+$/u, '')}${AGENT_MESSAGE_SCRIPT_EXTENSION}`;
|
|
4983
|
+
return resolvePromptbookTemporaryPath(projectPath, AGENT_MESSAGE_SCRIPT_DIRECTORY_NAME, scriptFileName);
|
|
4984
|
+
}
|
|
4985
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
4986
|
+
|
|
4600
4987
|
/**
|
|
4601
4988
|
* Builds a temporary script path for one agent message runner invocation.
|
|
4602
4989
|
*/
|
|
4603
4990
|
function buildAgentMessageScriptPath(projectPath, messageFile) {
|
|
4604
|
-
return
|
|
4605
|
-
projectPath,
|
|
4606
|
-
scriptDirectoryName: AGENT_MESSAGE_SCRIPT_DIRECTORY_NAME,
|
|
4607
|
-
sourceFileName: messageFile.fileName,
|
|
4608
|
-
});
|
|
4991
|
+
return buildAgentMessageScriptPathFromFileName(projectPath, messageFile.fileName);
|
|
4609
4992
|
}
|
|
4610
4993
|
|
|
4611
4994
|
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 +5451,95 @@ function pipelineJsonToString(pipelineJson) {
|
|
|
5068
5451
|
// TODO: [🏛] Escape all
|
|
5069
5452
|
// TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
|
|
5070
5453
|
|
|
5454
|
+
/**
|
|
5455
|
+
* This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
|
|
5456
|
+
*
|
|
5457
|
+
* @public exported from `@promptbook/core`
|
|
5458
|
+
*/
|
|
5459
|
+
class PipelineLogicError extends Error {
|
|
5460
|
+
constructor(message) {
|
|
5461
|
+
super(message);
|
|
5462
|
+
this.name = 'PipelineLogicError';
|
|
5463
|
+
Object.setPrototypeOf(this, PipelineLogicError.prototype);
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
|
|
5467
|
+
/**
|
|
5468
|
+
* Creates the shared validation context for one pipeline.
|
|
5469
|
+
*
|
|
5470
|
+
* @private function of `validatePipeline`
|
|
5471
|
+
*/
|
|
5472
|
+
function createPipelineValidationContext(pipeline) {
|
|
5473
|
+
return {
|
|
5474
|
+
pipeline,
|
|
5475
|
+
pipelineIdentification: getPipelineIdentification$1(pipeline),
|
|
5476
|
+
};
|
|
5477
|
+
}
|
|
5478
|
+
/**
|
|
5479
|
+
* Builds a short file/url identification block for validation errors.
|
|
5480
|
+
*
|
|
5481
|
+
* @private internal utility of `validatePipeline`
|
|
5482
|
+
*/
|
|
5483
|
+
function getPipelineIdentification$1(pipeline) {
|
|
5484
|
+
// Note: This is a 😐 implementation of [🚞]
|
|
5485
|
+
const pipelineIdentificationParts = [];
|
|
5486
|
+
if (pipeline.sourceFile !== undefined) {
|
|
5487
|
+
pipelineIdentificationParts.push(`File: ${pipeline.sourceFile}`);
|
|
5488
|
+
}
|
|
5489
|
+
if (pipeline.pipelineUrl !== undefined) {
|
|
5490
|
+
pipelineIdentificationParts.push(`Url: ${pipeline.pipelineUrl}`);
|
|
5491
|
+
}
|
|
5492
|
+
return pipelineIdentificationParts.join('\n');
|
|
5493
|
+
}
|
|
5494
|
+
|
|
5495
|
+
/**
|
|
5496
|
+
* Validates that the expected top-level collections have array structure.
|
|
5497
|
+
*
|
|
5498
|
+
* @private function of `validatePipeline`
|
|
5499
|
+
*/
|
|
5500
|
+
function validatePipelineCollectionsStructure({ pipeline, pipelineIdentification, }) {
|
|
5501
|
+
validatePipelineParametersCollection(pipeline, pipelineIdentification);
|
|
5502
|
+
validatePipelineTasksCollection(pipeline, pipelineIdentification);
|
|
5503
|
+
}
|
|
5504
|
+
/**
|
|
5505
|
+
* Validates that `pipeline.parameters` is an array.
|
|
5506
|
+
*
|
|
5507
|
+
* @private internal utility of `validatePipelineCollectionsStructure`
|
|
5508
|
+
*/
|
|
5509
|
+
function validatePipelineParametersCollection(pipeline, pipelineIdentification) {
|
|
5510
|
+
// TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
|
|
5511
|
+
if (Array.isArray(pipeline.parameters)) {
|
|
5512
|
+
return;
|
|
5513
|
+
}
|
|
5514
|
+
// TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
|
|
5515
|
+
throw new ParseError(spaceTrim$1((block) => `
|
|
5516
|
+
Pipeline is valid JSON but with wrong structure
|
|
5517
|
+
|
|
5518
|
+
\`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
|
|
5519
|
+
|
|
5520
|
+
${block(pipelineIdentification)}
|
|
5521
|
+
`));
|
|
5522
|
+
}
|
|
5523
|
+
/**
|
|
5524
|
+
* Validates that `pipeline.tasks` is an array.
|
|
5525
|
+
*
|
|
5526
|
+
* @private internal utility of `validatePipelineCollectionsStructure`
|
|
5527
|
+
*/
|
|
5528
|
+
function validatePipelineTasksCollection(pipeline, pipelineIdentification) {
|
|
5529
|
+
// TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
|
|
5530
|
+
if (Array.isArray(pipeline.tasks)) {
|
|
5531
|
+
return;
|
|
5532
|
+
}
|
|
5533
|
+
// TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
|
|
5534
|
+
throw new ParseError(spaceTrim$1((block) => `
|
|
5535
|
+
Pipeline is valid JSON but with wrong structure
|
|
5536
|
+
|
|
5537
|
+
\`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
|
|
5538
|
+
|
|
5539
|
+
${block(pipelineIdentification)}
|
|
5540
|
+
`));
|
|
5541
|
+
}
|
|
5542
|
+
|
|
5071
5543
|
/**
|
|
5072
5544
|
* Order of keys in the pipeline JSON
|
|
5073
5545
|
*
|
|
@@ -5180,16 +5652,137 @@ const LIMITS = {
|
|
|
5180
5652
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
5181
5653
|
|
|
5182
5654
|
/**
|
|
5183
|
-
*
|
|
5655
|
+
* Validates that task dependencies can be resolved without cycles or missing definitions.
|
|
5184
5656
|
*
|
|
5185
|
-
* @
|
|
5657
|
+
* @private function of `validatePipeline`
|
|
5186
5658
|
*/
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5659
|
+
function validatePipelineDependencyResolution({ pipeline, pipelineIdentification, }) {
|
|
5660
|
+
let dependencyResolutionState = createInitialDependencyResolutionState(pipeline);
|
|
5661
|
+
let loopLimit = LOOP_LIMIT;
|
|
5662
|
+
while (hasUnresolvedTasks(dependencyResolutionState)) {
|
|
5663
|
+
if (loopLimit-- < 0) {
|
|
5664
|
+
throw createDependencyResolutionLoopLimitError(pipelineIdentification);
|
|
5665
|
+
}
|
|
5666
|
+
dependencyResolutionState = resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification);
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5669
|
+
/**
|
|
5670
|
+
* Collects the parameter names that are already resolvable before dependency traversal starts.
|
|
5671
|
+
*
|
|
5672
|
+
* @private internal utility of `validatePipelineDependencyResolution`
|
|
5673
|
+
*/
|
|
5674
|
+
function createInitialDependencyResolutionState(pipeline) {
|
|
5675
|
+
return {
|
|
5676
|
+
resolvedParameterNames: createInitiallyResolvedParameterNames(pipeline),
|
|
5677
|
+
unresolvedTasks: [...pipeline.tasks],
|
|
5678
|
+
};
|
|
5679
|
+
}
|
|
5680
|
+
/**
|
|
5681
|
+
* Checks whether dependency resolution still has tasks left to process.
|
|
5682
|
+
*
|
|
5683
|
+
* @private internal utility of `validatePipelineDependencyResolution`
|
|
5684
|
+
*/
|
|
5685
|
+
function hasUnresolvedTasks({ unresolvedTasks }) {
|
|
5686
|
+
return unresolvedTasks.length > 0;
|
|
5687
|
+
}
|
|
5688
|
+
/**
|
|
5689
|
+
* Resolves the next batch of currently satisfiable tasks.
|
|
5690
|
+
*
|
|
5691
|
+
* @private internal utility of `validatePipelineDependencyResolution`
|
|
5692
|
+
*/
|
|
5693
|
+
function resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification) {
|
|
5694
|
+
const currentlyResolvedTasks = getCurrentlyResolvedTasks(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames);
|
|
5695
|
+
if (currentlyResolvedTasks.length === 0) {
|
|
5696
|
+
throw createUnresolvedTasksError(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames, pipelineIdentification);
|
|
5192
5697
|
}
|
|
5698
|
+
return {
|
|
5699
|
+
resolvedParameterNames: appendResolvedTaskParameterNames(dependencyResolutionState.resolvedParameterNames, currentlyResolvedTasks),
|
|
5700
|
+
unresolvedTasks: dependencyResolutionState.unresolvedTasks.filter((task) => !currentlyResolvedTasks.includes(task)),
|
|
5701
|
+
};
|
|
5702
|
+
}
|
|
5703
|
+
/**
|
|
5704
|
+
* Collects the parameter names that are already resolvable before dependency traversal starts.
|
|
5705
|
+
*
|
|
5706
|
+
* @private internal utility of `validatePipelineDependencyResolution`
|
|
5707
|
+
*/
|
|
5708
|
+
function createInitiallyResolvedParameterNames(pipeline) {
|
|
5709
|
+
let resolvedParameterNames = pipeline.parameters
|
|
5710
|
+
.filter(({ isInput }) => isInput)
|
|
5711
|
+
.map(({ name }) => name);
|
|
5712
|
+
for (const reservedParameterName of RESERVED_PARAMETER_NAMES) {
|
|
5713
|
+
resolvedParameterNames = [...resolvedParameterNames, reservedParameterName];
|
|
5714
|
+
}
|
|
5715
|
+
return resolvedParameterNames;
|
|
5716
|
+
}
|
|
5717
|
+
/**
|
|
5718
|
+
* Adds newly resolved task outputs to the resolved parameter list.
|
|
5719
|
+
*
|
|
5720
|
+
* @private internal utility of `validatePipelineDependencyResolution`
|
|
5721
|
+
*/
|
|
5722
|
+
function appendResolvedTaskParameterNames(resolvedParameterNames, currentlyResolvedTasks) {
|
|
5723
|
+
return [
|
|
5724
|
+
...resolvedParameterNames,
|
|
5725
|
+
...currentlyResolvedTasks.map(({ resultingParameterName }) => resultingParameterName),
|
|
5726
|
+
];
|
|
5727
|
+
}
|
|
5728
|
+
/**
|
|
5729
|
+
* Selects tasks whose dependencies are already resolved.
|
|
5730
|
+
*
|
|
5731
|
+
* @private internal utility of `validatePipelineDependencyResolution`
|
|
5732
|
+
*/
|
|
5733
|
+
function getCurrentlyResolvedTasks(unresolvedTasks, resolvedParameterNames) {
|
|
5734
|
+
return unresolvedTasks.filter((task) => task.dependentParameterNames.every((name) => resolvedParameterNames.includes(name)));
|
|
5735
|
+
}
|
|
5736
|
+
/**
|
|
5737
|
+
* Creates the unexpected loop-limit error for dependency resolution.
|
|
5738
|
+
*
|
|
5739
|
+
* @private internal utility of `validatePipelineDependencyResolution`
|
|
5740
|
+
*/
|
|
5741
|
+
function createDependencyResolutionLoopLimitError(pipelineIdentification) {
|
|
5742
|
+
// Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
|
|
5743
|
+
return new UnexpectedError(spaceTrim$1((block) => `
|
|
5744
|
+
Loop limit reached during detection of circular dependencies in \`validatePipeline\`
|
|
5745
|
+
|
|
5746
|
+
${block(pipelineIdentification)}
|
|
5747
|
+
`));
|
|
5748
|
+
}
|
|
5749
|
+
/**
|
|
5750
|
+
* Creates the detailed error for unresolved or circular task dependencies.
|
|
5751
|
+
*
|
|
5752
|
+
* @private internal utility of `validatePipelineDependencyResolution`
|
|
5753
|
+
*/
|
|
5754
|
+
function createUnresolvedTasksError(unresolvedTasks, resolvedParameterNames, pipelineIdentification) {
|
|
5755
|
+
return new PipelineLogicError(
|
|
5756
|
+
// TODO: [🐎] DRY
|
|
5757
|
+
spaceTrim$1((block) => `
|
|
5758
|
+
|
|
5759
|
+
Can not resolve some parameters:
|
|
5760
|
+
Either you are using a parameter that is not defined, or there are some circular dependencies.
|
|
5761
|
+
|
|
5762
|
+
${block(pipelineIdentification)}
|
|
5763
|
+
|
|
5764
|
+
**Can not resolve:**
|
|
5765
|
+
${block(unresolvedTasks
|
|
5766
|
+
.map(({ resultingParameterName, dependentParameterNames }) => `- Parameter \`{${resultingParameterName}}\` which depends on ${dependentParameterNames
|
|
5767
|
+
.map((dependentParameterName) => `\`{${dependentParameterName}}\``)
|
|
5768
|
+
.join(' and ')}`)
|
|
5769
|
+
.join('\n'))}
|
|
5770
|
+
|
|
5771
|
+
**Resolved:**
|
|
5772
|
+
${block(resolvedParameterNames
|
|
5773
|
+
.filter((name) => !RESERVED_PARAMETER_NAMES.includes(name))
|
|
5774
|
+
.map((name) => `- Parameter \`{${name}}\``)
|
|
5775
|
+
.join('\n'))}
|
|
5776
|
+
|
|
5777
|
+
|
|
5778
|
+
**Reserved (which are available):**
|
|
5779
|
+
${block(resolvedParameterNames
|
|
5780
|
+
.filter((name) => RESERVED_PARAMETER_NAMES.includes(name))
|
|
5781
|
+
.map((name) => `- Parameter \`{${name}}\``)
|
|
5782
|
+
.join('\n'))}
|
|
5783
|
+
|
|
5784
|
+
|
|
5785
|
+
`));
|
|
5193
5786
|
}
|
|
5194
5787
|
|
|
5195
5788
|
/**
|
|
@@ -5266,168 +5859,19 @@ function isValidPipelineUrl(url) {
|
|
|
5266
5859
|
}
|
|
5267
5860
|
// TODO: [🐠] Maybe more info why the URL is invalid
|
|
5268
5861
|
|
|
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
5862
|
/**
|
|
5353
5863
|
* Validates pipeline-level metadata fields.
|
|
5354
5864
|
*
|
|
5355
|
-
* @private
|
|
5865
|
+
* @private function of `validatePipeline`
|
|
5356
5866
|
*/
|
|
5357
5867
|
function validatePipelineMetadata({ pipeline, pipelineIdentification }) {
|
|
5358
5868
|
validatePipelineUrl(pipeline, pipelineIdentification);
|
|
5359
5869
|
validatePipelineBookVersion(pipeline, pipelineIdentification);
|
|
5360
5870
|
}
|
|
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
5871
|
/**
|
|
5428
5872
|
* Validates the pipeline URL, when present.
|
|
5429
5873
|
*
|
|
5430
|
-
* @private internal utility of `
|
|
5874
|
+
* @private internal utility of `validatePipelineMetadata`
|
|
5431
5875
|
*/
|
|
5432
5876
|
function validatePipelineUrl(pipeline, pipelineIdentification) {
|
|
5433
5877
|
if (pipeline.pipelineUrl === undefined || isValidPipelineUrl(pipeline.pipelineUrl)) {
|
|
@@ -5443,7 +5887,7 @@ function validatePipelineUrl(pipeline, pipelineIdentification) {
|
|
|
5443
5887
|
/**
|
|
5444
5888
|
* Validates the Promptbook version, when present.
|
|
5445
5889
|
*
|
|
5446
|
-
* @private internal utility of `
|
|
5890
|
+
* @private internal utility of `validatePipelineMetadata`
|
|
5447
5891
|
*/
|
|
5448
5892
|
function validatePipelineBookVersion(pipeline, pipelineIdentification) {
|
|
5449
5893
|
if (pipeline.bookVersion === undefined || isValidPromptbookVersion(pipeline.bookVersion)) {
|
|
@@ -5456,48 +5900,31 @@ function validatePipelineBookVersion(pipeline, pipelineIdentification) {
|
|
|
5456
5900
|
${block(pipelineIdentification)}
|
|
5457
5901
|
`));
|
|
5458
5902
|
}
|
|
5903
|
+
|
|
5459
5904
|
/**
|
|
5460
|
-
* Validates
|
|
5905
|
+
* Validates all pipeline parameter declarations.
|
|
5461
5906
|
*
|
|
5462
|
-
* @private
|
|
5907
|
+
* @private function of `validatePipeline`
|
|
5463
5908
|
*/
|
|
5464
|
-
function
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
return;
|
|
5909
|
+
function validatePipelineParameters({ pipeline, pipelineIdentification }) {
|
|
5910
|
+
for (const parameter of pipeline.parameters) {
|
|
5911
|
+
validatePipelineParameter(parameter, pipeline, pipelineIdentification);
|
|
5468
5912
|
}
|
|
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
5913
|
}
|
|
5478
5914
|
/**
|
|
5479
|
-
* Validates
|
|
5915
|
+
* Validates one pipeline parameter declaration.
|
|
5480
5916
|
*
|
|
5481
|
-
* @private internal utility of `
|
|
5917
|
+
* @private internal utility of `validatePipelineParameters`
|
|
5482
5918
|
*/
|
|
5483
|
-
function
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
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
|
-
`));
|
|
5919
|
+
function validatePipelineParameter(parameter, pipeline, pipelineIdentification) {
|
|
5920
|
+
validateParameterDirection(parameter, pipelineIdentification);
|
|
5921
|
+
validateParameterUsage(parameter, pipeline, pipelineIdentification);
|
|
5922
|
+
validateParameterDefinition(parameter, pipeline, pipelineIdentification);
|
|
5496
5923
|
}
|
|
5497
5924
|
/**
|
|
5498
5925
|
* Validates that one parameter does not declare incompatible directions.
|
|
5499
5926
|
*
|
|
5500
|
-
* @private internal utility of `
|
|
5927
|
+
* @private internal utility of `validatePipelineParameters`
|
|
5501
5928
|
*/
|
|
5502
5929
|
function validateParameterDirection(parameter, pipelineIdentification) {
|
|
5503
5930
|
if (!parameter.isInput || !parameter.isOutput) {
|
|
@@ -5514,7 +5941,7 @@ function validateParameterDirection(parameter, pipelineIdentification) {
|
|
|
5514
5941
|
/**
|
|
5515
5942
|
* Validates that one intermediate parameter is actually consumed by at least one task.
|
|
5516
5943
|
*
|
|
5517
|
-
* @private internal utility of `
|
|
5944
|
+
* @private internal utility of `validatePipelineParameters`
|
|
5518
5945
|
*/
|
|
5519
5946
|
function validateParameterUsage(parameter, pipeline, pipelineIdentification) {
|
|
5520
5947
|
if (parameter.isInput || parameter.isOutput || isParameterUsedByAnyTask(parameter, pipeline.tasks)) {
|
|
@@ -5533,7 +5960,7 @@ function validateParameterUsage(parameter, pipeline, pipelineIdentification) {
|
|
|
5533
5960
|
/**
|
|
5534
5961
|
* Validates that one non-input parameter is produced by at least one task.
|
|
5535
5962
|
*
|
|
5536
|
-
* @private internal utility of `
|
|
5963
|
+
* @private internal utility of `validatePipelineParameters`
|
|
5537
5964
|
*/
|
|
5538
5965
|
function validateParameterDefinition(parameter, pipeline, pipelineIdentification) {
|
|
5539
5966
|
if (parameter.isInput || isParameterDefinedByAnyTask(parameter, pipeline.tasks)) {
|
|
@@ -5552,7 +5979,7 @@ function validateParameterDefinition(parameter, pipeline, pipelineIdentification
|
|
|
5552
5979
|
/**
|
|
5553
5980
|
* Checks whether one parameter is consumed by at least one task dependency list.
|
|
5554
5981
|
*
|
|
5555
|
-
* @private internal utility of `
|
|
5982
|
+
* @private internal utility of `validatePipelineParameters`
|
|
5556
5983
|
*/
|
|
5557
5984
|
function isParameterUsedByAnyTask(parameter, tasks) {
|
|
5558
5985
|
return tasks.some((task) => task.dependentParameterNames.includes(parameter.name));
|
|
@@ -5560,23 +5987,46 @@ function isParameterUsedByAnyTask(parameter, tasks) {
|
|
|
5560
5987
|
/**
|
|
5561
5988
|
* Checks whether one parameter is produced by at least one task.
|
|
5562
5989
|
*
|
|
5563
|
-
* @private internal utility of `
|
|
5990
|
+
* @private internal utility of `validatePipelineParameters`
|
|
5564
5991
|
*/
|
|
5565
5992
|
function isParameterDefinedByAnyTask(parameter, tasks) {
|
|
5566
5993
|
return tasks.some((task) => task.resultingParameterName === parameter.name);
|
|
5567
5994
|
}
|
|
5995
|
+
|
|
5996
|
+
/**
|
|
5997
|
+
* Validates all pipeline tasks and their per-task invariants.
|
|
5998
|
+
*
|
|
5999
|
+
* @private function of `validatePipeline`
|
|
6000
|
+
*/
|
|
6001
|
+
function validatePipelineTasks({ pipeline, pipelineIdentification }) {
|
|
6002
|
+
// Note: All input parameters are defined - so that they can be used as result of some task
|
|
6003
|
+
const definedParameters = createInitiallyDefinedParameters(pipeline);
|
|
6004
|
+
for (const task of pipeline.tasks) {
|
|
6005
|
+
validatePipelineTask(task, definedParameters, pipelineIdentification);
|
|
6006
|
+
}
|
|
6007
|
+
}
|
|
5568
6008
|
/**
|
|
5569
6009
|
* Collects the parameter names that are already defined before task validation starts.
|
|
5570
6010
|
*
|
|
5571
|
-
* @private internal utility of `
|
|
6011
|
+
* @private internal utility of `validatePipelineTasks`
|
|
5572
6012
|
*/
|
|
5573
6013
|
function createInitiallyDefinedParameters(pipeline) {
|
|
5574
6014
|
return new Set(pipeline.parameters.filter(({ isInput }) => isInput).map(({ name }) => name));
|
|
5575
6015
|
}
|
|
6016
|
+
/**
|
|
6017
|
+
* Validates one pipeline task and its invariants.
|
|
6018
|
+
*
|
|
6019
|
+
* @private internal utility of `validatePipelineTasks`
|
|
6020
|
+
*/
|
|
6021
|
+
function validatePipelineTask(task, definedParameters, pipelineIdentification) {
|
|
6022
|
+
validateTaskResultingParameter(task, definedParameters, pipelineIdentification);
|
|
6023
|
+
validateTaskJokers(task, pipelineIdentification);
|
|
6024
|
+
validateTaskExpectations(task, pipelineIdentification);
|
|
6025
|
+
}
|
|
5576
6026
|
/**
|
|
5577
6027
|
* Validates one task result parameter declaration and marks it as defined.
|
|
5578
6028
|
*
|
|
5579
|
-
* @private internal utility of `
|
|
6029
|
+
* @private internal utility of `validatePipelineTasks`
|
|
5580
6030
|
*/
|
|
5581
6031
|
function validateTaskResultingParameter(task, definedParameters, pipelineIdentification) {
|
|
5582
6032
|
if (definedParameters.has(task.resultingParameterName)) {
|
|
@@ -5598,7 +6048,7 @@ function validateTaskResultingParameter(task, definedParameters, pipelineIdentif
|
|
|
5598
6048
|
/**
|
|
5599
6049
|
* Validates joker parameters for one task.
|
|
5600
6050
|
*
|
|
5601
|
-
* @private internal utility of `
|
|
6051
|
+
* @private internal utility of `validatePipelineTasks`
|
|
5602
6052
|
*/
|
|
5603
6053
|
function validateTaskJokers(task, pipelineIdentification) {
|
|
5604
6054
|
if (!hasTaskJokers(task)) {
|
|
@@ -5610,7 +6060,7 @@ function validateTaskJokers(task, pipelineIdentification) {
|
|
|
5610
6060
|
/**
|
|
5611
6061
|
* Checks whether one task declares any joker parameters.
|
|
5612
6062
|
*
|
|
5613
|
-
* @private internal utility of `
|
|
6063
|
+
* @private internal utility of `validatePipelineTasks`
|
|
5614
6064
|
*/
|
|
5615
6065
|
function hasTaskJokers(task) {
|
|
5616
6066
|
return !!task.jokerParameterNames && task.jokerParameterNames.length > 0;
|
|
@@ -5618,7 +6068,7 @@ function hasTaskJokers(task) {
|
|
|
5618
6068
|
/**
|
|
5619
6069
|
* Validates that a task has the required supporting features when using jokers.
|
|
5620
6070
|
*
|
|
5621
|
-
* @private internal utility of `
|
|
6071
|
+
* @private internal utility of `validatePipelineTasks`
|
|
5622
6072
|
*/
|
|
5623
6073
|
function validateTaskSupportsJokers(task, pipelineIdentification) {
|
|
5624
6074
|
if (task.format || task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
|
|
@@ -5633,7 +6083,7 @@ function validateTaskSupportsJokers(task, pipelineIdentification) {
|
|
|
5633
6083
|
/**
|
|
5634
6084
|
* Validates that every joker parameter is also listed among task dependencies.
|
|
5635
6085
|
*
|
|
5636
|
-
* @private internal utility of `
|
|
6086
|
+
* @private internal utility of `validatePipelineTasks`
|
|
5637
6087
|
*/
|
|
5638
6088
|
function validateTaskJokerDependencies(task, pipelineIdentification) {
|
|
5639
6089
|
for (const joker of task.jokerParameterNames) {
|
|
@@ -5650,7 +6100,7 @@ function validateTaskJokerDependencies(task, pipelineIdentification) {
|
|
|
5650
6100
|
/**
|
|
5651
6101
|
* Validates all expectation bounds configured on one task.
|
|
5652
6102
|
*
|
|
5653
|
-
* @private internal utility of `
|
|
6103
|
+
* @private internal utility of `validatePipelineTasks`
|
|
5654
6104
|
*/
|
|
5655
6105
|
function validateTaskExpectations(task, pipelineIdentification) {
|
|
5656
6106
|
if (!task.expectations) {
|
|
@@ -5665,7 +6115,7 @@ function validateTaskExpectations(task, pipelineIdentification) {
|
|
|
5665
6115
|
/**
|
|
5666
6116
|
* Validates the minimum and maximum expectation ordering for one unit.
|
|
5667
6117
|
*
|
|
5668
|
-
* @private internal utility of `
|
|
6118
|
+
* @private internal utility of `validatePipelineTasks`
|
|
5669
6119
|
*/
|
|
5670
6120
|
function validateTaskExpectationRange(unit, min, max, pipelineIdentification) {
|
|
5671
6121
|
if (min === undefined || max === undefined || min <= max) {
|
|
@@ -5680,7 +6130,7 @@ function validateTaskExpectationRange(unit, min, max, pipelineIdentification) {
|
|
|
5680
6130
|
/**
|
|
5681
6131
|
* Validates the minimum expectation bound for one unit.
|
|
5682
6132
|
*
|
|
5683
|
-
* @private internal utility of `
|
|
6133
|
+
* @private internal utility of `validatePipelineTasks`
|
|
5684
6134
|
*/
|
|
5685
6135
|
function validateTaskExpectationMin(unit, min, pipelineIdentification) {
|
|
5686
6136
|
if (min === undefined || min >= 0) {
|
|
@@ -5695,7 +6145,7 @@ function validateTaskExpectationMin(unit, min, pipelineIdentification) {
|
|
|
5695
6145
|
/**
|
|
5696
6146
|
* Validates the maximum expectation bound for one unit.
|
|
5697
6147
|
*
|
|
5698
|
-
* @private internal utility of `
|
|
6148
|
+
* @private internal utility of `validatePipelineTasks`
|
|
5699
6149
|
*/
|
|
5700
6150
|
function validateTaskExpectationMax(unit, max, pipelineIdentification) {
|
|
5701
6151
|
if (max === undefined || max > 0) {
|
|
@@ -5707,123 +6157,62 @@ function validateTaskExpectationMax(unit, max, pipelineIdentification) {
|
|
|
5707
6157
|
${block(pipelineIdentification)}
|
|
5708
6158
|
`));
|
|
5709
6159
|
}
|
|
6160
|
+
|
|
5710
6161
|
/**
|
|
5711
|
-
*
|
|
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.
|
|
6162
|
+
* Validates PipelineJson if it is logically valid
|
|
5731
6163
|
*
|
|
5732
|
-
*
|
|
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.
|
|
6164
|
+
* It checks:
|
|
6165
|
+
* - if it has correct parameters dependency
|
|
5746
6166
|
*
|
|
5747
|
-
*
|
|
5748
|
-
|
|
5749
|
-
|
|
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.
|
|
6167
|
+
* It does NOT check:
|
|
6168
|
+
* - if it is valid json
|
|
6169
|
+
* - if it is meaningful
|
|
5760
6170
|
*
|
|
5761
|
-
*
|
|
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.
|
|
6171
|
+
* Note: [🔂] This function is idempotent.
|
|
5771
6172
|
*
|
|
5772
|
-
* @
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
return unresolvedTasks.filter((task) => task.dependentParameterNames.every((name) => resolvedParameterNames.includes(name)));
|
|
5776
|
-
}
|
|
5777
|
-
/**
|
|
5778
|
-
* Creates the unexpected loop-limit error for dependency resolution.
|
|
6173
|
+
* @param pipeline valid or invalid PipelineJson
|
|
6174
|
+
* @returns the same pipeline if it is logically valid
|
|
6175
|
+
* @throws {PipelineLogicError} on logical error in the pipeline
|
|
5779
6176
|
*
|
|
5780
|
-
* @
|
|
6177
|
+
* @public exported from `@promptbook/core`
|
|
5781
6178
|
*/
|
|
5782
|
-
function
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
6179
|
+
function validatePipeline(pipeline) {
|
|
6180
|
+
if (IS_PIPELINE_LOGIC_VALIDATED) {
|
|
6181
|
+
validatePipeline_InnerFunction(pipeline);
|
|
6182
|
+
}
|
|
6183
|
+
else {
|
|
6184
|
+
try {
|
|
6185
|
+
validatePipeline_InnerFunction(pipeline);
|
|
6186
|
+
}
|
|
6187
|
+
catch (error) {
|
|
6188
|
+
if (!(error instanceof PipelineLogicError)) {
|
|
6189
|
+
throw error;
|
|
6190
|
+
}
|
|
6191
|
+
console.error(spaceTrim$1((block) => `
|
|
6192
|
+
Pipeline is not valid but logic errors are temporarily disabled via \`IS_PIPELINE_LOGIC_VALIDATED\`
|
|
5786
6193
|
|
|
5787
|
-
|
|
5788
|
-
|
|
6194
|
+
${block(error.message)}
|
|
6195
|
+
`));
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
return pipeline;
|
|
5789
6199
|
}
|
|
5790
6200
|
/**
|
|
5791
|
-
*
|
|
6201
|
+
* Validates pipeline inner function.
|
|
5792
6202
|
*
|
|
5793
|
-
* @private internal
|
|
6203
|
+
* @private internal function for `validatePipeline`
|
|
5794
6204
|
*/
|
|
5795
|
-
function
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
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
|
-
`));
|
|
6205
|
+
function validatePipeline_InnerFunction(pipeline) {
|
|
6206
|
+
// TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
|
|
6207
|
+
const context = createPipelineValidationContext(pipeline);
|
|
6208
|
+
validatePipelineMetadata(context);
|
|
6209
|
+
validatePipelineCollectionsStructure(context);
|
|
6210
|
+
validatePipelineParameters(context);
|
|
6211
|
+
validatePipelineTasks(context);
|
|
6212
|
+
validatePipelineDependencyResolution(context);
|
|
6213
|
+
// Note: Check that formfactor is corresponding to the pipeline interface
|
|
6214
|
+
// TODO: !!6 Implement this
|
|
6215
|
+
// pipeline.formfactorName
|
|
5827
6216
|
}
|
|
5828
6217
|
/**
|
|
5829
6218
|
* TODO: [🧞♀️] Do not allow joker + foreach
|
|
@@ -14143,7 +14532,7 @@ class MessageSuffixCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
14143
14532
|
* META commitment definition
|
|
14144
14533
|
*
|
|
14145
14534
|
* The META commitment handles all meta-information about the agent such as:
|
|
14146
|
-
* - META AVATAR: Sets the agent's built-in default avatar visual
|
|
14535
|
+
* - META AVATAR / META VISUAL: Sets the agent's built-in default avatar visual
|
|
14147
14536
|
* - META IMAGE: Sets the agent's avatar/profile image URL
|
|
14148
14537
|
* - META LINK: Provides profile/source links for the person the agent models
|
|
14149
14538
|
* - META DOMAIN: Sets the canonical custom domain/host of the agent
|
|
@@ -14158,7 +14547,7 @@ class MessageSuffixCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
14158
14547
|
* Example usage in agent source:
|
|
14159
14548
|
*
|
|
14160
14549
|
* ```book
|
|
14161
|
-
* META
|
|
14550
|
+
* META VISUAL pixel-art
|
|
14162
14551
|
* META IMAGE https://example.com/avatar.jpg
|
|
14163
14552
|
* META LINK https://twitter.com/username
|
|
14164
14553
|
* META DOMAIN my-agent.com
|
|
@@ -14198,7 +14587,7 @@ class MetaCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
14198
14587
|
|
|
14199
14588
|
## Supported META types
|
|
14200
14589
|
|
|
14201
|
-
- **META AVATAR** - Sets the agent's built-in default avatar visual
|
|
14590
|
+
- **META AVATAR** / **META VISUAL** - Sets the agent's built-in default avatar visual
|
|
14202
14591
|
- **META IMAGE** - Sets the agent's avatar/profile image URL
|
|
14203
14592
|
- **META LINK** - Provides profile/source links for the person the agent models
|
|
14204
14593
|
- **META DOMAIN** - Sets the canonical custom domain/host of the agent
|
|
@@ -14222,7 +14611,7 @@ class MetaCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
14222
14611
|
\`\`\`book
|
|
14223
14612
|
Professional Assistant
|
|
14224
14613
|
|
|
14225
|
-
META
|
|
14614
|
+
META VISUAL octopus3
|
|
14226
14615
|
META IMAGE https://example.com/professional-avatar.jpg
|
|
14227
14616
|
META TITLE Senior Business Consultant
|
|
14228
14617
|
META DESCRIPTION Specialized in strategic planning and project management
|
|
@@ -17393,452 +17782,13 @@ function formatAlphaHex$1(opacity) {
|
|
|
17393
17782
|
.padStart(2, '0');
|
|
17394
17783
|
}
|
|
17395
17784
|
|
|
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
17785
|
/* eslint-disable no-magic-numbers */
|
|
17836
17786
|
/**
|
|
17837
17787
|
* Light direction used by the single-mesh octopus shading.
|
|
17838
17788
|
*
|
|
17839
17789
|
* @private helper of `octopus3d2AvatarVisual`
|
|
17840
17790
|
*/
|
|
17841
|
-
const LIGHT_DIRECTION$
|
|
17791
|
+
const LIGHT_DIRECTION$3 = normalizeVector3({
|
|
17842
17792
|
x: 0.38,
|
|
17843
17793
|
y: -0.6,
|
|
17844
17794
|
z: 0.98,
|
|
@@ -18077,7 +18027,7 @@ function resolveVisibleBlobbyOctopusPatches(options) {
|
|
|
18077
18027
|
transformedCorners[2].z +
|
|
18078
18028
|
transformedCorners[3].z) /
|
|
18079
18029
|
4,
|
|
18080
|
-
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$
|
|
18030
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
|
|
18081
18031
|
fillStyle: resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
|
|
18082
18032
|
outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
|
|
18083
18033
|
});
|
|
@@ -18192,7 +18142,7 @@ function drawBlobbySurfacePatch(context, surfacePatch) {
|
|
|
18192
18142
|
*
|
|
18193
18143
|
* @private helper of `octopus3d3AvatarVisual`
|
|
18194
18144
|
*/
|
|
18195
|
-
const LIGHT_DIRECTION$
|
|
18145
|
+
const LIGHT_DIRECTION$2 = normalizeVector3({
|
|
18196
18146
|
x: 0.34,
|
|
18197
18147
|
y: -0.62,
|
|
18198
18148
|
z: 1,
|
|
@@ -18499,7 +18449,7 @@ function resolveVisibleContinuousOctopusPatches(options) {
|
|
|
18499
18449
|
transformedCorners[2].z +
|
|
18500
18450
|
transformedCorners[3].z) /
|
|
18501
18451
|
4,
|
|
18502
|
-
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$
|
|
18452
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
|
|
18503
18453
|
fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
|
|
18504
18454
|
outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
|
|
18505
18455
|
});
|
|
@@ -18814,7 +18764,7 @@ function smoothStep$1(edgeStart, edgeEnd, value) {
|
|
|
18814
18764
|
*
|
|
18815
18765
|
* @private helper of `octopus3d4AvatarVisual`
|
|
18816
18766
|
*/
|
|
18817
|
-
const LIGHT_DIRECTION = normalizeVector3({
|
|
18767
|
+
const LIGHT_DIRECTION$1 = normalizeVector3({
|
|
18818
18768
|
x: 0.32,
|
|
18819
18769
|
y: -0.66,
|
|
18820
18770
|
z: 1,
|
|
@@ -18885,7 +18835,7 @@ const octopus3d4AvatarVisual = {
|
|
|
18885
18835
|
isAnimated: true,
|
|
18886
18836
|
supportsPointerTracking: true,
|
|
18887
18837
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
18888
|
-
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots } = getOctopus3d4StableState(createRandom);
|
|
18838
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots, } = getOctopus3d4StableState(createRandom);
|
|
18889
18839
|
const sceneCenterX = size * 0.5;
|
|
18890
18840
|
const sceneCenterY = size * 0.535;
|
|
18891
18841
|
const bob = Math.sin(timeMs / 980 + animationPhase) * size * 0.013;
|
|
@@ -19160,7 +19110,7 @@ function resolveVisibleBlobbyContinuousPatches(options) {
|
|
|
19160
19110
|
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
19161
19111
|
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
19162
19112
|
];
|
|
19163
|
-
const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1);
|
|
19113
|
+
const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1);
|
|
19164
19114
|
const rimLightIntensity = Math.pow(clampNumber$1(dotProduct3D(surfaceNormal, RIM_LIGHT_DIRECTION), 0, 1), 2.6);
|
|
19165
19115
|
surfacePatches.push({
|
|
19166
19116
|
corners: projectedCorners,
|
|
@@ -19233,10 +19183,7 @@ function sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude,
|
|
|
19233
19183
|
Math.max(0, Math.cos(effectiveLongitude)) * 0.12 +
|
|
19234
19184
|
lowerBlend * tentacleInfluence.core * (0.12 + tentacleInfluence.depthScale * 0.07) -
|
|
19235
19185
|
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;
|
|
19186
|
+
const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) * radiusX;
|
|
19240
19187
|
const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
|
|
19241
19188
|
const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.74;
|
|
19242
19189
|
const lowerDrop = lowerBlend *
|
|
@@ -19248,9 +19195,7 @@ function sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude,
|
|
|
19248
19195
|
(morphologyProfile.tentacles.flowLengthScale - 1) * 0.1));
|
|
19249
19196
|
const combinedTentacleSway = (primaryTentacleWave + secondaryTentacleWave) * radiusX * (0.06 + tipBlend * 0.06);
|
|
19250
19197
|
return {
|
|
19251
|
-
x: Math.sin(effectiveLongitude) * planarRadiusX +
|
|
19252
|
-
combinedTentacleSway +
|
|
19253
|
-
tentacleCurl * radiusX * 0.18,
|
|
19198
|
+
x: Math.sin(effectiveLongitude) * planarRadiusX + combinedTentacleSway + tentacleCurl * radiusX * 0.18,
|
|
19254
19199
|
y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
|
|
19255
19200
|
upperBlend * radiusY * 0.12 +
|
|
19256
19201
|
lowerDrop +
|
|
@@ -19564,6 +19509,445 @@ function formatAlphaHex(opacity) {
|
|
|
19564
19509
|
.padStart(2, '0');
|
|
19565
19510
|
}
|
|
19566
19511
|
|
|
19512
|
+
/* eslint-disable no-magic-numbers */
|
|
19513
|
+
/**
|
|
19514
|
+
* Light direction used by the organic 3D octopus shading.
|
|
19515
|
+
*
|
|
19516
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19517
|
+
*/
|
|
19518
|
+
const LIGHT_DIRECTION = normalizeVector3({
|
|
19519
|
+
x: 0.48,
|
|
19520
|
+
y: -0.62,
|
|
19521
|
+
z: 0.94,
|
|
19522
|
+
});
|
|
19523
|
+
/**
|
|
19524
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
19525
|
+
*
|
|
19526
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19527
|
+
*/
|
|
19528
|
+
const octopus3dStableStateCache = new WeakMap();
|
|
19529
|
+
/**
|
|
19530
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
19531
|
+
*
|
|
19532
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19533
|
+
*/
|
|
19534
|
+
function getOctopus3dStableState(createRandom) {
|
|
19535
|
+
const cached = octopus3dStableStateCache.get(createRandom);
|
|
19536
|
+
if (cached !== undefined) {
|
|
19537
|
+
return cached;
|
|
19538
|
+
}
|
|
19539
|
+
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
19540
|
+
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
19541
|
+
const leftEyePhaseOffset = eyeRandom() * 0.6;
|
|
19542
|
+
const rightEyePhaseOffset = eyeRandom() * 0.6;
|
|
19543
|
+
const state = {
|
|
19544
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
19545
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
19546
|
+
leftEyePhaseOffset,
|
|
19547
|
+
rightEyePhaseOffset,
|
|
19548
|
+
};
|
|
19549
|
+
octopus3dStableStateCache.set(createRandom, state);
|
|
19550
|
+
return state;
|
|
19551
|
+
}
|
|
19552
|
+
/**
|
|
19553
|
+
* Proper 3D Octopus visual built from projected organic meshes and tentacles.
|
|
19554
|
+
*
|
|
19555
|
+
* @private built-in avatar visual
|
|
19556
|
+
*/
|
|
19557
|
+
const octopus3dAvatarVisual = {
|
|
19558
|
+
id: 'octopus3d',
|
|
19559
|
+
title: 'Octopus 3D',
|
|
19560
|
+
description: 'Proper 3D octopus portrait with a turning silhouette, expressive eyes, and depth-sorted tentacles.',
|
|
19561
|
+
isAnimated: true,
|
|
19562
|
+
supportsPointerTracking: true,
|
|
19563
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
19564
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
|
|
19565
|
+
const sceneCenterX = size * 0.5;
|
|
19566
|
+
const sceneCenterY = size * 0.56;
|
|
19567
|
+
const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
|
|
19568
|
+
const mantleCenter = {
|
|
19569
|
+
x: interaction.bodyOffsetX * size * 0.042 + size * morphologyProfile.body.centerXJitterRatio * 0.65,
|
|
19570
|
+
y: -size * 0.09 + interaction.bodyOffsetY * size * 0.028 + bob,
|
|
19571
|
+
z: interaction.intensity * size * 0.012,
|
|
19572
|
+
};
|
|
19573
|
+
const underbodyCenter = {
|
|
19574
|
+
x: mantleCenter.x * 0.86,
|
|
19575
|
+
y: mantleCenter.y + size * 0.168,
|
|
19576
|
+
z: mantleCenter.z - size * 0.018,
|
|
19577
|
+
};
|
|
19578
|
+
const mantleRadiusX = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch;
|
|
19579
|
+
const mantleRadiusY = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1;
|
|
19580
|
+
const mantleRadiusZ = size *
|
|
19581
|
+
morphologyProfile.body.bodyRadiusRatio *
|
|
19582
|
+
(0.9 + (morphologyProfile.body.horizontalStretch - 1) * 0.3);
|
|
19583
|
+
const underbodyRadiusX = mantleRadiusX * (0.9 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.08);
|
|
19584
|
+
const underbodyRadiusY = mantleRadiusY * (0.44 + morphologyProfile.body.lowerDropRatio * 3.1);
|
|
19585
|
+
const underbodyRadiusZ = mantleRadiusZ * 0.78;
|
|
19586
|
+
const bodyYaw = -0.18 +
|
|
19587
|
+
Math.sin(timeMs / 2400 + animationPhase) * 0.05 +
|
|
19588
|
+
interaction.bodyOffsetX * 0.18 +
|
|
19589
|
+
interaction.gazeX * 0.22;
|
|
19590
|
+
const bodyPitch = -0.08 +
|
|
19591
|
+
Math.cos(timeMs / 2700 + animationPhase * 0.6) * 0.025 -
|
|
19592
|
+
interaction.bodyOffsetY * 0.08 -
|
|
19593
|
+
interaction.gazeY * 0.08;
|
|
19594
|
+
const headYaw = bodyYaw - 0.04 + interaction.gazeX * 0.56;
|
|
19595
|
+
const headPitch = bodyPitch - 0.02 - interaction.gazeY * 0.32;
|
|
19596
|
+
const mantlePatches = resolveVisibleEllipsoidPatches({
|
|
19597
|
+
center: mantleCenter,
|
|
19598
|
+
radiusX: mantleRadiusX,
|
|
19599
|
+
radiusY: mantleRadiusY,
|
|
19600
|
+
radiusZ: mantleRadiusZ,
|
|
19601
|
+
rotationX: headPitch,
|
|
19602
|
+
rotationY: headYaw,
|
|
19603
|
+
sceneCenterX,
|
|
19604
|
+
sceneCenterY,
|
|
19605
|
+
size,
|
|
19606
|
+
palette,
|
|
19607
|
+
verticalColorBias: 0,
|
|
19608
|
+
outlineColor: `${palette.highlight}7a`,
|
|
19609
|
+
});
|
|
19610
|
+
const underbodyPatches = resolveVisibleEllipsoidPatches({
|
|
19611
|
+
center: underbodyCenter,
|
|
19612
|
+
radiusX: underbodyRadiusX,
|
|
19613
|
+
radiusY: underbodyRadiusY,
|
|
19614
|
+
radiusZ: underbodyRadiusZ,
|
|
19615
|
+
rotationX: bodyPitch,
|
|
19616
|
+
rotationY: bodyYaw,
|
|
19617
|
+
sceneCenterX,
|
|
19618
|
+
sceneCenterY,
|
|
19619
|
+
size,
|
|
19620
|
+
palette,
|
|
19621
|
+
verticalColorBias: 0.18,
|
|
19622
|
+
outlineColor: `${palette.shadow}8f`,
|
|
19623
|
+
});
|
|
19624
|
+
const tentacleStrokes = createOctopusTentacleStrokes({
|
|
19625
|
+
createRandom,
|
|
19626
|
+
morphologyProfile,
|
|
19627
|
+
timeMs,
|
|
19628
|
+
size,
|
|
19629
|
+
center: underbodyCenter,
|
|
19630
|
+
radiusX: underbodyRadiusX,
|
|
19631
|
+
radiusY: underbodyRadiusY,
|
|
19632
|
+
radiusZ: underbodyRadiusZ,
|
|
19633
|
+
rotationX: bodyPitch,
|
|
19634
|
+
rotationY: bodyYaw,
|
|
19635
|
+
sceneCenterX,
|
|
19636
|
+
sceneCenterY,
|
|
19637
|
+
animationPhase,
|
|
19638
|
+
});
|
|
19639
|
+
const faceEyeSpacing = size * morphologyProfile.face.eyeSpacingRatio * 0.92;
|
|
19640
|
+
const faceEyeYOffset = size * morphologyProfile.face.eyeCenterYOffsetRatio - mantleRadiusY * 0.02;
|
|
19641
|
+
const faceEyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.82;
|
|
19642
|
+
const faceEyeRadiusY = faceEyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.96;
|
|
19643
|
+
const mouthHalfWidth = size * morphologyProfile.face.mouthWidthRatio * 0.92;
|
|
19644
|
+
const mouthY = size * morphologyProfile.face.mouthYOffsetRatio + mantleRadiusY * 0.08;
|
|
19645
|
+
drawAvatarFrame(context, size, palette);
|
|
19646
|
+
drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
|
|
19647
|
+
drawOctopus3dShadow(context, size, palette, interaction, timeMs);
|
|
19648
|
+
for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => !candidateTentacleStroke.isFrontFacing)) {
|
|
19649
|
+
drawTentacleStroke(context, tentacleStroke, palette);
|
|
19650
|
+
}
|
|
19651
|
+
for (const surfacePatch of [...mantlePatches, ...underbodyPatches].sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
|
|
19652
|
+
drawSurfacePatch(context, surfacePatch);
|
|
19653
|
+
}
|
|
19654
|
+
for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
|
|
19655
|
+
drawTentacleStroke(context, tentacleStroke, palette);
|
|
19656
|
+
}
|
|
19657
|
+
drawProjectedOrganicEye(context, {
|
|
19658
|
+
x: -faceEyeSpacing,
|
|
19659
|
+
y: faceEyeYOffset,
|
|
19660
|
+
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
|
|
19661
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
19662
|
+
drawProjectedOrganicEye(context, {
|
|
19663
|
+
x: faceEyeSpacing,
|
|
19664
|
+
y: faceEyeYOffset,
|
|
19665
|
+
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
|
|
19666
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
19667
|
+
drawProjectedOrganicMouth(context, [
|
|
19668
|
+
{
|
|
19669
|
+
x: -mouthHalfWidth,
|
|
19670
|
+
y: mouthY,
|
|
19671
|
+
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -mouthHalfWidth, mouthY),
|
|
19672
|
+
},
|
|
19673
|
+
{
|
|
19674
|
+
x: size * morphologyProfile.face.mouthCenterOffsetRatio,
|
|
19675
|
+
y: mouthY +
|
|
19676
|
+
size * morphologyProfile.face.mouthCurveDepthRatio * 0.38 +
|
|
19677
|
+
Math.sin(timeMs / 760 + animationPhase) * size * 0.01 +
|
|
19678
|
+
interaction.gazeY * size * 0.01,
|
|
19679
|
+
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, size * morphologyProfile.face.mouthCenterOffsetRatio, mouthY),
|
|
19680
|
+
},
|
|
19681
|
+
{
|
|
19682
|
+
x: mouthHalfWidth,
|
|
19683
|
+
y: mouthY,
|
|
19684
|
+
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, mouthHalfWidth, mouthY),
|
|
19685
|
+
},
|
|
19686
|
+
], mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, palette, size);
|
|
19687
|
+
},
|
|
19688
|
+
};
|
|
19689
|
+
/**
|
|
19690
|
+
* Draws the atmospheric underwater glow behind the octopus mesh.
|
|
19691
|
+
*
|
|
19692
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19693
|
+
*/
|
|
19694
|
+
function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
|
|
19695
|
+
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);
|
|
19696
|
+
glowGradient.addColorStop(0, `${palette.highlight}5c`);
|
|
19697
|
+
glowGradient.addColorStop(0.36, `${palette.accent}24`);
|
|
19698
|
+
glowGradient.addColorStop(1, `${palette.highlight}00`);
|
|
19699
|
+
context.fillStyle = glowGradient;
|
|
19700
|
+
context.fillRect(0, 0, size, size);
|
|
19701
|
+
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);
|
|
19702
|
+
lowerGradient.addColorStop(0, `${palette.secondary}1d`);
|
|
19703
|
+
lowerGradient.addColorStop(1, `${palette.secondary}00`);
|
|
19704
|
+
context.fillStyle = lowerGradient;
|
|
19705
|
+
context.fillRect(0, 0, size, size);
|
|
19706
|
+
}
|
|
19707
|
+
/**
|
|
19708
|
+
* Draws the soft ground shadow below the octopus.
|
|
19709
|
+
*
|
|
19710
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
19711
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
19712
|
+
*
|
|
19713
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19714
|
+
*/
|
|
19715
|
+
function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
|
|
19716
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.04;
|
|
19717
|
+
const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
|
|
19718
|
+
const rx = size * (0.18 + interaction.intensity * 0.02);
|
|
19719
|
+
const ry = size * 0.06;
|
|
19720
|
+
context.save();
|
|
19721
|
+
context.translate(cx, cy);
|
|
19722
|
+
context.scale(1, ry / rx);
|
|
19723
|
+
const blurRadius = rx * 1.4;
|
|
19724
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
19725
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
19726
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
19727
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
19728
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
19729
|
+
context.fillStyle = shadowGradient;
|
|
19730
|
+
context.beginPath();
|
|
19731
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
19732
|
+
context.fill();
|
|
19733
|
+
context.restore();
|
|
19734
|
+
}
|
|
19735
|
+
/**
|
|
19736
|
+
* Resolves visible projected patches for one rotated ellipsoid mesh.
|
|
19737
|
+
*
|
|
19738
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19739
|
+
*/
|
|
19740
|
+
function resolveVisibleEllipsoidPatches(options) {
|
|
19741
|
+
const { center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, verticalColorBias, outlineColor, } = options;
|
|
19742
|
+
const latitudePatchCount = 10;
|
|
19743
|
+
const longitudePatchCount = 18;
|
|
19744
|
+
const surfacePatches = [];
|
|
19745
|
+
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
19746
|
+
const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
|
|
19747
|
+
const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
|
|
19748
|
+
const verticalProgress = (latitudeIndex + 0.5) / latitudePatchCount;
|
|
19749
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
19750
|
+
const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
|
|
19751
|
+
const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
|
|
19752
|
+
const localCorners = [
|
|
19753
|
+
sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, startLongitude),
|
|
19754
|
+
sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, endLongitude),
|
|
19755
|
+
sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, endLongitude),
|
|
19756
|
+
sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, startLongitude),
|
|
19757
|
+
];
|
|
19758
|
+
const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
|
|
19759
|
+
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
19760
|
+
if (surfaceNormal.z <= 0.01) {
|
|
19761
|
+
continue;
|
|
19762
|
+
}
|
|
19763
|
+
const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
|
|
19764
|
+
surfacePatches.push({
|
|
19765
|
+
corners: projectedCorners,
|
|
19766
|
+
averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
|
|
19767
|
+
transformedCorners.length,
|
|
19768
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
|
|
19769
|
+
fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
|
|
19770
|
+
outlineColor,
|
|
19771
|
+
});
|
|
19772
|
+
}
|
|
19773
|
+
}
|
|
19774
|
+
return surfacePatches;
|
|
19775
|
+
}
|
|
19776
|
+
/**
|
|
19777
|
+
* Samples one point on an ellipsoid aligned to the local axes.
|
|
19778
|
+
*
|
|
19779
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19780
|
+
*/
|
|
19781
|
+
function sampleEllipsoidPoint(radiusX, radiusY, radiusZ, latitude, longitude) {
|
|
19782
|
+
const cosineLatitude = Math.cos(latitude);
|
|
19783
|
+
return {
|
|
19784
|
+
x: Math.sin(longitude) * cosineLatitude * radiusX,
|
|
19785
|
+
y: Math.sin(latitude) * radiusY,
|
|
19786
|
+
z: Math.cos(longitude) * cosineLatitude * radiusZ,
|
|
19787
|
+
};
|
|
19788
|
+
}
|
|
19789
|
+
/**
|
|
19790
|
+
* Resolves one base fill tone for a surface patch across the octopus body.
|
|
19791
|
+
*
|
|
19792
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19793
|
+
*/
|
|
19794
|
+
function resolveSurfacePatchFillStyle(palette, verticalProgress) {
|
|
19795
|
+
const clampedVerticalProgress = clampNumber$1(verticalProgress, 0, 1);
|
|
19796
|
+
if (clampedVerticalProgress < 0.2) {
|
|
19797
|
+
return palette.highlight;
|
|
19798
|
+
}
|
|
19799
|
+
if (clampedVerticalProgress < 0.45) {
|
|
19800
|
+
return palette.secondary;
|
|
19801
|
+
}
|
|
19802
|
+
if (clampedVerticalProgress < 0.8) {
|
|
19803
|
+
return palette.primary;
|
|
19804
|
+
}
|
|
19805
|
+
return `${palette.shadow}f2`;
|
|
19806
|
+
}
|
|
19807
|
+
/**
|
|
19808
|
+
* Draws one projected mesh patch with organic shading.
|
|
19809
|
+
*
|
|
19810
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19811
|
+
*/
|
|
19812
|
+
function drawSurfacePatch(context, surfacePatch) {
|
|
19813
|
+
drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
|
|
19814
|
+
if (surfacePatch.lightIntensity > 0) {
|
|
19815
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.15 * surfacePatch.lightIntensity})`);
|
|
19816
|
+
}
|
|
19817
|
+
else if (surfacePatch.lightIntensity < 0) {
|
|
19818
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
|
|
19819
|
+
}
|
|
19820
|
+
context.save();
|
|
19821
|
+
context.beginPath();
|
|
19822
|
+
context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
|
|
19823
|
+
for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
|
|
19824
|
+
context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
|
|
19825
|
+
}
|
|
19826
|
+
context.closePath();
|
|
19827
|
+
context.strokeStyle = surfacePatch.outlineColor;
|
|
19828
|
+
context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0044);
|
|
19829
|
+
context.lineJoin = 'round';
|
|
19830
|
+
context.stroke();
|
|
19831
|
+
context.restore();
|
|
19832
|
+
}
|
|
19833
|
+
/**
|
|
19834
|
+
* Creates the projected 3D tentacle strokes orbiting around the lower octopus body.
|
|
19835
|
+
*
|
|
19836
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19837
|
+
*/
|
|
19838
|
+
function createOctopusTentacleStrokes(options) {
|
|
19839
|
+
const { createRandom, morphologyProfile, timeMs, size, center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, animationPhase, } = options;
|
|
19840
|
+
return Array.from({ length: morphologyProfile.tentacles.count }, (_, tentacleIndex) => {
|
|
19841
|
+
const tentacleRandom = createRandom(`octopus3d-tentacle-${tentacleIndex}`);
|
|
19842
|
+
const spreadProgress = morphologyProfile.tentacles.count === 1 ? 0.5 : tentacleIndex / (morphologyProfile.tentacles.count - 1);
|
|
19843
|
+
const orbitAngle = -Math.PI * 0.92 + spreadProgress * Math.PI * 1.84 + (tentacleRandom() - 0.5) * 0.16;
|
|
19844
|
+
const flowLength = size * (0.19 + morphologyProfile.tentacles.flowLengthScale * 0.075 + tentacleRandom() * 0.018);
|
|
19845
|
+
const lateralReach = size *
|
|
19846
|
+
(0.08 + morphologyProfile.tentacles.lateralReachScale * 0.05 + Math.abs(Math.sin(orbitAngle)) * 0.018);
|
|
19847
|
+
const depthReach = size * (0.028 + morphologyProfile.tentacles.tipReachScale * 0.032);
|
|
19848
|
+
const sway = Math.sin(timeMs / (760 + tentacleIndex * 36) + animationPhase + tentacleRandom() * Math.PI * 2);
|
|
19849
|
+
const anchorPoint = {
|
|
19850
|
+
x: Math.sin(orbitAngle) * radiusX * (0.84 + tentacleRandom() * 0.08),
|
|
19851
|
+
y: radiusY * (0.22 + tentacleRandom() * 0.18),
|
|
19852
|
+
z: Math.cos(orbitAngle) * radiusZ * (0.72 + tentacleRandom() * 0.12),
|
|
19853
|
+
};
|
|
19854
|
+
const controlPointOne = {
|
|
19855
|
+
x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * 0.44,
|
|
19856
|
+
y: anchorPoint.y + flowLength * 0.26,
|
|
19857
|
+
z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.3 + sway * size * 0.012,
|
|
19858
|
+
};
|
|
19859
|
+
const controlPointTwo = {
|
|
19860
|
+
x: anchorPoint.x +
|
|
19861
|
+
Math.sin(orbitAngle) * lateralReach * (0.82 + morphologyProfile.tentacles.swayScale * 0.12),
|
|
19862
|
+
y: anchorPoint.y + flowLength * 0.66,
|
|
19863
|
+
z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.72 + sway * size * 0.02,
|
|
19864
|
+
};
|
|
19865
|
+
const endPoint = {
|
|
19866
|
+
x: anchorPoint.x +
|
|
19867
|
+
Math.sin(orbitAngle) * lateralReach * (1.02 + morphologyProfile.tentacles.tipWidthScale * 0.12) +
|
|
19868
|
+
sway * size * 0.028,
|
|
19869
|
+
y: anchorPoint.y + flowLength,
|
|
19870
|
+
z: anchorPoint.z + Math.cos(orbitAngle) * depthReach + sway * size * 0.016,
|
|
19871
|
+
};
|
|
19872
|
+
const scenePoints = Array.from({ length: 12 }, (_, sampleIndex) => transformScenePoint(sampleCubicBezierPoint3D(anchorPoint, controlPointOne, controlPointTwo, endPoint, sampleIndex / 11), center, rotationX, rotationY));
|
|
19873
|
+
const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
19874
|
+
const averageDepth = scenePoints.reduce((depthSum, scenePoint) => depthSum + scenePoint.z, 0) / scenePoints.length;
|
|
19875
|
+
return {
|
|
19876
|
+
projectedPoints,
|
|
19877
|
+
averageDepth,
|
|
19878
|
+
isFrontFacing: averageDepth >= center.z - size * 0.006,
|
|
19879
|
+
baseWidth: size *
|
|
19880
|
+
(0.019 +
|
|
19881
|
+
morphologyProfile.tentacles.baseWidthScale * 0.007 +
|
|
19882
|
+
tentacleRandom() * 0.003 +
|
|
19883
|
+
Math.abs(Math.sin(orbitAngle)) * 0.002),
|
|
19884
|
+
tipWidth: size * (0.0046 + morphologyProfile.tentacles.tipWidthScale * 0.0018),
|
|
19885
|
+
colorBias: tentacleRandom(),
|
|
19886
|
+
};
|
|
19887
|
+
});
|
|
19888
|
+
}
|
|
19889
|
+
/**
|
|
19890
|
+
* Samples one point on a cubic Bezier curve in 3D.
|
|
19891
|
+
*
|
|
19892
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19893
|
+
*/
|
|
19894
|
+
function sampleCubicBezierPoint3D(startPoint, controlPointOne, controlPointTwo, endPoint, progress) {
|
|
19895
|
+
const inverseProgress = 1 - progress;
|
|
19896
|
+
return {
|
|
19897
|
+
x: inverseProgress * inverseProgress * inverseProgress * startPoint.x +
|
|
19898
|
+
3 * inverseProgress * inverseProgress * progress * controlPointOne.x +
|
|
19899
|
+
3 * inverseProgress * progress * progress * controlPointTwo.x +
|
|
19900
|
+
progress * progress * progress * endPoint.x,
|
|
19901
|
+
y: inverseProgress * inverseProgress * inverseProgress * startPoint.y +
|
|
19902
|
+
3 * inverseProgress * inverseProgress * progress * controlPointOne.y +
|
|
19903
|
+
3 * inverseProgress * progress * progress * controlPointTwo.y +
|
|
19904
|
+
progress * progress * progress * endPoint.y,
|
|
19905
|
+
z: inverseProgress * inverseProgress * inverseProgress * startPoint.z +
|
|
19906
|
+
3 * inverseProgress * inverseProgress * progress * controlPointOne.z +
|
|
19907
|
+
3 * inverseProgress * progress * progress * controlPointTwo.z +
|
|
19908
|
+
progress * progress * progress * endPoint.z,
|
|
19909
|
+
};
|
|
19910
|
+
}
|
|
19911
|
+
/**
|
|
19912
|
+
* Draws one projected tentacle stroke with a slim highlight ridge.
|
|
19913
|
+
*
|
|
19914
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19915
|
+
*/
|
|
19916
|
+
function drawTentacleStroke(context, tentacleStroke, palette) {
|
|
19917
|
+
const projectedSegments = tentacleStroke.projectedPoints.length - 1;
|
|
19918
|
+
for (let segmentIndex = 0; segmentIndex < projectedSegments; segmentIndex++) {
|
|
19919
|
+
const startPoint = tentacleStroke.projectedPoints[segmentIndex];
|
|
19920
|
+
const endPoint = tentacleStroke.projectedPoints[segmentIndex + 1];
|
|
19921
|
+
const progress = segmentIndex / projectedSegments;
|
|
19922
|
+
const width = tentacleStroke.baseWidth + (tentacleStroke.tipWidth - tentacleStroke.baseWidth) * progress;
|
|
19923
|
+
context.beginPath();
|
|
19924
|
+
context.moveTo(startPoint.x, startPoint.y);
|
|
19925
|
+
context.lineTo(endPoint.x, endPoint.y);
|
|
19926
|
+
context.strokeStyle = tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
|
|
19927
|
+
context.lineWidth = width;
|
|
19928
|
+
context.lineCap = 'round';
|
|
19929
|
+
context.stroke();
|
|
19930
|
+
context.beginPath();
|
|
19931
|
+
context.moveTo(startPoint.x, startPoint.y);
|
|
19932
|
+
context.lineTo(endPoint.x, endPoint.y);
|
|
19933
|
+
context.strokeStyle = tentacleStroke.isFrontFacing ? `${palette.highlight}80` : `${palette.highlight}40`;
|
|
19934
|
+
context.lineWidth = Math.max(1, width * 0.34);
|
|
19935
|
+
context.lineCap = 'round';
|
|
19936
|
+
context.stroke();
|
|
19937
|
+
}
|
|
19938
|
+
}
|
|
19939
|
+
/**
|
|
19940
|
+
* Resolves the front surface depth on an ellipsoid for one local face point.
|
|
19941
|
+
*
|
|
19942
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
19943
|
+
*/
|
|
19944
|
+
function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
|
|
19945
|
+
const normalizedX = x / radiusX;
|
|
19946
|
+
const normalizedY = y / radiusY;
|
|
19947
|
+
const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
|
|
19948
|
+
return Math.sqrt(remainingDepthRatio) * radiusZ;
|
|
19949
|
+
}
|
|
19950
|
+
|
|
19567
19951
|
/* eslint-disable no-magic-numbers */
|
|
19568
19952
|
/**
|
|
19569
19953
|
* Octopus avatar visual.
|
|
@@ -20384,14 +20768,14 @@ function resolveAvatarVisualId(value) {
|
|
|
20384
20768
|
/**
|
|
20385
20769
|
* META AVATAR commitment definition
|
|
20386
20770
|
*
|
|
20387
|
-
* The `META AVATAR`
|
|
20388
|
-
* the agent does not provide an explicit `META IMAGE`.
|
|
20771
|
+
* The `META AVATAR` and `META VISUAL` commitments set the built-in default avatar visual
|
|
20772
|
+
* used when the agent does not provide an explicit `META IMAGE`.
|
|
20389
20773
|
*
|
|
20390
20774
|
* @private [🪔] Maybe export the commitments through some package
|
|
20391
20775
|
*/
|
|
20392
20776
|
class MetaAvatarCommitmentDefinition extends BaseCommitmentDefinition {
|
|
20393
|
-
constructor() {
|
|
20394
|
-
super(
|
|
20777
|
+
constructor(type = 'META AVATAR') {
|
|
20778
|
+
super(type);
|
|
20395
20779
|
}
|
|
20396
20780
|
/**
|
|
20397
20781
|
* Short one-line description of META AVATAR.
|
|
@@ -20411,14 +20795,15 @@ class MetaAvatarCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
20411
20795
|
get documentation() {
|
|
20412
20796
|
const supportedVisuals = AVATAR_VISUALS.map((avatarVisual) => `\`${avatarVisual.id}\``).join(', ');
|
|
20413
20797
|
return spaceTrim$1(`
|
|
20414
|
-
#
|
|
20798
|
+
# ${this.type}
|
|
20415
20799
|
|
|
20416
20800
|
Sets the built-in avatar visual used for the agent when no explicit \`META IMAGE\` is provided.
|
|
20801
|
+
\`META AVATAR\` and \`META VISUAL\` are equivalent.
|
|
20417
20802
|
|
|
20418
20803
|
## Key aspects
|
|
20419
20804
|
|
|
20420
20805
|
- Does not modify the agent's behavior or responses.
|
|
20421
|
-
- Only one \`META AVATAR\` should be used per agent.
|
|
20806
|
+
- Only one \`META AVATAR\` or \`META VISUAL\` should be used per agent.
|
|
20422
20807
|
- If multiple are specified, the last one takes precedence.
|
|
20423
20808
|
- Values are matched case-insensitively and spaces, hyphens, and underscores are normalized.
|
|
20424
20809
|
- Supported visuals are derived from the shared avatar registry: ${supportedVisuals}.
|
|
@@ -20433,15 +20818,15 @@ class MetaAvatarCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
20433
20818
|
\`\`\`
|
|
20434
20819
|
|
|
20435
20820
|
\`\`\`book
|
|
20436
|
-
|
|
20821
|
+
Minecraft Assistant
|
|
20437
20822
|
|
|
20438
|
-
META
|
|
20823
|
+
META VISUAL Minecraft2
|
|
20439
20824
|
GOAL Answer in a calm and focused way.
|
|
20440
20825
|
\`\`\`
|
|
20441
20826
|
`);
|
|
20442
20827
|
}
|
|
20443
20828
|
applyToAgentModelRequirements(requirements, content) {
|
|
20444
|
-
// META AVATAR
|
|
20829
|
+
// META AVATAR and META VISUAL don't modify the system message or model requirements.
|
|
20445
20830
|
// It's handled separately in the parsing logic for profile avatar resolution.
|
|
20446
20831
|
return requirements;
|
|
20447
20832
|
}
|
|
@@ -22420,10 +22805,10 @@ const teamToolTitles = {};
|
|
|
22420
22805
|
*
|
|
22421
22806
|
* @private
|
|
22422
22807
|
*/
|
|
22423
|
-
const
|
|
22424
|
-
|
|
22425
|
-
|
|
22426
|
-
|
|
22808
|
+
const TEAM_SYSTEM_MESSAGE_GUIDANCE = spaceTrim$1(`
|
|
22809
|
+
- If a teammate is relevant to the request, consult that teammate using the matching tool.
|
|
22810
|
+
- Do not ask the user for information that a listed teammate can provide directly.
|
|
22811
|
+
`);
|
|
22427
22812
|
/**
|
|
22428
22813
|
* Constant for remote agents by Url.
|
|
22429
22814
|
*/
|
|
@@ -22605,7 +22990,7 @@ function buildTeamSystemMessageBody(teamEntries) {
|
|
|
22605
22990
|
`);
|
|
22606
22991
|
});
|
|
22607
22992
|
return spaceTrim$1((block) => `
|
|
22608
|
-
${block(
|
|
22993
|
+
${block(TEAM_SYSTEM_MESSAGE_GUIDANCE)}
|
|
22609
22994
|
|
|
22610
22995
|
${block(teammateSections.join('\n\n'))}
|
|
22611
22996
|
`);
|
|
@@ -29629,6 +30014,7 @@ const COMMITMENT_REGISTRY = [
|
|
|
29629
30014
|
new ActionCommitmentDefinition('ACTIONS'),
|
|
29630
30015
|
new ComponentCommitmentDefinition(),
|
|
29631
30016
|
new MetaAvatarCommitmentDefinition(),
|
|
30017
|
+
new MetaAvatarCommitmentDefinition('META VISUAL'),
|
|
29632
30018
|
new MetaImageCommitmentDefinition(),
|
|
29633
30019
|
new MetaColorCommitmentDefinition(),
|
|
29634
30020
|
new MetaFontCommitmentDefinition(),
|
|
@@ -30980,6 +31366,7 @@ function hasHttpProtocol(value) {
|
|
|
30980
31366
|
*/
|
|
30981
31367
|
const META_COMMITMENT_APPLIERS = {
|
|
30982
31368
|
'META AVATAR': applyMetaAvatarContent,
|
|
31369
|
+
'META VISUAL': applyMetaAvatarContent,
|
|
30983
31370
|
'META LINK': applyMetaLinkContent,
|
|
30984
31371
|
'META DOMAIN': applyMetaDomainContent,
|
|
30985
31372
|
'META IMAGE': applyMetaImageContent,
|
|
@@ -31028,7 +31415,7 @@ function applyGenericMetaCommitment(state, content) {
|
|
|
31028
31415
|
state.meta[metaType] = metaValue;
|
|
31029
31416
|
}
|
|
31030
31417
|
/**
|
|
31031
|
-
* Applies META AVATAR content into the canonical `meta.avatar` field.
|
|
31418
|
+
* Applies META AVATAR / META VISUAL content into the canonical `meta.avatar` field.
|
|
31032
31419
|
*/
|
|
31033
31420
|
function applyMetaAvatarContent(state, content) {
|
|
31034
31421
|
const avatarVisualId = resolveAvatarVisualId(content);
|
|
@@ -32092,6 +32479,78 @@ function normalizeCliAgentFileSegment(value) {
|
|
|
32092
32479
|
.replace(/^[._-]+|[._-]+$/gu, '');
|
|
32093
32480
|
}
|
|
32094
32481
|
|
|
32482
|
+
/**
|
|
32483
|
+
* Helper of usage compute
|
|
32484
|
+
*
|
|
32485
|
+
* @param content the content of prompt or response
|
|
32486
|
+
* @returns part of UsageCounts
|
|
32487
|
+
*
|
|
32488
|
+
* @private internal utility of LlmExecutionTools
|
|
32489
|
+
*/
|
|
32490
|
+
function computeUsageCounts(content) {
|
|
32491
|
+
return {
|
|
32492
|
+
charactersCount: { value: countCharacters(content) },
|
|
32493
|
+
wordsCount: { value: countWords(content) },
|
|
32494
|
+
sentencesCount: { value: countSentences(content) },
|
|
32495
|
+
linesCount: { value: countLines(content) },
|
|
32496
|
+
paragraphsCount: { value: countParagraphs(content) },
|
|
32497
|
+
pagesCount: { value: countPages(content) },
|
|
32498
|
+
};
|
|
32499
|
+
}
|
|
32500
|
+
|
|
32501
|
+
/**
|
|
32502
|
+
* Computes the usage of the OpenAI API based on the response from OpenAI
|
|
32503
|
+
*
|
|
32504
|
+
* @param promptContent The content of the prompt
|
|
32505
|
+
* @param resultContent The content of the result (for embedding prompts or failed prompts pass empty string)
|
|
32506
|
+
* @param rawResponse The raw response from OpenAI API
|
|
32507
|
+
* @param duration The duration of the execution
|
|
32508
|
+
* @throws {PipelineExecutionError} If the usage is not defined in the response from OpenAI
|
|
32509
|
+
*
|
|
32510
|
+
* @private internal utility of `OpenAiExecutionTools`
|
|
32511
|
+
*/
|
|
32512
|
+
function computeOpenAiUsage(promptContent, // <- Note: Intentionally using [] to access type properties to bring jsdoc from Prompt/PromptResult to consumer
|
|
32513
|
+
resultContent, rawResponse, duration = ZERO_VALUE) {
|
|
32514
|
+
var _a, _b;
|
|
32515
|
+
if (rawResponse.usage === undefined) {
|
|
32516
|
+
throw new PipelineExecutionError('The usage is not defined in the response from OpenAI');
|
|
32517
|
+
}
|
|
32518
|
+
if (((_a = rawResponse.usage) === null || _a === void 0 ? void 0 : _a.prompt_tokens) === undefined) {
|
|
32519
|
+
throw new PipelineExecutionError('In OpenAI response `usage.prompt_tokens` not defined');
|
|
32520
|
+
}
|
|
32521
|
+
const inputTokens = rawResponse.usage.prompt_tokens;
|
|
32522
|
+
const outputTokens = ((_b = rawResponse.usage) === null || _b === void 0 ? void 0 : _b.completion_tokens) || 0;
|
|
32523
|
+
let isUncertain = false;
|
|
32524
|
+
let modelInfo = OPENAI_MODELS.find((model) => model.modelName === rawResponse.model);
|
|
32525
|
+
if (modelInfo === undefined) {
|
|
32526
|
+
// Note: Model is not in the list of known models, fallback to the family of the models and mark price as uncertain
|
|
32527
|
+
modelInfo = OPENAI_MODELS.find((model) => (rawResponse.model || SALT_NONCE).startsWith(model.modelName));
|
|
32528
|
+
if (modelInfo !== undefined) {
|
|
32529
|
+
isUncertain = true;
|
|
32530
|
+
}
|
|
32531
|
+
}
|
|
32532
|
+
let price;
|
|
32533
|
+
if (modelInfo === undefined || modelInfo.pricing === undefined) {
|
|
32534
|
+
price = uncertainNumber();
|
|
32535
|
+
}
|
|
32536
|
+
else {
|
|
32537
|
+
price = uncertainNumber(inputTokens * modelInfo.pricing.prompt + outputTokens * modelInfo.pricing.output, isUncertain);
|
|
32538
|
+
}
|
|
32539
|
+
return {
|
|
32540
|
+
price,
|
|
32541
|
+
duration,
|
|
32542
|
+
input: {
|
|
32543
|
+
tokensCount: uncertainNumber(rawResponse.usage.prompt_tokens),
|
|
32544
|
+
...computeUsageCounts(promptContent),
|
|
32545
|
+
},
|
|
32546
|
+
output: {
|
|
32547
|
+
tokensCount: uncertainNumber(outputTokens),
|
|
32548
|
+
...computeUsageCounts(resultContent),
|
|
32549
|
+
},
|
|
32550
|
+
};
|
|
32551
|
+
}
|
|
32552
|
+
// TODO: [🤝] DRY Maybe some common abstraction between `computeOpenAiUsage` and `computeAnthropicClaudeUsage`
|
|
32553
|
+
|
|
32095
32554
|
/**
|
|
32096
32555
|
* Simple wrapper `new Date().toISOString()`
|
|
32097
32556
|
*
|
|
@@ -32500,78 +32959,6 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
|
|
|
32500
32959
|
}
|
|
32501
32960
|
}
|
|
32502
32961
|
|
|
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
32962
|
/**
|
|
32576
32963
|
* Finds the best hardcoded-model match for one API-listed model identifier.
|
|
32577
32964
|
*/
|
|
@@ -43029,18 +43416,6 @@ async function $provideExecutablesForNode(options) {
|
|
|
43029
43416
|
const $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
|
|
43030
43417
|
// TODO: [®] DRY Register logic
|
|
43031
43418
|
|
|
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
43419
|
/**
|
|
43045
43420
|
* Path to the `.env` file which was used to configure LLM tools
|
|
43046
43421
|
*
|
|
@@ -43048,61 +43423,53 @@ const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
|
|
|
43048
43423
|
*/
|
|
43049
43424
|
let $usedEnvFilename = null;
|
|
43050
43425
|
/**
|
|
43051
|
-
*
|
|
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.
|
|
43426
|
+
* Shared state for the `.env` file used to configure LLM tools.
|
|
43076
43427
|
*
|
|
43077
|
-
* @private internal
|
|
43428
|
+
* @private internal state of `$registeredLlmToolsMessage`
|
|
43078
43429
|
*/
|
|
43079
|
-
|
|
43080
|
-
|
|
43081
|
-
|
|
43082
|
-
|
|
43083
|
-
|
|
43084
|
-
|
|
43085
|
-
|
|
43086
|
-
|
|
43087
|
-
|
|
43088
|
-
|
|
43089
|
-
|
|
43430
|
+
const USED_ENV_FILENAME = {
|
|
43431
|
+
set(filepath) {
|
|
43432
|
+
$usedEnvFilename = filepath;
|
|
43433
|
+
},
|
|
43434
|
+
createMessage() {
|
|
43435
|
+
return $usedEnvFilename === null
|
|
43436
|
+
? `Unknown \`.env\` file`
|
|
43437
|
+
: spaceTrim$1(`
|
|
43438
|
+
Used \`.env\` file:
|
|
43439
|
+
${$usedEnvFilename}
|
|
43440
|
+
`);
|
|
43441
|
+
},
|
|
43442
|
+
};
|
|
43443
|
+
|
|
43090
43444
|
/**
|
|
43091
43445
|
* Reads environment variables relevant for provider configuration.
|
|
43092
43446
|
*
|
|
43093
|
-
* @private
|
|
43447
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43094
43448
|
*/
|
|
43095
|
-
function
|
|
43449
|
+
function getAvailableRegisteredLlmToolsEnvironmentVariables(isRunningInNode) {
|
|
43096
43450
|
if (isRunningInNode) {
|
|
43097
43451
|
return process.env;
|
|
43098
43452
|
// <- TODO: [⚛] Some DRY way how to get to `process.env` and pass it into functions - ACRY search for `env`
|
|
43099
43453
|
}
|
|
43100
43454
|
return {};
|
|
43101
43455
|
}
|
|
43456
|
+
|
|
43457
|
+
/**
|
|
43458
|
+
* Register for LLM tools.
|
|
43459
|
+
*
|
|
43460
|
+
* Note: `$` is used to indicate that this interacts with the global scope
|
|
43461
|
+
*
|
|
43462
|
+
* @singleton Only one instance of each register is created per build, but there can be more instances across different builds or environments.
|
|
43463
|
+
*
|
|
43464
|
+
* @public exported from `@promptbook/core`
|
|
43465
|
+
*/
|
|
43466
|
+
const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
|
|
43467
|
+
// TODO: [®] DRY Register logic
|
|
43468
|
+
|
|
43102
43469
|
/**
|
|
43103
43470
|
* Takes stable snapshots of both LLM provider registers.
|
|
43104
43471
|
*
|
|
43105
|
-
* @private
|
|
43472
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43106
43473
|
*/
|
|
43107
43474
|
function getRegisteredLlmToolRegisters() {
|
|
43108
43475
|
return {
|
|
@@ -43110,62 +43477,30 @@ function getRegisteredLlmToolRegisters() {
|
|
|
43110
43477
|
registeredTools: $llmToolsRegister.list(),
|
|
43111
43478
|
};
|
|
43112
43479
|
}
|
|
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
|
-
}
|
|
43480
|
+
|
|
43148
43481
|
/**
|
|
43149
43482
|
* Creates a deduplication key for a provider entry.
|
|
43150
43483
|
*
|
|
43151
|
-
* @private
|
|
43484
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43152
43485
|
*/
|
|
43153
43486
|
function createRegisteredLlmToolEntryKey({ packageName, className }) {
|
|
43154
43487
|
return `${packageName}::${className}`;
|
|
43155
43488
|
}
|
|
43489
|
+
|
|
43156
43490
|
/**
|
|
43157
43491
|
* Checks whether the given provider entry already exists in the target register list.
|
|
43158
43492
|
*
|
|
43159
|
-
* @private
|
|
43493
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43160
43494
|
*/
|
|
43161
43495
|
function hasRegisteredLlmToolEntry(registeredLlmToolEntries, registeredLlmToolEntry) {
|
|
43162
43496
|
const registeredLlmToolEntryKey = createRegisteredLlmToolEntryKey(registeredLlmToolEntry);
|
|
43163
43497
|
return registeredLlmToolEntries.some((listedRegisteredLlmToolEntry) => createRegisteredLlmToolEntryKey(listedRegisteredLlmToolEntry) === registeredLlmToolEntryKey);
|
|
43164
43498
|
}
|
|
43499
|
+
|
|
43165
43500
|
/**
|
|
43166
43501
|
* Builds the runtime status for one provider entry.
|
|
43167
43502
|
*
|
|
43168
|
-
* @private
|
|
43503
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43169
43504
|
*/
|
|
43170
43505
|
function createRegisteredLlmToolStatus({ registeredLlmToolEntry, registeredMetadata, registeredTools, env, }) {
|
|
43171
43506
|
const availabilityStatus = createRegisteredLlmToolAvailabilityStatus(registeredLlmToolEntry, registeredMetadata, registeredTools);
|
|
@@ -43179,7 +43514,7 @@ function createRegisteredLlmToolStatus({ registeredLlmToolEntry, registeredMetad
|
|
|
43179
43514
|
/**
|
|
43180
43515
|
* Resolves whether the provider has metadata and a registered constructor.
|
|
43181
43516
|
*
|
|
43182
|
-
* @private
|
|
43517
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43183
43518
|
*/
|
|
43184
43519
|
function createRegisteredLlmToolAvailabilityStatus(registeredLlmToolEntry, registeredMetadata, registeredTools) {
|
|
43185
43520
|
return {
|
|
@@ -43190,7 +43525,7 @@ function createRegisteredLlmToolAvailabilityStatus(registeredLlmToolEntry, regis
|
|
|
43190
43525
|
/**
|
|
43191
43526
|
* Resolves whether the provider has enough environment variables to be configured.
|
|
43192
43527
|
*
|
|
43193
|
-
* @private
|
|
43528
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43194
43529
|
*/
|
|
43195
43530
|
function createRegisteredLlmToolConfigurationStatus({ envVariables }, env) {
|
|
43196
43531
|
return {
|
|
@@ -43201,7 +43536,7 @@ function createRegisteredLlmToolConfigurationStatus({ envVariables }, env) {
|
|
|
43201
43536
|
/**
|
|
43202
43537
|
* Checks whether all required environment variables are present for a provider.
|
|
43203
43538
|
*
|
|
43204
|
-
* @private
|
|
43539
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43205
43540
|
*/
|
|
43206
43541
|
function isRegisteredLlmToolFullyConfigured(envVariables, env) {
|
|
43207
43542
|
if (envVariables === undefined || envVariables === null) {
|
|
@@ -43212,7 +43547,7 @@ function isRegisteredLlmToolFullyConfigured(envVariables, env) {
|
|
|
43212
43547
|
/**
|
|
43213
43548
|
* Checks whether at least one required environment variable is present for a provider.
|
|
43214
43549
|
*
|
|
43215
|
-
* @private
|
|
43550
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43216
43551
|
*/
|
|
43217
43552
|
function isRegisteredLlmToolPartiallyConfigured(envVariables, env) {
|
|
43218
43553
|
if (envVariables === undefined || envVariables === null) {
|
|
@@ -43220,109 +43555,128 @@ function isRegisteredLlmToolPartiallyConfigured(envVariables, env) {
|
|
|
43220
43555
|
}
|
|
43221
43556
|
return envVariables.some((envVariableName) => env[envVariableName] !== undefined);
|
|
43222
43557
|
}
|
|
43558
|
+
|
|
43223
43559
|
/**
|
|
43224
|
-
*
|
|
43560
|
+
* Merges provider entries from the metadata and constructor registers.
|
|
43225
43561
|
*
|
|
43226
|
-
* @private
|
|
43562
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43227
43563
|
*/
|
|
43228
|
-
function
|
|
43229
|
-
|
|
43230
|
-
|
|
43231
|
-
|
|
43232
|
-
|
|
43233
|
-
|
|
43564
|
+
function listRegisteredLlmToolEntries(registeredMetadata, registeredTools) {
|
|
43565
|
+
const registeredLlmToolEntries = new Map();
|
|
43566
|
+
for (const { title, packageName, className, envVariables } of registeredMetadata) {
|
|
43567
|
+
addRegisteredLlmToolEntry(registeredLlmToolEntries, { title, packageName, className, envVariables });
|
|
43568
|
+
}
|
|
43569
|
+
for (const { packageName, className } of registeredTools) {
|
|
43570
|
+
addRegisteredLlmToolEntry(registeredLlmToolEntries, { packageName, className });
|
|
43571
|
+
}
|
|
43572
|
+
return [...registeredLlmToolEntries.values()];
|
|
43234
43573
|
}
|
|
43235
43574
|
/**
|
|
43236
|
-
*
|
|
43575
|
+
* Adds a provider entry only when the provider is not already present.
|
|
43237
43576
|
*
|
|
43238
|
-
* @private
|
|
43577
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43239
43578
|
*/
|
|
43240
|
-
function
|
|
43241
|
-
|
|
43242
|
-
|
|
43243
|
-
|
|
43244
|
-
|
|
43245
|
-
|
|
43246
|
-
${block(renderRelevantEnvironmentVariables(env, llmToolStatuses))}
|
|
43247
|
-
|
|
43248
|
-
Available LLM providers are:
|
|
43249
|
-
${block(renderAvailableProviders(llmToolStatuses, env, isRunningInNode))}
|
|
43250
|
-
`);
|
|
43579
|
+
function addRegisteredLlmToolEntry(registeredLlmToolEntries, registeredLlmToolEntry) {
|
|
43580
|
+
const registeredLlmToolEntryKey = createRegisteredLlmToolEntryKey(registeredLlmToolEntry);
|
|
43581
|
+
if (registeredLlmToolEntries.has(registeredLlmToolEntryKey)) {
|
|
43582
|
+
return;
|
|
43583
|
+
}
|
|
43584
|
+
registeredLlmToolEntries.set(registeredLlmToolEntryKey, registeredLlmToolEntry);
|
|
43251
43585
|
}
|
|
43586
|
+
|
|
43252
43587
|
/**
|
|
43253
|
-
*
|
|
43588
|
+
* Lists provider entries enriched with installation and configuration state.
|
|
43254
43589
|
*
|
|
43255
|
-
* @private
|
|
43590
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43256
43591
|
*/
|
|
43257
|
-
function
|
|
43258
|
-
return
|
|
43259
|
-
.map((envVariableName) => `- \`${envVariableName}\``)
|
|
43260
|
-
.join('\n');
|
|
43592
|
+
function listRegisteredLlmToolStatuses({ registeredMetadata, registeredTools }, env) {
|
|
43593
|
+
return listRegisteredLlmToolEntries(registeredMetadata, registeredTools).map((registeredLlmToolEntry) => createRegisteredLlmToolStatus({ registeredLlmToolEntry, registeredMetadata, registeredTools, env }));
|
|
43261
43594
|
}
|
|
43595
|
+
|
|
43262
43596
|
/**
|
|
43263
|
-
*
|
|
43597
|
+
* Collects all state needed to render the provider summary.
|
|
43264
43598
|
*
|
|
43265
|
-
* @private
|
|
43599
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43266
43600
|
*/
|
|
43267
|
-
function
|
|
43268
|
-
|
|
43601
|
+
function createRegisteredLlmToolsMessageContext() {
|
|
43602
|
+
const isRunningInNode = $isRunningInNode();
|
|
43603
|
+
const env = getAvailableRegisteredLlmToolsEnvironmentVariables(isRunningInNode);
|
|
43604
|
+
const registeredLlmToolRegisters = getRegisteredLlmToolRegisters();
|
|
43605
|
+
return {
|
|
43606
|
+
env,
|
|
43607
|
+
llmToolStatuses: listRegisteredLlmToolStatuses(registeredLlmToolRegisters, env),
|
|
43608
|
+
usedEnvMessage: USED_ENV_FILENAME.createMessage(),
|
|
43609
|
+
isRunningInNode,
|
|
43610
|
+
};
|
|
43269
43611
|
}
|
|
43612
|
+
|
|
43270
43613
|
/**
|
|
43271
|
-
* Renders
|
|
43614
|
+
* Renders the fallback message for environments with no registered providers.
|
|
43272
43615
|
*
|
|
43273
|
-
* @private
|
|
43616
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43274
43617
|
*/
|
|
43275
|
-
function
|
|
43276
|
-
return
|
|
43277
|
-
|
|
43278
|
-
|
|
43618
|
+
function renderNoRegisteredLlmToolsMessage(usedEnvMessage) {
|
|
43619
|
+
return spaceTrim$1((block) => `
|
|
43620
|
+
No LLM providers are available.
|
|
43621
|
+
|
|
43622
|
+
${block(usedEnvMessage)}
|
|
43623
|
+
`);
|
|
43279
43624
|
}
|
|
43625
|
+
|
|
43280
43626
|
/**
|
|
43281
|
-
*
|
|
43627
|
+
* Creates the configuration-status sentence for one provider.
|
|
43282
43628
|
*
|
|
43283
|
-
* @private
|
|
43629
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43284
43630
|
*/
|
|
43285
|
-
function
|
|
43286
|
-
|
|
43287
|
-
|
|
43288
|
-
|
|
43631
|
+
function createRegisteredLlmToolConfigurationStatusMessage({ envVariables, isFullyConfigured, isPartiallyConfigured }, env) {
|
|
43632
|
+
if (isFullyConfigured) {
|
|
43633
|
+
return `Configured`;
|
|
43634
|
+
}
|
|
43635
|
+
if (isPartiallyConfigured) {
|
|
43636
|
+
return createPartiallyConfiguredStatusMessage(envVariables, env);
|
|
43637
|
+
}
|
|
43638
|
+
if (envVariables !== null) {
|
|
43639
|
+
return createNotConfiguredStatusMessage(envVariables);
|
|
43289
43640
|
}
|
|
43290
|
-
return
|
|
43641
|
+
return `Not configured`;
|
|
43642
|
+
// <- Note: Can not be configured via environment variables
|
|
43291
43643
|
}
|
|
43292
43644
|
/**
|
|
43293
|
-
* Creates the
|
|
43645
|
+
* Creates the partial-configuration sentence including missing variables.
|
|
43294
43646
|
*
|
|
43295
|
-
* @private
|
|
43647
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43296
43648
|
*/
|
|
43297
|
-
function
|
|
43298
|
-
|
|
43299
|
-
const providerStatusMessages = createProviderStatusMessages(llmToolStatus, env);
|
|
43300
|
-
return spaceTrim$1(`
|
|
43301
|
-
${index + 1}) **${title}** \`${className}\` from \`${packageName}\`
|
|
43302
|
-
${providerStatusMessages.join('; ')}
|
|
43303
|
-
`);
|
|
43649
|
+
function createPartiallyConfiguredStatusMessage(envVariables, env) {
|
|
43650
|
+
return `Partially confugured, missing ${listMissingEnvironmentVariables(envVariables, env).join(' + ')}`;
|
|
43304
43651
|
}
|
|
43305
43652
|
/**
|
|
43306
|
-
* Creates the
|
|
43653
|
+
* Creates the not-configured sentence including the expected environment variables.
|
|
43307
43654
|
*
|
|
43308
|
-
* @private
|
|
43655
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43309
43656
|
*/
|
|
43310
|
-
function
|
|
43311
|
-
return
|
|
43657
|
+
function createNotConfiguredStatusMessage(envVariables) {
|
|
43658
|
+
return `Not configured, to configure set env ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.join(' + ')}`;
|
|
43659
|
+
}
|
|
43660
|
+
/**
|
|
43661
|
+
* Lists environment variables that are required but currently missing.
|
|
43662
|
+
*
|
|
43663
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43664
|
+
*/
|
|
43665
|
+
function listMissingEnvironmentVariables(envVariables, env) {
|
|
43666
|
+
return (envVariables === null || envVariables === void 0 ? void 0 : envVariables.filter((envVariable) => env[envVariable] === undefined)) || [];
|
|
43312
43667
|
}
|
|
43668
|
+
|
|
43313
43669
|
/**
|
|
43314
43670
|
* Creates the installation-status sentence for one provider.
|
|
43315
43671
|
*
|
|
43316
|
-
* @private
|
|
43672
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43317
43673
|
*/
|
|
43318
|
-
function
|
|
43319
|
-
const installationStatusKey =
|
|
43674
|
+
function createRegisteredLlmToolInstallationStatusMessage({ isMetadataAvailable, isInstalled, }) {
|
|
43675
|
+
const installationStatusKey = createRegisteredLlmToolInstallationStatusKey(isMetadataAvailable, isInstalled);
|
|
43320
43676
|
switch (installationStatusKey) {
|
|
43321
43677
|
case 'missing-metadata-and-installation':
|
|
43322
|
-
// TODO: [�][�] Maybe do allow to do auto-install if package not registered and not found
|
|
43323
43678
|
return `Not installed and no metadata, looks like a unexpected behavior`;
|
|
43324
43679
|
case 'metadata-without-installation':
|
|
43325
|
-
// TODO: [�][�]
|
|
43326
43680
|
return `Not installed`;
|
|
43327
43681
|
case 'installation-without-metadata':
|
|
43328
43682
|
return `No metadata but installed, looks like a unexpected behavior`;
|
|
@@ -43335,9 +43689,9 @@ function createInstallationStatusMessage({ isMetadataAvailable, isInstalled, })
|
|
|
43335
43689
|
/**
|
|
43336
43690
|
* Creates a stable installation-state key from metadata and constructor availability.
|
|
43337
43691
|
*
|
|
43338
|
-
* @private
|
|
43692
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43339
43693
|
*/
|
|
43340
|
-
function
|
|
43694
|
+
function createRegisteredLlmToolInstallationStatusKey(isMetadataAvailable, isInstalled) {
|
|
43341
43695
|
if (!isMetadataAvailable && !isInstalled) {
|
|
43342
43696
|
return 'missing-metadata-and-installation';
|
|
43343
43697
|
}
|
|
@@ -43352,54 +43706,59 @@ function createInstallationStatusKey(isMetadataAvailable, isInstalled) {
|
|
|
43352
43706
|
}
|
|
43353
43707
|
return 'unknown';
|
|
43354
43708
|
}
|
|
43709
|
+
|
|
43355
43710
|
/**
|
|
43356
|
-
*
|
|
43711
|
+
* Renders all provider lines in the status summary.
|
|
43357
43712
|
*
|
|
43358
|
-
* @private
|
|
43713
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43359
43714
|
*/
|
|
43360
|
-
function
|
|
43361
|
-
|
|
43362
|
-
|
|
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
|
|
43715
|
+
function renderAvailableRegisteredLlmTools(llmToolStatuses, env, isRunningInNode) {
|
|
43716
|
+
return llmToolStatuses
|
|
43717
|
+
.map((llmToolStatus, index) => renderAvailableRegisteredLlmTool(llmToolStatus, index, env, isRunningInNode))
|
|
43718
|
+
.join('\n');
|
|
43372
43719
|
}
|
|
43373
43720
|
/**
|
|
43374
|
-
*
|
|
43721
|
+
* Renders one provider line in the status summary.
|
|
43375
43722
|
*
|
|
43376
|
-
* @private
|
|
43723
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43377
43724
|
*/
|
|
43378
|
-
function
|
|
43379
|
-
|
|
43725
|
+
function renderAvailableRegisteredLlmTool(llmToolStatus, index, env, isRunningInNode) {
|
|
43726
|
+
const providerMessage = createAvailableRegisteredLlmToolMessage(llmToolStatus, index, env);
|
|
43727
|
+
if (!isRunningInNode) {
|
|
43728
|
+
return providerMessage;
|
|
43729
|
+
}
|
|
43730
|
+
return colorizeAvailableRegisteredLlmToolMessage(providerMessage, llmToolStatus);
|
|
43380
43731
|
}
|
|
43381
43732
|
/**
|
|
43382
|
-
* Creates the
|
|
43733
|
+
* Creates the plain-text provider line before optional colorization.
|
|
43383
43734
|
*
|
|
43384
|
-
* @private
|
|
43735
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43385
43736
|
*/
|
|
43386
|
-
function
|
|
43387
|
-
|
|
43737
|
+
function createAvailableRegisteredLlmToolMessage(llmToolStatus, index, env) {
|
|
43738
|
+
const { title, packageName, className } = llmToolStatus;
|
|
43739
|
+
const providerStatusMessages = createRegisteredLlmToolStatusMessages(llmToolStatus, env);
|
|
43740
|
+
return spaceTrim$1(`
|
|
43741
|
+
${index + 1}) **${title}** \`${className}\` from \`${packageName}\`
|
|
43742
|
+
${providerStatusMessages.join('; ')}
|
|
43743
|
+
`);
|
|
43388
43744
|
}
|
|
43389
43745
|
/**
|
|
43390
|
-
*
|
|
43746
|
+
* Creates the install and configuration fragments for one provider line.
|
|
43391
43747
|
*
|
|
43392
|
-
* @private
|
|
43748
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43393
43749
|
*/
|
|
43394
|
-
function
|
|
43395
|
-
return
|
|
43750
|
+
function createRegisteredLlmToolStatusMessages(llmToolStatus, env) {
|
|
43751
|
+
return [
|
|
43752
|
+
createRegisteredLlmToolInstallationStatusMessage(llmToolStatus),
|
|
43753
|
+
createRegisteredLlmToolConfigurationStatusMessage(llmToolStatus, env),
|
|
43754
|
+
];
|
|
43396
43755
|
}
|
|
43397
43756
|
/**
|
|
43398
43757
|
* Applies the same terminal coloring rules as the original summary renderer.
|
|
43399
43758
|
*
|
|
43400
|
-
* @private
|
|
43759
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43401
43760
|
*/
|
|
43402
|
-
function
|
|
43761
|
+
function colorizeAvailableRegisteredLlmToolMessage(providerMessage, { isInstalled, isFullyConfigured, isPartiallyConfigured }) {
|
|
43403
43762
|
if (isInstalled && isFullyConfigured) {
|
|
43404
43763
|
return colors.green(providerMessage);
|
|
43405
43764
|
}
|
|
@@ -43408,18 +43767,67 @@ function colorizeAvailableProviderMessage(providerMessage, { isInstalled, isFull
|
|
|
43408
43767
|
}
|
|
43409
43768
|
return colors.gray(providerMessage);
|
|
43410
43769
|
}
|
|
43770
|
+
|
|
43411
43771
|
/**
|
|
43412
|
-
*
|
|
43772
|
+
* Renders the list of environment variables used by at least one registered provider.
|
|
43413
43773
|
*
|
|
43414
|
-
* @private
|
|
43774
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43415
43775
|
*/
|
|
43416
|
-
function
|
|
43417
|
-
return
|
|
43418
|
-
|
|
43419
|
-
|
|
43420
|
-
|
|
43421
|
-
|
|
43422
|
-
|
|
43776
|
+
function renderRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses) {
|
|
43777
|
+
return listRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses)
|
|
43778
|
+
.map((envVariableName) => `- \`${envVariableName}\``)
|
|
43779
|
+
.join('\n');
|
|
43780
|
+
}
|
|
43781
|
+
/**
|
|
43782
|
+
* Lists environment variables that are both present and used by at least one provider.
|
|
43783
|
+
*
|
|
43784
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43785
|
+
*/
|
|
43786
|
+
function listRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses) {
|
|
43787
|
+
return Object.keys(env).filter((envVariableName) => llmToolStatuses.some(({ envVariables }) => envVariables === null || envVariables === void 0 ? void 0 : envVariables.includes(envVariableName)));
|
|
43788
|
+
}
|
|
43789
|
+
|
|
43790
|
+
/**
|
|
43791
|
+
* Renders the full provider status summary.
|
|
43792
|
+
*
|
|
43793
|
+
* @private function of `$registeredLlmToolsMessage`
|
|
43794
|
+
*/
|
|
43795
|
+
function renderRegisteredLlmToolsMessage({ env, llmToolStatuses, usedEnvMessage, isRunningInNode, }) {
|
|
43796
|
+
return spaceTrim$1((block) => `
|
|
43797
|
+
|
|
43798
|
+
${block(usedEnvMessage)}
|
|
43799
|
+
|
|
43800
|
+
Relevant environment variables:
|
|
43801
|
+
${block(renderRelevantRegisteredLlmToolsEnvironmentVariables(env, llmToolStatuses))}
|
|
43802
|
+
|
|
43803
|
+
Available LLM providers are:
|
|
43804
|
+
${block(renderAvailableRegisteredLlmTools(llmToolStatuses, env, isRunningInNode))}
|
|
43805
|
+
`);
|
|
43806
|
+
}
|
|
43807
|
+
|
|
43808
|
+
/**
|
|
43809
|
+
* Pass the `.env` file which was used to configure LLM tools
|
|
43810
|
+
*
|
|
43811
|
+
* Note: `$` is used to indicate that this variable is making side effect
|
|
43812
|
+
*
|
|
43813
|
+
* @private internal log of `$provideLlmToolsConfigurationFromEnv` and `$registeredLlmToolsMessage`
|
|
43814
|
+
*/
|
|
43815
|
+
function $setUsedEnvFilename(filepath) {
|
|
43816
|
+
USED_ENV_FILENAME.set(filepath);
|
|
43817
|
+
}
|
|
43818
|
+
/**
|
|
43819
|
+
* Creates a message with all registered LLM tools
|
|
43820
|
+
*
|
|
43821
|
+
* Note: This function is used to create a (error) message when there is no constructor for some LLM provider
|
|
43822
|
+
*
|
|
43823
|
+
* @private internal function of `createLlmToolsFromConfiguration` and `$provideLlmToolsFromEnv`
|
|
43824
|
+
*/
|
|
43825
|
+
function $registeredLlmToolsMessage() {
|
|
43826
|
+
const registeredLlmToolsMessageContext = createRegisteredLlmToolsMessageContext();
|
|
43827
|
+
if (registeredLlmToolsMessageContext.llmToolStatuses.length === 0) {
|
|
43828
|
+
return renderNoRegisteredLlmToolsMessage(registeredLlmToolsMessageContext.usedEnvMessage);
|
|
43829
|
+
}
|
|
43830
|
+
return renderRegisteredLlmToolsMessage(registeredLlmToolsMessageContext);
|
|
43423
43831
|
}
|
|
43424
43832
|
// TODO: [®] DRY Register logic
|
|
43425
43833
|
// TODO: [🧠][⚛] Maybe pass env as argument
|