@promptbook/core 0.112.0-99 → 0.113.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +76 -44
  2. package/esm/index.es.js +2064 -158
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/servers.d.ts +1 -9
  5. package/esm/src/_packages/components.index.d.ts +4 -0
  6. package/esm/src/_packages/core.index.d.ts +22 -2
  7. package/esm/src/_packages/node.index.d.ts +40 -0
  8. package/esm/src/_packages/types.index.d.ts +20 -0
  9. package/esm/src/avatars/avatarAnimationScheduler.d.ts +4 -0
  10. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  11. package/esm/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
  12. package/esm/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
  13. package/esm/src/book-2.0/agent-source/AgentBasicInformation.d.ts +2 -0
  14. package/esm/src/book-2.0/agent-source/agentSourceVisibility.d.ts +97 -0
  15. package/esm/src/book-2.0/agent-source/agentSourceVisibility.test.d.ts +1 -0
  16. package/esm/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
  17. package/esm/src/book-3.0/CliAgent.d.ts +66 -0
  18. package/esm/src/book-3.0/CliAgent.test.d.ts +1 -0
  19. package/esm/src/book-3.0/LiteAgent.d.ts +68 -0
  20. package/esm/src/book-3.0/LiteAgent.test.d.ts +1 -0
  21. package/esm/src/book-3.0/agentFolderPaths.d.ts +30 -0
  22. package/esm/src/book-3.0/cliAgentEnv.d.ts +33 -0
  23. package/esm/src/book-components/BookEditor/BookEditor.d.ts +6 -5
  24. package/esm/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
  25. package/esm/src/book-components/BookEditor/BookEditorBrowserConfig.d.ts +2 -0
  26. package/esm/src/book-components/BookEditor/BookEditorForClient.d.ts +7 -0
  27. package/esm/src/book-components/BookEditor/BookEditorMonacoTokenization.d.ts +2 -0
  28. package/esm/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
  29. package/esm/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +9 -0
  30. package/esm/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
  31. package/esm/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
  32. package/esm/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
  33. package/esm/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
  34. package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  35. package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
  36. package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  37. package/esm/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
  38. package/esm/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
  39. package/esm/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
  40. package/esm/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
  41. package/esm/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  42. package/esm/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
  43. package/esm/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
  44. package/esm/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
  45. package/esm/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
  46. package/esm/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
  47. package/esm/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
  48. package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
  49. package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
  50. package/esm/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +10 -0
  51. package/esm/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
  52. package/esm/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
  53. package/esm/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
  54. package/esm/src/book-components/_common/Modal/Modal.d.ts +1 -1
  55. package/esm/src/book-components/icons/AboutIcon.d.ts +1 -1
  56. package/esm/src/book-components/icons/DownloadIcon.d.ts +1 -1
  57. package/esm/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
  58. package/esm/src/book-components/icons/FullscreenIcon.d.ts +1 -1
  59. package/esm/src/cli/cli-commands/agent/agentCliOptions.d.ts +38 -0
  60. package/esm/src/cli/cli-commands/agent/chat.d.ts +10 -0
  61. package/esm/src/cli/cli-commands/agent/exec.d.ts +10 -0
  62. package/esm/src/cli/cli-commands/agent/run.test.d.ts +1 -0
  63. package/esm/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +2 -24
  64. package/esm/src/cli/cli-commands/agent.d.ts +14 -0
  65. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +23 -1
  66. package/esm/src/cli/cli-commands/agents-server/run.d.ts +6 -0
  67. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +9 -2
  68. package/esm/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  69. package/esm/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.d.ts +25 -0
  70. package/esm/src/cli/cli-commands/coder/find-unwritten.d.ts +10 -0
  71. package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +2 -0
  72. package/esm/src/cli/cli-commands/coder/server.d.ts +13 -0
  73. package/esm/src/cli/cli-commands/coder/waitOptions.d.ts +14 -0
  74. package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -25
  75. package/esm/src/collection/agent-collection/CreateAgentInput.d.ts +2 -1
  76. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
  77. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.d.ts +21 -1
  78. package/esm/src/commitments/META_VISIBILITY/META_VISIBILITY.d.ts +27 -0
  79. package/esm/src/commitments/_common/teamInternalAgentAccess.d.ts +9 -1
  80. package/esm/src/commitments/index.d.ts +2 -1
  81. package/esm/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  82. package/esm/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  83. package/esm/src/scrapers/website/utils/createShowdownConverter.d.ts +2 -2
  84. package/esm/src/utils/isTimingSafeEqualString.d.ts +25 -0
  85. package/esm/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
  86. package/esm/src/version.d.ts +1 -1
  87. package/package.json +1 -1
  88. package/umd/index.umd.js +2075 -158
  89. package/umd/index.umd.js.map +1 -1
  90. package/umd/servers.d.ts +1 -9
  91. package/umd/src/_packages/components.index.d.ts +4 -0
  92. package/umd/src/_packages/core.index.d.ts +22 -2
  93. package/umd/src/_packages/node.index.d.ts +40 -0
  94. package/umd/src/_packages/types.index.d.ts +20 -0
  95. package/umd/src/avatars/avatarAnimationScheduler.d.ts +4 -0
  96. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  97. package/umd/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
  98. package/umd/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
  99. package/umd/src/book-2.0/agent-source/AgentBasicInformation.d.ts +2 -0
  100. package/umd/src/book-2.0/agent-source/agentSourceVisibility.d.ts +97 -0
  101. package/umd/src/book-2.0/agent-source/agentSourceVisibility.test.d.ts +1 -0
  102. package/umd/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
  103. package/umd/src/book-3.0/CliAgent.d.ts +66 -0
  104. package/umd/src/book-3.0/CliAgent.test.d.ts +1 -0
  105. package/umd/src/book-3.0/LiteAgent.d.ts +68 -0
  106. package/umd/src/book-3.0/LiteAgent.test.d.ts +1 -0
  107. package/umd/src/book-3.0/agentFolderPaths.d.ts +30 -0
  108. package/umd/src/book-3.0/cliAgentEnv.d.ts +33 -0
  109. package/umd/src/book-components/BookEditor/BookEditor.d.ts +6 -5
  110. package/umd/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
  111. package/umd/src/book-components/BookEditor/BookEditorBrowserConfig.d.ts +2 -0
  112. package/umd/src/book-components/BookEditor/BookEditorForClient.d.ts +7 -0
  113. package/umd/src/book-components/BookEditor/BookEditorMonacoTokenization.d.ts +2 -0
  114. package/umd/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
  115. package/umd/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +9 -0
  116. package/umd/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
  117. package/umd/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
  118. package/umd/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
  119. package/umd/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
  120. package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  121. package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
  122. package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  123. package/umd/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
  124. package/umd/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
  125. package/umd/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
  126. package/umd/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
  127. package/umd/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  128. package/umd/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
  129. package/umd/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
  130. package/umd/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
  131. package/umd/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
  132. package/umd/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
  133. package/umd/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
  134. package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
  135. package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
  136. package/umd/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +10 -0
  137. package/umd/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
  138. package/umd/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
  139. package/umd/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
  140. package/umd/src/book-components/_common/Modal/Modal.d.ts +1 -1
  141. package/umd/src/book-components/icons/AboutIcon.d.ts +1 -1
  142. package/umd/src/book-components/icons/DownloadIcon.d.ts +1 -1
  143. package/umd/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
  144. package/umd/src/book-components/icons/FullscreenIcon.d.ts +1 -1
  145. package/umd/src/cli/cli-commands/agent/agentCliOptions.d.ts +38 -0
  146. package/umd/src/cli/cli-commands/agent/chat.d.ts +10 -0
  147. package/umd/src/cli/cli-commands/agent/exec.d.ts +10 -0
  148. package/umd/src/cli/cli-commands/agent/run.test.d.ts +1 -0
  149. package/umd/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +2 -24
  150. package/umd/src/cli/cli-commands/agent.d.ts +14 -0
  151. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +23 -1
  152. package/umd/src/cli/cli-commands/agents-server/run.d.ts +6 -0
  153. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +9 -2
  154. package/umd/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  155. package/umd/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.d.ts +25 -0
  156. package/umd/src/cli/cli-commands/coder/find-unwritten.d.ts +10 -0
  157. package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +2 -0
  158. package/umd/src/cli/cli-commands/coder/server.d.ts +13 -0
  159. package/umd/src/cli/cli-commands/coder/waitOptions.d.ts +14 -0
  160. package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -25
  161. package/umd/src/collection/agent-collection/CreateAgentInput.d.ts +2 -1
  162. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
  163. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/prepareAgentSourceForPersistence.d.ts +21 -1
  164. package/umd/src/commitments/META_VISIBILITY/META_VISIBILITY.d.ts +27 -0
  165. package/umd/src/commitments/_common/teamInternalAgentAccess.d.ts +9 -1
  166. package/umd/src/commitments/index.d.ts +2 -1
  167. package/umd/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  168. package/umd/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  169. package/umd/src/scrapers/website/utils/createShowdownConverter.d.ts +2 -2
  170. package/umd/src/utils/isTimingSafeEqualString.d.ts +25 -0
  171. package/umd/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
  172. package/umd/src/version.d.ts +1 -1
package/esm/index.es.js CHANGED
@@ -1,14 +1,14 @@
1
- import { SHA256 } from 'crypto-js';
2
- import hexEncoder from 'crypto-js/enc-hex';
3
1
  import { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
- import { randomBytes } from 'crypto';
2
+ import CryptoJS from 'crypto-js';
3
+ import hexEncoder from 'crypto-js/enc-hex';
5
4
  import { Subject, BehaviorSubject } from 'rxjs';
6
5
  import { forTime } from 'waitasecond';
7
6
  import sha256 from 'crypto-js/sha256';
8
7
  import { basename, join, dirname, isAbsolute } from 'path';
9
8
  import { lookup, extension } from 'mime-types';
10
- import { parse, unparse } from 'papaparse';
9
+ import papaparse from 'papaparse';
11
10
  import moment from 'moment';
11
+ import { randomBytes } from 'crypto';
12
12
  import colors from 'colors';
13
13
  import { fileSearchTool, tool, Agent as Agent$1, webSearchTool, run, setDefaultOpenAIClient, setDefaultOpenAIKey } from '@openai/agents';
14
14
  import Bottleneck from 'bottleneck';
@@ -28,12 +28,26 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
28
28
  * @generated
29
29
  * @see https://github.com/webgptorg/promptbook
30
30
  */
31
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-99';
31
+ const PROMPTBOOK_ENGINE_VERSION = '0.113.0-0';
32
32
  /**
33
33
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
34
34
  * Note: [💞] Ignore a discrepancy between file name and entity name
35
35
  */
36
36
 
37
+ /**
38
+ * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
39
+ *
40
+ * @public exported from `@promptbook/core`
41
+ */
42
+ class ParseError extends Error {
43
+ constructor(message) {
44
+ super(message);
45
+ this.name = 'ParseError';
46
+ Object.setPrototypeOf(this, ParseError.prototype);
47
+ }
48
+ }
49
+ // TODO: Maybe split `ParseError` and `ApplyError`
50
+
37
51
  /**
38
52
  * Trims string from all 4 sides
39
53
  *
@@ -46,6 +60,181 @@ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-99';
46
60
  */
47
61
  const spaceTrim = spaceTrim$1;
48
62
 
63
+ /**
64
+ * Supported visibility states for persisted agents.
65
+ *
66
+ * @public exported from `@promptbook/core`
67
+ */
68
+ const AGENT_VISIBILITY_VALUES = ['PRIVATE', 'UNLISTED', 'PUBLIC'];
69
+ /**
70
+ * Fallback visibility used when no valid value is configured.
71
+ *
72
+ * @public exported from `@promptbook/core`
73
+ */
74
+ const DEFAULT_AGENT_VISIBILITY = 'UNLISTED';
75
+ /**
76
+ * Matches a single-line `META VISIBILITY` commitment in book source.
77
+ */
78
+ const META_VISIBILITY_LINE_PATTERN = /^(\s*)META\s+VISIBILITY\b([\s\S]*)$/iu;
79
+ /**
80
+ * Returns `true` when the value is one of supported visibility states.
81
+ *
82
+ * @param value - Raw value to validate.
83
+ * @returns Whether the value is a valid `AgentVisibility`.
84
+ *
85
+ * @public exported from `@promptbook/core`
86
+ */
87
+ function isAgentVisibility(value) {
88
+ return typeof value === 'string' && AGENT_VISIBILITY_VALUES.includes(value);
89
+ }
90
+ /**
91
+ * Normalizes raw visibility text into a supported value.
92
+ *
93
+ * @param value - Raw visibility value.
94
+ * @returns Normalized visibility, or `null` when invalid.
95
+ *
96
+ * @public exported from `@promptbook/core`
97
+ */
98
+ function normalizeAgentVisibility(value) {
99
+ if (typeof value !== 'string') {
100
+ return null;
101
+ }
102
+ const normalized = value.trim().toUpperCase();
103
+ return isAgentVisibility(normalized) ? normalized : null;
104
+ }
105
+ /**
106
+ * Parses visibility from an unknown value with a safe fallback.
107
+ *
108
+ * @param value - Raw visibility value.
109
+ * @param fallback - Fallback when the value is invalid.
110
+ * @returns Parsed visibility.
111
+ *
112
+ * @public exported from `@promptbook/core`
113
+ */
114
+ function parseAgentVisibility(value, fallback = DEFAULT_AGENT_VISIBILITY) {
115
+ var _a;
116
+ return (_a = normalizeAgentVisibility(value)) !== null && _a !== void 0 ? _a : fallback;
117
+ }
118
+ /**
119
+ * Parses visibility and throws when the value is not supported.
120
+ *
121
+ * @param value - Raw visibility value.
122
+ * @param sourceLabel - Human-readable source used in the error message.
123
+ * @returns Parsed visibility.
124
+ *
125
+ * @public exported from `@promptbook/core`
126
+ */
127
+ function parseAgentVisibilityStrict(value, sourceLabel = 'visibility') {
128
+ const visibility = normalizeAgentVisibility(value);
129
+ if (visibility) {
130
+ return visibility;
131
+ }
132
+ throw new ParseError(spaceTrim(`
133
+ Invalid ${sourceLabel}.
134
+
135
+ Value must be one of: ${AGENT_VISIBILITY_VALUES.map((allowedValue) => `\`${allowedValue}\``).join(', ')}.
136
+ `));
137
+ }
138
+ /**
139
+ * Extracts the last `META VISIBILITY` value from an agent source.
140
+ *
141
+ * @param agentSource - Raw book source.
142
+ * @param options - Strict parsing options.
143
+ * @returns Normalized visibility, or `null` when no commitment is present.
144
+ *
145
+ * @public exported from `@promptbook/core`
146
+ */
147
+ function parseAgentSourceVisibility(agentSource, options = {}) {
148
+ let visibilityContent = null;
149
+ for (const line of agentSource.split(/\r?\n/u)) {
150
+ const lineMatch = META_VISIBILITY_LINE_PATTERN.exec(line);
151
+ if (lineMatch) {
152
+ visibilityContent = lineMatch[2].trim();
153
+ }
154
+ }
155
+ if (visibilityContent === null) {
156
+ return null;
157
+ }
158
+ const visibility = normalizeAgentVisibility(visibilityContent);
159
+ if (visibility || !options.isStrict) {
160
+ return visibility;
161
+ }
162
+ return parseAgentVisibilityStrict(visibilityContent, '`META VISIBILITY` commitment');
163
+ }
164
+ /**
165
+ * Returns whether an agent should be listed publicly in anonymous views.
166
+ *
167
+ * @param visibility - Agent visibility to evaluate.
168
+ * @returns `true` for publicly listed agents.
169
+ *
170
+ * @public exported from `@promptbook/core`
171
+ */
172
+ function isPublicAgentVisibility(visibility) {
173
+ return visibility === 'PUBLIC';
174
+ }
175
+ /**
176
+ * Returns the next visibility in UI rotation order.
177
+ *
178
+ * @param visibility - Current visibility.
179
+ * @returns Next visibility value.
180
+ *
181
+ * @public exported from `@promptbook/core`
182
+ */
183
+ function getNextAgentVisibility(visibility) {
184
+ switch (visibility) {
185
+ case 'PRIVATE':
186
+ return 'UNLISTED';
187
+ case 'UNLISTED':
188
+ return 'PUBLIC';
189
+ case 'PUBLIC':
190
+ default:
191
+ return 'PRIVATE';
192
+ }
193
+ }
194
+ /**
195
+ * Inserts or replaces the `META VISIBILITY` commitment in a book source.
196
+ *
197
+ * @param agentSource - Raw book source.
198
+ * @param visibility - Visibility to persist.
199
+ * @returns Source with exactly one normalized `META VISIBILITY` line.
200
+ *
201
+ * @public exported from `@promptbook/core`
202
+ */
203
+ function setAgentSourceVisibility(agentSource, visibility) {
204
+ var _a;
205
+ const normalizedVisibility = parseAgentVisibilityStrict(visibility, '`META VISIBILITY` commitment');
206
+ const lines = agentSource.split(/\r?\n/u);
207
+ const nextLines = [];
208
+ let isVisibilityLineWritten = false;
209
+ for (const line of lines) {
210
+ const lineMatch = META_VISIBILITY_LINE_PATTERN.exec(line);
211
+ if (!lineMatch) {
212
+ nextLines.push(line);
213
+ continue;
214
+ }
215
+ if (isVisibilityLineWritten) {
216
+ continue;
217
+ }
218
+ nextLines.push(`${(_a = lineMatch[1]) !== null && _a !== void 0 ? _a : ''}META VISIBILITY ${normalizedVisibility}`);
219
+ isVisibilityLineWritten = true;
220
+ }
221
+ if (!isVisibilityLineWritten) {
222
+ nextLines.splice(findMetaVisibilityInsertIndex(nextLines), 0, `META VISIBILITY ${normalizedVisibility}`);
223
+ }
224
+ return nextLines.join('\n');
225
+ }
226
+ /**
227
+ * Finds the position after the first non-empty line, which is the agent title.
228
+ *
229
+ * @param lines - Source lines.
230
+ * @returns Insertion index for profile metadata.
231
+ */
232
+ function findMetaVisibilityInsertIndex(lines) {
233
+ const titleLineIndex = lines.findIndex((line) => line.trim().length > 0);
234
+ return titleLineIndex === -1 ? 0 : titleLineIndex + 1;
235
+ }
236
+ // Note: [💞] Ignore a discrepancy between file name and entity name
237
+
49
238
  /**
50
239
  * Class implementing take chain.
51
240
  *
@@ -1653,7 +1842,7 @@ function valueToString(value) {
1653
1842
  * @public exported from `@promptbook/utils`
1654
1843
  */
1655
1844
  function computeHash(value) {
1656
- return SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
1845
+ return CryptoJS.SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
1657
1846
  }
1658
1847
  // TODO: [🥬][🥬] Use this ACRY
1659
1848
 
@@ -1769,20 +1958,6 @@ function isValidUrl(url) {
1769
1958
  }
1770
1959
  }
1771
1960
 
1772
- /**
1773
- * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
1774
- *
1775
- * @public exported from `@promptbook/core`
1776
- */
1777
- class ParseError extends Error {
1778
- constructor(message) {
1779
- super(message);
1780
- this.name = 'ParseError';
1781
- Object.setPrototypeOf(this, ParseError.prototype);
1782
- }
1783
- }
1784
- // TODO: Maybe split `ParseError` and `ApplyError`
1785
-
1786
1961
  /**
1787
1962
  * Function isValidJsonString will tell you if the string is valid JSON or not
1788
1963
  *
@@ -3773,7 +3948,7 @@ class NotYetImplementedError extends Error {
3773
3948
  * @private internal helper function
3774
3949
  */
3775
3950
  function $randomToken(randomness) {
3776
- return randomBytes(randomness).toString('hex');
3951
+ return CryptoJS.lib.WordArray.random(randomness).toString(CryptoJS.enc.Hex);
3777
3952
  }
3778
3953
  // TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
3779
3954
  // TODO: Maybe use nanoid instead https://github.com/ai/nanoid
@@ -5457,7 +5632,7 @@ function normalizeToKebabCase(text) {
5457
5632
  * @public exported from `@promptbook/editable`
5458
5633
  */
5459
5634
  function knowledgeSourceContentToName(knowledgeSourceContent) {
5460
- const hash = SHA256(hexEncoder.parse(JSON.stringify(knowledgeSourceContent)))
5635
+ const hash = CryptoJS.SHA256(hexEncoder.parse(JSON.stringify(knowledgeSourceContent)))
5461
5636
  // <- TODO: [🥬] Encapsulate sha256 to some private utility function
5462
5637
  .toString( /* hex */)
5463
5638
  .substring(0, 20);
@@ -6370,7 +6545,7 @@ function csvParse(value /* <- TODO: string_csv */, settings, schema /* <- TODO:
6370
6545
  console.warn('CSV string contains carriage return characters, but in the CSV settings the `newline` setting does not include them. Autohealing the CSV string.');
6371
6546
  value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
6372
6547
  }
6373
- const csv = parse(value, settings);
6548
+ const csv = papaparse.parse(value, settings);
6374
6549
  return csv;
6375
6550
  }
6376
6551
 
@@ -6455,10 +6630,10 @@ const CsvFormatParser = {
6455
6630
  i > index ? { ...row, [outputParameterName]: PENDING_VALUE_PLACEHOLDER } : row,
6456
6631
  );
6457
6632
  */
6458
- await onProgress(unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
6633
+ await onProgress(papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
6459
6634
  }
6460
6635
  }
6461
- return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
6636
+ return papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
6462
6637
  },
6463
6638
  },
6464
6639
  {
@@ -6486,7 +6661,7 @@ const CsvFormatParser = {
6486
6661
  return /* not await */ mapCallback({ [key]: value }, index, array.length);
6487
6662
  }));
6488
6663
  }));
6489
- return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
6664
+ return papaparse.unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
6490
6665
  },
6491
6666
  },
6492
6667
  ],
@@ -9898,6 +10073,11 @@ function isVoidPseudoAgentReference(rawReference) {
9898
10073
  * - `isValidAgentUrl` *(this one)* which tests just agent URL
9899
10074
  * - `isValidPipelineUrl` which tests just pipeline URL
9900
10075
  *
10076
+ * Note: This is a pure structural validator and does not block private/internal network
10077
+ * addresses. Callers that fetch the URL server-side from an untrusted network context must
10078
+ * additionally apply the `assertSafeUrl` SSRF guard from the Agents Server before any
10079
+ * outbound request.
10080
+ *
9901
10081
  * @public exported from `@promptbook/utils`
9902
10082
  */
9903
10083
  function isValidAgentUrl(url) {
@@ -9911,12 +10091,6 @@ function isValidAgentUrl(url) {
9911
10091
  // TODO: [🐠]
9912
10092
  return false;
9913
10093
  }
9914
- /*
9915
- Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
9916
- if (isUrlOnPrivateNetwork(url)) {
9917
- return false;
9918
- }
9919
- */
9920
10094
  return true;
9921
10095
  }
9922
10096
  // TODO: [🐠] Maybe more info why the URL is invalid
@@ -10092,8 +10266,11 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
10092
10266
  if (!trimmedContent) {
10093
10267
  return requirements;
10094
10268
  }
10095
- // Add goal as a proper h2 section to the system message
10096
- const goalSection = `## Goal\n\n${trimmedContent}`;
10269
+ const goalSection = spaceTrim$1((block) => `
10270
+ ## Goal
10271
+
10272
+ ${block(trimmedContent)}
10273
+ `);
10097
10274
  const requirementsWithGoal = this.appendToSystemMessage(requirements, goalSection, '\n\n');
10098
10275
  return this.appendToPromptSuffix(requirementsWithGoal, trimmedContent);
10099
10276
  }
@@ -10615,8 +10792,11 @@ class LanguageCommitmentDefinition extends BaseCommitmentDefinition {
10615
10792
  if (!trimmedContent) {
10616
10793
  return requirements;
10617
10794
  }
10618
- // Add language as a bullet under a ## Language section
10619
- const languageSection = `## Language\n\n- Your language is ${trimmedContent}`;
10795
+ const languageSection = spaceTrim$1((block) => `
10796
+ ## Language
10797
+
10798
+ - Your language is ${block(trimmedContent)}
10799
+ `);
10620
10800
  return this.appendToSystemMessage(requirements, languageSection, '\n\n');
10621
10801
  }
10622
10802
  }
@@ -13063,21 +13243,22 @@ function getPointBounds(points) {
13063
13243
  * @private helper of `fractalAvatarVisual`
13064
13244
  */
13065
13245
  function drawDragonCurveLayer(context, points, options) {
13066
- const { size, primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
13246
+ const { primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
13067
13247
  const firstPoint = points[0];
13068
13248
  const lastPoint = points[points.length - 1];
13069
13249
  const ribbonGradient = context.createLinearGradient(firstPoint.x, firstPoint.y, lastPoint.x, lastPoint.y);
13070
13250
  ribbonGradient.addColorStop(0, `${primaryColor}f2`);
13071
13251
  ribbonGradient.addColorStop(0.5, `${secondaryColor}e6`);
13072
13252
  ribbonGradient.addColorStop(1, `${tertiaryColor}f2`);
13253
+ // Approximate the blurred shadow stroke with a wider semi-transparent stroke instead of
13254
+ // context.filter blur, which triggers a costly software rasterization pass every frame.
13073
13255
  context.save();
13074
13256
  context.beginPath();
13075
13257
  tracePolyline(context, points);
13076
- context.strokeStyle = `${shadowColor}82`;
13077
- context.lineWidth = strokeWidth * 1.8;
13258
+ context.strokeStyle = `${shadowColor}48`;
13259
+ context.lineWidth = strokeWidth * 4.5;
13078
13260
  context.lineJoin = 'round';
13079
13261
  context.lineCap = 'round';
13080
- context.filter = `blur(${size * 0.022}px)`;
13081
13262
  context.stroke();
13082
13263
  context.restore();
13083
13264
  context.beginPath();
@@ -13539,7 +13720,7 @@ function fillTextureRect(texture, x, y, width, height, color) {
13539
13720
  *
13540
13721
  * @private helper of `minecraft2AvatarVisual`
13541
13722
  */
13542
- const LIGHT_DIRECTION$2 = normalizeVector3({
13723
+ const LIGHT_DIRECTION$4 = normalizeVector3({
13543
13724
  x: 0.4,
13544
13725
  y: -0.65,
13545
13726
  z: 0.92,
@@ -13661,11 +13842,23 @@ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY,
13661
13842
  * @private helper of `minecraft2AvatarVisual`
13662
13843
  */
13663
13844
  function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
13845
+ const cx = size * 0.5 + interaction.gazeX * size * 0.03;
13846
+ const cy = size * 0.85 + Math.sin(timeMs / 880) * size * 0.01;
13847
+ const rx = size * (0.16 + interaction.intensity * 0.015);
13848
+ const ry = size * 0.055;
13849
+ // Radial gradient approximates the blurry ellipse shadow without context.filter blur.
13664
13850
  context.save();
13665
- context.fillStyle = `${palette.shadow}66`;
13666
- context.filter = `blur(${size * 0.02}px)`;
13851
+ context.translate(cx, cy);
13852
+ context.scale(1, ry / rx);
13853
+ const blurRadius = rx * 1.4;
13854
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
13855
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
13856
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
13857
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
13858
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
13859
+ context.fillStyle = shadowGradient;
13667
13860
  context.beginPath();
13668
- context.ellipse(size * 0.5 + interaction.gazeX * size * 0.03, size * 0.85 + Math.sin(timeMs / 880) * size * 0.01, size * (0.16 + interaction.intensity * 0.015), size * 0.055, 0, 0, Math.PI * 2);
13861
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
13669
13862
  context.fill();
13670
13863
  context.restore();
13671
13864
  }
@@ -13751,7 +13944,7 @@ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
13751
13944
  corners: projectedCorners,
13752
13945
  texture: faceDefinition.texture,
13753
13946
  averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
13754
- lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$2), -1, 1),
13947
+ lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$4), -1, 1),
13755
13948
  outlineColor: cuboid.outlineColor,
13756
13949
  };
13757
13950
  });
@@ -13889,13 +14082,27 @@ const minecraftAvatarVisual = {
13889
14082
  spotlight.addColorStop(1, `${palette.highlight}00`);
13890
14083
  context.fillStyle = spotlight;
13891
14084
  context.fillRect(0, 0, size, size);
13892
- context.save();
13893
- context.fillStyle = 'rgba(0, 0, 0, 0.22)';
13894
- context.filter = `blur(${size * 0.018}px)`;
13895
- context.beginPath();
13896
- context.ellipse(size * 0.5, size * 0.86, size * 0.2, size * 0.06, 0, 0, Math.PI * 2);
13897
- context.fill();
13898
- context.restore();
14085
+ {
14086
+ // Radial gradient approximates the blurry ellipse shadow without context.filter blur.
14087
+ const cx = size * 0.5;
14088
+ const cy = size * 0.86;
14089
+ const rx = size * 0.2;
14090
+ const ry = size * 0.06;
14091
+ const blurRadius = rx * 1.4;
14092
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
14093
+ shadowGradient.addColorStop(0, 'rgba(0,0,0,0.28)');
14094
+ shadowGradient.addColorStop(0.45, 'rgba(0,0,0,0.14)');
14095
+ shadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.05)');
14096
+ shadowGradient.addColorStop(1, 'rgba(0,0,0,0)');
14097
+ context.save();
14098
+ context.translate(cx, cy);
14099
+ context.scale(1, ry / rx);
14100
+ context.fillStyle = shadowGradient;
14101
+ context.beginPath();
14102
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
14103
+ context.fill();
14104
+ context.restore();
14105
+ }
13899
14106
  drawVoxelCuboid(context, {
13900
14107
  x: bodyX,
13901
14108
  y: bodyY,
@@ -14424,7 +14631,7 @@ function resolveSeededIntegerRange(random, minimumValue, maximumValue) {
14424
14631
  *
14425
14632
  * @private helper of `octopus3AvatarVisual`
14426
14633
  */
14427
- function formatAlphaHex$1(opacity) {
14634
+ function formatAlphaHex$2(opacity) {
14428
14635
  return Math.round(Math.min(1, Math.max(0, opacity)) * 255)
14429
14636
  .toString(16)
14430
14637
  .padStart(2, '0');
@@ -14803,7 +15010,7 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
14803
15010
  context.beginPath();
14804
15011
  context.moveTo(-radiusX * 0.74, radiusY * 0.2);
14805
15012
  context.quadraticCurveTo(0, radiusY * 0.38, radiusX * 0.74, radiusY * 0.2);
14806
- context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
15013
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex$2(eyeStyle.lowerLidOpacity)}`;
14807
15014
  context.lineWidth = radiusX * 0.08;
14808
15015
  context.lineCap = 'round';
14809
15016
  context.stroke();
@@ -14884,7 +15091,7 @@ function drawProjectedOrganicEye(context, localCenter, radiusX, radiusY, center,
14884
15091
  context.beginPath();
14885
15092
  context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
14886
15093
  context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
14887
- context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
15094
+ context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
14888
15095
  context.lineWidth = projectedRadiusX * 0.08;
14889
15096
  context.lineCap = 'round';
14890
15097
  context.stroke();
@@ -14930,7 +15137,7 @@ function drawProjectedQuad(context, corners, fillStyle) {
14930
15137
  *
14931
15138
  * @private helper of the 3D octopus avatar visuals
14932
15139
  */
14933
- function formatAlphaHex(opacity) {
15140
+ function formatAlphaHex$1(opacity) {
14934
15141
  return Math.round(clampNumber$1(opacity, 0, 1) * 255)
14935
15142
  .toString(16)
14936
15143
  .padStart(2, '0');
@@ -14942,11 +15149,40 @@ function formatAlphaHex(opacity) {
14942
15149
  *
14943
15150
  * @private helper of `octopus3dAvatarVisual`
14944
15151
  */
14945
- const LIGHT_DIRECTION$1 = normalizeVector3({
15152
+ const LIGHT_DIRECTION$3 = normalizeVector3({
14946
15153
  x: 0.48,
14947
15154
  y: -0.62,
14948
15155
  z: 0.94,
14949
15156
  });
15157
+ /**
15158
+ * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
15159
+ *
15160
+ * @private helper of `octopus3dAvatarVisual`
15161
+ */
15162
+ const octopus3dStableStateCache = new WeakMap();
15163
+ /**
15164
+ * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
15165
+ *
15166
+ * @private helper of `octopus3dAvatarVisual`
15167
+ */
15168
+ function getOctopus3dStableState(createRandom) {
15169
+ const cached = octopus3dStableStateCache.get(createRandom);
15170
+ if (cached !== undefined) {
15171
+ return cached;
15172
+ }
15173
+ const animationRandom = createRandom('octopus3d-animation-profile');
15174
+ const eyeRandom = createRandom('octopus3d-eye-profile');
15175
+ const leftEyePhaseOffset = eyeRandom() * 0.6;
15176
+ const rightEyePhaseOffset = eyeRandom() * 0.6;
15177
+ const state = {
15178
+ morphologyProfile: createOctopus3MorphologyProfile(createRandom),
15179
+ animationPhase: animationRandom() * Math.PI * 2,
15180
+ leftEyePhaseOffset,
15181
+ rightEyePhaseOffset,
15182
+ };
15183
+ octopus3dStableStateCache.set(createRandom, state);
15184
+ return state;
15185
+ }
14950
15186
  /**
14951
15187
  * Proper 3D Octopus visual built from projected organic meshes and tentacles.
14952
15188
  *
@@ -14959,10 +15195,7 @@ const octopus3dAvatarVisual = {
14959
15195
  isAnimated: true,
14960
15196
  supportsPointerTracking: true,
14961
15197
  render({ context, size, palette, createRandom, timeMs, interaction }) {
14962
- const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
14963
- const animationRandom = createRandom('octopus3d-animation-profile');
14964
- const eyeRandom = createRandom('octopus3d-eye-profile');
14965
- const animationPhase = animationRandom() * Math.PI * 2;
15198
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
14966
15199
  const sceneCenterX = size * 0.5;
14967
15200
  const sceneCenterY = size * 0.56;
14968
15201
  const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
@@ -15059,12 +15292,12 @@ const octopus3dAvatarVisual = {
15059
15292
  x: -faceEyeSpacing,
15060
15293
  y: faceEyeYOffset,
15061
15294
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
15062
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
15295
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
15063
15296
  drawProjectedOrganicEye(context, {
15064
15297
  x: faceEyeSpacing,
15065
15298
  y: faceEyeYOffset,
15066
15299
  z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
15067
- }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
15300
+ }, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
15068
15301
  drawProjectedOrganicMouth(context, [
15069
15302
  {
15070
15303
  x: -mouthHalfWidth,
@@ -15108,14 +15341,28 @@ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCent
15108
15341
  /**
15109
15342
  * Draws the soft ground shadow below the octopus.
15110
15343
  *
15344
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
15345
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
15346
+ *
15111
15347
  * @private helper of `octopus3dAvatarVisual`
15112
15348
  */
15113
15349
  function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
15350
+ const cx = size * 0.5 + interaction.gazeX * size * 0.04;
15351
+ const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
15352
+ const rx = size * (0.18 + interaction.intensity * 0.02);
15353
+ const ry = size * 0.06;
15114
15354
  context.save();
15115
- context.fillStyle = `${palette.shadow}66`;
15116
- context.filter = `blur(${size * 0.022}px)`;
15355
+ context.translate(cx, cy);
15356
+ context.scale(1, ry / rx);
15357
+ const blurRadius = rx * 1.4;
15358
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
15359
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
15360
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
15361
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
15362
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
15363
+ context.fillStyle = shadowGradient;
15117
15364
  context.beginPath();
15118
- context.ellipse(size * 0.5 + interaction.gazeX * size * 0.04, size * 0.87 + Math.sin(timeMs / 920) * size * 0.008, size * (0.18 + interaction.intensity * 0.02), size * 0.06, 0, 0, Math.PI * 2);
15365
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
15119
15366
  context.fill();
15120
15367
  context.restore();
15121
15368
  }
@@ -15152,7 +15399,7 @@ function resolveVisibleEllipsoidPatches(options) {
15152
15399
  corners: projectedCorners,
15153
15400
  averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
15154
15401
  transformedCorners.length,
15155
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
15402
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
15156
15403
  fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
15157
15404
  outlineColor,
15158
15405
  });
@@ -15341,11 +15588,40 @@ function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
15341
15588
  *
15342
15589
  * @private helper of `octopus3d2AvatarVisual`
15343
15590
  */
15344
- const LIGHT_DIRECTION = normalizeVector3({
15591
+ const LIGHT_DIRECTION$2 = normalizeVector3({
15345
15592
  x: 0.38,
15346
15593
  y: -0.6,
15347
15594
  z: 0.98,
15348
15595
  });
15596
+ /**
15597
+ * Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
15598
+ *
15599
+ * @private helper of `octopus3d2AvatarVisual`
15600
+ */
15601
+ const octopus3d2StableStateCache = new WeakMap();
15602
+ /**
15603
+ * Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
15604
+ *
15605
+ * @private helper of `octopus3d2AvatarVisual`
15606
+ */
15607
+ function getOctopus3d2StableState(createRandom) {
15608
+ const cached = octopus3d2StableStateCache.get(createRandom);
15609
+ if (cached !== undefined) {
15610
+ return cached;
15611
+ }
15612
+ const animationRandom = createRandom('octopus3d2-animation-profile');
15613
+ const eyeRandom = createRandom('octopus3d2-eye-profile');
15614
+ const leftEyePhaseOffset = eyeRandom() * 0.7;
15615
+ const rightEyePhaseOffset = eyeRandom() * 0.7;
15616
+ const state = {
15617
+ morphologyProfile: createOctopus3MorphologyProfile(createRandom),
15618
+ animationPhase: animationRandom() * Math.PI * 2,
15619
+ leftEyePhaseOffset,
15620
+ rightEyePhaseOffset,
15621
+ };
15622
+ octopus3d2StableStateCache.set(createRandom, state);
15623
+ return state;
15624
+ }
15349
15625
  /**
15350
15626
  * Octopus 3D 2 avatar visual.
15351
15627
  *
@@ -15358,10 +15634,7 @@ const octopus3d2AvatarVisual = {
15358
15634
  isAnimated: true,
15359
15635
  supportsPointerTracking: true,
15360
15636
  render({ context, size, palette, createRandom, timeMs, interaction }) {
15361
- const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
15362
- const animationRandom = createRandom('octopus3d2-animation-profile');
15363
- const eyeRandom = createRandom('octopus3d2-eye-profile');
15364
- const animationPhase = animationRandom() * Math.PI * 2;
15637
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3d2StableState(createRandom);
15365
15638
  const sceneCenterX = size * 0.5;
15366
15639
  const sceneCenterY = size * 0.575;
15367
15640
  const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
@@ -15414,8 +15687,8 @@ const octopus3d2AvatarVisual = {
15414
15687
  const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
15415
15688
  const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
15416
15689
  const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
15417
- drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
15418
- drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + eyeRandom() * 0.7, interaction, morphologyProfile.face.eyeStyle);
15690
+ drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
15691
+ drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
15419
15692
  drawProjectedOrganicMouth(context, [
15420
15693
  sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
15421
15694
  sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
@@ -15444,54 +15717,118 @@ function drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, scene
15444
15717
  /**
15445
15718
  * Draws the soft floor shadow that anchors the single mesh in the frame.
15446
15719
  *
15720
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
15721
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
15722
+ *
15447
15723
  * @private helper of `octopus3d2AvatarVisual`
15448
15724
  */
15449
15725
  function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
15726
+ const cx = size * 0.5 + interaction.gazeX * size * 0.045;
15727
+ const cy = size * 0.88 + Math.sin(timeMs / 940) * size * 0.008;
15728
+ const rx = size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018);
15729
+ const ry = size * 0.062;
15450
15730
  context.save();
15451
- context.fillStyle = `${palette.shadow}66`;
15452
- context.filter = `blur(${size * 0.024}px)`;
15731
+ context.translate(cx, cy);
15732
+ context.scale(1, ry / rx);
15733
+ const blurRadius = rx * 1.4;
15734
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
15735
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
15736
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
15737
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
15738
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
15739
+ context.fillStyle = shadowGradient;
15453
15740
  context.beginPath();
15454
- context.ellipse(size * 0.5 + interaction.gazeX * size * 0.045, size * 0.88 + Math.sin(timeMs / 940) * size * 0.008, size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018), size * 0.062, 0, 0, Math.PI * 2);
15741
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
15455
15742
  context.fill();
15456
15743
  context.restore();
15457
15744
  }
15745
+ /**
15746
+ * Number of latitude segments used by the single blobby octopus mesh.
15747
+ *
15748
+ * @private helper of `octopus3d2AvatarVisual`
15749
+ */
15750
+ const LATITUDE_PATCH_COUNT$2 = 12;
15751
+ /**
15752
+ * Number of longitude segments used by the single blobby octopus mesh.
15753
+ *
15754
+ * @private helper of `octopus3d2AvatarVisual`
15755
+ */
15756
+ const LONGITUDE_PATCH_COUNT$2 = 24;
15458
15757
  /**
15459
15758
  * Resolves all visible projected patches for the single blobby octopus mesh.
15460
15759
  *
15760
+ * Within a single frame, mesh corner samples and longitude-only lobe-wave values are
15761
+ * quantized to the patch grid and computed once each rather than re-evaluated for every
15762
+ * patch corner — the patch loop alone would call `sampleBlobbyOctopusSurfacePoint`
15763
+ * `latitudePatchCount * longitudePatchCount * 4` times without caching, even though most
15764
+ * corners are shared between neighboring patches.
15765
+ *
15461
15766
  * @private helper of `octopus3d2AvatarVisual`
15462
15767
  */
15463
15768
  function resolveVisibleBlobbyOctopusPatches(options) {
15464
15769
  const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, animationPhase, timeMs, } = options;
15465
- const latitudePatchCount = 12;
15466
- const longitudePatchCount = 24;
15770
+ const latitudePatchCount = LATITUDE_PATCH_COUNT$2;
15771
+ const longitudePatchCount = LONGITUDE_PATCH_COUNT$2;
15467
15772
  const surfacePatches = [];
15773
+ const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
15774
+ const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
15775
+ for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
15776
+ latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
15777
+ }
15778
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
15779
+ longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
15780
+ }
15781
+ const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
15782
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
15783
+ cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveLowerLobeWave(longitudeBoundaries[boundaryIndex], morphologyProfile, animationPhase, timeMs);
15784
+ }
15785
+ const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
15786
+ const transformedCornerSamples = new Array(cornerCount);
15787
+ for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
15788
+ const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
15789
+ for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
15790
+ const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
15791
+ const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
15792
+ const cornerSample = sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
15793
+ transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
15794
+ }
15795
+ }
15468
15796
  for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
15469
- const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
15470
- const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
15797
+ const startLatitude = latitudeBoundaries[latitudeIndex];
15798
+ const endLatitude = latitudeBoundaries[latitudeIndex + 1];
15471
15799
  const centerLatitude = (startLatitude + endLatitude) / 2;
15472
15800
  const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
15801
+ const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
15802
+ const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
15473
15803
  for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
15474
- const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
15475
- const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
15804
+ const startLongitude = longitudeBoundaries[longitudeIndex];
15805
+ const endLongitude = longitudeBoundaries[longitudeIndex + 1];
15476
15806
  const centerLongitude = (startLongitude + endLongitude) / 2;
15477
- const localCorners = [
15478
- sampleBlobbyOctopusSurfacePoint(options, startLatitude, startLongitude),
15479
- sampleBlobbyOctopusSurfacePoint(options, startLatitude, endLongitude),
15480
- sampleBlobbyOctopusSurfacePoint(options, endLatitude, endLongitude),
15481
- sampleBlobbyOctopusSurfacePoint(options, endLatitude, startLongitude),
15807
+ const transformedCorners = [
15808
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex],
15809
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
15810
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
15811
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex],
15482
15812
  ];
15483
- const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
15484
15813
  const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
15485
15814
  if (surfaceNormal.z <= 0.01) {
15486
15815
  continue;
15487
15816
  }
15488
- const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
15817
+ const projectedCorners = [
15818
+ projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
15819
+ projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
15820
+ projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
15821
+ projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
15822
+ ];
15489
15823
  surfacePatches.push({
15490
15824
  corners: projectedCorners,
15491
- averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
15492
- transformedCorners.length,
15493
- lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
15494
- fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
15825
+ averageDepth: (transformedCorners[0].z +
15826
+ transformedCorners[1].z +
15827
+ transformedCorners[2].z +
15828
+ transformedCorners[3].z) /
15829
+ 4,
15830
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
15831
+ fillStyle: resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
15495
15832
  outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
15496
15833
  });
15497
15834
  }
@@ -15507,12 +15844,21 @@ function resolveVisibleBlobbyOctopusPatches(options) {
15507
15844
  * @private helper of `octopus3d2AvatarVisual`
15508
15845
  */
15509
15846
  function sampleBlobbyOctopusSurfacePoint(options, latitude, longitude) {
15847
+ const { morphologyProfile, animationPhase, timeMs } = options;
15848
+ return sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs));
15849
+ }
15850
+ /**
15851
+ * Samples one point on the continuous Octopus 3D 2 surface using a precomputed lower-lobe wave
15852
+ * to skip the per-call trig evaluation for `latitudePatchCount + 1` longitude-shared corners.
15853
+ *
15854
+ * @private helper of `octopus3d2AvatarVisual`
15855
+ */
15856
+ function sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, lowerLobeWave) {
15510
15857
  const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
15511
15858
  const cosineLatitude = Math.max(0, Math.cos(latitude));
15512
15859
  const verticalProgress = (Math.sin(latitude) + 1) / 2;
15513
15860
  const upperBlend = Math.pow(1 - verticalProgress, 1.2);
15514
15861
  const lowerBlend = Math.pow(verticalProgress, 1.42);
15515
- const lowerLobeWave = resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs);
15516
15862
  const skirtEnvelope = Math.pow(cosineLatitude, 0.5) * lowerBlend;
15517
15863
  const horizontalScale = 1.02 +
15518
15864
  skirtEnvelope * (0.34 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.22 + lowerLobeWave * 0.22) -
@@ -15550,7 +15896,7 @@ function resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, time
15550
15896
  *
15551
15897
  * @private helper of `octopus3d2AvatarVisual`
15552
15898
  */
15553
- function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, lowerLobeWave) {
15899
+ function resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, forwardness, lowerLobeWave) {
15554
15900
  const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.12 - forwardness * 0.07, 0, 1);
15555
15901
  if (tonalProgress < 0.16) {
15556
15902
  return palette.highlight;
@@ -15590,6 +15936,1384 @@ function drawBlobbySurfacePatch(context, surfacePatch) {
15590
15936
  context.restore();
15591
15937
  }
15592
15938
 
15939
+ /* eslint-disable no-magic-numbers */
15940
+ /**
15941
+ * Light direction used by the continuous octopus mesh shading.
15942
+ *
15943
+ * @private helper of `octopus3d3AvatarVisual`
15944
+ */
15945
+ const LIGHT_DIRECTION$1 = normalizeVector3({
15946
+ x: 0.34,
15947
+ y: -0.62,
15948
+ z: 1,
15949
+ });
15950
+ /**
15951
+ * Real-octopus tentacle count used by the continuous lower mesh.
15952
+ *
15953
+ * @private helper of `octopus3d3AvatarVisual`
15954
+ */
15955
+ const OCTOPUS_TENTACLE_COUNT$1 = 8;
15956
+ /**
15957
+ * Cache keyed by the `createRandom` factory reference, which is stable for the lifetime of one
15958
+ * mounted `<Avatar/>` component (created inside `resolveAvatarRenderDefinition` and held in a
15959
+ * React `useMemo`). Using a `WeakMap` ensures the entry is collected when the component unmounts.
15960
+ *
15961
+ * @private helper of `octopus3d3AvatarVisual`
15962
+ */
15963
+ const stableStateCache = new WeakMap();
15964
+ /**
15965
+ * Returns the stable per-avatar state, computing it on first access and returning the cached
15966
+ * result on every subsequent call within the same `<Avatar/>` mount.
15967
+ *
15968
+ * @private helper of `octopus3d3AvatarVisual`
15969
+ */
15970
+ function getOctopus3d3StableState(createRandom) {
15971
+ const cached = stableStateCache.get(createRandom);
15972
+ if (cached !== undefined) {
15973
+ return cached;
15974
+ }
15975
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
15976
+ const animationRandom = createRandom('octopus3d3-animation-profile');
15977
+ const eyeRandom = createRandom('octopus3d3-eye-profile');
15978
+ const leftEyePhaseOffset = eyeRandom() * 0.7;
15979
+ const rightEyePhaseOffset = eyeRandom() * 0.7;
15980
+ const state = {
15981
+ morphologyProfile,
15982
+ animationPhase: animationRandom() * Math.PI * 2,
15983
+ leftEyePhaseOffset,
15984
+ rightEyePhaseOffset,
15985
+ tentacleProfiles: createContinuousTentacleProfiles(createRandom, morphologyProfile),
15986
+ };
15987
+ stableStateCache.set(createRandom, state);
15988
+ return state;
15989
+ }
15990
+ /**
15991
+ * Octopus 3D 3 avatar visual.
15992
+ *
15993
+ * @private built-in avatar visual
15994
+ */
15995
+ const octopus3d3AvatarVisual = {
15996
+ id: 'octopus3d3',
15997
+ title: 'Octopus 3D 3',
15998
+ description: 'Cute continuous 3D octopus with a blobby single mesh, waving tentacle lobes, rich shading, and cursor-aware eyes.',
15999
+ isAnimated: true,
16000
+ supportsPointerTracking: true,
16001
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
16002
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles } = getOctopus3d3StableState(createRandom);
16003
+ const sceneCenterX = size * 0.5;
16004
+ const sceneCenterY = size * 0.535;
16005
+ const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
16006
+ const meshCenter = {
16007
+ x: interaction.bodyOffsetX * size * 0.048 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
16008
+ y: -size * 0.07 + interaction.bodyOffsetY * size * 0.026 + bob,
16009
+ z: interaction.intensity * size * 0.018,
16010
+ };
16011
+ const rotationY = -0.1 +
16012
+ Math.sin(timeMs / 2700 + animationPhase) * 0.035 +
16013
+ interaction.bodyOffsetX * 0.22 +
16014
+ interaction.gazeX * 0.88;
16015
+ const rotationX = -0.07 +
16016
+ Math.cos(timeMs / 3100 + animationPhase * 0.7) * 0.018 -
16017
+ interaction.bodyOffsetY * 0.08 -
16018
+ interaction.gazeY * 0.38;
16019
+ const surfaceOptions = {
16020
+ radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.1,
16021
+ radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.08,
16022
+ radiusZ: size *
16023
+ morphologyProfile.body.bodyRadiusRatio *
16024
+ (1.02 + (morphologyProfile.body.horizontalStretch - 1) * 0.18),
16025
+ morphologyProfile,
16026
+ timeMs,
16027
+ animationPhase,
16028
+ tentacleProfiles,
16029
+ };
16030
+ const surfacePatches = resolveVisibleContinuousOctopusPatches({
16031
+ ...surfaceOptions,
16032
+ center: meshCenter,
16033
+ rotationX,
16034
+ rotationY,
16035
+ sceneCenterX,
16036
+ sceneCenterY,
16037
+ size,
16038
+ palette,
16039
+ });
16040
+ const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.2 - 0.03, -0.22, 0.08);
16041
+ const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.1, 0.18, 0.32);
16042
+ const mouthLatitude = clampNumber$1(eyeLatitude + 0.2 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
16043
+ const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
16044
+ const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
16045
+ const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
16046
+ const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.76;
16047
+ const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.9;
16048
+ drawAvatarFrame(context, size, palette);
16049
+ drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
16050
+ drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
16051
+ for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
16052
+ drawContinuousSurfacePatch(context, surfacePatch);
16053
+ }
16054
+ drawProjectedSurfaceCurrents({
16055
+ context,
16056
+ surfaceOptions,
16057
+ center: meshCenter,
16058
+ rotationX,
16059
+ rotationY,
16060
+ sceneCenterX,
16061
+ sceneCenterY,
16062
+ size,
16063
+ palette,
16064
+ morphologyProfile,
16065
+ timeMs,
16066
+ animationPhase,
16067
+ });
16068
+ drawProjectedTentacleSuckers({
16069
+ context,
16070
+ surfaceOptions,
16071
+ center: meshCenter,
16072
+ rotationX,
16073
+ rotationY,
16074
+ sceneCenterX,
16075
+ sceneCenterY,
16076
+ size,
16077
+ palette,
16078
+ });
16079
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
16080
+ drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
16081
+ drawProjectedOrganicMouth(context, [
16082
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
16083
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
16084
+ sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
16085
+ ], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
16086
+ },
16087
+ };
16088
+ /**
16089
+ * Creates seeded tentacle-lobe profiles around the visible lower octopus body.
16090
+ *
16091
+ * @private helper of `octopus3d3AvatarVisual`
16092
+ */
16093
+ function createContinuousTentacleProfiles(createRandom, morphologyProfile) {
16094
+ return Array.from({ length: OCTOPUS_TENTACLE_COUNT$1 }, (_, tentacleIndex) => {
16095
+ const tentacleRandom = createRandom(`octopus3d3-tentacle-${tentacleIndex}`);
16096
+ const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT$1 - 1);
16097
+ return {
16098
+ centerLongitude: -Math.PI * 0.86 +
16099
+ progress * Math.PI * 1.72 +
16100
+ (tentacleRandom() - 0.5) * (0.08 + morphologyProfile.tentacles.rootSpreadScale * 0.03),
16101
+ widthScale: 0.86 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.16,
16102
+ lengthScale: 0.86 + tentacleRandom() * 0.36 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.22,
16103
+ swayScale: 0.82 + tentacleRandom() * 0.38 + (morphologyProfile.tentacles.swayScale - 1) * 0.2,
16104
+ depthScale: 0.86 + tentacleRandom() * 0.32 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.2,
16105
+ phase: tentacleRandom() * Math.PI * 2,
16106
+ suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
16107
+ };
16108
+ });
16109
+ }
16110
+ /**
16111
+ * Draws the soft underwater atmosphere behind the continuous octopus mesh.
16112
+ *
16113
+ * @private helper of `octopus3d3AvatarVisual`
16114
+ */
16115
+ function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
16116
+ 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));
16117
+ glowGradient.addColorStop(0, `${palette.highlight}66`);
16118
+ glowGradient.addColorStop(0.34, `${palette.accent}2e`);
16119
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
16120
+ context.fillStyle = glowGradient;
16121
+ context.fillRect(0, 0, size, size);
16122
+ 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);
16123
+ lowerGradient.addColorStop(0, `${palette.secondary}25`);
16124
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
16125
+ context.fillStyle = lowerGradient;
16126
+ context.fillRect(0, 0, size, size);
16127
+ }
16128
+ /**
16129
+ * Draws the soft lower shadow that anchors the octopus in the avatar frame.
16130
+ *
16131
+ * Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
16132
+ * blurry ellipse without triggering a costly software rasterization pass on every frame.
16133
+ *
16134
+ * @private helper of `octopus3d3AvatarVisual`
16135
+ */
16136
+ function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
16137
+ const cx = size * 0.5 + interaction.gazeX * size * 0.045;
16138
+ const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
16139
+ const rx = size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02);
16140
+ const ry = size * 0.06;
16141
+ // Scale the context so that drawing a circle produces the correct ellipse aspect ratio,
16142
+ // then fill with a radial gradient that approximates the blurry edge without context.filter.
16143
+ context.save();
16144
+ context.translate(cx, cy);
16145
+ context.scale(1, ry / rx);
16146
+ const blurRadius = rx * 1.4;
16147
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
16148
+ shadowGradient.addColorStop(0, `${palette.shadow}7a`);
16149
+ shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
16150
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
16151
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
16152
+ context.fillStyle = shadowGradient;
16153
+ context.beginPath();
16154
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
16155
+ context.fill();
16156
+ context.restore();
16157
+ }
16158
+ /**
16159
+ * Number of latitude segments used by the continuous Octopus 3D 3 mesh.
16160
+ *
16161
+ * @private helper of `octopus3d3AvatarVisual`
16162
+ */
16163
+ const LATITUDE_PATCH_COUNT$1 = 16;
16164
+ /**
16165
+ * Number of longitude segments used by the continuous Octopus 3D 3 mesh.
16166
+ *
16167
+ * @private helper of `octopus3d3AvatarVisual`
16168
+ */
16169
+ const LONGITUDE_PATCH_COUNT$1 = 40;
16170
+ /**
16171
+ * Resolves visible projected patches for the continuous octopus mesh.
16172
+ *
16173
+ * Within a single frame, mesh corner samples and longitude-only computations (tentacle
16174
+ * influence and lobe wave) are quantized to the patch grid and computed once each rather
16175
+ * than re-evaluated for every patch corner — the patch loop alone calls `sampleContinuousOctopusSurfacePoint`
16176
+ * for `latitudePatchCount * longitudePatchCount * 4` corners without caching, and each call
16177
+ * triggers an inner 8-iteration `Math.exp` loop, which dominates the per-frame cost.
16178
+ *
16179
+ * @private helper of `octopus3d3AvatarVisual`
16180
+ */
16181
+ function resolveVisibleContinuousOctopusPatches(options) {
16182
+ const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
16183
+ const latitudePatchCount = LATITUDE_PATCH_COUNT$1;
16184
+ const longitudePatchCount = LONGITUDE_PATCH_COUNT$1;
16185
+ const surfacePatches = [];
16186
+ const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
16187
+ const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
16188
+ for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
16189
+ latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
16190
+ }
16191
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
16192
+ longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
16193
+ }
16194
+ const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
16195
+ const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
16196
+ const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
16197
+ const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
16198
+ const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
16199
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
16200
+ const cornerLongitude = longitudeBoundaries[boundaryIndex];
16201
+ cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveContinuousTentacleInfluence(options, cornerLongitude);
16202
+ cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveContinuousLobeWave(options, cornerLongitude);
16203
+ }
16204
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
16205
+ const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
16206
+ cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveContinuousTentacleInfluence(options, patchCenterLongitude);
16207
+ cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveContinuousLobeWave(options, patchCenterLongitude);
16208
+ cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
16209
+ }
16210
+ const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
16211
+ const transformedCornerSamples = new Array(cornerCount);
16212
+ for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
16213
+ const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
16214
+ for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
16215
+ const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
16216
+ const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
16217
+ const cornerSample = sampleContinuousOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
16218
+ transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
16219
+ }
16220
+ }
16221
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
16222
+ const startLatitude = latitudeBoundaries[latitudeIndex];
16223
+ const endLatitude = latitudeBoundaries[latitudeIndex + 1];
16224
+ const centerLatitude = (startLatitude + endLatitude) / 2;
16225
+ const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
16226
+ const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
16227
+ const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
16228
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
16229
+ const transformedCorners = [
16230
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex],
16231
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
16232
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
16233
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex],
16234
+ ];
16235
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
16236
+ if (surfaceNormal.z <= 0.008) {
16237
+ continue;
16238
+ }
16239
+ const projectedCorners = [
16240
+ projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
16241
+ projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
16242
+ projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
16243
+ projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
16244
+ ];
16245
+ surfacePatches.push({
16246
+ corners: projectedCorners,
16247
+ averageDepth: (transformedCorners[0].z +
16248
+ transformedCorners[1].z +
16249
+ transformedCorners[2].z +
16250
+ transformedCorners[3].z) /
16251
+ 4,
16252
+ lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
16253
+ fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
16254
+ outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
16255
+ });
16256
+ }
16257
+ }
16258
+ return surfacePatches;
16259
+ }
16260
+ /**
16261
+ * Samples one point on the continuous Octopus 3D 3 surface.
16262
+ *
16263
+ * The lower hemisphere is pulled into eight seeded waving lobes, so the portrait reads as
16264
+ * tentacled while still being rendered as one connected blobby mesh.
16265
+ *
16266
+ * @private helper of `octopus3d3AvatarVisual`
16267
+ */
16268
+ function sampleContinuousOctopusSurfacePoint(options, latitude, longitude) {
16269
+ return sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveContinuousTentacleInfluence(options, longitude), resolveContinuousLobeWave(options, longitude));
16270
+ }
16271
+ /**
16272
+ * Samples one point on the continuous Octopus 3D 3 surface using precomputed longitude-only
16273
+ * values to skip the per-call `Math.exp` tentacle-influence loop and the lobe-wave trig call.
16274
+ *
16275
+ * The patch loop quantizes the mesh into a fixed `(latitudePatchCount + 1) * (longitudePatchCount + 1)`
16276
+ * corner grid, so the same longitude is reused across every latitude row and each
16277
+ * tentacle/lobe value can be computed once per frame instead of `latitudePatchCount * 4` times.
16278
+ *
16279
+ * @private helper of `octopus3d3AvatarVisual`
16280
+ */
16281
+ function sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
16282
+ const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
16283
+ const cosineLatitude = Math.max(0, Math.cos(latitude));
16284
+ const verticalProgress = (Math.sin(latitude) + 1) / 2;
16285
+ const upperBlend = Math.pow(1 - verticalProgress, 1.28);
16286
+ const lowerBlend = smoothStep$1(0.38, 1, verticalProgress);
16287
+ const tipBlend = smoothStep$1(0.68, 1, verticalProgress);
16288
+ const centerPull = resolveSignedAngularDistance$1(longitude, tentacleInfluence.centerLongitude);
16289
+ const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
16290
+ const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
16291
+ animationPhase * 0.6 +
16292
+ timeMs / (1750 + morphologyProfile.body.lobeCount * 30)) *
16293
+ (0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.8) *
16294
+ (0.3 + lowerBlend * 0.7);
16295
+ const tentacleWave = Math.sin(timeMs / 760 + tentacleInfluence.phase + verticalProgress * 2.4) *
16296
+ lowerBlend *
16297
+ tentacleInfluence.core *
16298
+ tentacleInfluence.swayScale;
16299
+ const horizontalScale = 1.04 +
16300
+ mantleRipple +
16301
+ lowerBlend * (0.16 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.1) +
16302
+ lowerBlend * tentacleInfluence.core * (0.2 + lowerLobeWave * 0.12) -
16303
+ upperBlend * 0.08;
16304
+ const depthScale = 1.06 +
16305
+ upperBlend * 0.16 +
16306
+ Math.max(0, Math.cos(effectiveLongitude)) * 0.1 +
16307
+ lowerBlend * tentacleInfluence.core * (0.1 + tentacleInfluence.depthScale * 0.06) -
16308
+ Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
16309
+ const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.11 + tipBlend * 0.06 + tentacleInfluence.widthScale * 0.025) * radiusX;
16310
+ const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
16311
+ const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.72;
16312
+ const lowerDrop = lowerBlend *
16313
+ radiusY *
16314
+ (0.18 +
16315
+ tentacleInfluence.core *
16316
+ (0.38 +
16317
+ tentacleInfluence.lengthScale * 0.22 +
16318
+ (morphologyProfile.tentacles.flowLengthScale - 1) * 0.08));
16319
+ return {
16320
+ x: Math.sin(effectiveLongitude) * planarRadiusX + tentacleWave * radiusX * (0.052 + tipBlend * 0.05),
16321
+ y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.12) -
16322
+ upperBlend * radiusY * 0.1 +
16323
+ lowerDrop +
16324
+ Math.sin(timeMs / 1420 + animationPhase + latitude * 1.6) * lowerBlend * radiusY * 0.018 +
16325
+ Math.cos(timeMs / 880 + tentacleInfluence.phase) *
16326
+ lowerBlend *
16327
+ tipBlend *
16328
+ tentacleInfluence.core *
16329
+ radiusY *
16330
+ 0.034,
16331
+ z: Math.cos(effectiveLongitude) * planarRadiusZ +
16332
+ Math.cos(timeMs / 980 + tentacleInfluence.phase + verticalProgress) *
16333
+ lowerBlend *
16334
+ tentacleInfluence.core *
16335
+ radiusZ *
16336
+ 0.04,
16337
+ };
16338
+ }
16339
+ /**
16340
+ * Blends nearby seeded tentacle profiles at one mesh longitude.
16341
+ *
16342
+ * @private helper of `octopus3d3AvatarVisual`
16343
+ */
16344
+ function resolveContinuousTentacleInfluence(options, longitude) {
16345
+ let totalWeight = 0;
16346
+ let weightedSin = 0;
16347
+ let weightedCos = 0;
16348
+ let weightedWidthScale = 0;
16349
+ let weightedLengthScale = 0;
16350
+ let weightedSwayScale = 0;
16351
+ let weightedDepthScale = 0;
16352
+ let weightedPhase = 0;
16353
+ for (const tentacleProfile of options.tentacleProfiles) {
16354
+ const distance = Math.abs(resolveSignedAngularDistance$1(longitude, tentacleProfile.centerLongitude));
16355
+ const width = 0.2 * tentacleProfile.widthScale;
16356
+ const weight = Math.exp(-(distance * distance) / (width * width));
16357
+ totalWeight += weight;
16358
+ weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
16359
+ weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
16360
+ weightedWidthScale += tentacleProfile.widthScale * weight;
16361
+ weightedLengthScale += tentacleProfile.lengthScale * weight;
16362
+ weightedSwayScale += tentacleProfile.swayScale * weight;
16363
+ weightedDepthScale += tentacleProfile.depthScale * weight;
16364
+ weightedPhase += tentacleProfile.phase * weight;
16365
+ }
16366
+ if (totalWeight < 0.0001) {
16367
+ return {
16368
+ core: 0,
16369
+ centerLongitude: longitude,
16370
+ widthScale: 1,
16371
+ lengthScale: 1,
16372
+ swayScale: 1,
16373
+ depthScale: 1,
16374
+ phase: 0,
16375
+ };
16376
+ }
16377
+ return {
16378
+ core: clampNumber$1(totalWeight, 0, 1),
16379
+ centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
16380
+ widthScale: weightedWidthScale / totalWeight,
16381
+ lengthScale: weightedLengthScale / totalWeight,
16382
+ swayScale: weightedSwayScale / totalWeight,
16383
+ depthScale: weightedDepthScale / totalWeight,
16384
+ phase: weightedPhase / totalWeight,
16385
+ };
16386
+ }
16387
+ /**
16388
+ * Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
16389
+ *
16390
+ * @private helper of `octopus3d3AvatarVisual`
16391
+ */
16392
+ function resolveContinuousLobeWave(options, longitude) {
16393
+ const { morphologyProfile, animationPhase, timeMs } = options;
16394
+ return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT$1 +
16395
+ animationPhase +
16396
+ timeMs / (980 + morphologyProfile.body.lobeCount * 18)) +
16397
+ 1) /
16398
+ 2);
16399
+ }
16400
+ /**
16401
+ * Resolves one base fill tone for a patch on the continuous octopus mesh.
16402
+ *
16403
+ * @private helper of `octopus3d3AvatarVisual`
16404
+ */
16405
+ function resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
16406
+ const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
16407
+ if (tonalProgress < 0.14) {
16408
+ return palette.highlight;
16409
+ }
16410
+ if (tonalProgress < 0.32) {
16411
+ return palette.secondary;
16412
+ }
16413
+ if (tonalProgress < 0.72) {
16414
+ return forwardness > 0.55 ? palette.secondary : palette.primary;
16415
+ }
16416
+ return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
16417
+ }
16418
+ /**
16419
+ * Draws one projected mesh patch with soft shading and a subtle edge.
16420
+ *
16421
+ * @private helper of `octopus3d3AvatarVisual`
16422
+ */
16423
+ function drawContinuousSurfacePatch(context, surfacePatch) {
16424
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
16425
+ if (surfacePatch.lightIntensity > 0) {
16426
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.18 * surfacePatch.lightIntensity})`);
16427
+ }
16428
+ else if (surfacePatch.lightIntensity < 0) {
16429
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.25 * Math.abs(surfacePatch.lightIntensity)})`);
16430
+ }
16431
+ context.save();
16432
+ context.beginPath();
16433
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
16434
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
16435
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
16436
+ }
16437
+ context.closePath();
16438
+ context.strokeStyle = surfacePatch.outlineColor;
16439
+ context.lineWidth = Math.max(0.7, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0032);
16440
+ context.lineJoin = 'round';
16441
+ context.stroke();
16442
+ context.restore();
16443
+ }
16444
+ /**
16445
+ * Draws projected mantle-current lines on the front of the mesh.
16446
+ *
16447
+ * @private helper of `octopus3d3AvatarVisual`
16448
+ */
16449
+ function drawProjectedSurfaceCurrents(options) {
16450
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
16451
+ const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
16452
+ const centerIndex = (currentCount - 1) / 2;
16453
+ context.save();
16454
+ context.lineCap = 'round';
16455
+ context.lineJoin = 'round';
16456
+ for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
16457
+ const baseLongitude = (currentIndex - centerIndex) * 0.15;
16458
+ const projectedPoints = [];
16459
+ for (let sampleIndex = 0; sampleIndex < 8; sampleIndex++) {
16460
+ const progress = sampleIndex / 7;
16461
+ const latitude = -0.46 + progress * 0.74;
16462
+ const longitude = baseLongitude + Math.sin(timeMs / 1160 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.035;
16463
+ const scenePoint = transformScenePoint(sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
16464
+ if (scenePoint.z > center.z - size * 0.016) {
16465
+ projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
16466
+ }
16467
+ }
16468
+ if (projectedPoints.length < 3) {
16469
+ continue;
16470
+ }
16471
+ context.beginPath();
16472
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
16473
+ for (const projectedPoint of projectedPoints.slice(1)) {
16474
+ context.lineTo(projectedPoint.x, projectedPoint.y);
16475
+ }
16476
+ context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
16477
+ context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
16478
+ context.stroke();
16479
+ }
16480
+ context.restore();
16481
+ }
16482
+ /**
16483
+ * Draws small projected sucker highlights on the waving lower mesh lobes.
16484
+ *
16485
+ * @private helper of `octopus3d3AvatarVisual`
16486
+ */
16487
+ function drawProjectedTentacleSuckers(options) {
16488
+ const { surfaceOptions, size } = options;
16489
+ const { timeMs } = surfaceOptions;
16490
+ for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
16491
+ if (Math.cos(tentacleProfile.centerLongitude) < -0.12) {
16492
+ continue;
16493
+ }
16494
+ for (let suckerIndex = 0; suckerIndex < 3; suckerIndex++) {
16495
+ const latitude = 0.52 + suckerIndex * 0.14;
16496
+ const sideOffset = tentacleProfile.suckerSide * (0.035 + suckerIndex * 0.012) * tentacleProfile.widthScale;
16497
+ const waveOffset = Math.sin(timeMs / 900 + tentacleProfile.phase + suckerIndex * 0.8) * 0.018;
16498
+ drawProjectedSurfaceSpot({
16499
+ ...options,
16500
+ latitude,
16501
+ longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
16502
+ radiusScale: size * (0.0065 - suckerIndex * 0.0007),
16503
+ });
16504
+ }
16505
+ }
16506
+ }
16507
+ /**
16508
+ * Draws one tiny projected surface spot by sampling local mesh tangents.
16509
+ *
16510
+ * @private helper of `octopus3d3AvatarVisual`
16511
+ */
16512
+ function drawProjectedSurfaceSpot(options) {
16513
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
16514
+ const localCenter = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude);
16515
+ const localHorizontal = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
16516
+ const localVertical = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
16517
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
16518
+ if (sceneCenterPoint.z <= center.z - size * 0.012) {
16519
+ return;
16520
+ }
16521
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
16522
+ const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
16523
+ const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
16524
+ const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
16525
+ radiusScale *
16526
+ 0.74, size * 0.003, size * 0.018);
16527
+ const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
16528
+ radiusScale *
16529
+ 0.52, size * 0.0024, size * 0.014);
16530
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
16531
+ context.save();
16532
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
16533
+ context.rotate(rotation);
16534
+ context.beginPath();
16535
+ context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
16536
+ context.fillStyle = `${palette.highlight}73`;
16537
+ context.fill();
16538
+ context.strokeStyle = `${palette.highlight}99`;
16539
+ context.lineWidth = Math.max(0.7, size * 0.0028);
16540
+ context.stroke();
16541
+ context.restore();
16542
+ }
16543
+ /**
16544
+ * Resolves a signed angular distance from the source longitude to the target longitude.
16545
+ *
16546
+ * @private helper of `octopus3d3AvatarVisual`
16547
+ */
16548
+ function resolveSignedAngularDistance$1(sourceLongitude, targetLongitude) {
16549
+ return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
16550
+ }
16551
+ /**
16552
+ * Smoothly maps a value between two bounds into `[0, 1]`.
16553
+ *
16554
+ * @private helper of `octopus3d3AvatarVisual`
16555
+ */
16556
+ function smoothStep$1(edgeStart, edgeEnd, value) {
16557
+ const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
16558
+ return progress * progress * (3 - 2 * progress);
16559
+ }
16560
+
16561
+ /* eslint-disable no-magic-numbers */
16562
+ /**
16563
+ * Light direction used by the continuous Octopus 3D 4 mesh shading.
16564
+ *
16565
+ * @private helper of `octopus3d4AvatarVisual`
16566
+ */
16567
+ const LIGHT_DIRECTION = normalizeVector3({
16568
+ x: 0.32,
16569
+ y: -0.66,
16570
+ z: 1,
16571
+ });
16572
+ /**
16573
+ * Rim-light direction used to brighten the side of the silhouette.
16574
+ *
16575
+ * @private helper of `octopus3d4AvatarVisual`
16576
+ */
16577
+ const RIM_LIGHT_DIRECTION = normalizeVector3({
16578
+ x: -0.7,
16579
+ y: -0.18,
16580
+ z: 0.55,
16581
+ });
16582
+ /**
16583
+ * Real-octopus tentacle count used by the continuous lower mesh.
16584
+ *
16585
+ * @private helper of `octopus3d4AvatarVisual`
16586
+ */
16587
+ const OCTOPUS_TENTACLE_COUNT = 8;
16588
+ /**
16589
+ * Number of seeded skin spots painted across the octopus body.
16590
+ *
16591
+ * @private helper of `octopus3d4AvatarVisual`
16592
+ */
16593
+ const SKIN_SPOT_COUNT = 14;
16594
+ /**
16595
+ * Cache keyed by the `createRandom` factory reference, stable per mounted `<Avatar/>` component.
16596
+ *
16597
+ * @private helper of `octopus3d4AvatarVisual`
16598
+ */
16599
+ const octopus3d4StableStateCache = new WeakMap();
16600
+ /**
16601
+ * Returns the stable per-avatar state, computing it on first access and caching afterwards.
16602
+ *
16603
+ * @private helper of `octopus3d4AvatarVisual`
16604
+ */
16605
+ function getOctopus3d4StableState(createRandom) {
16606
+ const cached = octopus3d4StableStateCache.get(createRandom);
16607
+ if (cached !== undefined) {
16608
+ return cached;
16609
+ }
16610
+ const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
16611
+ const animationRandom = createRandom('octopus3d4-animation-profile');
16612
+ const eyeRandom = createRandom('octopus3d4-eye-profile');
16613
+ const leftEyePhaseOffset = eyeRandom() * 0.7;
16614
+ const rightEyePhaseOffset = eyeRandom() * 0.7;
16615
+ const state = {
16616
+ morphologyProfile,
16617
+ animationPhase: animationRandom() * Math.PI * 2,
16618
+ leftEyePhaseOffset,
16619
+ rightEyePhaseOffset,
16620
+ tentacleProfiles: createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile),
16621
+ skinSpots: createBlobbySkinSpots(createRandom),
16622
+ };
16623
+ octopus3d4StableStateCache.set(createRandom, state);
16624
+ return state;
16625
+ }
16626
+ /**
16627
+ * Octopus 3D 4 avatar visual.
16628
+ *
16629
+ * @private built-in avatar visual
16630
+ */
16631
+ const octopus3d4AvatarVisual = {
16632
+ id: 'octopus3d4',
16633
+ title: 'Octopus 3D 4',
16634
+ description: 'Cute blobby 3D octopus rendered as one smooth continuous mesh with naturally undulating tentacles, rich multi-pass shading, pigment spots, and cursor-aware head and eyes.',
16635
+ isAnimated: true,
16636
+ supportsPointerTracking: true,
16637
+ render({ context, size, palette, createRandom, timeMs, interaction }) {
16638
+ const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots } = getOctopus3d4StableState(createRandom);
16639
+ const sceneCenterX = size * 0.5;
16640
+ const sceneCenterY = size * 0.535;
16641
+ const bob = Math.sin(timeMs / 980 + animationPhase) * size * 0.013;
16642
+ const meshCenter = {
16643
+ x: interaction.bodyOffsetX * size * 0.052 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
16644
+ y: -size * 0.07 + interaction.bodyOffsetY * size * 0.028 + bob,
16645
+ z: interaction.intensity * size * 0.02,
16646
+ };
16647
+ const rotationY = -0.08 +
16648
+ Math.sin(timeMs / 2800 + animationPhase) * 0.04 +
16649
+ interaction.bodyOffsetX * 0.24 +
16650
+ interaction.gazeX * 0.98;
16651
+ const rotationX = -0.07 +
16652
+ Math.cos(timeMs / 3200 + animationPhase * 0.7) * 0.02 -
16653
+ interaction.bodyOffsetY * 0.08 -
16654
+ interaction.gazeY * 0.42;
16655
+ const surfaceOptions = {
16656
+ radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.12,
16657
+ radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1,
16658
+ radiusZ: size *
16659
+ morphologyProfile.body.bodyRadiusRatio *
16660
+ (1.04 + (morphologyProfile.body.horizontalStretch - 1) * 0.2),
16661
+ morphologyProfile,
16662
+ timeMs,
16663
+ animationPhase,
16664
+ tentacleProfiles,
16665
+ };
16666
+ const surfacePatches = resolveVisibleBlobbyContinuousPatches({
16667
+ ...surfaceOptions,
16668
+ center: meshCenter,
16669
+ rotationX,
16670
+ rotationY,
16671
+ sceneCenterX,
16672
+ sceneCenterY,
16673
+ size,
16674
+ palette,
16675
+ });
16676
+ const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.4 - 0.04, -0.24, 0.08);
16677
+ const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.0, 0.18, 0.32);
16678
+ const mouthLatitude = clampNumber$1(eyeLatitude + 0.21 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
16679
+ const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
16680
+ const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
16681
+ const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
16682
+ const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
16683
+ const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
16684
+ drawAvatarFrame(context, size, palette);
16685
+ drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
16686
+ drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile);
16687
+ for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
16688
+ drawBlobbyContinuousSurfacePatch(context, surfacePatch);
16689
+ }
16690
+ drawBlobbySkinSpots({
16691
+ context,
16692
+ surfaceOptions,
16693
+ center: meshCenter,
16694
+ rotationX,
16695
+ rotationY,
16696
+ sceneCenterX,
16697
+ sceneCenterY,
16698
+ size,
16699
+ palette,
16700
+ skinSpots,
16701
+ });
16702
+ drawBlobbyContinuousCurrents({
16703
+ context,
16704
+ surfaceOptions,
16705
+ center: meshCenter,
16706
+ rotationX,
16707
+ rotationY,
16708
+ sceneCenterX,
16709
+ sceneCenterY,
16710
+ size,
16711
+ palette,
16712
+ morphologyProfile,
16713
+ timeMs,
16714
+ animationPhase,
16715
+ });
16716
+ drawBlobbyContinuousSuckers({
16717
+ context,
16718
+ surfaceOptions,
16719
+ center: meshCenter,
16720
+ rotationX,
16721
+ rotationY,
16722
+ sceneCenterX,
16723
+ sceneCenterY,
16724
+ size,
16725
+ palette,
16726
+ });
16727
+ drawBlobbyContinuousGloss({
16728
+ context,
16729
+ surfaceOptions,
16730
+ center: meshCenter,
16731
+ rotationX,
16732
+ rotationY,
16733
+ sceneCenterX,
16734
+ sceneCenterY,
16735
+ size,
16736
+ palette,
16737
+ });
16738
+ drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
16739
+ drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
16740
+ drawProjectedOrganicMouth(context, [
16741
+ sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
16742
+ sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
16743
+ sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
16744
+ ], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
16745
+ },
16746
+ };
16747
+ /**
16748
+ * Creates seeded tentacle-lobe profiles around the visible lower octopus body.
16749
+ *
16750
+ * @private helper of `octopus3d4AvatarVisual`
16751
+ */
16752
+ function createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile) {
16753
+ return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
16754
+ const tentacleRandom = createRandom(`octopus3d4-tentacle-${tentacleIndex}`);
16755
+ const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
16756
+ return {
16757
+ centerLongitude: -Math.PI * 0.9 +
16758
+ progress * Math.PI * 1.8 +
16759
+ (tentacleRandom() - 0.5) * (0.06 + morphologyProfile.tentacles.rootSpreadScale * 0.025),
16760
+ widthScale: 0.92 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.18,
16761
+ lengthScale: 0.9 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.24,
16762
+ swayScale: 0.86 + tentacleRandom() * 0.4 + (morphologyProfile.tentacles.swayScale - 1) * 0.22,
16763
+ depthScale: 0.9 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.22,
16764
+ curlScale: 0.62 + tentacleRandom() * 0.46,
16765
+ primaryPhase: tentacleRandom() * Math.PI * 2,
16766
+ secondaryPhase: tentacleRandom() * Math.PI * 2,
16767
+ suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
16768
+ };
16769
+ });
16770
+ }
16771
+ /**
16772
+ * Creates seeded skin pigment spots distributed across the upper octopus mesh.
16773
+ *
16774
+ * @private helper of `octopus3d4AvatarVisual`
16775
+ */
16776
+ function createBlobbySkinSpots(createRandom) {
16777
+ const spotRandom = createRandom('octopus3d4-skin-spots');
16778
+ return Array.from({ length: SKIN_SPOT_COUNT }, () => ({
16779
+ latitude: -0.45 + spotRandom() * 0.7,
16780
+ longitude: -0.5 + spotRandom() * 1.0,
16781
+ radiusScale: 0.0028 + spotRandom() * 0.0052,
16782
+ opacity: 0.16 + spotRandom() * 0.22,
16783
+ }));
16784
+ }
16785
+ /**
16786
+ * Draws the soft underwater atmosphere behind the continuous octopus mesh.
16787
+ *
16788
+ * @private helper of `octopus3d4AvatarVisual`
16789
+ */
16790
+ function drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
16791
+ const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.12, sceneCenterY - size * 0.18 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY, size * (0.68 + interaction.intensity * 0.025));
16792
+ glowGradient.addColorStop(0, `${palette.highlight}74`);
16793
+ glowGradient.addColorStop(0.32, `${palette.accent}32`);
16794
+ glowGradient.addColorStop(1, `${palette.highlight}00`);
16795
+ context.fillStyle = glowGradient;
16796
+ context.fillRect(0, 0, size, size);
16797
+ const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1500) * size * 0.05, sceneCenterY + size * 0.3, size * 0.06, sceneCenterX, sceneCenterY + size * 0.3, size * 0.56);
16798
+ lowerGradient.addColorStop(0, `${palette.secondary}28`);
16799
+ lowerGradient.addColorStop(1, `${palette.secondary}00`);
16800
+ context.fillStyle = lowerGradient;
16801
+ context.fillRect(0, 0, size, size);
16802
+ }
16803
+ /**
16804
+ * Draws the soft lower shadow that anchors the octopus in the avatar frame.
16805
+ *
16806
+ * @private helper of `octopus3d4AvatarVisual`
16807
+ */
16808
+ function drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
16809
+ const cx = size * 0.5 + interaction.gazeX * size * 0.046;
16810
+ const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
16811
+ const rx = size * (0.2 + morphologyProfile.tentacles.rootSpreadScale * 0.024 + interaction.intensity * 0.022);
16812
+ const ry = size * 0.062;
16813
+ context.save();
16814
+ context.translate(cx, cy);
16815
+ context.scale(1, ry / rx);
16816
+ const blurRadius = rx * 1.42;
16817
+ const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
16818
+ shadowGradient.addColorStop(0, `${palette.shadow}82`);
16819
+ shadowGradient.addColorStop(0.45, `${palette.shadow}4a`);
16820
+ shadowGradient.addColorStop(0.8, `${palette.shadow}1c`);
16821
+ shadowGradient.addColorStop(1, `${palette.shadow}00`);
16822
+ context.fillStyle = shadowGradient;
16823
+ context.beginPath();
16824
+ context.arc(0, 0, blurRadius, 0, Math.PI * 2);
16825
+ context.fill();
16826
+ context.restore();
16827
+ }
16828
+ /**
16829
+ * Number of latitude segments used by the continuous Octopus 3D 4 mesh.
16830
+ *
16831
+ * @private helper of `octopus3d4AvatarVisual`
16832
+ */
16833
+ const LATITUDE_PATCH_COUNT = 20;
16834
+ /**
16835
+ * Number of longitude segments used by the continuous Octopus 3D 4 mesh.
16836
+ *
16837
+ * @private helper of `octopus3d4AvatarVisual`
16838
+ */
16839
+ const LONGITUDE_PATCH_COUNT = 48;
16840
+ /**
16841
+ * Resolves visible projected patches for the continuous Octopus 3D 4 mesh.
16842
+ *
16843
+ * Within a single frame, mesh corner samples and longitude-only computations (tentacle
16844
+ * influence and lobe wave) are quantized to the patch grid and computed once each rather
16845
+ * than re-evaluated for every patch corner.
16846
+ *
16847
+ * @private helper of `octopus3d4AvatarVisual`
16848
+ */
16849
+ function resolveVisibleBlobbyContinuousPatches(options) {
16850
+ const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
16851
+ const latitudePatchCount = LATITUDE_PATCH_COUNT;
16852
+ const longitudePatchCount = LONGITUDE_PATCH_COUNT;
16853
+ const surfacePatches = [];
16854
+ const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
16855
+ const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
16856
+ for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
16857
+ latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
16858
+ }
16859
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
16860
+ longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
16861
+ }
16862
+ const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
16863
+ const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
16864
+ const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
16865
+ const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
16866
+ const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
16867
+ for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
16868
+ const cornerLongitude = longitudeBoundaries[boundaryIndex];
16869
+ cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveBlobbyTentacleInfluence(options, cornerLongitude);
16870
+ cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveBlobbyLobeWave(options, cornerLongitude);
16871
+ }
16872
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
16873
+ const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
16874
+ cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyTentacleInfluence(options, patchCenterLongitude);
16875
+ cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyLobeWave(options, patchCenterLongitude);
16876
+ cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
16877
+ }
16878
+ const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
16879
+ const transformedCornerSamples = new Array(cornerCount);
16880
+ for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
16881
+ const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
16882
+ for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
16883
+ const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
16884
+ const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
16885
+ const cornerSample = sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
16886
+ transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
16887
+ }
16888
+ }
16889
+ for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
16890
+ const startLatitude = latitudeBoundaries[latitudeIndex];
16891
+ const endLatitude = latitudeBoundaries[latitudeIndex + 1];
16892
+ const centerLatitude = (startLatitude + endLatitude) / 2;
16893
+ const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
16894
+ const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
16895
+ const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
16896
+ for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
16897
+ const transformedCorners = [
16898
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex],
16899
+ transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
16900
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
16901
+ transformedCornerSamples[endCornerRowOffset + longitudeIndex],
16902
+ ];
16903
+ const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
16904
+ if (surfaceNormal.z <= 0.006) {
16905
+ continue;
16906
+ }
16907
+ const projectedCorners = [
16908
+ projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
16909
+ projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
16910
+ projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
16911
+ projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
16912
+ ];
16913
+ const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1);
16914
+ const rimLightIntensity = Math.pow(clampNumber$1(dotProduct3D(surfaceNormal, RIM_LIGHT_DIRECTION), 0, 1), 2.6);
16915
+ surfacePatches.push({
16916
+ corners: projectedCorners,
16917
+ averageDepth: (transformedCorners[0].z +
16918
+ transformedCorners[1].z +
16919
+ transformedCorners[2].z +
16920
+ transformedCorners[3].z) /
16921
+ 4,
16922
+ lightIntensity,
16923
+ rimLightIntensity,
16924
+ fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
16925
+ outlineColor: verticalProgress < 0.52 ? `${palette.highlight}5c` : `${palette.shadow}66`,
16926
+ });
16927
+ }
16928
+ }
16929
+ return surfacePatches;
16930
+ }
16931
+ /**
16932
+ * Samples one point on the continuous Octopus 3D 4 surface.
16933
+ *
16934
+ * @private helper of `octopus3d4AvatarVisual`
16935
+ */
16936
+ function sampleBlobbyContinuousSurfacePoint(options, latitude, longitude) {
16937
+ return sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, resolveBlobbyTentacleInfluence(options, longitude), resolveBlobbyLobeWave(options, longitude));
16938
+ }
16939
+ /**
16940
+ * Samples one point on the continuous Octopus 3D 4 surface using precomputed longitude-only values.
16941
+ *
16942
+ * The patch loop quantizes the mesh into a fixed corner grid, so the same longitude is reused
16943
+ * across every latitude row and each tentacle/lobe value is computed once per frame instead
16944
+ * of `latitudePatchCount * 4` times.
16945
+ *
16946
+ * @private helper of `octopus3d4AvatarVisual`
16947
+ */
16948
+ function sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
16949
+ const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
16950
+ const cosineLatitude = Math.max(0, Math.cos(latitude));
16951
+ const verticalProgress = (Math.sin(latitude) + 1) / 2;
16952
+ const upperBlend = Math.pow(1 - verticalProgress, 1.32);
16953
+ const lowerBlend = smoothStep(0.34, 1, verticalProgress);
16954
+ const tipBlend = smoothStep(0.66, 1, verticalProgress);
16955
+ const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
16956
+ const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.26 + tipBlend * 0.22);
16957
+ const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
16958
+ animationPhase * 0.6 +
16959
+ timeMs / (1700 + morphologyProfile.body.lobeCount * 28)) *
16960
+ (0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.78) *
16961
+ (0.32 + lowerBlend * 0.7);
16962
+ const primaryTentacleWave = Math.sin(timeMs / 740 + tentacleInfluence.primaryPhase + verticalProgress * 2.6) *
16963
+ lowerBlend *
16964
+ tentacleInfluence.core *
16965
+ tentacleInfluence.swayScale;
16966
+ const secondaryTentacleWave = Math.sin(timeMs / 470 + tentacleInfluence.secondaryPhase + verticalProgress * 4.2) *
16967
+ lowerBlend *
16968
+ tentacleInfluence.core *
16969
+ tentacleInfluence.swayScale *
16970
+ 0.42;
16971
+ const tentacleCurl = Math.sin(timeMs / 1180 + tentacleInfluence.primaryPhase * 0.6 + verticalProgress * 3.2) *
16972
+ tipBlend *
16973
+ tentacleInfluence.core *
16974
+ tentacleInfluence.curlScale *
16975
+ 0.36;
16976
+ const horizontalScale = 1.06 +
16977
+ mantleRipple +
16978
+ lowerBlend * (0.18 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.12) +
16979
+ lowerBlend * tentacleInfluence.core * (0.22 + lowerLobeWave * 0.14) -
16980
+ upperBlend * 0.08;
16981
+ const depthScale = 1.08 +
16982
+ upperBlend * 0.16 +
16983
+ Math.max(0, Math.cos(effectiveLongitude)) * 0.12 +
16984
+ lowerBlend * tentacleInfluence.core * (0.12 + tentacleInfluence.depthScale * 0.07) -
16985
+ Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
16986
+ const tentacleTubeRadius = lowerBlend *
16987
+ tentacleInfluence.core *
16988
+ (0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) *
16989
+ radiusX;
16990
+ const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
16991
+ const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.74;
16992
+ const lowerDrop = lowerBlend *
16993
+ radiusY *
16994
+ (0.2 +
16995
+ tentacleInfluence.core *
16996
+ (0.42 +
16997
+ tentacleInfluence.lengthScale * 0.24 +
16998
+ (morphologyProfile.tentacles.flowLengthScale - 1) * 0.1));
16999
+ const combinedTentacleSway = (primaryTentacleWave + secondaryTentacleWave) * radiusX * (0.06 + tipBlend * 0.06);
17000
+ return {
17001
+ x: Math.sin(effectiveLongitude) * planarRadiusX +
17002
+ combinedTentacleSway +
17003
+ tentacleCurl * radiusX * 0.18,
17004
+ y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
17005
+ upperBlend * radiusY * 0.12 +
17006
+ lowerDrop +
17007
+ Math.sin(timeMs / 1380 + animationPhase + latitude * 1.5) * lowerBlend * radiusY * 0.022 +
17008
+ Math.cos(timeMs / 820 + tentacleInfluence.primaryPhase) *
17009
+ lowerBlend *
17010
+ tipBlend *
17011
+ tentacleInfluence.core *
17012
+ radiusY *
17013
+ 0.04,
17014
+ z: Math.cos(effectiveLongitude) * planarRadiusZ +
17015
+ Math.cos(timeMs / 960 + tentacleInfluence.primaryPhase + verticalProgress) *
17016
+ lowerBlend *
17017
+ tentacleInfluence.core *
17018
+ radiusZ *
17019
+ 0.044 +
17020
+ tentacleCurl * radiusZ * 0.14,
17021
+ };
17022
+ }
17023
+ /**
17024
+ * Blends nearby seeded tentacle profiles at one mesh longitude.
17025
+ *
17026
+ * @private helper of `octopus3d4AvatarVisual`
17027
+ */
17028
+ function resolveBlobbyTentacleInfluence(options, longitude) {
17029
+ let totalWeight = 0;
17030
+ let weightedSin = 0;
17031
+ let weightedCos = 0;
17032
+ let weightedWidthScale = 0;
17033
+ let weightedLengthScale = 0;
17034
+ let weightedSwayScale = 0;
17035
+ let weightedDepthScale = 0;
17036
+ let weightedCurlScale = 0;
17037
+ let weightedPrimaryPhase = 0;
17038
+ let weightedSecondaryPhase = 0;
17039
+ for (const tentacleProfile of options.tentacleProfiles) {
17040
+ const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
17041
+ const width = 0.22 * tentacleProfile.widthScale;
17042
+ const weight = Math.exp(-(distance * distance) / (width * width));
17043
+ totalWeight += weight;
17044
+ weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
17045
+ weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
17046
+ weightedWidthScale += tentacleProfile.widthScale * weight;
17047
+ weightedLengthScale += tentacleProfile.lengthScale * weight;
17048
+ weightedSwayScale += tentacleProfile.swayScale * weight;
17049
+ weightedDepthScale += tentacleProfile.depthScale * weight;
17050
+ weightedCurlScale += tentacleProfile.curlScale * weight;
17051
+ weightedPrimaryPhase += tentacleProfile.primaryPhase * weight;
17052
+ weightedSecondaryPhase += tentacleProfile.secondaryPhase * weight;
17053
+ }
17054
+ if (totalWeight < 0.0001) {
17055
+ return {
17056
+ core: 0,
17057
+ centerLongitude: longitude,
17058
+ widthScale: 1,
17059
+ lengthScale: 1,
17060
+ swayScale: 1,
17061
+ depthScale: 1,
17062
+ curlScale: 1,
17063
+ primaryPhase: 0,
17064
+ secondaryPhase: 0,
17065
+ };
17066
+ }
17067
+ return {
17068
+ core: clampNumber$1(totalWeight, 0, 1),
17069
+ centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
17070
+ widthScale: weightedWidthScale / totalWeight,
17071
+ lengthScale: weightedLengthScale / totalWeight,
17072
+ swayScale: weightedSwayScale / totalWeight,
17073
+ depthScale: weightedDepthScale / totalWeight,
17074
+ curlScale: weightedCurlScale / totalWeight,
17075
+ primaryPhase: weightedPrimaryPhase / totalWeight,
17076
+ secondaryPhase: weightedSecondaryPhase / totalWeight,
17077
+ };
17078
+ }
17079
+ /**
17080
+ * Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
17081
+ *
17082
+ * @private helper of `octopus3d4AvatarVisual`
17083
+ */
17084
+ function resolveBlobbyLobeWave(options, longitude) {
17085
+ const { morphologyProfile, animationPhase, timeMs } = options;
17086
+ return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT +
17087
+ animationPhase +
17088
+ timeMs / (940 + morphologyProfile.body.lobeCount * 18)) +
17089
+ 1) /
17090
+ 2);
17091
+ }
17092
+ /**
17093
+ * Resolves one base fill tone for a patch on the continuous octopus mesh.
17094
+ *
17095
+ * @private helper of `octopus3d4AvatarVisual`
17096
+ */
17097
+ function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
17098
+ const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
17099
+ if (tonalProgress < 0.12) {
17100
+ return palette.highlight;
17101
+ }
17102
+ if (tonalProgress < 0.3) {
17103
+ return palette.secondary;
17104
+ }
17105
+ if (tonalProgress < 0.72) {
17106
+ return forwardness > 0.56 ? palette.secondary : palette.primary;
17107
+ }
17108
+ return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
17109
+ }
17110
+ /**
17111
+ * Draws one projected mesh patch with multi-pass shading and a soft edge.
17112
+ *
17113
+ * @private helper of `octopus3d4AvatarVisual`
17114
+ */
17115
+ function drawBlobbyContinuousSurfacePatch(context, surfacePatch) {
17116
+ drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
17117
+ if (surfacePatch.lightIntensity > 0) {
17118
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.2 * surfacePatch.lightIntensity})`);
17119
+ }
17120
+ else if (surfacePatch.lightIntensity < 0) {
17121
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.26 * Math.abs(surfacePatch.lightIntensity)})`);
17122
+ }
17123
+ if (surfacePatch.rimLightIntensity > 0.04) {
17124
+ drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 247, 230, ${0.22 * surfacePatch.rimLightIntensity})`);
17125
+ }
17126
+ context.save();
17127
+ context.beginPath();
17128
+ context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
17129
+ for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
17130
+ context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
17131
+ }
17132
+ context.closePath();
17133
+ context.strokeStyle = surfacePatch.outlineColor;
17134
+ context.lineWidth = Math.max(0.6, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0026);
17135
+ context.lineJoin = 'round';
17136
+ context.stroke();
17137
+ context.restore();
17138
+ }
17139
+ /**
17140
+ * Draws projected mantle-current lines on the front of the mesh.
17141
+ *
17142
+ * @private helper of `octopus3d4AvatarVisual`
17143
+ */
17144
+ function drawBlobbyContinuousCurrents(options) {
17145
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
17146
+ const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
17147
+ const centerIndex = (currentCount - 1) / 2;
17148
+ context.save();
17149
+ context.lineCap = 'round';
17150
+ context.lineJoin = 'round';
17151
+ for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
17152
+ const baseLongitude = (currentIndex - centerIndex) * 0.15;
17153
+ const projectedPoints = [];
17154
+ for (let sampleIndex = 0; sampleIndex < 9; sampleIndex++) {
17155
+ const progress = sampleIndex / 8;
17156
+ const latitude = -0.48 + progress * 0.78;
17157
+ const longitude = baseLongitude + Math.sin(timeMs / 1140 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.038;
17158
+ const scenePoint = transformScenePoint(sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
17159
+ if (scenePoint.z > center.z - size * 0.016) {
17160
+ projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
17161
+ }
17162
+ }
17163
+ if (projectedPoints.length < 3) {
17164
+ continue;
17165
+ }
17166
+ context.beginPath();
17167
+ context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
17168
+ for (const projectedPoint of projectedPoints.slice(1)) {
17169
+ context.lineTo(projectedPoint.x, projectedPoint.y);
17170
+ }
17171
+ context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
17172
+ context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
17173
+ context.stroke();
17174
+ }
17175
+ context.restore();
17176
+ }
17177
+ /**
17178
+ * Draws small projected sucker highlights on the waving lower mesh lobes.
17179
+ *
17180
+ * @private helper of `octopus3d4AvatarVisual`
17181
+ */
17182
+ function drawBlobbyContinuousSuckers(options) {
17183
+ const { surfaceOptions, size } = options;
17184
+ const { timeMs } = surfaceOptions;
17185
+ for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
17186
+ if (Math.cos(tentacleProfile.centerLongitude) < -0.16) {
17187
+ continue;
17188
+ }
17189
+ for (let suckerIndex = 0; suckerIndex < 4; suckerIndex++) {
17190
+ const latitude = 0.5 + suckerIndex * 0.12;
17191
+ const sideOffset = tentacleProfile.suckerSide * (0.038 + suckerIndex * 0.014) * tentacleProfile.widthScale;
17192
+ const waveOffset = Math.sin(timeMs / 880 + tentacleProfile.primaryPhase + suckerIndex * 0.78) * 0.02;
17193
+ drawBlobbyContinuousSurfaceSpot({
17194
+ ...options,
17195
+ latitude,
17196
+ longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
17197
+ radiusScale: size * (0.0068 - suckerIndex * 0.0006),
17198
+ });
17199
+ }
17200
+ }
17201
+ }
17202
+ /**
17203
+ * Draws seeded pigment spots across the upper mesh for a richer skin texture.
17204
+ *
17205
+ * @private helper of `octopus3d4AvatarVisual`
17206
+ */
17207
+ function drawBlobbySkinSpots(options) {
17208
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, skinSpots, } = options;
17209
+ for (const skinSpot of skinSpots) {
17210
+ const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, skinSpot.latitude, skinSpot.longitude);
17211
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
17212
+ if (sceneCenterPoint.z <= center.z - size * 0.01) {
17213
+ continue;
17214
+ }
17215
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
17216
+ const spotRadius = size * skinSpot.radiusScale;
17217
+ context.save();
17218
+ context.beginPath();
17219
+ context.arc(projectedCenterPoint.x, projectedCenterPoint.y, spotRadius, 0, Math.PI * 2);
17220
+ context.fillStyle = `${palette.shadow}${formatAlphaHex(skinSpot.opacity)}`;
17221
+ context.fill();
17222
+ context.restore();
17223
+ }
17224
+ }
17225
+ /**
17226
+ * Draws one tiny projected surface spot by sampling local mesh tangents.
17227
+ *
17228
+ * @private helper of `octopus3d4AvatarVisual`
17229
+ */
17230
+ function drawBlobbyContinuousSurfaceSpot(options) {
17231
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
17232
+ const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude);
17233
+ const localHorizontal = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
17234
+ const localVertical = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
17235
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
17236
+ if (sceneCenterPoint.z <= center.z - size * 0.012) {
17237
+ return;
17238
+ }
17239
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
17240
+ const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
17241
+ const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
17242
+ const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
17243
+ radiusScale *
17244
+ 0.78, size * 0.003, size * 0.02);
17245
+ const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
17246
+ radiusScale *
17247
+ 0.54, size * 0.0024, size * 0.015);
17248
+ const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
17249
+ context.save();
17250
+ context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
17251
+ context.rotate(rotation);
17252
+ context.beginPath();
17253
+ context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
17254
+ context.fillStyle = `${palette.highlight}80`;
17255
+ context.fill();
17256
+ context.strokeStyle = `${palette.highlight}a8`;
17257
+ context.lineWidth = Math.max(0.7, size * 0.0028);
17258
+ context.stroke();
17259
+ context.restore();
17260
+ }
17261
+ /**
17262
+ * Draws a soft, slowly drifting gloss highlight on the front of the mesh.
17263
+ *
17264
+ * @private helper of `octopus3d4AvatarVisual`
17265
+ */
17266
+ function drawBlobbyContinuousGloss(options) {
17267
+ const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size } = options;
17268
+ const { timeMs, animationPhase } = surfaceOptions;
17269
+ const glossLatitude = -0.3 + Math.sin(timeMs / 2700 + animationPhase) * 0.04;
17270
+ const glossLongitude = -0.18 + Math.cos(timeMs / 2300 + animationPhase * 0.8) * 0.05;
17271
+ const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, glossLatitude, glossLongitude);
17272
+ const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
17273
+ if (sceneCenterPoint.z <= center.z) {
17274
+ return;
17275
+ }
17276
+ const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
17277
+ const glossRadius = size * 0.058;
17278
+ context.save();
17279
+ const glossGradient = context.createRadialGradient(projectedCenterPoint.x - glossRadius * 0.3, projectedCenterPoint.y - glossRadius * 0.4, glossRadius * 0.04, projectedCenterPoint.x, projectedCenterPoint.y, glossRadius);
17280
+ glossGradient.addColorStop(0, 'rgba(255, 255, 255, 0.34)');
17281
+ glossGradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.1)');
17282
+ glossGradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
17283
+ context.fillStyle = glossGradient;
17284
+ context.beginPath();
17285
+ context.ellipse(projectedCenterPoint.x, projectedCenterPoint.y, glossRadius * 1.05, glossRadius * 0.78, 0, 0, Math.PI * 2);
17286
+ context.fill();
17287
+ context.restore();
17288
+ }
17289
+ /**
17290
+ * Resolves a signed angular distance from the source longitude to the target longitude.
17291
+ *
17292
+ * @private helper of `octopus3d4AvatarVisual`
17293
+ */
17294
+ function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
17295
+ return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
17296
+ }
17297
+ /**
17298
+ * Smoothly maps a value between two bounds into `[0, 1]`.
17299
+ *
17300
+ * @private helper of `octopus3d4AvatarVisual`
17301
+ */
17302
+ function smoothStep(edgeStart, edgeEnd, value) {
17303
+ const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
17304
+ return progress * progress * (3 - 2 * progress);
17305
+ }
17306
+ /**
17307
+ * Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
17308
+ *
17309
+ * @private helper of `octopus3d4AvatarVisual`
17310
+ */
17311
+ function formatAlphaHex(opacity) {
17312
+ return Math.round(clampNumber$1(opacity, 0, 1) * 255)
17313
+ .toString(16)
17314
+ .padStart(2, '0');
17315
+ }
17316
+
15593
17317
  /* eslint-disable no-magic-numbers */
15594
17318
  /**
15595
17319
  * Octopus avatar visual.
@@ -16360,6 +18084,8 @@ const AVATAR_VISUALS = [
16360
18084
  octopus3AvatarVisual,
16361
18085
  octopus3dAvatarVisual,
16362
18086
  octopus3d2AvatarVisual,
18087
+ octopus3d3AvatarVisual,
18088
+ octopus3d4AvatarVisual,
16363
18089
  asciiOctopusAvatarVisual,
16364
18090
  minecraftAvatarVisual,
16365
18091
  minecraft2AvatarVisual,
@@ -17039,6 +18765,70 @@ class MetaLinkCommitmentDefinition extends BaseCommitmentDefinition {
17039
18765
  }
17040
18766
  // Note: [💞] Ignore a discrepancy between file name and entity name
17041
18767
 
18768
+ /**
18769
+ * META VISIBILITY commitment definition.
18770
+ *
18771
+ * The `META VISIBILITY` commitment stores whether an agent is public, private, or unlisted.
18772
+ * Agents Server mirrors this value into the database for efficient filtering, but the book
18773
+ * commitment remains the editable source of truth.
18774
+ *
18775
+ * @private Metadata-only commitment used by Agents Server.
18776
+ */
18777
+ class MetaVisibilityCommitmentDefinition extends BaseCommitmentDefinition {
18778
+ constructor() {
18779
+ super('META VISIBILITY');
18780
+ }
18781
+ /**
18782
+ * Short one-line description of META VISIBILITY.
18783
+ */
18784
+ get description() {
18785
+ return 'Set whether the agent is private, unlisted, or public.';
18786
+ }
18787
+ /**
18788
+ * Icon for this commitment.
18789
+ */
18790
+ get icon() {
18791
+ return '👁️';
18792
+ }
18793
+ /**
18794
+ * Markdown documentation for META VISIBILITY commitment.
18795
+ */
18796
+ get documentation() {
18797
+ return spaceTrim$1(`
18798
+ # META VISIBILITY
18799
+
18800
+ Sets the agent visibility used by Agents Server.
18801
+
18802
+ ## Allowed values
18803
+
18804
+ - \`PRIVATE\` - accessible only to signed-in users with access.
18805
+ - \`UNLISTED\` - accessible by direct link but hidden from public listings.
18806
+ - \`PUBLIC\` - visible in public listings and accessible by anyone.
18807
+
18808
+ ## Key aspects
18809
+
18810
+ - Does not modify the agent's behavior, system message, or tools.
18811
+ - Whitespace and letter case are normalized when persisted.
18812
+ - If multiple \`META VISIBILITY\` commitments are present, persistence keeps one normalized value.
18813
+ - Agents Server mirrors the value into the database for filtering, but the book is the source of truth.
18814
+
18815
+ ## Example
18816
+
18817
+ \`\`\`book
18818
+ Helpful Assistant
18819
+
18820
+ GOAL Be helpful and friendly.
18821
+ META VISIBILITY PUBLIC
18822
+ \`\`\`
18823
+ `);
18824
+ }
18825
+ applyToAgentModelRequirements(requirements, content) {
18826
+ // META VISIBILITY is metadata only and does not alter model requirements.
18827
+ return requirements;
18828
+ }
18829
+ }
18830
+ // Note: [💞] Ignore a discrepancy between file name and entity name
18831
+
17042
18832
  /**
17043
18833
  * META VOICE commitment definition
17044
18834
  *
@@ -18648,7 +20438,15 @@ function buildTeammateMetadata(entry) {
18648
20438
  * Builds the teammate request text, optionally including context.
18649
20439
  */
18650
20440
  function buildTeammateRequest(message, context) {
18651
- return context ? `${message}\n\nContext:\n${context}` : message;
20441
+ if (!context) {
20442
+ return message;
20443
+ }
20444
+ return spaceTrim$1((block) => `
20445
+ ${block(message)}
20446
+
20447
+ Context:
20448
+ ${block(context)}
20449
+ `);
18652
20450
  }
18653
20451
  /**
18654
20452
  * Builds a minimal chat prompt for teammate calls.
@@ -22474,7 +24272,11 @@ function createUseProjectToolFunctions() {
22474
24272
  const lineRangedContent = applyOptionalLineRange(decodedContent, args.startLine, args.endLine);
22475
24273
  const wasCharacterTruncated = lineRangedContent.length > MAX_PROJECT_FILE_CONTENT_CHARACTERS;
22476
24274
  const contentToReturn = wasCharacterTruncated
22477
- ? `${lineRangedContent.slice(0, MAX_PROJECT_FILE_CONTENT_CHARACTERS)}\n\n[...truncated...]`
24275
+ ? spaceTrim$1((block) => `
24276
+ ${block(lineRangedContent.slice(0, MAX_PROJECT_FILE_CONTENT_CHARACTERS))}
24277
+
24278
+ [...truncated...]
24279
+ `)
22478
24280
  : lineRangedContent;
22479
24281
  const wasTruncated = decoded.isTruncated || wasCharacterTruncated;
22480
24282
  if (wasCharacterTruncated) {
@@ -25550,6 +27352,7 @@ const COMMITMENT_REGISTRY = [
25550
27352
  new MetaDomainCommitmentDefinition(),
25551
27353
  new MetaDisclaimerCommitmentDefinition(),
25552
27354
  new MetaInputPlaceholderCommitmentDefinition(),
27355
+ new MetaVisibilityCommitmentDefinition(),
25553
27356
  new MetaCommitmentDefinition(),
25554
27357
  new MetaVoiceCommitmentDefinition(),
25555
27358
  new NoteCommitmentDefinition('NOTE'),
@@ -26010,11 +27813,19 @@ const JsonFileImportPlugin = {
26010
27813
  try {
26011
27814
  const json = JSON.parse(content);
26012
27815
  const formattedJson = JSON.stringify(json, null, 4);
26013
- return `\`\`\`json\n${formattedJson}\n\`\`\``;
27816
+ return spaceTrim$1((block) => `
27817
+ \`\`\`json
27818
+ ${block(formattedJson)}
27819
+ \`\`\`
27820
+ `);
26014
27821
  }
26015
27822
  catch (error) {
26016
27823
  // If JSON is invalid, still import it but maybe not as pretty JSON
26017
- return `\`\`\`json\n${content}\n\`\`\``;
27824
+ return spaceTrim$1((block) => `
27825
+ \`\`\`json
27826
+ ${block(content)}
27827
+ \`\`\`
27828
+ `);
26018
27829
  }
26019
27830
  },
26020
27831
  };
@@ -26038,7 +27849,11 @@ const TextFileImportPlugin = {
26038
27849
  import(content, mimeType) {
26039
27850
  const extension = mimeTypeToExtension(mimeType);
26040
27851
  const codeBlockType = extension || 'txt';
26041
- return `\`\`\`${codeBlockType}\n${content}\n\`\`\``;
27852
+ return spaceTrim$1((block) => `
27853
+ \`\`\`${codeBlockType}
27854
+ ${block(content)}
27855
+ \`\`\`
27856
+ `);
26042
27857
  },
26043
27858
  };
26044
27859
 
@@ -26285,11 +28100,19 @@ function collectExampleInteractionLines(parseResult, samples) {
26285
28100
  const examples = [];
26286
28101
  const initialMessage = (_a = parseResult.commitments.find((commitment) => commitment.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
26287
28102
  if (initialMessage) {
26288
- examples.push(`**Agent:**\n${initialMessage}`);
28103
+ examples.push(spaceTrim$1((block) => `
28104
+ **Agent:**
28105
+ ${block(initialMessage)}
28106
+ `));
26289
28107
  }
26290
28108
  if (samples && samples.length > 0) {
26291
28109
  for (const sample of samples) {
26292
- examples.push(`**User:** ${sample.question}\n\n**Agent:**\n${sample.answer}`);
28110
+ examples.push(spaceTrim$1((block) => `
28111
+ **User:** ${block(String(sample.question))}
28112
+
28113
+ **Agent:**
28114
+ ${block(sample.answer)}
28115
+ `));
26293
28116
  }
26294
28117
  }
26295
28118
  return examples;
@@ -26874,6 +28697,7 @@ const META_COMMITMENT_APPLIERS = {
26874
28697
  'META COLOR': applyMetaColorContent,
26875
28698
  'META FONT': applyMetaFontContent,
26876
28699
  'META VOICE': applyMetaVoiceContent,
28700
+ 'META VISIBILITY': applyMetaVisibilityContent,
26877
28701
  };
26878
28702
  /**
26879
28703
  * Applies META-style commitments that mutate parsed profile metadata.
@@ -26903,6 +28727,10 @@ function applyGenericMetaCommitment(state, content) {
26903
28727
  applyMetaAvatarContent(state, metaValue);
26904
28728
  return;
26905
28729
  }
28730
+ if (metaTypeRaw.toUpperCase() === 'VISIBILITY') {
28731
+ applyMetaVisibilityContent(state, metaValue);
28732
+ return;
28733
+ }
26906
28734
  const metaType = normalizeTo_camelCase(metaTypeRaw);
26907
28735
  state.meta[metaType] = metaValue;
26908
28736
  }
@@ -26979,6 +28807,15 @@ function applyMetaFontContent(state, content) {
26979
28807
  function applyMetaVoiceContent(state, content) {
26980
28808
  state.meta.voice = spaceTrim$1(content);
26981
28809
  }
28810
+ /**
28811
+ * Applies META VISIBILITY content into the normalized `meta.visibility` field.
28812
+ */
28813
+ function applyMetaVisibilityContent(state, content) {
28814
+ const visibility = normalizeAgentVisibility(content);
28815
+ if (visibility) {
28816
+ state.meta.visibility = visibility;
28817
+ }
28818
+ }
26982
28819
  /**
26983
28820
  * Normalizes the separator in the content
26984
28821
  *
@@ -28004,7 +29841,11 @@ function removeLeadingTopLevelHeading(markdown) {
28004
29841
  * @private internal utility of `createStandaloneBookLanguageMarkdown`
28005
29842
  */
28006
29843
  function renderDocumentationSection(title, documentation) {
28007
- return `#### ${title}\n\n${removeLeadingTopLevelHeading(documentation)}`;
29844
+ return spaceTrim$1((block) => `
29845
+ #### ${title}
29846
+
29847
+ ${block(removeLeadingTopLevelHeading(documentation))}
29848
+ `);
28008
29849
  }
28009
29850
  /**
28010
29851
  * Renders the documentation body for one grouped commitment entry.
@@ -28080,6 +29921,7 @@ const PROFILE_COMMITMENT_TYPES = new Set([
28080
29921
  'META DESCRIPTION',
28081
29922
  'META DISCLAIMER',
28082
29923
  'META INPUT PLACEHOLDER',
29924
+ 'META VISIBILITY',
28083
29925
  'META COLOR',
28084
29926
  'META FONT',
28085
29927
  'META VOICE',
@@ -28655,7 +30497,9 @@ function parseBookMessageHeader(line) {
28655
30497
  * @private internal utility of `Book`
28656
30498
  */
28657
30499
  function parseCommitmentHeader(line) {
28658
- const match = /^([A-Z][A-Z0-9]*(?: [A-Z0-9]+)*)(?:\s+(.*))?$/u.exec(line);
30500
+ // Require at least 2 characters in the first keyword word to avoid treating common
30501
+ // single-letter words (e.g. "V" in Czech, "I" or "A" in English) as commitment headers.
30502
+ const match = /^([A-Z][A-Z0-9]+(?: [A-Z0-9]+)*)(?:\s+(.*))?$/u.exec(line);
28659
30503
  if (!match) {
28660
30504
  return null;
28661
30505
  }
@@ -28753,18 +30597,23 @@ function $randomBase58(length) {
28753
30597
  *
28754
30598
  * @private shared persistence helper for `AgentCollectionInSupabase`
28755
30599
  */
28756
- function prepareAgentSourceForPersistence(agentSource) {
28757
- let agentProfile = parseAgentSource(agentSource);
28758
- const permanentId = agentProfile.permanentId;
28759
- const strippedAgentSource = stripMetaIdLines(agentSource);
28760
- if (strippedAgentSource !== agentSource) {
28761
- agentSource = strippedAgentSource;
28762
- agentProfile = parseAgentSource(agentSource);
28763
- }
30600
+ function prepareAgentSourceForPersistence(agentSource, options = {}) {
30601
+ var _a, _b, _c;
30602
+ const originalAgentProfile = parseAgentSource(agentSource);
30603
+ const permanentId = originalAgentProfile.permanentId;
30604
+ agentSource = stripMetaIdLines(agentSource);
30605
+ const sourceVisibility = parseAgentSourceVisibility(agentSource, { isStrict: true });
30606
+ const resolvedVisibility = options.isVisibilityOverride || !sourceVisibility
30607
+ ? (_b = (_a = options.visibility) !== null && _a !== void 0 ? _a : sourceVisibility) !== null && _b !== void 0 ? _b : DEFAULT_AGENT_VISIBILITY
30608
+ : sourceVisibility;
30609
+ agentSource = setAgentSourceVisibility(agentSource, resolvedVisibility);
30610
+ const agentProfile = parseAgentSource(agentSource);
30611
+ const visibility = (_c = parseAgentSourceVisibility(agentSource, { isStrict: true })) !== null && _c !== void 0 ? _c : undefined;
28764
30612
  return {
28765
30613
  agentProfile,
28766
30614
  agentSource,
28767
30615
  permanentId,
30616
+ visibility,
28768
30617
  };
28769
30618
  }
28770
30619
  /**
@@ -28795,8 +30644,11 @@ function stripMetaIdLines(agentSource) {
28795
30644
  * @private shared persistence helper for `AgentCollectionInSupabase`
28796
30645
  */
28797
30646
  function createAgentPersistenceRecords(agentSource, options = {}, createdAt = new Date().toISOString()) {
28798
- const preparedAgentSource = prepareAgentSourceForPersistence(agentSource);
28799
- const { agentProfile, agentSource: normalizedAgentSource } = preparedAgentSource;
30647
+ const preparedAgentSource = prepareAgentSourceForPersistence(agentSource, {
30648
+ visibility: options.visibility,
30649
+ isVisibilityOverride: options.visibility !== undefined,
30650
+ });
30651
+ const { agentProfile, agentSource: normalizedAgentSource, visibility } = preparedAgentSource;
28800
30652
  const permanentId = preparedAgentSource.permanentId || $randomBase58(14);
28801
30653
  const { agentName, agentHash } = agentProfile;
28802
30654
  const agentInsertRecord = {
@@ -28816,8 +30668,8 @@ function createAgentPersistenceRecords(agentSource, options = {}, createdAt = ne
28816
30668
  if (options.sortOrder !== undefined) {
28817
30669
  agentInsertRecord.sortOrder = options.sortOrder;
28818
30670
  }
28819
- if (options.visibility !== undefined) {
28820
- agentInsertRecord.visibility = options.visibility;
30671
+ if (visibility !== undefined) {
30672
+ agentInsertRecord.visibility = visibility;
28821
30673
  }
28822
30674
  return {
28823
30675
  createdAgent: {
@@ -28851,6 +30703,18 @@ function normalizeHistoryVersionName(versionName) {
28851
30703
  const normalizedVersionName = versionName.trim();
28852
30704
  return normalizedVersionName.length > 0 ? normalizedVersionName : null;
28853
30705
  }
30706
+ /**
30707
+ * Builds a Supabase `.or()` filter for agent name or permanent id lookups.
30708
+ *
30709
+ * @param agentNameOrPermanentId - Agent name or stable permanent identifier to match.
30710
+ * @returns `.or()` filter string safe to pass to Supabase.
30711
+ *
30712
+ * @private internal helper of `AgentCollectionInSupabase`
30713
+ */
30714
+ function buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId) {
30715
+ const encodedAgentIdentifier = encodeURIComponent(agentNameOrPermanentId);
30716
+ return `agentName.eq.${encodedAgentIdentifier},permanentId.eq.${encodedAgentIdentifier}`;
30717
+ }
28854
30718
  /**
28855
30719
  * Agent collection stored in a Supabase table.
28856
30720
  *
@@ -28898,21 +30762,63 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱‍🚀] implements AgentCol
28898
30762
  if (isVerbose) {
28899
30763
  console.info(`Found ${selectResult.data.length} agents in directory`);
28900
30764
  }
28901
- return selectResult.data.map(({ agentName, agentProfile, permanentId }) => {
28902
- if (isVerbose && agentProfile.agentName !== agentName) {
28903
- console.warn(spaceTrim(`
30765
+ return selectResult.data.map((row) => this.mapAgentBasicInformationRow(row, isVerbose));
30766
+ }
30767
+ /**
30768
+ * Finds one active agent profile by its human-readable name or permanent id.
30769
+ *
30770
+ * This keeps route-level lookups from loading the whole agent collection when only
30771
+ * one canonical route identifier is needed.
30772
+ *
30773
+ * @param agentNameOrPermanentId - Agent name or stable permanent identifier.
30774
+ * @returns Matching active agent profile or `null` when not found.
30775
+ *
30776
+ * @public exported from `@promptbook/core`
30777
+ */
30778
+ async findAgentBasicInformation(agentNameOrPermanentId) {
30779
+ var _a;
30780
+ const { isVerbose = DEFAULT_IS_VERBOSE } = this.options || {};
30781
+ const selectResult = await this.supabaseClient
30782
+ .from(this.getTableName('Agent'))
30783
+ .select('agentName,agentProfile,permanentId')
30784
+ .or(buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId))
30785
+ .is('deletedAt', null)
30786
+ .order('createdAt', { ascending: true })
30787
+ .limit(1);
30788
+ if (selectResult.error) {
30789
+ throw new DatabaseError(spaceTrim((block) => `
30790
+
30791
+ Error fetching agent "${agentNameOrPermanentId}" from Supabase:
30792
+
30793
+ ${block(selectResult.error.message)}
30794
+ `));
30795
+ }
30796
+ const row = (_a = selectResult.data) === null || _a === void 0 ? void 0 : _a[0];
30797
+ return row ? this.mapAgentBasicInformationRow(row, isVerbose) : null;
30798
+ }
30799
+ /**
30800
+ * Converts one database row into public agent information.
30801
+ *
30802
+ * @param row - Database row carrying the persisted profile snapshot.
30803
+ * @param isVerbose - Whether profile-name mismatches should be logged.
30804
+ * @returns Agent profile with canonical database name and permanent id.
30805
+ *
30806
+ * @private internal helper of `AgentCollectionInSupabase`
30807
+ */
30808
+ mapAgentBasicInformationRow({ agentName, agentProfile, permanentId }, isVerbose) {
30809
+ if (isVerbose && agentProfile.agentName !== agentName) {
30810
+ console.warn(spaceTrim(`
28904
30811
  Agent name mismatch for agent "${agentName}". Using name from database.
28905
30812
 
28906
30813
  agentName: "${agentName}"
28907
30814
  agentProfile.agentName: "${agentProfile.agentName}"
28908
30815
  `));
28909
- }
28910
- return {
28911
- ...agentProfile,
28912
- agentName,
28913
- permanentId: permanentId || agentProfile.permanentId,
28914
- };
28915
- });
30816
+ }
30817
+ return {
30818
+ ...agentProfile,
30819
+ agentName,
30820
+ permanentId: permanentId || agentProfile.permanentId,
30821
+ };
28916
30822
  }
28917
30823
  /**
28918
30824
  * Retrieves the permanent ID of an agent by its name or permanent ID.
@@ -28921,7 +30827,7 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱‍🚀] implements AgentCol
28921
30827
  const selectResult = await this.supabaseClient
28922
30828
  .from(this.getTableName('Agent'))
28923
30829
  .select('permanentId')
28924
- .or(`agentName.eq.${agentNameOrPermanentId},permanentId.eq.${agentNameOrPermanentId}`)
30830
+ .or(buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId))
28925
30831
  .order('createdAt', { ascending: true }) // Pick oldest if multiple match by name
28926
30832
  .limit(1);
28927
30833
  if (selectResult.error || !selectResult.data || selectResult.data.length === 0) {
@@ -28936,7 +30842,7 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱‍🚀] implements AgentCol
28936
30842
  const selectResult = await this.supabaseClient
28937
30843
  .from(this.getTableName('Agent'))
28938
30844
  .select('agentSource')
28939
- .or(`agentName.eq.${agentNameOrPermanentId},permanentId.eq.${agentNameOrPermanentId}`)
30845
+ .or(buildAgentNameOrPermanentIdFilter(agentNameOrPermanentId))
28940
30846
  .is('deletedAt', null)
28941
30847
  .order('createdAt', { ascending: true }) // Pick oldest if multiple match by name
28942
30848
  .limit(1);
@@ -28980,7 +30886,7 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱‍🚀] implements AgentCol
28980
30886
  async updateAgentSource(permanentId, agentSource, options = {}) {
28981
30887
  const selectPreviousAgentResult = await this.supabaseClient
28982
30888
  .from(this.getTableName('Agent'))
28983
- .select('agentHash,agentName,permanentId')
30889
+ .select('agentHash,agentName,permanentId,visibility')
28984
30890
  .eq('permanentId', permanentId)
28985
30891
  .single();
28986
30892
  if (selectPreviousAgentResult.error) {
@@ -28995,8 +30901,11 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱‍🚀] implements AgentCol
28995
30901
  selectPreviousAgentResult.data.agentName;
28996
30902
  const previousAgentHash = selectPreviousAgentResult.data.agentHash;
28997
30903
  const previousPermanentId = selectPreviousAgentResult.data.permanentId;
28998
- const preparedAgentSource = prepareAgentSourceForPersistence(agentSource);
28999
- const { agentProfile, agentSource: normalizedAgentSource } = preparedAgentSource;
30904
+ const previousVisibility = selectPreviousAgentResult.data.visibility;
30905
+ const preparedAgentSource = prepareAgentSourceForPersistence(agentSource, {
30906
+ visibility: previousVisibility,
30907
+ });
30908
+ const { agentProfile, agentSource: normalizedAgentSource, visibility } = preparedAgentSource;
29000
30909
  let { permanentId: newPermanentId } = preparedAgentSource;
29001
30910
  const { agentHash, agentName } = agentProfile;
29002
30911
  if (!newPermanentId && previousPermanentId) {
@@ -29018,6 +30927,7 @@ class AgentCollectionInSupabase /* TODO: [🌈][🐱‍🚀] implements AgentCol
29018
30927
  updatedAt: new Date().toISOString(),
29019
30928
  agentHash: agentProfile.agentHash,
29020
30929
  agentSource: normalizedAgentSource,
30930
+ visibility: visibility !== null && visibility !== void 0 ? visibility : previousVisibility,
29021
30931
  promptbookEngineVersion: PROMPTBOOK_ENGINE_VERSION,
29022
30932
  })
29023
30933
  .eq('permanentId', permanentId);
@@ -34417,7 +36327,8 @@ function colorizeAvailableProviderMessage(providerMessage, { isInstalled, isFull
34417
36327
  * @private internal function of `$registeredLlmToolsMessage`
34418
36328
  */
34419
36329
  function createUsedEnvMessage() {
34420
- return `Unknown \`.env\` file` ;
36330
+ return `Unknown \`.env\` file`
36331
+ ;
34421
36332
  }
34422
36333
  // TODO: [®] DRY Register logic
34423
36334
  // TODO: [🧠][⚛] Maybe pass env as argument
@@ -35730,7 +37641,9 @@ class OpenAiAgentKitExecutionToolsToolBuilder {
35730
37641
  if (!executionTools || !executionTools.script) {
35731
37642
  throw new PipelineExecutionError(`Model requested tools but no executionTools.script were provided in OpenAiAgentKitExecutionTools options`);
35732
37643
  }
35733
- return Array.isArray(executionTools.script) ? executionTools.script : [executionTools.script];
37644
+ return Array.isArray(executionTools.script)
37645
+ ? executionTools.script
37646
+ : [executionTools.script];
35734
37647
  }
35735
37648
  /**
35736
37649
  * Resolves the assistant-visible AgentKit tool response while preserving structured tool result data.
@@ -38792,7 +40705,7 @@ class OpenAiVectorStoreKnowledgeSourcePreparer {
38792
40705
  return { skippedReason: 'invalid_data_url' };
38793
40706
  }
38794
40707
  return {
38795
- file: new File([parsed.buffer], parsed.filename, {
40708
+ file: new File([new Uint8Array(parsed.buffer)], parsed.filename, {
38796
40709
  type: parsed.mimeType,
38797
40710
  }),
38798
40711
  sizeBytes: parsed.buffer.length,
@@ -40555,7 +42468,7 @@ function emitAgentLlmExecutionToolsAssistantPreparationProgress(options) {
40555
42468
  * Computes one stable hash from a JSON-serializable value.
40556
42469
  */
40557
42470
  function computeJsonHash$1(value) {
40558
- return SHA256(JSON.stringify(value)).toString();
42471
+ return CryptoJS.SHA256(JSON.stringify(value)).toString();
40559
42472
  }
40560
42473
  /**
40561
42474
  * Handles OpenAI AgentKit-backed executions for `AgentLlmExecutionTools`.
@@ -40713,7 +42626,7 @@ AgentLlmExecutionToolsAgentKitRunner.vectorStoreCache = new Map();
40713
42626
  * Computes one stable hash from a JSON-serializable value.
40714
42627
  */
40715
42628
  function computeJsonHash(value) {
40716
- return SHA256(JSON.stringify(value)).toString();
42629
+ return CryptoJS.SHA256(JSON.stringify(value)).toString();
40717
42630
  }
40718
42631
  /**
40719
42632
  * Removes assistant-managed requirements before the prompt is executed via OpenAI Assistants.
@@ -41103,7 +43016,11 @@ function truncateAttachmentInlineText(content, maxCharacters) {
41103
43016
  };
41104
43017
  }
41105
43018
  return {
41106
- content: `${content.slice(0, Math.max(0, maxCharacters))}\n\n[...truncated...]`,
43019
+ content: spaceTrim$1((block) => `
43020
+ ${block(content.slice(0, Math.max(0, maxCharacters)))}
43021
+
43022
+ [...truncated...]
43023
+ `),
41107
43024
  isTruncated: true,
41108
43025
  };
41109
43026
  }
@@ -41569,7 +43486,7 @@ class AgentLlmExecutionTools {
41569
43486
  * Returns a virtual model name representing the agent behavior.
41570
43487
  */
41571
43488
  get modelName() {
41572
- const hash = SHA256(hexEncoder.parse(this.options.agentSource)).toString( /* hex */);
43489
+ const hash = CryptoJS.SHA256(hexEncoder.parse(this.options.agentSource)).toString( /* hex */);
41573
43490
  const agentId = hash.substring(0, 10);
41574
43491
  return (normalizeToKebabCase(this.title) + '-' + agentId);
41575
43492
  }
@@ -45280,7 +47197,7 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
45280
47197
  title: 'Anthropic Claude',
45281
47198
  packageName: '@promptbook/anthropic-claude',
45282
47199
  className: 'AnthropicClaudeExecutionTools',
45283
- envVariables: ['ANTHROPIC_CLAUDE_API_KEY'],
47200
+ envVariables: ['ANTHROPIC_API_KEY', 'ANTHROPIC_CLAUDE_API_KEY'],
45284
47201
  trustLevel: 'CLOSED',
45285
47202
  order: MODEL_ORDERS.TOP_TIER,
45286
47203
  getBoilerplateConfiguration() {
@@ -45297,14 +47214,17 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
45297
47214
  };
45298
47215
  },
45299
47216
  createConfigurationFromEnv(env) {
47217
+ var _a;
45300
47218
  // Note: Note using `process.env` BUT `env` to pass in the environment variables dynamically
45301
- if (typeof env.ANTHROPIC_CLAUDE_API_KEY === 'string') {
47219
+ // Note: `ANTHROPIC_API_KEY` takes precedence over the deprecated `ANTHROPIC_CLAUDE_API_KEY`
47220
+ const apiKey = (_a = env.ANTHROPIC_API_KEY) !== null && _a !== void 0 ? _a : env.ANTHROPIC_CLAUDE_API_KEY;
47221
+ if (typeof apiKey === 'string') {
45302
47222
  return {
45303
47223
  title: 'Claude (from env)',
45304
47224
  packageName: '@promptbook/anthropic-claude',
45305
47225
  className: 'AnthropicClaudeExecutionTools',
45306
47226
  options: {
45307
- apiKey: env.ANTHROPIC_CLAUDE_API_KEY,
47227
+ apiKey,
45308
47228
  },
45309
47229
  };
45310
47230
  }
@@ -48715,20 +50635,7 @@ function appendChatAttachmentContext(messageContent, attachments) {
48715
50635
  }
48716
50636
 
48717
50637
  /**
48718
- * Core Promptbook server configuration
48719
- *
48720
- * Used for "Adam" agent which is built in as default ancestor for new agents and other well known agents
48721
- *
48722
- * @public exported from `@promptbook/core`
48723
- */
48724
- const CORE_AGENTS_SERVER = {
48725
- title: 'Promptbook Core',
48726
- description: `Core Promptbook server used for Adam agent which is built in as default ancestor for new agents and other well known agents.`,
48727
- owner: PROMPTBOOK_LEGAL_ENTITY,
48728
- url: 'https://core.ptbk.io/',
48729
- };
48730
- /**
48731
- * Names of well known agents hosted on Core Promptbook server
50638
+ * Names of well known agents bundled in the local `.core` folder of every Agents Server
48732
50639
  *
48733
50640
  * - `Adam`: The default ancestor agent for new agents
48734
50641
  * - `Teacher`: Agent that knows book syntax and can help with self-learning
@@ -48754,7 +50661,6 @@ const CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES = {
48754
50661
  * @public exported from `@promptbook/core`
48755
50662
  */
48756
50663
  const PUBLIC_AGENTS_SERVERS = [
48757
- CORE_AGENTS_SERVER,
48758
50664
  {
48759
50665
  title: 'Promptbook Gallery',
48760
50666
  description: `Gallery of ideas, AI professions,... like AI Agenta photobank.`,
@@ -49204,5 +51110,5 @@ function $generateBookBoilerplate(options) {
49204
51110
  }
49205
51111
  // TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
49206
51112
 
49207
- export { $bookTranspilersRegister, $generateBookBoilerplate, $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, API_REQUEST_TIMEOUT, AbstractFormatError, Agent, AgentCollectionInSupabase, AgentLlmExecutionTools, AgentOsTranspiler, AnthropicClaudeManagedTranspiler, AnthropicClaudeSdkTranspiler, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, Book, CLAIM, CLI_APP_ID, COLOR_CONSTANTS, CORE_AGENTS_SERVER, CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, ConflictError, CsvFormatError, CsvFormatParser, DEFAULT_AGENTS_DIRNAME, DEFAULT_BOOK, DEFAULT_BOOKS_DIRNAME, DEFAULT_BOOK_OUTPUT_PARAMETER_NAME, DEFAULT_BOOK_TITLE, DEFAULT_CSV_SETTINGS, DEFAULT_DOWNLOAD_CACHE_DIRNAME, DEFAULT_EXECUTION_CACHE_DIRNAME, DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME, DEFAULT_INTERMEDIATE_FILES_STRATEGY, DEFAULT_IS_AUTO_INSTALLED, DEFAULT_IS_VERBOSE, DEFAULT_MAX_CONCURRENT_UPLOADS, DEFAULT_MAX_EXECUTION_ATTEMPTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_DEPTH, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_TOTAL, DEFAULT_MAX_PARALLEL_COUNT, DEFAULT_MAX_RECURSION, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_SIMULATED_DURATION_MS, DEFAULT_TASK_TITLE, DatabaseError, E2BTranspiler, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, FormattedBookInMarkdownTranspiler, GENERIC_PIPELINE_INTERFACE, GeneratorFormfactorDefinition, GenericFormfactorDefinition, HTTP_STATUS_CODES, ImageGeneratorFormfactorDefinition, KnowledgeScrapeError, LIMITS, LimitReachedError, MANDATORY_CSV_SETTINGS, MAX_FILENAME_LENGTH, MODEL_ORDERS, MODEL_TRUST_LEVELS, MODEL_VARIANTS, MatcherFormfactorDefinition, MemoryStorage, MissingToolsError, MultipleLlmExecutionTools, NAME, NETWORK_LIMITS, NonTaskSectionTypes, NotAllowed, NotFoundError, NotYetImplementedCommitmentDefinition, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, OpenAiAgentsTranspiler, OpenAiSdkTranspiler, PADDING_LINES, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_CHAT_COLOR, PROMPTBOOK_COLOR, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, PROMPTBOOK_LEGAL_ENTITY, PROMPTBOOK_LOGO_URL, PROMPTBOOK_SYNTAX_COLORS, PROMPT_PARAMETER_SELF_LEARNING_ENABLED, PUBLIC_AGENTS_SERVERS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, RESERVED_PARAMETER_NAMES, RemoteAgent, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TIME_INTERVALS, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, USER_CHAT_COLOR, UnexpectedError, VirusTotalFileSecurityChecker, WrappedError, ZERO_USAGE, ZERO_VALUE, _AgentMetadata, _AgentRegistration, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, aboutPromptbookInformation, addUsage, appendChatAttachmentContext, appendChatAttachmentContextWithContent, book, cacheLlmTools, compilePipeline, computeAgentHash, computeCosineSimilarity, countUsage, createAgentLlmExecutionTools, createAgentModelRequirements, createBasicAgentModelRequirements, createDefaultAgentName, createEmptyAgentModelRequirements, createLlmToolsFromConfiguration, createPipelineCollectionFromJson, createPipelineCollectionFromPromise, createPipelineCollectionFromUrl, createPipelineExecutor, createPipelineSubcollection, createStandaloneBookLanguageMarkdown, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, formatChatAttachmentContentContext, formatChatAttachmentContext, generatePlaceholderAgentProfileImageUrl, getAllCommitmentDefinitions, getAllCommitmentTypes, getAllCommitmentsToolTitles, getCommitmentDefinition, getGroupedCommitmentDefinitions, getPipelineInterface, getSingleLlmExecutionTools, identificationToPromptbookToken, isCommitmentSupported, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isValidBook, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, normalizeAgentName, normalizeChatAttachments, padBook, parseAgentSource, parseParameters, parsePipeline, pipelineCollectionToJson, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, resolveChatAttachmentContents, unpreparePipeline, usageToHuman, usageToWorktime, validateBook, validatePipeline, validatePipelineString };
51113
+ export { $bookTranspilersRegister, $generateBookBoilerplate, $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, AGENT_VISIBILITY_VALUES, API_REQUEST_TIMEOUT, AbstractFormatError, Agent, AgentCollectionInSupabase, AgentLlmExecutionTools, AgentOsTranspiler, AnthropicClaudeManagedTranspiler, AnthropicClaudeSdkTranspiler, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, Book, CLAIM, CLI_APP_ID, COLOR_CONSTANTS, CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, ConflictError, CsvFormatError, CsvFormatParser, DEFAULT_AGENTS_DIRNAME, DEFAULT_AGENT_VISIBILITY, DEFAULT_BOOK, DEFAULT_BOOKS_DIRNAME, DEFAULT_BOOK_OUTPUT_PARAMETER_NAME, DEFAULT_BOOK_TITLE, DEFAULT_CSV_SETTINGS, DEFAULT_DOWNLOAD_CACHE_DIRNAME, DEFAULT_EXECUTION_CACHE_DIRNAME, DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME, DEFAULT_INTERMEDIATE_FILES_STRATEGY, DEFAULT_IS_AUTO_INSTALLED, DEFAULT_IS_VERBOSE, DEFAULT_MAX_CONCURRENT_UPLOADS, DEFAULT_MAX_EXECUTION_ATTEMPTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_DEPTH, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_TOTAL, DEFAULT_MAX_PARALLEL_COUNT, DEFAULT_MAX_RECURSION, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_SIMULATED_DURATION_MS, DEFAULT_TASK_TITLE, DatabaseError, E2BTranspiler, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, FormattedBookInMarkdownTranspiler, GENERIC_PIPELINE_INTERFACE, GeneratorFormfactorDefinition, GenericFormfactorDefinition, HTTP_STATUS_CODES, ImageGeneratorFormfactorDefinition, KnowledgeScrapeError, LIMITS, LimitReachedError, MANDATORY_CSV_SETTINGS, MAX_FILENAME_LENGTH, MODEL_ORDERS, MODEL_TRUST_LEVELS, MODEL_VARIANTS, MatcherFormfactorDefinition, MemoryStorage, MissingToolsError, MultipleLlmExecutionTools, NAME, NETWORK_LIMITS, NonTaskSectionTypes, NotAllowed, NotFoundError, NotYetImplementedCommitmentDefinition, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, OpenAiAgentsTranspiler, OpenAiSdkTranspiler, PADDING_LINES, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_CHAT_COLOR, PROMPTBOOK_COLOR, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, PROMPTBOOK_LEGAL_ENTITY, PROMPTBOOK_LOGO_URL, PROMPTBOOK_SYNTAX_COLORS, PROMPT_PARAMETER_SELF_LEARNING_ENABLED, PUBLIC_AGENTS_SERVERS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, RESERVED_PARAMETER_NAMES, RemoteAgent, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TIME_INTERVALS, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, USER_CHAT_COLOR, UnexpectedError, VirusTotalFileSecurityChecker, WrappedError, ZERO_USAGE, ZERO_VALUE, _AgentMetadata, _AgentRegistration, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, aboutPromptbookInformation, addUsage, appendChatAttachmentContext, appendChatAttachmentContextWithContent, book, cacheLlmTools, compilePipeline, computeAgentHash, computeCosineSimilarity, countUsage, createAgentLlmExecutionTools, createAgentModelRequirements, createBasicAgentModelRequirements, createDefaultAgentName, createEmptyAgentModelRequirements, createLlmToolsFromConfiguration, createPipelineCollectionFromJson, createPipelineCollectionFromPromise, createPipelineCollectionFromUrl, createPipelineExecutor, createPipelineSubcollection, createStandaloneBookLanguageMarkdown, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, formatChatAttachmentContentContext, formatChatAttachmentContext, generatePlaceholderAgentProfileImageUrl, getAllCommitmentDefinitions, getAllCommitmentTypes, getAllCommitmentsToolTitles, getCommitmentDefinition, getGroupedCommitmentDefinitions, getNextAgentVisibility, getPipelineInterface, getSingleLlmExecutionTools, identificationToPromptbookToken, isAgentVisibility, isCommitmentSupported, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isPublicAgentVisibility, isValidBook, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, normalizeAgentName, normalizeAgentVisibility, normalizeChatAttachments, padBook, parseAgentSource, parseAgentSourceVisibility, parseAgentVisibility, parseAgentVisibilityStrict, parseParameters, parsePipeline, pipelineCollectionToJson, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, resolveChatAttachmentContents, setAgentSourceVisibility, unpreparePipeline, usageToHuman, usageToWorktime, validateBook, validatePipeline, validatePipelineString };
49208
51114
  //# sourceMappingURL=index.es.js.map