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